From f04221417edc455948542160992b55bfdffa3ba2 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 16 Jan 2026 12:31:54 +0530 Subject: [PATCH] test: add test case (cherry picked from commit b567184dd707b3b5b8aad995007d67bd6f466029) --- .../stock/doctype/stock_entry/stock_entry.js | 12 ++++-- .../stock/doctype/stock_entry/stock_entry.py | 17 ++++++-- .../doctype/stock_entry/stock_entry_utils.py | 1 + .../doctype/stock_entry/test_stock_entry.py | 40 +++++++++++++++++++ 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index b62dce837f1..7d2ae01357a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -440,12 +440,16 @@ frappe.ui.form.on("Stock Entry", { if ( frm.doc.docstatus == 1 && - frm.doc.purpose == "Material Receipt" && + ["Material Receipt", "Manufacture"].includes(frm.doc.purpose) && frm.get_sum("items", "sample_quantity") ) { - frm.add_custom_button(__("Create Sample Retention Stock Entry"), function () { - frm.trigger("make_retention_stock_entry"); - }); + frm.add_custom_button( + __("Sample Retention Stock Entry"), + function () { + frm.trigger("make_retention_stock_entry"); + }, + __("Create") + ); } frm.trigger("setup_quality_inspection"); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index e92ab143588..fc38aff9e2e 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2570,6 +2570,7 @@ class StockEntry(StockController, SubcontractingInwardController): "expense_account": expense_account, "cost_center": item.get("buying_cost_center"), "is_finished_item": 1, + "sample_quantity": item.get("sample_quantity"), } if ( @@ -3103,6 +3104,7 @@ class StockEntry(StockController, SubcontractingInwardController): se_child.po_detail = item_row.get("po_detail") se_child.sco_rm_detail = item_row.get("sco_rm_detail") se_child.scio_detail = item_row.get("scio_detail") + se_child.sample_quantity = item_row.get("sample_quantity", 0) for field in [ self.subcontract_data.rm_detail_field, @@ -3415,6 +3417,7 @@ def move_sample_to_retention_warehouse(company, items): if isinstance(items, str): items = json.loads(items) + retention_warehouse = frappe.get_single_value("Stock Settings", "sample_retention_warehouse") stock_entry = frappe.new_doc("Stock Entry") stock_entry.company = company @@ -3449,12 +3452,17 @@ def move_sample_to_retention_warehouse(company, items): total_qty += sample_quantity if sabb.has_serial_no: sabe_list.extend( - [entry for entry in sabb.entries if entry.batch_no == batch_no][ - : int(sample_quantity) - ] + [ + entry + for entry in sabb.entries + if entry.batch_no == batch_no + and frappe.db.exists( + "Serial No", {"name": entry.serial_no, "warehouse": warehouse} + ) + ][: int(sample_quantity)] ) else: - sabe.qty = -1 * sample_quantity + sabe.qty = sample_quantity else: sabb.entries.remove(sabe) @@ -3462,6 +3470,7 @@ def move_sample_to_retention_warehouse(company, items): if sabe_list: sabb.entries = sabe_list sabb.save() + stock_entry.append( "items", { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py index 576d129ee2d..6e54fd4e3b9 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py @@ -190,6 +190,7 @@ def make_stock_entry(**args): "cost_center": args.cost_center, "expense_account": args.expense_account, "use_serial_batch_fields": args.use_serial_batch_fields, + "sample_quantity": frappe.get_value("Item", args.item, "sample_quantity") or 0, }, ) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index cf1b12a8f28..7b710514519 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -2277,6 +2277,46 @@ class TestStockEntry(IntegrationTestCase): se.save() se.submit() + @IntegrationTestCase.change_settings( + "Stock Settings", {"sample_retention_warehouse": "_Test Warehouse 1 - _TC"} + ) + def test_sample_retention_stock_entry(self): + from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse + + warehouse = "_Test Warehouse - _TC" + retain_sample_item = make_item( + "Retain Sample Item", + properties={ + "is_stock_item": 1, + "retain_sample": 1, + "sample_quantity": 2, + "has_batch_no": 1, + "has_seral_no": 1, + "create_new_batch": 1, + "batch_number_series": "SAMPLE-RET-.#####", + "serial_no_series": "SAMPLE-RET-SN-.#####", + }, + ) + material_receipt = make_stock_entry( + item_code=retain_sample_item.item_code, target=warehouse, qty=10, purpose="Material Receipt" + ) + + source_sabb = frappe.get_doc( + "Serial and Batch Bundle", material_receipt.items[0].serial_and_batch_bundle + ) + batch = source_sabb.entries[0].batch_no + serial_nos = [entry.serial_no for entry in source_sabb.entries] + + sample_entry = frappe.get_doc( + move_sample_to_retention_warehouse(material_receipt.company, material_receipt.items) + ) + sample_entry.submit() + target_sabb = frappe.get_doc("Serial and Batch Bundle", sample_entry.items[0].serial_and_batch_bundle) + + self.assertEqual(sample_entry.items[0].transfer_qty, 2) + self.assertEqual(target_sabb.entries[0].batch_no, batch) + self.assertEqual([entry.serial_no for entry in target_sabb.entries], serial_nos[:2]) + def make_serialized_item(self, **args): args = frappe._dict(args)