# This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import datetime from decimal import Decimal from dateutil.relativedelta import relativedelta from trytond.exceptions import UserError from trytond.model.exceptions import SQLConstraintError from trytond.modules.account.exceptions import PeriodDatesError from trytond.modules.account.tax import TaxableMixin from trytond.modules.company.tests import ( CompanyTestMixin, PartyCompanyCheckEraseMixin, create_company, set_company) from trytond.modules.currency.tests import create_currency from trytond.modules.party.tests import PartyCheckReplaceMixin from trytond.pool import Pool from trytond.tests.test_tryton import ModuleTestCase, with_transaction from trytond.transaction import Transaction, inactive_records def create_chart(company, tax=False, chart='account.account_template_root_en'): pool = Pool() AccountTemplate = pool.get('account.account.template') TaxTemplate = pool.get('account.tax.template') TaxCodeTemplate = pool.get('account.tax.code.template') ModelData = pool.get('ir.model.data') CreateChart = pool.get('account.create_chart', type='wizard') Account = pool.get('account.account') module, xml_id = chart.split('.') template = AccountTemplate(ModelData.get_id(module, xml_id)) if tax: tax_account = AccountTemplate(ModelData.get_id( 'account', 'account_template_6_3_4_en')) with Transaction().set_user(0): tax = TaxTemplate() tax.name = tax.description = '20% VAT' tax.type = 'percentage' tax.rate = Decimal('0.2') tax.account = template tax.invoice_account = tax_account tax.credit_note_account = tax_account tax.save() TaxCodeTemplate.create([{ 'name': 'Tax Code', 'account': template, 'lines': [('create', [{ 'operator': '+', 'type': 'invoice', 'amount': 'tax', 'tax': tax.id, }])], }, { 'name': 'Base Code', 'account': template, 'lines': [('create', [{ 'operator': '+', 'type': 'invoice', 'amount': 'base', 'tax': tax.id, }])], }]) session_id, _, _ = CreateChart.create() create_chart = CreateChart(session_id) create_chart.account.account_template = template create_chart.account.company = company create_chart.transition_create_account() properties_state = create_chart.states['properties'] for name, value in properties_state.get_defaults( create_chart, 'properties', list(create_chart.properties._fields.keys())).items(): if name in create_chart.properties._fields: setattr(create_chart.properties, name, value) if not create_chart.properties.account_receivable: receivable, = Account.search([ ('type.receivable', '=', True), ('party_required', '=', True), ('company', '=', company.id), ], limit=1) create_chart.properties.account_receivable = receivable if not create_chart.properties.account_payable: payable, = Account.search([ ('type.payable', '=', True), ('party_required', '=', True), ('company', '=', company.id), ], limit=1) create_chart.properties.account_payable = payable create_chart.transition_create_properties() def get_fiscalyear(company, today=None, start_date=None, end_date=None): pool = Pool() Sequence = pool.get('ir.sequence.strict') SequenceType = pool.get('ir.sequence.type') FiscalYear = pool.get('account.fiscalyear') if not today: today = datetime.date.today() if not start_date: start_date = today.replace(month=1, day=1) if not end_date: end_date = today.replace(month=12, day=31) sequence_type, = SequenceType.search([ ('name', '=', "Account Move"), ], limit=1) sequence, = Sequence.create([{ 'name': '%s' % today.year, 'sequence_type': sequence_type.id, 'company': company.id, }]) fiscalyear = FiscalYear(name='%s' % today.year, company=company) fiscalyear.start_date = start_date fiscalyear.end_date = end_date fiscalyear.move_sequence = sequence return fiscalyear def close_fiscalyear(fiscalyear): pool = Pool() Journal = pool.get('account.journal') Period = pool.get('account.period') Account = pool.get('account.account') Move = pool.get('account.move') FiscalYear = pool.get('account.fiscalyear') BalanceNonDeferral = pool.get( 'account.fiscalyear.balance_non_deferral', type='wizard') # Balance non-deferral journal_closing, = Journal.create([{ 'name': 'Closing', 'code': 'CLO', 'type': 'situation', }]) period_closing, = Period.create([{ 'name': 'Closing', 'start_date': fiscalyear.end_date, 'end_date': fiscalyear.end_date, 'fiscalyear': fiscalyear.id, 'type': 'adjustment', }]) account_pl, = Account.search([ ('code', '=', '3.2.1'), ]) session_id = BalanceNonDeferral.create()[0] balance_non_deferral = BalanceNonDeferral(session_id) balance_non_deferral.start.fiscalyear = fiscalyear balance_non_deferral.start.journal = journal_closing balance_non_deferral.start.period = period_closing balance_non_deferral.start.credit_account = account_pl balance_non_deferral.start.debit_account = account_pl balance_non_deferral._execute('balance') moves = Move.search([ ('state', '=', 'draft'), ('period.fiscalyear', '=', fiscalyear.id), ]) Move.post(moves) # Close fiscalyear FiscalYear.close([fiscalyear]) class AccountTestCase( PartyCompanyCheckEraseMixin, PartyCheckReplaceMixin, CompanyTestMixin, ModuleTestCase): 'Test Account module' module = 'account' maxDiff = None @with_transaction() def test_account_chart(self): 'Test creation and update of minimal chart of accounts' pool = Pool() Account = pool.get('account.account') Tax = pool.get('account.tax') UpdateChart = pool.get('account.update_chart', type='wizard') company = create_company() with set_company(company): create_chart(company, tax=True) root, = Account.search([('parent', '=', None)]) # Create an account and tax without template cash, = Account.search([('code', '=', '1.1.1')]) Account.copy([cash.id]) tax, = Tax.search([]) Tax.copy([tax.id]) session_id, _, _ = UpdateChart.create() update_chart = UpdateChart(session_id) update_chart.start.account = root update_chart.transition_update() @with_transaction() def test_account_chart_many_companies(self): "Test creation of chart of accounts for many companies" company1 = create_company() with set_company(company1): create_chart(company1, tax=True) company2 = create_company() with set_company(company2): create_chart(company2, tax=True) @with_transaction() def test_fiscalyear(self): 'Test fiscalyear' pool = Pool() FiscalYear = pool.get('account.fiscalyear') company = create_company() with set_company(company): fiscalyear = get_fiscalyear(company) fiscalyear.save() FiscalYear.create_period([fiscalyear]) self.assertEqual(len(fiscalyear.periods), 12) @with_transaction() def test_fiscalyear_create_periods(self): 'Test fiscalyear create periods' FiscalYear = Pool().get('account.fiscalyear') company = create_company() with set_company(company): year = datetime.date.today().year date = datetime.date for start_date, end_date, interval, end_day, num_periods in [ (date(year, 1, 1), date(year, 12, 31), 1, 31, 12), (date(year + 1, 1, 1), date(year + 1, 12, 31), 3, 31, 4), (date(year + 2, 1, 1), date(year + 2, 12, 31), 5, 31, 3), (date(year + 3, 4, 6), date(year + 4, 4, 5), 1, 5, 12), (date(year + 4, 4, 6), date(year + 5, 4, 5), 3, 5, 4), (date(year + 5, 4, 6), date(year + 6, 4, 5), 5, 5, 3), (date(year + 6, 6, 6), date(year + 6, 12, 31), 1, 29, 8), (date(year + 7, 7, 7), date(year + 7, 12, 31), 3, 29, 3), (date(year + 8, 1, 1), date(year + 9, 8, 7), 1, 29, 20), (date(year + 9, 9, 9), date(year + 10, 11, 12), 3, 29, 5), ]: fiscalyear = get_fiscalyear( company, start_date, start_date, end_date) fiscalyear.save() FiscalYear.create_period([fiscalyear], interval, end_day) self.assertEqual(len(fiscalyear.periods), num_periods) self.assertEqual(fiscalyear.periods[-1].end_date, end_date) self.assertTrue(all( p.end_date == p.end_date + relativedelta(day=end_day) for p in fiscalyear.periods[:-1])) self.assertEqual(fiscalyear.periods[0].start_date, start_date) self.assertTrue(all( p1.end_date + relativedelta(days=1) == p2.start_date for p1, p2 in zip( fiscalyear.periods[:-1], fiscalyear.periods[1:]))) @with_transaction() def test_open_fiscalyear_before_open(self): "Test create open fiscal year before an open one" company = create_company() with set_company(company): create_chart(company) fiscalyear = get_fiscalyear( company, start_date=datetime.date(2022, 1, 1), end_date=datetime.date(2022, 12, 31)) fiscalyear.save() earlier_fiscalyear = get_fiscalyear( company, start_date=datetime.date(2021, 1, 1), end_date=datetime.date(2021, 12, 31)) earlier_fiscalyear.save() @with_transaction() def test_open_fiscalyear_before_close(self): "Test create open fiscal year before a close one" company = create_company() with set_company(company): create_chart(company) fiscalyear = get_fiscalyear( company, start_date=datetime.date(2022, 1, 1), end_date=datetime.date(2022, 12, 31)) fiscalyear.save() close_fiscalyear(fiscalyear) earlier_fiscalyear = get_fiscalyear( company, start_date=datetime.date(2021, 1, 1), end_date=datetime.date(2021, 12, 31)) with self.assertRaises(SQLConstraintError): earlier_fiscalyear.save() @with_transaction() def test_change_fiscalyear_dates_with_periods(self): "Test change fiscal year dates" company = create_company() with set_company(company): fiscalyear = get_fiscalyear( company, start_date=datetime.date(2023, 1, 1), end_date=datetime.date(2023, 12, 31)) fiscalyear.save() fiscalyear.create_period([fiscalyear]) # Extend fiscalyear.end_date = datetime.date(2024, 5, 31) fiscalyear.save() # Shorten with self.assertRaises(PeriodDatesError): fiscalyear.end_date = datetime.date(2023, 5, 31) fiscalyear.save() @with_transaction() def test_change_period_dates_with_move(self): "Test change period dates" pool = Pool() Period = pool.get('account.period') Move = pool.get('account.move') Journal = pool.get('account.journal') company = create_company() with set_company(company): create_chart(company) fiscalyear = get_fiscalyear(company) fiscalyear.save() period = Period( fiscalyear=fiscalyear, type='adjustment', name="Period", start_date=fiscalyear.start_date, end_date=fiscalyear.start_date) period.save() journal, = Journal.search([], limit=1) move, = Move.create([{ 'period': period.id, 'journal': journal.id, 'date': period.start_date, }]) # Extend period.end_date = fiscalyear.end_date period.save() # Shorten with self.assertRaises(PeriodDatesError): period.start_date = fiscalyear.end_date period.save() @with_transaction() def test_account_debit_credit(self): 'Test account debit/credit' pool = Pool() Party = pool.get('party.party') FiscalYear = pool.get('account.fiscalyear') Journal = pool.get('account.journal') Account = pool.get('account.account') Move = pool.get('account.move') party = Party(name='Party') party.save() company = create_company() with set_company(company): fiscalyear = get_fiscalyear(company) fiscalyear.save() FiscalYear.create_period([fiscalyear]) period = fiscalyear.periods[0] create_chart(company) sec_cur = create_currency('sec') journal_revenue, = Journal.search([ ('code', '=', 'REV'), ]) journal_expense, = Journal.search([ ('code', '=', 'EXP'), ]) revenue, = Account.search([ ('type.revenue', '=', True), ('closed', '=', False), ], limit=1) receivable, = Account.search([ ('type.receivable', '=', True), ('closed', '=', False), ], limit=1) expense, = Account.search([ ('type.expense', '=', True), ('closed', '=', False), ], limit=1) payable, = Account.search([ ('type.payable', '=', True), ('closed', '=', False), ], limit=1) cash, = Account.search([ ('code', '=', '1.1.1'), ]) cash_cur, = Account.copy([cash], default={ 'second_currency': sec_cur.id, }) # Create some moves vlist = [ { 'period': period.id, 'journal': journal_revenue.id, 'date': period.start_date, 'lines': [ ('create', [{ 'account': revenue.id, 'credit': Decimal(100), }, { 'account': receivable.id, 'debit': Decimal(100), 'party': party.id, }]), ], }, { 'period': period.id, 'journal': journal_revenue.id, 'date': period.start_date, 'lines': [ ('create', [{ 'account': receivable.id, 'credit': Decimal(80), 'second_currency': sec_cur.id, 'amount_second_currency': -Decimal(50), 'party': party.id, }, { 'account': cash_cur.id, 'debit': Decimal(80), 'second_currency': sec_cur.id, 'amount_second_currency': Decimal(50), }]), ], }, { 'period': period.id, 'journal': journal_expense.id, 'date': period.start_date, 'lines': [ ('create', [{ 'account': expense.id, 'debit': Decimal(30), }, { 'account': payable.id, 'credit': Decimal(30), 'party': party.id, }]), ], }, ] Move.create(vlist) # Test debit/credit self.assertEqual((revenue.debit, revenue.credit), (Decimal(0), Decimal(100))) self.assertEqual(revenue.balance, Decimal(-100)) self.assertEqual((cash_cur.debit, cash_cur.credit), (Decimal(80), Decimal(0))) self.assertEqual( cash_cur.amount_second_currency, Decimal(50)) # Use next fiscalyear today = datetime.date.today() next_fiscalyear = get_fiscalyear(company, today=today + relativedelta(years=1)) next_fiscalyear.save() FiscalYear.create_period([next_fiscalyear]) # Test debit/credit for next year with Transaction().set_context(fiscalyear=next_fiscalyear.id): revenue = Account(revenue.id) self.assertEqual((revenue.debit, revenue.credit), (Decimal(0), Decimal(0))) self.assertEqual(revenue.balance, Decimal(-100)) cash_cur = Account(cash_cur.id) self.assertEqual( cash_cur.amount_second_currency, Decimal(50)) # Test debit/credit cumulate for next year with Transaction().set_context(fiscalyear=next_fiscalyear.id, cumulate=True): revenue = Account(revenue.id) self.assertEqual((revenue.debit, revenue.credit), (Decimal(0), Decimal(100))) self.assertEqual(revenue.balance, Decimal(-100)) cash_cur = Account(cash_cur.id) self.assertEqual( cash_cur.amount_second_currency, Decimal(50)) close_fiscalyear(fiscalyear) # Check deferral self.assertEqual(revenue.deferrals, ()) deferral_receivable, = receivable.deferrals self.assertEqual( (deferral_receivable.debit, deferral_receivable.credit), (Decimal(100), Decimal(80))) self.assertEqual(deferral_receivable.fiscalyear, fiscalyear) cash_cur = Account(cash_cur.id) deferral_cash_cur, = cash_cur.deferrals self.assertEqual( deferral_cash_cur.amount_second_currency, Decimal(50)) # Test debit/credit with Transaction().set_context(fiscalyear=fiscalyear.id): revenue = Account(revenue.id) self.assertEqual((revenue.debit, revenue.credit), (Decimal(100), Decimal(100))) self.assertEqual(revenue.balance, Decimal(0)) receivable = Account(receivable.id) self.assertEqual((receivable.debit, receivable.credit), (Decimal(100), Decimal(80))) self.assertEqual(receivable.balance, Decimal(20)) cash_cur = Account(cash_cur.id) self.assertEqual( cash_cur.amount_second_currency, Decimal(50)) # Test debit/credit for next year with Transaction().set_context(fiscalyear=next_fiscalyear.id): revenue = Account(revenue.id) self.assertEqual((revenue.debit, revenue.credit), (Decimal(0), Decimal(0))) self.assertEqual(revenue.balance, Decimal(0)) receivable = Account(receivable.id) self.assertEqual((receivable.debit, receivable.credit), (Decimal(0), Decimal(0))) self.assertEqual(receivable.balance, Decimal(20)) cash_cur = Account(cash_cur.id) self.assertEqual( cash_cur.amount_second_currency, Decimal(50)) # Test debit/credit cumulate for next year with Transaction().set_context(fiscalyear=next_fiscalyear.id, cumulate=True): revenue = Account(revenue.id) self.assertEqual((revenue.debit, revenue.credit), (Decimal(0), Decimal(0))) self.assertEqual(revenue.balance, Decimal(0)) receivable = Account(receivable.id) self.assertEqual((receivable.debit, receivable.credit), (Decimal(100), Decimal(80))) self.assertEqual(receivable.balance, Decimal(20)) cash_cur = Account(cash_cur.id) self.assertEqual( cash_cur.amount_second_currency, Decimal(50)) @with_transaction() def test_account_type_amount(self): "Test account type amount" pool = Pool() Party = pool.get('party.party') FiscalYear = pool.get('account.fiscalyear') Journal = pool.get('account.journal') Account = pool.get('account.account') Move = pool.get('account.move') Type = pool.get('account.account.type') party = Party(name='Party') party.save() company = create_company() with set_company(company): fiscalyear = get_fiscalyear(company) fiscalyear.save() FiscalYear.create_period([fiscalyear]) period = fiscalyear.periods[0] create_chart(company) journal_revenue, = Journal.search([ ('code', '=', 'REV'), ]) revenue, = Account.search([ ('type.revenue', '=', True), ('closed', '=', False), ], limit=1) receivable, = Account.search([ ('type.receivable', '=', True), ('closed', '=', False), ], limit=1) Move.create([{ 'period': period.id, 'journal': journal_revenue.id, 'date': period.start_date, 'lines': [ ('create', [{ 'account': revenue.id, 'credit': Decimal(100), }, { 'account': receivable.id, 'debit': Decimal(100), 'party': party.id, }]), ], }]) with Transaction().set_context(fiscalyear=fiscalyear.id): revenue_type = Type(revenue.type) receivable_type = Type(receivable.type) # Test type amount self.assertEqual(revenue_type.amount, Decimal(100)) self.assertEqual(receivable_type.amount, Decimal(100)) # Set a debit type on receivable with Transaction().set_context(fiscalyear=fiscalyear.id): debit_receivable_type, = Type.copy([receivable_type]) receivable.debit_type = debit_receivable_type receivable.credit_type = None receivable.save() self.assertEqual(receivable_type.amount, Decimal(0)) self.assertEqual(debit_receivable_type.amount, Decimal(100)) # Set a debit type on revenue with Transaction().set_context(fiscalyear=fiscalyear.id): debit_revenue_type, = Type.copy([revenue_type]) revenue.debit_type = debit_revenue_type revenue.credit_type = None revenue.save() self.assertEqual(revenue_type.amount, Decimal(100)) self.assertEqual(debit_revenue_type.amount, Decimal(0)) # Set a credit type on revenue with Transaction().set_context(fiscalyear=fiscalyear.id): credit_revenue_type, = Type.copy([revenue_type]) revenue.credit_type = credit_revenue_type revenue.debit_type = None revenue.save() self.assertEqual(revenue_type.amount, Decimal(0)) self.assertEqual(credit_revenue_type.amount, Decimal(100)) # Set a credit type on receivable with Transaction().set_context(fiscalyear=fiscalyear.id): credit_receivable_type, = Type.copy([receivable_type]) receivable.credit_type = credit_receivable_type receivable.debit_type = None receivable.save() self.assertEqual(receivable_type.amount, Decimal(100)) self.assertEqual(credit_receivable_type.amount, Decimal(0)) @with_transaction() def test_move_post(self): "Test posting move" pool = Pool() Party = pool.get('party.party') FiscalYear = pool.get('account.fiscalyear') Journal = pool.get('account.journal') Account = pool.get('account.account') Move = pool.get('account.move') Line = pool.get('account.move.line') Period = pool.get('account.period') party = Party(name='Party') party.save() company = create_company() with set_company(company): fiscalyear = get_fiscalyear(company) fiscalyear.save() FiscalYear.create_period([fiscalyear]) period = fiscalyear.periods[0] create_chart(company) journal_revenue, = Journal.search([ ('code', '=', 'REV'), ]) revenue, = Account.search([ ('type.revenue', '=', True), ('closed', '=', False), ], limit=1) receivable, = Account.search([ ('type.receivable', '=', True), ('closed', '=', False), ], limit=1) move = Move() move.period = period move.journal = journal_revenue move.date = period.start_date move.lines = [ Line(account=revenue, credit=Decimal(100)), Line(account=receivable, debit=Decimal(100), party=party), ] move.save() Move.post([move]) move_id = move.id self.assertEqual(move.state, 'posted') # Move lines with debit = credit = 0 are automatically reconciled receivable_no_party, = Account.copy([receivable], default={ 'party_required': False, }) move = Move() move.period = period move.journal = journal_revenue move.date = period.start_date move.lines = [ Line(account=receivable_no_party, credit=Decimal(0)), Line(account=receivable, debit=Decimal(0), party=party), ] move.save() Move.post([move]) lines = Line.browse([l.id for l in move.lines if l.account.reconcile]) self.assertTrue(all(bool(l.reconciliation) for l in lines)) # Can not post an empty move with self.assertRaises(UserError): move = Move() move.period = period move.journal = journal_revenue move.date = period.start_date move.save() Move.post([move]) Move.delete([move]) # Can not modify posted move with self.assertRaises(UserError): move = Move(move_id) move.date = period.end_date move.save() # Can not go back to draft with self.assertRaises(UserError): move = Move(move_id) move.state = 'draft' move.save() Period.close([period]) # Can not create move with lines on closed period with self.assertRaises(UserError): move = Move() move.period = period move.journal = journal_revenue move.date = period.start_date move.lines = [ Line(account=revenue, credit=Decimal(100)), Line(account=receivable, debit=Decimal(100), party=party), ] move.save() @with_transaction() def test_find_best_reconciliation(self): "Test find best reconciliation" pool = Pool() Account = pool.get('account.account') FiscalYear = pool.get('account.fiscalyear') Journal = pool.get('account.journal') Line = pool.get('account.move.line') Move = pool.get('account.move') Party = pool.get('party.party') party = Party(name='Party') party.save() company = create_company() with set_company(company): create_chart(company) fiscalyear = get_fiscalyear(company) fiscalyear.save() FiscalYear.create_period([fiscalyear]) period = fiscalyear.periods[0] journal_revenue, = Journal.search([ ('code', '=', 'REV'), ]) receivable, = Account.search([ ('type.receivable', '=', True), ('closed', '=', False), ], limit=1) currency = company.currency move = Move() move.period = period move.journal = journal_revenue move.date = period.start_date move.save() line1 = Line( move=move, party=party, debit=Decimal(10), credit=0, account=receivable, maturity_date=datetime.date(2024, 1, 1)) line2 = Line( move=move, party=party, debit=0, credit=Decimal(10), account=receivable, maturity_date=datetime.date(2024, 1, 2)) line3 = Line( move=move, party=party, debit=0, credit=Decimal(5), account=receivable, maturity_date=datetime.date(2024, 1, 3)) Line.save([line1, line2, line3]) for (lines, amount, best, remaining) in [ ([line1], 0, [line1], Decimal('10')), ([line1], Decimal(10), [line1], Decimal(0)), ([line1], Decimal(3), [line1], Decimal(7)), ([line1, line2], 0, [line1, line2], Decimal(0)), ([line1, line2], Decimal(10), [line1], Decimal(0)), ([line1, line2], Decimal(-10), [line2], Decimal(0)), ([line1, line2], Decimal(20), [line1], Decimal(-10)), ([line1, line2], Decimal(-3), [line1, line2], Decimal(3)), ([line1, line2], Decimal(-9), [line2], Decimal(-1)), ([line1, line3], 0, [line1, line3], Decimal(5)), ([line1, line2, line3], 0, [line1, line2], Decimal(0)), ]: with self.subTest(lines=lines, amount=amount): self.assertEqual( Line.find_best_reconciliation(lines, currency, amount), (best, remaining)) @with_transaction() def test_find_best_reconciliation_currency(self): "Test find best reconciliation with different currencies" pool = Pool() Account = pool.get('account.account') FiscalYear = pool.get('account.fiscalyear') Journal = pool.get('account.journal') Line = pool.get('account.move.line') Move = pool.get('account.move') Party = pool.get('party.party') currency1 = create_currency('USD') currency2 = create_currency('EUR') party = Party(name='Party') party.save() company = create_company(currency=currency1) with set_company(company): create_chart(company) fiscalyear = get_fiscalyear(company) fiscalyear.save() FiscalYear.create_period([fiscalyear]) period = fiscalyear.periods[0] journal_revenue, = Journal.search([ ('code', '=', 'REV'), ]) receivable, = Account.search([ ('type.receivable', '=', True), ('closed', '=', False), ], limit=1) move = Move() move.period = period move.journal = journal_revenue move.date = period.start_date move.save() line1 = Line( move=move, party=party, debit=Decimal(10), credit=0, second_currency=currency2, amount_second_currency=Decimal(10), account=receivable, maturity_date=datetime.date(2024, 1, 1)) line2 = Line( move=move, party=party, debit=0, credit=Decimal(20), second_currency=currency2, amount_second_currency=Decimal('-10'), account=receivable, maturity_date=datetime.date(2024, 1, 1)) line3 = Line( move=move, party=party, debit=0, credit=Decimal(5), second_currency=None, account=receivable, maturity_date=datetime.date(2024, 1, 3)) Line.save([line1, line2, line3]) self.assertEqual( Line.find_best_reconciliation( [line1, line2, line3], currency2), ([line1, line2], Decimal(0))) @with_transaction() def test_tax_compute(self): 'Test tax compute/reverse_compute' pool = Pool() Account = pool.get('account.account') Tax = pool.get('account.tax') today = datetime.date.today() company = create_company() with set_company(company): create_chart(company) tax_account, = Account.search([ ('code', '=', '6.3.6'), ]) tax = Tax() tax.name = tax.description = 'Test' tax.type = 'none' tax.invoice_account = tax_account tax.credit_note_account = tax_account child1 = Tax() child1.name = child1.description = 'Child 1' child1.type = 'percentage' child1.rate = Decimal('0.2') child1.invoice_account = tax_account child1.credit_note_account = tax_account child1.save() child2 = Tax() child2.name = child2.description = 'Child 1' child2.type = 'fixed' child2.amount = Decimal('10') child2.invoice_account = tax_account child2.credit_note_account = tax_account child2.save() tax.childs = [child1, child2] tax.save() self.assertEqual(Tax.compute([tax], Decimal('100'), 2, today), [{ 'base': Decimal('200'), 'amount': Decimal('40.0'), 'tax': child1, }, { 'base': Decimal('200'), 'amount': Decimal('20'), 'tax': child2, }]) self.assertEqual( Tax.reverse_compute(Decimal('130'), [tax], today), Decimal('100')) child1.end_date = today + relativedelta(days=5) child1.save() self.assertEqual(Tax.compute([tax], Decimal('100'), 2, today), [{ 'base': Decimal('200'), 'amount': Decimal('40.0'), 'tax': child1, }, { 'base': Decimal('200'), 'amount': Decimal('20'), 'tax': child2, }]) self.assertEqual( Tax.reverse_compute(Decimal('130'), [tax], today), Decimal('100')) child1.start_date = today + relativedelta(days=1) child1.save() self.assertEqual(Tax.compute([tax], Decimal('100'), 2, today), [{ 'base': Decimal('200'), 'amount': Decimal('20'), 'tax': child2, }]) self.assertEqual( Tax.reverse_compute(Decimal('110'), [tax], today), Decimal('100')) self.assertEqual(Tax.compute([tax], Decimal('100'), 2, today + relativedelta(days=1)), [{ 'base': Decimal('200'), 'amount': Decimal('40.0'), 'tax': child1, }, { 'base': Decimal('200'), 'amount': Decimal('20'), 'tax': child2, }]) self.assertEqual( Tax.reverse_compute( Decimal('130'), [tax], today + relativedelta(days=1)), Decimal('100')) self.assertEqual(Tax.compute([tax], Decimal('100'), 2, today + relativedelta(days=5)), [{ 'base': Decimal('200'), 'amount': Decimal('40.0'), 'tax': child1, }, { 'base': Decimal('200'), 'amount': Decimal('20'), 'tax': child2, }]) self.assertEqual( Tax.reverse_compute(Decimal('130'), [tax], today + relativedelta(days=5)), Decimal('100')) self.assertEqual(Tax.compute([tax], Decimal('100'), 2, today + relativedelta(days=6)), [{ 'base': Decimal('200'), 'amount': Decimal('20'), 'tax': child2, }]) self.assertEqual( Tax.reverse_compute(Decimal('110'), [tax], today + relativedelta(days=6)), Decimal('100')) child1.end_date = None child1.save() self.assertEqual(Tax.compute([tax], Decimal('100'), 2, today + relativedelta(days=6)), [{ 'base': Decimal('200'), 'amount': Decimal('40.0'), 'tax': child1, }, { 'base': Decimal('200'), 'amount': Decimal('20'), 'tax': child2, }]) self.assertEqual( Tax.reverse_compute(Decimal('130'), [tax], today + relativedelta(days=6)), Decimal('100')) ecotax1 = Tax() ecotax1.name = ecotax1.description = 'EcoTax 1' ecotax1.type = 'fixed' ecotax1.amount = Decimal(5) ecotax1.invoice_account = tax_account ecotax1.credit_note_account = tax_account ecotax1.sequence = 10 ecotax1.save() vat0 = Tax() vat0.name = vat0.description = 'VAT0' vat0.type = 'percentage' vat0.rate = Decimal('0.1') vat0.invoice_account = tax_account vat0.credit_note_account = tax_account vat0.sequence = 5 vat0.save() vat1 = Tax() vat1.name = vat1.description = 'VAT1' vat1.type = 'percentage' vat1.rate = Decimal('0.2') vat1.invoice_account = tax_account vat1.credit_note_account = tax_account vat1.sequence = 20 vat1.save() self.assertEqual( Tax.compute([vat0, ecotax1, vat1], Decimal(100), 1, today), [{ 'base': Decimal(100), 'amount': Decimal(10), 'tax': vat0, }, { 'base': Decimal(100), 'amount': Decimal(5), 'tax': ecotax1, }, { 'base': Decimal(100), 'amount': Decimal(20), 'tax': vat1, }]) self.assertEqual( Tax.reverse_compute( Decimal(135), [vat0, ecotax1, vat1], today), Decimal(100)) @with_transaction() def test_tax_compute_with_update_unit_price(self): 'Test tax compute with unit_price modifying tax' pool = Pool() Account = pool.get('account.account') Date = pool.get('ir.date') Tax = pool.get('account.tax') today = Date.today() company = create_company() with set_company(company): create_chart(company) tax_account, = Account.search([ ('code', '=', '6.3.6'), ]) ecotax1 = Tax() ecotax1.name = ecotax1.description = 'EcoTax 1' ecotax1.type = 'fixed' ecotax1.amount = Decimal(5) ecotax1.invoice_account = tax_account ecotax1.credit_note_account = tax_account ecotax1.update_unit_price = True ecotax1.sequence = 10 ecotax1.save() vat1 = Tax() vat1.name = vat1.description = 'VAT1' vat1.type = 'percentage' vat1.rate = Decimal('0.2') vat1.invoice_account = tax_account vat1.credit_note_account = tax_account vat1.sequence = 20 vat1.save() self.assertEqual( Tax.compute([ecotax1, vat1], Decimal(100), 5, today), [{ 'base': Decimal(500), 'amount': Decimal(25), 'tax': ecotax1, }, { 'base': Decimal(525), 'amount': Decimal(105), 'tax': vat1, }]) self.assertEqual( Tax.reverse_compute(Decimal(126), [ecotax1, vat1], today), Decimal(100)) ecotax2 = Tax() ecotax2.name = ecotax2.description = 'EcoTax 2' ecotax2.type = 'percentage' ecotax2.rate = Decimal('0.5') ecotax2.invoice_account = tax_account ecotax2.credit_note_account = tax_account ecotax2.update_unit_price = True ecotax2.sequence = 10 ecotax2.save() self.assertEqual( Tax.compute([ecotax1, ecotax2, vat1], Decimal(100), 1, today), [{ 'base': Decimal(100), 'amount': Decimal(5), 'tax': ecotax1, }, { 'base': Decimal(100), 'amount': Decimal(50), 'tax': ecotax2, }, { 'base': Decimal(155), 'amount': Decimal(31), 'tax': vat1, }]) self.assertEqual( Tax.reverse_compute( Decimal(186), [ecotax1, ecotax2, vat1], today), Decimal(100)) vat0 = Tax() vat0.name = vat0.description = 'VAT0' vat0.type = 'percentage' vat0.rate = Decimal('0.1') vat0.invoice_account = tax_account vat0.credit_note_account = tax_account vat0.sequence = 5 vat0.save() self.assertEqual( Tax.compute([vat0, ecotax1, vat1], Decimal(100), 1, today), [{ 'base': Decimal(100), 'amount': Decimal(10), 'tax': vat0, }, { 'base': Decimal(100), 'amount': Decimal(5), 'tax': ecotax1, }, { 'base': Decimal(105), 'amount': Decimal(21), 'tax': vat1, }]) self.assertEqual( Tax.reverse_compute( Decimal(136), [vat0, ecotax1, vat1], today), Decimal(100)) self.assertEqual( Tax.compute([vat0, ecotax1, ecotax2, vat1], Decimal(100), 1, today), [{ 'base': Decimal(100), 'amount': Decimal(10), 'tax': vat0, }, { 'base': Decimal(100), 'amount': Decimal(5), 'tax': ecotax1, }, { 'base': Decimal(100), 'amount': Decimal(50), 'tax': ecotax2, }, { 'base': Decimal(155), 'amount': Decimal(31), 'tax': vat1, }]) self.assertEqual( Tax.reverse_compute( Decimal(196), [vat0, ecotax1, ecotax2, vat1], today), Decimal(100)) vat2 = Tax() vat2.name = vat2.description = 'VAT2' vat2.type = 'percentage' vat2.rate = Decimal('0.3') vat2.invoice_account = tax_account vat2.credit_note_account = tax_account vat2.sequence = 30 vat2.save() self.assertEqual( Tax.compute([vat0, ecotax1, vat1, vat2], Decimal(100), 1, today), [{ 'base': Decimal(100), 'amount': Decimal(10), 'tax': vat0, }, { 'base': Decimal(100), 'amount': Decimal(5), 'tax': ecotax1, }, { 'base': Decimal(105), 'amount': Decimal(21), 'tax': vat1, }, { 'base': Decimal(105), 'amount': Decimal('31.5'), 'tax': vat2, }]) self.assertEqual( Tax.reverse_compute( Decimal('167.5'), [vat0, ecotax1, vat1, vat2], today), Decimal(100)) ecotax3 = Tax() ecotax3.name = ecotax3.description = 'ECOTAX3' ecotax3.type = 'percentage' ecotax3.rate = Decimal('0.4') ecotax3.invoice_account = tax_account ecotax3.credit_note_account = tax_account ecotax3.update_unit_price = True ecotax3.sequence = 25 ecotax3.save() self.assertEqual( Tax.compute([vat0, ecotax1, vat1, ecotax3, vat2], Decimal(100), 1, today), [{ 'base': Decimal(100), 'amount': Decimal(10), 'tax': vat0, }, { 'base': Decimal(100), 'amount': Decimal(5), 'tax': ecotax1, }, { 'base': Decimal(105), 'amount': Decimal(21), 'tax': vat1, }, { 'base': Decimal(105), 'amount': Decimal('42'), 'tax': ecotax3, }, { 'base': Decimal(147), 'amount': Decimal('44.1'), 'tax': vat2 }]) self.assertEqual( Tax.reverse_compute( Decimal('222.1'), [vat0, ecotax1, vat1, ecotax3, vat2], today), Decimal(100)) class Taxable(TaxableMixin): __slots__ = ('currency', 'taxable_lines', 'company') def __init__(self, currency=None, taxable_lines=None, company=None): super().__init__() self.currency = currency self.taxable_lines = taxable_lines self.company = company @with_transaction() def test_taxable_mixin_line(self): "Test TaxableMixin with rounding on line" pool = Pool() Tax = pool.get('account.tax') Configuration = pool.get('account.configuration') currency = create_currency('cur') company = create_company() with set_company(company): create_chart(company, tax=True) tax, = Tax.search([]) config = Configuration(1) config.tax_rounding = 'line' config.save() taxable = self.Taxable( currency=currency, taxable_lines=[ ([tax], Decimal('1.001'), 1, None), ] * 100, company=company) taxes = taxable._get_taxes().values() tax, = taxes self.assertEqual(tax.base, Decimal('100.00')) self.assertEqual(tax.amount, Decimal('20.00')) @with_transaction() def test_taxable_mixin_line2(self): "Test TaxableMixin with rounding on line" pool = Pool() Tax = pool.get('account.tax') Configuration = pool.get('account.configuration') currency = create_currency('cur') company = create_company() with set_company(company): create_chart(company, tax=True) tax, = Tax.search([]) tax.rate = 1 tax.save() config = Configuration(1) config.tax_rounding = 'line' config.save() taxable = self.Taxable( currency=currency, taxable_lines=[ ([tax], Decimal('1.070'), 1, None), ([tax], Decimal('1.005'), 1, None), ], company=company) taxes = taxable._get_taxes().values() tax, = taxes self.assertEqual(tax.base, Decimal('2.07')) self.assertEqual(tax.amount, Decimal('2.07')) @with_transaction() def test_taxable_mixin_document(self): "Test TaxableMixin with rounding on document" pool = Pool() Tax = pool.get('account.tax') Configuration = pool.get('account.configuration') currency = create_currency('cur') company = create_company() with set_company(company): create_chart(company, tax=True) tax, = Tax.search([]) config = Configuration(1) config.tax_rounding = 'document' config.save() taxable = self.Taxable( currency=currency, taxable_lines=[ ([tax], Decimal('1.001'), 1, None), ] * 100, company=company) taxes = taxable._get_taxes().values() tax, = taxes self.assertEqual(tax.base, Decimal('100.00')) self.assertEqual(tax.amount, Decimal('20.02')) @with_transaction() def test_taxable_mixin_many_taxable_lines(self): "Test TaxableMixin with many taxable lines" pool = Pool() Tax = pool.get('account.tax') currency = create_currency('cur') company = create_company() with set_company(company): create_chart(company, tax=True) tax2, = Tax.search([]) tax1, = Tax.copy([tax2], default={'rate': Decimal('0.1')}) taxable = self.Taxable( currency=currency, taxable_lines=[ ([tax1], Decimal('10'), 1, None), ([tax1, tax2], Decimal('20'), 1, None), ], company=company) taxes = taxable._get_taxes().values() self.assertEqual(len(taxes), 2) tax_line1, = [t for t in taxes if t.tax == tax1] tax_line2, = [t for t in taxes if t.tax == tax2] self.assertEqual(tax_line1.base, Decimal('30.00')) self.assertEqual(tax_line1.amount, Decimal('3.00')) self.assertEqual(tax_line2.base, Decimal('20.00')) self.assertEqual(tax_line2.amount, Decimal('4.00')) @with_transaction() def test_taxable_mixin_tax_residual_rounding(self): "Test TaxableMixin for rounding with residual amount" pool = Pool() Account = pool.get('account.account') Tax = pool.get('account.tax') Configuration = pool.get('account.configuration') currency = create_currency('cur') company = create_company() with set_company(company): create_chart(company) config = Configuration(1) config.tax_rounding = 'line' config.save() tax_account, = Account.search([ ('code', '=', '6.3.6'), ]) tax1 = Tax() tax1.name = tax1.description = "Tax 1" tax1.type = 'percentage' tax1.rate = Decimal('.1') tax1.invoice_account = tax_account tax1.credit_note_account = tax_account tax1.save() tax2 = Tax() tax2.name = tax2.description = "Tax 2" tax2.type = 'percentage' tax2.rate = Decimal('.1') tax2.invoice_account = tax_account tax2.credit_note_account = tax_account tax2.save() taxable = self.Taxable( currency=currency, taxable_lines=[ ([tax1, tax2], Decimal('1.0417'), 1, None), ], company=company) taxline_1, taxline_2 = taxable._get_taxes().values() self.assertEqual(taxline_1.base, Decimal('1.04')) self.assertEqual(taxline_1.amount, Decimal('.11')) self.assertEqual(taxline_2.base, Decimal('1.04')) self.assertEqual(taxline_2.amount, Decimal('.10')) @with_transaction() def test_taxable_mixin_tax_residual_rounding_with_0(self): "Test TaxableMixin for rounding with residual amount and tax 0" pool = Pool() Account = pool.get('account.account') Tax = pool.get('account.tax') Configuration = pool.get('account.configuration') currency = create_currency('cur') company = create_company() with set_company(company): create_chart(company) config = Configuration(1) config.tax_rounding = 'line' config.save() tax_account, = Account.search([ ('code', '=', '6.3.6'), ]) tax0 = Tax() tax0.name = tax0.description = "Tax 0" tax0.type = 'percentage' tax0.rate = Decimal('0') tax0.invoice_account = tax_account tax0.credit_note_account = tax_account tax0.save() tax1 = Tax() tax1.name = tax1.description = "Tax 1" tax1.type = 'percentage' tax1.rate = Decimal('.1') tax1.invoice_account = tax_account tax1.credit_note_account = tax_account tax1.save() tax2 = Tax() tax2.name = tax2.description = "Tax 2" tax2.type = 'percentage' tax2.rate = Decimal('.1') tax2.invoice_account = tax_account tax2.credit_note_account = tax_account tax2.save() taxable = self.Taxable( currency=currency, taxable_lines=[ ([tax0, tax1, tax2], Decimal('1.0417'), 1, None), ], company=company) taxline_0, taxline_1, taxline_2 = taxable._get_taxes().values() self.assertEqual(taxline_0.base, Decimal('1.04')) self.assertEqual(taxline_0.amount, Decimal('0')) self.assertEqual(taxline_1.base, Decimal('1.04')) self.assertEqual(taxline_1.amount, Decimal('.11')) self.assertEqual(taxline_2.base, Decimal('1.04')) self.assertEqual(taxline_2.amount, Decimal('.10')) @with_transaction() def test_taxable_mixin_tax_residual_rounding_negative_residual(self): "Test TaxableMixin for rounding with negative residual amount" pool = Pool() Account = pool.get('account.account') Tax = pool.get('account.tax') Configuration = pool.get('account.configuration') currency = create_currency('cur') company = create_company() with set_company(company): create_chart(company) config = Configuration(1) config.tax_rounding = 'line' config.save() tax_account, = Account.search([ ('code', '=', '6.3.6'), ]) tax1 = Tax() tax1.name = tax1.description = "Tax 1" tax1.type = 'percentage' tax1.rate = Decimal('.1') tax1.invoice_account = tax_account tax1.credit_note_account = tax_account tax1.save() tax2 = Tax() tax2.name = tax2.description = "Tax 2" tax2.type = 'percentage' tax2.rate = Decimal('.1') tax2.invoice_account = tax_account tax2.credit_note_account = tax_account tax2.save() # -2.95 is the unit price of -3.54 with 20% tax included taxable = self.Taxable( currency=currency, taxable_lines=[ ([tax1], Decimal('30.00'), 1, None), ([tax1, tax2], Decimal('-2.95'), 1, None), ], company=company) taxline_1, taxline_2, taxline_3 = taxable._get_taxes().values() self.assertEqual(taxline_1.base, Decimal('30.00')) self.assertEqual(taxline_1.amount, Decimal('3.00')) self.assertEqual(taxline_2.base, Decimal('-2.95')) self.assertEqual(taxline_2.amount, Decimal('-0.29')) self.assertEqual(taxline_3.base, Decimal('-2.95')) self.assertEqual(taxline_3.amount, Decimal('-0.30')) @with_transaction() def test_taxable_mixin_tax_residual_rounding_per_same_taxes(self): """Test TaxableMixin for rounding with residual amount only for same taxes""" pool = Pool() Account = pool.get('account.account') Tax = pool.get('account.tax') Configuration = pool.get('account.configuration') currency = create_currency('cur') company = create_company() with set_company(company): create_chart(company) config = Configuration(1) config.tax_rounding = 'document' config.save() tax_account, = Account.search([ ('code', '=', '6.3.6'), ]) tax = Tax() tax.name = tax.description = "Tax" tax.type = 'percentage' tax.rate = Decimal('.06') tax.invoice_account = tax_account tax.credit_note_account = tax_account tax.save() taxable = self.Taxable( currency=currency, taxable_lines=[ ([tax], Decimal('11.2736'), 1.04, None), ([tax], Decimal('0.4529'), -1, None), ], company=company) taxline_1, taxline_2 = taxable._get_taxes().values() self.assertEqual(taxline_1.base, Decimal('11.72')) self.assertEqual(taxline_1.amount, Decimal('0.7')) self.assertEqual(taxline_2.base, Decimal('-0.45')) self.assertEqual(taxline_2.amount, Decimal('-0.03')) @with_transaction() def test_tax_compute_with_children_update_unit_price(self): "Test tax compute with children taxes modifying unit_price" pool = Pool() Account = pool.get('account.account') Date = pool.get('ir.date') Tax = pool.get('account.tax') today = Date.today() company = create_company() with set_company(company): create_chart(company) tax_account, = Account.search([ ('code', '=', '6.3.6'), ]) tax1 = Tax() tax1.name = tax1.description = "Tax 1" tax1.type = 'none' tax1.update_unit_price = True tax1.sequence = 1 tax1.save() child1 = Tax() child1.name = child1.description = "Child 1" child1.type = 'percentage' child1.rate = Decimal('0.1') child1.invoice_account = tax_account child1.credit_note_account = tax_account child1.parent = tax1 child1.save() tax2 = Tax() tax2.name = tax2.description = "Tax 2" tax2.type = 'fixed' tax2.amount = Decimal('10') tax2.invoice_account = tax_account tax2.credit_note_account = tax_account tax2.sequence = 2 tax2.save() self.assertEqual( Tax.compute([tax1, tax2], Decimal(100), 2, today), [{ 'base': Decimal(200), 'amount': Decimal(20), 'tax': child1, }, { 'base': Decimal('220'), 'amount': Decimal('20'), 'tax': tax2, }]) self.assertEqual( Tax.reverse_compute(Decimal('120'), [tax1, tax2], today), Decimal('100')) @with_transaction() def test_receivable_payable(self): 'Test party receivable payable' pool = Pool() Company = pool.get('company.company') Party = pool.get('party.party') FiscalYear = pool.get('account.fiscalyear') Journal = pool.get('account.journal') Account = pool.get('account.account') Move = pool.get('account.move') company = create_company() with set_company(company): create_chart(company) fiscalyear = get_fiscalyear(company) fiscalyear.save() FiscalYear.create_period([fiscalyear]) period = fiscalyear.periods[0] journal_revenue, = Journal.search([ ('code', '=', 'REV'), ]) journal_expense, = Journal.search([ ('code', '=', 'EXP'), ]) revenue, = Account.search([ ('type.revenue', '=', True), ('closed', '=', False), ], limit=1) receivable, = Account.search([ ('type.receivable', '=', True), ('closed', '=', False), ], limit=1) expense, = Account.search([ ('type.expense', '=', True), ('closed', '=', False), ], limit=1) payable, = Account.search([ ('type.payable', '=', True), ('closed', '=', False), ], limit=1) party, = Party.create([{ 'name': 'Receivable/Payable party', }]) tomorrow = datetime.date.today() + datetime.timedelta(days=1) def get_move(journal, amount, credit_account, debit_account, party, maturity_date=None): return { 'period': period.id, 'journal': journal.id, 'date': period.start_date, 'lines': [ ('create', [{ 'account': credit_account.id, 'credit': amount, }, { 'account': debit_account.id, 'debit': amount, 'party': party.id, 'maturity_date': maturity_date, }]), ], } vlist = [ get_move(journal_revenue, Decimal(100), revenue, receivable, party), get_move(journal_expense, Decimal(30), expense, payable, party), get_move(journal_revenue, Decimal(200), revenue, receivable, party, tomorrow), get_move(journal_revenue, Decimal(60), expense, payable, party, tomorrow), ] moves = Move.create(vlist) Move.post(moves) def check_fields(): party_test = Party(party.id) company_test = Company(company.id) for record, Model in [ (party_test, Party), (company_test, Company), ]: for field, value in [ ('receivable', Decimal('300')), ('receivable_today', Decimal('100')), ('payable', Decimal('90')), ('payable_today', Decimal('30')), ]: with self.subTest( record=record, field=field, value=value): self.assertEqual( getattr(record, field), value) self.assertEqual( Model.search([(field, '=', value)]), [record]) self.assertEqual( Model.search([(field, 'in', [value])]), [record]) self.assertEqual( Model.search([(field, '!=', value)]), []) self.assertEqual( Model.search([(field, 'not in', [value])]), []) check_fields() close_fiscalyear(fiscalyear) check_fields() @with_transaction() def test_sort_taxes(self): "Test sort_taxes" pool = Pool() Tax = pool.get('account.tax') tax1 = Tax(sequence=None, id=-3) tax2 = Tax(sequence=None, id=-2) tax3 = Tax(sequence=1, id=-1) self.assertSequenceEqual( Tax.sort_taxes([tax3, tax2, tax1]), [tax1, tax2, tax3]) @with_transaction() def test_configuration_accounts_on_party(self): 'Test configuration accounts are used as fallback on party' pool = Pool() Party = pool.get('party.party') Account = pool.get('account.account') party = Party(name='Party') party.save() self.assertIsNone(party.account_payable) self.assertIsNone(party.account_receivable) company = create_company() with set_company(company): create_chart(company) receivable, = Account.search([ ('type.receivable', '=', True), ('closed', '=', False), ], limit=1) payable, = Account.search([ ('type.payable', '=', True), ('closed', '=', False), ], limit=1) party = Party(party.id) self.assertEqual(party.account_payable_used, payable) self.assertEqual(party.account_receivable_used, receivable) @with_transaction() def test_tax_rule(self): "Test tax rule" pool = Pool() TaxRule = pool.get('account.tax.rule') Tax = pool.get('account.tax') company = create_company() with set_company(company): create_chart(company, tax=True) tax, = Tax.search([]) target_tax, = Tax.copy([tax]) tax_rule, = TaxRule.create([{ 'name': 'Test', 'kind': 'both', 'lines': [('create', [{ 'origin_tax': tax.id, 'tax': target_tax.id, }])], }]) self.assertListEqual(tax_rule.apply(tax, {}), [target_tax.id]) @with_transaction() def test_tax_rule_start_date(self): "Test tax rule start date" pool = Pool() TaxRule = pool.get('account.tax.rule') Tax = pool.get('account.tax') Date = pool.get('ir.date') today = Date.today() yesterday = today - datetime.timedelta(days=1) tomorrow = today + datetime.timedelta(days=1) company = create_company() with set_company(company): create_chart(company, tax=True) tax, = Tax.search([]) target_tax, = Tax.copy([tax]) tax_rule, = TaxRule.create([{ 'name': "Test", 'kind': 'both', 'lines': [('create', [{ 'start_date': today, 'tax': target_tax.id, }])], }]) self.assertListEqual(tax_rule.apply(tax, {}), [target_tax.id]) self.assertListEqual( tax_rule.apply(tax, {'date': yesterday}), [tax.id]) self.assertListEqual( tax_rule.apply(tax, {'date': tomorrow}), [target_tax.id]) @with_transaction() def test_tax_rule_end_date(self): "Test tax rule end date" pool = Pool() TaxRule = pool.get('account.tax.rule') Tax = pool.get('account.tax') Date = pool.get('ir.date') today = Date.today() yesterday = today - datetime.timedelta(days=1) tomorrow = today + datetime.timedelta(days=1) company = create_company() with set_company(company): create_chart(company, tax=True) tax, = Tax.search([]) target_tax, = Tax.copy([tax]) tax_rule, = TaxRule.create([{ 'name': "Test", 'kind': 'both', 'lines': [('create', [{ 'end_date': today, 'tax': target_tax.id, }])], }]) self.assertListEqual(tax_rule.apply(tax, {}), [target_tax.id]) self.assertListEqual( tax_rule.apply(tax, {'date': yesterday}), [target_tax.id]) self.assertListEqual( tax_rule.apply(tax, {'date': tomorrow}), [tax.id]) @with_transaction() def test_tax_rule_keep_origin(self): "Test tax rule keeps origin" pool = Pool() TaxRule = pool.get('account.tax.rule') Tax = pool.get('account.tax') company = create_company() with set_company(company): create_chart(company, tax=True) tax, = Tax.search([]) target_tax, = Tax.copy([tax]) tax_rule, = TaxRule.create([{ 'name': 'Test', 'kind': 'both', 'lines': [('create', [{ 'origin_tax': tax.id, 'tax': target_tax.id, 'keep_origin': True, }])], }]) self.assertListEqual( tax_rule.apply(tax, {}), [target_tax.id, tax.id]) @with_transaction() def test_update_chart(self): 'Test all template models are updated when updating chart' pool = Pool() TypeTemplate = pool.get('account.account.type.template') AccountTemplate = pool.get('account.account.template') TaxTemplate = pool.get('account.tax.template') TaxCodeTemplate = pool.get('account.tax.code.template') ModelData = pool.get('ir.model.data') UpdateChart = pool.get('account.update_chart', type='wizard') Type = pool.get('account.account.type') Account = pool.get('account.account') Tax = pool.get('account.tax') TaxCode = pool.get('account.tax.code') def check(): for type_ in Type.search([]): self.assertEqual(type_.name, type_.template.name) self.assertEqual( type_.statement, type_.template.statement) if type_.template.parent: self.assertEqual( type_.parent.name, type_.template.parent.name) else: self.assertEqual(type_.parent, None) for account in Account.search([]): self.assertEqual(account.name, account.template.name) self.assertEqual(account.code, account.template.code) self.assertEqual( account.type.template if account.type else None, account.template.type) self.assertEqual(account.reconcile, account.template.reconcile) self.assertEqual( account.start_date, account.template.start_date) self.assertEqual(account.end_date, account.template.end_date) self.assertEqual( account.party_required, account.template.party_required) self.assertEqual( account.general_ledger_balance, account.template.general_ledger_balance) self.assertEqual( set(t.template for t in account.taxes), set(t for t in account.template.taxes)) if account.template.parent: self.assertEqual( account.parent.name, account.template.parent.name) else: self.assertEqual(account.parent, None) if account.template.replaced_by: self.assertEqual( account.replaced_by.name, account.template.replaced_by.name) else: self.assertEqual(account.replaced_by, None) for tax_code in TaxCode.search([]): self.assertEqual(tax_code.name, tax_code.template.name) self.assertEqual(tax_code.code, tax_code.template.code) for line in tax_code.lines: self.assertEqual(line.code.template, line.template.code) self.assertEqual(line.operator, line.template.operator) self.assertEqual(line.type, line.template.type) self.assertEqual(line.tax.template, line.template.tax) for tax in Tax.search([]): self.assertEqual(tax.name, tax.template.name) self.assertEqual(tax.description, tax.template.description) self.assertEqual(tax.type, tax.template.type) self.assertEqual(tax.rate, tax.template.rate) self.assertEqual(tax.amount, tax.template.amount) self.assertEqual( tax.update_unit_price, tax.template.update_unit_price) self.assertEqual( tax.start_date, tax.template.start_date) self.assertEqual(tax.end_date, tax.template.end_date) self.assertEqual( tax.invoice_account.template, tax.template.invoice_account) self.assertEqual( tax.credit_note_account.template, tax.template.credit_note_account) company = create_company() with set_company(company): create_chart(company, True) with inactive_records(): self.assertEqual(Type.search([], count=True), 46) self.assertEqual(Account.search([], count=True), 135) self.assertEqual(Tax.search([], count=True), 1) check() with Transaction().set_user(0): root_type = TypeTemplate(ModelData.get_id( 'account', 'account_type_template_root_en')) root_type.name = "Updated " + root_type.name root_type.save() chart = AccountTemplate(ModelData.get_id( 'account', 'account_template_root_en')) new_type = TypeTemplate() new_type.name = 'New Type' new_type.parent = root_type new_type.statement = 'balance' new_type.save() updated_tax_type, = TypeTemplate.search([ ('name', '=', "Liabilities and assets for current tax"), ]) updated_tax_type.parent = updated_tax_type.parent.parent updated_tax_type.save() new_account = AccountTemplate() new_account.name = 'New Account' new_account.parent = chart new_account.type = new_type new_account.save() updated_tax, = TaxTemplate.search([]) updated_tax.name = 'VAT' updated_tax.invoice_account = new_account updated_tax.save() # Can only change type which is compatible with previous parent updated_account = AccountTemplate(ModelData.get_id( 'account', 'account_template_1_1_1_en')) updated_account.code = 'REV' updated_account.name = 'Updated Account' updated_account.parent = new_account updated_account.type = new_type updated_account.reconcile = True updated_account.end_date = datetime.date.today() updated_account.taxes = [updated_tax] updated_account.save() inactive_account = AccountTemplate(ModelData.get_id( 'account', 'account_template_5_1_1_en')) inactive_account.end_date = datetime.date.min inactive_account.replaced_by = new_account inactive_account.save() new_tax = TaxTemplate() new_tax.name = new_tax.description = '10% VAT' new_tax.type = 'percentage' new_tax.rate = Decimal('0.1') new_tax.account = chart new_tax.invoice_account = new_account new_tax.credit_note_account = new_account new_tax.save() updated_tax_code, = TaxCodeTemplate.search([ ('name', '=', 'Tax Code'), ]) updated_tax_code.name = 'Updated Tax Code' updated_tax_code.save() updated_tax_code_line, = updated_tax_code.lines updated_tax_code_line.operator = '-' updated_tax_code_line.save() with set_company(company): account, = Account.search([('parent', '=', None)]) session_id, _, _ = UpdateChart.create() update_chart = UpdateChart(session_id) update_chart.start.account = account update_chart.transition_update() with inactive_records(): self.assertEqual(Type.search([], count=True), 47) self.assertEqual(Account.search([], count=True), 136) self.assertEqual(Tax.search([], count=True), 2) check() @with_transaction() def test_update_override(self): "Test all models are not updated when template override is True" pool = Pool() ModelData = pool.get('ir.model.data') TypeTemplate = pool.get('account.account.type.template') AccountTemplate = pool.get('account.account.template') TaxTemplate = pool.get('account.tax.template') TaxCodeTemplate = pool.get('account.tax.code.template') TaxCodeTemplateLine = pool.get('account.tax.code.line.template') UpdateChart = pool.get('account.update_chart', type='wizard') Type = pool.get('account.account.type') Account = pool.get('account.account') Tax = pool.get('account.tax') TaxCode = pool.get('account.tax.code') TaxCodeLine = pool.get('account.tax.code.line') new_name = "Updated" company = create_company() with set_company(company): create_chart(company, True) type_count = Type.search([], count=True) account_count = Account.search([], count=True) tax_count = Tax.search([], count=True) tax_code_count = TaxCode.search([], count=True) tax_code_line_count = TaxCodeLine.search([], count=True) with Transaction().set_user(0): root = AccountTemplate(ModelData.get_id( 'account', 'account_template_root_en')) template_type, = TypeTemplate.search([ ('parent', '!=', None), ('parent', 'child_of', [root.type.id]), ], limit=1) template_type.name = new_name template_type.save() type_, = Type.search([('template', '=', template_type.id)]) type_.template_override = True type_.save() template_account, = AccountTemplate.search([ ('parent', '!=', None), ('parent', 'child_of', [root.id]), ], limit=1) template_account.name = new_name template_account.save() account, = Account.search([('template', '=', template_account.id)]) account.template_override = True account.save() template_tax, = TaxTemplate.search([]) template_tax.name = new_name template_tax.save() tax, = Tax.search([('template', '=', template_tax.id)]) tax.template_override = True tax.save() template_tax_code, = TaxCodeTemplate.search([], limit=1) template_tax_code.name = new_name template_tax_code.save() tax_code, = TaxCode.search( [('template', '=', template_tax_code.id)]) tax_code.template_override = True tax_code.save() template_tax_code_line, = TaxCodeTemplateLine.search([], limit=1) tax_code_line, = TaxCodeLine.search( [('template', '=', template_tax_code_line.id)]) tax_code_line.template_override = True tax_code_line.save() with set_company(company): account, = Account.search([('parent', '=', None)]) session_id, _, _ = UpdateChart.create() update_chart = UpdateChart(session_id) update_chart.start.account = account update_chart.transition_update() self.assertEqual(Type.search([], count=True), type_count) self.assertEqual(Account.search([], count=True), account_count) self.assertEqual(Tax.search([], count=True), tax_count) self.assertEqual( TaxCode.search([], count=True), tax_code_count) self.assertEqual( TaxCodeLine.search([], count=True), tax_code_line_count) for Model in [Type, Account, Tax, TaxCode]: for record in Model.search([]): self.assertNotEqual(record.name, new_name) @with_transaction() def test_update_inactive(self): "Test update chart of accounts with inactive account" pool = Pool() Account = pool.get('account.account') UpdateChart = pool.get('account.update_chart', type='wizard') company = create_company() with set_company(company): create_chart(company, tax=True) root, = Account.search([('parent', '=', None)]) cash, = Account.search([('code', '=', '1.1.1')]) cash.template_override = True cash.end_date = datetime.date.min cash.save() self.assertFalse(cash.active) session_id, _, _ = UpdateChart.create() update_chart = UpdateChart(session_id) update_chart.start.account = root update_chart.transition_update() with inactive_records(): self.assertEqual( Account.search([('code', '=', '1.1.1')], count=True), 1) @with_transaction() def test_update_chart_new_parent(self): "Test update chart of accounts with new parent" pool = Pool() ModelData = pool.get('ir.model.data') TypeTemplate = pool.get('account.account.type.template') Type = pool.get('account.account.type') AccountTemplate = pool.get('account.account.template') Account = pool.get('account.account') UpdateChart = pool.get('account.update_chart', type='wizard') company = create_company() with set_company(company): create_chart(company, True) with Transaction().set_user(0): root_type_template = TypeTemplate(ModelData.get_id( 'account', 'account_type_template_root_en')) type_template, = TypeTemplate.search([ ('parent', '!=', None), ('parent', 'child_of', [root_type_template.id]), ], limit=1) new_type_template, = TypeTemplate.copy([type_template]) type_template.parent = new_type_template type_template.save() root_template = AccountTemplate(ModelData.get_id( 'account', 'account_template_root_en')) account_template, = AccountTemplate.search([ ('parent', '!=', None), ('parent', 'child_of', [root_template.id]), ], limit=1) new_account_template, = AccountTemplate.copy([account_template]) account_template.parent = new_account_template account_template.save() with set_company(company): account, = Account.search([('parent', '=', None)]) session_id, _, _ = UpdateChart.create() update_chart = UpdateChart(session_id) update_chart.start.account = account update_chart.transition_update() new_type, = Type.search( [('template', '=', new_type_template.id)]) type, = Type.search( [('template', '=', type_template.id)]) self.assertEqual(type.parent, new_type) new_account, = Account.search( [('template', '=', new_account_template.id)]) account, = Account.search( [('template', '=', account_template.id)]) self.assertEqual(account.parent, new_account) del ModuleTestCase