feat: Provision to assign multiple payroll cost centers against a single employee (#29024)
* feat: Provision to assign multiple payroll cost centers against a single employee * fix: get fallback cost center from Employee/Department * fix: linter issues * fix: convert raw queries with frappe ORM Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
This commit is contained in:
commit
091e0bd72a
@ -6,6 +6,15 @@ frappe.ui.form.on('Department', {
|
|||||||
frm.set_query("parent_department", function(){
|
frm.set_query("parent_department", function(){
|
||||||
return {"filters": [["Department", "is_group", "=", 1]]};
|
return {"filters": [["Department", "is_group", "=", 1]]};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("payroll_cost_center", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"company": frm.doc.company,
|
||||||
|
"is_group": 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
// read-only for root department
|
// read-only for root department
|
||||||
|
|||||||
@ -47,6 +47,15 @@ frappe.ui.form.on('Employee', {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("payroll_cost_center", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"company": frm.doc.company,
|
||||||
|
"is_group": 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
frm.set_query("department", function() {
|
frm.set_query("department", function() {
|
||||||
|
|||||||
@ -68,12 +68,18 @@ class Employee(NestedSet):
|
|||||||
self.employee_name = ' '.join(filter(lambda x: x, [self.first_name, self.middle_name, self.last_name]))
|
self.employee_name = ' '.join(filter(lambda x: x, [self.first_name, self.middle_name, self.last_name]))
|
||||||
|
|
||||||
def validate_user_details(self):
|
def validate_user_details(self):
|
||||||
data = frappe.db.get_value('User',
|
if self.user_id:
|
||||||
self.user_id, ['enabled', 'user_image'], as_dict=1)
|
data = frappe.db.get_value('User',
|
||||||
if data.get("user_image") and self.image == '':
|
self.user_id, ['enabled', 'user_image'], as_dict=1)
|
||||||
self.image = data.get("user_image")
|
|
||||||
self.validate_for_enabled_user_id(data.get("enabled", 0))
|
if not data:
|
||||||
self.validate_duplicate_user_id()
|
self.user_id = None
|
||||||
|
return
|
||||||
|
|
||||||
|
if data.get("user_image") and self.image == '':
|
||||||
|
self.image = data.get("user_image")
|
||||||
|
self.validate_for_enabled_user_id(data.get("enabled", 0))
|
||||||
|
self.validate_duplicate_user_id()
|
||||||
|
|
||||||
def update_nsm_model(self):
|
def update_nsm_model(self):
|
||||||
frappe.utils.nestedset.update_nsm(self)
|
frappe.utils.nestedset.update_nsm(self)
|
||||||
|
|||||||
@ -320,3 +320,4 @@ erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021
|
|||||||
erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
|
erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
|
||||||
erpnext.patches.v13_0.update_tax_category_for_rcm
|
erpnext.patches.v13_0.update_tax_category_for_rcm
|
||||||
execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
|
execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
|
||||||
|
erpnext.patches.v14_0.set_payroll_cost_centers
|
||||||
32
erpnext/patches/v14_0/set_payroll_cost_centers.py
Normal file
32
erpnext/patches/v14_0/set_payroll_cost_centers.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc('payroll', 'doctype', 'employee_cost_center')
|
||||||
|
frappe.reload_doc('payroll', 'doctype', 'salary_structure_assignment')
|
||||||
|
|
||||||
|
employees = frappe.get_all("Employee", fields=["department", "payroll_cost_center", "name"])
|
||||||
|
|
||||||
|
employee_cost_center = {}
|
||||||
|
for d in employees:
|
||||||
|
cost_center = d.payroll_cost_center
|
||||||
|
if not cost_center and d.department:
|
||||||
|
cost_center = frappe.get_cached_value("Department", d.department, "payroll_cost_center")
|
||||||
|
|
||||||
|
if cost_center:
|
||||||
|
employee_cost_center.setdefault(d.name, cost_center)
|
||||||
|
|
||||||
|
salary_structure_assignments = frappe.get_all("Salary Structure Assignment",
|
||||||
|
filters = {"docstatus": ["!=", 2]},
|
||||||
|
fields=["name", "employee"])
|
||||||
|
|
||||||
|
for d in salary_structure_assignments:
|
||||||
|
cost_center = employee_cost_center.get(d.employee)
|
||||||
|
if cost_center:
|
||||||
|
assignment = frappe.get_doc("Salary Structure Assignment", d.name)
|
||||||
|
if not assignment.get("payroll_cost_centers"):
|
||||||
|
assignment.append("payroll_cost_centers", {
|
||||||
|
"cost_center": cost_center,
|
||||||
|
"percentage": 100
|
||||||
|
})
|
||||||
|
assignment.save()
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2021-12-23 12:44:38.389283",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"cost_center",
|
||||||
|
"percentage"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"fieldname": "cost_center",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Cost Center",
|
||||||
|
"options": "Cost Center",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"fieldname": "percentage",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Percentage (%)",
|
||||||
|
"non_negative": 1,
|
||||||
|
"reqd": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-12-23 17:39:03.410924",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Payroll",
|
||||||
|
"name": "Employee Cost Center",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class EmployeeCostCenter(Document):
|
||||||
|
pass
|
||||||
@ -7,6 +7,7 @@ from dateutil.relativedelta import relativedelta
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.desk.reportview import get_filters_cond, get_match_cond
|
from frappe.desk.reportview import get_filters_cond, get_match_cond
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.query_builder.functions import Coalesce
|
||||||
from frappe.utils import (
|
from frappe.utils import (
|
||||||
DATE_FORMAT,
|
DATE_FORMAT,
|
||||||
add_days,
|
add_days,
|
||||||
@ -157,11 +158,20 @@ class PayrollEntry(Document):
|
|||||||
Returns list of salary slips based on selected criteria
|
Returns list of salary slips based on selected criteria
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ss_list = frappe.db.sql("""
|
ss = frappe.qb.DocType("Salary Slip")
|
||||||
select t1.name, t1.salary_structure, t1.payroll_cost_center from `tabSalary Slip` t1
|
ss_list = (
|
||||||
where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s and t1.payroll_entry = %s
|
frappe.qb.from_(ss)
|
||||||
and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s
|
.select(ss.name, ss.salary_structure)
|
||||||
""", (ss_status, self.start_date, self.end_date, self.name, self.salary_slip_based_on_timesheet), as_dict=as_dict)
|
.where(
|
||||||
|
(ss.docstatus == ss_status)
|
||||||
|
& (ss.start_date >= self.start_date)
|
||||||
|
& (ss.end_date <= self.end_date)
|
||||||
|
& (ss.payroll_entry == self.name)
|
||||||
|
& ((ss.journal_entry.isnull()) | (ss.journal_entry == ""))
|
||||||
|
& (Coalesce(ss.salary_slip_based_on_timesheet, 0) == self.salary_slip_based_on_timesheet)
|
||||||
|
)
|
||||||
|
).run(as_dict=as_dict)
|
||||||
|
|
||||||
return ss_list
|
return ss_list
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -190,13 +200,20 @@ class PayrollEntry(Document):
|
|||||||
|
|
||||||
def get_salary_components(self, component_type):
|
def get_salary_components(self, component_type):
|
||||||
salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
|
salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
|
||||||
|
|
||||||
if salary_slips:
|
if salary_slips:
|
||||||
salary_components = frappe.db.sql("""
|
ss = frappe.qb.DocType("Salary Slip")
|
||||||
select ssd.salary_component, ssd.amount, ssd.parentfield, ss.payroll_cost_center
|
ssd = frappe.qb.DocType("Salary Detail")
|
||||||
from `tabSalary Slip` ss, `tabSalary Detail` ssd
|
salary_components = (
|
||||||
where ss.name = ssd.parent and ssd.parentfield = '%s' and ss.name in (%s)
|
frappe.qb.from_(ss)
|
||||||
""" % (component_type, ', '.join(['%s']*len(salary_slips))),
|
.join(ssd)
|
||||||
tuple([d.name for d in salary_slips]), as_dict=True)
|
.on(ss.name == ssd.parent)
|
||||||
|
.select(ssd.salary_component, ssd.amount, ssd.parentfield, ss.salary_structure, ss.employee)
|
||||||
|
.where(
|
||||||
|
(ssd.parentfield == component_type)
|
||||||
|
& (ss.name.isin(tuple([d.name for d in salary_slips])))
|
||||||
|
)
|
||||||
|
).run(as_dict=True)
|
||||||
|
|
||||||
return salary_components
|
return salary_components
|
||||||
|
|
||||||
@ -204,18 +221,49 @@ class PayrollEntry(Document):
|
|||||||
salary_components = self.get_salary_components(component_type)
|
salary_components = self.get_salary_components(component_type)
|
||||||
if salary_components:
|
if salary_components:
|
||||||
component_dict = {}
|
component_dict = {}
|
||||||
|
self.employee_cost_centers = {}
|
||||||
for item in salary_components:
|
for item in salary_components:
|
||||||
|
employee_cost_centers = self.get_payroll_cost_centers_for_employee(item.employee, item.salary_structure)
|
||||||
|
|
||||||
add_component_to_accrual_jv_entry = True
|
add_component_to_accrual_jv_entry = True
|
||||||
if component_type == "earnings":
|
if component_type == "earnings":
|
||||||
is_flexible_benefit, only_tax_impact = frappe.db.get_value("Salary Component", item['salary_component'], ['is_flexible_benefit', 'only_tax_impact'])
|
is_flexible_benefit, only_tax_impact = \
|
||||||
|
frappe.get_cached_value("Salary Component",item['salary_component'], ['is_flexible_benefit', 'only_tax_impact'])
|
||||||
if is_flexible_benefit == 1 and only_tax_impact ==1:
|
if is_flexible_benefit == 1 and only_tax_impact ==1:
|
||||||
add_component_to_accrual_jv_entry = False
|
add_component_to_accrual_jv_entry = False
|
||||||
|
|
||||||
if add_component_to_accrual_jv_entry:
|
if add_component_to_accrual_jv_entry:
|
||||||
component_dict[(item.salary_component, item.payroll_cost_center)] \
|
for cost_center, percentage in employee_cost_centers.items():
|
||||||
= component_dict.get((item.salary_component, item.payroll_cost_center), 0) + flt(item.amount)
|
amount_against_cost_center = flt(item.amount) * percentage / 100
|
||||||
|
component_dict[(item.salary_component, cost_center)] \
|
||||||
|
= component_dict.get((item.salary_component, cost_center), 0) + amount_against_cost_center
|
||||||
|
|
||||||
account_details = self.get_account(component_dict = component_dict)
|
account_details = self.get_account(component_dict = component_dict)
|
||||||
return account_details
|
return account_details
|
||||||
|
|
||||||
|
def get_payroll_cost_centers_for_employee(self, employee, salary_structure):
|
||||||
|
if not self.employee_cost_centers.get(employee):
|
||||||
|
ss_assignment_name = frappe.db.get_value("Salary Structure Assignment",
|
||||||
|
{"employee": employee, "salary_structure": salary_structure, "docstatus": 1}, 'name')
|
||||||
|
|
||||||
|
if ss_assignment_name:
|
||||||
|
cost_centers = dict(frappe.get_all("Employee Cost Center", {"parent": ss_assignment_name},
|
||||||
|
["cost_center", "percentage"], as_list=1))
|
||||||
|
if not cost_centers:
|
||||||
|
default_cost_center, department = frappe.get_cached_value("Employee", employee, ["payroll_cost_center", "department"])
|
||||||
|
if not default_cost_center and department:
|
||||||
|
default_cost_center = frappe.get_cached_value("Department", department, "payroll_cost_center")
|
||||||
|
if not default_cost_center:
|
||||||
|
default_cost_center = self.cost_center
|
||||||
|
|
||||||
|
cost_centers = {
|
||||||
|
default_cost_center: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
self.employee_cost_centers.setdefault(employee, cost_centers)
|
||||||
|
|
||||||
|
return self.employee_cost_centers.get(employee, {})
|
||||||
|
|
||||||
def get_account(self, component_dict = None):
|
def get_account(self, component_dict = None):
|
||||||
account_dict = {}
|
account_dict = {}
|
||||||
for key, amount in component_dict.items():
|
for key, amount in component_dict.items():
|
||||||
|
|||||||
@ -120,8 +120,7 @@ class TestPayrollEntry(unittest.TestCase):
|
|||||||
|
|
||||||
employee1 = make_employee("test_employee1@example.com", payroll_cost_center="_Test Cost Center - _TC",
|
employee1 = make_employee("test_employee1@example.com", payroll_cost_center="_Test Cost Center - _TC",
|
||||||
department="cc - _TC", company="_Test Company")
|
department="cc - _TC", company="_Test Company")
|
||||||
employee2 = make_employee("test_employee2@example.com", payroll_cost_center="_Test Cost Center 2 - _TC",
|
employee2 = make_employee("test_employee2@example.com", department="cc - _TC", company="_Test Company")
|
||||||
department="cc - _TC", company="_Test Company")
|
|
||||||
|
|
||||||
if not frappe.db.exists("Account", "_Test Payroll Payable - _TC"):
|
if not frappe.db.exists("Account", "_Test Payroll Payable - _TC"):
|
||||||
create_account(account_name="_Test Payroll Payable",
|
create_account(account_name="_Test Payroll Payable",
|
||||||
@ -132,8 +131,26 @@ class TestPayrollEntry(unittest.TestCase):
|
|||||||
frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account",
|
frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account",
|
||||||
"_Test Payroll Payable - _TC")
|
"_Test Payroll Payable - _TC")
|
||||||
currency=frappe.db.get_value("Company", "_Test Company", "default_currency")
|
currency=frappe.db.get_value("Company", "_Test Company", "default_currency")
|
||||||
|
|
||||||
make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False)
|
make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False)
|
||||||
make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False)
|
ss = make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False)
|
||||||
|
|
||||||
|
# update cost centers in salary structure assignment for employee2
|
||||||
|
ssa = frappe.db.get_value("Salary Structure Assignment",
|
||||||
|
{"employee": employee2, "salary_structure": ss.name, "docstatus": 1}, 'name')
|
||||||
|
|
||||||
|
ssa_doc = frappe.get_doc("Salary Structure Assignment", ssa)
|
||||||
|
ssa_doc.payroll_cost_centers = []
|
||||||
|
ssa_doc.append("payroll_cost_centers", {
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"percentage": 60
|
||||||
|
})
|
||||||
|
ssa_doc.append("payroll_cost_centers", {
|
||||||
|
"cost_center": "_Test Cost Center 2 - _TC",
|
||||||
|
"percentage": 40
|
||||||
|
})
|
||||||
|
|
||||||
|
ssa_doc.save()
|
||||||
|
|
||||||
dates = get_start_end_dates('Monthly', nowdate())
|
dates = get_start_end_dates('Monthly', nowdate())
|
||||||
if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
|
if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
|
||||||
@ -148,10 +165,10 @@ class TestPayrollEntry(unittest.TestCase):
|
|||||||
""", je)
|
""", je)
|
||||||
expected_je = (
|
expected_je = (
|
||||||
('_Test Payroll Payable - _TC', 'Main - _TC', 0.0, 155600.0),
|
('_Test Payroll Payable - _TC', 'Main - _TC', 0.0, 155600.0),
|
||||||
('Salary - _TC', '_Test Cost Center - _TC', 78000.0, 0.0),
|
('Salary - _TC', '_Test Cost Center - _TC', 124800.0, 0.0),
|
||||||
('Salary - _TC', '_Test Cost Center 2 - _TC', 78000.0, 0.0),
|
('Salary - _TC', '_Test Cost Center 2 - _TC', 31200.0, 0.0),
|
||||||
('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 200.0),
|
('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 320.0),
|
||||||
('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 200.0)
|
('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 80.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(je_entries, expected_je)
|
self.assertEqual(je_entries, expected_je)
|
||||||
|
|||||||
@ -12,7 +12,6 @@
|
|||||||
"department",
|
"department",
|
||||||
"designation",
|
"designation",
|
||||||
"branch",
|
"branch",
|
||||||
"payroll_cost_center",
|
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"status",
|
"status",
|
||||||
"journal_entry",
|
"journal_entry",
|
||||||
@ -462,15 +461,6 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fetch_from": "employee.payroll_cost_center",
|
|
||||||
"fetch_if_empty": 1,
|
|
||||||
"fieldname": "payroll_cost_center",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Payroll Cost Center",
|
|
||||||
"options": "Cost Center",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "mode_of_payment",
|
"fieldname": "mode_of_payment",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
@ -647,7 +637,7 @@
|
|||||||
"idx": 9,
|
"idx": 9,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-10-08 11:47:47.098248",
|
"modified": "2021-12-23 11:47:47.098248",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Slip",
|
"name": "Salary Slip",
|
||||||
|
|||||||
@ -167,15 +167,12 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
|
|||||||
def postprocess(source, target):
|
def postprocess(source, target):
|
||||||
if employee:
|
if employee:
|
||||||
employee_details = frappe.db.get_value("Employee", employee,
|
employee_details = frappe.db.get_value("Employee", employee,
|
||||||
["employee_name", "branch", "designation", "department", "payroll_cost_center"], as_dict=1)
|
["employee_name", "branch", "designation", "department"], as_dict=1)
|
||||||
target.employee = employee
|
target.employee = employee
|
||||||
target.employee_name = employee_details.employee_name
|
target.employee_name = employee_details.employee_name
|
||||||
target.branch = employee_details.branch
|
target.branch = employee_details.branch
|
||||||
target.designation = employee_details.designation
|
target.designation = employee_details.designation
|
||||||
target.department = employee_details.department
|
target.department = employee_details.department
|
||||||
target.payroll_cost_center = employee_details.payroll_cost_center
|
|
||||||
if not target.payroll_cost_center and target.department:
|
|
||||||
target.payroll_cost_center = frappe.db.get_value("Department", target.department, "payroll_cost_center")
|
|
||||||
|
|
||||||
target.run_method('process_salary_structure', for_preview=for_preview)
|
target.run_method('process_salary_structure', for_preview=for_preview)
|
||||||
|
|
||||||
|
|||||||
@ -40,28 +40,29 @@ frappe.ui.form.on('Salary Structure Assignment', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("cost_center", "payroll_cost_centers", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"company": frm.doc.company,
|
||||||
|
"is_group": 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
employee: function(frm) {
|
employee: function(frm) {
|
||||||
if(frm.doc.employee){
|
if (frm.doc.employee) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "frappe.client.get_value",
|
method: "set_payroll_cost_centers",
|
||||||
args:{
|
doc: frm.doc,
|
||||||
doctype: "Employee",
|
|
||||||
fieldname: "company",
|
|
||||||
filters:{
|
|
||||||
name: frm.doc.employee
|
|
||||||
}
|
|
||||||
},
|
|
||||||
callback: function(data) {
|
callback: function(data) {
|
||||||
if(data.message){
|
refresh_field("payroll_cost_centers");
|
||||||
frm.set_value("company", data.message.company);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
frm.set_value("company", null);
|
frm.set_value("payroll_cost_centers", []);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,9 @@
|
|||||||
"base",
|
"base",
|
||||||
"column_break_9",
|
"column_break_9",
|
||||||
"variable",
|
"variable",
|
||||||
"amended_from"
|
"amended_from",
|
||||||
|
"section_break_17",
|
||||||
|
"payroll_cost_centers"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -90,7 +92,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "section_break_7",
|
"fieldname": "section_break_7",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Base & Variable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "base",
|
"fieldname": "base",
|
||||||
@ -141,14 +144,29 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Payroll Payable Account",
|
"label": "Payroll Payable Account",
|
||||||
"options": "Account"
|
"options": "Account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"depends_on": "employee",
|
||||||
|
"fieldname": "section_break_17",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Payroll Cost Centers"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"fieldname": "payroll_cost_centers",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Cost Centers",
|
||||||
|
"options": "Employee Cost Center"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 22:44:46.267974",
|
"modified": "2021-12-23 17:28:09.794444",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Structure Assignment",
|
"name": "Salary Structure Assignment",
|
||||||
|
"naming_rule": "Expression (old style)",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@ -193,6 +211,7 @@
|
|||||||
],
|
],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"title_field": "employee_name",
|
"title_field": "employee_name",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@ -5,7 +5,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import getdate
|
from frappe.utils import flt, getdate
|
||||||
|
|
||||||
|
|
||||||
class DuplicateAssignment(frappe.ValidationError): pass
|
class DuplicateAssignment(frappe.ValidationError): pass
|
||||||
@ -15,6 +15,10 @@ class SalaryStructureAssignment(Document):
|
|||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
self.validate_income_tax_slab()
|
self.validate_income_tax_slab()
|
||||||
self.set_payroll_payable_account()
|
self.set_payroll_payable_account()
|
||||||
|
if not self.get("payroll_cost_centers"):
|
||||||
|
self.set_payroll_cost_centers()
|
||||||
|
|
||||||
|
self.validate_cost_center_distribution()
|
||||||
|
|
||||||
def validate_dates(self):
|
def validate_dates(self):
|
||||||
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
||||||
@ -51,6 +55,30 @@ class SalaryStructureAssignment(Document):
|
|||||||
"Company", self.company, "default_currency"), "is_group": 0})
|
"Company", self.company, "default_currency"), "is_group": 0})
|
||||||
self.payroll_payable_account = payroll_payable_account
|
self.payroll_payable_account = payroll_payable_account
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def set_payroll_cost_centers(self):
|
||||||
|
self.payroll_cost_centers = []
|
||||||
|
default_payroll_cost_center = self.get_payroll_cost_center()
|
||||||
|
if default_payroll_cost_center:
|
||||||
|
self.append("payroll_cost_centers", {
|
||||||
|
"cost_center": default_payroll_cost_center,
|
||||||
|
"percentage": 100
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_payroll_cost_center(self):
|
||||||
|
payroll_cost_center = frappe.db.get_value("Employee", self.employee, "payroll_cost_center")
|
||||||
|
if not payroll_cost_center and self.department:
|
||||||
|
payroll_cost_center = frappe.db.get_value("Department", self.department, "payroll_cost_center")
|
||||||
|
|
||||||
|
return payroll_cost_center
|
||||||
|
|
||||||
|
def validate_cost_center_distribution(self):
|
||||||
|
if self.get("payroll_cost_centers"):
|
||||||
|
total_percentage = sum([flt(d.percentage) for d in self.get("payroll_cost_centers", [])])
|
||||||
|
if total_percentage != 100:
|
||||||
|
frappe.throw(_("Total percentage against cost centers should be 100"))
|
||||||
|
|
||||||
|
|
||||||
def get_assigned_salary_structure(employee, on_date):
|
def get_assigned_salary_structure(employee, on_date):
|
||||||
if not employee or not on_date:
|
if not employee or not on_date:
|
||||||
return None
|
return None
|
||||||
@ -64,6 +92,7 @@ def get_assigned_salary_structure(employee, on_date):
|
|||||||
})
|
})
|
||||||
return salary_structure[0][0] if salary_structure else None
|
return salary_structure[0][0] if salary_structure else None
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_employee_currency(employee):
|
def get_employee_currency(employee):
|
||||||
employee_currency = frappe.db.get_value('Salary Structure Assignment', {'employee': employee}, 'currency')
|
employee_currency = frappe.db.get_value('Salary Structure Assignment', {'employee': employee}, 'currency')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user