From ba1aac1613f182d40a4d19f3ddcda381b44a4527 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 24 Nov 2022 17:16:21 +0530 Subject: [PATCH] chore: used frappe.db.bulk_insert to create serial nos and serial bunndle --- .../js/utils/serial_no_batch_selector.js | 4 +- .../purchase_receipt_item.json | 1 + .../serial_and_batch_bundle.py | 4 +- erpnext/stock/doctype/serial_no/serial_no.py | 151 +++++++++++++++++- .../stock_ledger_entry/stock_ledger_entry.py | 3 +- 5 files changed, 152 insertions(+), 11 deletions(-) diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 1c98037509..90967d93b5 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -771,7 +771,7 @@ erpnext.SerialNoBatchBundleUpdate = class SerialNoBatchBundleUpdate { } frappe.call({ - method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.add_serial_batch_no_ledgers', + method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.add_serial_batch_ledgers', args: { ledgers: ledgers, child_row: this.item @@ -786,7 +786,7 @@ erpnext.SerialNoBatchBundleUpdate = class SerialNoBatchBundleUpdate { render_data() { if (!this.frm.is_new() && this.item.serial_and_batch_bundle) { frappe.call({ - method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.get_serial_batch_no_ledgers', + method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.get_serial_batch_ledgers', args: { item_code: this.item.item_code, name: this.item.serial_and_batch_bundle, 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 97e7d72bb0..900fb75a5f 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -991,6 +991,7 @@ "fieldname": "serial_and_batch_bundle", "fieldtype": "Link", "label": "Serial and Batch Bundle", + "no_copy": 1, "options": "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 ae25aad612..554c032f04 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 @@ -48,7 +48,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals @frappe.whitelist() -def get_serial_batch_no_ledgers(item_code, voucher_no, name=None): +def get_serial_batch_ledgers(item_code, voucher_no, name=None): return frappe.get_all( "Serial and Batch Bundle", fields=[ @@ -68,7 +68,7 @@ def get_serial_batch_no_ledgers(item_code, voucher_no, name=None): @frappe.whitelist() -def add_serial_batch_no_ledgers(ledgers, child_row) -> object: +def add_serial_batch_ledgers(ledgers, child_row) -> object: if isinstance(child_row, str): child_row = frappe._dict(frappe.parse_json(child_row)) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 9338dc5735..98beda0534 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -16,6 +16,7 @@ from frappe.utils import ( flt, get_link_to_form, getdate, + now, nowdate, safe_json_loads, ) @@ -189,7 +190,6 @@ class SerialNo(StockController): def get_last_sle(self, serial_no=None): entries = {} sle_dict = self.get_stock_ledger_entries(serial_no) - print("sle_dict", sle_dict) if sle_dict: if sle_dict.get("incoming", []): entries["purchase_sle"] = sle_dict["incoming"][0] @@ -538,13 +538,151 @@ def update_serial_nos(sle, item_det): and item_det.has_serial_no == 1 and item_det.serial_no_series ): - serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty) - sle.db_set("serial_no", serial_nos) - validate_serial_no(sle, item_det) - if sle.serial_and_batch_bundle: + bundle = make_serial_bundle(sle, item_det) + if bundle: + sle.db_set("serial_and_batch_bundle", bundle.name) + child_doctype = sle.voucher_type + " Item" + if sle.voucher_type == "Stock Entry": + child_doctype = "Stock Entry Detail" + elif sle.voucher_type == "Stock Reconciliation": + child_doctype = "Stock Reconciliation Item" + + frappe.db.set_value( + child_doctype, sle.voucher_detail_no, "serial_and_batch_bundle", bundle.name + ) + + elif sle.serial_and_batch_bundle: auto_make_serial_nos(sle) +def make_serial_bundle(sle, item_details): + sr_nos = auto_create_serial_nos(sle, item_details) + + if sr_nos: + sn_doc = frappe.new_doc("Serial and Batch Bundle") + sn_doc.item_code = item_details.name + sn_doc.item_name = item_details.item_name + sn_doc.item_group = item_details.item_group + sn_doc.has_serial_no = item_details.has_serial_no + sn_doc.has_batch_no = item_details.has_batch_no + sn_doc.voucher_type = sle.voucher_type + sn_doc.voucher_no = sle.voucher_no + sn_doc.flags.ignore_mandatory = True + sn_doc.qty = sle.actual_qty + sn_doc.insert() + + batch_no = "" + if item_details.has_batch_no: + batch_no = create_batch_for_serial_no(sle) + + ledgers = [] + fields = [ + "name", + "serial_no", + "batch_no", + "warehouse", + "qty", + "parent", + "parenttype", + "parentfield", + ] + + for serial_no in sr_nos: + ledgers.append( + ( + frappe.generate_hash("", 10), + serial_no, + batch_no, + sle.warehouse, + 1, + sn_doc.name, + sn_doc.doctype, + "ledgers", + ) + ) + + frappe.db.bulk_insert( + "Serial and Batch Ledger", + fields=fields, + values=set(ledgers), + ignore_duplicates=True, + ) + + sn_doc.load_from_db() + return sn_doc.submit() + + +def create_batch_for_serial_no(sle): + from erpnext.stock.doctype.batch.batch import make_batch + + return make_batch( + frappe._dict( + { + "item": sle.item_code, + "reference_doctype": sle.voucher_type, + "reference_name": sle.voucher_no, + } + ) + ) + + +def auto_create_serial_nos(sle, item_details) -> List[str]: + sr_nos = [] + serial_nos_details = [] + for i in range(cint(sle.actual_qty)): + serial_no = make_autoname(item_details.serial_no_series, "Serial No") + sr_nos.append(serial_no) + serial_nos_details.append( + ( + serial_no, + serial_no, + now(), + now(), + frappe.session.user, + frappe.session.user, + sle.voucher_type, + sle.voucher_no, + sle.warehouse, + sle.company, + sle.posting_date, + sle.posting_time, + sle.incoming_rate, + sle.item_code, + item_details.item_name, + item_details.description, + ) + ) + + if serial_nos_details: + fields = [ + "name", + "serial_no", + "creation", + "modified", + "owner", + "modified_by", + "purchase_document_type", + "purchase_document_no", + "warehouse", + "company", + "purchase_date", + "purchase_time", + "purchase_rate", + "item_code", + "item_name", + "description", + ] + + frappe.db.bulk_insert( + "Serial No", + fields=fields, + values=set(serial_nos_details), + ignore_duplicates=True, + ) + + return sr_nos + + def get_auto_serial_nos(serial_no_series, qty): serial_nos = [] for i in range(cint(qty)): @@ -609,7 +747,8 @@ def get_items_html(serial_nos, item_code): def get_item_details(item_code): return frappe.db.sql( """select name, has_batch_no, docstatus, - is_stock_item, has_serial_no, serial_no_series + is_stock_item, has_serial_no, serial_no_series, description, item_name, + item_group, stock_uom from tabItem where name=%s""", item_code, as_dict=True, diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 916b14a663..1bcea69dff 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -40,7 +40,6 @@ class StockLedgerEntry(Document): from erpnext.stock.utils import validate_disabled_warehouse, validate_warehouse_company self.validate_mandatory() - self.validate_serial_batch_no_bundle() self.validate_batch() validate_disabled_warehouse(self.warehouse) validate_warehouse_company(self.warehouse, self.company) @@ -58,6 +57,8 @@ class StockLedgerEntry(Document): process_serial_no(self) + self.validate_serial_batch_no_bundle() + def calculate_batch_qty(self): if self.batch_no: batch_qty = (