testAccountingRulesSimulationLegacy.py 78.6 KB
Newer Older
1 2
##############################################################################
#
3
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
4
#          Sebastien Robin <seb@nexedi.com>
5
#          Jerome Perrin <jerome@nexedi.com>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability 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
# garantees 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
26
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 28
#
##############################################################################
29 30 31 32 33
"""
  Tests accounting simulation rules and delivery builder.
This tests also do basic checks for XMLMatrix and Predicate matching the
way it is used in the invoice related simulation.
"""
34

35
# TODO :
Jérome Perrin's avatar
Jérome Perrin committed
36
#   * test with a Person as destination_section
37
#   * test cancelling / deleting an invoice
38
#   * test payment rule & payment builder
39
#   * test simulation purge when Payment delivered or top level Order cancelled
40
#   * test removing cells for a line
41
#
42

43 44
import unittest
import random
45

46

47
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
48
from Products.ERP5Type.tests.Sequence import SequenceList
49
from Products.ERP5.tests.utils import newSimulationExpectedFailure
50

51 52 53 54
from Testing import ZopeTestCase
from zLOG import LOG, INFO
from DateTime import DateTime

55 56

class PurchaseInvoiceTest:
57
  invoice_portal_type = 'Purchase Invoice Transaction'
58
  invoice_transaction_line_portal_type \
59
                     = "Purchase Invoice Transaction Line"
60 61
  invoice_line_portal_type = "Invoice Line"
  invoice_cell_portal_type = "Invoice Cell"
62
  payment_transaction_line_portal_type = "Accounting Transaction Line"
63 64

class SaleInvoiceTest:
65
  invoice_portal_type = 'Sale Invoice Transaction'
66 67 68 69
  invoice_transaction_line_portal_type \
                     = "Sale Invoice Transaction Line"
  invoice_line_portal_type = "Invoice Line"
  invoice_cell_portal_type = "Invoice Cell"
70
  payment_transaction_line_portal_type = "Accounting Transaction Line"
71

72
class TestAccountingRulesMixin:
73
  # define portal_types
74 75 76 77 78 79 80 81 82 83 84 85
  account_module_portal_type           = "Account Module"
  accounting_module_portal_type        = "Accounting Module"
  product_module_portal_type           = "Product Module"
  currency_module_portal_type          = "Currency Module"
  organisation_portal_type             = "Organisation"
  account_portal_type                  = "Account"
  product_portal_type                  = "Product"
  currency_portal_type                 = "Currency"
  predicate_portal_type                = "Predicate"
  applied_rule_portal_type             = "Applied Rule"
  simulation_movement_portal_type      = "Simulation Movement"
  accounting_rule_cell_portal_type     = "Accounting Rule Cell"
86

87
  payment_transaction_portal_type      = "Payment Transaction"
88
  payment_transaction_line_definition_list = (
89
    ('bank', 'account_module/bank', 'account_module/bank'),
90
    )
91

92
  def getBusinessTemplateList(self):
93
    """  Return the list of business templates. """
94
    return ('erp5_base','erp5_pdm', 'erp5_simulation', 'erp5_trade',
95 96 97
            'erp5_accounting',
            'erp5_invoicing', 'erp5_simplified_invoicing',
            'erp5_accounting_simulation_legacy',
98
            'erp5_simulation_test')
99

100
  def getAccountModule(self):
101 102
    return getattr(self.getPortal(), 'account',
        getattr(self.getPortal(), 'account_module'))
103

104
  def getAccountingModule(self):
105 106
    return getattr(self.getPortal(), 'accounting',
        getattr(self.getPortal(), 'accounting_module'))
107 108

  def getProductModule(self):
109 110
    return getattr(self.getPortal(), 'product',
        getattr(self.getPortal(), 'product_module'))
111

112
  ## XXX move this to "Sequence class"
Jérome Perrin's avatar
Jérome Perrin committed
113
  def playSequence(self, sequence_string, quiet=0) :
114 115
    sequence_list = SequenceList()
    sequence_list.addSequenceString(sequence_string)
Jérome Perrin's avatar
Jérome Perrin committed
116
    sequence_list.play(self, quiet=quiet)
117

118

Jérome Perrin's avatar
Jérome Perrin committed
119
class TestAccountingRules(TestAccountingRulesMixin, ERP5TypeTestCase):
120 121 122 123 124 125
  """
  This should test the simulation tree and builds starting from the
  invoice.

  """
  RUN_ALL_TESTS = 1
126 127
  QUIET = 0

128 129
  def getTitle(self):
    return "Accounting Rules"
130

131 132 133
  def afterSetUp(self) :
    self.login()
    self.createCategories()
Jérome Perrin's avatar
Jérome Perrin committed
134
    self.validateRules()
135

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
  def createCategories(self) :
    """ create all categories that are needed for this test.
    It uses getCategoriesToCreate, so you should overload this method.
    """
    # create base categories
    for base_cat in self.getBaseCategoriesToCreate() :
      if not base_cat in self.getCategoryTool().objectIds() :
        self.getCategoryTool().newContent(
          portal_type = 'Base Category',
          id = base_cat)
    # create categories
    for cat_string in self.getCategoriesToCreate() :
      base_cat = cat_string.split("/")[0]
      path = self.getPortal().portal_categories[base_cat]
      for cat in cat_string.split("/")[1:] :
        if not cat in path.objectIds() :
          path = path.newContent(
            portal_type = 'Category',
154
            id = cat)
Jérome Perrin's avatar
Jérome Perrin committed
155 156
        else:
          path = getattr(path, cat)
157 158 159 160 161
    # check categories have been created
    for cat_string in self.getCategoriesToCreate() :
      self.assertNotEquals(None,
                self.getCategoryTool().restrictedTraverse(cat_string),
                cat_string)
162

163 164 165 166 167 168 169 170
  def getBaseCategoriesToCreate(self) :
    return ("hd_size", "cpu_freq")

  def getCategoriesToCreate(self):
    return (
      # regions for our organisations
      "region/europe/west/france",
      "region/africa",
171

172
      # those are mandatory for account, and accounting rules depends on
173
      # the account_type category. (ie payable, will create a Payment
174 175 176 177 178 179 180
      # Transaction accordingly)
      "account_type/asset/cash",
      "account_type/asset/receivable/refundable_vat",
      "account_type/equity",
      "account_type/expense",
      "account_type/income",
      "account_type/liability/payable/collected_vat",
181

182 183 184 185 186
      # some products lines for our products
      "product_line/storever/notebook",
      "product_line/storever/barebone",
      "product_line/storever/openbrick",
      "product_line/not_used/not_matched",
187

188 189 190 191 192 193 194
      # some categories for variating our products
      "cpu_freq/1Ghz",
      "cpu_freq/2Ghz",
      "hd_size/60Go",
      "hd_size/120Go",
    )

195
  def stepCreateInvoiceTransactionRule(self, sequence, **kw) :
196
    """
197 198 199
      Create some predicates in the Invoice Transaction Rule
    """
    invoice_transaction_rule = getattr(self.getRuleTool(),
200
            'default_invoice_transaction_simulation_rule')
201 202
    if invoice_transaction_rule.getValidationState() == 'validated':
      invoice_transaction_rule.invalidate()
203
      self.commit()
204

205 206 207 208 209
    # delete anything inside the rule first
    # clear the message queue, so that it does not contains unexistant paths
    self.tic()
    invoice_transaction_rule.deleteContent(
                [x for x in invoice_transaction_rule.objectIds()])
210
    self.assertEqual(len(invoice_transaction_rule.objectValues()), 0)
211
    self.commit()
212

213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
    # and add new content, predicate product_line
    predicate_product_notebook = invoice_transaction_rule.newContent(
      id = 'product_notebook',
      title = 'Product Notebook',
      portal_type = self.predicate_portal_type,
      string_index = 'product',
      int_index = '1',
      membership_criterion_base_category_list = ['product_line',],
      membership_criterion_category_list = ['product_line/storever/notebook'],
    )
    predicate_product_barebone = invoice_transaction_rule.newContent(
      id = 'product_barebone',
      title = 'Product Barebone',
      portal_type = self.predicate_portal_type,
      string_index = 'product',
      int_index = '2',
      membership_criterion_base_category_list = ['product_line',],
      membership_criterion_category_list = ['product_line/storever/barebone'],
    )
    # ... and destination_region
    predicate_region_france = invoice_transaction_rule.newContent(
      id = 'region_france',
      title = 'Region France',
      portal_type = self.predicate_portal_type,
      string_index = 'region',
      int_index ='1',
      membership_criterion_base_category_list = ['destination_region',],
      membership_criterion_category_list =
                    ['destination_region/region/europe/west/france'],
    )
    predicate_region_africa = invoice_transaction_rule.newContent(
      id = 'region_africa',
      title = 'region_africa',
      portal_type = self.predicate_portal_type,
      string_index = 'region',
      int_index = '2',
      membership_criterion_base_category_list = ['destination_region',],
      membership_criterion_category_list = ['destination_region/region/africa'],
    )
    # sanity checks
253 254 255 256
    self.assertTrue(predicate_product_notebook != None)
    self.assertTrue(predicate_product_barebone != None)
    self.assertTrue(predicate_region_france  != None)
    self.assertTrue(predicate_region_africa  != None)
257 258 259 260 261 262 263 264 265 266
    predicate_list = invoice_transaction_rule.contentValues(
          filter = {'portal_type': self.predicate_portal_type})
    self.assertEqual(len(predicate_list), 4)
    sequence.edit(
      invoice_transaction_rule = invoice_transaction_rule,
      predicate_product_notebook = predicate_product_notebook,
      predicate_product_barebone = predicate_product_barebone,
      predicate_region_france  = predicate_region_france,
      predicate_region_africa  = predicate_region_africa,
    )
267

268
  def stepUpdateInvoiceTransactionRuleMatrix(self, sequence, **kw) :
Jérome Perrin's avatar
Jérome Perrin committed
269
    """Creates/updates the matrix of the sale invoice transaction rule """
270
    invoice_transaction_rule = sequence.get('invoice_transaction_rule')
271

272
    # update the matrix, generates the accounting rule cells
273 274 275
    invoice_transaction_rule.edit()
    invoice_transaction_rule.updateMatrix()
    self.tic()
276

277
    # check the accounting rule cells inside the matrix
278
    cell_list = invoice_transaction_rule.contentValues(
279 280 281 282 283 284 285 286 287 288
                portal_type=self.accounting_rule_cell_portal_type)
    kw = dict(
      product_notebook_region_france_cell=invoice_transaction_rule.getCell(
        'product_notebook', 'region_france', base_id='movement'),
      product_notebook_region_africa_cell=invoice_transaction_rule.getCell(
        'product_notebook', 'region_africa', base_id='movement'),
      product_barebone_region_france_cell=invoice_transaction_rule.getCell(
        'product_barebone', 'region_france', base_id='movement'),
      product_barebone_region_africa_cell=invoice_transaction_rule.getCell(
        'product_barebone', 'region_africa', base_id='movement'),
289
    )
290 291
    self.assertSameSet(cell_list, kw.values())
    sequence.edit(**kw)
292

293
  def stepValidateInvoiceTransaction(self, sequence, **kw) :
294 295 296
    """validates the sale invoice transaction rule"""
    sequence.get('invoice_transaction_rule').validate()

297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
  def stepCreateNotebookFranceCell(self, sequence, **kw):
    """ creates the content of product_notebook_region_france_cell """
    # create content in the notebook / france cell
    product_notebook_region_france_cell = sequence.get(
        'product_notebook_region_france_cell')
    product_notebook_region_france_cell_income = \
        product_notebook_region_france_cell.newContent(
            id = 'income',
            source = sequence.get('income').getRelativeUrl(),
            quantity = 1)
    product_notebook_region_france_cell_receivable = \
        product_notebook_region_france_cell.newContent(
            id = 'receivable',
            source = sequence.get('receivable').getRelativeUrl(),
            quantity = -1.196)
    product_notebook_region_france_cell_vat = \
        product_notebook_region_france_cell.newContent(
            id = 'collected_vat',
            source = sequence.get('collected_vat').getRelativeUrl(),
            quantity = 0.196)
    sequence.edit(
      invoice_transaction_rule_cell = product_notebook_region_france_cell,
      product_notebook_region_france_cell_income =
            product_notebook_region_france_cell_income,
      product_notebook_region_france_cell_receivable =
            product_notebook_region_france_cell_receivable,
      product_notebook_region_france_cell_vat =
            product_notebook_region_france_cell_vat,
    )
326

327
  def stepCreateBareboneFranceCell(self, sequence, **kw):
328
    """ creates the content of product_barebone_region_france_cell,
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
      the same as product_notebook_region_france_cell, but the income
      account is differrent """
    # create content in the notebook / france cell
    product_barebone_region_france_cell = sequence.get(
        'product_barebone_region_france_cell')
    product_barebone_region_france_cell_income = \
        product_barebone_region_france_cell.newContent(
            id = 'income',
            source = sequence.get('income_barebone').getRelativeUrl(),
            quantity = 1)
    product_barebone_region_france_cell_receivable = \
        product_barebone_region_france_cell.newContent(
            id = 'receivable',
            source = sequence.get('receivable').getRelativeUrl(),
            quantity = -1.196)
    product_barebone_region_france_cell_vat = \
        product_barebone_region_france_cell.newContent(
            id = 'collected_vat',
            source = sequence.get('collected_vat').getRelativeUrl(),
            quantity = 0.196)
    sequence.edit(
      product_barebone_region_france_cell = product_barebone_region_france_cell,
      product_barebone_region_france_cell_income =\
                product_barebone_region_france_cell_income,
      product_barebone_region_france_cell_vat =\
                product_barebone_region_france_cell_vat,
      product_barebone_region_france_cell_receivable =\
                product_barebone_region_france_cell_receivable
    )
358 359


360 361 362 363 364 365 366 367 368 369 370 371 372
  def stepCreateAccounts(self, sequence, **kw):
    """
      Create an income, an payable and a collected_vat account
    """
    portal = self.getPortal()
    account_module = self.getAccountModule()
    if not hasattr(account_module, 'income') :
      income = account_module.newContent(
        id = "income",
        portal_type = self.account_portal_type,
        title = "Income Notebook",
        account_type = "income",
      )
373
      income_barebone = account_module.newContent(
374 375 376 377 378 379 380 381 382 383 384
        id = "income_barebone",
        portal_type = self.account_portal_type,
        title = "Income Barebone",
        account_type = "income",
      )
      receivable = account_module.newContent(
        id = "receivable",
        portal_type=self.account_portal_type,
        title = "Receivable",
        account_type = "asset/receivable",
      )
385 386 387 388 389 390
      bank = account_module.newContent(
        id = "bank",
        portal_type=self.account_portal_type,
        title = "Bank",
        account_type = "asset/cash/bank",
      )
391 392 393 394 395 396 397 398 399 400 401
      collected_vat = account_module.newContent(
        id = "collected_vat",
        portal_type=self.account_portal_type,
        title = "Collected VAT",
        account_type = "liability/payable/collected_vat",
      )
    # store accounts in sequence object
    sequence.edit(
      income          = account_module.income,
      income_barebone = account_module.income_barebone,
      receivable      = account_module.receivable,
402
      bank   = account_module.bank,
403 404
      collected_vat   = account_module.collected_vat,
    )
405

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
  def stepCreateEntities(self, sequence, **kw) :
    """ Create a vendor and a client organisation.
      The region of the client is the same as the region
      defined in the rule.
    """
    organisation_module = self.getOrganisationModule()
    if not hasattr(organisation_module, 'vendor') :
      vendor = organisation_module.newContent(
        portal_type = self.organisation_portal_type,
        id = "vendor",
        title = "Vendor",
        region = "europe/west/france",
      )
      self.assertNotEquals(vendor.getDefaultRegionValue(), None)
      client_fr = organisation_module.newContent(
        portal_type = self.organisation_portal_type,
        id = "client_fr",
        title = "French Client",
        region = "europe/west/france",
      )
      self.assertNotEquals(client_fr.getDefaultRegionValue(), None)
    sequence.edit(
      vendor      = organisation_module.vendor,
      client_fr   = organisation_module.client_fr,
      client      = organisation_module.client_fr,
    )
432

433 434
  def stepCreateProducts(self, sequence, **kw) :
    """
435
      Create 2 kind of products, a notebook (Varianted)
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
      and a barebone not varianted.
    """
    product_module = self.getProductModule()
    if not hasattr(product_module, 'notebook') :
      # Create some products
      notebook = product_module.newContent(
        id = 'notebook',
        title = 'Notebook',
        portal_type = self.product_portal_type,
        product_line = 'storever/notebook',
        base_price = 3.0,
      )
      # sets some variation categories on the notebook product
      notebook.setVariationBaseCategoryList(["hd_size", "cpu_freq"])
      notebook.setVariationCategoryList([
        "cpu_freq/1Ghz",
        "cpu_freq/2Ghz",
        "hd_size/60Go",
        "hd_size/120Go",])
455

456 457 458 459 460 461 462 463 464 465 466 467 468
      barebone = product_module.newContent(
        id = 'barebone',
        title = 'Barebone',
        portal_type = self.product_portal_type,
        product_line = 'storever/barebone',
        base_price = 5.0,
      )
    sequence.edit(
      notebook = product_module.notebook,
      barebone = product_module.barebone,
      product_notebook = product_module.notebook,
      product_barebone = product_module.barebone,
    )
469

470 471 472 473 474 475 476 477 478 479
  def stepCreateCurrencies(self, sequence, **kw) :
    """
      Create EUR currency
    """
    currency_module = self.getCurrencyModule()
    if not hasattr(currency_module, 'EUR') :
      currency_module.newContent(
        id = 'EUR',
        title = 'Euro',
        portal_type = self.currency_portal_type,
480
        base_unit_quantity = .01,
481 482
      )
    sequence.edit(euro=currency_module.EUR, currency=currency_module.EUR)
483

484
  def stepCreatePaymentRule(self, sequence, **kw) :
485
    """ create a rule payment transaction generation """
486 487 488 489
    payment_rule = getattr(self.getRuleTool(),
                           'default_payment_simulation_rule')
    if payment_rule.getValidationState() == 'validated':
      payment_rule.invalidate()
490
      self.commit()
491 492 493 494 495

    # delete anything inside the rule first
    # clear the message queue, so that it does not contains unexistant paths
    self.tic()
    payment_rule.deleteContent(
496 497
      [x.getId() for x in payment_rule.objectValues(
      portal_type=['Predicate', self.accounting_rule_cell_portal_type])])
498
    self.commit()
499

Kazuhiko Shiozaki's avatar
Kazuhiko Shiozaki committed
500 501
    # and add a new predicate
    payment_rule.newContent(
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
      id = 'all',
      title = 'all',
      portal_type = self.predicate_portal_type,
      string_index = 'all',
      int_index = '1',
    )
    sequence.edit(payment_rule=payment_rule)

  def stepUpdatePaymentRuleMatrix(self, sequence, **kw) :
    """Creates/updates the matrix of the sale invoice transaction rule """
    payment_rule = sequence.get('payment_rule')

    # update the matrix, generates the accounting rule cells
    payment_rule.edit()
    payment_rule.updateMatrix()
    self.tic()

    # check the accounting rule cells inside the matrix
    cell_list = payment_rule.contentValues(
                filter = {'portal_type':self.accounting_rule_cell_portal_type})
    self.assertEqual(len(cell_list), 1)
    cell = cell_list[0]

525
    for line_id, line_source_id, line_destination_id in \
526 527
        self.payment_transaction_line_definition_list:
      line = cell.newContent(id=line_id,
528
          portal_type='Accounting Transaction Line',
529 530 531 532
          source=line_source_id,
          destination=line_destination_id)
    payment_rule.validate()
    self.tic()
533

Jérome Perrin's avatar
Jérome Perrin committed
534 535 536 537 538
  def stepCreateEmptyInvoice(self, sequence, **kw) :
    """ Create an empty invoice that will be modified later """
    vendor = sequence.get('vendor')
    client = sequence.get('client')
    currency = sequence.get('currency')
539

Jérome Perrin's avatar
Jérome Perrin committed
540 541
    empty_invoice = self.getAccountingModule().newContent(
                id = 'empty_invoice',
542
                portal_type = self.invoice_portal_type,
Jérome Perrin's avatar
Jérome Perrin committed
543 544 545 546 547
                resource = currency.getRelativeUrl(),
                stop_date = DateTime(2004, 01, 01),
                start_date = DateTime(2004, 01, 01),
                source_section = vendor.getRelativeUrl(),
                destination_section = client.getRelativeUrl(),
548
                created_by_builder = 1,
Jérome Perrin's avatar
Jérome Perrin committed
549
              )
550

551 552
    empty_invoice.setPaymentConditionEfficiency(1.0)

Jérome Perrin's avatar
Jérome Perrin committed
553 554 555 556
    sequence.edit(
      simple_invoice = empty_invoice,
      invoice = empty_invoice,
    )
557

558
  def stepCreateSimpleInvoice(self, sequence, **kw) :
559 560
    """ creates a simple sale invoice for non varianted notebook product.
      The invoice is from `vendor` to `client_fr`, so the cell defined in
561
      stepUpdateInvoiceTransactionRuleMatrix should match.
562 563 564 565 566 567 568
      This invoice containts one line, 10 notebook * 10 EUR, so total price
      is 100
    """
    vendor = sequence.get('vendor')
    client = sequence.get('client')
    product_notebook = sequence.get('product_notebook')
    currency = sequence.get('currency')
569

570 571
    simple_invoice = self.getAccountingModule().newContent(
                id = 'simple_invoice',
572
                portal_type = self.invoice_portal_type,
573
                resource = currency.getRelativeUrl(),
574
                price_currency = currency.getRelativeUrl(),
575 576 577 578
                stop_date = DateTime(2004, 01, 01),
                start_date = DateTime(2004, 01, 01),
                source_section = vendor.getRelativeUrl(),
                destination_section = client.getRelativeUrl(),
579
                created_by_builder = 1,
580
              )
