[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:
@@ -158,7 +158,7 @@
|
||||
{% endif %}
|
||||
<div class="oh-sticky-table__td oh-sticky-table__right"style="width: 251px !important;">
|
||||
<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 %}
|
||||
<button type='submit' title="{% trans 'Download' %}" class="oh-btn oh-btn--light-bkg w-100"> <ion-icon name="download"></ion-icon></button>
|
||||
</form>
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
<div class="oh-sticky-table__td">
|
||||
<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 '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 %}
|
||||
<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"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="oh-payslip" id="payslips-table">
|
||||
<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;">
|
||||
<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 %}
|
||||
<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"
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
{% endif %}
|
||||
<div class="oh-sticky-table__td oh-sticky-table__right"style="width: 251px !important;">
|
||||
<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 %}
|
||||
<button type='submit' title="{% trans 'Download' %}" class="oh-btn oh-btn--light-bkg w-100"> <ion-icon name="download"></ion-icon></button>
|
||||
</form>
|
||||
|
||||
421
payroll/templates/payroll/payslip/test_pdf.html
Normal file
421
payroll/templates/payroll/payslip/test_pdf.html
Normal 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>
|
||||
@@ -75,6 +75,11 @@ urlpatterns = [
|
||||
name="view-created-payslip",
|
||||
kwargs={"model": Payslip},
|
||||
),
|
||||
path(
|
||||
"view-payslip-pdf/<int:payslip_id>/",
|
||||
views.view_payslip_pdf,
|
||||
name="view-payslip-pdf",
|
||||
),
|
||||
path(
|
||||
"delete-payslip/<int:payslip_id>/", views.delete_payslip, name="delete-payslip"
|
||||
),
|
||||
|
||||
@@ -11,10 +11,12 @@ from itertools import groupby
|
||||
from urllib.parse import parse_qs
|
||||
|
||||
import pandas as pd
|
||||
import pdfkit
|
||||
from django.contrib import messages
|
||||
from django.db.models import ProtectedError, Q
|
||||
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
|
||||
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.utils import timezone
|
||||
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"})
|
||||
|
||||
|
||||
@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
|
||||
# @permission_required("payroll.view_payslip")
|
||||
def view_created_payslip(request, payslip_id, **kwargs):
|
||||
@@ -1351,80 +1438,139 @@ def equalize_lists_length(allowances, deductions):
|
||||
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):
|
||||
payslip = Payslip.objects.get(id=id)
|
||||
if (
|
||||
request.user.has_perm("payroll.view_payslip")
|
||||
or payslip.employee_id.employee_user_id == request.user
|
||||
):
|
||||
user = request.user
|
||||
employee = user.employee_get
|
||||
"""
|
||||
Generate the payslip as a PDF and return it in an HttpResponse.
|
||||
|
||||
# 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()
|
||||
Args:
|
||||
request (HttpRequest): The request object.
|
||||
id (int): The ID of the payslip to generate.
|
||||
|
||||
# Access the date_format attribute directly
|
||||
date_format = (
|
||||
emp_company.date_format
|
||||
if emp_company and emp_company.date_format
|
||||
else "MMM. D, YYYY"
|
||||
Returns:
|
||||
HttpResponse: A response containing the PDF content.
|
||||
"""
|
||||
|
||||
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
|
||||
start_date_str = data["start_date"]
|
||||
end_date_str = data["end_date"]
|
||||
# Merge deductions and allowances for display
|
||||
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)
|
||||
|
||||
# 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()
|
||||
equalize_lists_length(data["allowances"], data["all_deductions"])
|
||||
data["zipped_data"] = zip(data["allowances"], data["all_deductions"])
|
||||
template_path = "payroll/payslip/test_pdf.html"
|
||||
|
||||
month_start_name = start_date.strftime("%B %d")
|
||||
month_end_name = end_date.strftime("%B %d")
|
||||
|
||||
# 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)
|
||||
return generate_payslip_pdf(template_path, context=data, html=False)
|
||||
return redirect(filter_payslip)
|
||||
return render(request, "405.html")
|
||||
|
||||
|
||||
@login_required
|
||||
|
||||
Reference in New Issue
Block a user