Blame view

product/ERP5/Tool/RuleTool.py 6.2 KB
Jean-Paul Smets committed
1 2 3
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
Jean-Paul Smets committed
4
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
Jean-Paul Smets committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#
# 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.
#
##############################################################################

Julien Muchembled committed
29 30
import sys
from collections import defaultdict
Julien Muchembled committed
31
from zLOG import LOG, INFO
Jérome Perrin committed
32
from Products.ERP5Type.Tool.BaseTool import BaseTool
Jean-Paul Smets committed
33
from AccessControl import ClassSecurityInfo
34
from Products.ERP5Type.Globals import InitializeClass, DTMLFile
Jean-Paul Smets committed
35 36
from Products.ERP5Type import Permissions
from Products.ERP5 import _dtmldir
Julien Muchembled committed
37
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
Jean-Paul Smets committed
38

Jérome Perrin committed
39
class RuleTool(BaseTool):
Julien Muchembled committed
40 41 42 43 44 45 46 47 48 49
  """
  The RulesTool implements portal object
  transformation policies.

  An object transformation template is defined by
  a domain and a transformation pattent:

  The domain is defined as:

  - the meta_type it applies to
Jean-Paul Smets committed
50

Julien Muchembled committed
51
  - the portal_type it applies to
Jean-Paul Smets committed
52

Julien Muchembled committed
53 54
  - the conditions of application (category membership, value range,
    security, function, etc.)
Jean-Paul Smets committed
55

Julien Muchembled committed
56
  The transformation template is defined as:
Jean-Paul Smets committed
57

Julien Muchembled committed
58
  - a tree of portal_types starting on the object itself
Jean-Paul Smets committed
59

Julien Muchembled committed
60
  - default values for each node of the tree, incl. the root itself
Jean-Paul Smets committed
61

Julien Muchembled committed
62 63
  When a transformation is triggered, it will check the existence of
  each node and eventually update values
Jean-Paul Smets committed
64

Julien Muchembled committed
65
  Transformations are very similar to XSLT in the XML world.
Jean-Paul Smets committed
66

Julien Muchembled committed
67
  Examples of applications:
Jean-Paul Smets committed
68

Julien Muchembled committed
69
  - generate accounting movements from a stock movement
Jean-Paul Smets committed
70

Julien Muchembled committed
71
  - generate a birthday event from a person
Jean-Paul Smets committed
72

Julien Muchembled committed
73 74 75
  ERP5 main application : generate submovements from movements
  according to templates. Allows to parametrize modules
  such as payroll.
Jean-Paul Smets committed
76

Julien Muchembled committed
77 78 79 80 81 82 83
  """
  id = 'portal_rules'
  meta_type = 'ERP5 Rule Tool'
  portal_type = 'Rule Tool'
  allowed_types = ( 'ERP5 Order Rule', 'ERP5 Transformation Rule',
                    'ERP5 Zero Stock Rule', 'ERP5 Delivery Rule',
                    'ERP5 Amortisation Rule')
Jean-Paul Smets committed
84

Julien Muchembled committed
85 86
  # Declarative Security
  security = ClassSecurityInfo()
Jean-Paul Smets committed
87

Julien Muchembled committed
88 89
  security.declareProtected( Permissions.ManagePortal, 'manage_overview' )
  manage_overview = DTMLFile( 'explainRuleTool', _dtmldir )
Jean-Paul Smets committed
90

Julien Muchembled committed
91 92 93
  security.declareProtected(Permissions.AccessContentsInformation,
                            'searchRuleList')
  def searchRuleList(self, movement, tested_base_category_list=None, **kw):
Jean-Paul Smets committed
94
    """
Julien Muchembled committed
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    this method searches for rules, as predicates against movement

    - the rule must be in "validated" state
    - the rule must be of a known portal type
    - Predicate criterions can be used (like start_date_range_min)
    """
    portal = self.getPortalObject()

    # XXX: For performance reasons, current implementation does not use
    #      DomainTool._searchPredicateList anymore, because in most cases, it
    #      does not filter anything before actualling calling Predicate.test()
    #      Properties must be added on rules to minimize the number of test
    #      scripts/expressions, like the portal types of the possible parent
    #      applied rules, so that filtering can be done via the catalog.
    #      Then it would be possible to use Domain Tool again.
    #return portal.domain_tool._searchPredicateList(context=movement,
    #  tested_base_category_list=tested_base_category_list,
    #  portal_type=portal.getPortalRuleTypeList(),
    #  validation_state="validated", **kw) #XXX "validated" is hardcoded

    # Most rules are only configured through their test_method_id,
    # so filter out them quickly before calling Predicate.test()
    rule_list = []
    for rule in portal.portal_catalog.unrestrictedSearchResults(
        portal_type=portal.getPortalRuleTypeList(),
        validation_state="validated", **kw): #XXX "validated" is hardcoded
      rule = rule.getObject()
      try:
        for test_method_id in rule.getTestMethodIdList():
          if test_method_id == 'Rule_testFalse' or \
             not getattr(movement, test_method_id)(rule):
            break
        else:
Julien Muchembled committed
128 129 130
          if rule.test(movement,
              tested_base_category_list=tested_base_category_list):
            rule_list.append(rule)
Julien Muchembled committed
131 132 133 134 135 136 137 138 139 140
      except Exception:
        # Maybe the script is old (= it takes no argument). Or we should not
        # have called it (= rule would have been excluded before, depending
        # on other criterions). Or there may be a bug.
        # We don't know why it failed so let Predicate.test() do the work.
        if rule.test(movement,
            tested_base_category_list=tested_base_category_list):
          rule_list.append(rule)

    return rule_list
Alexandre Boeglin committed
141

Julien Muchembled committed
142 143 144
  security.declarePrivate('updateSimulation')
  @UnrestrictedMethod
  def updateSimulation(self, message_list):
Julien Muchembled committed
145
    expandable_dict = defaultdict(list)
Julien Muchembled committed
146
    for m in message_list:
Julien Muchembled committed
147 148 149
      expandable_dict[m.object].append(m)
    try:
      for expandable, message_list in expandable_dict.iteritems():
Julien Muchembled committed
150 151
        kw = {}
        for m in message_list:
Julien Muchembled committed
152 153
          kw.update(m.kw)
          m.result = None
Julien Muchembled committed
154 155 156
        LOG("RuleTool", INFO, "Updating simulation for %s: %r"
                              % (expandable.getPath(), kw))
        expandable._updateSimulation(**kw)
Julien Muchembled committed
157 158 159 160
    except Exception:
      exc_info = sys.exc_info()
      for m in message_list:
        m.raised(exc_info)
Jean-Paul Smets committed
161 162

InitializeClass(RuleTool)