diff --git a/product/ERP5/Document/GeneratedDeliveryBuilder.py b/product/ERP5/Document/GeneratedDeliveryBuilder.py index 7c785357753cea7ca784d66846c1b77cae10965f..3596e7460e2f27e8495d599ac0bdd7f330de85ca 100644 --- a/product/ERP5/Document/GeneratedDeliveryBuilder.py +++ b/product/ERP5/Document/GeneratedDeliveryBuilder.py @@ -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) diff --git a/product/ERP5/mixin/builder.py b/product/ERP5/mixin/builder.py index 336e469d9f1db9f02e18f09f4e78c099d6af9a04..fae25d57ecce9ec949b02be747312fa3ca62a259 100644 --- a/product/ERP5/mixin/builder.py +++ b/product/ERP5/mixin/builder.py @@ -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): diff --git a/product/ERP5/tests/testOrderBuilder.py b/product/ERP5/tests/testOrderBuilder.py index 73a26ed7f27c32f8b75b1ac8c283918bfb58fd1b..66780709a50f5fa5dbd8e4fdde48ebf5a280c4b1 100644 --- a/product/ERP5/tests/testOrderBuilder.py +++ b/product/ERP5/tests/testOrderBuilder.py @@ -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