581

582 583 584 585
    invoice_line = simple_invoice.newContent(
      id = 'invoice_line',
      resource = product_notebook.getRelativeUrl(),
      quantity = 10,
Jérome Perrin's avatar
Jérome Perrin committed
586
      price = 10,
587
      portal_type = self.invoice_line_portal_type)
588

589 590
    simple_invoice.setPaymentConditionEfficiency(1.0)

591
    self.assertEqual(invoice_line.getTotalPrice(), 100)
592

593 594 595 596 597 598
    sequence.edit(
      simple_invoice = simple_invoice,
      invoice = simple_invoice,
      invoice_line = invoice_line,
      invoice_lines = [invoice_line]
    )
599

600
  def stepCreateOtherSimpleInvoice(self, sequence, **kw) :
Jérome Perrin's avatar
Jérome Perrin committed
601 602 603 604 605 606 607
    """ creates a simple sale invoice for non varianted notebook product.
      It will contain one line that will later be changed.
    """
    vendor = sequence.get('vendor')
    client = sequence.get('client')
    product_notebook = sequence.get('product_notebook')
    currency = sequence.get('currency')
608

Jérome Perrin's avatar
Jérome Perrin committed
609 610
    simple_invoice = self.getAccountingModule().newContent(
                id = 'other_simple_invoice',
611
                portal_type = self.invoice_portal_type,
Jérome Perrin's avatar
Jérome Perrin committed
612 613 614 615 616
                resource = currency.getRelativeUrl(),
                stop_date = DateTime(2004, 01, 01),
                start_date = DateTime(2004, 01, 01),
                source_section = vendor.getRelativeUrl(),
                destination_section = client.getRelativeUrl(),
617
                created_by_builder = 1,
Jérome Perrin's avatar
Jérome Perrin committed
618
              )
619

Jérome Perrin's avatar
Jérome Perrin committed
620 621 622 623 624
    invoice_line = simple_invoice.newContent(
      id = 'invoice_line',
      resource = product_notebook.getRelativeUrl(),
      quantity = 123,
      price = 456,
625
      portal_type = self.invoice_line_portal_type)
Jérome Perrin's avatar
Jérome Perrin committed
626

627 628
    simple_invoice.setPaymentConditionEfficiency(1.0)

Jérome Perrin's avatar
Jérome Perrin committed
629 630 631 632 633 634 635 636
    sequence.edit(
      simple_invoice = simple_invoice,
      invoice = simple_invoice,
      invoice_line = invoice_line,
      invoice_lines = [invoice_line]
    )

  def stepAddInvoiceLine(self, sequence, **kw) :
637
    """ add an invoice line in the current invoice :
Jérome Perrin's avatar
Jérome Perrin committed
638 639 640 641
      10 notebook * 10 EUR, so total price is 100
    """
    product_notebook = sequence.get('product_notebook')
    invoice = sequence.get('invoice')
642

Jérome Perrin's avatar
Jérome Perrin committed
643 644
    invoice_line = invoice.newContent(
      id = 'invoice_line_%s'%(int(random.random()*1000)),
645
      portal_type = self.invoice_line_portal_type)
Jérome Perrin's avatar
Jérome Perrin committed
646

647 648 649 650 651
    invoice_line.edit(
      resource = product_notebook.getRelativeUrl(),
      quantity = 10,
      price = 10
    )
652

Jérome Perrin's avatar
Jérome Perrin committed
653
    self.assertEqual(invoice_line.getTotalPrice(), 100)
654

Jérome Perrin's avatar
Jérome Perrin committed
655 656 657 658 659 660
    sequence.edit(
      invoice_line = invoice_line,
      invoice_lines = [invoice_line]
    )

  def stepEditInvoiceLine(self, sequence, **kw) :
661
    """ edit the invoice line :
Jérome Perrin's avatar
Jérome Perrin committed
662 663 664 665 666 667 668
      10 notebook * 10 EUR, so total price is 100
    """
    invoice = sequence.get('invoice')
    invoice_line = sequence.get('invoice_line')
    invoice_line.edit(
      quantity = 10,
      price = 10)
669

Jérome Perrin's avatar
Jérome Perrin committed
670 671
    self.assertEqual(invoice_line.getTotalPrice(), 100)

672

Jérome Perrin's avatar
Jérome Perrin committed
673 674 675 676 677
  def stepDeleteInvoiceLine(self, sequence, **kw) :
    """ remove an invoice line from the invoice
    """
    invoice = sequence.get('invoice')
    invoice_line = sequence.get('invoice_line')
678 679
    invoice._delObject(invoice_line.getId())
    invoice.recursiveReindexObject()
680

681
  def stepCreateSimpleInvoiceTwoLines(self, sequence, **kw) :
682 683
    """
      similar to stepCreateSimpleInvoice, but replace
684 685 686 687 688 689 690
      "10 notebook * 10 EUR, so total price is 100" by :
      "5 notebook * 10 EUR + 5 notebook * 10 EUR , so total price is 100"
    """
    vendor = sequence.get('vendor')
    client = sequence.get('client')
    product_notebook = sequence.get('product_notebook')
    currency = sequence.get('currency')
691

692 693
    simple_invoice = self.getAccountingModule().newContent(
                id = 'simple_invoice_two_lines',
694
                portal_type = self.invoice_portal_type,
695 696 697 698 699
                resource = currency.getRelativeUrl(),
                stop_date = DateTime(2004, 01, 01),
                start_date = DateTime(2004, 01, 01),
                source_section = vendor.getRelativeUrl(),
                destination_section = client.getRelativeUrl(),
700
                created_by_builder = 1,
701
              )
702

703 704 705 706
    invoice_line1 = simple_invoice.newContent(
      id = 'invoice_line1',
      resource = product_notebook.getRelativeUrl(),
      quantity = 5,
Jérome Perrin's avatar
Jérome Perrin committed
707
      price = 10,
708
      portal_type = self.invoice_line_portal_type)
709 710
    invoice_line2 = simple_invoice.newContent(
      id = 'invoice_line2',
Jérome Perrin's avatar
Jérome Perrin committed
711
      REsource = product_notebook.getRelativeUrl(),
712
      quantity = 5,
Jérome Perrin's avatar
Jérome Perrin committed
713
      price = 10,
714
      portal_type = self.invoice_line_portal_type)
715

716 717
    simple_invoice.setPaymentConditionEfficiency(1.0)

718 719
    self.assertEqual(invoice_line1.getTotalPrice()
            + invoice_line2.getTotalPrice(), 100)
720

721 722 723 724 725 726
    sequence.edit(
      simple_invoice = simple_invoice,
      invoice = simple_invoice,
      invoice_lines = [invoice_line1, invoice_line2]
    )

727
  def stepCreateSimpleInvoiceTwoCells(self, sequence, **kw) :
728
    """
729
      similar to stepCreateSimpleInvoiceTwoLines, but use two
730
      differents cells on the same line instead of two differents lines.
Jérome Perrin's avatar
Jérome Perrin committed
731 732 733 734 735
    """
    vendor = sequence.get('vendor')
    client = sequence.get('client')
    product_notebook = sequence.get('product_notebook')
    currency = sequence.get('currency')
736

Jérome Perrin's avatar
Jérome Perrin committed
737 738
    simple_invoice = self.getAccountingModule().newContent(
                id = 'simple_invoice_two_cells',
739
                portal_type = self.invoice_portal_type,
Jérome Perrin's avatar
Jérome Perrin committed
740 741 742 743 744
                resource = currency.getRelativeUrl(),
                stop_date = DateTime(2004, 01, 01),
                start_date = DateTime(2004, 01, 01),
                source_section = vendor.getRelativeUrl(),
                destination_section = client.getRelativeUrl(),
745
                created_by_builder = 1,
Jérome Perrin's avatar
Jérome Perrin committed
746
              )
747

Jérome Perrin's avatar
Jérome Perrin committed
748 749 750
    invoice_line = simple_invoice.newContent(
      id = 'invoice_line',
      resource = product_notebook.getRelativeUrl(),
751
      portal_type = self.invoice_line_portal_type)
752

753 754
    simple_invoice.setPaymentConditionEfficiency(1.0)

Jérome Perrin's avatar
Jérome Perrin committed
755 756 757 758 759
    sequence.edit(
      simple_invoice = simple_invoice,
      invoice = simple_invoice,
      invoice_line = invoice_line,
      invoice_lines = [invoice_line]
760
    )
Jérome Perrin's avatar
Jérome Perrin committed
761 762 763 764 765 766
    self.stepAddCellsInInvoiceLine(sequence)

  def stepAddCellsInInvoiceLine(self, sequence, **kw):
    """ add 2 cells in the invoice line, same quantity as simple invoice
    """
    invoice_line = sequence.get('invoice_line')
767

Jérome Perrin's avatar
Jérome Perrin committed
768 769 770 771 772 773 774 775
    # initialy, the line must not contain cells
    self.assertEqual(len(invoice_line.objectIds()), 0)
    invoice_line._setVariationBaseCategoryList(['hd_size', 'cpu_freq'])
    invoice_line._setVariationCategoryList(
                    ['hd_size/60Go', 'hd_size/120Go', 'cpu_freq/1Ghz' ])
    base_id = 'movement'
    invoice_line.updateCellRange(base_id)
    cell_key_list = list(invoice_line.getCellKeyList(base_id = base_id))
776

Jérome Perrin's avatar
Jérome Perrin committed
777 778 779 780 781
    # this is probably not the easiest way to create cells ...
    price = 10
    quantity = 5
    for cell_key in cell_key_list:
      cell = invoice_line.newCell(base_id = base_id,
782
             portal_type = self.invoice_cell_portal_type, *cell_key)
Jérome Perrin's avatar
Jérome Perrin committed
783 784 785 786
      cell.edit(mapped_value_property_list = ['price','quantity'],
                price = price, quantity = quantity,
                predicate_category_list = cell_key,
                variation_category_list = cell_key)
787

Jérome Perrin's avatar
Jérome Perrin committed
788 789
    # getTotalPrice uses mysql, so we must make sure the invoice is cataloged
    # to have correct results
790
    invoice_line.getParentValue().recursiveImmediateReindexObject()
Jérome Perrin's avatar
Jérome Perrin committed
791 792
    self.assertEqual(invoice_line.getTotalPrice(), 100)
    self.assertEqual(invoice_line.getTotalQuantity(), 10)
793

Jérome Perrin's avatar
Jérome Perrin committed
794 795 796
    # then we must have 2 cells inside our line
    self.assertEqual(len(invoice_line.objectIds()), 2)

797
  def stepCreateMultiLineInvoice(self, sequence, **kw) :
