From 2f80c4dee508ec6eaed01622f684d908aa1bf2b3 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 3 Jan 2025 16:51:44 +0530 Subject: [PATCH 1/5] feat: Added valuation of quantity for each age group in stock ageing report --- .../stock/report/stock_ageing/stock_ageing.py | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index c7e0af6cd383..feaa0ba766dd 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -89,18 +89,22 @@ def get_average_age(fifo_queue: list, to_date: str) -> float: def get_range_age(filters: Filters, fifo_queue: list, to_date: str, item_dict: dict) -> list: precision = cint(frappe.db.get_single_value("System Settings", "float_precision", cache=True)) - range_values = [0.0] * (len(filters.ranges) + 1) + range_values = [0.0] * ((len(filters.ranges) * 2) + 2) for item in fifo_queue: age = flt(date_diff(to_date, item[1])) qty = flt(item[0]) if not item_dict["has_serial_no"] else 1.0 + stock_value = flt(item[2]) for i, age_limit in enumerate(filters.ranges): if age <= flt(age_limit): + i *= 2 range_values[i] = flt(range_values[i] + qty, precision) + range_values[i + 1] = flt(range_values[i + 1] + stock_value, precision) break else: - range_values[-1] = flt(range_values[-1] + qty, precision) + range_values[-2] = flt(range_values[-2] + qty, precision) + range_values[-1] = flt(range_values[-1] + stock_value, precision) return range_values @@ -199,6 +203,7 @@ def setup_ageing_columns(filters: Filters, range_columns: list): for i, label in enumerate(ranges): fieldname = "range" + str(i + 1) add_column(range_columns, label=_("Age ({0})").format(label), fieldname=fieldname) + add_column(range_columns, label=_("Value ({0})").format(label), fieldname=fieldname + "value") def add_column(range_columns: list, label: str, fieldname: str, fieldtype: str = "Float", width: int = 140): @@ -298,16 +303,22 @@ def __compute_incoming_stock(self, row: dict, fifo_queue: list, transfer_key: tu # neutralize 0/negative stock by adding positive stock fifo_queue[0][0] += flt(row.actual_qty) fifo_queue[0][1] = row.posting_date + fifo_queue[0][2] += flt(row.stock_value_difference) else: - fifo_queue.append([flt(row.actual_qty), row.posting_date]) + fifo_queue.append( + [flt(row.actual_qty), row.posting_date, flt(row.stock_value_difference)] + ) return + valuation = row.stock_value_difference / row.actual_qty for serial_no in serial_nos: if self.serial_no_batch_purchase_details.get(serial_no): - fifo_queue.append([serial_no, self.serial_no_batch_purchase_details.get(serial_no)]) + fifo_queue.append( + [serial_no, self.serial_no_batch_purchase_details.get(serial_no), valuation] + ) else: self.serial_no_batch_purchase_details.setdefault(serial_no, row.posting_date) - fifo_queue.append([serial_no, row.posting_date]) + fifo_queue.append([serial_no, row.posting_date, valuation]) def __compute_outgoing_stock(self, row: dict, fifo_queue: list, transfer_key: tuple, serial_nos: list): "Update FIFO Queue on outward stock." @@ -316,34 +327,44 @@ def __compute_outgoing_stock(self, row: dict, fifo_queue: list, transfer_key: tu return qty_to_pop = abs(row.actual_qty) + stock_value = abs(row.stock_value_difference) + while qty_to_pop: - slot = fifo_queue[0] if fifo_queue else [0, None] + slot = fifo_queue[0] if fifo_queue else [0, None, 0] if 0 < flt(slot[0]) <= qty_to_pop: # qty to pop >= slot qty # if +ve and not enough or exactly same balance in current slot, consume whole slot qty_to_pop -= flt(slot[0]) + stock_value -= flt(slot[2]) self.transferred_item_details[transfer_key].append(fifo_queue.pop(0)) elif not fifo_queue: # negative stock, no balance but qty yet to consume - fifo_queue.append([-(qty_to_pop), row.posting_date]) - self.transferred_item_details[transfer_key].append([qty_to_pop, row.posting_date]) + fifo_queue.append([-(qty_to_pop), row.posting_date, -(stock_value)]) + self.transferred_item_details[transfer_key].append( + [qty_to_pop, row.posting_date, stock_value] + ) qty_to_pop = 0 + stock_value = 0 else: # qty to pop < slot qty, ample balance # consume actual_qty from first slot slot[0] = flt(slot[0]) - qty_to_pop - self.transferred_item_details[transfer_key].append([qty_to_pop, slot[1]]) + slot[2] = flt(slot[2]) - stock_value + self.transferred_item_details[transfer_key].append([qty_to_pop, slot[1], stock_value]) qty_to_pop = 0 + stock_value = 0 def __adjust_incoming_transfer_qty(self, transfer_data: dict, fifo_queue: list, row: dict): "Add previously removed stock back to FIFO Queue." transfer_qty_to_pop = flt(row.actual_qty) + stock_value = flt(row.stock_value_difference) def add_to_fifo_queue(slot): if fifo_queue and flt(fifo_queue[0][0]) <= 0: # neutralize 0/negative stock by adding positive stock fifo_queue[0][0] += flt(slot[0]) fifo_queue[0][1] = slot[1] + fifo_queue[0][2] += flt(slot[2]) else: fifo_queue.append(slot) @@ -351,16 +372,20 @@ def add_to_fifo_queue(slot): if transfer_data and 0 < transfer_data[0][0] <= transfer_qty_to_pop: # bucket qty is not enough, consume whole transfer_qty_to_pop -= transfer_data[0][0] + stock_value -= transfer_data[0][2] add_to_fifo_queue(transfer_data.pop(0)) elif not transfer_data: # transfer bucket is empty, extra incoming qty - add_to_fifo_queue([transfer_qty_to_pop, row.posting_date]) + add_to_fifo_queue([transfer_qty_to_pop, row.posting_date, stock_value]) transfer_qty_to_pop = 0 + stock_value = 0 else: # ample bucket qty to consume transfer_data[0][0] -= transfer_qty_to_pop - add_to_fifo_queue([transfer_qty_to_pop, transfer_data[0][1]]) + transfer_data[0][2] -= stock_value + add_to_fifo_queue([transfer_qty_to_pop, transfer_data[0][1], stock_value]) transfer_qty_to_pop = 0 + stock_value = 0 def __update_balances(self, row: dict, key: tuple | str): self.item_details[key]["qty_after_transaction"] = row.qty_after_transaction @@ -412,6 +437,7 @@ def __get_stock_ledger_entries(self) -> Iterator[dict]: item.stock_uom, item.has_serial_no, sle.actual_qty, + sle.stock_value_difference, sle.posting_date, sle.voucher_type, sle.voucher_no, From 839b79ffd0545650a2225dbc23b9db58efbe3147 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 3 Jan 2025 20:00:36 +0530 Subject: [PATCH 2/5] fix: Test case for ageing report --- erpnext/stock/report/stock_ageing/test_stock_ageing.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py index 24ede79d9e8c..d622a298ca93 100644 --- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -178,6 +178,7 @@ def test_sequential_stock_reco_same_warehouse(self): name="Flask Item", actual_qty=0, qty_after_transaction=1000, + stock_value_difference=2000, warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Reconciliation", @@ -189,6 +190,7 @@ def test_sequential_stock_reco_same_warehouse(self): name="Flask Item", actual_qty=0, qty_after_transaction=400, + stock_value_difference=(-1200), warehouse="WH 1", posting_date="2021-12-02", voucher_type="Stock Reconciliation", @@ -200,6 +202,7 @@ def test_sequential_stock_reco_same_warehouse(self): name="Flask Item", actual_qty=(-10), qty_after_transaction=390, + stock_value_difference=(-20), warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -216,6 +219,7 @@ def test_sequential_stock_reco_same_warehouse(self): self.assertEqual(result["qty_after_transaction"], result["total_qty"]) self.assertEqual(result["total_qty"], 390.0) self.assertEqual(queue[0][0], 390.0) + self.assertEqual(queue[0][2], 780) def test_sequential_stock_reco_different_warehouse(self): """ From f996f71d16f425f0af00970088e084bf4773c870 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sat, 4 Jan 2025 15:17:31 +0530 Subject: [PATCH 3/5] fix: Fixed more test cases --- .../report/stock_ageing/test_stock_ageing.py | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py index d622a298ca93..bd46d3b9557a 100644 --- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -18,6 +18,7 @@ def test_normal_inward_outward_queue(self): name="Flask Item", actual_qty=30, qty_after_transaction=30, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Entry", @@ -29,6 +30,7 @@ def test_normal_inward_outward_queue(self): name="Flask Item", actual_qty=20, qty_after_transaction=50, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-02", voucher_type="Stock Entry", @@ -40,6 +42,7 @@ def test_normal_inward_outward_queue(self): name="Flask Item", actual_qty=(-10), qty_after_transaction=40, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -65,6 +68,7 @@ def test_insufficient_balance(self): name="Flask Item", actual_qty=(-30), qty_after_transaction=(-30), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Entry", @@ -76,6 +80,7 @@ def test_insufficient_balance(self): name="Flask Item", actual_qty=20, qty_after_transaction=(-10), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-02", voucher_type="Stock Entry", @@ -87,6 +92,7 @@ def test_insufficient_balance(self): name="Flask Item", actual_qty=20, qty_after_transaction=10, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -98,6 +104,7 @@ def test_insufficient_balance(self): name="Flask Item", actual_qty=10, qty_after_transaction=20, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -126,6 +133,7 @@ def test_basic_stock_reconciliation(self): name="Flask Item", actual_qty=30, qty_after_transaction=30, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Entry", @@ -137,6 +145,7 @@ def test_basic_stock_reconciliation(self): name="Flask Item", actual_qty=0, qty_after_transaction=50, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-02", voucher_type="Stock Reconciliation", @@ -148,6 +157,7 @@ def test_basic_stock_reconciliation(self): name="Flask Item", actual_qty=(-10), qty_after_transaction=40, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -178,7 +188,7 @@ def test_sequential_stock_reco_same_warehouse(self): name="Flask Item", actual_qty=0, qty_after_transaction=1000, - stock_value_difference=2000, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Reconciliation", @@ -190,7 +200,7 @@ def test_sequential_stock_reco_same_warehouse(self): name="Flask Item", actual_qty=0, qty_after_transaction=400, - stock_value_difference=(-1200), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-02", voucher_type="Stock Reconciliation", @@ -202,7 +212,7 @@ def test_sequential_stock_reco_same_warehouse(self): name="Flask Item", actual_qty=(-10), qty_after_transaction=390, - stock_value_difference=(-20), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -237,6 +247,7 @@ def test_sequential_stock_reco_different_warehouse(self): name="Flask Item", actual_qty=0, qty_after_transaction=1000, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Reconciliation", @@ -248,6 +259,7 @@ def test_sequential_stock_reco_different_warehouse(self): name="Flask Item", actual_qty=0, qty_after_transaction=400, + stock_value_difference=0, warehouse="WH 2", posting_date="2021-12-02", voucher_type="Stock Reconciliation", @@ -259,6 +271,7 @@ def test_sequential_stock_reco_different_warehouse(self): name="Flask Item", actual_qty=(-10), qty_after_transaction=990, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -305,6 +318,7 @@ def test_repack_entry_same_item_split_rows(self): name="Flask Item", actual_qty=500, qty_after_transaction=500, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -316,6 +330,7 @@ def test_repack_entry_same_item_split_rows(self): name="Flask Item", actual_qty=(-50), qty_after_transaction=450, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -327,6 +342,7 @@ def test_repack_entry_same_item_split_rows(self): name="Flask Item", actual_qty=(-50), qty_after_transaction=400, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -338,6 +354,7 @@ def test_repack_entry_same_item_split_rows(self): name="Flask Item", actual_qty=100, qty_after_transaction=500, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -374,6 +391,7 @@ def test_repack_entry_same_item_overconsume(self): name="Flask Item", actual_qty=500, qty_after_transaction=500, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -385,6 +403,7 @@ def test_repack_entry_same_item_overconsume(self): name="Flask Item", actual_qty=(-100), qty_after_transaction=400, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -396,6 +415,7 @@ def test_repack_entry_same_item_overconsume(self): name="Flask Item", actual_qty=50, qty_after_transaction=450, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -430,6 +450,7 @@ def test_repack_entry_same_item_overconsume_with_split_rows(self): name="Flask Item", actual_qty=20, qty_after_transaction=20, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -441,6 +462,7 @@ def test_repack_entry_same_item_overconsume_with_split_rows(self): name="Flask Item", actual_qty=(-50), qty_after_transaction=(-30), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -452,6 +474,7 @@ def test_repack_entry_same_item_overconsume_with_split_rows(self): name="Flask Item", actual_qty=(-50), qty_after_transaction=(-80), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -463,6 +486,7 @@ def test_repack_entry_same_item_overconsume_with_split_rows(self): name="Flask Item", actual_qty=50, qty_after_transaction=(-30), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -500,6 +524,7 @@ def test_repack_entry_same_item_overproduce(self): name="Flask Item", actual_qty=500, qty_after_transaction=500, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -511,6 +536,7 @@ def test_repack_entry_same_item_overproduce(self): name="Flask Item", actual_qty=(-50), qty_after_transaction=450, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -522,6 +548,7 @@ def test_repack_entry_same_item_overproduce(self): name="Flask Item", actual_qty=100, qty_after_transaction=550, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -557,6 +584,7 @@ def test_repack_entry_same_item_overproduce_with_split_rows(self): name="Flask Item", actual_qty=20, qty_after_transaction=20, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -568,6 +596,7 @@ def test_repack_entry_same_item_overproduce_with_split_rows(self): name="Flask Item", actual_qty=(-50), qty_after_transaction=(-30), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -579,6 +608,7 @@ def test_repack_entry_same_item_overproduce_with_split_rows(self): name="Flask Item", actual_qty=50, qty_after_transaction=20, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -590,6 +620,7 @@ def test_repack_entry_same_item_overproduce_with_split_rows(self): name="Flask Item", actual_qty=50, qty_after_transaction=70, + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-04", voucher_type="Stock Entry", @@ -627,6 +658,7 @@ def test_negative_stock_same_voucher(self): name="Flask Item", actual_qty=(-50), qty_after_transaction=(-50), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Entry", @@ -638,6 +670,7 @@ def test_negative_stock_same_voucher(self): name="Flask Item", actual_qty=(-50), qty_after_transaction=(-100), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Entry", @@ -649,6 +682,7 @@ def test_negative_stock_same_voucher(self): name="Flask Item", actual_qty=30, qty_after_transaction=(-70), + stock_value_difference=0, warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Entry", From 1f2d7da426defbac58513aa478e5bd439b8976c9 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sat, 4 Jan 2025 15:22:28 +0530 Subject: [PATCH 4/5] fix: Fixed final test case --- erpnext/stock/report/stock_ageing/test_stock_ageing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py index bd46d3b9557a..39c572b5e2ea 100644 --- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -229,7 +229,6 @@ def test_sequential_stock_reco_same_warehouse(self): self.assertEqual(result["qty_after_transaction"], result["total_qty"]) self.assertEqual(result["total_qty"], 390.0) self.assertEqual(queue[0][0], 390.0) - self.assertEqual(queue[0][2], 780) def test_sequential_stock_reco_different_warehouse(self): """ From dbb572eec17191fba5c44db91207c2b049ca67aa Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 8 Jan 2025 11:52:24 +0530 Subject: [PATCH 5/5] test: Valuation of ageing stock --- .../report/stock_ageing/test_stock_ageing.py | 115 +++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py index 39c572b5e2ea..52d8abf0c287 100644 --- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -18,7 +18,7 @@ def test_normal_inward_outward_queue(self): name="Flask Item", actual_qty=30, qty_after_transaction=30, - stock_value_difference=0, + stock_value_difference=30, warehouse="WH 1", posting_date="2021-12-01", voucher_type="Stock Entry", @@ -30,7 +30,7 @@ def test_normal_inward_outward_queue(self): name="Flask Item", actual_qty=20, qty_after_transaction=50, - stock_value_difference=0, + stock_value_difference=20, warehouse="WH 1", posting_date="2021-12-02", voucher_type="Stock Entry", @@ -42,7 +42,7 @@ def test_normal_inward_outward_queue(self): name="Flask Item", actual_qty=(-10), qty_after_transaction=40, - stock_value_difference=0, + stock_value_difference=(-10), warehouse="WH 1", posting_date="2021-12-03", voucher_type="Stock Entry", @@ -60,6 +60,8 @@ def test_normal_inward_outward_queue(self): self.assertEqual(result["qty_after_transaction"], result["total_qty"]) self.assertEqual(queue[0][0], 20.0) + data = format_report_data(self.filters, slots, self.filters["to_date"]) + self.assertEqual(data[0][8], 40.0) # valuating for stock value between age 0-30 def test_insufficient_balance(self): "Reference: Case 3 in stock_ageing_fifo_logic.md (same wh)" @@ -759,6 +761,113 @@ def test_precision(self): self.assertEqual(bal_qty, 0.9) self.assertEqual(bal_qty, range_qty_sum) + def test_ageing_stock_valuation(self): + "Test stock valuation for each time bucket." + sle = [ + frappe._dict( + name="Flask Item", + actual_qty=10, + qty_after_transaction=10, + stock_value_difference=10, + warehouse="WH 1", + posting_date="2021-12-01", + voucher_type="Stock Entry", + voucher_no="001", + has_serial_no=False, + serial_no=None, + ), + frappe._dict( + name="Flask Item", + actual_qty=20, + qty_after_transaction=30, + stock_value_difference=20, + warehouse="WH 1", + posting_date="2021-12-02", + voucher_type="Stock Entry", + voucher_no="002", + has_serial_no=False, + serial_no=None, + ), + frappe._dict( + name="Flask Item", + actual_qty=(-10), + qty_after_transaction=20, + stock_value_difference=(-10), + warehouse="WH 1", + posting_date="2021-12-03", + voucher_type="Stock Entry", + voucher_no="003", + has_serial_no=False, + serial_no=None, + ), + frappe._dict( + name="Flask Item", + actual_qty=10, + qty_after_transaction=30, + stock_value_difference=20, + warehouse="WH 1", + posting_date="2022-01-01", + voucher_type="Stock Entry", + voucher_no="004", + has_serial_no=False, + serial_no=None, + ), + frappe._dict( + name="Flask Item", + actual_qty=(-15), + qty_after_transaction=15, + stock_value_difference=(-15), + warehouse="WH 1", + posting_date="2022-01-02", + voucher_type="Stock Entry", + voucher_no="005", + has_serial_no=False, + serial_no=None, + ), + frappe._dict( + name="Flask Item", + actual_qty=10, + qty_after_transaction=25, + stock_value_difference=5, + warehouse="WH 1", + posting_date="2022-02-01", + voucher_type="Stock Entry", + voucher_no="006", + has_serial_no=False, + serial_no=None, + ), + frappe._dict( + name="Flask Item", + actual_qty=5, + qty_after_transaction=30, + stock_value_difference=2.5, + warehouse="WH 1", + posting_date="2022-02-02", + voucher_type="Stock Entry", + voucher_no="007", + has_serial_no=False, + serial_no=None, + ), + frappe._dict( + name="Flask Item", + actual_qty=5, + qty_after_transaction=35, + stock_value_difference=15, + warehouse="WH 1", + posting_date="2022-03-01", + voucher_type="Stock Entry", + voucher_no="008", + has_serial_no=False, + serial_no=None, + ), + ] + + slots = FIFOSlots(self.filters, sle).generate() + report_data = format_report_data(self.filters, slots, "2022-03-31") + range_values = report_data[0][7:15] + range_valuations = range_values[1::2] + self.assertEqual(range_valuations, [15, 7.5, 20, 5]) + def generate_item_and_item_wh_wise_slots(filters, sle): "Return results with and without 'show_warehouse_wise_stock'"