From 160d9cbd0d81b7656bc36710f2374b6f6338cc96 Mon Sep 17 00:00:00 2001 From: krantheman Date: Thu, 21 Dec 2023 14:24:32 +0530 Subject: [PATCH 01/29] feat: add syntax highlighting to code fields (cherry picked from commit 6a743721c2481a92ef11d10860954693e702dbe5) --- .../doctype/salary_component/salary_component.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.json b/hrms/payroll/doctype/salary_component/salary_component.json index 1c0cad7379..8353616c79 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.json +++ b/hrms/payroll/doctype/salary_component/salary_component.json @@ -186,7 +186,8 @@ { "fieldname": "condition", "fieldtype": "Code", - "label": "Condition" + "label": "Condition", + "options": "Python" }, { "default": "0", @@ -198,7 +199,8 @@ "depends_on": "amount_based_on_formula", "fieldname": "formula", "fieldtype": "Code", - "label": "Formula" + "label": "Formula", + "options": "Python" }, { "depends_on": "eval:doc.amount_based_on_formula!==1", @@ -266,7 +268,7 @@ "icon": "fa fa-flag", "index_web_pages_for_search": 1, "links": [], - "modified": "2023-08-25 13:35:37.413696", + "modified": "2023-12-19 16:25:36.745511", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Component", From 3fed929fba499aebb67efd5152b65e5a7276a4fd Mon Sep 17 00:00:00 2001 From: krantheman Date: Thu, 21 Dec 2023 15:53:01 +0530 Subject: [PATCH 02/29] feat: add autocompletion to code fields (cherry picked from commit 0625e7bc23a3afee4e8e01e69d1b83e8a21ae501) # Conflicts: # hrms/payroll/doctype/salary_component/salary_component.js --- .../salary_component/salary_component.js | 19 +++++++++++++++++++ .../salary_component/salary_component.json | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index 270dc51a08..ad8e5f54f4 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -23,11 +23,15 @@ frappe.ui.form.on("Salary Component", { }, refresh: function (frm) { +<<<<<<< HEAD if (!frm.doc.__islocal) { frm.add_custom_button(__("Salary Structure"), () => { frm.trigger("create_salary_structure"); }, __("Create")); } +======= + frm.trigger("setup_autocompletions"); +>>>>>>> 0625e7bc2 (feat: add autocompletion to code fields) }, is_flexible_benefit: function (frm) { @@ -70,6 +74,7 @@ frappe.ui.form.on("Salary Component", { } }, +<<<<<<< HEAD create_salary_structure: function (frm) { frappe.model.with_doctype("Salary Structure", () => { const salary_structure = frappe.model.get_new_doc("Salary Structure"); @@ -80,6 +85,20 @@ frappe.ui.form.on("Salary Component", { salary_detail.salary_component = frm.doc.name; frappe.set_route("Form", "Salary Structure", salary_structure.name); }); +======= + setup_autocompletions: function (frm) { + frappe.db + .get_list("Salary Component", { fields: ["salary_component_abbr"] }) + .then((data) => { + const autocompletions = data.map((d) => ({ + value: d.salary_component_abbr, + score: 10, + meta: "Salary Component", + })); + frm.set_df_property("condition", "autocompletions", autocompletions); + frm.set_df_property("formula", "autocompletions", autocompletions); + }); +>>>>>>> 0625e7bc2 (feat: add autocompletion to code fields) }, }); diff --git a/hrms/payroll/doctype/salary_component/salary_component.json b/hrms/payroll/doctype/salary_component/salary_component.json index 8353616c79..0743305dca 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.json +++ b/hrms/payroll/doctype/salary_component/salary_component.json @@ -187,7 +187,7 @@ "fieldname": "condition", "fieldtype": "Code", "label": "Condition", - "options": "Python" + "options": "PythonExpression" }, { "default": "0", @@ -268,7 +268,7 @@ "icon": "fa fa-flag", "index_web_pages_for_search": 1, "links": [], - "modified": "2023-12-19 16:25:36.745511", + "modified": "2023-12-21 15:20:57.785309", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Component", From 076f23fd9ed2f36945254e8382fe025489d50b56 Mon Sep 17 00:00:00 2001 From: krantheman Date: Fri, 22 Dec 2023 12:37:14 +0530 Subject: [PATCH 03/29] feat: add option to sync formula and condition for existing structures (cherry picked from commit 5c5f91b36e445d1b379e40697b63d8e6cb46064d) # Conflicts: # hrms/payroll/doctype/salary_component/salary_component.js --- .../salary_component/salary_component.js | 31 +++++++++++++++++++ .../doctype/salary_detail/salary_detail.py | 10 ++++++ 2 files changed, 41 insertions(+) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index ad8e5f54f4..86367f675f 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -31,7 +31,13 @@ frappe.ui.form.on("Salary Component", { } ======= frm.trigger("setup_autocompletions"); +<<<<<<< HEAD >>>>>>> 0625e7bc2 (feat: add autocompletion to code fields) +======= + if (!frm.doc.__islocal) { + frm.trigger("add_update_structure_button"); + } +>>>>>>> 5c5f91b36 (feat: add option to sync formula and condition for existing structures) }, is_flexible_benefit: function (frm) { @@ -100,6 +106,31 @@ frappe.ui.form.on("Salary Component", { }); >>>>>>> 0625e7bc2 (feat: add autocompletion to code fields) }, + + add_update_structure_button: function (frm) { + for (const df of ["Condition", "Formula"]) { + frm.add_custom_button( + __("Sync {0}", [df]), + function () { + frappe.confirm( + __("Update {0} for all existing Salary Structures?", [df]), + () => { + frappe.call({ + method: + "hrms.payroll.doctype.salary_detail.salary_detail.update_salary_structures", + args: { + component: frm.doc.name, + field: df.toLowerCase(), + value: frm.get_field(df.toLowerCase()).value, + }, + }); + } + ); + }, + __("Update Salary Structures") + ); + } + }, }); var set_value_for_condition_and_formula = function (frm) { diff --git a/hrms/payroll/doctype/salary_detail/salary_detail.py b/hrms/payroll/doctype/salary_detail/salary_detail.py index c74bd546eb..ad22fea208 100644 --- a/hrms/payroll/doctype/salary_detail/salary_detail.py +++ b/hrms/payroll/doctype/salary_detail/salary_detail.py @@ -2,8 +2,18 @@ # For license information, please see license.txt +import frappe from frappe.model.document import Document class SalaryDetail(Document): pass + + +@frappe.whitelist() +def update_salary_structures(component, field, value): + salary_details = frappe.get_list( + "Salary Detail", filters={"salary_component": component}, pluck="name" + ) + for d in salary_details: + frappe.db.set_value("Salary Detail", d, field, value) From 8c5517064fa2920d63ebba983058199430d7e221 Mon Sep 17 00:00:00 2001 From: krantheman Date: Fri, 22 Dec 2023 13:31:06 +0530 Subject: [PATCH 04/29] feat: add warning for unset accounts on insert (cherry picked from commit 18b82b2c9a23eb0d947f9fd4fe73c1ff20377d2f) --- .../payroll/doctype/salary_component/salary_component.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py index 4ba740209f..105d511c30 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.py +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -2,6 +2,7 @@ # For license information, please see license.txt import frappe +from frappe import _ from frappe.model.document import Document from frappe.model.naming import append_number_if_name_exists @@ -10,6 +11,14 @@ class SalaryComponent(Document): def validate(self): self.validate_abbr() + def after_insert(self): + if not (self.statistical_component or (self.accounts and all(d.account for d in self.accounts))): + frappe.msgprint( + title=_("Warning"), + msg=_("Accounts not set for Salary Component {0}").format(self.name), + indicator="orange", + ) + def clear_cache(self): from hrms.payroll.doctype.salary_slip.salary_slip import ( SALARY_COMPONENT_VALUES, From f86b484b82bdf97e21a215c7c3a4d2e51ddb6ff8 Mon Sep 17 00:00:00 2001 From: krantheman Date: Fri, 22 Dec 2023 15:02:50 +0530 Subject: [PATCH 05/29] feat: add employee field autocompletions (cherry picked from commit 57a70cf8f3392b8d1d6f0c42c37b3f2cc568666f) --- .../doctype/salary_component/salary_component.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index 86367f675f..89726d58a1 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -95,14 +95,22 @@ frappe.ui.form.on("Salary Component", { setup_autocompletions: function (frm) { frappe.db .get_list("Salary Component", { fields: ["salary_component_abbr"] }) - .then((data) => { - const autocompletions = data.map((d) => ({ + .then((salary_components) => { + const autocompletions = salary_components.map((d) => ({ value: d.salary_component_abbr, score: 10, meta: "Salary Component", })); - frm.set_df_property("condition", "autocompletions", autocompletions); - frm.set_df_property("formula", "autocompletions", autocompletions); + frappe.db.get_doc("DocType", "Employee").then((employee_doc) => { + const employee_fields = employee_doc.fields.map((f) => ({ + value: f.fieldname, + score: 9, + meta: "Employee Field", + })); + autocompletions.push(...employee_fields); + frm.set_df_property("condition", "autocompletions", autocompletions); + frm.set_df_property("formula", "autocompletions", autocompletions); + }); }); >>>>>>> 0625e7bc2 (feat: add autocompletion to code fields) }, From 3870a0e8f956664ecdf149dbb732b6e39df076a0 Mon Sep 17 00:00:00 2001 From: krantheman Date: Fri, 22 Dec 2023 17:21:41 +0530 Subject: [PATCH 06/29] fix: update only submitted salary structures (cherry picked from commit 0942fb74f21c1fb499f27ca1bf552e7ce06f1241) --- hrms/payroll/doctype/salary_detail/salary_detail.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hrms/payroll/doctype/salary_detail/salary_detail.py b/hrms/payroll/doctype/salary_detail/salary_detail.py index ad22fea208..1a647cd28e 100644 --- a/hrms/payroll/doctype/salary_detail/salary_detail.py +++ b/hrms/payroll/doctype/salary_detail/salary_detail.py @@ -12,8 +12,10 @@ class SalaryDetail(Document): @frappe.whitelist() def update_salary_structures(component, field, value): - salary_details = frappe.get_list( - "Salary Detail", filters={"salary_component": component}, pluck="name" - ) - for d in salary_details: - frappe.db.set_value("Salary Detail", d, field, value) + SalaryDetail = frappe.qb.DocType("Salary Detail") + SalaryStructure = frappe.qb.DocType("Salary Structure") + frappe.qb.update(SalaryDetail).inner_join(SalaryStructure).on( + SalaryDetail.parent == SalaryStructure.name + ).set(SalaryDetail[field], value).where( + (SalaryDetail.salary_component == component) & (SalaryStructure.docstatus == 1) + ).run() From 49567921edc07cc847a7865fcf855079af90f53e Mon Sep 17 00:00:00 2001 From: krantheman Date: Tue, 26 Dec 2023 13:29:00 +0530 Subject: [PATCH 07/29] feat: add autocompletions for Salary Structure and Salary Slip fields (cherry picked from commit 6c06213b6b28806ae551d847a630f3b56bbc074d) # Conflicts: # hrms/payroll/doctype/salary_component/salary_component.js --- .../salary_component/salary_component.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index 89726d58a1..2c3e31b746 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -93,6 +93,7 @@ frappe.ui.form.on("Salary Component", { }); ======= setup_autocompletions: function (frm) { +<<<<<<< HEAD frappe.db .get_list("Salary Component", { fields: ["salary_component_abbr"] }) .then((salary_components) => { @@ -113,6 +114,43 @@ frappe.ui.form.on("Salary Component", { }); }); >>>>>>> 0625e7bc2 (feat: add autocompletion to code fields) +======= + const autocompletions = []; + frappe.run_serially([ + ...["Employee", "Salary Structure", "Salary Slip"].map((doctype) => + frappe.model.with_doctype(doctype, () => { + autocompletions.push( + ...frappe.get_meta(doctype).fields.map((f) => ({ + value: f.fieldname, + score: 9, + meta: __("{0} Field", [doctype]), + })) + ); + }) + ), + () => { + frappe.db + .get_list("Salary Component", { + fields: ["salary_component_abbr"], + }) + .then((salary_components) => { + autocompletions.push( + ...salary_components.map((d) => ({ + value: d.salary_component_abbr, + score: 10, + meta: __("Salary Component"), + })) + ); + frm.set_df_property( + "condition", + "autocompletions", + autocompletions + ); + frm.set_df_property("formula", "autocompletions", autocompletions); + }); + }, + ]); +>>>>>>> 6c06213b6 (feat: add autocompletions for Salary Structure and Salary Slip fields) }, add_update_structure_button: function (frm) { From cd8ac084cae13ef5deb560608cfbad8f513964ef Mon Sep 17 00:00:00 2001 From: krantheman Date: Mon, 1 Jan 2024 17:29:09 +0530 Subject: [PATCH 08/29] feat: add success toast (cherry picked from commit d2c6bb0fe91debe6ffd0b6dcd42f159d4ecf5c83) --- .../salary_component/salary_component.js | 26 ++++++++++++------- .../salary_component/salary_component.py | 10 +++++++ .../doctype/salary_detail/salary_detail.py | 12 --------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index 2c3e31b746..e6b6368382 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -161,15 +161,23 @@ frappe.ui.form.on("Salary Component", { frappe.confirm( __("Update {0} for all existing Salary Structures?", [df]), () => { - frappe.call({ - method: - "hrms.payroll.doctype.salary_detail.salary_detail.update_salary_structures", - args: { - component: frm.doc.name, - field: df.toLowerCase(), - value: frm.get_field(df.toLowerCase()).value, - }, - }); + frappe + .call({ + method: "update_salary_structures", + doc: frm.doc, + args: { + field: df.toLowerCase(), + value: frm.get_field(df.toLowerCase()).value, + }, + }) + .then((r) => { + if (!r.exc) { + frappe.show_alert({ + message: __("Salary Structures updated successfully"), + indicator: "green", + }); + } + }); } ); }, diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py index 105d511c30..591cc3c34c 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.py +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -41,3 +41,13 @@ def validate_abbr(self): separator="_", filters={"name": ["!=", self.name]}, ) + + @frappe.whitelist() + def update_salary_structures(self, field, value): + SalaryDetail = frappe.qb.DocType("Salary Detail") + SalaryStructure = frappe.qb.DocType("Salary Structure") + frappe.qb.update(SalaryDetail).inner_join(SalaryStructure).on( + SalaryDetail.parent == SalaryStructure.name + ).set(SalaryDetail[field], value).where( + (SalaryDetail.salary_component == self.name) & (SalaryStructure.docstatus == 1) + ).run() diff --git a/hrms/payroll/doctype/salary_detail/salary_detail.py b/hrms/payroll/doctype/salary_detail/salary_detail.py index 1a647cd28e..c74bd546eb 100644 --- a/hrms/payroll/doctype/salary_detail/salary_detail.py +++ b/hrms/payroll/doctype/salary_detail/salary_detail.py @@ -2,20 +2,8 @@ # For license information, please see license.txt -import frappe from frappe.model.document import Document class SalaryDetail(Document): pass - - -@frappe.whitelist() -def update_salary_structures(component, field, value): - SalaryDetail = frappe.qb.DocType("Salary Detail") - SalaryStructure = frappe.qb.DocType("Salary Structure") - frappe.qb.update(SalaryDetail).inner_join(SalaryStructure).on( - SalaryDetail.parent == SalaryStructure.name - ).set(SalaryDetail[field], value).where( - (SalaryDetail.salary_component == component) & (SalaryStructure.docstatus == 1) - ).run() From 805dfe56d73ca7509f7ee8c27ccb3d1c98a2fb34 Mon Sep 17 00:00:00 2001 From: krantheman Date: Mon, 1 Jan 2024 18:29:40 +0530 Subject: [PATCH 09/29] test: add test for update_salary_structures (cherry picked from commit a56c52847ccdff607e4547a88f1fef48c8cefd96) --- .../salary_component/salary_component.py | 2 +- .../salary_component/test_salary_component.py | 50 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py index 591cc3c34c..0049364b0c 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.py +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -49,5 +49,5 @@ def update_salary_structures(self, field, value): frappe.qb.update(SalaryDetail).inner_join(SalaryStructure).on( SalaryDetail.parent == SalaryStructure.name ).set(SalaryDetail[field], value).where( - (SalaryDetail.salary_component == self.name) & (SalaryStructure.docstatus == 1) + (SalaryDetail.salary_component == self.name) & (SalaryStructure.docstatus != 2) ).run() diff --git a/hrms/payroll/doctype/salary_component/test_salary_component.py b/hrms/payroll/doctype/salary_component/test_salary_component.py index 0a00693774..f17147515c 100644 --- a/hrms/payroll/doctype/salary_component/test_salary_component.py +++ b/hrms/payroll/doctype/salary_component/test_salary_component.py @@ -4,9 +4,57 @@ import frappe from frappe.tests.utils import FrappeTestCase +from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + class TestSalaryComponent(FrappeTestCase): - pass + def test_update_salary_structuress(self): + salary_component = create_salary_component("Special Allowance") + salary_component.condition = "H < 10000" + salary_component.formula = "BS*.5" + salary_component.save() + + salary_structure1 = make_salary_structure("Salary Structure 1", "Monthly") + salary_structure2 = make_salary_structure("Salary Structure 2", "Monthly") + salary_structure3 = make_salary_structure("Salary Structure 3", "Monthly") + salary_structure3.cancel() # Details should not update for cancelled Salary Structures + + ss1_detail = [ + d for d in salary_structure1.earnings if d.salary_component == "Special Allowance" + ][0] + self.assertEqual(ss1_detail.condition, "H < 10000") + self.assertEqual(ss1_detail.formula, "BS*.5") + + ss2_detail = [ + d for d in salary_structure2.earnings if d.salary_component == "Special Allowance" + ][0] + self.assertEqual(ss2_detail.condition, "H < 10000") + self.assertEqual(ss2_detail.formula, "BS*.5") + + ss3_detail = [ + d for d in salary_structure3.earnings if d.salary_component == "Special Allowance" + ][0] + self.assertEqual(ss3_detail.condition, "H < 10000") + self.assertEqual(ss3_detail.formula, "BS*.5") + + salary_component.update_salary_structures("condition", "H < 8000") + ss1_detail.reload() + self.assertEqual(ss1_detail.condition, "H < 8000") + ss2_detail.reload() + self.assertEqual(ss2_detail.condition, "H < 8000") + ss3_detail.reload() + self.assertEqual(ss3_detail.condition, "H < 10000") + + salary_component.update_salary_structures("formula", "BS*.3") + ss1_detail.reload() + self.assertEqual(ss1_detail.formula, "BS*.3") + ss2_detail.reload() + self.assertEqual(ss2_detail.formula, "BS*.3") + ss3_detail.reload() + self.assertEqual(ss3_detail.formula, "BS*.5") + + def tearDown(self): + frappe.db.rollback() def create_salary_component(component_name, **args): From f17608f33f7795003f34fc7df9f7b9f93b4d6489 Mon Sep 17 00:00:00 2001 From: krantheman Date: Wed, 24 Jan 2024 16:27:48 +0530 Subject: [PATCH 10/29] fix: sanitize condition and formulae expressions before validation (cherry picked from commit 47605c8b329095fb36ddd6ed5ebf8afcd6d33737) --- hrms/payroll/doctype/salary_component/salary_component.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py index 0049364b0c..1f65d96ff1 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.py +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -6,8 +6,14 @@ from frappe.model.document import Document from frappe.model.naming import append_number_if_name_exists +from hrms.payroll.utils import sanitize_expression + class SalaryComponent(Document): + def before_validate(self): + self.condition = sanitize_expression(self.condition) + self.formula = sanitize_expression(self.formula) + def validate(self): self.validate_abbr() From 8fdaca2ed4f8d36bbe2599275be19914352d259d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 2 Feb 2024 11:51:01 +0530 Subject: [PATCH 11/29] chore: fix typo (cherry picked from commit 4d77fca5b4facfdcca39cb1f5db96f8da24c0995) --- .../doctype/salary_component/test_salary_component.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/test_salary_component.py b/hrms/payroll/doctype/salary_component/test_salary_component.py index f17147515c..e1e7cae6fd 100644 --- a/hrms/payroll/doctype/salary_component/test_salary_component.py +++ b/hrms/payroll/doctype/salary_component/test_salary_component.py @@ -8,7 +8,7 @@ class TestSalaryComponent(FrappeTestCase): - def test_update_salary_structuress(self): + def test_update_salary_structures(self): salary_component = create_salary_component("Special Allowance") salary_component.condition = "H < 10000" salary_component.formula = "BS*.5" @@ -53,9 +53,6 @@ def test_update_salary_structuress(self): ss3_detail.reload() self.assertEqual(ss3_detail.formula, "BS*.5") - def tearDown(self): - frappe.db.rollback() - def create_salary_component(component_name, **args): if frappe.db.exists("Salary Component", component_name): From 79f4616ff1009a518285b57b48a2b3d00d202750 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 2 Feb 2024 13:53:35 +0530 Subject: [PATCH 12/29] fix: reset old code field values on update for better readability in the doctype form (cherry picked from commit 403a0d3753d6e5955be2d5e2d0d2de7da156b098) --- .../doctype/salary_component/salary_component.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py index 1f65d96ff1..a1bec30786 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.py +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -11,12 +11,19 @@ class SalaryComponent(Document): def before_validate(self): - self.condition = sanitize_expression(self.condition) - self.formula = sanitize_expression(self.formula) + self._condition, self.condition = self.condition, sanitize_expression(self.condition) + self._formula, self.formula = self.formula, sanitize_expression(self.formula) def validate(self): self.validate_abbr() + def on_update(self): + # set old values (allowing multiline strings for better readability in the doctype form) + if self._condition != self.condition: + self.db_set("condition", self._condition) + if self._formula != self.formula: + self.db_set("formula", self._formula) + def after_insert(self): if not (self.statistical_component or (self.accounts and all(d.account for d in self.accounts))): frappe.msgprint( From 7965d7cf82e0a901bb74f36a66f3c7b0376a53d5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 2 Feb 2024 13:57:06 +0530 Subject: [PATCH 13/29] chore: change formula field to PythonExpression (cherry picked from commit c25f003268efb2763b7464506421788ca5d6b426) --- hrms/payroll/doctype/salary_component/salary_component.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.json b/hrms/payroll/doctype/salary_component/salary_component.json index 0743305dca..27f2cdb1f5 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.json +++ b/hrms/payroll/doctype/salary_component/salary_component.json @@ -200,7 +200,7 @@ "fieldname": "formula", "fieldtype": "Code", "label": "Formula", - "options": "Python" + "options": "PythonExpression" }, { "depends_on": "eval:doc.amount_based_on_formula!==1", @@ -268,7 +268,7 @@ "icon": "fa fa-flag", "index_web_pages_for_search": 1, "links": [], - "modified": "2023-12-21 15:20:57.785309", + "modified": "2024-02-02 13:55:55.989527", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Component", From 37641a664599e336e2fa52cbefcb1614d2cc6d90 Mon Sep 17 00:00:00 2001 From: krantheman Date: Fri, 2 Feb 2024 16:13:17 +0530 Subject: [PATCH 14/29] feat(Salary Structure): add syntax highlighting (cherry picked from commit 33e81e37bfdd2ecce43e868956ef93e2bcf4429f) --- hrms/payroll/doctype/salary_detail/salary_detail.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hrms/payroll/doctype/salary_detail/salary_detail.json b/hrms/payroll/doctype/salary_detail/salary_detail.json index 1e3d7a7d00..4c0f825ba3 100644 --- a/hrms/payroll/doctype/salary_detail/salary_detail.json +++ b/hrms/payroll/doctype/salary_detail/salary_detail.json @@ -131,7 +131,8 @@ "depends_on": "eval:doc.parenttype=='Salary Structure'", "fieldname": "condition", "fieldtype": "Code", - "label": "Condition" + "label": "Condition", + "options": "PythonExpression" }, { "default": "0", @@ -147,7 +148,8 @@ "fieldname": "formula", "fieldtype": "Code", "in_list_view": 1, - "label": "Formula" + "label": "Formula", + "options": "PythonExpression" }, { "depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'", @@ -255,7 +257,7 @@ ], "istable": 1, "links": [], - "modified": "2023-12-12 13:52:30.726505", + "modified": "2024-02-02 16:10:45.570565", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Detail", From 83a736d1e76771d6b1cbbc2c5d26a48e6194ed64 Mon Sep 17 00:00:00 2001 From: krantheman Date: Fri, 2 Feb 2024 19:15:23 +0530 Subject: [PATCH 15/29] chore: commonify condition/formula autocompletions (cherry picked from commit cbf31f93e4b756e9728f3c4f97c68df8a6a0de95) # Conflicts: # hrms/payroll/doctype/salary_component/salary_component.js --- .../salary_component/salary_component.js | 32 +++++++++++++++ hrms/public/js/hrms.bundle.js | 1 + hrms/public/js/payroll_common.js | 39 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 hrms/public/js/payroll_common.js diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index e6b6368382..355431cc3f 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -23,11 +23,25 @@ frappe.ui.form.on("Salary Component", { }, refresh: function (frm) { +<<<<<<< HEAD <<<<<<< HEAD if (!frm.doc.__islocal) { frm.add_custom_button(__("Salary Structure"), () => { frm.trigger("create_salary_structure"); }, __("Create")); +======= + hrms.payroll_common.get_autocompletions_for_condition_and_formula(frm); + + if (!frm.doc.__islocal) { + frm.trigger("add_update_structure_button"); + frm.add_custom_button( + __("Salary Structure"), + () => { + frm.trigger("create_salary_structure"); + }, + __("Create") + ); +>>>>>>> cbf31f93e (chore: commonify condition/formula autocompletions) } ======= frm.trigger("setup_autocompletions"); @@ -80,6 +94,7 @@ frappe.ui.form.on("Salary Component", { } }, +<<<<<<< HEAD <<<<<<< HEAD create_salary_structure: function (frm) { frappe.model.with_doctype("Salary Structure", () => { @@ -153,6 +168,8 @@ frappe.ui.form.on("Salary Component", { >>>>>>> 6c06213b6 (feat: add autocompletions for Salary Structure and Salary Slip fields) }, +======= +>>>>>>> cbf31f93e (chore: commonify condition/formula autocompletions) add_update_structure_button: function (frm) { for (const df of ["Condition", "Formula"]) { frm.add_custom_button( @@ -184,6 +201,21 @@ frappe.ui.form.on("Salary Component", { __("Update Salary Structures") ); } +<<<<<<< HEAD +======= + }, + + create_salary_structure: function (frm) { + frappe.model.with_doctype("Salary Structure", () => { + const salary_structure = frappe.model.get_new_doc("Salary Structure"); + const salary_detail = frappe.model.add_child( + salary_structure, + frm.doc.type === "Earning" ? "earnings" : "deductions" + ); + salary_detail.salary_component = frm.doc.name; + frappe.set_route("Form", "Salary Structure", salary_structure.name); + }); +>>>>>>> cbf31f93e (chore: commonify condition/formula autocompletions) }, }); diff --git a/hrms/public/js/hrms.bundle.js b/hrms/public/js/hrms.bundle.js index 36af73a67a..e769d68eb7 100644 --- a/hrms/public/js/hrms.bundle.js +++ b/hrms/public/js/hrms.bundle.js @@ -3,3 +3,4 @@ import "./templates/feedback_summary.html"; import "./templates/feedback_history.html"; import "./templates/rating.html"; import "./utils"; +import "./payroll_common"; \ No newline at end of file diff --git a/hrms/public/js/payroll_common.js b/hrms/public/js/payroll_common.js new file mode 100644 index 0000000000..5291751b73 --- /dev/null +++ b/hrms/public/js/payroll_common.js @@ -0,0 +1,39 @@ +hrms.payroll_common = { + get_autocompletions_for_condition_and_formula: function (frm) { + const autocompletions = []; + frappe.run_serially([ + ...["Employee", "Salary Structure", "Salary Slip"].map((doctype) => + frappe.model.with_doctype(doctype, () => { + autocompletions.push( + ...frappe.get_meta(doctype).fields.map((f) => ({ + value: f.fieldname, + score: 9, + meta: __("{0} Field", [doctype]), + })) + ); + }) + ), + () => { + frappe.db + .get_list("Salary Component", { + fields: ["salary_component_abbr"], + }) + .then((salary_components) => { + autocompletions.push( + ...salary_components.map((d) => ({ + value: d.salary_component_abbr, + score: 10, + meta: __("Salary Component"), + })) + ); + frm.set_df_property( + "condition", + "autocompletions", + autocompletions + ); + frm.set_df_property("formula", "autocompletions", autocompletions); + }); + }, + ]); + }, +}; From bd2d3dead1457aad3234d0cd795870bfefe175f5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 2 Feb 2024 23:05:57 +0530 Subject: [PATCH 16/29] feat: setup autocompletions for salary structure fields (cherry picked from commit c6817a20341e4c00a0675156bef7486c54af2d88) # Conflicts: # hrms/payroll/doctype/salary_component/salary_component.js --- .../salary_component/salary_component.js | 4 +++ .../salary_structure/salary_structure.js | 5 ++++ hrms/public/js/payroll_common.js | 27 ++++++++++++++----- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index 355431cc3f..b6d509c240 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -24,6 +24,7 @@ frappe.ui.form.on("Salary Component", { refresh: function (frm) { <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD if (!frm.doc.__islocal) { frm.add_custom_button(__("Salary Structure"), () => { @@ -31,6 +32,9 @@ frappe.ui.form.on("Salary Component", { }, __("Create")); ======= hrms.payroll_common.get_autocompletions_for_condition_and_formula(frm); +======= + hrms.payroll_common.set_autocompletions_for_condition_and_formula(frm); +>>>>>>> c6817a203 (feat: setup autocompletions for salary structure fields) if (!frm.doc.__islocal) { frm.trigger("add_update_structure_button"); diff --git a/hrms/payroll/doctype/salary_structure/salary_structure.js b/hrms/payroll/doctype/salary_structure/salary_structure.js index a54ada62ee..1ac3fa9b72 100755 --- a/hrms/payroll/doctype/salary_structure/salary_structure.js +++ b/hrms/payroll/doctype/salary_structure/salary_structure.js @@ -300,6 +300,11 @@ cur_frm.cscript.validate = function(doc, cdt, cdn) { frappe.ui.form.on('Salary Detail', { + form_render: function(frm, cdt, cdn) { + const row = locals[cdt][cdn]; + hrms.payroll_common.set_autocompletions_for_condition_and_formula(frm, row); + }, + amount: function(frm) { calculate_totals(frm.doc); }, diff --git a/hrms/public/js/payroll_common.js b/hrms/public/js/payroll_common.js index 5291751b73..df1e68874e 100644 --- a/hrms/public/js/payroll_common.js +++ b/hrms/public/js/payroll_common.js @@ -1,5 +1,5 @@ hrms.payroll_common = { - get_autocompletions_for_condition_and_formula: function (frm) { + set_autocompletions_for_condition_and_formula: function (frm, child_row="") { const autocompletions = []; frappe.run_serially([ ...["Employee", "Salary Structure", "Salary Slip"].map((doctype) => @@ -26,12 +26,25 @@ hrms.payroll_common = { meta: __("Salary Component"), })) ); - frm.set_df_property( - "condition", - "autocompletions", - autocompletions - ); - frm.set_df_property("formula", "autocompletions", autocompletions); + + if (child_row) { + ["condition", "formula"].forEach((field) => { + frm.set_df_property( + child_row.parentfield, + "autocompletions", + autocompletions, + frm.doc.name, + field, + child_row.name + ); + }); + + frm.refresh_field(child_row.parentfield); + } else { + ["condition", "formula"].forEach((field) => { + frm.set_df_property(field, "autocompletions", autocompletions); + }); + } }); }, ]); From f626c4e38c460c98efc50c45fafc863104fddd19 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 5 Feb 2024 13:25:00 +0530 Subject: [PATCH 17/29] fix: sanitize structure fields + reset fields on `on_update` for readability (cherry picked from commit 09c1d5039cb11561b34c560fa4c71dde0c7344ba) --- .../salary_structure/salary_structure.py | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/hrms/payroll/doctype/salary_structure/salary_structure.py b/hrms/payroll/doctype/salary_structure/salary_structure.py index bd973cd482..cbbfc4dba6 100644 --- a/hrms/payroll/doctype/salary_structure/salary_structure.py +++ b/hrms/payroll/doctype/salary_structure/salary_structure.py @@ -11,18 +11,25 @@ import erpnext +from hrms.payroll.utils import sanitize_expression + class SalaryStructure(Document): + def before_validate(self): + self.sanitize_condition_and_formula_fields() + def validate(self): self.set_missing_values() self.validate_amount() - self.strip_condition_and_formula_fields() self.validate_max_benefits_with_flexi() self.validate_component_based_on_tax_slab() self.validate_payment_days_based_dependent_component() self.validate_timesheet_component() self.validate_formula_setup() + def on_update(self): + self.reset_condition_and_formula_fields() + def validate_formula_setup(self): for table in ["earnings", "deductions"]: for row in self.get(table): @@ -121,15 +128,22 @@ def validate_timesheet_component(self): ) break - def strip_condition_and_formula_fields(self): - # remove whitespaces from condition and formula fields - for row in self.earnings: - row.condition = row.condition.strip() if row.condition else "" - row.formula = row.formula.strip() if row.formula else "" + def sanitize_condition_and_formula_fields(self): + for table in ("earnings", "deductions"): + for row in self.get(table): + row.condition = row.condition.strip() if row.condition else "" + row.formula = row.formula.strip() if row.formula else "" + row._condition, row.condition = row.condition, sanitize_expression(row.condition) + row._formula, row.formula = row.formula, sanitize_expression(row.formula) + + def reset_condition_and_formula_fields(self): + # set old values (allowing multiline strings for better readability in the doctype form) + for table in ("earnings", "deductions"): + for row in self.get(table): + row.condition = row._condition + row.formula = row._formula - for row in self.deductions: - row.condition = row.condition.strip() if row.condition else "" - row.formula = row.formula.strip() if row.formula else "" + self.db_update_all() def validate_max_benefits_with_flexi(self): have_a_flexi = False From 5d1d0303cbdc1c678cf85f1c31ad4c0e8a1fcae3 Mon Sep 17 00:00:00 2001 From: krantheman Date: Mon, 5 Feb 2024 18:08:59 +0530 Subject: [PATCH 18/29] fix: accounts validation (cherry picked from commit 4f64abe30f00df9580cf093e3b101557c59b0d67) --- .../doctype/salary_component/salary_component.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py index a1bec30786..c6a00eeb93 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.py +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -16,6 +16,12 @@ def before_validate(self): def validate(self): self.validate_abbr() + if not (self.statistical_component or (self.accounts and all(d.account for d in self.accounts))): + frappe.msgprint( + title=_("Warning"), + msg=_("Accounts not set for Salary Component {0}").format(self.name), + indicator="orange", + ) def on_update(self): # set old values (allowing multiline strings for better readability in the doctype form) @@ -24,14 +30,6 @@ def on_update(self): if self._formula != self.formula: self.db_set("formula", self._formula) - def after_insert(self): - if not (self.statistical_component or (self.accounts and all(d.account for d in self.accounts))): - frappe.msgprint( - title=_("Warning"), - msg=_("Accounts not set for Salary Component {0}").format(self.name), - indicator="orange", - ) - def clear_cache(self): from hrms.payroll.doctype.salary_slip.salary_slip import ( SALARY_COMPONENT_VALUES, From bf962676599a9e88effee0171d3079cdc844c6ae Mon Sep 17 00:00:00 2001 From: krantheman Date: Mon, 5 Feb 2024 19:01:17 +0530 Subject: [PATCH 19/29] feat: show Salary Structures to be updated in confirm dialog (cherry picked from commit c64adeb93a9be5c0b3a0cfcb1a19c4f3e7ff7513) --- .../salary_component/salary_component.js | 54 +++++++++++-------- .../salary_component/salary_component.py | 13 +++++ 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index b6d509c240..eb0351c0b1 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -179,28 +179,38 @@ frappe.ui.form.on("Salary Component", { frm.add_custom_button( __("Sync {0}", [df]), function () { - frappe.confirm( - __("Update {0} for all existing Salary Structures?", [df]), - () => { - frappe - .call({ - method: "update_salary_structures", - doc: frm.doc, - args: { - field: df.toLowerCase(), - value: frm.get_field(df.toLowerCase()).value, - }, - }) - .then((r) => { - if (!r.exc) { - frappe.show_alert({ - message: __("Salary Structures updated successfully"), - indicator: "green", - }); - } - }); - } - ); + frappe + .call({ + method: "get_structures_to_be_updated", + doc: frm.doc, + }) + .then((res) => { + let msg = __( + "{0} will be updated for the following Salary Structures: {1}.", + [df, frappe.utils.comma_and(res.message.map((d) => d.bold()))] + ); + msg += "
"; + msg += __("Are you sure you want to proceed?"); + frappe.confirm(msg, () => { + frappe + .call({ + method: "update_salary_structures", + doc: frm.doc, + args: { + field: df.toLowerCase(), + value: frm.get_field(df.toLowerCase()).value, + }, + }) + .then((r) => { + if (!r.exc) { + frappe.show_alert({ + message: __("Salary Structures updated successfully"), + indicator: "green", + }); + } + }); + }); + }); }, __("Update Salary Structures") ); diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py index c6a00eeb93..50a898afee 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.py +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -53,6 +53,19 @@ def validate_abbr(self): filters={"name": ["!=", self.name]}, ) + @frappe.whitelist() + def get_structures_to_be_updated(self): + SalaryStructure = frappe.qb.DocType("Salary Structure") + SalaryDetail = frappe.qb.DocType("Salary Detail") + return ( + frappe.qb.from_(SalaryStructure) + .inner_join(SalaryDetail) + .on(SalaryStructure.name == SalaryDetail.parent) + .select(SalaryStructure.name) + .where((SalaryDetail.salary_component == self.name) & (SalaryStructure.docstatus != 2)) + .run(pluck=True) + ) + @frappe.whitelist() def update_salary_structures(self, field, value): SalaryDetail = frappe.qb.DocType("Salary Detail") From 6a47f1fac1cd77729f0f81b21d76d20395291332 Mon Sep 17 00:00:00 2001 From: krantheman Date: Mon, 5 Feb 2024 19:50:33 +0530 Subject: [PATCH 20/29] refactor: use doc objects to update salary details (cherry picked from commit b929fb8869e5c2d144633a061cc81644ca4a37e0) --- .../salary_component/salary_component.js | 1 + .../salary_component/salary_component.py | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index eb0351c0b1..c2445d6565 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -197,6 +197,7 @@ frappe.ui.form.on("Salary Component", { method: "update_salary_structures", doc: frm.doc, args: { + structures: res.message, field: df.toLowerCase(), value: frm.get_field(df.toLowerCase()).value, }, diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py index 50a898afee..c20c007bcf 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.py +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -1,6 +1,8 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import copy + import frappe from frappe import _ from frappe.model.document import Document @@ -67,11 +69,12 @@ def get_structures_to_be_updated(self): ) @frappe.whitelist() - def update_salary_structures(self, field, value): - SalaryDetail = frappe.qb.DocType("Salary Detail") - SalaryStructure = frappe.qb.DocType("Salary Structure") - frappe.qb.update(SalaryDetail).inner_join(SalaryStructure).on( - SalaryDetail.parent == SalaryStructure.name - ).set(SalaryDetail[field], value).where( - (SalaryDetail.salary_component == self.name) & (SalaryStructure.docstatus != 2) - ).run() + def update_salary_structures(self, structures, field, value): + for structure in structures: + salary_detail = frappe.get_last_doc( + "Salary Detail", filters={"parent": structure, "salary_component": self.name} + ) + salary_detail._doc_before_save = copy.deepcopy(salary_detail) + salary_detail.db_set(field, value) + salary_detail.db_update() + salary_detail.save_version() From b239dd00000caec489d0c8dac40530f35e7801d0 Mon Sep 17 00:00:00 2001 From: krantheman Date: Mon, 5 Feb 2024 19:52:22 +0530 Subject: [PATCH 21/29] chore: move validate_account to a function (cherry picked from commit 060423f58508c4d70e4706e276c0952fc1feec36) --- .../doctype/salary_component/salary_component.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py index c20c007bcf..17f31361f1 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.py +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -18,12 +18,7 @@ def before_validate(self): def validate(self): self.validate_abbr() - if not (self.statistical_component or (self.accounts and all(d.account for d in self.accounts))): - frappe.msgprint( - title=_("Warning"), - msg=_("Accounts not set for Salary Component {0}").format(self.name), - indicator="orange", - ) + self.validate_accounts() def on_update(self): # set old values (allowing multiline strings for better readability in the doctype form) @@ -55,6 +50,14 @@ def validate_abbr(self): filters={"name": ["!=", self.name]}, ) + def validate_accounts(self): + if not (self.statistical_component or (self.accounts and all(d.account for d in self.accounts))): + frappe.msgprint( + title=_("Warning"), + msg=_("Accounts not set for Salary Component {0}").format(self.name), + indicator="orange", + ) + @frappe.whitelist() def get_structures_to_be_updated(self): SalaryStructure = frappe.qb.DocType("Salary Structure") From 907eb93efab2cacdc361b499f589634a0fe79215 Mon Sep 17 00:00:00 2001 From: krantheman Date: Mon, 5 Feb 2024 19:59:38 +0530 Subject: [PATCH 22/29] feat: use form links for structures in confirm dialog (cherry picked from commit 31f7f029ed1f4052dcf67c75a737355872a0a796) --- .../doctype/salary_component/salary_component.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index c2445d6565..3cdc68534d 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -187,7 +187,16 @@ frappe.ui.form.on("Salary Component", { .then((res) => { let msg = __( "{0} will be updated for the following Salary Structures: {1}.", - [df, frappe.utils.comma_and(res.message.map((d) => d.bold()))] + [ + df, + frappe.utils.comma_and( + res.message.map((d) => + frappe.utils + .get_form_link("Salary Structure", d, true) + .bold() + ) + ), + ] ); msg += "
"; msg += __("Are you sure you want to proceed?"); From 7a54e4e38327487737aab3a85db3adbf7477dd34 Mon Sep 17 00:00:00 2001 From: krantheman Date: Mon, 5 Feb 2024 21:12:17 +0530 Subject: [PATCH 23/29] feat: show alert in case of no Salary Structures to be updated (cherry picked from commit 818e97665c546123840a35d4693a9424a9c6ea76) --- .../salary_component/salary_component.js | 82 +++++++++++-------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index 3cdc68534d..e0eeff8b12 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -184,42 +184,18 @@ frappe.ui.form.on("Salary Component", { method: "get_structures_to_be_updated", doc: frm.doc, }) - .then((res) => { - let msg = __( - "{0} will be updated for the following Salary Structures: {1}.", - [ - df, - frappe.utils.comma_and( - res.message.map((d) => - frappe.utils - .get_form_link("Salary Structure", d, true) - .bold() - ) + .then((r) => { + if (r.message.length) + frm.events.update_salary_structures(frm, df, r.message); + else + frappe.msgprint({ + message: __( + "Salary Component {0} is currently not utilized by any Salary Structure.", + [frm.doc.name.bold()] ), - ] - ); - msg += "
"; - msg += __("Are you sure you want to proceed?"); - frappe.confirm(msg, () => { - frappe - .call({ - method: "update_salary_structures", - doc: frm.doc, - args: { - structures: res.message, - field: df.toLowerCase(), - value: frm.get_field(df.toLowerCase()).value, - }, - }) - .then((r) => { - if (!r.exc) { - frappe.show_alert({ - message: __("Salary Structures updated successfully"), - indicator: "green", - }); - } - }); - }); + title: __("No Salary Structures"), + indicator: "orange", + }); }); }, __("Update Salary Structures") @@ -229,6 +205,42 @@ frappe.ui.form.on("Salary Component", { ======= }, + update_salary_structures: function (frm, df, structures) { + let msg = __( + "{0} will be updated for the following Salary Structures: {1}.", + [ + df, + frappe.utils.comma_and( + structures.map((d) => + frappe.utils.get_form_link("Salary Structure", d, true).bold() + ) + ), + ] + ); + msg += "
"; + msg += __("Are you sure you want to proceed?"); + frappe.confirm(msg, () => { + frappe + .call({ + method: "update_salary_structures", + doc: frm.doc, + args: { + structures: structures, + field: df.toLowerCase(), + value: frm.get_field(df.toLowerCase()).value, + }, + }) + .then((r) => { + if (!r.exc) { + frappe.show_alert({ + message: __("Salary Structures updated successfully"), + indicator: "green", + }); + } + }); + }); + }, + create_salary_structure: function (frm) { frappe.model.with_doctype("Salary Structure", () => { const salary_structure = frappe.model.get_new_doc("Salary Structure"); From e2aff3e8b0d9e133eea60b9d0eb09b2e4a33650d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 6 Feb 2024 14:31:50 +0530 Subject: [PATCH 24/29] chore: fix message (cherry picked from commit f7cb82e1d34705513d09c04d8251869d4f7bc8fa) --- hrms/payroll/doctype/salary_component/salary_component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index e0eeff8b12..931cac42c7 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -190,7 +190,7 @@ frappe.ui.form.on("Salary Component", { else frappe.msgprint({ message: __( - "Salary Component {0} is currently not utilized by any Salary Structure.", + "Salary Component {0} is currently not used in any Salary Structure.", [frm.doc.name.bold()] ), title: __("No Salary Structures"), From 74d4100529f4fb0fd962c58109751c336079ddcf Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 6 Feb 2024 14:32:20 +0530 Subject: [PATCH 25/29] chore: enable track changes for salary structure (cherry picked from commit f4d4e6c98a430bdf0af5b7e7b9cea9dfdbab52a2) --- hrms/payroll/doctype/salary_structure/salary_structure.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hrms/payroll/doctype/salary_structure/salary_structure.json b/hrms/payroll/doctype/salary_structure/salary_structure.json index 2e8b748b44..59dfc65ea9 100644 --- a/hrms/payroll/doctype/salary_structure/salary_structure.json +++ b/hrms/payroll/doctype/salary_structure/salary_structure.json @@ -240,7 +240,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2023-12-12 14:11:22.774017", + "modified": "2024-02-06 13:06:59.579135", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Structure", @@ -281,5 +281,6 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "states": [] + "states": [], + "track_changes": 1 } \ No newline at end of file From 26f6b75a0ebe2c91021e400f7fa334f694001dd9 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 6 Feb 2024 14:33:15 +0530 Subject: [PATCH 26/29] fix: save formula/condition changes in version - get salary structures linked to component if not provided (cherry picked from commit dae59b9f49fae43fed38843df7d6dd6dec3b333e) --- .../salary_component/salary_component.py | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.py b/hrms/payroll/doctype/salary_component/salary_component.py index 17f31361f1..df5371a0f9 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.py +++ b/hrms/payroll/doctype/salary_component/salary_component.py @@ -72,12 +72,26 @@ def get_structures_to_be_updated(self): ) @frappe.whitelist() - def update_salary_structures(self, structures, field, value): + def update_salary_structures(self, field, value, structures=None): + if not structures: + structures = self.get_structures_to_be_updated() + for structure in structures: - salary_detail = frappe.get_last_doc( - "Salary Detail", filters={"parent": structure, "salary_component": self.name} + salary_structure = frappe.get_doc("Salary Structure", structure) + # this is only used for versioning and we do not want + # to make separate db calls by using load_doc_before_save + # which proves to be expensive while doing bulk replace + salary_structure._doc_before_save = copy.deepcopy(salary_structure) + + salary_detail_row = next( + (d for d in salary_structure.get(f"{self.type.lower()}s") if d.salary_component == self.name), + None, ) - salary_detail._doc_before_save = copy.deepcopy(salary_detail) - salary_detail.db_set(field, value) - salary_detail.db_update() - salary_detail.save_version() + salary_detail_row.set(field, value) + salary_structure.db_update_all() + salary_structure.flags.updater_reference = { + "doctype": self.doctype, + "docname": self.name, + "label": _("via Salary Component sync"), + } + salary_structure.save_version() From d0dca02f2a3c44ffbd030940b0515da48095d530 Mon Sep 17 00:00:00 2001 From: krantheman Date: Tue, 6 Feb 2024 15:28:23 +0530 Subject: [PATCH 27/29] feat: add Salary Structure Assignment to autocompletions (cherry picked from commit ea2da81865511ccb066659a158a473f357fe86c1) --- hrms/public/js/payroll_common.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hrms/public/js/payroll_common.js b/hrms/public/js/payroll_common.js index df1e68874e..8e2ff1f207 100644 --- a/hrms/public/js/payroll_common.js +++ b/hrms/public/js/payroll_common.js @@ -1,8 +1,16 @@ hrms.payroll_common = { - set_autocompletions_for_condition_and_formula: function (frm, child_row="") { + set_autocompletions_for_condition_and_formula: function ( + frm, + child_row = "" + ) { const autocompletions = []; frappe.run_serially([ - ...["Employee", "Salary Structure", "Salary Slip"].map((doctype) => + ...[ + "Employee", + "Salary Structure", + "Salary Structure Assignment", + "Salary Slip", + ].map((doctype) => frappe.model.with_doctype(doctype, () => { autocompletions.push( ...frappe.get_meta(doctype).fields.map((f) => ({ From 719951184d0bba31e0466d6dedbc6290cad28936 Mon Sep 17 00:00:00 2001 From: krantheman Date: Tue, 6 Feb 2024 15:56:19 +0530 Subject: [PATCH 28/29] feat: increase base/variable weight in autocompletions (cherry picked from commit a7c2ce8250daed15ce093ff06545b77b938ba1fa) --- hrms/public/js/payroll_common.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hrms/public/js/payroll_common.js b/hrms/public/js/payroll_common.js index 8e2ff1f207..2f6300dc8c 100644 --- a/hrms/public/js/payroll_common.js +++ b/hrms/public/js/payroll_common.js @@ -15,7 +15,7 @@ hrms.payroll_common = { autocompletions.push( ...frappe.get_meta(doctype).fields.map((f) => ({ value: f.fieldname, - score: 9, + score: 8, meta: __("{0} Field", [doctype]), })) ); @@ -30,11 +30,19 @@ hrms.payroll_common = { autocompletions.push( ...salary_components.map((d) => ({ value: d.salary_component_abbr, - score: 10, + score: 9, meta: __("Salary Component"), })) ); + autocompletions.push( + ...["base", "variable"].map((d) => ({ + value: d, + score: 10, + meta: __("Salary Structure Assignment field"), + })) + ); + if (child_row) { ["condition", "formula"].forEach((field) => { frm.set_df_property( From 51e41a92980a99f819dfa27cbf03d1dc672671da Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 6 Feb 2024 17:26:21 +0530 Subject: [PATCH 29/29] chore: fix conflicts --- .../salary_component/salary_component.js | 100 ------------------ 1 file changed, 100 deletions(-) diff --git a/hrms/payroll/doctype/salary_component/salary_component.js b/hrms/payroll/doctype/salary_component/salary_component.js index 931cac42c7..b1a713c884 100644 --- a/hrms/payroll/doctype/salary_component/salary_component.js +++ b/hrms/payroll/doctype/salary_component/salary_component.js @@ -23,18 +23,7 @@ frappe.ui.form.on("Salary Component", { }, refresh: function (frm) { -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD - if (!frm.doc.__islocal) { - frm.add_custom_button(__("Salary Structure"), () => { - frm.trigger("create_salary_structure"); - }, __("Create")); -======= - hrms.payroll_common.get_autocompletions_for_condition_and_formula(frm); -======= hrms.payroll_common.set_autocompletions_for_condition_and_formula(frm); ->>>>>>> c6817a203 (feat: setup autocompletions for salary structure fields) if (!frm.doc.__islocal) { frm.trigger("add_update_structure_button"); @@ -45,17 +34,7 @@ frappe.ui.form.on("Salary Component", { }, __("Create") ); ->>>>>>> cbf31f93e (chore: commonify condition/formula autocompletions) } -======= - frm.trigger("setup_autocompletions"); -<<<<<<< HEAD ->>>>>>> 0625e7bc2 (feat: add autocompletion to code fields) -======= - if (!frm.doc.__islocal) { - frm.trigger("add_update_structure_button"); - } ->>>>>>> 5c5f91b36 (feat: add option to sync formula and condition for existing structures) }, is_flexible_benefit: function (frm) { @@ -98,82 +77,6 @@ frappe.ui.form.on("Salary Component", { } }, -<<<<<<< HEAD -<<<<<<< HEAD - create_salary_structure: function (frm) { - frappe.model.with_doctype("Salary Structure", () => { - const salary_structure = frappe.model.get_new_doc("Salary Structure"); - const salary_detail = frappe.model.add_child( - salary_structure, - frm.doc.type === "Earning" ? "earnings" : "deductions" - ); - salary_detail.salary_component = frm.doc.name; - frappe.set_route("Form", "Salary Structure", salary_structure.name); - }); -======= - setup_autocompletions: function (frm) { -<<<<<<< HEAD - frappe.db - .get_list("Salary Component", { fields: ["salary_component_abbr"] }) - .then((salary_components) => { - const autocompletions = salary_components.map((d) => ({ - value: d.salary_component_abbr, - score: 10, - meta: "Salary Component", - })); - frappe.db.get_doc("DocType", "Employee").then((employee_doc) => { - const employee_fields = employee_doc.fields.map((f) => ({ - value: f.fieldname, - score: 9, - meta: "Employee Field", - })); - autocompletions.push(...employee_fields); - frm.set_df_property("condition", "autocompletions", autocompletions); - frm.set_df_property("formula", "autocompletions", autocompletions); - }); - }); ->>>>>>> 0625e7bc2 (feat: add autocompletion to code fields) -======= - const autocompletions = []; - frappe.run_serially([ - ...["Employee", "Salary Structure", "Salary Slip"].map((doctype) => - frappe.model.with_doctype(doctype, () => { - autocompletions.push( - ...frappe.get_meta(doctype).fields.map((f) => ({ - value: f.fieldname, - score: 9, - meta: __("{0} Field", [doctype]), - })) - ); - }) - ), - () => { - frappe.db - .get_list("Salary Component", { - fields: ["salary_component_abbr"], - }) - .then((salary_components) => { - autocompletions.push( - ...salary_components.map((d) => ({ - value: d.salary_component_abbr, - score: 10, - meta: __("Salary Component"), - })) - ); - frm.set_df_property( - "condition", - "autocompletions", - autocompletions - ); - frm.set_df_property("formula", "autocompletions", autocompletions); - }); - }, - ]); ->>>>>>> 6c06213b6 (feat: add autocompletions for Salary Structure and Salary Slip fields) - }, - -======= ->>>>>>> cbf31f93e (chore: commonify condition/formula autocompletions) add_update_structure_button: function (frm) { for (const df of ["Condition", "Formula"]) { frm.add_custom_button( @@ -201,8 +104,6 @@ frappe.ui.form.on("Salary Component", { __("Update Salary Structures") ); } -<<<<<<< HEAD -======= }, update_salary_structures: function (frm, df, structures) { @@ -251,7 +152,6 @@ frappe.ui.form.on("Salary Component", { salary_detail.salary_component = frm.doc.name; frappe.set_route("Form", "Salary Structure", salary_structure.name); }); ->>>>>>> cbf31f93e (chore: commonify condition/formula autocompletions) }, });