Commit 6b696094 authored by Romain Courteaud's avatar Romain Courteaud

Start to implement the new simulation rules.

http://erp5.org/Discussion/SimulationRules

By Alexandre Boeglin and Rafael Monnerat.
Verified by Romain Courteaud.

Currently, the rule 18 is not implemented (we don't copy yet order 
movement's properties to the simulation movement)

The behavior of the simulation should not be changed by this commit.



git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@9783 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent c4b84da0
......@@ -86,7 +86,7 @@ class AmortisationRule(Rule):
'correction': 'correction'
}
def test(self, movement):
def _test(self, movement):
"""
Tests if the rule (still) applies
"""
......
......@@ -28,7 +28,6 @@
from AccessControl import ClassSecurityInfo
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.WorkflowCore import WorkflowMethod
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type.PsycoWrapper import psyco
......@@ -37,20 +36,21 @@ from zLOG import LOG
class AppliedRule(XMLObject):
"""
An applied rule holds a list of simulation movements
An applied rule holds a list of simulation movements.
An applied rule points to an instance of Rule
(which defines the actual rule to apply with its parameters)
An applied rule points to an instance of Rule (which defines the actual
rule to apply with its parameters) through the specialise relation.
An applied rule can expand itself (look at its direct parent
and take conclusions on what should be inside). This is similar
to the base_fix_consistency mechanism
An applied rule can expand itself (look at its direct parent and take
conclusions on what should be inside).
An applied rule can "solve" or "backtrack". In this case
it looks at its children, looks at the difference between
target and actual, and takes conclusions on its parent
An applied rule can tell if it is stable (if its children are consistent
with what would be expanded from its direct parent).
All algorithms are implemented by the rule
An applied rule can tell if any of his direct children is divergent (not
consistent with the delivery).
All algorithms are implemented by the rule.
"""
# CMF Type Definition
......@@ -76,21 +76,21 @@ class AppliedRule(XMLObject):
security.declareProtected(Permissions.AccessContentsInformation, 'test')
def test(self):
"""
Tests if the rule (still) applies
Tests if the rule (still) applies
"""
my_parent = self.aq_parent
if my_parent is None: # Should be is portal_simulation
if self.isRootAppliedRule():
return 1
else:
parent_value = self.aq_parent
rule = self.getSpecialiseValue()
return rule.test(my_parent)
return rule.test(parent_value)
security.declareProtected(Permissions.AccessContentsInformation,
'isAccountable')
'isAccountable')
def isAccountable(self, movement):
"""Tells wether generated movement needs to be accounted or not."""
"""Tells whether generated movement needs to be accounted or not."""
return self.getSpecialiseValue().isAccountable(movement)
security.declareProtected(Permissions.ModifyPortalContent, 'expand')
def expand(self, **kw):
"""
......@@ -110,11 +110,9 @@ class AppliedRule(XMLObject):
# XXX This part must be done with a interaction workflow is needed.
# if self.isRootAppliedRule():
# self.activate(
# after_method_id=["immediateReindexObject",
# after_method_id=["immediateReindexObject",
# "recursiveImmediateReindexObject"]).\
# notifySimulationChange(rule._v_notify_dict)
#expand = WorkflowMethod(expand)
security.declareProtected(Permissions.ModifyPortalContent, 'solve')
def solve(self, solution_list):
......@@ -133,8 +131,6 @@ class AppliedRule(XMLObject):
if rule is not None:
rule.solve(self)
#solve = WorkflowMethod(solve)
security.declareProtected(Permissions.ModifyPortalContent, 'diverge')
def diverge(self):
"""
......@@ -147,41 +143,41 @@ class AppliedRule(XMLObject):
if rule is not None:
rule.diverge(self)
#diverge = WorkflowMethod(diverge)
# Solvers
security.declareProtected(Permissions.View, 'isDivergent')
def isDivergent(self):
security.declareProtected(Permissions.AccessContentsInformation,
'isStable')
def isStable(self):
"""
Returns 1 if divergent rule
Tells whether the rule is stable or not.
"""
rule = self.getSpecialiseValue()
if rule is not None:
return rule.isDivergent(self)
return 0
return self.getSpecialiseValue().isStable(self)
security.declareProtected(Permissions.View, 'getDivergenceList')
def getDivergenceList(self):
security.declareProtected(Permissions.AccessContentsInformation,
'isDivergent')
def isDivergent(self, movement):
"""
Returns a list Divergence descriptors
Tells whether generated movement is divergent or not.
"""
rule = self.getSpecialiseValue()
if rule is not None:
return rule.getDivergenceList(self)
return ()
return self.getSpecialiseValue().isDivergent(movement)
security.declareProtected(Permissions.View, 'getSolverList')
def getSolverList(self):
security.declareProtected(Permissions.AccessContentsInformation,
'getDivergenceList')
def getDivergenceList(self, movement):
"""
Returns a list Divergence solvers
Returns a list Divergence descriptors
"""
rule = self.getSpecialiseValue()
if rule is not None:
return rule.getSolverList(self)
return ()
return self.getSpecialiseValue().getDivergenceList(movement)
security.declareProtected(Permissions.AccessContentsInformation,
'getSolverList')
def getSolverList(self, movement):
"""
Returns a list Divergence solvers
"""
return self.getSpecialiseValue().getSolverList(movement)
security.declareProtected(Permissions.AccessContentsInformation,
'isRootAppliedRule')
'isRootAppliedRule')
def isRootAppliedRule(self):
"""
Returns 1 is this is a root applied rule
......@@ -189,7 +185,7 @@ class AppliedRule(XMLObject):
return self.getParentValue().getMetaType() == "ERP5 Simulation Tool"
security.declareProtected(Permissions.AccessContentsInformation,
'getRootAppliedRule')
'getRootAppliedRule')
def getRootAppliedRule(self):
"""Return the root applied rule.
useful if some reindexing is needed from inside
......
This diff is collapsed.
This diff is collapsed.
......@@ -35,7 +35,7 @@ from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5.Document.Amount import Amount
from zLOG import LOG
from zLOG import LOG, WARNING
class Movement(XMLObject, Amount):
"""
......@@ -394,6 +394,25 @@ class Movement(XMLObject, Amount):
return 1
return 0
security.declareProtected(Permissions.AccessContentsInformation,
'isFrozen')
def isFrozen(self):
"""
Returns the frozen status of this movemnt.
a movement in started, stopped, delivered is automatically frozen.
If frozen is locally set to '0', we must check for a parent set to '1', in
which case, we want the children to be frozen as well.
"""
# XXX Hardcoded
# Maybe, we should use getPortalCurrentInventoryStateList
# and another portal method for cancelled (and deleted)
LOG("Movement, isFrozen", WARNING, "Hardcoded state list")
if self.getSimulationState() in ('stopped', 'delivered', 'cancelled'):
return 1
if self._baseIsFrozen() == 0:
self._baseSetFrozen(None)
return self._baseGetFrozen() or 0
security.declareProtected( Permissions.AccessContentsInformation,
'getExplanation')
def getExplanation(self):
......
This diff is collapsed.
......@@ -64,8 +64,7 @@ class PaymentRule(Rule):
, PropertySheet.DublinCore
)
security.declareProtected(Permissions.AccessContentsInformation, 'test')
def test(self, movement):
def _test(self, movement):
"""
Tests if the rule (still) applies
"""
......
This diff is collapsed.
......@@ -29,10 +29,8 @@
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.WorkflowCore import WorkflowMethod
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5.Core import MetaNode, MetaResource
from Products.ERP5.Document.Movement import Movement
......@@ -114,11 +112,11 @@ class SimulationMovement(Movement):
, PropertySheet.AppliedRule
, PropertySheet.ItemAggregation
)
def tpValues(self) :
""" show the content in the left pane of the ZMI """
return self.objectValues()
# Price should be acquired
security.declareProtected( Permissions.AccessContentsInformation,
'getPrice')
......@@ -221,9 +219,9 @@ class SimulationMovement(Movement):
# reexpanded each time, because the rule apply only if predicates
# are true, then this kind of rule must always be tested. Currently,
# we know that invoicing rule acts like this, and that it comes after
# invoice or invoicing_rule, so we if we come from invoince rule or
# invoice or invoicing_rule, so we if we come from invoince rule or
# invoicing rule, we always expand regardless of the causality state.
if ((self.getParentValue().getSpecialiseId() not in
if ((self.getParentValue().getSpecialiseId() not in
('default_invoicing_rule', 'default_invoice_rule')
and self.getCausalityState() == 'expanded' ) or \
len(self.objectIds()) != 0):
......@@ -250,8 +248,6 @@ class SimulationMovement(Movement):
"""
self.setCausalityState('diverged')
#diverge = WorkflowMethod(diverge) USELESS NOW
security.declareProtected( Permissions.AccessContentsInformation,
'getExplanation')
def getExplanation(self):
......@@ -271,7 +267,7 @@ class SimulationMovement(Movement):
explanation_value = self.getExplanationValue()
if explanation_value is not None :
return explanation_value.getUid()
security.declareProtected( Permissions.AccessContentsInformation,
'getExplanationValue')
def getExplanationValue(self):
......@@ -295,15 +291,6 @@ class SimulationMovement(Movement):
if explanation_value != self.getPortalObject():
return explanation_value
def isFrozen(self):
"""
A frozen simulation movement can not change its target anylonger
Also, once a movement is frozen, we do not calculate anylonger
its direct consequences. (ex. we do not calculate again a transformation)
"""
return 0
# Deliverability / orderability
security.declareProtected( Permissions.AccessContentsInformation,
'isOrderable')
......@@ -327,14 +314,14 @@ class SimulationMovement(Movement):
getDeliverable = isDeliverable
# Simulation Dates - acquire target dates
# Simulation Dates - acquire target dates
security.declareProtected( Permissions.AccessContentsInformation,
'getOrderStartDate')
def getOrderStartDate(self):
order_value = self.getOrderValue()
if order_value is not None:
return order_value.getStartDate()
security.declareProtected( Permissions.AccessContentsInformation,
'getOrderStopDate')
def getOrderStopDate(self):
......@@ -353,7 +340,7 @@ class SimulationMovement(Movement):
if delivery_movement is not None:
start_date_list.append(delivery_movement.getStartDate())
return start_date_list
security.declareProtected( Permissions.AccessContentsInformation,
'getDeliveryStopDateList')
def getDeliveryStopDateList(self):
......@@ -365,7 +352,7 @@ class SimulationMovement(Movement):
if delivery_movement is not None:
stop_date_list.append(delivery_movement.getStopDate())
return stop_date_list
security.declareProtected( Permissions.AccessContentsInformation,
'getDeliveryQuantity')
def getDeliveryQuantity(self):
......@@ -377,75 +364,41 @@ class SimulationMovement(Movement):
if delivery_movement is not None:
quantity = delivery_movement.getQuantity()
return quantity
security.declareProtected( Permissions.AccessContentsInformation,
'isConvergent')
def isConvergent(self):
"""
Returns true if the Simulation Movement is convergent comparing to
Returns true if the Simulation Movement is convergent with the
the delivery value
"""
return not self.isDivergent()
security.declareProtected( Permissions.AccessContentsInformation,
'isDivergent')
'isDivergent')
def isDivergent(self):
"""
Returns true if the Simulation Movement is divergent comparing to
Returns true if the Simulation Movement is divergent from the
the delivery value
"""
delivery = self.getDeliveryValue()
if delivery is None:
return 0
# XXX Those properties are the same than defined in DeliveryBuilder.
# We need to defined it only 1 time.
#LOG('SimulationMovement.isDivergent',0,delivery.getPath())
#LOG('SimulationMovement.isDivergent self.getStartDate()',0,self.getStartDate())
#LOG('SimulationMovement.isDivergent delivery.getStartDate()',0,delivery.getStartDate())
if self.getSourceSection() != delivery.getSourceSection() or \
self.getDestinationSection() != delivery.getDestinationSection() or \
self.getSource() != delivery.getSource() or \
self.getDestination() != delivery.getDestination() or \
self.getResource() != delivery.getResource() or \
self.getVariationCategoryList() != delivery.getVariationCategoryList()\
or \
self.getAggregateList() != delivery.getAggregateList() or \
self.getStartDate() != delivery.getStartDate() or \
self.getStopDate() != delivery.getStopDate():
# for method in ["getSourceSection",
# "getDestinationSection",
# "getSource",
# "getDestination",
# "getResource",
# "getVariationCategoryList",
# "getStartDate",
# "getStopDate"]:
# LOG("SimulationMovement, isDivergent", 0,
# "method: %s, self: %s , delivery: %s" % \
# tuple([method]+[str(getattr(x,method)()) for x in (self, delivery)]))
return 1
d_quantity = delivery.getQuantity()
quantity = self.getCorrectedQuantity()
d_error = self.getDeliveryError()
if quantity is None:
if d_quantity is None:
return 0
return 1
if d_quantity is None:
d_quantity = 0
if d_error is None:
d_error = 0
delivery_ratio = self.getDeliveryRatio()
# if the delivery_ratio is None, make sure that we are
# divergent even if the delivery quantity is 0
if delivery_ratio is not None:
d_quantity *= delivery_ratio
if delivery_ratio == 0 and quantity >0:
return 1
if d_quantity != quantity + d_error:
return 1
return 0
return self.getParentValue().isDivergent(self)
security.declareProtected( Permissions.AccessContentsInformation,
'getDivergenceList')
def getDivergenceList(self):
"""
Returns detailed information about the divergence
"""
return self.getParentValue().getDivergenceList(self)
security.declareProtected( Permissions.AccessContentsInformation,
'getSolverList')
def getSolverList(self):
"""
Returns solvers that can fix the current divergence
"""
return self.getParentValue().getSolverList(self)
security.declareProtected( Permissions.ModifyPortalContent,
'setDefaultDeliveryProperties')
def setDefaultDeliveryProperties(self):
......@@ -505,10 +458,10 @@ class SimulationMovement(Movement):
"""
root_rule = self.getRootAppliedRule()
return root_rule.getCausalityValueList()
# XXX FIXME Use a interaction workflow instead
# XXX This behavior is now done by simulation_movement_interaction_workflow
# The call to activate() must be done after actual call to
# The call to activate() must be done after actual call to
# setDelivery() on the movement,
# but activate() must be called on the previous delivery...
#def _setDelivery(self, value):
......@@ -519,10 +472,10 @@ class SimulationMovement(Movement):
# if delivery_value is not None:
# LOG('delivery_value = ', 0, repr(delivery_value))
# activity = delivery_value.activate(
# activity='SQLQueue',
# activity='SQLQueue',
# after_path_and_method_id=(
# self.getPath(),
# ['immediateReindexObject',
# self.getPath(),
# ['immediateReindexObject',
# 'recursiveImmediateReindexObject']))
# activity.edit()
......@@ -62,8 +62,7 @@ class TransformationRule(Rule):
# Class variable
simulation_movement_portal_type = "Simulation Movement"
security.declareProtected(Permissions.AccessContentsInformation, 'test')
def test(self, movement):
def _test(self, movement):
"""
Tests if the rule (still) applies
"""
......
......@@ -145,8 +145,7 @@ class TransformationSourcingRule(Rule):
# Class variable
simulation_movement_portal_type = "Simulation Movement"
security.declareProtected(Permissions.AccessContentsInformation, 'test')
def test(self, movement):
def _test(self, movement):
"""
Tests if the rule (still) applies
"""
......
......@@ -39,6 +39,15 @@ class AppliedRule:
'description' : 'Contains the id of the simulation state when the '\
'object was last expanded (in order to avoid '\
'recalculation)',
'acquisition_base_category' : ( 'parent',),
'acquisition_portal_type' : ('Applied Rule', ),
'acquisition_copy_value' : 0,
'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getLastExpandSimulationState',
'acquisition_depends' : None,
'alt_accessor_id' : ('getLastExpandSimulationState', ),
'type' : 'string',
'mode' : 'w' },
)
......
......@@ -71,3 +71,4 @@ class Task:
'mode' : 'w' },
)
_categories = ( 'source_project', 'destination_project' )
......@@ -31,9 +31,16 @@ from TargetSolver import TargetSolver
class CopyToTarget(TargetSolver):
"""
Copy values simulation movement as target. This is
only acceptable for root movements. The meaning of
this solver of other movements is far from certain.
This solver calculates the ratio between the new (delivery) and old
(simulation) quantity and applies this ratio to the simulation movement
and to its parent, until a stable one is found
XXX: This solver's name is not good, and it tries too many things.
Once the new isDivergent engine is implemented, this solver can be
splitted in smaller ones (one for profit and loss, one for backtracking)
Backtracking alone is not enough to solve completely, it must be used with
another solver (profit and loss, or creating a compensation branch ...)
"""
def _generateValueDeltaDict(self, simulation_movement):
"""
......@@ -94,10 +101,21 @@ class CopyToTarget(TargetSolver):
"""
Get parent movement, and its value delta dict.
"""
#XXX max_allowed_delta is the maximum number of days we want not to
# account as a divergence. It should be configurable through a Rule
max_allowed_delta = 15
applied_rule = simulation_movement.getParentValue()
parent_movement = applied_rule.getParentValue()
if parent_movement.getPortalType() != "Simulation Movement":
parent_movement = None
for date_delta in ('start_date_delta', 'stop_date_delta'):
if date_delta in value_delta_dict.keys():
if abs(value_delta_dict[date_delta]) <= \
applied_rule.getProperty('max_allowed_delta', max_allowed_delta):
value_delta_dict.pop(date_delta)
return parent_movement, value_delta_dict
def _recursivelySolve(self, simulation_movement, is_last_movement=1, **value_delta_dict):
......@@ -106,12 +124,34 @@ class CopyToTarget(TargetSolver):
his parent movement.
"""
value_dict = self._generateValueDict(simulation_movement, **value_delta_dict)
simulation_movement.edit(**value_dict)
if is_last_movement:
delivery_quantity = simulation_movement.getDeliveryValue().getQuantity()
simulation_movement.setDeliveryError(delivery_quantity - value_dict['quantity'])
parent_movement, parent_value_delta_dict = \
self._getParentParameters(simulation_movement, **value_delta_dict)
if parent_movement is not None:
# Modify the parent movement
self._recursivelySolve(parent_movement, is_last_movement=0, **parent_value_delta_dict)
if parent_movement is not None and parent_movement.isFrozen():
# If backtraxcking is not possible, we have to make sure that the
# divergence is solved locally by using profit and loss
sm_quantity = simulation_movement.getQuantity()
delivery_quantity = \
simulation_movement.getDeliveryValue().getQuantity()
# simulation_movement.edit(
# profit_quantity=sm_quantity - delivery_quantity)
else:
# fix foating point rounding error
if is_last_movement:
delivery_quantity = \
simulation_movement.getDeliveryValue().getQuantity()
simulation_movement.setDeliveryError(delivery_quantity -
value_dict['quantity'])
delivery = simulation_movement.getDeliveryValue()
simulation_movement.setDestination(delivery.getDestination())
simulation_movement.setSource(delivery.getSource())
simulation_movement.setDestinationSection(delivery.getDestinationSection())
simulation_movement.setSourceSection(delivery.getSourceSection())
simulation_movement.edit(**value_dict)
if parent_movement is not None:
# backtrack to the parent movement only if it is not frozen
self._recursivelySolve(parent_movement, is_last_movement=0,
**parent_value_delta_dict)
......@@ -76,6 +76,10 @@ class SplitAndDefer(CopyToTarget):
**self.additional_parameters
)
new_movement.activate(**self.additional_parameters).expand()
# adopt new quantity on original simulation movement
simulation_movement.edit(quantity=new_movement_quantity)
simulation_movement._v_activate_kw = self.activate_kw
simulation_movement.activate(**self.additional_parameters).expand()
CopyToTarget.solve(self, simulation_movement)
# SplitAndDefer solves the divergence at the current level, no need to
# backtrack.
......@@ -1266,6 +1266,7 @@ class TestAccountingRules(TestAccountingRulesMixin, ERP5TypeTestCase):
invoice_transaction_line.getSourceCredit())
self.assertEquals(simulation_movement.getSourceDebit(),
invoice_transaction_line.getSourceDebit())
self.assertEquals(simulation_movement.getDelivery(),
invoice_transaction_line.getRelativeUrl())
self.assert_(len(simulation_movement_found.keys()), 3)
......
This diff is collapsed.
This diff is collapsed.
......@@ -59,6 +59,7 @@ class TestPackingListMixin(TestOrderMixin):
"""
Test business template erp5_trade
"""
packable_packing_list_portal_type_list = ['Sale Packing List']
container_portal_type = 'Container'
container_line_portal_type = 'Container Line'
container_cell_portal_type = 'Container Cell'
......@@ -81,7 +82,8 @@ class TestPackingListMixin(TestOrderMixin):
stepCheckDeliveryBuilding \
stepCheckPackingListIsNotDivergent '
default_sequence_with_two_lines = 'stepCreateOrganisation1 \
default_sequence_with_two_lines = '\
stepCreateOrganisation1 \
stepCreateOrganisation2 \
stepCreateOrganisation3 \
stepCreateOrder \
......@@ -104,7 +106,8 @@ class TestPackingListMixin(TestOrderMixin):
stepCheckDeliveryBuilding \
stepCheckPackingListIsNotDivergent '
variated_default_sequence = 'stepCreateOrganisation1 \
variated_default_sequence = '\
stepCreateOrganisation1 \
stepCreateOrganisation2 \
stepCreateOrganisation3 \
stepCreateOrder \
......@@ -178,7 +181,7 @@ class TestPackingListMixin(TestOrderMixin):
Test if packing list is divergent
"""
packing_list = sequence.get('packing_list')
self.assertEquals('diverged',packing_list.getCausalityState())
self.assertEquals('diverged', packing_list.getCausalityState())
def stepCheckPackingListIsNotDivergent(self, sequence=None, sequence_list=None, **kw):
"""
......@@ -205,11 +208,12 @@ class TestPackingListMixin(TestOrderMixin):
"""
packing_list = sequence.get('packing_list')
quantity = sequence.get('line_quantity',default=self.default_quantity)
quantity = quantity -1
quantity = quantity - 1
sequence.edit(line_quantity=quantity)
for packing_list_line in packing_list.objectValues(
portal_type=self.packing_list_line_portal_type):
packing_list_line.edit(quantity=quantity)
sequence.edit(last_delta = sequence.get('last_delta', 0.0) - 1.0)
def stepIncreasePackingListLineQuantity(self, sequence=None,
sequence_list=None, **kw):
......@@ -217,9 +221,13 @@ class TestPackingListMixin(TestOrderMixin):
Set a increased quantity on packing list lines
"""
packing_list = sequence.get('packing_list')
quantity = sequence.get('line_quantity',default=self.default_quantity)
quantity = quantity + 1
sequence.edit(line_quantity=quantity)
for packing_list_line in packing_list.objectValues(
portal_type=self.packing_list_line_portal_type):
packing_list_line.edit(quantity=self.default_quantity+1)
packing_list_line.edit(quantity=quantity)
sequence.edit(last_delta = sequence.get('last_delta', 0.0) + 1.0)
def stepSplitAndDeferPackingList(self, sequence=None, sequence_list=None, **kw):
"""
......@@ -285,14 +293,41 @@ class TestPackingListMixin(TestOrderMixin):
portal_type=self.packing_list_portal_type)
self.assertEquals(1,len(packing_list_list))
packing_list1 = sequence.get('packing_list')
last_delta = sequence.get('last_delta', 0.0)
for line in packing_list1.objectValues(
portal_type= self.packing_list_line_portal_type):
self.assertEquals(self.default_quantity+1,line.getQuantity())
self.assertEquals(self.default_quantity + last_delta,
line.getQuantity())
simulation_list = line.getDeliveryRelatedValueList(
portal_type='Simulation Movement')
self.assertEquals(len(simulation_list),1)
simulation_movement = simulation_list[0]
self.assertEquals(simulation_movement.getQuantity(),self.default_quantity+1)
self.assertEquals(self.default_quantity + last_delta,
simulation_movement.getCorrectedQuantity())
def stepCheckPackingListNotSolved(self, sequence=None, sequence_list=None, **kw):
"""
This step is specific to test_10 : the incorrectly used solver didn't
solve anything.
"""
order = sequence.get('order')
packing_list_list = order.getCausalityRelatedValueList(
portal_type=self.packing_list_portal_type)
self.assertEquals(1,len(packing_list_list))
packing_list1 = sequence.get('packing_list')
last_delta = sequence.get('last_delta', 0.0)
for line in packing_list1.objectValues(
portal_type= self.packing_list_line_portal_type):
self.assertEquals(self.default_quantity + last_delta,
line.getQuantity())
simulation_list = line.getDeliveryRelatedValueList(
portal_type='Simulation Movement')
self.assertEquals(len(simulation_list),1)
simulation_movement = simulation_list[0]
# Here we don't add last_delta, as the solver didn't do its work.
self.assertEquals(self.default_quantity,
simulation_movement.getCorrectedQuantity())
def stepChangePackingListDestination(self, sequence=None,
sequence_list=None, **kw):
......@@ -429,7 +464,7 @@ class TestPackingListMixin(TestOrderMixin):
packing_list = sequence.get('new_packing_list')
self.stepAdoptPrevision(sequence=sequence,packing_list=packing_list)
def stepAcceptDecision(self,sequence=None, sequence_list=None, **kw):
def stepAcceptDecisionPackingList(self,sequence=None, sequence_list=None, **kw):
"""
Check if simulation movement are disconnected
"""
......@@ -561,6 +596,8 @@ class TestPackingListMixin(TestOrderMixin):
not equals to the quantity of the packing list
"""
packing_list = sequence.get('packing_list')
if packing_list.getPortalType() not in \
self.packable_packing_list_portal_type_list: return
self.assertEquals(0,packing_list.isPacked())
self.assertEquals('missing',packing_list.getContainerState())
......@@ -572,6 +609,8 @@ class TestPackingListMixin(TestOrderMixin):
"""
if packing_list is None:
packing_list = sequence.get('packing_list')
if packing_list.getPortalType() not in \
self.packable_packing_list_portal_type_list: return
get_transaction().commit()
self.assertEquals(1,packing_list.isPacked())
self.assertEquals('packed',packing_list.getContainerState())
......@@ -582,6 +621,8 @@ class TestPackingListMixin(TestOrderMixin):
equals to the quantity of the packing list
"""
packing_list = sequence.get('new_packing_list')
if packing_list.getPortalType() not in \
self.packable_packing_list_portal_type_list: return
self.stepCheckPackingListIsPacked(sequence=sequence,
packing_list=packing_list)
......@@ -621,8 +662,11 @@ class TestPackingList(TestPackingListMixin, ERP5TypeTestCase) :
# Test with a simply order without cell
sequence_string = self.default_sequence + '\
stepChangePackingListDestination \
stepCheckPackingListIsDivergent \
stepCheckPackingListIsCalculating \
stepAcceptDecisionPackingList \
stepTic \
stepCheckPackingListIsSolved \
stepCheckPackingListIsNotDivergent \
stepCheckSimulationDestinationUpdated \
'
sequence_list.addSequenceString(sequence_string)
......@@ -640,7 +684,7 @@ class TestPackingList(TestPackingListMixin, ERP5TypeTestCase) :
sequence_string = self.default_sequence + '\
stepChangePackingListStartDate \
stepCheckPackingListIsCalculating \
stepAcceptDecision \
stepAcceptDecisionPackingList \
stepTic \
stepCheckPackingListIsSolved \
stepCheckPackingListIsNotDivergent \
......@@ -783,9 +827,14 @@ class TestPackingList(TestPackingListMixin, ERP5TypeTestCase) :
def test_10_PackingListIncreaseQuantity(self, quiet=0, run=run_all_test):
"""
Change the quantity on an delivery line, then
see if the packing list is divergent and then
split and defer the packing list
- Increase the quantity on an delivery line
- check if the packing list is divergent
- Apply the "split and defer" solver to the packing list
- check that nothing was splitted and the packing list is still divergent
(reset the delta before, as we don't expect a modification)
Basically, when we apply "split and defer" to a packing list, we don't
want it to modify lines which have been increased.
"""
if not run: return
sequence_list = SequenceList()
......@@ -796,8 +845,9 @@ class TestPackingList(TestPackingListMixin, ERP5TypeTestCase) :
stepCheckPackingListIsCalculating \
stepSplitAndDeferPackingList \
stepTic \
stepCheckPackingListIsSolved \
stepCheckPackingListNotSplitted \
stepCheckPackingListIsDiverged \
stepCheckPackingListIsDivergent \
stepCheckPackingListNotSolved \
'
sequence_list.addSequenceString(sequence_string)
......
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