@ -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 " ] ) )
frappe . throw ( message , NegativeStockError , title = ' Insufficient Stock ' )
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 " )
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.