testBPMEvaluation.py 27.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
# -*- 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
40
import transaction
41 42 43 44 45 46 47 48

from Products.ERP5.tests.testBPMCore 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'
49 50
  packing_list_portal_type = 'Sale Packing List'
  packing_list_line_portal_type = 'Sale Packing List Line'
51
  trade_condition_portal_type = 'Sale Trade Condition'
52
  invoice_portal_type = 'Sale Invoice Transaction'
53 54 55 56 57 58 59 60 61 62 63 64 65
  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')

  def afterSetUp(self):
    TestBPMMixin.afterSetUp(self)
    self._createNodes()
    self._createBusinessProcess()
    self._createTradeCondition()
66
    self._createRootDocument()
67
    self._setUpRules()
68 69
    self.stepTic()

70 71 72 73 74 75 76 77 78 79 80 81
  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()
82 83 84
    self._createOrderRule()
    self._createDeliveryRule()
    self._createInvoicingRule()
85
    self._createInvoiceRule()
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    self._createTradeModelRule()

  def _createRootTradeRule(self, **kw):
    edit_dict = {}
    edit_dict.update(
      trade_phase = 'default/delivery',
      expandable_property = ('aggregate_list', 'base_application_list',
        'base_contribution_list', 'causality_list', 'description',
        'destination_account_list', 'destination_function_list',
        'destination_list', 'destination_section_list', 'price',
        'price_currency_list', 'quantity', 'quantity_unit_list',
        'resource_list', 'source_account_list', 'source_function_list',
        'source_list', 'source_section_list', 'start_date', 'stop_date',
        'variation_category_list', 'variation_property_dict'),
      matching_property = ('resource_list', 'variation_category_list',
        'variation_property_dict')
    )
    # TODO: version
    edit_dict.update(**kw)
    rule = self.rule_tool.newContent(**edit_dict)
    rule.newContent(portal_type='Category Divergence Tester',
        tested_property = ('source_section_list | Source Section',
          'resource_list | Resource',
          'destination_section_list | Destination Section',
          'source_list | Source', 'destination_list | Destination',
          'aggregate_list | Aggregate'))
    rule.newContent(portal_type='Property Divergence Tester',
        tested_property = ('start_date | Start Date',
          'stop_date | Stop Date'))
    rule.newContent(portal_type='Quantity Divergence Tester')

    return rule

119 120
  def _createOrderRule(self):
    rule = self._createRootTradeRule(portal_type='Order Rule',
121 122
        reference='default_order_rule', matching_property = ('resource_list',
          'variation_category_list', 'variation_property_dict', 'order_list'))
123 124 125
    rule.validate()
    transaction.commit()

126 127
  def _createDeliveryRule(self):
    rule = self._createRootTradeRule(portal_type='Delivery Rule',
128 129 130
        reference='default_delivery_rule', matching_property = (
          'resource_list', 'variation_category_list',
          'variation_property_dict', 'order_list')
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
        )
    rule.validate()
    transaction.commit()

  def _createTradeModelRule(self):
    # TODO: version
    edit_dict = {}
    edit_dict.update(
    )
    rule = self.rule_tool.newContent(portal_type='Trade Model Rule',
      reference='default_trade_model_rule',
      expandable_property = ('delivery_mode_list', 'incoterm_list',
        'source_list', 'destination_list', 'source_section_list',
        'destination_section_list', 'source_decision_list',
        'destination_decision_list', 'source_administration_list',
        'destination_administration_list', 'price_currency_list',
        'resource_list', 'aggregate_list', 'source_function_list',
        'destination_function_list', 'source_account_list',
        'destination_account_list', 'description',
        'destination_payment_list', 'source_payment_list'),
      test_method_id = ('SimulationMovement_testTradeModelRule',)
      )
    rule.newContent(portal_type='Category Divergence Tester',
        tested_property = ('resource_list | Resource',
          'source_section_list | Source Section',
          'destination_section_list | Destination Section',
          'source_list | Source', 'destination_list | Destination',
          'source_function_list | Source Function',
          'destination_function_list | Destination Function',
          'source_project_list | Source Project',
          'destination_project_list | Destination Project',
          'aggregate_list | Aggregate',
          'price_currency_list | Price Currency',
          'base_contribution_list | Base Contribution',
          'base_application_list | Base Application',
          'source_account_list | Source Account',
          'destination_account_list | Destination Account'))
    rule.newContent(portal_type='Property Divergence Tester',
        tested_property = ('start_date | Start Date',
          'stop_date | Stop Date', 'price | Price'))
    rule.newContent(portal_type='Quantity Divergence Tester')

    rule.validate()
    transaction.commit()

