Commit 975baadb authored by Titouan Soulard's avatar Titouan Soulard

erp5_trade: increase test coverage

- Test case where new stock in lesser than old stock
- Test recalculation of inventory
- Test full inventories
- Test `getTotalQuantity` and `getTotalPrice` methods
parent 96606fea
Pipeline #39222 failed with stage
in 0 seconds
......@@ -64,6 +64,23 @@ class TestInventoryModule(TestOrderMixin, ERP5TypeTestCase):
)
self.tic()
def _createVariatedProduct(self):
variated_product = self.portal.product_module.newContent(
portal_type=self.default_product_portal_type,
title="Variated product",
)
variated_product.setQuantityUnitList(["unit/piece"])
variated_product.setIndividualVariationBaseCategoryList(["variation"])
for variation_name in ["A", "B", "C"]:
variated_product.newContent(
portal_type=self.default_product_variation_portal_type,
title="Variation " + variation_name,
)
self.tic()
return variated_product
def _createPurchasePackingList(self, lines):
purchase_packing_list_value = self.portal.purchase_packing_list_module.newContent(
portal_type="Purchase Packing List",
......@@ -119,12 +136,13 @@ class TestInventoryModule(TestOrderMixin, ERP5TypeTestCase):
return purchase_packing_list_value
def _createInventory(self, lines):
def _createInventory(self, lines, is_full=False, record=True):
inventory_value = self.portal.inventory_module.newContent(
portal_type="Inventory",
destination_section_value=self.destination_organisation_value,
destination_value=self.destination_organisation_value,
start_date=self.default_inventory_date,
full_inventory=is_full,
)
for product_relative_url, line in lines.items():
......@@ -161,6 +179,8 @@ class TestInventoryModule(TestOrderMixin, ERP5TypeTestCase):
inventory_value.Inventory_computeOffsetAction()
self.tic()
if record:
inventory_value.deliver()
self.tic()
......@@ -181,7 +201,7 @@ class TestInventoryModule(TestOrderMixin, ERP5TypeTestCase):
return (inventory, total_price)
def test_01_addUnvariatedToStock(self):
def test_01_addsToStock(self):
"""
For an unvariated product, checks that quantity and price are correct
after noticing that there are more products than expected in stock.
......@@ -226,42 +246,78 @@ class TestInventoryModule(TestOrderMixin, ERP5TypeTestCase):
# are movements. Otherwise, it is totally discarded.
self.assertEqual(310.0, final_price)
def test_02_addVariatedToStock(self):
def test_02_removesFromStock(self):
"""
For a variated product, checks that quantity and price are correct
after noticing that there are more products than expected in stock.
For an unvariated product, checks that quantity and price are correct
after noticing that there are less products than expected in stock.
"""
self.variated_product = self.portal.product_module.newContent(
self.unvariated_product = self.portal.product_module.newContent(
portal_type=self.default_product_portal_type,
title="Variated product",
title="Unvariated product",
)
self.variated_product.setQuantityUnitList(["unit/piece"])
self.variated_product.setIndividualVariationBaseCategoryList(["variation"])
self.unvariated_product.setQuantityUnitList(["unit/piece"])
self.tic()
self._createPurchasePackingList({
self.unvariated_product.getRelativeUrl(): (5.0, 100.0),
})
# Sanity check: ensure Inventory API reports what's expected
(initial_inventory, initial_price) = self._getInventory(self.unvariated_product)
# Assert: initial stock is 2 units
self.assertEqual(5.0, initial_inventory)
# Assert: initial price of stock is 100.0 * 5.0 = 500.0
self.assertEqual(500.0, initial_price)
inventory_value = self._createInventory({
self.unvariated_product.getRelativeUrl(): (3.0, 110.0),
})
inventory_offset_line_list = inventory_value.objectValues(portal_type="Inventory Offset Line")
# Assert: an offset line was created
self.assertEqual(1, len(inventory_offset_line_list))
# Assert: offset line was computed correctly
self.assertEqual(-2.0, inventory_offset_line_list[0].getQuantity())
self.assertEqual(110.0, inventory_offset_line_list[0].getPrice())
(final_inventory, final_price) = self._getInventory(self.unvariated_product)
# Assert: final stock is 3 units
self.assertEqual(3.0, final_inventory)
# Assert: final price of stock is 100.0 * 5.0 - 110.0 * 2.0 = 280.0
# Note that the price is only taken into account when there actually
# are movements. Otherwise, it is totally discarded.
self.assertEqual(280.0, final_price)
def test_03_supportsVariations(self):
"""
For a variated product, checks that quantity and price are correct
after noticing that there are more products than expected in stock.
"""
initial_variated_product_variation_dict = {}
extra_variated_product_variation_dict = {}
total_variated_product_variation_dict = {}
for (i, variation_name) in enumerate(["A", "B", "C"]):
variated_product_variation = self.variated_product.newContent(
portal_type=self.default_product_variation_portal_type,
title="Variation " + variation_name,
)
initial_variated_product_variation_dict[variated_product_variation.getRelativeUrl()] = ((i + 1) * 1.0, (i + 1) * 100.0)
product_value = self._createVariatedProduct()
product_variation_list = product_value.objectValues(portal_type=self.default_product_variation_portal_type)
self.tic()
for i, product_variation in enumerate(product_variation_list):
initial_variated_product_variation_dict[product_variation.getRelativeUrl()] = ((i + 1) * 1.0, (i + 1) * 100.0)
self._createPurchasePackingList({
self.variated_product.getRelativeUrl(): initial_variated_product_variation_dict,
product_value.getRelativeUrl(): initial_variated_product_variation_dict,
})
# Sanity check: ensure Inventory API reports what's expected
for variated_product_variation in initial_variated_product_variation_dict.keys():
(expected_quantity, unit_price) = initial_variated_product_variation_dict[variated_product_variation]
(quantity, total_price) = self._getInventory(self.variated_product, variated_product_variation)
(quantity, total_price) = self._getInventory(product_value, variated_product_variation)
# Assert: quantity is consistent
self.assertEqual(expected_quantity, quantity)
# Assert: price is consistent
self.assertEqual(unit_price * quantity, total_price)
self.assertEqual(unit_price * expected_quantity, total_price)
# We are going to add:
# - 1 variation B at price 100;
......@@ -273,7 +329,7 @@ class TestInventoryModule(TestOrderMixin, ERP5TypeTestCase):
total_variated_product_variation_dict[variated_product_variation] = (quantity + i, i * 100.0)
inventory_value = self._createInventory({
self.variated_product.getRelativeUrl(): total_variated_product_variation_dict,
product_value.getRelativeUrl(): total_variated_product_variation_dict,
})
# Assert: one and only one offset line was created
......@@ -296,8 +352,176 @@ class TestInventoryModule(TestOrderMixin, ERP5TypeTestCase):
(initial_quantity, initial_price) = initial_variated_product_variation_dict[variated_product_variation]
(extra_quantity, extra_price) = extra_variated_product_variation_dict[variated_product_variation]
(quantity, total_price) = self._getInventory(self.variated_product, variated_product_variation)
(quantity, total_price) = self._getInventory(product_value, variated_product_variation)
# Assert: quantity is consistent
self.assertEqual(expected_quantity, quantity)
# Assert: price is consistent
self.assertEqual(initial_quantity * initial_price + extra_quantity * extra_price, total_price)
def test_04_getsTotalQuantityAndPrice(self):
"""
Checks that `getTotalQuantity` and `getTotalPrice` methods work
properly both on Lines and Offset Lines.
"""
product_value = self._createVariatedProduct()
product_variation_list = product_value.objectValues(portal_type=self.default_product_variation_portal_type)
variated_product_variation_dict = {}
total_quantity = 0.0
total_price = 0.0
for i, product_variation in enumerate(product_variation_list):
quantity = (i + 1) * 1.0
price = quantity * 100.0
total_quantity += quantity
total_price += quantity * price
variated_product_variation_dict[product_variation.getRelativeUrl()] = (quantity, price)
inventory_value = self._createInventory({
product_value.getRelativeUrl(): variated_product_variation_dict,
})
inventory_line_value = inventory_value.objectValues(portal_type="Inventory Line")[0]
inventory_offset_line_value = inventory_value.objectValues(portal_type="Inventory Offset Line")[0]
# Assert: `getTotalInventory` is correct for lines
self.assertEqual(total_quantity, inventory_line_value.getTotalInventory())
# Assert: `getTotalPrice` is correct for lines
self.assertEqual(total_price, inventory_line_value.getTotalPrice())
# Assert: `getTotalQuantity` is correct for offset lines
self.assertEqual(total_quantity, inventory_offset_line_value.getTotalQuantity())
# Assert: `getTotalPrice` is correct for offset lines
self.assertEqual(total_price, inventory_offset_line_value.getTotalPrice())
def test_05_supportsFull(self):
"""
Checks that full inventories are supported, adding new products to
stock and removing unreferenced products from stock.
"""
initial_product_name_list = ["A", "B"]
initial_product_stock = (2.0, 100.0)
initial_stock_dict = {}
final_product_name_list = ["B", "C"]
final_product_stock = (3.0, 100.0)
final_stock_dict = {}
diff_stock_dict = {}
# Create two dictionaries: one with A and B in stock, the other with B and C in stock
for product_name in set(initial_product_name_list + final_product_name_list):
product_value = self.portal.product_module.newContent(
portal_type=self.default_product_portal_type,
title="Product " + product_name,
)
product_value.setQuantityUnitList(["unit/piece"])
product_relative_url = product_value.getRelativeUrl()
initial_quantity = 0.0
final_quantity = 0.0
if product_name in initial_product_name_list:
initial_stock_dict[product_relative_url] = initial_product_stock
initial_quantity = initial_product_stock[0]
if product_name in final_product_name_list:
final_stock_dict[product_relative_url] = final_product_stock
final_quantity = final_product_stock[0]
# Price is set to zero by default when unset on inventory
diff_price = 100.0 if product_relative_url in final_stock_dict else 0.0
diff_stock_dict[product_relative_url] = (final_quantity - initial_quantity, diff_price)
self.tic()
self._createPurchasePackingList(initial_stock_dict)
# Sanity check: ensure Inventory API reports what's expected
for product in initial_stock_dict.keys():
# XXX-Titouan: traversing bad when we can easily get a handle to the value
product_value = self.portal.restrictedTraverse(product)
(expected_quantity, unit_price) = initial_product_stock
(quantity, total_price) = self._getInventory(product_value)
# Assert: quantity is consistent
self.assertEqual(expected_quantity, quantity)
# Assert: price is consistent
self.assertEqual(unit_price * expected_quantity, total_price)
inventory_value = self._createInventory(final_stock_dict, is_full=True)
# This is a bit tricky: we expect one line for removing all As, one for adding all Cs, and one for modifying stock of Bs
inventory_offset_line_list = inventory_value.objectValues(portal_type="Inventory Offset Line")
# Assert: three offset lines were created
self.assertEqual(3, len(inventory_offset_line_list))
# Assert: offset lines were computed correctly
for inventory_offset_line in inventory_offset_line_list:
product = inventory_offset_line.getResource()
(expected_quantity, expected_price) = diff_stock_dict[product]
self.assertEqual(expected_quantity, inventory_offset_line.getQuantity())
self.assertEqual(expected_price, inventory_offset_line.getPrice())
def test_06_allowsRecalculation(self):
"""
Ensures that Inventory Offset Lines can be recalculated without
double-counting.
"""
self.unvariated_product = self.portal.product_module.newContent(
portal_type=self.default_product_portal_type,
title="Unvariated product",
)
self.unvariated_product.setQuantityUnitList(["unit/piece"])
self.tic()
self._createPurchasePackingList({
self.unvariated_product.getRelativeUrl(): (2.0, 100.0),
})
# Sanity check: ensure Inventory API reports what's expected
(initial_inventory, initial_price) = self._getInventory(self.unvariated_product)
# Assert: initial stock is 2 units
self.assertEqual(2.0, initial_inventory)
# Assert: initial price of stock is 100.0 * 2.0 = 200.0
self.assertEqual(200.0, initial_price)
# First calculation of the Inventory, without delivering it (recording),
# which should not change stock value but generate offset lines.
inventory_value = self._createInventory({
self.unvariated_product.getRelativeUrl(): (3.0, 110.0),
}, record=False)
inventory_offset_line_list = inventory_value.objectValues(portal_type="Inventory Offset Line")
# Assert: an offset line was created
self.assertEqual(1, len(inventory_offset_line_list))
# Assert: offset line was computed correctly
self.assertEqual(1.0, inventory_offset_line_list[0].getQuantity())
self.assertEqual(110.0, inventory_offset_line_list[0].getPrice())
(final_inventory, final_price) = self._getInventory(self.unvariated_product)
# Assert: stock is unchanged
self.assertEqual(2.0, final_inventory)
# Assert: total price is unchanged
self.assertEqual(200.0, final_price)
inventory_line = inventory_value.objectValues(portal_type="Inventory Line")[0]
inventory_line.setInventory(4.0)
self.tic()
inventory_value.Inventory_computeOffsetAction()
self.tic()
inventory_value.deliver()
self.tic()
inventory_offset_line_list = inventory_value.objectValues(portal_type="Inventory Offset Line")
# Assert: only one offset line exists (previous line was deleted)
self.assertEqual(1, len(inventory_offset_line_list))
# Assert: offset line was recomputed correctly
self.assertEqual(2.0, inventory_offset_line_list[0].getQuantity())
self.assertEqual(110.0, inventory_offset_line_list[0].getPrice())
(final_inventory, final_price) = self._getInventory(self.unvariated_product)
# Assert: stock was updated
self.assertEqual(4.0, final_inventory)
# Assert: final price is 100.0 * 2.0 + 110.0 * 2.0 = 420.0
self.assertEqual(420.0, final_price)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment