diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index f46cec6fa4..230a8b3c58 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -510,10 +510,6 @@ class PurchaseInvoice(BuyingController): if self.is_old_subcontracting_flow: self.set_consumed_qty_in_subcontract_order() - from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit - - update_serial_nos_after_submit(self, "items") - # this sequence because outstanding may get -negative self.make_gl_entries() diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 714f24a789..69e0cf2231 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -38,11 +38,7 @@ from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timeshe from erpnext.setup.doctype.company.company import update_company_current_month_sales from erpnext.stock.doctype.batch.batch import set_batch_nos from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so -from erpnext.stock.doctype.serial_no.serial_no import ( - get_delivery_note_serial_no, - get_serial_nos, - update_serial_nos_after_submit, -) +from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no, get_serial_nos form_grid_templates = {"items": "templates/form_grid/item_grid.html"} @@ -262,8 +258,6 @@ class SalesInvoice(SellingController): # because updating reserved qty in bin depends upon updated delivered qty in SO if self.update_stock == 1: self.update_stock_ledger() - if self.is_return and self.update_stock: - update_serial_nos_after_submit(self, "items") # this sequence because outstanding may get -ve self.make_gl_entries() diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 8c3bd4d75d..8b9e0aa0f8 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -387,7 +387,7 @@ class StockController(AccountsController): bundle_doc.voucher_no = self.name bundle_doc.is_cancelled = 0 - for row in bundle_doc.ledgers: + for row in bundle_doc.entries: row.is_outward = 0 row.qty = abs(row.qty) row.stock_value_difference = abs(row.stock_value_difference) @@ -398,8 +398,7 @@ class StockController(AccountsController): row.warehouse = warehouse - bundle_doc.set_total_qty() - bundle_doc.set_avg_rate() + bundle_doc.calculate_qty_and_amount() bundle_doc.flags.ignore_permissions = True if not do_not_submit: diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 1418e5f939..b92988342a 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -509,7 +509,7 @@ class SubcontractingController(StockController): qty -= qty_to_consumed if qty_to_consumed > 0: - bundle.append("ledgers", {"batch_no": batch_no, "qty": qty_to_consumed * -1}) + bundle.append("entries", {"batch_no": batch_no, "qty": qty_to_consumed * -1}) def __set_serial_nos_for_bundle(self, bundle, qty, key): bundle.has_serial_no = 1 @@ -525,7 +525,7 @@ class SubcontractingController(StockController): if batch_no: self.available_materials[key]["batch_no"][batch_no] -= 1 - bundle.append("ledgers", {"serial_no": sn, "batch_no": batch_no, "qty": -1}) + bundle.append("entries", {"serial_no": sn, "batch_no": batch_no, "qty": -1}) self.available_materials[key]["serial_no"].remove(sn) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 2a81651440..14ea2f8003 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -360,7 +360,7 @@ erpnext.buying.BuyingController = class BuyingController extends erpnext.Transac item.is_rejected = false; frappe.require(path, function() { - new erpnext.SerialNoBatchBundleUpdate( + new erpnext.SerialBatchPackageSelector( me.frm, item, (r) => { if (r) { me.frm.refresh_fields(); @@ -388,7 +388,7 @@ erpnext.buying.BuyingController = class BuyingController extends erpnext.Transac item.is_rejected = true; frappe.require(path, function() { - new erpnext.SerialNoBatchBundleUpdate( + new erpnext.SerialBatchPackageSelector( me.frm, item, (r) => { if (r) { me.frm.refresh_fields(); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index e706ab9783..70c403b786 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2318,7 +2318,16 @@ erpnext.show_serial_batch_selector = function (frm, item_row, callback, on_close } frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() { - new erpnext.SerialNoBatchBundleUpdate(frm, item_row, (r) => { + if (in_list(["Sales Invoice", "Delivery Note"], frm.doc.doctype)) { + item_row.outward = frm.doc.is_return ? 0 : 1; + } else { + item_row.outward = frm.doc.is_return ? 1 : 0; + } + + item_row.type_of_transaction = (item_row.outward === 1 + ? "Outward":"Inward"); + + new erpnext.SerialBatchPackageSelector(frm, item_row, (r) => { if (r) { frm.refresh_fields(); frappe.model.set_value(item_row.doctype, item_row.name, diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index c35e4a5967..b893231012 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -1,4 +1,4 @@ -erpnext.SerialNoBatchBundleUpdate = class SerialNoBatchBundleUpdate { +erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { constructor(frm, item, callback) { this.frm = frm; this.item = item; @@ -105,7 +105,7 @@ erpnext.SerialNoBatchBundleUpdate = class SerialNoBatchBundleUpdate { }); fields.push({ - fieldname: 'ledgers', + fieldname: 'entries', fieldtype: 'Table', allow_bulk_edit: true, data: [], @@ -228,8 +228,8 @@ erpnext.SerialNoBatchBundleUpdate = class SerialNoBatchBundleUpdate { callback: (r) => { debugger if (r.message) { - this.dialog.fields_dict.ledgers.df.data = r.message; - this.dialog.fields_dict.ledgers.grid.refresh(); + this.dialog.fields_dict.entries.df.data = r.message; + this.dialog.fields_dict.entries.grid.refresh(); } } }); @@ -239,44 +239,40 @@ erpnext.SerialNoBatchBundleUpdate = class SerialNoBatchBundleUpdate { const { scan_serial_no, scan_batch_no } = this.dialog.get_values(); if (scan_serial_no) { - this.dialog.fields_dict.ledgers.df.data.push({ + this.dialog.fields_dict.entries.df.data.push({ serial_no: scan_serial_no }); this.dialog.fields_dict.scan_serial_no.set_value(''); } else if (scan_batch_no) { - this.dialog.fields_dict.ledgers.df.data.push({ + this.dialog.fields_dict.entries.df.data.push({ batch_no: scan_batch_no }); this.dialog.fields_dict.scan_batch_no.set_value(''); } - this.dialog.fields_dict.ledgers.grid.refresh(); + this.dialog.fields_dict.entries.grid.refresh(); } update_ledgers() { - if (!this.frm.is_new()) { - let ledgers = this.dialog.get_values().ledgers; + let entries = this.dialog.get_values().entries; - if (ledgers && !ledgers.length || !ledgers) { - frappe.throw(__('Please add atleast one Serial No / Batch No')); - } - - frappe.call({ - method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.add_serial_batch_ledgers', - args: { - ledgers: ledgers, - child_row: this.item, - doc: this.frm.doc, - } - }).then(r => { - this.callback && this.callback(r.message); - this.dialog.hide(); - }) - } else { - frappe.msgprint(__('Please save the document first')); + if (entries && !entries.length || !entries) { + frappe.throw(__('Please add atleast one Serial No / Batch No')); } + + frappe.call({ + method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.add_serial_batch_ledgers', + args: { + entries: entries, + child_row: this.item, + doc: this.frm.doc, + } + }).then(r => { + this.callback && this.callback(r.message); + this.dialog.hide(); + }) } render_data() { @@ -298,9 +294,9 @@ erpnext.SerialNoBatchBundleUpdate = class SerialNoBatchBundleUpdate { set_data(data) { data.forEach(d => { - this.dialog.fields_dict.ledgers.df.data.push(d); + this.dialog.fields_dict.entries.df.data.push(d); }); - this.dialog.fields_dict.ledgers.grid.refresh(); + this.dialog.fields_dict.entries.grid.refresh(); } } \ No newline at end of file diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 6c18b74b84..f8e000a111 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -441,7 +441,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran } frappe.require(path, function() { - new erpnext.SerialNoBatchBundleUpdate( + new erpnext.SerialBatchPackageSelector( me.frm, item, (r) => { if (r) { me.frm.refresh_fields(); diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py index f2d266afbc..14717c6902 100644 --- a/erpnext/stock/deprecated_serial_batch.py +++ b/erpnext/stock/deprecated_serial_batch.py @@ -67,8 +67,8 @@ class DeprecatedSerialNoValuation: class DeprecatedBatchNoValuation: def calculate_avg_rate_from_deprecarated_ledgers(self): - ledgers = self.get_sle_for_batches() - for ledger in ledgers: + entries = self.get_sle_for_batches() + for ledger in entries: self.batch_avg_rate[ledger.batch_no] += flt(ledger.batch_value) / flt(ledger.batch_qty) self.available_qty[ledger.batch_no] += flt(ledger.batch_qty) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 284d003cf9..1ac2f35019 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -240,11 +240,6 @@ class PurchaseReceipt(BuyingController): # because updating ordered qty, reserved_qty_for_subcontract in bin # depends upon updated ordered qty in PO self.update_stock_ledger() - - from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit - - update_serial_nos_after_submit(self, "items") - self.make_gl_entries() self.repost_future_sle_and_gle() self.set_consumed_qty_in_subcontract_order() 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 f16a72b2b8..858b3335d3 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 @@ -16,7 +16,7 @@ frappe.ui.form.on('Serial and Batch Bundle', { method: "set_warehouse", doc: frm.doc, callback(r) { - refresh_field("ledgers"); + refresh_field("entries"); } }) } @@ -31,11 +31,11 @@ frappe.ui.form.on('Serial and Batch Bundle', { }, toggle_fields(frm) { - frm.fields_dict.ledgers.grid.update_docfield_property( + frm.fields_dict.entries.grid.update_docfield_property( 'serial_no', 'read_only', !frm.doc.has_serial_no ); - frm.fields_dict.ledgers.grid.update_docfield_property( + frm.fields_dict.entries.grid.update_docfield_property( 'batch_no', 'read_only', !frm.doc.has_batch_no ); }, @@ -74,7 +74,7 @@ frappe.ui.form.on('Serial and Batch Bundle', { }; }); - frm.set_query('serial_no', 'ledgers', () => { + frm.set_query('serial_no', 'entries', () => { return { filters: { item_code: frm.doc.item_code, @@ -82,7 +82,7 @@ frappe.ui.form.on('Serial and Batch Bundle', { }; }); - frm.set_query('batch_no', 'ledgers', () => { + frm.set_query('batch_no', 'entries', () => { return { filters: { item: frm.doc.item_code, @@ -90,7 +90,7 @@ frappe.ui.form.on('Serial and Batch Bundle', { }; }); - frm.set_query('warehouse', 'ledgers', () => { + frm.set_query('warehouse', 'entries', () => { return { filters: { company: frm.doc.company, @@ -101,7 +101,7 @@ frappe.ui.form.on('Serial and Batch Bundle', { }); -frappe.ui.form.on("Serial and Batch Ledger", { +frappe.ui.form.on("Serial and Batch Entry", { ledgers_add(frm, cdt, cdn) { if (frm.doc.warehouse) { locals[cdt][cdn].warehouse = frm.doc.warehouse; 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 7493c79c77..00d6b3f72b 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 @@ -17,7 +17,7 @@ "has_serial_no", "has_batch_no", "serial_no_and_batch_no_tab", - "ledgers", + "entries", "quantity_and_rate_section", "total_qty", "item_group", @@ -95,15 +95,8 @@ }, { "fieldname": "serial_no_and_batch_no_tab", - "fieldtype": "Section Break" - }, - { - "allow_bulk_edit": 1, - "fieldname": "ledgers", - "fieldtype": "Table", - "label": "Ledgers", - "options": "Serial and Batch Ledger", - "reqd": 1 + "fieldtype": "Section Break", + "label": "Serial / Batch No" }, { "fieldname": "voucher_type", @@ -232,12 +225,19 @@ "label": "Voucher Detail No", "no_copy": 1, "read_only": 1 + }, + { + "allow_bulk_edit": 1, + "fieldname": "entries", + "fieldtype": "Table", + "options": "Serial and Batch Entry", + "reqd": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-03-12 16:05:18.141958", + "modified": "2023-03-21 10:52:25.105421", "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 4969713e21..9013ef07d7 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 @@ -28,19 +28,16 @@ class SerialandBatchBundle(Document): def before_save(self): self.set_is_outward() - self.set_total_qty() + self.calculate_qty_and_amount() self.set_warehouse() self.set_incoming_rate() self.validate_qty_and_stock_value_difference() - if self.ledgers: - self.set_avg_rate() - def validate_serial_nos_inventory(self): if not (self.has_serial_no and self.type_of_transaction == "Outward"): return - serial_nos = [d.serial_no for d in self.ledgers if d.serial_no] + serial_nos = [d.serial_no for d in self.entries if d.serial_no] serial_no_warehouse = frappe._dict( frappe.get_all( "Serial No", @@ -68,7 +65,7 @@ class SerialandBatchBundle(Document): if self.type_of_transaction != "Outward": return - for d in self.ledgers: + for d in self.entries: if d.qty and d.qty > 0: d.qty *= -1 @@ -76,7 +73,7 @@ class SerialandBatchBundle(Document): d.stock_value_difference *= -1 def get_serial_nos(self): - return [d.serial_no for d in self.ledgers if d.serial_no] + return [d.serial_no for d in self.entries if d.serial_no] def set_incoming_rate_for_outward_transaction(self, row=None, save=False): sle = self.get_sle_for_outward_transaction(row) @@ -94,7 +91,7 @@ class SerialandBatchBundle(Document): item_code=self.warehouse, ) - for d in self.ledgers: + for d in self.entries: available_qty = 0 if self.has_serial_no: d.incoming_rate = abs(sn_obj.serial_no_incoming_rate.get(d.serial_no, 0.0)) @@ -130,19 +127,23 @@ class SerialandBatchBundle(Document): "serial_and_batch_bundle": self.name, "actual_qty": self.total_qty, "company": self.company, - "serial_nos": [row.serial_no for row in self.ledgers if row.serial_no], - "batch_nos": {row.batch_no: row for row in self.ledgers if row.batch_no}, + "serial_nos": [row.serial_no for row in self.entries if row.serial_no], + "batch_nos": {row.batch_no: row for row in self.entries if row.batch_no}, } ) def set_incoming_rate_for_inward_transaction(self, row=None, save=False): - rate = row.valuation_rate if row else 0.0 - precision = frappe.get_precision(self.child_table, "valuation_rate") or 2 + valuation_field = "valuation_rate" + if self.voucher_type in ["Sales Invoice", "Delivery Note"]: + valuation_field = "incoming_rate" + + rate = row.get(valuation_field) if row else 0.0 + precision = frappe.get_precision(self.child_table, valuation_field) or 2 if not rate and self.voucher_detail_no and self.voucher_no: - rate = frappe.db.get_value(self.child_table, self.voucher_detail_no, "valuation_rate") + rate = frappe.db.get_value(self.child_table, self.voucher_detail_no, valuation_field) - for d in self.ledgers: + for d in self.entries: if self.voucher_type in ["Stock Reconciliation", "Stock Entry"] and d.incoming_rate: continue @@ -180,8 +181,9 @@ class SerialandBatchBundle(Document): self.db_set(values_to_set) # self.validate_voucher_no() - self.validate_quantity(row) self.set_incoming_rate(save=True, row=row) + self.calculate_qty_and_amount(save=True) + self.validate_quantity(row) def validate_voucher_no(self): if self.is_new(): @@ -215,10 +217,13 @@ class SerialandBatchBundle(Document): if not self.has_serial_no: return - serial_nos = [d.serial_no for d in self.ledgers if d.serial_no] + serial_nos = [d.serial_no for d in self.entries if d.serial_no] + + if not serial_nos: + return parent = frappe.qb.DocType("Serial and Batch Bundle") - child = frappe.qb.DocType("Serial and Batch Ledger") + child = frappe.qb.DocType("Serial and Batch Entry") timestamp_condition = CombineDatetime( parent.posting_date, parent.posting_time @@ -260,8 +265,6 @@ class SerialandBatchBundle(Document): frappe.throw(_(msg), title=_(title), exc=SerialNoExistsInFutureTransactionError) def validate_quantity(self, row): - self.set_total_qty(save=True) - precision = row.precision qty_field = "qty" if self.voucher_type in ["Subcontracting Receipt"]: @@ -275,7 +278,7 @@ class SerialandBatchBundle(Document): ) def set_is_outward(self): - for row in self.ledgers: + for row in self.entries: if self.type_of_transaction == "Outward" and row.qty > 0: row.qty *= -1 elif self.type_of_transaction == "Inward" and row.qty < 0: @@ -285,30 +288,34 @@ class SerialandBatchBundle(Document): @frappe.whitelist() def set_warehouse(self): - for row in self.ledgers: + for row in self.entries: if row.warehouse != self.warehouse: row.warehouse = self.warehouse - def set_total_qty(self, save=False): - if not self.ledgers: - return - - self.total_qty = sum([row.qty for row in self.ledgers]) - if save: - self.db_set("total_qty", self.total_qty) - - def set_avg_rate(self): + def calculate_qty_and_amount(self, save=False): self.total_amount = 0.0 + self.total_qty = 0.0 + self.avg_rate = 0.0 - for row in self.ledgers: + for row in self.entries: rate = flt(row.incoming_rate) or flt(row.outgoing_rate) self.total_amount += flt(row.qty) * rate + self.total_qty += flt(row.qty) if self.total_qty: self.avg_rate = flt(self.total_amount) / flt(self.total_qty) + if save: + self.db_set( + { + "total_qty": self.total_qty, + "avg_rate": self.avg_rate, + "total_amount": self.total_amount, + } + ) + def calculate_outgoing_rate(self): - if not (self.has_serial_no and self.ledgers): + if not (self.has_serial_no and self.entries): return if not (self.voucher_type and self.voucher_no): @@ -332,7 +339,7 @@ class SerialandBatchBundle(Document): serial_nos = [] batch_nos = [] - for row in self.ledgers: + for row in self.entries: if row.serial_no: serial_nos.append(row.serial_no) @@ -362,7 +369,7 @@ class SerialandBatchBundle(Document): frappe.db.set_value("Stock Ledger Entry", sle.name, "serial_and_batch_bundle", None) def clear_table(self): - self.set("ledgers", []) + self.set("entries", []) @property def child_table(self): @@ -434,15 +441,15 @@ def get_serial_batch_ledgers(item_code, voucher_no, name=None): return frappe.get_all( "Serial and Batch Bundle", fields=[ - "`tabSerial and Batch Ledger`.`name`", - "`tabSerial and Batch Ledger`.`qty`", - "`tabSerial and Batch Ledger`.`warehouse`", - "`tabSerial and Batch Ledger`.`batch_no`", - "`tabSerial and Batch Ledger`.`serial_no`", + "`tabSerial and Batch Entry`.`name`", + "`tabSerial and Batch Entry`.`qty`", + "`tabSerial and Batch Entry`.`warehouse`", + "`tabSerial and Batch Entry`.`batch_no`", + "`tabSerial and Batch Entry`.`serial_no`", ], filters=[ ["Serial and Batch Bundle", "item_code", "=", item_code], - ["Serial and Batch Ledger", "parent", "=", name], + ["Serial and Batch Entry", "parent", "=", name], ["Serial and Batch Bundle", "voucher_no", "=", voucher_no], ["Serial and Batch Bundle", "docstatus", "!=", 2], ], @@ -450,31 +457,30 @@ def get_serial_batch_ledgers(item_code, voucher_no, name=None): @frappe.whitelist() -def add_serial_batch_ledgers(ledgers, child_row, doc) -> object: +def add_serial_batch_ledgers(entries, child_row, doc) -> object: if isinstance(child_row, str): child_row = frappe._dict(frappe.parse_json(child_row)) - if isinstance(ledgers, str): - ledgers = frappe.parse_json(ledgers) + if isinstance(entries, str): + entries = frappe.parse_json(entries) if doc and isinstance(doc, str): - d = frappe.parse_json(doc) - parent_doc = frappe.get_doc(d.doctype, d.name) + parent_doc = frappe.parse_json(doc) if frappe.db.exists("Serial and Batch Bundle", child_row.serial_and_batch_bundle): - doc = update_serial_batch_no_ledgers(ledgers, child_row, parent_doc) + doc = update_serial_batch_no_ledgers(entries, child_row, parent_doc) else: - doc = create_serial_batch_no_ledgers(ledgers, child_row, parent_doc) + doc = create_serial_batch_no_ledgers(entries, child_row, parent_doc) return doc -def create_serial_batch_no_ledgers(ledgers, child_row, parent_doc) -> object: +def create_serial_batch_no_ledgers(entries, child_row, parent_doc) -> object: warehouse = child_row.rejected_warhouse if child_row.is_rejected else child_row.warehouse type_of_transaction = child_row.type_of_transaction - if parent_doc.doctype == "Stock Entry": + if parent_doc.get("doctype") == "Stock Entry": type_of_transaction = "Outward" if child_row.s_warehouse else "Inward" warehouse = child_row.s_warehouse or child_row.t_warehouse @@ -482,21 +488,19 @@ def create_serial_batch_no_ledgers(ledgers, child_row, parent_doc) -> object: { "doctype": "Serial and Batch Bundle", "voucher_type": child_row.parenttype, - "voucher_no": child_row.parent, "item_code": child_row.item_code, "warehouse": warehouse, - "voucher_detail_no": child_row.name, "is_rejected": child_row.is_rejected, "type_of_transaction": type_of_transaction, - "posting_date": parent_doc.posting_date, - "posting_time": parent_doc.posting_time, + "posting_date": parent_doc.get("posting_date"), + "posting_time": parent_doc.get("posting_time"), } ) - for row in ledgers: + for row in entries: row = frappe._dict(row) doc.append( - "ledgers", + "entries", { "qty": (row.qty or 1.0) * (1 if type_of_transaction == "Inward" else -1), "warehouse": warehouse, @@ -514,16 +518,16 @@ def create_serial_batch_no_ledgers(ledgers, child_row, parent_doc) -> object: return doc -def update_serial_batch_no_ledgers(ledgers, child_row, parent_doc) -> object: +def update_serial_batch_no_ledgers(entries, child_row, parent_doc) -> object: doc = frappe.get_doc("Serial and Batch Bundle", child_row.serial_and_batch_bundle) doc.voucher_detail_no = child_row.name doc.posting_date = parent_doc.posting_date doc.posting_time = parent_doc.posting_time - doc.set("ledgers", []) + doc.set("entries", []) - for d in ledgers: + for d in entries: doc.append( - "ledgers", + "entries", { "qty": 1 if doc.type_of_transaction == "Inward" else -1, "warehouse": d.get("warehouse"), @@ -543,7 +547,7 @@ def get_serial_and_batch_ledger(**kwargs): kwargs = frappe._dict(kwargs) sle_table = frappe.qb.DocType("Stock Ledger Entry") - serial_batch_table = frappe.qb.DocType("Serial and Batch Ledger") + serial_batch_table = frappe.qb.DocType("Serial and Batch Entry") query = ( frappe.qb.from_(sle_table) @@ -638,7 +642,7 @@ def get_auto_batch_nos(kwargs): def get_available_batches(kwargs): stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry") - batch_ledger = frappe.qb.DocType("Serial and Batch Ledger") + batch_ledger = frappe.qb.DocType("Serial and Batch Entry") batch_table = frappe.qb.DocType("Batch") query = ( @@ -708,7 +712,7 @@ def get_voucher_wise_serial_batch_from_bundle(**kwargs) -> Dict[str, Dict]: def get_ledgers_from_serial_batch_bundle(**kwargs) -> List[frappe._dict]: bundle_table = frappe.qb.DocType("Serial and Batch Bundle") - serial_batch_table = frappe.qb.DocType("Serial and Batch Ledger") + serial_batch_table = frappe.qb.DocType("Serial and Batch Entry") query = ( frappe.qb.from_(bundle_table) @@ -776,7 +780,7 @@ def get_available_batch_nos(item_code, warehouse): def get_stock_ledger_entries(item_code, warehouse): stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry") - batch_ledger = frappe.qb.DocType("Serial and Batch Ledger") + batch_ledger = frappe.qb.DocType("Serial and Batch Entry") return ( frappe.qb.from_(stock_ledger_entry) diff --git a/erpnext/stock/doctype/serial_and_batch_ledger/__init__.py b/erpnext/stock/doctype/serial_and_batch_entry/__init__.py similarity index 100% rename from erpnext/stock/doctype/serial_and_batch_ledger/__init__.py rename to erpnext/stock/doctype/serial_and_batch_entry/__init__.py diff --git a/erpnext/stock/doctype/serial_and_batch_ledger/serial_and_batch_ledger.json b/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.json similarity index 98% rename from erpnext/stock/doctype/serial_and_batch_ledger/serial_and_batch_ledger.json rename to erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.json index f2d4d55032..44f3c0893a 100644 --- a/erpnext/stock/doctype/serial_and_batch_ledger/serial_and_batch_ledger.json +++ b/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.json @@ -110,7 +110,7 @@ "modified": "2023-03-17 09:11:31.548862", "modified_by": "Administrator", "module": "Stock", - "name": "Serial and Batch Ledger", + "name": "Serial and Batch Entry", "owner": "Administrator", "permissions": [], "sort_field": "modified", diff --git a/erpnext/stock/doctype/serial_and_batch_ledger/serial_and_batch_ledger.py b/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.py similarity index 83% rename from erpnext/stock/doctype/serial_and_batch_ledger/serial_and_batch_ledger.py rename to erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.py index 945fdc1bc3..337403e2e1 100644 --- a/erpnext/stock/doctype/serial_and_batch_ledger/serial_and_batch_ledger.py +++ b/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.py @@ -5,5 +5,5 @@ from frappe.model.document import Document -class SerialandBatchLedger(Document): +class SerialandBatchEntry(Document): pass diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index e4e8e170d6..e0c32e42b5 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -1114,7 +1114,7 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => { item.outward = item.s_warehouse ? 1 : 0; frappe.require(path, function() { - new erpnext.SerialNoBatchBundleUpdate( + new erpnext.SerialBatchPackageSelector( frm, item, (r) => { if (r) { frm.refresh_fields(); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index d71814bc7c..0bfecae6bf 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -242,6 +242,9 @@ class StockEntry(StockController): if self.purpose == "Material Transfer" and self.outgoing_stock_entry: self.set_material_request_transfer_status("In Transit") + def on_update(self): + self.set_serial_and_batch_bundle() + def set_job_card_data(self): if self.job_card and not self.work_order: data = frappe.db.get_value( @@ -696,6 +699,9 @@ class StockEntry(StockController): self.set_total_incoming_outgoing_value() self.set_total_amount() + if not reset_outgoing_rate: + self.set_serial_and_batch_bundle() + def set_basic_rate(self, reset_outgoing_rate=True, raise_error_if_no_rate=True): """ Set rate for outgoing, scrapped and finished items @@ -2830,7 +2836,7 @@ def create_serial_and_batch_bundle(row, child, type_of_transaction=None): while qty > 0: qty -= 1 doc.append( - "ledgers", + "entries", { "batch_no": batch_no, "serial_no": batchwise_serial_nos.get(batch_no).pop(0), @@ -2842,12 +2848,12 @@ def create_serial_and_batch_bundle(row, child, type_of_transaction=None): elif row.serial_nos: doc.has_serial_no = 1 for serial_no in row.serial_nos: - doc.append("ledgers", {"serial_no": serial_no, "warehouse": row.warehouse, "qty": -1}) + doc.append("entries", {"serial_no": serial_no, "warehouse": row.warehouse, "qty": -1}) elif row.batches_to_be_consume: doc.has_batch_no = 1 for batch_no, qty in row.batches_to_be_consume.items(): - doc.append("ledgers", {"batch_no": batch_no, "warehouse": row.warehouse, "qty": qty * -1}) + doc.append("entries", {"batch_no": batch_no, "warehouse": row.warehouse, "qty": qty * -1}) return doc.insert(ignore_permissions=True).name diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index eda4d2d9a7..19f48e7224 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -63,10 +63,6 @@ class StockReconciliation(StockController): self.make_gl_entries() self.repost_future_sle_and_gle() - from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit - - update_serial_nos_after_submit(self, "items") - def on_cancel(self): self.validate_reserved_stock() self.ignore_linked_doctypes = ( @@ -108,7 +104,7 @@ class StockReconciliation(StockController): for serial_no_row in serial_nos_details: serial_and_batch_bundle.append( - "ledgers", + "entries", { "serial_no": serial_no_row.serial_no, "qty": -1, @@ -122,7 +118,7 @@ class StockReconciliation(StockController): for batch_no, qty in batch_nos_details.items(): serial_and_batch_bundle.append( - "ledgers", + "entries", { "batch_no": batch_no, "qty": qty * -1, @@ -144,7 +140,7 @@ class StockReconciliation(StockController): bundle_doc.warehouse = item.warehouse bundle_doc.type_of_transaction = "Inward" - for row in bundle_doc.ledgers: + for row in bundle_doc.entries: if row.qty < 0: row.qty = abs(row.qty) @@ -153,8 +149,7 @@ class StockReconciliation(StockController): row.is_outward = 0 - bundle_doc.set_total_qty() - bundle_doc.set_avg_rate() + bundle_doc.calculate_qty_and_amount() bundle_doc.flags.ignore_permissions = True bundle_doc.save() item.serial_and_batch_bundle = bundle_doc.name diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index e3752233a4..f82c309f94 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -141,12 +141,8 @@ class SerialBatchBundle: self.add_serial_no_to_bundle(sn_doc, sr_nos, incoming_rate, batch_no) elif self.item_details.has_batch_no: self.add_batch_no_to_bundle(sn_doc, batch_no, incoming_rate) - sn_doc.save() - - sn_doc.load_from_db() - sn_doc.flags.ignore_validate = True - sn_doc.flags.ignore_mandatory = True + sn_doc.save() sn_doc.submit() self.set_serial_and_batch_bundle(sn_doc) @@ -174,39 +170,19 @@ class SerialBatchBundle: return is_rejected(self.sle.voucher_type, self.sle.voucher_detail_no, self.sle.warehouse) def add_serial_no_to_bundle(self, sn_doc, serial_nos, incoming_rate, batch_no=None): - ledgers = [] - - fields = [ - "name", - "serial_no", - "batch_no", - "warehouse", - "item_code", - "qty", - "incoming_rate", - "parent", - "parenttype", - "parentfield", - ] - for serial_no in serial_nos: - ledgers.append( - ( - frappe.generate_hash("Serial and Batch Ledger", 10), - serial_no, - batch_no, - self.warehouse, - self.item_details.item_code, - 1, - incoming_rate, - sn_doc.name, - sn_doc.doctype, - "ledgers", - ) + sn_doc.append( + "entries", + { + "serial_no": serial_no, + "qty": 1, + "incoming_rate": incoming_rate, + "batch_no": batch_no, + "warehouse": self.warehouse, + "is_outward": 0, + }, ) - frappe.db.bulk_insert("Serial and Batch Ledger", fields=fields, values=set(ledgers)) - def add_batch_no_to_bundle(self, sn_doc, batch_no, incoming_rate): stock_value_difference = flt(self.sle.actual_qty) * flt(incoming_rate) @@ -214,7 +190,7 @@ class SerialBatchBundle: stock_value_difference *= -1 sn_doc.append( - "ledgers", + "entries", { "batch_no": batch_no, "qty": self.sle.actual_qty, @@ -336,14 +312,14 @@ class SerialBatchBundle: ).run() def set_batch_no_in_serial_nos(self): - ledgers = frappe.get_all( - "Serial and Batch Ledger", + entries = frappe.get_all( + "Serial and Batch Entry", fields=["serial_no", "batch_no"], filters={"parent": self.sle.serial_and_batch_bundle}, ) batch_serial_nos = {} - for ledger in ledgers: + for ledger in entries: batch_serial_nos.setdefault(ledger.batch_no, []).append(ledger.serial_no) for batch_no, serial_nos in batch_serial_nos.items(): @@ -360,9 +336,9 @@ def get_serial_nos(serial_and_batch_bundle, check_outward=True): if check_outward: filters["is_outward"] = 1 - ledgers = frappe.get_all("Serial and Batch Ledger", fields=["serial_no"], filters=filters) + entries = frappe.get_all("Serial and Batch Entry", fields=["serial_no"], filters=filters) - return [d.serial_no for d in ledgers] + return [d.serial_no for d in entries] class SerialNoBundleValuation(DeprecatedSerialNoValuation): @@ -380,12 +356,12 @@ class SerialNoBundleValuation(DeprecatedSerialNoValuation): ) else: - ledgers = self.get_serial_no_ledgers() + entries = self.get_serial_no_ledgers() self.serial_no_incoming_rate = defaultdict(float) self.stock_value_change = 0.0 - for ledger in ledgers: + for ledger in entries: self.stock_value_change += ledger.incoming_rate * -1 self.serial_no_incoming_rate[ledger.serial_no] = ledger.incoming_rate @@ -403,7 +379,7 @@ class SerialNoBundleValuation(DeprecatedSerialNoValuation): ), child.name, child.serial_no, child.warehouse FROM `tabSerial and Batch Bundle` as parent, - `tabSerial and Batch Ledger` as child + `tabSerial and Batch Entry` as child WHERE parent.name = child.parent AND child.serial_no IN ({', '.join([frappe.db.escape(s) for s in serial_nos])}) @@ -428,7 +404,7 @@ class SerialNoBundleValuation(DeprecatedSerialNoValuation): SELECT ledger.serial_no, ledger.incoming_rate, ledger.warehouse FROM - `tabSerial and Batch Ledger` AS ledger, + `tabSerial and Batch Entry` AS ledger, ({subquery}) AS SubQuery WHERE ledger.name = SubQuery.name @@ -508,11 +484,11 @@ class BatchNoBundleValuation(DeprecatedBatchNoValuation): "Serial and Batch Bundle", self.sle.serial_and_batch_bundle, "total_amount" ) else: - ledgers = self.get_batch_no_ledgers() + entries = self.get_batch_no_ledgers() self.batch_avg_rate = defaultdict(float) self.available_qty = defaultdict(float) - for ledger in ledgers: + for ledger in entries: self.batch_avg_rate[ledger.batch_no] += flt(ledger.incoming_rate) / flt(ledger.qty) self.available_qty[ledger.batch_no] += flt(ledger.qty) @@ -521,7 +497,7 @@ class BatchNoBundleValuation(DeprecatedBatchNoValuation): def get_batch_no_ledgers(self) -> List[dict]: parent = frappe.qb.DocType("Serial and Batch Bundle") - child = frappe.qb.DocType("Serial and Batch Ledger") + child = frappe.qb.DocType("Serial and Batch Entry") batch_nos = list(self.batch_nos.keys()) @@ -554,13 +530,13 @@ class BatchNoBundleValuation(DeprecatedBatchNoValuation): if self.sle.get("batch_nos"): return self.sle.batch_nos - ledgers = frappe.get_all( - "Serial and Batch Ledger", + entries = frappe.get_all( + "Serial and Batch Entry", fields=["batch_no", "qty", "name"], filters={"parent": self.sle.serial_and_batch_bundle, "is_outward": 1}, ) - return {d.batch_no: d for d in ledgers} + return {d.batch_no: d for d in entries} def set_stock_value_difference(self): self.stock_value_change = 0 @@ -568,7 +544,7 @@ class BatchNoBundleValuation(DeprecatedBatchNoValuation): stock_value_change = self.batch_avg_rate[batch_no] * ledger.qty self.stock_value_change += stock_value_change frappe.db.set_value( - "Serial and Batch Ledger", ledger.name, "stock_value_difference", stock_value_change + "Serial and Batch Entry", ledger.name, "stock_value_difference", stock_value_change ) def calculate_valuation_rate(self): @@ -638,7 +614,7 @@ def get_batches_from_stock_entries(work_order): def set_batch_details_from_package(ids, batches): entries = frappe.get_all( - "Serial and Batch Ledger", + "Serial and Batch Entry", filters={"parent": ("in", ids), "is_outward": 0}, fields=["batch_no", "qty"], ) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 416355a47f..dfb77864cd 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1370,7 +1370,7 @@ def get_batch_incoming_rate( ): sle = frappe.qb.DocType("Stock Ledger Entry") - batch_ledger = frappe.qb.DocType("Serial and Batch Ledger") + batch_ledger = frappe.qb.DocType("Serial and Batch Entry") timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime( posting_date, posting_time @@ -1382,7 +1382,7 @@ def get_batch_incoming_rate( ) & (sle.creation < creation) batches = frappe.get_all( - "Serial and Batch Ledger", fields=["batch_no"], filters={"parent": serial_and_batch_bundle} + "Serial and Batch Entry", fields=["batch_no"], filters={"parent": serial_and_batch_bundle} ) batch_details = ( diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 40dfd0dab6..212bf7fc82 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -95,11 +95,6 @@ class SubcontractingReceipt(SubcontractingController): self.set_subcontracting_order_status() self.set_consumed_qty_in_subcontract_order() self.update_stock_ledger() - - from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit - - update_serial_nos_after_submit(self, "items") - self.make_gl_entries() self.repost_future_sle_and_gle() self.update_status()