# -*- 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.CMFCore.utils import getToolByName from Products.ERP5Type import Permissions, PropertySheet from Products.ERP5Type.XMLObject import XMLObject from Products.CMFActivity.ActiveProcess import ActiveProcess class SolverProcess(XMLObject, ActiveProcess): """ Solver Process class represents the decision of the user to solve a divergence. The data structure is the following: Solver Process can contain: - Solver Decision documents which represent the decision of the user to solve a divergence on a given Delivery Line by using a certain heuristic - Target Solver documents which encapsulate the resolution heuristic in relation with DivergenceTester (ie. each DivergenceTester must provide a list of Target Solver portal types whch are suitable to solve a given divergence) and which may eventually use a Delivery Solver each time divergence is related to quantities. Every Simulation Movement affected by a Solver Process has a relation to the solver process through the "solver" base category. """ meta_type = 'ERP5 Solver Process' portal_type = 'Solver Process' add_permission = Permissions.AddPortalContent isPortalContent = 1 isRADContent = 1 isIndexable = 0 # We do not want to fill the catalog with objects on which we need no reporting # Declarative security security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) # Default Properties property_sheets = ( PropertySheet.Base , PropertySheet.XMLObject , PropertySheet.CategoryCore , PropertySheet.DublinCore ) def buildTargetSolverList(self): """ Builds target solvers from solver decisions """ solver_dict = {} movement_dict = {} types_tool = context.portal_types # First create a mapping between delivery movements and solvers # in order to know for each movements which solvers are needed # and which parameters with for decision in context.contentValues(portal_type="Solver Decision"): solver = decision.getSolverValue() solver_type = solver.getId() # ex. Postpone Production Solver solver_conviguration_dict = decision.getConfigurationPropertyDict() solver_conviguration_key = solver_conviguration_dict.items() for movement in decision.getDeliveryValueList(): # Detect incompatibilities movement_solver_dict = movement_dict.setdefault(movement.getRelativeUrl(), {}) movement_solver_configuration_dict = movement_solver_dict.setdefault(solver_type, {}) movement_solver_configuration_dict[solver_key] = None # Second, make sure solvers do not conflict and configuration is valid for movement_url, movement_solver_dict in movement_dict.items(): for solver_type, movement_solver_configuration_dict in movement_solver_dict.items(): solver = types_tool[solver_type] for other_solver in movement_solver_dict.keys(): if solver.conflictsWithSolver(other_solver): raise "Solver %s conflicts with solver %s on movement %s" % (solver_type, other_solver, movement_url) # Make sure multiple configuration are possible try: # Solver key contains only those properties which differentiate # solvers (ex. there should be only Production Reduction Solver) solver_key = solver.reduceConfigurationList(movement_solver_configuration_dict.keys()) except: raise solver_key_dict = solver_dict.setdefault(solver_type, {}) solver_movement_dict = solver_key_dict.setdefault(solver_key, {}) solver_movement_dict[movement_url] = movement_solver_configuration_dict.keys() # Third, build target solvers for portal_type, solver_key_dict in solver_dict.items(): for solver_key, solver_movement_dict in solver_key_dict.items(): solver_instance = self.newContent(portal_type=solver_type) solver_instance._setDeliveryList(solver_movement_dict.keys()) for movement_url, configuration_list: for configuration_kw in configuration_list: solver_instance.updateConfiguration(**configuration_kw) # Solver Process Workflow Interface # NOTE: how can we consider that a workflow defines or provides an interface ? def solve(self): """ Start solving """ for solver in self.contentValues(portal_type=self.getPortalObject().getPortalTargetSolverTypeList()): solver.activate(active_process=self).solve()