176 177 178 179 180 181 182 183 184 185 186
  def _createInvoiceRule(self):
    # XXX: This is not needed, but invoices, even if built from simulation
    #      need those rule to create empty one
    rule_tool = self.portal.portal_rules

    clipboard = rule_tool.manage_copyObjects(ids = ['default_invoice_rule'])
    pasted = rule_tool.manage_pasteObjects(clipboard)
    new_rule = getattr(rule_tool, pasted[0]['new_id'])
    new_rule.validate()
    transaction.commit()

187
  def _createInvoicingRule(self):
188 189 190 191
    # TODO: version
    edit_dict = {}
    edit_dict.update(
    )
192 193
    rule = self.rule_tool.newContent(portal_type='Invoicing Rule',
      reference='default_invoicing_rule',
194 195 196 197 198 199 200 201 202 203 204 205
      trade_phase = 'default/invoicing',
      expandable_property = ('aggregate_list', 'base_application_list',
        'base_contribution_list', 'causality_list', 'delivery_mode_list',
        'description', 'destination_account_list',
        'destination_function_list', 'destination_list',
        'destination_section_list', 'efficiency', 'incoterm_list', 'price',
        'price_currency_list', 'quantity', 'quantity_unit_list',
        'resource_list', 'source_account_list', 'source_function_list',
        'source_list', 'source_section_list', 'start_date', 'stop_date',
        'variation_category_list', 'variation_property_dict'),
      matching_property = ('resource_list', 'variation_category_list',
        'variation_property_dict'),
206
      test_method_id = ('SimulationMovement_testInvoicingRule',)
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
      )
    rule.newContent(portal_type='Category Divergence Tester',
        tested_property = ('resource_list | Resource',
          'source_section_list | Source Section',
          'destination_section_list | Destination Section',
          'source_list | Source', 'destination_list | Destination',
          'source_function_list | Source Function',
          'destination_function_list | Destination Function',
          'source_project_list | Source Project',
          'destination_project_list | Destination Project',
          'aggregate_list | Aggregate',
          'price_currency_list | Price Currency',
          'base_contribution_list | Base Contribution',
          'base_application_list | Base Application',
          'source_account_list | Source Account',
          'destination_account_list | Destination Account'))
    rule.newContent(portal_type='Property Divergence Tester',
        tested_property = ('start_date | Start Date',
          'stop_date | Stop Date'))
    rule.newContent(portal_type='Quantity Divergence Tester')

    rule.validate()
    transaction.commit()

231 232 233 234 235 236 237 238 239 240 241 242 243
  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,
244
        title = self.id(),
245 246
        specialise_value=self.business_process, **kw)

247 248 249
  def _createRootDocumentLine(self, **kw):
    return self.root_document.newContent(
        portal_type=self.root_document_line_portal_type, **kw)
250 251 252 253 254 255

  def _createNodes(self):
    self.source, self.source_section = self._createNode(), self._createNode()
    self.destination, self.destination_section = self._createNode() \
        , self._createNode()

Łukasz Nowak's avatar
Łukasz Nowak committed
256 257 258 259 260 261 262 263
  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)

264 265
  def _createRootDocument(self):
    self.root_document = self._createDocument(self.root_document_portal_type,
266 267 268 269 270 271 272 273
        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)

274
  def _checkBPMSimulation(self):
275
    """Checks BPMised related simumation.
Łukasz Nowak's avatar
Łukasz Nowak committed
276 277 278

    Note: Simulation tree is the same, it is totally independent from
    BPM sequence"""
279 280
    # TODO:
    #  - gather errors into one list
281
    bpm_root_rule = self.root_document.getCausalityRelatedValue(
282
        portal_type='Applied Rule')
283
    # check that correct root rule applied
284
    self.assertEqual(bpm_root_rule.getSpecialiseValue().getPortalType(),
285
        self.root_rule_portal_type)
286 287 288
    root_simulation_movement_list = bpm_root_rule.contentValues()
    for root_simulation_movement in root_simulation_movement_list:
      self.assertEqual(root_simulation_movement.getPortalType(),
289
          'Simulation Movement')
290
      movement = root_simulation_movement.getOrderValue()
291
      property_problem_list = []
292 293
      # check some properties equality between delivery line and simulation
      # movement, gather errors
294 295 296
      for property in 'resource', 'price', 'start_date', 'stop_date', \
                      'source', 'destination', 'source_section', \
                      'destination_section':
297
        if movement.getProperty(property) != root_simulation_movement \
298 299
            .getProperty(property):
          property_problem_list.append('property %s movement %s '
300 301
              'simulation %s' % (property, movement.getProperty(property),
                root_simulation_movement.getProperty(property)))
302 303
      if len(property_problem_list) > 0:
        self.fail('\n'.join(property_problem_list))
304 305 306
      self.assertEqual(
        movement.getQuantity() * root_simulation_movement.getOrderRatio(),
        root_simulation_movement.getQuantity())
307 308
      # root rule is order or delivery - so below each movement invoicing one
      # is expected
309 310
      self.assertEquals(len(root_simulation_movement.contentValues()), 1)
      for bpm_invoicing_rule in root_simulation_movement.contentValues():
311 312
        self.assertEqual(bpm_invoicing_rule.getPortalType(), 'Applied Rule')
        self.assertEqual(bpm_invoicing_rule.getSpecialiseValue() \
313
            .getPortalType(), 'Invoicing Rule')
314
        # only one movement inside invoicing rule
315
        self.assertEquals(len(bpm_invoicing_rule.contentValues()), 1)
316 317 318 319 320 321
        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)
Łukasz Nowak's avatar
Łukasz Nowak committed
322
          property_problem_list = []
323
          # check equality of some properties, gather them
324
          for property in 'resource', 'price', 'start_date', \
Łukasz Nowak's avatar
Łukasz Nowak committed
325 326
            'stop_date', 'source', 'destination', 'source_section', \
            'destination_section':
327 328
            if movement.getProperty(property) != \
                invoicing_simulation_movement.getProperty(property):
Łukasz Nowak's avatar
Łukasz Nowak committed
329 330 331 332 333
              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))
334 335 336
          self.assertEqual(
            movement.getQuantity() * root_simulation_movement.getOrderRatio(),
            invoicing_simulation_movement.getQuantity())
337 338
          # simple check for trade model rule existence, without movements,
          # as no trade condition configured
339 340
          self.assertEquals(
              len(invoicing_simulation_movement.contentValues()), 1)
341 342 343 344 345 346 347 348 349 350
          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 Rule')
            self.assertSameSet(trade_model_rule.contentValues(
              portal_type='Simulation Movement'), [])

class TestBPMEvaluationDefaultProcessMixin:
  def _createBusinessProcess(self):
Łukasz Nowak's avatar
Łukasz Nowak committed
351 352
    self.business_process = self.createBusinessProcess(title=self.id())
    self._createBusinessStateList()
353 354

    self.delivery_path = self.createBusinessPath(self.business_process,
355 356
        predecessor_value=self.ordered_state,
        successor_value=self.delivered_state,
Łukasz Nowak's avatar
Łukasz Nowak committed
357 358
        trade_phase='default/delivery',
        deliverable=1,
359 360
        completed_state_list=['started', 'stopped', 'delivered'],
        frozen_state_list=['started', 'stopped', 'delivered'],
Łukasz Nowak's avatar
Łukasz Nowak committed
361
        delivery_builder='portal_deliveries/bpm_sale_packing_list_builder',
Łukasz Nowak's avatar
Łukasz Nowak committed
362
        )
363 364

    self.invoice_path = self.createBusinessPath(self.business_process,
Łukasz Nowak's avatar
Łukasz Nowak committed
365 366 367 368
        predecessor_value=self.delivered_state,
        successor_value=self.invoiced_state,
        completed_state_list=['delivered'],
        frozen_state_list=['stopped', 'delivered'],
369
        delivery_builder='portal_deliveries/bpm_sale_invoice_builder',
370 371 372
        trade_phase='default/invoicing')

    self.account_path = self.createBusinessPath(self.business_process,
Łukasz Nowak's avatar
Łukasz Nowak committed
373 374 375 376
        predecessor_value=self.invoiced_state,
        successor_value=self.accounted_state,
        completed_state_list=['delivered'],
        frozen_state_list=['stopped', 'delivered'],
377 378 379
        trade_phase='default/accounting')

    self.pay_path = self.createBusinessPath(self.business_process,
Łukasz Nowak's avatar
Łukasz Nowak committed
380 381 382 383
        predecessor_value=self.invoiced_state,
        successor_value=self.accounted_state,
        completed_state_list=['delivered'],
        frozen_state_list=['stopped', 'delivered'],
384 385 386 387 388 389
        trade_phase='default/payment')

    self.stepTic()

