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
master ERP5Type_no_immediate_reindexation allow_login_change allow_login_change_differentiate_id_and_login allow_login_change_wip arnau arnau-kns arnau-kns-without-property-mapping auto_extend_select_list bk_erp5ish_actions_tool bk_sqlcatalog cedriclen-eos cherry-pick-4a8e045d ckeditor_update cmfactivity compact_title_no_reference douglas_forum echarts_with_event echarts_wrapper eos-dev erp5-component erp5-data-notebook erp5-vifib erp5-vifib-cleanup erp5_free_subscription erp5_officejs_fixing erp5_workflow fix/accounting_period_constraint_vs_acquired_node fix/change_state_priority fix/login_validate_check_consistency for_private_testrunner_1 for_testrunner_1 for_testrunner_2 formbox gadget-json-value import_zip_package improve_default_caching_policy_manager isDeletable item_tracking_graph_editor jm/form-action-guard joblib-activity kns kns-kr master_calendar_wip_patches master_calendar_wip_patches_extend_security master_no_guard_on_workflow_transition master_no_guard_on_workflow_transition_plus_calendar_wip_patchs no_longer_simulated_state notebook_submodule notebook_submodule_backup officejs officejs_clean officejs_mixing_echarts portal_callables portal_solver_process_security_configuration pyodide pyodide_submodule pyodide_webworker reindex reindex_calendar_after_change_calendar_exception resursive_clone sms_more_than_140_characters strict_catalog submodule support_request support_request_1 support_request_2 support_request_3 support_request_4 support_request_update testnode_software_link timezones tristan tristan-merge view-aggregated-amounts vivekpab_jabberclient xiaowu_newui erp5.util-0.4.46 erp5.util-0.4.44 erp5.util-0.4.43
No related merge requests found
......@@ -90,3 +90,20 @@ class GeneratedDeliveryBuilder(BuilderMixin):
, PropertySheet.Comment
, PropertySheet.DeliveryBuilder
)
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'
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)
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):
resource = portal.portal_catalog.getObject(inventory_item.resource_uid)
# 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