From e602cad39aca3331b98a6f2e8390ba8ad306017f Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 14 May 2026 03:28:07 +0530 Subject: [PATCH] fix: use Stock Reconciliation for opening stock entry --- erpnext/stock/doctype/item/item.js | 93 ++++++++++++++---------------- erpnext/stock/doctype/item/item.py | 58 +++++++++++-------- 2 files changed, 79 insertions(+), 72 deletions(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 362fa7a948b..ed6d4efe43d 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -883,6 +883,29 @@ $.extend(erpnext.item, { }, show_opening_stock_dialog: function (frm) { + const has_serial = cint(frm.doc.has_serial_no); + const has_batch = cint(frm.doc.has_batch_no); + + if (has_serial || has_batch) { + const default_company = frappe.defaults.get_default("company"); + const row = (frm.doc.item_defaults || []).find((d) => d.company === default_company); + const default_warehouse = (row && row.default_warehouse) || ""; + + frappe.route_options = { + purpose: "Opening Stock", + company: default_company, + }; + + frappe.new_doc("Stock Reconciliation", null, (doc) => { + const child = doc.items[0]; + frappe.model.set_value(child.doctype, child.name, "item_code", frm.doc.name); + if (default_warehouse) { + frappe.model.set_value(child.doctype, child.name, "warehouse", default_warehouse); + } + }); + return; + } + const companies = (frm.doc.item_defaults || []).map((d) => d.company).filter(Boolean); if (!companies.length) { @@ -901,9 +924,6 @@ $.extend(erpnext.item, { return (row && row.default_warehouse) || ""; }; - const has_serial = cint(frm.doc.has_serial_no); - const has_batch = cint(frm.doc.has_batch_no); - const fields = [ { label: __("Company"), @@ -950,51 +970,10 @@ $.extend(erpnext.item, { }, ]; - if (has_serial) { - fields.push( - { fieldtype: "Section Break", label: __("Serial / Batch") }, - { - label: __("Serial No Series"), - fieldname: "serial_no_series", - fieldtype: "Data", - default: frm.doc.serial_no_series || "", - reqd: 1, - description: __( - "Example: SN-.YYYY.-.#####. - One serial number will be created per unit of qty." - ), - } - ); - } - - if (has_batch) { - if (!has_serial) { - fields.push({ fieldtype: "Section Break", label: __("Serial Nos / Batches") }); - } - fields.push( - { - label: __("Automatically Create New Batch"), - fieldname: "create_new_batch", - fieldtype: "Check", - default: 1, - read_only: 1, - }, - { - label: __("Batch Number Series"), - fieldname: "batch_number_series", - fieldtype: "Data", - default: frm.doc.batch_number_series || "", - reqd: 1, - description: __( - "Example: BATCH-.YYYY.-.#####. - A new batch will be auto-created from this series." - ), - } - ); - } - const dialog = new frappe.ui.Dialog({ - title: __("Set Opening Stock"), + title: __("Add Opening Stock"), fields: fields, - primary_action_label: __("Create Stock Entry"), + primary_action_label: __("Save"), primary_action: function (values) { frappe.call({ method: "erpnext.stock.doctype.item.item.make_opening_stock_entry", @@ -1004,9 +983,6 @@ $.extend(erpnext.item, { qty: values.qty, valuation_rate: values.valuation_rate || 0, warehouse: values.warehouse || null, - serial_no_series: values.serial_no_series || null, - create_new_batch: values.create_new_batch ? 1 : 0, - batch_number_series: values.batch_number_series || null, }, freeze: true, freeze_message: __("Creating Opening Stock Entry..."), @@ -1022,6 +998,25 @@ $.extend(erpnext.item, { dialog.set_value("warehouse", get_warehouse_for_company(companies[0])); dialog.show(); + dialog.add_custom_action(__("Edit Full Form"), function () { + const default_company = frappe.defaults.get_default("company"); + const row = (frm.doc.item_defaults || []).find((d) => d.company === default_company); + + frappe.route_options = { + purpose: "Opening Stock", + company: default_company, + }; + + frappe.new_doc("Stock Reconciliation", null, (doc) => { + const child = doc.items[0]; + frappe.model.set_value(child.doctype, child.name, "item_code", frm.doc.name); + if (row && row.default_warehouse) { + frappe.model.set_value(child.doctype, child.name, "warehouse", row.default_warehouse); + } + }); + + dialog.hide(); + }); }, weight_to_validate: function (frm) { diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 29e233a7bc8..8a397ac67ee 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -312,9 +312,6 @@ class Item(Document): if self.valuation_rate is None and not self.is_customer_provided_item: frappe.throw(_("Valuation Rate is mandatory if Opening Stock entered")) - from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry - - # default warehouse, or Stores for default in self.item_defaults or [ frappe._dict({"company": frappe.defaults.get_defaults().company}) ]: @@ -329,39 +326,54 @@ class Item(Document): "Warehouse", {"warehouse_name": _("Stores"), "company": default.company} ) - if default_warehouse: - stock_entry = make_stock_entry( - item_code=self.name, - target=default_warehouse, - qty=self.opening_stock, - rate=self.valuation_rate, - company=default.company, - posting_date=getdate(), - posting_time=nowtime(), - do_not_save=True, + opening_account = frappe.db.get_value( + "Account", + {"company": default.company, "account_type": "Temporary", "is_group": 0}, + "name", + ) + + if not opening_account: + frappe.throw( + _( + "Please set a Temporary Opening account for company {0} to create an Opening Stock entry." + ).format(frappe.bold(default.company)) ) - if self.valuation_rate == 0: - for item in stock_entry.items: - item.allow_zero_valuation_rate = 1 + if default_warehouse: + stock_reco = frappe.get_doc( + { + "doctype": "Stock Reconciliation", + "purpose": "Opening Stock", + "company": default.company, + "expense_account": opening_account, + "items": [ + { + "item_code": self.name, + "warehouse": default_warehouse, + "qty": self.opening_stock, + "valuation_rate": self.valuation_rate, + "allow_zero_valuation_rate": 1 if flt(self.valuation_rate) == 0 else 0, + } + ], + } + ) - stock_entry.insert() - stock_entry.submit() - stock_entry.load_from_db() - stock_entry.add_comment("Comment", _("Opening Stock")) + stock_reco.insert() + stock_reco.submit() + stock_reco.add_comment("Comment", _("Opening Stock")) - stock_entry_link = frappe.utils.get_link_to_form("Stock Entry", stock_entry.name) + stock_reco_link = frappe.utils.get_link_to_form("Stock Reconciliation", stock_reco.name) if self.valuation_rate == 0: frappe.msgprint( _("Opening Stock entry created with zero valuation rate: {0}").format( - stock_entry_link + stock_reco_link ), indicator="orange", alert=True, ) else: frappe.msgprint( - _("Opening Stock entry created: {0}").format(stock_entry_link), + _("Opening Stock entry created: {0}").format(stock_reco_link), indicator="green", alert=True, )