798
    """ create an invoice with varianted products
799 800
      The invoice is from `vendor` to `client_fr`, so the cell defined in
      This invoice containts two lines :
801
      10 notebook * 10 EUR, so total price is 100
802
            (matched by product_notebook_region_france_cell)
803
      10 barebone * 100 EUR, so total price is 1000
804 805 806 807 808 809 810 811
            (matched by product_notebook_region_france_cell)
      total price for the invoice is 100 + 1000
    """
    vendor = sequence.get('vendor')
    client = sequence.get('client')
    product_notebook = sequence.get('product_notebook')
    product_barebone = sequence.get('product_barebone')
    currency = sequence.get('currency')
812

813 814
    multi_line_invoice = self.getAccountingModule().newContent(
                id = 'multi_line_invoice',
815
                portal_type = self.invoice_portal_type,
816 817 818 819 820 821
                resource = currency.getRelativeUrl(),
                price_currency = currency.getRelativeUrl(),
                stop_date = DateTime(2004, 01, 01),
                start_date = DateTime(2004, 01, 01),
                source_section = vendor.getRelativeUrl(),
                destination_section = client.getRelativeUrl(),
822
                created_by_builder = 1,
823
              )
824

825
    notebook_line = multi_line_invoice.newContent(
826
      portal_type = self.invoice_line_portal_type,
827 828 829 830 831 832
      id = 'notebook_line',
      resource = product_notebook.getRelativeUrl(),
      quantity = 10,
      price = 10)

    barebone_line = multi_line_invoice.newContent(
833
      portal_type = self.invoice_line_portal_type,
834 835 836 837 838
      id = 'barebone_line',
      resource = product_barebone.getRelativeUrl(),
      quantity = 10,
      price = 100)

839 840
    multi_line_invoice.setPaymentConditionEfficiency(1.0)

841 842
    self.assertEqual( 10*10 + 10*100,
        notebook_line.getTotalPrice() + barebone_line.getTotalPrice())
843

844 845 846 847 848
    sequence.edit(
      multi_line_invoice = multi_line_invoice,
      invoice = multi_line_invoice,
      invoice_lines = [notebook_line, barebone_line],
    )
849

850 851 852 853 854 855 856 857 858 859 860 861 862
  def stepCheckAddPredicate(self, sequence, **kw) :
    invoice_transaction_rule = sequence.get('invoice_transaction_rule')
    # next, we add a predicate to see if it is still okay (3x2 cells)
    predicate_product3 = invoice_transaction_rule.newContent(
      id = 'product_3',
      title = 'product_3',
      portal_type = self.predicate_portal_type,
      string_index = 'product',
      int_index = '3',
      membership_criterion_base_category_list = ['product_line',],
      membership_criterion_category_list = ['product_line/storever/openbrick'],
    )
    invoice_transaction_rule.updateMatrix()
863
    self.tic()
864 865 866 867 868 869 870 871 872 873 874 875 876
    cell_list = invoice_transaction_rule.contentValues(
        filter = {'portal_type': self.accounting_rule_cell_portal_type})
    self.assertEqual(len(cell_list), 6)

  def stepCheckRemovePredicate(self, sequence, **kw) :
    invoice_transaction_rule = sequence.get('invoice_transaction_rule')
    self.tic() # make sure message queue is empty
    # then, we remove a predicate and check again (3x3 cells)
    invoice_transaction_rule.deleteContent(id = 'region_africa')
    invoice_transaction_rule.updateMatrix()
    cell_list = invoice_transaction_rule.contentValues(
                 filter = {'portal_type':self.accounting_rule_cell_portal_type})
    self.assertEqual(len(cell_list), 3)
877

878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
  def stepCheckRestoreOriginalPredicates(self, sequence, **kw) :
    """ we put back the matrix in the original format (2x2 cells) """
    invoice_transaction_rule = sequence.get("invoice_transaction_rule")
    self.tic() # make sure message queue is empty
    invoice_transaction_rule.deleteContent(id='product_3')
    predicate_region_africa = invoice_transaction_rule.newContent(
      id = 'region_africa', title = 'Region Africa',
      portal_type = self.predicate_portal_type,
      string_index = 'region', int_index = '2',
      membership_criterion_base_category_list = ['destination_region',],
      membership_criterion_category_list = ['destination_region/region/africa'],
    )
    invoice_transaction_rule.updateMatrix()
    cell_list = invoice_transaction_rule.contentValues(
        filter = {'portal_type':self.accounting_rule_cell_portal_type})
    self.assertEqual(len(cell_list), 4)
894

895 896 897 898 899 900
  def stepCreateDummyInvoice(self, sequence, **kw) :
    """ Create a dummy invoice for temp movements """
    invoice = self.getAccountingModule().newContent(
                      id = "dummy_invoice",
                   )
    sequence.edit(invoice = invoice)
901

902
  def stepCreateMatchableInvoiceMovements(self, sequence, **kw) :
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
    """ Create a temp movement that will be matched by the
      default_invoice_transaction_rule """
    from Products.ERP5Type.Document import newTempMovement
    product_notebook_region_france_movement = newTempMovement(
      sequence.get('invoice'),
      'test1',
      resource = sequence.get('notebook').getRelativeUrl(),
      destination = sequence.get('client_fr').getRelativeUrl(),
    )
    product_barebone_region_france_movement = newTempMovement(
      sequence.get('invoice'),
      'test2',
      resource = sequence.get('barebone').getRelativeUrl(),
      destination = sequence.get('client_fr').getRelativeUrl(),
    )
    sequence.edit(
      product_notebook_region_france_movement =
                  product_notebook_region_france_movement ,
      product_barebone_region_france_movement =
                  product_barebone_region_france_movement ,
    )
924

925
  def stepCheckMatchableInvoiceMovements(self, sequence, **kw) :
926 927 928 929 930 931
    """ Check that we have a matching cell for the movement """
    invoice_transaction_rule = sequence.get("invoice_transaction_rule")
    product_barebone_region_france_movement  = sequence.get(
                          'product_barebone_region_france_movement')
    product_notebook_region_france_movement  = sequence.get(
                          'product_notebook_region_france_movement')
932

933 934 935 936 937 938 939
    # Make sure acquisition is working for destination_region
    self.assertEqual(
          product_barebone_region_france_movement.getDestinationRegion(),
          'region/europe/west/france')
    self.assertEqual(
          product_notebook_region_france_movement.getDestinationRegion(),
          'region/europe/west/france')
940

941 942 943 944 945
    # Make sure category is working for resource
    self.assertEqual(product_barebone_region_france_movement.getProductLine(),
            'storever/barebone')
    self.assertEqual(product_notebook_region_france_movement.getProductLine(),
            'storever/notebook')
946 947

    # check the predicates
948 949 950 951
    predicate_product_notebook = sequence.get("predicate_product_notebook")
    predicate_product_barebone = sequence.get("predicate_product_barebone")
    predicate_region_france  = sequence.get("predicate_region_france")
    predicate_region_africa  = sequence.get("predicate_region_africa")
952

953 954 955 956 957 958 959 960
    self.assert_(not predicate_region_africa.test(
                      product_barebone_region_france_movement ))
    self.assert_( predicate_region_france.test(
                      product_barebone_region_france_movement ))
    self.assert_(not predicate_product_notebook.test(
                      product_barebone_region_france_movement ))
    self.assert_( predicate_product_barebone.test(
                      product_barebone_region_france_movement ))
961

962 963 964 965 966 967 968 969
    self.assert_(not predicate_region_africa.test(
                      product_notebook_region_france_movement ))
    self.assert_( predicate_region_france.test(
                      product_notebook_region_france_movement ))
    self.assert_(not predicate_product_barebone.test(
                      product_notebook_region_france_movement ))
    self.assert_( predicate_product_notebook.test(
                      product_notebook_region_france_movement ))
970

971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987
    # check the cells
    product_notebook_region_france_cell = sequence.get(
                     'product_notebook_region_france_cell')
    product_barebone_region_france_cell = sequence.get(
                     'product_barebone_region_france_cell')
    product_notebook_region_africa_cell = sequence.get(
                     'product_notebook_region_africa_cell')
    product_barebone_region_africa_cell = sequence.get(
                     'product_barebone_region_africa_cell')
    self.assert_(not product_notebook_region_france_cell.test(
                        product_barebone_region_france_movement ))
    self.assert_(    product_barebone_region_france_cell.test(
                        product_barebone_region_france_movement ))
    self.assert_(not product_notebook_region_africa_cell.test(
                        product_barebone_region_france_movement ))
    self.assert_(not product_barebone_region_africa_cell.test(
                        product_barebone_region_france_movement ))
988

989 990 991 992 993 994 995 996
    self.assert_(    product_notebook_region_france_cell.test(
                        product_notebook_region_france_movement ))
    self.assert_(not product_barebone_region_france_cell.test(
                        product_notebook_region_france_movement ))
    self.assert_(not product_notebook_region_africa_cell.test(
                        product_notebook_region_france_movement ))
    self.assert_(not product_barebone_region_africa_cell.test(
                        product_notebook_region_france_movement ))
997

998
    # finally check the matching cell is the good one
999
    self.assertEqual(product_notebook_region_france_cell,
1000 1001 1002 1003 1004
              invoice_transaction_rule._getMatchingCell(
                product_notebook_region_france_movement ))
    self.assertEqual(product_barebone_region_france_cell,
              invoice_transaction_rule._getMatchingCell(
                product_barebone_region_france_movement ))
1005

1006
  def stepCreateNotMatchableInvoiceMovements(self, sequence, **kw) :
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
    """ create a temp movement that not any cell could match. """
    from Products.ERP5Type.Document import newTempMovement
    bad_movement1 = newTempMovement(
      sequence.get("invoice"),
      'test3',
      product = None,
      destination = sequence.get('client').getRelativeUrl(),
    )
    bad_movement2 = newTempMovement(
      sequence.get("invoice"),
      'test4',
      gap = 'gap/1',
      destination = sequence.get('client').getRelativeUrl(),
    )
    sequence.edit(
      bad_movement1 = bad_movement1,
      bad_movement2 = bad_movement2,
    )

1026
  def stepCheckNotMatchableInvoiceMovements(self, sequence, **kw) :
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
    """ check that temp movement that cannot be matched is not matched. """
    invoice_transaction_rule = sequence.get('invoice_transaction_rule')
    self.assertEqual(None,
      invoice_transaction_rule._getMatchingCell(
        sequence.get('bad_movement1')))
    self.assertEqual(None,
      invoice_transaction_rule._getMatchingCell(
        sequence.get('bad_movement2')))

  def stepClearSimulation(self, sequence, **kw) :
    """ clear the content of portal_simulation """
    self.tic() # make sure message queue is empty
    self.getSimulationTool().deleteContent(
        list(self.getSimulationTool().objectIds()))
1041

1042 1043 1044 1045 1046 1047
  def stepClearAccountingModule(self, sequence, **kw) :
    """ clear the content of accounting module """
    self.tic() # make sure message queue is empty
    # delete
    self.getAccountingModule().deleteContent(
        list(self.getAccountingModule().objectIds()))
1048

1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
  def stepCheckFirstRuleIsApplied(self, sequence, **kw) :
    """ check every level of the simulation """
    invoice = sequence.get('invoice')
    invoice_line = sequence.get('invoice_line')
    invoice_transaction_rule = sequence.get('invoice_transaction_rule')
    vendor = sequence.get('vendor')
    client = sequence.get('client')
    currency = sequence.get('currency')
    invoice_transaction_rule_cell = sequence.get(
                                  'invoice_transaction_rule_cell')
