From fe43975cdd6b57ca3dad21752238452170f2564e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 17 Feb 2025 14:19:34 +0530 Subject: [PATCH] fix: provision to enable naming series for SABB --- .../serial_and_batch_bundle.js | 7 +++ .../serial_and_batch_bundle.json | 11 +++- .../serial_and_batch_bundle.py | 20 ++++++ .../test_serial_and_batch_bundle.py | 61 +++++++++++++++++++ .../stock_settings/stock_settings.json | 15 ++++- .../doctype/stock_settings/stock_settings.py | 2 + 6 files changed, 114 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js index ad8360ea75c..404abbd21bc 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js @@ -134,6 +134,13 @@ frappe.ui.form.on("Serial and Batch Bundle", { }, toggle_fields(frm) { + let show_naming_series_field = + frappe.user_defaults.set_serial_and_batch_bundle_naming_based_on_naming_series; + frm.toggle_display("naming_series", cint(show_naming_series_field)); + frm.toggle_reqd("naming_series", cint(show_naming_series_field)); + + frm.toggle_display("naming_series", frm.doc.__islocal ? true : false); + if (frm.doc.has_serial_no) { frm.doc.entries.forEach((row) => { if (Math.abs(row.qty) !== 1) { diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json index b3bb7ff6634..a44a9e5deef 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json @@ -7,6 +7,7 @@ "engine": "InnoDB", "field_order": [ "item_details_tab", + "naming_series", "company", "item_name", "has_serial_no", @@ -242,12 +243,20 @@ "fieldtype": "Data", "label": "Returned Against", "read_only": 1 + }, + { + "default": "SABB-.########", + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "\nSABB-.########", + "set_only_once": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-02-12 09:53:32.090309", + "modified": "2025-02-17 16:22:36.056205", "modified_by": "Administrator", "module": "Stock", "name": "Serial and Batch Bundle", 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 568dcc37268..f71a66ad2f1 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 @@ -8,6 +8,7 @@ from collections import Counter, defaultdict import frappe from frappe import _, _dict, bold from frappe.model.document import Document +from frappe.model.naming import make_autoname from frappe.query_builder.functions import CombineDatetime, Sum from frappe.utils import ( add_days, @@ -68,6 +69,7 @@ class SerialandBatchBundle(Document): item_code: DF.Link item_group: DF.Link | None item_name: DF.Data | None + naming_series: DF.Literal["", "SABB-.########"] posting_date: DF.Date | None posting_time: DF.Time | None returned_against: DF.Data | None @@ -80,6 +82,24 @@ class SerialandBatchBundle(Document): warehouse: DF.Link | None # end: auto-generated types + def autoname(self): + if frappe.db.get_single_value( + "Stock Settings", "set_serial_and_batch_bundle_naming_based_on_naming_series" + ): + if not self.naming_series: + frappe.throw(_("Naming Series is mandatory")) + + naming_series = self.naming_series + if "#" not in naming_series: + naming_series += ".#####" + + self.name = make_autoname(self.naming_series) + else: + try: + self.name = frappe.generate_hash(length=20) + except frappe.DuplicateEntryError: + self.autoname() + def validate(self): if self.docstatus == 1 and self.voucher_detail_no: self.validate_voucher_detail_no() diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py index 1179f0711eb..ba1740d4b38 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py @@ -26,6 +26,67 @@ class UnitTestSerialAndBatchBundle(UnitTestCase): class TestSerialandBatchBundle(IntegrationTestCase): + def test_naming_for_sabb(self): + frappe.db.set_single_value( + "Stock Settings", "set_serial_and_batch_bundle_naming_based_on_naming_series", 1 + ) + + serial_item_code = "New Serial No Valuation 11" + make_item( + serial_item_code, + { + "has_serial_no": 1, + "serial_no_series": "TEST-A-SER-VAL-.#####", + "is_stock_item": 1, + }, + ) + + for sn in ["TEST-A-SER-VAL-00001", "TEST-A-SER-VAL-00002"]: + if not frappe.db.exists("Serial No", sn): + frappe.get_doc( + { + "doctype": "Serial No", + "serial_no": sn, + "item_code": serial_item_code, + } + ).insert(ignore_permissions=True) + + bundle_doc = make_serial_batch_bundle( + { + "item_code": serial_item_code, + "warehouse": "_Test Warehouse - _TC", + "voucher_type": "Stock Entry", + "posting_date": today(), + "posting_time": nowtime(), + "qty": 10, + "serial_nos": ["TEST-A-SER-VAL-00001", "TEST-A-SER-VAL-00002"], + "type_of_transaction": "Inward", + "do_not_submit": True, + } + ) + + self.assertTrue(bundle_doc.name.startswith("SABB-")) + + frappe.db.set_single_value( + "Stock Settings", "set_serial_and_batch_bundle_naming_based_on_naming_series", 0 + ) + + bundle_doc = make_serial_batch_bundle( + { + "item_code": serial_item_code, + "warehouse": "_Test Warehouse - _TC", + "voucher_type": "Stock Entry", + "posting_date": today(), + "posting_time": nowtime(), + "qty": 10, + "serial_nos": ["TEST-A-SER-VAL-00001", "TEST-A-SER-VAL-00002"], + "type_of_transaction": "Inward", + "do_not_submit": True, + } + ) + + self.assertFalse(bundle_doc.name.startswith("SABB-")) + def test_inward_outward_serial_valuation(self): from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index c6ce6b29124..3e17e8708e8 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -57,6 +57,8 @@ "use_serial_batch_fields", "do_not_update_serial_batch_on_creation_of_auto_bundle", "allow_existing_serial_no", + "serial_and_batch_bundle_section", + "set_serial_and_batch_bundle_naming_based_on_naming_series", "stock_planning_tab", "auto_material_request", "auto_indent", @@ -475,6 +477,17 @@ "fieldname": "auto_reserve_stock", "fieldtype": "Check", "label": "Auto Reserve Stock" + }, + { + "fieldname": "serial_and_batch_bundle_section", + "fieldtype": "Section Break", + "label": "Serial and Batch Bundle" + }, + { + "default": "0", + "fieldname": "set_serial_and_batch_bundle_naming_based_on_naming_series", + "fieldtype": "Check", + "label": "Set Serial and Batch Bundle Naming Based on Naming Series" } ], "icon": "icon-cog", @@ -482,7 +495,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-12-10 17:52:36.030456", + "modified": "2025-02-17 13:36:36.177743", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index b7a9602594f..850b4fa14a7 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -56,6 +56,7 @@ class StockSettings(Document): role_allowed_to_create_edit_back_dated_transactions: DF.Link | None role_allowed_to_over_deliver_receive: DF.Link | None sample_retention_warehouse: DF.Link | None + set_serial_and_batch_bundle_naming_based_on_naming_series: DF.Check show_barcode_field: DF.Check stock_auth_role: DF.Link | None stock_frozen_upto: DF.Date | None @@ -76,6 +77,7 @@ class StockSettings(Document): "default_warehouse", "set_qty_in_transactions_based_on_serial_no_input", "use_serial_batch_fields", + "set_serial_and_batch_bundle_naming_based_on_naming_series", ]: frappe.db.set_default(key, self.get(key, ""))