[UPDT] PAYROLL: Added new design for payslip and method for generating the pdf of payslip and sending it in the mail

This commit is contained in:
Horilla
2025-01-09 13:25:16 +05:30
parent dfff6453dc
commit 3f90b197c7
7 changed files with 643 additions and 71 deletions

View File

@@ -158,7 +158,7 @@
{% endif %} {% endif %}
<div class="oh-sticky-table__td oh-sticky-table__right"style="width: 251px !important;"> <div class="oh-sticky-table__td oh-sticky-table__right"style="width: 251px !important;">
<div class="oh-btn-group"> <div class="oh-btn-group">
<form action="{% url 'payslip-pdf' payslip.id %}" method='post' class="w-75"> <form action="{% url 'view-payslip-pdf' payslip.id %}" method='post' class="w-75">
{% csrf_token %} {% csrf_token %}
<button type='submit' title="{% trans 'Download' %}" class="oh-btn oh-btn--light-bkg w-100"> <ion-icon name="download"></ion-icon></button> <button type='submit' title="{% trans 'Download' %}" class="oh-btn oh-btn--light-bkg w-100"> <ion-icon name="download"></ion-icon></button>
</form> </form>

View File

@@ -115,7 +115,7 @@
<div class="oh-sticky-table__td"> <div class="oh-sticky-table__td">
<div class="oh-btn-group"> <div class="oh-btn-group">
<a href="{% url 'view-created-payslip' payslip.id %}" type="button" title="{% trans 'View' %}" class="oh-btn oh-btn--light-bkg w-50"> <ion-icon name="eye-outline"></ion-icon></a> <a href="{% url 'view-created-payslip' payslip.id %}" type="button" title="{% trans 'View' %}" class="oh-btn oh-btn--light-bkg w-50"> <ion-icon name="eye-outline"></ion-icon></a>
<a href="{% url 'payslip-pdf' payslip.id %}" type="button" title="{% trans 'Download' %}" class="oh-btn oh-btn--light-bkg w-50"> <ion-icon name="download"></ion-icon></a> <a href="{% url 'view-payslip-pdf' payslip.id %}" type="button" title="{% trans 'Download' %}" class="oh-btn oh-btn--light-bkg w-50"> <ion-icon name="download"></ion-icon></a>
{% if perms.payroll.add_payslip %} {% if perms.payroll.add_payslip %}
<a <a
hx-confirm="{% trans 'Do you want to sent the payslip by mail?' %}" hx-get="{% url "send-slip" %}?id={{payslip.id}}" hx-target="#payslips-table" hx-confirm="{% trans 'Do you want to sent the payslip by mail?' %}" hx-get="{% url "send-slip" %}?id={{payslip.id}}" hx-target="#payslips-table"

View File

@@ -13,7 +13,7 @@
<div class="oh-payslip" id="payslips-table"> <div class="oh-payslip" id="payslips-table">
<div class="oh-payslip__header"> <div class="oh-payslip__header">
<div class="oh-payslip__header-left" style="min-width: 170px; height: 35px; border: 1px solid hsl(213,22%,84%); display:flex;"> <div class="oh-payslip__header-left" style="min-width: 170px; height: 35px; border: 1px solid hsl(213,22%,84%); display:flex;">
<a href="{% url 'payslip-pdf' instance.id %}" type="button" title="{% trans 'Download' %}" class="oh-btn oh-btn--gray-bkg w-100" style="background-color: #dadada;"> <ion-icon name="download"></ion-icon></a> <a href="{% url 'view-payslip-pdf' instance.id %}" type="button" title="{% trans 'Download' %}" class="oh-btn oh-btn--gray-bkg w-100" style="background-color: #dadada;"> <ion-icon name="download"></ion-icon></a>
{% if perms.payroll.add_payslip %} {% if perms.payroll.add_payslip %}
<a <a
hx-confirm="{% trans 'Do you want to sent the payslip by mail?' %}" hx-get="{% url "send-slip" %}?id={{instance.id}}&view=individual-payslip" hx-target="#payslips-table" hx-confirm="{% trans 'Do you want to sent the payslip by mail?' %}" hx-get="{% url "send-slip" %}?id={{instance.id}}&view=individual-payslip" hx-target="#payslips-table"

View File

