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):
return {
'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
OrderRootSimulationRule
PaymentSimulationRule
RootAppliedRuleCausalityMovementGroup
SimulationLegacyPatches
TradeModelSimulationRule
Transformation
TransformedResource
\ No newline at end of file
......@@ -165,6 +165,9 @@ class asComposedDocument(object):
object_list)
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:
"""
......@@ -203,6 +206,9 @@ class CompositionMixin:
# we don't use getSpecialiseValueList to avoid acquisition on the parent
model_list = effective_list[effective_index].getValueList('specialise',
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:
effective_index += 1
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
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5.Document.Path import Path
from Products.ERP5.Document.Predicate import Predicate
from Products.ERP5.Document.Amount import Amount
import zope.interface
class BusinessPath(Path, Predicate):
class BusinessPath(Path, Amount):
"""
The BusinessPath class embeds all information related to
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):
f.close()
# load the file, so that an error is raised if file is invalid
module = imp.load_source(class_id, path)
getattr(module, class_id)
getattr(module, class_id, 'patch')
def setDefaultClassProperties(property_holder):
"""Initialize default properties for ERP5Type Documents.
......@@ -948,10 +948,6 @@ def importLocalDocument(class_id, document_path = None):
f = open(path)
try:
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:
f.close()
if document_module is not None:
......@@ -959,6 +955,19 @@ def importLocalDocument(class_id, document_path = None):
raise
else:
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, document_constructor_name,
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