2022-03-01 11:45:04 +00:00
|
|
|
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
|
2021-12-27 21:03:54 +00:00
|
|
|
# See license.txt
|
|
|
|
|
|
|
|
import frappe
|
2022-02-28 11:25:46 +00:00
|
|
|
from frappe.tests.utils import FrappeTestCase
|
2021-12-27 21:03:54 +00:00
|
|
|
|
2022-02-18 13:23:05 +00:00
|
|
|
from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, format_report_data
|
2021-12-27 21:03:54 +00:00
|
|
|
|
2021-12-28 06:16:46 +00:00
|
|
|
|
2022-02-28 11:25:46 +00:00
|
|
|
class TestStockAgeing(FrappeTestCase):
|
2021-12-27 21:03:54 +00:00
|
|
|
def setUp(self) -> None:
|
|
|
|
self.filters = frappe._dict(
|
|
|
|
company="_Test Company",
|
2022-02-18 13:23:05 +00:00
|
|
|
to_date="2021-12-10",
|
|
|
|
range1=30, range2=60, range3=90
|
2021-12-27 21:03:54 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
def test_normal_inward_outward_queue(self):
|
2022-02-14 14:44:14 +00:00
|
|
|
"Reference: Case 1 in stock_ageing_fifo_logic.md (same wh)"
|
2021-12-27 21:03:54 +00:00
|
|
|
sle = [
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=30, qty_after_transaction=30,
|
2022-02-14 14:44:14 +00:00
|
|
|
warehouse="WH 1",
|
2021-12-27 21:03:54 +00:00
|
|
|
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=50,
|
2022-02-14 14:44:14 +00:00
|
|
|
warehouse="WH 1",
|
2021-12-27 21:03:54 +00:00
|
|
|
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=40,
|
2022-02-14 14:44:14 +00:00
|
|
|
warehouse="WH 1",
|
2021-12-27 21:03:54 +00:00
|
|
|
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()
|
|
|
|
|
|
|
|
self.assertTrue(slots["Flask Item"]["fifo_queue"])
|
|
|
|
result = slots["Flask Item"]
|
|
|
|
queue = result["fifo_queue"]
|
|
|
|
|
|
|
|
self.assertEqual(result["qty_after_transaction"], result["total_qty"])
|
|
|
|
self.assertEqual(queue[0][0], 20.0)
|
|
|
|
|
|
|
|
def test_insufficient_balance(self):
|
2022-02-14 14:44:14 +00:00
|
|
|
"Reference: Case 3 in stock_ageing_fifo_logic.md (same wh)"
|
2021-12-27 21:03:54 +00:00
|
|
|
sle = [
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-30), qty_after_transaction=(-30),
|
2022-02-14 14:44:14 +00:00
|
|
|
warehouse="WH 1",
|
2021-12-27 21:03:54 +00:00
|
|
|
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=(-10),
|
2022-02-14 14:44:14 +00:00
|
|
|
warehouse="WH 1",
|
2021-12-27 21:03:54 +00:00
|
|
|
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=20, qty_after_transaction=10,
|
2022-02-14 14:44:14 +00:00
|
|
|
warehouse="WH 1",
|
2021-12-27 21:03:54 +00:00
|
|
|
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=20,
|
2022-02-14 14:44:14 +00:00
|
|
|
warehouse="WH 1",
|
2021-12-27 21:03:54 +00:00
|
|
|
posting_date="2021-12-03", voucher_type="Stock Entry",
|
|
|
|
voucher_no="004",
|
|
|
|
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(queue[0][0], 10.0)
|
|
|
|
self.assertEqual(queue[1][0], 10.0)
|
|
|
|
|
2022-02-14 14:44:14 +00:00
|
|
|
def test_basic_stock_reconciliation(self):
|
|
|
|
"""
|
|
|
|
Ledger (same wh): [+30, reco reset >> 50, -10]
|
|
|
|
Bal: 40
|
|
|
|
"""
|
2021-12-27 21:03:54 +00:00
|
|
|
sle = [
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=30, qty_after_transaction=30,
|
2022-02-14 14:44:14 +00:00
|
|
|
warehouse="WH 1",
|
2021-12-27 21:03:54 +00:00
|
|
|
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=0, qty_after_transaction=50,
|
2022-02-14 14:44:14 +00:00
|
|
|
warehouse="WH 1",
|
2021-12-27 21:03:54 +00:00
|
|
|
posting_date="2021-12-02", voucher_type="Stock Reconciliation",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-10), qty_after_transaction=40,
|
2022-02-14 14:44:14 +00:00
|
|
|
warehouse="WH 1",
|
2021-12-27 21:03:54 +00:00
|
|
|
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"])
|
2022-02-14 14:44:14 +00:00
|
|
|
self.assertEqual(result["total_qty"], 40.0)
|
2021-12-27 21:03:54 +00:00
|
|
|
self.assertEqual(queue[0][0], 20.0)
|
|
|
|
self.assertEqual(queue[1][0], 20.0)
|
2022-02-14 14:44:14 +00:00
|
|
|
|
|
|
|
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"])
|
|
|
|
|
2022-02-15 13:11:42 +00:00
|
|
|
def test_repack_entry_same_item_split_rows(self):
|
|
|
|
"""
|
|
|
|
Split consumption rows and have single repacked item row (same warehouse).
|
|
|
|
Ledger:
|
|
|
|
Item | Qty | Voucher
|
|
|
|
------------------------
|
|
|
|
Item 1 | 500 | 001
|
|
|
|
Item 1 | -50 | 002 (repack)
|
|
|
|
Item 1 | -50 | 002 (repack)
|
|
|
|
Item 1 | 100 | 002 (repack)
|
|
|
|
|
|
|
|
Case most likely for batch items. Test time bucket computation.
|
|
|
|
"""
|
|
|
|
sle = [
|
|
|
|
frappe._dict( # stock up item
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=500, qty_after_transaction=500,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-03", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-50), qty_after_transaction=450,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-50), qty_after_transaction=400,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=100, qty_after_transaction=500,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
]
|
|
|
|
slots = FIFOSlots(self.filters, sle).generate()
|
|
|
|
item_result = slots["Flask Item"]
|
|
|
|
queue = item_result["fifo_queue"]
|
|
|
|
|
|
|
|
self.assertEqual(item_result["total_qty"], 500.0)
|
|
|
|
self.assertEqual(queue[0][0], 400.0)
|
2022-02-18 13:23:05 +00:00
|
|
|
self.assertEqual(queue[1][0], 50.0)
|
|
|
|
self.assertEqual(queue[2][0], 50.0)
|
2022-02-15 13:11:42 +00:00
|
|
|
# check if time buckets add up to balance qty
|
|
|
|
self.assertEqual(sum([i[0] for i in queue]), 500.0)
|
|
|
|
|
|
|
|
def test_repack_entry_same_item_overconsume(self):
|
|
|
|
"""
|
|
|
|
Over consume item and have less repacked item qty (same warehouse).
|
|
|
|
Ledger:
|
|
|
|
Item | Qty | Voucher
|
|
|
|
------------------------
|
|
|
|
Item 1 | 500 | 001
|
|
|
|
Item 1 | -100 | 002 (repack)
|
|
|
|
Item 1 | 50 | 002 (repack)
|
|
|
|
|
|
|
|
Case most likely for batch items. Test time bucket computation.
|
|
|
|
"""
|
|
|
|
sle = [
|
|
|
|
frappe._dict( # stock up item
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=500, qty_after_transaction=500,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-03", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-100), qty_after_transaction=400,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=50, qty_after_transaction=450,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
]
|
|
|
|
slots = FIFOSlots(self.filters, sle).generate()
|
|
|
|
item_result = slots["Flask Item"]
|
|
|
|
queue = item_result["fifo_queue"]
|
|
|
|
|
|
|
|
self.assertEqual(item_result["total_qty"], 450.0)
|
|
|
|
self.assertEqual(queue[0][0], 400.0)
|
|
|
|
self.assertEqual(queue[1][0], 50.0)
|
|
|
|
# check if time buckets add up to balance qty
|
|
|
|
self.assertEqual(sum([i[0] for i in queue]), 450.0)
|
|
|
|
|
2022-02-18 13:23:05 +00:00
|
|
|
def test_repack_entry_same_item_overconsume_with_split_rows(self):
|
|
|
|
"""
|
|
|
|
Over consume item and have less repacked item qty (same warehouse).
|
|
|
|
Ledger:
|
|
|
|
Item | Qty | Voucher
|
|
|
|
------------------------
|
|
|
|
Item 1 | 20 | 001
|
|
|
|
Item 1 | -50 | 002 (repack)
|
|
|
|
Item 1 | -50 | 002 (repack)
|
|
|
|
Item 1 | 50 | 002 (repack)
|
|
|
|
"""
|
|
|
|
sle = [
|
|
|
|
frappe._dict( # stock up item
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=20, qty_after_transaction=20,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-03", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-50), qty_after_transaction=(-30),
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-50), qty_after_transaction=(-80),
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=50, qty_after_transaction=(-30),
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
]
|
|
|
|
fifo_slots = FIFOSlots(self.filters, sle)
|
|
|
|
slots = fifo_slots.generate()
|
|
|
|
item_result = slots["Flask Item"]
|
|
|
|
queue = item_result["fifo_queue"]
|
|
|
|
|
|
|
|
self.assertEqual(item_result["total_qty"], -30.0)
|
|
|
|
self.assertEqual(queue[0][0], -30.0)
|
|
|
|
|
|
|
|
# check transfer bucket
|
|
|
|
transfer_bucket = fifo_slots.transferred_item_details[('002', 'Flask Item', 'WH 1')]
|
|
|
|
self.assertEqual(transfer_bucket[0][0], 50)
|
|
|
|
|
2022-02-15 13:11:42 +00:00
|
|
|
def test_repack_entry_same_item_overproduce(self):
|
|
|
|
"""
|
|
|
|
Under consume item and have more repacked item qty (same warehouse).
|
|
|
|
Ledger:
|
|
|
|
Item | Qty | Voucher
|
|
|
|
------------------------
|
|
|
|
Item 1 | 500 | 001
|
|
|
|
Item 1 | -50 | 002 (repack)
|
|
|
|
Item 1 | 100 | 002 (repack)
|
|
|
|
|
|
|
|
Case most likely for batch items. Test time bucket computation.
|
|
|
|
"""
|
|
|
|
sle = [
|
|
|
|
frappe._dict( # stock up item
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=500, qty_after_transaction=500,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-03", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-50), qty_after_transaction=450,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=100, qty_after_transaction=550,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
]
|
|
|
|
slots = FIFOSlots(self.filters, sle).generate()
|
|
|
|
item_result = slots["Flask Item"]
|
|
|
|
queue = item_result["fifo_queue"]
|
|
|
|
|
|
|
|
self.assertEqual(item_result["total_qty"], 550.0)
|
|
|
|
self.assertEqual(queue[0][0], 450.0)
|
2022-02-18 13:23:05 +00:00
|
|
|
self.assertEqual(queue[1][0], 50.0)
|
|
|
|
self.assertEqual(queue[2][0], 50.0)
|
2022-02-15 13:11:42 +00:00
|
|
|
# check if time buckets add up to balance qty
|
|
|
|
self.assertEqual(sum([i[0] for i in queue]), 550.0)
|
|
|
|
|
2022-02-18 13:23:05 +00:00
|
|
|
def test_repack_entry_same_item_overproduce_with_split_rows(self):
|
|
|
|
"""
|
|
|
|
Over consume item and have less repacked item qty (same warehouse).
|
|
|
|
Ledger:
|
|
|
|
Item | Qty | Voucher
|
|
|
|
------------------------
|
|
|
|
Item 1 | 20 | 001
|
|
|
|
Item 1 | -50 | 002 (repack)
|
|
|
|
Item 1 | 50 | 002 (repack)
|
|
|
|
Item 1 | 50 | 002 (repack)
|
|
|
|
"""
|
|
|
|
sle = [
|
|
|
|
frappe._dict( # stock up item
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=20, qty_after_transaction=20,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-03", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-50), qty_after_transaction=(-30),
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=50, qty_after_transaction=20,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=50, qty_after_transaction=70,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-04", voucher_type="Stock Entry",
|
|
|
|
voucher_no="002",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
]
|
|
|
|
fifo_slots = FIFOSlots(self.filters, sle)
|
|
|
|
slots = fifo_slots.generate()
|
|
|
|
item_result = slots["Flask Item"]
|
|
|
|
queue = item_result["fifo_queue"]
|
|
|
|
|
|
|
|
self.assertEqual(item_result["total_qty"], 70.0)
|
|
|
|
self.assertEqual(queue[0][0], 20.0)
|
|
|
|
self.assertEqual(queue[1][0], 50.0)
|
|
|
|
|
|
|
|
# check transfer bucket
|
|
|
|
transfer_bucket = fifo_slots.transferred_item_details[('002', 'Flask Item', 'WH 1')]
|
|
|
|
self.assertFalse(transfer_bucket)
|
|
|
|
|
|
|
|
def test_negative_stock_same_voucher(self):
|
|
|
|
"""
|
|
|
|
Test negative stock scenario in transfer bucket via repack entry (same wh).
|
|
|
|
Ledger:
|
|
|
|
Item | Qty | Voucher
|
|
|
|
------------------------
|
|
|
|
Item 1 | -50 | 001
|
|
|
|
Item 1 | -50 | 001
|
|
|
|
Item 1 | 30 | 001
|
|
|
|
Item 1 | 80 | 001
|
|
|
|
"""
|
|
|
|
sle = [
|
|
|
|
frappe._dict( # stock up item
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-50), qty_after_transaction=(-50),
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-01", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict( # stock up item
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=(-50), qty_after_transaction=(-100),
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-01", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict( # stock up item
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=30, qty_after_transaction=(-70),
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-01", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
]
|
|
|
|
fifo_slots = FIFOSlots(self.filters, sle)
|
|
|
|
slots = fifo_slots.generate()
|
|
|
|
item_result = slots["Flask Item"]
|
|
|
|
|
|
|
|
# check transfer bucket
|
|
|
|
transfer_bucket = fifo_slots.transferred_item_details[('001', 'Flask Item', 'WH 1')]
|
|
|
|
self.assertEqual(transfer_bucket[0][0], 20)
|
|
|
|
self.assertEqual(transfer_bucket[1][0], 50)
|
|
|
|
self.assertEqual(item_result["fifo_queue"][0][0], -70.0)
|
|
|
|
|
|
|
|
sle.append(frappe._dict(
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=80, qty_after_transaction=10,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-01", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
))
|
|
|
|
|
|
|
|
fifo_slots = FIFOSlots(self.filters, sle)
|
|
|
|
slots = fifo_slots.generate()
|
|
|
|
item_result = slots["Flask Item"]
|
|
|
|
|
|
|
|
transfer_bucket = fifo_slots.transferred_item_details[('001', 'Flask Item', 'WH 1')]
|
|
|
|
self.assertFalse(transfer_bucket)
|
|
|
|
self.assertEqual(item_result["fifo_queue"][0][0], 10.0)
|
|
|
|
|
|
|
|
def test_precision(self):
|
|
|
|
"Test if final balance qty is rounded off correctly."
|
|
|
|
sle = [
|
|
|
|
frappe._dict( # stock up item
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=0.3, qty_after_transaction=0.3,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-01", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
frappe._dict( # stock up item
|
|
|
|
name="Flask Item",
|
|
|
|
actual_qty=0.6, qty_after_transaction=0.9,
|
|
|
|
warehouse="WH 1",
|
|
|
|
posting_date="2021-12-01", voucher_type="Stock Entry",
|
|
|
|
voucher_no="001",
|
|
|
|
has_serial_no=False, serial_no=None
|
|
|
|
),
|
|
|
|
]
|
|
|
|
|
|
|
|
slots = FIFOSlots(self.filters, sle).generate()
|
|
|
|
report_data = format_report_data(self.filters, slots, self.filters["to_date"])
|
|
|
|
row = report_data[0] # first row in report
|
|
|
|
bal_qty = row[5]
|
|
|
|
range_qty_sum = sum([i for i in row[7:11]]) # get sum of range balance
|
|
|
|
|
|
|
|
# check if value of Available Qty column matches with range bucket post format
|
|
|
|
self.assertEqual(bal_qty, 0.9)
|
|
|
|
self.assertEqual(bal_qty, range_qty_sum)
|
|
|
|
|
2022-02-14 14:44:14 +00:00
|
|
|
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
|
|
|
|
|
2022-02-28 11:25:46 +00:00
|
|
|
return item_wise_slots, item_wh_wise_slots
|