Commit 3b8cdf7e authored by Jean-Paul Smets's avatar Jean-Paul Smets

Moved to rule mixin (since this was very specific to rules)

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/amount_generator@35767 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 4d8dfd53
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility 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
# guarantees 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from Products.ERP5.MovementCollectionDiff import _getPropertyAndCategoryList
class MovementGeneratorMixin:
"""
This class provides a generic implementation of IMovementGenerator.
It is used by rules to generate a collection of movements from
the context of an applied rule.
TODO:
Deliveries ? Movements ? Items ?
Does it depend by default on IAmountGeneratorMixin
"""
# Default values
_applied_rule = None
_rule = None
_trade_phase_list = None
def __init__(self, applied_rule=None, rule=None, trade_phase_list=None):
self._trade_phase_list = trade_phase_list # XXX
self._applied_rule = applied_rule
if rule is None and applied_rule is not None:
self._rule = applied_rule.getSpecialiseValue()
else:
self._rule = rule # for rule specific stuff
# Implementation of IMovementGenerator
def getGeneratedMovementList(self, movement_list=None, rounding=False):
"""
Returns an IMovementList generated by a model applied to the context
context - an IMovementCollection, an IMovementList or an IMovement
movement_list - optional IMovementList which can be passed explicitely
whenever context is an IMovementCollection and whenever
we want to filter context.getMovementList
rounding - boolean argument, which controls if rounding shall be applied on
generated movements or not
NOTE:
- implement rounding appropriately (True or False seems
simplistic)
"""
# Default implementation bellow can be overriden by subclasses
# however it should be generic enough not to be overriden
# by most classes
# Results will be appended to result, objects created inside folder
from Products.ERP5Type.Document import newTempMovement
result = []
if self._applied_rule is None:
folder = self # Really useful ?
else:
folder = self._applied_rule
# Build a list of movement and business path
id_index = 0
for input_movement, business_path in self \
._getInputMovementAndPathTupleList(movement_list=movement_list,
rounding=rounding):
# Merge movement and business path properties (core implementation)
kw = self._getPropertyAndCategoryDict(input_movement, business_path)
# Update movement properties (class overridable)
kw.update(self._getUpdatePropertyDict(input_movement))
# And build temp movement of appropriate type
simulation_movement = newTempMovement(folder,
'generated_movement_%s' % id_index)
simulation_movement._edit(**kw)
result.append(simulation_movement)
id_index += 1
return result
def _getUpdatePropertyDict(self, input_movement):
# Default implementation bellow can be overriden by subclasses
return {'delivery': input_movement.getRelativeUrl(), # XXX-JPS empty is better
}
def _getTradePhaseList(self, input_movement, business_process):
if self._trade_phase_list:
return self._trade_phase_list
if self._rule:
self._rule.getTradePhaseList()
return business_process.getTradePhaseList()
def _getInputMovementAndPathTupleList(self, movement_list=None, rounding=None):
"""
Returns list of tuples (movement, business_path)
"""
# Init result
result = []
# First generate a list of movements with any appropriate method
input_movement_list = self._getInputMovementList(movement_list=movement_list, rounding=rounding)
# For each input movement
for input_movement in input_movement_list:
# Find its business process, if any
business_process = input_movement.asComposedDocument() # This produces a business process ideally
# Initialise trade phase list for a movement and business process
trade_phase_list = self._getTradePhaseList(input_movement, business_process)
if business_process is None or len(trade_phase_list) == 0:
result.append((input_movement, None))
else:
for business_path in business_process.getPathValueList(
trade_phase_list,
input_movement) or [None]:
result.append((input_movement, business_path))
return result
def _getInputMovementList(self, movement_list=None, rounding=None):
raise NotImplementedError
# Default implementation takes amounts ?
# Use TradeModelRuleMovementGenerator._getInputMovementList as default implementation
# and potentially use trade phase for that.... as a way to filter out
def _getPropertyAndCategoryDict(self, movement, business_path):
"""
Merge a movement and a business_path and return a dict of
properties and categories whch can be used to create a new movement.
movement -- an IMovement instance
business_path -- an IBusinessPath instance
rule -- optional rule parameter which can be used to
narrow down properties to be copied
"""
rule = self._rule
if rule is None:
property_dict = _getPropertyAndCategoryList(movement)
else:
property_dict = {}
for tester in rule._getUpdatingTesterList(exclude_quantity=False):
property_dict.update(tester.getUpdatablePropertyDict(
movement, None))
if business_path is None:
return property_dict
# Arrow
for base_category, category_url_list in \
business_path.getArrowCategoryDict(context=movement).iteritems():
property_dict[base_category] = category_url_list
# Amount
if business_path.getQuantity():
property_dict['quantity'] = business_path.getQuantity()
elif business_path.getEfficiency():
property_dict['quantity'] = movement.getQuantity() *\
business_path.getEfficiency()
else:
property_dict['quantity'] = movement.getQuantity()
movement_start_date = movement.getStartDate()
movement_stop_date = movement.getStopDate()
if movement_start_date == movement_stop_date:
property_dict['start_date'] = business_path.getExpectedStartDate(
movement)
property_dict['stop_date'] = business_path.getExpectedStopDate(movement)
# in case of not fully working BPM get dates from movement
# XXX: as soon as BPM will be fully operational this hack will not be
# needed anymore
if property_dict['start_date'] is None:
property_dict['start_date'] = movement_start_date
if property_dict['stop_date'] is None:
property_dict['stop_date'] = movement_stop_date
else: # XXX shall not be used, but business_path.getExpectedStart/StopDate
# do not works on second path...
property_dict['start_date'] = movement_start_date
property_dict['stop_date'] = movement_stop_date
# save a relation to business path
property_dict['causality'] = [business_path.getRelativeUrl()]
return property_dict
......@@ -32,6 +32,9 @@ from Acquisition import aq_base
from Products.CMFCore.utils import getToolByName
from Products.ERP5Type import Permissions, interfaces
from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.MovementCollectionDiff import _getPropertyAndCategoryList
from zLOG import LOG
def _compare(tester_list, prevision_movement, decision_movement):
for tester in tester_list:
......@@ -39,6 +42,97 @@ def _compare(tester_list, prevision_movement, decision_movement):
return False
return True
class MovementGeneratorMixin:
"""
This class provides a generic implementation of IMovementGenerator
which can be used together the Rule mixin class bellow. It does not
have any pretention to provide more than that.
"""
# Default values
_applied_rule = None
_rule = None
_trade_phase_list = None
_explanation = None
def __init__(self, applied_rule, explanation=None, rule=None, trade_phase_list=None):
self._trade_phase_list = trade_phase_list # XXX-JPS Why a list ?
self._applied_rule = applied_rule
if rule is None and applied_rule is not None:
self._rule = applied_rule.getSpecialiseValue()
else:
self._rule = rule # for rule specific stuff
if explanation is None:
self._explanation = applied_rule.getRootExplanationValue()
else:
self._explanation = explanation
# XXX-JPS handle delay_mode
# Implementation of IMovementGenerator
def getGeneratedMovementList(self, movement_list=None, rounding=False):
"""
Returns an IMovementList generated by a model applied to the context
context - an IMovementCollection, an IMovementList or an IMovement
movement_list - optional IMovementList which can be passed explicitely
whenever context is an IMovementCollection and whenever
we want to filter context.getMovementList
rounding - boolean argument, which controls if rounding shall be applied on
generated movements or not
NOTE:
- implement rounding appropriately (True or False seems
simplistic)
"""
# Default implementation bellow can be overriden by subclasses
# however it should be generic enough not to be overriden
# by most classes
# Results will be appended to result, objects created inside folder
from Products.ERP5Type.Document import newTempMovement
result = []
folder = self._applied_rule
# Build a list of movement and business path
for input_amount in self._getInputAmountList(movement_list=movement_list,
rounding=rounding):
# Merge movement and business path properties (core implementation)
LOG('getGeneratedMovementList input_movement', 0, repr(input_movement))
# Lookup Business Process through composition (NOT UNION)
business_process = input_movement.asComposedDocument()
explanation = self._explanation
LOG('getGeneratedMovementList business_process', 0, repr(business_process))
trade_phase = self._getTradePhaseList(input_movement, business_process) # XXX-JPS not convenient to handle
result.extend(business_process.getTradePhaseMovementList(explanation, input_amount,
trade_phase=trade_phase, delay_mode=None))
# Extend movement properties
for movement in result:
movement._edit(self._getUpdatePropertyDict(movement))
# And return list of generated movements
return result
def _getUpdatePropertyDict(self, input_movement):
# Default implementation bellow can be overriden by subclasses
return {'delivery': input_movement.getRelativeUrl(), # XXX-JPS empty is better
}
def _getTradePhaseList(self, input_movement, business_process): # XXX-JPS WEIRD
LOG('_getTradePhaseList _trade_phase_list', 0, str(self._trade_phase_list))
if self._trade_phase_list:
return self._trade_phase_list
if self._rule:
LOG('_getTradePhaseList _trade_phase_list', 0, str(self._rule.getTradePhaseList()))
return self._rule.getTradePhaseList()
return business_process.getTradePhaseList()
def _getInputMovementList(self, movement_list=None, rounding=None):
raise NotImplementedError
# Default implementation takes amounts ?
# Use TradeModelRuleMovementGenerator._getInputMovementList as default implementation
# and potentially use trade phase for that.... as a way to filter out
class RuleMixin:
"""
Provides generic methods and helper methods to implement
......@@ -161,6 +255,7 @@ class RuleMixin:
def _getMovementGeneratorContext(self, applied_rule):
"""
Return the movement generator context to use for expand
XXX-JPS likely useless
"""
raise NotImplementedError
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment