From 8a995f28c9f943ad3ec523c43b9d2521e4fa6d44 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 22 Oct 2025 11:23:51 +0530 Subject: [PATCH 01/31] fix: sabb missed in the incorrect serial no valuation report (cherry picked from commit b50bac6788cba0110d14c8ffc05a871762d149b1) --- .../incorrect_serial_no_valuation.py | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py index 4d740bd829a..d3c5d2e8db5 100644 --- a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py +++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py @@ -18,16 +18,41 @@ def execute(filters=None): def get_data(filters): data = get_stock_ledger_entries(filters) - serial_nos_data = prepare_serial_nos(data) + bundles = get_bundles(data) + serial_nos_data = prepare_serial_nos(data, bundles) data = get_incorrect_serial_nos(serial_nos_data) return data -def prepare_serial_nos(data): +def get_bundles(data): + bundles = [d.serial_and_batch_bundle for d in data if d.serial_and_batch_bundle] + bundle_dict = frappe._dict() + serial_nos_data = frappe.get_all( + "Serial and Batch Entry", + fields=["parent", "serial_no", "incoming_rate", "qty"], + filters={"parent": ("in", bundles), "serial_no": ("is", "set")}, + ) + + for entry in serial_nos_data: + bundle_dict.setdefault(entry.parent, []).append(entry) + + return bundle_dict + + +def prepare_serial_nos(data, bundles): serial_no_wise_data = {} for row in data: - if not row.serial_nos: + if not row.serial_nos and not row.serial_and_batch_bundle: + continue + + if row.serial_and_batch_bundle: + for bundle in bundles.get(row.serial_and_batch_bundle, []): + sle = copy.deepcopy(row) + sle.serial_no = bundle.serial_no + sle.qty = bundle.qty + sle.valuation_rate = bundle.incoming_rate * (1 if sle.qty > 0 else -1) + serial_no_wise_data.setdefault(bundle.serial_no, []).append(sle) continue for serial_no in get_serial_nos(row.serial_nos): @@ -54,6 +79,9 @@ def get_incorrect_serial_nos(serial_nos_data): total_value.qty += total_dict.qty total_value.valuation_rate += total_dict.valuation_rate + if total_dict.qty == 0 and abs(total_dict.valuation_rate) == 0: + continue + result.append(total_dict) result.append({}) @@ -81,6 +109,7 @@ def get_stock_ledger_entries(report_filters): "voucher_no", "item_code", "serial_no as serial_nos", + "serial_and_batch_bundle", "actual_qty", "posting_date", "posting_time", @@ -89,7 +118,8 @@ def get_stock_ledger_entries(report_filters): "(stock_value_difference / actual_qty) as valuation_rate", ] - filters = {"serial_no": ("is", "set"), "is_cancelled": 0} + filters = {"is_cancelled": 0} + or_filters = {"serial_no": ("is", "set"), "serial_and_batch_bundle": ("is", "set")} if report_filters.get("item_code"): filters["item_code"] = report_filters.get("item_code") @@ -104,6 +134,7 @@ def get_stock_ledger_entries(report_filters): "Stock Ledger Entry", fields=fields, filters=filters, + or_filters=or_filters, order_by="posting_date asc, posting_time asc, creation asc", ) From 8b2be438972ccc02b44f5b21c1dbbe713a638567 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Fri, 17 Oct 2025 15:56:13 +0530 Subject: [PATCH 02/31] chore: remove print statement (cherry picked from commit 1ad4dc90664144b5a3ee17a28460f1b2ed4e7d2d) --- erpnext/manufacturing/report/bom_explorer/bom_explorer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py index 97c85502c98..de6dec9ebb8 100644 --- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py +++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py @@ -26,7 +26,6 @@ def get_exploded_items(bom, data, indent=0, qty=1): ) for item in exploded_items: - print(item.bom_no, indent) item["indent"] = indent data.append( { From 89d305ac9f8302ea03b2a07add54fc150e436902 Mon Sep 17 00:00:00 2001 From: Kerolles Fathy Date: Wed, 22 Oct 2025 11:13:13 +0300 Subject: [PATCH 03/31] Merge pull request #50153 from KerollesFathy/fix-address-error-when-create-transporter fix: Address title error when create new transporter on driver (cherry picked from commit 8efcf6cb38812226ca19bf340ea5115704b8473a) --- erpnext/setup/doctype/driver/driver.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/setup/doctype/driver/driver.js b/erpnext/setup/doctype/driver/driver.js index 0bd80edb921..35f8bff5874 100644 --- a/erpnext/setup/doctype/driver/driver.js +++ b/erpnext/setup/doctype/driver/driver.js @@ -24,6 +24,7 @@ frappe.ui.form.on("Driver", { transporter: function (frm, cdt, cdn) { // this assumes that supplier's address has same title as supplier's name + if (!frm.doc.transporter) return; frappe.db .get_doc("Address", null, { address_title: frm.doc.transporter }) .then((r) => { From 4988ff84dfce2566d8f7a3af8f3be238c71208b4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 08:34:11 +0000 Subject: [PATCH 04/31] fix(Task): make Timesheet-dependent fields no_copy (backport #50130) (#50196) * fix(Task): make Timesheet-dependent fields no_copy (cherry picked from commit 2a2e4b5423f9ced9d91a175301dcb3b13dd5515d) # Conflicts: # erpnext/projects/doctype/task/task.json * chore: resolve conflicts --------- Co-authored-by: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Co-authored-by: Mihir Kandoi --- erpnext/projects/doctype/task/task.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index cc9832b5845..8ff7fabc23e 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -245,6 +245,7 @@ "fieldname": "act_start_date", "fieldtype": "Date", "label": "Actual Start Date (via Timesheet)", + "no_copy": 1, "oldfieldname": "act_start_date", "oldfieldtype": "Date", "read_only": 1 @@ -253,6 +254,7 @@ "fieldname": "actual_time", "fieldtype": "Float", "label": "Actual Time in Hours (via Timesheet)", + "no_copy": 1, "read_only": 1 }, { @@ -263,6 +265,7 @@ "fieldname": "act_end_date", "fieldtype": "Date", "label": "Actual End Date (via Timesheet)", + "no_copy": 1, "oldfieldname": "act_end_date", "oldfieldtype": "Date", "read_only": 1 @@ -277,6 +280,7 @@ "fieldname": "total_costing_amount", "fieldtype": "Currency", "label": "Total Costing Amount (via Timesheet)", + "no_copy": 1, "oldfieldname": "actual_budget", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -290,6 +294,7 @@ "fieldname": "total_billing_amount", "fieldtype": "Currency", "label": "Total Billable Amount (via Timesheet)", + "no_copy": 1, "read_only": 1 }, { @@ -399,7 +404,7 @@ "is_tree": 1, "links": [], "max_attachments": 5, - "modified": "2024-01-08 16:00:41.296203", + "modified": "2025-10-16 08:39:12.214577", "modified_by": "Administrator", "module": "Projects", "name": "Task", @@ -429,4 +434,4 @@ "timeline_field": "project", "title_field": "subject", "track_seen": 1 -} \ No newline at end of file +} From 6c0694ff1707b2115fe22c7cd19aeddb44980c75 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 22 Oct 2025 19:17:43 +0530 Subject: [PATCH 05/31] fix: stock difference value for adjustment entry (cherry picked from commit fb4c7de86c107088dca8e7804d5845c9bc08c0ff) --- erpnext/stock/stock_ledger.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 54616149b37..2df4b257aa5 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -893,7 +893,11 @@ class update_entries_after: sle.stock_queue = json.dumps(self.wh_data.stock_queue) sle.stock_value_difference = stock_value_difference - if sle.is_adjustment_entry and flt(sle.qty_after_transaction, self.flt_precision) == 0: + if ( + sle.is_adjustment_entry + and flt(sle.qty_after_transaction, self.flt_precision) == 0 + and flt(sle.stock_value, self.currency_precision) != 0 + ): sle.stock_value_difference = ( get_stock_value_difference( sle.item_code, @@ -901,6 +905,7 @@ class update_entries_after: sle.posting_date, sle.posting_time, voucher_detail_no=sle.voucher_detail_no, + creation=sle.creation, ) * -1 ) @@ -2358,7 +2363,7 @@ def is_internal_transfer(sle): def get_stock_value_difference( - item_code, warehouse, posting_date, posting_time, voucher_no=None, voucher_detail_no=None + item_code, warehouse, posting_date, posting_time, voucher_no=None, voucher_detail_no=None, creation=None ): table = frappe.qb.DocType("Stock Ledger Entry") posting_datetime = get_combine_datetime(posting_date, posting_time) @@ -2366,12 +2371,7 @@ def get_stock_value_difference( query = ( frappe.qb.from_(table) .select(Sum(table.stock_value_difference).as_("value")) - .where( - (table.is_cancelled == 0) - & (table.item_code == item_code) - & (table.warehouse == warehouse) - & (table.posting_datetime <= posting_datetime) - ) + .where((table.is_cancelled == 0) & (table.item_code == item_code) & (table.warehouse == warehouse)) ) if voucher_detail_no: @@ -2380,6 +2380,14 @@ def get_stock_value_difference( elif voucher_no: query = query.where(table.voucher_no != voucher_no) + if creation: + query = query.where( + (table.posting_datetime < posting_datetime) + | ((table.posting_datetime == posting_datetime) & (table.creation < creation)) + ) + else: + query = query.where(table.posting_datetime <= posting_datetime) + difference_amount = query.run() return flt(difference_amount[0][0]) if difference_amount else 0 From 1a7092d7b665aacf3ee49a6ebe30063ce0968a26 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 23 Oct 2025 13:00:15 +0530 Subject: [PATCH 06/31] feat: Add posting date param for reverse GL entries (cherry picked from commit 38988bf7979d8f301a7d3262c7813d52e5dd854e) --- erpnext/accounts/general_ledger.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 7df3bce9b4a..f1947cf2338 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -648,6 +648,7 @@ def make_reverse_gl_entries( adv_adj=False, update_outstanding="Yes", partial_cancel=False, + posting_date=None ): """ Get original gl entries of the voucher @@ -745,6 +746,8 @@ def make_reverse_gl_entries( if immutable_ledger_enabled: new_gle["is_cancelled"] = 0 new_gle["posting_date"] = frappe.form_dict.get("posting_date") or getdate() + elif posting_date: + new_gle["posting_date"] = posting_date if new_gle["debit"] or new_gle["credit"]: make_entry(new_gle, adv_adj, "Yes") From db84df0cbda532fc9188f1a3d7cad864e2fc40a7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 23 Oct 2025 13:06:48 +0530 Subject: [PATCH 07/31] chore: Linting issues (cherry picked from commit 05e1a737f14cf4d3fca85ad81627f1e65cec20f5) --- erpnext/accounts/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index f1947cf2338..32fda83943b 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -648,7 +648,7 @@ def make_reverse_gl_entries( adv_adj=False, update_outstanding="Yes", partial_cancel=False, - posting_date=None + posting_date=None, ): """ Get original gl entries of the voucher From d8eddbfd03650592d7c54db99df3a501465227ad Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 23 Oct 2025 17:41:51 +0530 Subject: [PATCH 08/31] fix: set status to Draft for auto-created assets from Purchase Receipt (cherry picked from commit 20c2cb40d1acba45c9a73eb34f18a8205ad0c38c) # Conflicts: # erpnext/assets/doctype/asset/asset.py # erpnext/patches.txt --- erpnext/assets/doctype/asset/asset.py | 7 +++++++ erpnext/controllers/buying_controller.py | 1 + erpnext/patches.txt | 10 ++++++++++ .../v15_0/set_asset_status_if_not_already_set.py | 13 +++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 erpnext/patches/v15_0/set_asset_status_if_not_already_set.py diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index e35ebf6ed06..0c50b4460e4 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -131,6 +131,13 @@ class Asset(AccountsController): self.set_missing_values() self.validate_gross_and_purchase_amount() self.validate_finance_books() +<<<<<<< HEAD +======= + + def before_save(self): + self.total_asset_cost = self.net_purchase_amount + self.additional_asset_cost + self.status = self.get_status() +>>>>>>> 20c2cb40d1 (fix: set status to Draft for auto-created assets from Purchase Receipt) if not self.split_from: self.prepare_depreciation_data() diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index fb5cc130f0b..643fafb74ab 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -878,6 +878,7 @@ class BuyingController(SubcontractingController): "asset_category": item_data.get("asset_category"), "location": row.asset_location, "company": self.company, + "status": "Draft", "supplier": self.supplier, "purchase_date": self.posting_date, "calculate_depreciation": 0, diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0abf1b42ee4..dc083849dca 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -422,3 +422,13 @@ execute:frappe.db.set_single_value("Accounts Settings", "fetch_valuation_rate_fo erpnext.patches.v15_0.add_company_payment_gateway_account erpnext.patches.v15_0.update_uae_zero_rated_fetch erpnext.patches.v15_0.update_fieldname_in_accounting_dimension_filter +<<<<<<< HEAD +======= +erpnext.patches.v16_0.make_workstation_operating_components #1 +erpnext.patches.v16_0.set_reporting_currency +erpnext.patches.v16_0.set_posting_datetime_for_sabb_and_drop_indexes +erpnext.patches.v16_0.update_serial_no_reference_name +erpnext.patches.v16_0.rename_subcontracted_quantity +erpnext.patches.v16_0.add_new_stock_entry_types +erpnext.patches.v15_0.set_asset_status_if_not_already_set +>>>>>>> 20c2cb40d1 (fix: set status to Draft for auto-created assets from Purchase Receipt) diff --git a/erpnext/patches/v15_0/set_asset_status_if_not_already_set.py b/erpnext/patches/v15_0/set_asset_status_if_not_already_set.py new file mode 100644 index 00000000000..ac0af708de4 --- /dev/null +++ b/erpnext/patches/v15_0/set_asset_status_if_not_already_set.py @@ -0,0 +1,13 @@ +import frappe +from frappe.query_builder import DocType + + +def execute(): + Asset = DocType("Asset") + + query = ( + frappe.qb.update(Asset) + .set(Asset.status, "Draft") + .where((Asset.docstatus == 0) & ((Asset.status.isnull()) | (Asset.status == ""))) + ) + query.run() From 97147a484df66894e8e29b6e319be8c083eb585e Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 23 Oct 2025 19:09:30 +0530 Subject: [PATCH 09/31] fix: resolve conflicts --- erpnext/assets/doctype/asset/asset.py | 11 +++-------- erpnext/patches.txt | 9 --------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 0c50b4460e4..c4c5aad25ad 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -131,13 +131,6 @@ class Asset(AccountsController): self.set_missing_values() self.validate_gross_and_purchase_amount() self.validate_finance_books() -<<<<<<< HEAD -======= - - def before_save(self): - self.total_asset_cost = self.net_purchase_amount + self.additional_asset_cost - self.status = self.get_status() ->>>>>>> 20c2cb40d1 (fix: set status to Draft for auto-created assets from Purchase Receipt) if not self.split_from: self.prepare_depreciation_data() @@ -159,7 +152,9 @@ class Asset(AccountsController): ) self.validate_expected_value_after_useful_life() self.set_total_booked_depreciations() - self.total_asset_cost = self.gross_purchase_amount + + def before_save(self): + self.total_asset_cost = self.net_purchase_amount + self.additional_asset_cost self.status = self.get_status() def on_submit(self): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index dc083849dca..4e05b974d16 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -422,13 +422,4 @@ execute:frappe.db.set_single_value("Accounts Settings", "fetch_valuation_rate_fo erpnext.patches.v15_0.add_company_payment_gateway_account erpnext.patches.v15_0.update_uae_zero_rated_fetch erpnext.patches.v15_0.update_fieldname_in_accounting_dimension_filter -<<<<<<< HEAD -======= -erpnext.patches.v16_0.make_workstation_operating_components #1 -erpnext.patches.v16_0.set_reporting_currency -erpnext.patches.v16_0.set_posting_datetime_for_sabb_and_drop_indexes -erpnext.patches.v16_0.update_serial_no_reference_name -erpnext.patches.v16_0.rename_subcontracted_quantity -erpnext.patches.v16_0.add_new_stock_entry_types erpnext.patches.v15_0.set_asset_status_if_not_already_set ->>>>>>> 20c2cb40d1 (fix: set status to Draft for auto-created assets from Purchase Receipt) From a82fa8c26b59ac49f6bf9c431679c70dde288501 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 23 Oct 2025 20:45:08 +0530 Subject: [PATCH 10/31] fix: use correct field name --- erpnext/assets/doctype/asset/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index c4c5aad25ad..b87b3135944 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -154,7 +154,7 @@ class Asset(AccountsController): self.set_total_booked_depreciations() def before_save(self): - self.total_asset_cost = self.net_purchase_amount + self.additional_asset_cost + self.total_asset_cost = self.gross_purchase_amount + self.additional_asset_cost self.status = self.get_status() def on_submit(self): From ee5e4eccecef33660c037106842fa952d17217d6 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 23 Oct 2025 22:51:30 +0530 Subject: [PATCH 11/31] fix: set default value for as zero for additional asset cost --- erpnext/assets/doctype/asset/asset.json | 5 +++-- erpnext/assets/doctype/asset/asset.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 8e75762a92d..487f67669ff 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -518,6 +518,7 @@ "read_only": 1 }, { + "default": "0", "depends_on": "eval:doc.docstatus > 0", "fieldname": "additional_asset_cost", "fieldtype": "Currency", @@ -596,7 +597,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2025-05-20 00:44:06.229177", + "modified": "2025-10-23 22:43:33.634452", "modified_by": "Administrator", "module": "Assets", "name": "Asset", @@ -641,4 +642,4 @@ "states": [], "title_field": "asset_name", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index b87b3135944..929a60d0e6d 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -69,6 +69,7 @@ class Asset(AccountsController): default_finance_book: DF.Link | None department: DF.Link | None depr_entry_posting_status: DF.Literal["", "Successful", "Failed"] + depreciation_completed: DF.Check depreciation_method: DF.Literal["", "Straight Line", "Double Declining Balance", "Manual"] disposal_date: DF.Date | None finance_books: DF.Table[AssetFinanceBook] From e9f4a34d8dcd775c747bb1e5df3d5bfe154e25f3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 23 Oct 2025 23:49:50 +0530 Subject: [PATCH 12/31] fix: get valuation rate based of previous SLEs for material receipt (cherry picked from commit fa9ef6708f4c5450a39f770298a5901dfef9271a) --- erpnext/stock/doctype/stock_entry/stock_entry.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 4b28f6094c1..332f05fafd2 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -539,7 +539,7 @@ frappe.ui.form.on("Stock Entry", { const item = locals[cdt][cdn]; item.transfer_qty = flt(item.qty) * flt(item.conversion_factor); - const args = { + let args = { item_code: item.item_code, posting_date: frm.doc.posting_date, posting_time: frm.doc.posting_time, @@ -553,6 +553,10 @@ frappe.ui.form.on("Stock Entry", { allow_zero_valuation: 1, }; + if (item.batch_no && frm.doc.purpose == "Material Receipt") { + args.qty = Math.abs(args.qty) * -1; + } + if (item.item_code || item.serial_no) { frappe.call({ method: "erpnext.stock.utils.get_incoming_rate", From 82ca729e2b5e9b2b5d0dceb9338c3b30700ea3e4 Mon Sep 17 00:00:00 2001 From: Rehan Ansari Date: Mon, 6 Oct 2025 00:21:00 +0530 Subject: [PATCH 13/31] feat: add project filter to Delayed Tasks Summary report (cherry picked from commit 88097e78d269396f0dd8bd236ea4f068f3e14f6f) --- .../delayed_tasks_summary/delayed_tasks_summary.js | 6 ++++++ .../delayed_tasks_summary/delayed_tasks_summary.py | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js index ad90d049659..0f9427cd520 100644 --- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js +++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js @@ -3,6 +3,12 @@ frappe.query_reports["Delayed Tasks Summary"] = { filters: [ + { + fieldname: "project", + label: __("Project"), + fieldtype: "Link", + options: "Project", + }, { fieldname: "from_date", label: __("From Date"), diff --git a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py index dc3da259662..fe47cf7541e 100644 --- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py +++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py @@ -23,6 +23,7 @@ def get_data(filters): fields=[ "name", "subject", + "project", "exp_start_date", "exp_end_date", "status", @@ -56,7 +57,7 @@ def get_data(filters): def get_conditions(filters): conditions = frappe._dict() - keys = ["priority", "status"] + keys = ["priority", "status", "project"] for key in keys: if filters.get(key): conditions[key] = filters.get(key) @@ -89,6 +90,13 @@ def get_columns(): columns = [ {"fieldname": "name", "fieldtype": "Link", "label": _("Task"), "options": "Task", "width": 150}, {"fieldname": "subject", "fieldtype": "Data", "label": _("Subject"), "width": 200}, + { + "fieldname": "project", + "fieldtype": "Link", + "label": _("Project"), + "options": "Project", + "width": 150, + }, {"fieldname": "status", "fieldtype": "Data", "label": _("Status"), "width": 100}, {"fieldname": "priority", "fieldtype": "Data", "label": _("Priority"), "width": 80}, {"fieldname": "progress", "fieldtype": "Data", "label": _("Progress (%)"), "width": 120}, From 482a7962129adc915fd9261ada5f6dc98c76153c Mon Sep 17 00:00:00 2001 From: Rehan Ansari Date: Tue, 14 Oct 2025 23:38:17 +0530 Subject: [PATCH 14/31] fix: fiscal year overlap validation for company-specific years (cherry picked from commit 57aaf34d3e97b6fa0c6c9bc7ece94eec26940e92) --- erpnext/accounts/doctype/fiscal_year/fiscal_year.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py index 25aa3f30363..f97fcf1ec34 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py @@ -99,7 +99,7 @@ class FiscalYear(Document): ) overlap = False - if not self.get("companies") or not company_for_existing: + if not self.get("companies") and not company_for_existing: overlap = True for d in self.get("companies"): From d84a29e1a1b35ff6af9e5cad49388df28500332b Mon Sep 17 00:00:00 2001 From: Rehan Ansari Date: Wed, 15 Oct 2025 23:33:02 +0530 Subject: [PATCH 15/31] test: fiscal year overlap validation for company-specific years (cherry picked from commit 94ae0988549ff0e411d9157acbe84795e59708b0) --- .../doctype/fiscal_year/test_fiscal_year.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py index 8d437164015..6bdcf7b2067 100644 --- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py @@ -26,6 +26,35 @@ class TestFiscalYear(unittest.TestCase): self.assertRaises(frappe.exceptions.InvalidDates, fy.insert) + def test_company_fiscal_year_overlap(self): + for name in ["_Test Global FY 2001", "_Test Company FY 2001"]: + if frappe.db.exists("Fiscal Year", name): + frappe.delete_doc("Fiscal Year", name) + + global_fy = frappe.get_doc( + { + "doctype": "Fiscal Year", + "year": "_Test Global FY 2001", + "year_start_date": "2001-04-01", + "year_end_date": "2002-03-31", + } + ) + global_fy.insert() + + company_fy = frappe.get_doc( + { + "doctype": "Fiscal Year", + "year": "_Test Company FY 2001", + "year_start_date": "2001-01-01", + "year_end_date": "2001-12-31", + "companies": [{"company": "_Test Company"}], + } + ) + + company_fy.insert() + self.assertTrue(frappe.db.exists("Fiscal Year", global_fy.name)) + self.assertTrue(frappe.db.exists("Fiscal Year", company_fy.name)) + def test_record_generator(): test_records = [ From 4bb66179ab7581930a568f4eec6a25195b9ab6fe Mon Sep 17 00:00:00 2001 From: rehansari26 Date: Thu, 16 Oct 2025 11:04:48 +0530 Subject: [PATCH 16/31] test: replace get_doc with new_doc in fiscal year tests (cherry picked from commit d59e55fb08296f1258ee1ed80b514d251a53b815) --- .../doctype/fiscal_year/test_fiscal_year.py | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py index 6bdcf7b2067..9fb78bf41bf 100644 --- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py @@ -31,25 +31,17 @@ class TestFiscalYear(unittest.TestCase): if frappe.db.exists("Fiscal Year", name): frappe.delete_doc("Fiscal Year", name) - global_fy = frappe.get_doc( - { - "doctype": "Fiscal Year", - "year": "_Test Global FY 2001", - "year_start_date": "2001-04-01", - "year_end_date": "2002-03-31", - } - ) + global_fy = frappe.new_doc("Fiscal Year") + global_fy.year = "_Test Global FY 2001" + global_fy.year_start_date = "2001-04-01" + global_fy.year_end_date = "2002-03-31" global_fy.insert() - company_fy = frappe.get_doc( - { - "doctype": "Fiscal Year", - "year": "_Test Company FY 2001", - "year_start_date": "2001-01-01", - "year_end_date": "2001-12-31", - "companies": [{"company": "_Test Company"}], - } - ) + company_fy = frappe.new_doc("Fiscal Year") + company_fy.year = "_Test Company FY 2001" + company_fy.year_start_date = "2001-01-01" + company_fy.year_end_date = "2001-12-31" + company_fy.append("companies", {"company": "_Test Company"}) company_fy.insert() self.assertTrue(frappe.db.exists("Fiscal Year", global_fy.name)) From 1b227b8b4fbec3918f827b22214ec3317bca7faf Mon Sep 17 00:00:00 2001 From: Bhavan23 Date: Mon, 27 Oct 2025 08:21:42 +0000 Subject: [PATCH 17/31] fix(journal-entry): allow copy account currency when duplicating JV (cherry picked from commit 76748e45734a35883cdcb429ecf4dd702c8df017) --- .../doctype/journal_entry_account/journal_entry_account.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index b333e103b59..0b520ecd0e4 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -106,7 +106,6 @@ "fieldname": "account_currency", "fieldtype": "Link", "label": "Account Currency", - "no_copy": 1, "options": "Currency", "print_hide": 1, "read_only": 1 @@ -288,7 +287,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2025-10-20 17:46:47.344089", + "modified": "2025-10-27 13:48:32.805100", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", From 5798409f695d8adadd5a5bbba971708015658250 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 27 Oct 2025 15:09:22 +0530 Subject: [PATCH 18/31] fix: add message for missing purchase orders in production plan --- .../manufacturing/doctype/production_plan/production_plan.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index a1422bcb265..b7fcb45b7ec 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -724,6 +724,9 @@ class ProductionPlan(Document): if not wo_list: frappe.msgprint(_("No Work Orders were created")) + if not po_list: + frappe.msgprint(_("No Purchase Orders were created")) + def make_work_order_for_finished_goods(self, wo_list, default_warehouses): items_data = self.get_production_items() From 81f19b950d0384abc250573540b5fa8189984a36 Mon Sep 17 00:00:00 2001 From: ravibharathi656 Date: Fri, 24 Oct 2025 18:31:44 +0530 Subject: [PATCH 19/31] fix(asset depreciations and balances): showing opening entries (cherry picked from commit d3afa67be309be59c1e48d27d581d7926ddad0cb) --- .../asset_depreciations_and_balances.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index c196d52d744..57c81712cc1 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -273,6 +273,7 @@ def get_asset_value_adjustment_map_by_category(filters): AND a.company = %(company)s AND a.purchase_date <= %(to_date)s AND gle.account = aca.fixed_asset_account + AND gle.is_opening = 'No' GROUP BY a.asset_category """, {"from_date": filters.from_date, "to_date": filters.to_date, "company": filters.company}, @@ -543,6 +544,7 @@ def get_asset_value_adjustment_map(filters): AND a.company = %(company)s AND a.purchase_date <= %(to_date)s AND gle.account = aca.fixed_asset_account + AND gle.is_opening = 'No' GROUP BY a.name """, {"from_date": filters.from_date, "to_date": filters.to_date, "company": filters.company}, From 85bf9366b0c819e04b47a16d067f93c1d8d6e49b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 27 Oct 2025 13:16:01 +0530 Subject: [PATCH 20/31] fix: optimized the slow query to get the batchwise available qty (cherry picked from commit 9c215673090ea67374aa6312a59fa2f73a7c75f1) # Conflicts: # erpnext/stock/serial_batch_bundle.py --- erpnext/stock/serial_batch_bundle.py | 65 +++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index fbd30075be3..37d539026e9 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -731,19 +731,24 @@ class BatchNoValuation(DeprecatedBatchNoValuation): for ledger in entries: self.stock_value_differece[ledger.batch_no] += flt(ledger.incoming_rate) self.available_qty[ledger.batch_no] += flt(ledger.qty) - self.total_qty[ledger.batch_no] += flt(ledger.total_qty) + + entries = self.get_batch_wise_total_available_qty() + for row in entries: + self.total_qty[row.batch_no] += flt(row.total_qty) self.calculate_avg_rate_from_deprecarated_ledgers() self.calculate_avg_rate_for_non_batchwise_valuation() self.set_stock_value_difference() - def get_batch_no_ledgers(self) -> list[dict]: + def get_batch_wise_total_available_qty(self) -> list[dict]: + # Get total qty of each batch no from Serial and Batch Bundle without checking time condition if not self.batchwise_valuation_batches: return [] parent = frappe.qb.DocType("Serial and Batch Bundle") child = frappe.qb.DocType("Serial and Batch Entry") +<<<<<<< HEAD timestamp_condition = "" if self.sle.posting_date: if self.sle.posting_time is None: @@ -759,16 +764,14 @@ class BatchNoValuation(DeprecatedBatchNoValuation): == CombineDatetime(self.sle.posting_date, self.sle.posting_time) ) & (parent.creation < self.sle.creation) +======= +>>>>>>> 9c21567309 (fix: optimized the slow query to get the batchwise available qty) query = ( frappe.qb.from_(parent) .inner_join(child) .on(parent.name == child.parent) .select( child.batch_no, - Sum(Case().when(timestamp_condition, child.stock_value_difference).else_(0)).as_( - "incoming_rate" - ), - Sum(Case().when(timestamp_condition, child.qty).else_(0)).as_("qty"), Sum(child.qty).as_("total_qty"), ) .where( @@ -793,6 +796,56 @@ class BatchNoValuation(DeprecatedBatchNoValuation): return query.run(as_dict=True) + def get_batch_no_ledgers(self) -> list[dict]: + # Get batch wise stock value difference from Serial and Batch Bundle considering time condition + if not self.batchwise_valuation_batches: + return [] + + parent = frappe.qb.DocType("Serial and Batch Bundle") + child = frappe.qb.DocType("Serial and Batch Entry") + + timestamp_condition = "" + if self.sle.posting_datetime: + timestamp_condition = parent.posting_datetime < self.sle.posting_datetime + + if self.sle.creation: + timestamp_condition |= (parent.posting_datetime == self.sle.posting_datetime) & ( + parent.creation < self.sle.creation + ) + + query = ( + frappe.qb.from_(parent) + .inner_join(child) + .on(parent.name == child.parent) + .select( + child.batch_no, + Sum(child.stock_value_difference).as_("incoming_rate"), + Sum(child.qty).as_("qty"), + ) + .where( + (parent.warehouse == self.sle.warehouse) + & (parent.item_code == self.sle.item_code) + & (child.batch_no.isin(self.batchwise_valuation_batches)) + & (parent.docstatus == 1) + & (parent.is_cancelled == 0) + & (parent.type_of_transaction.isin(["Inward", "Outward"])) + ) + .for_update() + .groupby(child.batch_no) + ) + + # Important to exclude the current voucher detail no / voucher no to calculate the correct stock value difference + if self.sle.voucher_detail_no: + query = query.where(parent.voucher_detail_no != self.sle.voucher_detail_no) + elif self.sle.voucher_no: + query = query.where(parent.voucher_no != self.sle.voucher_no) + + query = query.where(parent.voucher_type != "Pick List") + if timestamp_condition: + query = query.where(timestamp_condition) + + return query.run(as_dict=True) + def prepare_batches(self): from erpnext.stock.utils import get_valuation_method From 2d7fde024d859a90bb17868b06fde17c2890ab57 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 27 Oct 2025 16:57:00 +0530 Subject: [PATCH 21/31] chore: fix conflicts --- erpnext/stock/serial_batch_bundle.py | 35 ++++++++++------------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 37d539026e9..7365d388fd0 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -748,24 +748,6 @@ class BatchNoValuation(DeprecatedBatchNoValuation): parent = frappe.qb.DocType("Serial and Batch Bundle") child = frappe.qb.DocType("Serial and Batch Entry") -<<<<<<< HEAD - timestamp_condition = "" - if self.sle.posting_date: - if self.sle.posting_time is None: - self.sle.posting_time = nowtime() - - timestamp_condition = CombineDatetime(parent.posting_date, parent.posting_time) < CombineDatetime( - self.sle.posting_date, self.sle.posting_time - ) - - if self.sle.creation: - timestamp_condition |= ( - CombineDatetime(parent.posting_date, parent.posting_time) - == CombineDatetime(self.sle.posting_date, self.sle.posting_time) - ) & (parent.creation < self.sle.creation) - -======= ->>>>>>> 9c21567309 (fix: optimized the slow query to get the batchwise available qty) query = ( frappe.qb.from_(parent) .inner_join(child) @@ -805,13 +787,20 @@ class BatchNoValuation(DeprecatedBatchNoValuation): child = frappe.qb.DocType("Serial and Batch Entry") timestamp_condition = "" - if self.sle.posting_datetime: - timestamp_condition = parent.posting_datetime < self.sle.posting_datetime + if self.sle.posting_date: + if self.sle.posting_time is None: + self.sle.posting_time = nowtime() + + timestamp_condition = CombineDatetime(parent.posting_date, parent.posting_time) < CombineDatetime( + self.sle.posting_date, self.sle.posting_time + ) if self.sle.creation: - timestamp_condition |= (parent.posting_datetime == self.sle.posting_datetime) & ( - parent.creation < self.sle.creation - ) + timestamp_condition |= ( + CombineDatetime(parent.posting_date, parent.posting_time) + == CombineDatetime(self.sle.posting_date, self.sle.posting_time) + ) & (parent.creation < self.sle.creation) + query = ( frappe.qb.from_(parent) From 2680f8543025c1e8b40d363dc8f3b7f345c2cd6b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 27 Oct 2025 17:06:45 +0530 Subject: [PATCH 22/31] chore: fix linters issue --- erpnext/stock/serial_batch_bundle.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 7365d388fd0..890899c7343 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -801,7 +801,6 @@ class BatchNoValuation(DeprecatedBatchNoValuation): == CombineDatetime(self.sle.posting_date, self.sle.posting_time) ) & (parent.creation < self.sle.creation) - query = ( frappe.qb.from_(parent) .inner_join(child) From 93b27868655e5eb5f07fc7acbc6ebcb2d93ea1ed Mon Sep 17 00:00:00 2001 From: Kavin <78342682+kavin0411@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:06:51 +0530 Subject: [PATCH 23/31] fix: Pass uom field name to update existing item qty (cherry picked from commit 23d69389ec7f8161a9d88c5e8cc97f771566da56) --- .../doctype/stock_reconciliation/stock_reconciliation.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index d8dd2a7560a..450bc01a67c 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -7,7 +7,10 @@ frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on("Stock Reconciliation", { setup(frm) { frm.ignore_doctypes_on_cancel_all = ["Serial and Batch Bundle"]; - frm.barcode_scanner = new erpnext.utils.BarcodeScanner({ frm }); + frm.barcode_scanner = new erpnext.utils.BarcodeScanner({ + frm: frm, + uom_field: "stock_uom", + }); }, onload: function (frm) { From 23006601b282513675dfc090894f038062a47b11 Mon Sep 17 00:00:00 2001 From: KerollesFathy Date: Wed, 17 Sep 2025 13:14:35 +0000 Subject: [PATCH 24/31] fix(accounts): update payment mode account retrieval to use namespaced function (cherry picked from commit 0af74aef001b645d2ef9c182ffac0dd2fbb651a0) # Conflicts: # erpnext/public/js/controllers/accounts.js --- erpnext/public/js/controllers/accounts.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index c7b08f1dc15..fb85092c2a0 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -237,10 +237,17 @@ erpnext.accounts.pos = { frappe.ui.form.on(doctype, { mode_of_payment: function(frm, cdt, cdn) { var d = locals[cdt][cdn]; +<<<<<<< HEAD get_payment_mode_account(frm, d.mode_of_payment, function(account){ frappe.model.set_value(cdt, cdn, 'account', account) }) } +======= + erpnext.accounts.pos.get_payment_mode_account(frm, d.mode_of_payment, function (account) { + frappe.model.set_value(cdt, cdn, "account", account); + }); + }, +>>>>>>> 0af74aef00 (fix(accounts): update payment mode account retrieval to use namespaced function) }); }, From 36983c97cb54f926e04be353868ca897d2657289 Mon Sep 17 00:00:00 2001 From: Diptanil Saha Date: Tue, 28 Oct 2025 08:06:10 +0530 Subject: [PATCH 25/31] chore: resolve conflict --- erpnext/public/js/controllers/accounts.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index fb85092c2a0..dec0f1c024d 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -237,17 +237,10 @@ erpnext.accounts.pos = { frappe.ui.form.on(doctype, { mode_of_payment: function(frm, cdt, cdn) { var d = locals[cdt][cdn]; -<<<<<<< HEAD - get_payment_mode_account(frm, d.mode_of_payment, function(account){ - frappe.model.set_value(cdt, cdn, 'account', account) - }) - } -======= erpnext.accounts.pos.get_payment_mode_account(frm, d.mode_of_payment, function (account) { frappe.model.set_value(cdt, cdn, "account", account); }); }, ->>>>>>> 0af74aef00 (fix(accounts): update payment mode account retrieval to use namespaced function) }); }, From b52b04a10c25b32b0c5b5779c25213cf3338e6fe Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:38:34 +0200 Subject: [PATCH 26/31] fix(Bank Transaction): make transaction ID non-unique (cherry picked from commit a2a41a0eaa570341333b5a0a006734310fc7ef43) # Conflicts: # erpnext/accounts/doctype/bank_transaction/bank_transaction.json --- .../doctype/bank_transaction/bank_transaction.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json index 02ff1dbf3e9..b5d7eb1d4b7 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json @@ -123,8 +123,7 @@ "fieldname": "transaction_id", "fieldtype": "Data", "label": "Transaction ID", - "read_only": 1, - "unique": 1 + "read_only": 1 }, { "allow_on_submit": 1, @@ -239,7 +238,11 @@ "grid_page_length": 50, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2025-10-14 11:53:45.908169", +======= + "modified": "2025-10-23 17:32:58.514807", +>>>>>>> a2a41a0eaa (fix(Bank Transaction): make transaction ID non-unique) "modified_by": "Administrator", "module": "Accounts", "name": "Bank Transaction", From 977f38500677c2e4baa8e25fff988bda0b386e45 Mon Sep 17 00:00:00 2001 From: Diptanil Saha Date: Tue, 28 Oct 2025 09:07:14 +0530 Subject: [PATCH 27/31] chore: resolve conflict --- .../accounts/doctype/bank_transaction/bank_transaction.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json index b5d7eb1d4b7..599a0604755 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json @@ -238,11 +238,7 @@ "grid_page_length": 50, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2025-10-14 11:53:45.908169", -======= "modified": "2025-10-23 17:32:58.514807", ->>>>>>> a2a41a0eaa (fix(Bank Transaction): make transaction ID non-unique) "modified_by": "Administrator", "module": "Accounts", "name": "Bank Transaction", From a7672530f43521ba94609af5aaf35d33ec93e271 Mon Sep 17 00:00:00 2001 From: ravibharathi656 Date: Fri, 24 Oct 2025 14:08:04 +0530 Subject: [PATCH 28/31] fix(gross profit): remove customer name from columns (cherry picked from commit 0009925af01aed0741b86e09391abcfd71c1416c) --- erpnext/accounts/report/gross_profit/gross_profit.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 367fe4f4fd2..256e4f959e8 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -231,6 +231,12 @@ def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_ group_columns = group_wise_columns.get(scrub(filters.group_by)) + # removing customer_name from group columns + customer_master_name = frappe.db.get_single_value("Selling Settings", "cust_master_name") + + if "customer_name" in group_columns and customer_master_name == "Customer Name": + group_columns.remove("customer_name") + for src in gross_profit_data.grouped_data: total_base_amount += src.base_amount or 0.00 total_buying_amount += src.buying_amount or 0.00 From 158e1b28a9682a6f5f73d1783f99d6a746b7efa0 Mon Sep 17 00:00:00 2001 From: ravibharathi656 Date: Mon, 27 Oct 2025 17:44:12 +0530 Subject: [PATCH 29/31] fix: avoid group columns mutation (cherry picked from commit 7ce81127d2321519d009bbee3f68bf181cd4cc57) --- erpnext/accounts/report/gross_profit/gross_profit.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 256e4f959e8..baf2da6ceea 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -233,9 +233,12 @@ def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_ # removing customer_name from group columns customer_master_name = frappe.db.get_single_value("Selling Settings", "cust_master_name") + supplier_master_name = frappe.db.get_single_value("Buying Settings", "supp_master_name") - if "customer_name" in group_columns and customer_master_name == "Customer Name": - group_columns.remove("customer_name") + if "customer_name" in group_columns and ( + supplier_master_name == "Supplier Name" and customer_master_name == "Customer Name" + ): + group_columns = [col for col in group_columns if col != "customer_name"] for src in gross_profit_data.grouped_data: total_base_amount += src.base_amount or 0.00 From 2a90bffb5f774bd82dd7e14f081ba0585c160bf4 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Thu, 16 Oct 2025 17:03:37 +0530 Subject: [PATCH 30/31] fix: recalculate amount based on allocated amount (cherry picked from commit 5a7a6a9bd5aa691cddeb832e484c9cbb4b09ca71) --- .../payment_reconciliation/payment_reconciliation.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index c5b815b5e61..75a86318fbb 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -385,6 +385,16 @@ frappe.ui.form.on("Payment Reconciliation Allocation", { // filter payment let payment = frm.doc.payments.filter((x) => x.reference_name == row.reference_name); + let amount = payment[0].amount; + for (const d of frm.doc.allocation) { + if (row.reference_name == d.reference_name && amount) { + if (d.allocated_amount <= amount) { + d.amount = amount; + amount -= d.allocated_amount; + } + } + } + frm.call({ doc: frm.doc, method: "calculate_difference_on_allocation_change", From 22767410d56602dc14077844538fbd2ca904b26d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 28 Oct 2025 13:46:07 +0530 Subject: [PATCH 31/31] fix: provision to find and fix incorrect serial and batch bundles (cherry picked from commit 10ad56060cc632899a2d254139038d2ba1024b5d) --- .../incorrect_serial_and_batch_bundle.js | 38 +++++----- .../incorrect_serial_and_batch_bundle.py | 76 ++++++++++++++++--- 2 files changed, 86 insertions(+), 28 deletions(-) diff --git a/erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.js b/erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.js index dccb543115e..eb146a7b447 100644 --- a/erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.js +++ b/erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.js @@ -24,24 +24,26 @@ frappe.query_reports["Incorrect Serial and Batch Bundle"] = { }, onload(report) { - report.page.add_inner_button(__("Remove SABB Entry"), () => { - let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows(); - let selected_rows = indexes.map((i) => frappe.query_report.data[i]); + report.page + .add_inner_button(__("Fix SABB Entry"), () => { + let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows(); + let selected_rows = indexes.map((i) => frappe.query_report.data[i]); - if (!selected_rows.length) { - frappe.throw(__("Please select a row to create a Reposting Entry")); - } else { - frappe.call({ - method: "erpnext.stock.report.incorrect_serial_and_batch_bundle.incorrect_serial_and_batch_bundle.remove_sabb_entry", - freeze: true, - args: { - selected_rows: selected_rows, - }, - callback: function (r) { - frappe.query_report.refresh(); - }, - }); - } - }); + if (!selected_rows.length) { + frappe.throw(__("Please select at least one row to fix")); + } else { + frappe.call({ + method: "erpnext.stock.report.incorrect_serial_and_batch_bundle.incorrect_serial_and_batch_bundle.fix_sabb_entries", + freeze: true, + args: { + selected_rows: selected_rows, + }, + callback: function (r) { + frappe.query_report.refresh(); + }, + }); + } + }) + .addClass("btn-primary"); }, }; diff --git a/erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.py b/erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.py index e65725f3c3d..0b27d697a4d 100644 --- a/erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.py +++ b/erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.py @@ -13,7 +13,10 @@ def execute(filters: dict | None = None): every time the report is refreshed or a filter is updated. """ columns = get_columns() - data = get_data(filters) + unlinked_bundles = get_unlinked_serial_batch_bundles(filters) or [] + linked_cancelled_bundles = get_linked_cancelled_sabb(filters) or [] + + data = unlinked_bundles + linked_cancelled_bundles return columns, data @@ -50,14 +53,17 @@ def get_columns() -> list[dict]: "fieldtype": "Data", "width": 200, }, + { + "label": _("Is Cancelled"), + "fieldname": "is_cancelled", + "fieldtype": "Check", + "width": 200, + }, ] -def get_data(filters) -> list[list]: - """Return data for the report. - - The report data is a list of rows, with each row being a list of cell values. - """ +def get_unlinked_serial_batch_bundles(filters) -> list[list]: + # SABB has not been linked to any SLE SABB = frappe.qb.DocType("Serial and Batch Bundle") SLE = frappe.qb.DocType("Stock Ledger Entry") @@ -77,6 +83,7 @@ def get_data(filters) -> list[list]: SABB.voucher_type, SABB.voucher_no, SABB.voucher_detail_no, + SABB.is_cancelled, ) .where( (SLE.serial_and_batch_bundle.isnull()) @@ -94,14 +101,63 @@ def get_data(filters) -> list[list]: return data +def get_linked_cancelled_sabb(filters): + # SABB has cancelled but voucher is not cancelled + + SABB = frappe.qb.DocType("Serial and Batch Bundle") + SLE = frappe.qb.DocType("Stock Ledger Entry") + + query = ( + frappe.qb.from_(SABB) + .inner_join(SLE) + .on(SABB.name == SLE.serial_and_batch_bundle) + .select( + SABB.name, + SABB.voucher_type, + SABB.voucher_no, + SABB.voucher_detail_no, + SABB.is_cancelled, + ) + .where( + (SLE.serial_and_batch_bundle.isnotnull()) + & (SABB.docstatus == 2) + & (SABB.is_cancelled == 1) + & (SLE.is_cancelled == 0) + ) + ) + + for field in filters: + query = query.where(SABB[field] == filters[field]) + + data = query.run(as_dict=1) + return data + + @frappe.whitelist() -def remove_sabb_entry(selected_rows): +def fix_sabb_entries(selected_rows): if isinstance(selected_rows, str): selected_rows = frappe.parse_json(selected_rows) for row in selected_rows: doc = frappe.get_doc("Serial and Batch Bundle", row.get("name")) - doc.cancel() - doc.delete() + if doc.is_cancelled == 0 and not frappe.db.get_value( + "Stock Ledger Entry", + {"serial_and_batch_bundle": doc.name, "is_cancelled": 0}, + "name", + ): + doc.db_set({"is_cancelled": 1, "docstatus": 2}) - frappe.msgprint(_("Selected Serial and Batch Bundle entries have been removed.")) + for row in doc.entries: + row.db_set("docstatus", 2) + + elif doc.is_cancelled == 1 and frappe.db.get_value( + "Stock Ledger Entry", + {"serial_and_batch_bundle": doc.name, "is_cancelled": 0}, + "name", + ): + doc.db_set({"is_cancelled": 0, "docstatus": 1}) + + for row in doc.entries: + row.db_set("docstatus", 1) + + frappe.msgprint(_("Selected Serial and Batch Bundle entries have been fixed."))