diff --git a/product/ERP5/Document/BusinessProcess.py b/product/ERP5/Document/BusinessProcess.py index fe3d551f3c9458606cee2b679da268ebb501cd65..9eaacac28bc838fe5c68481ea2dcf179b33261e4 100644 --- a/product/ERP5/Document/BusinessProcess.py +++ b/product/ERP5/Document/BusinessProcess.py @@ -30,6 +30,7 @@ from AccessControl import ClassSecurityInfo from Products.ERP5Type import Permissions, PropertySheet, interfaces +from Products.ERP5Type.TransactionalVariable import getTransactionalVariable from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5.Document.Path import Path from Products.ERP5.ExplanationCache import _getExplanationCache, _getBusinessLinkClosure @@ -594,8 +595,9 @@ class BusinessProcess(Path, XMLObject): return False return True - security.declareProtected(Permissions.AccessContentsInformation, 'getRemainingTradePhaseList') - def getRemainingTradePhaseList(self, explanation, business_link, trade_phase_list=None): + security.declareProtected(Permissions.AccessContentsInformation, + 'getRemainingTradePhaseList') + def getRemainingTradePhaseList(self, business_link): """Returns the list of remaining trade phases which to be achieved as part of a business process. This list is calculated by analysing the graph of business link and trade states, starting from a given @@ -608,11 +610,6 @@ class BusinessProcess(Path, XMLObject): business_link -- a Business Link document - trade_phase_list -- if provided, the result is filtered by it after - being collected - XXX-JPS - is this really useful ? - - NOTE: this code has not been reviewed and needs review - NOTE: explanation is not involved here because we consider here that self is the result of asUnionBusinessProcess and thus only contains applicable Business Link to a given simulation subtree. Since the list @@ -622,25 +619,39 @@ class BusinessProcess(Path, XMLObject): """ remaining_trade_phase_list = [] trade_state = business_link.getSuccessor() - for link in [x for x in self.objectValues(portal_type="Business Link") \ - if x.getPredecessor() == trade_state]: - # XXX When no simulations related to link, what should link.isCompleted return? - # if True we don't have way to add remaining trade phases to new movement - if not (link.getRelatedSimulationMovementValueList(explanation) and - link.isCompleted(explanation)): - remaining_trade_phase_list += link.getTradePhaseValueList() + tv = getTransactionalVariable(self) + # We might need a key which depends on the explanation + key = 'BusinessProcess_predecessor_successor_%s' % self.getRelativeUrl() + predecessor_successor_dict = tv.get(key, None) + if predecessor_successor_dict is None: + predecessor_successor_dict = {'predecessor':{}, + 'successor':{}} + for business_link in self.objectValues(portal_type="Business Link"): + for property_name in ('predecessor', 'successor'): + property_value = business_link.getProperty(property_name) + if property_value: + business_link_list = predecessor_successor_dict[property_name].\ + setdefault(property_value, []) + business_link_list.append(business_link) + tv[key] = predecessor_successor_dict + + business_link_list = predecessor_successor_dict['predecessor'].\ + get(trade_state, []) + assert len(business_link_list) <= 1, \ + "code is not able yet to manage this case" + for link in business_link_list: + remaining_trade_phase_list += link.getTradePhaseValueList() # collect to successor direction recursively - state = link.getSuccessorValue() + state = link.getSuccessor() if state is not None: + next_business_link_list = predecessor_successor_dict['successor'].\ + get(state, []) + assert len(next_business_link_list) == 1, \ + "code is not able yet to manage this case" remaining_trade_phase_list.extend( - self.getRemainingTradePhaseList(explanation, state, None)) - - # filter just at once if given - if trade_phase_list is not None: - remaining_trade_phase_list = filter( - lambda x : x.getLogicalPath() in trade_phase_list, - remaining_trade_phase_list) + self.getRemainingTradePhaseList( + next_business_link_list[0])) return remaining_trade_phase_list diff --git a/product/ERP5/interfaces/business_process.py b/product/ERP5/interfaces/business_process.py index 80ea0a31edf8428a20fb2723c2f878d781771486..de0c073f5cc24ca9b2056b42ca26ecb7d9655f44 100644 --- a/product/ERP5/interfaces/business_process.py +++ b/product/ERP5/interfaces/business_process.py @@ -356,7 +356,7 @@ class ITradePhaseProcess(Interface): trade_phase -- a Trade Phase category """ - def getRemainingTradePhaseList(business_link, trade_phase_list=None): + def getRemainingTradePhaseList(business_link): """Returns the list of remaining trade phases which to be achieved as part of a business process. This list is calculated by analysing the graph of business link and trade states, starting from a given @@ -366,9 +366,6 @@ class ITradePhaseProcess(Interface): business_link -- a Business Link document - trade_phase_list -- if provided, the result is filtered by it after - being collected - ???? useful ? XXX-JPS ? - NOTE: explanation is not involved here because we consider here that self is the result of asUnionBusinessProcess and thus only contains applicable Business Link to a given simulation subtree. Since the list @@ -491,4 +488,4 @@ class IBusinessProcess(ITradeModelPathProcess, IBusinessLinkProcess, IBuildableB include_partially_buildable -- if set to True, also build partially buildable business link. Else only build strictly buildable link. - """ \ No newline at end of file + """ diff --git a/product/ERP5/tests/testBPMCore.py b/product/ERP5/tests/testBPMCore.py index efc5a4c97b35e2f32424d20575da8262a13fdbd3..e4e1284ee0fe9195e474b7f2d3f7a42e3341537a 100644 --- a/product/ERP5/tests/testBPMCore.py +++ b/product/ERP5/tests/testBPMCore.py @@ -302,129 +302,42 @@ class TestBPMImplementation(TestBPMMixin): self.assertEquals('something', business_path.getSource(context=context_movement, default='something')) - @newSimulationExpectedFailure 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 + This test case is described for what trade_phase is remaining after the + given business link. """ # define business process category_tool = self.getCategoryTool() business_process = self.createBusinessProcess() - business_link_a_b = self.createBusinessLink(business_process) - business_link_b_c = self.createBusinessLink(business_process) - business_link_b_d = self.createBusinessLink(business_process) - business_link_c_d = self.createBusinessLink(business_process) - business_link_d_e = self.createBusinessLink(business_process) - business_state_a = category_tool.trade_state.state_a - business_state_b = category_tool.trade_state.state_b - business_state_c = category_tool.trade_state.state_c - business_state_d = category_tool.trade_state.state_d - business_state_e = category_tool.trade_state.state_e - business_link_a_b.setPredecessorValue(business_state_a) - business_link_b_c.setPredecessorValue(business_state_b) - business_link_b_d.setPredecessorValue(business_state_b) - business_link_c_d.setPredecessorValue(business_state_c) - business_link_d_e.setPredecessorValue(business_state_d) - business_link_a_b.setSuccessorValue(business_state_b) - business_link_b_c.setSuccessorValue(business_state_c) - business_link_b_d.setSuccessorValue(business_state_d) - business_link_c_d.setSuccessorValue(business_state_d) - business_link_d_e.setSuccessorValue(business_state_e) - - # set title for debug - business_link_a_b.edit(title="a_b") - business_link_b_c.edit(title="b_c") - business_link_b_d.edit(title="b_d") - business_link_c_d.edit(title="c_d") - business_link_d_e.edit(title="d_e") - - # set trade_phase - business_link_a_b.edit(trade_phase=['default/discount'], - completed_state=['ordered']) # (*1) - business_link_b_c.edit(trade_phase=['default/delivery']) - business_link_b_d.edit(trade_phase=['default/invoicing']) - business_link_c_d.edit(trade_phase=['default/payment']) - business_link_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", quantity=1) - - # make simulation - order.order() - - self.stepTic() - - applied_rule = order.getCausalityRelatedValue() - sm = applied_rule.contentValues(portal_type="Simulation Movement")[0] - sm.edit(causality_value=business_link_a_b) - - # make other movements for each business path - applied_rule.newContent(portal_type="Simulation Movement", - causality_value=business_link_b_c, - order_value=order_line) - applied_rule.newContent(portal_type="Simulation Movement", - causality_value=business_link_b_d, - order_value=order_line) - applied_rule.newContent(portal_type="Simulation Movement", - causality_value=business_link_c_d, - order_value=order_line) - applied_rule.newContent(portal_type="Simulation Movement", - causality_value=business_link_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_link_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_process.getRemainingTradePhaseList(order, - business_state_a))) - self.assertEquals(set([trade_phase.delivery, - trade_phase.invoicing, - trade_phase.payment, - trade_phase.accounting]), - set(business_process.getRemainingTradePhaseList(order, - business_state_b))) - self.assertEquals(set([trade_phase.payment, - trade_phase.accounting]), - set(business_process.getRemainingTradePhaseList(order, - business_state_c))) - self.assertEquals(set([trade_phase.accounting]), - set(business_process.getRemainingTradePhaseList(order, - business_state_d))) - - # 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_process\ - .getRemainingTradePhaseList(order, business_state_a, - trade_phase_list=['default/delivery', - 'default/accounting']))) + business_link_order = self.createBusinessLink(business_process, + title='order', id='order', + trade_phase='default/order') + business_link_deliver = self.createBusinessLink(business_process, + title='deliver', id='deliver', + trade_phase='default/delivery') + business_link_invoice = self.createBusinessLink(business_process, + title='invoice', id='invoice', + trade_phase='default/invoicing') + trade_state = category_tool.trade_state + business_link_order.setSuccessorValue(trade_state.ordered) + business_link_deliver.setPredecessorValue(trade_state.ordered) + business_link_deliver.setSuccessorValue(trade_state.delivered) + business_link_invoice.setPredecessorValue(trade_state.delivered) + business_link_invoice.setSuccessorValue(trade_state.invoiced) + + trade_phase = category_tool.trade_phase.default + + self.assertEquals([trade_phase.delivery, + trade_phase.invoicing], + business_process.getRemainingTradePhaseList( + business_process.order)) + self.assertEquals([trade_phase.invoicing], + business_process.getRemainingTradePhaseList( + business_process.deliver)) + self.assertEquals([], + business_process.getRemainingTradePhaseList( + business_process.invoice)) @newSimulationExpectedFailure def test_BusinessLink_calculateExpectedDate(self):