From 77f70b4c26752e449d4fc55e755d5c7535e8785e Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 10 Dec 2025 00:38:33 +0530 Subject: [PATCH 1/7] fix: make amount and percent field read only when distribute equally is enabled (cherry picked from commit 75999a7ae4d62927687a3448a9e2cd0e331d9210) --- erpnext/accounts/doctype/budget/budget.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/erpnext/accounts/doctype/budget/budget.js b/erpnext/accounts/doctype/budget/budget.js index 3ac7b8fe8f8..968700ede4c 100644 --- a/erpnext/accounts/doctype/budget/budget.js +++ b/erpnext/accounts/doctype/budget/budget.js @@ -42,6 +42,8 @@ frappe.ui.form.on("Budget", { ); } } + + toggle_distribution_fields(frm); }, budget_against: function (frm) { @@ -58,6 +60,12 @@ frappe.ui.form.on("Budget", { } }, + distribute_equally: function (frm) { + console.log("here"); + + toggle_distribution_fields(frm); + }, + set_null_value: function (frm) { if (frm.doc.budget_against == "Cost Center") { frm.set_value("project", null); @@ -111,3 +119,13 @@ frappe.ui.form.on("Budget Distribution", { } }, }); + +function toggle_distribution_fields(frm) { + const grid = frm.fields_dict.budget_distribution.grid; + + ["amount", "percent"].forEach((field) => { + grid.update_docfield_property(field, "read_only", frm.doc.distribute_equally); + }); + + grid.refresh(); +} From 7ea82ce2f0c67038c8d2924eff6ed09a7f09d4ba Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 10 Dec 2025 00:41:39 +0530 Subject: [PATCH 2/7] fix: add company-based filter to account field (cherry picked from commit 6a03fc6edecf93e9ff43e8bdf0724cb5c1f2905c) --- erpnext/accounts/doctype/budget/budget.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/accounts/doctype/budget/budget.js b/erpnext/accounts/doctype/budget/budget.js index 968700ede4c..4ab94ed0f55 100644 --- a/erpnext/accounts/doctype/budget/budget.js +++ b/erpnext/accounts/doctype/budget/budget.js @@ -12,6 +12,15 @@ frappe.ui.form.on("Budget", { }; }); + frm.set_query("account", function () { + return { + filters: { + is_group: 0, + company: frm.doc.company, + }, + }; + }); + erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); frappe.db.get_single_value("Accounts Settings", "use_legacy_budget_controller").then((value) => { if (value) { From 88af49f6c0fd17129fa1870c84ab66c62919284e Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 10 Dec 2025 00:46:03 +0530 Subject: [PATCH 3/7] fix: remove revise budget permission (cherry picked from commit d42aad18a71c0f40f25fc9d604b3f4fafe07fa9d) --- erpnext/accounts/doctype/budget/budget.js | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.js b/erpnext/accounts/doctype/budget/budget.js index 4ab94ed0f55..0d590b4817f 100644 --- a/erpnext/accounts/doctype/budget/budget.js +++ b/erpnext/accounts/doctype/budget/budget.js @@ -33,23 +33,13 @@ frappe.ui.form.on("Budget", { frm.trigger("toggle_reqd_fields"); if (!frm.doc.__islocal && frm.doc.docstatus == 1) { - let exception_role = await frappe.db.get_value( - "Company", - frm.doc.company, - "exception_budget_approver_role" + frm.add_custom_button( + __("Revise Budget"), + function () { + frm.events.revise_budget_action(frm); + }, + __("Actions") ); - - const role = exception_role.message.exception_budget_approver_role; - - if (role && frappe.user.has_role(role)) { - frm.add_custom_button( - __("Revise Budget"), - function () { - frm.events.revise_budget_action(frm); - }, - __("Actions") - ); - } } toggle_distribution_fields(frm); From 341ea89d16879b44f1aa3133ab6f2dcb16f2ab12 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 10 Dec 2025 02:19:29 +0530 Subject: [PATCH 4/7] fix: better manual budget distribution on update (cherry picked from commit 1c82f42fa81351805b744fa6685a7423a2cf29c4) --- erpnext/accounts/doctype/budget/budget.js | 2 - erpnext/accounts/doctype/budget/budget.py | 62 ++++++++++++++++------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.js b/erpnext/accounts/doctype/budget/budget.js index 0d590b4817f..6f8e0cdd43a 100644 --- a/erpnext/accounts/doctype/budget/budget.js +++ b/erpnext/accounts/doctype/budget/budget.js @@ -60,8 +60,6 @@ frappe.ui.form.on("Budget", { }, distribute_equally: function (frm) { - console.log("here"); - toggle_distribution_fields(frm); }, diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index d798da5b589..7280f2ee5fc 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -235,23 +235,43 @@ class Budget(Document): self.validate_distribution_totals() def allocate_budget(self): - if self.revision_of: + if self._should_skip_allocation(): + return + + if self._should_recalculate_manual_distribution(): + self._recalculate_manual_distribution() return if not self.should_regenerate_budget_distribution(): return - self.set("budget_distribution", []) + self._regenerate_distribution() - periods = self.get_budget_periods() - total_periods = len(periods) - row_percent = 100 / total_periods if total_periods else 0 + def _should_skip_allocation(self): + return self.revision_of and not self.distribute_equally - for start_date, end_date in periods: - row = self.append("budget_distribution", {}) - row.start_date = start_date - row.end_date = end_date - self.add_allocated_amount(row, row_percent) + def _should_recalculate_manual_distribution(self): + return ( + not self.distribute_equally + and bool(self.budget_distribution) + and self._is_only_budget_amount_changed() + ) + + def _is_only_budget_amount_changed(self): + old = self.get_doc_before_save() + if not old: + return False + + return ( + old.budget_amount != self.budget_amount + and old.distribution_frequency == self.distribution_frequency + and old.budget_start_date == self.budget_start_date + and old.budget_end_date == self.budget_end_date + ) + + def _recalculate_manual_distribution(self): + for row in self.budget_distribution: + row.amount = flt((row.percent / 100) * self.budget_amount, 3) def should_regenerate_budget_distribution(self): """Check whether budget distribution should be recalculated.""" @@ -265,7 +285,6 @@ class Budget(Document): "to_fiscal_year", "budget_amount", "distribution_frequency", - "distribute_equally", ] for field in changed_fields: if old_doc.get(field) != self.get(field): @@ -273,6 +292,19 @@ class Budget(Document): return bool(self.distribute_equally) + def _regenerate_distribution(self): + self.set("budget_distribution", []) + + periods = self.get_budget_periods() + total_periods = len(periods) + row_percent = 100 / total_periods if total_periods else 0 + + for start_date, end_date in periods: + row = self.append("budget_distribution", {}) + row.start_date = start_date + row.end_date = end_date + self.add_allocated_amount(row, row_percent) + def get_budget_periods(self): """Return list of (start_date, end_date) tuples based on frequency.""" frequency = self.distribution_frequency @@ -312,12 +344,8 @@ class Budget(Document): }.get(frequency, 1) def add_allocated_amount(self, row, row_percent): - if not self.distribute_equally: - row.amount = 0 - row.percent = 0 - else: - row.amount = flt(self.budget_amount * row_percent / 100, 3) - row.percent = flt(row_percent, 3) + row.amount = flt(self.budget_amount * row_percent / 100, 3) + row.percent = flt(row_percent, 3) def validate_distribution_totals(self): if self.should_regenerate_budget_distribution(): From e1d46d4dad6729504f841fb79ccbe592d9e94f27 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 10 Dec 2025 02:48:44 +0530 Subject: [PATCH 5/7] feat: show budget distribution total (cherry picked from commit f194ac093c0a4e75201ab8b48b83274a631153c9) --- erpnext/accounts/doctype/budget/budget.js | 15 +++++++++++ erpnext/accounts/doctype/budget/budget.json | 28 +++++++++++++++++++-- erpnext/accounts/doctype/budget/budget.py | 4 +++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.js b/erpnext/accounts/doctype/budget/budget.js index 6f8e0cdd43a..5128fdc9c92 100644 --- a/erpnext/accounts/doctype/budget/budget.js +++ b/erpnext/accounts/doctype/budget/budget.js @@ -55,6 +55,7 @@ frappe.ui.form.on("Budget", { frm.doc.budget_distribution.forEach((row) => { row.amount = flt((row.percent / 100) * frm.doc.budget_amount, 2); }); + set_total_budget_amount(frm); frm.refresh_field("budget_distribution"); } }, @@ -105,6 +106,8 @@ frappe.ui.form.on("Budget Distribution", { let row = frappe.get_doc(cdt, cdn); if (frm.doc.budget_amount) { row.percent = flt((row.amount / frm.doc.budget_amount) * 100, 2); + + set_total_budget_amount(frm); frm.refresh_field("budget_distribution"); } }, @@ -112,11 +115,23 @@ frappe.ui.form.on("Budget Distribution", { let row = frappe.get_doc(cdt, cdn); if (frm.doc.budget_amount) { row.amount = flt((row.percent / 100) * frm.doc.budget_amount, 2); + + set_total_budget_amount(frm); frm.refresh_field("budget_distribution"); } }, }); +function set_total_budget_amount(frm) { + let total = 0; + + (frm.doc.budget_distribution || []).forEach((row) => { + total += flt(row.amount); + }); + + frm.set_value("budget_distribution_total", total); +} + function toggle_distribution_fields(frm) { const grid = frm.fields_dict.budget_distribution.grid; diff --git a/erpnext/accounts/doctype/budget/budget.json b/erpnext/accounts/doctype/budget/budget.json index 8476a2831f0..960d62e4c99 100644 --- a/erpnext/accounts/doctype/budget/budget.json +++ b/erpnext/accounts/doctype/budget/budget.json @@ -25,6 +25,10 @@ "distribute_equally", "section_break_fpdt", "budget_distribution", + "section_break_wkqb", + "column_break_paum", + "column_break_nwor", + "budget_distribution_total", "section_break_6", "applicable_on_material_request", "action_if_annual_budget_exceeded_on_mr", @@ -222,7 +226,8 @@ }, { "fieldname": "section_break_fpdt", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "budget_distribution", @@ -303,13 +308,32 @@ "options": "Monthly\nQuarterly\nHalf-Yearly\nYearly", "read_only_depends_on": "eval: doc.revision_of", "reqd": 1 + }, + { + "fieldname": "section_break_wkqb", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_paum", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_nwor", + "fieldtype": "Column Break" + }, + { + "fieldname": "budget_distribution_total", + "fieldtype": "Currency", + "label": "Budget Distribution Total", + "no_copy": 1, + "read_only": 1 } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-11-19 17:00:00.648224", + "modified": "2025-12-10 02:35:01.197613", "modified_by": "Administrator", "module": "Accounts", "name": "Budget", diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index 7280f2ee5fc..39528da99db 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -53,6 +53,7 @@ class Budget(Document): budget_against: DF.Literal["", "Cost Center", "Project"] budget_amount: DF.Currency budget_distribution: DF.Table[BudgetDistribution] + budget_distribution_total: DF.Currency budget_end_date: DF.Date | None budget_start_date: DF.Date | None company: DF.Link @@ -230,6 +231,7 @@ class Budget(Document): def before_save(self): self.allocate_budget() + self.budget_distribution_total = sum(flt(row.amount) for row in self.budget_distribution) def on_update(self): self.validate_distribution_totals() @@ -305,6 +307,8 @@ class Budget(Document): row.end_date = end_date self.add_allocated_amount(row, row_percent) + self.budget_distribution_total = self.budget_amount + def get_budget_periods(self): """Return list of (start_date, end_date) tuples based on frequency.""" frequency = self.distribution_frequency From 07c83246d0e76ed40f8590dc92ff81be95e7a43b Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 10 Dec 2025 03:06:05 +0530 Subject: [PATCH 6/7] fix: patch to set budget distribution total (cherry picked from commit ed4c17d3a20df5c83eea2b67e4ec06dbc51183cb) # Conflicts: # erpnext/patches.txt --- erpnext/patches.txt | 6 ++++++ .../v16_0/populate_budget_distribution_total.py | 11 +++++++++++ 2 files changed, 17 insertions(+) create mode 100644 erpnext/patches/v16_0/populate_budget_distribution_total.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 74f6e8a275b..5eba5fe2711 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -449,3 +449,9 @@ erpnext.patches.v16_0.set_company_wise_warehouses erpnext.patches.v16_0.set_valuation_method_on_companies erpnext.patches.v15_0.migrate_old_item_wise_tax_detail_data_to_table erpnext.patches.v16_0.migrate_budget_records_to_new_structure +<<<<<<< HEAD +======= +erpnext.patches.v16_0.update_currency_exchange_settings_for_frankfurter +erpnext.patches.v16_0.migrate_account_freezing_settings_to_company +erpnext.patches.v16_0.populate_budget_distribution_total +>>>>>>> ed4c17d3a2 (fix: patch to set budget distribution total) diff --git a/erpnext/patches/v16_0/populate_budget_distribution_total.py b/erpnext/patches/v16_0/populate_budget_distribution_total.py new file mode 100644 index 00000000000..033fb968b4f --- /dev/null +++ b/erpnext/patches/v16_0/populate_budget_distribution_total.py @@ -0,0 +1,11 @@ +import frappe +from frappe.utils import flt + + +def execute(): + budgets = frappe.get_all("Budget", filters={"docstatus": ["in", [0, 1]]}, fields=["name"]) + + for b in budgets: + doc = frappe.get_doc("Budget", b.name) + total = sum(flt(row.amount) for row in doc.budget_distribution) + doc.db_set("budget_distribution_total", total, update_modified=False) From b84531d4eaa3994ed465444700080104c8e04673 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:59:14 +0530 Subject: [PATCH 7/7] fix: conflicts --- erpnext/patches.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 5eba5fe2711..83dca1839fb 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -449,9 +449,4 @@ erpnext.patches.v16_0.set_company_wise_warehouses erpnext.patches.v16_0.set_valuation_method_on_companies erpnext.patches.v15_0.migrate_old_item_wise_tax_detail_data_to_table erpnext.patches.v16_0.migrate_budget_records_to_new_structure -<<<<<<< HEAD -======= -erpnext.patches.v16_0.update_currency_exchange_settings_for_frankfurter -erpnext.patches.v16_0.migrate_account_freezing_settings_to_company erpnext.patches.v16_0.populate_budget_distribution_total ->>>>>>> ed4c17d3a2 (fix: patch to set budget distribution total)