Blame view

product/ERP5/Tool/SolverTool.py 9.58 KB
Jean-Paul Smets committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
# -*- 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.
#
##############################################################################

Jean-Paul Smets committed
30
import zope.interface
Sebastien Robin committed
31
import re
Jean-Paul Smets committed
32 33

from AccessControl import ClassSecurityInfo
Kazuhiko Shiozaki committed
34
from Products.ERP5Type.Globals import InitializeClass
Jean-Paul Smets committed
35
from Products.ERP5Type import Permissions, interfaces
Sebastien Robin committed
36
from Products.ERP5Type.Tool.TypesTool import TypeProvider
Kazuhiko Shiozaki committed
37
from Products.ERP5 import DeliverySolver
Sebastien Robin committed
38 39
from Products.ERP5Type.Message import translateString
from Products.CMFCore.utils import getToolByName
Jean-Paul Smets committed
40

Sebastien Robin committed
41 42 43 44 45
class SolverTool(TypeProvider):
  """ The SolverTool provides API to find out which solver can be applied in
  which case and contains SolverProcess instances which are used to keep track
  of solver decisions, solver history and global optimisation.
  It also contains solvers.
Jean-Paul Smets committed
46 47 48 49
  """
  id = 'portal_solvers'
  meta_type = 'ERP5 Solver Tool'
  portal_type = 'Solver Tool'
Sebastien Robin committed
50
  allowed_types = ( 'ERP5 Solver Type', )
Jean-Paul Smets committed
51 52 53 54 55

  # Declarative Security
  security = ClassSecurityInfo()

  # Declarative interfaces
Sebastien Robin committed
56
  zope.interface.implements(interfaces.IDeliverySolverFactory,)
Jean-Paul Smets committed
57

Jean-Paul Smets committed
58
  # IDeliverySolverFactory implementation
Kazuhiko Shiozaki committed
59 60
  security.declareProtected(Permissions.AccessContentsInformation,
                            'newDeliverySolver')
Sebastien Robin committed
61
  def newDeliverySolver(self, portal_type, movement_list):
Jean-Paul Smets committed
62
    """
Sebastien Robin committed
63 64
    Return a new instance of delivery solver of the given
    portal_type and with appropriate parameters.
Jean-Paul Smets committed
65

Sebastien Robin committed
66
    portal_type -- the portal type of the delivery solver.
Jean-Paul Smets committed
67

Sebastien Robin committed
68
    movement_list -- movements to initialise the instance with
Jean-Paul Smets committed
69
    """
Sebastien Robin committed
70 71 72 73 74 75 76 77
    solver_type = self._getOb(portal_type)
    solver_class = re.sub('^add', 'newTemp',
                       solver_type.getTypeFactoryMethodId())
    module = __import__('Products.ERP5Type.Document', globals(), locals(),
                        [solver_class])
    tmp_solver = getattr(module, solver_class)(self, 'delivery_solver')
    tmp_solver.setDeliveryValueList(movement_list)
    return tmp_solver
Jean-Paul Smets committed
78

Kazuhiko Shiozaki committed
79 80
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getDeliverySolverTranslatedItemList')
Sebastien Robin committed
81
  def getDeliverySolverTranslatedItemList(self, portal_type_list=None):
Jean-Paul Smets committed
82 83
    """
    """
Sebastien Robin committed
84 85 86 87
    return sorted([(translateString(x), 'portal_solvers/%s' % x) \
                   for x in self.getPortalDeliverySolverTypeList() \
                   if portal_type_list is None or x in portal_type_list],
                  key=lambda x:str(x[0]))
Jean-Paul Smets committed
88

Kazuhiko Shiozaki committed
89 90
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getSolverProcessValueList')
Jean-Paul Smets committed
91 92 93 94 95 96 97
  def getSolverProcessValueList(self, delivery_or_movement=None, validation_state=None):
    """
    Returns the list of solver processes which are
    are in a given state and which apply to delivery_or_movement.
    This method is useful to find applicable solver processes
    for a delivery.

Gabriel Monnerat committed
98
    delivery_or_movement -- a movement, a delivery,
Jean-Paul Smets committed
99 100 101 102 103 104
                            or a list thereof

    validation_state -- a state of a list of states
                        to filter the result
    """

Kazuhiko Shiozaki committed
105 106
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getSolverDecisionValueList')
Jean-Paul Smets committed
107 108 109 110 111
  def getSolverDecisionValueList(self, delivery_or_movement=None, validation_state=None):
    """
    Returns the list of solver decisions which apply
    to a given movement.

Gabriel Monnerat committed
112
    delivery_or_movement -- a movement, a simulation movement, a delivery,
Jean-Paul Smets committed
113 114 115 116 117 118
                            or a list thereof

    validation_state -- a state of a list of states
                        to filter the result
    """

Kazuhiko Shiozaki committed
119 120
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getSolverDecisionApplicationValueList')
Jean-Paul Smets committed
121 122 123 124 125 126 127 128 129 130 131 132 133
  def getSolverDecisionApplicationValueList(self, movement, divergence_tester=None):
    """
    Returns the list of documents at which a given divergence resolution
    can be resolved at. For example, in most cases, date divergences can
    only be resolved at delivery level whereas quantities are usually
    resolved at cell level.

    The result of this method is a list of ERP5 documents.

    NOTE: renaming probably required. I do not like this name nor the one
    of the interface definition.
    """
    # Short Term Implementation Approach
Kazuhiko Shiozaki committed
134 135 136
    # XXX tested_property can be multiple for some testers like Net
    # Converted Quantity Divergence Tester or Variation Divergence
    # Tester.
Kazuhiko Shiozaki committed
137 138 139 140 141 142 143
    test_property = divergence_tester.getTestedProperty()
    application_value = movement
    try:
      while not application_value.hasProperty(test_property):
        application_value = application_value.getParentValue()
    except AttributeError:
      # if missing, it should be in Delivery level ?
Aurel committed
144
      application_value = movement.getRootDeliveryValue()
Kazuhiko Shiozaki committed
145 146 147
    return [application_value]

    # Alternate short Term Implementation Approach
Jean-Paul Smets committed
148 149 150
    return self.SolverTool_getSolverDecisionApplicationValueList(movement, divergence_tester)

    # Alternate short Term Implementation Approach
Gabriel Monnerat committed
151
    return divergence_tester.getTypeBasedMethod('getSolverDecisionApplicationValueList')(
Jean-Paul Smets committed
152 153 154 155 156 157 158 159
                                                movement, divergence_tester)

    # Mid-term implementation (we suppose movement is a delivery)
    # use delivery builders to find out at which level the given
    # property can be modified
    test_property = divergence_tester.getTestedProperty()
    application_value_level = {}
    for simulation_movement in movement.getDeliveryRelatedValueList():
Jean-Paul Smets committed
160 161
      business_link = simulation_movement.getCausalityValue()
      for delivery_builder in business_link.getDeliveryBuilderValueList():
Jean-Paul Smets committed
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
        for movement_group in delivery_builder.contentValues(): # filter missing
          if test_property in movement_group.getTestedPropertyList():
            application_value_level[movement_group.getCollectGroupOrder()] = None
    result = []
    # Delivery level
    if 'delivery' in application_value_level:
      result.append(movement.getDeliveryValue())
    # Line level
    if 'line' in application_value_level and not movement.isLine():
      result.append(movement)
    elif 'line' in application_value_level and not movement.isLine():
      result.append(movement.getParentValue())
    # Cell level
    if 'cell' in application_value_level and movement.isCell():
      result.append(movement)
    # Group of lines level (we try to find the most appropriate enclosing group)
    if 'group' in application_value_level:
      application_value = movement
      while not application_value.hasProperty(test_property):
        application_value = application_value.getParentValue()
      if application_value not in result: result.append(application_value)
    # Group of lines level (we try to find the most appropriate enclosing group)
    if 'all_group' in application_value_level:
      application_value = movement
      while not application_value.hasProperty(test_property):
        application_value = application_value.getParentValue()
        if application_value not in result: result.append(application_value)
    return result

    # Longer-term implementation (we suppose movement is a delivery)
    # use delivery builders to find out at which level the given
    # property can be modified
    test_property = divergence_tester.getTestedProperty()
    application_value_level = {}
    for simulation_movement in movement.getDeliveryRelatedValueList():
Jean-Paul Smets committed
197 198
      business_link = simulation_movement.getCausalityValue()
      for delivery_builder in business_link.getDeliveryBuilderValueList():
Jean-Paul Smets committed
199 200 201 202
        for property_group in delivery_builder.contentValues(portal_type="Property group"):
          if test_property in property_group.getTestedPropertyList():
            application_value_level[property_group.getCollectGroupOrder()] = None
    # etc. same
Sebastien Robin committed
203

Kazuhiko Shiozaki committed
204 205
  security.declareProtected(Permissions.AccessContentsInformation,
                            'searchTargetSolverList')
Sebastien Robin committed
206 207 208 209 210 211 212 213 214 215 216 217 218
  def searchTargetSolverList(self, divergence_tester,
                             simulation_movement,
                             automatic_solver_only=False, **kw):
    """
    this method returns a list of target solvers, as predicates against
    simulation movement.
    """
    solver_list = divergence_tester.getSolverValueList()
    if automatic_solver_only:
      return [x for x in solver_list if x.isAutomaticSolver() and \
              x.test(simulation_movement, **kw)]
    else:
      return [x for x in solver_list if x.test(simulation_movement, **kw)]
Kazuhiko Shiozaki committed
219 220

InitializeClass(SolverTool)