diff --git a/product/ERP5/tests/testAccountingRules.py b/product/ERP5/tests/testAccountingRules.py index fba9f731a501ab0fdc35ad992e9fe72946ac1607..5a1a374010ff66ffb7e2d5b66aba753094bf9c7b 100755 --- a/product/ERP5/tests/testAccountingRules.py +++ b/product/ERP5/tests/testAccountingRules.py @@ -2,6 +2,7 @@ # # Copyright (c) 2004 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> +# Jérome Perrin <jerome@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential @@ -25,218 +26,1059 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## +""" + 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. +""" - - -# -# Skeleton ZopeTestCase # - -#from random import randint +# TODO : +# * test match fr different predicate +# * test invoice with cells +# * test updateAppliedRule when modifying cell / lines +# * test payment rule & payment builder +# * test invoicing rule by connecting to order test. +# import os, sys if __name__ == '__main__': - execfile(os.path.join(sys.path[0], 'framework.py')) + execfile(os.path.join(sys.path[0], 'framework.py')) # Needed in order to have a log file inside the current folder -import os os.environ['EVENT_LOG_FILE'] = os.path.join(os.getcwd(), 'zLOG.log') os.environ['EVENT_LOG_SEVERITY'] = '-300' -from Testing import ZopeTestCase from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase -from AccessControl.SecurityManagement import newSecurityManager -#from DateTime import DateTime -#from Acquisition import aq_base, aq_inner -from zLOG import LOG -#from Products.ERP5Type.DateUtils import addToDate -#import time -#from Products.ERP5Type import product_path -#from DateTime import DateTime +from Products.ERP5Type.tests.Sequence import Sequence, SequenceList +from Testing import ZopeTestCase +from AccessControl.SecurityManagement import newSecurityManager +from zLOG import LOG, INFO +from DateTime import DateTime + class TestAccountingRules(ERP5TypeTestCase): """ This should test these functions : - in InvoiceRule.py : - expand - - collectSimulationMovements - - in InvoiceTransactionRule.py : + - in InvoiceTransactionRule.py and PaymentRule.py : - test - expand - newCellContent - updateMatrix - getCellByPredicate - """ - # Different variables used for this test - run_all_test = 1 - #source_company_id = 'Nexedi' - #destination_company_id = 'Coramy' - #component_id = 'brick' - #sales_order_id = '1' - #quantity = 10 - #base_price = 0.7832 + """ + RUN_ALL_TESTS = 1 + + # define portal_types + 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" + sale_invoice_transaction_portal_type = "Sale Invoice Transaction" + sale_invoice_transaction_line_portal_type \ + = "Sale Invoice Transaction Line" + sale_invoice_line_portal_type = "Sale Invoice Line" + + purchase_invoice_transaction_portal_type \ + = "Purchase Invoice Transaction" + payment_transaction_portal_type = "Payment Transaction" + def getTitle(self): - return "Accouting Rules" - + return "Test Accounting Rules" + def getBusinessTemplateList(self): - """ - Return the list of business templates. - """ - return ('erp5_accounting','erp5_trade', 'erp5_pdm') - - def enableLightInstall(self): - """ - You can override this. Return if we should do a light install (1) or not (0) - """ - return 1 - - def enableActivityTool(self): - """ - You can override this. Return if we should create (1) or not (0) an activity tool - """ - return 1 - - def getActivityTool(self): - return getattr(self.getPortal(), 'portal_activities', None) - - def getRuleTool(self): - return getattr(self.getPortal(), 'portal_rules', None) - - def getWorkflowTool(self): - return getattr(self.getPortal(), 'portal_workflow', None) + """ Return the list of business templates. """ + return ('erp5_pdm', 'erp5_trade', 'erp5_accounting') def getAccountModule(self): - return getattr(self.getPortal(), 'account', None) - + return getattr(self.getPortal(), 'account', + getattr(self.getPortal(), 'account_module')) + def getAccountingModule(self): - return getattr(self.getPortal(), 'accounting', None) - - def getOrganisationModule(self): - return getattr(self.getPortal(), 'organisation', None) + return getattr(self.getPortal(), 'accounting', + getattr(self.getPortal(), 'accounting_module')) def getProductModule(self): - return getattr(self.getPortal(), 'product', None) - - def getCurrencyModule(self) : - return getattr(self.getPortal(), 'currency', None) - - def login(self, quiet=0, run=run_all_test): + return getattr(self.getPortal(), 'product', + getattr(self.getPortal(), 'product_module')) + + ## XXX move this to "Sequence class" + def playSequence(self, sequence_string) : + sequence_list = SequenceList() + sequence_list.addSequenceString(sequence_string) + sequence_list.play(self) + + def afterSetUp(self) : + self.login() + self.createCategories() + + def login(self): uf = self.getPortal().acl_users - uf._doAddUser('alex', '', ['Manager'], []) + uf._doAddUser('alex', '', ['Manager', 'Owner', 'Assignor'], []) user = uf.getUserById('alex').__of__(uf) newSecurityManager(None, user) - - def afterSetUp(self, quiet=1, run=1): - self.login() - # Must add some accounts, accounting transactions, products, etc. - account_module = self.getAccountModule() - self.accounting_module = self.getAccountingModule() - self.currency_module = self.getCurrencyModule() - self.organisation_module = self.getOrganisationModule() - product_module = self.getProductModule() - self.activity_tool = self.getActivityTool() - self.catalog_tool = self.getCatalogTool() - self.category_tool = self.getCategoryTool() - self.simulation_tool = self.getSimulationTool() - self.workflow_tool = self.getWorkflowTool() - self.portal = self.getPortal() - # flush activities - get_transaction().commit() + + 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', + id = cat, + immediate_reindex = 1 ) + # check categories have been created + for cat_string in self.getCategoriesToCreate() : + self.assertNotEquals(None, + self.getCategoryTool().restrictedTraverse(cat_string), + cat_string) + + def getBaseCategoriesToCreate(self) : + return ("hd_size", "cpu_freq") + + def getCategoriesToCreate(self): + return ( + # regions for our organisations + "region/europe/west/france", + "region/africa", + + # those are mandatory for account, and accounting rules depends on + # the account_type category. (ie payable, will create a Payment + # 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", + + # some products lines for our products + "product_line/storever/notebook", + "product_line/storever/barebone", + "product_line/storever/openbrick", + "product_line/not_used/not_matched", + + # some categories for variating our products + "cpu_freq/1Ghz", + "cpu_freq/2Ghz", + "hd_size/60Go", + "hd_size/120Go", + ) + + def rollbackTest(self) : + """ delete everything created by the test (for 'live debugin')""" + portal = self.getPortal() + from Products.ERP5.Document.AccountingTransaction \ + import AccountingTransaction self.tic() - # When using light install, only base categories are created - if len(self.category_tool.region.contentValues()) == 0 : - self.category_tool.region.newContent(portal_type='Category', id='africa') - o = self.category_tool.region.newContent(portal_type='Category', id='europe') - o = o.newContent(portal_type='Category', id='west') - o.newContent(portal_type='Category', id='france') - - self.category_tool.pcg.newContent(portal_type='Category', id='1') - - self.category_tool.product_line.newContent(portal_type='Category', id='erp5') - o = self.category_tool.product_line.newContent(portal_type='Category', id='storever') - o.newContent(portal_type='Category', id='barebone') - o.newContent(portal_type='Category', id='notebook') - o.newContent(portal_type='Category', id='openbrick') - # If currency/EUR already exists, it means that the afterSetUp actions were already commited. Then, we just need to link to them. - old_euro = getattr( self.currency_module, 'EUR', None) - if old_euro is not None : - self.invoice_transaction_rule = getattr(self.getRuleTool(), 'default_invoice_transaction_rule') - self.predicate_product1 = getattr(self.invoice_transaction_rule, 'product_1') - self.predicate_product2 = getattr(self.invoice_transaction_rule, 'product_2') - self.predicate_region1 = getattr(self.invoice_transaction_rule, 'region_1') - self.predicate_region2 = getattr(self.invoice_transaction_rule, 'region_2') - self.invoice = getattr(self.accounting_module, 'invoice1') - return - # Create some currencies - euro = self.currency_module.newContent(id='EUR', title='Euro', portal_type='Currency') - # Create some accounts - account_module.newContent(id='prestation_service', title='prestation_service', portal_type='Account') - account_module.newContent(id='creance_client', title='creance_client', portal_type='Account') - account_module.newContent(id='tva_collectee_196', title='tva_collectee_196', portal_type='Account') - account_module.newContent(id='account1', title='Account1', portal_type='Account') - account_module.newContent(id='account2', title='Account2', portal_type='Account') - account_module.newContent(id='account3', title='Account3', portal_type='Account') - account_module.newContent(id='account4', title='Account4', portal_type='Account') - # Create some organisations - self.organisation1 = self.organisation_module.newContent(id='nexedi', title='Nexedi', portal_type='Organisation') - self.organisation1.newContent(id='default_address', portal_type='Address', region='europe/west/france') - self.organisation2 = self.organisation_module.newContent(id='client1', title='Client1', portal_type='Organisation') - self.organisation2.newContent(id='default_address', portal_type='Address', region='europe/west/france') - # Create some products - self.product1 = product_module.newContent(id='product1', title='Product1', product_line='storever/notebook', base_price=3.0) - self.product2 = product_module.newContent(id='product2', title='Product2', product_line='storever/barebone', base_price=5.0) - # Create some predicates in the Invoice Transaction Rule - self.invoice_transaction_rule = getattr(self.getRuleTool(), 'default_invoice_transaction_rule') - self.invoice_transaction_rule.deleteContent(self.invoice_transaction_rule.contentIds()) # delete anything inside the rule first - - self.predicate_product1 = self.invoice_transaction_rule.newContent(id='product_1', title='product_1', portal_type='Predicate Group', string_index='product', int_index='1', membership_criterion_base_category_list=['product_line',], membership_criterion_category_list=['product_line/storever/notebook'], immediate_reindex=1) - self.predicate_product2 = self.invoice_transaction_rule.newContent(id='product_2', title='product_2', portal_type='Predicate Group', string_index='product', int_index='2', membership_criterion_base_category_list=['product_line',], membership_criterion_category_list=['product_line/storever/barebone'], immediate_reindex=1) - self.predicate_region1 = self.invoice_transaction_rule.newContent(id='region_1', title='region_1', portal_type='Predicate Group', string_index='region', int_index='1', membership_criterion_base_category_list=['destination_region',], membership_criterion_category_list=['destination_region/region/europe/west/france'], immediate_reindex=1) - self.predicate_region2 = self.invoice_transaction_rule.newContent(id='region_2', title='region_2', portal_type='Predicate Group', string_index='region', int_index='2', membership_criterion_base_category_list=['destination_region',], membership_criterion_category_list=['destination_region/region/africa'], immediate_reindex=1) - # Create some invoices (now that there is nothing harmful inside the rule) - self.invoice = self.accounting_module.newContent(id='invoice1', portal_type='Sale Invoice Transaction', destination='organisation/client1', destination_section='organisation/client1', resource='currency/EUR') - invoice_line = self.invoice.newContent(id='1', portal_type='Invoice Line', resource='product/product1', quantity=7.0, price=11.0) - # flush activities - get_transaction().commit() + old_manage_beforeDelete = AccountingTransaction.manage_beforeDelete + AccountingTransaction.manage_beforeDelete = lambda x, y, z: 1 + self.getAccountModule().deleteContent( + list(self.getAccountModule().objectIds())) + self.getAccountingModule().deleteContent( + list(self.getAccountingModule().objectIds())) + self.getOrganisationModule().deleteContent( + list(self.getOrganisationModule().objectIds())) + self.getCurrencyModule().deleteContent( + list(self.getCurrencyModule().objectIds())) + self.getProductModule().deleteContent( + list(self.getProductModule().objectIds())) + self.getSimulationTool().deleteContent( + list(self.getSimulationTool().objectIds())) + AccountingTransaction.manage_beforeDelete = old_manage_beforeDelete + self.tic() + + def stepTic(self, **kw): self.tic() - def updateInvoiceTransactionRuleMatrix(self) : - - base_id = 'vat_per_region' + def stepCreateSaleInvoiceTransactionRule(self, sequence, **kw) : + """ + Create some predicates in the Invoice Transaction Rule + """ + invoice_transaction_rule = getattr(self.getRuleTool(), + 'default_invoice_transaction_rule') + # 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()]) + self.assertEquals(len(invoice_transaction_rule.objectValues()), 0) + + # 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'], + immediate_reindex = 1, + ) + 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'], + immediate_reindex = 1, + ) + # ... 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'], + immediate_reindex = 1, + ) + 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'], + immediate_reindex = 1, + ) + # sanity checks + self.failUnless(predicate_product_notebook != None) + self.failUnless(predicate_product_barebone != None) + self.failUnless(predicate_region_france != None) + self.failUnless(predicate_region_africa != None) + 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, + ) + + def stepUpdateSaleInvoiceTransactionRuleMatrix(self, sequence, **kw) : + """ creates/updates the matrix of the sale invoice transacction rule """ + invoice_transaction_rule = sequence.get('invoice_transaction_rule') + base_id = 'movement' kwd = {'base_id': base_id} - + # update the matrix, generates the accounting rule cells - self.invoice_transaction_rule.updateMatrix() - + invoice_transaction_rule.edit() + invoice_transaction_rule.updateMatrix() + self.tic() + # check the accounting rule cells inside the matrix - cell_list = self.invoice_transaction_rule.contentValues(filter={'portal_type':'Accounting Rule Cell'}) + cell_list = invoice_transaction_rule.contentValues( + filter = {'portal_type':self.accounting_rule_cell_portal_type}) self.assertEqual(len(cell_list), 4) - self.product1_region1_cell = getattr(self.invoice_transaction_rule, 'vat_per_region_0_0', None) - self.product1_region2_cell = getattr(self.invoice_transaction_rule, 'vat_per_region_0_1', None) - self.product2_region1_cell = getattr(self.invoice_transaction_rule, 'vat_per_region_1_0', None) - self.product2_region2_cell = getattr(self.invoice_transaction_rule, 'vat_per_region_1_1', None) - - self.failUnless(self.product1_region1_cell != None) - self.failUnless(self.product1_region2_cell != None) - self.failUnless(self.product2_region1_cell != None) - self.failUnless(self.product2_region2_cell != None) - - self.product1_region1_line1 = getattr(self.product1_region1_cell, 'income', None) - self.failUnless(self.product1_region1_line1 != None) - self.product1_region1_line1.edit(title='income', source='account/account1', destination='account/account2', quantity=19.0) - # flush activities - get_transaction().commit() + # In the matrix, cells are named on the scheme : + # ${base_id} + '_'.join(predicate_dimension ordered by int_index) + product_notebook_region_france_cell = getattr(invoice_transaction_rule, + '%s_0_0'%base_id, None) + product_notebook_region_africa_cell = getattr(invoice_transaction_rule, + '%s_0_1'%base_id, None) + product_barebone_region_france_cell = getattr(invoice_transaction_rule, + '%s_1_0'%base_id, None) + product_barebone_region_africa_cell = getattr(invoice_transaction_rule, + '%s_1_1'%base_id, None) + + self.failUnless(product_notebook_region_france_cell != None) + self.failUnless(product_notebook_region_africa_cell != None) + self.failUnless(product_barebone_region_france_cell != None) + self.failUnless(product_barebone_region_africa_cell != None) + + sequence.edit( + product_notebook_region_france_cell = product_notebook_region_france_cell, + product_notebook_region_africa_cell = product_notebook_region_africa_cell, + product_barebone_region_france_cell = product_barebone_region_france_cell, + product_barebone_region_africa_cell = product_barebone_region_africa_cell, + ) + + 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, + ) + + def stepCreateBareboneFranceCell(self, sequence, **kw): + """ creates the content of product_barebone_region_france_cell, + 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 + ) + + + 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", + immediate_reindex = 1, + ) + income = account_module.newContent( + id = "income_barebone", + portal_type = self.account_portal_type, + title = "Income Barebone", + account_type = "income", + immediate_reindex = 1, + ) + receivable = account_module.newContent( + id = "receivable", + portal_type=self.account_portal_type, + title = "Receivable", + account_type = "asset/receivable", + immediate_reindex = 1, + ) + collected_vat = account_module.newContent( + id = "collected_vat", + portal_type=self.account_portal_type, + title = "Collected VAT", + account_type = "liability/payable/collected_vat", + immediate_reindex = 1, + ) + # store accounts in sequence object + sequence.edit( + income = account_module.income, + income_barebone = account_module.income_barebone, + receivable = account_module.receivable, + collected_vat = account_module.collected_vat, + ) + + 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", + immediate_reindex = 1, + ) + 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", + immediate_reindex = 1, + ) + self.assertNotEquals(client_fr.getDefaultRegionValue(), None) + sequence.edit( + vendor = organisation_module.vendor, + client_fr = organisation_module.client_fr, + client = organisation_module.client_fr, + ) + + def stepCreateProducts(self, sequence, **kw) : + """ + Create 2 kind of products, a notebook (Varianted) + 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, + immediate_reindex = 1, + ) + # 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",]) + + barebone = product_module.newContent( + id = 'barebone', + title = 'Barebone', + portal_type = self.product_portal_type, + product_line = 'storever/barebone', + base_price = 5.0, + immediate_reindex = 1, + ) + sequence.edit( + notebook = product_module.notebook, + barebone = product_module.barebone, + product_notebook = product_module.notebook, + product_barebone = product_module.barebone, + ) + + 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, + ) + sequence.edit(euro=currency_module.EUR, currency=currency_module.EUR) + + def stepCreatePaymentRule(self, **kw) : + """ create a rule payment transaction generation """ + # XXX: for now there are no cells in payment rule, so nothing to do here + # TODO + + def stepCreateSimpleSaleInvoice(self, sequence, **kw) : + """ creates a simple sale invoice for non varianted notebook product. + The invoice is from `vendor` to `client_fr`, so the cell defined in + stepUpdateSaleInvoiceTransactionRuleMatrix should match. + 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') + + simple_invoice = self.getAccountingModule().newContent( + id = 'simple_invoice', + portal_type = self.sale_invoice_transaction_portal_type, + resource = currency.getRelativeUrl(), + stop_date = DateTime(2004, 01, 01), + start_date = DateTime(2004, 01, 01), + source_section = vendor.getRelativeUrl(), + destination_section = client.getRelativeUrl(), + ) + + invoice_line = simple_invoice.newContent( + id = 'invoice_line', + resource = product_notebook.getRelativeUrl(), + quantity = 10, + price = 10) + + self.assertEqual(invoice_line.getTotalPrice(), 100) + + sequence.edit( + simple_invoice = simple_invoice, + invoice = simple_invoice, + invoice_line = invoice_line, + invoice_lines = [invoice_line] + ) + + def stepCreateSimpleSaleInvoiceTwoLines(self, sequence, **kw) : + """ + similar to stepCreateSimpleSaleInvoice, but replace + "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') + + simple_invoice = self.getAccountingModule().newContent( + id = 'simple_invoice_two_lines', + portal_type = self.sale_invoice_transaction_portal_type, + resource = currency.getRelativeUrl(), + stop_date = DateTime(2004, 01, 01), + start_date = DateTime(2004, 01, 01), + source_section = vendor.getRelativeUrl(), + destination_section = client.getRelativeUrl(), + ) + + invoice_line1 = simple_invoice.newContent( + id = 'invoice_line1', + resource = product_notebook.getRelativeUrl(), + quantity = 5, + price = 10) + invoice_line2 = simple_invoice.newContent( + id = 'invoice_line2', + resource = product_notebook.getRelativeUrl(), + quantity = 5, + price = 10) + + self.assertEqual(invoice_line1.getTotalPrice() + + invoice_line2.getTotalPrice(), 100) + + sequence.edit( + simple_invoice = simple_invoice, + invoice = simple_invoice, + invoice_lines = [invoice_line1, invoice_line2] + ) + + def stepPlanInvoice(self, sequence, **kw) : + """ put the invoice in the `planned` state, which will + start the simulation process. """ + invoice = sequence.get('invoice') + self.getPortal().portal_workflow.doActionFor( + invoice, 'plan_action', + wf_id = 'accounting_workflow' + ) + self.assertEquals(invoice.getSimulationState(), 'planned') + + def stepCreateMultiLineSaleInvoice(self, sequence, **kw) : + """ create an invoice with varianted products + The invoice is from `vendor` to `client_fr`, so the cell defined in + This invoice containts two lines : + 10 notebook * 10 EUR, so total price is 100 + (matched by product_notebook_region_france_cell) + 10 barebone * 100 EUR, so total price is 1000 + (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') + + multi_line_invoice = self.getAccountingModule().newContent( + id = 'multi_line_invoice', + portal_type = self.sale_invoice_transaction_portal_type, + 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(), + ) + + notebook_line = multi_line_invoice.newContent( + id = 'notebook_line', + resource = product_notebook.getRelativeUrl(), + quantity = 10, + price = 10) + + barebone_line = multi_line_invoice.newContent( + id = 'barebone_line', + resource = product_barebone.getRelativeUrl(), + quantity = 10, + price = 100) + + self.assertEqual( 10*10 + 10*100, + notebook_line.getTotalPrice() + barebone_line.getTotalPrice()) + + sequence.edit( + multi_line_invoice = multi_line_invoice, + invoice = multi_line_invoice, + invoice_lines = [notebook_line, barebone_line], + ) + + 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'], + immediate_reindex = 1, + ) + invoice_transaction_rule.updateMatrix() self.tic() + 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) - def test_01_HasEverything(self, quiet=0, run=run_all_test): - if not run: return + 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'], + immediate_reindex = 1, + ) + 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) + + def stepCreateDummyInvoice(self, sequence, **kw) : + """ Create a dummy invoice for temp movements """ + invoice = self.getAccountingModule().newContent( + id = "dummy_invoice", + ) + sequence.edit(invoice = invoice) + + def stepCreateMatchableSaleInvoiceMovements(self, sequence, **kw) : + """ 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 , + ) + + def stepCheckMatchableSaleInvoiceMovements(self, sequence, **kw) : + """ 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') + + # 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') + + # 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') + + # check the predicates + 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") + + 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 )) + + 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 )) + + # 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 )) + + 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 )) + + # finally check the matching cell is the good one + self.assertEquals(product_notebook_region_france_cell, + 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 )) + + def stepCreateNotMatchableSaleInvoiceMovements(self, sequence, **kw) : + """ 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, + ) + + def stepCheckNotMatchableSaleInvoiceMovements(self, sequence, **kw) : + """ 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())) + + def stepClearAccountingModule(self, sequence, **kw) : + """ clear the content of accounting module """ + self.tic() # make sure message queue is empty + # cancel accounting transaction to be able to delete them + for transaction in self.getAccountingModule().objectValues() : + self.getPortal().portal_workflow.doActionFor( + transaction, 'cancel_action', wf_id='accounting_workflow') + # delete + self.getAccountingModule().deleteContent( + list(self.getAccountingModule().objectIds())) + + 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') + + # content of the simulation tool is a list of invoice rules + applied_rule_list = self.getSimulationTool().contentValues() + + self.assertEqual(len(applied_rule_list), 1) + applied_rule = applied_rule_list[0] + self.assertEqual( applied_rule.getPortalType(), + self.applied_rule_portal_type) + self.assertEqual( applied_rule.getSpecialise(), + 'portal_rules/default_invoice_rule') + self.assertEqual( applied_rule.getCausality(), + invoice.getRelativeUrl()) + + # 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 + self.assertEqual( len(simulation_movement_list), 1) + simulation_movement = simulation_movement_list[0] + + 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()) + + # inside this movement there is an applied rule which + # specialize invoice_transaction_rule + applied_rule_list = simulation_movement.contentValues() + self.assertEquals( len(applied_rule_list), 1) + applied_rule = applied_rule_list[0] + self.assertEquals( applied_rule.getPortalType(), + self.applied_rule_portal_type) + self.assertEquals( applied_rule.getSpecialise(), + invoice_transaction_rule.getRelativeUrl()) + + # and in this applied rule, we got simulation movements, + # 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 : + self.assertEquals( simulation_movement.getSourceSection(), + vendor.getRelativeUrl()) + self.assertEquals( simulation_movement.getDestinationSection(), + client.getRelativeUrl()) + self.assertEquals( simulation_movement.getResource(), + currency.getRelativeUrl()) + self.assertEquals( simulation_movement.getCausalityState(), + 'expanded') + for rule_movement in invoice_transaction_rule_cell.contentValues() : + if simulation_movement.getSource() == rule_movement.getSource() : + rule_movement_found[rule_movement.getSource()] = 1 + self.assertEquals(simulation_movement.getQuantity(), + rule_movement.getQuantity() * invoice_line.getTotalPrice()) + self.assertEquals(simulation_movement.getSourceCredit(), + rule_movement.getSourceCredit() * invoice_line.getTotalPrice()) + self.assertEquals(simulation_movement.getSourceDebit(), + rule_movement.getSourceDebit() * invoice_line.getTotalPrice()) + self.assert_(len(rule_movement_found.keys()), 3) + sequence.edit( simulation_movement_list = simulation_movement_list ) + + def stepCheckPaymentRuleIsApplied(self, sequence, **kw) : + """ checks that a payment rule is applied for the total amount + of receivable """ + # TODO + + def stepConfirmInvoice(self, sequence, **kw) : + """ put the invoice in the `confirmed` state, which will + start the delivery builder """ + invoice = sequence.get('invoice') + self.getPortal().portal_workflow.doActionFor( + invoice, 'confirm_action', + wf_id = 'accounting_workflow' + ) + self.assertEquals(invoice.getSimulationState(), 'confirmed') + + def stepCheckAccountingLinesCoherantWithSimulation(self, sequence, **kw) : + """ checks that accounting lines are created on the sale invoice + 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': + self.sale_invoice_transaction_line_portal_type}) + self.assertEquals( len(invoice_transaction_line_list), + len(simulation_movement_list)) + + simulation_movement_found = {} + for invoice_transaction_line in invoice_transaction_line_list : + self.assertEquals( invoice_transaction_line.getSourceSection(), + vendor.getRelativeUrl()) + self.assertEquals( invoice_transaction_line.getDestinationSection(), + client.getRelativeUrl()) + self.assertEquals( invoice_transaction_line.getResource(), + currency.getRelativeUrl()) + for simulation_movement in simulation_movement_list : + if simulation_movement.getSource() == \ + invoice_transaction_line.getSource() : + simulation_movement_found[simulation_movement.getSource()] = 1 + self.assertEquals(simulation_movement.getQuantity(), + invoice_transaction_line.getQuantity()) + self.assertEquals(simulation_movement.getSourceCredit(), + invoice_transaction_line.getSourceCredit()) + self.assertEquals(simulation_movement.getSourceDebit(), + invoice_transaction_line.getSourceDebit()) + self.assertEquals(simulation_movement.getDelivery(), + invoice_transaction_line.getRelativeUrl()) + self.assert_(len(simulation_movement_found.keys()), 3) + + + def stepCheckAccountingLinesCreatedForSimpleInvoice( + self, sequence, **kw) : + """ Checks that accounting lines are created on the sale invoice + 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 + =============== ======= ======= + income 100 + collected_vat 19.60 + receivable 119.60 + =============== ======= ======= + """ + invoice = sequence.get('invoice') + vendor = sequence.get('vendor') + client = sequence.get('client') + currency = sequence.get('currency') + invoice_lines = sequence.get('invoice_lines') + + invoice_transaction_line_list = invoice.contentValues( + filter = {'portal_type': self.sale_invoice_transaction_line_portal_type}) + self.assertEquals(len(invoice_transaction_line_list), 3) + + accounting_lines_layout = { + 'income' : (0, 100), + 'collected_vat' : (0, 19.60), + 'receivable' : (119.60, 0), + } + + for invoice_transaction_line in invoice_transaction_line_list : + debit, credit = accounting_lines_layout[invoice_transaction_line.getSourceId()] + self.assertEquals(debit, invoice_transaction_line.getSourceDebit()) + self.assertEquals(credit, invoice_transaction_line.getSourceCredit()) + + def stepCheckAccountingLinesCreatedForMultiLineInvoice( + self, sequence, **kw) : + """ Checks that accounting lines are created on the sale invoice + transaction and that all movement are correctly aggregated. + The price of the invoice was 1100, it should result in the following + accounting layout : + + =============== ======= ======= + account Debit Credit + =============== ======= ======= + income 100 + income_barebone 1000 + collected_vat 215.60 + receivable 1315.60 + =============== ======= ======= + """ + invoice = sequence.get('invoice') + vendor = sequence.get('vendor') + client = sequence.get('client') + currency = sequence.get('currency') + invoice_lines = sequence.get('invoice_lines') + + invoice_transaction_line_list = invoice.contentValues( + filter = {'portal_type': self.sale_invoice_transaction_line_portal_type}) + self.assertEquals(len(invoice_transaction_line_list), 4) + + accounting_lines_layout = { + 'income' : (0, 100), + 'income_barebone' : (0, 1000), + 'collected_vat' : (0, 215.60), + 'receivable' : (1315.60, 0), + } + + for invoice_transaction_line in invoice_transaction_line_list : + debit, credit = accounting_lines_layout[invoice_transaction_line.getSourceId()] + self.assertEquals(debit, invoice_transaction_line.getSourceDebit()) + self.assertEquals(credit, invoice_transaction_line.getSourceCredit()) + + def test_01_HasEverything(self, quiet=0, run=RUN_ALL_TESTS): + """ check necessary tools and modules are present. """ + if not run: + return if not quiet: ZopeTestCase._print('\nTest Has Everything ') - LOG('Testing... ',0,'testHasEverything') + LOG('Testing... ', INFO, 'testHasEverything') self.failUnless(self.getCategoryTool() != None) self.failUnless(self.getSimulationTool() != None) self.failUnless(self.getTypeTool() != None) @@ -249,372 +1091,194 @@ class TestAccountingRules(ERP5TypeTestCase): self.failUnless(self.getProductModule() != None) self.failUnless(self.getCurrencyModule() != None) - def test_02_UpdateInvoiceTransactionRuleMatrix(self, quiet=0, run=run_all_test): + def test_02_UpdateInvoiceTransactionRuleMatrix(self, quiet=0, + run=RUN_ALL_TESTS): """ - Try to update the matrix after adding some predicates, and check if all objects were created + Try to update the matrix after adding some predicates, + and check if all objects were created """ - if not run: return + if not run: + return if not quiet: message = 'Test Update Invoice Transaction Rule Matrix' ZopeTestCase._print('\n%s ' % message) - LOG('Testing... ',0,message) - - # before the tests, we need to be sure we have four predicates - self.failUnless(self.predicate_product1 != None) - self.failUnless(self.predicate_product2 != None) - self.failUnless(self.predicate_region1 != None) - self.failUnless(self.predicate_region2 != None) - predicate_list = self.invoice_transaction_rule.contentValues(filter={'portal_type':'Predicate Group'}) - self.assertEqual(len(predicate_list), 4) - - # first, we check the matrix was initialized correctly (2x2 cells) - self.updateInvoiceTransactionRuleMatrix() - cell_list = self.invoice_transaction_rule.contentValues(filter={'portal_type':'Accounting Rule Cell'}) - self.assertEqual(len(cell_list), 4) - - # next, we add a predicate to see if it is still okay (3x2 cells) - self.predicate_product3 = self.invoice_transaction_rule.newContent(id='product_3', title='product_3', portal_type='Predicate Group', string_index='product', int_index='3', membership_criterion_base_category_list=['product_line',], membership_criterion_category_list=['product_line/storever/openbrick'], immediate_reindex=1) - self.invoice_transaction_rule.updateMatrix() - cell_list = self.invoice_transaction_rule.contentValues(filter={'portal_type':'Accounting Rule Cell'}) - self.assertEqual(len(cell_list), 6) - - # then, we remove a predicate and check again (3x1 cells) - self.invoice_transaction_rule.deleteContent(id='region_2') - self.invoice_transaction_rule.updateMatrix() - cell_list = self.invoice_transaction_rule.contentValues(filter={'portal_type':'Accounting Rule Cell'}) - self.assertEqual(len(cell_list), 3) - - # finally, we put back the matrix in the original format (2x2 cells) - self.invoice_transaction_rule.deleteContent(id='product_3') - self.predicate_region2 = self.invoice_transaction_rule.newContent(id='region_2', title='region_2', portal_type='Predicate Group', string_index='region', int_index='2', membership_criterion_base_category_list=['region',], membership_criterion_category_list=['region/africa'], immediate_reindex=1) - self.invoice_transaction_rule.updateMatrix() - cell_list = self.invoice_transaction_rule.contentValues(filter={'portal_type':'Accounting Rule Cell'}) - self.assertEqual(len(cell_list), 4) - - - def test_03_invoiceTransactionRule_getMatchingCell(self, quiet=0, run=run_all_test): + LOG('Testing... ', INFO, message) + + self.playSequence(""" + stepCreateSaleInvoiceTransactionRule + stepUpdateSaleInvoiceTransactionRuleMatrix + stepTic + stepCheckAddPredicate + stepTic + stepCheckRemovePredicate + stepTic + stepCheckRestoreOriginalPredicates + """) + + def test_03_invoiceTransactionRule_getMatchingCell(self, + quiet=0, run=RUN_ALL_TESTS): """ - test InvoiceTransactionRule.getCellByPredicate() + test predicates for the cells of invoice transaction rule """ - if not run: return + if not run: + return if not quiet: - message = 'Test Invoice Transaction Rule getCellByPredicate ' + message = 'Test Invoice Transaction Rule getMatchingCell ' ZopeTestCase._print('\n%s ' % message) - LOG('Testing... ',0,message) - # before the tests, we must update the matrix - self.updateInvoiceTransactionRuleMatrix() - from Products.ERP5Type.Document import newTempMovement - movement = newTempMovement(self.invoice, 'test1', resource = 'product/product1', - destination = 'organisation/nexedi') - # Make sure acquisition is working for destination_region - self.assertEqual(movement.getDestinationRegion(), 'region/europe/west/france') - # Make sure category is working for destination_region - self.assertEqual(movement.getProductLine(), 'storever/notebook') - # Test cell on movement - # correct cell - self.assertEqual(self.product1_region1_cell, self.invoice_transaction_rule._getMatchingCell(movement)) - # no predicate for this category - movement = newTempMovement(self.invoice, 'test2', resource = 'product/product2', - destination = 'organisation/nexedi') - self.assertEqual(self.product2_region1_cell, self.invoice_transaction_rule._getMatchingCell(movement)) - # incorrect category - movement = newTempMovement(self.invoice, 'test3', product = None, destination = 'organisation/nexedi') - self.assertEqual(None, self.invoice_transaction_rule._getMatchingCell(movement)) - # incorrect dimension - movement = newTempMovement(self.invoice, 'test4', pcg = 'pcg/1', destination = 'organisation/nexedi') - self.assertEqual(None, self.invoice_transaction_rule._getMatchingCell(movement)) - - def test_04_invoiceRule_expand(self, quiet=0, run=run_all_test): + LOG('Testing... ', INFO, message) + + self.playSequence(""" + stepCreateAccounts + stepCreateEntities + stepCreateCurrencies + stepTic + stepCreateSaleInvoiceTransactionRule + stepUpdateSaleInvoiceTransactionRuleMatrix + stepCreateProducts + stepTic + stepCreateDummyInvoice + stepCreateMatchableSaleInvoiceMovements + stepCheckMatchableSaleInvoiceMovements + stepCreateNotMatchableSaleInvoiceMovements + stepCheckNotMatchableSaleInvoiceMovements + """) + + def test_04_SimpleInvoice(self, quiet=0, run=RUN_ALL_TESTS): """ - Try to expand an invoice containing Invoice Lines + Try to expand an invoice containing only one simple Invoice Line. + Check that the build is correct. """ - if not run: return + if not run: + return if not quiet: - message = 'Test Invoice Rule Expand ' + message = 'Test Simple Invoice Rule' ZopeTestCase._print('\n%s ' % message) - LOG('Testing... ',0,message) - - # before the tests, we must update the matrix - self.updateInvoiceTransactionRuleMatrix() - - #### - # TEST NO 1 : one Invoice Line (quantity * price) * tax == (7 * 11) * 19 - #### - # the invoice is expanded by the invoice_edit_workflow when it is edited. - self.invoice.edit(title='Invoice1') - - # flush activities - get_transaction().commit() - self.tic() - - LOG('history', 0, repr(( self.workflow_tool.getHistoryOf('edit_workflow', self.invoice) ))) - - # check every level of the simulation - applied_rule_list = self.simulation_tool.contentValues() # list of Invoice Rules - self.assertEqual(len(applied_rule_list), 1) - - applied_rule = applied_rule_list[0] - self.assertEqual(applied_rule.getPortalType(), 'Applied Rule') - self.assertEqual(applied_rule.getSpecialise(), 'portal_rules/default_invoice_rule') - self.assertEqual(applied_rule.getCausality(), 'accounting/invoice1') - - movement_list = applied_rule.contentValues() # list of Invoice Lines - self.assertEqual(len(movement_list), 1) - - # Make sure the movement acquires all properties - movement = movement_list[0] - self.assertEqual(movement.getId(), '1') - self.assertEqual(movement.getPortalType(), 'Simulation Movement') - self.assertEqual(movement.getDelivery(), 'accounting/invoice1/1') - self.assertEqual(movement.getResource(), 'product/product1') - self.assertEqual(movement.getDestination(), 'organisation/client1') - self.assertEqual(movement.getDestinationSection(), 'organisation/client1') - self.assertEqual(movement.getProductLine(), 'storever/notebook') - self.assertEqual(movement.getDestinationRegion(), 'region/europe/west/france') - - # Make sure the invoice_transaction_rule applies to this movement - self.assertEqual(self.invoice_transaction_rule.test(movement), True) - # And make sure the first cell applies - self.assertEqual(self.product1_region1_line1.test(movement), True) - - # Make sure expand succeeded - sub_applied_rule_list = movement.contentValues() # list of Invoice Transaction Rules - self.assertEqual(len(sub_applied_rule_list), 1) - - sub_applied_rule = sub_applied_rule_list[0] - self.assertEqual(sub_applied_rule.getId(), 'default_invoice_transaction_rule') - self.assertEqual(sub_applied_rule.getPortalType(), 'Applied Rule') - self.assertEqual(sub_applied_rule.getSpecialise(), 'portal_rules/default_invoice_transaction_rule') - - sub_movement_list = sub_applied_rule.contentValues() # list of Sale Invoice Transaction Lines - self.assertEqual(len(sub_movement_list), 3) # there should be 'income', 'receivable', 'collected_vat' - - for sub_movement in sub_movement_list : - if sub_movement.getId() not in ('income', 'receivable', 'collected_vat',) : - self.fail(msg='%s is not a normal Sale Invoice Transaction Line name' % sub_movement) - self.assertEqual(movement.getPortalType(), 'Simulation Movement') - if sub_movement.getId() == 'income' : - self.assertEqual(sub_movement.getSource(), 'account/account1') - self.assertEqual(sub_movement.getDestination(), 'account/account2') - self.assertEqual(sub_movement.getQuantity(), (7.0 * 11.0) * 19.0) - - # check if invoice transaction lines are added and correct (outside simulation too) - invoice_transaction_line = getattr(self.invoice, 'income', None) - self.failIf(invoice_transaction_line is None) - self.assertEqual(invoice_transaction_line.getPortalType(), 'Sale Invoice Transaction Line') - self.assertEqual(invoice_transaction_line.getSource(), 'account/account1') - self.assertEqual(invoice_transaction_line.getDestination(), 'account/account2') - self.assertEqual(invoice_transaction_line.getQuantity(), (7.0 * 11.0) * 19.0) - - #### - # TEST NO 2 : one Invoice Line (quantity * price) * tax == (7 * 11) * 19 - # expand once again and check that everithing is still the same - #### - # the invoice is expanded by the invoice_edit_workflow when it is edited. - self.invoice.edit(title='Invoice1') - - # flush activities - get_transaction().commit() - self.tic() - - # check every level of the simulation - applied_rule_list = self.simulation_tool.contentValues() # list of Invoice Rules - self.assertEqual(len(applied_rule_list), 1) - - applied_rule = applied_rule_list[0] - self.assertEqual(applied_rule.getPortalType(), 'Applied Rule') - self.assertEqual(applied_rule.getSpecialise(), 'portal_rules/default_invoice_rule') - self.assertEqual(applied_rule.getCausality(), 'accounting/invoice1') - - movement_list = applied_rule.contentValues() # list of Invoice Lines - self.assertEqual(len(movement_list), 1) - - movement = movement_list[0] - self.assertEqual(movement.getId(), '1') - self.assertEqual(movement.getPortalType(), 'Simulation Movement') - self.assertEqual(movement.getDelivery(), 'accounting/invoice1/1') - - sub_applied_rule_list = movement.contentValues() # list of Invoice Transaction Rules - self.assertEqual(len(sub_applied_rule_list), 1) - - sub_applied_rule = sub_applied_rule_list[0] - self.assertEqual(sub_applied_rule.getId(), 'default_invoice_transaction_rule') - self.assertEqual(sub_applied_rule.getPortalType(), 'Applied Rule') - self.assertEqual(sub_applied_rule.getSpecialise(), 'portal_rules/default_invoice_transaction_rule') - - sub_movement_list = sub_applied_rule.contentValues() # list of Sale Invoice Transaction Lines - self.assertEqual(len(sub_movement_list), 3) # there should be 'income', 'receivable', 'collected_vat' - - for sub_movement in sub_movement_list : - if sub_movement.getId() not in ('income', 'receivable', 'collected_vat',) : - self.fail(msg='%s is not a normal Sale Invoice Transaction Line name' % sub_movement) - self.assertEqual(movement.getPortalType(), 'Simulation Movement') - if sub_movement.getId() == 'income' : - self.assertEqual(sub_movement.getSource(), 'account/account1') - self.assertEqual(sub_movement.getDestination(), 'account/account2') - self.assertEqual(sub_movement.getQuantity(), (7.0 * 11.0) * 19.0) - - # check if invoice transaction lines are added and correct (outside simulation too) - #self.invoice.buildInvoiceTransactionList() - invoice_transaction_line = getattr(self.invoice, 'income', None) - self.failIf(invoice_transaction_line is None) - self.assertEqual(invoice_transaction_line.getPortalType(), 'Sale Invoice Transaction Line') - self.assertEqual(invoice_transaction_line.getSource(), 'account/account1') - self.assertEqual(invoice_transaction_line.getDestination(), 'account/account2') - self.assertEqual(invoice_transaction_line.getQuantity(), (7.0 * 11.0) * 19.0) - - #### - # TEST NO 3 : two Invoice Lines (quantity * price) * tax == (7 * 11) * 19 + (13 * 17) * 19 - # add a line with same product_line and test again - #### - invoice_line2 = self.invoice.newContent(id='2', portal_type='Invoice Line', resource='product/product1', quantity=13.0, price=17.0) - # the invoice is expanded by the invoice_edit_workflow when it is edited. - self.invoice.edit(title='Invoice1') - - # flush activities - get_transaction().commit() - self.tic() - - # check every level of the simulation - applied_rule_list = self.simulation_tool.contentValues() # list of Invoice Rules - self.assertEqual(len(applied_rule_list), 1) - - applied_rule = applied_rule_list[0] - self.assertEqual(applied_rule.getPortalType(), 'Applied Rule') - self.assertEqual(applied_rule.getSpecialise(), 'portal_rules/default_invoice_rule') - self.assertEqual(applied_rule.getCausality(), 'accounting/invoice1') - - movement_list = applied_rule.contentValues() # list of Invoice Lines - self.assertEqual(len(movement_list), 2) - - for movement in movement_list : - movement_id = movement.getId() - if movement_id not in ('1', '2',) : - self.fail(msg='%s is not a normal Invoice Line name' % sub_movement) - self.assertEqual(movement.getPortalType(), 'Simulation Movement') - self.assertEqual(movement.getDelivery(), 'accounting/invoice1/%s' % movement_id) - - sub_applied_rule_list = movement.contentValues() # list of Invoice Transaction Rules - self.assertEqual(len(sub_applied_rule_list), 1) - - sub_applied_rule = sub_applied_rule_list[0] - self.assertEqual(sub_applied_rule.getId(), 'default_invoice_transaction_rule') - self.assertEqual(sub_applied_rule.getPortalType(), 'Applied Rule') - self.assertEqual(sub_applied_rule.getSpecialise(), 'portal_rules/default_invoice_transaction_rule') - - sub_movement_list = sub_applied_rule.contentValues() # list of Sale Invoice Transaction Lines - self.assertEqual(len(sub_movement_list), 3) # there should be 'income', 'receivable', 'collected_vat' - - for sub_movement in sub_movement_list : - if sub_movement.getId() not in ('income', 'receivable', 'collected_vat',) : - self.fail(msg='%s is not a normal Sale Invoice Transaction Line name' % sub_movement) - self.assertEqual(movement.getPortalType(), 'Simulation Movement') - if sub_movement.getId() == 'income' : - self.assertEqual(sub_movement.getSource(), 'account/account1') - self.assertEqual(sub_movement.getDestination(), 'account/account2') - if movement_id == '1' : - self.assertEqual(sub_movement.getQuantity(), (7.0 * 11.0) * 19.0) - - elif movement_id == '2' : - self.assertEqual(sub_movement.getQuantity(), (13.0 * 17.0) * 19.0) - - # check if invoice transaction lines are added and correct (outside simulation too) - invoice_transaction_line = getattr(self.invoice, 'income', None) - self.failIf(invoice_transaction_line is None) - self.assertEqual(invoice_transaction_line.getPortalType(), 'Sale Invoice Transaction Line') - self.assertEqual(invoice_transaction_line.getSource(), 'account/account1') - self.assertEqual(invoice_transaction_line.getDestination(), 'account/account2') - self.assertEqual(invoice_transaction_line.getQuantity(), (7.0 * 11.0 + 13.0 * 17.0) * 19.0) - - #### - # TEST NO 4 : three Invoice Lines (quantity * price) * tax == (7 * 11) * 19 + (13 * 17) * 19 + (23 * 29) * 31 - #### - # add a line with different product_line and test again (we first need a line for this one) - self.product2_region1_line1 = getattr(self.product2_region1_cell, 'income', None) - self.failUnless(self.product2_region1_line1 != None) - self.product2_region1_line1.edit(title='income', source='account/account3', destination='account/account4', quantity=31.0) - - invoice_line3 = self.invoice.newContent(id='3', portal_type='Invoice Line', resource='product/product2', quantity=23.0, price=29.0) - # the invoice is expanded by the invoice_edit_workflow when it is edited. - self.invoice.edit(title='Invoice1') - - # flush activities - get_transaction().commit() - self.tic() - - # check every level of the simulation - applied_rule_list = self.simulation_tool.contentValues() # list of Invoice Rules - self.assertEqual(len(applied_rule_list), 1) - - applied_rule = applied_rule_list[0] - self.assertEqual(applied_rule.getPortalType(), 'Applied Rule') - self.assertEqual(applied_rule.getSpecialise(), 'portal_rules/default_invoice_rule') - self.assertEqual(applied_rule.getCausality(), 'accounting/invoice1') - - movement_list = applied_rule.contentValues() # list of Invoice Lines - self.assertEqual(len(movement_list), 3) - - for movement in movement_list : - movement_id = movement.getId() - if movement_id not in ('1', '2', '3',) : - self.fail(msg='%s is not a normal Invoice Line name' % sub_movement) - self.assertEqual(movement.getPortalType(), 'Simulation Movement') - self.assertEqual(movement.getDelivery(), 'accounting/invoice1/%s' % movement_id) - - sub_applied_rule_list = movement.contentValues() # list of Invoice Transaction Rules - self.assertEqual(len(sub_applied_rule_list), 1) - - sub_applied_rule = sub_applied_rule_list[0] - self.assertEqual(sub_applied_rule.getId(), 'default_invoice_transaction_rule') - self.assertEqual(sub_applied_rule.getPortalType(), 'Applied Rule') - self.assertEqual(sub_applied_rule.getSpecialise(), 'portal_rules/default_invoice_transaction_rule') - - sub_movement_list = sub_applied_rule.contentValues() # list of Sale Invoice Transaction Lines - self.assertEqual(len(sub_movement_list), 3) # there should be 'income', 'receivable', 'collected_vat' - - for sub_movement in sub_movement_list : - if sub_movement.getId() not in ('income', 'receivable', 'collected_vat',) : - self.fail(msg='%s is not a normal Sale Invoice Transaction Line name' % sub_movement) - self.assertEqual(movement.getPortalType(), 'Simulation Movement') - if sub_movement.getId() == 'income' : - if movement_id == '1' : - self.assertEqual(sub_movement.getSource(), 'account/account1') - self.assertEqual(sub_movement.getDestination(), 'account/account2') - self.assertEqual(sub_movement.getQuantity(), (7.0 * 11.0) * 19.0) - elif movement_id == '2' : - self.assertEqual(sub_movement.getSource(), 'account/account1') - self.assertEqual(sub_movement.getDestination(), 'account/account2') - self.assertEqual(sub_movement.getQuantity(), (13.0 * 17.0) * 19.0) - elif movement_id == '3' : - self.assertEqual(sub_movement.getSource(), 'account/account3') - self.assertEqual(sub_movement.getDestination(), 'account/account4') - self.assertEqual(sub_movement.getQuantity(), (23.0 * 29.0) * 31.0) - - # check if invoice transaction lines are added and correct (outside simulation too) - invoice_transaction_line = getattr(self.invoice, 'income', None) - if invoice_transaction_line.getSource() != 'account/account1': - other_invoice_transaction_line = invoice_transaction_line - invoice_transaction_line = getattr(self.invoice, 'income_1', None) - else: - other_invoice_transaction_line = getattr(self.invoice, 'income_1', None) - self.failIf(invoice_transaction_line is None) - self.assertEqual(invoice_transaction_line.getPortalType(), 'Sale Invoice Transaction Line') - self.assertEqual(invoice_transaction_line.getSource(), 'account/account1') - self.assertEqual(invoice_transaction_line.getDestination(), 'account/account2') - self.assertEqual(invoice_transaction_line.getQuantity(), (7.0 * 11.0 + 13.0 * 17.0) * 19.0) - - invoice_transaction_line = other_invoice_transaction_line - self.failIf(invoice_transaction_line is None) - self.assertEqual(invoice_transaction_line.getPortalType(), 'Sale Invoice Transaction Line') - self.assertEqual(invoice_transaction_line.getSource(), 'account/account3') - self.assertEqual(invoice_transaction_line.getDestination(), 'account/account4') - self.assertEqual(invoice_transaction_line.getQuantity(), (23.0 * 29.0) * 31.0) - + LOG('Testing... ', INFO, message) + + self.playSequence(""" + stepCreateAccounts + stepCreateEntities + stepCreateCurrencies + stepCreateProducts + stepCreateSaleInvoiceTransactionRule + stepUpdateSaleInvoiceTransactionRuleMatrix + stepCreateNotebookFranceCell + stepTic + stepClearSimulation + stepClearAccountingModule + stepCreateSimpleSaleInvoice + stepPlanInvoice + stepTic + stepCheckFirstRuleIsApplied + stepCheckPaymentRuleIsApplied + stepConfirmInvoice + stepTic + stepCheckAccountingLinesCoherantWithSimulation + """ ) + + def test_04b_SimpleInvoiceConfirm(self, quiet=0, run=RUN_ALL_TESTS): + """ + Same test as SimpleInvoice but directly confirm the invoice without planning it + """ + 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 + stepCreateSaleInvoiceTransactionRule + stepUpdateSaleInvoiceTransactionRuleMatrix + stepCreateNotebookFranceCell + stepTic + stepClearSimulation + stepClearAccountingModule + stepCreateSimpleSaleInvoice + stepConfirmInvoice + stepTic + stepCheckAccountingLinesCreatedForSimpleInvoice + """ ) + + def test_04c_SimpleInvoiceConfirm(self, quiet=0, run=RUN_ALL_TESTS): + """ + 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: + message = 'Test Simple Invoice Rule (without plan)' + ZopeTestCase._print('\n%s ' % message) + LOG('Testing... ', INFO, message) + + self.playSequence(""" + stepCreateAccounts + stepCreateEntities + stepCreateCurrencies + stepCreateProducts + stepCreateSaleInvoiceTransactionRule + stepUpdateSaleInvoiceTransactionRuleMatrix + stepCreateNotebookFranceCell + stepTic + stepClearSimulation + stepClearAccountingModule + stepCreateSimpleSaleInvoiceTwoLines + stepConfirmInvoice + stepTic + stepCheckAccountingLinesCreatedForSimpleInvoice + """ ) + + def test_05_MultiLineInvoice(self, quiet=0, run=RUN_ALL_TESTS): + """ + Try to expand an invoice containing multiples Invoice Line. + Check that the build is correct, ie the movement are aggregated. + """ + 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 + stepCreateSaleInvoiceTransactionRule + stepTic + stepUpdateSaleInvoiceTransactionRuleMatrix + stepTic + stepCreateNotebookFranceCell + stepCreateBareboneFranceCell + stepTic + stepClearSimulation + stepClearAccountingModule + stepCreateMultiLineSaleInvoice + stepPlanInvoice + stepConfirmInvoice + stepTic + stepCheckAccountingLinesCreatedForMultiLineInvoice + """ ) + + def test_06_PaymentRuleForSaleInvoice(self, quiet=0, run=RUN_ALL_TESTS): + """ checks the payment rule is applied on sale invoice simulation + movement. """ + # checks : + # date from trade condition + # quantity from sum of receivable movement + # link to sale invoice + if __name__ == '__main__': - framework() + framework() else: - import unittest - def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestAccountingRules)) - return suite + import unittest + def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestAccountingRules)) + return suite