fix(manufacturing): ignore sales order validation for subassembly item

(cherry picked from commit 6b1aac4aee)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order/work_order.py
This commit is contained in:
Sudharsanan11
2026-03-02 12:26:55 +05:30
committed by Mergify
parent 0c0f1a6591
commit cf75779278

View File

@@ -169,6 +169,54 @@ class WorkOrder(Document):
self.set_required_items(reset_only_qty=len(self.get("required_items"))) self.set_required_items(reset_only_qty=len(self.get("required_items")))
self.validate_operations_sequence() self.validate_operations_sequence()
<<<<<<< HEAD
=======
self.validate_subcontracting_inward_order()
def validate_dates(self):
if self.actual_start_date and self.actual_end_date:
if self.actual_end_date < self.actual_start_date:
frappe.throw(_("Actual End Date cannot be before Actual Start Date"))
def validate_fg_warehouse_for_reservation(self):
if (
self.reserve_stock
and self.sales_order
and not self.subcontracting_inward_order
and not self.production_plan_sub_assembly_item
):
warehouses = frappe.get_all(
"Sales Order Item",
filters={"parent": self.sales_order, "item_code": self.production_item},
pluck="warehouse",
)
if self.fg_warehouse not in warehouses:
frappe.throw(
_("Warehouse {0} is not allowed for Sales Order {1}, it should be {2}").format(
self.fg_warehouse, self.sales_order, warehouses[0]
),
title=_("Target Warehouse Reservation Error"),
)
def set_reserve_stock(self):
for row in self.required_items:
row.reserve_stock = self.reserve_stock
def enable_auto_reserve_stock(self):
if self.is_new() and frappe.db.get_single_value("Stock Settings", "auto_reserve_stock"):
self.reserve_stock = 1
def before_save(self):
self.set_skip_transfer_for_operations()
def set_skip_transfer_for_operations(self):
if not self.track_semi_finished_goods:
return
for op in self.operations:
op.skip_material_transfer = self.skip_transfer
>>>>>>> 6b1aac4aee (fix(manufacturing): ignore sales order validation for subassembly item)
def validate_operations_sequence(self): def validate_operations_sequence(self):
if all([not op.sequence_id for op in self.operations]): if all([not op.sequence_id for op in self.operations]):
@@ -220,39 +268,52 @@ class WorkOrder(Document):
) )
def validate_sales_order(self): def validate_sales_order(self):
if self.production_plan_sub_assembly_item:
return
if self.sales_order: if self.sales_order:
self.check_sales_order_on_hold_or_close() self.check_sales_order_on_hold_or_close()
so = frappe.db.sql(
""" SalesOrder = frappe.qb.DocType("Sales Order")
select so.name, so_item.delivery_date, so.project SalesOrderItem = frappe.qb.DocType("Sales Order Item")
from `tabSales Order` so PackedItem = frappe.qb.DocType("Packed Item")
inner join `tabSales Order Item` so_item on so_item.parent = so.name ProductBundleItem = frappe.qb.DocType("Product Bundle Item")
left join `tabProduct Bundle Item` pk_item on so_item.item_code = pk_item.parent
where so.name=%s and so.docstatus = 1 so = (
and so.skip_delivery_note = 0 and ( frappe.qb.from_(SalesOrder)
so_item.item_code=%s or .inner_join(SalesOrderItem)
pk_item.item_code=%s ) .on(SalesOrderItem.parent == SalesOrder.name)
""", .left_join(ProductBundleItem)
(self.sales_order, self.production_item, self.production_item), .on(ProductBundleItem.parent == SalesOrderItem.item_code)
as_dict=1, .select(SalesOrder.name, SalesOrder.project, SalesOrderItem.delivery_date)
.where(
(SalesOrder.skip_delivery_note == 0)
& (SalesOrder.docstatus == 1)
& (SalesOrder.name == self.sales_order)
& (
(SalesOrderItem.item_code == self.production_item)
| (ProductBundleItem.item_code == self.production_item)
)
)
.run(as_dict=1)
) )
if not so: if not so:
so = frappe.db.sql( so = (
""" frappe.qb.from_(SalesOrder)
select .inner_join(SalesOrderItem)
so.name, so_item.delivery_date, so.project .on(SalesOrderItem.parent == SalesOrder.name)
from .inner_join(PackedItem)
`tabSales Order` so, `tabSales Order Item` so_item, `tabPacked Item` packed_item .on(PackedItem.parent == SalesOrder.name)
where so.name=%s .select(SalesOrder.name, SalesOrder.project, SalesOrderItem.delivery_date)
and so.name=so_item.parent .where(
and so.name=packed_item.parent (SalesOrder.name == self.sales_order)
and so.skip_delivery_note = 0 & (SalesOrder.skip_delivery_note == 0)
and so_item.item_code = packed_item.parent_item & (SalesOrderItem.item_code == PackedItem.parent_item)
and so.docstatus = 1 and packed_item.item_code=%s & (SalesOrder.docstatus == 1)
""", & (PackedItem.item_code == self.production_item)
(self.sales_order, self.production_item), )
as_dict=1, .run(as_dict=1)
) )
if len(so): if len(so):
@@ -426,7 +487,7 @@ class WorkOrder(Document):
from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
if self.sales_order and self.sales_order_item: if self.sales_order and self.sales_order_item and not self.production_plan_sub_assembly_item:
update_produced_qty_in_so_item(self.sales_order, self.sales_order_item) update_produced_qty_in_so_item(self.sales_order, self.sales_order_item)
if self.production_plan: if self.production_plan:
@@ -818,7 +879,7 @@ class WorkOrder(Document):
doc.db_set("status", doc.status) doc.db_set("status", doc.status)
def update_work_order_qty_in_so(self): def update_work_order_qty_in_so(self):
if not self.sales_order and not self.sales_order_item: if (not self.sales_order and not self.sales_order_item) or self.production_plan_sub_assembly_item:
return return
total_bundle_qty = 1 total_bundle_qty = 1