From e5b55c9a63b4b7822de8a3d8fe5cd116f874dbc0 Mon Sep 17 00:00:00 2001 From: Bernat Brunet Date: Wed, 1 Nov 2023 17:13:48 +0100 Subject: [PATCH] Backport patch of core changes, that support second currency on statement line --- account_statement_second_currency.diff | 456 +++++++++++++++++++++++++ series | 2 + 2 files changed, 458 insertions(+) create mode 100644 account_statement_second_currency.diff diff --git a/account_statement_second_currency.diff b/account_statement_second_currency.diff new file mode 100644 index 0000000..5a2ded0 --- /dev/null +++ b/account_statement_second_currency.diff @@ -0,0 +1,456 @@ +diff --git a/tryton/modules/account_payment/account.py b/tryton/modules/account_payment/account.py +index 801a0e674e..236d5ef56e 100644 +--- a/tryton/modules/account_payment/account.py ++++ b/tryton/modules/account_payment/account.py +@@ -574,7 +574,9 @@ class StatementLine(metaclass=PoolMeta): + ('party', '=', Eval('party')), + ()), + ('state', 'in', ['processing', 'succeeded', 'failed']), +- ('currency', '=', Eval('currency', -1)), ++ If(Eval('second_currency'), ++ ('currency', '=', Eval('second_currency', -1)), ++ ('currency', '=', Eval('currency', -1))), + ('kind', '=', + If(Eval('amount', 0) > 0, 'receivable', + If(Eval('amount', 0) < 0, 'payable', ''))), +diff --git a/tryton/modules/account_payment_clearing/statement.py b/tryton/modules/account_payment_clearing/statement.py +index 60e8f4955d..d76430fd44 100644 +--- a/tryton/modules/account_payment_clearing/statement.py ++++ b/tryton/modules/account_payment_clearing/statement.py +@@ -110,7 +110,9 @@ class StatementLine(metaclass=PoolMeta): + ] + cls.related_to.domain['account.payment.group'] = [ + ('company', '=', Eval('company', -1)), +- ('currency', '=', Eval('currency', -1)), ++ If(Eval('second_currency'), ++ ('currency', '=', Eval('second_currency', -1)), ++ ('currency', '=', Eval('currency', -1))), + If(Eval('statement_state') == 'draft', + ('clearing_reconciled', '!=', True), + ()), +diff --git a/tryton/modules/account_statement/message.xml b/tryton/modules/account_statement/message.xml +index bab7027298..0d19d9439b 100644 +--- a/tryton/modules/account_statement/message.xml ++++ b/tryton/modules/account_statement/message.xml +@@ -36,6 +36,9 @@ this repository contains the full copyright notices and license terms. --> + + To post statement "%(statement)s" you must create lines for pending %(amount)s of origin "%(origin)s". + ++ ++ You must set the same sign for second currency than amount. ++ + + To post the move "%(move)s" you must post the statement "%(statement)s". + +diff --git a/tryton/modules/account_statement/statement.py b/tryton/modules/account_statement/statement.py +index d3227e77a5..aa93ceebdb 100644 +--- a/tryton/modules/account_statement/statement.py ++++ b/tryton/modules/account_statement/statement.py +@@ -12,7 +12,7 @@ from sql.operators import Concat + from trytond.config import config + from trytond.i18n import gettext + from trytond.model import ( +- DictSchemaMixin, Index, ModelSQL, ModelView, Workflow, fields, ++ Check, DictSchemaMixin, Index, ModelSQL, ModelView, Workflow, fields, + sequence_ordered) + from trytond.model.exceptions import AccessError + from trytond.modules.company import CompanyReport +@@ -672,6 +672,9 @@ def origin_mixin(_states): + company = fields.Function( + fields.Many2One('company.company', "Company"), + 'on_change_with_company', searcher='search_company') ++ company_currency = fields.Function( ++ fields.Many2One('currency.currency', "Company Currency"), ++ 'on_change_with_company_currency') + number = fields.Char("Number") + date = fields.Date( + "Date", required=True, states=_states) +@@ -680,6 +683,25 @@ def origin_mixin(_states): + states=_states) + currency = fields.Function(fields.Many2One( + 'currency.currency', "Currency"), 'on_change_with_currency') ++ amount_second_currency = Monetary( ++ "Amount Second Currency", ++ currency='second_currency', digits='second_currency', ++ states={ ++ 'required': Bool(Eval('second_currency')), ++ 'readonly': _states['readonly'], ++ }) ++ second_currency = fields.Many2One( ++ 'currency.currency', "Second Currency", ++ domain=[ ++ ('id', '!=', Eval('currency', -1)), ++ If(Eval('currency', -1) != Eval('company_currency', -1), ++ ('id', '=', Eval('company_currency', -1)), ++ ()), ++ ], ++ states={ ++ 'required': Bool(Eval('amount_second_currency')), ++ 'readonly': _states['readonly'], ++ }) + party = fields.Many2One( + 'party.party', "Party", states=_states, + context={ +@@ -723,6 +745,10 @@ def origin_mixin(_states): + def search_company(cls, name, clause): + return [('statement.' + clause[0],) + tuple(clause[1:])] + ++ @fields.depends('statement', '_parent_statement.company') ++ def on_change_with_company_currency(self, name=None): ++ return self.statement.company.currency if self.statement else None ++ + @fields.depends('statement', '_parent_statement.journal') + def on_change_with_currency(self, name=None): + if self.statement and self.statement.journal: +@@ -750,7 +776,10 @@ class Line(origin_mixin(_states), sequence_ordered(), ModelSQL, ModelView): + domain={ + 'account.invoice': [ + ('company', '=', Eval('company', -1)), +- ('currency', '=', Eval('currency', -1)), ++ If(Eval('second_currency'), ++ ('currency', '=', Eval('second_currency', -1)), ++ ('currency', '=', Eval('currency', -1)) ++ ), + If(Bool(Eval('party')), + ['OR', + ('party', '=', Eval('party', -1)), +@@ -782,6 +811,7 @@ class Line(origin_mixin(_states), sequence_ordered(), ModelSQL, ModelView): + @classmethod + def __setup__(cls): + super(Line, cls).__setup__() ++ table = cls.__table__() + cls.date.states = { + 'readonly': ( + (Eval('statement_state') != 'draft') +@@ -792,6 +822,14 @@ class Line(origin_mixin(_states), sequence_ordered(), ModelSQL, ModelView): + 'required': (Eval('party_required', False) + & (Eval('statement_state') == 'draft')), + } ++ cls._sql_constraints += [ ++ ('second_currency_sign', ++ Check( ++ table, ++ Coalesce(table.amount_second_currency, 0) ++ * table.amount >= 0), ++ 'account_statement.msg_statement_line_second_currency_sign'), ++ ] + + @classmethod + def __register__(cls, module): +@@ -1029,24 +1067,29 @@ class Line(origin_mixin(_states), sequence_ordered(), ModelSQL, ModelView): + pool = Pool() + MoveLine = pool.get('account.move.line') + Currency = Pool().get('currency.currency') +- zero = Decimal("0.0") + if not self.amount: + return +- with Transaction().set_context(date=self.date): +- amount = Currency.compute(self.statement.journal.currency, +- self.amount, self.statement.company.currency) +- if self.statement.journal.currency != self.statement.company.currency: +- second_currency = self.statement.journal.currency.id ++ if self.second_currency == self.company_currency: ++ amount = self.amount_second_currency ++ else: ++ with Transaction().set_context(date=self.date): ++ amount = Currency.compute( ++ self.currency, self.amount, self.company_currency) ++ if self.currency != self.company_currency: ++ second_currency = self.currency + amount_second_currency = -self.amount ++ elif self.second_currency: ++ second_currency = self.second_currency ++ amount_second_currency = -self.amount_second_currency + else: +- amount_second_currency = None + second_currency = None ++ amount_second_currency = None + + return MoveLine( + origin=self, + description=self.description, +- debit=amount < zero and -amount or zero, +- credit=amount >= zero and amount or zero, ++ debit=abs(amount) if amount < 0 else 0, ++ credit=abs(amount) if amount > 0 else 0, + account=self.account, + party=self.party if self.account.party_required else None, + second_currency=second_currency, +@@ -1070,6 +1113,10 @@ class LineGroup(ModelSQL, ModelView): + "Amount", currency='currency', digits='currency') + currency = fields.Function(fields.Many2One('currency.currency', + 'Currency'), 'get_currency') ++ amount_second_currency = Monetary( ++ "Amount Second Currency", ++ currency='second_currency', digits='second_currency') ++ second_currency = fields.Many2One('currency.currency', "Second Currency") + party = fields.Many2One('party.party', 'Party') + move = fields.Many2One('account.move', 'Move') + +@@ -1086,6 +1133,7 @@ class LineGroup(ModelSQL, ModelView): + Max(line.number).as_('number'), + Max(line.date).as_('date'), + Sum(line.amount).as_('amount'), ++ Sum(line.amount_second_currency).as_('amount_second_currency'), + Max(line.party).as_('party'), + ] + +@@ -1103,6 +1151,7 @@ class LineGroup(ModelSQL, ModelView): + move.create_date, + move.write_uid, + move.write_date, ++ line.second_currency, + ] + + columns = (std_columns + [move.id.as_('move')] +diff --git a/tryton/modules/account_statement/tests/scenario_account_statement_second_currency_invoice.rst b/tryton/modules/account_statement/tests/scenario_account_statement_second_currency_invoice.rst +new file mode 100644 +index 0000000000..5209accc58 +--- /dev/null ++++ b/tryton/modules/account_statement/tests/scenario_account_statement_second_currency_invoice.rst +@@ -0,0 +1,103 @@ ++========================================= ++Account Statement Second Currency Invoice ++========================================= ++ ++Imports:: ++ ++ >>> from decimal import Decimal ++ >>> import datetime as dt ++ ++ >>> from proteus import Model ++ >>> from trytond.tests.tools import activate_modules ++ >>> from trytond.modules.currency.tests.tools import get_currency ++ >>> from trytond.modules.company.tests.tools import create_company ++ >>> from trytond.modules.account.tests.tools import ( ++ ... create_fiscalyear, create_chart, get_accounts) ++ >>> from trytond.modules.account_invoice.tests.tools import ( ++ ... set_fiscalyear_invoice_sequences) ++ ++ >>> today = dt.date.today() ++ ++Activate modules:: ++ ++ >>> config = activate_modules(['account_statement', 'account_invoice']) ++ ++ >>> AccountConfiguration = Model.get('account.configuration') ++ >>> AccountJournal = Model.get('account.journal') ++ >>> Invoice = Model.get('account.invoice') ++ >>> Party = Model.get('party.party') ++ >>> Statement = Model.get('account.statement') ++ >>> StatementJournal = Model.get('account.statement.journal') ++ ++Create company:: ++ ++ >>> usd = get_currency('USD') ++ >>> eur = get_currency('EUR') ++ >>> _ = create_company(currency=usd) ++ ++Create fiscal year:: ++ ++ >>> fiscalyear = set_fiscalyear_invoice_sequences(create_fiscalyear()) ++ >>> fiscalyear.click('create_period') ++ ++Create chart of accounts:: ++ ++ >>> _ = create_chart() ++ >>> accounts = get_accounts() ++ ++Configure currency exchange:: ++ ++ >>> currency_exchange_account, = ( ++ ... accounts['revenue'].duplicate( ++ ... default={'name': "Currency Exchange"})) ++ >>> account_configuration = AccountConfiguration(1) ++ >>> account_configuration.currency_exchange_debit_account = ( ++ ... currency_exchange_account) ++ >>> account_configuration.save() ++ ++Create party:: ++ ++ >>> customer = Party(name="Customer") ++ >>> customer.save() ++ ++Create customer invoice in alternate currency:: ++ ++ >>> invoice = Invoice(type='out') ++ >>> invoice.party = customer ++ >>> invoice.currency = eur ++ >>> line = invoice.lines.new() ++ >>> line.quantity = 1 ++ >>> line.unit_price = Decimal('50.0000') ++ >>> line.account = accounts['revenue'] ++ >>> invoice.click('post') ++ >>> invoice.state ++ 'posted' ++ ++Post statement in company currency with second currency:: ++ ++ >>> account_journal, = AccountJournal.find([('code', '=', 'STA')], limit=1) ++ >>> statement_journal = StatementJournal( ++ ... name="Statement Journal", journal=account_journal, ++ ... currency=usd, account=accounts['cash']) ++ >>> statement_journal.save() ++ ++ >>> statement = Statement( ++ ... name="Test", journal=statement_journal, ++ ... start_balance=Decimal('0.00'), end_balance=Decimal('20.00')) ++ >>> line = statement.lines.new() ++ >>> line.number = "1" ++ >>> line.date = today ++ >>> line.party = customer ++ >>> line.amount = Decimal('20.00') ++ >>> line.amount_second_currency = Decimal('50.00') ++ >>> line.second_currency = eur ++ >>> line.related_to = invoice ++ >>> statement.click('validate_statement') ++ >>> statement.state ++ 'validated' ++ ++Check invoice is paid:: ++ ++ >>> invoice.reload() ++ >>> invoice.state ++ 'paid' +diff --git a/tryton/modules/account_statement/view/line_group_form.xml b/tryton/modules/account_statement/view/line_group_form.xml +index 24c045781e..f66bd0fd82 100644 +--- a/tryton/modules/account_statement/view/line_group_form.xml ++++ b/tryton/modules/account_statement/view/line_group_form.xml +@@ -5,14 +5,17 @@ this repository contains the full copyright notices and license terms. --> +