From 54fe2520c9e055194fe6e46edff329a7481c11cc Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 26 Aug 2021 15:59:37 +0530 Subject: [PATCH 1/3] test: negative stock validation on SR cancel --- .../test_stock_reconciliation.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 94b006c8944d..dae66ad47109 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -15,6 +15,7 @@ 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): @@ -353,6 +354,46 @@ def test_backdated_stock_reco_future_negative_stock(self): 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 cuases 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") From a7fc62e19566f45302611112b7ad1c3dfa7e040b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 26 Aug 2021 15:48:51 +0530 Subject: [PATCH 2/3] 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. --- .../doctype/stock_reconciliation/stock_reconciliation.py | 2 +- erpnext/stock/stock_ledger.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index cda7c1d31ae7..24b7b9aeb65a 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -390,7 +390,7 @@ def make_sle_on_cancel(self): 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) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 27feec1d15f1..48fd7d3d2619 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -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: From 9101ff2c96623bcd34328c2e7e06d0c57c12378c Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 26 Aug 2021 16:17:27 +0530 Subject: [PATCH 3/3] test: typo and minor refactor --- .../stock_reconciliation/test_stock_reconciliation.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index dae66ad47109..e4381271ed2a 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -311,6 +311,7 @@ def test_backdated_stock_reco_qty_reposting(self): 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. @@ -328,8 +329,6 @@ def test_backdated_stock_reco_future_negative_stock(self): 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)) @@ -349,7 +348,6 @@ def test_backdated_stock_reco_future_negative_stock(self): 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() @@ -358,7 +356,7 @@ def test_backdated_stock_reco_future_negative_stock(self): @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 cuases future negative stock is blocked. + Test if a backdated stock reco cancellation that causes future negative stock is blocked. ------------------------------------------- Var | Doc | Qty | Balance -------------------------------------------