From de8f8ef9f4d882f75bfb35aff4331a8e14e8746c Mon Sep 17 00:00:00 2001 From: Sudharsanan11 Date: Sun, 1 Feb 2026 10:42:33 +0530 Subject: [PATCH] fix(stock): include subcontracting order qty while calculating the bin qty --- erpnext/stock/stock_balance.py | 70 ++++++++++++++++--- .../subcontracting_order.py | 27 +------ 2 files changed, 62 insertions(+), 35 deletions(-) diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index e650fb607ba..b2401da4f8f 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -3,6 +3,7 @@ import frappe +from frappe.query_builder.functions import Coalesce, Sum from frappe.utils import cstr, flt, now, nowdate, nowtime from erpnext.controllers.stock_controller import create_repost_item_valuation_entry @@ -182,18 +183,67 @@ def get_indented_qty(item_code, warehouse): def get_ordered_qty(item_code, warehouse): - ordered_qty = frappe.db.sql( - """ - select sum((po_item.qty - po_item.received_qty)*po_item.conversion_factor) - from `tabPurchase Order Item` po_item, `tabPurchase Order` po - where po_item.item_code=%s and po_item.warehouse=%s - and po_item.qty > po_item.received_qty and po_item.parent=po.name - and po.status not in ('Closed', 'Delivered') and po.docstatus=1 - and po_item.delivered_by_supplier = 0""", - (item_code, warehouse), + """Return total pending ordered quantity for an item in a warehouse. + Includes outstanding quantities from Purchase Orders and Subcontracting Orders""" + + purchase_order_qty = get_purchase_order_qty(item_code, warehouse) + subcontracting_order_qty = get_subcontracting_order_qty(item_code, warehouse) + + return flt(purchase_order_qty) + flt(subcontracting_order_qty) + + +def get_purchase_order_qty(item_code, warehouse): + PurchaseOrder = frappe.qb.DocType("Purchase Order") + PurchaseOrderItem = frappe.qb.DocType("Purchase Order Item") + + purchase_order_qty = ( + frappe.qb.from_(PurchaseOrderItem) + .join(PurchaseOrder) + .on(PurchaseOrderItem.parent == PurchaseOrder.name) + .select( + Sum( + (PurchaseOrderItem.qty - PurchaseOrderItem.received_qty) * PurchaseOrderItem.conversion_factor + ) + ) + .where( + (PurchaseOrderItem.item_code == item_code) + & (PurchaseOrderItem.warehouse == warehouse) + & (PurchaseOrderItem.qty > PurchaseOrderItem.received_qty) + & (PurchaseOrder.status.notin(["Closed", "Delivered"])) + & (PurchaseOrder.docstatus == 1) + & (Coalesce(PurchaseOrderItem.delivered_by_supplier, 0) == 0) + ) + .run() ) - return flt(ordered_qty[0][0]) if ordered_qty else 0 + return purchase_order_qty[0][0] if purchase_order_qty else 0 + + +def get_subcontracting_order_qty(item_code, warehouse): + SubcontractingOrder = frappe.qb.DocType("Subcontracting Order") + SubcontractingOrderItem = frappe.qb.DocType("Subcontracting Order Item") + + subcontracting_order_qty = ( + frappe.qb.from_(SubcontractingOrderItem) + .join(SubcontractingOrder) + .on(SubcontractingOrderItem.parent == SubcontractingOrder.name) + .select( + Sum( + (SubcontractingOrderItem.qty - SubcontractingOrderItem.received_qty) + * SubcontractingOrderItem.conversion_factor + ) + ) + .where( + (SubcontractingOrderItem.item_code == item_code) + & (SubcontractingOrderItem.warehouse == warehouse) + & (SubcontractingOrderItem.qty > SubcontractingOrderItem.received_qty) + & (SubcontractingOrder.status.notin(["Closed", "Completed"])) + & (SubcontractingOrder.docstatus == 1) + ) + .run() + ) + + return subcontracting_order_qty[0][0] if subcontracting_order_qty else 0 def get_planned_qty(item_code, warehouse): diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index ee9cf7a8ee5..8eb369d120f 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -12,7 +12,7 @@ from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry impor StockReservation, has_reserved_stock, ) -from erpnext.stock.stock_balance import update_bin_qty +from erpnext.stock.stock_balance import get_ordered_qty, update_bin_qty from erpnext.stock.utils import get_bin @@ -234,30 +234,7 @@ class SubcontractingOrder(SubcontractingController): ): item_wh_list.append([item.item_code, item.warehouse]) for item_code, warehouse in item_wh_list: - update_bin_qty(item_code, warehouse, {"ordered_qty": self.get_ordered_qty(item_code, warehouse)}) - - @staticmethod - def get_ordered_qty(item_code, warehouse): - table = frappe.qb.DocType("Subcontracting Order") - child = frappe.qb.DocType("Subcontracting Order Item") - - query = ( - frappe.qb.from_(table) - .inner_join(child) - .on(table.name == child.parent) - .select((child.qty - child.received_qty) * child.conversion_factor) - .where( - (table.docstatus == 1) - & (child.item_code == item_code) - & (child.warehouse == warehouse) - & (child.qty > child.received_qty) - & (table.status != "Completed") - ) - ) - - query = query.run() - - return flt(query[0][0]) if query else 0 + update_bin_qty(item_code, warehouse, {"ordered_qty": get_ordered_qty(item_code, warehouse)}) def update_reserved_qty_for_subcontracting(self, sco_item_rows=None): for item in self.supplied_items: