diff --git a/product/ERP5/Document/SubscriptionItem.py b/product/ERP5/Document/SubscriptionItem.py index 8311337ce5484af698e87ee05f60e8b803a1839f..5c4c07de053698236c149ee2c6b673a7aae3c0e0 100644 --- a/product/ERP5/Document/SubscriptionItem.py +++ b/product/ERP5/Document/SubscriptionItem.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- ############################################################################## # # Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved. @@ -31,11 +32,15 @@ from AccessControl import ClassSecurityInfo from Products.ERP5Type import Permissions, PropertySheet, interfaces from Products.ERP5.Document.Item import Item +from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin -class SubscriptionItem(Item): +class SubscriptionItem(Item, MovementGeneratorMixin): """ - A SubscriptionItem is an Item which can be expanded - whenever it related to a valid Open Order + A SubscriptionItem is an Item which expands itself + into simulation movements which represent the item future. + Examples of subscription items (or subclasses) include: + employee paysheet contracts, telecommunication subscriptions, + banking service subscriptions, etc """ meta_type = 'ERP5 Subscription Item' portal_type = 'Subscription Item' @@ -44,9 +49,6 @@ class SubscriptionItem(Item): security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) - # Declarative interfaces - zope.interface.implements(interfaces.IExpandable) - # Declarative properties property_sheets = ( PropertySheet.Base , PropertySheet.XMLObject @@ -58,16 +60,106 @@ class SubscriptionItem(Item): , PropertySheet.Reference ) - # Expandable Interface Implementation - def expand(self, *args, **kw): + # Declarative interfaces + zope.interface.implements(interfaces.IExpandable, + interfaces.IMovementGenerator, + ) + + # IExpandable interface implementation + def expand(self, applied_rule_id=None, force=0, activate_kw=None, **kw): """ Lookup start / stop properties in related Open Order or Path and expand. """ + # only try to expand if we are not in draft state + if self.getValidationState() == 'draft': # XXX-JPS harcoded + return + + # use hint if provided (but what for ?) XXX-JPS + if applied_rule_id is not None: + portal_simulation = getToolByName(self, 'portal_simulation') + my_applied_rule = portal_simulation[applied_rule_id] + else: + my_applied_rule = self._getRootAppliedRule(activate_kw=activate_kw) + + # Pass expand + if my_applied_rule is not None: + my_applied_rule.expand(activate_kw=activate_kw, **kw) # XXX-JPS why **kw ? + + def _getRootAppliedRule(self, tested_base_category_list=None, + activate_kw=None): + """ + Returns existing root applied rule or, if none, + create a new one a return it + """ + # Look up if existing applied rule + my_applied_rule_list = self.getCausalityRelatedValueList( + portal_type='Applied Rule') + my_applied_rule = None + if len(my_applied_rule_list) == 0: + if self.isSimulated(): + # No need to create a DeliveryRule + # if we are already in the simulation process + pass + else: + # Create a new applied order rule (portal_rules.order_rule) + portal_rules = getToolByName(self, 'portal_rules') + portal_simulation = getToolByName(self, 'portal_simulation') + rule_value_list = portal_rules.searchRuleList(self, + tested_base_category_list=tested_base_category_list) + if len(rule_value_list) > 1: + raise "SimulationError", 'Expandable Document %s has more than one matching'\ + ' rule.' % self.getRelativeUrl() + if len(rule_value_list): + rule_value = rule_value_list[0] + my_applied_rule = rule_value.constructNewAppliedRule(portal_simulation, + activate_kw=activate_kw) + # Set causality + my_applied_rule.setCausalityValue(self) + # We must make sure this rule is indexed + # now in order not to create another one later + my_applied_rule.reindexObject(activate_kw=activate_kw) # XXX-JPS removed **kw + elif len(my_applied_rule_list) == 1: + # Re expand the rule if possible + my_applied_rule = my_applied_rule_list[0] + else: + raise "SimulationError", 'Expandable Document %s has more than one root applied'\ + ' rule.' % self.getRelativeUrl() + + return my_applied_rule + + # IMovementGenerator interface implementation + def getGeneratedMovementList(self, context, movement_list=None, + rounding=False): + """ + Input movement list comes from Open Order + """ + ret = [] + rule = context.getSpecialiseValue() + for input_movement, business_path in self \ + ._getInputMovementAndPathTupleList(context): + kw = self._getPropertyAndCategoryList(input_movement, business_path, + rule) + input_movement_url = input_movement.getRelativeUrl() + kw.update({'delivery': input_movement_url}) + simulation_movement = context.newContent( + portal_type=RuleMixin.movement_type, + temp_object=True, + **kw) + ret.append(simulation_movement) + return ret - security.declarePrivate('expandOpenOrderRule') - def expandOpenOrderRule(self, applied_rule_id=None, force=0, **kw): + def _getInputMovementList(self, context): """ - Provides the default implementation of expand for Open Orders + Input movement list comes from order """ + # Here, we just generate movements + # as a time sries + return + order = context.getDefaultCausalityValue() + if order is None: + return [] + else: + return order.getMovementList( + portal_type=order.getPortalOrderMovementTypeList())