diff --git a/taxes_az/taxes_az/report/employee_payroll_register/employee_payroll_register.js b/taxes_az/taxes_az/report/employee_payroll_register/employee_payroll_register.js index 8c4f843..59d91b0 100644 --- a/taxes_az/taxes_az/report/employee_payroll_register/employee_payroll_register.js +++ b/taxes_az/taxes_az/report/employee_payroll_register/employee_payroll_register.js @@ -4,20 +4,26 @@ frappe.query_reports["Employee Payroll Register"] = { filters: [ { - fieldname: "from_date", - label: __("From"), - fieldtype: "Date", - default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + fieldname: "year", + label: __("Year"), + fieldtype: "Int", + default: new Date().getFullYear(), reqd: 1, - width: "100px", + width: "80px", }, { - fieldname: "to_date", - label: __("To"), - fieldtype: "Date", - default: frappe.datetime.get_today(), + fieldname: "quarter", + label: __("Quarter"), + fieldtype: "Select", + options: [ + { value: "1", label: __("Q1 (Jan – Mar)") }, + { value: "2", label: __("Q2 (Apr – Jun)") }, + { value: "3", label: __("Q3 (Jul – Sep)") }, + { value: "4", label: __("Q4 (Oct – Dec)") }, + ], + default: String(Math.ceil((new Date().getMonth() + 1) / 3)), reqd: 1, - width: "100px", + width: "120px", }, { fieldname: "company", diff --git a/taxes_az/taxes_az/report/employee_payroll_register/employee_payroll_register.py b/taxes_az/taxes_az/report/employee_payroll_register/employee_payroll_register.py index 804daea..2202f81 100644 --- a/taxes_az/taxes_az/report/employee_payroll_register/employee_payroll_register.py +++ b/taxes_az/taxes_az/report/employee_payroll_register/employee_payroll_register.py @@ -1,3 +1,6 @@ +from calendar import monthrange +from datetime import date + import frappe from frappe import _ from frappe.utils import flt @@ -25,100 +28,116 @@ DEDUCTION_TYPES = [ "Gəlir vergisi", ] -TAX_FREE_FIELDS = [ - ("custom_vergi_indicator", "custom_vergi_amount", "Vergi tutulmayan gəlir"), - ("custom_mdss_indicator", "custom_mdss_amount", "MDSS cəlb olunmayan gəlir"), - ( - "custom_unemployment_insurance_indicator", - "custom_unemployment_insurance_amount", - "İşsizlik sığortasına cəlb olunmayan gəlir", - ), - ( - "custom_medical_insurance_indicator", - "custom_medical_insurance_amount", - "İcbari tibbi sığortaya cəlb olunmayan gəlir", - ), +TAX_FREE_AMOUNT_FIELDS = [ + ("custom_vergi_amount", "Vergi tutulmayan gəlir"), + ("custom_mdss_amount", "MDSS cəlb olunmayan gəlir"), + ("custom_unemployment_insurance_amount", "İşsizlik sığortasına cəlb olunmayan gəlir"), + ("custom_medical_insurance_amount", "İcbari tibbi sığortaya cəlb olunmayan gəlir"), ] +MONTHS = (1, 2, 3) + def execute(filters=None): if not filters: filters = {} + today = date.today() + year = int(filters.get("year") or today.year) + quarter = int(filters.get("quarter") or ((today.month - 1) // 3 + 1)) + start_month = (quarter - 1) * 3 + 1 + end_month = start_month + 2 + from_date = date(year, start_month, 1) + to_date = date(year, end_month, monthrange(year, end_month)[1]) + currency = "AZN" company_currency = erpnext.get_company_currency(filters.get("company")) filters["currency"] = currency filters["docstatus"] = "Submitted" - - salary_slips = get_salary_slips(filters, company_currency) - if not salary_slips: - return get_columns(), [] + filters["from_date"] = from_date + filters["to_date"] = to_date columns = get_columns() + salary_slips = get_salary_slips(filters, company_currency) + if not salary_slips: + return columns, [] ss_earning_map = get_salary_slip_details(salary_slips, currency, company_currency, "earnings") ss_ded_map = get_salary_slip_details(salary_slips, currency, company_currency, "deductions") doj_map = get_employee_doj_map() - data = [] + employee_rows = {} for ss in salary_slips: - row = { - "salary_slip_id": ss.name, - "employee": ss.employee, - "employee_name": ss.employee_name, - "date_of_joining": doj_map.get(ss.employee), - "branch": ss.branch, - "department": ss.department, - "designation": ss.designation, - "company": ss.company, - "start_date": ss.start_date, - "end_date": ss.end_date, - "leave_without_pay": ss.leave_without_pay, - "absent_days": ss.absent_days, - "payment_days": ss.payment_days, - "currency": currency or company_currency, - } + emp = ss.employee + row = employee_rows.get(emp) + if row is None: + row = build_empty_row(ss, doj_map, currency, company_currency) + employee_rows[emp] = row + + month_index = ss.start_date.month - start_month + 1 + if month_index not in MONTHS: + continue + prefix = f"ay_{month_index}_" + + for field in ("leave_without_pay", "absent_days", "payment_days"): + row[prefix + field] += flt(ss.get(field)) for e in EARNING_TYPES: - row[frappe.scrub(e)] = flt(ss_earning_map.get(ss.name, {}).get(e, 0)) - - if currency == company_currency: - row["gross_pay"] = flt(ss.gross_pay) * flt(ss.exchange_rate) - else: - row["gross_pay"] = flt(ss.gross_pay) + row[prefix + frappe.scrub(e)] += flt(ss_earning_map.get(ss.name, {}).get(e, 0)) for d in DEDUCTION_TYPES: - row[frappe.scrub(d)] = flt(ss_ded_map.get(ss.name, {}).get(d, 0)) + row[prefix + frappe.scrub(d)] += flt(ss_ded_map.get(ss.name, {}).get(d, 0)) if currency == company_currency: - row["total_deduction"] = flt(ss.total_deduction) * flt(ss.exchange_rate) - row["net_pay"] = flt(ss.net_pay) * flt(ss.exchange_rate) + rate = flt(ss.exchange_rate) + row[prefix + "gross_pay"] += flt(ss.gross_pay) * rate + row[prefix + "total_deduction"] += flt(ss.total_deduction) * rate + row[prefix + "net_pay"] += flt(ss.net_pay) * rate else: - row["total_deduction"] = flt(ss.total_deduction) - row["net_pay"] = flt(ss.net_pay) + row[prefix + "gross_pay"] += flt(ss.gross_pay) + row[prefix + "total_deduction"] += flt(ss.total_deduction) + row[prefix + "net_pay"] += flt(ss.net_pay) - for indicator_field, amount_field, _label in TAX_FREE_FIELDS: - row[indicator_field] = ss.get(indicator_field) + for amount_field, _label in TAX_FREE_AMOUNT_FIELDS: amount = flt(ss.get(amount_field)) if currency == company_currency: amount *= flt(ss.exchange_rate) - row[amount_field] = amount + row[prefix + amount_field] += amount - data.append(row) + return columns, list(employee_rows.values()) - return columns, data + +def build_empty_row(ss, doj_map, currency, company_currency): + row = { + "employee": ss.employee, + "employee_name": ss.employee_name, + "date_of_joining": doj_map.get(ss.employee), + "branch": ss.branch, + "department": ss.department, + "designation": ss.designation, + "company": ss.company, + "currency": currency or company_currency, + } + for i in MONTHS: + prefix = f"ay_{i}_" + row[prefix + "leave_without_pay"] = 0.0 + row[prefix + "absent_days"] = 0.0 + row[prefix + "payment_days"] = 0.0 + row[prefix + "gross_pay"] = 0.0 + row[prefix + "total_deduction"] = 0.0 + row[prefix + "net_pay"] = 0.0 + for e in EARNING_TYPES: + row[prefix + frappe.scrub(e)] = 0.0 + for d in DEDUCTION_TYPES: + row[prefix + frappe.scrub(d)] = 0.0 + for amount_field, _lbl in TAX_FREE_AMOUNT_FIELDS: + row[prefix + amount_field] = 0.0 + return row def get_columns(): columns = [ - { - "label": _("Salary Slip ID"), - "fieldname": "salary_slip_id", - "fieldtype": "Link", - "options": "Salary Slip", - "width": 150, - }, { "label": _("Employee"), "fieldname": "employee", @@ -136,7 +155,7 @@ def get_columns(): "label": _("Date of Joining"), "fieldname": "date_of_joining", "fieldtype": "Date", - "width": 80, + "width": 100, }, { "label": _("Branch"), @@ -167,114 +186,96 @@ def get_columns(): "width": 120, }, { - "label": _("Start Date"), - "fieldname": "start_date", + "label": _("Currency"), "fieldtype": "Data", - "width": 80, - }, - { - "label": _("End Date"), - "fieldname": "end_date", - "fieldtype": "Data", - "width": 80, - }, - { - "label": _("Leave Without Pay"), - "fieldname": "leave_without_pay", - "fieldtype": "Float", - "width": 50, - }, - { - "label": _("Absent Days"), - "fieldname": "absent_days", - "fieldtype": "Float", - "width": 50, - }, - { - "label": _("Payment Days"), - "fieldname": "payment_days", - "fieldtype": "Float", - "width": 120, + "fieldname": "currency", + "options": "Currency", + "hidden": 1, }, ] - for earning in EARNING_TYPES: + for i in MONTHS: + prefix = f"ay_{i}_" + suffix = f" ({i}ci ay)" + columns.extend( + [ + { + "label": _("Leave Without Pay") + suffix, + "fieldname": prefix + "leave_without_pay", + "fieldtype": "Float", + "width": 120, + }, + { + "label": _("Absent Days") + suffix, + "fieldname": prefix + "absent_days", + "fieldtype": "Float", + "width": 120, + }, + { + "label": _("Payment Days") + suffix, + "fieldname": prefix + "payment_days", + "fieldtype": "Float", + "width": 120, + }, + ] + ) + for earning in EARNING_TYPES: + columns.append( + { + "label": earning + suffix, + "fieldname": prefix + frappe.scrub(earning), + "fieldtype": "Currency", + "options": "currency", + "width": 140, + } + ) columns.append( { - "label": earning, - "fieldname": frappe.scrub(earning), + "label": _("Gross Pay") + suffix, + "fieldname": prefix + "gross_pay", "fieldtype": "Currency", "options": "currency", - "width": 120, + "width": 140, } ) - - columns.append( - { - "label": _("Gross Pay"), - "fieldname": "gross_pay", - "fieldtype": "Currency", - "options": "currency", - "width": 120, - } - ) - - for deduction in DEDUCTION_TYPES: - columns.append( - { - "label": deduction, - "fieldname": frappe.scrub(deduction), - "fieldtype": "Currency", - "options": "currency", - "width": 120, - } - ) - - columns.extend( - [ - { - "label": _("Total Deduction"), - "fieldname": "total_deduction", - "fieldtype": "Currency", - "options": "currency", - "width": 120, - }, - { - "label": _("Net Pay"), - "fieldname": "net_pay", - "fieldtype": "Currency", - "options": "currency", - "width": 120, - }, - { - "label": _("Currency"), - "fieldtype": "Data", - "fieldname": "currency", - "options": "Currency", - "hidden": 1, - }, - ] - ) - - for indicator_field, amount_field, label in TAX_FREE_FIELDS: - columns.append( - { - "label": _(label) + " - Göstərici", - "fieldname": indicator_field, - "fieldtype": "Link", - "options": "Tax Free Indicator", - "width": 200, - } - ) - columns.append( - { - "label": _(label), - "fieldname": amount_field, - "fieldtype": "Currency", - "options": "currency", - "width": 160, - } + for deduction in DEDUCTION_TYPES: + columns.append( + { + "label": deduction + suffix, + "fieldname": prefix + frappe.scrub(deduction), + "fieldtype": "Currency", + "options": "currency", + "width": 140, + } + ) + columns.extend( + [ + { + "label": _("Total Deduction") + suffix, + "fieldname": prefix + "total_deduction", + "fieldtype": "Currency", + "options": "currency", + "width": 140, + }, + { + "label": _("Net Pay") + suffix, + "fieldname": prefix + "net_pay", + "fieldtype": "Currency", + "options": "currency", + "width": 140, + }, + ] ) + for amount_field, label in TAX_FREE_AMOUNT_FIELDS: + columns.append( + { + "label": _(label) + suffix, + "fieldname": prefix + amount_field, + "fieldtype": "Currency", + "options": "currency", + "width": 180, + } + ) return columns