Commit 03671cd7 authored by Nicolas Delaby's avatar Nicolas Delaby

New solver dedicated to solve conflicts on Item List (through aggregate category)

This is a Split And Defer solver, based on item list
 * It support only removed items from prevision (Additional Items raise NotImplementedError)
 * it creates always new movement with detected differences as new list of items
 * Quantity of movement is updated with sum of quantities defined on items
 * it can accept start_date and stop_date parameters.



git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@45299 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 134939b6
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2011 Nexedi SA and Contributors. All Rights Reserved.
# Nicolas Delaby <nicolas@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 Acquisition import aq_base
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5.mixin.solver import SolverMixin
from Products.ERP5.mixin.configurable import ConfigurableMixin
from Products.ERP5.MovementCollectionDiff import _getPropertyAndCategoryList
class ItemListSplitSolver(SolverMixin, ConfigurableMixin, XMLObject):
"""
QUESTION: is a solver a process ? (ie. subprocess of Solver Process)
"""
meta_type = 'ERP5 Item List Split Solver'
portal_type = 'Item List Split Solver'
add_permission = Permissions.AddPortalContent
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
, PropertySheet.Arrow
, PropertySheet.TargetSolver
)
# Declarative interfaces
zope.interface.implements(interfaces.ISolver,
interfaces.IConfigurable,
)
# ISolver Implementation
def solve(self, activate_kw=None):
"""This method create new movement based on difference of aggregate sets.
It supports only removed items.
Quantity divergence is also solved with sum of aggregated quantities stored
on each updated movements.
"""
configuration_dict = self.getConfigurationPropertyDict()
delivery_dict = {}
portal = self.getPortalObject()
for simulation_movement in self.getDeliveryValueList():
delivery_dict.setdefault(simulation_movement.getDeliveryValue(),
[]).append(simulation_movement)
for movement, simulation_movement_list in delivery_dict.iteritems():
decision_aggregate_set = set(movement.getAggregateList())
split_list = []
for simulation_movement in simulation_movement_list:
simulated_aggregate_set = set(simulation_movement.getAggregateList())
difference_set = simulated_aggregate_set.difference(decision_aggregate_set)
mirror_difference_set = decision_aggregate_set.difference(simulated_aggregate_set)
if difference_set:
# There is less aggregates in prevision compare to decision
split_list.append((simulation_movement, difference_set))
elif mirror_difference_set:
# There is additional aggregates in decision compare to prevision
raise NotImplementedError('Additional items detected. This solver'\
' does not support such divergence resolution.')
else:
# Same set, no divergence
continue
# Create split movements
for (simulation_movement, splitted_aggregate_set) in split_list:
split_index = 0
new_id = "%s_split_%s" % (simulation_movement.getId(), split_index)
applied_rule = simulation_movement.getParentValue()
while getattr(aq_base(applied_rule), new_id, None) is not None:
split_index += 1
new_id = "%s_split_%s" % (simulation_movement.getId(), split_index)
# Copy at same level
kw = _getPropertyAndCategoryList(simulation_movement)
previous_aggregate_list = simulation_movement.getAggregateList()
new_aggregate_list = list(set(previous_aggregate_list)\
.symmetric_difference(splitted_aggregate_set))
# freeze those properties only if not yet recorded
# to avoid freezing already recorded value
if not simulation_movement.isPropertyRecorded('aggregate'):
simulation_movement.recordProperty('aggregate')
# edit prevision movement
simulation_movement.setAggregateList(new_aggregate_list)
total_quantity = sum(item.getQuantity() for item in\
simulation_movement.getAggregateValueList())
simulation_movement.setQuantity(total_quantity)
# create compensation decision movement
total_quantity = sum([portal.restrictedTraverse(aggregate).getQuantity()\
for aggregate in splitted_aggregate_set])
kw.update({'portal_type': simulation_movement.getPortalType(),
'id': new_id,
'delivery': None})
# propagate same recorded properties from original movement
# to store them in recorded_property
for frozen_property in ('aggregate', 'start_date', 'stop_date',):
if simulation_movement.isPropertyRecorded(frozen_property):
kw[frozen_property] = simulation_movement.getRecordedProperty(frozen_property)
new_movement = applied_rule.newContent(activate_kw=activate_kw, **kw)
# freeze aggregate property
new_movement.recordProperty('aggregate')
# edit compensation decision movement
new_movement.setAggregateList(list(splitted_aggregate_set))
new_movement.setQuantity(total_quantity)
if activate_kw is not None:
new_movement.setDefaultActivateParameters(
activate_kw=activate_kw, **activate_kw)
start_date = configuration_dict.get('start_date', None)
if start_date is not None:
new_movement.recordProperty('start_date')
new_movement.setStartDate(start_date)
stop_date = configuration_dict.get('stop_date', None)
if stop_date is not None:
new_movement.recordProperty('stop_date')
new_movement.setStopDate(stop_date)
# Finish solving
if self.getPortalObject().portal_workflow.isTransitionPossible(
self, 'succeed'):
self.succeed()
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