1059

1060 1061
    # content of the simulation tool is a list of invoice rules
    applied_rule_list = self.getSimulationTool().contentValues()
1062

1063 1064 1065 1066
    self.assertEqual(len(applied_rule_list), 1)
    applied_rule = applied_rule_list[0]
    self.assertEqual( applied_rule.getPortalType(),
                      self.applied_rule_portal_type)
1067 1068
    self.assertEqual( applied_rule.getSpecialiseReference(),
                      'default_invoice_rule')
1069 1070
    self.assertEqual( applied_rule.getCausality(),
                      invoice.getRelativeUrl())
1071

1072 1073 1074
    # inside the rule there are simulation movements
    simulation_movement_list = applied_rule.contentValues()
    # the first one is a mirror of the movement in the invoice line
1075 1076 1077 1078 1079
    # this applied rule can also contain movement related to those
    # created in the init script, so we only take into account sim.
    # movement linked to an invoice line
    invoice_line_simulation_movement_list = []
    for simulation_movement in simulation_movement_list :
1080 1081
      self.assertNotEquals(simulation_movement.getDeliveryValue(), None)
      if simulation_movement.getDeliveryValue().getPortalType() == \
1082
              self.invoice_line_portal_type :
1083
        invoice_line_simulation_movement_list.append(simulation_movement)
1084

1085 1086
    self.assertEqual( len(invoice_line_simulation_movement_list), 1)
    simulation_movement = invoice_line_simulation_movement_list[0]
1087

1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
    self.assertEqual( simulation_movement.getPortalType(),
                      self.simulation_movement_portal_type)
    self.assertEqual( invoice_line.getResource(),
                      simulation_movement.getResource())
    self.assertEqual( invoice_line.getQuantity(),
                      simulation_movement.getQuantity())
    self.assertEqual( invoice_line.getStopDate(),
                      simulation_movement.getStopDate())
    self.assertEqual( invoice_line.getStartDate(),
                      simulation_movement.getStartDate())
    self.assertEqual( invoice_line.getSourceSection(),
                      simulation_movement.getSourceSection())
    self.assertEqual( invoice_line.getDestinationSection(),
                      simulation_movement.getDestinationSection())
    self.assertEqual( invoice_line.getSource(),
                      simulation_movement.getSource())
    self.assertEqual( invoice_line.getDestination(),
                      simulation_movement.getDestination())
1106

1107 1108
    # inside this movement there are applied rules which specialize
    # invoice_transaction_rule and trade_model_rule...
1109
    applied_rule_list = simulation_movement.contentValues()
1110
    self.assertEqual( len(applied_rule_list), 2)
1111
    # ...but only invoice_transaction_rule is interesting
1112 1113
    applied_rule = [applied_rule for applied_rule in applied_rule_list if
      applied_rule.getSpecialiseValue().getPortalType() ==
1114
      'Invoice Transaction Simulation Rule'][0]
1115
    self.assertEqual( applied_rule.getPortalType(),
1116
                      self.applied_rule_portal_type)
1117
    self.assertEqual( applied_rule.getSpecialise(),
1118
                      invoice_transaction_rule.getRelativeUrl())
1119 1120

    # and in this applied rule, we got simulation movements,
1121 1122 1123 1124 1125
    # based on those inside product_notebook_region_france_cell
    simulation_movement_list = applied_rule.contentValues()
    self.assertEqual( len(simulation_movement_list), 3)
    rule_movement_found = {}
    for simulation_movement in simulation_movement_list :
1126
      self.assertEqual( simulation_movement.getSourceSection(),
1127
                        vendor.getRelativeUrl())
1128
      self.assertEqual( simulation_movement.getDestinationSection(),
1129
                        client.getRelativeUrl())
1130
      self.assertEqual( simulation_movement.getResource(),
1131
                        currency.getRelativeUrl())
1132
      self.assertEqual( simulation_movement.getCausalityState(),
1133 1134 1135 1136
                         'expanded')
      for rule_movement in invoice_transaction_rule_cell.contentValues() :
        if simulation_movement.getSource() == rule_movement.getSource() :
          rule_movement_found[rule_movement.getSource()] = 1
1137
          self.assertEqual(simulation_movement.getQuantity(),
1138
                rule_movement.getQuantity() * invoice_line.getTotalPrice())
1139
          self.assertEqual(simulation_movement.getSourceCredit(),
1140
                rule_movement.getSourceCredit() * invoice_line.getTotalPrice())
1141
          self.assertEqual(simulation_movement.getSourceDebit(),
1142 1143 1144 1145
                rule_movement.getSourceDebit() * invoice_line.getTotalPrice())
      self.assert_(len(rule_movement_found.keys()), 3)
    sequence.edit( simulation_movement_list = simulation_movement_list )

1146

Jérome Perrin's avatar
Jérome Perrin committed
1147 1148 1149 1150
  def stepCollectSimulationMovements(self, sequence, **kw) :
    """ put some simulation movements in sequence for later checkings """
    invoice = sequence.get('invoice')
    invoice_line = sequence.get('invoice_line')
1151

Jérome Perrin's avatar
Jérome Perrin committed
1152
    applied_rule_list = self.getSimulationTool().contentValues()
1153
    self.assertEqual(len(applied_rule_list), 1)
Jérome Perrin's avatar
Jérome Perrin committed
1154 1155 1156 1157 1158
    simulation_movement_list = []
    simulation_movement_quantities = {}
    simulation_movement_resources = {}
    simulation_movement_paths = {}
    simulation_movement_section_paths = {}
1159

Jérome Perrin's avatar
Jérome Perrin committed
1160 1161 1162 1163 1164 1165
    applied_rule = applied_rule_list[0]
    for invoice_simulation_movement in applied_rule.objectValues() :
      for invoice_transaction_applied_rule in \
                        invoice_simulation_movement.objectValues() :
        for simulation_movement in \
                   invoice_transaction_applied_rule.objectValues() :
1166
          path = simulation_movement.getPath()
Jérome Perrin's avatar
Jérome Perrin committed
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
          simulation_movement_list.append(simulation_movement)
          simulation_movement_quantities[path] = \
                                    simulation_movement.getQuantity()
          simulation_movement_resources[path] = \
                                    simulation_movement.getResource()
          simulation_movement_paths[path] = (
                      simulation_movement.getSource(),
                      simulation_movement.getDestination())
          simulation_movement_section_paths[path] = (
                      simulation_movement.getSourceSection(),
                      simulation_movement.getDestinationSection())
    sequence.edit(
         simulation_movement_list = simulation_movement_list
      ,  simulation_movement_quantities = simulation_movement_quantities
      ,  simulation_movement_resources = simulation_movement_resources
      ,  simulation_movement_paths = simulation_movement_paths
      ,  simulation_movement_section_paths = simulation_movement_section_paths
    )

  def stepCheckSimulationMovements(self, sequence, **kw) :
    """ checks simulation movements from the sequence object """
    simulation_movement_list = sequence.get(
                                        'simulation_movement_list')
    simulation_movement_quantities = sequence.get(
                                  'simulation_movement_quantities')
    simulation_movement_resources = sequence.get(
                                   'simulation_movement_resources')
    simulation_movement_paths = sequence.get(
                                       'simulation_movement_paths')
    simulation_movement_section_paths = sequence.get(
                               'simulation_movement_section_paths')
1198

Jérome Perrin's avatar
Jérome Perrin committed
1199
    for simulation_movement in simulation_movement_list :
1200
      path = simulation_movement.getPath()
1201
      self.assertEqual(
Jérome Perrin's avatar
Jérome Perrin committed
1202 1203 1204
        simulation_movement.getQuantity(),
        simulation_movement_quantities[path]
      )
1205
      self.assertEqual(
Jérome Perrin's avatar
Jérome Perrin committed
1206 1207 1208
        simulation_movement.getResource(),
        simulation_movement_resources[path]
      )
1209
      self.assertEqual(
Jérome Perrin's avatar
Jérome Perrin committed
1210 1211 1212
        (simulation_movement.getSource(), simulation_movement.getDestination()),
        simulation_movement_paths[path]
      )
1213
      self.assertEqual(
Jérome Perrin's avatar
Jérome Perrin committed
1214 1215 1216 1217
           ( simulation_movement.getSourceSection(),
             simulation_movement.getDestinationSection()),
        simulation_movement_section_paths[path]
      )
1218

1219 1220 1221
  def stepCheckPaymentRuleIsApplied(self, sequence, **kw) :
    """ checks that a payment rule is applied for the total amount
      of receivable """
1222 1223 1224 1225
    invoice_line = sequence.get('invoice_line')
    simulation_movement = invoice_line.getDeliveryRelatedValue()
    applied_rule_list = simulation_movement.objectValues()
    # Invoice Transaction Rule and Trade Model Rule
1226
    self.assertEqual(2, len(applied_rule_list))
1227 1228 1229 1230 1231 1232
    applied_rule = [x for x in applied_rule_list \
                    if x.getSpecialiseReference() == \
                    'default_invoice_transaction_rule'][0]
    simulation_movement_list = [x for x in applied_rule.objectValues() \
                                if x.getSourceTitle() in ('Receivable',
                                                          'Payable')]
1233
    self.assertEqual(1, len(simulation_movement_list))
1234 1235
    simulation_movement = simulation_movement_list[0]
    applied_rule_list = simulation_movement.objectValues()
1236
    self.assertEqual(1, len(applied_rule_list))
1237
    payment_applied_rule = applied_rule_list[0]
1238

Jérome Perrin's avatar
Jérome Perrin committed
1239
  def stepPlanInvoice(self, sequence, **kw) :
1240
    """ put the invoice in the `planned` state, which will
Jérome Perrin's avatar
Jérome Perrin committed
1241 1242 1243 1244
      start the simulation process. """
    invoice = sequence.get('invoice')
    self.getPortal().portal_workflow.doActionFor(
      invoice, 'plan_action',
1245
      skip_period_validation=1
Jérome Perrin's avatar
Jérome Perrin committed
1246
    )
1247
    self.assertEqual(invoice.getSimulationState(), 'planned')
1248 1249

  def stepConfirmInvoice(self, sequence, **kw) :
1250 1251 1252
    """ put the invoice in the `confirmed` state, which does nothing specific,
    the delivery builder is invoked when starting the invoice.
    """
1253 1254 1255
    invoice = sequence.get('invoice')
    self.getPortal().portal_workflow.doActionFor(
      invoice, 'confirm_action',
1256 1257
      wf_id = 'accounting_workflow',
      skip_period_validation = 1
1258
    )
1259
    self.assertEqual(invoice.getSimulationState(), 'confirmed')
1260

1261 1262
  def stepCheckNoAccountingLinesBuiltYet(self, sequence, **kw) :
    invoice = sequence.get('invoice')
1263
    self.assertEqual(0, len(invoice.getMovementList(
1264
                    portal_type=invoice.getPortalAccountingMovementTypeList())))
