feat: New Payroll module (#21990)
* feat: Moved Document to Payroll Module
* feat: Moved Reports to Payroll Module
* feat: Moved Print fromat With Patch
* feat: Moved Notifiction to Payroll Module and patches
* feat: added dashboard and desk page to Payroll
* feat: Payroll Dashboard
* feat: Module onboarding
* feat: Income tax Deductions Report
* feat: Ecs Checklist Report
* feat: Provident Fund Report
* feat: Professional Fund report and commonified Code
* feat: Total Payments Based On Payment Mode Report
* fix: refactor and added chart Total Payments Based On Payment Mode
* feat: Payroll Settings
* fix: Bank remittance Report
* feat(Payroll based on): Considered unmarked days
* feat: Added Help for condition an formula in Salary structure
* fix: requested changes
* fix: rename report Ecs checklist to salary_payments_via_ecs
* fix: renamed report report/total_payments_based_on_payment_mode
* fix: added role via setup.py for regional report
* feat: added All reports to desk page
* fix: frappe.reload doc in all patches
* fix: codacy
* fix: frappe.reload_doctype for patches
* patch: is_income_tax_component and component_type for salary component
* fix: uncommented code
* test: fixture
* fix: test
* test: test_payment_days_based_on_attendance
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 8593966..290694a 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -60,6 +60,19 @@
]
)).insert()
+ for report_name in ('Professional Tax Deductions', 'Provident Fund Deductions'):
+
+ if not frappe.db.get_value('Custom Role', dict(report=report_name)):
+ frappe.get_doc(dict(
+ doctype='Custom Role',
+ report=report_name,
+ roles= [
+ dict(role='HR User'),
+ dict(role='HR Manager'),
+ dict(role='Employee')
+ ]
+ )).insert()
+
def add_permissions():
for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'):
add_permission(doctype, 'All', 0)
@@ -402,10 +415,45 @@
'Purchase Receipt Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Purchase Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Material Request Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
+ 'Salary Component': [
+ dict(fieldname= 'component_type',
+ label= 'Component Type',
+ fieldtype= 'Select',
+ insert_after= 'description',
+ options= "\nProvident Fund\nAdditional Provident Fund\nProvident Fund Loan\nProfessional Tax",
+ depends_on = 'eval:doc.type == "Deduction"'
+ )
+ ],
'Employee': [
- dict(fieldname='ifsc_code', label='IFSC Code',
- fieldtype='Data', insert_after='bank_ac_no', print_hide=1,
- depends_on='eval:doc.salary_mode == "Bank"')
+ dict(fieldname='ifsc_code',
+ label='IFSC Code',
+ fieldtype='Data',
+ insert_after='bank_ac_no',
+ print_hide=1,
+ depends_on='eval:doc.salary_mode == "Bank"'
+ ),
+ dict(
+ fieldname = 'pan_number',
+ label = 'PAN Number',
+ fieldtype = 'Data',
+ insert_after = 'payroll_cost_center',
+ print_hide = 1
+ ),
+ dict(
+ fieldname = 'micr_code',
+ label = 'MICR Code',
+ fieldtype = 'Data',
+ insert_after = 'ifsc_code',
+ print_hide = 1,
+ depends_on='eval:doc.salary_mode == "Bank"'
+ ),
+ dict(
+ fieldname = 'provident_fund_account',
+ label = 'Provident Fund Account',
+ fieldtype = 'Data',
+ insert_after = 'pan_number'
+ )
+
],
'Company': [
dict(fieldname='hra_section', label='HRA Settings',
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 9fe29eb..05ffa87 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -6,7 +6,7 @@
from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount
from erpnext.controllers.accounts_controller import get_taxes_and_charges
from erpnext.hr.utils import get_salary_assignment
-from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
+from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.regional.india import number_state_mapping
from six import string_types
from erpnext.accounts.general_ledger import make_gl_entries
diff --git a/erpnext/regional/report/professional_tax_deductions/__init__.py b/erpnext/regional/report/professional_tax_deductions/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/report/professional_tax_deductions/__init__.py
diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js
new file mode 100644
index 0000000..29c7dbf
--- /dev/null
+++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() {
+ frappe.query_reports["Professional Tax Deductions"] = erpnext.salary_slip_deductions_report_filters;
+});
\ No newline at end of file
diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.json b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.json
new file mode 100644
index 0000000..9938e9d
--- /dev/null
+++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.json
@@ -0,0 +1,20 @@
+{
+ "add_total_row": 0,
+ "creation": "2020-06-02 00:37:44.537355",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-06-16 19:02:26.306348",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "Professional Tax Deductions",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Salary Slip",
+ "report_name": "Professional Tax Deductions",
+ "report_type": "Script Report",
+ "roles": []
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
new file mode 100644
index 0000000..900fe96
--- /dev/null
+++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
@@ -0,0 +1,69 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions
+
+def execute(filters=None):
+ columns = get_columns(filters)
+ data = get_data(filters)
+
+ return columns, data
+
+def get_columns(filters):
+ columns = [
+ {
+ "label": _("Employee"),
+ "options": "Employee",
+ "fieldname": "employee",
+ "fieldtype": "Link",
+ "width": 200
+ },
+ {
+ "label": _("Employee Name"),
+ "options": "Employee",
+ "fieldname": "employee_name",
+ "fieldtype": "Link",
+ "width": 160
+ },
+ {
+ "label": _("Amount"),
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "width": 140
+ }
+ ]
+
+ return columns
+
+def get_data(filters):
+
+ data = []
+
+ component_type_dict = frappe._dict(frappe.db.sql(""" select name, component_type from `tabSalary Component`
+ where component_type = 'Professional Tax' """))
+
+ conditions = get_conditions(filters)
+
+ entry = frappe.db.sql(""" select sal.employee, sal.employee_name, ded.salary_component, ded.amount
+ from `tabSalary Slip` sal, `tabSalary Detail` ded
+ where sal.name = ded.parent
+ and ded.parentfield = 'deductions'
+ and ded.parenttype = 'Salary Slip'
+ and sal.docstatus = 1 %s
+ and ded.salary_component in (%s)
+ """ % (conditions , ", ".join(['%s']*len(component_type_dict))), tuple(component_type_dict.keys()), as_dict=1)
+
+ for d in entry:
+
+ employee = {
+ "employee": d.employee,
+ "employee_name": d.employee_name,
+ "amount": d.amount
+ }
+
+ data.append(employee)
+
+ return data
\ No newline at end of file
diff --git a/erpnext/regional/report/provident_fund_deductions/__init__.py b/erpnext/regional/report/provident_fund_deductions/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/report/provident_fund_deductions/__init__.py
diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js
new file mode 100644
index 0000000..b4dc28d
--- /dev/null
+++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() {
+ frappe.query_reports["Provident Fund Deductions"] = erpnext.salary_slip_deductions_report_filters;
+});
\ No newline at end of file
diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.json b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.json
new file mode 100644
index 0000000..e25d335
--- /dev/null
+++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.json
@@ -0,0 +1,20 @@
+{
+ "add_total_row": 0,
+ "creation": "2020-06-01 23:44:07.919117",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-06-16 18:54:19.305763",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "Provident Fund Deductions",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Salary Slip",
+ "report_name": "Provident Fund Deductions",
+ "report_type": "Script Report",
+ "roles": []
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py
new file mode 100644
index 0000000..9f58957
--- /dev/null
+++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py
@@ -0,0 +1,153 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+
+def execute(filters=None):
+ columns = get_columns(filters)
+ data = get_data(filters)
+
+ return columns, data
+
+def get_columns(filters):
+ columns = [
+ {
+ "label": _("Employee"),
+ "options": "Employee",
+ "fieldname": "employee",
+ "fieldtype": "Link",
+ "width": 200
+ },
+ {
+ "label": _("Employee Name"),
+ "options": "Employee",
+ "fieldname": "employee_name",
+ "fieldtype": "Link",
+ "width": 160
+ },
+ {
+ "label": _("PF Account"),
+ "fieldname": "pf_account",
+ "fieldtype": "Data",
+ "width": 140
+ },
+ {
+ "label": _("PF Amount"),
+ "fieldname": "pf_amount",
+ "fieldtype": "Currency",
+ "width": 140
+ },
+ {
+ "label": _("Additional PF"),
+ "fieldname": "additional_pf",
+ "fieldtype": "Currency",
+ "width": 140
+ },
+ {
+ "label": _("PF Loan"),
+ "fieldname": "pf_loan",
+ "fieldtype": "Currency",
+ "width": 140
+ },
+ {
+ "label": _("Total"),
+ "fieldname": "total",
+ "fieldtype": "Currency",
+ "width": 140
+ }
+ ]
+
+ return columns
+
+def get_conditions(filters):
+ conditions = [""]
+
+ if filters.get("department"):
+ conditions.append("sal.department = '%s' " % (filters["department"]) )
+
+ if filters.get("branch"):
+ conditions.append("sal.branch = '%s' " % (filters["branch"]) )
+
+ if filters.get("company"):
+ conditions.append("sal.company = '%s' " % (filters["company"]) )
+
+ if filters.get("period"):
+ conditions.append("month(sal.start_date) = '%s' " % (filters["period"]))
+
+ if filters.get("mode_of_payment"):
+ conditions.append("sal.mode_of_payment = '%s' " % (filters["mode_of_payment"]))
+
+ return " and ".join(conditions)
+
+def prepare_data(entry,component_type_dict):
+ data_list = {}
+
+ employee_account_dict = frappe._dict(frappe.db.sql(""" select name, provident_fund_account from `tabEmployee`"""))
+
+ for d in entry:
+
+ component_type = component_type_dict.get(d.salary_component)
+
+ if data_list.get(d.name):
+ data_list[d.name][component_type] = d.amount
+ else:
+ data_list.setdefault(d.name,{
+ "employee": d.employee,
+ "employee_name": d.employee_name,
+ "pf_account": employee_account_dict.get(d.employee),
+ "component_type": d.amount
+ })
+
+ return data_list
+
+def get_data(filters):
+ data = []
+
+ conditions = get_conditions(filters)
+
+ salary_slips = frappe.db.sql(""" select sal.name from `tabSalary Slip` sal
+ where docstatus = 1 %s
+ """ % (conditions), as_dict=1)
+
+ component_type_dict = frappe._dict(frappe.db.sql(""" select name, component_type from `tabSalary Component`
+ where component_type in ('Provident Fund', 'Additional Provident Fund', 'Provident Fund Loan')"""))
+
+ entry = frappe.db.sql(""" select sal.name, sal.employee, sal.employee_name, ded.salary_component, ded.amount
+ from `tabSalary Slip` sal, `tabSalary Detail` ded
+ where sal.name = ded.parent
+ and ded.parentfield = 'deductions'
+ and ded.parenttype = 'Salary Slip'
+ and sal.docstatus = 1 %s
+ and ded.salary_component in (%s)
+ """ % (conditions, ", ".join(['%s']*len(component_type_dict))), tuple(component_type_dict.keys()), as_dict=1)
+
+ data_list = prepare_data(entry,component_type_dict)
+
+ for d in salary_slips:
+ total = 0
+ if data_list.get(d.name):
+ employee = {
+ "employee": data_list.get(d.name).get("employee"),
+ "employee_name": data_list.get(d.name).get("employee_name"),
+ "pf_account": data_list.get(d.name).get("pf_account")
+ }
+
+ if data_list.get(d.name).get("Provident Fund"):
+ employee["pf_amount"] = data_list.get(d.name).get("Provident Fund")
+ total += data_list.get(d.name).get("Provident Fund")
+
+ if data_list.get(d.name).get("Additional Provident Fund"):
+ employee["additional_pf"] = data_list.get(d.name).get("Additional Provident Fund")
+ total += data_list.get(d.name).get("Additional Provident Fund")
+
+ if data_list.get(d.name).get("Provident Fund Loan"):
+ employee["pf_loan"] = data_list.get(d.name).get("Provident Fund Loan")
+ total += data_list.get(d.name).get("Provident Fund Loan")
+
+ employee["total"] = total
+
+ data.append(employee)
+
+ return data
\ No newline at end of file