############################################################################## # # Copyright (c) 2004 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> # Jerome 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 # 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 # 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. """ # TODO : # * test with a Person as destination_section # * test cancelling / deleting an invoice # * test payment rule & payment builder # * test simulation purge when Payment delivered or top level Order cancelled # * test removing cells for a line # import os, sys, random if __name__ == '__main__': execfile(os.path.join(sys.path[0], 'framework.py')) # Needed in order to have a log file inside the current folder os.environ['EVENT_LOG_FILE'] = os.path.join(os.getcwd(), 'zLOG.log') os.environ['EVENT_LOG_SEVERITY'] = '-300' from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase 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 the simulation tree and builds starting from the invoice. """ RUN_ALL_TESTS = 1 RUN_BROKEN_TESTS = os.getenv('RUN_BROKEN_TESTS', 0) # 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 = "Invoice Line" sale_invoice_cell_portal_type = "Invoice Cell" invoice_transaction_rule_portal_type \ = "Invoice Transaction Rule" purchase_invoice_transaction_portal_type \ = "Purchase Invoice Transaction" payment_transaction_portal_type = "Payment Transaction" def getTitle(self): return "Accounting Rules" def getBusinessTemplateList(self): """ Return the list of business templates. """ return ('erp5_base','erp5_pdm', 'erp5_trade', 'erp5_accounting') def getAccountModule(self): return getattr(self.getPortal(), 'account', getattr(self.getPortal(), 'account_module')) def getAccountingModule(self): return getattr(self.getPortal(), 'accounting', getattr(self.getPortal(), 'accounting_module')) def getProductModule(self): 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', 'Owner', 'Assignor'], []) user = uf.getUserById('alex').__of__(uf) newSecurityManager(None, user) 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() 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 stepCommitTransaction(self, **kw): get_transaction().commit() 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 invoice_transaction_rule.edit() invoice_transaction_rule.updateMatrix() self.tic() # check the accounting rule cells inside the matrix cell_list = invoice_transaction_rule.contentValues( filter = {'portal_type':self.accounting_rule_cell_portal_type}) self.assertEqual(len(cell_list), 4) # 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 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') empty_invoice = self.getAccountingModule().newContent( id = 'empty_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(), created_by_builder = 1, ) sequence.edit( simple_invoice = empty_invoice, invoice = empty_invoice, ) 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(), created_by_builder = 1, ) invoice_line = simple_invoice.newContent( id = 'invoice_line', resource = product_notebook.getRelativeUrl(), quantity = 10, price = 10, portal_type = self.sale_invoice_line_portal_type) self.assertEqual(invoice_line.getTotalPrice(), 100) sequence.edit( simple_invoice = simple_invoice, invoice = simple_invoice, invoice_line = invoice_line, invoice_lines = [invoice_line] ) def stepCreateOtherSimpleSaleInvoice(self, sequence, **kw) : """ 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') simple_invoice = self.getAccountingModule().newContent( id = 'other_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(), created_by_builder = 1, ) invoice_line = simple_invoice.newContent( id = 'invoice_line', resource = product_notebook.getRelativeUrl(), quantity = 123, price = 456, portal_type = self.sale_invoice_line_portal_type) sequence.edit( simple_invoice = simple_invoice, invoice = simple_invoice, invoice_line = invoice_line, invoice_lines = [invoice_line] ) def stepAddInvoiceLine(self, sequence, **kw) : """ add an invoice line in the current invoice : 10 notebook * 10 EUR, so total price is 100 """ product_notebook = sequence.get('product_notebook') invoice = sequence.get('invoice') invoice_line = invoice.newContent( id = 'invoice_line_%s'%(int(random.random()*1000)), portal_type = self.sale_invoice_line_portal_type) invoice_line.edit( resource = product_notebook.getRelativeUrl(), quantity = 10, price = 10 ) self.assertEqual(invoice_line.getTotalPrice(), 100) sequence.edit( invoice_line = invoice_line, invoice_lines = [invoice_line] ) def stepEditInvoiceLine(self, sequence, **kw) : """ edit the invoice line : 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) self.assertEqual(invoice_line.getTotalPrice(), 100) def stepDeleteInvoiceLine(self, sequence, **kw) : """ remove an invoice line from the invoice """ invoice = sequence.get('invoice') invoice_line = sequence.get('invoice_line') invoice._delObject(invoice_line.getId()) invoice.recursiveReindexObject() def stepUpdateAppliedRule(self, sequence, **kw) : """ update the applied rule for the invoice. In the UI, the call to updateAppliedRule is made in an interraction workflow when you edit an invoice or its content.""" # edit is done through interaction workflow, so we just call 'edit' # on the invoice (but this is not necessary) invoice=sequence.get('invoice') invoice.edit() 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(), created_by_builder = 1, ) invoice_line1 = simple_invoice.newContent( id = 'invoice_line1', resource = product_notebook.getRelativeUrl(), quantity = 5, price = 10, portal_type = self.sale_invoice_line_portal_type) invoice_line2 = simple_invoice.newContent( id = 'invoice_line2', REsource = product_notebook.getRelativeUrl(), quantity = 5, price = 10, portal_type = self.sale_invoice_line_portal_type) 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 stepCreateSimpleSaleInvoiceTwoCells(self, sequence, **kw) : """ similar to stepCreateSimpleSaleInvoiceTwoLines, but use two differents cells on the same line instead of two differents lines. """ 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_cells', 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(), created_by_builder = 1, ) invoice_line = simple_invoice.newContent( id = 'invoice_line', resource = product_notebook.getRelativeUrl(), portal_type = self.sale_invoice_line_portal_type) sequence.edit( simple_invoice = simple_invoice, invoice = simple_invoice, invoice_line = invoice_line, invoice_lines = [invoice_line] ) 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') # 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)) # 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, portal_type = self.sale_invoice_cell_portal_type, *cell_key) cell.edit(mapped_value_property_list = ['price','quantity'], price = price, quantity = quantity, predicate_category_list = cell_key, variation_category_list = cell_key) # getTotalPrice uses mysql, so we must make sure the invoice is cataloged # to have correct results invoice_line.getParent().recursiveImmediateReindexObject() self.assertEqual(invoice_line.getTotalPrice(), 100) self.assertEqual(invoice_line.getTotalQuantity(), 10) # then we must have 2 cells inside our line self.assertEqual(len(invoice_line.objectIds()), 2) 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(), created_by_builder = 1, ) 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 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 # 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 : self.assertNotEquals(simulation_movement.getOrderValue(), None) if simulation_movement.getOrderValue().getPortalType() == \ self.sale_invoice_line_portal_type : invoice_line_simulation_movement_list.append(simulation_movement) self.assertEqual( len(invoice_line_simulation_movement_list), 1) simulation_movement = invoice_line_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 stepCollectSimulationMovements(self, sequence, **kw) : """ put some simulation movements in sequence for later checkings """ invoice = sequence.get('invoice') invoice_line = sequence.get('invoice_line') applied_rule_list = self.getSimulationTool().contentValues() self.assertEquals(len(applied_rule_list), 1) simulation_movement_list = [] simulation_movement_quantities = {} simulation_movement_resources = {} simulation_movement_paths = {} simulation_movement_section_paths = {} 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() : path = simulation_movement.getPath() 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') for simulation_movement in simulation_movement_list : path = simulation_movement.getPath() self.assertEquals( simulation_movement.getQuantity(), simulation_movement_quantities[path] ) self.assertEquals( simulation_movement.getResource(), simulation_movement_resources[path] ) self.assertEquals( (simulation_movement.getSource(), simulation_movement.getDestination()), simulation_movement_paths[path] ) self.assertEquals( ( simulation_movement.getSourceSection(), simulation_movement.getDestinationSection()), simulation_movement_section_paths[path] ) def stepCheckPaymentRuleIsApplied(self, sequence, **kw) : """ checks that a payment rule is applied for the total amount of receivable """ # TODO 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', skip_period_validation = 1 ) self.assertEquals(invoice.getSimulationState(), 'planned') 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', skip_period_validation = 1 ) 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 : self.assert_( invoice_transaction_line.getSourceId() in accounting_lines_layout.keys(), 'unexepected source_id %s' % invoice_transaction_line.getSourceId()) debit, credit = accounting_lines_layout[ invoice_transaction_line.getSourceId()] self.assertEquals(debit, invoice_transaction_line.getSourceDebit()) self.assertEquals(credit, invoice_transaction_line.getSourceCredit()) self.assertNotEquals( len(invoice_transaction_line.getDeliveryRelatedValueList( portal_type='Simulation Movement')), 0) 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()) self.assertNotEquals( len(invoice_transaction_line.getDeliveryRelatedValueList( portal_type='Simulation Movement')), 0) 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() != \ self.sale_invoice_line_portal_type : transaction_dict[accounting_line.getId()] = \ accounting_line.getTotalQuantity() accounting_lines_dict[transaction.getId()] = transaction_dict # 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() 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() # nothing should have changed self.assertEquals(accounting_transaction_count, len(self.getAccountingModule().objectIds())) for transaction in self.getAccountingModule().objectValues() : transaction_dict = accounting_lines_dict[transaction.getId()] for accounting_line in transaction.objectValues() : if accounting_line.getPortalType() != \ self.sale_invoice_line_portal_type : self.assertEquals( transaction_dict[accounting_line.getId()], accounting_line.getTotalQuantity()) 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... ', INFO, 'testHasEverything') self.failUnless(self.getCategoryTool() != None) self.failUnless(self.getSimulationTool() != None) self.failUnless(self.getTypeTool() != None) self.failUnless(self.getSqlConnection() != None) self.failUnless(self.getCatalogTool() != None) self.failUnless(self.getRuleTool() != None) self.failUnless(self.getAccountModule() != None) self.failUnless(self.getAccountingModule() != None) self.failUnless(self.getOrganisationModule() != None) self.failUnless(self.getProductModule() != None) self.failUnless(self.getCurrencyModule() != None) def test_02_UpdateInvoiceTransactionRuleMatrix(self, quiet=0, run=RUN_ALL_TESTS): """ test edition of matrix and rule. Try to update the matrix after adding some predicates, and check if all objects were created """ if not run: return if not quiet: message = 'Test Update Invoice Transaction Rule Matrix' ZopeTestCase._print('\n%s ' % message) 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 predicates for the cells of invoice transaction rule """ if not run: return if not quiet: message = 'Test Invoice Transaction Rule getMatchingCell ' ZopeTestCase._print('\n%s ' % message) 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): """ Simple Invoice. Try to expand an invoice containing only one simple Invoice Line. Check that the build is correct. """ if not run: return if not quiet: message = 'Test Simple Invoice Rule' ZopeTestCase._print('\n%s ' % message) 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 stepRebuildAndCheckNothingIsCreated """ ) def test_04c_SimpleInvoiceTwoLines(self, quiet=0, run=RUN_ALL_TESTS): """ Simple Invoice, 2 lines. 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 (with 2 lines)' 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 stepRebuildAndCheckNothingIsCreated """ ) def test_04d_SimpleInvoiceTwoCells(self, quiet=0, run=RUN_ALL_TESTS): """ Simple Invoice, 2 cells. Same test as SimpleInvoice but use 2 cells of quantity 5 instead of 1 line of quantity 10. """ 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 stepCreateSaleInvoiceTransactionRule stepUpdateSaleInvoiceTransactionRuleMatrix stepCreateNotebookFranceCell stepTic stepClearSimulation stepClearAccountingModule stepCreateSimpleSaleInvoiceTwoCells stepConfirmInvoice stepTic stepCheckAccountingLinesCreatedForSimpleInvoice stepRebuildAndCheckNothingIsCreated """ ) # next 5 tests will check update of applied rules. def test_05a_SimpleInvoiceReExpandAddLine(self, quiet=0, run=RUN_ALL_TESTS): """ Add a new line then updateAppliedRule. 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 stepCreateSaleInvoiceTransactionRule stepUpdateSaleInvoiceTransactionRuleMatrix stepCreateNotebookFranceCell stepTic stepClearSimulation stepClearAccountingModule stepCreateEmptyInvoice stepPlanInvoice stepTic stepAddInvoiceLine stepConfirmInvoice stepTic stepCheckAccountingLinesCreatedForSimpleInvoice stepRebuildAndCheckNothingIsCreated """ ) def test_05b_SimpleInvoiceReExpandEditLine(self, quiet=0, run = RUN_ALL_TESTS): """ 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 stepCreateSaleInvoiceTransactionRule stepUpdateSaleInvoiceTransactionRuleMatrix stepCreateNotebookFranceCell stepTic stepClearSimulation stepClearAccountingModule stepCreateOtherSimpleSaleInvoice stepPlanInvoice stepTic stepEditInvoiceLine stepConfirmInvoice stepTic stepCheckAccountingLinesCreatedForSimpleInvoice stepRebuildAndCheckNothingIsCreated """ ) def test_05c_SimpleInvoiceReExpandDeleteLine( self, quiet=0, run=RUN_ALL_TESTS): """ 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 stepCreateSaleInvoiceTransactionRule stepUpdateSaleInvoiceTransactionRuleMatrix stepCreateNotebookFranceCell stepTic stepClearSimulation stepClearAccountingModule stepCreateSimpleSaleInvoice stepAddInvoiceLine stepPlanInvoice stepTic stepDeleteInvoiceLine stepTic stepConfirmInvoice stepTic stepCheckAccountingLinesCreatedForSimpleInvoice stepRebuildAndCheckNothingIsCreated """ ) def test_05d_SimpleInvoiceReExpandCreateCell(self, quiet=0, run=RUN_ALL_TESTS): """ 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 stepCreateSaleInvoiceTransactionRule stepUpdateSaleInvoiceTransactionRuleMatrix stepCreateNotebookFranceCell stepTic stepClearSimulation stepClearAccountingModule stepCreateOtherSimpleSaleInvoice stepPlanInvoice stepTic stepAddCellsInInvoiceLine stepConfirmInvoice stepTic stepCheckAccountingLinesCreatedForSimpleInvoice stepRebuildAndCheckNothingIsCreated """) def test_05e_SimpleInvoiceExpandManyTimes( self, quiet=0, run=RUN_ALL_TESTS): """ Tests that updating an applied rule many times doesn't break the build """ if not run: return if not quiet: message = 'Test Simple Invoice Rule (many updateAppliedRule)' ZopeTestCase._print('\n%s ' % message) LOG('Testing... ', INFO, message) self.playSequence(""" stepCreateAccounts stepCreateEntities stepCreateCurrencies stepCreateProducts stepCreateSaleInvoiceTransactionRule stepUpdateSaleInvoiceTransactionRuleMatrix stepCreateNotebookFranceCell stepTic stepClearSimulation stepClearAccountingModule stepCreateSimpleSaleInvoice stepPlanInvoice stepTic """ + (""" stepEditInvoiceLine stepUpdateAppliedRule stepTic""" * 4) + """ stepConfirmInvoice stepTic stepCheckAccountingLinesCreatedForSimpleInvoice stepRebuildAndCheckNothingIsCreated """ ) def test_06_MultiLineInvoice(self, quiet=0, run=RUN_ALL_TESTS): """ Multiple lines invoice. Try to expand an invoice containing multiples Invoice Line. Check that the build is correct, ie similar movements 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 stepRebuildAndCheckNothingIsCreated """ ) def test_07_PaymentRuleForSaleInvoice(self, quiet=0, run=RUN_ALL_TESTS): """ Payment Rule. 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() else: import unittest def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TestAccountingRules)) return suite