def generateMovementListForStockOptimisation(self, **kw): # XXX: unused from Products.ERP5Type.Document import newTempMovement movement_list = [] for attribute, method in [("node_uid", "getDestinationUid"), ("section_uid", "getDestinationSectionUid")]: if getattr(self, method)() not in ("", None): kw[attribute] = getattr(self, method)() # We have to check the inventory for each stock movement date. # Inventory can be negative in some date, and positive in futur !! # This must be done by subclassing OrderBuilder with a new inventory # algorithm. sql_list = self.portal_simulation.getFutureInventoryList( group_by_variation=1, group_by_resource=1, group_by_node=1, group_by_section=0, **kw ) id_count = 0 for inventory_item in sql_list: # XXX FIXME SQL return None inventory... # It may be better to return always good values 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 movement.edit( resource=inventory_item.resource_relative_url, variation_category_list=dumb_movement.getVariationCategoryList(), destination_value=self.getDestinationValue(), destination_section_value=self.getDestinationSectionValue(), ) # We can do other test on inventory here # XXX It is better if it can be sql parameters resource_portal_type = self.getResourcePortalType() resource = movement.getResourceValue() # FIXME: XXX Those properties are defined on a supply line !! # min_flow, max_delay min_flow = resource.getMinFlow(0) if (resource.getPortalType() == resource_portal_type) and ( round(inventory_item.inventory, 5) < min_flow ): # FIXME XXX getNextNegativeInventoryDate must work stop_date = DateTime() + 10 # stop_date = resource.getNextNegativeInventoryDate( # variation_text=movement.getVariationText(), # from_date=DateTime(), # # node_category=node_category, # # section_category=section_category) # node_uid=self.getDestinationUid(), # section_uid=self.getDestinationSectionUid()) max_delay = resource.getMaxDelay(0) movement.edit( start_date=DateTime(((stop_date - max_delay).Date())), stop_date=DateTime(stop_date.Date()), quantity=min_flow - inventory_item.inventory, quantity_unit=resource.getQuantityUnit() # XXX FIXME define on a supply line # quantity_unit ) movement_list.append(movement) return movement_list
def test_PropertyCriterion(self): movement = newTempMovement(self.portal, 'tmp') predicate = self.createPredicate() predicate.setCriterionPropertyList(['quantity']) request = self.portal.REQUEST request.set( 'listbox', {'quantity': { 'max': '', 'identity': [], 'min': '' }}, ) predicate.Predicate_edit('Predicate_view') self.assertEqual(predicate._identity_criterion, {'quantity': []}) self.assertEqual(predicate._range_criterion, {}) self.assertTrue(predicate.test(movement)) request.set( 'listbox', {'quantity': { 'max': '', 'identity': [], 'min': 1.0 }}, ) predicate.Predicate_edit('Predicate_view') self.assertEqual(predicate._range_criterion, {'quantity': (1.0, None)}) self.assertFalse(predicate.test(movement.asContext(quantity=0.5))) self.assertTrue(predicate.test(movement.asContext(quantity=1.0))) request.set( 'listbox', {'quantity': { 'max': 2.0, 'identity': [], 'min': '' }}, ) predicate.Predicate_edit('Predicate_view') self.assertEqual(predicate._range_criterion, {'quantity': (None, 2.0)}) self.assertFalse(predicate.test(movement.asContext(quantity=2.0))) self.assertTrue(predicate.test(movement.asContext(quantity=1.5))) request.set( 'listbox', {'quantity': { 'max': 2.0, 'identity': [], 'min': 1.0 }}, ) predicate.Predicate_edit('Predicate_view') self.assertEqual(predicate._range_criterion, {'quantity': (1.0, 2.0)}) self.assertFalse(predicate.test(movement.asContext(quantity=0.5))) self.assertTrue(predicate.test(movement.asContext(quantity=1.0))) self.assertTrue(predicate.test(movement.asContext(quantity=1.5))) self.assertFalse(predicate.test(movement.asContext(quantity=2.0)))
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
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(), destination_value=self.getDestinationValue(), resource_portal_type=resource_portal_type, destination_section_value=self.getDestinationSectionValue()) # define variation after resource is set movement.edit(variation_text=inventory_item.variation_text) return movement
def generateMovementListForStockOptimisation(self, **kw): # XXX: unused from Products.ERP5Type.Document import newTempMovement movement_list = [] for attribute, method in [('node_uid', 'getDestinationUid'), ('section_uid', 'getDestinationSectionUid')]: if getattr(self, method)() not in ("", None): kw[attribute] = getattr(self, method)() # We have to check the inventory for each stock movement date. # Inventory can be negative in some date, and positive in futur !! # This must be done by subclassing OrderBuilder with a new inventory # algorithm. sql_list = self.portal_simulation.getFutureInventoryList( group_by_variation=1, group_by_resource=1, group_by_node=1, group_by_section=0, **kw) id_count = 0 for inventory_item in sql_list: # XXX FIXME SQL return None inventory... # It may be better to return always good values 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 movement.edit(resource=inventory_item.resource_relative_url, variation_category_list=dumb_movement. getVariationCategoryList(), destination_value=self.getDestinationValue(), destination_section_value=self. getDestinationSectionValue()) # We can do other test on inventory here # XXX It is better if it can be sql parameters resource_portal_type = self.getResourcePortalType() resource = movement.getResourceValue() # FIXME: XXX Those properties are defined on a supply line !! # min_flow, max_delay min_flow = resource.getMinFlow(0) if (resource.getPortalType() == resource_portal_type) and\ (round(inventory_item.inventory, 5) < min_flow): # FIXME XXX getNextNegativeInventoryDate must work stop_date = DateTime() + 10 # stop_date = resource.getNextNegativeInventoryDate( # variation_text=movement.getVariationText(), # from_date=DateTime(), # # node_category=node_category, # # section_category=section_category) # node_uid=self.getDestinationUid(), # section_uid=self.getDestinationSectionUid()) max_delay = resource.getMaxDelay(0) movement.edit( start_date=DateTime(((stop_date - max_delay).Date())), stop_date=DateTime(stop_date.Date()), quantity=min_flow - inventory_item.inventory, quantity_unit=resource.getQuantityUnit() # XXX FIXME define on a supply line # quantity_unit ) movement_list.append(movement) return movement_list
# { id:resource_id, # variation_text: resource_variation_text, # row_dict_list: list of rows to insert; each row is represented as a dict.} row_dict_dict_list = [] portal = context.getPortalObject() for transformation_relative_url, variation_list_list in transformation_item_list: transformation = portal.restrictedTraverse(transformation_relative_url) resource = transformation.getResourceValue() if resource is None: continue for variation_list in variation_list_list: movement = newTempMovement(resource, 'temp', specialise_value=transformation, variation_category_list=variation_list, resource_value=resource, quantity=1.0) base_row = dict(uid=resource.getUid(), variation_text=movement.getVariationText()) row_dict_list = [] for amount in movement.getAggregatedAmountList(): transformed_resource_uid = amount.getResourceUid() quantity = amount.getQuantity() if transformed_resource_uid is not None and quantity is not None: row_dict = base_row.copy() row_dict.update(transformed_uid=transformed_resource_uid, transformed_variation_text=amount.getVariationText(), quantity=quantity) row_dict_list.append(row_dict)
def _getInputMovementList(self, movement_list=None, rounding=None): """ Generate the list of input movements by looking at all open order lines relating to this subscription item. TODO: clever handling of quantity (based on the nature of resource, ie. float or unit) """ from Products.ERP5Type.Document import newTempMovement result = [] catalog_tool = getToolByName(self, 'portal_catalog') # Try to find the source open order open_order_movement_list = self.getAggregateRelatedValueList( portal_type="Open Sale Order Line") # XXX-JPS Hard Coded if not open_order_movement_list: return result # Find out which parent open orders explanation_uid_list = map(lambda x:x.getParentUid(), open_order_movement_list) # Instead, should call getDeliveryValue or equivalent open_order_list = catalog_tool.searchResults(uid = explanation_uid_list, validation_state = 'validated') # XXX-JPS hard coding # Now generate movements for each valid open order for movement in open_order_movement_list: if movement.getParentValue().getValidationState() in ('open', 'validated'): # XXX-JPS hard coding resource = movement.getResource() start_date = movement.getStartDateRangeMin() # Is this appropriate ? stop_date = movement.getStartDateRangeMax() # Is this appropriate ? source = movement.getSource() source_section = movement.getSourceSection() destination = movement.getDestination() destination_section = movement.getDestinationSection() # XXX More arrows ? use context instead ? quantity = self.getQuantity() # Is it so ? XXX-JPS quantity_unit = movement.getQuantityUnit() price = movement.getPrice() specialise = movement.getSpecialise() current_date = self.getNextPeriodicalDate(start_date) id_index = 0 while current_date < stop_date: next_date = self.getNextPeriodicalDate(current_date) generated_movement = newTempMovement(self, 'subscription_%s' % id_index) generated_movement._edit( aggregate_value=self, resource=resource, quantity=quantity, quantity_unit=quantity_unit, price=price, start_date=current_date, stop_date=next_date, source=source, source_section=source_section, destination=destination, destination_section=destination_section, specialise=specialise, # delivery_value=movement # ??? to be confirmed - if we want order step or not ) result.append(generated_movement) current_date = next_date id_index += 1 # And now return result return result
def _getPriceContext(self, **kw): """Returns a temp movement that we can use for getPrice(context= """ from Products.ERP5Type.Document import newTempMovement return newTempMovement(self.portal, 'tmp', **kw)
def _getInputMovementList(self, movement_list=None, rounding=None): """ Generate the list of input movements by looking at all open order lines relating to this subscription item. TODO: clever handling of quantity (based on the nature of resource, ie. float or unit) """ from Products.ERP5Type.Document import newTempMovement result = [] # Try to find the source open order open_order_movement_list = self.getAggregateRelatedValueList( portal_type="Open Sale Order Line") # XXX-JPS Hard Coded if not open_order_movement_list: return result # Now generate movements for each valid open order for movement in open_order_movement_list: # YXU-Why we have a list here? if movement.getParentValue().getValidationState() in ('open', 'validated'): # XXX-JPS hard coding resource = movement.getResource() start_date = movement.getStartDate() stop_date = movement.getStopDate() source = movement.getSource() source_section = movement.getSourceSection() source_decision = movement.getSourceDecision() destination = movement.getDestination() destination_section = movement.getDestinationSection() destination_decision = movement.getDestinationDecision() quantity = movement.getQuantity() quantity_unit = movement.getQuantityUnit() price = movement.getPrice() price_currency = movement.getPriceCurrency() base_application_list = movement.getBaseApplicationList() base_contribution_list = movhement.getBaseContributionList() use_list = movement.getUseList() specialise = movement.getSpecialise() current_date = start_date id_index = 0 while current_date < stop_date: next_date = self.getNextPeriodicalDate(current_date) if next_date > stop_date: next_date = stop_date generated_movement = newTempMovement(self, 'subscription_%s' % id_index) generated_movement._edit( aggregate_value=self, resource=resource, quantity=quantity, quantity_unit=quantity_unit, price=price, price_currency=price_currency, start_date=current_date, stop_date=next_date, source=source, source_section=source_section, source_decision=source_decision, destination=destination, destination_section=destination_section, destination_decision=destination_decision, specialise=specialise, base_application_list=base_application_list, base_contribution_list=base_contribution_list, use_list=use_list ) result.append(generated_movement) current_date = next_date id_index += 1 return result
def generateMovementListForStockOptimisation(self, **kw): from Products.ERP5Type.Document import newTempMovement movement_list = [] for attribute, method in [('node_uid', 'getDestinationUid'), ('section_uid', 'getDestinationSectionUid')]: if getattr(self, method)() not in ("", None): kw[attribute] = getattr(self, method)() # We have to check the inventory for each stock movement date. # Inventory can be negative in some date, and positive in futur !! # This must be done by subclassing OrderBuilder with a new inventory # algorithm. sql_list = self.portal_simulation.getFutureInventoryList( group_by_variation=1, group_by_resource=1, 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 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: 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 min_stock = 0 if supply_prefix: min_flow = resource.getProperty(supply_prefix + '_supply_line_min_flow', 0) max_delay = resource.getProperty(supply_prefix + '_supply_line_max_delay', 0) min_stock = resource.getProperty(supply_prefix + '_supply_line_min_stock', 0) if round(inventory_item.inventory, 5) < min_stock: stop_date = resource.getNextAlertInventoryDate( reference_quantity=min_stock, variation_text=movement.getVariationText(), from_date=DateTime(), **kw) if stop_date != None: max_delay = resource.getMaxDelay(0) movement.edit( start_date=DateTime(((stop_date-max_delay).Date())), stop_date=DateTime(stop_date.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) return movement_list
def _getInputMovementList(self, movement_list=None, rounding=None): """ Generate the list of input movements by looking at all open order lines relating to this subscription item. TODO: clever handling of quantity (based on the nature of resource, ie. float or unit) """ from Products.ERP5Type.Document import newTempMovement result = [] # Try to find the source open order open_order_movement_list = self.getAggregateRelatedValueList( portal_type="Open Sale Order Line") # XXX-JPS Hard Coded if not open_order_movement_list: return result # Now generate movements for each valid open order for movement in open_order_movement_list: # YXU-Why we have a list here? if movement.getParentValue().getValidationState() in ( 'open', 'validated'): # XXX-JPS hard coding resource = movement.getResource() start_date = movement.getStartDate() stop_date = movement.getStopDate() source = movement.getSource() source_section = movement.getSourceSection() source_decision = movement.getSourceDecision() destination = movement.getDestination() destination_section = movement.getDestinationSection() destination_decision = movement.getDestinationDecision() quantity = movement.getQuantity() quantity_unit = movement.getQuantityUnit() price = movement.getPrice() price_currency = movement.getPriceCurrency() base_application_list = movement.getBaseApplicationList() base_contribution_list = movhement.getBaseContributionList() use_list = movement.getUseList() specialise = movement.getSpecialise() current_date = start_date id_index = 0 while current_date < stop_date: next_date = self.getNextPeriodicalDate(current_date) if next_date > stop_date: next_date = stop_date generated_movement = newTempMovement( self, 'subscription_%s' % id_index) generated_movement._edit( aggregate_value=self, resource=resource, quantity=quantity, quantity_unit=quantity_unit, price=price, price_currency=price_currency, start_date=current_date, stop_date=next_date, source=source, source_section=source_section, source_decision=source_decision, destination=destination, destination_section=destination_section, destination_decision=destination_decision, specialise=specialise, base_application_list=base_application_list, base_contribution_list=base_contribution_list, use_list=use_list) result.append(generated_movement) current_date = next_date id_index += 1 return result
def generateMovementListForStockOptimisation(self, **kw): from Products.ERP5Type.Document import newTempMovement movement_list = [] for attribute, method in [('node_uid', 'getDestinationUid'), ('section_uid', 'getDestinationSectionUid')]: if getattr(self, method)() not in ("", None): kw[attribute] = getattr(self, method)() # We have to check the inventory for each stock movement date. # Inventory can be negative in some date, and positive in futur !! # This must be done by subclassing OrderBuilder with a new inventory # algorithm. sql_list = self.portal_simulation.getFutureInventoryList( group_by_variation=1, group_by_resource=1, 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 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: 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 min_stock = 0 if supply_prefix: min_flow = resource.getProperty( supply_prefix + '_supply_line_min_flow', 0) max_delay = resource.getProperty( supply_prefix + '_supply_line_max_delay', 0) min_stock = resource.getProperty( supply_prefix + '_supply_line_min_stock', 0) if round(inventory_item.inventory, 5) < min_stock: stop_date = resource.getNextAlertInventoryDate( reference_quantity=min_stock, variation_text=movement.getVariationText(), from_date=DateTime(), **kw) if stop_date != None: max_delay = resource.getMaxDelay(0) movement.edit( start_date=DateTime( ((stop_date - max_delay).Date())), stop_date=DateTime(stop_date.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) return movement_list