From c31455fc89a5b6d8450a9770b58ad22546116e5c Mon Sep 17 00:00:00 2001 From: Horilla Date: Wed, 6 Mar 2024 10:05:56 +0530 Subject: [PATCH] [UPDT] PAYROLL: Added contribution to export --- payroll/forms/forms.py | 18 ++ payroll/methods/payslip_calc.py | 16 +- payroll/static/payroll/dashboard.js | 19 -- payroll/templates/payroll/dashboard.html | 91 ++++----- payroll/views/component_views.py | 2 +- payroll/views/views.py | 235 ++++++++++++++++------- 6 files changed, 243 insertions(+), 138 deletions(-) diff --git a/payroll/forms/forms.py b/payroll/forms/forms.py index 9920c7cad..9c5e8f51e 100644 --- a/payroll/forms/forms.py +++ b/payroll/forms/forms.py @@ -8,6 +8,9 @@ from django.forms import widgets from django.utils.translation import gettext_lazy as trans from django.template.loader import render_to_string from base import thread_local_middleware +from base.forms import Form +from employee.models import Employee +from payroll.context_processors import get_active_employees from payroll.models.models import ( EncashmentGeneralSettings, PayrollGeneralSetting, @@ -176,3 +179,18 @@ class EncashmentGeneralSettingsForm(ModelForm): class Meta: model = EncashmentGeneralSettings fields = "__all__" + + +class DashboardExport(Form): + status_choices = [ + ("",""), + ("draft", "Draft"), + ("review_ongoing", "Review Ongoing"), + ("confirmed", "Confirmed"), + ("paid", "Paid"), + ] + start_date = forms.DateField(required=False, widget=forms.DateInput(attrs={'type': 'date',"class":"oh-input w-100"})) + end_date = forms.DateField(required=False, widget=forms.DateInput(attrs={'type': 'date',"class":"oh-input w-100"})) + employees = forms.ChoiceField(required=False, choices=[(emp.id, emp.get_full_name()) for emp in Employee.objects.all()],widget=forms.SelectMultiple) + status = forms.ChoiceField(required=False, choices=status_choices) + contributions = forms.ChoiceField(required=False, choices=[(emp.id, emp.get_full_name()) for emp in get_active_employees(None)["get_active_employees"]],widget=forms.SelectMultiple) diff --git a/payroll/methods/payslip_calc.py b/payroll/methods/payslip_calc.py index 0ffc5af86..3ee4801d0 100644 --- a/payroll/methods/payslip_calc.py +++ b/payroll/methods/payslip_calc.py @@ -357,7 +357,7 @@ def calculate_tax_deduction(*_args, **kwargs): deductions_amt.append(amount) for deduction, amount in zip(deductions, deductions_amt): employer_contribution_amount = calculate_employer_contribution( - deduction, amount + deduction, kwargs[deduction.based_on] ) serialized_deduction = { "deduction_id": deduction.id, @@ -466,7 +466,7 @@ def calculate_pre_tax_deduction(*_args, **kwargs): pre_tax_deductions_amt.append(if_condition_on(**kwargs)) for deduction, amount in zip(pre_tax_deductions, pre_tax_deductions_amt): employer_contribution_amount = calculate_employer_contribution( - deduction, amount + deduction, kwargs[deduction.based_on] ) serialized_deduction = { "deduction_id": deduction.id, @@ -547,7 +547,7 @@ def calculate_post_tax_deduction(*_args, **kwargs): else: if deduction.based_on != "net_pay": calculation_function = calculation_mapping.get(deduction.based_on) - amount = calculation_function( + amount, taxable_gross_pay = calculation_function( **{ "employee": employee, "start_date": start_date, @@ -557,6 +557,7 @@ def calculate_post_tax_deduction(*_args, **kwargs): "total_allowance": total_allowance, "basic_pay": basic_pay, "day_dict": day_dict, + "taxable_deduction":True, } ) kwargs["amount"] = amount @@ -566,7 +567,7 @@ def calculate_post_tax_deduction(*_args, **kwargs): for deduction, amount in zip(post_tax_deductions, post_tax_deductions_amt): employer_contribution_amount = calculate_employer_contribution( - deduction, amount + deduction, taxable_gross_pay ) serialized_deduction = { "deduction_id": deduction.id, @@ -613,7 +614,7 @@ def calculate_net_pay_deduction(net_pay, net_pay_deductions, **kwargs): net_deduction = 0 for deduction, amount in zip(deductions, deduction_amt): employer_contribution_amount = calculate_employer_contribution( - deduction, amount + deduction, net_pay ) serialized_deduction = { "deduction_id": deduction.id, @@ -732,7 +733,10 @@ def calculate_based_on_taxable_gross_pay(*_args, **kwargs): taxable_gross_pay = taxable_gross_pay["taxable_gross_pay"] rate = component.rate amount = taxable_gross_pay * rate / 100 - return amount + if kwargs["taxable_deduction"]: + return amount, taxable_gross_pay + else: + return amount def calculate_based_on_net_pay(component, net_pay, day_dict): diff --git a/payroll/static/payroll/dashboard.js b/payroll/static/payroll/dashboard.js index 03cbf9851..b517d13aa 100644 --- a/payroll/static/payroll/dashboard.js +++ b/payroll/static/payroll/dashboard.js @@ -423,25 +423,6 @@ $(document).ready(function () { }); }); - $("#export").on("click", function () { - var period = $("#monthYearField").val(); - var start_date = $("#start_date").val(); - var end_date = $("#end_date").val(); - var employee = $("#select_employee").val(); - var status = $("#select_status").val(); - - let url = - "/payroll/dashboard-export/" + - "?start_date=" + - start_date + - "&end_date=" + - end_date + - "&employee=" + - employee + - "&status=" + - status; - window.location.href = url; - }); $(".filter").on("click", function () { $("#back_button").removeClass("d-none"); diff --git a/payroll/templates/payroll/dashboard.html b/payroll/templates/payroll/dashboard.html index f4e56ce5e..91544bc3a 100644 --- a/payroll/templates/payroll/dashboard.html +++ b/payroll/templates/payroll/dashboard.html @@ -77,51 +77,54 @@ @click.outside="open = false" style="display: none;" > - -
-
-
- - -
-
-
-
- - -
-
-
-
- - +
+ {% csrf_token %} +
+
+
+ + {{export_form.start_date}} +
+
+
+
+ + {{export_form.end_date}} +
+
+
+
+ + {{export_form.employees}} +
+
+
+
+ + {{export_form.status}} +
+
+
+
+ + {{export_form.contributions}} +
+
+
+ +
-
-
-
- - -
-
-
- - {% trans "Export" %} -
-
+
diff --git a/payroll/views/component_views.py b/payroll/views/component_views.py index 83a77b4c5..466904eb1 100644 --- a/payroll/views/component_views.py +++ b/payroll/views/component_views.py @@ -136,6 +136,7 @@ def payroll_calculation(employee, start_date, end_date): gross_pay = updated_gross_pay_data["compensation_amount"] gross_pay_deductions = updated_gross_pay_data["deductions"] + kwargs["gross_pay"] = gross_pay pretax_deductions = calculate_pre_tax_deduction(**kwargs) post_tax_deductions = calculate_post_tax_deduction(**kwargs) @@ -1414,7 +1415,6 @@ def get_contribution_report(request): This method is used to get the contribution report """ employee_id = request.GET["employee_id"] - deudction_id = request.GET.get("deduction_id") pay_heads = Payslip.objects.filter(employee_id__id=employee_id).values_list( "pay_head_data", flat=True ) diff --git a/payroll/views/views.py b/payroll/views/views.py index 2cc2fa7f7..700942b12 100644 --- a/payroll/views/views.py +++ b/payroll/views/views.py @@ -5,6 +5,7 @@ This module is used to define the method for the path in the urls """ from collections import defaultdict +from itertools import groupby from urllib.parse import parse_qs import pandas as pd import json @@ -22,6 +23,7 @@ from base.methods import export_data, generate_colors, get_key_instances, sortby from employee.models import Employee, EmployeeWorkInformation from base.methods import closest_numbers from base.methods import generate_pdf +from payroll.context_processors import get_active_employees from payroll.models.models import ( FilingStatus, PayrollGeneralSetting, @@ -33,6 +35,7 @@ from payroll.models.models import ( ) from payroll.forms.forms import ( ContractForm, + DashboardExport, ReimbursementrequestCommentForm, WorkRecordForm, ) @@ -573,11 +576,13 @@ def view_payroll_dashboard(request): posted = Payslip.objects.filter(status="confirmed") review_ongoing = Payslip.objects.filter(status="review_ongoing") draft = Payslip.objects.filter(status="draft") + export_form = DashboardExport() context = { "paid": paid, "posted": posted, "review_ongoing": review_ongoing, "draft": draft, + "export_form":export_form, } return render(request, "payroll/dashboard.html", context=context) @@ -794,10 +799,16 @@ def payslip_export(request): """ - start_date = request.GET.get("start_date") - end_date = request.GET.get("end_date") - employee = request.GET.get("employee") - status = request.GET.get("status") + start_date = request.POST.get("start_date") + end_date = request.POST.get("end_date") + employee = request.POST.getlist("employees") + status = request.POST.get("status") + contributions = request.POST.getlist("contributions") if request.POST.getlist("contributions") else get_active_employees(None)["get_active_employees"].values_list("id",flat=True) + print(start_date,"start_date") + print(end_date,"end_date") + print(contributions,"contributions") + print(employee,"employee") + print(status,"status") department = [] total_amount = 0 @@ -805,6 +816,7 @@ def payslip_export(request): table2_data = [] table3_data = [] table4_data = [] + table5_data = [] employee_payslip_list = Payslip.objects.all() @@ -815,72 +827,132 @@ def payslip_export(request): employee_payslip_list = employee_payslip_list.filter(end_date__lte=end_date) if employee: - employee_payslip_list = employee_payslip_list.filter(employee_id=employee) + employee_payslip_list = employee_payslip_list.filter(employee_id__in=employee) if status: employee_payslip_list = employee_payslip_list.filter(status=status) - user = request.user - emp = user.employee_get - for payslip in employee_payslip_list: - # Taking the company_name of the user - info = EmployeeWorkInformation.objects.filter(employee_id=emp) - 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 else "MMM. D, YYYY" - else: - date_format = "MMM. D, YYYY" - # Define date formats - date_formats = { - "DD-MM-YYYY": "%d-%m-%Y", - "DD.MM.YYYY": "%d.%m.%Y", - "DD/MM/YYYY": "%d/%m/%Y", - "MM/DD/YYYY": "%m/%d/%Y", - "YYYY-MM-DD": "%Y-%m-%d", - "YYYY/MM/DD": "%Y/%m/%d", - "MMMM D, YYYY": "%B %d, %Y", - "DD MMMM, YYYY": "%d %B, %Y", - "MMM. D, YYYY": "%b. %d, %Y", - "D MMM. YYYY": "%d %b. %Y", - "dddd, MMMM D, YYYY": "%A, %B %d, %Y", - } - start_date_str = str(payslip.start_date) - end_date_str = str(payslip.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() - - # Print the formatted date for each format - for format_name, format_string in date_formats.items(): - if format_name == date_format: - formatted_start_date = start_date.strftime(format_string) - - for format_name, format_string in date_formats.items(): - if format_name == date_format: - formatted_end_date = end_date.strftime(format_string) - - table1_data.append( - { - "employee": payslip.employee_id.employee_first_name - + " " - + payslip.employee_id.employee_last_name, - "start_date": formatted_start_date, - "end_date": formatted_end_date, - "basic_pay": round(payslip.basic_pay, 2), - "deduction": round(payslip.deduction, 2), - "allowance": round(payslip.gross_pay - payslip.basic_pay, 2), - "gross_pay": round(payslip.gross_pay, 2), - "net_pay": round(payslip.net_pay, 2), - "status": status_choices.get(payslip.status), - }, + emp = request.user.employee_get + + for employ in contributions: + payslips = Payslip.objects.filter(employee_id__id=employ) + if end_date: + payslips = Payslip.objects.filter(employee_id__id=employ,end_date__lte=end_date) + if start_date: + payslips = Payslip.objects.filter(employee_id__id=employ,start_date__gte=start_date) + if end_date: + payslips=payslips.filter(end_date__lte=end_date) + pay_heads = payslips.values_list( + "pay_head_data", flat=True ) + contribution_deductions = [] + deductions = [] + for head in pay_heads: + for deduction in head["gross_pay_deductions"]: + if deduction.get("deduction_id"): + deductions.append(deduction) + for deduction in head["basic_pay_deductions"]: + if deduction.get("deduction_id"): + deductions.append(deduction) + for deduction in head["pretax_deductions"]: + if deduction.get("deduction_id"): + deductions.append(deduction) + for deduction in head["post_tax_deductions"]: + if deduction.get("deduction_id"): + deductions.append(deduction) + for deduction in head["tax_deductions"]: + if deduction.get("deduction_id"): + deductions.append(deduction) + for deduction in head["net_deductions"]: + deductions.append(deduction) - if not employee_payslip_list: + deductions.sort(key=lambda x: x["deduction_id"]) + grouped_deductions = { + key: list(group) + for key, group in groupby(deductions, key=lambda x: x["deduction_id"]) + } + + for deduction_id, group in grouped_deductions.items(): + title = group[0]["title"] + employee_contribution = sum(item["amount"] for item in group) + employer_contribution = sum( + item["employer_contribution_amount"] for item in group + ) + total_contribution = employee_contribution + employer_contribution + if employer_contribution > 0: + contribution_deductions.append( + { + "deduction_id": deduction_id, + "title": title, + "employee_contribution": employee_contribution, + "employer_contribution": employer_contribution, + "total_contribution": total_contribution, + } + ) + table5_data.append( + { + "Employee" : Employee.objects.get(id=emp), + "Employer Contribution" : employer_contribution, + "Employee Contribution" : employee_contribution, + } + ) + + if employee_payslip_list: + for payslip in employee_payslip_list: + # Taking the company_name of the user + info = EmployeeWorkInformation.objects.filter(employee_id=emp).first() + + if info: + employee_company = info.company_id + company_name = Company.objects.filter(company=employee_company).first() + date_format = company_name.date_format if company_name and company_name.date_format else "MMM. D, YYYY" + else: + date_format = "MMM. D, YYYY" + + # Define date formats + date_formats = { + "DD-MM-YYYY": "%d-%m-%Y", + "DD.MM.YYYY": "%d.%m.%Y", + "DD/MM/YYYY": "%d/%m/%Y", + "MM/DD/YYYY": "%m/%d/%Y", + "YYYY-MM-DD": "%Y-%m-%d", + "YYYY/MM/DD": "%Y/%m/%d", + "MMMM D, YYYY": "%B %d, %Y", + "DD MMMM, YYYY": "%d %B, %Y", + "MMM. D, YYYY": "%b. %d, %Y", + "D MMM. YYYY": "%d %b. %Y", + "dddd, MMMM D, YYYY": "%A, %B %d, %Y", + } + start_date_str = str(payslip.start_date) + end_date_str = str(payslip.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() + + # Print the formatted date for each format + for format_name, format_string in date_formats.items(): + if format_name == date_format: + formatted_start_date = start_date.strftime(format_string) + + for format_name, format_string in date_formats.items(): + if format_name == date_format: + formatted_end_date = end_date.strftime(format_string) + + table1_data.append( + { + "employee": f"{payslip.employee_id.employee_first_name} {payslip.employee_id.employee_last_name}", + "start_date": formatted_start_date, + "end_date": formatted_end_date, + "basic_pay": round(payslip.basic_pay, 2), + "deduction": round(payslip.deduction, 2), + "allowance": round(payslip.gross_pay - payslip.basic_pay, 2), + "gross_pay": round(payslip.gross_pay, 2), + "net_pay": round(payslip.net_pay, 2), + "status": status_choices.get(payslip.status), + }, + ) + else: table1_data.append( { "employee": "None", @@ -955,6 +1027,7 @@ def payslip_export(request): df_table2 = pd.DataFrame(table2_data) df_table3 = pd.DataFrame(table3_data) df_table4 = pd.DataFrame(table4_data) + df_table5 = pd.DataFrame(table5_data) df_table1 = df_table1.rename( columns={ @@ -985,6 +1058,16 @@ def payslip_export(request): "total_amount": "Total Amount", } ) + + df_table5 = df_table5.rename( + columns={ + "contract_ending": ( + f"Employee - Employer Contributions {start_date} to {end_date}" + if start_date and end_date + else f"Contract Ending" + ), + } + ) response = HttpResponse( content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" @@ -1007,12 +1090,18 @@ def payslip_export(request): index=False, startrow=len(df_table1) + 3 + len(df_table2) + 6, ) - df_table4.to_excel( + df_table5.to_excel( writer, sheet_name="Payroll Dashboard details", index=False, startrow=len(df_table1) + 3 + len(df_table2) + len(df_table3) + 9, ) + df_table4.to_excel( + writer, + sheet_name="Payroll Dashboard details", + index=False, + startrow=len(df_table1) + 3 + len(df_table2) + len(df_table3) + len(df_table5) + 12, + ) workbook = writer.book worksheet = writer.sheets["Payroll Dashboard details"] @@ -1021,6 +1110,7 @@ def payslip_export(request): len(df_table2.columns), len(df_table3.columns), len(df_table4.columns), + len(df_table5.columns), ) heading_format = workbook.add_format( @@ -1029,7 +1119,7 @@ def payslip_export(request): "font_size": 14, "align": "center", "valign": "vcenter", - "bg_color": "#B2ED67", + "bg_color": "#eb7968", "font_size": 20, } ) @@ -1049,7 +1139,7 @@ def payslip_export(request): ) header_format = workbook.add_format( - {"bg_color": "#B2ED67", "bold": True, "text_wrap": True} + {"bg_color": "#eb7968", "bold": True, "text_wrap": True} ) for col_num, value in enumerate(df_table1.columns.values): @@ -1075,7 +1165,7 @@ def payslip_export(request): header_width = max(len(value) + 2, len(df_table3[value].astype(str).max()) + 2) worksheet.set_column(f"{col_letter}:{col_letter}", header_width) - for col_num, value in enumerate(df_table4.columns.values): + for col_num, value in enumerate(df_table5.columns.values): worksheet.write( len(df_table1) + 3 + len(df_table2) + len(df_table3) + 9, col_num, @@ -1083,6 +1173,15 @@ def payslip_export(request): header_format, ) col_letter = chr(65 + col_num) + + for col_num, value in enumerate(df_table4.columns.values): + worksheet.write( + len(df_table1) + 3 + len(df_table2) + len(df_table3) + len(df_table5) + 12, + col_num, + value, + header_format, + ) + col_letter = chr(65 + col_num) header_width = max(len(value) + 2, len(df_table4[value].astype(str).max()) + 2) worksheet.set_column(f"{col_letter}:{col_letter}", header_width)