1265

1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
  def stepStartInvoice(self, sequence, **kw) :
    """ put the invoice in the `started` state, which starts the delivery
    builder.
    """
    invoice = sequence.get('invoice')
    self.getPortal().portal_workflow.doActionFor(
      invoice, 'start_action',
      wf_id = 'accounting_workflow',
      skip_period_validation = 1
    )
1276
    self.assertEqual(invoice.getSimulationState(), 'started')
1277

1278
  def stepCheckAccountingLinesCoherantWithSimulation(self, sequence, **kw) :
1279
    """ checks that accounting lines are created on the sale invoice
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290
    transaction """
    invoice  = sequence.get('invoice')
    vendor   = sequence.get('vendor')
    client   = sequence.get('client')
    currency = sequence.get('currency')
    invoice_line = sequence.get('invoice_line')
    simulation_movement_list = sequence.get('simulation_movement_list')
    invoice_transaction_rule_cell = sequence.get(
                          'invoice_transaction_rule_cell')
    invoice_transaction_line_list = invoice.contentValues(
        filter = {'portal_type':
1291
                  self.invoice_transaction_line_portal_type})
1292
    self.assertEqual( len(invoice_transaction_line_list),
1293
                       len(simulation_movement_list))
1294

1295 1296
    simulation_movement_found = {}
    for invoice_transaction_line in invoice_transaction_line_list :
1297
      self.assertEqual( invoice_transaction_line.getSourceSection(),
1298
                         vendor.getRelativeUrl())
1299
      self.assertEqual( invoice_transaction_line.getDestinationSection(),
1300
                         client.getRelativeUrl())
1301
      self.assertEqual( invoice_transaction_line.getResource(),
1302 1303 1304 1305 1306
                         currency.getRelativeUrl())
      for simulation_movement in simulation_movement_list :
        if simulation_movement.getSource() == \
                            invoice_transaction_line.getSource() :
          simulation_movement_found[simulation_movement.getSource()] = 1
1307
          self.assertEqual(simulation_movement.getQuantity(),
1308
                            invoice_transaction_line.getQuantity())
1309
          self.assertEqual(simulation_movement.getSourceCredit(),
1310
                            invoice_transaction_line.getSourceCredit())
1311
          self.assertEqual(simulation_movement.getSourceDebit(),
1312
                            invoice_transaction_line.getSourceDebit())
1313

1314
          self.assertEqual(simulation_movement.getDelivery(),
1315 1316
                            invoice_transaction_line.getRelativeUrl())
      self.assert_(len(simulation_movement_found.keys()), 3)
1317 1318


1319 1320
  def stepCheckAccountingLinesCreatedForSimpleInvoice(
            self, sequence, **kw) :
1321
    """ Checks that accounting lines are created on the sale invoice
1322 1323 1324
    transaction and that all movement are correctly aggregated.
    The price of the invoice was 100, it should result in the following
    accounting layout :
1325

1326 1327 1328 1329 1330
      ===============   =======   =======
      account           Debit     Credit
      ===============   =======   =======
      income                       100
      collected_vat                 19.60
1331
      receivable         119.60
1332 1333 1334 1335 1336 1337 1338
      ===============   =======   =======
    """
    invoice  = sequence.get('invoice')
    vendor   = sequence.get('vendor')
    client   = sequence.get('client')
    currency = sequence.get('currency')
    invoice_lines = sequence.get('invoice_lines')
1339

1340
    invoice_transaction_line_list = invoice.contentValues(
1341
        filter = {'portal_type': self.invoice_transaction_line_portal_type})
1342
    self.assertEqual(len(invoice_transaction_line_list), 3)
1343

1344 1345 1346 1347 1348
    accounting_lines_layout = {
      'income'            : (0, 100),
      'collected_vat'     : (0, 19.60),
      'receivable'        : (119.60, 0),
    }
1349

1350
    for invoice_transaction_line in invoice_transaction_line_list :
1351 1352 1353
      self.assert_(
          invoice_transaction_line.getSourceId() in accounting_lines_layout.keys(),
          'unexepected source_id %s' % invoice_transaction_line.getSourceId())
Jérome Perrin's avatar
Jérome Perrin committed
1354 1355
      debit, credit = accounting_lines_layout[
                            invoice_transaction_line.getSourceId()]
1356 1357
      self.assertEqual(debit, invoice_transaction_line.getSourceDebit())
      self.assertEqual(credit, invoice_transaction_line.getSourceCredit())
1358 1359 1360
      self.assertNotEquals(
              len(invoice_transaction_line.getDeliveryRelatedValueList(
                              portal_type='Simulation Movement')), 0)
1361

1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
  def stepCheckPaymentLinesCreatedForSimpleInvoice(
            self, sequence, **kw) :
    """ Checks that payment transaction lines are created on the payment
    transaction and that all movement are correctly aggregated.  The
    price of the invoice was 100, it should result in the following
    accounting layout :

      ===============   =======   =======
      account           Debit     Credit
      ===============   =======   =======
      receivable                   119.60
      bank               119.60
      ===============   =======   =======
    """
    payment  = sequence.get('invoice').getCausalityRelatedValue(
        portal_type='Payment Transaction')

    payment_transaction_line_list = payment.contentValues(
        filter = {'portal_type': self.payment_transaction_line_portal_type})
1381
    self.assertEqual(len(payment_transaction_line_list), 2)
1382 1383 1384 1385 1386 1387 1388

    accounting_lines_layout = {
      'receivable'        : (0, 119.60),
      'bank'              : (119.60, 0),
    }

    for payment_transaction_line in payment_transaction_line_list :
1389 1390 1391 1392 1393 1394
      if _isMirrored(payment_transaction_line):
        self.assert_(
            payment_transaction_line.getDestinationId() in accounting_lines_layout.keys(),
            'unexepected destination_id %s' % payment_transaction_line.getDestinationId())
        debit, credit = accounting_lines_layout[
                              payment_transaction_line.getDestinationId()]
1395 1396
        self.assertEqual(debit, payment_transaction_line.getDestinationDebit())
        self.assertEqual(credit, payment_transaction_line.getDestinationCredit())
1397 1398 1399 1400 1401 1402 1403 1404 1405
        self.assertNotEquals(
                len(payment_transaction_line.getDeliveryRelatedValueList(
                                portal_type='Simulation Movement')), 0)
      else:
        self.assert_(
            payment_transaction_line.getSourceId() in accounting_lines_layout.keys(),
            'unexepected source_id %s' % payment_transaction_line.getSourceId())
        debit, credit = accounting_lines_layout[
                              payment_transaction_line.getSourceId()]
1406 1407
        self.assertEqual(debit, payment_transaction_line.getSourceDebit())
        self.assertEqual(credit, payment_transaction_line.getSourceCredit())
1408 1409 1410
        self.assertNotEquals(
                len(payment_transaction_line.getDeliveryRelatedValueList(
                                portal_type='Simulation Movement')), 0)
1411

1412 1413
  def stepCheckAccountingLinesCreatedForMultiLineInvoice(
            self, sequence, **kw) :
1414
    """ Checks that accounting lines are created on the sale invoice
1415 1416 1417
    transaction and that all movement are correctly aggregated.
    The price of the invoice was 1100, it should result in the following
    accounting layout :
1418

1419 1420 1421 1422 1423 1424
      ===============   =======   =======
      account           Debit     Credit
      ===============   =======   =======
      income                       100
      income_barebone              1000
      collected_vat                215.60
1425
      receivable        1315.60
1426 1427 1428 1429 1430 1431 1432
      ===============   =======   =======
    """
    invoice  = sequence.get('invoice')
    vendor   = sequence.get('vendor')
    client   = sequence.get('client')
    currency = sequence.get('currency')
    invoice_lines = sequence.get('invoice_lines')
1433

1434
    invoice_transaction_line_list = invoice.contentValues(
1435
        filter = {'portal_type': self.invoice_transaction_line_portal_type})
1436
    self.assertEqual(len(invoice_transaction_line_list), 4)
1437

1438 1439 1440 1441 1442 1443
    accounting_lines_layout = {
      'income'            : (0, 100),
      'income_barebone'   : (0, 1000),
      'collected_vat'     : (0, 215.60),
      'receivable'        : (1315.60, 0),
    }
1444

1445
    for invoice_transaction_line in invoice_transaction_line_list :
Jérome Perrin's avatar
Jérome Perrin committed
1446 1447
      debit, credit = accounting_lines_layout[
                                    invoice_transaction_line.getSourceId()]
1448 1449
      self.assertEqual(debit, invoice_transaction_line.getSourceDebit())
      self.assertEqual(credit, invoice_transaction_line.getSourceCredit())
1450 1451 1452
      self.assertNotEquals(
              len(invoice_transaction_line.getDeliveryRelatedValueList(
                              portal_type='Simulation Movement')), 0)
Jérome Perrin's avatar
Jérome Perrin committed
1453

1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472
  def stepCheckPaymentLinesCreatedForMultiLineInvoice(
            self, sequence, **kw) :
    """ Checks that payment transaction lines are created on the payment
    transaction and that all movement are correctly aggregated.  The
    price of the invoice was 100, it should result in the following
    accounting layout :

      ===============   =======   =======
      account           Debit     Credit
      ===============   =======   =======
      receivable                  1315.60
      bank              1315.60
      ===============   =======   =======
    """
    payment  = sequence.get('invoice').getCausalityRelatedValue(
        portal_type='Payment Transaction')

    payment_transaction_line_list = payment.contentValues(
        filter = {'portal_type': self.payment_transaction_line_portal_type})
1473
    self.assertEqual(len(payment_transaction_line_list), 2)
1474 1475 1476 1477 1478 1479 1480

    accounting_lines_layout = {
      'receivable'        : (0, 1315.60),
      'bank'              : (1315.60, 0),
    }

    for payment_transaction_line in payment_transaction_line_list :
1481 1482 1483 1484 1485 1486
      if _isMirrored(payment_transaction_line):
        self.assert_(
            payment_transaction_line.getDestinationId() in accounting_lines_layout.keys(),
            'unexepected source_id %s' % payment_transaction_line.getDestinationId())
        debit, credit = accounting_lines_layout[
                              payment_transaction_line.getDestinationId()]
1487 1488
        self.assertEqual(debit, payment_transaction_line.getDestinationDebit())
        self.assertEqual(credit, payment_transaction_line.getDestinationCredit())
1489 1490 1491 1492 1493 1494 1495 1496 1497
        self.assertNotEquals(
                len(payment_transaction_line.getDeliveryRelatedValueList(
                                portal_type='Simulation Movement')), 0)
      else:
        self.assert_(
            payment_transaction_line.getSourceId() in accounting_lines_layout.keys(),
            'unexepected source_id %s' % payment_transaction_line.getSourceId())
        debit, credit = accounting_lines_layout[
                              payment_transaction_line.getSourceId()]
1498 1499
        self.assertEqual(debit, payment_transaction_line.getSourceDebit())
        self.assertEqual(credit, payment_transaction_line.getSourceCredit())
1500 1501 1502
        self.assertNotEquals(
                len(payment_transaction_line.getDeliveryRelatedValueList(
                                portal_type='Simulation Movement')), 0)
1503

Jérome Perrin's avatar
Jérome Perrin committed
1504 1505 1506 1507 1508 1509 1510 1511 1512 1513
  def stepRebuildAndCheckNothingIsCreated(self, sequence, **kw) :
    """ Calls the DeliveryBuilder again and checks that the accounting module
    remains unchanged.
    """
    accounting_transaction_count = len(self.getAccountingModule().objectIds())
    accounting_lines_dict = {}
    for transaction in self.getAccountingModule().objectValues():
      transaction_dict = {}
      for accounting_line in transaction.objectValues() :
        if accounting_line.getPortalType() != \
1514
                          self.invoice_line_portal_type :
Jérome Perrin's avatar
Jérome Perrin committed
1515 1516 1517
          transaction_dict[accounting_line.getId()] = \
                accounting_line.getTotalQuantity()
      accounting_lines_dict[transaction.getId()] = transaction_dict
1518

Jérome Perrin's avatar
Jérome Perrin committed
1519 1520 1521 1522 1523 1524
    # reindex the simulation for testing purposes
    self.getSimulationTool().recursiveReindexObject()
    self.tic()
    delivery_tool = self.getPortal().portal_deliveries
    # and build again ...
    delivery_tool.sale_invoice_transaction_builder.build()
1525 1526
    delivery_tool.purchase_invoice_transaction_builder.build()
    delivery_tool.payment_transaction_builder.build()
Jérome Perrin's avatar
Jérome Perrin committed
1527 1528 1529 1530
    if hasattr(delivery_tool, 'pay_sheet_transaction_builder') :
      # TODO: conflict with pay_sheet_transaction_builder must be tested too
      delivery_tool.pay_sheet_transaction_builder.build()
    self.tic()
1531

Jérome Perrin's avatar
Jérome Perrin committed
1532
    # nothing should have changed
1533
    self.assertEqual(accounting_transaction_count,
Jérome Perrin's avatar
Jérome Perrin committed
1534
            len(self.getAccountingModule().objectIds()))
1535

Jérome Perrin's avatar
Jérome Perrin committed
1536 1537 1538 1539
    for transaction in self.getAccountingModule().objectValues() :
      transaction_dict = accounting_lines_dict[transaction.getId()]
      for accounting_line in transaction.objectValues() :
        if accounting_line.getPortalType() != \
1540
                          self.invoice_line_portal_type :
1541
          self.assertEqual(
Jérome Perrin's avatar
Jérome Perrin committed
1542 1543
              transaction_dict[accounting_line.getId()],
              accounting_line.getTotalQuantity())
1544

1545 1546 1547 1548 1549 1550 1551 1552
  def stepCheckInvoiceSimulation(self, sequence=None, \
                                    sequence_list=None, **kw):
    invoice = sequence.get('invoice')

    applied_rule_list = invoice.getCausalityRelatedValueList(
      portal_type = self.applied_rule_portal_type,
        )

1553
    self.assertEqual(
1554 1555 1556 1557 1558 1559
        1,
        len(applied_rule_list)
    )

    applied_rule = applied_rule_list[0]

1560
    self.assertEqual(
1561 1562
      'default_invoice_rule',
      applied_rule.getSpecialiseReference()
1563 1564
    )

1565
    self.assertEqual(
1566 1567 1568 1569 1570 1571 1572
      # reduntant?
      invoice.getRelativeUrl(),
      applied_rule.getCausality()
    )

    simulation_movement_list = applied_rule.objectValues()

1573
    self.assertEqual(
1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585
        1,
        len(simulation_movement_list)
    )

    simulation_movement = simulation_movement_list[0]

    invoice_line = sequence.get('invoice_line')
    resource = sequence.get('product_notebook')
    vendor = sequence.get('vendor')
    client = sequence.get('client')
    currency = sequence.get('currency')

1586
    self.assertEqual(
1587 1588 1589 1590
      resource.getRelativeUrl(),
      simulation_movement.getResource()
    )

1591
    self.assertEqual(
1592 1593 1594 1595
      currency.getRelativeUrl(),
      simulation_movement.getPriceCurrency()
    )

1596
    self.assertEqual(
1597 1598 1599 1600
      vendor.getRelativeUrl(),
      simulation_movement.getSourceSection()
    )

1601
    self.assertEqual(
1602 1603 1604 1605
      client.getRelativeUrl(),
      simulation_movement.getDestinationSection()
    )

1606
    self.assertEqual(
1607 1608 1609 1610
      invoice_line.getRelativeUrl(),
      simulation_movement.getDelivery()
    )

1611
    self.assertEqual(
1612 1613 1614 1615
      10,
      simulation_movement.getQuantity()
    )

1616
    self.assertEqual(
1617 1618 1619
      10,
      simulation_movement.getPrice()
    )
1620

1621
  @newSimulationExpectedFailure
Jérome Perrin's avatar
Jérome Perrin committed
1622
  def test_04_SimpleInvoice(self, quiet=QUIET, run=RUN_ALL_TESTS):
Jérome Perrin's avatar
Jérome Perrin committed
1623
    """ Simple Invoice.
1624 1625
    Try to expand an invoice containing only one simple Invoice Line.
    Check that the build is correct.
1626
    """
1627 1628
    if not run:
      return
1629
    if not quiet:
1630
      message = 'Test Simple Invoice Rule'
1631
      ZopeTestCase._print('\n%s ' % message)
1632 1633 1634 1635 1636 1637 1638
      LOG('Testing... ', INFO, message)

    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
1639 1640
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
1641 1642
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
1643
      stepValidateInvoiceTransaction
1644 1645 1646 1647
      stepCreateNotebookFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
1648
      stepCreateSimpleInvoice
1649 1650
      stepPlanInvoice
      stepTic
1651
      stepCheckFirstRuleIsApplied
1652 1653 1654
      stepCheckPaymentRuleIsApplied
      stepConfirmInvoice
      stepTic
1655 1656 1657
      stepCheckNoAccountingLinesBuiltYet
      stepStartInvoice
      stepTic
1658
      stepCheckAccountingLinesCoherantWithSimulation
1659 1660
      stepCheckAccountingLinesCreatedForSimpleInvoice
      stepCheckPaymentLinesCreatedForSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1661
      """, quiet=quiet )
1662

1663
  @newSimulationExpectedFailure
Jérome Perrin's avatar
Jérome Perrin committed
1664
  def test_04b_SimpleInvoiceConfirm(self, quiet=QUIET, run=RUN_ALL_TESTS):
Jérome Perrin's avatar
Jérome Perrin committed
1665 1666
    """  Same test as SimpleInvoice but directly confirm the invoice
    without planning it """
1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678
    if not run:
      return
    if not quiet:
      message = 'Test Simple Invoice Rule (without plan)'
      ZopeTestCase._print('\n%s ' % message)
      LOG('Testing... ', INFO, message)

    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
1679 1680
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
1681 1682
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
1683
      stepValidateInvoiceTransaction
1684 1685 1686 1687
      stepCreateNotebookFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
1688
      stepCreateSimpleInvoice
1689 1690
      stepConfirmInvoice
      stepTic
1691 1692 1693
      stepCheckNoAccountingLinesBuiltYet
      stepStartInvoice
      stepTic
1694
      stepCheckAccountingLinesCreatedForSimpleInvoice
1695
      stepCheckPaymentLinesCreatedForSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1696
      stepRebuildAndCheckNothingIsCreated
Jérome Perrin's avatar
Jérome Perrin committed
1697
      """, quiet=quiet )
1698

1699
  @newSimulationExpectedFailure
Jérome Perrin's avatar
Jérome Perrin committed
1700
  def test_04c_SimpleInvoiceTwoLines(self, quiet=QUIET, run=RUN_ALL_TESTS):
Jérome Perrin's avatar
Jérome Perrin committed
1701
    """ Simple Invoice, 2 lines.
1702 1703 1704 1705 1706 1707
    Same test as SimpleInvoice but use 2 lines of quantity 5 instead of
    1 line of quantity 10.
    """
    if not run:
      return
    if not quiet:
Jérome Perrin's avatar
Jérome Perrin committed
1708
      message = 'Test Simple Invoice Rule (with 2 lines)'
1709 1710 1711 1712 1713 1714 1715 1716
      ZopeTestCase._print('\n%s ' % message)
      LOG('Testing... ', INFO, message)

    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
1717 1718
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
1719 1720
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
1721
      stepValidateInvoiceTransaction
1722 1723 1724 1725
      stepCreateNotebookFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
1726
      stepCreateSimpleInvoiceTwoLines
1727 1728
      stepConfirmInvoice
      stepTic
1729 1730 1731
      stepCheckNoAccountingLinesBuiltYet
      stepStartInvoice
      stepTic
1732
      stepCheckAccountingLinesCreatedForSimpleInvoice
1733
      stepCheckPaymentLinesCreatedForSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1734
      stepRebuildAndCheckNothingIsCreated
Jérome Perrin's avatar
Jérome Perrin committed
1735
      """, quiet=quiet )
1736

1737
  @newSimulationExpectedFailure
Jérome Perrin's avatar
Jérome Perrin committed
1738
  def test_04d_SimpleInvoiceTwoCells(self, quiet=QUIET, run=RUN_ALL_TESTS):
Jérome Perrin's avatar
Jérome Perrin committed
1739 1740 1741
    """ Simple Invoice, 2 cells.
    Same test as SimpleInvoice but use 2 cells of quantity 5 instead of
    1 line of quantity 10.
1742
    """
Jérome Perrin's avatar
Jérome Perrin committed
1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754
    if not run:
      return
    if not quiet:
      message = 'Test Simple Invoice Rule (with 2 cells)'
      ZopeTestCase._print('\n%s ' % message)
      LOG('Testing... ', INFO, message)

    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
1755 1756
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
1757 1758
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
1759
      stepValidateInvoiceTransaction
Jérome Perrin's avatar
Jérome Perrin committed
1760 1761 1762 1763
      stepCreateNotebookFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
1764
      stepCreateSimpleInvoiceTwoCells
Jérome Perrin's avatar
Jérome Perrin committed
1765 1766
      stepConfirmInvoice
      stepTic
1767 1768 1769
      stepCheckNoAccountingLinesBuiltYet
      stepStartInvoice
      stepTic
Jérome Perrin's avatar
Jérome Perrin committed
1770
      stepCheckAccountingLinesCreatedForSimpleInvoice
1771
      stepCheckPaymentLinesCreatedForSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1772
      stepRebuildAndCheckNothingIsCreated
Jérome Perrin's avatar
Jérome Perrin committed
1773
      """, quiet=quiet )
1774 1775

  # next 5 tests will check update of applied rules.
1776
  @newSimulationExpectedFailure
Jérome Perrin's avatar
Jérome Perrin committed
1777
  def test_05a_SimpleInvoiceReExpandAddLine(self, quiet=QUIET,
1778
        run=RUN_ALL_TESTS):
