############################################################################# # # Copyright (c) 2004 Nexedi SARL and Contributors. All Rights Reserved. # 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 some functionnalities of accounting. """ import os, sys 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.DCWorkflow.DCWorkflow import ValidationFailed from AccessControl.SecurityManagement import newSecurityManager from zLOG import LOG from Products.ERP5Type.tests.Sequence import Sequence, SequenceList from Products.ERP5.Document.Delivery import Delivery from DateTime import DateTime SOURCE = 'source' DESTINATION = 'destination' RUN_ALL_TESTS = 1 QUIET = 1 def manage_beforeDelete(self, item, container): Delivery.manage_beforeDelete(self, item, container) def allowAccountingTransactionDeletion(): from Products.ERP5.Document.AccountingTransaction \ import AccountingTransaction old_manage_beforeDelete = AccountingTransaction.manage_beforeDelete AccountingTransaction.manage_beforeDelete = manage_beforeDelete try: from Products.ERP5Type.Document.AccountingTransaction \ import AccountingTransaction AccountingTransaction.manage_beforeDelete = manage_beforeDelete except ImportError: # ERP5Type version of this class is only available when ERP5Type document # registry has been initialized. pass class TestAccounting(ERP5TypeTestCase): """Test Accounting. """ def getAccountingModule(self): return getattr(self.getPortal(), 'accounting_module', getattr(self.getPortal(), 'accounting', None)) def getAccountModule(self) : return getattr(self.getPortal(), 'account_module', getattr(self.getPortal(), 'account', None)) # XXX def playSequence(self, sequence_string, quiet=1) : sequence_list = SequenceList() sequence_list.addSequenceString(sequence_string) sequence_list.play(self, quiet=quiet) account_portal_type = 'Account' accounting_period_portal_type = 'Accounting Period' accounting_transaction_portal_type = 'Accounting Transaction' accounting_transaction_line_portal_type = 'Accounting Transaction Line' currency_portal_type = 'Currency' organisation_portal_type = 'Organisation' sale_invoice_portal_type = 'Sale Invoice Transaction' sale_invoice_line_portal_type = 'Sale Invoice Line' sale_invoice_transaction_line_portal_type = 'Sale Invoice Transaction Line' sale_invoice_cell_portal_type = 'Invoice Cell' purchase_invoice_portal_type = 'Purchase Invoice Transaction' purchase_invoice_line_portal_type = 'Purchase Invoice Line' purchase_invoice_transaction_line_portal_type = \ 'Purchase Invoice Transaction Line' purchase_invoice_cell_portal_type = 'Invoice Cell' start_date = DateTime(2004, 01, 01) stop_date = DateTime(2004, 12, 31) default_region = 'europe/west/france' def getTitle(self): return "Accounting" def afterSetUp(self): """Prepare the test.""" self.portal = self.getPortal() self.workflow_tool = self.portal.portal_workflow self.organisation_module = self.portal.organisation_module self.account_module = self.portal.account_module self.accounting_module = self.portal.accounting_module self.createCategories() self.createCurrencies() self.createEntities() self.createAccounts() # setup preference for the vendor group self.pref = self.portal.portal_preferences.newContent( portal_type='Preference', preferred_section_category='group/vendor', preferred_accounting_transaction_section_category='group/vendor', priority=3 ) self.workflow_tool.doActionFor(self.pref, 'enable_action') self.login() def beforeTearDown(self): """Cleanup for next test. All tests uses the same accounts and same entities, so we just cleanup accounting module and simulation. """ get_transaction().abort() allowAccountingTransactionDeletion() for folder in (self.accounting_module, self.portal.portal_simulation): folder.manage_delObjects([i for i in folder.objectIds()]) get_transaction().commit() self.tic() def login(self) : """sets the security manager""" uf = self.getPortal().acl_users uf._doAddUser('alex', '', ['Member', 'Assignee', 'Assignor', 'Auditor', 'Author', 'Manager'], []) user = uf.getUserById('alex').__of__(uf) newSecurityManager(None, user) def createCategories(self): """Create the categories for our test. """ # create categories for cat_string in self.getNeededCategoryList(): 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) else: path = path[cat] # check categories have been created for cat_string in self.getNeededCategoryList() : self.assertNotEquals(None, self.getCategoryTool().restrictedTraverse(cat_string), cat_string) def getNeededCategoryList(self): """Returns a list of categories that should be created.""" return ('group/client', 'group/vendor/sub1', 'group/vendor/sub2', 'payment_mode/check', 'region/%s' % self.default_region, ) def getBusinessTemplateList(self): """Returns list of BT to be installed.""" return ('erp5_base', 'erp5_pdm', 'erp5_trade', 'erp5_accounting', ) def stepTic(self, **kw): """Flush activity queue. """ self.tic() def createEntities(self): """Create entities. """ self.client = self.getOrganisationModule().newContent( title = 'Client', portal_type = self.organisation_portal_type, group = "client", price_currency = "currency_module/USD") self.vendor = self.getOrganisationModule().newContent( title = 'Vendor', portal_type = self.organisation_portal_type, group = "vendor/sub1", price_currency = "currency_module/EUR") self.other_vendor = self.getOrganisationModule().newContent( title = 'Other Vendor', portal_type = self.organisation_portal_type, group = "vendor/sub2", price_currency = "currency_module/EUR") # validate entities for entity in (self.client, self.vendor, self.other_vendor): entity.setRegion(self.default_region) self.getWorkflowTool().doActionFor(entity, 'validate_action') get_transaction().commit() self.tic() def stepCreateEntities(self, sequence, **kw) : """Create entities. """ # TODO: remove this method sequence.edit( client=self.client, vendor=self.vendor, other_vendor=self.other_vendor, organisation=self.vendor ) def stepCreateAccountingPeriod(self, sequence, **kw): """Creates an Accounting Period for the Organisation.""" organisation = sequence.get('organisation') start_date = self.start_date stop_date = self.stop_date accounting_period = organisation.newContent( portal_type = self.accounting_period_portal_type, start_date = start_date, stop_date = stop_date ) sequence.edit( accounting_period = accounting_period, valid_date_list = [ start_date, start_date+1, stop_date], invalid_date_list = [start_date-1, stop_date+1] ) def stepUseValidDates(self, sequence, **kw): """Puts some valid dates in sequence.""" sequence.edit(date_list = sequence.get('valid_date_list')) def stepUseInvalidDates(self, sequence, **kw): """Puts some invalid dates in sequence.""" sequence.edit(date_list = sequence.get('invalid_date_list')) def stepOpenAccountingPeriod(self, sequence, **kw): """Opens the Accounting Period.""" accounting_period = sequence.get('accounting_period') self.getPortal().portal_workflow.doActionFor( accounting_period, 'plan_action' ) self.assertEquals(accounting_period.getSimulationState(), 'planned') def stepConfirmAccountingPeriod(self, sequence, **kw): """Confirm the Accounting Period.""" accounting_period = sequence.get('accounting_period') self.getPortal().portal_workflow.doActionFor( accounting_period, 'confirm_action' ) self.assertEquals(accounting_period.getSimulationState(), 'confirmed') def stepCheckAccountingPeriodRefusesClosing(self, sequence, **kw): """Checks the Accounting Period refuses closing.""" accounting_period = sequence.get('accounting_period') self.assertRaises(ValidationFailed, self.getPortal().portal_workflow.doActionFor, accounting_period, 'confirm_action' ) def stepDeliverAccountingPeriod(self, sequence, **kw): """Deliver the Accounting Period.""" accounting_period = sequence.get('accounting_period') self.getPortal().portal_workflow.doActionFor( accounting_period, 'close_action' ) self.assertEquals(accounting_period.getSimulationState(), 'closing') def stepCheckAccountingPeriodDelivered(self, sequence, **kw): """Check the Accounting Period is delivered.""" accounting_period = sequence.get('accounting_period') self.assertEquals(accounting_period.getSimulationState(), 'delivered') def createCurrencies(self): """Create some currencies. This script will reuse existing currencies, because we want currency ids to be stable, as we use them as categories. """ currency_module = self.getCurrencyModule() if not hasattr(currency_module, 'EUR'): self.EUR = currency_module.newContent( portal_type = self.currency_portal_type, reference = "EUR", id = "EUR" ) self.USD = currency_module.newContent( portal_type = self.currency_portal_type, reference = "USD", id = "USD" ) self.YEN = currency_module.newContent( portal_type = self.currency_portal_type, reference = "YEN", id = "YEN" ) get_transaction().commit() self.tic() else: self.EUR = currency_module.EUR self.USD = currency_module.USD self.YEN = currency_module.YEN def stepCreateCurrencies(self, sequence, **kw) : """Create some currencies. """ # TODO: remove sequence.edit(EUR=self.EUR, USD=self.USD, YEN=self.YEN) def createAccounts(self): """Create some accounts. """ receivable = self.receivable_account = self.getAccountModule().newContent( title = 'receivable', portal_type = self.account_portal_type, account_type = 'asset/receivable' ) payable = self.payable_account = self.getAccountModule().newContent( title = 'payable', portal_type = self.account_portal_type, account_type = 'liability/payable' ) expense = self.expense_account = self.getAccountModule().newContent( title = 'expense', portal_type = self.account_portal_type, account_type = 'expense' ) income = self.income_account = self.getAccountModule().newContent( title = 'income', portal_type = self.account_portal_type, account_type = 'income' ) collected_vat = self.collected_vat_account = self\ .getAccountModule().newContent( title = 'collected_vat', portal_type = self.account_portal_type, account_type = 'liability/payable/collected_vat' ) refundable_vat = self.refundable_vat_account = self\ .getAccountModule().newContent( title = 'refundable_vat', portal_type = self.account_portal_type, account_type = 'asset/receivable/refundable_vat' ) bank = self.bank_account = self.getAccountModule().newContent( title = 'bank', portal_type = self.account_portal_type, account_type = 'asset/cash/bank') # set mirror accounts. receivable.setDestinationValue(payable) payable.setDestinationValue(receivable) expense.setDestinationValue(income) income.setDestinationValue(expense) collected_vat.setDestinationValue(refundable_vat) refundable_vat.setDestinationValue(collected_vat) bank.setDestinationValue(bank) self.account_list = [ receivable, payable, expense, income, collected_vat, refundable_vat, bank ] for account in self.account_list : account.validate() self.failUnless('Site Error' not in account.view()) self.assertEquals(account.getValidationState(), 'validated') get_transaction().commit() self.tic() def stepCreateAccounts(self, sequence, **kw) : """Create necessary accounts. """ # XXX remove me ! sequence.edit( receivable_account=self.receivable_account, payable_account=self.payable_account, expense_account=self.expense_account, income_account=self.income_account, collected_vat_account=self.collected_vat_account, refundable_vat_account=self.refundable_vat_account, bank_account=self.bank_account, account_list=self.account_list ) def stepCreateAccountingTransactionAndCheckMirrorAccount(self, sequence, **kw): """Check that mirror account are set automatically. """ account_list = sequence.get('account_list') for account in account_list : self.assertNotEquals(account.getDestinationValue(), None) transaction = self.getAccountingModule().newContent( portal_type = self.accounting_transaction_portal_type, source_section_value = sequence.get('client'), resource_value = sequence.get('EUR'), created_by_builder = 1, ) # setting both source and destination shouldn't use mirror accounts destination = sequence.get('receivable_account') for account in account_list : transaction_line = transaction.newContent( portal_type = self.accounting_transaction_line_portal_type, source = account.getRelativeUrl(), destination = destination.getRelativeUrl(), ) self.assertEquals( destination.getRelativeUrl(), transaction_line.getDestination() ) # setting only a source must use mirror account as destination for account in account_list : transaction_line = transaction.newContent( portal_type = self.accounting_transaction_line_portal_type, source = account.getRelativeUrl(), ) self.assertEquals( account.getDestination(), transaction_line.getDestination() ) # editing the destination later should not change the source once # the mirror account has been set. account = sequence.get('receivable_account') destination = sequence.get('bank_account') another_destination = sequence.get('expense_account') account.setDestinationValueList(account_list) transaction_line = transaction.newContent( portal_type = self.accounting_transaction_line_portal_type, source = account.getRelativeUrl(), ) automatically_set_destination = transaction_line.getDestinationValue() # get another account. if automatically_set_destination == destination : forced_destination = destination else : forced_destination = another_destination # set all other accounts as mirror account to this one. forced_destination.setDestinationValueList(account_list) # change the destination and check the source didn't change. transaction_line.edit(destination = forced_destination.getRelativeUrl()) self.assertEquals( transaction_line.getSourceValue(), account ) def getInvoicePropertyList(self): """Returns the list of properties for invoices, stored as a list of dictionnaries. """ # source currency is EUR # destination currency is USD return [ # in currency of destination, converted for source { 'income' : -200, 'source_converted_income' : -180, 'collected_vat' : -40, 'source_converted_collected_vat' : -36, 'receivable' : 240, 'source_converted_receivable' : 216, 'currency' : 'currency_module/USD' }, # in currency of source, converted for destination { 'income' : -100, 'destination_converted_expense' : -200, 'collected_vat' : 10, 'destination_converted_refundable_vat' : 100, 'receivable' : 90, 'destination_converted_payable' : 100, 'currency' : 'currency_module/EUR' }, { 'income' : -100, 'destination_converted_expense' : -200, 'collected_vat' : 10, 'destination_converted_refundable_vat' : 100, 'receivable' : 90, 'destination_converted_payable' : 100, 'currency' : 'currency_module/EUR' }, # in an external currency, converted for both source and dest. { 'income' : -300, 'source_converted_income' : -200, 'destination_converted_expense' : -400, 'collected_vat' : 40, 'source_converted_collected_vat' : 36, 'destination_converted_refundable_vat' : 50, 'receivable' : 260, 'source_converted_receivable' : 164, 'destination_converted_payable': 350, 'currency' : 'currency_module/YEN' }, # currency of source, not converted for destination -> 0 { 'income' : -100, 'collected_vat' : -20, 'receivable' : 120, 'currency' : 'currency_module/EUR' }, ] def stepCreateInvoices(self, sequence, **kw) : """Create invoices with properties from getInvoicePropertyList. """ invoice_prop_list = self.getInvoicePropertyList() invoice_list = [] date_list = sequence.get('date_list') if not date_list : date_list = [ DateTime(2004, 12, 31) ] i = 0 for invoice_prop in invoice_prop_list : i += 1 date = date_list[i % len(date_list)] invoice = self.getAccountingModule().newContent( portal_type = self.sale_invoice_portal_type, source_section_value = sequence.get('vendor'), source_value = sequence.get('vendor'), destination_section_value = sequence.get('client'), destination_value = sequence.get('client'), resource = invoice_prop['currency'], start_date = date, stop_date = date, created_by_builder = 0, ) for line_type in ['income', 'receivable', 'collected_vat'] : source_account = sequence.get('%s_account' % line_type) line = invoice.newContent( portal_type = self.sale_invoice_transaction_line_portal_type, quantity = invoice_prop[line_type], source_value = source_account ) source_converted = invoice_prop.get( 'source_converted_%s' % line_type, None) if source_converted is not None : line.setSourceTotalAssetPrice(source_converted) destination_account = source_account.getDestinationValue( portal_type = 'Account' ) destination_converted = invoice_prop.get( 'destination_converted_%s' % destination_account.getAccountTypeId(), None) if destination_converted is not None : line.setDestinationTotalAssetPrice(destination_converted) invoice_list.append(invoice) sequence.edit( invoice_list = invoice_list ) def stepCreateOtherSectionInvoices(self, sequence, **kw): """Create invoice for other sections.""" other_source = self.getOrganisationModule().newContent( portal_type = 'Organisation' ) other_destination = self.getOrganisationModule().newContent( portal_type = 'Organisation' ) invoice = self.getAccountingModule().newContent( portal_type = self.sale_invoice_portal_type, source_section_value = other_source, source_value = other_source, destination_section_value = other_destination, destination_value = other_destination, resource_value = sequence.get('EUR'), start_date = self.start_date, stop_date = self.start_date, created_by_builder = 0, ) line = invoice.newContent( portal_type = self.sale_invoice_transaction_line_portal_type, quantity = 100, source_value = sequence.get('account_list')[0]) line = invoice.newContent( portal_type = self.sale_invoice_transaction_line_portal_type, quantity = -100, source_value = sequence.get('account_list')[1]) sequence.edit(invoice_list = [invoice]) def stepStopInvoices(self, sequence, **kw) : """Validates invoices.""" invoice_list = sequence.get('invoice_list') for invoice in invoice_list: self.getPortal().portal_workflow.doActionFor( invoice, 'stop_action') def stepCheckStopInvoicesRefused(self, sequence, **kw) : """Checks that invoices cannot be validated.""" invoice_list = sequence.get('invoice_list') for invoice in invoice_list: self.assertRaises(ValidationFailed, self.getPortal().portal_workflow.doActionFor, invoice, 'stop_action') def stepCheckInvoicesAreDraft(self, sequence, **kw) : """Checks invoices are in draft state.""" invoice_list = sequence.get('invoice_list') for invoice in invoice_list: self.assertEquals(invoice.getSimulationState(), 'draft') def stepCheckInvoicesAreStopped(self, sequence, **kw) : """Checks invoices are in stopped state.""" invoice_list = sequence.get('invoice_list') for invoice in invoice_list: self.assertEquals(invoice.getSimulationState(), 'stopped') def stepCheckInvoicesAreDelivered(self, sequence, **kw) : """Checks invoices are in delivered state.""" invoice_list = sequence.get('invoice_list') for invoice in invoice_list: self.assertEquals(invoice.getSimulationState(), 'delivered') def checkAccountBalanceInCurrency(self, section, currency, sequence, **kw) : """ Checks accounts balances in a given currency.""" invoice_list = sequence.get('invoice_list') for account_type in [ 'income', 'receivable', 'collected_vat', 'expense', 'payable', 'refundable_vat' ] : account = sequence.get('%s_account' % account_type) calculated_balance = 0 for invoice in invoice_list : for line in invoice.getMovementList(): # source if line.getSourceValue() == account and\ line.getResourceValue() == currency and\ section == line.getSourceSectionValue() : calculated_balance += ( line.getSourceDebit() - line.getSourceCredit()) # dest. elif line.getDestinationValue() == account and\ line.getResourceValue() == currency and\ section == line.getDestinationSectionValue() : calculated_balance += ( line.getDestinationDebit() - line.getDestinationCredit()) self.assertEquals(calculated_balance, self.getPortal().portal_simulation.getInventory( node_uid = account.getUid(), section_uid = section.getUid(), resource_uid = currency.getUid(), )) def stepCheckAccountBalanceLocalCurrency(self, sequence, **kw) : """ Checks accounts balances in the organisation default currency.""" for section in (sequence.get('vendor'), sequence.get('client')) : currency = section.getPriceCurrencyValue() self.checkAccountBalanceInCurrency(section, currency, sequence) def stepCheckAccountBalanceExternalCurrency(self, sequence, **kw) : """ Checks accounts balances in external currencies .""" for section in (sequence.get('vendor'), sequence.get('client')) : for currency in (sequence.get('USD'), sequence.get('YEN')) : self.checkAccountBalanceInCurrency(section, currency, sequence) def checkAccountBalanceInConvertedCurrency(self, section, sequence, **kw) : """ Checks accounts balances converted in section default currency.""" invoice_list = sequence.get('invoice_list') for account_type in [ 'income', 'receivable', 'collected_vat', 'expense', 'payable', 'refundable_vat' ] : account = sequence.get('%s_account' % account_type) calculated_balance = 0 for invoice in invoice_list : for line in invoice.getMovementList() : if line.getSourceValue() == account and \ section == line.getSourceSectionValue() : calculated_balance += line.getSourceInventoriatedTotalAssetPrice() elif line.getDestinationValue() == account and\ section == line.getDestinationSectionValue() : calculated_balance += \ line.getDestinationInventoriatedTotalAssetPrice() self.assertEquals(calculated_balance, self.getPortal().portal_simulation.getInventoryAssetPrice( node_uid = account.getUid(), section_uid = section.getUid(), )) def stepCheckAccountBalanceConvertedCurrency(self, sequence, **kw): """Checks accounts balances converted in the organisation default currency.""" for section in (sequence.get('vendor'), sequence.get('client')) : self.checkAccountBalanceInConvertedCurrency(section, sequence) def stepCheckAccountingTransactionDelivered(self, sequence, **kw): """Checks all accounting transaction related to `organisation` are in delivered state. """ organisation = sequence.get('organisation').getRelativeUrl() accounting_module = self.getPortal().accounting_module for transaction in accounting_module.objectValues() : if transaction.getSourceSection() == organisation \ or transaction.getDestinationSection() == organisation : if self.start_date <= transaction.getStartDate() <= self.stop_date : self.assertEquals(transaction.getSimulationState(), 'delivered') def stepCheckAcquisition(self, sequence, **kw): """Checks acquisition and portal types configuration. """ resource_value = sequence.get('EUR') source_section_title = "Source Section Title" destination_section_title = "Destination Section Title" source_section_value = self.getOrganisationModule().newContent( portal_type = self.organisation_portal_type, title = source_section_title, group = "group/client", price_currency = "currency_module/USD") destination_section_value = self.getOrganisationModule().newContent( portal_type = self.organisation_portal_type, title = destination_section_title, group = "group/vendor", price_currency = "currency_module/EUR") portal = self.getPortal() accounting_module = portal.accounting_module self.failUnless('Site Error' not in accounting_module.view()) self.assertNotEquals( len(portal.getPortalAccountingMovementTypeList()), 0) self.assertNotEquals( len(portal.getPortalAccountingTransactionTypeList()), 0) for accounting_portal_type in portal\ .getPortalAccountingTransactionTypeList(): accounting_transaction = accounting_module.newContent( portal_type = accounting_portal_type, source_section_value = source_section_value, destination_section_value = destination_section_value, resource_value = resource_value ) self.failUnless('Site Error' not in accounting_transaction.view()) self.assertEquals( accounting_transaction.getSourceSectionValue(), source_section_value ) self.assertEquals( accounting_transaction.getDestinationSectionValue(), destination_section_value ) self.assertEquals( accounting_transaction.getResourceValue(), resource_value ) self.assertNotEquals( len(accounting_transaction.allowedContentTypes()), 0) tested_line_portal_type = 0 for line_portal_type in portal.getPortalAccountingMovementTypeList(): allowed_content_types = [x.id for x in accounting_transaction.allowedContentTypes()] if line_portal_type in allowed_content_types : line = accounting_transaction.newContent( portal_type = line_portal_type, ) self.failUnless('Site Error' not in line.view()) # section and resource is acquired from parent transaction. self.assertEquals( line.getDestinationSectionValue(), destination_section_value ) self.assertEquals( line.getDestinationSectionTitle(), destination_section_title ) self.assertEquals( line.getSourceSectionValue(), source_section_value ) self.assertEquals( line.getSourceSectionTitle(), source_section_title ) self.assertEquals( line.getResourceValue(), resource_value ) tested_line_portal_type = 1 self.assert_(tested_line_portal_type, ("No lines tested ... " + "getPortalAccountingMovementTypeList = %s " + "<%s>.allowedContentTypes = %s") % (portal.getPortalAccountingMovementTypeList(), accounting_transaction.getPortalType(), allowed_content_types )) def createAccountingTransaction(self, portal_type=accounting_transaction_portal_type, line_portal_type=accounting_transaction_line_portal_type, quantity=100, reindex=1, check_consistency=1, **kw): """Creates an accounting transaction. By default, this transaction contains 2 lines, income and receivable. quantity - The quantity property on created lines. reindex - The transaction will be reindexed. check_consistency - a consistency check will be performed on the transaction. """ kw.setdefault('resource_value', self.EUR) kw.setdefault('source_section_value', self.vendor) kw.setdefault('destination_section_value', self.client) if 'start_date' not in kw: start_date = DateTime(2000, 01, 01) # get a valid date for source section for openned_source_section_period in\ kw['source_section_value'].searchFolder( portal_type=self.accounting_period_portal_type, simulation_state='planned' ): start_date = openned_source_section_period.getStartDate() + 1 kw['start_date'] = start_date if 'stop_date' not in kw: # get a valid date for destination section stop_date = DateTime(2000, 02, 02) for openned_destination_section_period in\ kw['destination_section_value'].searchFolder( portal_type=self.accounting_period_portal_type, simulation_state='planned' ): stop_date = openned_destination_section_period.getStartDate() + 1 kw['stop_date'] = stop_date # create the transaction. transaction = self.getAccountingModule().newContent( portal_type=portal_type, start_date=kw['start_date'], stop_date=kw['stop_date'], resource_value=kw['resource_value'], source_section_value=kw['source_section_value'], destination_section_value=kw['destination_section_value'], created_by_builder = 1 # prevent the init script from # creating lines. ) income = transaction.newContent( id='income', portal_type=line_portal_type, quantity=-quantity, source_value=kw.get('income_account', self.income_account), destination_value=kw.get('expense_account', self.expense_account), ) self.failUnless(income.getSource() != None) self.failUnless(income.getDestination() != None) receivable = transaction.newContent( id='receivable', portal_type=line_portal_type, quantity=quantity, source_value=kw.get('receivable_account', self.receivable_account), destination_value=kw.get('payable_account', self.payable_account), ) self.failUnless(receivable.getSource() != None) self.failUnless(receivable.getDestination() != None) if reindex: get_transaction().commit() self.tic() if check_consistency: self.failUnless(len(transaction.checkConsistency()) == 0, "Check consistency failed : %s" % transaction.checkConsistency()) return transaction def test_createAccountingTransaction(self): """Make sure acounting transactions created by createAccountingTransaction method are valid. """ transaction = self.createAccountingTransaction() self.assertEquals(self.vendor, transaction.getSourceSectionValue()) self.assertEquals(self.client, transaction.getDestinationSectionValue()) self.assertEquals(self.EUR, transaction.getResourceValue()) self.failUnless(transaction.AccountingTransaction_isSourceView()) self.workflow_tool.doActionFor(transaction, 'stop_action') self.assertEquals('stopped', transaction.getSimulationState()) self.assertEquals([] , transaction.checkConsistency()) def stepCreateValidAccountingTransaction(self, sequence, sequence_list=None, **kw) : """Creates a valid accounting transaction and put it in the sequence as `transaction` key. """ transaction = self.createAccountingTransaction( resource_value=sequence.get('EUR'), source_section_value=sequence.get('vendor'), destination_section_value=sequence.get('client'), income_account=sequence.get('income_account'), expense_account=sequence.get('expense_account'), receivable_account=sequence.get('receivable_account'), payable_account=sequence.get('payable_account'), ) sequence.edit( transaction = transaction, income = transaction.income, receivable = transaction.receivable ) def stepValidateNoDate(self, sequence, sequence_list=None, **kw) : """When no date is defined, validation should be impossible. Actually, we could say that if we have source_section, we need start_date, and if we have destination section, we need stop_date only, but we decided to update a date (of start_date / stop_date) using the other one if one is missing. (ie. stop_date defaults automatically to start_date if not set and start_date is set to stop_date in the workflow script if not set. """ transaction = sequence.get('transaction') old_stop_date = transaction.getStopDate() old_start_date = transaction.getStartDate() transaction.setStopDate(None) if transaction.getStopDate() != None : transaction.setStartDate(None) transaction.setStopDate(None) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') transaction.setStartDate(old_start_date) transaction.setStopDate(old_stop_date) self.getWorkflowTool().doActionFor(transaction, 'stop_action') self.assertEquals(transaction.getSimulationState(), 'stopped') def stepValidateNoSection(self, sequence, sequence_list=None, **kw) : """Check validation behaviour related to section & mirror_section. When no source section is defined, we are in one of the following cases : o if we use payable or receivable account, the validation should be refused. o if we do not use any payable or receivable accounts and we have a destination section, validation should be ok. """ transaction = sequence.get('transaction') old_source_section = transaction.getSourceSection() old_destination_section = transaction.getDestinationSection() # default transaction uses payable accounts, so validating without # source section is refused. transaction.setSourceSection(None) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') # ... as well as validation without destination section transaction.setSourceSection(old_source_section) transaction.setDestinationSection(None) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') # mirror section can be set only on the line for line in transaction.getMovementList() : if line.getSourceValue().isMemberOf( 'account_type/asset/receivable') or \ line.getSourceValue().isMemberOf( 'account_type/liability/payable') : line.setDestinationSection(old_destination_section) try: self.getWorkflowTool().doActionFor(transaction, 'stop_action') self.assertEquals(transaction.getSimulationState(), 'stopped') except ValidationFailed, err : self.assert_(0, "Validation failed : %s" % err.msg) # if we do not use any payable / receivable account, then we can # validate the transaction without setting the mirror section. for side in (SOURCE, ): # DESTINATION) : # TODO: for now, we only test for source, as it makes no sense to use for # destination section only. We could theoritically support it. # get a new valid transaction transaction = self.createAccountingTransaction() expense_account = sequence.get('expense_account') for line in transaction.getMovementList() : line.edit( source_value = expense_account, destination_value = expense_account ) if side == SOURCE : transaction.setDestinationSection(None) else : transaction.setSourceSection(None) try: self.getWorkflowTool().doActionFor(transaction, 'stop_action') self.assertEquals(transaction.getSimulationState(), 'stopped') except ValidationFailed, err : self.assert_(0, "Validation failed : %s" % err.msg) def stepValidateNoCurrency(self, sequence, sequence_list=None, **kw) : """Check validation behaviour related to currency. """ transaction = sequence.get('transaction') old_resource = transaction.getResource() transaction.setResource(None) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') # setting a dummy relationship is not enough, resource must be a # currency transaction.setResource(transaction.getDestinationSection()) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') def stepValidateClosedAccount(self, sequence, sequence_list=None, **kw) : """Check validation behaviour related to closed accounts. If an account is blocked, then it's impossible to validate a transaction related to this account. """ transaction = sequence.get('transaction') account = transaction.getMovementList()[0].getSourceValue() self.getWorkflowTool().doActionFor(account, 'invalidate_action') self.assertEquals(account.getValidationState(), 'invalidated') self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') # reopen the account for other tests account.validate() self.assertEquals(account.getValidationState(), 'validated') def stepValidateNoAccounts(self, sequence, sequence_list=None, **kw) : """Simple check that the validation is refused when we do not have accounts correctly defined on lines. """ transaction = sequence.get('transaction') # no account at all is refused for line in transaction.getMovementList(): line.setSource(None) line.setDestination(None) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') # only one line without account and with a quantity is also refused transaction = self.createAccountingTransaction() transaction.getMovementList()[0].setSource(None) transaction.getMovementList()[0].setDestination(None) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') # but if we have a line with 0 quantity on both sides, we can # validate the transaction and delete this line. transaction = self.createAccountingTransaction() line_count = len(transaction.getMovementList()) transaction.newContent( portal_type = self.accounting_transaction_line_portal_type) self.getWorkflowTool().doActionFor(transaction, 'stop_action') self.assertEquals(transaction.getSimulationState(), 'stopped') self.assertEquals(line_count, len(transaction.getMovementList())) # 0 quantity, but a destination asset price => do not delete the # line transaction = self.createAccountingTransaction() new_line = transaction.newContent( portal_type = self.accounting_transaction_line_portal_type) self.assertEquals(len(transaction.getMovementList()), 3) line_list = transaction.getMovementList() line_list[0].setDestinationTotalAssetPrice(100) line_list[0]._setCategoryMembership( 'destination', sequence.get('expense_account').getRelativeUrl()) line_list[1].setDestinationTotalAssetPrice(- 50) line_list[1]._setCategoryMembership( 'destination', sequence.get('expense_account').getRelativeUrl()) line_list[2].setDestinationTotalAssetPrice(- 50) line_list[2]._setCategoryMembership( 'destination', sequence.get('expense_account').getRelativeUrl()) try: self.getWorkflowTool().doActionFor(transaction, 'stop_action') self.assertEquals(transaction.getSimulationState(), 'stopped') except ValidationFailed, err : self.assert_(0, "Validation failed : %s" % err.msg) def stepValidateNotBalanced(self, sequence, sequence_list=None, **kw) : """Check validation behaviour when transaction is not balanced. """ transaction = sequence.get('transaction') transaction.getMovementList()[0].setQuantity(4325) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') # asset price have priority (ie. if asset price is not balanced, # refuses validation even if quantity is balanced) transaction = self.createAccountingTransaction() line_list = transaction.getMovementList() line_list[0].setDestinationTotalAssetPrice(10) line_list[1].setDestinationTotalAssetPrice(100) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') transaction = self.createAccountingTransaction() line_list = transaction.getMovementList() line_list[0].setSourceTotalAssetPrice(10) line_list[1].setSourceTotalAssetPrice(100) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') # only asset price needs to be balanced transaction = self.createAccountingTransaction() line_list = transaction.getMovementList() line_list[0].setSourceTotalAssetPrice(100) line_list[0].setDestinationTotalAssetPrice(100) line_list[0].setQuantity(432432) line_list[1].setSourceTotalAssetPrice(-100) line_list[1].setDestinationTotalAssetPrice(-100) line_list[1].setQuantity(32546787) try: self.getWorkflowTool().doActionFor(transaction, 'stop_action') self.assertEquals(transaction.getSimulationState(), 'stopped') except ValidationFailed, err : self.assert_(0, "Validation failed : %s" % err.msg) def stepValidateNoPayment(self, sequence, sequence_list=None, **kw) : """Check validation behaviour related to payment & mirror_payment. If we use an account of type asset/cash/bank, we must use set a Bank Account as source_payment or destination_payment. This this source/destination payment must be a portal type from the `payment node` portal type group. It can be defined on transaction or line. """ def useBankAccount(transaction): """Modify the transaction, so that a line will use an account member of account_type/cash/bank , which requires to use a payment category. """ # get the default and replace income account by bank income_account_found = 0 for line in transaction.getMovementList() : source_account = line.getSourceValue() if source_account.isMemberOf('account_type/income') : income_account_found = 1 line.edit( source_value = sequence.get('bank_account'), destination_value = sequence.get('bank_account') ) self.failUnless(income_account_found) # XXX transaction = sequence.get('transaction') useBankAccount(transaction) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') source_section_value = transaction.getSourceSectionValue() destination_section_value = transaction.getDestinationSectionValue() for ptype in self.getPortal().getPortalPaymentNodeTypeList() : source_payment_value = source_section_value.newContent( portal_type = ptype, ) destination_payment_value = destination_section_value.newContent( portal_type = ptype, ) transaction = self.createAccountingTransaction( destination_section_value=self.other_vendor) useBankAccount(transaction) # payment node have to be set on both sides if both sides are member of # the same group. transaction.setSourcePaymentValue(source_payment_value) transaction.setDestinationPaymentValue(None) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') transaction.setSourcePaymentValue(None) transaction.setDestinationPaymentValue(destination_payment_value) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') transaction.setSourcePaymentValue(source_payment_value) transaction.setDestinationPaymentValue(destination_payment_value) try: self.getWorkflowTool().doActionFor(transaction, 'stop_action') self.assertEquals(transaction.getSimulationState(), 'stopped') except ValidationFailed, err : self.fail("Validation failed : %s" % err.msg) # if we are not interested in the accounting for the third party, no need # to have a destination_payment transaction = self.createAccountingTransaction() useBankAccount(transaction) # only set payment for source transaction.setSourcePaymentValue(source_payment_value) transaction.setDestinationPaymentValue(None) # then we should be able to validate. try: self.getWorkflowTool().doActionFor(transaction, 'stop_action') self.assertEquals(transaction.getSimulationState(), 'stopped') except ValidationFailed, err: self.fail("Validation failed : %s" % err.msg) def stepValidateRemoveEmptyLines(self, sequence, sequence_list=None, **kw): """Check validating a transaction remove empty lines. """ transaction = sequence.get('transaction') lines_count = len(transaction.getMovementList()) empty_lines_count = 0 for line in transaction.getMovementList(): if line.getSourceTotalAssetPrice() == \ line.getDestinationTotalAssetPrice() == 0: empty_lines_count += 1 if empty_lines_count == 0: transaction.newContent( portal_type=self.accounting_transaction_line_portal_type) self.getWorkflowTool().doActionFor(transaction, 'stop_action') self.assertEquals(len(transaction.getMovementList()), lines_count - empty_lines_count) # we don't remove empty lines if there is only empty lines transaction = self.getAccountingModule().newContent( portal_type=self.accounting_transaction_portal_type, created_by_builder=1) for i in range(3): transaction.newContent( portal_type=self.accounting_transaction_line_portal_type) lines_count = len(transaction.getMovementList()) transaction.AccountingTransaction_deleteEmptyLines(redirect=0) self.assertEquals(len(transaction.getMovementList()), lines_count) ############################################################################ ## Test Methods ############################################################ ############################################################################ def test_MultiCurrencyInvoice(self, quiet=QUIET, run=RUN_ALL_TESTS): """Basic test for multi currency accounting""" if not run : return self.playSequence(""" stepCreateCurrencies stepCreateEntities stepCreateAccounts stepCreateInvoices stepTic stepCheckAccountBalanceLocalCurrency stepCheckAccountBalanceExternalCurrency stepCheckAccountBalanceConvertedCurrency """, quiet=quiet) def test_AccountingPeriod(self, quiet=QUIET, run=RUN_ALL_TESTS): """Basic test for Accounting Periods""" if not run : return self.playSequence(""" stepCreateCurrencies stepCreateEntities stepCreateAccounts stepCreateAccountingPeriod stepOpenAccountingPeriod stepTic stepUseValidDates stepCreateInvoices stepStopInvoices stepCheckInvoicesAreStopped stepTic stepConfirmAccountingPeriod stepTic stepDeliverAccountingPeriod stepTic stepCheckAccountingPeriodDelivered stepCheckInvoicesAreDelivered stepTic stepCheckAccountingTransactionDelivered """, quiet=quiet) def test_AccountingPeriodRefusesWrongDateTransactionValidation( self, quiet=QUIET, run=RUN_ALL_TESTS): """Accounting Periods prevents transactions to be validated when there is no oppened accounting period""" if not run : return self.playSequence(""" stepCreateCurrencies stepCreateEntities stepCreateAccounts stepCreateAccountingPeriod stepOpenAccountingPeriod stepTic stepUseInvalidDates stepCreateInvoices stepCheckStopInvoicesRefused stepTic stepCheckInvoicesAreDraft """, quiet=quiet) def test_AccountingPeriodNotStoppedTransactions(self, quiet=QUIET, run=RUN_ALL_TESTS): """Accounting Periods refuse to close when some transactions are not stopped""" if not run : return self.playSequence(""" stepCreateCurrencies stepCreateEntities stepCreateAccounts stepCreateAccountingPeriod stepOpenAccountingPeriod stepTic stepCreateInvoices stepTic stepCheckAccountingPeriodRefusesClosing stepTic stepCheckInvoicesAreDraft """, quiet=quiet) def test_AccountingPeriodOtherSections(self, quiet=QUIET, run=RUN_ALL_TESTS): """Accounting Periods does not change other section transactions.""" if not run : return self.playSequence(""" stepCreateCurrencies stepCreateEntities stepCreateAccounts stepCreateAccountingPeriod stepOpenAccountingPeriod stepTic stepCreateOtherSectionInvoices stepTic stepConfirmAccountingPeriod stepTic stepDeliverAccountingPeriod stepTic stepCheckAccountingPeriodDelivered stepCheckInvoicesAreDraft """, quiet=quiet) def test_MirrorAccounts(self, quiet=QUIET, run=RUN_ALL_TESTS): """Tests using an account on one sides uses the mirror account on the other size. """ if not run : return self.playSequence(""" stepCreateEntities stepCreateAccounts stepCreateAccountingTransactionAndCheckMirrorAccount """, quiet=quiet) def test_Acquisition(self, quiet=QUIET, run=RUN_ALL_TESTS): """Tests acquisition, categories and portal types are well configured. """ if not run : return self.playSequence(""" stepCreateCurrencies stepCheckAcquisition """, quiet=quiet) def test_AccountingTransactionValidationDate(self, quiet=QUIET, run=RUN_ALL_TESTS): """Transaction validation and dates""" if not run : return self.playSequence(""" stepCreateEntities stepCreateCurrencies stepCreateAccounts stepCreateValidAccountingTransaction stepValidateNoDate""", quiet=quiet) def test_AccountingTransactionValidationSection(self, quiet=QUIET, run=RUN_ALL_TESTS): """Transaction validation and section""" if not run : return self.playSequence(""" stepCreateEntities stepCreateCurrencies stepCreateAccounts stepCreateValidAccountingTransaction stepValidateNoSection""", quiet=quiet) def test_AccountingTransactionValidationCurrency(self, quiet=QUIET, run=RUN_ALL_TESTS): """Transaction validation and currency""" if not run : return self.playSequence(""" stepCreateEntities stepCreateCurrencies stepCreateAccounts stepCreateValidAccountingTransaction stepValidateNoCurrency""", quiet=quiet) def test_AccountingTransactionValidationAccounts(self, quiet=QUIET, run=RUN_ALL_TESTS): """Transaction validation and accounts""" if not run : return self.playSequence(""" stepCreateEntities stepCreateCurrencies stepCreateAccounts stepCreateValidAccountingTransaction stepValidateClosedAccount stepCreateValidAccountingTransaction stepValidateNoAccounts""", quiet=quiet) def test_AccountingTransactionValidationBalanced(self, quiet=QUIET, run=RUN_ALL_TESTS): """Transaction validation and balance""" if not run : return self.playSequence(""" stepCreateEntities stepCreateCurrencies stepCreateAccounts stepCreateValidAccountingTransaction stepValidateNotBalanced""", quiet=quiet) def test_AccountingTransactionValidationPayment(self, quiet=QUIET, run=RUN_ALL_TESTS): """Transaction validation and payment""" if not run : return self.playSequence(""" stepCreateEntities stepCreateCurrencies stepCreateAccounts stepCreateValidAccountingTransaction stepValidateNoPayment """, quiet=quiet) def test_AccountingTransactionValidationRemoveEmptyLines(self, quiet=QUIET, run=RUN_ALL_TESTS): """Transaction validation removes empty lines""" if not run : return self.playSequence(""" stepCreateEntities stepCreateCurrencies stepCreateAccounts stepCreateValidAccountingTransaction stepValidateRemoveEmptyLines """, quiet=quiet) def test_AccountingTransactionValidationRefusedWithCategoriesAsSections(self, quiet=QUIET, run=RUN_ALL_TESTS): """Validating a transaction with categories as sections is refused. See http://wiki.erp5.org/Discussion/AccountingProblems """ category = self.vendor.getGroupValue() self.assertNotEquals(category, None) transaction = self.createAccountingTransaction( source_section_value=category) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') transaction = self.createAccountingTransaction( destination_section_value=category) self.assertRaises(ValidationFailed, self.getWorkflowTool().doActionFor, transaction, 'stop_action') def test_Account_isCreditAccount(self): """Tests the 'credit_account' property on account, which was named is_credit_account, which generated isIsCreditAccount accessor""" account = self.getAccountModule().newContent(portal_type='Account') # simulate an old object account.is_credit_account = True account.setCreditAccount(True) self.failUnless(account.isCreditAccount()) # this is what Account_view would do when you update the code before # without updating business template. account.edit(is_credit_account=False) self.failIf(account.isCreditAccount()) account.edit(is_credit_account=True) self.failUnless(account.getProperty('is_credit_account')) # tests for Invoice_createRelatedPaymentTransaction def _checkRelatedSalePayment(self, invoice, payment, payment_node, quantity): """Check payment of a Sale Invoice. """ eq = self.assertEquals eq('Payment Transaction', payment.getPortalTypeName()) eq([invoice], payment.getCausalityValueList()) eq(invoice.getSourceSection(), payment.getSourceSection()) eq(invoice.getDestinationSection(), payment.getDestinationSection()) eq(payment_node, payment.getSourcePaymentValue()) eq(self.getCategoryTool().payment_mode.check, payment.getPaymentModeValue()) # test lines eq(2, len(payment.getMovementList())) for line in payment.getMovementList(): if line.getId() == 'bank': eq(quantity, line.getSourceCredit()) eq(self.bank_account, line.getSourceValue()) else: eq(quantity, line.getSourceDebit()) eq(self.receivable_account, line.getSourceValue()) # this transaction can be validated eq([], payment.checkConsistency()) self.workflow_tool.doActionFor(payment, 'stop_action') self.assertEquals('stopped', payment.getSimulationState()) def test_Invoice_createRelatedPaymentTransactionSimple(self): """Simple case of creating a related payment transaction. """ payment_node = self.vendor.newContent(portal_type='Bank Account') invoice = self.createAccountingTransaction() payment = invoice.Invoice_createRelatedPaymentTransaction( node=self.bank_account.getRelativeUrl(), payment=payment_node.getRelativeUrl(), payment_mode='check', batch_mode=1) self._checkRelatedSalePayment(invoice, payment, payment_node, 100) def test_Invoice_createRelatedPaymentTransactionGroupedLines(self): """Simple creating a related payment transaction when grouping reference of some lines is already set. """ payment_node = self.vendor.newContent(portal_type='Bank Account') invoice = self.createAccountingTransaction() invoice.receivable.setSourceCredit(60) invoice.newContent(id='receivable_groupped', source_credit=40, source_value=self.receivable_account) invoice.receivable_groupped.setGroupingReference('A') payment = invoice.Invoice_createRelatedPaymentTransaction( node=self.bank_account.getRelativeUrl(), payment=payment_node.getRelativeUrl(), payment_mode='check', batch_mode=1) self._checkRelatedSalePayment(invoice, payment, payment_node, 60) def test_Invoice_createRelatedPaymentTransactionDifferentSection(self): """Simple creating a related payment transaction when we have two line for 2 different destination sections. """ payment_node = self.vendor.newContent(portal_type='Bank Account') invoice = self.createAccountingTransaction() invoice.receivable.setSourceCredit(60) invoice.newContent(id='receivable_other_third_party', destination_section_value=self.other_vendor, source_credit=40, source_value=self.receivable_account) payment = invoice.Invoice_createRelatedPaymentTransaction( node=self.bank_account.getRelativeUrl(), payment=payment_node.getRelativeUrl(), payment_mode='check', batch_mode=1) self._checkRelatedSalePayment(invoice, payment, payment_node, 60) def test_Invoice_createRelatedPaymentTransactionRelatedInvoice(self): """Simple creating a related payment transaction when we have related transactions. """ payment_node = self.vendor.newContent(portal_type='Bank Account') invoice = self.createAccountingTransaction() accounting_transaction = self.createAccountingTransaction() accounting_transaction.receivable.setSourceDebit(20) accounting_transaction.income.setSourceCredit(20) accounting_transaction.setCausalityValue(invoice) self.portal.portal_workflow.doActionFor(accounting_transaction, 'stop_action') self.assertEquals('stopped', accounting_transaction.getSimulationState()) get_transaction().commit() self.tic() payment = invoice.Invoice_createRelatedPaymentTransaction( node=self.bank_account.getRelativeUrl(), payment=payment_node.getRelativeUrl(), payment_mode='check', batch_mode=1) self._checkRelatedSalePayment(invoice, payment, payment_node, 80) def test_Invoice_createRelatedPaymentTransactionRelatedInvoiceDifferentSide(self): """Simple creating a related payment transaction when we have related transactions with different side """ payment_node = self.vendor.newContent(portal_type='Bank Account') invoice = self.createAccountingTransaction() accounting_transaction = self.createAccountingTransaction() accounting_transaction.edit( source_section=accounting_transaction.getDestinationSection(), destination_section=accounting_transaction.getSourceSection()) accounting_transaction.receivable.edit( source=accounting_transaction.receivable.getDestination(), destination=accounting_transaction.receivable.getSource(), destination_debit=20) accounting_transaction.income.edit( source=accounting_transaction.income.getDestination(), destination=accounting_transaction.income.getSource(), destination_credit=20) accounting_transaction.setCausalityValue(invoice) self.portal.portal_workflow.doActionFor(accounting_transaction, 'stop_action') self.assertEquals('stopped', accounting_transaction.getSimulationState()) get_transaction().commit() self.tic() payment = invoice.Invoice_createRelatedPaymentTransaction( node=self.bank_account.getRelativeUrl(), payment=payment_node.getRelativeUrl(), payment_mode='check', batch_mode=1) self._checkRelatedSalePayment(invoice, payment, payment_node, 80) def test_Invoice_createRelatedPaymentTransactionRelatedInvoiceDraft(self): """Simple creating a related payment transaction when we have related transactions in draft/cancelled state (they are ignored) """ payment_node = self.vendor.newContent(portal_type='Bank Account') invoice = self.createAccountingTransaction() accounting_transaction = self.createAccountingTransaction() accounting_transaction.setCausalityValue(invoice) other_accounting_transaction = self.createAccountingTransaction() other_accounting_transaction.setCausalityValue(invoice) other_accounting_transaction.cancel() get_transaction().commit() self.tic() payment = invoice.Invoice_createRelatedPaymentTransaction( node=self.bank_account.getRelativeUrl(), payment=payment_node.getRelativeUrl(), payment_mode='check', batch_mode=1) self._checkRelatedSalePayment(invoice, payment, payment_node, 100) if __name__ == '__main__': framework() else: import unittest def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TestAccounting)) return suite