From 4d93d339f79f1edb30a873d280a4a8cb76b3ddf0 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Fri, 11 May 2018 14:08:40 +0530 Subject: [PATCH 01/21] Employee benefit - filters fro earning component --- .../employee_benefit_application.js | 13 +++++++++++-- .../employee_benefit_claim.js | 12 ++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js index 8cf6909a125..4a04b5afb37 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js @@ -2,7 +2,16 @@ // For license information, please see license.txt frappe.ui.form.on('Employee Benefit Application', { - refresh: function(frm) { - + setup: function(frm) { + frm.set_query("earning_component", "employee_benefits", function() { + return { + filters: { + type: "Earning", + is_flexible_benefit: true, + disabled: false, + is_pro_rata_applicable: true + } + } + }) } }); diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js index 816b1bd90af..99d7809d1a1 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js +++ b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js @@ -2,7 +2,15 @@ // For license information, please see license.txt frappe.ui.form.on('Employee Benefit Claim', { - refresh: function(frm) { - + setup: function(frm) { + frm.set_query("earning_component", function() { + return { + filters: { + type: "Earning", + is_flexible_benefit: true, + disabled: false + } + } + }) } }); From 9cd292b187e3d1310d018e7841556a429edae51f Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Fri, 11 May 2018 15:14:43 +0530 Subject: [PATCH 02/21] Employee Benefit Application - filter updated --- .../employee_benefit_application.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js index 4a04b5afb37..9af2b5b23ea 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js @@ -8,8 +8,7 @@ frappe.ui.form.on('Employee Benefit Application', { filters: { type: "Earning", is_flexible_benefit: true, - disabled: false, - is_pro_rata_applicable: true + disabled: false } } }) From 41616de9cc90a224cb4d429231d92afa3c4081a6 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Fri, 11 May 2018 15:15:01 +0530 Subject: [PATCH 03/21] Employee Benefit Application - validate duplicate --- .../employee_benefit_application.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py index 9282d984755..f409a135fca 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py @@ -4,7 +4,21 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document class EmployeeBenefitApplication(Document): - pass + def before_submit(self): + self.validate_duplicate_on_payroll_period() + + def validate_duplicate_on_payroll_period(self): + application = frappe.db.exists( + "Employee Benefit Application", + { + 'employee': self.employee, + 'payroll_period': self.payroll_period, + 'docstatus': 1 + } + ) + if application: + frappe.throw(_("Employee {0} already submited an apllication {1} for the payroll period {2}").format(self.employee, application, self.payroll_period)) From 779f8ebbb3beea77cfbffe8c77824db43f6a7e0b Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 14 May 2018 12:05:11 +0530 Subject: [PATCH 04/21] Employee Benefit Application - get max benefit --- .../employee_benefit_application.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py index f409a135fca..061eb12f72d 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils import nowdate from frappe.model.document import Document class EmployeeBenefitApplication(Document): @@ -22,3 +23,26 @@ class EmployeeBenefitApplication(Document): ) if application: frappe.throw(_("Employee {0} already submited an apllication {1} for the payroll period {2}").format(self.employee, application, self.payroll_period)) + + def get_max_benefits(self): + sal_struct = get_assigned_salary_sturecture(self.employee, self.date) + if sal_struct: + return frappe.db.get_value("Salary Structure", sal_struct[0][0], "max_benefits") + + +@frappe.whitelist() +def get_assigned_salary_sturecture(employee, _date): + if not _date: + _date = nowdate() + salary_structure = frappe.db.sql(""" + select salary_structure from `tabSalary Structure Assignment` + where employee=%(employee)s + and docstatus = 1 + and ( + (%(_date)s between from_date and ifnull(to_date, '2199-12-31')) + )""", { + 'employee': employee, + '_date': _date, + }) + if salary_structure: + return salary_structure From d48998bae13ce5c2ba7e866ba5b243fc3b27ddb1 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Mon, 14 May 2018 12:05:47 +0530 Subject: [PATCH 05/21] Employee Benefit Application - Calculate benefit amounts --- .../employee_benefit_application.js | 74 ++++++++ .../employee_benefit_application.json | 158 +++++++++++++++++- 2 files changed, 231 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js index 9af2b5b23ea..b5fc99e5977 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js @@ -12,5 +12,79 @@ frappe.ui.form.on('Employee Benefit Application', { } } }) + }, + employee: function(frm) { + frappe.call({ + doc: frm.doc, + method: "get_max_benefits", + callback: function (data) { + if(!data.exc){ + if(data.message){ + frm.set_value("max_benefits", data.message) + } + } + } + }); } }); + +frappe.ui.form.on("Employee Benefit Application Detail",{ + amount: function(frm, cdt, cdn) { + calculate_all(frm.doc, cdt, cdn) + var child = locals[cdt][cdn]; + if(child.amount && child.earning_component){ + frappe.call({ + "method": "frappe.client.get", + args: { + doctype: "Salary Component", + name: child.earning_component, + }, + callback: function (data) { + if(data.message){ + validate_max_benefit_for_component(frm.doc, data.message) + } + } + }); + } + }, +}) + +var calculate_all = function(doc, dt, dn) { + var tbl = doc.employee_benefits || []; + var pro_rata_dispensed_amount = 0; + var total_amount = 0; + for(var i = 0; i < tbl.length; i++){ + if(cint(tbl[i].amount) > 0) { + total_amount += flt(tbl[i].amount); + } + var amount = tbl[i].amount; + frappe.call({ + method: "frappe.client.get_value", + args: { + doctype: "Salary Component", + fieldname: "is_pro_rata_applicable", + filters:{ + name: tbl[i].earning_component + } + }, + callback: function (data) { + if(data.message){ + if(data.message.is_pro_rata_applicable == 1){ + console.log("Any time here"); + pro_rata_dispensed_amount += amount + console.log(amount); + console.log(pro_rata_dispensed_amount); + } + } + } + }); + } + doc.total_amount = total_amount; + doc.remainig_benefits = doc.max_benefits - total_amount + doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount + refresh_many(['pro_rata_dispensed_amount', 'total_amount','remainig_benefits']); +} + +var validate_max_benefit_for_component = function(doc, salary_component) { + // TODO: Validate Max Benefit +} diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json index 0fff4fdab23..3c1c8aaee21 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json @@ -77,6 +77,68 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "max_benefits", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Max Benefits (Yearly)", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "remainig_benefits", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Remainig Benefits (Yearly)", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -107,6 +169,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Today", + "fieldname": "date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -139,6 +233,68 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "pro_rata_dispensed_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Pro Rata Dispensed Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "total_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Total Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -243,7 +399,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-04-14 15:35:20.748301", + "modified": "2018-05-11 16:58:31.662866", "modified_by": "Administrator", "module": "HR", "name": "Employee Benefit Application", From 9cb2fc27665c0bdb1d813ade710e7b8b70ee0387 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Tue, 15 May 2018 12:35:34 +0530 Subject: [PATCH 06/21] Employee Benefit Application - calcluate benefit amount --- .../employee_benefit_application.js | 49 +++---------------- .../employee_benefit_application_detail.json | 34 ++++++++++++- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js index b5fc99e5977..45c67e66399 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js @@ -30,22 +30,7 @@ frappe.ui.form.on('Employee Benefit Application', { frappe.ui.form.on("Employee Benefit Application Detail",{ amount: function(frm, cdt, cdn) { - calculate_all(frm.doc, cdt, cdn) - var child = locals[cdt][cdn]; - if(child.amount && child.earning_component){ - frappe.call({ - "method": "frappe.client.get", - args: { - doctype: "Salary Component", - name: child.earning_component, - }, - callback: function (data) { - if(data.message){ - validate_max_benefit_for_component(frm.doc, data.message) - } - } - }); - } + calculate_all(frm.doc, cdt, cdn); }, }) @@ -57,34 +42,12 @@ var calculate_all = function(doc, dt, dn) { if(cint(tbl[i].amount) > 0) { total_amount += flt(tbl[i].amount); } - var amount = tbl[i].amount; - frappe.call({ - method: "frappe.client.get_value", - args: { - doctype: "Salary Component", - fieldname: "is_pro_rata_applicable", - filters:{ - name: tbl[i].earning_component - } - }, - callback: function (data) { - if(data.message){ - if(data.message.is_pro_rata_applicable == 1){ - console.log("Any time here"); - pro_rata_dispensed_amount += amount - console.log(amount); - console.log(pro_rata_dispensed_amount); - } - } - } - }); + if(tbl[i].is_pro_rata_applicable == 1){ + pro_rata_dispensed_amount += flt(tbl[i].amount) + } } doc.total_amount = total_amount; - doc.remainig_benefits = doc.max_benefits - total_amount - doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount + doc.remainig_benefits = doc.max_benefits - total_amount; + doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount; refresh_many(['pro_rata_dispensed_amount', 'total_amount','remainig_benefits']); } - -var validate_max_benefit_for_component = function(doc, salary_component) { - // TODO: Validate Max Benefit -} diff --git a/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json index 35918e17228..37f32429e04 100644 --- a/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json +++ b/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json @@ -45,6 +45,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_pro_rata_applicable", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Is Pro-rata Applicable", + "length": 0, + "no_copy": 0, + "options": "earning_component.is_pro_rata_applicable", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -87,7 +119,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-04-13 19:32:35.064272", + "modified": "2018-05-15 12:03:25.545041", "modified_by": "Administrator", "module": "HR", "name": "Employee Benefit Application Detail", From 2d6901d5dc75bd70e1245c32696be8615dabe95b Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Tue, 15 May 2018 12:36:12 +0530 Subject: [PATCH 07/21] Employee Benefit Application - validate max benefit amount for component --- .../employee_benefit_application.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py index 061eb12f72d..edc72882591 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py @@ -11,6 +11,21 @@ from frappe.model.document import Document class EmployeeBenefitApplication(Document): def before_submit(self): self.validate_duplicate_on_payroll_period() + self.validate_max_benefit_for_component() + + def validate_max_benefit_for_component(self): + if self.employee_benefits: + for employee_benefit in self.employee_benefits: + self.validate_max_benefit(employee_benefit.earning_component) + + def validate_max_benefit(self, earning_component_name): + max_benefit_amount = frappe.db.get_value("Salary Component", earning_component_name, "max_benefit_amount") + benefit_amount = 0 + for employee_benefit in self.employee_benefits: + if employee_benefit.earning_component == earning_component_name: + benefit_amount += employee_benefit.amount + if benefit_amount > max_benefit_amount: + frappe.throw(_("Maximum benefit amount of component {0} exceeds {1}").format(earning_component_name, max_benefit_amount)) def validate_duplicate_on_payroll_period(self): application = frappe.db.exists( From d66fb842c1de5ef8071d77656d263146877049c6 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Tue, 15 May 2018 13:01:49 +0530 Subject: [PATCH 08/21] Employee Benefit Application - validate max benefit for employee --- .../employee_benefit_application.js | 2 +- .../employee_benefit_application.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js index 45c67e66399..65619b37776 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js @@ -20,7 +20,7 @@ frappe.ui.form.on('Employee Benefit Application', { callback: function (data) { if(!data.exc){ if(data.message){ - frm.set_value("max_benefits", data.message) + frm.set_value("max_benefits", data.message); } } } diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py index edc72882591..33ad75846fa 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py @@ -9,6 +9,10 @@ from frappe.utils import nowdate from frappe.model.document import Document class EmployeeBenefitApplication(Document): + def validate(self): + if self.max_benefits <= 0: + frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee)) + def before_submit(self): self.validate_duplicate_on_payroll_period() self.validate_max_benefit_for_component() @@ -42,7 +46,13 @@ class EmployeeBenefitApplication(Document): def get_max_benefits(self): sal_struct = get_assigned_salary_sturecture(self.employee, self.date) if sal_struct: - return frappe.db.get_value("Salary Structure", sal_struct[0][0], "max_benefits") + max_benefits = frappe.db.get_value("Salary Structure", sal_struct[0][0], "max_benefits") + if max_benefits > 0: + return max_benefits + else: + frappe.throw(_("Employee {0} has no max benefits in salary structure {1}").format(self.employee, sal_struct[0][0])) + else: + frappe.throw(_("Employee {0} has no salary structure assigned").format(self.employee)) @frappe.whitelist() From d63c39a5144dec4dfb170bde21b3b94064c338e6 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Tue, 15 May 2018 17:08:10 +0530 Subject: [PATCH 09/21] Payroll Period - get payroll period days --- .../doctype/payroll_period/payroll_period.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.py b/erpnext/hr/doctype/payroll_period/payroll_period.py index 1e18388e16c..c95c7bdbaf0 100644 --- a/erpnext/hr/doctype/payroll_period/payroll_period.py +++ b/erpnext/hr/doctype/payroll_period/payroll_period.py @@ -4,7 +4,27 @@ from __future__ import unicode_literals import frappe +from frappe.utils import date_diff, getdate from frappe.model.document import Document class PayrollPeriod(Document): pass + +def get_payroll_period_days(start_date, end_date, company): + payroll_period_dates = frappe.db.sql(""" + select ppd.start_date, ppd.end_date from `tabPayroll Period Date` ppd, `tabPayroll Period` pp + where pp.company=%(company)s + and ppd.parent = pp.name + and ( + (%(start_date)s between ppd.start_date and ppd.end_date) + or (%(end_date)s between ppd.start_date and ppd.end_date) + or (ppd.start_date between %(start_date)s and %(end_date)s) + )""", { + 'company': company, + 'start_date': start_date, + 'end_date': end_date + }) + + if len(payroll_period_dates) > 0: + print payroll_period_dates[0][0], payroll_period_dates[0][1] + return date_diff(getdate(payroll_period_dates[0][1]), getdate(payroll_period_dates[0][0])) + 1 From 15613e46ad0264b46e6c098baf5c3e2dcb2bd7a5 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Tue, 15 May 2018 18:48:44 +0530 Subject: [PATCH 10/21] Salary Copmonent - Defualt flexible componnent --- .../employee_benefit_application.py | 50 ++++++- .../salary_component/salary_component.json | 133 +++++++++--------- .../salary_component/salary_component.py | 17 ++- erpnext/hr/doctype/salary_slip/salary_slip.py | 7 + 4 files changed, 138 insertions(+), 69 deletions(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py index 33ad75846fa..c9c2685a90a 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py @@ -5,8 +5,9 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import nowdate +from frappe.utils import nowdate, date_diff, getdate from frappe.model.document import Document +from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days class EmployeeBenefitApplication(Document): def validate(self): @@ -71,3 +72,50 @@ def get_assigned_salary_sturecture(employee, _date): }) if salary_structure: return salary_structure + +def get_employee_benefit_application(salary_slip): + employee_benefits = frappe.db.sql(""" + select name from `tabEmployee Benefit Application` + where employee=%(employee)s + and docstatus = 1 + and (date between %(start_date)s and %(end_date)s) + """, { + 'employee': salary_slip.employee, + 'start_date': salary_slip.start_date, + 'end_date': salary_slip.end_date + }) + + if employee_benefits: + for employee_benefit in employee_benefits: + employee_benefit_obj = frappe.get_doc("Employee Benefit Application", employee_benefit[0]) + return get_components(employee_benefit_obj, salary_slip) + +def get_components(employee_benefit_application, salary_slip): + salary_components_array = [] + payroll_period_days = get_payroll_period_days(salary_slip.start_date, salary_slip.end_date, salary_slip.company) + for employee_benefit in employee_benefit_application.employee_benefits: + if employee_benefit.is_pro_rata_applicable == 1: + struct_row = {} + salary_components_dict = {} + salary_component = frappe.get_doc("Salary Component", employee_benefit.earning_component) + amount = get_amount(payroll_period_days, salary_slip.start_date, salary_slip.end_date, employee_benefit.amount) + struct_row['depends_on_lwp'] = salary_component.depends_on_lwp + struct_row['salary_component'] = salary_component.name + struct_row['abbr'] = salary_component.salary_component_abbr + struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total + salary_components_dict['amount'] = amount + salary_components_dict['struct_row'] = struct_row + salary_components_array.append(salary_components_dict) + + if len(salary_components_array) > 0: + return salary_components_array + return False + +def get_amount(payroll_period_days, start_date, end_date, amount): + salary_slip_days = date_diff(getdate(end_date), getdate(start_date)) + 1 + amount_per_day = amount / payroll_period_days + total_amount = amount_per_day * salary_slip_days + if total_amount > amount: + return amount + else: + return total_amount diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/hr/doctype/salary_component/salary_component.json index cf85af4a769..6764e0ca922 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.json +++ b/erpnext/hr/doctype/salary_component/salary_component.json @@ -108,70 +108,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.type==\"Earning\"", - "fieldname": "is_tax_applicable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Tax Applicable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fieldname": "is_payable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Payable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -366,6 +302,38 @@ "collapsible": 0, "columns": 0, "depends_on": "is_flexible_benefit", + "fieldname": "is_group", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Is Group", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.is_flexible_benefit && !doc.is_group && !doc.flexi_default", "fieldname": "earning_component_group", "fieldtype": "Link", "hidden": 0, @@ -454,6 +422,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.is_flexible_benefit && doc.is_pro_rata_applicable", + "fieldname": "flexi_default", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Default Flexible Component", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -618,7 +618,6 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "is_payable", "fieldname": "section_break_5", "fieldtype": "Section Break", "hidden": 0, @@ -1002,7 +1001,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-16 12:15:43.117948", + "modified": "2018-05-16 12:27:03.005070", "modified_by": "Administrator", "module": "HR", "name": "Salary Component", @@ -1037,4 +1036,4 @@ "sort_order": "DESC", "track_changes": 0, "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 index 9108f31f9b7..be0bf17a095 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.py +++ b/erpnext/hr/doctype/salary_component/salary_component.py @@ -4,12 +4,27 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document from frappe.model.naming import append_number_if_name_exists class SalaryComponent(Document): def validate(self): self.validate_abbr() + self.validate_flexi_defualt() + + def validate_flexi_defualt(self): + if self.is_flexible_benefit and self.is_pro_rata_applicable and self.flexi_defualt: + salary_component = frappe.db.exists( + 'Salary Component', + { + 'is_flexible_benefit': 1, + 'is_pro_rata_applicable': 1, + 'flexi_defualt': 1 + } + ) + if salary_component and salary_component != self.name: + frappe.throw(_("{0} is already marked as default flexible component").format(salary_component)) def validate_abbr(self): if not self.salary_component_abbr: @@ -18,4 +33,4 @@ class SalaryComponent(Document): self.salary_component_abbr = self.salary_component_abbr.strip() self.salary_component_abbr = append_number_if_name_exists('Salary Component', self.salary_component_abbr, - 'salary_component_abbr', separator='_', filters={"name": ["!=", self.name]}) \ No newline at end of file + 'salary_component_abbr', separator='_', filters={"name": ["!=", self.name]}) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 984a78c75ff..b7d26f2098a 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -13,6 +13,7 @@ from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.utilities.transaction_base import TransactionBase from frappe.utils.background_jobs import enqueue from erpnext.hr.doctype.additional_salary_component.additional_salary_component import get_additional_salary_component +from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_employee_benefit_application class SalarySlip(TransactionBase): def autoname(self): @@ -65,6 +66,12 @@ class SalarySlip(TransactionBase): additional_component = frappe._dict(additional_component) self.update_component_row(frappe._dict(additional_component.struct_row), additional_component.amount, "earnings") + employee_benefits = get_employee_benefit_application(self) + if employee_benefits: + for employee_benefit in employee_benefits: + benefit_component = frappe._dict(employee_benefit) + self.update_component_row(frappe._dict(benefit_component.struct_row), benefit_component.amount, "earnings") + def update_component_row(self, struct_row, amount, key): component_row = None for d in self.get(key): From 0bc665c0a557b6e8b30fe5577215e73b1c3f68bd Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Tue, 15 May 2018 19:03:17 +0530 Subject: [PATCH 11/21] Salary Copmonent - Default flexible componnent --- erpnext/hr/doctype/salary_component/salary_component.json | 2 +- erpnext/hr/doctype/salary_component/salary_component.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/hr/doctype/salary_component/salary_component.json index 6764e0ca922..3a9402f0807 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.json +++ b/erpnext/hr/doctype/salary_component/salary_component.json @@ -1001,7 +1001,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-16 12:27:03.005070", + "modified": "2018-05-15 18:53:09.115910", "modified_by": "Administrator", "module": "HR", "name": "Salary Component", diff --git a/erpnext/hr/doctype/salary_component/salary_component.py b/erpnext/hr/doctype/salary_component/salary_component.py index be0bf17a095..7a9e52efe25 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.py +++ b/erpnext/hr/doctype/salary_component/salary_component.py @@ -11,16 +11,16 @@ from frappe.model.naming import append_number_if_name_exists class SalaryComponent(Document): def validate(self): self.validate_abbr() - self.validate_flexi_defualt() + self.validate_flexi_default() def validate_flexi_defualt(self): - if self.is_flexible_benefit and self.is_pro_rata_applicable and self.flexi_defualt: + if self.is_flexible_benefit and self.is_pro_rata_applicable and self.flexi_default: salary_component = frappe.db.exists( 'Salary Component', { 'is_flexible_benefit': 1, 'is_pro_rata_applicable': 1, - 'flexi_defualt': 1 + 'flexi_default': 1 } ) if salary_component and salary_component != self.name: From 43740a6511733ca8e7901e49c66569c098ec7527 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Tue, 15 May 2018 19:04:52 +0530 Subject: [PATCH 12/21] Employee Benefit Application - employee benefit in salary slip --- erpnext/hr/doctype/salary_slip/salary_slip.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index b7d26f2098a..043b6934bed 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -13,7 +13,8 @@ from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.utilities.transaction_base import TransactionBase from frappe.utils.background_jobs import enqueue from erpnext.hr.doctype.additional_salary_component.additional_salary_component import get_additional_salary_component -from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_employee_benefit_application +from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_employee_benefit_application, get_amount +from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days class SalarySlip(TransactionBase): def autoname(self): @@ -71,6 +72,27 @@ class SalarySlip(TransactionBase): for employee_benefit in employee_benefits: benefit_component = frappe._dict(employee_benefit) self.update_component_row(frappe._dict(benefit_component.struct_row), benefit_component.amount, "earnings") + else: + max_benefits = self._salary_structure_doc.get("max_benefits") + if max_benefits > 0: + default_flexi_compenent = frappe.db.exists( + 'Salary Component', + { + 'is_flexible_benefit': 1, + 'is_pro_rata_applicable': 1, + 'flexi_default': 1 + } + ) + if default_flexi_compenent: + salary_component = frappe.get_doc("Salary Component", default_flexi_compenent) + flexi_struct_row = {} + flexi_struct_row['depends_on_lwp'] = salary_component.depends_on_lwp + flexi_struct_row['salary_component'] = salary_component.name + flexi_struct_row['abbr'] = salary_component.salary_component_abbr + flexi_struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total + payroll_period_days = get_payroll_period_days(self.start_date, self.end_date, self.company) + amount = get_amount(payroll_period_days, self.start_date, self.end_date, max_benefits) + self.update_component_row(flexi_struct_row, amount, "earnings") def update_component_row(self, struct_row, amount, key): component_row = None From 5f6e2745055ca382e49af29df71db40e777c87e5 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 16 May 2018 11:53:49 +0530 Subject: [PATCH 13/21] Salary slip - default flexi component --- .../doctype/salary_component/salary_component.py | 2 +- erpnext/hr/doctype/salary_slip/salary_slip.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/salary_component/salary_component.py b/erpnext/hr/doctype/salary_component/salary_component.py index 7a9e52efe25..9a43069619b 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.py +++ b/erpnext/hr/doctype/salary_component/salary_component.py @@ -13,7 +13,7 @@ class SalaryComponent(Document): self.validate_abbr() self.validate_flexi_default() - def validate_flexi_defualt(self): + def validate_flexi_default(self): if self.is_flexible_benefit and self.is_pro_rata_applicable and self.flexi_default: salary_component = frappe.db.exists( 'Salary Component', diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 043b6934bed..85afc906d9f 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -84,16 +84,20 @@ class SalarySlip(TransactionBase): } ) if default_flexi_compenent: - salary_component = frappe.get_doc("Salary Component", default_flexi_compenent) - flexi_struct_row = {} - flexi_struct_row['depends_on_lwp'] = salary_component.depends_on_lwp - flexi_struct_row['salary_component'] = salary_component.name - flexi_struct_row['abbr'] = salary_component.salary_component_abbr - flexi_struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total + flexi_struct_row = self.create_flexi_struct_row(default_flexi_compenent) payroll_period_days = get_payroll_period_days(self.start_date, self.end_date, self.company) amount = get_amount(payroll_period_days, self.start_date, self.end_date, max_benefits) self.update_component_row(flexi_struct_row, amount, "earnings") + def create_flexi_struct_row(self, default_flexi_compenent): + salary_component = frappe.get_doc("Salary Component", default_flexi_compenent) + flexi_struct_row = {} + flexi_struct_row['depends_on_lwp'] = salary_component.depends_on_lwp + flexi_struct_row['salary_component'] = salary_component.name + flexi_struct_row['abbr'] = salary_component.salary_component_abbr + flexi_struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total + return frappe._dict(flexi_struct_row) + def update_component_row(self, struct_row, amount, key): component_row = None for d in self.get(key): From c678d037e7142090972b4f7788d92fa5359624a1 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 16 May 2018 12:16:18 +0530 Subject: [PATCH 14/21] Salary slip - throw if no default flexi component --- erpnext/hr/doctype/salary_slip/salary_slip.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 85afc906d9f..6fdcb3d9a64 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -88,6 +88,8 @@ class SalarySlip(TransactionBase): payroll_period_days = get_payroll_period_days(self.start_date, self.end_date, self.company) amount = get_amount(payroll_period_days, self.start_date, self.end_date, max_benefits) self.update_component_row(flexi_struct_row, amount, "earnings") + else: + frappe.throw(_("Configure default flexible benefit salary component for apply pro-rata benefit")) def create_flexi_struct_row(self, default_flexi_compenent): salary_component = frappe.get_doc("Salary Component", default_flexi_compenent) From 6410022cab5d78f7d53675d2e5063a308dacc762 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 16 May 2018 15:10:22 +0530 Subject: [PATCH 15/21] minor fixes --- erpnext/hr/doctype/salary_slip/salary_slip.py | 2 +- erpnext/hr/doctype/salary_structure/salary_structure.js | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 6fdcb3d9a64..0da3094cd3b 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -212,7 +212,7 @@ class SalarySlip(TransactionBase): where employee=%s and (from_date <= %s or from_date <= %s) and (to_date is null or to_date >= %s or to_date >= %s) and salary_structure in (select name from `tabSalary Structure` - where is_active = 'Yes'%s) + where docstatus = 1 and is_active = 'Yes'%s) """% ('%s', '%s', '%s','%s','%s', cond),(self.employee, self.start_date, joining_date, self.end_date, relieving_date)) if st_name: diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js index ca922344610..c63887bcf6c 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/salary_structure.js @@ -123,15 +123,6 @@ frappe.ui.form.on('Salary Structure', { } }); -frappe.ui.form.on('Salary Structure Employee', { - from_date: function(frm, cdt, cdn) { - validate_date(frm, cdt, cdn); - }, - to_date: function(frm, cdt, cdn) { - validate_date(frm, cdt, cdn); - } -}); - var validate_date = function(frm, cdt, cdn) { var doc = locals[cdt][cdn]; if(doc.to_date && doc.from_date) { From c9287b5b886c5b3a83c578cbd149e6a53d3cfbd5 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 16 May 2018 15:13:20 +0530 Subject: [PATCH 16/21] Manage grouping of salary component for flexible benefit --- .../employee_benefit_application.py | 11 ++++++++++- .../doctype/salary_component/salary_component.js | 14 +++++++++++--- .../doctype/salary_component/salary_component.json | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py index c9c2685a90a..a01f6ac3858 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py @@ -92,13 +92,22 @@ def get_employee_benefit_application(salary_slip): def get_components(employee_benefit_application, salary_slip): salary_components_array = [] + group_component_amount = {} payroll_period_days = get_payroll_period_days(salary_slip.start_date, salary_slip.end_date, salary_slip.company) for employee_benefit in employee_benefit_application.employee_benefits: if employee_benefit.is_pro_rata_applicable == 1: struct_row = {} salary_components_dict = {} - salary_component = frappe.get_doc("Salary Component", employee_benefit.earning_component) amount = get_amount(payroll_period_days, salary_slip.start_date, salary_slip.end_date, employee_benefit.amount) + sc = frappe.get_doc("Salary Component", employee_benefit.earning_component) + salary_component = sc + if sc.earning_component_group and not sc.is_group and not sc.flexi_default: + salary_component = frappe.get_doc("Salary Component", sc.earning_component_group) + if group_component_amount and group_component_amount.has_key(sc.earning_component_group): + group_component_amount[sc.earning_component_group] += amount + else: + group_component_amount[sc.earning_component_group] = amount + amount = group_component_amount[sc.earning_component_group] struct_row['depends_on_lwp'] = salary_component.depends_on_lwp struct_row['salary_component'] = salary_component.name struct_row['abbr'] = salary_component.salary_component_abbr diff --git a/erpnext/hr/doctype/salary_component/salary_component.js b/erpnext/hr/doctype/salary_component/salary_component.js index 86203ab44e7..e58a05e7546 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.js +++ b/erpnext/hr/doctype/salary_component/salary_component.js @@ -12,7 +12,15 @@ frappe.ui.form.on('Salary Component', { "is_group": 0, "company": d.company } - } - }) + }; + }); + frm.set_query("earning_component_group", function(frm) { + return { + filters: { + "is_group": 1, + "is_flexible_benefit": 1 + } + }; + }); } -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/hr/doctype/salary_component/salary_component.json index 3a9402f0807..6764e0ca922 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.json +++ b/erpnext/hr/doctype/salary_component/salary_component.json @@ -1001,7 +1001,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-15 18:53:09.115910", + "modified": "2018-05-16 12:27:03.005070", "modified_by": "Administrator", "module": "HR", "name": "Salary Component", From 3cd0ad4c98a9f8d6b132fa104ed648888c1ac43b Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 16 May 2018 15:17:49 +0530 Subject: [PATCH 17/21] Codacy fixes --- .../employee_benefit_application.js | 10 +++++----- erpnext/hr/doctype/payroll_period/payroll_period.py | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js index 65619b37776..624a3d5eb0a 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js @@ -10,8 +10,8 @@ frappe.ui.form.on('Employee Benefit Application', { is_flexible_benefit: true, disabled: false } - } - }) + }; + }); }, employee: function(frm) { frappe.call({ @@ -31,8 +31,8 @@ frappe.ui.form.on('Employee Benefit Application', { frappe.ui.form.on("Employee Benefit Application Detail",{ amount: function(frm, cdt, cdn) { calculate_all(frm.doc, cdt, cdn); - }, -}) + } +}); var calculate_all = function(doc, dt, dn) { var tbl = doc.employee_benefits || []; @@ -50,4 +50,4 @@ var calculate_all = function(doc, dt, dn) { doc.remainig_benefits = doc.max_benefits - total_amount; doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount; refresh_many(['pro_rata_dispensed_amount', 'total_amount','remainig_benefits']); -} +}; diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.py b/erpnext/hr/doctype/payroll_period/payroll_period.py index c95c7bdbaf0..a818bc42765 100644 --- a/erpnext/hr/doctype/payroll_period/payroll_period.py +++ b/erpnext/hr/doctype/payroll_period/payroll_period.py @@ -26,5 +26,4 @@ def get_payroll_period_days(start_date, end_date, company): }) if len(payroll_period_dates) > 0: - print payroll_period_dates[0][0], payroll_period_dates[0][1] return date_diff(getdate(payroll_period_dates[0][1]), getdate(payroll_period_dates[0][0])) + 1 From 3e64297a3d3907f21f7a194a6c58f66705b0daf5 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 16 May 2018 16:07:54 +0530 Subject: [PATCH 18/21] Employee Benefit Application - validate maximun benefit --- .../employee_benefit_application.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py index a01f6ac3858..0ce9e0bbbd4 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py @@ -13,15 +13,19 @@ class EmployeeBenefitApplication(Document): def validate(self): if self.max_benefits <= 0: frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee)) + self.validate_max_benefit_for_component() def before_submit(self): self.validate_duplicate_on_payroll_period() - self.validate_max_benefit_for_component() def validate_max_benefit_for_component(self): if self.employee_benefits: + max_benefit_amount = 0 for employee_benefit in self.employee_benefits: self.validate_max_benefit(employee_benefit.earning_component) + max_benefit_amount += employee_benefit.amount + if max_benefit_amount > self.max_benefits: + frappe.throw(_("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, self.max_benefits)) def validate_max_benefit(self, earning_component_name): max_benefit_amount = frappe.db.get_value("Salary Component", earning_component_name, "max_benefit_amount") From 3abae9c1ba7530328c52afe5a1e156ffdf73457c Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 16 May 2018 17:02:21 +0530 Subject: [PATCH 19/21] Employee Benefit Application - whitlist method get_max_benefits --- .../employee_benefit_application.js | 7 +++++-- .../employee_benefit_application.py | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js index 624a3d5eb0a..7859a470cc8 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js @@ -15,8 +15,11 @@ frappe.ui.form.on('Employee Benefit Application', { }, employee: function(frm) { frappe.call({ - doc: frm.doc, - method: "get_max_benefits", + method: "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits", + args:{ + employee: frm.doc.employee, + on_date: frm.doc.date + }, callback: function (data) { if(!data.exc){ if(data.message){ diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py index 0ce9e0bbbd4..8e59bf556b9 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py @@ -48,16 +48,17 @@ class EmployeeBenefitApplication(Document): if application: frappe.throw(_("Employee {0} already submited an apllication {1} for the payroll period {2}").format(self.employee, application, self.payroll_period)) - def get_max_benefits(self): - sal_struct = get_assigned_salary_sturecture(self.employee, self.date) - if sal_struct: - max_benefits = frappe.db.get_value("Salary Structure", sal_struct[0][0], "max_benefits") - if max_benefits > 0: - return max_benefits - else: - frappe.throw(_("Employee {0} has no max benefits in salary structure {1}").format(self.employee, sal_struct[0][0])) +@frappe.whitelist() +def get_max_benefits(employee, on_date): + sal_struct = get_assigned_salary_sturecture(employee, on_date) + if sal_struct: + max_benefits = frappe.db.get_value("Salary Structure", sal_struct[0][0], "max_benefits") + if max_benefits > 0: + return max_benefits else: - frappe.throw(_("Employee {0} has no salary structure assigned").format(self.employee)) + frappe.throw(_("Employee {0} has no max benefits in salary structure {1}").format(employee, sal_struct[0][0])) + else: + frappe.throw(_("Employee {0} has no salary structure assigned").format(employee)) @frappe.whitelist() From 3434aabc28e4cafce7c5163e0a8fd00d283c3877 Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 16 May 2018 19:25:20 +0530 Subject: [PATCH 20/21] Employee Benefit Claim in Salary Slip --- .../employee_benefit_claim.js | 4 +- .../employee_benefit_claim.json | 37 +++++++++++- .../employee_benefit_claim.py | 57 ++++++++++++++++++- erpnext/hr/doctype/salary_slip/salary_slip.py | 41 +++++++++---- 4 files changed, 123 insertions(+), 16 deletions(-) diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js index 99d7809d1a1..64fde037633 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js +++ b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js @@ -10,7 +10,7 @@ frappe.ui.form.on('Employee Benefit Claim', { is_flexible_benefit: true, disabled: false } - } - }) + }; + }); } }); diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json index 00d5159c512..9da7cdb53b5 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json +++ b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json @@ -188,6 +188,7 @@ "label": "Max Amount Eligible", "length": 0, "no_copy": 0, + "options": "earning_component.max_benefit_amount", "permlevel": 0, "precision": "", "print_hide": 0, @@ -201,6 +202,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_pro_rata_applicable", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Is Pro-Rata Applicable", + "length": 0, + "no_copy": 0, + "options": "earning_component.is_pro_rata_applicable", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -367,7 +400,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-04-14 15:38:41.538646", + "modified": "2018-05-16 17:21:25.598531", "modified_by": "Administrator", "module": "HR", "name": "Employee Benefit Claim", @@ -460,4 +493,4 @@ "title_field": "employee_name", "track_changes": 1, "track_seen": 0 -} \ No newline at end of file +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py index 551d1accd5a..39b3540e06a 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py +++ b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -4,7 +4,62 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document +from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits class EmployeeBenefitClaim(Document): - pass + def validate(self): + if not self.is_pro_rata_applicable: + self.validate_max_benefit_for_sal_struct() + # TODO: Validate all cases + + def validate_max_benefit_for_sal_struct(self): + max_benefits = get_max_benefits(self.employee, self.claim_date) + if self.claimed_amount > max_benefits: + frappe.throw(_("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, max_benefits)) + + +def get_employee_benefit_claim(salary_slip): + employee_benefits = frappe.db.sql(""" + select name from `tabEmployee Benefit Claim` + where employee=%(employee)s + and docstatus = 1 and is_pro_rata_applicable = 0 + and (claim_date between %(start_date)s and %(end_date)s) + """, { + 'employee': salary_slip.employee, + 'start_date': salary_slip.start_date, + 'end_date': salary_slip.end_date + }) + + if employee_benefits: + salary_components_array = [] + for employee_benefit in employee_benefits: + struct_row = {} + salary_components_dict = {} + group_component_amount = {} + + employee_benefit_claim = frappe.get_doc("Employee Benefit Claim", employee_benefit[0]) + amount = employee_benefit_claim.claimed_amount + sc = frappe.get_doc("Salary Component", employee_benefit_claim.earning_component) + + salary_component = sc + if sc.earning_component_group and not sc.is_group and not sc.flexi_default: + salary_component = frappe.get_doc("Salary Component", sc.earning_component_group) + if group_component_amount and group_component_amount.has_key(sc.earning_component_group): + group_component_amount[sc.earning_component_group] += amount + else: + group_component_amount[sc.earning_component_group] = amount + amount = group_component_amount[sc.earning_component_group] + + struct_row['depends_on_lwp'] = salary_component.depends_on_lwp + struct_row['salary_component'] = salary_component.name + struct_row['abbr'] = salary_component.salary_component_abbr + struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total + salary_components_dict['amount'] = amount + salary_components_dict['struct_row'] = struct_row + salary_components_array.append(salary_components_dict) + + if len(salary_components_array) > 0: + return salary_components_array + return False diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 0da3094cd3b..5f284789fac 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -15,6 +15,7 @@ from frappe.utils.background_jobs import enqueue from erpnext.hr.doctype.additional_salary_component.additional_salary_component import get_additional_salary_component from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_employee_benefit_application, get_amount from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days +from erpnext.hr.doctype.employee_benefit_claim.employee_benefit_claim import get_employee_benefit_claim class SalarySlip(TransactionBase): def autoname(self): @@ -65,16 +66,18 @@ class SalarySlip(TransactionBase): if additional_components: for additional_component in additional_components: additional_component = frappe._dict(additional_component) - self.update_component_row(frappe._dict(additional_component.struct_row), additional_component.amount, "earnings") + amount = self.update_amount_for_other_component(frappe._dict(additional_component.struct_row).salary_component, additional_component.amount) + self.update_component_row(frappe._dict(additional_component.struct_row), amount, "earnings") - employee_benefits = get_employee_benefit_application(self) - if employee_benefits: - for employee_benefit in employee_benefits: - benefit_component = frappe._dict(employee_benefit) - self.update_component_row(frappe._dict(benefit_component.struct_row), benefit_component.amount, "earnings") - else: - max_benefits = self._salary_structure_doc.get("max_benefits") - if max_benefits > 0: + max_benefits = self._salary_structure_doc.get("max_benefits") + if max_benefits > 0: + employee_benefits = get_employee_benefit_application(self) + if employee_benefits: + for employee_benefit in employee_benefits: + benefit_component = frappe._dict(employee_benefit) + amount = self.update_amount_for_other_component(frappe._dict(benefit_component.struct_row).salary_component, benefit_component.amount) + self.update_component_row(frappe._dict(benefit_component.struct_row), amount, "earnings") + else: default_flexi_compenent = frappe.db.exists( 'Salary Component', { @@ -86,11 +89,26 @@ class SalarySlip(TransactionBase): if default_flexi_compenent: flexi_struct_row = self.create_flexi_struct_row(default_flexi_compenent) payroll_period_days = get_payroll_period_days(self.start_date, self.end_date, self.company) - amount = get_amount(payroll_period_days, self.start_date, self.end_date, max_benefits) + amount = self.update_amount_for_other_component(default_flexi_compenent, get_amount(payroll_period_days, self.start_date, self.end_date, max_benefits)) self.update_component_row(flexi_struct_row, amount, "earnings") else: frappe.throw(_("Configure default flexible benefit salary component for apply pro-rata benefit")) + benefit_claims = get_employee_benefit_claim(self) + if benefit_claims: + for benefit_claim in benefit_claims: + benefit_component = frappe._dict(benefit_claim) + amount = self.update_amount_for_other_component(frappe._dict(benefit_component.struct_row).salary_component, benefit_component.amount) + self.update_component_row(frappe._dict(benefit_component.struct_row), amount, "earnings") + + def update_amount_for_other_component(self, salary_component, new_amount): + amount = new_amount + for d in self.get("earnings"): + if d.salary_component == salary_component: + d.amount += new_amount + amount = d.amount + return amount + def create_flexi_struct_row(self, default_flexi_compenent): salary_component = frappe.get_doc("Salary Component", default_flexi_compenent) flexi_struct_row = {} @@ -211,8 +229,9 @@ class SalarySlip(TransactionBase): st_name = frappe.db.sql("""select salary_structure from `tabSalary Structure Assignment` where employee=%s and (from_date <= %s or from_date <= %s) and (to_date is null or to_date >= %s or to_date >= %s) + and docstatus = 1 and salary_structure in (select name from `tabSalary Structure` - where docstatus = 1 and is_active = 'Yes'%s) + where is_active = 'Yes'%s) """% ('%s', '%s', '%s','%s','%s', cond),(self.employee, self.start_date, joining_date, self.end_date, relieving_date)) if st_name: From 8b86d18f735ab86de77823be3188942e9e82501f Mon Sep 17 00:00:00 2001 From: Jamsheer Date: Wed, 16 May 2018 19:32:03 +0530 Subject: [PATCH 21/21] Remove unused imports --- .../test_employee_benefit_application.py | 2 -- .../employee_benefit_claim/test_employee_benefit_claim.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.py index 9b915a66b9d..34e1a8fbc1d 100644 --- a/erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.py @@ -2,8 +2,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt from __future__ import unicode_literals - -import frappe import unittest class TestEmployeeBenefitApplication(unittest.TestCase): diff --git a/erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.py b/erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.py index e8dc0da47ca..aff73e5c816 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.py +++ b/erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.py @@ -2,8 +2,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt from __future__ import unicode_literals - -import frappe import unittest class TestEmployeeBenefitClaim(unittest.TestCase):