1717 cint ,
1818 cstr ,
1919 flt ,
20+ get_datetime ,
2021 get_link_to_form ,
2122 getdate ,
2223 now ,
@@ -1452,31 +1453,44 @@ def validate_batch_quantity(self):
14521453 for d in self .entries :
14531454 available_qty = batch_wise_available_qty .get (d .batch_no , 0 )
14541455 if flt (available_qty , precision ) < 0 :
1455- frappe .throw (
1456- _ (
1457- """
1458- The Batch {0} of an item {1} has negative stock in the warehouse {2}. Please add a stock quantity of {3} to proceed with this entry."""
1459- ).format (
1460- bold (d .batch_no ),
1461- bold (self .item_code ),
1462- bold (self .warehouse ),
1463- bold (abs (flt (available_qty , precision ))),
1464- ),
1465- title = _ ("Negative Stock Error" ),
1466- )
1456+ self .throw_negative_batch (d .batch_no , available_qty , precision )
1457+
1458+ def throw_negative_batch (self , batch_no , available_qty , precision ):
1459+ from erpnext .stock .stock_ledger import NegativeStockError
1460+
1461+ frappe .throw (
1462+ _ (
1463+ """
1464+ The Batch {0} of an item {1} has negative stock in the warehouse {2}. Please add a stock quantity of {3} to proceed with this entry."""
1465+ ).format (
1466+ bold (batch_no ),
1467+ bold (self .item_code ),
1468+ bold (self .warehouse ),
1469+ bold (abs (flt (available_qty , precision ))),
1470+ ),
1471+ title = _ ("Negative Stock Error" ),
1472+ exc = NegativeStockError ,
1473+ )
14671474
14681475 def get_batchwise_available_qty (self ):
1469- available_qty = self .get_available_qty_from_sabb ()
1470- available_qty_from_ledger = self .get_available_qty_from_stock_ledger ()
1476+ batchwise_entries = self .get_available_qty_from_sabb ()
1477+ batchwise_entries . extend ( self .get_available_qty_from_stock_ledger () )
14711478
1472- if not available_qty_from_ledger :
1473- return available_qty
1479+ available_qty = frappe ._dict ({})
1480+ batchwise_entries = sorted (
1481+ batchwise_entries ,
1482+ key = lambda x : (get_datetime (x .get ("posting_datetime" )), get_datetime (x .get ("creation" ))),
1483+ )
14741484
1475- for batch_no , qty in available_qty_from_ledger .items ():
1476- if batch_no in available_qty :
1477- available_qty [batch_no ] += qty
1485+ precision = frappe .get_precision ("Serial and Batch Entry" , "qty" )
1486+ for row in batchwise_entries :
1487+ if row .batch_no in available_qty :
1488+ available_qty [row .batch_no ] += flt (row .qty )
14781489 else :
1479- available_qty [batch_no ] = qty
1490+ available_qty [row .batch_no ] = flt (row .qty )
1491+
1492+ if flt (available_qty [row .batch_no ], precision ) < 0 :
1493+ self .throw_negative_batch (row .batch_no , available_qty [row .batch_no ], precision )
14801494
14811495 return available_qty
14821496
@@ -1489,7 +1503,9 @@ def get_available_qty_from_stock_ledger(self):
14891503 frappe .qb .from_ (sle )
14901504 .select (
14911505 sle .batch_no ,
1492- Sum (sle .actual_qty ).as_ ("available_qty" ),
1506+ sle .actual_qty .as_ ("qty" ),
1507+ sle .posting_datetime ,
1508+ sle .creation ,
14931509 )
14941510 .where (
14951511 (sle .item_code == self .item_code )
@@ -1501,12 +1517,9 @@ def get_available_qty_from_stock_ledger(self):
15011517 & (sle .batch_no .isnotnull ())
15021518 )
15031519 .for_update ()
1504- .groupby (sle .batch_no )
15051520 )
15061521
1507- res = query .run (as_list = True )
1508-
1509- return frappe ._dict (res ) if res else frappe ._dict ()
1522+ return query .run (as_dict = True )
15101523
15111524 def get_available_qty_from_sabb (self ):
15121525 batches = [d .batch_no for d in self .entries if d .batch_no ]
@@ -1517,7 +1530,9 @@ def get_available_qty_from_sabb(self):
15171530 frappe .qb .from_ (child )
15181531 .select (
15191532 child .batch_no ,
1520- Sum (child .qty ).as_ ("available_qty" ),
1533+ child .qty ,
1534+ child .posting_datetime ,
1535+ child .creation ,
15211536 )
15221537 .where (
15231538 (child .item_code == self .item_code )
@@ -1528,13 +1543,10 @@ def get_available_qty_from_sabb(self):
15281543 & (child .type_of_transaction .isin (["Inward" , "Outward" ]))
15291544 )
15301545 .for_update ()
1531- .groupby (child .batch_no )
15321546 )
15331547 query = query .where (child .voucher_type != "Pick List" )
15341548
1535- res = query .run (as_list = True )
1536-
1537- return frappe ._dict (res ) if res else frappe ._dict ()
1549+ return query .run (as_dict = True )
15381550
15391551 def validate_voucher_no_docstatus (self ):
15401552 if self .voucher_type == "POS Invoice" :
0 commit comments