From 24013ddf2727ef7049d81e81d8f49903c502628c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 30 Sep 2025 13:28:59 +0000 Subject: [PATCH 01/18] chore(release): Bumped to Version 15.81.0 # [15.81.0](https://github.com/frappe/erpnext/compare/v15.80.1...v15.81.0) (2025-09-30) ### Bug Fixes * add date filter for getting return invoice items ([bc77704](https://github.com/frappe/erpnext/commit/bc77704462d2d8090094ceb0fbfb0a63a8df576c)) * auto batch not set for raw materials in subcontracting receipt ([6c8e838](https://github.com/frappe/erpnext/commit/6c8e8384d5ba5c9b36804488cd4a21f513b98f36)) * **bank transaction:** change reference number to small text ([5a0bf7a](https://github.com/frappe/erpnext/commit/5a0bf7a5e10de4146e7e2f01a14bce264f7b0feb)) * convert with flt ([3975627](https://github.com/frappe/erpnext/commit/3975627b33f6ef68cc3a56be0b2b304ddc7e95bb)) * do not validate cost center in cancelled gl entry ([366bb82](https://github.com/frappe/erpnext/commit/366bb8269ca1ce91f493f5ade2bcffa58d4a4e69)) * don't recalculate stock_qty with conversion_factor ([c88a277](https://github.com/frappe/erpnext/commit/c88a277ad920d6282bf261d5bdfd74a538e554cf)) * include precision in validation ([abffbca](https://github.com/frappe/erpnext/commit/abffbcad9998a1a3c32620538f5f39c4011ffe0a)) * **payment entry:** trigger currency on account set ([84a2596](https://github.com/frappe/erpnext/commit/84a2596ce573dfc0d3932b27d811ae51c733c52c)) * remove item name to avoid overriding item row name ([d0e511f](https://github.com/frappe/erpnext/commit/d0e511fd2f119f27fab1b904ddc268e3fd4dd73c)) * set cost center in taxes if not set ([4dcd502](https://github.com/frappe/erpnext/commit/4dcd502b760d68e5cefe14b94e1df9f35b2c8775)) * **stock entry:** set expense account from company for manufacture ([50f9521](https://github.com/frappe/erpnext/commit/50f9521febe8b1a59bdc2f68da3d803994a74e64)) * update item details only in draft state ([30f6b53](https://github.com/frappe/erpnext/commit/30f6b53240a569a77264cfa2b63ff807f3e5b709)) * update subcontracted_quantity with set_value ([17e415d](https://github.com/frappe/erpnext/commit/17e415d03aac4522a03c906e96accbe31e277d90)) * use get_value instead of get_doc ([f706811](https://github.com/frappe/erpnext/commit/f706811757792b3d445bcadf2551509dc5c5e8e7)) * use sales_order from data instead of doc ([7ee8281](https://github.com/frappe/erpnext/commit/7ee82813fd81ceddbde3a5dde8836178a92feaff)) * use stock adjustment account if no expense account ([9c81f44](https://github.com/frappe/erpnext/commit/9c81f448c8a7aa22788b55f0eca01c912d78f35d)) * valuation rate for old batch ([37a03f1](https://github.com/frappe/erpnext/commit/37a03f10ab9d054a1e8df22c28516cef303f5124)) ### Features * add show zero value filter in profit and loss and balance sheet ([c32ad97](https://github.com/frappe/erpnext/commit/c32ad9711fe5e49e1c078ffa27568d226ea911cd)) * service expense account in the subcontracting receipt ([ba9b63a](https://github.com/frappe/erpnext/commit/ba9b63af4940644745af186ca98187b8459675d9)) ### Performance Improvements * reposting for backdated transactions ([cd5a836](https://github.com/frappe/erpnext/commit/cd5a8366850a0ff3c621488c3d94277d18f01a57)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6b2e9750ff3..6f041375e18 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.80.1" +__version__ = "15.81.0" def get_default_company(user=None): From c455e7390eb3c57b692619f243a5003c06b88236 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 1 Oct 2025 18:25:11 +0530 Subject: [PATCH 02/18] fix: too many writes on patch run (cherry picked from commit 44ff6ed6a14f6757f9e3e910173a72d152acdecf) (cherry picked from commit a2bf53ff0aea3debc69ef696351c4ce2a5dfdb17) --- .../patch_missing_buying_price_list_in_material_request.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py b/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py index 48f85335dd2..369be8469d5 100644 --- a/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py +++ b/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py @@ -9,5 +9,8 @@ def execute(): docs = frappe.get_all( "Material Request", filters={"buying_price_list": ["is", "not set"], "docstatus": 1}, pluck="name" ) + old_limit = frappe.db.MAX_WRITES_PER_TRANSACTION + frappe.db.MAX_WRITES_PER_TRANSACTION *= 4 for doc in docs: frappe.db.set_value("Material Request", doc, "buying_price_list", default_buying_price_list) + frappe.db.MAX_WRITES_PER_TRANSACTION = old_limit From e10ed89be5bc9ffa7fca46b370acdd062889189b Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 1 Oct 2025 18:39:29 +0530 Subject: [PATCH 03/18] fix: Add try-finally for setting buying price list (cherry picked from commit 35a8d02866260961ab224936817de9f74bc2f138) (cherry picked from commit e21baec246177c677b05fbb56f98719e743d7151) --- ...patch_missing_buying_price_list_in_material_request.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py b/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py index 369be8469d5..379a1a50983 100644 --- a/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py +++ b/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py @@ -11,6 +11,8 @@ def execute(): ) old_limit = frappe.db.MAX_WRITES_PER_TRANSACTION frappe.db.MAX_WRITES_PER_TRANSACTION *= 4 - for doc in docs: - frappe.db.set_value("Material Request", doc, "buying_price_list", default_buying_price_list) - frappe.db.MAX_WRITES_PER_TRANSACTION = old_limit + try: + for doc in docs: + frappe.db.set_value("Material Request", doc, "buying_price_list", default_buying_price_list) + finally: + frappe.db.MAX_WRITES_PER_TRANSACTION = old_limit From 4c1cada22210d55e10db4e856e68ab3013a1bbb1 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 1 Oct 2025 14:55:26 +0000 Subject: [PATCH 04/18] chore(release): Bumped to Version 15.81.1 ## [15.81.1](https://github.com/frappe/erpnext/compare/v15.81.0...v15.81.1) (2025-10-01) ### Bug Fixes * Add try-finally for setting buying price list ([e10ed89](https://github.com/frappe/erpnext/commit/e10ed89be5bc9ffa7fca46b370acdd062889189b)) * too many writes on patch run ([c455e73](https://github.com/frappe/erpnext/commit/c455e7390eb3c57b692619f243a5003c06b88236)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6f041375e18..32fb91616e4 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.81.0" +__version__ = "15.81.1" def get_default_company(user=None): From 65061cc459178769a4214e8f0db19aa5002a100c Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 3 Oct 2025 15:11:06 +0530 Subject: [PATCH 05/18] fix: failing patch (cherry picked from commit 41d1703e7c374694897e2470ef3fec9d5de45010) (cherry picked from commit 0dad1957c8cdf673eb96ea86c943874a8fe25dff) --- .../patch_missing_buying_price_list_in_material_request.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py b/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py index 379a1a50983..473c0d5fbc3 100644 --- a/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py +++ b/erpnext/patches/v15_0/patch_missing_buying_price_list_in_material_request.py @@ -9,10 +9,9 @@ def execute(): docs = frappe.get_all( "Material Request", filters={"buying_price_list": ["is", "not set"], "docstatus": 1}, pluck="name" ) - old_limit = frappe.db.MAX_WRITES_PER_TRANSACTION - frappe.db.MAX_WRITES_PER_TRANSACTION *= 4 + frappe.db.auto_commit_on_many_writes = 1 try: for doc in docs: frappe.db.set_value("Material Request", doc, "buying_price_list", default_buying_price_list) finally: - frappe.db.MAX_WRITES_PER_TRANSACTION = old_limit + frappe.db.auto_commit_on_many_writes = 0 From 161c1fb7a426cbef3b4b6b40192f82a046805da4 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 6 Oct 2025 03:54:42 +0000 Subject: [PATCH 06/18] chore(release): Bumped to Version 15.81.2 ## [15.81.2](https://github.com/frappe/erpnext/compare/v15.81.1...v15.81.2) (2025-10-06) ### Bug Fixes * failing patch ([65061cc](https://github.com/frappe/erpnext/commit/65061cc459178769a4214e8f0db19aa5002a100c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 32fb91616e4..05fedae1a26 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.81.1" +__version__ = "15.81.2" def get_default_company(user=None): From 92afae718548a994699fcd4a92be9592551ba854 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 6 Oct 2025 18:53:33 +0530 Subject: [PATCH 07/18] fix: do not consider draft bundles (cherry picked from commit a60f7eaf3a33ce988f1d7967d874ac98213ed7b7) (cherry picked from commit 75323fda0119e568da4b16c91b312b084a0d9ef5) --- erpnext/stock/serial_batch_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index d4defccc452..08cacfe3bb2 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -1374,7 +1374,7 @@ def throw_negative_batch_validation(batch_no, qty): def get_batchwise_qty(voucher_type, voucher_no): bundles = frappe.get_all( "Serial and Batch Bundle", - filters={"voucher_no": voucher_no, "voucher_type": voucher_type}, + filters={"voucher_no": voucher_no, "voucher_type": voucher_type, "docstatus": (">", 0)}, pluck="name", ) if not bundles: From b017f07343cce81a4d03f392591b58d56824a9bd Mon Sep 17 00:00:00 2001 From: Kavin <78342682+kavin0411@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:49:28 +0530 Subject: [PATCH 08/18] fix: check is_rejected attribute (cherry picked from commit 2ac2e02b2ff59a345c011dd0c654841274c250aa) (cherry picked from commit bdf150bdf8bd2df036b23b80190e1312b9bd0add) --- erpnext/stock/serial_batch_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index d4defccc452..8c9f47e0806 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -1227,7 +1227,7 @@ class SerialBatchCreation: def create_batch(self): from erpnext.stock.doctype.batch.batch import make_batch - if self.is_rejected: + if hasattr(self, "is_rejected") and self.is_rejected: bundle = frappe.db.get_value( "Serial and Batch Bundle", { From 42b43b372ada776c20b21adf503c00d3741e719a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 6 Oct 2025 14:45:22 +0000 Subject: [PATCH 09/18] chore(release): Bumped to Version 15.81.3 ## [15.81.3](https://github.com/frappe/erpnext/compare/v15.81.2...v15.81.3) (2025-10-06) ### Bug Fixes * check is_rejected attribute ([b017f07](https://github.com/frappe/erpnext/commit/b017f07343cce81a4d03f392591b58d56824a9bd)) * do not consider draft bundles ([92afae7](https://github.com/frappe/erpnext/commit/92afae718548a994699fcd4a92be9592551ba854)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 05fedae1a26..d17928ba9c2 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.81.2" +__version__ = "15.81.3" def get_default_company(user=None): From e2e1ac0dba40fba1bbb94e6bd19e5099f784b946 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 7 Oct 2025 13:23:00 +0000 Subject: [PATCH 10/18] chore(release): Bumped to Version 15.82.0 # [15.82.0](https://github.com/frappe/erpnext/compare/v15.81.3...v15.82.0) (2025-10-07) ### Bug Fixes * **accounting:** ensure proper removal of advance references during unreconcillation ([825ccd3](https://github.com/frappe/erpnext/commit/825ccd34224d22487507f2d4f90797802887c15f)) * add default scrap warehouse in wo ([a5ed9fd](https://github.com/frappe/erpnext/commit/a5ed9fdc67986988388346dc97e832ecf3739ae6)) * Add try-finally for setting buying price list ([e21baec](https://github.com/frappe/erpnext/commit/e21baec246177c677b05fbb56f98719e743d7151)) * check is_rejected attribute ([bdf150b](https://github.com/frappe/erpnext/commit/bdf150bdf8bd2df036b23b80190e1312b9bd0add)) * clear asset custodian when asset take back from employee without assign to another employee ([f4cdb49](https://github.com/frappe/erpnext/commit/f4cdb4912676f811d3390f270c2d7b279e6c0bba)) * **Common Code:** fetch canonical URI from Code List (backport [#49882](https://github.com/frappe/erpnext/issues/49882)) ([#49884](https://github.com/frappe/erpnext/issues/49884)) ([2c0501b](https://github.com/frappe/erpnext/commit/2c0501b05f3a10316284879cc14e47a7327e000f)) * delete column dynamically based on the naming by ([040873a](https://github.com/frappe/erpnext/commit/040873a442c971a977412272b099b54c28a782ce)) * do not consider draft bundles ([75323fd](https://github.com/frappe/erpnext/commit/75323fda0119e568da4b16c91b312b084a0d9ef5)) * do not fetch disabled item tax template ([c42dcbe](https://github.com/frappe/erpnext/commit/c42dcbe739f9866c5e9d65504fe5def0b3a9290e)) * exclude opening entries ([ed1c270](https://github.com/frappe/erpnext/commit/ed1c2703985fa4642cb297ab839bd1f9fe8735e7)) * failing patch ([0dad195](https://github.com/frappe/erpnext/commit/0dad1957c8cdf673eb96ea86c943874a8fe25dff)) * get unconsumed qty as per BOM qty ([82386b1](https://github.com/frappe/erpnext/commit/82386b18aa05ec3166942132fd42e6e5839bbe87)) * linter; dont change doc after DB update ([#49907](https://github.com/frappe/erpnext/issues/49907)) ([6c47353](https://github.com/frappe/erpnext/commit/6c47353205230f1057761ed96af6f8a4a5fabe19)) * **manufacturing:** prevent KeyError in BOM Creator when sub-assembly reused ([07c3755](https://github.com/frappe/erpnext/commit/07c3755f319e23715a40d0c9707324417e6c4a91)) * optimize SQL query by adding index on batch ([a83331b](https://github.com/frappe/erpnext/commit/a83331bd2f8b4e6d6c32e0959e582ab22f952e13)) * **profit and loss statement:** incorrect total calculation ([e132c45](https://github.com/frappe/erpnext/commit/e132c457f25baa4dc8d738be856a0f5db1db4e4a)) * remove allow_on_submit for pick list items ([4ccdede](https://github.com/frappe/erpnext/commit/4ccdedeb12c2c39868a0644c60b642d396fb9e45)) * resolved conflict ([a39bc62](https://github.com/frappe/erpnext/commit/a39bc626c705a7f3be88dea2a67801b0891b57d6)) * retain shipping address in doc ([20c21a4](https://github.com/frappe/erpnext/commit/20c21a4dc089dd81acc340e1cfee6e1bf7b6bac8)) * set fg_completed_qty based upon fg item qty ([b6d57ff](https://github.com/frappe/erpnext/commit/b6d57ff8a5d66c09dbd4098f2747c9f8c692b3ad)) * Set paid amount automatically only if return entry validated and has negative grand total ([#49829](https://github.com/frappe/erpnext/issues/49829)) ([bd3503a](https://github.com/frappe/erpnext/commit/bd3503a3d8ec2b1fa28a02ade52a8c438c935ab5)) * SQL operator precedence in Project query customer filter ([d950de2](https://github.com/frappe/erpnext/commit/d950de2d099509093cf21093290c086e0db03980)) * **subscription:** include days before ([f27b754](https://github.com/frappe/erpnext/commit/f27b754570b006c415bc3675ad64772ee112426d)) * too many writes on patch run ([a2bf53f](https://github.com/frappe/erpnext/commit/a2bf53ff0aea3debc69ef696351c4ce2a5dfdb17)) * use item valuation rate if no bin ([a4b5a74](https://github.com/frappe/erpnext/commit/a4b5a74644ccd07e8536e139f58689084c20accc)) * validate transfer_qty based on overproduction wo percentage ([7ce97ce](https://github.com/frappe/erpnext/commit/7ce97ce0c2fdc2f6308e5b9266b1db198984061b)) * warning message if the batch has incorrect qty ([3f3fd20](https://github.com/frappe/erpnext/commit/3f3fd20b31dfba428ad5e9cba40be4bf44cb5982)) ### Features * allow fallback to default selling price list (backport [#49634](https://github.com/frappe/erpnext/issues/49634)) ([#49704](https://github.com/frappe/erpnext/issues/49704)) ([95387b4](https://github.com/frappe/erpnext/commit/95387b4bf0723138abf7b7572b384849f6d11fe8)) * dynamic due date in payment terms when fetched from order (backport [#48864](https://github.com/frappe/erpnext/issues/48864)) ([#49938](https://github.com/frappe/erpnext/issues/49938)) ([baa6d2b](https://github.com/frappe/erpnext/commit/baa6d2bcdca633d60bfb596fc76df5cc5ab8b8fd)) * recalculate batch qty ([d49b64d](https://github.com/frappe/erpnext/commit/d49b64dc7c44624028892cc4d921ae93981a072a)) * validating asset scrap date ([#43093](https://github.com/frappe/erpnext/issues/43093)) ([0eb76f4](https://github.com/frappe/erpnext/commit/0eb76f4d2ce9a89022af266177c78afaf9a0f1e0)) ### Performance Improvements * serial nos / batches reposting ([8a310ef](https://github.com/frappe/erpnext/commit/8a310efc974179e4f89fa20f32257e66b6e3fe85)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d17928ba9c2..7978873c49c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.81.3" +__version__ = "15.82.0" def get_default_company(user=None): From 9c3f4b69af5312cc7cd8d15dafafa112efb318cd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 8 Oct 2025 18:27:39 +0530 Subject: [PATCH 11/18] fix: incorrect field valuation_rate (cherry picked from commit 630d8732143b1273cc1f7ee4f2d350f80a888d27) (cherry picked from commit 93df11a0cf3865c94f6342dc79d88027f7e8a629) --- erpnext/stock/stock_ledger.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index e92d45ec5dd..a06412373e2 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1070,16 +1070,15 @@ class update_entries_after: for d in sabb_data: incoming_rate = get_incoming_rate_for_serial_and_batch(self.item_code, d, sn_obj) - - if flt(incoming_rate, self.currency_precision) == flt( - d.valuation_rate, self.currency_precision - ) and not getattr(d, "stock_queue", None): - continue - amount = incoming_rate * flt(d.qty) tot_amt += flt(amount) total_qty += flt(d.qty) + if flt(incoming_rate, self.currency_precision) == flt( + d.incoming_rate, self.currency_precision + ) and not getattr(d, "stock_queue", None): + continue + values_to_update = { "incoming_rate": incoming_rate, "stock_value_difference": amount, From 4e2a42d092a00ffcd85706b5a3a63a41504703ce Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 8 Oct 2025 18:37:40 +0530 Subject: [PATCH 12/18] fix: batch qty for expired batches (cherry picked from commit ff2faf36a7640cda4a9fe4bfb5b1380752af7f97) (cherry picked from commit f4816e4960c1fd0cafb34cdac6a8c0361cf62a2b) --- erpnext/stock/doctype/batch/batch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 46618a2ca4a..9955b485c22 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -158,7 +158,7 @@ class Batch(Document): @frappe.whitelist() def recalculate_batch_qty(self): - batches = get_batch_qty(batch_no=self.name, item_code=self.item) + batches = get_batch_qty(batch_no=self.name, item_code=self.item, for_stock_levels=True) batch_qty = 0.0 if batches: for row in batches: From c84952169fd5f996bccddf00461ad919ab1a38a0 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 8 Oct 2025 14:32:21 +0000 Subject: [PATCH 13/18] chore(release): Bumped to Version 15.82.1 ## [15.82.1](https://github.com/frappe/erpnext/compare/v15.82.0...v15.82.1) (2025-10-08) ### Bug Fixes * batch qty for expired batches ([4e2a42d](https://github.com/frappe/erpnext/commit/4e2a42d092a00ffcd85706b5a3a63a41504703ce)) * incorrect field valuation_rate ([9c3f4b6](https://github.com/frappe/erpnext/commit/9c3f4b69af5312cc7cd8d15dafafa112efb318cd)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 7978873c49c..f65055d09db 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.82.0" +__version__ = "15.82.1" def get_default_company(user=None): From 00db37e30614dca9ce9e90fe825d98beb5b6eaea Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 8 Oct 2025 21:46:17 +0530 Subject: [PATCH 14/18] fix: Reset Raw Materials Table button not working (cherry picked from commit 128e243945457d36e720ffb4633113fdba172d93) (cherry picked from commit 81ed32ff51eff145293eca55203a2f131b8a41b5) --- erpnext/controllers/subcontracting_controller.py | 6 ++++++ .../subcontracting_receipt/subcontracting_receipt.js | 4 ++++ .../subcontracting_receipt/subcontracting_receipt.json | 4 +++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index da03f907e3b..4666c953fbe 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -201,6 +201,9 @@ class SubcontractingController(StockController): self.set(self.raw_material_table, []) return + if not self.get(self.raw_material_table): + return + item_dict = self.__get_data_before_save() if not item_dict: return True @@ -651,6 +654,9 @@ class SubcontractingController(StockController): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos_for_outward from erpnext.stock.get_item_details import get_filtered_serial_nos + if self.is_return: + return + for row in self.supplied_items: item_details = frappe.get_cached_value( "Item", row.rm_item_code, ["has_batch_no", "has_serial_no"], as_dict=1 diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index c9fe457ef79..fee1cac2542 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -336,6 +336,10 @@ frappe.ui.form.on("Subcontracting Receipt", { reset_raw_materials_table: (frm) => { frm.clear_table("supplied_items"); + frm.doc.__unsaved = true; + if (!frm.doc.set_posting_time) { + frm.set_value("posting_time", frappe.datetime.now_time()); + } frm.call({ method: "reset_raw_materials", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index e975f384c25..dcc02f7a5a1 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -649,6 +649,7 @@ "label": "Raw Materials Actions" }, { + "description": "Click this button if you encounter a negative stock error for a serial or batch item. The system will fetch the available serials or batches automatically.", "fieldname": "reset_raw_materials_table", "fieldtype": "Button", "label": "Reset Raw Materials Table" @@ -678,7 +679,7 @@ "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2024-12-06 15:24:38.384232", + "modified": "2025-10-08 21:43:27.065640", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", @@ -739,6 +740,7 @@ "write": 1 } ], + "row_format": "Dynamic", "search_fields": "status, posting_date, supplier", "show_name_in_global_search": 1, "sort_field": "modified", From 2ea583ee16258c607c5acef68b47a567578d6fbd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 9 Oct 2025 01:00:31 +0530 Subject: [PATCH 15/18] fix: sales return for product bundle items (cherry picked from commit 13ce7279a87a538fae272748b0e84e4efb673b7c) (cherry picked from commit ac46b3d1cab3a0bdf08c7e772bbbc28b7c859d73) --- .../controllers/sales_and_purchase_return.py | 22 +++++++++++++--- erpnext/controllers/selling_controller.py | 9 ++++++- erpnext/controllers/stock_controller.py | 26 ++++++++++++++++++- .../serial_and_batch_bundle.py | 14 ++++++++++ 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index a45b5813584..f45b06b01b0 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -852,10 +852,10 @@ def get_available_serial_batches(field, doctype, reference_ids, is_rejected=Fals if not _bundle_ids: return frappe._dict({}) - return get_serial_batches_based_on_bundle(field, _bundle_ids) + return get_serial_batches_based_on_bundle(doctype, field, _bundle_ids) -def get_serial_batches_based_on_bundle(field, _bundle_ids): +def get_serial_batches_based_on_bundle(doctype, field, _bundle_ids): available_dict = frappe._dict({}) batch_serial_nos = frappe.get_all( "Serial and Batch Bundle", @@ -867,6 +867,7 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids): "`tabSerial and Batch Bundle`.`voucher_detail_no`", "`tabSerial and Batch Bundle`.`voucher_type`", "`tabSerial and Batch Bundle`.`voucher_no`", + "`tabSerial and Batch Bundle`.`item_code`", ], filters=[ ["Serial and Batch Bundle", "name", "in", _bundle_ids], @@ -880,6 +881,9 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids): if frappe.get_cached_value(row.voucher_type, row.voucher_no, "is_return"): key = frappe.get_cached_value(row.voucher_type + " Item", row.voucher_detail_no, field) + if doctype == "Packed Item": + key = (row.item_code, key) + if row.voucher_type in ["Sales Invoice", "Delivery Note"]: row.qty = -1 * row.qty @@ -908,6 +912,8 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids): def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False): filters = {"docstatus": 1, "name": ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")} + if doctype == "Packed Item": + filters = {"docstatus": 1, field: ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")} pluck_field = "serial_and_batch_bundle" if is_rejected: @@ -921,10 +927,14 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False pluck=pluck_field, ) + if _bundle_ids and doctype == "Packed Item": + return _bundle_ids + if not _bundle_ids: return {} - del filters["name"] + if "name" in filters: + del filters["name"] filters[field] = ("in", reference_ids) @@ -971,6 +981,9 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field if not qty_field: qty_field = "stock_qty" + if not hasattr(row, qty_field): + qty_field = "qty" + if not warehouse_field: warehouse_field = "warehouse" @@ -1060,6 +1073,9 @@ def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_f if not qty_field: qty_field = "stock_qty" + if not hasattr(child_doc, qty_field): + qty_field = "qty" + warehouse = child_doc.get(warehouse_field) if parent_doc.get("is_internal_customer"): warehouse = child_doc.get("target_warehouse") diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index d85c1b28f97..8fc49d09c0e 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -517,8 +517,15 @@ class SellingController(StockController): if not frappe.get_cached_value("Item", d.item_code, "is_stock_item"): continue + item_details = frappe.get_cached_value( + "Item", d.item_code, ["has_serial_no", "has_batch_no"], as_dict=1 + ) + if not self.get("return_against") or ( - get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return") + get_valuation_method(d.item_code) == "Moving Average" + and self.get("is_return") + and not item_details.has_serial_no + and not item_details.has_batch_no ): # Get incoming rate based on original item cost based on valuation method qty = flt(d.get("stock_qty") or d.get("actual_qty") or d.get("qty")) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 3b5dbf2ee81..d7c9bb61a20 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -335,10 +335,20 @@ class StockController(AccountsController): return child_doctype = self.doctype + " Item" + if table_name == "packed_items": + field = "parent_detail_docname" + child_doctype = "Packed Item" + available_dict = available_serial_batch_for_return(field, child_doctype, reference_ids) for row in self.get(table_name): - if data := available_dict.get(row.get(field)): + value = row.get(field) + if table_name == "packed_items" and row.get("parent_detail_docname"): + value = self.get_value_for_packed_item(row) + if not value: + continue + + if data := available_dict.get(value): data = filter_serial_batches(self, data, row) bundle = make_serial_batch_bundle_for_return(data, row, self) row.db_set( @@ -354,6 +364,14 @@ class StockController(AccountsController): "incoming_rate", frappe.db.get_value("Serial and Batch Bundle", bundle, "avg_rate") ) + def get_value_for_packed_item(self, row): + parent_items = self.get("items", {"name": row.parent_detail_docname}) + if parent_items: + ref = parent_items[0].get("dn_detail") + return (row.item_code, ref) + + return None + def get_reference_ids(self, table_name, qty_field=None, bundle_field=None) -> tuple[str, list[str]]: field = { "Sales Invoice": "sales_invoice_item", @@ -388,6 +406,12 @@ class StockController(AccountsController): ): reference_ids.append(row.get(field)) + if table_name == "packed_items" and row.get("parent_detail_docname"): + parent_rows = self.get("items", {"name": row.parent_detail_docname}) or [] + for d in parent_rows: + if d.get(field) and not d.get(bundle_field): + reference_ids.append(d.get(field)) + return field, reference_ids @frappe.request_cache diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 9c69d856d45..d8d68f34fbb 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -638,6 +638,17 @@ class SerialandBatchBundle(Document): if not rate and self.voucher_detail_no and self.voucher_no: rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field) + is_packed_item = False + if rate is None and child_table == "Delivery Note Item": + rate = frappe.db.get_value( + "Packed Item", + self.voucher_detail_no, + "incoming_rate", + ) + + if rate is not None: + is_packed_item = True + stock_queue = [] batches = [] if prev_sle and prev_sle.stock_queue: @@ -659,6 +670,9 @@ class SerialandBatchBundle(Document): elif (d.incoming_rate == rate) and not stock_queue and d.qty and d.stock_value_difference: continue + if is_packed_item and d.incoming_rate: + rate = d.incoming_rate + d.incoming_rate = flt(rate) if d.qty: d.stock_value_difference = flt(d.qty) * d.incoming_rate From 789cea9a853868fb6b4ed46c322ecab6089e47dd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 9 Oct 2025 13:56:04 +0530 Subject: [PATCH 16/18] test: test case for sales return for product bundle (cherry picked from commit 1d57bbca1105a041a8be88d0daf6f593465cbd7f) # Conflicts: # erpnext/stock/doctype/delivery_note/test_delivery_note.py (cherry picked from commit 6ba55bbee09b813d31aac76472643d1a8ce65e08) --- .../controllers/sales_and_purchase_return.py | 26 ++- erpnext/controllers/selling_controller.py | 3 + .../delivery_note/test_delivery_note.py | 167 ++++++++++++++++++ .../serial_and_batch_bundle.py | 2 +- 4 files changed, 196 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index f45b06b01b0..5c0f78ac986 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -849,6 +849,7 @@ def available_serial_batch_for_return(field, doctype, reference_ids, is_rejected def get_available_serial_batches(field, doctype, reference_ids, is_rejected=False): _bundle_ids = get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=is_rejected) + if not _bundle_ids: return frappe._dict({}) @@ -882,6 +883,13 @@ def get_serial_batches_based_on_bundle(doctype, field, _bundle_ids): key = frappe.get_cached_value(row.voucher_type + " Item", row.voucher_detail_no, field) if doctype == "Packed Item": + if key is None: + key = frappe.get_cached_value("Packed Item", row.voucher_detail_no, field) + if row.voucher_type == "Delivery Note": + key = frappe.get_cached_value("Delivery Note Item", key, "dn_detail") + elif row.voucher_type == "Sales Invoice": + key = frappe.get_cached_value("Sales Invoice Item", key, "sales_invoice_item") + key = (row.item_code, key) if row.voucher_type in ["Sales Invoice", "Delivery Note"]: @@ -913,7 +921,7 @@ def get_serial_batches_based_on_bundle(doctype, field, _bundle_ids): def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False): filters = {"docstatus": 1, "name": ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")} if doctype == "Packed Item": - filters = {"docstatus": 1, field: ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")} + filters = get_filters_for_packed_item(field, reference_ids) pluck_field = "serial_and_batch_bundle" if is_rejected: @@ -977,6 +985,22 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False return _bundle_ids +def get_filters_for_packed_item(field, reference_ids): + names = [] + filters = {"docstatus": 1, "dn_detail": ("in", reference_ids)} + if dns := frappe.get_all("Delivery Note Item", filters=filters, pluck="name"): + names.extend(dns) + + filters = {"docstatus": 1, "sales_invoice_item": ("in", reference_ids)} + if sis := frappe.get_all("Sales Invoice Item", filters=filters, pluck="name"): + names.extend(sis) + + if names: + reference_ids.extend(names) + + return {"docstatus": 1, field: ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")} + + def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field=None): if not qty_field: qty_field = "stock_qty" diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 8fc49d09c0e..e4e2ee29d9b 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -1004,6 +1004,9 @@ def set_default_income_account_for_item(obj): def get_serial_and_batch_bundle(child, parent, delivery_note_child=None): from erpnext.stock.serial_batch_bundle import SerialBatchCreation + if parent.get("is_return") and parent.get("packed_items"): + return + if child.get("use_serial_batch_fields"): return diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 8e1a38b5ea7..407e005a1b8 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -2596,6 +2596,173 @@ class TestDeliveryNote(FrappeTestCase): self.assertEqual(dn.per_billed, 100) self.assertEqual(dn.per_returned, 100) +<<<<<<< HEAD +======= + def test_packed_item_serial_no_status(self): + from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle + from erpnext.stock.doctype.item.test_item import make_item + + # test Update Items with product bundle + if not frappe.db.exists("Item", "_Test Product Bundle Item New 1"): + bundle_item = make_item("_Test Product Bundle Item New 1", {"is_stock_item": 0}) + bundle_item.append( + "item_defaults", {"company": "_Test Company", "default_warehouse": "_Test Warehouse - _TC"} + ) + bundle_item.save(ignore_permissions=True) + + make_item( + "_Packed Item New Sn Item", + {"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "SN-PACKED-NEW-.#####"}, + ) + make_product_bundle("_Test Product Bundle Item New 1", ["_Packed Item New Sn Item"], 1) + + make_stock_entry(item="_Packed Item New Sn Item", target="_Test Warehouse - _TC", qty=5, rate=100) + + dn = create_delivery_note( + item_code="_Test Product Bundle Item New 1", + warehouse="_Test Warehouse - _TC", + qty=5, + ) + + dn.reload() + + serial_nos = [] + for row in dn.packed_items: + self.assertTrue(row.serial_and_batch_bundle) + doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) + for row in doc.entries: + status = frappe.db.get_value("Serial No", row.serial_no, "status") + self.assertEqual(status, "Delivered") + serial_nos.append(row.serial_no) + + dn.cancel() + + for row in serial_nos: + status = frappe.db.get_value("Serial No", row, "status") + self.assertEqual(status, "Active") + + def test_sales_return_for_product_bundle(self): + from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle + from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return + from erpnext.stock.doctype.item.test_item import make_item + + rm_items = [] + for item_code, properties in { + "_Packed Service Item": {"is_stock_item": 0}, + "_Packed FG Item New 1": { + "is_stock_item": 1, + "has_serial_no": 1, + "serial_no_series": "SN-PACKED-1-.#####", + }, + "_Packed FG Item New 2": { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-PACKED-2-.#####", + }, + "_Packed FG Item New 3": { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-PACKED-3-.#####", + "has_serial_no": 1, + "serial_no_series": "SN-PACKED-3-.#####", + }, + }.items(): + if not frappe.db.exists("Item", item_code): + make_item(item_code, properties) + + if item_code != "_Packed Service Item": + rm_items.append(item_code) + + for rate in [100, 200]: + make_stock_entry(item=item_code, target="_Test Warehouse - _TC", qty=5, rate=rate) + + make_product_bundle("_Packed Service Item", rm_items) + dn = create_delivery_note( + item_code="_Packed Service Item", + warehouse="_Test Warehouse - _TC", + qty=5, + ) + + serial_batch_map = {} + for row in dn.packed_items: + self.assertTrue(row.serial_and_batch_bundle) + if row.item_code not in serial_batch_map: + serial_batch_map[row.item_code] = frappe._dict( + { + "serial_nos": [], + "batches": defaultdict(int), + "serial_no_valuation": defaultdict(float), + "batch_no_valuation": defaultdict(float), + } + ) + + doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) + for entry in doc.entries: + if entry.serial_no: + serial_batch_map[row.item_code].serial_nos.append(entry.serial_no) + serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no] = entry.incoming_rate + if entry.batch_no: + serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty + serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no] = entry.incoming_rate + + dn1 = make_sales_return(dn.name) + dn1.items[0].qty = -2 + dn1.submit() + dn1.reload() + + for row in dn1.packed_items: + doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) + for entry in doc.entries: + if entry.serial_no: + self.assertTrue(entry.serial_no in serial_batch_map[row.item_code].serial_nos) + self.assertEqual( + entry.incoming_rate, + serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no], + ) + serial_batch_map[row.item_code].serial_nos.remove(entry.serial_no) + serial_batch_map[row.item_code].serial_no_valuation.pop(entry.serial_no) + + elif entry.batch_no: + serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty + self.assertTrue(entry.batch_no in serial_batch_map[row.item_code].batches) + self.assertEqual(entry.qty, 2.0) + self.assertEqual( + entry.incoming_rate, + serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no], + ) + + dn2 = make_sales_return(dn.name) + dn2.items[0].qty = -3 + dn2.submit() + dn2.reload() + + for row in dn2.packed_items: + doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) + for entry in doc.entries: + if entry.serial_no: + self.assertTrue(entry.serial_no in serial_batch_map[row.item_code].serial_nos) + self.assertEqual( + entry.incoming_rate, + serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no], + ) + serial_batch_map[row.item_code].serial_nos.remove(entry.serial_no) + serial_batch_map[row.item_code].serial_no_valuation.pop(entry.serial_no) + + elif entry.batch_no: + serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty + self.assertEqual(serial_batch_map[row.item_code].batches[entry.batch_no], 0.0) + + self.assertTrue(entry.batch_no in serial_batch_map[row.item_code].batches) + + self.assertEqual(entry.qty, 3.0) + self.assertEqual( + entry.incoming_rate, + serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no], + ) + +>>>>>>> 1d57bbca11 (test: test case for sales return for product bundle) def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index d8d68f34fbb..ca3e7ec1ad7 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -639,7 +639,7 @@ class SerialandBatchBundle(Document): rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field) is_packed_item = False - if rate is None and child_table == "Delivery Note Item": + if rate is None and child_table in ["Delivery Note Item", "Sales Invoice Item"]: rate = frappe.db.get_value( "Packed Item", self.voucher_detail_no, From 3e816f6ea1f793a5071d94433568a6c49aa4f834 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 9 Oct 2025 15:26:08 +0530 Subject: [PATCH 17/18] chore: fix conflicts (cherry picked from commit 1fbc03c1045d7832ffdd35f2aecdd00cbe458c8c) --- .../delivery_note/test_delivery_note.py | 48 +------------------ erpnext/stock/serial_batch_bundle.py | 22 ++++++++- 2 files changed, 23 insertions(+), 47 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 407e005a1b8..dd9d247902b 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -2596,51 +2596,6 @@ class TestDeliveryNote(FrappeTestCase): self.assertEqual(dn.per_billed, 100) self.assertEqual(dn.per_returned, 100) -<<<<<<< HEAD -======= - def test_packed_item_serial_no_status(self): - from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle - from erpnext.stock.doctype.item.test_item import make_item - - # test Update Items with product bundle - if not frappe.db.exists("Item", "_Test Product Bundle Item New 1"): - bundle_item = make_item("_Test Product Bundle Item New 1", {"is_stock_item": 0}) - bundle_item.append( - "item_defaults", {"company": "_Test Company", "default_warehouse": "_Test Warehouse - _TC"} - ) - bundle_item.save(ignore_permissions=True) - - make_item( - "_Packed Item New Sn Item", - {"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "SN-PACKED-NEW-.#####"}, - ) - make_product_bundle("_Test Product Bundle Item New 1", ["_Packed Item New Sn Item"], 1) - - make_stock_entry(item="_Packed Item New Sn Item", target="_Test Warehouse - _TC", qty=5, rate=100) - - dn = create_delivery_note( - item_code="_Test Product Bundle Item New 1", - warehouse="_Test Warehouse - _TC", - qty=5, - ) - - dn.reload() - - serial_nos = [] - for row in dn.packed_items: - self.assertTrue(row.serial_and_batch_bundle) - doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) - for row in doc.entries: - status = frappe.db.get_value("Serial No", row.serial_no, "status") - self.assertEqual(status, "Delivered") - serial_nos.append(row.serial_no) - - dn.cancel() - - for row in serial_nos: - status = frappe.db.get_value("Serial No", row, "status") - self.assertEqual(status, "Active") - def test_sales_return_for_product_bundle(self): from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return @@ -2685,6 +2640,8 @@ class TestDeliveryNote(FrappeTestCase): qty=5, ) + dn.reload() + serial_batch_map = {} for row in dn.packed_items: self.assertTrue(row.serial_and_batch_bundle) @@ -2762,7 +2719,6 @@ class TestDeliveryNote(FrappeTestCase): serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no], ) ->>>>>>> 1d57bbca11 (test: test case for sales return for product bundle) def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 2408c178f2b..589861809d4 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -209,7 +209,20 @@ class SerialBatchBundle: elif sn_doc.has_batch_no and len(sn_doc.entries) == 1: values_to_update["batch_no"] = sn_doc.entries[0].batch_no - frappe.db.set_value(self.child_doctype, self.sle.voucher_detail_no, values_to_update) + if self.child_doctype == "Packed Item": + name = frappe.db.get_value( + "Packed Item", + { + "parent_detail_docname": sn_doc.voucher_detail_no, + "item_code": self.sle.item_code, + "serial_and_batch_bundle": ("is", "not set"), + }, + "name", + ) + + frappe.db.set_value(self.child_doctype, name, values_to_update) + else: + frappe.db.set_value(self.child_doctype, self.sle.voucher_detail_no, values_to_update) @property def child_doctype(self): @@ -227,6 +240,13 @@ class SerialBatchBundle: if self.sle.voucher_type == "Asset Repair": child_doctype = "Asset Repair Consumed Item" + if self.sle.voucher_type in ["Delivery Note", "Sales Invoice"] and self.sle.voucher_detail_no: + if ( + frappe.db.get_value(self.sle.voucher_type + " Item", self.sle.voucher_detail_no, "item_code") + != self.sle.item_code + ): + child_doctype = "Packed Item" + return child_doctype def is_rejected_entry(self): From cc0a8957607a77b05737b99ac7d502bb7accd7c9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 9 Oct 2025 12:31:16 +0000 Subject: [PATCH 18/18] chore(release): Bumped to Version 15.82.2 ## [15.82.2](https://github.com/frappe/erpnext/compare/v15.82.1...v15.82.2) (2025-10-09) ### Bug Fixes * Reset Raw Materials Table button not working ([00db37e](https://github.com/frappe/erpnext/commit/00db37e30614dca9ce9e90fe825d98beb5b6eaea)) * sales return for product bundle items ([2ea583e](https://github.com/frappe/erpnext/commit/2ea583ee16258c607c5acef68b47a567578d6fbd)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f65055d09db..cbcfb489235 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.82.1" +__version__ = "15.82.2" def get_default_company(user=None):