Commit e7cc8b8b authored by Julien Muchembled's avatar Julien Muchembled

Rewrite testTradeModelLine to test new and legacy simulation

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/amount_generator@38055 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent e228ee51
...@@ -50,3 +50,19 @@ class DeliveryRootSimulationRule(DeliveryRule): ...@@ -50,3 +50,19 @@ class DeliveryRootSimulationRule(DeliveryRule):
return { return {
'delivery': movement.getRelativeUrl(), 'delivery': movement.getRelativeUrl(),
} }
# Fix subclasses (ex: AccountingTransactionRootSimulationRule)
from Products.ERP5.Document import DeliveryRootSimulationRule as original_module
original_class = original_module.DeliveryRootSimulationRule
try:
original_module.DeliveryRootSimulationRule = DeliveryRootSimulationRule
import gc, os, sys
from Products.ERP5Type.Utils import importLocalDocument
for bases in gc.get_referrers(original_class):
if type(bases) is tuple:
for subclass in gc.get_referrers(bases):
if getattr(subclass, '__bases__', None) is bases:
importLocalDocument(subclass.__name__,
os.path.dirname(sys.modules[subclass.__module__].__file__))
finally:
original_module.DeliveryRootSimulationRule = original_class
from Products.ERP5.mixin import composition
from Products.ERP5.ERP5Site import ERP5Site
def patch():
from AccessControl.PermissionRole import PermissionRole
from Products.ERP5Type import Permissions
def declareProtected(cls, permission, *name_list):
roles = PermissionRole(permission)
for name in name_list:
setattr(cls, name + '__roles__', roles)
## ERP5Site
declareProtected(ERP5Site, Permissions.AccessContentsInformation,
'getPortalBusinessStateTypeList',
'getPortalBusinessPathTypeList')
def getPortalBusinessStateTypeList(self):
"""
Return business state types.
"""
return ('Business State',)
ERP5Site.getPortalBusinessStateTypeList = getPortalBusinessStateTypeList
def getPortalBusinessPathTypeList(self):
"""
Return business path types.
"""
return ('Business Path',)
ERP5Site.getPortalBusinessPathTypeList = getPortalBusinessPathTypeList
## CompositionMixin
composition._LEGACY_SIMULATION = True
## SimulationMovement
def asComposedDocument(self, *args, **kw):
# XXX: What delivery should be used to find amount generator lines ?
# With the currently enabled code, entire branches in the simulation
# tree get (temporary) deleted when new delivery lines are being built
# (and don't have yet a specialise value).
# With the commented code, changing the STC on a SIT generated from a
# SPL/SO would have no impact (and would never make the SIT divergent).
#return self.getRootSimulationMovement() \
# .getDeliveryValue() \
# .asComposedDocument(*args, **kw)
while 1:
delivery_value = self.getDeliveryValue()
if delivery_value is not None:
return delivery_value.asComposedDocument(*args, **kw)
self = self.getParentValue().getParentValue()
from Products.ERP5.Document.SimulationMovement import SimulationMovement
SimulationMovement.asComposedDocument = asComposedDocument
from Products.ERP5Type.Document.SimulationMovement import SimulationMovement
SimulationMovement.asComposedDocument = asComposedDocument
patch()
...@@ -7,6 +7,7 @@ InvoiceTransactionSimulationRule ...@@ -7,6 +7,7 @@ InvoiceTransactionSimulationRule
OrderRootSimulationRule OrderRootSimulationRule
PaymentSimulationRule PaymentSimulationRule
RootAppliedRuleCausalityMovementGroup RootAppliedRuleCausalityMovementGroup
SimulationLegacyPatches
TradeModelSimulationRule TradeModelSimulationRule
Transformation Transformation
TransformedResource TransformedResource
\ No newline at end of file
...@@ -165,6 +165,9 @@ class asComposedDocument(object): ...@@ -165,6 +165,9 @@ class asComposedDocument(object):
object_list) object_list)
return sortValueList(object_list, sort_on, sort_order, **kw) return sortValueList(object_list, sort_on, sort_order, **kw)
# XXX Legacy simulation allows model lines on deliveries.
# Enabled if erp5_simulation_legacy BT is installed.
_LEGACY_SIMULATION = False
class CompositionMixin: class CompositionMixin:
""" """
...@@ -203,6 +206,9 @@ class CompositionMixin: ...@@ -203,6 +206,9 @@ class CompositionMixin:
# we don't use getSpecialiseValueList to avoid acquisition on the parent # we don't use getSpecialiseValueList to avoid acquisition on the parent
model_list = effective_list[effective_index].getValueList('specialise', model_list = effective_list[effective_index].getValueList('specialise',
portal_type=specialise_type_list or ()) portal_type=specialise_type_list or ())
if _LEGACY_SIMULATION and not effective_index: # XXX compatibility
effective_set.add(self)
specialise_value_list = model_list
if effective_set: if effective_set:
effective_index += 1 effective_index += 1
else: # first iteration else: # first iteration
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -33,11 +33,11 @@ from AccessControl import ClassSecurityInfo ...@@ -33,11 +33,11 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, interfaces from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5.Document.Path import Path from Products.ERP5.Document.Path import Path
from Products.ERP5.Document.Predicate import Predicate from Products.ERP5.Document.Amount import Amount
import zope.interface import zope.interface
class BusinessPath(Path, Predicate): class BusinessPath(Path, Amount):
""" """
The BusinessPath class embeds all information related to The BusinessPath class embeds all information related to
lead times and parties involved at a given phase of a business lead times and parties involved at a given phase of a business
......
# -*- coding: utf-8 -*-
##############################################################################
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# Julien Muchembled <jm@nexedi.com>
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import sys
from Products.ERP5Legacy.tests import testLegacyBPMCore
sys.modules['Products.ERP5.tests.testBPMCore'] = testLegacyBPMCore
from Products.ERP5.tests.testTradeModelLine import *
###
## TestTradeModelLine
##
TestTradeModelLine.trade_model_path_portal_type = None
TestTradeModelLine.business_link_portal_type = None
def createBusinessProcess(self, *args, **kw):
business_process = super(TestTradeModelLine, self) \
.createBusinessProcess(*args, **kw)
taxed = self.createBusinessState(business_process, reference='taxed')
invoiced = self.createBusinessState(business_process, reference='invoiced')
self.createBusinessPath(business_process, trade_phase='default/discount',
predecessor_value=invoiced, successor_value=taxed)
self.createBusinessPath(business_process, trade_phase='default/tax',
predecessor_value=invoiced, successor_value=taxed)
return business_process
TestTradeModelLine.createBusinessProcess = createBusinessProcess
def checkWithoutBPM(self, order):
transaction.commit() # clear transactional cache
order.getSpecialiseValue()._setSpecialise(None)
self.checkAggregatedAmountList(order)
applied_rule_id = order.getCausalityRelatedId(portal_type='Applied Rule')
order.expand(applied_rule_id=applied_rule_id)
for line in order.getMovementList():
simulation_movement_list, = self.getTradeModelSimulationMovementList(line)
self.assertFalse(simulation_movement_list)
transaction.abort()
TestTradeModelLine.checkWithoutBPM = checkWithoutBPM
def checkModelLineOnDelivery(self, delivery):
transaction.commit() # clear transactional cache
delivery.newContent(portal_type='Trade Model Line',
price=0.5,
base_application='base_amount/discount',
base_contribution='base_amount/total_discount',
trade_phase='default/discount',
resource_value=self['service/discount'],
reference='total_dicount_2',
int_index=10)
discount_price = (3*4) * 0.5
tax_price = (1*2) * 0.2
total_tax_price = tax_price * 2 * 0.12
self.getAggregatedAmountDict(delivery,
service_tax=dict(total_price=tax_price),
total_dicount_2=dict(total_price=discount_price),
service_tax_2=dict(total_price=tax_price),
tax_3=dict(total_price=total_tax_price),
total_discount=dict(total_price=(total_tax_price+discount_price) * 0.8))
transaction.abort()
TestTradeModelLine.checkModelLineOnDelivery = checkModelLineOnDelivery
def checkComposition(self, movement, specialise_value_list, type_count_dict):
composed = movement.asComposedDocument()
self.assertTrue(movement in composed._effective_model_list)
self.assertSameSet(composed.getSpecialiseValueList(),
specialise_value_list)
count = 0
for portal_type, n in type_count_dict.iteritems():
if portal_type:
count += n
self.assertEqual(n, len(composed.objectValues(portal_type=portal_type)))
self.assertTrue(count, len(composed.objectValues()))
TestTradeModelLine.checkComposition = checkComposition
def checkTradeModelRuleSimulationExpand(self, delivery):
expected_result_dict = self[delivery.getPath()]
price_currency = self['price_currency']
# There is no 'specialise' on the packing list, deleting entire branches
# of the simulation tree. See also SimulationMovement.asComposedDocument
no_expand = 'packing_list' in self and 'invoice' not in self
for line in delivery.getMovementList():
simulation_movement_list, = \
self.getTradeModelSimulationMovementList(line)
if no_expand:
self.assertFalse(simulation_movement_list)
continue
result_dict = dict((sm.getResourceValue().getUse(), sm)
for sm in simulation_movement_list)
self.assertEqual(len(simulation_movement_list),
len(result_dict))
for use in 'discount', 'tax':
total_price = expected_result_dict[use].get(line.getId())
if total_price:
sm = result_dict.pop(use)
self.assertEqual(sm.getTotalPrice(), total_price)
self.assertEqual(1, len(sm.getCausalityValueList()))
self.assertEqual(1, len(sm.getCausalityValueList(
portal_type='Business Path')))
self.assertEqual(0, len(sm.getCausalityValueList(
portal_type='Trade Model Line')))
self.assertEqual(sm.getBaseApplicationList(),
['base_amount/' + use])
self.assertEqual(sm.getBaseContributionList(),
dict(discount=['base_amount/tax'], tax=[])[use])
self.assertEqual({}, result_dict)
TestTradeModelLine.checkTradeModelRuleSimulationExpand = \
checkTradeModelRuleSimulationExpand
...@@ -889,7 +889,7 @@ def writeLocalDocument(class_id, text, create=1, instance_home=None): ...@@ -889,7 +889,7 @@ def writeLocalDocument(class_id, text, create=1, instance_home=None):
f.close() f.close()
# load the file, so that an error is raised if file is invalid # load the file, so that an error is raised if file is invalid
module = imp.load_source(class_id, path) module = imp.load_source(class_id, path)
getattr(module, class_id) getattr(module, class_id, 'patch')
def setDefaultClassProperties(property_holder): def setDefaultClassProperties(property_holder):
"""Initialize default properties for ERP5Type Documents. """Initialize default properties for ERP5Type Documents.
...@@ -948,10 +948,6 @@ def importLocalDocument(class_id, document_path = None): ...@@ -948,10 +948,6 @@ def importLocalDocument(class_id, document_path = None):
f = open(path) f = open(path)
try: try:
document_module = imp.load_source(module_path, path, f) document_module = imp.load_source(module_path, path, f)
document_class = getattr(document_module, class_id)
document_constructor = DocumentConstructor(document_class)
document_constructor_name = "add%s" % class_id
document_constructor.__name__ = document_constructor_name
except Exception: except Exception:
f.close() f.close()
if document_module is not None: if document_module is not None:
...@@ -959,6 +955,19 @@ def importLocalDocument(class_id, document_path = None): ...@@ -959,6 +955,19 @@ def importLocalDocument(class_id, document_path = None):
raise raise
else: else:
f.close() f.close()
# Tolerate that Document doesn't define any class, which can be useful if we
# only want to monkey patch.
# XXX A new 'Patch' folder should be introduced instead. Each module would
# define 2 methods: 'patch' and 'unpatch' (for proper upgrading).
try:
document_class = getattr(document_module, class_id)
except AttributeError:
document_module.patch
return
else:
document_constructor = DocumentConstructor(document_class)
document_constructor_name = "add%s" % class_id
document_constructor.__name__ = document_constructor_name
setattr(Products.ERP5Type.Document, class_id, document_module) setattr(Products.ERP5Type.Document, class_id, document_module)
setattr(Products.ERP5Type.Document, document_constructor_name, setattr(Products.ERP5Type.Document, document_constructor_name,
document_constructor) document_constructor)
......
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