From 293eb8d722c773864eef6ef45ce36a8bda25340e Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 10 May 2022 21:30:31 +0530 Subject: [PATCH] test: create stock test mixin for assertion/utils --- .../doctype/stock_entry/stock_entry_utils.py | 26 +++++++++++ .../test_stock_ledger_entry.py | 27 +----------- .../test_stock_reconciliation.py | 15 ++++--- erpnext/stock/tests/test_utils.py | 44 ++++++++++++++++--- 4 files changed, 75 insertions(+), 37 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py index c5c0cefe51..41a3b8916d 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py @@ -2,11 +2,37 @@ # See license.txt +from typing import TYPE_CHECKING, Optional, overload + import frappe from frappe.utils import cint, flt import erpnext +if TYPE_CHECKING: + from erpnext.stock.doctype.stock_entry.stock_entry import StockEntry + + +@overload +def make_stock_entry( + *, + item_code: str, + qty: float, + company: Optional[str] = None, + from_warehouse: Optional[str] = None, + to_warehouse: Optional[str] = None, + rate: Optional[float] = None, + serial_no: Optional[str] = None, + batch_no: Optional[str] = None, + posting_date: Optional[str] = None, + posting_time: Optional[str] = None, + purpose: Optional[str] = None, + do_not_save: bool = False, + do_not_submit: bool = False, + inspection_required: bool = False, +) -> "StockEntry": + ... + @frappe.whitelist() def make_stock_entry(**args): diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index eb1e0fc25f..55a213ccc3 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -24,9 +24,10 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation, ) from erpnext.stock.stock_ledger import get_previous_sle +from erpnext.stock.tests.test_utils import StockTestMixin -class TestStockLedgerEntry(FrappeTestCase): +class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): def setUp(self): items = create_items() reset("Stock Entry") @@ -541,30 +542,6 @@ class TestStockLedgerEntry(FrappeTestCase): "Incorrect 'Incoming Rate' values fetched for DN items", ) - def assertSLEs(self, doc, expected_sles, sle_filters=None): - """Compare sorted SLEs, useful for vouchers that create multiple SLEs for same line""" - - filters = {"voucher_no": doc.name, "voucher_type": doc.doctype, "is_cancelled": 0} - if sle_filters: - filters.update(sle_filters) - sles = frappe.get_all( - "Stock Ledger Entry", - fields=["*"], - filters=filters, - order_by="timestamp(posting_date, posting_time), creation", - ) - - for exp_sle, act_sle in zip(expected_sles, sles): - for k, v in exp_sle.items(): - act_value = act_sle[k] - if k == "stock_queue": - act_value = json.loads(act_value) - if act_value and act_value[0][0] == 0: - # ignore empty fifo bins - continue - - self.assertEqual(v, act_value, msg=f"{k} doesn't match \n{exp_sle}\n{act_sle}") - def test_batchwise_item_valuation_stock_reco(self): item, warehouses, batches = setup_item_valuation_test() state = {"stock_value": 0.0, "qty": 0.0} diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 9088eb802b..191c03f5f1 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -10,7 +10,7 @@ from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, cstr, flt, nowdate, nowtime, random_string from erpnext.accounts.utils import get_stock_and_account_balance -from erpnext.stock.doctype.item.test_item import create_item, make_item +from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import ( @@ -19,10 +19,11 @@ from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import ( ) from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after +from erpnext.stock.tests.test_utils import StockTestMixin from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method -class TestStockReconciliation(FrappeTestCase): +class TestStockReconciliation(FrappeTestCase, StockTestMixin): @classmethod def setUpClass(cls): create_batch_or_serial_no_items() @@ -40,7 +41,7 @@ class TestStockReconciliation(FrappeTestCase): self._test_reco_sle_gle("Moving Average") def _test_reco_sle_gle(self, valuation_method): - item_code = make_item(properties={"valuation_method": valuation_method}).name + item_code = self.make_item(properties={"valuation_method": valuation_method}).name se1, se2, se3 = insert_existing_sle(warehouse="Stores - TCP1", item_code=item_code) company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company") @@ -392,7 +393,7 @@ class TestStockReconciliation(FrappeTestCase): SR4 | Reco | 0 | 6 (posting date: today-1) [backdated] PR3 | PR | 1 | 7 (posting date: today) # can't post future PR """ - item_code = make_item().name + item_code = self.make_item().name warehouse = "_Test Warehouse - _TC" frappe.flags.dont_execute_stock_reposts = True @@ -458,7 +459,7 @@ class TestStockReconciliation(FrappeTestCase): from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.stock_ledger import NegativeStockError - item_code = make_item().name + item_code = self.make_item().name warehouse = "_Test Warehouse - _TC" pr1 = make_purchase_receipt( @@ -506,7 +507,7 @@ class TestStockReconciliation(FrappeTestCase): from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.stock_ledger import NegativeStockError - item_code = make_item().name + item_code = self.make_item().name warehouse = "_Test Warehouse - _TC" sr = create_stock_reconciliation( @@ -549,7 +550,7 @@ class TestStockReconciliation(FrappeTestCase): # repost will make this test useless, qty should update in realtime without reposts frappe.flags.dont_execute_stock_reposts = True - item_code = make_item().name + item_code = self.make_item().name warehouse = "_Test Warehouse - _TC" sr = create_stock_reconciliation( diff --git a/erpnext/stock/tests/test_utils.py b/erpnext/stock/tests/test_utils.py index 9ee0c9f3b5..e07e08c79e 100644 --- a/erpnext/stock/tests/test_utils.py +++ b/erpnext/stock/tests/test_utils.py @@ -1,16 +1,50 @@ +import json + import frappe from frappe.tests.utils import FrappeTestCase -from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.utils import scan_barcode -class TestStockUtilities(FrappeTestCase): +class StockTestMixin: + """Mixin to simplfy stock ledger tests, useful for all stock transactions.""" + + def make_item(self, item_code=None, properties=None, *args, **kwargs): + from erpnext.stock.doctype.item.test_item import make_item + + return make_item(item_code, properties, *args, **kwargs) + + def assertSLEs(self, doc, expected_sles, sle_filters=None): + """Compare sorted SLEs, useful for vouchers that create multiple SLEs for same line""" + + filters = {"voucher_no": doc.name, "voucher_type": doc.doctype, "is_cancelled": 0} + if sle_filters: + filters.update(sle_filters) + sles = frappe.get_all( + "Stock Ledger Entry", + fields=["*"], + filters=filters, + order_by="timestamp(posting_date, posting_time), creation", + ) + + for exp_sle, act_sle in zip(expected_sles, sles): + for k, v in exp_sle.items(): + act_value = act_sle[k] + if k == "stock_queue": + act_value = json.loads(act_value) + if act_value and act_value[0][0] == 0: + # ignore empty fifo bins + continue + + self.assertEqual(v, act_value, msg=f"{k} doesn't match \n{exp_sle}\n{act_sle}") + + +class TestStockUtilities(FrappeTestCase, StockTestMixin): def test_barcode_scanning(self): - simple_item = make_item(properties={"barcodes": [{"barcode": "12399"}]}) + simple_item = self.make_item(properties={"barcodes": [{"barcode": "12399"}]}) self.assertEqual(scan_barcode("12399")["item_code"], simple_item.name) - batch_item = make_item(properties={"has_batch_no": 1, "create_new_batch": 1}) + batch_item = self.make_item(properties={"has_batch_no": 1, "create_new_batch": 1}) batch = frappe.get_doc(doctype="Batch", item=batch_item.name).insert() batch_scan = scan_barcode(batch.name) @@ -19,7 +53,7 @@ class TestStockUtilities(FrappeTestCase): self.assertEqual(batch_scan["has_batch_no"], 1) self.assertEqual(batch_scan["has_serial_no"], 0) - serial_item = make_item(properties={"has_serial_no": 1}) + serial_item = self.make_item(properties={"has_serial_no": 1}) serial = frappe.get_doc( doctype="Serial No", item_code=serial_item.name, serial_no=frappe.generate_hash() ).insert()