Commit c92c2cb2 authored by Sebastien Robin's avatar Sebastien Robin

Order Builders: remove the whole testOrderBuilder passing

So all newSimulation expected failures are removed. Make
generateMovementListForStockOptimisation looking min_flow and max_delay
on supply lines.

Introduce a getNextAlertInventoryDate in addition to
getNextNegativeInventoryDate. This allows to know when an inventory
will be below a reference quantity. This is particularly helpful
to know when an inventory is below the minimal admitted stock
parent d7e19a97
...@@ -566,12 +566,22 @@ class Resource(XMLObject, XMLMatrix, VariatedMixin): ...@@ -566,12 +566,22 @@ class Resource(XMLObject, XMLMatrix, VariatedMixin):
'getNextNegativeInventoryDate') 'getNextNegativeInventoryDate')
def getNextNegativeInventoryDate(self, **kw): def getNextNegativeInventoryDate(self, **kw):
""" """
Returns list of inventory grouped by section or site Returns next date where the inventory will be negative
"""
return self.getNextAlertInventoryDate(
reference_quantity=0, **kw)
security.declareProtected(Permissions.AccessContentsInformation,
'getNextNegativeInventoryDate')
def getNextAlertInventoryDate(self, reference_quantity=0, **kw):
"""
Returns next date where the inventory will be below reference
quantity
""" """
kw['resource_uid'] = self.getUid() kw['resource_uid'] = self.getUid()
portal_simulation = self.getPortalObject().portal_simulation portal_simulation = self.getPortalObject().portal_simulation
return portal_simulation.getNextNegativeInventoryDate(**kw) return portal_simulation.getNextAlertInventoryDate(
reference_quantity=reference_quantity, **kw)
# Asset Price API # Asset Price API
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
......
...@@ -2049,29 +2049,41 @@ class SimulationTool(BaseTool): ...@@ -2049,29 +2049,41 @@ class SimulationTool(BaseTool):
assert len(inventory_list) == 1 assert len(inventory_list) == 1
return inventory_list[0] return inventory_list[0]
security.declareProtected(Permissions.AccessContentsInformation,
'getNextDeficientInventoryDate')
def getNextAlertInventoryDate(self, reference_quantity=0, src__=0, **kw):
"""
Give the next date where the quantity is lower than the
reference quantity.
"""
result = None
# First look at current inventory, we might have already an inventory
# lower than reference_quantity
current_inventory = self.getCurrentInventory(**kw)
if current_inventory < reference_quantity:
result = DateTime()
else:
result = self.getInventoryList(src__=src__,
sort_on = (('date', 'ascending'),), group_by_movement=1, **kw)
if src__ :
return result
total_inventory = 0.
for inventory in result:
if inventory['inventory'] is not None:
total_inventory += inventory['inventory']
if total_inventory < reference_quantity:
result = inventory['date']
break
return result
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getNextNegativeInventoryDate') 'getNextNegativeInventoryDate')
def getNextNegativeInventoryDate(self, src__=0, **kw): def getNextNegativeInventoryDate(self, **kw):
""" """
Returns statistics of inventory grouped by section or site Deficient Inventory with a reference_quantity of 0, so when the
stock will be negative
""" """
#sql_kw = self._generateSQLKeywordDict(order_by_expression='stock.date', **kw) return self.getNextAlertInventoryDate(reference_quantity=0, **kw)
#sql_kw['group_by_expression'] = 'stock.uid'
#sql_kw['order_by_expression'] = 'stock.date'
result = self.getInventoryList(src__=src__,
sort_on = (('date', 'ascending'),), group_by_movement=1, **kw)
if src__ :
return result
total_inventory = 0.
for inventory in result:
if inventory['inventory'] is not None:
total_inventory += inventory['inventory']
if total_inventory < 0:
return inventory['date']
return None
####################################################### #######################################################
# Traceability management # Traceability management
......
...@@ -183,6 +183,18 @@ class BuilderMixin(XMLObject, Amount, Predicate): ...@@ -183,6 +183,18 @@ class BuilderMixin(XMLObject, Amount, Predicate):
group_by_section=0, group_by_section=0,
**kw) **kw)
id_count = 0 id_count = 0
# min_flow and max_delay are stored on a supply line. By default
# we can get them through a method having the right supply type prefix
# like getPurchaseSupplyLineMinFlow. So we need to guess the supply prefix
supply_prefix = ''
delivery_type = self.getDeliveryPortalType()
portal = self.getPortalObject()
if delivery_type in portal.getPortalPurchaseTypeList():
supply_prefix = 'purchase'
elif delivery_type in portal.getPortalSaleTypeList():
supply_prefix = 'sale'
else:
supply_prefix = 'internal'
for inventory_item in sql_list: for inventory_item in sql_list:
if (inventory_item.inventory is not None): if (inventory_item.inventory is not None):
dumb_movement = inventory_item.getObject() dumb_movement = inventory_item.getObject()
...@@ -191,27 +203,29 @@ class BuilderMixin(XMLObject, Amount, Predicate): ...@@ -191,27 +203,29 @@ class BuilderMixin(XMLObject, Amount, Predicate):
str(id_count)) str(id_count))
id_count += 1 id_count += 1
resource_portal_type = self.getResourcePortalType() resource_portal_type = self.getResourcePortalType()
resource = portal.portal_catalog.getObject(inventory_item.resource_uid)
assert resource.getPortalType() == resource_portal_type
movement.edit( movement.edit(
resource=inventory_item.resource_relative_url, resource=inventory_item.resource_relative_url,
variation_category_list=dumb_movement.getVariationCategoryList(), variation_category_list=dumb_movement.getVariationCategoryList(),
destination_value=self.getDestinationValue(), destination_value=self.getDestinationValue(),
resource_portal_type=resource_portal_type, resource_portal_type=resource_portal_type,
destination_section_value=self.getDestinationSectionValue()) destination_section_value=self.getDestinationSectionValue())
# We can do other test on inventory here # Get min_flow, max_delay on supply line
# XXX It is better if it can be sql parameters min_flow = 0
#resource_portal_type = self.getResourcePortalType() max_delay = 0
resource = movement.getResourceValue() min_stock = 0
# FIXME: XXX Those properties are defined on a supply line !! if supply_prefix:
# min_flow, max_delay min_flow = resource.getProperty(supply_prefix + '_supply_line_min_flow', 0)
min_flow = resource.getMinFlow(0) max_delay = resource.getProperty(supply_prefix + '_supply_line_max_delay', 0)
assert resource.getPortalType() == resource_portal_type min_stock = resource.getProperty(supply_prefix + '_supply_line_min_stock', 0)
if round(inventory_item.inventory, 5) < min_flow: if round(inventory_item.inventory, 5) < min_stock:
stop_date = resource.getNextNegativeInventoryDate( stop_date = resource.getNextAlertInventoryDate(
reference_quantity=min_stock,
variation_text=movement.getVariationText(), variation_text=movement.getVariationText(),
from_date=DateTime(), from_date=DateTime(),
**kw) **kw)
if stop_date is None: assert stop_date is not None
stop_date = DateTime()
max_delay = resource.getMaxDelay(0) max_delay = resource.getMaxDelay(0)
movement.edit( movement.edit(
start_date=DateTime(((stop_date-max_delay).Date())), start_date=DateTime(((stop_date-max_delay).Date())),
......
...@@ -55,8 +55,8 @@ class TestOrderBuilderMixin(TestOrderMixin): ...@@ -55,8 +55,8 @@ class TestOrderBuilderMixin(TestOrderMixin):
# defaults # defaults
decrease_quantity = 1.0 decrease_quantity = 1.0
max_delay = 4.0 max_delay = 0.0
min_flow = 7.0 min_flow = 0.0
def afterSetUp(self): def afterSetUp(self):
""" """
...@@ -76,7 +76,7 @@ class TestOrderBuilderMixin(TestOrderMixin): ...@@ -76,7 +76,7 @@ class TestOrderBuilderMixin(TestOrderMixin):
Sets min_flow on resource Sets min_flow on resource
""" """
resource = sequence.get('resource') resource = sequence.get('resource')
resource.edit(min_flow=self.min_flow) resource.edit(purchase_supply_line_min_flow=self.min_flow)
def stepFillOrderBuilder(self, sequence): def stepFillOrderBuilder(self, sequence):
""" """
...@@ -169,7 +169,7 @@ class TestOrderBuilderMixin(TestOrderMixin): ...@@ -169,7 +169,7 @@ class TestOrderBuilderMixin(TestOrderMixin):
# XXX: ... and for more lines/cells too # XXX: ... and for more lines/cells too
order_line, = order.contentValues(portal_type=self.order_line_portal_type) order_line, = order.contentValues(portal_type=self.order_line_portal_type)
self.assertEqual(order_line.getResourceValue(), resource) self.assertEqual(order_line.getResourceValue(), resource)
self.assertEqual(order_line.getTotalQuantity(), self.min_flow) self.assertEqual(order_line.getTotalQuantity(), self.wanted_quantity)
def stepBuildOrderBuilder(self, sequence): def stepBuildOrderBuilder(self, sequence):
""" """
...@@ -204,7 +204,7 @@ class TestOrderBuilderMixin(TestOrderMixin): ...@@ -204,7 +204,7 @@ class TestOrderBuilderMixin(TestOrderMixin):
packing_list = packing_list_module.newContent( packing_list = packing_list_module.newContent(
portal_type = self.packing_list_portal_type, portal_type = self.packing_list_portal_type,
source_value = organisation, source_value = organisation,
start_date = self.datetime, start_date = self.datetime+10,
specialise = self.business_process, specialise = self.business_process,
) )
...@@ -214,6 +214,13 @@ class TestOrderBuilderMixin(TestOrderMixin): ...@@ -214,6 +214,13 @@ class TestOrderBuilderMixin(TestOrderMixin):
quantity = self.decrease_quantity, quantity = self.decrease_quantity,
) )
self.decrease_quantity_matrix = {
'variation/%s/blue' % resource.getRelativeUrl() : 1.0,
'variation/%s/green' % resource.getRelativeUrl() : 2.0,
}
self.wanted_quantity_matrix = self.decrease_quantity_matrix.copy()
packing_list_line.setVariationCategoryList( packing_list_line.setVariationCategoryList(
self.decrease_quantity_matrix.keys(), self.decrease_quantity_matrix.keys(),
) )
...@@ -251,7 +258,7 @@ class TestOrderBuilderMixin(TestOrderMixin): ...@@ -251,7 +258,7 @@ class TestOrderBuilderMixin(TestOrderMixin):
packing_list = packing_list_module.newContent( packing_list = packing_list_module.newContent(
portal_type = self.packing_list_portal_type, portal_type = self.packing_list_portal_type,
source_value = organisation, source_value = organisation,
start_date = self.datetime+14, start_date = self.datetime+10,
specialise = self.business_process, specialise = self.business_process,
) )
...@@ -263,6 +270,23 @@ class TestOrderBuilderMixin(TestOrderMixin): ...@@ -263,6 +270,23 @@ class TestOrderBuilderMixin(TestOrderMixin):
packing_list.confirm() packing_list.confirm()
def stepCreateVariatedResource(self, sequence=None, sequence_list=None, \
**kw):
"""
Create a resource with variation
"""
portal = self.getPortal()
resource_module = portal.getDefaultModule(self.resource_portal_type)
resource = resource_module.newContent(portal_type=self.resource_portal_type)
resource.edit(
title = "VariatedResource%s" % resource.getId(),
variation_base_category_list = ['variation'],
)
for color in ['blue', 'green']:
resource.newContent(portal_type='Product Individual Variation',
id=color, title=color)
sequence.edit(resource=resource)
class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase): class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase):
""" """
Test Order Builder functionality Test Order Builder functionality
...@@ -301,14 +325,12 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase): ...@@ -301,14 +325,12 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase):
str(self.datetime.earliestTime() str(self.datetime.earliestTime()
+ self.order_builder_hardcoded_time_diff)) + self.order_builder_hardcoded_time_diff))
# We add 4 days to start date to reflect delays self.wanted_stop_date = self.wanted_start_date
self.wanted_stop_date = self.wanted_start_date + 4
sequence_list = SequenceList() sequence_list = SequenceList()
sequence_list.addSequenceString(self.common_sequence_string) sequence_list.addSequenceString(self.common_sequence_string)
sequence_list.play(self) sequence_list.play(self)
@newSimulationExpectedFailure
def test_01a_simpleOrderBuilderVariatedResource(self, quiet=0, run=run_all_test): def test_01a_simpleOrderBuilderVariatedResource(self, quiet=0, run=run_all_test):
""" """
Test simple Order Builder for Variated Resource Test simple Order Builder for Variated Resource
...@@ -338,25 +360,17 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase): ...@@ -338,25 +360,17 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase):
self.wanted_stop_date = self.wanted_start_date self.wanted_stop_date = self.wanted_start_date
self.decrease_quantity_matrix = {
'size/Man' : 1.0,
'size/Woman' : 2.0,
}
self.wanted_quantity_matrix = self.decrease_quantity_matrix.copy()
sequence_list = SequenceList() sequence_list = SequenceList()
sequence_list.addSequenceString(sequence_string) sequence_list.addSequenceString(sequence_string)
sequence_list.play(self) sequence_list.play(self)
@newSimulationExpectedFailure
def test_02_maxDelayResourceOrderBuilder(self, quiet=0, run=run_all_test): def test_02_maxDelayResourceOrderBuilder(self, quiet=0, run=run_all_test):
""" """
Test max_delay impact on generated order start date Test max_delay impact on generated order start date
""" """
if not run: return if not run: return
self.max_delay = 14.0 self.max_delay = 4.0
self.wanted_quantity = 1.0 self.wanted_quantity = 1.0
self.wanted_start_date = DateTime( self.wanted_start_date = DateTime(
...@@ -372,7 +386,6 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase): ...@@ -372,7 +386,6 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase):
sequence_list.addSequenceString(self.common_sequence_string) sequence_list.addSequenceString(self.common_sequence_string)
sequence_list.play(self) sequence_list.play(self)
@newSimulationExpectedFailure
def test_03_minFlowResourceOrderBuilder(self, quiet=0, run=run_all_test): def test_03_minFlowResourceOrderBuilder(self, quiet=0, run=run_all_test):
""" """
Test min_flow impact on generated order line quantity Test min_flow impact on generated order line quantity
...@@ -390,20 +403,9 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase): ...@@ -390,20 +403,9 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase):
sequence_list.addSequenceString(self.common_sequence_string) sequence_list.addSequenceString(self.common_sequence_string)
# case when min_flow > decreased_quantity # case when min_flow > decreased_quantity
self.min_flow = 144.0
self.wanted_quantity = self.min_flow + self.decrease_quantity
# why to order more than needed?
# self.wanted_quantity = self.min_flow
sequence_list.play(self)
# case when min_flow < decreased_quantity
self.min_flow = 15.0 self.min_flow = 15.0
self.decrease_quantity = 20.0
self.wanted_quantity = self.min_flow + self.decrease_quantity self.wanted_quantity = self.min_flow
# why to order more than needed?
# self.wanted_quantity = self.decreased_quantity
sequence_list.play(self) sequence_list.play(self)
......
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