mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-31 10:49:09 +00:00
Merge pull request #49108 from LewisMojica/develop
fix(pos): include Product Bundle components in reserved qty to preven…
This commit is contained in:
@@ -217,6 +217,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
self.validate_loyalty_transaction()
|
self.validate_loyalty_transaction()
|
||||||
self.validate_company_with_pos_company()
|
self.validate_company_with_pos_company()
|
||||||
self.validate_full_payment()
|
self.validate_full_payment()
|
||||||
|
self.update_packing_list()
|
||||||
if self.coupon_code:
|
if self.coupon_code:
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
|
from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
|
||||||
|
|
||||||
@@ -410,9 +411,9 @@ class POSInvoice(SalesInvoice):
|
|||||||
)
|
)
|
||||||
elif is_stock_item and flt(available_stock) < flt(d.stock_qty):
|
elif is_stock_item and flt(available_stock) < flt(d.stock_qty):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_("Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}.").format(
|
||||||
"Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}."
|
d.idx, item_code, warehouse
|
||||||
).format(d.idx, item_code, warehouse, available_stock),
|
),
|
||||||
title=_("Item Unavailable"),
|
title=_("Item Unavailable"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -867,10 +868,8 @@ def get_bundle_availability(bundle_item_code, warehouse):
|
|||||||
bundle_bin_qty = 1000000
|
bundle_bin_qty = 1000000
|
||||||
for item in product_bundle.items:
|
for item in product_bundle.items:
|
||||||
item_bin_qty = get_bin_qty(item.item_code, warehouse)
|
item_bin_qty = get_bin_qty(item.item_code, warehouse)
|
||||||
item_pos_reserved_qty = get_pos_reserved_qty(item.item_code, warehouse)
|
|
||||||
available_qty = item_bin_qty - item_pos_reserved_qty
|
|
||||||
|
|
||||||
max_available_bundles = available_qty / item.qty
|
max_available_bundles = item_bin_qty / item.qty
|
||||||
if bundle_bin_qty > max_available_bundles and frappe.get_value(
|
if bundle_bin_qty > max_available_bundles and frappe.get_value(
|
||||||
"Item", item.item_code, "is_stock_item"
|
"Item", item.item_code, "is_stock_item"
|
||||||
):
|
):
|
||||||
@@ -893,13 +892,49 @@ def get_bin_qty(item_code, warehouse):
|
|||||||
|
|
||||||
|
|
||||||
def get_pos_reserved_qty(item_code, warehouse):
|
def get_pos_reserved_qty(item_code, warehouse):
|
||||||
|
"""
|
||||||
|
Calculate total quantity reserved for the given item and warehouse.
|
||||||
|
|
||||||
|
Includes:
|
||||||
|
- Direct sales of the item in submitted POS Invoices
|
||||||
|
- Sales of the item as a component of a Product Bundle
|
||||||
|
|
||||||
|
Excludes consolidated invoices (already merged into Sales Invoices via
|
||||||
|
POS Closing Entry). Used to reflect near real-time availability in the
|
||||||
|
POS UI and to prevent overselling while multiple sessions may be active.
|
||||||
|
"""
|
||||||
|
pinv_item_reserved_qty = get_pos_reserved_qty_from_table("POS Invoice Item", item_code, warehouse)
|
||||||
|
packed_item_reserved_qty = get_pos_reserved_qty_from_table("Packed Item", item_code, warehouse)
|
||||||
|
|
||||||
|
reserved_qty = pinv_item_reserved_qty + packed_item_reserved_qty
|
||||||
|
|
||||||
|
return reserved_qty
|
||||||
|
|
||||||
|
|
||||||
|
def get_pos_reserved_qty_from_table(child_table, item_code, warehouse):
|
||||||
|
"""
|
||||||
|
Get the total reserved quantity for a given item in POS Invoices
|
||||||
|
from a specific child table.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
child_table (str): Name of the child table to query
|
||||||
|
(e.g., "POS Invoice Item", "Packed Item").
|
||||||
|
item_code (str): The Item Code to filter by.
|
||||||
|
warehouse (str): The Warehouse to filter by.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: The total reserved quantity for the item in the given
|
||||||
|
warehouse from submitted, unconsolidated POS Invoices.
|
||||||
|
"""
|
||||||
p_inv = frappe.qb.DocType("POS Invoice")
|
p_inv = frappe.qb.DocType("POS Invoice")
|
||||||
p_item = frappe.qb.DocType("POS Invoice Item")
|
p_item = frappe.qb.DocType(child_table)
|
||||||
|
|
||||||
|
qty_column = "qty" if child_table == "Packed Item" else "stock_qty"
|
||||||
|
|
||||||
reserved_qty = (
|
reserved_qty = (
|
||||||
frappe.qb.from_(p_inv)
|
frappe.qb.from_(p_inv)
|
||||||
.from_(p_item)
|
.from_(p_item)
|
||||||
.select(Sum(p_item.stock_qty).as_("stock_qty"))
|
.select(Sum(p_item[qty_column]).as_("stock_qty"))
|
||||||
.where(
|
.where(
|
||||||
(p_inv.name == p_item.parent)
|
(p_inv.name == p_item.parent)
|
||||||
& (IfNull(p_inv.consolidated_invoice, "") == "")
|
& (IfNull(p_inv.consolidated_invoice, "") == "")
|
||||||
|
|||||||
Reference in New Issue
Block a user