From 904ac628302f205b2af104e51aab21e6fe2ac6a0 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Tue, 7 Apr 2026 14:51:35 +0530 Subject: [PATCH] chore: resolve conflicts --- .../doctype/work_order/test_work_order.py | 5 - .../doctype/work_order/work_order.js | 9 +- .../doctype/work_order/work_order.py | 22 +- .../doctype/stock_entry/stock_entry.json | 9 +- .../stock/doctype/stock_entry/stock_entry.py | 218 +----------------- 5 files changed, 8 insertions(+), 255 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index acf095d1000..631baa4229c 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -3,13 +3,8 @@ import frappe -<<<<<<< HEAD from frappe.tests.utils import FrappeTestCase, change_settings, timeout -from frappe.utils import add_days, add_months, add_to_date, cint, flt, now, today -======= -from frappe.tests import timeout from frappe.utils import add_days, add_months, add_to_date, cint, flt, now, nowdate, nowtime, today ->>>>>>> 93ad48bc1b (fix: process loss with bom path disassembly) from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError from erpnext.manufacturing.doctype.job_card.job_card import make_stock_entry as make_stock_entry_from_jc diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index b6f3f740e55..20ece137c50 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -868,10 +868,6 @@ erpnext.work_order = { return flt(max, precision("qty")); }, -<<<<<<< HEAD - show_prompt_for_qty_input: function (frm, purpose) { - let max = this.get_max_transferable_qty(frm, purpose); -======= show_disassembly_prompt: function (frm) { let max_qty = flt(frm.doc.produced_qty - frm.doc.disassembled_qty); @@ -926,9 +922,8 @@ erpnext.work_order = { }); }, - show_prompt_for_qty_input: function (frm, purpose, qty, additional_transfer_entry) { - let max = !additional_transfer_entry ? this.get_max_transferable_qty(frm, purpose) : qty; ->>>>>>> 68e97808c5 (fix: disassembly prompt with source stock entry field) + show_prompt_for_qty_input: function (frm, purpose) { + let max = this.get_max_transferable_qty(frm, purpose); let fields = [ { diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 7ae844d6a67..3a1dc2c6360 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1480,18 +1480,13 @@ def set_work_order_ops(name): @frappe.whitelist() -<<<<<<< HEAD -def make_stock_entry(work_order_id, purpose, qty=None, target_warehouse=None): -======= def make_stock_entry( work_order_id: str, purpose: str, qty: float | None = None, target_warehouse: str | None = None, - is_additional_transfer_entry: bool = False, source_stock_entry: str | None = None, ): ->>>>>>> 68e97808c5 (fix: disassembly prompt with source stock entry field) work_order = frappe.get_doc("Work Order", work_order_id) if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group"): wip_warehouse = work_order.wip_warehouse @@ -1541,16 +1536,7 @@ def make_stock_entry( @frappe.whitelist() -<<<<<<< HEAD -<<<<<<< HEAD -def get_default_warehouse(): - doc = frappe.get_cached_doc("Manufacturing Settings") - -======= -def get_disassembly_available_qty(stock_entry_name: str) -> float: -======= def get_disassembly_available_qty(stock_entry_name: str, current_se_name: str | None = None) -> float: ->>>>>>> 6394dead72 (fix: validate qty that can be disassembled from source stock entry.) se = frappe.db.get_value("Stock Entry", stock_entry_name, ["fg_completed_qty"], as_dict=True) if not se: return 0.0 @@ -1570,11 +1556,9 @@ def get_disassembly_available_qty(stock_entry_name: str, current_se_name: str | @frappe.whitelist() -def get_default_warehouse(company: str): - wip, fg, scrap = frappe.get_cached_value( - "Company", company, ["default_wip_warehouse", "default_fg_warehouse", "default_scrap_warehouse"] - ) ->>>>>>> 68e97808c5 (fix: disassembly prompt with source stock entry field) +def get_default_warehouse(): + doc = frappe.get_cached_doc("Manufacturing Settings") + return { "wip_warehouse": doc.default_wip_warehouse, "fg_warehouse": doc.default_fg_warehouse, diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 8559e1339e2..797bb8ac5dd 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -11,6 +11,7 @@ "naming_series", "stock_entry_type", "outgoing_stock_entry", + "source_stock_entry", "purpose", "add_to_transit", "work_order", @@ -28,15 +29,7 @@ "column_break_eaoa", "set_posting_time", "inspection_required", -<<<<<<< HEAD "apply_putaway_rule", -======= - "column_break_jabv", - "work_order", - "subcontracting_order", - "outgoing_stock_entry", - "source_stock_entry", ->>>>>>> d4baa9a74a (fix: create source_stock_entry to refer to original manufacturing entry) "bom_info_section", "from_bom", "use_multi_level_bom", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index faec439909c..e2b4309cd9a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -141,12 +141,8 @@ class StockEntry(StockController): scan_barcode: DF.Data | None select_print_heading: DF.Link | None set_posting_time: DF.Check -<<<<<<< HEAD source_address_display: DF.SmallText | None -======= - source_address_display: DF.TextEditor | None source_stock_entry: DF.Link | None ->>>>>>> d4baa9a74a (fix: create source_stock_entry to refer to original manufacturing entry) source_warehouse_address: DF.Link | None stock_entry_type: DF.Link subcontracting_order: DF.Link | None @@ -375,34 +371,17 @@ class StockEntry(StockController): self._set_serial_batch_bundle_for_disassembly_row(row, serial_nos, batches) -<<<<<<< HEAD - bundle_doc = SerialBatchCreation( - { - "item_code": row.item_code, - "warehouse": 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.transfer_qty, - "type_of_transaction": "Inward" if row.t_warehouse else "Outward", - "company": self.company, - "do_not_submit": True, - } - ).make_serial_and_batch_bundle(serial_nos=serial_nos, batch_nos=batches) -======= def _set_serial_batch_bundle_for_disassembly_row(self, row, serial_nos, batches): if not serial_nos and not batches: return ->>>>>>> 13b019ab8e (fix: set serial and batch from source stock entry - on disassemble) warehouse = row.s_warehouse or row.t_warehouse bundle_doc = SerialBatchCreation( { "item_code": row.item_code, "warehouse": warehouse, - "posting_datetime": get_combine_datetime(self.posting_date, self.posting_time), + "posting_date": self.posting_date, + "posting_time": self.posting_time, "voucher_type": self.doctype, "voucher_no": self.name, "voucher_detail_no": row.name, @@ -2215,45 +2194,7 @@ class StockEntry(StockController): # Finished goods self.load_items_from_bom() -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD - def get_items_from_manufacture_entry(self): - return frappe.get_all( - "Stock Entry", - fields=[ - "`tabStock Entry Detail`.`item_code`", - "`tabStock Entry Detail`.`item_name`", - "`tabStock Entry Detail`.`description`", - "sum(`tabStock Entry Detail`.qty) as qty", - "sum(`tabStock Entry Detail`.transfer_qty) as transfer_qty", - "`tabStock Entry Detail`.`stock_uom`", - "`tabStock Entry Detail`.`uom`", - "`tabStock Entry Detail`.`basic_rate`", - "`tabStock Entry Detail`.`conversion_factor`", - "`tabStock Entry Detail`.`is_finished_item`", - "`tabStock Entry Detail`.`batch_no`", - "`tabStock Entry Detail`.`serial_no`", - "`tabStock Entry Detail`.`s_warehouse`", - "`tabStock Entry Detail`.`t_warehouse`", - "`tabStock Entry Detail`.`use_serial_batch_fields`", - ], - filters=[ - ["Stock Entry", "purpose", "=", "Manufacture"], - ["Stock Entry", "work_order", "=", self.work_order], - ["Stock Entry", "docstatus", "=", 1], - ["Stock Entry Detail", "docstatus", "=", 1], - ], - order_by="`tabStock Entry Detail`.`idx` desc, `tabStock Entry Detail`.`is_finished_item` desc", - group_by="`tabStock Entry Detail`.`item_code`", -======= - def get_items_from_manufacture_stock_entry(self, stock_entry): -======= - def get_items_from_manufacture_stock_entry(self, stock_entry=None): ->>>>>>> 3cf1ce8360 (fix: manufacture entry with group_by support) -======= def get_items_from_manufacture_stock_entry(self): ->>>>>>> 98dfd64f63 (fix: remove unnecessary param, and use value from self) SE = frappe.qb.DocType("Stock Entry") SED = frappe.qb.DocType("Stock Entry Detail") query = frappe.qb.from_(SED).join(SE).on(SED.parent == SE.name).where(SE.docstatus == 1) @@ -2293,7 +2234,6 @@ class StockEntry(StockController): .groupby(SED.item_code) .orderby(SED.idx) .run(as_dict=True) ->>>>>>> 1ed0124ad7 (fix: add support to fetch items based on manufacture stock entry; fix how it's done from work order) ) @frappe.whitelist() @@ -2420,161 +2360,7 @@ class StockEntry(StockController): if self.pro_doc and self.pro_doc.scrap_warehouse: item["to_warehouse"] = self.pro_doc.scrap_warehouse -<<<<<<< HEAD self.add_to_stock_entry_detail(scrap_item_dict, bom_no=self.bom_no) -======= - if ( - self.purpose not in ["Material Transfer for Manufacture"] - and frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on") - != "BOM" - and not skip_transfer - ): - return - - reservation_entries = self.get_available_reserved_materials() - if not reservation_entries: - return - - new_items_to_add = [] - for d in self.items: - if d.serial_and_batch_bundle or d.serial_no or d.batch_no: - continue - - key = (d.item_code, d.s_warehouse) - if details := reservation_entries.get(key): - original_qty = d.qty - if batches := details.get("batch_no"): - for batch_no, qty in batches.items(): - if original_qty <= 0: - break - - if qty <= 0: - continue - - if d.batch_no and original_qty > 0: - new_row = frappe.copy_doc(d) - new_row.name = None - new_row.batch_no = batch_no - new_row.qty = qty - new_row.idx = d.idx + 1 - if new_row.batch_no and details.get("batchwise_sn"): - new_row.serial_no = "\n".join( - details.get("batchwise_sn")[new_row.batch_no][: cint(new_row.qty)] - ) - - new_items_to_add.append(new_row) - original_qty -= qty - batches[batch_no] -= qty - - if qty >= d.qty and not d.batch_no: - d.batch_no = batch_no - batches[batch_no] -= d.qty - if d.batch_no and details.get("batchwise_sn"): - d.serial_no = "\n".join( - details.get("batchwise_sn")[d.batch_no][: cint(d.qty)] - ) - elif not d.batch_no: - d.batch_no = batch_no - d.qty = qty - original_qty -= qty - batches[batch_no] = 0 - - if d.batch_no and details.get("batchwise_sn"): - d.serial_no = "\n".join( - details.get("batchwise_sn")[d.batch_no][: cint(d.qty)] - ) - - if details.get("serial_no"): - d.serial_no = "\n".join(details.get("serial_no")[: cint(d.qty)]) - - d.use_serial_batch_fields = 1 - - for new_row in new_items_to_add: - self.append("items", new_row) - - sorted_items = sorted(self.items, key=lambda x: x.item_code) - if self.purpose == "Manufacture": - # ensure finished item at last - sorted_items = sorted(sorted_items, key=lambda x: x.t_warehouse) - - idx = 0 - for row in sorted_items: - idx += 1 - row.idx = idx - self.set("items", sorted_items) - - def get_available_reserved_materials(self): - reserved_entries = self.get_reserved_materials() - if not reserved_entries: - return {} - - itemwise_serial_batch_qty = frappe._dict() - - for d in reserved_entries: - key = (d.item_code, d.warehouse) - if key not in itemwise_serial_batch_qty: - itemwise_serial_batch_qty[key] = frappe._dict( - { - "serial_no": [], - "batch_no": defaultdict(float), - "batchwise_sn": defaultdict(list), - } - ) - - details = itemwise_serial_batch_qty[key] - if d.batch_no: - details.batch_no[d.batch_no] += d.qty - if d.serial_no: - details.batchwise_sn[d.batch_no].extend(d.serial_no.split("\n")) - elif d.serial_no: - details.serial_no.append(d.serial_no) - - return itemwise_serial_batch_qty - - def get_reserved_materials(self): - doctype = frappe.qb.DocType("Stock Reservation Entry") - serial_batch_doc = frappe.qb.DocType("Serial and Batch Entry") - - query = ( - frappe.qb.from_(doctype) - .inner_join(serial_batch_doc) - .on(doctype.name == serial_batch_doc.parent) - .select( - serial_batch_doc.serial_no, - serial_batch_doc.batch_no, - serial_batch_doc.qty, - doctype.item_code, - doctype.warehouse, - doctype.name, - doctype.transferred_qty, - doctype.consumed_qty, - ) - .where( - (doctype.docstatus == 1) - & (doctype.voucher_no == (self.work_order or self.subcontracting_order)) - & (serial_batch_doc.delivered_qty < serial_batch_doc.qty) - ) - .orderby(serial_batch_doc.idx) - ) - - return query.run(as_dict=True) - - def set_secondary_items(self): - if self.purpose in ["Manufacture", "Repack"]: - secondary_items_dict = self.get_secondary_items(self.fg_completed_qty) - for item in secondary_items_dict.values(): - if self.pro_doc and item.type: - if self.pro_doc.scrap_warehouse and item.type == "Scrap": - item["to_warehouse"] = self.pro_doc.scrap_warehouse - - if item.process_loss_per: - item["qty"] -= flt( - item["qty"] * (item.process_loss_per / 100), - self.precision("fg_completed_qty"), - ) - - self.add_to_stock_entry_detail(secondary_items_dict, bom_no=self.bom_no) ->>>>>>> ea392b2009 (fix: validate work order consistency in stock entry) def set_process_loss_qty(self): if self.purpose not in ("Manufacture", "Repack"):