From 25dcdde09ba2e09b4d28d3e7e5fc3d03db4e19fc Mon Sep 17 00:00:00 2001 From: Frederik Jaeckel Date: Tue, 4 Oct 2022 16:47:14 +0200 Subject: [PATCH] =?UTF-8?q?line/splitline:=20fremdw=C3=A4hrung=20ok+test+m?= =?UTF-8?q?igration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- line.py | 257 ++++++++++------------------------------ locale/de.po | 24 ++++ locale/en.po | 24 ++++ mixin.py | 191 +++++++++++++++++++++++++++++ splitline.py | 29 ++--- tests/test_book.py | 4 + tests/test_line.py | 65 ++++++++++ tests/test_splitline.py | 97 +++++++++++++++ view/split_form.xml | 9 +- view/split_list.xml | 2 + 10 files changed, 485 insertions(+), 217 deletions(-) create mode 100644 mixin.py diff --git a/line.py b/line.py index f82e6fd..bfece53 100644 --- a/line.py +++ b/line.py @@ -10,12 +10,12 @@ from trytond.transaction import Transaction from trytond.report import Report from trytond.exceptions import UserError from trytond.i18n import gettext -from trytond.modules.currency.ir import rate_decimal from decimal import Decimal -from sql import Cast, Literal +from sql import Literal from sql.functions import DatePart from sql.conditionals import Case from .book import sel_state_book +from .mixin import SecondCurrencyMixin sel_payee = [ @@ -47,7 +47,7 @@ STATES = { DEPENDS=['state', 'state_cashbook'] -class Line(Workflow, ModelSQL, ModelView): +class Line(SecondCurrencyMixin, Workflow, ModelSQL, ModelView): 'Cashbook Line' __name__ = 'cashbook.line' @@ -121,23 +121,6 @@ class Line(Workflow, ModelSQL, ModelView): payee = fields.Function(fields.Reference(string='Payee', readonly=True, selection=sel_payee), 'on_change_with_payee', searcher='search_payee') - amount_2nd_currency = fields.Numeric(string='Amount Second Currency', - digits=(16, Eval('currency2nd_digits', 2)), - states={ - 'readonly': STATES['readonly'], - 'required': Bool(Eval('currency2nd')), - 'invisible': ~Bool(Eval('currency2nd')), - }, depends=DEPENDS+['currency2nd_digits', 'currency2nd']) - rate_2nd_currency = fields.Function(fields.Numeric(string='Rate', - help='Exchange rate between the currencies of the participating cashbooks.', - digits=(rate_decimal * 2, rate_decimal), - states={ - 'readonly': STATES['readonly'], - 'required': Bool(Eval('currency2nd')), - 'invisible': ~Bool(Eval('currency2nd')), - }, depends=DEPENDS+['currency2nd_digits', 'currency2nd']), - 'on_change_with_rate_2nd_currency', setter='set_rate_2nd_currency') - # link to lines created by this record reference = fields.Many2One(string='Reference', readonly=True, select=True, states={ @@ -180,10 +163,6 @@ class Line(Workflow, ModelSQL, ModelView): string="Currency", readonly=True), 'on_change_with_currency') currency_digits = fields.Function(fields.Integer(string='Currency Digits', readonly=True), 'on_change_with_currency_digits') - currency2nd = fields.Function(fields.Many2One(model_name='currency.currency', - string="2nd Currency", readonly=True), 'on_change_with_currency2nd') - currency2nd_digits = fields.Function(fields.Integer(string='2nd Currency Digits', - readonly=True), 'on_change_with_currency2nd_digits') state = fields.Selection(string='State', required=True, readonly=True, select=True, selection=sel_linetype) @@ -203,6 +182,7 @@ class Line(Workflow, ModelSQL, ModelView): table = cls.__table_handler__(module_name) table.drop_constraint('amount_val') + cls.migrate_amount_2nd_currency() @classmethod def __setup__(cls): @@ -236,6 +216,48 @@ class Line(Workflow, ModelSQL, ModelView): }, }) + @classmethod + def migrate_amount_2nd_currency(cls): + """ add amount-2nd-currency + """ + pool = Pool() + Line2 = pool.get('cashbook.line') + Book = pool.get('cashbook.book') + Book2 = pool.get('cashbook.book') + tab_line = Line2.__table__() + tab_book = Book.__table__() # cashbook of line + tab_book2 = Book2.__table__() # transfer-target + cursor = Transaction().connection.cursor() + + query = tab_line.join(tab_book, + condition=tab_line.cashbook == tab_book.id, + ).join(tab_book2, + condition=tab_line.booktransf== tab_book2.id, + ).select(tab_line.id, + where=tab_line.bookingtype.in_(['mvin', 'mvout']) & \ + (tab_line.amount_2nd_currency == None) & \ + (tab_book.currency != tab_book2.currency) + ) + lines = Line2.search([('id', 'in', query)]) + to_write = [] + for line in lines: + values = Line2.add_2nd_currency({ + 'date': line.date, + 'booktransf': line.booktransf.id, + 'amount': line.amount, + }, line.currency) + if 'amount_2nd_currency' in values.keys(): + values['id'] = line.id + to_write.append(values) + + for line in to_write: + qu1 = tab_line.update( + columns = [tab_line.amount_2nd_currency], + values = [line['amount_2nd_currency']], + where = tab_line.id == line['id'], + ) + cursor.execute(*qu1) + @classmethod @ModelView.button @Workflow.transition('edit') @@ -315,10 +337,10 @@ class Line(Workflow, ModelSQL, ModelView): 'booktransf': line.cashbook.id, 'reference': line.id, 'amount': line.amount \ - if line.cashbook.currency.id == line.booktransf.currency.id \ + if line.currency.id == line.booktransf.currency.id \ else line.amount_2nd_currency, 'amount_2nd_currency': line.amount \ - if line.cashbook.currency.id != line.booktransf.currency.id \ + if line.currency.id != line.booktransf.currency.id \ else None, } values.update(cls.get_debit_credit(values)) @@ -335,15 +357,17 @@ class Line(Workflow, ModelSQL, ModelView): 'description': sp_line.description, 'booktransf': line.cashbook.id, 'reference': line.id, + 'amount': sp_line.amount \ + if sp_line.currency.id == sp_line.booktransf.currency.id \ + else sp_line.amount_2nd_currency, + 'amount_2nd_currency': sp_line.amount \ + if sp_line.currency.id != sp_line.booktransf.currency.id \ + else None, } if line.bookingtype.endswith('out'): values['bookingtype'] = 'mvin' else : values['bookingtype'] = 'mvout' - values.update(line.get_amount_by_second_currency( - sp_line.booktransf.currency, - amount = sp_line.amount, - )) values.update(cls.get_debit_credit(values)) to_create_line.append(values) @@ -431,26 +455,6 @@ class Line(Workflow, ModelSQL, ModelView): 'type': gettext('cashbook.msg_line_bookingtype_%s' % self.bookingtype), } - def get_amount_by_second_currency(self, to_currency, amount=None): - """ get amount, calculate credit/debit from currency of current - cashbook to 'to_currency' - """ - Currency = Pool().get('currency.currency') - - values = { - 'amount': amount if amount is not None else self.amount, - } - - if to_currency.id != self.cashbook.currency.id: - with Transaction().set_context({ - 'date': self.date, - }): - values['amount'] = Currency.compute( - self.cashbook.currency, - values['amount'], - to_currency) - return values - @staticmethod def order_state(tables): """ edit = 0, check/done = 1 @@ -541,7 +545,8 @@ class Line(Workflow, ModelSQL, ModelView): """ self.amount = sum([x.amount for x in self.splitlines if x.amount is not None]) - @fields.depends('bookingtype', 'category', 'splitlines') + @fields.depends('bookingtype', 'category', 'splitlines', 'booktransf',\ + 'currency2nd') def on_change_bookingtype(self): """ clear category if not valid type """ @@ -567,98 +572,7 @@ class Line(Workflow, ModelSQL, ModelView): else : # category self.splitlines = [] self.booktransf = None - - @fields.depends('booktransf', '_parent_booktransf.currency', 'date',\ - 'currency', 'amount', 'amount_2nd_currency') - def on_change_booktransf(self): - """ update amount_2nd_currency - """ - pool = Pool() - Currency = pool.get('currency.currency') - IrDate = pool.get('ir.date') - - if (self.booktransf is None) or (self.currency is None): - return - - if self.amount is not None: - if self.booktransf.currency.id != self.currency.id: - with Transaction().set_context({ - 'date': self.date or IrDate.today(), - }): - self.amount_2nd_currency = Currency.compute( - self.currency, - self.amount, - self.booktransf.currency - ) - return - self.amount_2nd_currency = None - - @fields.depends('currency', 'booktransf', '_parent_booktransf.currency', \ - 'amount', 'amount_2nd_currency', 'rate_2nd_currency') - def on_change_amount(self): - """ update amount_2nd_currency - """ - self.on_change_rate_2nd_currency() - - @fields.depends('booktransf', '_parent_booktransf.currency', \ - 'amount', 'amount_2nd_currency', 'rate_2nd_currency') - def on_change_rate_2nd_currency(self): - """ update amount_2nd_currency by rate - """ - if (self.amount is None) or (self.rate_2nd_currency is None) or \ - (self.booktransf is None): - return - - if self.currency.id != self.booktransf.currency.id: - self.amount_2nd_currency = self.booktransf.currency.round( - self.amount * self.rate_2nd_currency - ) - - @classmethod - def set_rate_2nd_currency(cls, lines, name, value): - """ compute amount_2nd_currency, write to db - """ - Line2 = Pool().get('cashbook.line') - - to_write = [] - - if not name == 'rate_2nd_currency': - return - - for line in lines: - if line.booktransf is None: - continue - - if line.cashbook.currency.id == line.booktransf.currency.id: - continue - - to_write.extend([ - [line], - { - 'amount_2nd_currency': line.booktransf.currency.round( - line.amount * value), - }]) - - if len(to_write) > 0: - Line2.write(*to_write) - - @fields.depends('amount', 'amount_2nd_currency', 'rate_2nd_currency') - def on_change_amount_2nd_currency(self): - """ update rate_2nd_currency by rate - """ - self.rate_2nd_currency = self.on_change_with_rate_2nd_currency() - - @fields.depends('amount', 'amount_2nd_currency') - def on_change_with_rate_2nd_currency(self, name=None): - """ get current rate from amount - """ - Rate = Pool().get('currency.currency.rate') - - if (self.amount is not None) and \ - (self.amount_2nd_currency is not None): - if self.amount != Decimal('0.0'): - exp = Decimal(Decimal(1) / 10 ** Rate.rate.digits[1]) - return (self.amount_2nd_currency / self.amount).quantize(exp) + self.currency2nd = self.on_change_with_currency2nd() @fields.depends('description') def on_change_with_descr_short(self, name=None): @@ -733,24 +647,6 @@ class Line(Workflow, ModelSQL, ModelView): else: return 2 - @fields.depends('currency', 'booktransf', '_parent_booktransf.currency') - def on_change_with_currency2nd(self, name=None): - """ currency of transfer-target - """ - if self.booktransf: - if self.currency: - if self.currency.id != self.booktransf.currency.id: - return self.booktransf.currency.id - - @fields.depends('currency', 'booktransf', '_parent_booktransf.currency') - def on_change_with_currency2nd_digits(self, name=None): - """ currency of transfer-target - """ - if self.booktransf: - return self.booktransf.currency.digits - else: - return 2 - @fields.depends('id', 'date', 'cashbook', \ '_parent_cashbook.id', 'reconciliation', \ '_parent_reconciliation.start_amount',\ @@ -872,38 +768,6 @@ class Line(Workflow, ModelSQL, ModelView): raise ValueError('invalid "bookingtype"') return {} - @classmethod - def add_2nd_currency(cls, values): - """ add second currency amount if missing - """ - pool = Pool() - Currency = pool.get('currency.currency') - Cashbook = pool.get('cashbook.book') - IrDate = pool.get('ir.date') - - cashbook = values.get('cashbook', None) - booktransf = values.get('booktransf', None) - amount = values.get('amount', None) - amount_2nd_currency = values.get('amount_2nd_currency', None) - - if (cashbook is None) or (booktransf is None): - return values - - if amount is not None: - if amount_2nd_currency is None: - cashbook = Cashbook(cashbook) - booktransf = Cashbook(booktransf) - if cashbook.currency.id != booktransf.currency.id: - with Transaction().set_context({ - 'date': values.get('date', IrDate.today()), - }): - values['amount_2nd_currency'] = Currency.compute( - cashbook.currency, - amount, - booktransf.currency, - ) - return values - @classmethod def update_amount_by_splitlines(cls, lines): """ update amounts from split-lines @@ -1028,11 +892,16 @@ class Line(Workflow, ModelSQL, ModelView): def create(cls, vlist): """ add debit/credit """ + Cashbook = Pool().get('cashbook.book') + vlist = [x.copy() for x in vlist] for values in vlist: values.update(cls.get_debit_credit(values)) values.update(cls.clear_by_bookingtype(values)) - values.update(cls.add_2nd_currency(values)) + + cashbook = values.get('cashbook', None) + if cashbook: + values.update(cls.add_2nd_currency(values, Cashbook(cashbook).currency)) # deny add to reconciliation if state is not 'check' or 'done' if values.get('reconciliation', None): diff --git a/locale/de.po b/locale/de.po index 3c4a5ac..1a3347e 100644 --- a/locale/de.po +++ b/locale/de.po @@ -582,6 +582,10 @@ msgctxt "view:cashbook.split:" msgid "Description" msgstr "Beschreibung" +msgctxt "view:cashbook.split:" +msgid "Amount" +msgstr "Betrag" + msgctxt "field:cashbook.split,line:" msgid "Line" msgstr "Zeile" @@ -698,6 +702,26 @@ msgctxt "field:cashbook.split,booktransf:" msgid "Source/Dest" msgstr "Quelle/Ziel" +msgctxt "field:cashbook.split,currency2nd:" +msgid "2nd Currency" +msgstr "Fremdwährung" + +msgctxt "field:cashbook.split,currency2nd_digits:" +msgid "2nd Currency Digits" +msgstr "Nachkommastellen Fremdwährung" + +msgctxt "field:cashbook.split,amount_2nd_currency:" +msgid "Amount Second Currency" +msgstr "Fremdwährungsbetrag" + +msgctxt "field:cashbook.split,rate_2nd_currency:" +msgid "Rate" +msgstr "Kurs" + +msgctxt "help:cashbook.split,rate_2nd_currency:" +msgid "Exchange rate between the currencies of the participating cashbooks." +msgstr "Wechselkurs zwischen der Währungen der beteiligten Kassenbücher." + ################# # cashbook.line # diff --git a/locale/en.po b/locale/en.po index 518714b..aa8ea3d 100644 --- a/locale/en.po +++ b/locale/en.po @@ -542,6 +542,10 @@ msgctxt "view:cashbook.split:" msgid "Description" msgstr "Description" +msgctxt "view:cashbook.split:" +msgid "Amount" +msgstr "Amount" + msgctxt "field:cashbook.split,line:" msgid "Line" msgstr "Line" @@ -658,6 +662,26 @@ msgctxt "field:cashbook.split,booktransf:" msgid "Source/Dest" msgstr "Source/Dest" +msgctxt "field:cashbook.split,currency2nd:" +msgid "2nd Currency" +msgstr "2nd Currency" + +msgctxt "field:cashbook.split,currency2nd_digits:" +msgid "2nd Currency Digits" +msgstr "2nd Currency Digits" + +msgctxt "field:cashbook.split,amount_2nd_currency:" +msgid "Amount Second Currency" +msgstr "Amount Second Currency" + +msgctxt "field:cashbook.split,rate_2nd_currency:" +msgid "Rate" +msgstr "Rate" + +msgctxt "help:cashbook.split,rate_2nd_currency:" +msgid "Exchange rate between the currencies of the participating cashbooks." +msgstr "Exchange rate between the currencies of the participating cashbooks." + msgctxt "model:cashbook.line,name:" msgid "Cashbook Line" msgstr "Cashbook Line" diff --git a/mixin.py b/mixin.py new file mode 100644 index 0000000..973e8b5 --- /dev/null +++ b/mixin.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- +# This file is part of the cashbook-module from m-ds for Tryton. +# The COPYRIGHT file at the top level of this repository contains the +# full copyright notices and license terms. + +from trytond.model import ModelView, ModelSQL, fields +from trytond.pyson import Eval, Bool, Or +from trytond.pool import Pool +from trytond.modules.currency.ir import rate_decimal +from trytond.transaction import Transaction +from decimal import Decimal + +STATES = { + 'readonly': Or( + Eval('state', '') != 'edit', + Eval('state_cashbook', '') != 'open', + ), + } +DEPENDS=['state', 'state_cashbook'] + + +class SecondCurrencyMixin: + """ two fields for 2nd currency: amount + rate + """ + amount_2nd_currency = fields.Numeric(string='Amount Second Currency', + digits=(16, Eval('currency2nd_digits', 2)), + states={ + 'readonly': Or( + STATES['readonly'], + ~Bool(Eval('currency2nd')) + ), + 'required': Bool(Eval('currency2nd')), + 'invisible': ~Bool(Eval('currency2nd')), + }, depends=DEPENDS+['currency2nd_digits', 'currency2nd']) + rate_2nd_currency = fields.Function(fields.Numeric(string='Rate', + help='Exchange rate between the currencies of the participating cashbooks.', + digits=(rate_decimal * 2, rate_decimal), + states={ + 'readonly': Or( + STATES['readonly'], + ~Bool(Eval('currency2nd')) + ), + 'required': Bool(Eval('currency2nd')), + 'invisible': ~Bool(Eval('currency2nd')), + }, depends=DEPENDS+['currency2nd_digits', 'currency2nd']), + 'on_change_with_rate_2nd_currency', setter='set_rate_2nd_currency') + + currency2nd = fields.Function(fields.Many2One(model_name='currency.currency', + string="2nd Currency", readonly=True), 'on_change_with_currency2nd') + currency2nd_digits = fields.Function(fields.Integer(string='2nd Currency Digits', + readonly=True), 'on_change_with_currency2nd_digits') + + @classmethod + def add_2nd_currency(cls, values, from_currency): + """ add second currency amount if missing + """ + pool = Pool() + Currency = pool.get('currency.currency') + Cashbook = pool.get('cashbook.book') + IrDate = pool.get('ir.date') + + #cashbook = values.get('cashbook', None) + booktransf = values.get('booktransf', None) + amount = values.get('amount', None) + amount_2nd_currency = values.get('amount_2nd_currency', None) + + if (amount is not None) and (booktransf is not None): + if amount_2nd_currency is None: + #cashbook = Cashbook(cashbook) + booktransf = Cashbook(booktransf) + if from_currency.id != booktransf.currency.id: + with Transaction().set_context({ + 'date': values.get('date', IrDate.today()), + }): + values['amount_2nd_currency'] = Currency.compute( + from_currency, + amount, + booktransf.currency, + ) + return values + + @fields.depends('booktransf', '_parent_booktransf.currency', \ + 'currency', 'amount', 'date', 'amount_2nd_currency', 'rate_2nd_currency') + def on_change_booktransf(self): + """ update amount_2nd_currency + """ + self.on_change_rate_2nd_currency() + + @fields.depends('booktransf', '_parent_booktransf.currency', \ + 'currency', 'amount', 'date', 'amount_2nd_currency', 'rate_2nd_currency') + def on_change_amount(self): + """ update amount_2nd_currency + """ + self.on_change_rate_2nd_currency() + + @fields.depends('booktransf', '_parent_booktransf.currency', \ + 'currency', 'amount', 'date', 'amount_2nd_currency', 'rate_2nd_currency') + def on_change_rate_2nd_currency(self): + """ update amount_2nd_currency + rate_2nd_currency + """ + pool = Pool() + IrDate = pool.get('ir.date') + Currency = pool.get('currency.currency') + + if (self.amount is None) or (self.booktransf is None): + self.amount_2nd_currency = None + self.rate_2nd_currency = None + return + + if self.rate_2nd_currency is None: + # no rate set, use current rate of target-currency + with Transaction().set_context({ + 'date': self.date or IrDate.today(), + }): + self.amount_2nd_currency = Currency.compute( + self.currency, + self.amount, + self.booktransf.currency + ) + if self.amount != Decimal('0.0'): + self.rate_2nd_currency = self.amount_2nd_currency / self.amount + else : + self.amount_2nd_currency = self.booktransf.currency.round( + self.amount * self.rate_2nd_currency + ) + + @classmethod + def set_rate_2nd_currency(cls, lines, name, value): + """ compute amount_2nd_currency, write to db + """ + Line2 = Pool().get(cls.__name__) + + to_write = [] + + if not name == 'rate_2nd_currency': + return + + for line in lines: + if line.booktransf is None: + continue + + if line.cashbook.currency.id == line.booktransf.currency.id: + continue + + to_write.extend([ + [line], + { + 'amount_2nd_currency': line.booktransf.currency.round( + line.amount * value), + }]) + + if len(to_write) > 0: + Line2.write(*to_write) + + @fields.depends('amount', 'amount_2nd_currency', 'rate_2nd_currency') + def on_change_amount_2nd_currency(self): + """ update rate_2nd_currency by rate + """ + self.rate_2nd_currency = self.on_change_with_rate_2nd_currency() + + @fields.depends('amount', 'amount_2nd_currency') + def on_change_with_rate_2nd_currency(self, name=None): + """ get current rate from amount + """ + Rate = Pool().get('currency.currency.rate') + + if (self.amount is not None) and \ + (self.amount_2nd_currency is not None): + if self.amount != Decimal('0.0'): + exp = Decimal(Decimal(1) / 10 ** Rate.rate.digits[1]) + return (self.amount_2nd_currency / self.amount).quantize(exp) + + @fields.depends('currency', 'booktransf', '_parent_booktransf.currency') + def on_change_with_currency2nd(self, name=None): + """ currency of transfer-target + """ + if self.booktransf: + if self.currency: + if self.currency.id != self.booktransf.currency.id: + return self.booktransf.currency.id + + @fields.depends('currency', 'booktransf', '_parent_booktransf.currency') + def on_change_with_currency2nd_digits(self, name=None): + """ currency of transfer-target + """ + if self.booktransf: + return self.booktransf.currency.digits + else: + return 2 + +# end SecondCurrencyMixin diff --git a/splitline.py b/splitline.py index 649c5f3..2913a38 100644 --- a/splitline.py +++ b/splitline.py @@ -12,6 +12,7 @@ from trytond.i18n import gettext from trytond.transaction import Transaction from .line import sel_linetype, sel_bookingtype, STATES, DEPENDS from .book import sel_state_book +from .mixin import SecondCurrencyMixin sel_linetype = [ @@ -25,7 +26,7 @@ sel_target = [ ] -class SplitLine(ModelSQL, ModelView): +class SplitLine(SecondCurrencyMixin, ModelSQL, ModelView): 'Split booking line' __name__ = 'cashbook.split' @@ -57,6 +58,7 @@ class SplitLine(ModelSQL, ModelView): domain=[ ('owner.id', '=', Eval('owner_cashbook', -1)), ('id', '!=', Eval('cashbook', -1)), + ('btype', '!=', None), ], states={ 'readonly': STATES['readonly'], @@ -106,26 +108,6 @@ class SplitLine(ModelSQL, ModelView): 'type': gettext('cashbook.msg_line_bookingtype_%s' % self.line.bookingtype), } - def get_amount_by_second_currency(self, to_currency, amount=None): - """ get amount, calculate credit/debit from currency of current - cashbook to 'to_currency' - """ - Currency = Pool().get('currency.currency') - - values = { - 'amount': amount if amount is not None else self.amount, - } - - if to_currency.id != self.line.cashbook.currency.id: - with Transaction().set_context({ - 'date': self.line.date, - }): - values['amount'] = Currency.compute( - self.line.cashbook.currency, - values['amount'], - to_currency) - return values - @fields.depends('splittype', 'category', 'booktransf') def on_change_splittype(self): """ clear category if not valid type @@ -228,6 +210,11 @@ class SplitLine(ModelSQL, ModelView): Line2 = Pool().get('cashbook.line') vlist = [x.copy() for x in vlist] + for values in vlist: + line = Line2(values.get('line', None)) + if line: + values.update(cls.add_2nd_currency(values, line.cashbook.currency)) + records = super(SplitLine, cls).create(vlist) to_update_line = [] diff --git a/tests/test_book.py b/tests/test_book.py index 0183094..878ebf2 100644 --- a/tests/test_book.py +++ b/tests/test_book.py @@ -70,6 +70,7 @@ class BookTestCase(ModuleTestCase): (usd, euro) = self.prep_2nd_currency(company) category = self.prep_category(cattype='in') self.assertEqual(company.currency.rec_name, 'Euro') + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', @@ -84,6 +85,7 @@ class BookTestCase(ModuleTestCase): 'bookingtype': 'in', 'category': category.id, 'amount': Decimal('10.0'), + 'party': party.id, }])], }]) @@ -118,6 +120,7 @@ class BookTestCase(ModuleTestCase): (usd, euro) = self.prep_2nd_currency(company) category = self.prep_category(cattype='in') self.assertEqual(company.currency.rec_name, 'Euro') + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', @@ -136,6 +139,7 @@ class BookTestCase(ModuleTestCase): 'bookingtype': 'in', 'category': category.id, 'amount': Decimal('10.0'), + 'party': party.id, }])], }])], }]) diff --git a/tests/test_line.py b/tests/test_line.py index f2f6d1a..e8b7a31 100644 --- a/tests/test_line.py +++ b/tests/test_line.py @@ -156,6 +156,71 @@ class LineTestCase(ModuleTestCase): self.assertEqual(books[0].lines[0].amount_2nd_currency, Decimal('10.5')) self.assertEqual(books[0].lines[0].amount, Decimal('12.0')) + @with_transaction() + def test_line_check_migrate_amount_2nd_currency(self): + """ create cashbook, lines, transfer + check migration + """ + pool = Pool() + Book = pool.get('cashbook.book') + Lines = pool.get('cashbook.line') + tab_line = Lines.__table__() + cursor = Transaction().connection.cursor() + + types = self.prep_type() + company = self.prep_company() + + # add EURO, set company-currency to EURO + (usd, euro) = self.prep_2nd_currency(company) + + books = Book.create([{ + 'name': 'Book USD', + 'btype': types.id, + 'company': company.id, + 'currency': usd.id, + 'number_sequ': self.prep_sequence().id, + 'start_date': date(2022, 5, 1), + }, { + 'name': 'Book EURO', + 'btype': types.id, + 'company': company.id, + 'currency': euro.id, + 'number_sequ': self.prep_sequence().id, + 'start_date': date(2022, 5, 1), + }]) + self.assertEqual(len(books), 2) + self.assertEqual(books[0].rec_name, 'Book USD | 0.00 usd | Open') + self.assertEqual(books[1].rec_name, 'Book EURO | 0.00 € | Open') + + Book.write(*[ + [books[0]], + { + 'lines': [('create', [{ + 'date': date(2022, 5, 5), + 'description': 'Transfer USD --> EUR', + 'bookingtype': 'mvout', + 'amount': Decimal('10.0'), + 'booktransf': books[1].id, + }])], + }]) + self.assertEqual(len(books[0].lines), 1) + self.assertEqual(len(books[1].lines), 0) + self.assertEqual(books[0].lines[0].rec_name, + '05/05/2022|to|-10.00 usd|Transfer USD --> EUR [Book EURO | 0.00 € | Open]') + self.assertEqual(books[0].lines[0].amount_2nd_currency, Decimal('9.52')) + + # clear field 'amount_2nd_currency' to prepare for migration + clear_field = tab_line.update( + columns = [tab_line.amount_2nd_currency], + values = [None], + where = (tab_line.id == books[0].lines[0].id), + ) + cursor.execute(*clear_field) + + # migrate + Lines.migrate_amount_2nd_currency() + self.assertEqual(books[0].lines[0].amount_2nd_currency, Decimal('9.52')) + @with_transaction() def test_line_check_transfer_2nd_currency_out(self): """ create cashbook, lines, transfer amount between diff --git a/tests/test_splitline.py b/tests/test_splitline.py index c04d242..ddd9889 100644 --- a/tests/test_splitline.py +++ b/tests/test_splitline.py @@ -106,6 +106,103 @@ class SplitLineTestCase(ModuleTestCase): self.assertEqual(len(books[0].lines[0].references), 0) self.assertEqual(len(books[1].lines), 0) + @with_transaction() + def test_splitline_category_and_transfer_2ndcurrency(self): + """ add book, line, two split-lines, + category + transfer, target-cashbook in USD + """ + pool = Pool() + Book = pool.get('cashbook.book') + Line = pool.get('cashbook.line') + + types = self.prep_type() + category1 = self.prep_category(cattype='out') + company = self.prep_company() + party = self.prep_party() + (usd, euro) = self.prep_2nd_currency(company) + + books = Book.create([{ + 'name': 'Book 1', + 'btype': types.id, + 'company': company.id, + 'currency': euro.id, + 'number_sequ': self.prep_sequence().id, + 'start_date': date(2022, 5, 1), + 'lines': [('create', [{ + 'date': date(2022, 5, 1), + 'description': 'Text 1', + 'category': category1.id, + 'bookingtype': 'out', + 'amount': Decimal('1.0'), + 'party': party.id, + }])], + }, { + 'name': 'Book 2', + 'btype': types.id, + 'company': company.id, + 'currency': usd.id, + 'number_sequ': self.prep_sequence().id, + 'start_date': date(2022, 5, 1), + }]) + self.assertEqual(books[0].rec_name, 'Book 1 | -1.00 € | Open') + self.assertEqual(len(books[0].lines), 1) + self.assertEqual(books[0].lines[0].rec_name, '05/01/2022|Exp|-1.00 €|Text 1 [Cat1]') + self.assertEqual(books[1].rec_name, 'Book 2 | 0.00 usd | Open') + + # EUR --> USD + Book.write(*[ + [books[0]], + { + 'lines': [('write', [books[0].lines[0]], { + 'bookingtype': 'spout', + 'splitlines': [('create', [{ + 'amount': Decimal('5.0'), + 'splittype': 'cat', + 'description': 'to category', + 'category': category1.id, + }, { + 'amount': Decimal('6.0'), + 'splittype': 'tr', + 'description': 'to book2', + 'booktransf': books[1].id, + }])], + })] + }]) + self.assertEqual(len(books[0].lines), 1) + self.assertEqual(books[0].lines[0].rec_name, '05/01/2022|Exp/Sp|-11.00 €|Text 1 [-]') + self.assertEqual(books[0].lines[0].category, None) + self.assertEqual(len(books[0].lines[0].splitlines), 2) + self.assertEqual(books[0].lines[0].splitlines[0].rec_name, + 'Exp/Sp|5.00 €|to category [Cat1]') + self.assertEqual(books[0].lines[0].splitlines[0].amount_2nd_currency, None) + self.assertEqual(books[0].lines[0].splitlines[1].rec_name, + 'Exp/Sp|6.00 €|to book2 [Book 2 | 0.00 usd | Open]') + self.assertEqual(books[0].lines[0].splitlines[1].amount_2nd_currency, Decimal('6.3')) + self.assertEqual(len(books[1].lines), 0) + + # wf: edit -> check + Line.wfcheck(books[0].lines) + self.assertEqual(len(books[0].lines), 1) + self.assertEqual(books[0].lines[0].state, 'check') + self.assertEqual(books[0].lines[0].number, '1') + self.assertEqual(len(books[0].lines[0].references), 1) + self.assertEqual(books[0].lines[0].references[0].rec_name, + '05/01/2022|from|6.30 usd|to book2 [Book 1 | -11.00 € | Open]') + + self.assertEqual(len(books[1].lines), 1) + self.assertEqual(books[1].lines[0].reference.rec_name, + '05/01/2022|Exp/Sp|-11.00 €|Text 1 [-]') + self.assertEqual(books[1].lines[0].rec_name, + '05/01/2022|from|6.30 usd|to book2 [Book 1 | -11.00 € | Open]') + self.assertEqual(books[1].lines[0].amount, Decimal('6.3')) + self.assertEqual(books[1].lines[0].amount_2nd_currency, Decimal('6.0')) + + # wf: check --> edit + Line.wfedit(books[0].lines) + self.assertEqual(len(books[0].lines), 1) + self.assertEqual(len(books[0].lines[0].references), 0) + self.assertEqual(len(books[1].lines), 0) + @with_transaction() def test_splitline_check_clear_by_bookingtype(self): """ add book, line, category, set line to 'in', diff --git a/view/split_form.xml b/view/split_form.xml index 71ed265..c899966 100644 --- a/view/split_form.xml +++ b/view/split_form.xml @@ -5,7 +5,7 @@ full copyright notices and license terms. -->