# -*- coding: utf-8 -*- ############################################################################## # # Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved. # Jean-Paul Smets-Solanes <jp@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## import zope.interface 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, MovementGeneratorMixin): """ 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' # Declarative security security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) # Declarative properties property_sheets = ( PropertySheet.Base , PropertySheet.XMLObject , PropertySheet.CategoryCore , PropertySheet.DublinCore , PropertySheet.Price , PropertySheet.Item , PropertySheet.Amount , PropertySheet.Reference ) # 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 def _getInputMovementList(self, context): """ 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())