mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-26 21:35:19 +00:00
test(stock): add unit test for get_bundle_wise_serial_nos query
(cherry picked from commit e748bf512b)
This commit is contained in:
committed by
Sudharsanan11
parent
cc457433fe
commit
dcff9164c4
@@ -11,8 +11,12 @@ from erpnext.stock.doctype.item.test_item import make_item
|
|||||||
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
||||||
add_serial_batch_ledgers,
|
add_serial_batch_ledgers,
|
||||||
combine_datetime,
|
combine_datetime,
|
||||||
|
get_available_batches_qty,
|
||||||
|
get_qty_based_available_batches,
|
||||||
|
get_type_of_transaction,
|
||||||
make_batch_nos,
|
make_batch_nos,
|
||||||
make_serial_nos,
|
make_serial_nos,
|
||||||
|
parse_serial_nos,
|
||||||
)
|
)
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
|
||||||
@@ -1383,3 +1387,128 @@ def make_serial_batch_bundle(kwargs):
|
|||||||
return sb.make_serial_and_batch_bundle()
|
return sb.make_serial_and_batch_bundle()
|
||||||
|
|
||||||
return sb
|
return sb
|
||||||
|
|
||||||
|
|
||||||
|
class TestSerialandBatchBundleLogic(FrappeTestCase):
|
||||||
|
"""Pure helpers and in-memory document validations, covering branches the
|
||||||
|
integration suite doesn't reach (no stock-ledger / serial / batch fixtures)."""
|
||||||
|
|
||||||
|
def test_parse_serial_nos_splits_and_trims(self):
|
||||||
|
self.assertEqual(parse_serial_nos("SN1\nSN2"), ["SN1", "SN2"])
|
||||||
|
self.assertEqual(parse_serial_nos("SN1, SN2 , SN3"), ["SN1", "SN2", "SN3"])
|
||||||
|
# blanks are dropped and an existing list is returned unchanged
|
||||||
|
self.assertEqual(parse_serial_nos("SN1,,\n , SN2"), ["SN1", "SN2"])
|
||||||
|
self.assertEqual(parse_serial_nos(["SN1", "SN2"]), ["SN1", "SN2"])
|
||||||
|
|
||||||
|
def test_get_qty_based_available_batches_allocates_across_batches(self):
|
||||||
|
batches = [
|
||||||
|
frappe._dict(batch_no="B1", qty=10, warehouse="W"),
|
||||||
|
frappe._dict(batch_no="B2", qty=5, warehouse="W"),
|
||||||
|
]
|
||||||
|
# 12 consumes B1 fully then 2 from B2
|
||||||
|
result = get_qty_based_available_batches(batches, 12)
|
||||||
|
self.assertEqual([(b.batch_no, b.qty) for b in result], [("B1", 10), ("B2", 2)])
|
||||||
|
# 8 is satisfied by B1 alone; B2 is not touched
|
||||||
|
result = get_qty_based_available_batches(batches, 8)
|
||||||
|
self.assertEqual([(b.batch_no, b.qty) for b in result], [("B1", 8)])
|
||||||
|
|
||||||
|
def test_get_available_batches_qty_aggregates_by_batch(self):
|
||||||
|
batches = [
|
||||||
|
frappe._dict(batch_no="B1", qty=10),
|
||||||
|
frappe._dict(batch_no="B2", qty=5),
|
||||||
|
frappe._dict(batch_no="B1", qty=3),
|
||||||
|
]
|
||||||
|
agg = get_available_batches_qty(batches)
|
||||||
|
self.assertEqual(agg["B1"], 13)
|
||||||
|
self.assertEqual(agg["B2"], 5)
|
||||||
|
|
||||||
|
def test_get_type_of_transaction_derives_direction(self):
|
||||||
|
def se(**kw):
|
||||||
|
return get_type_of_transaction(frappe._dict(doctype="Stock Entry"), frappe._dict(**kw))
|
||||||
|
|
||||||
|
self.assertEqual(se(s_warehouse="W"), "Outward") # issuing from a source warehouse
|
||||||
|
self.assertEqual(se(), "Inward") # only a target warehouse
|
||||||
|
self.assertEqual(
|
||||||
|
get_type_of_transaction(frappe._dict(doctype="Purchase Receipt"), frappe._dict()), "Inward"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
get_type_of_transaction(frappe._dict(doctype="Stock Reconciliation"), frappe._dict()), "Inward"
|
||||||
|
)
|
||||||
|
# a purchase return reverses the direction to Outward
|
||||||
|
self.assertEqual(
|
||||||
|
get_type_of_transaction(frappe._dict(doctype="Purchase Receipt", is_return=1), frappe._dict()),
|
||||||
|
"Outward",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_duplicate_serial_no_in_entries_is_rejected(self):
|
||||||
|
doc = frappe.new_doc("Serial and Batch Bundle")
|
||||||
|
doc.append("entries", {"serial_no": "SN1"})
|
||||||
|
doc.append("entries", {"serial_no": "SN1"})
|
||||||
|
self.assertRaises(frappe.ValidationError, doc.validate_duplicate_serial_and_batch_no)
|
||||||
|
|
||||||
|
def test_duplicate_batch_no_in_entries_is_rejected(self):
|
||||||
|
doc = frappe.new_doc("Serial and Batch Bundle")
|
||||||
|
doc.append("entries", {"batch_no": "B1"})
|
||||||
|
doc.append("entries", {"batch_no": "B1"})
|
||||||
|
self.assertRaises(frappe.ValidationError, doc.validate_duplicate_serial_and_batch_no)
|
||||||
|
|
||||||
|
def test_voucher_no_is_mandatory(self):
|
||||||
|
doc = frappe.new_doc("Serial and Batch Bundle")
|
||||||
|
self.assertRaises(frappe.ValidationError, doc.validate_serial_and_batch_data)
|
||||||
|
|
||||||
|
def test_calculate_total_qty_normalizes_and_signs(self):
|
||||||
|
inward = frappe.new_doc("Serial and Batch Bundle")
|
||||||
|
inward.type_of_transaction = "Inward"
|
||||||
|
inward.append("entries", {"qty": 5})
|
||||||
|
inward.append("entries", {"qty": 3})
|
||||||
|
inward.calculate_total_qty(save=False)
|
||||||
|
self.assertEqual(inward.total_qty, 8)
|
||||||
|
|
||||||
|
# Outward flips the sign
|
||||||
|
outward = frappe.new_doc("Serial and Batch Bundle")
|
||||||
|
outward.type_of_transaction = "Outward"
|
||||||
|
outward.append("entries", {"qty": 5})
|
||||||
|
outward.calculate_total_qty(save=False)
|
||||||
|
self.assertEqual(outward.total_qty, -5)
|
||||||
|
|
||||||
|
# a serialized bundle normalizes each row qty to 1
|
||||||
|
serialized = frappe.new_doc("Serial and Batch Bundle")
|
||||||
|
serialized.has_serial_no = 1
|
||||||
|
serialized.type_of_transaction = "Inward"
|
||||||
|
serialized.append("entries", {"qty": 5})
|
||||||
|
serialized.calculate_total_qty(save=False)
|
||||||
|
self.assertEqual(serialized.total_qty, 1)
|
||||||
|
|
||||||
|
def test_get_bundle_wise_serial_nos(self):
|
||||||
|
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
||||||
|
get_bundle_wise_serial_nos,
|
||||||
|
)
|
||||||
|
|
||||||
|
item_code = make_item(properties={"has_serial_no": 1, "serial_no_series": "TEST-BWSN-.#####"}).name
|
||||||
|
|
||||||
|
bundles = []
|
||||||
|
for _ in range(2):
|
||||||
|
se = make_stock_entry(
|
||||||
|
item_code=item_code,
|
||||||
|
target="_Test Warehouse - _TC",
|
||||||
|
qty=3,
|
||||||
|
rate=100,
|
||||||
|
)
|
||||||
|
bundles.append(se.items[0].serial_and_batch_bundle)
|
||||||
|
|
||||||
|
data = [frappe._dict(serial_and_batch_bundle=bundle) for bundle in bundles]
|
||||||
|
|
||||||
|
self.assertEqual(get_bundle_wise_serial_nos([], {}), {})
|
||||||
|
|
||||||
|
bundle_wise_serial_nos = get_bundle_wise_serial_nos(data, {})
|
||||||
|
for bundle in bundles:
|
||||||
|
self.assertEqual(sorted(bundle_wise_serial_nos[bundle]), get_serial_nos_from_bundle(bundle))
|
||||||
|
|
||||||
|
# check_serial_nos must restrict the result to the requested serial nos
|
||||||
|
serial_no = get_serial_nos_from_bundle(bundles[0])[0]
|
||||||
|
bundle_wise_serial_nos = get_bundle_wise_serial_nos(
|
||||||
|
data, {"check_serial_nos": True, "serial_nos": [serial_no]}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertNotIn(bundles[1], bundle_wise_serial_nos)
|
||||||
|
self.assertEqual(bundle_wise_serial_nos[bundles[0]], [serial_no])
|
||||||
|
|||||||
Reference in New Issue
Block a user