diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json index 5a281aaa4f..ad2889d0a0 100644 --- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json +++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json @@ -80,13 +80,16 @@ "target_warehouse", "quality_inspection", "serial_and_batch_bundle", - "batch_no", + "use_serial_batch_fields", "col_break5", "allow_zero_valuation_rate", - "serial_no", "item_tax_rate", "actual_batch_qty", "actual_qty", + "section_break_tlhi", + "serial_no", + "column_break_ciit", + "batch_no", "edit_references", "sales_order", "so_detail", @@ -628,13 +631,13 @@ "options": "Quality Inspection" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "batch_no", "fieldtype": "Link", "hidden": 1, "label": "Batch No", "options": "Batch", - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "col_break5", @@ -649,14 +652,14 @@ "print_hide": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "serial_no", "fieldtype": "Small Text", "hidden": 1, "in_list_view": 1, "label": "Serial No", "oldfieldname": "serial_no", - "oldfieldtype": "Small Text", - "read_only": 1 + "oldfieldtype": "Small Text" }, { "fieldname": "item_tax_rate", @@ -824,17 +827,33 @@ "read_only": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", "no_copy": 1, "options": "Serial and Batch Bundle", "print_hide": 1 + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:doc.use_serial_batch_fields === 1", + "fieldname": "section_break_tlhi", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_ciit", + "fieldtype": "Column Break" } ], "istable": 1, "links": [], - "modified": "2023-11-14 18:33:22.585715", + "modified": "2024-02-04 16:36:25.665743", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice Item", diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py index e2a62f1336..55a577b0c5 100644 --- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py +++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py @@ -82,6 +82,7 @@ class POSInvoiceItem(Document): target_warehouse: DF.Link | None total_weight: DF.Float uom: DF.Link + use_serial_batch_fields: DF.Check warehouse: DF.Link | None weight_per_unit: DF.Float weight_uom: DF.Link | None diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index cb0b8e1fb1..45b2482640 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -696,6 +696,7 @@ class PurchaseInvoice(BuyingController): # Updating stock ledger should always be called after updating prevdoc status, # because updating ordered qty in bin depends upon updated ordered qty in PO if self.update_stock == 1: + self.make_bundle_using_old_serial_batch_fields() self.update_stock_ledger() if self.is_old_subcontracting_flow: diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 26984d96ef..3ee4214ae7 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -62,16 +62,19 @@ "rm_supp_cost", "warehouse_section", "warehouse", - "from_warehouse", - "quality_inspection", "add_serial_batch_bundle", "serial_and_batch_bundle", - "serial_no", + "use_serial_batch_fields", "col_br_wh", + "from_warehouse", + "quality_inspection", "rejected_warehouse", "rejected_serial_and_batch_bundle", - "batch_no", + "section_break_rqbe", + "serial_no", "rejected_serial_no", + "column_break_vbbb", + "batch_no", "manufacture_details", "manufacturer", "column_break_13", @@ -440,13 +443,11 @@ "print_hide": 1 }, { - "depends_on": "eval:!doc.is_fixed_asset", + "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1", "fieldname": "batch_no", "fieldtype": "Link", - "hidden": 1, "label": "Batch No", "options": "Batch", - "read_only": 1, "search_index": 1 }, { @@ -454,21 +455,18 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:!doc.is_fixed_asset", + "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1", "fieldname": "serial_no", "fieldtype": "Text", - "hidden": 1, - "label": "Serial No", - "read_only": 1 + "label": "Serial No" }, { - "depends_on": "eval:!doc.is_fixed_asset", + "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1", "fieldname": "rejected_serial_no", "fieldtype": "Text", "label": "Rejected Serial No", "no_copy": 1, - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "accounting", @@ -891,7 +889,7 @@ "label": "Apply TDS" }, { - "depends_on": "eval:parent.update_stock == 1", + "depends_on": "eval:parent.update_stock == 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", @@ -901,7 +899,7 @@ "search_index": 1 }, { - "depends_on": "eval:parent.update_stock == 1", + "depends_on": "eval:parent.update_stock == 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)", "fieldname": "rejected_serial_and_batch_bundle", "fieldtype": "Link", "label": "Rejected Serial and Batch Bundle", @@ -916,16 +914,31 @@ "options": "Asset" }, { - "depends_on": "eval:parent.update_stock === 1", + "depends_on": "eval:parent.update_stock === 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)", "fieldname": "add_serial_batch_bundle", "fieldtype": "Button", "label": "Add Serial / Batch No" + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1", + "fieldname": "section_break_rqbe", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_vbbb", + "fieldtype": "Column Break" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2024-01-21 19:46:25.537861", + "modified": "2024-02-04 14:11:52.742228", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py index e48d22379a..ccbc34749d 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py @@ -88,6 +88,7 @@ class PurchaseInvoiceItem(Document): stock_uom_rate: DF.Currency total_weight: DF.Float uom: DF.Link + use_serial_batch_fields: DF.Check valuation_rate: DF.Currency warehouse: DF.Link | None weight_per_unit: DF.Float diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 343f3033bf..bbfe6a38d8 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -446,6 +446,7 @@ class SalesInvoice(SellingController): # Updating stock ledger should always be called after updating prevdoc status, # because updating reserved qty in bin depends upon updated delivered qty in SO if self.update_stock == 1: + self.make_bundle_using_old_serial_batch_fields() self.update_stock_ledger() # this sequence because outstanding may get -ve diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index ec9e792d7d..d06c7861da 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -83,14 +83,17 @@ "quality_inspection", "pick_serial_and_batch", "serial_and_batch_bundle", - "batch_no", - "incoming_rate", + "use_serial_batch_fields", "col_break5", "allow_zero_valuation_rate", - "serial_no", + "incoming_rate", "item_tax_rate", "actual_batch_qty", "actual_qty", + "section_break_eoec", + "serial_no", + "column_break_ytgd", + "batch_no", "edit_references", "sales_order", "so_detail", @@ -600,12 +603,11 @@ "options": "Quality Inspection" }, { + "depends_on": "eval: doc.use_serial_batch_fields === 1 && parent.update_stock === 1", "fieldname": "batch_no", "fieldtype": "Link", - "hidden": 1, "label": "Batch No", "options": "Batch", - "read_only": 1, "search_index": 1 }, { @@ -621,13 +623,12 @@ "print_hide": 1 }, { + "depends_on": "eval: doc.use_serial_batch_fields === 1 && parent.update_stock === 1", "fieldname": "serial_no", "fieldtype": "Small Text", - "hidden": 1, "label": "Serial No", "oldfieldname": "serial_no", - "oldfieldtype": "Small Text", - "read_only": 1 + "oldfieldtype": "Small Text" }, { "fieldname": "item_group", @@ -891,6 +892,7 @@ "read_only": 1 }, { + "depends_on": "eval:parent.update_stock == 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", @@ -904,12 +906,27 @@ "fieldname": "pick_serial_and_batch", "fieldtype": "Button", "label": "Pick Serial / Batch No" + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:doc.use_serial_batch_fields === 1 && parent.update_stock === 1", + "fieldname": "section_break_eoec", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_ytgd", + "fieldtype": "Column Break" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-12-29 13:03:14.121298", + "modified": "2024-02-04 11:52:16.106541", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py index 80f67748f4..c71d08e7f7 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py @@ -86,6 +86,7 @@ class SalesInvoiceItem(Document): target_warehouse: DF.Link | None total_weight: DF.Float uom: DF.Link + use_serial_batch_fields: DF.Check warehouse: DF.Link | None weight_per_unit: DF.Float weight_uom: DF.Link | None diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index cad74df51e..66014904cc 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -126,6 +126,7 @@ class AssetCapitalization(StockController): self.create_target_asset() def on_submit(self): + self.make_bundle_using_old_serial_batch_fields() self.update_stock_ledger() self.make_gl_entries() self.update_target_asset() diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json index 26e1c3c270..d301454be8 100644 --- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json +++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json @@ -18,9 +18,12 @@ "amount", "batch_and_serial_no_section", "serial_and_batch_bundle", + "use_serial_batch_fields", "column_break_13", - "batch_no", + "section_break_bfqc", "serial_no", + "column_break_mbuv", + "batch_no", "accounting_dimensions_section", "cost_center", "dimension_col_break" @@ -39,13 +42,13 @@ "reqd": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", "no_copy": 1, "options": "Batch", - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "section_break_6", @@ -102,12 +105,12 @@ "fieldtype": "Column Break" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "serial_no", "fieldtype": "Small Text", "hidden": 1, "label": "Serial No", - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "item_code", @@ -148,18 +151,34 @@ "fieldtype": "Column Break" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", "no_copy": 1, "options": "Serial and Batch Bundle", "print_hide": 1 + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:doc.use_serial_batch_fields === 1", + "fieldname": "section_break_bfqc", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_mbuv", + "fieldtype": "Column Break" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-04-06 01:10:17.947952", + "modified": "2024-02-04 16:41:09.239762", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization Stock Item", diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py index 122cbb600d..d2b075c3e6 100644 --- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py +++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py @@ -27,6 +27,7 @@ class AssetCapitalizationStockItem(Document): serial_no: DF.SmallText | None stock_qty: DF.Float stock_uom: DF.Link + use_serial_batch_fields: DF.Check valuation_rate: DF.Currency warehouse: DF.Link # end: auto-generated types diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index c8516820ef..626d341443 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -126,6 +126,54 @@ class StockController(AccountsController): # remove extra whitespace and store one serial no on each line row.serial_no = clean_serial_no_string(row.serial_no) + def make_bundle_using_old_serial_batch_fields(self): + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + from erpnext.stock.serial_batch_bundle import SerialBatchCreation + + for row in self.items: + if not row.serial_no and not row.batch_no and not row.get("rejected_serial_no"): + continue + + if not row.use_serial_batch_fields and ( + row.serial_no or row.batch_no or row.get("rejected_serial_no") + ): + frappe.throw(_("Please enable Use Old Serial / Batch Fields to make_bundle")) + + if row.use_serial_batch_fields and ( + not row.serial_and_batch_bundle or not row.get("rejected_serial_and_batch_bundle") + ): + sn_doc = SerialBatchCreation( + { + "item_code": row.item_code, + "warehouse": row.warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "voucher_type": self.doctype, + "voucher_no": self.name, + "voucher_detail_no": row.name, + "qty": row.stock_qty, + "type_of_transaction": "Inward" if row.stock_qty > 0 else "Outward", + "company": self.company, + "is_rejected": 1 if row.get("rejected_warehouse") else 0, + "serial_nos": get_serial_nos(row.serial_no) if row.serial_no else None, + "batches": frappe._dict({row.batch_no: row.stock_qty}) if row.batch_no else None, + "batch_no": row.batch_no, + "use_serial_batch_fields": row.use_serial_batch_fields, + } + ).make_serial_and_batch_bundle() + + if sn_doc.is_rejected: + row.rejected_serial_and_batch_bundle = sn_doc.name + row.db_set("rejected_serial_and_batch_bundle", sn_doc.name) + else: + row.serial_and_batch_bundle = sn_doc.name + row.db_set("serial_and_batch_bundle", sn_doc.name) + + def set_use_serial_batch_fields(self): + if frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields"): + for row in self.items: + row.use_serial_batch_fields = 1 + def get_gl_entries( self, warehouse_account=None, default_expense_account=None, default_cost_center=None ): diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 5da6d7ec61..846c071c43 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -7,6 +7,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe super.setup(); let me = this; + this.set_fields_onload_for_line_item(); this.frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle']; frappe.flags.hide_serial_batch_dialog = true; @@ -105,6 +106,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe frappe.ui.form.on(this.frm.doctype + " Item", { items_add: function(frm, cdt, cdn) { + debugger var item = frappe.get_doc(cdt, cdn); if (!item.warehouse && frm.doc.set_warehouse) { item.warehouse = frm.doc.set_warehouse; @@ -118,6 +120,13 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe item.from_warehouse = frm.doc.set_from_warehouse; } + if (item.docstatus === 0 + && frappe.meta.has_field(item.doctype, "use_serial_batch_fields") + && cint(frappe.user_defaults?.use_serial_batch_fields) === 1 + ) { + frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1); + } + erpnext.accounts.dimensions.copy_dimension_from_first_row(frm, cdt, cdn, 'items'); } }); @@ -222,7 +231,19 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe }; }); } + } + set_fields_onload_for_line_item() { + if (this.frm.is_new && this.frm.doc?.items) { + this.frm.doc.items.forEach(item => { + if (item.docstatus === 0 + && frappe.meta.has_field(item.doctype, "use_serial_batch_fields") + && cint(frappe.user_defaults?.use_serial_batch_fields) === 1 + ) { + frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1); + } + }) + } } toggle_enable_for_stock_uom(field) { @@ -462,6 +483,11 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.frm.doc.doctype === 'Delivery Note') { show_batch_dialog = 1; } + + if (show_batch_dialog && item.use_serial_batch_fields === 1) { + show_batch_dialog = 0; + } + item.barcode = null; @@ -1242,20 +1268,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } } - sync_bundle_data() { - let doctypes = ["Sales Invoice", "Purchase Invoice", "Delivery Note", "Purchase Receipt"]; - - if (this.frm.is_new() && doctypes.includes(this.frm.doc.doctype)) { - const barcode_scanner = new erpnext.utils.BarcodeScanner({frm:this.frm}); - barcode_scanner.sync_bundle_data(); - barcode_scanner.remove_item_from_localstorage(); - } - } - - before_save(doc) { - this.sync_bundle_data(); - } - service_start_date(frm, cdt, cdn) { var child = locals[cdt][cdn]; diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js index aacab0fe6c..4d1c0c1ad3 100644 --- a/erpnext/public/js/utils/barcode_scanner.js +++ b/erpnext/public/js/utils/barcode_scanner.js @@ -1,12 +1,15 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { constructor(opts) { this.frm = opts.frm; + // frappe.flags.trigger_from_barcode_scanner is used for custom scripts // field from which to capture input of scanned data this.scan_field_name = opts.scan_field_name || "scan_barcode"; this.scan_barcode_field = this.frm.fields_dict[this.scan_field_name]; this.barcode_field = opts.barcode_field || "barcode"; + this.serial_no_field = opts.serial_no_field || "serial_no"; + this.batch_no_field = opts.batch_no_field || "batch_no"; this.uom_field = opts.uom_field || "uom"; this.qty_field = opts.qty_field || "qty"; // field name on row which defines max quantity to be scanned e.g. picklist @@ -105,53 +108,52 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { this.frm.has_items = false; } - if (serial_no) { - this.is_duplicate_serial_no(row, item_code, serial_no) - .then((is_duplicate) => { - if (!is_duplicate) { - this.run_serially_tasks(row, data, resolve); - } else { - this.clean_up(); - reject(); - return; - } - }); - } else { - this.run_serially_tasks(row, data, resolve); + if (this.is_duplicate_serial_no(row, serial_no)) { + this.clean_up(); + reject(); + return; } - + frappe.run_serially([ + () => this.set_selector_trigger_flag(data), + () => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => { + this.show_scan_message(row.idx, row.item_code, qty); + }), + () => this.set_barcode_uom(row, uom), + () => this.set_serial_no(row, serial_no), + () => this.set_batch_no(row, batch_no), + () => this.set_barcode(row, barcode), + () => this.clean_up(), + () => this.revert_selector_flag(), + () => resolve(row) + ]); }); } - run_serially_tasks(row, data, resolve) { - const {item_code, barcode, batch_no, serial_no, uom} = data; + // batch and serial selector is reduandant when all info can be added by scan + // this flag on item row is used by transaction.js to avoid triggering selector + set_selector_trigger_flag(data) { + const {batch_no, serial_no, has_batch_no, has_serial_no} = data; - frappe.run_serially([ - () => this.set_serial_and_batch(row, item_code, serial_no, batch_no), - () => this.set_barcode(row, barcode), - () => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => { - this.show_scan_message(row.idx, row.item_code, qty); - }), - () => this.set_barcode_uom(row, uom), - () => this.clean_up(), - () => { - if (row.serial_and_batch_bundle && !this.frm.is_new()) { - this.frm.save(); - } + const require_selecting_batch = has_batch_no && !batch_no; + const require_selecting_serial = has_serial_no && !serial_no; - frappe.flags.trigger_from_barcode_scanner = false; - }, - () => resolve(row), - ]); + if (!(require_selecting_batch || require_selecting_serial)) { + frappe.flags.hide_serial_batch_dialog = true; + } + } + + revert_selector_flag() { + frappe.flags.hide_serial_batch_dialog = false; + frappe.flags.trigger_from_barcode_scanner = false; } set_item(row, item_code, barcode, batch_no, serial_no) { return new Promise(resolve => { const increment = async (value = 1) => { - const item_data = {item_code: item_code}; - item_data[this.qty_field] = Number((row[this.qty_field] || 0)) + Number(value); + const item_data = {item_code: item_code, use_serial_batch_fields: 1.0}; frappe.flags.trigger_from_barcode_scanner = true; + item_data[this.qty_field] = Number((row[this.qty_field] || 0)) + Number(value); await frappe.model.set_value(row.doctype, row.name, item_data); return value; }; @@ -160,6 +162,8 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { frappe.prompt(__("Please enter quantity for item {0}", [item_code]), ({value}) => { increment(value).then((value) => resolve(value)); }); + } else if (this.frm.has_items) { + this.prepare_item_for_scan(row, item_code, barcode, batch_no, serial_no); } else { increment().then((value) => resolve(value)); } @@ -182,8 +186,9 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { frappe.model.set_value(row.doctype, row.name, item_data); frappe.run_serially([ + () => this.set_batch_no(row, this.dialog.get_value("batch_no")), () => this.set_barcode(row, this.dialog.get_value("barcode")), - () => this.set_serial_and_batch(row, item_code, this.dialog.get_value("serial_no"), this.dialog.get_value("batch_no")), + () => this.set_serial_no(row, this.dialog.get_value("serial_no")), () => this.add_child_for_remaining_qty(row), () => this.clean_up() ]); @@ -337,144 +342,32 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } } - async set_serial_and_batch(row, item_code, serial_no, batch_no) { - if (this.frm.is_new() || !row.serial_and_batch_bundle) { - this.set_bundle_in_localstorage(row, item_code, serial_no, batch_no); - } else if(row.serial_and_batch_bundle) { - frappe.call({ - method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.update_serial_or_batch", - args: { - bundle_id: row.serial_and_batch_bundle, - serial_no: serial_no, - batch_no: batch_no, - }, - }) - } - } + async set_serial_no(row, serial_no) { + if (serial_no && frappe.meta.has_field(row.doctype, this.serial_no_field)) { + const existing_serial_nos = row[this.serial_no_field]; + let new_serial_nos = ""; - get_key_for_localstorage() { - let parts = this.frm.doc.name.split("-"); - return parts[parts.length - 1] + this.frm.doc.doctype; - } - - update_localstorage_scanned_data() { - let docname = this.frm.doc.name - if (localStorage[docname]) { - let items = JSON.parse(localStorage[docname]); - let existing_items = this.frm.doc.items.map(d => d.item_code); - if (!existing_items.length) { - localStorage.removeItem(docname); - return; + if (!!existing_serial_nos) { + new_serial_nos = existing_serial_nos + "\n" + serial_no; + } else { + new_serial_nos = serial_no; } - - for (let item_code in items) { - if (!existing_items.includes(item_code)) { - delete items[item_code]; - } - } - - localStorage[docname] = JSON.stringify(items); + await frappe.model.set_value(row.doctype, row.name, this.serial_no_field, new_serial_nos); } } - async set_bundle_in_localstorage(row, item_code, serial_no, batch_no) { - let docname = this.frm.doc.name - - let entries = JSON.parse(localStorage.getItem(docname)); - if (!entries) { - entries = {}; - } - - let key = item_code; - if (!entries[key]) { - entries[key] = []; - } - - let existing_row = []; - if (!serial_no && batch_no) { - existing_row = entries[key].filter((e) => e.batch_no === batch_no); - if (existing_row.length) { - existing_row[0].qty += 1; - } - } else if (serial_no) { - existing_row = entries[key].filter((e) => e.serial_no === serial_no); - if (existing_row.length) { - frappe.throw(__("Serial No {0} has already scanned.", [serial_no])); - } - } - - if (!existing_row.length) { - entries[key].push({ - "serial_no": serial_no, - "batch_no": batch_no, - "qty": 1 - }); - } - - localStorage.setItem(docname, JSON.stringify(entries)); - - // Auto remove from localstorage after 1 hour - setTimeout(() => { - localStorage.removeItem(docname); - }, 3600000) - } - - remove_item_from_localstorage() { - let docname = this.frm.doc.name; - if (localStorage[docname]) { - localStorage.removeItem(docname); - } - } - - async sync_bundle_data() { - let docname = this.frm.doc.name; - - if (localStorage[docname]) { - let entries = JSON.parse(localStorage[docname]); - if (entries) { - for (let entry in entries) { - let row = this.frm.doc.items.filter((item) => { - if (item.item_code === entry) { - return true; - } - })[0]; - - if (row) { - this.create_serial_and_batch_bundle(row, entries, entry) - .then(() => { - if (!entries) { - localStorage.removeItem(docname); - } - }); - } - } - } - } - } - - async create_serial_and_batch_bundle(row, entries, key) { - frappe.call({ - method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.add_serial_batch_ledgers", - args: { - entries: entries[key], - child_row: row, - doc: this.frm.doc, - warehouse: row.warehouse, - do_not_save: 1 - }, - callback: function(r) { - row.serial_and_batch_bundle = r.message.name; - delete entries[key]; - } - }) - } - async set_barcode_uom(row, uom) { if (uom && frappe.meta.has_field(row.doctype, this.uom_field)) { await frappe.model.set_value(row.doctype, row.name, this.uom_field, uom); } } + async set_batch_no(row, batch_no) { + if (batch_no && frappe.meta.has_field(row.doctype, this.batch_no_field)) { + await frappe.model.set_value(row.doctype, row.name, this.batch_no_field, batch_no); + } + } + async set_barcode(row, barcode) { if (barcode && frappe.meta.has_field(row.doctype, this.barcode_field)) { await frappe.model.set_value(row.doctype, row.name, this.barcode_field, barcode); @@ -490,58 +383,13 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } } - async is_duplicate_serial_no(row, item_code, serial_no) { - let is_duplicate = false; - const promise = new Promise((resolve, reject) => { - if (this.frm.is_new() || !row.serial_and_batch_bundle) { - is_duplicate = this.check_duplicate_serial_no_in_localstorage(item_code, serial_no); - if (is_duplicate) { - this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange"); - } + is_duplicate_serial_no(row, serial_no) { + const is_duplicate = row[this.serial_no_field]?.includes(serial_no); - resolve(is_duplicate); - } else if (row.serial_and_batch_bundle) { - this.check_duplicate_serial_no_in_db(row, serial_no, (r) => { - if (r.message) { - this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange"); - } - - is_duplicate = r.message; - resolve(is_duplicate); - }) - } - }); - - return await promise; - } - - check_duplicate_serial_no_in_db(row, serial_no, response) { - frappe.call({ - method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.is_duplicate_serial_no", - args: { - serial_no: serial_no, - bundle_id: row.serial_and_batch_bundle - }, - callback(r) { - response(r); - } - }); - } - - check_duplicate_serial_no_in_localstorage(item_code, serial_no) { - let docname = this.frm.doc.name - let entries = JSON.parse(localStorage.getItem(docname)); - - if (!entries) { - return false; + if (is_duplicate) { + this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange"); } - - let existing_row = []; - if (entries[item_code]) { - existing_row = entries[item_code].filter((e) => e.serial_no === serial_no); - } - - return existing_row.length; + return is_duplicate; } get_row_to_modify_on_scan(item_code, batch_no, uom, barcode) { @@ -587,4 +435,4 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { show_alert(msg, indicator, duration=3) { frappe.show_alert({message: msg, indicator: indicator}, duration); } -}; +}; \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 5ef2c50146..f00e6ac512 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -904,6 +904,7 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None): target.run_method("set_missing_values") target.run_method("set_po_nos") target.run_method("calculate_taxes_and_totals") + target.run_method("set_use_serial_batch_fields") if source.company_address: target.update({"company_address": source.company_address}) @@ -1024,6 +1025,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): target.run_method("set_missing_values") target.run_method("set_po_nos") target.run_method("calculate_taxes_and_totals") + target.run_method("set_use_serial_batch_fields") if source.company_address: target.update({"company_address": source.company_address}) @@ -1606,7 +1608,11 @@ def create_pick_list(source_name, target_doc=None): "Sales Order", source_name, { - "Sales Order": {"doctype": "Pick List", "validation": {"docstatus": ["=", 1]}}, + "Sales Order": { + "doctype": "Pick List", + "field_map": {"set_warehouse": "parent_warehouse"}, + "validation": {"docstatus": ["=", 1]}, + }, "Sales Order Item": { "doctype": "Pick List Item", "field_map": {"parent": "sales_order", "name": "sales_order_item"}, diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 7d7b0cd476..df45fdded8 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -398,6 +398,8 @@ class DeliveryNote(SellingController): self.check_credit_limit() elif self.issue_credit_note: self.make_return_invoice() + + self.make_bundle_using_old_serial_batch_fields() # Updating stock ledger should always be called after updating prevdoc status, # because updating reserved qty in bin depends upon updated delivered qty in SO self.update_stock_ledger() diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index a44b9ac44b..247672fe12 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -80,8 +80,11 @@ "section_break_40", "pick_serial_and_batch", "serial_and_batch_bundle", + "use_serial_batch_fields", "column_break_eaoe", + "section_break_qyjv", "serial_no", + "column_break_rxvc", "batch_no", "available_qty_section", "actual_batch_qty", @@ -850,6 +853,7 @@ "read_only": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", @@ -859,6 +863,7 @@ "search_index": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1", "fieldname": "pick_serial_and_batch", "fieldtype": "Button", "label": "Pick Serial / Batch No" @@ -874,27 +879,40 @@ "fieldtype": "Column Break" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "serial_no", "fieldtype": "Text", - "hidden": 1, - "label": "Serial No", - "read_only": 1 + "label": "Serial No" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "batch_no", "fieldtype": "Link", - "hidden": 1, "label": "Batch No", "options": "Batch", - "read_only": 1, "search_index": 1 + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:doc.use_serial_batch_fields === 1", + "fieldname": "section_break_qyjv", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_rxvc", + "fieldtype": "Column Break" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:37:38.638144", + "modified": "2024-02-04 14:10:31.750340", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py index c11c4103e5..b76f742972 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py @@ -82,6 +82,7 @@ class DeliveryNoteItem(Document): target_warehouse: DF.Link | None total_weight: DF.Float uom: DF.Link + use_serial_batch_fields: DF.Check warehouse: DF.Link | None weight_per_unit: DF.Float weight_uom: DF.Link | None diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json index 5dd8934d43..0b006ab363 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.json +++ b/erpnext/stock/doctype/packed_item/packed_item.json @@ -20,9 +20,12 @@ "uom", "section_break_9", "pick_serial_and_batch", - "serial_and_batch_bundle", - "serial_no", + "use_serial_batch_fields", "column_break_11", + "serial_and_batch_bundle", + "section_break_bgys", + "serial_no", + "column_break_qlha", "batch_no", "actual_batch_qty", "section_break_13", @@ -118,10 +121,10 @@ "fieldtype": "Section Break" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "serial_no", "fieldtype": "Text", - "label": "Serial No", - "read_only": 1 + "label": "Serial No" }, { "fieldname": "column_break_11", @@ -131,8 +134,7 @@ "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", - "options": "Batch", - "read_only": 1 + "options": "Batch" }, { "fieldname": "section_break_13", @@ -259,6 +261,7 @@ "read_only": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", @@ -267,16 +270,32 @@ "print_hide": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "pick_serial_and_batch", "fieldtype": "Button", "label": "Pick Serial / Batch No" + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:doc.use_serial_batch_fields === 1", + "fieldname": "section_break_bgys", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_qlha", + "fieldtype": "Column Break" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-04-28 13:16:38.460806", + "modified": "2024-02-04 16:30:44.263964", "modified_by": "Administrator", "module": "Stock", "name": "Packed Item", diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py index ed667c2b99..c115e33e17 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.py +++ b/erpnext/stock/doctype/packed_item/packed_item.py @@ -47,6 +47,7 @@ class PackedItem(Document): serial_no: DF.Text | None target_warehouse: DF.Link | None uom: DF.Link | None + use_serial_batch_fields: DF.Check warehouse: DF.Link | None # end: auto-generated types diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index afd6ce8138..aa0e125496 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -16,7 +16,6 @@ frappe.ui.form.on('Pick List', { frm.set_query('parent_warehouse', () => { return { filters: { - 'is_group': 1, 'company': frm.doc.company } }; diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 7259dc00a8..bd84aadef7 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -51,7 +51,7 @@ "description": "Items under this warehouse will be suggested", "fieldname": "parent_warehouse", "fieldtype": "Link", - "label": "Parent Warehouse", + "label": "Warehouse", "options": "Warehouse" }, { @@ -188,7 +188,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2023-01-24 10:33:43.244476", + "modified": "2024-02-01 16:17:44.877426", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 758448af79..f2edeea56d 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -13,7 +13,7 @@ from frappe.model.mapper import map_child_doc from frappe.query_builder import Case from frappe.query_builder.custom import GROUP_CONCAT from frappe.query_builder.functions import Coalesce, Locate, Replace, Sum -from frappe.utils import cint, floor, flt +from frappe.utils import ceil, cint, floor, flt from frappe.utils.nestedset import get_descendants_of from erpnext.selling.doctype.sales_order.sales_order import ( @@ -122,11 +122,43 @@ class PickList(Document): def on_submit(self): self.validate_serial_and_batch_bundle() + self.make_bundle_using_old_serial_batch_fields() self.update_status() self.update_bundle_picked_qty() self.update_reference_qty() self.update_sales_order_picking_status() + def make_bundle_using_old_serial_batch_fields(self): + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + + for row in self.locations: + if not row.serial_no and not row.batch_no: + continue + + if not row.use_serial_batch_fields and (row.serial_no or row.batch_no): + frappe.throw(_("Please enable Use Old Serial / Batch Fields to make_bundle")) + + if row.use_serial_batch_fields and (not row.serial_and_batch_bundle): + sn_doc = SerialBatchCreation( + { + "item_code": row.item_code, + "warehouse": row.warehouse, + "voucher_type": self.doctype, + "voucher_no": self.name, + "voucher_detail_no": row.name, + "qty": row.stock_qty, + "type_of_transaction": "Inward" if row.stock_qty > 0 else "Outward", + "company": self.company, + "serial_nos": get_serial_nos(row.serial_no) if row.serial_no else None, + "batches": frappe._dict({row.batch_no: row.stock_qty}) if row.batch_no else None, + "batch_no": row.batch_no, + "use_serial_batch_fields": row.use_serial_batch_fields, + } + ).make_serial_and_batch_bundle() + + row.serial_and_batch_bundle = sn_doc.name + row.db_set("serial_and_batch_bundle", sn_doc.name) + def on_update_after_submit(self) -> None: if self.has_reserved_stock(): msg = _( @@ -317,8 +349,12 @@ class PickList(Document): self.item_location_map = frappe._dict() from_warehouses = None - if self.parent_warehouse: + if self.parent_warehouse and frappe.get_cached_value( + "Warehouse", self.parent_warehouse, "is_group" + ): from_warehouses = get_descendants_of("Warehouse", self.parent_warehouse) + elif self.parent_warehouse: + from_warehouses = [self.parent_warehouse] # Create replica before resetting, to handle empty table on update after submit. locations_replica = self.get("locations") @@ -351,13 +387,13 @@ class PickList(Document): for row in locations: location = item_doc.as_dict() location.update(row) + bundle = location.serial_and_batch_bundle or location.serial_no or location.batch_no key = ( location.item_code, location.warehouse, location.uom, - location.batch_no, - location.serial_no, location.sales_order_item or location.material_request_item, + bundle, ) if key not in updated_locations: @@ -645,7 +681,9 @@ def get_items_with_location_and_quantity(item_doc, item_location_map, docstatus) "qty": qty, "stock_qty": stock_qty, "warehouse": item_location.warehouse, - "serial_and_batch_bundle": item_location.serial_and_batch_bundle, + "serial_no": item_location.serial_no, + "batch_no": item_location.batch_no, + "use_serial_batch_fields": 1, } ) ) @@ -673,6 +711,7 @@ def get_available_item_locations( company, ignore_validation=False, picked_item_details=None, + consider_rejected_warehouses=False, ): locations = [] total_picked_qty = ( @@ -681,7 +720,16 @@ def get_available_item_locations( has_serial_no = frappe.get_cached_value("Item", item_code, "has_serial_no") has_batch_no = frappe.get_cached_value("Item", item_code, "has_batch_no") - if has_serial_no: + if has_batch_no and has_serial_no: + locations = get_available_item_locations_for_serial_and_batched_item( + item_code, + from_warehouses, + required_qty, + company, + total_picked_qty, + consider_rejected_warehouses=consider_rejected_warehouses, + ) + elif has_serial_no: locations = get_available_item_locations_for_serialized_item( item_code, from_warehouses, required_qty, company, total_picked_qty ) @@ -724,6 +772,49 @@ def get_available_item_locations( return locations +def get_available_item_locations_for_serial_and_batched_item( + item_code, + from_warehouses, + required_qty, + company, + total_picked_qty=0, + consider_rejected_warehouses=False, +): + # Get batch nos by FIFO + locations = get_available_item_locations_for_batched_item( + item_code, + from_warehouses, + required_qty, + company, + consider_rejected_warehouses=consider_rejected_warehouses, + ) + + if locations: + sn = frappe.qb.DocType("Serial No") + conditions = (sn.item_code == item_code) & (sn.company == company) + + for location in locations: + location.qty = ( + required_qty if location.qty > required_qty else location.qty + ) # if extra qty in batch + + serial_nos = ( + frappe.qb.from_(sn) + .select(sn.name) + .where( + (conditions) & (sn.batch_no == location.batch_no) & (sn.warehouse == location.warehouse) + ) + .orderby(sn.purchase_date) + .limit(ceil(location.qty + total_picked_qty)) + ).run(as_dict=True) + + serial_nos = [sn.name for sn in serial_nos] + location.serial_no = serial_nos + location.qty = len(serial_nos) + + return locations + + def get_available_item_locations_for_serialized_item( item_code, from_warehouses, required_qty, company, total_picked_qty=0 ): @@ -760,25 +851,12 @@ def get_available_item_locations_for_serialized_item( for warehouse, serial_nos in warehouse_serial_nos_map.items(): qty = len(serial_nos) - bundle_doc = SerialBatchCreation( - { - "item_code": item_code, - "warehouse": warehouse, - "voucher_type": "Pick List", - "total_qty": qty * -1, - "serial_nos": serial_nos, - "type_of_transaction": "Outward", - "company": company, - "do_not_submit": True, - } - ).make_serial_and_batch_bundle() - locations.append( { "qty": qty, "warehouse": warehouse, "item_code": item_code, - "serial_and_batch_bundle": bundle_doc.name, + "serial_nos": serial_nos, } ) @@ -808,29 +886,15 @@ def get_available_item_locations_for_batched_item( warehouse_wise_batches[d.warehouse][d.batch_no] += d.qty for warehouse, batches in warehouse_wise_batches.items(): - qty = sum(batches.values()) - - bundle_doc = SerialBatchCreation( - { - "item_code": item_code, - "warehouse": warehouse, - "voucher_type": "Pick List", - "total_qty": qty * -1, - "batches": batches, - "type_of_transaction": "Outward", - "company": company, - "do_not_submit": True, - } - ).make_serial_and_batch_bundle() - - locations.append( - { - "qty": qty, - "warehouse": warehouse, - "item_code": item_code, - "serial_and_batch_bundle": bundle_doc.name, - } - ) + for batch_no, qty in batches.items(): + locations.append( + { + "qty": qty, + "warehouse": warehouse, + "item_code": item_code, + "batch_no": batch_no, + } + ) return locations diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json index e8e4afc6e3..c8001fd508 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -24,8 +24,11 @@ "serial_no_and_batch_section", "pick_serial_and_batch", "serial_and_batch_bundle", - "serial_no", + "use_serial_batch_fields", "column_break_20", + "section_break_ecxc", + "serial_no", + "column_break_belw", "batch_no", "column_break_15", "sales_order", @@ -72,19 +75,17 @@ "read_only": 1 }, { - "depends_on": "serial_no", + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "serial_no", "fieldtype": "Small Text", - "label": "Serial No", - "read_only": 1 + "label": "Serial No" }, { - "depends_on": "batch_no", + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", "options": "Batch", - "read_only": 1, "search_index": 1 }, { @@ -195,6 +196,7 @@ "read_only": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", @@ -204,6 +206,7 @@ "search_index": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "pick_serial_and_batch", "fieldtype": "Button", "label": "Pick Serial / Batch No" @@ -218,11 +221,26 @@ "print_hide": 1, "read_only": 1, "report_hide": 1 + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:doc.use_serial_batch_fields === 1", + "fieldname": "section_break_ecxc", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_belw", + "fieldtype": "Column Break" } ], "istable": 1, "links": [], - "modified": "2023-07-26 12:54:15.785962", + "modified": "2024-02-04 16:12:16.257951", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.py b/erpnext/stock/doctype/pick_list_item/pick_list_item.py index 6e5a94e446..f3f6298a30 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.py +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.py @@ -37,6 +37,7 @@ class PickListItem(Document): stock_reserved_qty: DF.Float stock_uom: DF.Link | None uom: DF.Link | None + use_serial_batch_fields: DF.Check warehouse: DF.Link | None # end: auto-generated types diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 8da0596631..28d55f6ce3 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -368,6 +368,7 @@ class PurchaseReceipt(BuyingController): else: self.db_set("status", "Completed") + self.make_bundle_using_old_serial_batch_fields() # Updating stock ledger should always be called after updating prevdoc status, # because updating ordered qty, reserved_qty_for_subcontract in bin # depends upon updated ordered qty in PO diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 9bd692ad61..6b01047f00 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -94,6 +94,7 @@ "section_break_45", "add_serial_batch_bundle", "serial_and_batch_bundle", + "use_serial_batch_fields", "col_break5", "add_serial_batch_for_rejected_qty", "rejected_serial_and_batch_bundle", @@ -1003,6 +1004,7 @@ "read_only": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", @@ -1020,24 +1022,22 @@ { "fieldname": "serial_no", "fieldtype": "Text", - "label": "Serial No", - "read_only": 1 + "label": "Serial No" }, { "fieldname": "rejected_serial_no", "fieldtype": "Text", - "label": "Rejected Serial No", - "read_only": 1 + "label": "Rejected Serial No" }, { "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", "options": "Batch", - "read_only": 1, "search_index": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1", "fieldname": "rejected_serial_and_batch_bundle", "fieldtype": "Link", "label": "Rejected Serial and Batch Bundle", @@ -1045,11 +1045,13 @@ "options": "Serial and Batch Bundle" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "add_serial_batch_for_rejected_qty", "fieldtype": "Button", "label": "Add Serial / Batch No (Rejected Qty)" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "section_break_3vxt", "fieldtype": "Section Break" }, @@ -1058,6 +1060,7 @@ "fieldtype": "Column Break" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "add_serial_batch_bundle", "fieldtype": "Button", "label": "Add Serial / Batch No" @@ -1098,12 +1101,18 @@ "read_only": 1, "report_hide": 1, "search_index": 1 + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-12-25 22:32:09.801965", + "modified": "2024-02-04 11:48:06.653771", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py index aed8d21dae..3c6dcdca48 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py @@ -99,6 +99,7 @@ class PurchaseReceiptItem(Document): supplier_part_no: DF.Data | None total_weight: DF.Float uom: DF.Link + use_serial_batch_fields: DF.Check valuation_rate: DF.Currency warehouse: DF.Link | None weight_per_unit: DF.Float 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 9cad8f62b8..ea33c54544 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 @@ -1677,7 +1677,10 @@ def get_reserved_batches_for_sre(kwargs) -> dict: query = query.where(sb_entry.batch_no == kwargs.batch_no) if kwargs.warehouse: - query = query.where(sre.warehouse == kwargs.warehouse) + if isinstance(kwargs.warehouse, list): + query = query.where(sre.warehouse.isin(kwargs.warehouse)) + else: + query = query.where(sre.warehouse == kwargs.warehouse) if kwargs.ignore_voucher_nos: query = query.where(sre.name.notin(kwargs.ignore_voucher_nos)) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 00cc8be4bb..4239191383 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -274,6 +274,7 @@ class StockEntry(StockController): def on_submit(self): self.validate_closed_subcontracting_order() + self.make_bundle_using_old_serial_batch_fields() self.update_stock_ledger() self.update_work_order() self.validate_subcontract_order() diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index bd84a2b0d9..bd11d0b861 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -47,9 +47,12 @@ "amount", "serial_no_batch", "add_serial_batch_bundle", - "serial_and_batch_bundle", + "use_serial_batch_fields", "col_break4", + "serial_and_batch_bundle", + "section_break_rdtg", "serial_no", + "column_break_prps", "batch_no", "accounting", "expense_account", @@ -289,27 +292,27 @@ "no_copy": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "serial_no", "fieldtype": "Small Text", "label": "Serial No", "no_copy": 1, "oldfieldname": "serial_no", - "oldfieldtype": "Text", - "read_only": 1 + "oldfieldtype": "Text" }, { "fieldname": "col_break4", "fieldtype": "Column Break" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", "no_copy": 1, "oldfieldname": "batch_no", "oldfieldtype": "Link", - "options": "Batch", - "read_only": 1 + "options": "Batch" }, { "depends_on": "eval:parent.inspection_required && doc.t_warehouse", @@ -573,24 +576,41 @@ "read_only": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "add_serial_batch_bundle", "fieldtype": "Button", "label": "Add Serial / Batch No" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", "no_copy": 1, "options": "Serial and Batch Bundle", "print_hide": 1 + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:doc.use_serial_batch_fields === 1", + "fieldname": "section_break_rdtg", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_prps", + "fieldtype": "Column Break" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-01-12 11:56:04.626103", + "modified": "2024-02-04 16:16:47.606270", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py index a6dd0faadf..47c443c519 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py @@ -63,6 +63,7 @@ class StockEntryDetail(Document): transfer_qty: DF.Float transferred_qty: DF.Float uom: DF.Link + use_serial_batch_fields: DF.Check valuation_rate: DF.Currency # end: auto-generated types diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 788ae0d3ab..cc8a7c57b3 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -99,6 +99,7 @@ class StockReconciliation(StockController): ) def on_submit(self): + self.make_bundle_using_old_serial_batch_fields() self.update_stock_ledger() self.make_gl_entries() self.repost_future_sle_and_gle() diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json index fc4ae6a5fa..27693d2f1b 100644 --- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json +++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json @@ -19,11 +19,14 @@ "allow_zero_valuation_rate", "serial_no_and_batch_section", "add_serial_batch_bundle", - "serial_and_batch_bundle", - "batch_no", + "use_serial_batch_fields", "column_break_11", + "serial_and_batch_bundle", "current_serial_and_batch_bundle", + "section_break_lypk", "serial_no", + "column_break_eefq", + "batch_no", "section_break_3", "current_qty", "current_amount", @@ -103,10 +106,10 @@ "label": "Serial No and Batch" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "serial_no", "fieldtype": "Long Text", - "label": "Serial No", - "read_only": 1 + "label": "Serial No" }, { "fieldname": "column_break_11", @@ -171,11 +174,11 @@ "read_only": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", "options": "Batch", - "read_only": 1, "search_index": 1 }, { @@ -195,6 +198,7 @@ "read_only": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial / Batch Bundle", @@ -204,6 +208,7 @@ "search_index": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "current_serial_and_batch_bundle", "fieldtype": "Link", "label": "Current Serial / Batch Bundle", @@ -212,6 +217,7 @@ "read_only": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "add_serial_batch_bundle", "fieldtype": "Button", "label": "Add Serial / Batch No" @@ -222,11 +228,26 @@ "fieldtype": "Link", "label": "Item Group", "options": "Item Group" + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:doc.use_serial_batch_fields === 1", + "fieldname": "section_break_lypk", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_eefq", + "fieldtype": "Column Break" } ], "istable": 1, "links": [], - "modified": "2024-01-14 10:04:23.599951", + "modified": "2024-02-04 16:19:44.576022", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation Item", diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py index c82cdf58de..1938fec32b 100644 --- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py +++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py @@ -26,6 +26,7 @@ class StockReconciliationItem(Document): current_valuation_rate: DF.Currency has_item_scanned: DF.Data | None item_code: DF.Link + item_group: DF.Link | None item_name: DF.Data | None parent: DF.Data parentfield: DF.Data @@ -34,6 +35,7 @@ class StockReconciliationItem(Document): quantity_difference: DF.ReadOnly | None serial_and_batch_bundle: DF.Link | None serial_no: DF.LongText | None + use_serial_batch_fields: DF.Check valuation_rate: DF.Currency warehouse: DF.Link # end: auto-generated types diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index 40fac4113d..32ef46915b 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -50,6 +50,7 @@ "disable_serial_no_and_batch_selector", "use_naming_series", "naming_series_prefix", + "use_serial_batch_fields", "stock_planning_tab", "auto_material_request", "auto_indent", @@ -420,6 +421,12 @@ "fieldname": "auto_reserve_stock_for_sales_order_on_purchase", "fieldtype": "Check", "label": "Auto Reserve Stock for Sales Order on Purchase" + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial / Batch Fields" } ], "icon": "icon-cog", @@ -427,7 +434,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-01-30 14:03:52.143457", + "modified": "2024-02-04 12:01:31.931864", "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 088c7cdfe1..c4960aa67a 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -57,6 +57,7 @@ class StockSettings(Document): stock_uom: DF.Link | None update_existing_price_list_rate: DF.Check use_naming_series: DF.Check + use_serial_batch_fields: DF.Check valuation_method: DF.Literal["FIFO", "Moving Average", "LIFO"] # end: auto-generated types @@ -68,6 +69,7 @@ class StockSettings(Document): "allow_negative_stock", "default_warehouse", "set_qty_in_transactions_based_on_serial_no_input", + "use_serial_batch_fields", ]: frappe.db.set_default(key, self.get(key, "")) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 78df755d74..d8b5b34d44 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -794,6 +794,9 @@ class SerialBatchCreation: setattr(self, "actual_qty", qty) self.__dict__["actual_qty"] = self.actual_qty + if not hasattr(self, "use_serial_batch_fields"): + setattr(self, "use_serial_batch_fields", 0) + def duplicate_package(self): if not self.serial_and_batch_bundle: return @@ -902,9 +905,14 @@ class SerialBatchCreation: self.batches = get_available_batches(kwargs) def set_auto_serial_batch_entries_for_inward(self): + print(self.get("serial_nos")) + if (self.get("batches") and self.has_batch_no) or ( self.get("serial_nos") and self.has_serial_no ): + if self.use_serial_batch_fields and self.get("serial_nos"): + self.make_serial_no_if_not_exists() + return self.batch_no = None @@ -916,6 +924,59 @@ class SerialBatchCreation: else: self.batches = frappe._dict({self.batch_no: abs(self.actual_qty)}) + def make_serial_no_if_not_exists(self): + non_exists_serial_nos = [] + for row in self.serial_nos: + if not frappe.db.exists("Serial No", row): + non_exists_serial_nos.append(row) + + if non_exists_serial_nos: + self.make_serial_nos(non_exists_serial_nos) + + def make_serial_nos(self, serial_nos): + serial_nos_details = [] + batch_no = None + if self.batches: + batch_no = list(self.batches.keys())[0] + + for serial_no in serial_nos: + serial_nos_details.append( + ( + serial_no, + serial_no, + now(), + now(), + frappe.session.user, + frappe.session.user, + self.warehouse, + self.company, + self.item_code, + self.item_name, + self.description, + "Active", + batch_no, + ) + ) + + if serial_nos_details: + fields = [ + "name", + "serial_no", + "creation", + "modified", + "owner", + "modified_by", + "warehouse", + "company", + "item_code", + "item_name", + "description", + "status", + "batch_no", + ] + + frappe.db.bulk_insert("Serial No", fields=fields, values=set(serial_nos_details)) + def set_serial_batch_entries(self, doc): if self.get("serial_nos"): serial_no_wise_batch = frappe._dict({}) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 475b603078..8d82709e75 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -149,6 +149,7 @@ class SubcontractingReceipt(SubcontractingController): self.update_prevdoc_status() self.set_subcontracting_order_status() self.set_consumed_qty_in_subcontract_order() + self.make_bundle_using_old_serial_batch_fields() self.update_stock_ledger() self.make_gl_entries() self.repost_future_sle_and_gle() diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index 9bfc2fdb7a..9d36359944 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -48,11 +48,14 @@ "reference_name", "section_break_45", "serial_and_batch_bundle", - "serial_no", + "use_serial_batch_fields", "col_break5", "rejected_serial_and_batch_bundle", - "batch_no", + "section_break_jshh", + "serial_no", "rejected_serial_no", + "column_break_henr", + "batch_no", "manufacture_details", "manufacturer", "column_break_16", @@ -311,22 +314,20 @@ "label": "Serial and Batch Details" }, { - "depends_on": "eval:!doc.is_fixed_asset", + "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1", "fieldname": "serial_no", "fieldtype": "Small Text", "label": "Serial No", - "no_copy": 1, - "read_only": 1 + "no_copy": 1 }, { - "depends_on": "eval:!doc.is_fixed_asset", + "depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1", "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", "no_copy": 1, "options": "Batch", - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "depends_on": "eval: !parent.is_return", @@ -478,6 +479,7 @@ "label": "Accounting Details" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", @@ -486,6 +488,7 @@ "print_hide": 1 }, { + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "rejected_serial_and_batch_bundle", "fieldtype": "Link", "label": "Rejected Serial and Batch Bundle", @@ -546,12 +549,27 @@ "fieldtype": "Check", "label": "Include Exploded Items", "print_hide": 1 + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:doc.use_serial_batch_fields === 1", + "fieldname": "section_break_jshh", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_henr", + "fieldtype": "Column Break" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-11-30 12:05:51.920705", + "modified": "2024-02-04 16:23:30.374865", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py index d02160ece4..1a4ce5b977 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py @@ -58,6 +58,7 @@ class SubcontractingReceiptItem(Document): subcontracting_order: DF.Link | None subcontracting_order_item: DF.Data | None subcontracting_receipt_item: DF.Data | None + use_serial_batch_fields: DF.Check warehouse: DF.Link | None # end: auto-generated types diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json index 90bcf4e544..1c8e9dd227 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json @@ -26,10 +26,13 @@ "current_stock", "secbreak_3", "serial_and_batch_bundle", - "batch_no", + "use_serial_batch_fields", "col_break4", + "subcontracting_order", + "section_break_zwnh", "serial_no", - "subcontracting_order" + "column_break_qibi", + "batch_no" ], "fields": [ { @@ -60,19 +63,19 @@ "width": "300px" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", "no_copy": 1, - "options": "Batch", - "read_only": 1 + "options": "Batch" }, { + "depends_on": "eval:doc.use_serial_batch_fields === 1", "fieldname": "serial_no", "fieldtype": "Text", "label": "Serial No", - "no_copy": 1, - "read_only": 1 + "no_copy": 1 }, { "fieldname": "col_break1", @@ -198,6 +201,7 @@ }, { "columns": 2, + "depends_on": "eval:doc.use_serial_batch_fields === 0", "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "in_list_view": 1, @@ -205,12 +209,27 @@ "no_copy": 1, "options": "Serial and Batch Bundle", "print_hide": 1 + }, + { + "default": "0", + "fieldname": "use_serial_batch_fields", + "fieldtype": "Check", + "label": "Use Serial No / Batch Fields" + }, + { + "depends_on": "eval:doc.use_serial_batch_fields === 1", + "fieldname": "section_break_zwnh", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_qibi", + "fieldtype": "Column Break" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-03-15 13:55:08.132626", + "modified": "2024-02-04 16:32:17.534162", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Supplied Item", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py index 2ee55518d5..8f09197aa8 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py @@ -35,6 +35,7 @@ class SubcontractingReceiptSuppliedItem(Document): serial_no: DF.Text | None stock_uom: DF.Link | None subcontracting_order: DF.Link | None + use_serial_batch_fields: DF.Check # end: auto-generated types pass