mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-01 12:38:27 +00:00
fix(pos): add negative stock validation for product bundle
(cherry picked from commit 38b4536300)
This commit is contained in:
@@ -18,6 +18,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
|
|||||||
from erpnext.accounts.party import get_due_date, get_party_account
|
from erpnext.accounts.party import get_due_date, get_party_account
|
||||||
from erpnext.controllers.queries import item_query as _item_query
|
from erpnext.controllers.queries import item_query as _item_query
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
from erpnext.stock.stock_ledger import is_negative_stock_allowed
|
||||||
|
|
||||||
|
|
||||||
class PartialPaymentValidationError(frappe.ValidationError):
|
class PartialPaymentValidationError(frappe.ValidationError):
|
||||||
@@ -350,32 +351,66 @@ class POSInvoice(SalesInvoice):
|
|||||||
|
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if not d.serial_and_batch_bundle:
|
if not d.serial_and_batch_bundle:
|
||||||
available_stock, is_stock_item, is_negative_stock_allowed = get_stock_availability(
|
if frappe.db.exists("Product Bundle", d.item_code):
|
||||||
d.item_code, d.warehouse
|
(
|
||||||
)
|
availability,
|
||||||
|
is_stock_item,
|
||||||
|
is_negative_stock_allowed,
|
||||||
|
) = get_product_bundle_stock_availability(d.item_code, d.warehouse, d.stock_qty)
|
||||||
|
|
||||||
|
else:
|
||||||
|
availability, is_stock_item, is_negative_stock_allowed = get_stock_availability(
|
||||||
|
d.item_code, d.warehouse
|
||||||
|
)
|
||||||
|
|
||||||
if is_negative_stock_allowed:
|
if is_negative_stock_allowed:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
item_code, warehouse, _qty = (
|
if isinstance(availability, list):
|
||||||
frappe.bold(d.item_code),
|
error_msgs = []
|
||||||
frappe.bold(d.warehouse),
|
for item in availability:
|
||||||
frappe.bold(d.qty),
|
if flt(item["available"]) < flt(item["required"]):
|
||||||
)
|
error_msgs.append(
|
||||||
if is_stock_item and flt(available_stock) <= 0:
|
_("<li>Packed Item {0}: Required {1}, Available {2}</li>").format(
|
||||||
frappe.throw(
|
frappe.bold(item["item_code"]),
|
||||||
_("Row #{}: Item Code: {} is not available under warehouse {}.").format(
|
frappe.bold(flt(item["required"], 2)),
|
||||||
d.idx, item_code, warehouse
|
frappe.bold(flt(item["available"], 2)),
|
||||||
),
|
)
|
||||||
title=_("Item Unavailable"),
|
)
|
||||||
)
|
|
||||||
elif is_stock_item and flt(available_stock) < flt(d.stock_qty):
|
if error_msgs:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}.").format(
|
_(
|
||||||
d.idx, item_code, warehouse
|
"<b>Row #{0}:</b> Bundle {1} in warehouse {2} has insufficient packed items:<br><div style='margin-top: 15px;'><ul style='line-height: 0.8;'>{3}</ul></div>"
|
||||||
),
|
).format(
|
||||||
title=_("Item Unavailable"),
|
d.idx,
|
||||||
)
|
frappe.bold(d.item_code),
|
||||||
|
frappe.bold(d.warehouse),
|
||||||
|
"<br>".join(error_msgs),
|
||||||
|
),
|
||||||
|
title=_("Insufficient Stock for Product Bundle Items"),
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
item_code, warehouse = frappe.bold(d.item_code), frappe.bold(d.warehouse)
|
||||||
|
if is_stock_item and flt(availability) <= 0:
|
||||||
|
frappe.throw(
|
||||||
|
_("Row #{0}: Item {1} has no stock in warehouse {2}.").format(
|
||||||
|
d.idx, item_code, warehouse
|
||||||
|
),
|
||||||
|
title=_("Item Out of Stock"),
|
||||||
|
)
|
||||||
|
elif is_stock_item and flt(availability) < flt(d.stock_qty):
|
||||||
|
frappe.throw(
|
||||||
|
_("Row #{0}: Item {1} in warehouse {2}: Available {3}, Needed {4}.").format(
|
||||||
|
d.idx,
|
||||||
|
item_code,
|
||||||
|
warehouse,
|
||||||
|
frappe.bold(flt(availability, 2)),
|
||||||
|
frappe.bold(flt(d.stock_qty, 2)),
|
||||||
|
),
|
||||||
|
title=_("Insufficient Stock"),
|
||||||
|
)
|
||||||
|
|
||||||
def validate_serialised_or_batched_item(self):
|
def validate_serialised_or_batched_item(self):
|
||||||
error_msg = []
|
error_msg = []
|
||||||
@@ -763,8 +798,6 @@ class POSInvoice(SalesInvoice):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_stock_availability(item_code, warehouse):
|
def get_stock_availability(item_code, warehouse):
|
||||||
from erpnext.stock.stock_ledger import is_negative_stock_allowed
|
|
||||||
|
|
||||||
if frappe.db.get_value("Item", item_code, "is_stock_item"):
|
if frappe.db.get_value("Item", item_code, "is_stock_item"):
|
||||||
is_stock_item = True
|
is_stock_item = True
|
||||||
bin_qty = get_bin_qty(item_code, warehouse)
|
bin_qty = get_bin_qty(item_code, warehouse)
|
||||||
@@ -781,6 +814,26 @@ def get_stock_availability(item_code, warehouse):
|
|||||||
return 0, is_stock_item, False
|
return 0, is_stock_item, False
|
||||||
|
|
||||||
|
|
||||||
|
def get_product_bundle_stock_availability(item_code, warehouse, item_qty):
|
||||||
|
is_stock_item = True
|
||||||
|
bundle = frappe.get_doc("Product Bundle", item_code)
|
||||||
|
availabilities = []
|
||||||
|
for bundle_item in bundle.items:
|
||||||
|
if frappe.get_value("Item", bundle_item.item_code, "is_stock_item"):
|
||||||
|
bin_qty = get_bin_qty(bundle_item.item_code, warehouse)
|
||||||
|
reserved_qty = get_pos_reserved_qty(bundle_item.item_code, warehouse)
|
||||||
|
available = bin_qty - reserved_qty
|
||||||
|
availabilities.append(
|
||||||
|
{
|
||||||
|
"item_code": bundle_item.item_code,
|
||||||
|
"required": bundle_item.qty * item_qty,
|
||||||
|
"available": available,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return availabilities, is_stock_item, is_negative_stock_allowed(item_code=item_code)
|
||||||
|
|
||||||
|
|
||||||
def get_bundle_availability(bundle_item_code, warehouse):
|
def get_bundle_availability(bundle_item_code, warehouse):
|
||||||
product_bundle = frappe.get_doc("Product Bundle", bundle_item_code)
|
product_bundle = frappe.get_doc("Product Bundle", bundle_item_code)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user