From f221a0d253c6c4a2dc1faf4b41f371bf5a7e86ad Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 14 Feb 2022 20:14:14 +0530 Subject: [PATCH] test: Cover back to back recos from different warehouses --- .../stock_ageing/stock_ageing_fifo_logic.md | 1 + .../report/stock_ageing/test_stock_ageing.py | 127 +++++++++++++++++- 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md b/erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md index 5ffe97fd74..9e9bed48e3 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md +++ b/erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md @@ -15,6 +15,7 @@ Here, the balance qty is 70. 50 qty is (today-the 1st) days old 20 qty is (today-the 2nd) days old +> Note: We generate FIFO slots warehouse wise as stock reconciliations from different warehouses can cause incorrect values. ### Calculation of FIFO Slots #### Case 1: Outward from sufficient balance qty diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py index 949bb7c15a..66d2f6b753 100644 --- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -15,11 +15,12 @@ class TestStockAgeing(ERPNextTestCase): ) def test_normal_inward_outward_queue(self): - "Reference: Case 1 in stock_ageing_fifo_logic.md" + "Reference: Case 1 in stock_ageing_fifo_logic.md (same wh)" sle = [ frappe._dict( name="Flask Item", actual_qty=30, qty_after_transaction=30, + warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Entry", voucher_no="001", has_serial_no=False, serial_no=None @@ -27,6 +28,7 @@ class TestStockAgeing(ERPNextTestCase): frappe._dict( name="Flask Item", actual_qty=20, qty_after_transaction=50, + warehouse="WH 1", posting_date="2021-12-02", voucher_type="Stock Entry", voucher_no="002", has_serial_no=False, serial_no=None @@ -34,6 +36,7 @@ class TestStockAgeing(ERPNextTestCase): frappe._dict( name="Flask Item", actual_qty=(-10), qty_after_transaction=40, + warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", voucher_no="003", has_serial_no=False, serial_no=None @@ -50,11 +53,12 @@ class TestStockAgeing(ERPNextTestCase): self.assertEqual(queue[0][0], 20.0) def test_insufficient_balance(self): - "Reference: Case 3 in stock_ageing_fifo_logic.md" + "Reference: Case 3 in stock_ageing_fifo_logic.md (same wh)" sle = [ frappe._dict( name="Flask Item", actual_qty=(-30), qty_after_transaction=(-30), + warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Entry", voucher_no="001", has_serial_no=False, serial_no=None @@ -62,6 +66,7 @@ class TestStockAgeing(ERPNextTestCase): frappe._dict( name="Flask Item", actual_qty=20, qty_after_transaction=(-10), + warehouse="WH 1", posting_date="2021-12-02", voucher_type="Stock Entry", voucher_no="002", has_serial_no=False, serial_no=None @@ -69,6 +74,7 @@ class TestStockAgeing(ERPNextTestCase): frappe._dict( name="Flask Item", actual_qty=20, qty_after_transaction=10, + warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", voucher_no="003", has_serial_no=False, serial_no=None @@ -76,6 +82,7 @@ class TestStockAgeing(ERPNextTestCase): frappe._dict( name="Flask Item", actual_qty=10, qty_after_transaction=20, + warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", voucher_no="004", has_serial_no=False, serial_no=None @@ -91,11 +98,16 @@ class TestStockAgeing(ERPNextTestCase): self.assertEqual(queue[0][0], 10.0) self.assertEqual(queue[1][0], 10.0) - def test_stock_reconciliation(self): + def test_basic_stock_reconciliation(self): + """ + Ledger (same wh): [+30, reco reset >> 50, -10] + Bal: 40 + """ sle = [ frappe._dict( name="Flask Item", actual_qty=30, qty_after_transaction=30, + warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Entry", voucher_no="001", has_serial_no=False, serial_no=None @@ -103,6 +115,7 @@ class TestStockAgeing(ERPNextTestCase): frappe._dict( name="Flask Item", actual_qty=0, qty_after_transaction=50, + warehouse="WH 1", posting_date="2021-12-02", voucher_type="Stock Reconciliation", voucher_no="002", has_serial_no=False, serial_no=None @@ -110,6 +123,7 @@ class TestStockAgeing(ERPNextTestCase): frappe._dict( name="Flask Item", actual_qty=(-10), qty_after_transaction=40, + warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", voucher_no="003", has_serial_no=False, serial_no=None @@ -122,5 +136,112 @@ class TestStockAgeing(ERPNextTestCase): queue = result["fifo_queue"] self.assertEqual(result["qty_after_transaction"], result["total_qty"]) + self.assertEqual(result["total_qty"], 40.0) self.assertEqual(queue[0][0], 20.0) self.assertEqual(queue[1][0], 20.0) + + def test_sequential_stock_reco_same_warehouse(self): + """ + Test back to back stock recos (same warehouse). + Ledger: [reco opening >> +1000, reco reset >> 400, -10] + Bal: 390 + """ + sle = [ + frappe._dict( + name="Flask Item", + actual_qty=0, qty_after_transaction=1000, + warehouse="WH 1", + posting_date="2021-12-01", voucher_type="Stock Reconciliation", + voucher_no="002", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=0, qty_after_transaction=400, + warehouse="WH 1", + posting_date="2021-12-02", voucher_type="Stock Reconciliation", + voucher_no="003", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=(-10), qty_after_transaction=390, + warehouse="WH 1", + posting_date="2021-12-03", voucher_type="Stock Entry", + voucher_no="003", + has_serial_no=False, serial_no=None + ) + ] + slots = FIFOSlots(self.filters, sle).generate() + + result = slots["Flask Item"] + queue = result["fifo_queue"] + + self.assertEqual(result["qty_after_transaction"], result["total_qty"]) + self.assertEqual(result["total_qty"], 390.0) + self.assertEqual(queue[0][0], 390.0) + + def test_sequential_stock_reco_different_warehouse(self): + """ + Ledger: + WH | Voucher | Qty + ------------------- + WH1 | Reco | 1000 + WH2 | Reco | 400 + WH1 | SE | -10 + + Bal: WH1 bal + WH2 bal = 990 + 400 = 1390 + """ + sle = [ + frappe._dict( + name="Flask Item", + actual_qty=0, qty_after_transaction=1000, + warehouse="WH 1", + posting_date="2021-12-01", voucher_type="Stock Reconciliation", + voucher_no="002", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=0, qty_after_transaction=400, + warehouse="WH 2", + posting_date="2021-12-02", voucher_type="Stock Reconciliation", + voucher_no="003", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=(-10), qty_after_transaction=990, + warehouse="WH 1", + posting_date="2021-12-03", voucher_type="Stock Entry", + voucher_no="004", + has_serial_no=False, serial_no=None + ) + ] + + item_wise_slots, item_wh_wise_slots = generate_item_and_item_wh_wise_slots( + filters=self.filters,sle=sle + ) + + # test without 'show_warehouse_wise_stock' + item_result = item_wise_slots["Flask Item"] + queue = item_result["fifo_queue"] + + self.assertEqual(item_result["qty_after_transaction"], item_result["total_qty"]) + self.assertEqual(item_result["total_qty"], 1390.0) + self.assertEqual(queue[0][0], 990.0) + self.assertEqual(queue[1][0], 400.0) + + # test with 'show_warehouse_wise_stock' checked + item_wh_balances = [item_wh_wise_slots.get(i).get("qty_after_transaction") for i in item_wh_wise_slots] + self.assertEqual(sum(item_wh_balances), item_result["qty_after_transaction"]) + +def generate_item_and_item_wh_wise_slots(filters, sle): + "Return results with and without 'show_warehouse_wise_stock'" + item_wise_slots = FIFOSlots(filters, sle).generate() + + filters.show_warehouse_wise_stock = True + item_wh_wise_slots = FIFOSlots(filters, sle).generate() + filters.show_warehouse_wise_stock = False + + return item_wise_slots, item_wh_wise_slots \ No newline at end of file