class TestBPMEvaluationDifferentProcessMixin:
  def _createBusinessProcess(self):
Łukasz Nowak's avatar
Łukasz Nowak committed
390 391
    self.business_process = self.createBusinessProcess(title=self.id())
    self._createBusinessStateList()
392 393

    self.invoice_path = self.createBusinessPath(self.business_process,
Łukasz Nowak's avatar
Łukasz Nowak committed
394 395 396 397
        predecessor_value=self.ordered_state,
        successor_value=self.invoiced_state,
        completed_state_list=['delivered'],
        frozen_state_list=['stopped', 'delivered'],
398 399 400
        trade_phase='default/invoicing')

    self.account_path = self.createBusinessPath(self.business_process,
Łukasz Nowak's avatar
Łukasz Nowak committed
401 402 403 404
        predecessor_value=self.invoiced_state,
        successor_value=self.accounted_state,
        completed_state_list=['delivered'],
        frozen_state_list=['stopped', 'delivered'],
405 406 407
        trade_phase='default/accounting')

    self.pay_path = self.createBusinessPath(self.business_process,
Łukasz Nowak's avatar
Łukasz Nowak committed
408 409 410 411
        predecessor_value=self.accounted_state,
        successor_value=self.paid_state,
        completed_state_list=['delivered'],
        frozen_state_list=['stopped', 'delivered'],
412 413 414
        trade_phase='default/payment')

    self.delivery_path = self.createBusinessPath(self.business_process,
Łukasz Nowak's avatar
Łukasz Nowak committed
415 416 417 418 419 420
        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'])
421 422 423

    self.stepTic()

424
class GenericRuleTestsMixin:
425
  """Tests which are generic for BPMised Order, Delivery and Invoice Rule"""
426 427 428
  def test_transition(self):
    self.order_line = self._createRootDocumentLine(
      resource_value = self._createProduct(), quantity = 10, price = 5)
429 430
    self.stepTic()

431
    self._doFirstTransition(self.root_document)
432
    self.stepTic()
433
    self._checkBPMSimulation()
434

435 436 437
  def _split(self):
    """Invoke manual splitting"""
    ratio = .5 # hardcoded value, hopefully float friendly
Łukasz Nowak's avatar
Łukasz Nowak committed
438 439 440 441
    applied_rule = self.root_document.getCausalityRelatedValue(
        portal_type='Applied Rule')
    for movement in applied_rule.contentValues(
        portal_type='Simulation Movement'):
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
      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.getOrderRelatedValueList():
        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()

505 506
  def test_transition_line_edit(self):
    self.test_transition()
507 508
    self.order_line.edit(quantity = 8, price = 6)
    self.stepTic()
509
    self._checkBPMSimulation()
510

511 512 513
  def test_transition_line_edit_add(self):
    self.test_transition_line_edit()
    self.order_line_2 = self._createRootDocumentLine(
514 515
        resource_value = self._createProduct(), quantity = 4, price = 2)
    self.stepTic()
516
    self._checkBPMSimulation()
517

518 519 520
  def test_transition_line_edit_add_many_transactions(self):
    self.test_transition_line_edit()
    self.order_line_9 = self._createRootDocumentLine()
521
    self.stepTic()
522
    self._checkBPMSimulation()
523 524 525

    self.order_line_9.edit(resource_value = self._createProduct())
    self.stepTic()
526
    self._checkBPMSimulation()
527 528 529

    self.order_line_9.edit(quantity = 1)
    self.stepTic()
530
    self._checkBPMSimulation()
531 532 533

    self.order_line_9.edit(price = 33)
    self.stepTic()
534
    self._checkBPMSimulation()
535 536 537

    self.order_line_9.edit(resource_value = self._createProduct())
    self.stepTic()
538
    self._checkBPMSimulation()
539

540 541
  def test_transition_line_edit_add_same_resource(self):
    self.test_transition_line_edit()
542
    resource = self.order_line.getResourceValue()
543 544
    self.order_line_10 = self._createRootDocumentLine(
      resource_value = resource, quantity = 9, price = 2)
545
    self.stepTic()
546
    self._checkBPMSimulation()
547

548 549
  def test_transition_line_edit_add_same_resource_edit_again(self):
    self.test_transition_line_edit_add_same_resource()
550

551 552 553
    self.root_document.edit(title = self.root_document.getTitle() + 'a' )
    self.stepTic()
    self._checkBPMSimulation()
554

555
class TestOrder(TestBPMEvaluationMixin, GenericRuleTestsMixin):
556
  """Check BPMised Order Rule behaviour"""
557 558
  root_document_portal_type = 'Sale Order'
  root_document_line_portal_type = 'Sale Order Line'
559
  root_rule_portal_type = 'Order Rule'
560

561 562
  def _doFirstTransition(self, document):
    document.plan()
563

564 565 566
  def test_confirming(self):
    self.order_line = self._createRootDocumentLine(
      resource_value = self._createProduct(), quantity = 10, price = 5)
567 568
    self.stepTic()

569
    self.root_document.confirm()
570
    self.stepTic()
571 572 573 574 575 576 577 578 579 580
    self._checkBPMSimulation()
    self.assertEqual(
      2,
      len(self.root_document.getCausalityRelatedList())
    )
    self.assertEqual(
      'Applied Rule',
      self.root_document.getCausalityRelatedValue(
        portal_type='Applied Rule').getPortalType()
    )
581

582 583 584 585 586
    self.assertEqual(
      self.packing_list_portal_type,
      self.root_document.getCausalityRelatedValue(
        portal_type=self.packing_list_portal_type).getPortalType()
    )
587

588 589 590 591
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'
592
  root_rule_portal_type = 'Delivery Rule'
593

594 595 596 597 598 599 600 601 602 603 604
  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()

605 606 607
  def _doFirstTransition(self, document):
    document.confirm()

608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
  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()
    )