@@ -162,7 +162,7 @@
{% endif %} {% endif %}
<div class="oh-sticky-table__td oh-sticky-table__right"style="width: 251px !important;"> <div class="oh-sticky-table__td oh-sticky-table__right"style="width: 251px !important;">
<div class="oh-btn-group"> <div class="oh-btn-group">
<form action="{% url 'payslip-pdf' payslip.id %}" method='post' class="w-100"> <form action="{% url 'view-payslip-pdf' payslip.id %}" method='post' class="w-100">
{% csrf_token %} {% csrf_token %}
<button type='submit' title="{% trans 'Download' %}" class="oh-btn oh-btn--light-bkg w-100"> <ion-icon name="download"></ion-icon></button> <button type='submit' title="{% trans 'Download' %}" class="oh-btn oh-btn--light-bkg w-100"> <ion-icon name="download"></ion-icon></button>
</form> </form>

View File

@@ -0,0 +1,421 @@
{% load static i18n horillafilters %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
rel="stylesheet"
/>
<style>
html {
-webkit-box-sizing: border-box;
box-sizing: border-box;
overflow-x: hidden;
font-family: "Inter", serif;
}
body {
background-color: #f5f7ff;
margin: 0;
}
table {
caption-side: bottom;
border-collapse: collapse;
border: 1px solid #ccc;
}
table tr,
table td,
table th {
border: 1px solid #ccc;
padding: 10px;
text-align: left;
}
.oh-payslip {
background: #fff;
padding: 40px;
font-size: 16px;
}
.oh-payslip-container {
max-width: 880px;
padding: 30px 15px;
margin-left: auto;
margin-right: auto;
}
.oh-payslip-table .oh-payslip-td-head {
color: #777;
margin-right: 16px;
}
.oh-payslip-table .oh-payslip-td-value {
color: #111;
}
.oh-footer-total .oh-footer-gross-text {
margin: 0;
color: #777;
}
.oh-footer-total .oh-footer-total-amount {
font-weight: 700;
width: 25%;
}
.oh-footer-total h5 {
font-weight: 700;
margin: 0px 0 12px 0;
}
.oh-payslip_head {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
-webkit-box-align: end;
-ms-flex-align: end;
align-items: flex-end;
padding-bottom: 25px;
border-bottom: 1px solid #eaeaea;
margin-bottom: 25px;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.oh-logo img {
width: 150px;
-o-object-fit: cover;
object-fit: cover;
}
.oh-payslip-secondhead p {
color: #777;
}
.oh-payslip-secondhead h4 {
font-size: 16px;
margin: 10px 0;
font-weight: 700;
}
.oh-payslip-company-name {
font-weight: 700;
font-size: 20px;
}
.oh-payslip-company-address {
font-size: 16px;
color: #777;
width: 90%;
word-wrap: break-word;
margin-bottom: 0;
}
.oh-payslip_left .oh-payslip-left-title {
font-size: 16px;
color: #777;
}
.oh-payslip_left .oh-payslip-left-value {
font-size: 16px;
font-weight: 700;
color: #111;
margin: 10px 0;
}
.oh-allowance-table td {
color: #777;
}
.oh-allowance-table .oh-allowance-amount {
color: #111;
width: 25%;
}
.oh-bg-color {
background-color: #f7f7f7;
}
.oh-footer-page-number {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
margin-top: 24px;
}
.oh-footer-page-number p {
color: #777;
margin: 0;
}
.mt-4 {
margin-top: 24px;
}
.w-100 {
width: 100%;
}
.w-50 {
width: 50%;
}
.d-flex {
display: flex;
}
.justify-content-between {
justify-content: space-between;
}
.align-item-center {
align-items: center;
}
.gap-2 {
gap: 16px;
}
</style>
<script>
function printDiv() {
const printContents = document.getElementById('printDiv').innerHTML;
const originalContents = document.body.innerHTML;
// Replace body content with print content
document.body.innerHTML = printContents;
// Add event listener for after printing
window.onafterprint = () => {
window.location.href = document.referrer; // Redirect to the previous page
};
// Open print dialog
window.print();
// Restore original content
document.body.innerHTML = originalContents;
}
// Call printDiv on page load
window.onload = printDiv;
</script>
</head>
<body id="printDiv">
<div class="container oh-payslip-container">
<div class="oh-payslip">
<div class="oh-payslip_in" id="download_section">
{% if employee.employee_work_info.company_id %}
<div class="oh-payslip_head">
<div class="oh-payslip_left">
<p class="oh-payslip-company-name">{{employee.employee_work_info.company_id}}</p>
<p class="oh-payslip-company-address">
{{employee.employee_work_info.company_id.address}}
{{employee.employee_work_info.company_id.country}} {{employee.employee_work_info.company_id.state}}, {{employee.employee_work_info.company_id.city}}
{{employee.employee_work_info.company_id.zip}}
</p>
</div>
<div class="cs-payslip_right cs-text_right">
<div class="oh-logo cs-mb5">
<img src="{{protocol}}://{{host}}{{employee.employee_work_info.company_id.icon.url}}" alt="Logo" />
</div>
</div>
</div>
{% else %}
<div class="oh-payslip_head">
<div class="oh-payslip_left">
<p class="oh-payslip-company-name">{{company}} - Headquarters</p>
<p class="oh-payslip-company-address">
{{company.address}}
{{company.country}} {{company.state}}, {{company.city}}
{{company.zip}}
</p>
</div>
<div class="cs-payslip_right cs-text_right">
<div class="oh-logo cs-mb5">
<img src="{{protocol}}://{{host}}{{company.icon.url}}" alt="Logo" />
</div>
</div>
</div>
{% endif %}
<div
class="oh-payslip-secondhead d-flex justify-content-between cs-mb10"
>
<h4>{{month_start_name}} {% trans "to" %} {{month_end_name}} {% trans "Payslip" %}</h4>
<div class="oh-payslip_left">
<div class="d-flex align-item-center gap-2">
<span class="oh-payslip-left-title">{% trans "Employee Netpay :" %} </span>
<p class="oh-payslip-left-value">{{net_pay|floatformat:2|currency_symbol_position}}<br /></p>
</div>
</div>
</div>
<table class="oh-payslip-table w-100 mt-4">
<tbody>
<tr class="">
<td class="w-50">
<span class="oh-payslip-td-head">{% trans "Employee ID :" %}</span>
<span class="oh-payslip-td-value">{{employee.badge_id}}</span>
</td>
<td class="w-50">
<span class="oh-payslip-td-head">{% trans "Employee Name :" %}</span>
<span class="oh-payslip-td-value">{{employee}}</span>
</td>
</tr>
<tr>
<td class="w-50">
<span class="oh-payslip-td-head">{% trans "Department :" %}</span>
<span class="oh-payslip-td-value">{{employee.employee_work_info.department_id.department}}</span>
</td>
<td class="w-50">
<span class="oh-payslip-td-head">{% trans "Bank Acc./Cheque No :" %}</span>
<span class="oh-payslip-td-value">{{employee.employee_bank_details.account_number}}</span>
</td>
</tr>
</tbody>
</table>
<table class="w-100 oh-allowance-table mt-4">
<thead>
<tr class="oh-bg-color">
<th class="">{% trans "Allowance" %}</th>
<th class="">{% trans "Amount" %}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
{% trans "Basic Pay" %}</td>
<td class="oh-allowance-amount">
{{basic_pay|floatformat:2|currency_symbol_position}}
</td>
</tr>
{% for allowance in all_allowances %}
<tr>
<td class="">{{allowance.title}}</td>
<td class="oh-allowance-amount">
{{allowance.amount|floatformat:2|currency_symbol_position}}
</td>
</tr>
{% endfor %}
</tbody>
<tfoot class="oh-payslip__table-tfoot">
<tr class="oh-payslip__table-tr">
<th
class="oh-payslip__table-tf"
>
{% trans "Total Gross Pay" %}
</th>
<th class="oh-payslip__table-tf">
{{gross_pay|floatformat:2|currency_symbol_position}}
</th>
</tr>
</tfoot>
</table>
<table class="w-100 oh-allowance-table mt-4">
<thead>
<tr class="oh-bg-color">
<th class="">{% trans "Deducation" %}</th>
<th class="">{% trans "Amount" %}</th>
</tr>
</thead>
<tbody>
<tr>
<td class="">{% trans "Loss of Pay" %}</td>
<td class="oh-allowance-amount">
{{loss_of_pay|floatformat:2|currency_symbol_position}}
</td>
</tr>
{% for deduction in basic_pay_deductions %}
<tr>
<td class="">{{deduction.title}}</td>
<td class="oh-allowance-amount">
{{deduction.amount|floatformat:2|currency_symbol_position}}
</td>
</tr>
{% endfor %}
{% for deduction in gross_pay_deductions %}
<tr>
<td class="">{{deduction.title}}</td>
<td class="oh-allowance-amount">
{{deduction.amount|floatformat:2|currency_symbol_position}}
</td>
</tr>
{% endfor %}
{% for deduction in pretax_deductions %}
<tr>
<td class="">{{deduction.title}}</td>
<td class="oh-allowance-amount">
{{deduction.amount|floatformat:2|currency_symbol_position}}
</td>
</tr>
{% endfor %}
{% for deduction in post_tax_deductions %}
<tr>
<td class="">{{deduction.title}}</td>
<td class="oh-allowance-amount">
{{deduction.amount|floatformat:2|currency_symbol_position}}
</td>
</tr>
{% endfor %}
<tr>
<td class="">{% trans "Federal Tax" %}</td>
<td class="oh-allowance-amount">
{{federal_tax|floatformat:2|currency_symbol_position}}
</td>
</tr>
{% for deduction in tax_deductions %}
<tr>
<td class="">{{deduction.title}}</td>
<td class="oh-allowance-amount">
{{deduction.amount|floatformat:2|currency_symbol_position}}
</td>
</tr>
{% endfor %}
{% for deduction in net_deductions %}
<tr>
<td class="">{{deduction.title}}</td>
<td class="oh-allowance-amount">
{{deduction.amount|floatformat:2|currency_symbol_position}}
</td>
</tr>
{% endfor %}
</tbody>
<tfoot class="oh-payslip__table-tfoot">
<tr class="oh-payslip__table-tr">
<th class="oh-payslip__table-tf">{% trans "Total Deductions" %}</th>
<th class="oh-payslip__table-tf">
{{total_deductions|floatformat:2|currency_symbol_position}}
</th>
</tr>
</tfoot>
</table>
</div>
<div class="oh-footer-total mt-4">
<table class="w-100">
<tbody>
<tr class="">
<td class="">
<h5>{% trans "Total Net Payable" %}</h5>
<p class="oh-footer-gross-text">
{% trans "Gross earning - Total Deduction" %}
</p>
</td>
<td class="oh-footer-total-amount">
{{net_pay|floatformat:2|currency_symbol_position}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</body>
<button type="button" onclick="printDiv()" id="" style="display:none;">
print as PDF
</button>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>
</html>

View File

@@ -75,6 +75,11 @@ urlpatterns = [
name="view-created-payslip", name="view-created-payslip",
kwargs={"model": Payslip}, kwargs={"model": Payslip},
), ),
path(
"view-payslip-pdf/<int:payslip_id>/",
views.view_payslip_pdf,
name="view-payslip-pdf",
),
path( path(
"delete-payslip/<int:payslip_id>/", views.delete_payslip, name="delete-payslip" "delete-payslip/<int:payslip_id>/", views.delete_payslip, name="delete-payslip"
), ),

View File

@@ -11,10 +11,12 @@ from itertools import groupby
from urllib.parse import parse_qs from urllib.parse import parse_qs
import pandas as pd import pandas as pd
import pdfkit
from django.contrib import messages from django.contrib import messages
from django.db.models import ProtectedError, Q from django.db.models import ProtectedError, Q
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@@ -535,6 +537,91 @@ def bulk_update_payslip_status(request):
return JsonResponse({"type": "success", "message": "Payslips status updated"}) return JsonResponse({"type": "success", "message": "Payslips status updated"})
@login_required
def view_payslip_pdf(request, payslip_id):
from .component_views import filter_payslip
if Payslip.objects.filter(id=payslip_id).exists():
payslip = Payslip.objects.get(id=payslip_id)
company = Company.objects.filter(hq=True).first()
if (
request.user.has_perm("payroll.view_payslip")
or payslip.employee_id.employee_user_id == request.user
):
user = request.user
employee = user.employee_get
# Taking the company_name of the user
info = EmployeeWorkInformation.objects.filter(employee_id=employee)
if info.exists():
for data in info:
employee_company = data.company_id
company_name = Company.objects.filter(company=employee_company)
emp_company = company_name.first()
# Access the date_format attribute directly
date_format = (
emp_company.date_format
if emp_company and emp_company.date_format
else "MMM. D, YYYY"
)
else:
date_format = "MMM. D, YYYY"
data = payslip.pay_head_data
start_date_str = data["start_date"]
end_date_str = data["end_date"]
# Convert the string to a datetime.date object
start_date = datetime.strptime(start_date_str, "%Y-%m-%d").date()
end_date = datetime.strptime(end_date_str, "%Y-%m-%d").date()
month_start_name = start_date.strftime("%B %d, %Y")
month_end_name = end_date.strftime("%B %d, %Y")
# Formatted date for each format
for format_name, format_string in HORILLA_DATE_FORMATS.items():
if format_name == date_format:
formatted_start_date = start_date.strftime(format_string)
for format_name, format_string in HORILLA_DATE_FORMATS.items():
if format_name == date_format:
formatted_end_date = end_date.strftime(format_string)
data["month_start_name"] = month_start_name
data["month_end_name"] = month_end_name
data["formatted_start_date"] = formatted_start_date
data["formatted_end_date"] = formatted_end_date
data["employee"] = payslip.employee_id
data["payslip"] = payslip
data["json_data"] = data.copy()
data["json_data"]["employee"] = payslip.employee_id.id
data["json_data"]["payslip"] = payslip.id
data["instance"] = payslip
data["currency"] = PayrollSettings.objects.first().currency_symbol
data["all_deductions"] = []
for deduction_list in [
data["basic_pay_deductions"],
data["gross_pay_deductions"],
data["pretax_deductions"],
data["post_tax_deductions"],
data["tax_deductions"],
data["net_deductions"],
]:
data["all_deductions"].extend(deduction_list)
data["all_allowances"] = data["allowances"].copy()
equalize_lists_length(data["allowances"], data["all_deductions"])
data["zipped_data"] = zip(data["allowances"], data["all_deductions"])
data["host"] = request.get_host()
data["protocol"] = "https" if request.is_secure() else "http"
data["company"] = company
return render(request, "payroll/payslip/test_pdf.html", context=data)
return redirect(filter_payslip)
return render(request, "405.html")
@login_required @login_required
# @permission_required("payroll.view_payslip") # @permission_required("payroll.view_payslip")
def view_created_payslip(request, payslip_id, **kwargs): def view_created_payslip(request, payslip_id, **kwargs):
@@ -1351,80 +1438,139 @@ def equalize_lists_length(allowances, deductions):
return deductions, allowances return deductions, allowances
def generate_payslip_pdf(template_path, context, html=False):
"""
Generate a PDF file from an HTML template and context data.
Args:
template_path (str): The path to the HTML template.
context (dict): The context data to render the template.
html (bool): If True, return raw HTML instead of a PDF.
Returns:
HttpResponse: A response with the generated PDF file or raw HTML.
"""
try:
# Render the HTML content from the template and context
html_content = render_to_string(template_path, context)
# Return raw HTML if requested
if html:
return HttpResponse(html_content, content_type="text/html")
# PDF options for pdfkit
pdf_options = {
"page-size": "A4",
"margin-top": "10mm",
"margin-bottom": "10mm",
"margin-left": "10mm",
"margin-right": "10mm",
"encoding": "UTF-8",
"enable-local-file-access": None, # Required to load local CSS/images
}
# Generate the PDF as binary content
pdf = pdfkit.from_string(html_content, False, options=pdf_options)
# Return an HttpResponse containing the PDF content
response = HttpResponse(pdf, content_type="application/pdf")
response["Content-Disposition"] = "inline; filename=payslip.pdf"
return response
except Exception as e:
# Handle errors gracefully
return HttpResponse(f"Error generating PDF: {str(e)}", status=500)
def payslip_pdf(request, id): def payslip_pdf(request, id):
payslip = Payslip.objects.get(id=id) """
if ( Generate the payslip as a PDF and return it in an HttpResponse.
request.user.has_perm("payroll.view_payslip")
or payslip.employee_id.employee_user_id == request.user
):
user = request.user
employee = user.employee_get
# Taking the company_name of the user Args:
info = EmployeeWorkInformation.objects.filter(employee_id=employee) request (HttpRequest): The request object.
if info.exists(): id (int): The ID of the payslip to generate.
for data in info:
employee_company = data.company_id
company_name = Company.objects.filter(company=employee_company)
emp_company = company_name.first()
# Access the date_format attribute directly Returns:
date_format = ( HttpResponse: A response containing the PDF content.
emp_company.date_format """
if emp_company and emp_company.date_format
else "MMM. D, YYYY" from .component_views import filter_payslip
if Payslip.objects.filter(id=id).exists():
payslip = Payslip.objects.get(id=id)
company = Company.objects.filter(hq=True).first()
if (
request.user.has_perm("payroll.view_payslip")
or payslip.employee_id.employee_user_id == request.user
):
user = request.user
employee = user.employee_get
# Taking the company_name of the user
info = EmployeeWorkInformation.objects.filter(employee_id=employee)
if info.exists():
for data in info:
employee_company = data.company_id
company_name = Company.objects.filter(company=employee_company)
emp_company = company_name.first()
# Access the date_format attribute directly
date_format = (
emp_company.date_format
if emp_company and emp_company.date_format
else "MMM. D, YYYY"
)
data = payslip.pay_head_data
start_date_str = data["start_date"]
end_date_str = data["end_date"]
# Convert the string to a datetime.date object
start_date = datetime.strptime(start_date_str, "%Y-%m-%d").date()
end_date = datetime.strptime(end_date_str, "%Y-%m-%d").date()
# Format the start and end dates
for format_name, format_string in HORILLA_DATE_FORMATS.items():
if format_name == date_format:
formatted_start_date = start_date.strftime(format_string)
formatted_end_date = end_date.strftime(format_string)
# Prepare context for the template
data.update(
{
"month_start_name": start_date.strftime("%B %d, %Y"),
"month_end_name": end_date.strftime("%B %d, %Y"),
"formatted_start_date": formatted_start_date,
"formatted_end_date": formatted_end_date,
"employee": payslip.employee_id,
"payslip": payslip,
"json_data": data.copy(),
"currency": PayrollSettings.objects.first().currency_symbol,
"all_deductions": [],
"all_allowances": data["allowances"].copy(),
"host": request.get_host(),
"protocol": "https" if request.is_secure() else "http",
"company": company,
}
) )
else:
date_format = "MMM. D, YYYY"
data = payslip.pay_head_data # Merge deductions and allowances for display
start_date_str = data["start_date"] for deduction_list in [
end_date_str = data["end_date"] data["basic_pay_deductions"],
data["gross_pay_deductions"],
data["pretax_deductions"],
data["post_tax_deductions"],
data["tax_deductions"],
data["net_deductions"],
]:
data["all_deductions"].extend(deduction_list)
# Convert the string to a datetime.date object equalize_lists_length(data["allowances"], data["all_deductions"])
start_date = datetime.strptime(start_date_str, "%Y-%m-%d").date() data["zipped_data"] = zip(data["allowances"], data["all_deductions"])
end_date = datetime.strptime(end_date_str, "%Y-%m-%d").date() template_path = "payroll/payslip/test_pdf.html"
month_start_name = start_date.strftime("%B %d") return generate_payslip_pdf(template_path, context=data, html=False)
month_end_name = end_date.strftime("%B %d") return redirect(filter_payslip)
return render(request, "405.html")
# Formatted date for each format
for format_name, format_string in HORILLA_DATE_FORMATS.items():
if format_name == date_format:
formatted_start_date = start_date.strftime(format_string)
for format_name, format_string in HORILLA_DATE_FORMATS.items():
if format_name == date_format:
formatted_end_date = end_date.strftime(format_string)
data["month_start_name"] = month_start_name
data["month_end_name"] = month_end_name
data["formatted_start_date"] = formatted_start_date
data["formatted_end_date"] = formatted_end_date
data["employee"] = payslip.employee_id
data["payslip"] = payslip
data["json_data"] = data.copy()
data["json_data"]["employee"] = payslip.employee_id.id
data["json_data"]["payslip"] = payslip.id
data["instance"] = payslip
data["currency"] = PayrollSettings.objects.first().currency_symbol
data["all_deductions"] = []
for deduction_list in [
data["basic_pay_deductions"],
data["gross_pay_deductions"],
data["pretax_deductions"],
data["post_tax_deductions"],
data["tax_deductions"],
data["net_deductions"],
]:
data["all_deductions"].extend(deduction_list)
data["all_allowances"] = data["allowances"].copy()
equalize_lists_length(data["allowances"], data["all_deductions"])
data["zipped_data"] = zip(data["allowances"], data["all_deductions"])
data["host"] = request.get_host()
data["protocol"] = "https" if request.is_secure() else "http"
return generate_pdf("payroll/payslip/individual_pdf.html", context=data, html=False)
@login_required @login_required