Commit 800f68bf authored by Julien Muchembled's avatar Julien Muchembled

Merge testERP5SimulationBPMCore into testBPMCore and create legacy BPM tests

- testLegacy{BPMCore,BPMEvaluation,MRP} are copied from trunk
- use new simulation_test BT
- this fixes testTradeModelLine.test_TradeModelRuleSimulationExpand

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/amount_generator@37599 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 64ae698e
......@@ -42,7 +42,8 @@ class TestBPMMixin(ERP5TypeTestCase):
def getBusinessTemplateList(self):
return ('erp5_base', 'erp5_pdm', 'erp5_trade', 'erp5_accounting',
'erp5_invoicing', 'erp5_simplified_invoicing')
'erp5_invoicing', 'erp5_simplified_invoicing', 'erp5_simulation',
'erp5_simulation_test')
business_process_portal_type = 'Business Process'
business_link_portal_type = 'Business Link'
......
# -*- 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 advised 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.
#
##############################################################################
import transaction
from Products.ERP5.tests.testBPMCore import TestBPMMixin, test_suite
if True:
def getBusinessTemplateList(self):
return ('erp5_base', 'erp5_pdm', 'erp5_trade', 'erp5_accounting',
'erp5_invoicing', 'erp5_simplified_invoicing', 'erp5_simulation')
TestBPMMixin.getBusinessTemplateList = getBusinessTemplateList
def createInvoiceTransactionRule(self):
self.receivable_account = self.createAndValidateAccount('receivable',
'asset/receivable')
self.payable_account = self.createAndValidateAccount('payable',
'liability/payable')
self.income_account = self.createAndValidateAccount('income', 'income')
self.expense_account = self.createAndValidateAccount('expense', 'expense')
self.collected_tax_account = self.createAndValidateAccount(
'collected_tax', 'liability/payable/collected_vat')
self.refundable_tax_account = self.createAndValidateAccount(
'refundable_tax',
'asset/receivable/refundable_vat')
itr = self.portal.portal_rules.newContent(
portal_type='Invoice Transaction Simulation Rule',
reference='default_invoice_transaction_rule',
id='test_invoice_transaction_simulation_rule',
title='Transaction Simulation Rule',
test_method_id=
'SimulationMovement_testInvoiceTransactionSimulationRule',
version=100)
# matching provider for source and destination
for category in ('resource', 'source', 'destination',
'destination_total_asset_price',
'source_total_asset_price'):
itr.newContent(
portal_type='Category Membership Divergence Tester',
title='%s divergence tester' % category,
tested_property=category,
divergence_provider=False,
matching_provider=True)
# non-matching/non-divergence provider quantity divergence tester
# (i.e. only used for expand)
itr.newContent(
portal_type='Net Converted Quantity Divergence Tester',
title='quantity divergence tester',
tested_property='quantity',
quantity=0,
divergence_provider=False,
matching_provider=False)
# divergence provider for date
for property_id in ('start_date', 'stop_date'):
itr.newContent(
portal_type='DateTime Divergence Tester',
title='%s divergence tester' % property_id,
tested_property=property_id,
quantity=0,
divergence_provider=True,
matching_provider=False)
for category in ('source_administration', 'source_decision', 'source_function', 'source_payment', 'source_project', 'source_section', 'destination_administration', 'destination_decision', 'destination_function', 'destination_payment', 'destination_project', 'destination_section'):
itr.newContent(
portal_type='Category Membership Divergence Tester',
title='%s divergence tester' % category,
tested_property=category,
divergence_provider=True,
matching_provider=False)
itr.newContent(
portal_type='Float Divergence Tester',
title='price divergence tester',
tested_property='price',
quantity=0,
divergence_provider=True,
matching_provider=False)
predicate = itr.newContent(portal_type='Predicate',)
predicate.edit(
string_index='use',
title='tax',
int_index=1,
membership_criterion_base_category='resource_use',
membership_criterion_category='resource_use/use/tax')
predicate = itr.newContent(portal_type='Predicate',)
predicate.edit(
string_index='use',
title='discount',
int_index=2,
membership_criterion_base_category='resource_use',
membership_criterion_category='resource_use/use/discount')
predicate = itr.newContent(portal_type='Predicate',)
predicate.edit(
string_index='use',
title='normal',
int_index=3,
membership_criterion_base_category='resource_use',
membership_criterion_category='resource_use/use/normal')
transaction.commit()
self.tic()
accounting_rule_cell_list = itr.contentValues(
portal_type='Accounting Rule Cell')
self.assertEquals(3, len(accounting_rule_cell_list))
tax_rule_cell = itr._getOb("movement_0")
self.assertEquals(tax_rule_cell.getTitle(), 'tax')
tax_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.receivable_account,
destination_value=self.payable_account,
quantity=-1)
tax_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.collected_tax_account,
destination_value=self.refundable_tax_account,
quantity=1)
discount_rule_cell = itr._getOb("movement_1")
self.assertEquals(discount_rule_cell.getTitle(), 'discount')
discount_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.receivable_account,
destination_value=self.payable_account,
quantity=-1)
discount_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.income_account,
destination_value=self.expense_account,
quantity=1)
normal_rule_cell = itr._getOb("movement_2")
self.assertEquals(normal_rule_cell.getTitle(), 'normal')
normal_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.receivable_account,
destination_value=self.payable_account,
quantity=-1)
normal_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.income_account,
destination_value=self.expense_account,
quantity=1)
itr.validate()
TestBPMMixin.createInvoiceTransactionRule = createInvoiceTransactionRule
......@@ -30,7 +30,7 @@
import unittest
import transaction
from Products.ERP5.tests.testERP5SimulationBPMCore import TestBPMMixin
from Products.ERP5.tests.testBPMCore import TestBPMMixin
from Products.ERP5Type.tests.backportUnittest import expectedFailure
from Products.ERP5Type.tests.Sequence import SequenceList
from DateTime import DateTime
......
# -*- coding: utf-8 -*-
##############################################################################
# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
# Łukasz Nowak <luke@nexedi.com>
# Yusuke Muraoka <yusuke@nexedi.com>
# Fabien Morin <fabien@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 unittest
import transaction
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from DateTime import DateTime
from Products.CMFCore.utils import getToolByName
from Products.ERP5Type.tests.utils import reindex
class TestBPMMixin(ERP5TypeTestCase):
"""Skeletons for tests which depend on BPM"""
def getBusinessTemplateList(self):
return ('erp5_base', 'erp5_pdm', 'erp5_trade', 'erp5_accounting',
'erp5_invoicing', 'erp5_simplified_invoicing')
business_process_portal_type = 'Business Process'
business_path_portal_type = 'Business Path'
business_state_portal_type = 'Business State'
normal_resource_use_category_list = ['normal']
invoicing_resource_use_category_list = ['discount', 'tax']
def createCategoriesInCategory(self, category, category_id_list):
for category_id in category_id_list:
if not category.hasObject(category_id):
category.newContent(portal_type='Category', id = category_id,
title = category_id)
@reindex
def createCategories(self):
category_tool = getToolByName(self.portal, 'portal_categories')
self.createCategoriesInCategory(category_tool.base_amount, ['discount',
'tax', 'total_tax', 'total_discount', 'total'])
self.createCategoriesInCategory(category_tool.use,
self.normal_resource_use_category_list + \
self.invoicing_resource_use_category_list)
self.createCategoriesInCategory(category_tool.trade_phase, ['default',])
self.createCategoriesInCategory(category_tool.trade_phase.default,
['accounting', 'delivery', 'invoicing', 'discount', 'tax', 'payment'])
@reindex
def createBusinessProcess(self, **kw):
module = self.portal.getDefaultModule(
portal_type=self.business_process_portal_type)
return module.newContent(portal_type=self.business_process_portal_type,
**kw)
@reindex
def createBusinessPath(self, business_process=None, **kw):
if business_process is None:
business_process = self.createBusinessProcess()
kw['destination_method_id'] = kw.pop('destination_method_id',
'BusinessPath_getDefaultDestinationList')
kw['source_method_id'] = kw.pop('source_method_id',
'BusinessPath_getDefaultSourceList')
business_path = business_process.newContent(
portal_type=self.business_path_portal_type, **kw)
return business_path
@reindex
def createBusinessState(self, business_process=None, **kw):
if business_process is None:
business_process = self.createBusinessProcess()
business_path = business_process.newContent(
portal_type=self.business_state_portal_type, **kw)
return business_path
def createMovement(self):
# returns a movement for testing
applied_rule = self.portal.portal_simulation.newContent(
portal_type='Applied Rule')
return applied_rule.newContent(portal_type='Simulation Movement')
@reindex
def createAndValidateAccount(self, account_id, account_type):
account_module = self.portal.account_module
account = account_module.newContent(portal_type='Account',
title=account_id,
account_type=account_type)
self.assertNotEqual(None, account.getAccountTypeValue())
account.validate()
return account
def createInvoiceTransactionRule(self):
self.receivable_account = self.createAndValidateAccount('receivable',
'asset/receivable')
self.payable_account = self.createAndValidateAccount('payable',
'liability/payable')
self.income_account = self.createAndValidateAccount('income', 'income')
self.expense_account = self.createAndValidateAccount('expense', 'expense')
self.collected_tax_account = self.createAndValidateAccount(
'collected_tax', 'liability/payable/collected_vat')
self.refundable_tax_account = self.createAndValidateAccount(
'refundable_tax',
'asset/receivable/refundable_vat')
itr = self.portal.portal_rules.newContent(
portal_type='Invoice Transaction Simulation Rule',
reference='default_invoice_transaction_rule',
id='test_invoice_transaction_simulation_rule',
title='Transaction Simulation Rule',
test_method_id=
'SimulationMovement_testInvoiceTransactionSimulationRule',
version=100)
predicate = itr.newContent(portal_type='Predicate',)
predicate.edit(
string_index='use',
title='tax',
int_index=1,
membership_criterion_base_category='resource_use',
membership_criterion_category='resource_use/use/tax')
predicate = itr.newContent(portal_type='Predicate',)
predicate.edit(
string_index='use',
title='discount',
int_index=2,
membership_criterion_base_category='resource_use',
membership_criterion_category='resource_use/use/discount')
predicate = itr.newContent(portal_type='Predicate',)
predicate.edit(
string_index='use',
title='normal',
int_index=3,
membership_criterion_base_category='resource_use',
membership_criterion_category='resource_use/use/normal')
transaction.commit()
self.tic()
accounting_rule_cell_list = itr.contentValues(
portal_type='Accounting Rule Cell')
self.assertEquals(3, len(accounting_rule_cell_list))
tax_rule_cell = itr._getOb("movement_0")
self.assertEquals(tax_rule_cell.getTitle(), 'tax')
tax_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.receivable_account,
destination_value=self.payable_account,
quantity=-1)
tax_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.collected_tax_account,
destination_value=self.refundable_tax_account,
quantity=1)
discount_rule_cell = itr._getOb("movement_1")
self.assertEquals(discount_rule_cell.getTitle(), 'discount')
discount_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.receivable_account,
destination_value=self.payable_account,
quantity=-1)
discount_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.income_account,
destination_value=self.expense_account,
quantity=1)
normal_rule_cell = itr._getOb("movement_2")
self.assertEquals(normal_rule_cell.getTitle(), 'normal')
normal_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.receivable_account,
destination_value=self.payable_account,
quantity=-1)
normal_rule_cell.newContent(
portal_type='Accounting Transaction Line',
source_value=self.income_account,
destination_value=self.expense_account,
quantity=1)
itr.validate()
def afterSetUp(self):
self.validateRules()
self.createCategories()
self.createInvoiceTransactionRule()
self.stepTic()
def beforeTearDown(self):
# abort any transaction
transaction.abort()
# put non finished activities into ignored state
activity_connection = self.portal.cmf_activity_sql_connection
for table in 'message', 'message_queue':
activity_connection.manage_test(
'delete from %s where processing_node=-2' % table)
# remove not needed rules
self.portal.portal_rules.manage_delObjects(
ids=['test_invoice_transaction_simulation_rule'])
self.stepTic()
class TestBPMImplementation(TestBPMMixin):
"""Business Process implementation tests"""
def test_BusinessProcess_getPathValueList(self):
business_process = self.createBusinessProcess()
accounting_business_path = business_process.newContent(
portal_type=self.business_path_portal_type,
trade_phase='default/accounting')
delivery_business_path = business_process.newContent(
portal_type=self.business_path_portal_type,
trade_phase='default/delivery')
accounting_delivery_business_path = business_process.newContent(
portal_type=self.business_path_portal_type,
trade_phase=('default/accounting', 'default/delivery'))
self.stepTic()
self.assertSameSet(
(accounting_business_path, accounting_delivery_business_path),
business_process.getPathValueList(trade_phase='default/accounting')
)
self.assertSameSet(
(delivery_business_path, accounting_delivery_business_path),
business_process.getPathValueList(trade_phase='default/delivery')
)
self.assertSameSet(
(accounting_delivery_business_path, delivery_business_path,
accounting_business_path),
business_process.getPathValueList(trade_phase=('default/delivery',
'default/accounting'))
)
def test_BusinessPathStandardCategoryAccessProvider(self):
source_node = self.portal.organisation_module.newContent(
portal_type='Organisation')
source_section_node = self.portal.organisation_module.newContent(
portal_type='Organisation')
business_path = self.createBusinessPath()
business_path.setSourceValue(source_node)
business_path.setSourceSectionValue(source_section_node)
self.assertEquals([source_node], business_path.getSourceValueList())
self.assertEquals([source_node.getRelativeUrl()], business_path.getSourceList())
self.assertEquals(source_node.getRelativeUrl(),
business_path.getSource(default='something'))
def test_EmptyBusinessPathStandardCategoryAccessProvider(self):
business_path = self.createBusinessPath()
self.assertEquals(None, business_path.getSourceValue())
self.assertEquals(None, business_path.getSource())
self.assertEquals('something',
business_path.getSource(default='something'))
def test_BuinessPathDynamicCategoryAccessProvider(self):
source_node = self.portal.organisation_module.newContent(
portal_type='Organisation')
source_section_node = self.portal.organisation_module.newContent(
portal_type='Organisation')
business_path = self.createBusinessPath()
business_path.setSourceMethodId('BusinessPath_getDefaultSourceList')
context_movement = self.createMovement()
context_movement.setSourceValue(source_node)
context_movement.setSourceSectionValue(source_section_node)
self.assertEquals(None, business_path.getSourceValue())
self.assertEquals([source_node],
business_path.getSourceValueList(context=context_movement))
self.assertEquals([source_node.getRelativeUrl()],
business_path.getSourceList(context=context_movement))
self.assertEquals(source_node.getRelativeUrl(),
business_path.getSource(context=context_movement, default='something'))
def test_BuinessPathDynamicCategoryAccessProviderBusinessPathPrecedence(self):
movement_node = self.portal.organisation_module.newContent(
portal_type='Organisation')
path_node = self.portal.organisation_module.newContent(
portal_type='Organisation')
business_path = self.createBusinessPath()
business_path.setSourceMethodId('BusinessPath_getDefaultSourceList')
business_path.setSourceValue(path_node)
context_movement = self.createMovement()
context_movement.setSourceValue(movement_node)
self.assertEquals(path_node, business_path.getSourceValue())
self.assertEquals(path_node,
business_path.getSourceValue(context=context_movement))
self.assertEquals([path_node],
business_path.getSourceValueList(context=context_movement))
def test_BuinessPathDynamicCategoryAccessProviderEmptyMovement(self):
business_path = self.createBusinessPath()
business_path.setSourceMethodId('BusinessPath_getDefaultSourceList')
context_movement = self.createMovement()
self.assertEquals(None, business_path.getSourceValue())
self.assertEquals(None,
business_path.getSourceValue(context=context_movement))
self.assertEquals(None,
business_path.getSource(context=context_movement))
self.assertEquals('something',
business_path.getSource(context=context_movement, default='something'))
def test_BusinessState_getRemainingTradePhaseList(self):
"""
This test case is described for what trade_phase is remaining after the state.
In this case, root explanation is path of between "b" and "d", and
path of between "a" and "b" has a condition which simulation state of
explanation must be "ordered" to pass the path. (*1)
But this test case will be passed the condition.
(root explanation)
default/discount default/invoicing default/accounting
a ------------------ b ------------------- d -------------------- e
(cond="ordered") \ /
\ /
default/delivery \ / default/payment
\ /
\ /
\ /
\ /
\ /
\ /
\ /
c
"""
# define business process
business_process = self.createBusinessProcess()
business_path_a_b = self.createBusinessPath(business_process)
business_path_b_c = self.createBusinessPath(business_process)
business_path_b_d = self.createBusinessPath(business_process)
business_path_c_d = self.createBusinessPath(business_process)
business_path_d_e = self.createBusinessPath(business_process)
business_state_a = self.createBusinessState(business_process)
business_state_b = self.createBusinessState(business_process)
business_state_c = self.createBusinessState(business_process)
business_state_d = self.createBusinessState(business_process)
business_state_e = self.createBusinessState(business_process)
business_path_a_b.setPredecessorValue(business_state_a)
business_path_b_c.setPredecessorValue(business_state_b)
business_path_b_d.setPredecessorValue(business_state_b)
business_path_c_d.setPredecessorValue(business_state_c)
business_path_d_e.setPredecessorValue(business_state_d)
business_path_a_b.setSuccessorValue(business_state_b)
business_path_b_c.setSuccessorValue(business_state_c)
business_path_b_d.setSuccessorValue(business_state_d)
business_path_c_d.setSuccessorValue(business_state_d)
business_path_d_e.setSuccessorValue(business_state_e)
# set title for debug
business_path_a_b.edit(title="a_b")
business_path_b_c.edit(title="b_c")
business_path_b_d.edit(title="b_d")
business_path_c_d.edit(title="c_d")
business_path_d_e.edit(title="d_e")
business_state_a.edit(title="a")
business_state_b.edit(title="b")
business_state_c.edit(title="c")
business_state_d.edit(title="d")
business_state_e.edit(title="e")
# set trade_phase
business_path_a_b.edit(trade_phase=['default/discount'],
completed_state=['ordered']) # (*1)
business_path_b_c.edit(trade_phase=['default/delivery'])
business_path_b_d.edit(trade_phase=['default/invoicing'])
business_path_c_d.edit(trade_phase=['default/payment'])
business_path_d_e.edit(trade_phase=['default/accounting'])
# mock order
order = self.portal.sale_order_module.newContent(portal_type="Sale Order")
order_line = order.newContent(portal_type="Sale Order Line")
# make simulation
order.order()
self.stepTic()
applied_rule = order.getCausalityRelatedValue()
sm = applied_rule.contentValues(portal_type="Simulation Movement")[0]
sm.edit(causality_value=business_path_a_b)
# make other movements for each business path
applied_rule.newContent(portal_type="Simulation Movement",
causality_value=business_path_b_c,
order_value=order_line)
applied_rule.newContent(portal_type="Simulation Movement",
causality_value=business_path_b_d,
order_value=order_line)
applied_rule.newContent(portal_type="Simulation Movement",
causality_value=business_path_c_d,
order_value=order_line)
applied_rule.newContent(portal_type="Simulation Movement",
causality_value=business_path_d_e,
order_value=order_line)
self.stepTic()
trade_phase = self.portal.portal_categories.trade_phase.default
# assertion which getRemainingTradePhaseList must return category which will be passed
# discount is passed, business_path_a_b is already completed, because simulation state is "ordered"
self.assertEquals(set([trade_phase.delivery,
trade_phase.invoicing,
trade_phase.payment,
trade_phase.accounting]),
set(business_state_a.getRemainingTradePhaseList(order)))
self.assertEquals(set([trade_phase.delivery,
trade_phase.invoicing,
trade_phase.payment,
trade_phase.accounting]),
set(business_state_b.getRemainingTradePhaseList(order)))
self.assertEquals(set([trade_phase.payment,
trade_phase.accounting]),
set(business_state_c.getRemainingTradePhaseList(order)))
self.assertEquals(set([trade_phase.accounting]),
set(business_state_d.getRemainingTradePhaseList(order)))
# when trade_phase_list is defined in arguments, the result is filtered by base category.
self.assertEquals(set([trade_phase.delivery,
trade_phase.accounting]),
set(business_state_a\
.getRemainingTradePhaseList(order,
trade_phase_list=['default/delivery',
'default/accounting'])))
def test_BusinessPath_calculateExpectedDate(self):
"""
This test case is described for what start/stop date is expected on
each path by explanation.
In this case, root explanation is path of between "b" and "d", and
lead time and wait time is set on each path.
("l" is lead time, "w" is wait_time)
Each path must calculate most early day from getting most longest
path in the simulation.
"referential_date" represents for which date have to get of explanation from reality.
(root_explanation)
l:2, w:1 l:3, w:1 l:4, w:2
a ------------ b -------------- d -------------- e
\ /
\ /
l:2, w:1 \ / l:3, w:0
\ /
\ /
\ /
\ /
c
"""
# define business process
business_process = self.createBusinessProcess()
business_path_a_b = self.createBusinessPath(business_process)
business_path_b_c = self.createBusinessPath(business_process)
business_path_b_d = self.createBusinessPath(business_process)
business_path_c_d = self.createBusinessPath(business_process)
business_path_d_e = self.createBusinessPath(business_process)
business_state_a = self.createBusinessState(business_process)
business_state_b = self.createBusinessState(business_process)
business_state_c = self.createBusinessState(business_process)
business_state_d = self.createBusinessState(business_process)
business_state_e = self.createBusinessState(business_process)
business_path_a_b.setPredecessorValue(business_state_a)
business_path_b_c.setPredecessorValue(business_state_b)
business_path_b_d.setPredecessorValue(business_state_b)
business_path_c_d.setPredecessorValue(business_state_c)
business_path_d_e.setPredecessorValue(business_state_d)
business_path_a_b.setSuccessorValue(business_state_b)
business_path_b_c.setSuccessorValue(business_state_c)
business_path_b_d.setSuccessorValue(business_state_d)
business_path_c_d.setSuccessorValue(business_state_d)
business_path_d_e.setSuccessorValue(business_state_e)
business_process.edit(referential_date='stop_date')
business_state_a.edit(title='a')
business_state_b.edit(title='b')
business_state_c.edit(title='c')
business_state_d.edit(title='d')
business_state_e.edit(title='e')
business_path_a_b.edit(title='a_b', lead_time=2, wait_time=1)
business_path_b_c.edit(title='b_c', lead_time=2, wait_time=1)
business_path_b_d.edit(title='b_d', lead_time=3, wait_time=1)
business_path_c_d.edit(title='c_d', lead_time=3, wait_time=0)
business_path_d_e.edit(title='d_e', lead_time=4, wait_time=2)
# root explanation
business_path_b_d.edit(deliverable=True)
self.stepTic()
"""
Basic test, lead time of reality and simulation are consistent.
"""
class Mock:
def __init__(self, date):
self.date = date
def getStartDate(self):
return self.date
def getStopDate(self):
return self.date + 3 # lead time of reality
base_date = DateTime('2009/04/01 GMT+9')
mock = Mock(base_date)
# root explanation.
self.assertEquals(business_path_b_d.getExpectedStartDate(mock), DateTime('2009/04/01 GMT+9'))
self.assertEquals(business_path_b_d.getExpectedStopDate(mock), DateTime('2009/04/04 GMT+9'))
# assertion for each path without root explanation.
self.assertEquals(business_path_a_b.getExpectedStartDate(mock), DateTime('2009/03/27 GMT+9'))
self.assertEquals(business_path_a_b.getExpectedStopDate(mock), DateTime('2009/03/29 GMT+9'))
self.assertEquals(business_path_b_c.getExpectedStartDate(mock), DateTime('2009/03/30 GMT+9'))
self.assertEquals(business_path_b_c.getExpectedStopDate(mock), DateTime('2009/04/01 GMT+9'))
self.assertEquals(business_path_c_d.getExpectedStartDate(mock), DateTime('2009/04/01 GMT+9'))
self.assertEquals(business_path_c_d.getExpectedStopDate(mock), DateTime('2009/04/04 GMT+9'))
self.assertEquals(business_path_d_e.getExpectedStartDate(mock), DateTime('2009/04/06 GMT+9'))
self.assertEquals(business_path_d_e.getExpectedStopDate(mock), DateTime('2009/04/10 GMT+9'))
"""
Test of illegal case, lead time of reality and simulation are inconsistent,
always reality is taken, but it depends on which date(e.g. start_date and stop_date) is referential.
How we know which is referential, currently implementation of it can be known by
BusinessProcess.isStartDateReferential and BusinessProcess.isStopDateReferential.
In this test case, stop_date on business_path_b_d is referential, because business_path_b_d is
root explanation and business_process refer to stop_date as referential.
calculation example(when referential date is 2009/04/06 GMT+9):
start_date of business_path_b_d = referential_date - 3(lead_time of business_path_b_d)
= 2009/04/06 GMT+9 - 3
= 2009/04/03 GMT+9
"""
class Mock:
def __init__(self, date):
self.date = date
def getStartDate(self):
return self.date
def getStopDate(self):
return self.date + 5 # changed
base_date = DateTime('2009/04/01 GMT+9')
mock = Mock(base_date)
self.assertEquals(business_path_b_d.getExpectedStartDate(mock), DateTime('2009/04/03 GMT+9'))
# This is base in this context, because referential_date is 'stop_date'
self.assertEquals(business_path_b_d.getExpectedStopDate(mock), DateTime('2009/04/06 GMT+9'))
# assertion for each path without root explanation.
self.assertEquals(business_path_a_b.getExpectedStartDate(mock), DateTime('2009/03/29 GMT+9'))
self.assertEquals(business_path_a_b.getExpectedStopDate(mock), DateTime('2009/03/31 GMT+9'))
self.assertEquals(business_path_b_c.getExpectedStartDate(mock), DateTime('2009/04/01 GMT+9'))
self.assertEquals(business_path_b_c.getExpectedStopDate(mock), DateTime('2009/04/03 GMT+9'))
self.assertEquals(business_path_c_d.getExpectedStartDate(mock), DateTime('2009/04/03 GMT+9'))
self.assertEquals(business_path_c_d.getExpectedStopDate(mock), DateTime('2009/04/06 GMT+9'))
self.assertEquals(business_path_d_e.getExpectedStartDate(mock), DateTime('2009/04/08 GMT+9'))
self.assertEquals(business_path_d_e.getExpectedStopDate(mock), DateTime('2009/04/12 GMT+9'))
def testBPMCopyAndPaste(self):
business_process = self.createBusinessProcess()
state = business_process.newContent(
portal_type=self.business_state_portal_type)
path = business_process.newContent(
portal_type=self.business_path_portal_type, predecessor_value=state,
successor_value=state)
transaction.commit()
self.tic()
pasted_business_process = business_process.Base_createCloneDocument(
batch_mode=1)
transaction.commit()
self.tic()
pasted_path = pasted_business_process.contentValues(
portal_type=self.business_path_portal_type)[0]
pasted_state = pasted_business_process.contentValues(
portal_type=self.business_state_portal_type)[0]
self.assertEqual(pasted_state, pasted_path.getSuccessorValue())
self.assertEqual(pasted_state, pasted_path.getPredecessorValue())
class TestBPMDummyDeliveryMovementMixin(TestBPMMixin):
def _createDelivery(self, **kw):
return self.folder.newContent(portal_type='Dummy Delivery', **kw)
def _createMovement(self, delivery, **kw):
return delivery.newContent(portal_type='Dummy Movement', **kw)
def getBusinessTemplateList(self):
return TestBPMMixin.getBusinessTemplateList(self) \
+ ('erp5_dummy_movement', )
def afterSetUp(self):
TestBPMMixin.afterSetUp(self)
if not hasattr(self.portal, 'testing_folder'):
self.portal.newContent(portal_type='Folder',
id='testing_folder')
self.folder = self.portal.testing_folder
self.stepTic()
def beforeTearDown(self):
TestBPMMixin.beforeTearDown(self)
self.portal.deleteContent(id='testing_folder')
self.stepTic()
completed_state = 'delivered'
frozen_state = 'confirmed'
completed_state_list = [completed_state, frozen_state]
frozen_state_list = [frozen_state]
def _createOrderedDeliveredInvoicedBusinessProcess(self):
# simple business process preparation
business_process = self.createBusinessProcess()
ordered = self.createBusinessState(business_process)
delivered = self.createBusinessState(business_process)
invoiced = self.createBusinessState(business_process)
# path which is completed, as soon as related simulation movements are in
# proper state
self.order_path = self.createBusinessPath(business_process,
successor_value = ordered,
trade_phase='default/order',
completed_state_list = self.completed_state_list,
frozen_state_list = self.frozen_state_list)
self.delivery_path = self.createBusinessPath(business_process,
predecessor_value = ordered, successor_value = delivered,
trade_phase='default/delivery',
completed_state_list = self.completed_state_list,
frozen_state_list = self.frozen_state_list)
self.invoice_path = self.createBusinessPath(business_process,
predecessor_value = delivered, successor_value = invoiced,
trade_phase='default/invoicing')
self.stepTic()
def _createOrderedInvoicedDeliveredBusinessProcess(self):
business_process = self.createBusinessProcess()
ordered = self.createBusinessState(business_process)
delivered = self.createBusinessState(business_process)
invoiced = self.createBusinessState(business_process)
self.order_path = self.createBusinessPath(business_process,
successor_value = ordered,
trade_phase='default/order',
completed_state_list = self.completed_state_list,
frozen_state_list = self.frozen_state_list)
self.invoice_path = self.createBusinessPath(business_process,
predecessor_value = ordered, successor_value = invoiced,
trade_phase='default/invoicing',
completed_state_list = self.completed_state_list,
frozen_state_list = self.frozen_state_list)
self.delivery_path = self.createBusinessPath(business_process,
predecessor_value = invoiced, successor_value = delivered,
trade_phase='default/delivery')
self.stepTic()
class TestBPMisBuildableImplementation(TestBPMDummyDeliveryMovementMixin):
def test_isBuildable_OrderedDeliveredInvoiced(self):
"""Test isBuildable for ordered, delivered and invoiced sequence
Here Business Process sequence corresponds simulation tree.
delivery_path is related to root applied rule, and invoice_path is related
to rule below, and invoice_path is after delivery_path
"""
self._createOrderedDeliveredInvoicedBusinessProcess()
# create order and order line to have starting point for business process
order = self._createDelivery()
order_line = self._createMovement(order)
# first level rule with simulation movement
applied_rule = self.portal.portal_simulation.newContent(
portal_type='Applied Rule', causality_value=order)
simulation_movement = applied_rule.newContent(
portal_type = 'Simulation Movement',
delivery_value = order_line,
causality_value = self.order_path
)
# second level rule with simulation movement
delivery_rule = simulation_movement.newContent(
portal_type='Applied Rule')
delivery_simulation_movement = delivery_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.delivery_path)
# third level rule with simulation movement
invoicing_rule = delivery_simulation_movement.newContent(
portal_type='Applied Rule')
invoicing_simulation_movement = invoicing_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.invoice_path)
# split simulation movement for first level applied rule
split_simulation_movement = applied_rule.newContent(
portal_type = 'Simulation Movement', delivery_value = order_line,
causality_value = self.order_path)
# second level rule with simulation movement for split parent movement
split_delivery_rule = split_simulation_movement.newContent(
portal_type='Applied Rule')
split_delivery_simulation_movement = split_delivery_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.delivery_path)
# third level rule with simulation movement for split parent movement
split_invoicing_rule = split_delivery_simulation_movement.newContent(
portal_type='Applied Rule')
split_invoicing_simulation_movement = split_invoicing_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.invoice_path)
order.setSimulationState(self.completed_state)
self.stepTic()
# in the beginning only order related movements shall be buildable
self.assertEquals(self.delivery_path.isBuildable(order), True)
self.assertEquals(delivery_simulation_movement.isBuildable(), True)
self.assertEquals(split_delivery_simulation_movement.isBuildable(), True)
self.assertEquals(self.invoice_path.isBuildable(order), False)
self.assertEquals(invoicing_simulation_movement.isBuildable(), False)
self.assertEquals(split_invoicing_simulation_movement.isBuildable(),
False)
# add delivery
delivery = self._createDelivery(causality_value = order)
delivery_line = self._createMovement(delivery)
# relate not split movement with delivery (deliver it)
delivery_simulation_movement.edit(delivery_value = delivery_line)
self.stepTic()
# delivery_path (for order) is still buildable, as split movement is not
# delivered yet
#
# invoice_path is not yet buildable, delivery is in inproper simulation
# state
#
# delivery_path (for delivery) is not buildable - delivery is already
# built for those movements
self.assertEquals(self.delivery_path.isBuildable(order), True)
self.assertEquals(split_delivery_simulation_movement.isBuildable(), True)
self.assertEquals(self.delivery_path.isBuildable(delivery), False)
self.assertEquals(self.invoice_path.isBuildable(delivery), False)
self.assertEquals(delivery_simulation_movement.isBuildable(), False)
self.assertEquals(invoicing_simulation_movement.isBuildable(), False)
self.assertEquals(self.invoice_path.isBuildable(order), False)
self.assertEquals(split_invoicing_simulation_movement.isBuildable(),
False)
# put delivery in simulation state configured on path (and this state is
# available directly on movements)
delivery.setSimulationState(self.completed_state)
self.assertEqual(self.completed_state, delivery.getSimulationState())
self.stepTic()
# delivery_path (for order) is still buildable, as split movement is not
# delivered yet
#
# invoicing_path (for delivery and order) is buildable - in case of order,
# because part of tree is buildable
#
# split movement for invoicing is not buildable - no proper delivery
# related for previous path
self.assertEquals(self.delivery_path.isBuildable(order), True)
self.assertEquals(invoicing_simulation_movement.isBuildable(), True)
self.assertEquals(self.invoice_path.isBuildable(delivery), True)
# XXX look at comments in BusinessPath.isBuildable
self.assertEquals(self.invoice_path.isBuildable(order), True)
self.assertEquals(self.delivery_path.isBuildable(delivery), False)
self.assertEquals(delivery_simulation_movement.isBuildable(), False)
self.assertEquals(split_invoicing_simulation_movement.isBuildable(),
False)
def test_isBuildable_OrderedInvoicedDelivered(self):
"""Test isBuildable for ordered, invoiced and delivered sequence
Here Business Process sequence do not corresponds simulation tree.
delivery_path is related to root applied rule, and invoice_path is related
to rule below, but invoice_path is before delivery_path in seuqence.
"""
self._createOrderedInvoicedDeliveredBusinessProcess()
order = self._createDelivery()
order_line = self._createMovement(order)
applied_rule = self.portal.portal_simulation.newContent(
portal_type='Applied Rule', causality_value=order)
simulation_movement = applied_rule.newContent(
portal_type = 'Simulation Movement',
delivery_value = order_line,
causality_value = self.order_path
)
delivery_rule = simulation_movement.newContent(
portal_type='Applied Rule')
delivery_simulation_movement = delivery_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.delivery_path)
invoicing_rule = delivery_simulation_movement.newContent(
portal_type='Applied Rule')
invoicing_simulation_movement = invoicing_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.invoice_path)
order.setSimulationState(self.completed_state)
self.stepTic()
self.assertEquals(self.delivery_path.isBuildable(order), False)
self.assertEquals(delivery_simulation_movement.isBuildable(), False)
self.assertEquals(self.invoice_path.isBuildable(order), True)
self.assertEquals(invoicing_simulation_movement.isBuildable(), True)
delivery = self._createDelivery(causality_value = order)
delivery_line = self._createMovement(delivery)
invoicing_simulation_movement.edit(delivery_value = delivery_line)
self.stepTic()
self.assertEquals(self.delivery_path.isBuildable(order), False)
self.assertEquals(self.delivery_path.isBuildable(delivery), False)
self.assertEquals(self.invoice_path.isBuildable(delivery), False)
self.assertEquals(delivery_simulation_movement.isBuildable(), False)
self.assertEquals(invoicing_simulation_movement.isBuildable(), False)
self.assertEquals(self.invoice_path.isBuildable(order), False)
# put delivery in simulation state configured on path (and this state is
# available directly on movements)
delivery.setSimulationState(self.completed_state)
self.assertEqual(self.completed_state, delivery.getSimulationState())
self.stepTic()
self.assertEquals(self.delivery_path.isBuildable(order), True)
self.assertEquals(self.delivery_path.isBuildable(delivery), True)
self.assertEquals(invoicing_simulation_movement.isBuildable(), False)
self.assertEquals(self.invoice_path.isBuildable(delivery), False)
self.assertEquals(self.invoice_path.isBuildable(order), False)
self.assertEquals(delivery_simulation_movement.isBuildable(), True)
# now simulate compensation
compensated_simulation_movement = delivery_rule.newContent(
portal_type = 'Simulation Movement',
delivery_value = order_line,
causality_value = self.delivery_path
)
compensated_invoicing_rule = compensated_simulation_movement.newContent(
portal_type='Applied Rule')
compensated_invoicing_simulation_movement = compensated_invoicing_rule \
.newContent(portal_type='Simulation Movement',
causality_value = self.invoice_path)
# and delivery some part of tree
another_delivery = self._createDelivery(causality_value = delivery)
another_delivery_line = self._createMovement(another_delivery)
delivery_simulation_movement.edit(delivery_value=another_delivery_line)
self.stepTic()
self.assertEquals(self.delivery_path.isBuildable(order), False)
self.assertEquals(delivery_simulation_movement.isBuildable(), False)
self.assertEquals(invoicing_simulation_movement.isBuildable(), False)
self.assertEquals(self.invoice_path.isBuildable(order), True)
self.assertEquals(compensated_invoicing_simulation_movement.isBuildable(),
True)
self.assertEquals(compensated_simulation_movement.isBuildable(), False)
class TestBPMisCompletedImplementation(TestBPMDummyDeliveryMovementMixin):
def test_isCompleted_OrderedDeliveredInvoiced(self):
"""Test isCompleted for ordered, delivered and invoiced sequence"""
self._createOrderedDeliveredInvoicedBusinessProcess()
# create order and order line to have starting point for business process
order = self._createDelivery()
order_line = self._createMovement(order)
# first level rule with simulation movement
applied_rule = self.portal.portal_simulation.newContent(
portal_type='Applied Rule', causality_value=order)
simulation_movement = applied_rule.newContent(
portal_type = 'Simulation Movement',
delivery_value = order_line,
causality_value = self.order_path
)
# second level rule with simulation movement
delivery_rule = simulation_movement.newContent(
portal_type='Applied Rule')
delivery_simulation_movement = delivery_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.delivery_path)
# third level rule with simulation movement
invoicing_rule = delivery_simulation_movement.newContent(
portal_type='Applied Rule')
invoicing_simulation_movement = invoicing_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.invoice_path)
# split simulation movement for first level applied rule
split_simulation_movement = applied_rule.newContent(
portal_type = 'Simulation Movement', delivery_value = order_line,
causality_value = self.order_path)
# second level rule with simulation movement for split parent movement
split_delivery_rule = split_simulation_movement.newContent(
portal_type='Applied Rule')
split_delivery_simulation_movement = split_delivery_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.delivery_path)
# third level rule with simulation movement for split parent movement
split_invoicing_rule = split_delivery_simulation_movement.newContent(
portal_type='Applied Rule')
split_invoicing_simulation_movement = split_invoicing_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.invoice_path)
self.stepTic()
self.assertEqual(self.delivery_path.isCompleted(order), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(order), False)
self.assertEqual(self.invoice_path.isCompleted(order), False)
self.assertEqual(self.invoice_path.isPartiallyCompleted(order), False)
# add delivery
delivery = self._createDelivery(causality_value = order)
delivery_line = self._createMovement(delivery)
# relate not split movement with delivery (deliver it)
delivery_simulation_movement.edit(delivery_value = delivery_line)
self.stepTic()
# nothing changes
self.assertEqual(self.delivery_path.isCompleted(order), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(order), False)
self.assertEqual(self.invoice_path.isCompleted(order), False)
self.assertEqual(self.invoice_path.isPartiallyCompleted(order), False)
# from delivery point of view everything is same
self.assertEqual(self.delivery_path.isCompleted(delivery), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(delivery), False)
self.assertEqual(self.invoice_path.isCompleted(delivery), False)
self.assertEqual(self.invoice_path.isPartiallyCompleted(delivery), False)
# put delivery in simulation state configured on path (and this state is
# available directly on movements)
delivery.setSimulationState(self.completed_state)
self.assertEqual(self.completed_state, delivery.getSimulationState())
self.stepTic()
self.assertEqual(self.delivery_path.isCompleted(order), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(order), True)
self.assertEqual(self.invoice_path.isCompleted(order), False)
self.assertEqual(self.invoice_path.isPartiallyCompleted(order), False)
self.assertEqual(self.delivery_path.isCompleted(delivery), True)
self.assertEqual(self.delivery_path.isPartiallyCompleted(delivery), True)
self.assertEqual(self.invoice_path.isCompleted(delivery), False)
self.assertEqual(self.invoice_path.isPartiallyCompleted(delivery), False)
def test_isCompleted_OrderedInvoicedDelivered(self):
"""Test isCompleted for ordered, invoiced and invoiced sequence"""
self._createOrderedInvoicedDeliveredBusinessProcess()
order = self._createDelivery()
order_line = self._createMovement(order)
applied_rule = self.portal.portal_simulation.newContent(
portal_type='Applied Rule', causality_value=order)
simulation_movement = applied_rule.newContent(
portal_type = 'Simulation Movement',
delivery_value = order_line,
causality_value = self.delivery_path
)
delivery_rule = simulation_movement.newContent(
portal_type='Applied Rule')
delivery_simulation_movement = delivery_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.delivery_path)
invoicing_rule = delivery_simulation_movement.newContent(
portal_type='Applied Rule')
invoicing_simulation_movement = invoicing_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.invoice_path)
self.stepTic()
self.assertEqual(self.delivery_path.isCompleted(order), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(order), False)
self.assertEqual(self.invoice_path.isCompleted(order), False)
self.assertEqual(self.invoice_path.isPartiallyCompleted(order), False)
delivery = self._createDelivery(causality_value = order)
delivery_line = self._createMovement(delivery)
invoicing_simulation_movement.edit(delivery_value = delivery_line)
self.stepTic()
self.assertEqual(self.delivery_path.isCompleted(order), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(order), False)
self.assertEqual(self.invoice_path.isCompleted(order), False)
self.assertEqual(self.invoice_path.isPartiallyCompleted(order), False)
self.assertEqual(self.delivery_path.isCompleted(delivery), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(delivery), False)
self.assertEqual(self.invoice_path.isCompleted(delivery), False)
self.assertEqual(self.invoice_path.isPartiallyCompleted(delivery), False)
# put delivery in simulation state configured on path (and this state is
# available directly on movements)
delivery.setSimulationState(self.completed_state)
self.assertEqual(self.completed_state, delivery.getSimulationState())
self.stepTic()
self.assertEqual(self.delivery_path.isCompleted(order), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(order), False)
self.assertEqual(self.invoice_path.isCompleted(order), True)
self.assertEqual(self.invoice_path.isPartiallyCompleted(order), True)
self.assertEqual(self.delivery_path.isCompleted(delivery), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(delivery), False)
self.assertEqual(self.invoice_path.isCompleted(delivery), True)
self.assertEqual(self.invoice_path.isPartiallyCompleted(delivery), True)
# now simulate compensation
compensated_simulation_movement = delivery_rule.newContent(
portal_type = 'Simulation Movement',
delivery_value = order_line,
causality_value = self.delivery_path
)
compensated_invoicing_rule = compensated_simulation_movement.newContent(
portal_type='Applied Rule')
compensated_invoicing_simulation_movement = compensated_invoicing_rule \
.newContent(portal_type='Simulation Movement',
causality_value = self.invoice_path)
# and delivery some part of tree
another_delivery = self._createDelivery(causality_value = delivery)
another_delivery_line = self._createMovement(another_delivery)
delivery_simulation_movement.edit(delivery_value=another_delivery_line)
self.stepTic()
self.assertEqual(self.delivery_path.isCompleted(order), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(order), False)
self.assertEqual(self.invoice_path.isCompleted(order), False)
self.assertEqual(self.invoice_path.isPartiallyCompleted(order), True)
self.assertEqual(self.delivery_path.isCompleted(delivery), False)
self.assertEqual(self.delivery_path.isPartiallyCompleted(delivery), False)
self.assertEqual(self.invoice_path.isCompleted(delivery), True)
self.assertEqual(self.invoice_path.isPartiallyCompleted(delivery), True)
class TestBPMisFrozenImplementation(TestBPMDummyDeliveryMovementMixin):
def test_isFrozen_OrderedDeliveredInvoiced(self):
"""Test isFrozen for ordered, delivered and invoiced sequence"""
self._createOrderedDeliveredInvoicedBusinessProcess()
# create order and order line to have starting point for business process
order = self._createDelivery()
order_line = self._createMovement(order)
# first level rule with simulation movement
applied_rule = self.portal.portal_simulation.newContent(
portal_type='Applied Rule', causality_value=order)
simulation_movement = applied_rule.newContent(
portal_type = 'Simulation Movement',
delivery_value = order_line,
causality_value = self.delivery_path
)
# second level rule with simulation movement
delivery_rule = simulation_movement.newContent(
portal_type='Applied Rule')
delivery_simulation_movement = delivery_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.delivery_path)
# third level rule with simulation movement
invoicing_rule = delivery_simulation_movement.newContent(
portal_type='Applied Rule')
invoicing_simulation_movement = invoicing_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.invoice_path)
# split simulation movement for first level applied rule
split_simulation_movement = applied_rule.newContent(
portal_type = 'Simulation Movement', delivery_value = order_line,
causality_value = self.order_path)
# second level rule with simulation movement for split parent movement
split_delivery_rule = split_simulation_movement.newContent(
portal_type='Applied Rule')
split_delivery_simulation_movement = split_delivery_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.delivery_path)
# third level rule with simulation movement for split parent movement
split_invoicing_rule = split_delivery_simulation_movement.newContent(
portal_type='Applied Rule')
split_invoicing_simulation_movement = split_invoicing_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.invoice_path)
self.stepTic()
self.assertEqual(self.delivery_path.isFrozen(order), False)
self.assertEqual(self.invoice_path.isFrozen(order), False)
self.assertEqual(simulation_movement.isFrozen(), False)
self.assertEqual(invoicing_simulation_movement.isFrozen(), False)
self.assertEqual(split_simulation_movement.isFrozen(), False)
self.assertEqual(split_invoicing_simulation_movement.isFrozen(), False)
# add delivery
delivery = self._createDelivery(causality_value = order)
delivery_line = self._createMovement(delivery)
# relate not split movement with delivery (deliver it)
delivery_simulation_movement.edit(delivery_value = delivery_line)
self.stepTic()
# nothing changes
self.assertEqual(self.delivery_path.isFrozen(order), False)
self.assertEqual(self.invoice_path.isFrozen(order), False)
# from delivery point of view everything is same
self.assertEqual(self.delivery_path.isFrozen(delivery), False)
self.assertEqual(self.invoice_path.isFrozen(delivery), False)
self.assertEqual(simulation_movement.isFrozen(), False)
self.assertEqual(invoicing_simulation_movement.isFrozen(), False)
self.assertEqual(split_simulation_movement.isFrozen(), False)
self.assertEqual(split_invoicing_simulation_movement.isFrozen(), False)
# put delivery in simulation state configured on path (and this state is
# available directly on movements)
delivery.setSimulationState(self.frozen_state)
self.assertEqual(self.frozen_state, delivery.getSimulationState())
self.stepTic()
self.assertEqual(self.delivery_path.isFrozen(order), False)
self.assertEqual(self.invoice_path.isFrozen(order), False)
self.assertEqual(self.delivery_path.isFrozen(delivery), False)
self.assertEqual(self.invoice_path.isFrozen(delivery), False)
self.assertEqual(delivery_simulation_movement.isFrozen(), True)
self.assertEqual(invoicing_simulation_movement.isFrozen(), False)
self.assertEqual(split_simulation_movement.isFrozen(), False)
self.assertEqual(split_invoicing_simulation_movement.isFrozen(), False)
def test_isFrozen_OrderedInvoicedDelivered(self):
"""Test isFrozen for ordered, invoiced and invoiced sequence"""
self._createOrderedInvoicedDeliveredBusinessProcess()
order = self._createDelivery()
order_line = self._createMovement(order)
applied_rule = self.portal.portal_simulation.newContent(
portal_type='Applied Rule', causality_value=order)
simulation_movement = applied_rule.newContent(
portal_type = 'Simulation Movement',
delivery_value = order_line,
causality_value = self.delivery_path
)
delivery_rule = simulation_movement.newContent(
portal_type='Applied Rule')
delivery_simulation_movement = delivery_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.delivery_path)
invoicing_rule = delivery_simulation_movement.newContent(
portal_type='Applied Rule')
invoicing_simulation_movement = invoicing_rule.newContent(
portal_type='Simulation Movement',
causality_value = self.invoice_path)
self.stepTic()
self.assertEqual(self.delivery_path.isFrozen(order), False)
self.assertEqual(self.invoice_path.isFrozen(order), False)
self.assertEqual(simulation_movement.isFrozen(), False)
self.assertEqual(invoicing_simulation_movement.isFrozen(), False)
delivery = self._createDelivery(causality_value = order)
delivery_line = self._createMovement(delivery)
invoicing_simulation_movement.edit(delivery_value = delivery_line)
self.stepTic()
self.assertEqual(self.delivery_path.isFrozen(order), False)
self.assertEqual(self.invoice_path.isFrozen(order), False)
self.assertEqual(self.delivery_path.isFrozen(delivery), False)
self.assertEqual(self.invoice_path.isFrozen(delivery), False)
self.assertEqual(simulation_movement.isFrozen(), False)
self.assertEqual(invoicing_simulation_movement.isFrozen(), False)
# put delivery in simulation state configured on path (and this state is
# available directly on movements)
delivery.setSimulationState(self.frozen_state)
self.assertEqual(self.frozen_state, delivery.getSimulationState())
self.stepTic()
self.assertEqual(self.delivery_path.isFrozen(order), False)
self.assertEqual(self.invoice_path.isFrozen(order), True)
self.assertEqual(self.delivery_path.isFrozen(delivery), False)
self.assertEqual(self.invoice_path.isFrozen(delivery), True)
self.assertEqual(simulation_movement.isFrozen(), False)
self.assertEqual(invoicing_simulation_movement.isFrozen(), True)
# now simulate compensation
compensated_simulation_movement = delivery_rule.newContent(
portal_type = 'Simulation Movement',
delivery_value = order_line,
causality_value = self.delivery_path
)
compensated_invoicing_rule = compensated_simulation_movement.newContent(
portal_type='Applied Rule')
compensated_invoicing_simulation_movement = compensated_invoicing_rule \
.newContent(portal_type='Simulation Movement',
causality_value = self.invoice_path)
# and delivery some part of tree
another_delivery = self._createDelivery(causality_value = delivery)
another_delivery_line = self._createMovement(another_delivery)
delivery_simulation_movement.edit(delivery_value=another_delivery_line)
self.stepTic()
self.assertEqual(self.delivery_path.isFrozen(order), False)
self.assertEqual(self.invoice_path.isFrozen(order), False)
self.assertEqual(self.delivery_path.isFrozen(delivery), False)
self.assertEqual(self.invoice_path.isFrozen(delivery), True)
self.assertEqual(simulation_movement.isFrozen(), False)
self.assertEqual(invoicing_simulation_movement.isFrozen(), True)
self.assertEqual(compensated_simulation_movement.isFrozen(), False)
self.assertEqual(compensated_invoicing_simulation_movement.isFrozen(),
False)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestBPMImplementation))
suite.addTest(unittest.makeSuite(TestBPMisBuildableImplementation))
suite.addTest(unittest.makeSuite(TestBPMisCompletedImplementation))
suite.addTest(unittest.makeSuite(TestBPMisFrozenImplementation))
return suite
# -*- coding: utf-8 -*-
##############################################################################
# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
# Łukasz Nowak <luke@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 advised 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.
#
##############################################################################
"""
This is BPM Evaluation Test class using erp5_bpm development Business Template
Generally it tries to use two Business Processes - one with sequence very
similar to normal ERP5 - TestBPMEvaluationDefaultProcessMixin, second one
inverted - TestBPMEvaluationDifferentProcessMixin.
It uses only Sale path to demonstrate BPM.
It is advised to *NOT* remove erp5_administration.
"""
import unittest
import transaction
from Products.ERP5Legacy.tests.testLegacyBPMCore import TestBPMMixin
from DateTime import DateTime
class TestBPMEvaluationMixin(TestBPMMixin):
node_portal_type = 'Organisation'
order_portal_type = 'Sale Order'
order_line_portal_type = 'Sale Order Line'
packing_list_portal_type = 'Sale Packing List'
packing_list_line_portal_type = 'Sale Packing List Line'
trade_condition_portal_type = 'Sale Trade Condition'
invoice_portal_type = 'Sale Invoice Transaction'
product_portal_type = 'Product'
order_start_date = DateTime()
order_stop_date = order_start_date + 10
def getBusinessTemplateList(self):
return TestBPMMixin.getBusinessTemplateList(self) + ('erp5_bpm',
'erp5_administration', 'erp5_simulation',)
def afterSetUp(self):
TestBPMMixin.afterSetUp(self)
self._createNodes()
self._createBusinessProcess()
self._createTradeCondition()
self._createRootDocument()
self._setUpRules()
self.stepTic()
def _setUpRules(self):
"""Setups rules
Rules are part of configuration, so anything provided by Business
Templates or previous test runs is ignored - all old rules are invalidated
between tests and new rules are created, configured and validated.
"""
self.rule_tool = self.portal.portal_rules
for rule in self.rule_tool.contentValues():
if rule.getValidationState() == 'validated':
rule.invalidate()
transaction.commit()
self._createOrderRootSimulationRule()
self._createDeliveryRootSimulationRule()
self._createDeliverySimulationRule()
self._createInvoiceSimulationRule()
self._createInvoiceRootSimulationRule()
self._createTradeModelSimulationRule()
def _createTradeRootSimulationRule(self, **kw):
edit_dict = {}
edit_dict.update(
trade_phase = 'default/delivery',
)
edit_dict.update(**kw)
rule = self.rule_tool.newContent(**edit_dict)
# matching providers
for category in ('delivery',):
rule.newContent(
portal_type='Category Membership Divergence Tester',
title='%s divergence tester' % category,
tested_property=category,
divergence_provider=False,
matching_provider=True)
# divergence providers
for category in ('source_section',
'resource',
'destination_section',
'source',
'aggregate'):
rule.newContent(
portal_type='Category Membership Divergence Tester',
title='%s divergence tester' % category,
tested_property=category,
divergence_provider=True,
matching_provider=False)
rule.newContent(
portal_type='Net Converted Quantity Divergence Tester',
title='quantity divergence tester',
tested_property='quantity',
quantity=0,
divergence_provider=True,
matching_provider=False)
for property_id in ('start_date', 'stop_date'):
rule.newContent(
portal_type='DateTime Divergence Tester',
title='%s divergence tester' % property_id,
tested_property=property_id,
quantity=0,
divergence_provider=True,
matching_provider=False)
rule.newContent(
portal_type='Float Divergence Tester',
title='price divergence tester',
tested_property='price',
quantity=0,
divergence_provider=True,
matching_provider=False)
return rule
def _createOrderRootSimulationRule(self):
rule = self._createTradeRootSimulationRule(portal_type='Order Root Simulation Rule',
trade_phase='default/order',
reference='default_order_rule')
rule.validate()
transaction.commit()
def _createDeliveryRootSimulationRule(self):
rule = self._createTradeRootSimulationRule(portal_type='Delivery Root Simulation Rule',
reference='default_delivery_rule')
rule.validate()
transaction.commit()
def _createDeliverySimulationRule(self):
rule = self.rule_tool.newContent(portal_type='Delivery Simulation Rule',
reference='default_delivering_rule',
trade_phase='default/delivery',
test_method_id = ('SimulationMovement_testDeliverySimulationRule',)
)
# matching providers
for category in ('resource',):
rule.newContent(
portal_type='Category Membership Divergence Tester',
title='%s divergence tester' % category,
tested_property=category,
divergence_provider=False,
matching_provider=True)
rule.newContent(
portal_type='Variation Divergence Tester',
title='variation divergence tester',
tested_property='variation_property_dict',
divergence_provider=False,
matching_provider=True)
# divergence providers
for category in ('resource',
'source_section',
'destination_section',
'source',
'source_function',
'destination',
'destination_function',
'source_project',
'destination_project',
'aggregate',
'price_currency',
'base_contribution',
'base_application',
'source_account',
'destination_account',
):
rule.newContent(
portal_type='Category Membership Divergence Tester',
title='%s divergence tester' % category,
tested_property=category,
divergence_provider=True,
matching_provider=False)
rule.newContent(
portal_type='Net Converted Quantity Divergence Tester',
title='quantity divergence tester',
tested_property='quantity',
quantity=0,
divergence_provider=True,
matching_provider=False)
for property_id in ('start_date', 'stop_date'):
rule.newContent(
portal_type='DateTime Divergence Tester',
title='%s divergence tester' % property_id,
tested_property=property_id,
quantity=0,
divergence_provider=True,
matching_provider=False)
rule.newContent(
portal_type='Float Divergence Tester',
title='price divergence tester',
tested_property='price',
quantity=0,
divergence_provider=True,
matching_provider=False)
rule.validate()
transaction.commit()
def _createTradeModelSimulationRule(self):
rule = self.rule_tool.newContent(portal_type='Trade Model Simulation Rule',
reference='default_trade_model_rule',
test_method_id = ('SimulationMovement_testTradeModelSimulationRule',)
)
# matching providers
for category in ('resource',):
rule.newContent(
portal_type='Category Membership Divergence Tester',
title='%s divergence tester' % category,
tested_property=category,
divergence_provider=False,
matching_provider=True)
rule.newContent(
portal_type='Variation Divergence Tester',
title='variation divergence tester',
tested_property='variation_property_dict',
divergence_provider=False,
matching_provider=True)
# divergence providers
for category in ('resource',
'source_section',
'destination_section',
'source',
'source_function',
'destination_function',
'source_project',
'destination_project',
'aggregate',
'price_currency',
'base_contribution',
'base_application',
'source_account',
'destination_account',
):
rule.newContent(
portal_type='Category Membership Divergence Tester',
title='%s divergence tester' % category,
tested_property=category,
divergence_provider=True,
matching_provider=False)
rule.newContent(
portal_type='Net Converted Quantity Divergence Tester',
title='quantity divergence tester',
tested_property='quantity',
quantity=0,
divergence_provider=True,
matching_provider=False)
for property_id in ('start_date', 'stop_date'):
rule.newContent(
portal_type='DateTime Divergence Tester',
title='%s divergence tester' % property_id,
tested_property=property_id,
quantity=0,
divergence_provider=True,
matching_provider=False)
rule.newContent(
portal_type='Float Divergence Tester',
title='price divergence tester',
tested_property='price',
quantity=0,
divergence_provider=True,
matching_provider=False)
rule.validate()
transaction.commit()
def _createInvoiceRootSimulationRule(self):
# Note: This is not used, but invoices, even if built from simulation,
# need those rule to create empty one applied rule
rule_tool = self.portal.portal_rules
clipboard = rule_tool.manage_copyObjects(ids = ['new_invoice_root_simulation_rule'])
pasted = rule_tool.manage_pasteObjects(clipboard)
new_rule = getattr(rule_tool, pasted[0]['new_id'])
new_rule.validate()
transaction.commit()
def _createInvoiceSimulationRule(self):
edit_dict = {}
edit_dict.update(
)
rule = self.rule_tool.newContent(portal_type='Invoice Simulation Rule',
reference='default_invoicing_rule',
trade_phase = 'default/invoicing',
test_method_id = ('SimulationMovement_testInvoiceSimulationRule',)
)
# matching providers
for category in ('resource',):
rule.newContent(
portal_type='Category Membership Divergence Tester',
title='%s divergence tester' % category,
tested_property=category,
divergence_provider=False,
matching_provider=True)
rule.newContent(
portal_type='Variation Divergence Tester',
title='variation divergence tester',
tested_property='variation_property_dict',
divergence_provider=False,
matching_provider=True)
# divergence providers
for category in ('resource',
'source_section',
'destination_section',
'source',
'source_function',
'destination_function',
'source_project',
'destination_project',
'aggregate',
'price_currency',
'base_contribution',
'base_application',
'source_account',
'destination_account',
):
rule.newContent(
portal_type='Category Membership Divergence Tester',
title='%s divergence tester' % category,
tested_property=category,
divergence_provider=True,
matching_provider=False)
rule.newContent(
portal_type='Net Converted Quantity Divergence Tester',
title='quantity divergence tester',
tested_property='quantity',
quantity=0,
divergence_provider=True,
matching_provider=False)
for property_id in ('start_date', 'stop_date'):
rule.newContent(
portal_type='DateTime Divergence Tester',
title='%s divergence tester' % property_id,
tested_property=property_id,
quantity=0,
divergence_provider=True,
matching_provider=False)
rule.newContent(
portal_type='Float Divergence Tester',
title='price divergence tester',
tested_property='price',
quantity=0,
divergence_provider=True,
matching_provider=False)
rule.validate()
transaction.commit()
def _createDocument(self, portal_type, **kw):
module = self.portal.getDefaultModule(portal_type=portal_type)
return module.newContent(portal_type=portal_type, **kw)
def _createProduct(self, **kw):
return self._createDocument(self.product_portal_type, **kw)
def _createNode(self, **kw):
return self._createDocument(self.node_portal_type, **kw)
def _createTradeCondition(self, **kw):
self.trade_condition = self._createDocument(
self.trade_condition_portal_type,
title = self.id(),
specialise_value=self.business_process, **kw)
def _createRootDocumentLine(self, **kw):
return self.root_document.newContent(
portal_type=self.root_document_line_portal_type, **kw)
def _createNodes(self):
self.source, self.source_section = self._createNode(), self._createNode()
self.destination, self.destination_section = self._createNode() \
, self._createNode()
def _createBusinessStateList(self):
"""Creates list of defaults states, set them on self as name_state property"""
for state_name in ('ordered', 'delivered', 'invoiced', 'accounted',
'paid'):
state_document = self.createBusinessState(self.business_process,
title=state_name)
setattr(self,'%s_state' % state_name, state_document)
def _createRootDocument(self):
self.root_document = self._createDocument(self.root_document_portal_type,
source_value = self.source,
source_section_value = self.source_section,
destination_value = self.destination,
destination_section_value = self.destination_section,
start_date = self.order_start_date,
stop_date = self.order_stop_date,
specialise_value = self.trade_condition)
def _checkBPMSimulation(self):
"""Checks BPMised related simumation.
Note: Simulation tree is the same, it is totally independent from
BPM sequence"""
# TODO:
# - gather errors into one list
bpm_root_rule = self.root_document.getCausalityRelatedValue(
portal_type='Applied Rule')
# check that correct root rule applied
self.assertEqual(bpm_root_rule.getSpecialiseValue().getPortalType(),
self.root_rule_portal_type)
root_simulation_movement_list = bpm_root_rule.contentValues()
for root_simulation_movement in root_simulation_movement_list:
self.assertEqual(root_simulation_movement.getPortalType(),
'Simulation Movement')
movement = root_simulation_movement.getDeliveryValue()
property_problem_list = []
# check some properties equality between delivery line and simulation
# movement, gather errors
for property in 'resource', 'price', 'start_date', 'stop_date', \
'source', 'destination', 'source_section', \
'destination_section':
if movement.getProperty(property) != root_simulation_movement \
.getProperty(property):
property_problem_list.append('property %s movement %s '
'simulation %s' % (property, movement.getProperty(property),
root_simulation_movement.getProperty(property)))
if len(property_problem_list) > 0:
self.fail('\n'.join(property_problem_list))
self.assertEqual(
movement.getQuantity() * root_simulation_movement.getDeliveryRatio(),
root_simulation_movement.getQuantity())
# root rule is order or delivery - so below each movement invoicing one
# is expected
self.assertEquals(len(root_simulation_movement.contentValues()), 1)
if self.root_rule_portal_type == 'Order Root Simulation Rule':
delivery_rule = root_simulation_movement.contentValues()[0]
delivery_simulation_movement_list = delivery_rule.contentValues()
self.assertEqual(1, len(delivery_simulation_movement_list))
delivery_simulation_movement = delivery_simulation_movement_list[0]
else:
delivery_simulation_movement = root_simulation_movement
for bpm_invoicing_rule in delivery_simulation_movement.contentValues():
self.assertEqual(bpm_invoicing_rule.getPortalType(), 'Applied Rule')
self.assertEqual(bpm_invoicing_rule.getSpecialiseValue() \
.getPortalType(), 'Invoice Simulation Rule')
# only one movement inside invoicing rule
self.assertEquals(len(bpm_invoicing_rule.contentValues()), 1)
for invoicing_simulation_movement in bpm_invoicing_rule \
.contentValues():
self.assertEqual(invoicing_simulation_movement.getPortalType(),
'Simulation Movement')
self.assertEqual(invoicing_simulation_movement.getCausalityValue(),
self.invoice_path)
property_problem_list = []
# check equality of some properties, gather them
for property in 'resource', 'price', 'start_date', \
'stop_date', 'source', 'destination', 'source_section', \
'destination_section':
if movement.getProperty(property) != \
invoicing_simulation_movement.getProperty(property):
property_problem_list.append('property %s movement %s '
'simulation %s' % (property, movement.getProperty(property),
invoicing_simulation_movement.getProperty(property)))
if len(property_problem_list) > 0:
self.fail('\n'.join(property_problem_list))
self.assertEqual(
movement.getQuantity() * root_simulation_movement.getDeliveryRatio(),
invoicing_simulation_movement.getQuantity())
# simple check for trade model rule existence, without movements,
# as no trade condition configured
self.assertEquals(
len(invoicing_simulation_movement.contentValues()), 1)
for trade_model_rule in invoicing_simulation_movement \
.contentValues():
self.assertEqual(trade_model_rule.getPortalType(), 'Applied Rule')
self.assertEqual(trade_model_rule.getSpecialiseValue() \
.getPortalType(), 'Trade Model Simulation Rule')
self.assertSameSet(trade_model_rule.contentValues(
portal_type='Simulation Movement'), [])
class TestBPMEvaluationDefaultProcessMixin:
def _createBusinessProcess(self):
self.business_process = self.createBusinessProcess(title=self.id(),
referential_date='start_date')
self._createBusinessStateList()
self.order_path = self.createBusinessPath(self.business_process,
successor_value=self.ordered_state,
trade_phase='default/order',
deliverable=1,
completed_state_list=['confirmed'],
frozen_state_list=['confirmed'],
)
self.delivery_path = self.createBusinessPath(self.business_process,
predecessor_value=self.ordered_state,
successor_value=self.delivered_state,
trade_phase='default/delivery',
deliverable=1,
completed_state_list=['started', 'stopped', 'delivered'],
frozen_state_list=['started', 'stopped', 'delivered'],
delivery_builder='portal_deliveries/bpm_sale_packing_list_builder',
)
self.invoice_path = self.createBusinessPath(self.business_process,
predecessor_value=self.delivered_state,
successor_value=self.invoiced_state,
completed_state_list=['delivered'],
frozen_state_list=['stopped', 'delivered'],
delivery_builder='portal_deliveries/bpm_sale_invoice_builder',
trade_phase='default/invoicing')
self.account_path = self.createBusinessPath(self.business_process,
predecessor_value=self.invoiced_state,
successor_value=self.accounted_state,
completed_state_list=['delivered'],
frozen_state_list=['stopped', 'delivered'],
trade_phase='default/accounting')
self.pay_path = self.createBusinessPath(self.business_process,
predecessor_value=self.invoiced_state,
successor_value=self.accounted_state,
completed_state_list=['delivered'],
frozen_state_list=['stopped', 'delivered'],
trade_phase='default/payment')
self.stepTic()
class TestBPMEvaluationDifferentProcessMixin:
def _createBusinessProcess(self):
self.business_process = self.createBusinessProcess(title=self.id(),
referential_date='start_date')
self._createBusinessStateList()
self.order_path = self.createBusinessPath(self.business_process,
successor_value=self.ordered_state,
trade_phase='default/order',
deliverable=1,
completed_state_list=['confirmed'],
frozen_state_list=['confirmed'],
)
self.invoice_path = self.createBusinessPath(self.business_process,
predecessor_value=self.ordered_state,
successor_value=self.invoiced_state,
completed_state_list=['delivered'],
frozen_state_list=['stopped', 'delivered'],
trade_phase='default/invoicing')
self.account_path = self.createBusinessPath(self.business_process,
predecessor_value=self.invoiced_state,
successor_value=self.accounted_state,
completed_state_list=['delivered'],
frozen_state_list=['stopped', 'delivered'],
trade_phase='default/accounting')
self.pay_path = self.createBusinessPath(self.business_process,
predecessor_value=self.accounted_state,
successor_value=self.paid_state,
completed_state_list=['delivered'],
frozen_state_list=['stopped', 'delivered'],
trade_phase='default/payment')
self.delivery_path = self.createBusinessPath(self.business_process,
predecessor_value=self.paid_state,
successor_value=self.delivered_state,
trade_phase='default/delivery',
deliverable=1,
completed_state_list=['delivered'],
frozen_state_list=['stopped', 'delivered'])
self.stepTic()
class GenericRuleTestsMixin:
"""Tests which are generic for BPMised Order, Delivery and Invoice Rule"""
def test_transition(self):
self.order_line = self._createRootDocumentLine(
resource_value = self._createProduct(), quantity = 10, price = 5)
self.stepTic()
self._doFirstTransition(self.root_document)
self.stepTic()
self._checkBPMSimulation()
def _split(self):
"""Invoke manual splitting"""
ratio = .5 # hardcoded value, hopefully float friendly
applied_rule = self.root_document.getCausalityRelatedValue(
portal_type='Applied Rule')
for movement in applied_rule.contentValues(
portal_type='Simulation Movement'):
new_movement = movement.Base_createCloneDocument(batch_mode=1)
old_quantity = movement.getQuantity()
movement.edit(
quantity = old_quantity * ratio
)
new_movement.edit(
quantity = old_quantity * (1 - ratio)
)
self.stepTic()
# recalculate order ratio
for movement in self.root_document.getMovementList():
movement_quantity = movement.getQuantity()
for simulation_movement in movement.getDeliveryRelatedValueList():
new_ratio = simulation_movement.getQuantity() / movement_quantity
simulation_movement.edit(order_ratio = new_ratio)
if simulation_movement.getDelivery() is not None:
simulation_movement.edit(delivery_ratio = new_ratio)
# reexpand
applied_rule.expand()
self.stepTic()
self._checkBPMSimulation()
def test_transition_split(self):
self.order_line = self._createRootDocumentLine(
resource_value = self._createProduct(), quantity = 10, price = 5)
self.stepTic()
self._doFirstTransition(self.root_document)
self.stepTic()
self._checkBPMSimulation()
self._split()
# expand
self.root_document.edit(title = self.root_document.getTitle() + 'a')
self.stepTic()
self._checkBPMSimulation()
def test_transition_split_line_add(self):
self.test_transition_split()
self.order_line_2 = self._createRootDocumentLine(
resource_value = self._createProduct(), quantity = 4, price = 2)
self.stepTic()
self._checkBPMSimulation()
def test_transition_split_line_add_split(self):
self.test_transition_split_line_add()
# second split
self._split()
# expand
self.root_document.edit(title = self.root_document.getTitle() + 'a')
self.stepTic()
self._checkBPMSimulation()
def test_transition_line_edit(self):
self.test_transition()
self.order_line.edit(quantity = 8, price = 6)
self.stepTic()
self._checkBPMSimulation()
def test_transition_line_edit_add(self):
self.test_transition_line_edit()
self.order_line_2 = self._createRootDocumentLine(
resource_value = self._createProduct(), quantity = 4, price = 2)
self.stepTic()
self._checkBPMSimulation()
def test_transition_line_edit_add_many_transactions(self):
self.test_transition_line_edit()
self.order_line_9 = self._createRootDocumentLine()
self.stepTic()
self._checkBPMSimulation()
self.order_line_9.edit(resource_value = self._createProduct())
self.stepTic()
self._checkBPMSimulation()
self.order_line_9.edit(quantity = 1)
self.stepTic()
self._checkBPMSimulation()
self.order_line_9.edit(price = 33)
self.stepTic()
self._checkBPMSimulation()
self.order_line_9.edit(resource_value = self._createProduct())
self.stepTic()
self._checkBPMSimulation()
def test_transition_line_edit_add_same_resource(self):
self.test_transition_line_edit()
resource = self.order_line.getResourceValue()
self.order_line_10 = self._createRootDocumentLine(
resource_value = resource, quantity = 9, price = 2)
self.stepTic()
self._checkBPMSimulation()
def test_transition_line_edit_add_same_resource_edit_again(self):
self.test_transition_line_edit_add_same_resource()
self.root_document.edit(title = self.root_document.getTitle() + 'a' )
self.stepTic()
self._checkBPMSimulation()
class TestOrder(TestBPMEvaluationMixin, GenericRuleTestsMixin):
"""Check BPMised Order Rule behaviour"""
root_document_portal_type = 'Sale Order'
root_document_line_portal_type = 'Sale Order Line'
root_rule_portal_type = 'Order Root Simulation Rule'
def _doFirstTransition(self, document):
document.plan()
def test_confirming(self):
self.order_line = self._createRootDocumentLine(
resource_value = self._createProduct(), quantity = 10, price = 5)
self.stepTic()
self.root_document.confirm()
self.stepTic()
self._checkBPMSimulation()
self.assertEqual(
2,
len(self.root_document.getCausalityRelatedList())
)
self.assertEqual(
'Applied Rule',
self.root_document.getCausalityRelatedValue(
portal_type='Applied Rule').getPortalType()
)
self.assertEqual(
self.packing_list_portal_type,
self.root_document.getCausalityRelatedValue(
portal_type=self.packing_list_portal_type).getPortalType()
)
class TestPackingList(TestBPMEvaluationMixin, GenericRuleTestsMixin):
"""Check BPM Delivery Rule behaviour"""
root_document_portal_type = 'Sale Packing List'
root_document_line_portal_type = 'Sale Packing List Line'
root_rule_portal_type = 'Delivery Root Simulation Rule'
def _packDelivery(self):
"""Packs delivery fully, removes possible containers before"""
self.root_document.deleteContent(self.root_document.contentIds(
filter={'portal_type':'Container'}))
cont = self.root_document.newContent(portal_type='Container')
for movement in self.root_document.getMovementList():
cont.newContent(portal_type='Container Line',
resource = movement.getResource(), quantity = movement.getQuantity())
self.stepTic()
self._checkBPMSimulation()
def _doFirstTransition(self, document):
document.confirm()
def test_starting(self):
self.delivery_line = self._createRootDocumentLine(
resource_value = self._createProduct(), quantity = 10, price = 5)
self.stepTic()
self.root_document.confirm()
self.stepTic()
self._checkBPMSimulation()
self._packDelivery()
self.root_document.start()
self.stepTic()
self._checkBPMSimulation()
self.assertEqual(
2,
len(self.root_document.getCausalityRelatedList())
)
self.assertEqual(
'Applied Rule',
self.root_document.getCausalityRelatedValue(
portal_type='Applied Rule').getPortalType()
)
self.assertEqual(
self.invoice_portal_type,
self.root_document.getCausalityRelatedValue(
portal_type=self.invoice_portal_type).getPortalType()
)
class TestInvoice(TestBPMEvaluationMixin, GenericRuleTestsMixin):
"""Check BPM Invoice Rule behaviour"""
# not implemented yet
pass
class TestOrderDefaultProcess(TestOrder,
TestBPMEvaluationDefaultProcessMixin):
pass
class TestPackingListDefaultProcess(TestPackingList,
TestBPMEvaluationDefaultProcessMixin):
pass
class TestInvoiceDefaultProcess(TestInvoice,
TestBPMEvaluationDefaultProcessMixin):
pass
class TestOrderDifferentProcess(TestOrder,
TestBPMEvaluationDifferentProcessMixin):
def test_confirming(self):
# in current BPM configuration nothing shall be built
# as soon as test business process will be finished, it shall built proper
# delivery
self.order_line = self._createRootDocumentLine(
resource_value = self._createProduct(), quantity = 10, price = 5)
self.stepTic()
self.root_document.confirm()
self.stepTic()
self._checkBPMSimulation()
self.assertEqual(
1,
len(self.root_document.getCausalityRelatedList())
)
self.assertEqual(
'Applied Rule',
self.root_document.getCausalityRelatedValue().getPortalType()
)
class TestPackingListDifferentProcess(TestPackingList,
TestBPMEvaluationDifferentProcessMixin):
def test_starting(self):
self.delivery_line = self._createRootDocumentLine(
resource_value = self._createProduct(), quantity = 10, price = 5)
self.stepTic()
self.root_document.confirm()
self.stepTic()
self._checkBPMSimulation()
self._packDelivery()
self.root_document.start()
self.stepTic()
self._checkBPMSimulation()
self.assertEqual(
1,
len(self.root_document.getCausalityRelatedList())
)
self.assertEqual(
'Applied Rule',
self.root_document.getCausalityRelatedValue(
portal_type='Applied Rule').getPortalType()
)
class TestInvoiceDifferentProcess(TestInvoice,
TestBPMEvaluationDifferentProcessMixin):
pass
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestOrderDefaultProcess))
suite.addTest(unittest.makeSuite(TestPackingListDefaultProcess))
# suite.addTest(unittest.makeSuite(TestInvoiceDefaultProcess))
suite.addTest(unittest.makeSuite(TestOrderDifferentProcess))
suite.addTest(unittest.makeSuite(TestPackingListDifferentProcess))
# suite.addTest(unittest.makeSuite(TestInvoiceDifferentProcess))
return suite
# -*- coding: utf-8 -*-
##############################################################################
# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
# Yusuke Muraoka <yusuke@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 unittest
import transaction
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from DateTime import DateTime
from Products.CMFCore.utils import getToolByName
from Products.ERP5Type.tests.utils import reindex
from Products.ERP5Legacy.tests.testLegacyBPMCore import TestBPMMixin
from Products.ERP5Type.tests.backportUnittest import skip
class TestMRPMixin(TestBPMMixin):
transformation_portal_type = 'Transformation'
transformed_resource_portal_type = 'Transformation Transformed Resource'
product_portal_type = 'Product'
organisation_portal_type = 'Organisation'
order_portal_type = 'Production Order'
order_line_portal_type = 'Production Order Line'
def getBusinessTemplateList(self):
return TestBPMMixin.getBusinessTemplateList(self) + ('erp5_mrp', )
def invalidateRules(self):
"""
do reversely of validateRules
"""
rule_tool = self.getRuleTool()
for rule in rule_tool.contentValues(
portal_type=rule_tool.getPortalRuleTypeList()):
if rule.getValidationState() == 'validated':
rule.invalidate()
def _createDocument(self, portal_type, **kw):
module = self.portal.getDefaultModule(
portal_type=portal_type)
return self._createObject(module, portal_type, **kw)
def _createObject(self, parent, portal_type, id=None, **kw):
o = None
if id is not None:
o = parent.get(str(id), None)
if o is None:
o = parent.newContent(portal_type=portal_type)
o.edit(**kw)
return o
def createTransformation(self, **kw):
return self._createDocument(self.transformation_portal_type, **kw)
def createProduct(self, **kw):
return self._createDocument(self.product_portal_type, **kw)
def createOrganisation(self, **kw):
return self._createDocument(self.organisation_portal_type, **kw)
def createOrder(self, **kw):
return self._createDocument(self.order_portal_type, **kw)
def createOrderLine(self, order, **kw):
return self._createObject(order, self.order_line_portal_type, **kw)
def createTransformedResource(self, transformation, **kw):
return self._createObject(transformation, self.transformed_resource_portal_type, **kw)
@reindex
def createCategories(self):
category_tool = getToolByName(self.portal, 'portal_categories')
self.createCategoriesInCategory(category_tool.base_amount, ['weight'])
self.createCategoriesInCategory(category_tool.base_amount.weight, ['kg'])
self.createCategoriesInCategory(category_tool.trade_phase, ['mrp',])
self.createCategoriesInCategory(category_tool.trade_phase.mrp,
['p' + str(i) for i in range(5)]) # phase0 ~ 4
@reindex
def createDefaultOrder(self, transformation=None, business_process=None):
if transformation is None:
transformation = self.createDefaultTransformation()
if business_process is None:
business_process = self.createSimpleBusinessProcess()
base_date = DateTime()
order = self.createOrder(specialise_value=business_process,
start_date=base_date,
stop_date=base_date+3)
order_line = self.createOrderLine(order,
quantity=10,
resource=transformation.getResource(),
specialise_value=transformation)
# XXX in some case, specialise_value is not related to order_line by edit,
# but by setSpecialise() is ok, Why?
order_line.setSpecialiseValue(transformation)
return order
@reindex
def createDefaultTransformation(self):
resource1 = self.createProduct(id='1', quantity_unit_list=['weight/kg'])
resource2 = self.createProduct(id='2', quantity_unit_list=['weight/kg'])
resource3 = self.createProduct(id='3', quantity_unit_list=['weight/kg'])
resource4 = self.createProduct(id='4', quantity_unit_list=['weight/kg'])
resource5 = self.createProduct(id='5', quantity_unit_list=['weight/kg'])
transformation = self.createTransformation(resource_value=resource5)
self.createTransformedResource(transformation=transformation,
resource_value=resource1,
quantity=3,
quantity_unit_list=['weight/kg'],
trade_phase='mrp/p2')
self.createTransformedResource(transformation=transformation,
resource_value=resource2,
quantity=1,
quantity_unit_list=['weight/kg'],
trade_phase='mrp/p2')
self.createTransformedResource(transformation=transformation,
resource_value=resource3,
quantity=4,
quantity_unit_list=['weight/kg'],
trade_phase='mrp/p3')
self.createTransformedResource(transformation=transformation,
resource_value=resource4,
quantity=1,
quantity_unit_list=['weight/kg'],
trade_phase='mrp/p3')
return transformation
@reindex
def createSimpleBusinessProcess(self):
""" mrp/p2 mrp/3
ready -------- partial_produced ------- done
"""
business_process = self.createBusinessProcess()
business_path_p2 = self.createBusinessPath(business_process)
business_path_p3 = self.createBusinessPath(business_process)
business_state_ready = self.createBusinessState(business_process)
business_state_partial = self.createBusinessState(business_process)
business_state_done = self.createBusinessState(business_process)
# organisations
source_section = self.createOrganisation(title='source_section')
source = self.createOrganisation(title='source')
destination_section = self.createOrganisation(title='destination_section')
destination = self.createOrganisation(title='destination')
business_process.edit(referential_date='stop_date')
business_path_p2.edit(id='p2',
predecessor_value=business_state_ready,
successor_value=business_state_partial,
quantity=1,
trade_phase=['mrp/p2'],
source_section_value=source_section,
source_value=source,
destination_section_value=destination_section,
destination_value=destination,
)
business_path_p3.edit(id='p3',
predecessor_value=business_state_partial,
successor_value=business_state_done,
quantity=1,
deliverable=1, # root explanation
trade_phase=['mrp/p3'],
source_section_value=source_section,
source_value=source,
destination_section_value=destination_section,
destination_value=destination,
)
return business_process
@reindex
def createConcurrentBusinessProcess(self):
""" mrp/p2
ready ======== partial_produced
mrp/p3
"""
business_process = self.createBusinessProcess()
business_path_p2 = self.createBusinessPath(business_process)
business_path_p3 = self.createBusinessPath(business_process)
business_state_ready = self.createBusinessState(business_process)
business_state_partial = self.createBusinessState(business_process)
# organisations
source_section = self.createOrganisation(title='source_section')
source = self.createOrganisation(title='source')
destination_section = self.createOrganisation(title='destination_section')
destination = self.createOrganisation(title='destination')
business_process.edit(referential_date='stop_date')
business_path_p2.edit(id='p2',
predecessor_value=business_state_ready,
successor_value=business_state_partial,
quantity=1,
trade_phase=['mrp/p2'],
source_section_value=source_section,
source_value=source,
destination_section_value=destination_section,
destination_value=destination,
)
business_path_p3.edit(id='p3',
predecessor_value=business_state_ready,
successor_value=business_state_partial,
quantity=1,
deliverable=1, # root explanation
trade_phase=['mrp/p3'],
source_section_value=source_section,
source_value=source,
destination_section_value=destination_section,
destination_value=destination,
)
return business_process
@reindex
def beforeTearDown(self):
super(TestMRPMixin, self).beforeTearDown()
transaction.abort()
for module in (
self.portal.organisation_module,
self.portal.production_order_module,
self.portal.transformation_module,
self.portal.business_process_module,
# don't remove document because reuse it for testing of id
# self.portal.product_module,
self.portal.portal_simulation,):
module.manage_delObjects(list(module.objectIds()))
transaction.commit()
class TestMRPImplementation(TestMRPMixin, ERP5TypeTestCase):
"""the test for implementation"""
@skip('Unfinished experimental feature')
def test_TransformationRule_getHeadProductionPathList(self):
rule = self.portal.portal_rules.default_transformation_model_rule
transformation = self.createDefaultTransformation()
business_process = self.createSimpleBusinessProcess()
self.assertEquals([business_process.p2],
rule.getHeadProductionPathList(transformation, business_process))
business_process = self.createConcurrentBusinessProcess()
self.assertEquals(set([business_process.p2, business_process.p3]),
set(rule.getHeadProductionPathList(transformation, business_process)))
def test_TransformationRule_expand(self):
# mock order
order = self.createDefaultOrder()
order_line = order.objectValues()[0]
business_process = order.getSpecialiseValue()
# paths
path_p2 = '%s/p2' % business_process.getRelativeUrl()
path_p3 = '%s/p3' % business_process.getRelativeUrl()
# organisations
path = business_process.objectValues(
portal_type=self.portal.getPortalBusinessPathTypeList())[0]
source_section = path.getSourceSection()
source = path.getSource()
destination_section = path.getDestinationSection()
destination = path.getDestination()
consumed_organisations = (source_section, source, destination_section, None)
produced_organisations = (source_section, None, destination_section, destination)
# don't need another rules, just need TransformationRule for test
self.invalidateRules()
self.stepTic()
# alter simulations of the order
# root
applied_rule = self.portal.portal_simulation.newContent(portal_type='Applied Rule')
movement = applied_rule.newContent(portal_type='Simulation Movement')
applied_rule.edit(causality_value=order)
movement.edit(order_value=order_line,
quantity=order_line.getQuantity(),
resource=order_line.getResource())
# test mock
applied_rule = movement.newContent(potal_type='Applied Rule')
rule = self.portal.portal_rules.default_transformation_model_rule
rule.expand(applied_rule)
# assertion
expected_value_set = set([
((path_p2,), 'product_module/5', produced_organisations, 'mrp/p3', -10),
((path_p2,), 'product_module/1', consumed_organisations, 'mrp/p2', 30),
((path_p2,), 'product_module/2', consumed_organisations, 'mrp/p2', 10),
((path_p3,), 'product_module/5', consumed_organisations, 'mrp/p3', 10),
((path_p3,), 'product_module/3', consumed_organisations, 'mrp/p3', 40),
((path_p3,), 'product_module/4', consumed_organisations, 'mrp/p3', 10),
((path_p3,), 'product_module/5', produced_organisations, None, -10)])
movement_list = applied_rule.objectValues()
self.assertEquals(len(expected_value_set), len(movement_list))
movement_value_set = set([])
for movement in movement_list:
movement_value_set |= set([(tuple(movement.getCausalityList()),
movement.getResource(),
(movement.getSourceSection(),
movement.getSource(),
movement.getDestinationSection(),
movement.getDestination(),), # organisations
movement.getTradePhase(),
movement.getQuantity())])
self.assertEquals(expected_value_set, movement_value_set)
@skip('Unfinished experimental feature')
def test_TransformationRule_expand_concurrent(self):
business_process = self.createConcurrentBusinessProcess()
# mock order
order = self.createDefaultOrder(business_process=business_process)
order_line = order.objectValues()[0]
# phases
phase_p2 = '%s/p2' % business_process.getRelativeUrl()
phase_p3 = '%s/p3' % business_process.getRelativeUrl()
# organisations
path = business_process.objectValues(
portal_type=self.portal.getPortalBusinessPathTypeList())[0]
source_section = path.getSourceSection()
source = path.getSource()
destination_section = path.getDestinationSection()
destination = path.getDestination()
organisations = (source_section, source, destination_section, destination)
consumed_organisations = (source_section, source, destination_section, None)
produced_organisations = (source_section, None, destination_section, destination)
# don't need another rules, just need TransformationRule for test
self.invalidateRules()
self.stepTic()
# alter simulations of the order
# root
applied_rule = self.portal.portal_simulation.newContent(portal_type='Applied Rule')
movement = applied_rule.newContent(portal_type='Simulation Movement')
applied_rule.edit(causality_value=order)
movement.edit(order_value=order_line,
quantity=order_line.getQuantity(),
resource=order_line.getResource())
# test mock
applied_rule = movement.newContent(potal_type='Applied Rule')
rule = self.portal.portal_rules.default_transformation_model_rule
rule.expand(applied_rule)
# assertion
expected_value_set = set([
((phase_p2,), 'product_module/1', consumed_organisations, 'mrp/p2', 30),
((phase_p2,), 'product_module/2', consumed_organisations, 'mrp/p2', 10),
((phase_p3,), 'product_module/3', consumed_organisations, 'mrp/p3', 40),
((phase_p3,), 'product_module/4', consumed_organisations, 'mrp/p3', 10),
((phase_p2, phase_p3), 'product_module/5', produced_organisations, None, -10)])
movement_list = applied_rule.objectValues()
self.assertEquals(len(expected_value_set), len(movement_list))
movement_value_set = set([])
for movement in movement_list:
movement_value_set |= set([(tuple(movement.getCausalityList()),
movement.getResource(),
(movement.getSourceSection(),
movement.getSource(),
movement.getDestinationSection(),
movement.getDestination(),), # organisations
movement.getTradePhase(),
movement.getQuantity())])
self.assertEquals(expected_value_set, movement_value_set)
@skip('Unfinished experimental feature')
def test_TransformationRule_expand_reexpand(self):
"""
test case of difference when any movement are frozen
by using above result
"""
self.test_TransformationRule_expand_concurrent()
self.stepTic()
applied_rule = self.portal.portal_simulation.objectValues()[0]
business_process = applied_rule.getCausalityValue().getSpecialiseValue()
# phases
phase_p2 = '%s/p2' % business_process.getRelativeUrl()
phase_p3 = '%s/p3' % business_process.getRelativeUrl()
# organisations
path = business_process.objectValues(
portal_type=self.portal.getPortalBusinessPathTypeList())[0]
source_section = path.getSourceSection()
source = path.getSource()
destination_section = path.getDestinationSection()
destination = path.getDestination()
consumed_organisations = (source_section, source, destination_section, None)
produced_organisations = (source_section, None, destination_section, destination)
movement = applied_rule.objectValues()[0]
applied_rule = movement.objectValues()[0]
# these movements are made by transformation
for movement in applied_rule.objectValues():
movement.edit(quantity=1)
# set the state value of isFrozen to 1,
movement._baseSetFrozen(1)
# re-expand
rule = self.portal.portal_rules.default_transformation_model_rule
rule.expand(applied_rule)
# assertion
expected_value_set = set([
((phase_p2,), 'product_module/1', consumed_organisations, 'mrp/p2', 1), # Frozen
((phase_p2,), 'product_module/1', consumed_organisations, 'mrp/p2', 29),
((phase_p2,), 'product_module/2', consumed_organisations, 'mrp/p2', 1), # Frozen
((phase_p2,), 'product_module/2', consumed_organisations, 'mrp/p2', 9),
((phase_p3,), 'product_module/3', consumed_organisations, 'mrp/p3', 1), # Frozen
((phase_p3,), 'product_module/3', consumed_organisations, 'mrp/p3', 39),
((phase_p3,), 'product_module/4', consumed_organisations, 'mrp/p3', 1), # Frozen
((phase_p3,), 'product_module/4', consumed_organisations, 'mrp/p3', 9),
((phase_p2, phase_p3), 'product_module/5', produced_organisations, None, 1), # Frozen
((phase_p2, phase_p3), 'product_module/5', produced_organisations, None, -11)])
movement_list = applied_rule.objectValues()
self.assertEquals(len(expected_value_set), len(movement_list))
movement_value_set = set([])
for movement in movement_list:
movement_value_set |= set([(tuple(movement.getCausalityList()),
movement.getResource(),
(movement.getSourceSection(),
movement.getSource(),
movement.getDestinationSection(),
movement.getDestination(),), # organisations
movement.getTradePhase(),
movement.getQuantity())])
self.assertEquals(expected_value_set, movement_value_set)
@skip('Unfinished experimental feature')
def test_TransformationSourcingRule_expand(self):
# mock order
order = self.createDefaultOrder()
order_line = order.objectValues()[0]
# don't need another rules, just need TransformationSourcingRule for test
self.invalidateRules()
self.stepTic()
business_process = order.getSpecialiseValue()
# get last path of a business process
# in simple business path, the last is between "partial_produced" and "done"
causality_path = None
for state in business_process.objectValues(
portal_type=self.portal.getPortalBusinessStateTypeList()):
if len(state.getRemainingTradePhaseList(self.portal)) == 0:
causality_path = state.getSuccessorRelatedValue()
# phases
phase_p2 = '%s/p2' % business_process.getRelativeUrl()
# organisations
source_section = causality_path.getSourceSection()
source = causality_path.getSource()
destination_section = causality_path.getDestinationSection()
destination = causality_path.getDestination()
organisations = (source_section, source, destination_section, destination)
# sourcing resource
sourcing_resource = order_line.getResource()
# alter simulations of the order
# root
applied_rule = self.portal.portal_simulation.newContent(portal_type='Applied Rule')
movement = applied_rule.newContent(portal_type='Simulation Movement')
applied_rule.edit(causality_value=order)
movement.edit(order_value=order_line,
causality_value=causality_path,
quantity=order_line.getQuantity(),
resource=sourcing_resource,
)
self.stepTic()
# test mock
applied_rule = movement.newContent(potal_type='Applied Rule')
rule = self.portal.portal_rules.default_transformation_sourcing_model_rule
rule.expand(applied_rule)
# assertion
expected_value_set = set([
((phase_p2,), sourcing_resource, organisations, 10)])
movement_list = applied_rule.objectValues()
self.assertEquals(len(expected_value_set), len(movement_list))
movement_value_set = set([])
for movement in movement_list:
movement_value_set |= set([(tuple(movement.getCausalityList()),
movement.getResource(),
(movement.getSourceSection(),
movement.getSource(),
movement.getDestinationSection(),
movement.getDestination(),), # organisations
movement.getQuantity())])
self.assertEquals(expected_value_set, movement_value_set)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestMRPImplementation))
return suite
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