def expand(self, applied_rule, activate_kw=None,**kw):
   """
     Expands the current movement downward.
     -> new status -> expanded
     An applied rule can be expanded only if its parent movement
     is expanded.
   """
   parent_movement = applied_rule.getParentValue()
   # Calculate the previous supply link
   supply_chain = self.getSupplyChain(parent_movement.getParentValue())
   parent_supply_link = self.getCurrentSupplyLink(parent_movement)
   previous_supply_link_list = supply_chain.\
                  getPreviousPackingListSupplyLinkList(
                                                 parent_supply_link,
                                                 movement=parent_movement)
   if len(previous_supply_link_list) == 0:
     raise TransformationSourcingRuleError,\
           "Expand must not be called on %r" %\
               applied_rule.getRelativeUrl()
   else:
     movement_dict = {}
     for previous_supply_link in previous_supply_link_list:
       # Calculate the source
       source_value = None
       source_node = previous_supply_link.getSourceValue()
       if source_node is not None:
         source_value = source_node.getDestinationValue()
       source_section_value = previous_supply_link.getSourceSectionValue()
       # Generate the dict
       stop_date = parent_movement.getStartDate()
       movement_dict.update({
         "ts": {
           'source_value': source_value,
           'source_section_value': source_section_value,
           'destination_value': parent_movement.getSourceValue(),
           'destination_section_value': \
               parent_movement.getSourceSectionValue(),
           'resource_value': parent_movement.getResourceValue(),
           'variation_category_list': parent_movement.\
                                         getVariationCategoryList(),
           "variation_property_dict": \
                         parent_movement.getVariationPropertyDict(),
           'quantity': parent_movement.getNetQuantity(), # getNetQuantity to support efficency from transformation
           'price': parent_movement.getPrice(),
           'quantity_unit': parent_movement.getQuantityUnit(),
           'start_date': previous_supply_link.calculateStartDate(stop_date),
           'stop_date': stop_date,
           # Save the value of the current supply link
           'causality_value': previous_supply_link,
         }
       })
     # Build the movement
     self._buildMovementList(applied_rule, movement_dict,
                             activate_kw=activate_kw)
   # Create one submovement which sources the transformation
   Rule.expand(self, applied_rule, activate_kw=activate_kw, **kw)
  def expand(self, applied_rule, **kw):
    """
    Expands the current movement downward.
    -> new status -> expanded
    An applied rule can be expanded only if its parent movement
    is expanded.
    """
    parent_movement = applied_rule.getParentValue()

    explanation = self.getExplanationValue(applied_rule=applied_rule)
    state = parent_movement.getCausalityValue().getPredecessorValue()
    path_list = state.getSuccessorRelatedValueList()

    if len(path_list) == 0:
      raise TransformationSourcingRuleError,\
            "Not found deliverable business path"
    if len(path_list) > 1:
      raise TransformationSourcingRuleError,\
            "Found 2 or more deliverable business path"

    path = path_list[0]

    source_section = path.getSourceSection(context=parent_movement)
    destination_section = path.getDestinationSection(context=parent_movement)
    source = path.getSource(context=parent_movement)
    destination = path.getDestination(context=parent_movement)

    start_date = path.getExpectedStartDate(explanation)
    stop_date = path.getExpectedStopDate(explanation)

    quantity = parent_movement.getNetQuantity() * path.getQuantity()
    price = parent_movement.getPrice()
    if price is not None:
      price *= path.getQuantity()

    factory = self.getFactory()
    factory.requestSourcing(
      causality_value=path,
      source=source,
      source_section=source_section,
      destination=destination,
      destination_section=destination_section,
      resource=parent_movement.getResource(),
      variation_category_list=parent_movement.getVariationCategoryList(),
      variation_property_dict=parent_movement.getVariationPropertyDict(),
      quantity=quantity,
      price=price,
      quantity_unit=parent_movement.getQuantityUnit(),
      start_date=start_date,
      stop_date=stop_date,
      )

    factory.makeMovements(applied_rule)
    Rule.expand(self, applied_rule, **kw)
Example #3
0
  def expand(self, applied_rule, force=0, **kw):
    """ """ 
    immutable_movement_list = []

    root_simulation_movement = applied_rule.getRootSimulationMovement()
    order_movement = root_simulation_movement.getDefaultOrderValue()
    if order_movement is None:
      order_movement = root_simulation_movement.getDefaultDeliveryValue()
    
    order_movement_dict = {}
    for s_m in applied_rule.objectValues():
      order_movement_dict.setdefault(s_m.getOrder() or s_m.getDelivery(), []).append(s_m)

    order_movement_total_price = order_movement.getTotalPrice()
    root_simulation_movement_total_price = \
                    root_simulation_movement.getTotalPrice()

    # XXX round 
    if order_movement_total_price != 0 and \
        root_simulation_movement_total_price != 0:
                      
      ratio = root_simulation_movement_total_price / \
                           order_movement_total_price
      for tax_movement in order_movement\
                        .DeliveryMovement_getCorrespondingTaxLineList():
        existing_simulation_movement_list = order_movement_dict.get(
                                tax_movement.getRelativeUrl(), [])

        property_dict = dict()
        for prop in ('price', 'base_application_list',
                     'price_currency', 'payment_mode',
                     'base_contribution_list', 'resource'):
          property_dict[prop] = tax_movement.getProperty(prop)

        property_dict['quantity'] = tax_movement.getQuantity() * ratio

        if not existing_simulation_movement_list:
          applied_rule.newContent(
                portal_type=self.movement_type,
                order_value=tax_movement,
                order_ratio=1,
                delivery_ratio=1,
                **property_dict )
        else:
          for existing_simulation_movement in \
                existing_simulation_movement_list:
            if existing_simulation_movement.getDelivery() is None\
                and not "split" in existing_simulation_movement.getId():
              existing_simulation_movement.edit(**property_dict)

    # Pass to base class
    Rule.expand(self, applied_rule, force=force, **kw)
 def expand(self, applied_rule, force=0, **kw):
   """
   Expands the rule:
   - generate a list of previsions
   - compare the prevision with existing children
     - get the list of existing movements (immutable, mutable, deletable)
     - compute the difference between prevision and existing (add,
       modify, remove)
   - add/modify/remove child movements to match prevision
   """
   return Rule._expand(self, applied_rule, force=force, **kw)
 def expand(self, applied_rule, **kw):
   """
     Expands the current movement downward.
     -> new status -> expanded
     An applied rule can be expanded only if its parent movement
     is expanded.
   """
   parent_movement = applied_rule.getParentValue()
   # Get production node and production section
   production = parent_movement.getSource()
   production_section = parent_movement.getSourceSection()
   # Get the current supply link used to calculate consumed resource
   # The current supply link is calculated from the parent AppliedRule.
   supply_chain = self.getSupplyChain(parent_movement.getParentValue())
   parent_supply_link = self.getCurrentSupplyLink(parent_movement)
   current_supply_link_list = supply_chain.\
                  getPreviousProductionSupplyLinkList(parent_supply_link)
   if len(current_supply_link_list) != 1:
     # We shall no pass here.
     # The test method returned a wrong value !
     raise TransformationRuleError,\
           "Expand must not be called on %r" %\
               applied_rule.getRelativeUrl()
   else:
     current_supply_link = current_supply_link_list[0]
     # Generate produced movement
     movement_dict = self._expandProducedResource(applied_rule,
                                                  production,
                                                  production_section,
                                                  current_supply_link)
     # Generate consumed movement
     consumed_mvt_dict = self._expandConsumedResource(applied_rule,
                                                      production,
                                                      production_section,
                                                      current_supply_link)
     movement_dict.update(consumed_mvt_dict)
     # Finally, build movement
     self._buildMovementList(applied_rule, movement_dict, **kw)
   # Expand each movement created
   Rule.expand(self, applied_rule, **kw)
Example #6
0
  def expand(self, applied_rule, force=0, **kw):
    """
      Expands the Order to a new simulation tree.
      expand is only allowed to modify a simulation movement if it doesn't
      have a delivery relation yet.

      If the movement is in ordered or planned state, has no delivered
      child, and is not in order, it can be deleted.
      Else, if the movement is in ordered or planned state, has no
      delivered child, and is in order, it can be modified.
      Else, it cannot be modified.
    """
    return Rule._expand(self, applied_rule, force=force, **kw)
Example #7
0
  def expand(self, applied_rule, delivery_movement_type_list=None, **kw):
    """
    Expands the additional Delivery movements to a new simulation tree.
    Expand is only allowed to create or modify simulation movements for
    delivery lines which are not already linked to another simulation
    movement.

    If the movement is not in current state, has no delivered child, and not
    in delivery movements, it can be deleted.
    Else if the movement is not in current state, it can be modified.
    Else, it cannot be modified.
    """
    return Rule._expand(self, applied_rule, **kw)
Example #8
0
 def isDivergent(self, movement):
   """
   Checks that the movement is divergent
   """
   return Rule.isDivergent(self, movement)
Example #9
0
 def expand(self, applied_rule, **kw):
     """Expands the current movement downward.
 """
     return Rule._expand(self, applied_rule, **kw)
Example #10
0
  def expand(self, applied_rule, force=0, calculation_base_date=None, **kw):
    """
      Expands the Order to a new simulation tree.
      expand is only allowed to modify a simulation movement if it doesn't
      have a delivery relation yet.

      If the movement is in ordered or planned state, has no delivered
      child, and is not in order, it can be deleted.
      Else, if the movement is in ordered or planned state, has no
      delivered child, and is in order, it can be modified.
      Else, it cannot be modified.
    """
    order = applied_rule.getDefaultCausalityValue()
    if getattr(order, 'expandOpenOrderRule', None) is not None:
      # Delegate implementation of expand to the SubscriptionItem or 
      # to the OpenOrder instance
      return order.expandOpenOrderRule(applied_rule, force=force, **kw)
    if order is not None:
      order_movement_list = order.getMovementList(
        portal_type=order.getPortalOrderMovementTypeList())

      now = DateTime()
      passed_calculation_base_date = calculation_base_date
      for order_movement in order_movement_list:
        if passed_calculation_base_date is None:
          end_date = order_movement.getStopDate() - order.getForecastingTermDayCount()
          if end_date > now:
            calculation_base_date = now
          else:
            calculation_base_date = end_date
        else:
          calculation_base_date = passed_calculation_base_date
        last_simulation_movement = self._getLastSimulationMovementValue(applied_rule, order_movement)
        if last_simulation_movement is not None:
          schedule_start_date = last_simulation_movement.getStartDate()
          schedule_list = self._getOrderDateScheduleTupleList(order_movement, schedule_start_date,
                                                              calculation_base_date=calculation_base_date,
                                                              **kw)
        else:
          # Because order's start_date might be matched with the periodicity.
          order_start_date = order_movement.getStartDate()
          schedule_start_date = order_start_date - 1
          schedule_list = []
          for date_pair in self._getOrderDateScheduleTupleList(order_movement,
                  schedule_start_date,
                  calculation_base_date=calculation_base_date,
                  **kw):
            if date_pair[0] >= order_start_date:
              schedule_list.append(date_pair)

        for start_date, stop_date in schedule_list:
          property_dict = {'start_date':start_date, 'stop_date':stop_date}
          property_dict = self._getExpandablePropertyDict(order_movement,
                                                          property_dict)
          simulation_movement = applied_rule.newContent(
            portal_type=self.movement_type,
            order_value=order_movement,
            order_ratio=1,
            delivery_ratio=1,
            **property_dict
            )

    # Pass to base class
    Rule.expand(self, applied_rule, force=force, **kw)
  def expand(self, applied_rule, **kw):
    """
    """
    parent_movement = applied_rule.getParentValue()

    transformation = self.getTransformationValue(movement=parent_movement)
    business_process = self.getBusinessProcessValue(movement=parent_movement)
    explanation = self.getExplanationValue(movement=parent_movement)

    # get all trade_phase of the Business Process
    trade_phase_list = business_process.getTradePhaseList()

    # get head of production path from business process with trade_phase_list
    head_production_path_value_list = self.getHeadProductionPathValueList(transformation,
                                                                          business_process)
    # the factory which is to make simulation movements
    factory = self.getFactory()
    factory.product = dict(
      resource=transformation.getResource(),
      quantity=parent_movement.getNetQuantity(),
      quantity_unit=parent_movement.getQuantityUnit(),
      variation_category_list=parent_movement.getVariationCategoryList(),
      variation_property_dict=parent_movement.getVariationPropertyDict(),)

    # consumed amounts are sorted by phase, but not ordered.
    amount_dict = {}
    for amount in transformation.getAggregatedAmountList():
      phase = amount.getTradePhase()
      if phase not in trade_phase_list:
        raise TransformationRuleError,\
              "the trade phase %r is not part of Business Process %r" % (phase, business_process)
      amount_dict.setdefault(phase, [])
      amount_dict[phase].append(amount)

    transformation_phase_list = amount_dict.keys()

    last_phase_path_list = list() # to keep phase_path_list
    last_prop_dict = dict()
    for (phase, amount_list) in amount_dict.items():
      phase_path_value_list = business_process.getPathValueList(phase)
      """
      XXX: In this context, we assume quantity as ratio,
           but this "quantity as ratio" is consistent with transformation.
      """
      if sum(map(lambda path: path.getQuantity(), phase_path_value_list)) != 1:
        raise TransformationRuleError,\
              "the sum ratio at the Trade Phase %r on the Business Process %r is not 1"\
              % (phase, business_process)

      for path in phase_path_value_list:
        path_common_dict = dict(causality_value=path,
                                start_date=path.getExpectedStartDate(explanation),
                                stop_date=path.getExpectedStopDate(explanation))

        # the quantity which is produced/consumed at the path.
        quantity = factory.product['quantity'] * path.getQuantity()

        # nodes at the path
        source_section = path.getSourceSection(context=parent_movement)
        destination_section = path.getDestinationSection(context=parent_movement)
        source = path.getSource(context=parent_movement)
        destination = path.getDestination(context=parent_movement)

        # the remaining at the start and the end on the path
        predecessor_remaining_phase_list = path.getPredecessorValue()\
          .getRemainingTradePhaseList(explanation,
                                      trade_phase_list=transformation_phase_list)
        successor_remaining_phase_list = path.getSuccessorValue()\
          .getRemainingTradePhaseList(explanation,
                                      trade_phase_list=transformation_phase_list)
 
        consumed_common_dict = dict(source_section=source_section,
                                    destination_section=destination_section,
                                    source=source,
                                    **path_common_dict)

        produced_common_dict = dict(source_section=source_section,
                                    destination_section=destination_section,
                                    destination=destination,
                                    trade_phase_value_list=successor_remaining_phase_list,
                                    **path_common_dict)

        # when the path is not a part in the last phase on the transformation.
        if len(successor_remaining_phase_list) != 0:
          # partial produced movement
          factory.requestProduced(
            quantity=quantity,
            **produced_common_dict)
        else:
          last_phase_path_list.append(path)

          # used for matching
          work_dict = dict(filter(lambda x: x[0] != 'causality_value',
                                  produced_common_dict.items()))

          # when empty
          if not last_prop_dict:
            last_prop_dict.update(work_dict)

          # must be same, because the path(s) are integrated in the last phase on the transformation.
          if last_prop_dict != work_dict:
            raise TransformationRuleError,\
                  """the Properties which is used to make a movement on the last path
are different with the Transformation %r and the Business Process %r"""\
              % (transformation, business_process)

        # when the path is part of production, but not first, consume previous partial product
        if path not in head_production_path_value_list:
          factory.requestConsumed(
            quantity=quantity,
            trade_phase_value_list=predecessor_remaining_phase_list,
            **consumed_common_dict)

        # consumed movement
        for amount in amount_list:
          factory.requestConsumed(
            resource=amount.getResource(),
            quantity=quantity * amount.getQuantity() / amount.getEfficiency(),
            quantity_unit=amount.getQuantityUnit(),
            trade_phase=path.getTradePhase(),
            **consumed_common_dict)

    """
    valid graph for transformation
    a --- b --- c

    a --
        \
         X b
        /
    c --

    invalid graph
    a ------- b
    c ------- d

        -- b
       /
    a X
       \
        -- c
    """
    # when empty
    if last_phase_path_list is None or len(last_phase_path_list) == 0:
      raise TransformationRuleError,\
            """could not make the product with the Transformation %r on the Business Process %r,
because last_phase_path_list is empty.""" % (transformation, business_process)

    factory.requestProduced(
      causality_value_list=last_phase_path_list,
      # in the last phase of transformation, produced quantity must be planned as same as ordered.
      quantity=factory.product['quantity'],
      **last_prop_dict)

    # make actual simulation movements
    factory.makeMovements(applied_rule)

    Rule.expand(self, applied_rule, **kw)