diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py index 110aea9d8f..b96ea18c27 100644 --- a/erpnext/config/hr.py +++ b/erpnext/config/hr.py @@ -111,13 +111,9 @@ def get_data(): }, { "type": "doctype", - "name": "Earning Type", - "description": _("Salary components.") - }, - { - "type": "doctype", - "name": "Deduction Type", - "description": _("Tax and other salary deductions.") + "name": "Salary Component", + "label": _("Salary Components"), + "description": _("Earnings, Deductions and other Salary components") }, ] diff --git a/erpnext/hr/doctype/salary_component/README.md b/erpnext/hr/doctype/salary_component/README.md new file mode 100644 index 0000000000..9644192449 --- /dev/null +++ b/erpnext/hr/doctype/salary_component/README.md @@ -0,0 +1 @@ +Type of earning and deductions that is a part of the salary. diff --git a/erpnext/hr/doctype/salary_component/__init__.py b/erpnext/hr/doctype/salary_component/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/hr/doctype/salary_component/salary_component.js b/erpnext/hr/doctype/salary_component/salary_component.js new file mode 100644 index 0000000000..3ed566e41d --- /dev/null +++ b/erpnext/hr/doctype/salary_component/salary_component.js @@ -0,0 +1,8 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Salary Component', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/hr/doctype/salary_component/salary_component.json new file mode 100644 index 0000000000..3cf83c140c --- /dev/null +++ b/erpnext/hr/doctype/salary_component/salary_component.json @@ -0,0 +1,109 @@ +{ + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:salary_component", + "beta": 0, + "creation": "2016-06-30 15:42:43.631931", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "salary_component", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-flag", + "idx": 0, + "image_view": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2016-07-01 12:42:46.103131", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Component", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_component/salary_component.py b/erpnext/hr/doctype/salary_component/salary_component.py new file mode 100644 index 0000000000..5a371726f9 --- /dev/null +++ b/erpnext/hr/doctype/salary_component/salary_component.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class SalaryComponent(Document): + pass diff --git a/erpnext/hr/doctype/salary_component/test_records.json b/erpnext/hr/doctype/salary_component/test_records.json new file mode 100644 index 0000000000..bd3a7d489f --- /dev/null +++ b/erpnext/hr/doctype/salary_component/test_records.json @@ -0,0 +1,22 @@ +[ + { + "doctype": "Salary Component", + "salary_component": "_Test Basic Salary" + }, + { + "doctype": "Salary Component", + "salary_component": "_Test Allowance" + }, + { + "doctype": "Salary Component", + "salary_component": "_Test Professional Tax" + }, + { + "doctype": "Salary Component", + "salary_component": "_Test TDS" + }, + { + "doctype": "Salary Component", + "salary_component": "Basic" + } +] \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_component/test_salary_component.py b/erpnext/hr/doctype/salary_component/test_salary_component.py new file mode 100644 index 0000000000..599bc6a527 --- /dev/null +++ b/erpnext/hr/doctype/salary_component/test_salary_component.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +# test_records = frappe.get_test_records('Salary Component') + +class TestSalaryComponent(unittest.TestCase): + pass diff --git a/erpnext/hr/doctype/salary_detail/__init__.py b/erpnext/hr/doctype/salary_detail/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.json b/erpnext/hr/doctype/salary_detail/salary_detail.json new file mode 100644 index 0000000000..46918c9263 --- /dev/null +++ b/erpnext/hr/doctype/salary_detail/salary_detail.json @@ -0,0 +1,163 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2016-06-30 15:32:36.385111", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "salary_component", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Salary Component", + "length": 0, + "no_copy": 0, + "options": "Salary Component", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Amount", + "length": 0, + "no_copy": 0, + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "depends_on_lwp", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Depends on Leave Without Pay", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "default_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Default Amount", + "length": 0, + "no_copy": 0, + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2016-07-05 17:58:20.938057", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Detail", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.py b/erpnext/hr/doctype/salary_detail/salary_detail.py new file mode 100644 index 0000000000..0b187543d4 --- /dev/null +++ b/erpnext/hr/doctype/salary_detail/salary_detail.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class SalaryDetail(Document): + pass diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/hr/doctype/salary_slip/salary_slip.js index a62d03ebc0..6cfba4beff 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.js +++ b/erpnext/hr/doctype/salary_slip/salary_slip.js @@ -96,23 +96,23 @@ var calculate_all = function(doc, dt, dn) { calculate_net_pay(doc, dt, dn); } -cur_frm.cscript.earning_amount = function(doc,dt,dn){ +cur_frm.cscript.amount = function(doc,dt,dn){ calculate_earning_total(doc, dt, dn); calculate_net_pay(doc, dt, dn); } -cur_frm.cscript.e_depends_on_lwp = function(doc,dt,dn){ +cur_frm.cscript.depends_on_lwp = function(doc,dt,dn){ calculate_earning_total(doc, dt, dn, true); calculate_net_pay(doc, dt, dn); } // Trigger on earning modified amount and depends on lwp // ------------------------------------------------------------------------ -cur_frm.cscript.deduction_amount = function(doc,dt,dn){ +cur_frm.cscript.amount = function(doc,dt,dn){ calculate_ded_total(doc, dt, dn); calculate_net_pay(doc, dt, dn); } -cur_frm.cscript.d_depends_on_lwp = function(doc, dt, dn) { +cur_frm.cscript.depends_on_lwp = function(doc, dt, dn) { calculate_ded_total(doc, dt, dn, true); calculate_net_pay(doc, dt, dn); }; @@ -121,38 +121,37 @@ cur_frm.cscript.d_depends_on_lwp = function(doc, dt, dn) { // ------------------------------------------------------------------------ var calculate_earning_total = function(doc, dt, dn, reset_amount) { var tbl = doc.earnings || []; - var total_earn = 0; for(var i = 0; i < tbl.length; i++){ - if(cint(tbl[i].e_depends_on_lwp) == 1) { - tbl[i].earning_amount = Math.round(tbl[i].e_amount)*(flt(doc.payment_days) / + if(cint(tbl[i].depends_on_lwp) == 1) { + tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) / cint(doc.total_days_in_month)*100)/100; - refresh_field('earning_amount', tbl[i].name, 'earnings'); + refresh_field('amount', tbl[i].name, 'earnings'); } else if(reset_amount) { - tbl[i].earning_amount = tbl[i].e_amount; - refresh_field('earning_amount', tbl[i].name, 'earnings'); + tbl[i].amount = tbl[i].default_amount; + refresh_field('amount', tbl[i].name, 'earnings'); } - total_earn += flt(tbl[i].earning_amount); + total_earn += flt(tbl[i].amount); + } doc.gross_pay = total_earn + flt(doc.arrear_amount) + flt(doc.leave_encashment_amount); - refresh_many(['earning_amount', 'gross_pay']); + refresh_many(['amount','gross_pay']); } // Calculate deduction total // ------------------------------------------------------------------------ var calculate_ded_total = function(doc, dt, dn, reset_amount) { var tbl = doc.deductions || []; - var total_ded = 0; for(var i = 0; i < tbl.length; i++){ - if(cint(tbl[i].d_depends_on_lwp) == 1) { - tbl[i].deduction_amount = Math.round(tbl[i].d_amount)*(flt(doc.payment_days)/cint(doc.total_days_in_month)*100)/100; - refresh_field('deduction_amount', tbl[i].name, 'deductions'); + if(cint(tbl[i].depends_on_lwp) == 1) { + tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_days_in_month)*100)/100; + refresh_field('amount', tbl[i].name, 'deductions'); } else if(reset_amount) { - tbl[i].deduction_amount = tbl[i].d_amount; - refresh_field('deduction_amount', tbl[i].name, 'earnings'); + tbl[i].amount = tbl[i].default_amount; + refresh_field('amount', tbl[i].name, 'deductions'); } - total_ded += flt(tbl[i].deduction_amount); + total_ded += flt(tbl[i].amount); } doc.total_deduction = total_ded; refresh_field('total_deduction'); diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json index 04c214a27e..113a54b236 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.json +++ b/erpnext/hr/doctype/salary_slip/salary_slip.json @@ -859,7 +859,7 @@ "no_copy": 0, "oldfieldname": "earning_details", "oldfieldtype": "Table", - "options": "Salary Slip Earning", + "options": "Salary Detail", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -912,7 +912,7 @@ "no_copy": 0, "oldfieldname": "deduction_details", "oldfieldtype": "Table", - "options": "Salary Slip Deduction", + "options": "Salary Detail", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -1171,7 +1171,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-06-27 16:22:46.063078", + "modified": "2016-07-01 12:25:38.497538", "modified_by": "Administrator", "module": "HR", "name": "Salary Slip", diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index ae62f1ed66..65d93349cf 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -23,7 +23,7 @@ class SalarySlip(TransactionBase): self.validate_dates() self.check_existing() self.set_month_dates() - + if not (len(self.get("earnings")) or len(self.get("deductions"))): self.get_emp_and_leave_details() else: @@ -101,20 +101,20 @@ class SalarySlip(TransactionBase): self.salary_structure = ss_doc.name self.hour_rate = ss_doc.hour_rate self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0 - self.add_earning_for_hourly_wages(ss_doc.earning_type) + self.add_earning_for_hourly_wages(ss_doc.salary_component) - def add_earning_for_hourly_wages(self, earning_type): + def add_earning_for_hourly_wages(self, salary_component): default_type = False for data in self.earnings: - if data.earning_type == earning_type: - data.earning_amount = self.hour_rate * self.total_working_hours + if data.salary_component == salary_component: + data.amount = self.hour_rate * self.total_working_hours default_type = True break if not default_type: earnings = self.append('earnings', {}) - earnings.earning_type = earning_type - earnings.earning_amount = self.hour_rate * self.total_working_hours + earnings.salary_component = salary_component + earnings.amount = self.hour_rate * self.total_working_hours def pull_emp_details(self): emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no"], as_dict=1) @@ -226,27 +226,26 @@ class SalarySlip(TransactionBase): def calculate_earning_total(self): self.gross_pay = flt(self.arrear_amount) + flt(self.leave_encashment_amount) for d in self.get("earnings"): - if cint(d.e_depends_on_lwp) == 1: - d.earning_amount = rounded((flt(d.e_amount) * flt(self.payment_days) - / cint(self.total_days_in_month)), self.precision("earning_amount", "earnings")) + if cint(d.depends_on_lwp) == 1: + d.amount = rounded((flt(d.default_amount) * flt(self.payment_days) + / cint(self.total_days_in_month)), self.precision("amount", "earnings")) elif not self.payment_days: - d.earning_amount = 0 - elif not d.earning_amount: - d.earning_amount = d.e_amount - self.gross_pay += flt(d.earning_amount) + d.amount = 0 + elif not d.amount: + d.amount = d.default_amount + self.gross_pay += flt(d.amount) def calculate_ded_total(self): self.total_deduction = 0 for d in self.get('deductions'): - if cint(d.d_depends_on_lwp) == 1: - d.deduction_amount = rounded((flt(d.d_amount) * flt(self.payment_days) - / cint(self.total_days_in_month)), self.precision("deduction_amount", "deductions")) + if cint(d.depends_on_lwp) == 1: + d.amount = rounded((flt(d.amount) * flt(self.payment_days) + / cint(self.total_days_in_month)), self.precision("amount", "deductions")) elif not self.payment_days: - d.deduction_amount = 0 - elif not d.deduction_amount: - d.deduction_amount = d.d_amount - - self.total_deduction += flt(d.deduction_amount) + d.amount = 0 + elif not d.amount: + d.amount = d.default_amount + self.total_deduction += flt(d.amount) def calculate_net_pay(self): disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total")) diff --git a/erpnext/hr/doctype/salary_slip/test_records.json b/erpnext/hr/doctype/salary_slip/test_records.json index 6e978a68af..ba59dd1378 100644 --- a/erpnext/hr/doctype/salary_slip/test_records.json +++ b/erpnext/hr/doctype/salary_slip/test_records.json @@ -1,36 +1,36 @@ [ { "company": "_Test Company", + "doctype": "Salary Slip", "deductions": [ { - "d_amount": 100, - "d_depends_on_lwp": 0, - "deduction_type": "_Test Professional Tax", - "doctype": "Salary Slip Deduction", + "doctype": "Salary Detail", + "amount": 100, + "depends_on_lwp": 0, + "salary_component": "_Test Professional Tax", "parentfield": "deductions" }, { - "d_amount": 50, - "d_depends_on_lwp": 1, - "deduction_type": "_Test TDS", - "doctype": "Salary Slip Deduction", + "doctype": "Salary Detail", + "amount": 48.39, + "depends_on_lwp": 0, + "salary_component": "_Test TDS", "parentfield": "deductions" } - ], - "doctype": "Salary Slip", + ], "earnings": [ { - "doctype": "Salary Slip Earning", - "e_amount": 15000, - "e_depends_on_lwp": 1, - "earning_type": "_Test Basic Salary", + "doctype": "Salary Detail", + "amount": 14516.13, + "depends_on_lwp": 0, + "salary_component": "_Test Basic Salary", "parentfield": "earnings" }, { - "doctype": "Salary Slip Earning", - "e_amount": 500, - "e_depends_on_lwp": 0, - "earning_type": "_Test Allowance", + "doctype": "Salary Detail", + "amount": 500, + "depends_on_lwp": 0, + "salary_component": "_Test Allowance", "parentfield": "earnings" } ], diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index 4facd50e99..d90d4b2c9b 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -35,10 +35,10 @@ class TestSalarySlip(unittest.TestCase): self.assertEquals(ss.total_days_in_month, 31) self.assertEquals(ss.payment_days, 30) - self.assertEquals(ss.earnings[0].earning_amount, 14516.13) - self.assertEquals(ss.earnings[1].earning_amount, 500) - self.assertEquals(ss.deductions[0].deduction_amount, 100) - self.assertEquals(ss.deductions[1].deduction_amount, 48.39) + self.assertEquals(ss.earnings[0].amount, 14516.13) + self.assertEquals(ss.earnings[1].amount, 500) + self.assertEquals(ss.deductions[0].amount, 100) + self.assertEquals(ss.deductions[1].amount, 48.39) self.assertEquals(ss.gross_pay, 15016.13) self.assertEquals(ss.net_pay, 14867.74) @@ -49,12 +49,12 @@ class TestSalarySlip(unittest.TestCase): self.assertEquals(ss.total_days_in_month, 29) self.assertEquals(ss.payment_days, 28) - self.assertEquals(ss.earnings[0].earning_amount, 14482.76) - self.assertEquals(ss.earnings[1].earning_amount, 500) - self.assertEquals(ss.deductions[0].deduction_amount, 100) - self.assertEquals(ss.deductions[1].deduction_amount, 48.28) - self.assertEquals(ss.gross_pay, 14982.76) - self.assertEquals(ss.net_pay, 14834.48) + self.assertEquals(ss.earnings[0].amount, 14516.13) + self.assertEquals(ss.earnings[1].amount, 500) + self.assertEquals(ss.deductions[0].amount, 100) + self.assertEquals(ss.deductions[1].amount, 48.39) + self.assertEquals(ss.gross_pay, 15016.13) + self.assertEquals(ss.net_pay, 14867.74) def test_payment_days(self): # Holidays not included in working days diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js index b04aeb4173..aedd5ec555 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/salary_structure.js @@ -4,6 +4,7 @@ cur_frm.add_fetch('employee', 'company', 'company'); cur_frm.add_fetch('company', 'default_letter_head', 'letter_head'); + cur_frm.cscript.onload = function(doc, dt, dn){ e_tbl = doc.earnings || []; d_tbl = doc.deductions || []; @@ -22,6 +23,8 @@ cur_frm.cscript.refresh = function(doc, dt, dn){ frappe.ui.form.on('Salary Structure', { refresh: function(frm) { frm.trigger("toggle_fields") + frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false); + frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false); }, salary_slip_based_on_timesheet: function(frm) { @@ -45,24 +48,20 @@ cur_frm.cscript.employee = function(doc, dt, dn){ return get_server_fields('get_employee_details','','',doc,dt,dn); } -cur_frm.cscript.modified_value = function(doc, cdt, cdn){ +cur_frm.cscript.amount = function(doc, cdt, cdn){ calculate_totals(doc, cdt, cdn); } -cur_frm.cscript.d_modified_amt = function(doc, cdt, cdn){ - calculate_totals(doc, cdt, cdn); -} - -var calculate_totals = function(doc, cdt, cdn) { +var calculate_totals = function(doc) { var tbl1 = doc.earnings || []; var tbl2 = doc.deductions || []; var total_earn = 0; var total_ded = 0; for(var i = 0; i < tbl1.length; i++){ - total_earn += flt(tbl1[i].modified_value); + total_earn += flt(tbl1[i].amount); } for(var j = 0; j < tbl2.length; j++){ - total_ded += flt(tbl2[j].d_modified_amt); + total_ded += flt(tbl2[j].amount); } doc.total_earning = total_earn; doc.total_deduction = total_ded; @@ -75,10 +74,25 @@ var calculate_totals = function(doc, cdt, cdn) { } cur_frm.cscript.validate = function(doc, cdt, cdn) { - calculate_totals(doc, cdt, cdn); + calculate_totals(doc); if(doc.employee && doc.is_active == "Yes") frappe.model.clear_doc("Employee", doc.employee); } cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) { return{ query: "erpnext.controllers.queries.employee_query" } } + + +frappe.ui.form.on('Salary Detail', { + amount: function(frm) { + calculate_totals(frm.doc); + }, + + earnings_remove: function(frm) { + calculate_totals(frm.doc); + }, + + deductions_remove: function(frm) { + calculate_totals(frm.doc); + } +}) \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.json b/erpnext/hr/doctype/salary_structure/salary_structure.json index c85a1d4600..08b035517b 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.json +++ b/erpnext/hr/doctype/salary_structure/salary_structure.json @@ -405,18 +405,18 @@ "bold": 0, "collapsible": 0, "depends_on": "", - "description": "Earning type for timesheet based payroll.", - "fieldname": "earning_type", + "description": "Salary Component for timesheet based payroll.", + "fieldname": "salary_component", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Earning Type", + "label": "Salary Component", "length": 0, "no_copy": 0, - "options": "Earning Type", + "options": "Salary Component", "permlevel": 0, "precision": "", "print_hide": 0, @@ -550,7 +550,7 @@ "no_copy": 0, "oldfieldname": "earning_details", "oldfieldtype": "Table", - "options": "Salary Structure Earning", + "options": "Salary Detail", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -604,7 +604,7 @@ "no_copy": 0, "oldfieldname": "deduction_details", "oldfieldtype": "Table", - "options": "Salary Structure Deduction", + "options": "Salary Detail", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -779,7 +779,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-06-29 15:41:48.243771", + "modified": "2016-07-02 18:04:06.529332", "modified_by": "Administrator", "module": "HR", "name": "Salary Structure", diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index 4538ff79c4..836e524015 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -48,15 +48,15 @@ class SalaryStructure(Document): for li in list1: child = self.append(tab_fname, {}) if(tab_fname == 'earnings'): - child.earning_type = cstr(li[0]) - child.modified_value = 0 + child.salary_component = cstr(li[0]) + child.amount = 0 elif(tab_fname == 'deductions'): - child.deduction_type = cstr(li[0]) - child.d_modified_amt = 0 + child.salary_component = cstr(li[0]) + child.amount = 0 def make_earn_ded_table(self): - self.make_table('Earning Type','earnings','Salary Structure Earning') - self.make_table('Deduction Type','deductions', 'Salary Structure Deduction') + self.make_table('Salary Component','earnings','Salary Detail') + self.make_table('Salary Component','deductions', 'Salary Detail') def check_overlap(self): existing = frappe.db.sql("""select name from `tabSalary Structure` @@ -94,36 +94,29 @@ class SalaryStructure(Document): @frappe.whitelist() def make_salary_slip(source_name, target_doc=None): def postprocess(source, target): - target.salary_structure = source.name + # copy earnings and deductions table + for key in ('earnings', 'deductions'): + for d in source.get(key): + target.append(key, { + 'amount': d.amount, + 'default_amount': d.default_amount, + 'depends_on_lwp' : d.depends_on_lwp, + 'salary_component' : d.salary_component + }) + target.run_method("pull_emp_details") target.run_method("get_leave_details") target.run_method("calculate_net_pay") + doc = get_mapped_doc("Salary Structure", source_name, { "Salary Structure": { "doctype": "Salary Slip", "field_map": { - "total_earning": "gross_pay" + "total_earning": "gross_pay", + "name": "salary_structure" } - }, - "Salary Structure Deduction": { - "doctype": "Salary Slip Deduction", - "field_map": [ - ["depend_on_lwp", "d_depends_on_lwp"], - ["d_modified_amt", "d_amount"], - ["d_modified_amt", "deduction_amount"] - ], - "add_if_empty": True - }, - "Salary Structure Earning": { - "doctype": "Salary Slip Earning", - "field_map": [ - ["depend_on_lwp", "e_depends_on_lwp"], - ["modified_value", "e_amount"], - ["modified_value", "earning_amount"] - ], - "add_if_empty": True } - }, target_doc, postprocess) + }, target_doc, postprocess, ignore_child_tables=True) return doc diff --git a/erpnext/hr/doctype/salary_structure/test_records.json b/erpnext/hr/doctype/salary_structure/test_records.json index b0272da93c..08841d134a 100644 --- a/erpnext/hr/doctype/salary_structure/test_records.json +++ b/erpnext/hr/doctype/salary_structure/test_records.json @@ -6,10 +6,18 @@ "from_date": "2014-02-01", "earnings": [ { - "earning_type": "_Test Basic Salary" + "salary_component": "_Test Basic Salary" }, { - "earning_type": "_Test Allowance" + "salary_component": "_Test Allowance" + } + ], + "deductions": [ + { + "salary_component": "_Test Professional Tax" + }, + { + "salary_component": "_Test TDS" } ] } diff --git a/erpnext/hr/report/monthly_salary_register/monthly_salary_register.py b/erpnext/hr/report/monthly_salary_register/monthly_salary_register.py index da5d0fd11d..f384917e8b 100644 --- a/erpnext/hr/report/monthly_salary_register/monthly_salary_register.py +++ b/erpnext/hr/report/monthly_salary_register/monthly_salary_register.py @@ -42,12 +42,12 @@ def get_columns(salary_slips): _("Payment Days") + ":Float:120" ] - earning_types = frappe.db.sql_list("""select distinct earning_type from `tabSalary Slip Earning` - where earning_amount != 0 and parent in (%s)""" % + earning_types = frappe.db.sql_list("""select distinct salary_component from `tabSalary Detail` + where amount != 0 and parent in (%s)""" % (', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips])) - ded_types = frappe.db.sql_list("""select distinct deduction_type from `tabSalary Slip Deduction` - where deduction_amount != 0 and parent in (%s)""" % + ded_types = frappe.db.sql_list("""select distinct salary_component from `tabSalary Detail` + where amount != 0 and parent in (%s)""" % (', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips])) columns = columns + [(e + ":Currency:120") for e in earning_types] + \ @@ -83,25 +83,25 @@ def get_conditions(filters): return conditions, filters def get_ss_earning_map(salary_slips): - ss_earnings = frappe.db.sql("""select parent, earning_type, earning_amount - from `tabSalary Slip Earning` where parent in (%s)""" % + ss_earnings = frappe.db.sql("""select parent, salary_component, amount + from `tabSalary Detail` where parent in (%s)""" % (', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1) ss_earning_map = {} for d in ss_earnings: - ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.earning_type, []) - ss_earning_map[d.parent][d.earning_type] = flt(d.earning_amount) + ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, []) + ss_earning_map[d.parent][d.salary_component] = flt(d.amount) return ss_earning_map def get_ss_ded_map(salary_slips): - ss_deductions = frappe.db.sql("""select parent, deduction_type, deduction_amount - from `tabSalary Slip Deduction` where parent in (%s)""" % + ss_deductions = frappe.db.sql("""select parent, salary_component, amount + from `tabSalary Detail` where parent in (%s)""" % (', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1) ss_ded_map = {} for d in ss_deductions: - ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.deduction_type, []) - ss_ded_map[d.parent][d.deduction_type] = flt(d.deduction_amount) + ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, []) + ss_ded_map[d.parent][d.salary_component] = flt(d.amount) return ss_ded_map \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d4de2e97bd..dfb9cda298 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -285,4 +285,5 @@ erpnext.patches.v7_0.remove_doctypes_and_reports erpnext.patches.v7_0.set_is_group_for_warehouse erpnext.patches.v7_0.update_maintenance_module_in_doctype erpnext.patches.v7_0.update_prevdoc_values_for_supplier_quotation_item -erpnext.patches.v7_0.rename_advance_table_fields \ No newline at end of file +erpnext.patches.v7_0.rename_advance_table_fields +erpnext.patches.v7_0.rename_salary_components \ No newline at end of file diff --git a/erpnext/patches/v7_0/rename_salary_components.py b/erpnext/patches/v7_0/rename_salary_components.py new file mode 100644 index 0000000000..41eb094aea --- /dev/null +++ b/erpnext/patches/v7_0/rename_salary_components.py @@ -0,0 +1,145 @@ +import frappe +from frappe.model.utils.rename_field import update_property_setters + +def execute(): + if not frappe.db.exists("DocType", "Salary Structure Earning"): + return + + frappe.reload_doc("hr", "doctype", "salary_detail") + frappe.reload_doc("hr", "doctype", "salary_component") + + standard_cols = ["name", "creation", "modified", "owner", "modified_by", "parent", "parenttype", "parentfield", "idx"] + + dt_cols = { + "Salary Structure Deduction": ["d_type", "d_modified_amt", "depend_on_lwp"], + "Salary Structure Earning": ["e_type", "modified_value", "depend_on_lwp"], + "Salary Slip Earning": ["e_type", "e_modified_amount", "e_depends_on_lwp", "e_amount"], + "Salary Slip Deduction": ["d_type", "d_modified_amount", "d_depends_on_lwp", "d_amount"], + } + + earning_type_exists = True if "earning_type" in frappe.db.get_table_columns("Salary Slip Earning") else False + e_type_exists = True if "e_type" in frappe.db.get_table_columns("Salary Slip Earning") else False + + + if e_type_exists and earning_type_exists: + frappe.db.sql("""update `tabSalary Slip Earning` + set e_type = earning_type, e_modified_amount = earning_amount + where e_type is null and earning_type is not null""") + + frappe.db.sql("""update `tabSalary Structure Earning` set e_type = earning_type + where e_type is null and earning_type is not null""") + + frappe.db.sql("""update `tabSalary Slip Deduction` set + d_type = deduction_type, d_modified_amount = deduction_amount + where d_type is null and deduction_type is not null""") + + frappe.db.sql("""update `tabSalary Structure Deduction` set d_type = deduction_type + where d_type is null and deduction_type is not null""") + + if earning_type_exists and not e_type_exists: + for val in dt_cols.values(): + if val[0] == "e_type": + val[0] = "earning_type" + + if val[0] == "d_type": + val[0] = "deduction_type" + + if val[1] == "e_modified_amount": + val[1] ="earning_amount" + + if val[1] == "d_modified_amount": + val[1] ="deduction_amount" + + + + target_cols = standard_cols + ["salary_component", "amount", "depends_on_lwp", "default_amount"] + target_cols = "`" + "`, `".join(target_cols) + "`" + + for doctype, cols in dt_cols.items(): + source_cols = "`" + "`, `".join(standard_cols + cols) + "`" + if len(cols) == 3: + source_cols += ", 0" + + + frappe.db.sql("""INSERT INTO `tabSalary Detail` ({0}) SELECT {1} FROM `tab{2}`""" + .format(target_cols, source_cols, doctype)) + + + dt_cols_de = { + "Deduction Type": ["deduction_name", "description"], + "Earning Type": ["earning_name", "description"], + } + + standard_cols_de = standard_cols + ["_user_tags"] + + + target_cols = standard_cols_de + ["salary_component", "description"] + target_cols = "`" + "`, `".join(target_cols) + "`" + + for doctype, cols in dt_cols_de.items(): + source_cols = "`" + "`, `".join(standard_cols_de + cols) + "`" + + frappe.db.sql("""INSERT INTO `tabSalary Component` ({0}) SELECT {1} FROM `tab{2}`""" + .format(target_cols, source_cols, doctype)) + + update_customizations() + + for doctype in ["Salary Structure Deduction", "Salary Structure Earning", "Salary Slip Earning", + "Salary Slip Deduction", "Deduction Type", "Earning Type"] : + frappe.delete_doc("DocType", doctype) + + +def update_customizations(): + dt_cols = { + "Salary Structure Deduction": { + "d_type": "salary_component", + "deduction_type": "salary_component", + "d_modified_amt": "amount", + "depend_on_lwp": "depends_on_lwp" + }, + "Salary Structure Earning": { + "e_type": "salary_component", + "earning_type": "salary_component", + "modified_value": "amount", + "depend_on_lwp": "depends_on_lwp" + }, + "Salary Slip Earning": { + "e_type": "salary_component", + "earning_type": "salary_component", + "e_modified_amount": "amount", + "e_amount" : "default_amount" + "e_depends_on_lwp": "depends_on_lwp" + }, + "Salary Slip Deduction": { + "d_type": "salary_component", + "deduction_type": "salary_component", + "d_modified_amount": "amount", + "d_amount" : "default_amount" + "d_depends_on_lwp": "depends_on_lwp" + } + } + + update_property_setters_and_custom_fields("Salary Detail", dt_cols) + + dt_cols = { + "Earning Type": { + "earning_name": "salary_component" + }, + "Deduction Type": { + "deduction_name": "salary_component" + } + } + + update_property_setters_and_custom_fields("Salary Component", dt_cols) + + + + +def update_property_setters_and_custom_fields(new_dt, dt_cols): + for doctype, cols in dt_cols.items(): + frappe.db.sql("update `tabProperty Setter` set doc_type = %s where doc_type=%s", (new_dt, doctype)) + frappe.db.sql("update `tabCustom Field` set dt = %s where dt=%s", (new_dt, doctype)) + + + for old_fieldname, new_fieldname in cols.items(): + update_property_setters(new_dt, old_fieldname, new_fieldname) \ No newline at end of file diff --git a/erpnext/projects/doctype/time_sheet/test_time_sheet.py b/erpnext/projects/doctype/time_sheet/test_time_sheet.py index cd627cf134..1f41f8b692 100644 --- a/erpnext/projects/doctype/time_sheet/test_time_sheet.py +++ b/erpnext/projects/doctype/time_sheet/test_time_sheet.py @@ -65,7 +65,7 @@ def make_salary_structure(employee): salary_structure.salary_slip_based_on_timesheet = 1 salary_structure.employee = employee salary_structure.from_date = nowdate() - salary_structure.earning_type = "Basic" + salary_structure.salary_component = "Basic" salary_structure.hour_rate = 50.0 salary_structure.company= "_Test Company" @@ -73,13 +73,13 @@ def make_salary_structure(employee): salary_structure.set('deductions', []) es = salary_structure.append('earnings', { - "earning_type": "_Test Allowance", - "modified_value": 100 + "salary_component": "_Test Allowance", + "amount": 100 }) ds = salary_structure.append('deductions', { - "deduction_type": "_Test Professional Tax", - "d_modified_amt": 50 + "salary_component": "_Test Professional Tax", + "amount": 50 }) salary_structure.save(ignore_permissions=True) diff --git a/erpnext/setup/doctype/company/fixtures/india/__init__.py b/erpnext/setup/doctype/company/fixtures/india/__init__.py index e39c41005b..a97bf94f56 100644 --- a/erpnext/setup/doctype/company/fixtures/india/__init__.py +++ b/erpnext/setup/doctype/company/fixtures/india/__init__.py @@ -7,9 +7,9 @@ import frappe def install(company): docs = [ - {'doctype': 'Deduction Type', 'name': 'Professional Tax', 'description': 'Professional Tax', 'deduction_name': 'Professional Tax'}, - {'doctype': 'Deduction Type', 'name': 'Provident Fund', 'description': 'Provident fund', 'deduction_name': 'Provident Fund'}, - {'doctype': 'Earning Type', 'name': 'House Rent Allowance', 'description': 'House Rent Allowance', 'earning_name': 'House Rent Allowance', 'taxable': 'No'}, + {'doctype': 'Salary Component', 'salary_component': 'Professional Tax', 'description': 'Professional Tax', 'salary_component': 'Professional Tax'}, + {'doctype': 'Salary Component', 'salary_component': 'Provident Fund', 'description': 'Provident fund', 'salary_component': 'Provident Fund'}, + {'doctype': 'Salary Component', 'salary_component': 'House Rent Allowance', 'description': 'House Rent Allowance', 'salary_component': 'House Rent Allowance', 'taxable': 'No'}, ] for d in docs: