diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index f6e1e05fe35..15c84a96c8c 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -38,7 +38,9 @@ class SellingController(StockController): self.validate_for_duplicate_items() self.validate_target_warehouse() self.validate_auto_repeat_subscription_dates() - self.set_serial_and_batch_bundle() + for table_field in ["items", "packed_items"]: + if self.get(table_field): + self.set_serial_and_batch_bundle(table_field) def set_missing_values(self, for_validate=False): @@ -426,8 +428,7 @@ class SellingController(StockController): "posting_date": self.get("posting_date") or self.get("transaction_date"), "posting_time": self.get("posting_time") or nowtime(), "qty": qty if cint(self.get("is_return")) else (-1 * qty), - "serial_no": d.get("serial_no"), - "batch_no": d.get("batch_no"), + "serial_and_batch_bundle": d.serial_and_batch_bundle, "company": self.company, "voucher_type": self.doctype, "voucher_no": self.name, diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 74ba2b846ae..2e705eaf2c0 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -719,7 +719,7 @@ class StockController(AccountsController): table_name = "items" for row in self.get(table_name): - if row.serial_and_batch_bundle: + if row.get("serial_and_batch_bundle"): frappe.get_doc( "Serial and Batch Bundle", row.serial_and_batch_bundle ).set_serial_and_batch_values(self, row) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index f7bc5d5494a..0e666ffa7b1 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -51,7 +51,9 @@ class SubcontractingController(StockController): if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"]: self.validate_items() self.create_raw_materials_supplied() - self.set_serial_and_batch_bundle("supplied_items") + for table_field in ["items", "supplied_items"]: + if self.get(table_field): + self.set_total_in_words(table_field) else: super(SubcontractingController, self).validate() diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 4d17f4ed8fe..6c18b74b847 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -431,6 +431,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran item.has_serial_no = r.message.has_serial_no; item.has_batch_no = r.message.has_batch_no; item.type_of_transaction = item.qty > 0 ? "Outward":"Inward"; + item.outward = item.qty > 0 ? 1 : 0; item.title = item.has_serial_no ? __("Select Serial No") : __("Select Batch No"); @@ -446,6 +447,8 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran me.frm.refresh_fields(); frappe.model.set_value(cdt, cdn, "serial_and_batch_bundle", r.name); + + me.frm.save(); } } ); 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 35a3ca8211b..98da0afdee4 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 @@ -5,7 +5,7 @@ import collections from typing import Dict, List import frappe -from frappe import _ +from frappe import _, bold from frappe.model.document import Document from frappe.query_builder.functions import Sum from frappe.utils import cint, flt, today @@ -301,6 +301,15 @@ class SerialandBatchBundle(Document): for batch in batches: frappe.db.set_value("Batch", batch.name, {"reference_name": None, "reference_doctype": None}) + def on_cancel(self): + self.validate_voucher_no_docstatus() + + def validate_voucher_no_docstatus(self): + if frappe.db.get_value(self.voucher_type, self.voucher_no, "docstatus") == 1: + msg = f"""The {self.voucher_type} {bold(self.voucher_no)} + is in submitted state, please cancel it first""" + frappe.throw(_(msg)) + def on_trash(self): self.delink_refernce_from_voucher() self.delink_reference_from_batch() diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 0691d639462..a7f5b801a5c 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1220,11 +1220,18 @@ class StockEntry(StockController): sle.recalculate_rate = 1 if d.serial_and_batch_bundle and self.docstatus == 1: - self.copy_serial_and_batch_bundle(sle, d) + d.serial_and_batch_bundle = self.copy_serial_and_batch_bundle(sle) if d.serial_and_batch_bundle and self.docstatus == 2: bundle_id = frappe.get_cached_value( - "Serial and Batch Bundle", {"voucher_detail_no": d.name, "is_cancelled": 0}, "name" + "Serial and Batch Bundle", + { + "voucher_detail_no": d.name, + "voucher_no": self.name, + "is_cancelled": 0, + "type_of_transaction": "Inward", + }, + "name", ) if d.serial_and_batch_bundle != bundle_id: @@ -1232,7 +1239,7 @@ class StockEntry(StockController): sl_entries.append(sle) - def copy_serial_and_batch_bundle(self, sle, child): + def copy_serial_and_batch_bundle(self, child): allowed_types = [ "Material Transfer", "Send to Subcontractor", @@ -1260,7 +1267,7 @@ class StockEntry(StockController): bundle_doc.set_avg_rate() bundle_doc.flags.ignore_permissions = True bundle_doc.submit() - sle.serial_and_batch_bundle = bundle_doc.name + return bundle_doc.name def get_gl_entries(self, warehouse_account): gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 7c4f062038f..a4fac4d68fb 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -307,6 +307,9 @@ class SerialBatchBundle: return False def post_process(self): + if not self.sle.serial_and_batch_bundle: + return + if self.item_details.has_serial_no == 1: self.set_warehouse_and_status_in_serial_nos() diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index c8fffdfee16..18e0b90efcf 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -259,8 +259,11 @@ def get_incoming_rate(args, raise_error_if_no_rate=True): "Item", args.get("item_code"), ["has_serial_no", "has_batch_no"], as_dict=1 ) + if isinstance(args, dict): + args = frappe._dict(args) + if item_details.has_serial_no and args.get("serial_and_batch_bundle"): - args["actual_qty"] = args["qty"] + args.actual_qty = args.qty sn_obj = SerialNoBundleValuation( sle=args, warehouse=args.get("warehouse"), @@ -270,7 +273,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True): in_rate = sn_obj.get_incoming_rate() elif item_details.has_batch_no and args.get("serial_and_batch_bundle"): - args["actual_qty"] = args["qty"] + args.actual_qty = args.qty batch_obj = BatchNoBundleValuation( sle=args, warehouse=args.get("warehouse"),