1779
    """ Add a new line then updateSimulation.
Jérome Perrin's avatar
Jérome Perrin committed
1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795
    Create an empty invoice, plan, add a line so that this
    invoice is the same as `SimpleInvoice`, confirm it then check
    accounting lines
    """
    if not run:
      return
    if not quiet:
      message = 'Test Simple Invoice Rule (add invoice line and reexpand)'
      ZopeTestCase._print('\n%s ' % message)
      LOG('Testing... ', INFO, message)

    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
1796 1797
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
1798 1799
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
1800
      stepValidateInvoiceTransaction
Jérome Perrin's avatar
Jérome Perrin committed
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810
      stepCreateNotebookFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
      stepCreateEmptyInvoice
      stepPlanInvoice
      stepTic
      stepAddInvoiceLine
      stepConfirmInvoice
      stepTic
1811 1812 1813
      stepCheckNoAccountingLinesBuiltYet
      stepStartInvoice
      stepTic
Jérome Perrin's avatar
Jérome Perrin committed
1814
      stepCheckAccountingLinesCreatedForSimpleInvoice
1815
      stepCheckPaymentLinesCreatedForSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1816
      stepRebuildAndCheckNothingIsCreated
Jérome Perrin's avatar
Jérome Perrin committed
1817
      """, quiet=quiet )
1818

1819
  @newSimulationExpectedFailure
Jérome Perrin's avatar
Jérome Perrin committed
1820
  def test_05b_SimpleInvoiceReExpandEditLine(self, quiet=QUIET,
1821
              run = RUN_ALL_TESTS):
Jérome Perrin's avatar
Jérome Perrin committed
1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834
    """ Tests that editing a line updates simulation correctly """
    if not run:
      return
    if not quiet:
      message = 'Test Simple Invoice Rule (edit line and reexpand)'
      ZopeTestCase._print('\n%s ' % message)
      LOG('Testing... ', INFO, message)

    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
1835 1836
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
1837 1838
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
1839
      stepValidateInvoiceTransaction
Jérome Perrin's avatar
Jérome Perrin committed
1840 1841 1842 1843
      stepCreateNotebookFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
1844
      stepCreateOtherSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1845 1846 1847 1848 1849
      stepPlanInvoice
      stepTic
      stepEditInvoiceLine
      stepConfirmInvoice
      stepTic
1850 1851 1852
      stepCheckNoAccountingLinesBuiltYet
      stepStartInvoice
      stepTic
Jérome Perrin's avatar
Jérome Perrin committed
1853
      stepCheckAccountingLinesCreatedForSimpleInvoice
1854
      stepCheckPaymentLinesCreatedForSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1855
      stepRebuildAndCheckNothingIsCreated
Jérome Perrin's avatar
Jérome Perrin committed
1856
      """, quiet=quiet )
Jérome Perrin's avatar
Jérome Perrin committed
1857

1858
  @newSimulationExpectedFailure
Jérome Perrin's avatar
Jérome Perrin committed
1859
  def test_05c_SimpleInvoiceReExpandDeleteLine(
Jérome Perrin's avatar
Jérome Perrin committed
1860
                        self, quiet=QUIET, run=RUN_ALL_TESTS):
Jérome Perrin's avatar
Jérome Perrin committed
1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873
    """ Tests that removing a line updates simulation correctly """
    if not run:
      return
    if not quiet:
      message = 'Test Simple Invoice Rule (delete line and reexpand)'
      ZopeTestCase._print('\n%s ' % message)
      LOG('Testing... ', INFO, message)

    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
1874 1875
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
1876 1877
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
1878
      stepValidateInvoiceTransaction
Jérome Perrin's avatar
Jérome Perrin committed
1879 1880 1881 1882
      stepCreateNotebookFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
1883
      stepCreateSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1884 1885 1886 1887 1888 1889 1890
      stepAddInvoiceLine
      stepPlanInvoice
      stepTic
      stepDeleteInvoiceLine
      stepTic
      stepConfirmInvoice
      stepTic
1891 1892 1893
      stepCheckNoAccountingLinesBuiltYet
      stepStartInvoice
      stepTic
Jérome Perrin's avatar
Jérome Perrin committed
1894
      stepCheckAccountingLinesCreatedForSimpleInvoice
1895
      stepCheckPaymentLinesCreatedForSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1896
      stepRebuildAndCheckNothingIsCreated
Jérome Perrin's avatar
Jérome Perrin committed
1897
      """, quiet=quiet )
1898

1899
  @newSimulationExpectedFailure
Jérome Perrin's avatar
Jérome Perrin committed
1900
  def test_05d_SimpleInvoiceReExpandCreateCell(self, quiet=QUIET,
1901
                  run=RUN_ALL_TESTS):
Jérome Perrin's avatar
Jérome Perrin committed
1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914
    """ Tests that replacing a line by cells updates simulation correctly """
    if not run:
      return
    if not quiet:
      message = 'Test Simple Invoice Rule (add cells in a line and reexpand)'
      ZopeTestCase._print('\n%s ' % message)
      LOG('Testing... ', INFO, message)

    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
1915 1916
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
1917 1918
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
1919
      stepValidateInvoiceTransaction
Jérome Perrin's avatar
Jérome Perrin committed
1920 1921 1922 1923
      stepCreateNotebookFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
1924
      stepCreateOtherSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1925 1926 1927 1928 1929
      stepPlanInvoice
      stepTic
      stepAddCellsInInvoiceLine
      stepConfirmInvoice
      stepTic
1930 1931 1932
      stepCheckNoAccountingLinesBuiltYet
      stepStartInvoice
      stepTic
Jérome Perrin's avatar
Jérome Perrin committed
1933
      stepCheckAccountingLinesCreatedForSimpleInvoice
1934
      stepCheckPaymentLinesCreatedForSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1935
      stepRebuildAndCheckNothingIsCreated
Jérome Perrin's avatar
Jérome Perrin committed
1936
      """, quiet=quiet)
1937

1938
  @newSimulationExpectedFailure
Jérome Perrin's avatar
Jérome Perrin committed
1939
  def test_05e_SimpleInvoiceExpandManyTimes(
Jérome Perrin's avatar
Jérome Perrin committed
1940
                                self, quiet=QUIET, run=RUN_ALL_TESTS):
Jérome Perrin's avatar
Jérome Perrin committed
1941 1942 1943 1944 1945
    """ Tests that updating an applied rule many times doesn't break the
    build """
    if not run:
      return
    if not quiet:
1946
      message = 'Test Simple Invoice Rule (many updateSimulation)'
Jérome Perrin's avatar
Jérome Perrin committed
1947 1948 1949 1950 1951 1952 1953 1954
      ZopeTestCase._print('\n%s ' % message)
      LOG('Testing... ', INFO, message)

    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
1955 1956
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
1957 1958
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
1959
      stepValidateInvoiceTransaction
Jérome Perrin's avatar
Jérome Perrin committed
1960 1961 1962 1963
      stepCreateNotebookFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
1964
      stepCreateSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1965 1966 1967 1968 1969 1970 1971 1972
      stepPlanInvoice
      stepTic """ +
      ("""
      stepEditInvoiceLine
      stepTic""" * 4) +
      """
      stepConfirmInvoice
      stepTic
1973 1974 1975
      stepCheckNoAccountingLinesBuiltYet
      stepStartInvoice
      stepTic
Jérome Perrin's avatar
Jérome Perrin committed
1976
      stepCheckAccountingLinesCreatedForSimpleInvoice
1977
      stepCheckPaymentLinesCreatedForSimpleInvoice
Jérome Perrin's avatar
Jérome Perrin committed
1978
      stepRebuildAndCheckNothingIsCreated
Jérome Perrin's avatar
Jérome Perrin committed
1979
      """, quiet=quiet )
Jérome Perrin's avatar
Jérome Perrin committed
1980

1981
  @newSimulationExpectedFailure
Jérome Perrin's avatar
Jérome Perrin committed
1982
  def test_06_MultiLineInvoice(self, quiet=QUIET, run=RUN_ALL_TESTS):
Jérome Perrin's avatar
Jérome Perrin committed
1983
    """ Multiple lines invoice.
1984
    Try to expand an invoice containing multiples Invoice Line.
Jérome Perrin's avatar
Jérome Perrin committed
1985
    Check that the build is correct, ie similar movements are aggregated.
1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998
    """
    if not run:
      return
    if not quiet:
      message = 'Test Multi Line Invoice Rule'
      ZopeTestCase._print('\n%s ' % message)
      LOG('Testing... ', INFO, message)

    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
1999 2000
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
2001 2002
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
2003
      stepValidateInvoiceTransaction
2004 2005 2006 2007 2008 2009
      stepTic
      stepCreateNotebookFranceCell
      stepCreateBareboneFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
2010
      stepCreateMultiLineInvoice
2011 2012 2013
      stepPlanInvoice
      stepConfirmInvoice
      stepTic
2014 2015 2016
      stepCheckNoAccountingLinesBuiltYet
      stepStartInvoice
      stepTic
2017
      stepCheckAccountingLinesCreatedForMultiLineInvoice
2018
      stepCheckPaymentLinesCreatedForMultiLineInvoice
Jérome Perrin's avatar
Jérome Perrin committed
2019
      stepRebuildAndCheckNothingIsCreated
Jérome Perrin's avatar
Jérome Perrin committed
2020
      """, quiet=quiet )
2021 2022


2023
  @newSimulationExpectedFailure
2024 2025 2026 2027 2028 2029 2030 2031 2032
  def test_planning_invoice_creates_simulation(self, quiet=QUIET):
    # http://mail.nexedi.com/pipermail/erp5-dev/2008-June/001969.html
    self.playSequence("""
      stepCreateAccounts
      stepCreateEntities
      stepCreateCurrencies
      stepCreateProducts
      stepCreateInvoiceTransactionRule
      stepUpdateInvoiceTransactionRuleMatrix
2033 2034
      stepCreatePaymentRule
      stepUpdatePaymentRuleMatrix
2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047
      stepValidateInvoiceTransaction
      stepTic
      stepCreateNotebookFranceCell
      stepCreateBareboneFranceCell
      stepTic
      stepClearSimulation
      stepClearAccountingModule
      stepCreateSimpleInvoice
      stepTic
      stepPlanInvoice
      stepTic
      stepCheckInvoiceSimulation
      """, quiet=quiet)
2048 2049 2050 2051 2052 2053

class TestSaleAccountingRules(SaleInvoiceTest, TestAccountingRules):
  pass


class TestPurchaseAccountingRules(PurchaseInvoiceTest, TestAccountingRules):
2054 2055 2056 2057
  # XXX this test is not really complete, originally we were testing Sale only,
  # so the test steps just test that source is correct, and not the
  # destination. For now it just tests that workflows for Purchase invokes
  # building correctly ...
2058 2059 2060
  pass


2061 2062
def test_suite():
  suite = unittest.TestSuite()
2063 2064
  suite.addTest(unittest.makeSuite(TestSaleAccountingRules))
  suite.addTest(unittest.makeSuite(TestPurchaseAccountingRules))
2065
  return suite