fix: negative qty validation on stock reco cancellation (#27170)
* test: negative stock validation on SR cancel * fix: negative stock setting ignored in stock reco In stock reconcilation cancellation negative stock setting is ignored as `db.get_value` is returning string `'0'` which is not casted to int/bool for further logic. This causes negative qty, which evantually gets caught by reposting but by design this should stop cancellation. * test: typo and minor refactor
This commit is contained in:
		
							parent
							
								
									e11bfe7da4
								
							
						
					
					
						commit
						e7109c18db
					
				| @ -390,7 +390,7 @@ class StockReconciliation(StockController): | ||||
| 				sl_entries = self.merge_similar_item_serial_nos(sl_entries) | ||||
| 
 | ||||
| 			sl_entries.reverse() | ||||
| 			allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock") | ||||
| 			allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) | ||||
| 			self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock) | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -15,6 +15,7 @@ from erpnext.stock.doctype.item.test_item import create_item | ||||
| from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method | ||||
| from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos | ||||
| from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt | ||||
| from erpnext.tests.utils import change_settings | ||||
| 
 | ||||
| 
 | ||||
| class TestStockReconciliation(unittest.TestCase): | ||||
| @ -310,6 +311,7 @@ class TestStockReconciliation(unittest.TestCase): | ||||
| 		pr2.cancel() | ||||
| 		pr1.cancel() | ||||
| 
 | ||||
| 	@change_settings("Stock Settings", {"allow_negative_stock": 0}) | ||||
| 	def test_backdated_stock_reco_future_negative_stock(self): | ||||
| 		""" | ||||
| 			Test if a backdated stock reco causes future negative stock and is blocked. | ||||
| @ -327,8 +329,6 @@ class TestStockReconciliation(unittest.TestCase): | ||||
| 		warehouse = "_Test Warehouse - _TC" | ||||
| 		create_item(item_code) | ||||
| 
 | ||||
| 		negative_stock_setting = frappe.db.get_single_value("Stock Settings", "allow_negative_stock") | ||||
| 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 0) | ||||
| 
 | ||||
| 		pr1 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=10, rate=100, | ||||
| 			posting_date=add_days(nowdate(), -2)) | ||||
| @ -348,11 +348,50 @@ class TestStockReconciliation(unittest.TestCase): | ||||
| 		self.assertRaises(NegativeStockError, sr3.submit) | ||||
| 
 | ||||
| 		# teardown | ||||
| 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", negative_stock_setting) | ||||
| 		sr3.cancel() | ||||
| 		dn2.cancel() | ||||
| 		pr1.cancel() | ||||
| 
 | ||||
| 
 | ||||
| 	@change_settings("Stock Settings", {"allow_negative_stock": 0}) | ||||
| 	def test_backdated_stock_reco_cancellation_future_negative_stock(self): | ||||
| 		""" | ||||
| 			Test if a backdated stock reco cancellation that causes future negative stock is blocked. | ||||
| 			------------------------------------------- | ||||
| 			Var | Doc  | Qty | Balance | ||||
| 			------------------------------------------- | ||||
| 			SR  | Reco | 100 | 100     (posting date: today-1) (shouldn't be cancelled after DN) | ||||
| 			DN  | DN   | 100 |   0     (posting date: today) | ||||
| 		""" | ||||
| 		from erpnext.stock.stock_ledger import NegativeStockError | ||||
| 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note | ||||
| 		frappe.db.commit() | ||||
| 
 | ||||
| 		item_code = "Backdated-Reco-Cancellation-Item" | ||||
| 		warehouse = "_Test Warehouse - _TC" | ||||
| 		create_item(item_code) | ||||
| 
 | ||||
| 
 | ||||
| 		sr = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=100, rate=100, | ||||
| 			posting_date=add_days(nowdate(), -1)) | ||||
| 
 | ||||
| 		dn = create_delivery_note(item_code=item_code, warehouse=warehouse, qty=100, rate=120, | ||||
| 			posting_date=nowdate()) | ||||
| 
 | ||||
| 		dn_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": dn.name, "is_cancelled": 0}, | ||||
| 			"qty_after_transaction") | ||||
| 		self.assertEqual(dn_balance, 0) | ||||
| 
 | ||||
| 		# check if cancellation of stock reco is blocked | ||||
| 		self.assertRaises(NegativeStockError, sr.cancel) | ||||
| 
 | ||||
| 		repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name})) | ||||
| 		self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation") | ||||
| 
 | ||||
| 		# teardown | ||||
| 		frappe.db.rollback() | ||||
| 
 | ||||
| 
 | ||||
| 	def test_valid_batch(self): | ||||
| 		create_batch_item_with_batch("Testing Batch Item 1", "001") | ||||
| 		create_batch_item_with_batch("Testing Batch Item 2", "002") | ||||
|  | ||||
| @ -955,7 +955,7 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, | ||||
| 
 | ||||
| 	return valuation_rate | ||||
| 
 | ||||
| def update_qty_in_future_sle(args, allow_negative_stock=None): | ||||
| def update_qty_in_future_sle(args, allow_negative_stock=False): | ||||
| 	"""Recalculate Qty after Transaction in future SLEs based on current SLE.""" | ||||
| 	datetime_limit_condition = "" | ||||
| 	qty_shift = args.actual_qty | ||||
| @ -1044,8 +1044,8 @@ def get_datetime_limit_condition(detail): | ||||
| 			) | ||||
| 		)""" | ||||
| 
 | ||||
| def validate_negative_qty_in_future_sle(args, allow_negative_stock=None): | ||||
| 	allow_negative_stock = allow_negative_stock \ | ||||
| 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: | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user