fix: check future negative stock for batches
batch's ledger is only maintained in form of `actual_qty` on batch's SLEs. To validate if batch has any negative qty in future, cumulative total of `actual_qty` is required to ensure it never goes negative.
This commit is contained in:
parent
1cbeba5f1d
commit
5eba57528c
@ -1089,17 +1089,36 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=False):
|
||||
allow_negative_stock = cint(allow_negative_stock) \
|
||||
or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
|
||||
|
||||
if (args.actual_qty < 0 or args.voucher_type == "Stock Reconciliation") and not allow_negative_stock:
|
||||
sle = get_future_sle_with_negative_qty(args)
|
||||
if sle:
|
||||
message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
|
||||
abs(sle[0]["qty_after_transaction"]),
|
||||
frappe.get_desk_link('Item', args.item_code),
|
||||
frappe.get_desk_link('Warehouse', args.warehouse),
|
||||
sle[0]["posting_date"], sle[0]["posting_time"],
|
||||
frappe.get_desk_link(sle[0]["voucher_type"], sle[0]["voucher_no"]))
|
||||
if allow_negative_stock:
|
||||
return
|
||||
if not (args.actual_qty < 0 or args.voucher_type == "Stock Reconciliation"):
|
||||
return
|
||||
|
||||
neg_sle = get_future_sle_with_negative_qty(args)
|
||||
if neg_sle:
|
||||
message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
|
||||
abs(neg_sle[0]["qty_after_transaction"]),
|
||||
frappe.get_desk_link('Item', args.item_code),
|
||||
frappe.get_desk_link('Warehouse', args.warehouse),
|
||||
neg_sle[0]["posting_date"], neg_sle[0]["posting_time"],
|
||||
frappe.get_desk_link(neg_sle[0]["voucher_type"], neg_sle[0]["voucher_no"]))
|
||||
|
||||
frappe.throw(message, NegativeStockError, title='Insufficient Stock')
|
||||
|
||||
|
||||
if not args.batch_no:
|
||||
return
|
||||
|
||||
neg_batch_sle = get_future_sle_with_negative_batch_qty(args)
|
||||
if neg_batch_sle:
|
||||
message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
|
||||
abs(neg_batch_sle[0]["cumulative_total"]),
|
||||
frappe.get_desk_link('Batch', args.batch_no),
|
||||
frappe.get_desk_link('Warehouse', args.warehouse),
|
||||
neg_batch_sle[0]["posting_date"], neg_batch_sle[0]["posting_time"],
|
||||
frappe.get_desk_link(neg_batch_sle[0]["voucher_type"], neg_batch_sle[0]["voucher_no"]))
|
||||
frappe.throw(message, NegativeStockError, title="Insufficient Stock for Batch")
|
||||
|
||||
frappe.throw(message, NegativeStockError, title='Insufficient Stock')
|
||||
|
||||
def get_future_sle_with_negative_qty(args):
|
||||
return frappe.db.sql("""
|
||||
@ -1118,6 +1137,29 @@ def get_future_sle_with_negative_qty(args):
|
||||
limit 1
|
||||
""", args, as_dict=1)
|
||||
|
||||
|
||||
def get_future_sle_with_negative_batch_qty(args):
|
||||
return frappe.db.sql("""
|
||||
with batch_ledger as (
|
||||
select
|
||||
posting_date, posting_time, voucher_type, voucher_no,
|
||||
sum(actual_qty) over (order by posting_date, posting_time, creation) as cumulative_total
|
||||
from `tabStock Ledger Entry`
|
||||
where
|
||||
item_code = %(item_code)s
|
||||
and warehouse = %(warehouse)s
|
||||
and batch_no=%(batch_no)s
|
||||
and is_cancelled = 0
|
||||
order by posting_date, posting_time, creation
|
||||
)
|
||||
select * from batch_ledger
|
||||
where
|
||||
cumulative_total < 0.0
|
||||
and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
|
||||
limit 1
|
||||
""", args, as_dict=1)
|
||||
|
||||
|
||||
def _round_off_if_near_zero(number: float, precision: int = 6) -> float:
|
||||
""" Rounds off the number to zero only if number is close to zero for decimal
|
||||
specified in precision. Precision defaults to 6.
|
||||
|
Loading…
Reference in New Issue
Block a user