Merge branch 'develop' into jam_enterprise_emp_benefit
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 8cf6909..7859a47 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,55 @@
// 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
+ }
+ };
+ });
+ },
+ employee: function(frm) {
+ frappe.call({
+ 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){
+ 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 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);
+ }
+ 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;
+ refresh_many(['pro_rata_dispensed_amount', 'total_amount','remainig_benefits']);
+};
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 0fff4fd..3c1c8aa 100644
--- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json
+++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json
@@ -83,6 +83,68 @@
"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,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
@@ -113,6 +175,38 @@
"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,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "payroll_period",
"fieldtype": "Link",
"hidden": 0,
@@ -145,6 +239,68 @@
"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,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 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",
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 9282d98..8e59bf5 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,132 @@
from __future__ import unicode_literals
import frappe
+from frappe import _
+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):
- pass
+ 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()
+
+ 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")
+ 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(
+ "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))
+
+@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 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()
+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
+
+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 = []
+ 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 = {}
+ 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
+ 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/employee_benefit_application/test_employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.py
index 9b915a6..34e1a8f 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_application_detail/employee_benefit_application_detail.json b/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json
index 35918e1..37f3242 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
@@ -51,6 +51,38 @@
"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,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "amount",
"fieldtype": "Currency",
"hidden": 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",
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 816b1bd..64fde03 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
+ }
+ };
+ });
}
});
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 00d5159..9da7cdb 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,
@@ -207,6 +208,38 @@
"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,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "claimed_amount",
"fieldtype": "Currency",
"hidden": 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 551d1ac..39b3540 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/employee_benefit_claim/test_employee_benefit_claim.py b/erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.py
index e8dc0da..aff73e5 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):
diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.py b/erpnext/hr/doctype/payroll_period/payroll_period.py
index 1e18388..a818bc4 100644
--- a/erpnext/hr/doctype/payroll_period/payroll_period.py
+++ b/erpnext/hr/doctype/payroll_period/payroll_period.py
@@ -4,7 +4,26 @@
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:
+ return date_diff(getdate(payroll_period_dates[0][1]), getdate(payroll_period_dates[0][0])) + 1
diff --git a/erpnext/hr/doctype/salary_component/salary_component.js b/erpnext/hr/doctype/salary_component/salary_component.js
index 86203ab..e58a05e 100644
--- a/erpnext/hr/doctype/salary_component/salary_component.js
+++ b/erpnext/hr/doctype/salary_component/salary_component.js
@@ -12,7 +12,15 @@
"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 cf85af4..6764e0c 100644
--- a/erpnext/hr/doctype/salary_component/salary_component.json
+++ b/erpnext/hr/doctype/salary_component/salary_component.json
@@ -114,70 +114,6 @@
"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,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 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,
@@ -460,6 +428,38 @@
"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,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"depends_on": "is_flexible_benefit",
"fieldname": "only_tax_impact",
"fieldtype": "Check",
@@ -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 beffaec..132930f 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_default()
+
+ 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',
+ {
+ 'is_flexible_benefit': 1,
+ 'is_pro_rata_applicable': 1,
+ 'flexi_default': 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:
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index 53e6aa4..87511b6 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -13,6 +13,9 @@
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, 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
from erpnext.hr.utils import get_payroll_period
class SalarySlip(TransactionBase):
@@ -68,7 +71,57 @@
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")
+
+ 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',
+ {
+ 'is_flexible_benefit': 1,
+ 'is_pro_rata_applicable': 1,
+ 'flexi_default': 1
+ }
+ )
+ 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 = 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 = {}
+ 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
@@ -181,6 +234,7 @@
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 is_active = 'Yes'%s)
"""% ('%s', '%s', '%s','%s','%s', cond),(self.employee, self.start_date, joining_date, self.end_date, relieving_date))
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js
index 8fd51d9..9e62b66 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 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) {