diff --git a/product/ERP5/Document/BPMRule.py b/product/ERP5/Document/BPMRule.py index 4b75fe57d911f5139edce0a5774d48c815462c02..e50f4560ac08691d3971947eb7eaf6abb8af6f09 100644 --- a/product/ERP5/Document/BPMRule.py +++ b/product/ERP5/Document/BPMRule.py @@ -31,9 +31,12 @@ import zope.interface from AccessControl import ClassSecurityInfo from Products.ERP5Type import Permissions, PropertySheet, interfaces -from Products.ERP5.Document.Rule import Rule +from Products.ERP5.Document.Predicate import Predicate +from Products.ERP5Type.XMLObject import XMLObject +from Acquisition import aq_base +from Products.CMFCore.utils import getToolByName -class BPMRule(Rule): +class BPMRule(Predicate, XMLObject): """ DISCLAIMER: Do not use this in any production system. This is only proof of concept and evaluation of new system @@ -72,7 +75,134 @@ class BPMRule(Rule): , PropertySheet.AppliedRule ) -#### Helpers + movement_type = 'Simulation Movement' + + security.declareProtected(Permissions.AccessContentsInformation, + 'isAccountable') + def isAccountable(self, movement): + """Tells wether generated movement needs to be accounted or not. + + Only account movements which are not associated to a delivery; + Whenever delivery is there, delivery has priority + """ + return movement.getDeliveryValue() is None + + security.declareProtected(Permissions.ModifyPortalContent, + 'constructNewAppliedRule') + def constructNewAppliedRule(self, context, id=None, activate_kw=None): + """ + Creates a new applied rule which points to self + """ + portal_types = getToolByName(self, 'portal_types') + if id is None: + id = context.generateNewId() + if getattr(aq_base(context), id, None) is None: + context.newContent(id=id, + portal_type='Applied Rule', + specialise_value=self, + activate_kw=activate_kw) + return context.get(id) + + # Simulation workflow + def test(self, *args, **kw): + """ + If no test method is defined, return False, to prevent infinite loop + """ + if not self.getTestMethodId(): + return False + return Predicate.test(self, *args, **kw) + + # Solvers + security.declareProtected( Permissions.AccessContentsInformation, + 'isDivergent') + def isDivergent(self, sim_mvt, ignore_list=[]): + """ + Returns true if the Simulation Movement is divergent comparing to + the delivery value + """ + delivery = sim_mvt.getDeliveryValue() + if delivery is None: + return 0 + + if self.getDivergenceList(sim_mvt) == []: + return 0 + else: + return 1 + + security.declareProtected(Permissions.View, 'getDivergenceList') + def getDivergenceList(self, sim_mvt): + """ + Return a list of messages that contains the divergences. + """ + result_list = [] + for divergence_tester in self.contentValues( + portal_type=self.getPortalDivergenceTesterTypeList()): + result = divergence_tester.explain(sim_mvt) + result_list.extend(result) + return result_list + + # Deliverability / orderability + def isOrderable(self, movement): + return 0 + + def isDeliverable(self, movement): + return 0 + + def isStable(self, applied_rule, **kw): + """ + - generate a list of previsions + - compare the prevision with existing children + - return 1 if they match, 0 else + """ + list = self._getCompensatedMovementList(applied_rule, **kw) + for e in list: + if len(e) > 0: + return 0 + return 1 + +#### Helpers to overload + def _getExpandablePropertyUpdateDict(self, applied_rule, movement, business_path, **kw): + """Rule specific dictionary used to update _getExpandablePropertyDict + This method might be overloaded. + """ + return {} + + def _getInputMovementList(self, applied_rule): + """Return list of movements for applied rule. + This method might be overloaded""" + return [applied_rule.getParentValue()] + + def _generatePrevisionList(self, applied_rule, **kw): + """ + Generate a list of dictionaries, that contain calculated content of + current Simulation Movements in applied rule. + based on its context (parent movement, delivery, configuration ...) + + These previsions are returned as dictionaries. + """ + input_movement_list = self._getInputMovementList(applied_rule) + business_process = applied_rule.getBusinessProcessValue() + + input_movement_and_path_list = [] + business_path_list = [] + for input_movement in input_movement_list: + for business_path in business_process.getPathValueList( + self.getProperty('trade_phase_list'), + input_movement): + input_movement_and_path_list.append((input_movement, business_path)) + business_path not in business_path_list and business_path_list \ + .append(business_path) + + if len(business_path_list) > 1: + raise NotImplementedError + + prevision_dict_list = [] + for input_movement, business_path in input_movement_and_path_list: + prevision_dict_list.append(self._getExpandablePropertyDict(applied_rule, + input_movement, business_path)) + return prevision_dict_list + +#### Helpers NOT to overload def _getCurrentMovementList(self, applied_rule, **kw): """ Returns the list of current children of the applied rule, sorted in 3 @@ -199,17 +329,6 @@ class BPMRule(Rule): movement.getRelativeUrl()) return (add_list, modify_dict, delete_list) - def _getExpandablePropertyUpdateDict(self, applied_rule, movement, business_path, **kw): - """Rule specific dictionary used to update _getExpandablePropertyDict - This method might be overloaded. - """ - return {} - - def _getInputMovementList(self, applied_rule): - """Return list of movements for applied rule. - This method might be overloaded""" - return [applied_rule.getParentValue()] - def _getExpandablePropertyDict(self, applied_rule, movement, business_path, **kw): """ @@ -255,38 +374,10 @@ class BPMRule(Rule): movement, business_path, **kw)) return property_dict - def _generatePrevisionList(self, applied_rule, **kw): - """ - Generate a list of dictionaries, that contain calculated content of - current Simulation Movements in applied rule. - based on its context (parent movement, delivery, configuration ...) - - These previsions are returned as dictionaries. - """ - input_movement_list = self._getInputMovementList(applied_rule) - business_process = applied_rule.getBusinessProcessValue() - - input_movement_and_path_list = [] - business_path_list = [] - for input_movement in input_movement_list: - for business_path in business_process.getPathValueList( - self.getProperty('trade_phase_list'), - input_movement): - input_movement_and_path_list.append((input_movement, business_path)) - business_path not in business_path_list and business_path_list \ - .append(business_path) - - if len(business_path_list) > 1: - raise NotImplementedError - - prevision_dict_list = [] - for input_movement, business_path in input_movement_and_path_list: - prevision_dict_list.append(self._getExpandablePropertyDict(applied_rule, - input_movement, business_path)) - return prevision_dict_list - security.declareProtected(Permissions.ModifyPortalContent, 'expand') def expand(self, applied_rule, force=0, **kw): + """Generic expand with helpers. + Do NOT overload, use helpers.""" add_list, modify_dict, \ delete_list = self._getCompensatedMovementList(applied_rule, **kw) @@ -304,5 +395,5 @@ class BPMRule(Rule): new_movement = applied_rule.newContent(id=movement_id, portal_type=self.movement_type, **movement_dict) - # Pass to base class - Rule.expand(self, applied_rule, force=force, **kw) # Simulation workflow + for o in applied_rule.objectValues(): + o.expand(**kw)