Commit 15f08b29 authored by Sebastien Robin's avatar Sebastien Robin

simulation: review generated delivery builder (update existing deliveries, date precision)

Up to know, order builders were mostly used having in mind the idea of
deleting all previously created automated orders.

This way was mostly requesting to run generated delivery builder only
once per night, assuming there is no users working at that time.

So to allows running very often (many times a day) generated delivery
builders, allow them to update existing deliveries. This way :
- automated deliveries could be much more up to date
- there is no need to delete everything
- this generate few activities, there is no need of a long calculation
  to update everything

Also, the stock optimization was using dates with a precision of one
full day. Generic code must support much better precision.
parent 71abefe6
......@@ -89,4 +89,21 @@ class GeneratedDeliveryBuilder(BuilderMixin):
, PropertySheet.Amount
, PropertySheet.Comment
, PropertySheet.DeliveryBuilder
)
\ No newline at end of file
)
def _setDeliveryMovementProperties(self, delivery_movement,
simulation_movement, property_dict,
update_existing_movement=0,
force_update=0, activate_kw=None):
"""
Initialize or update delivery movement properties.
"""
if getattr(simulation_movement, 'getMappedProperty', None) is not None:
property_dict['quantity'] = simulation_movement.getMappedProperty('quantity')
else:
property_dict['quantity'] = simulation_movement.getQuantity()
property_dict['price'] = simulation_movement.getPrice()
if update_existing_movement:
property_dict['quantity'] = (delivery_movement.getQuantity() or 0.0) + property_dict['quantity']
# Update properties on object (quantity, price...)
delivery_movement._edit(force_update=1, **property_dict)
......@@ -182,7 +182,6 @@ class BuilderMixin(XMLObject, Amount, Predicate):
group_by_node=1,
group_by_section=0,
**kw)
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
......@@ -195,25 +194,30 @@ class BuilderMixin(XMLObject, Amount, Predicate):
supply_prefix = 'sale'
else:
supply_prefix = 'internal'
resource_portal_type_list = self.getResourcePortalTypeList()
def newMovement(inventory_item, resource):
# Create temporary movement
movement = newTempMovement(self.getPortalObject(), "temp")
dumb_movement = inventory_item.getObject()
resource_portal_type = resource.getPortalType()
assert resource_portal_type in (resource_portal_type_list), \
"Builder %r does not support resource of type : %r" % (
self.getRelativeUrl(), resource_portal_type)
movement.edit(
resource=inventory_item.resource_relative_url,
# XXX FIXME define on a supply line
# quantity_unit
quantity_unit=resource.getQuantityUnit(),
variation_category_list=dumb_movement.getVariationCategoryList(),
destination_value=self.getDestinationValue(),
resource_portal_type=resource_portal_type,
destination_section_value=self.getDestinationSectionValue())
return movement
for inventory_item in sql_list:
if (inventory_item.inventory is not None):
dumb_movement = inventory_item.getObject()
# Create temporary movement
movement = newTempMovement(self.getPortalObject(),
str(id_count))
id_count += 1
resource_portal_type_list = self.getResourcePortalTypeList()
resource = portal.portal_catalog.getObject(inventory_item.resource_uid)
resource_portal_type = resource.getPortalType()
assert resource_portal_type in (resource_portal_type_list), \
"Builder %r does not support resource of type : %r" % (
self.getRelativeUrl(), resource_portal_type)
movement.edit(
resource=inventory_item.resource_relative_url,
variation_category_list=dumb_movement.getVariationCategoryList(),
destination_value=self.getDestinationValue(),
resource_portal_type=resource_portal_type,
destination_section_value=self.getDestinationSectionValue())
# Get min_flow, max_delay on supply line
min_flow = 0
max_delay = 0
......@@ -225,20 +229,38 @@ class BuilderMixin(XMLObject, Amount, Predicate):
if round(inventory_item.inventory, 5) < min_stock:
stop_date = resource.getNextAlertInventoryDate(
reference_quantity=min_stock,
variation_text=movement.getVariationText(),
variation_text=inventory_item.variation_text,
from_date=DateTime(),
**kw)
if stop_date != None:
movement = newMovement(inventory_item, resource)
max_delay = resource.getMaxDelay(0)
movement.edit(
start_date=DateTime(((stop_date-max_delay).Date())),
stop_date=DateTime(stop_date.Date()),
start_date=stop_date-max_delay,
stop_date=stop_date,
quantity=max(min_flow, -inventory_item.inventory),
quantity_unit=resource.getQuantityUnit()
# XXX FIXME define on a supply line
# quantity_unit
)
movement_list.append(movement)
# We could need to cancel automated stock optimization if for some reasons
# previous optimisations are obsolete
elif round(inventory_item.inventory, 5) > min_stock:
delta = inventory_item.inventory - min_stock
optimized_inventory_list = portal.portal_simulation.getInventoryList(
resource_uid=inventory_item.resource_uid,
node_uid=inventory_item.node_uid,
variation_text=inventory_item.variation_text,
simulation_state="auto_planned",
sort_on=[("date", "descending")],
)
for optimized_inventory in optimized_inventory_list:
movement = newMovement(inventory_item, resource)
quantity = min(delta, optimized_inventory.inventory)
delta = delta - quantity
movement.edit(start_date=optimized_inventory.date,
quantity=-quantity)
movement_list.append(movement)
if delta <= 0:
break
return movement_list
def _searchMovementList(self, **kw):
......
......@@ -64,6 +64,10 @@ class TestOrderBuilderMixin(TestOrderMixin):
self.createCategories()
self.validateRules()
def assertDateAlmostEquals(self, first_date, second_date):
self.assertTrue(abs(first_date - second_date) < 1.0/86400,
"%r != %r" % (first_date, second_date))
def stepSetMaxDelayOnResource(self, sequence):
"""
Sets max_delay on resource
......@@ -137,8 +141,8 @@ class TestOrderBuilderMixin(TestOrderMixin):
# XXX: add support for more generated documents
order, = sequence.get('generated_document_list')
self.assertEqual(order.getDestinationValue(), organisation)
self.assertEqual(order.getStartDate(), self.wanted_start_date)
self.assertEqual(order.getStopDate(), self.wanted_stop_date)
self.assertDateAlmostEquals(order.getStartDate(), self.wanted_start_date)
self.assertDateAlmostEquals(order.getStopDate(), self.wanted_stop_date)
# XXX: ... and for more lines/cells too
order_line, = order.contentValues(portal_type=self.order_line_portal_type)
......@@ -163,8 +167,8 @@ class TestOrderBuilderMixin(TestOrderMixin):
# XXX: add support for more generated documents
order, = sequence.get('generated_document_list')
self.assertEqual(order.getDestinationValue(), organisation)
self.assertEqual(order.getStartDate(), self.wanted_start_date)
self.assertEqual(order.getStopDate(), self.wanted_stop_date)
self.assertDateAlmostEquals(self.wanted_start_date, order.getStartDate())
self.assertDateAlmostEquals(self.wanted_stop_date, order.getStopDate())
# XXX: ... and for more lines/cells too
order_line, = order.contentValues(portal_type=self.order_line_portal_type)
......@@ -322,8 +326,7 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase):
self.wanted_quantity = 1.0
self.wanted_start_date = DateTime(
str(self.datetime.earliestTime()
+ self.order_builder_hardcoded_time_diff))
str(self.datetime + self.order_builder_hardcoded_time_diff))
self.wanted_stop_date = self.wanted_start_date
......@@ -355,7 +358,7 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase):
self.wanted_quantity = 1.0
self.wanted_start_date = DateTime(
str(self.datetime.earliestTime() +
str(self.datetime +
self.order_builder_hardcoded_time_diff))
self.wanted_stop_date = self.wanted_start_date
......@@ -374,13 +377,11 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase):
self.wanted_quantity = 1.0
self.wanted_start_date = DateTime(
str(self.datetime.earliestTime()
- self.max_delay
str(self.datetime - self.max_delay
+ self.order_builder_hardcoded_time_diff))
self.wanted_stop_date = DateTime(
str(self.datetime.earliestTime()
+ self.order_builder_hardcoded_time_diff))
str(self.datetime + self.order_builder_hardcoded_time_diff))
sequence_list = SequenceList()
sequence_list.addSequenceString(self.common_sequence_string)
......@@ -394,8 +395,7 @@ class TestOrderBuilder(TestOrderBuilderMixin, ERP5TypeTestCase):
self.wanted_quantity = 1.0
self.wanted_start_date = DateTime(
str(self.datetime.earliestTime()
+ self.order_builder_hardcoded_time_diff))
str(self.datetime + self.order_builder_hardcoded_time_diff))
self.wanted_stop_date = self.wanted_start_date
......
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