639 640 641
class TestInvoice(TestBPMEvaluationMixin, GenericRuleTestsMixin):
  """Check BPM Invoice Rule behaviour"""
  # not implemented yet
642 643
  pass

644 645 646
class TestOrderDefaultProcess(TestOrder,
    TestBPMEvaluationDefaultProcessMixin):
  pass
647

648 649 650
class TestPackingListDefaultProcess(TestPackingList,
    TestBPMEvaluationDefaultProcessMixin):
  pass
651

652 653 654
class TestInvoiceDefaultProcess(TestInvoice,
    TestBPMEvaluationDefaultProcessMixin):
  pass
655

656 657
class TestOrderDifferentProcess(TestOrder,
    TestBPMEvaluationDifferentProcessMixin):
658 659 660 661
  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
662 663
    self.order_line = self._createRootDocumentLine(
      resource_value = self._createProduct(), quantity = 10, price = 5)
664 665
    self.stepTic()

666
    self.root_document.confirm()
667
    self.stepTic()
668
    self._checkBPMSimulation()
669 670
    self.assertEqual(
      1,
671
      len(self.root_document.getCausalityRelatedList())
672 673 674
    )
    self.assertEqual(
      'Applied Rule',
675
      self.root_document.getCausalityRelatedValue().getPortalType()
676
    )
677

678 679
class TestPackingListDifferentProcess(TestPackingList,
    TestBPMEvaluationDifferentProcessMixin):
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
  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()
    )
703

704 705
class TestInvoiceDifferentProcess(TestInvoice,
    TestBPMEvaluationDifferentProcessMixin):
706 707 708 709 710 711
  pass

def test_suite():
  suite = unittest.TestSuite()
  suite.addTest(unittest.makeSuite(TestOrderDefaultProcess))
  suite.addTest(unittest.makeSuite(TestPackingListDefaultProcess))
712 713
#  suite.addTest(unittest.makeSuite(TestInvoiceDefaultProcess))

714 715
  suite.addTest(unittest.makeSuite(TestOrderDifferentProcess))
  suite.addTest(unittest.makeSuite(TestPackingListDifferentProcess))
716 717
#  suite.addTest(unittest.makeSuite(TestInvoiceDifferentProcess))

718
  return suite