From 59dfb94beea51bd5a123c359c49950144f357f7c Mon Sep 17 00:00:00 2001 From: Frederik Jaeckel Date: Sun, 2 Oct 2022 15:04:10 +0200 Subject: [PATCH] =?UTF-8?q?book:=20view=20zeigt=20in=20listansicht=20bei?= =?UTF-8?q?=20unterkonten=20in=20fremdw=C3=A4hrung=20den=20korrekten=20wer?= =?UTF-8?q?t,=20book-form:=20feld=20f=C3=BCr=20saldo=20in=20unternehmens-w?= =?UTF-8?q?=C3=A4hrung?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book.py | 91 +++++++++++++++++++++++++++++++++---- locale/de.po | 16 +++++++ locale/en.po | 16 +++++++ tests/test_book.py | 104 +++++++++++++++++++++++++++++++++++++++++++ tests/test_config.py | 7 +++ view/book_form.xml | 15 +++++-- view/book_tree.xml | 2 +- 7 files changed, 238 insertions(+), 13 deletions(-) diff --git a/book.py b/book.py index 7db0871..e480030 100644 --- a/book.py +++ b/book.py @@ -11,8 +11,10 @@ from trytond.transaction import Transaction from trytond.pool import Pool from trytond.report import Report from decimal import Decimal +from sql import Literal from sql.aggregate import Sum -from sql.conditionals import Case +from sql.conditionals import Case, Coalesce +from sql.functions import CurrentDate from .model import order_name_hierarchical @@ -103,15 +105,29 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView): balance = fields.Function(fields.Numeric(string='Balance', readonly=True, digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']), 'on_change_with_balance') - currency = fields.Many2One(string='Currency', - model_name='currency.currency', + + balance_ref = fields.Function(fields.Numeric(string='Balance (Ref.)', + help='Balance in company currency', + readonly=True, digits=(16, Eval('company_currency_digits', 2)), + states={ + 'invisible': ~Bool(Eval('company_currency')), + }, depends=['company_currency_digits', 'company_currency']), + 'on_change_with_balance_ref') + company_currency = fields.Function(fields.Many2One(readonly=True, + string='Company Currency', states={'invisible': True}, + model_name='currency.currency'), + 'on_change_with_company_currency') + company_currency_digits = fields.Function(fields.Integer( + string='Currency Digits (Ref.)', readonly=True), + 'on_change_with_currency_digits') + + currency = fields.Many2One(string='Currency', select=True, + model_name='currency.currency', readonly=True, states={ 'readonly': Or( STATES2['readonly'], Bool(Eval('lines', [])), ), - 'invisible': STATES2['invisible'], - 'required': ~STATES2['invisible'], }, depends=DEPENDS2+['lines']) currency_digits = fields.Function(fields.Integer(string='Currency Digits', readonly=True), 'on_change_with_currency_digits') @@ -255,36 +271,95 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView): else: return 2 + @fields.depends('company', 'currency', 'btype') + def on_change_with_company_currency(self, name=None): + """ get company-currency if its different from current + cashbook-currency, disable if book is a view + """ + if self.company: + if self.currency: + if self.btype: + if self.company.currency.id != self.currency.id: + return self.company.currency.id + @fields.depends('id') def on_change_with_balance(self, name=None): """ compute balance """ pool = Pool() Book2 = pool.get('cashbook.book') + Book3 = pool.get('cashbook.book') Line = pool.get('cashbook.line') + Currency = pool.get('currency.currency') + tab_line = Line.__table__() + tab_book = Book3.__table__() cursor = Transaction().connection.cursor() + # select cashbook-lines from current cashbook and below line_query = Line.search([ ('cashbook.id', 'in', Book2.search([ ('parent', 'child_of', [self.id]), ], query=True)), ], query=True) - query = line_query.join(tab_line, + # sum lines by currency + bal_by_currency = line_query.join(tab_line, condition=tab_line.id==line_query.id, + ).join(tab_book, + condition=tab_book.id==tab_line.cashbook, ).select( Sum(tab_line.credit - tab_line.debit).as_('balance'), + tab_book.currency, + group_by=[tab_book.currency], + ) + + if self.id: + total = Decimal('0.0') + + cursor.execute(*bal_by_currency) + balance_lines = cursor.fetchall() + + for line in balance_lines: + (balance, id_currency) = line + + total += Currency.compute( + Currency(id_currency), # from + balance, + self.currency, # to + ) + return total + + @fields.depends('company', 'currency', 'id', 'btype') + def on_change_with_balance_ref(self, name=None): + """ balance converted to company-currency + """ + pool = Pool() + Line = pool.get('cashbook.line') + tab_line = Line.__table__() + cursor = Transaction().connection.cursor() + + if self.btype is None: + return None + + query = tab_line.select( + Sum(tab_line.credit - tab_line.debit), + where = tab_line.cashbook == self.id, ) if self.id: - balance = Decimal('0.0') cursor.execute(*query) result = cursor.fetchone() + balance = Decimal('0.0') if result: if result[0] is not None: balance += result[0] - return balance + if self.currency: + return self.currency.compute( + self.currency, # from + balance, + self.company.currency # to + ) @classmethod @ModelView.button diff --git a/locale/de.po b/locale/de.po index ab6761f..3fac21d 100644 --- a/locale/de.po +++ b/locale/de.po @@ -554,6 +554,22 @@ msgctxt "field:cashbook.book,right:" msgid "Right" msgstr "Rechts" +msgctxt "field:cashbook.book,balance_ref:" +msgid "Balance (Ref.)" +msgstr "Saldo (Ref.)" + +msgctxt "help:cashbook.book,balance_ref:" +msgid "Balance in company currency" +msgstr "Saldo in der Unternehmenswährung" + +msgctxt "field:cashbook.book,company_currency:" +msgid "Company Currency" +msgstr "Unternehmenswährung" + +msgctxt "field:cashbook.book,company_currency_digits:" +msgid "Currency Digits (Ref.)" +msgstr "Nachkommastellen Währung (Ref.)" + ################## # cashbook.split # diff --git a/locale/en.po b/locale/en.po index 338f7cf..dcea347 100644 --- a/locale/en.po +++ b/locale/en.po @@ -518,6 +518,22 @@ msgctxt "field:cashbook.book,right:" msgid "Right" msgstr "Right" +msgctxt "field:cashbook.book,balance_ref:" +msgid "Balance (Ref.)" +msgstr "Balance (Ref.)" + +msgctxt "help:cashbook.book,balance_ref:" +msgid "Balance in company currency" +msgstr "Balance in company currency" + +msgctxt "field:cashbook.book,company_currency:" +msgid "Company Currency" +msgstr "Company Currency" + +msgctxt "field:cashbook.book,company_currency_digits:" +msgid "Currency Digits (Ref.)" +msgstr "Currency Digits (Ref.)" + msgctxt "model:cashbook.split,name:" msgid "Split booking line" msgstr "Split booking line" diff --git a/tests/test_book.py b/tests/test_book.py index 67fea04..0183094 100644 --- a/tests/test_book.py +++ b/tests/test_book.py @@ -56,6 +56,110 @@ class BookTestCase(ModuleTestCase): self.assertEqual(book.state, 'open') self.assertEqual(book.state_string, 'Open') + @with_transaction() + def test_book_create_2nd_currency(self): + """ create cashbook, in 2nd currency, check balance-fields + """ + pool = Pool() + Book = pool.get('cashbook.book') + + types = self.prep_type() + company = self.prep_company() + + # add EURO, set company-currency to EURO + (usd, euro) = self.prep_2nd_currency(company) + category = self.prep_category(cattype='in') + self.assertEqual(company.currency.rec_name, 'Euro') + + book, = Book.create([{ + 'name': 'Book 1', + 'btype': types.id, + 'company': company.id, + 'currency': usd.id, + 'number_sequ': self.prep_sequence().id, + 'start_date': date(2022, 5, 1), + 'lines': [('create', [{ + 'date': date(2022, 5, 5), + 'description': 'Amount in USD', + 'bookingtype': 'in', + 'category': category.id, + 'amount': Decimal('10.0'), + }])], + }]) + + with Transaction().set_context({ + 'date': date(2022, 5, 5), + }): + self.assertEqual(book.rec_name, 'Book 1 | 10.00 usd | Open') + self.assertEqual(book.currency.rec_name, 'usd') + self.assertEqual(book.currency.rate, Decimal('1.05')) + self.assertEqual(book.company_currency.rec_name, 'Euro') + self.assertEqual(book.company_currency.rate, Decimal('1.0')) + + self.assertEqual(book.balance, Decimal('10.0')) + self.assertEqual(book.balance_ref, Decimal('9.52')) + + self.assertEqual(len(book.lines), 1) + self.assertEqual(book.lines[0].rec_name, + '05/05/2022|Rev|10.00 usd|Amount in USD [Cat1]') + + @with_transaction() + def test_book_create_2nd_currency_hierarchical(self): + """ create cashbook-hierarchy, in 2nd currency, + check balance-fields + """ + pool = Pool() + Book = pool.get('cashbook.book') + + types = self.prep_type() + company = self.prep_company() + + # add EURO, set company-currency to EURO + (usd, euro) = self.prep_2nd_currency(company) + category = self.prep_category(cattype='in') + self.assertEqual(company.currency.rec_name, 'Euro') + + book, = Book.create([{ + 'name': 'Book 1', + 'company': company.id, + 'currency': euro.id, + 'childs': [('create', [{ + 'name': 'Book 2', + 'btype': types.id, + 'company': company.id, + 'currency': usd.id, + 'number_sequ': self.prep_sequence().id, + 'start_date': date(2022, 5, 1), + 'lines': [('create', [{ + 'date': date(2022, 5, 5), + 'description': 'Amount in USD', + 'bookingtype': 'in', + 'category': category.id, + 'amount': Decimal('10.0'), + }])], + }])], + }]) + + with Transaction().set_context({ + 'date': date(2022, 5, 5), + }): + self.assertEqual(book.rec_name, 'Book 1') + self.assertEqual(book.currency.rec_name, 'Euro') + self.assertEqual(book.currency.rate, Decimal('1.0')) + self.assertEqual(book.company_currency, None) + self.assertEqual(book.balance, Decimal('9.52')) + self.assertEqual(book.balance_ref, None) + self.assertEqual(len(book.lines), 0) + self.assertEqual(len(book.childs), 1) + + self.assertEqual(book.childs[0].rec_name, 'Book 1/Book 2 | 10.00 usd | Open') + self.assertEqual(book.childs[0].currency.rec_name, 'usd') + self.assertEqual(book.childs[0].currency.rate, Decimal('1.05')) + self.assertEqual(book.childs[0].company_currency.rec_name, 'Euro') + self.assertEqual(book.childs[0].balance, Decimal('10.0')) + self.assertEqual(book.childs[0].balance_ref, Decimal('9.52')) + self.assertEqual(len(book.childs[0].lines), 1) + @with_transaction() def test_book_create_hierarchy(self): """ create cashbook, hierarchical diff --git a/tests/test_config.py b/tests/test_config.py index 368ad10..7ff9105 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -98,6 +98,13 @@ class ConfigTestCase(ModuleTestCase): 'rate': Decimal('1.05'), }]) + # delete unwanted rates + usd_1 = CurrencyRate.search([ + ('currency.id', '=', usd.id), + ('date', '!=', date(2022, 5, 2)), + ]) + CurrencyRate.delete(usd_1) + return (usd, euro) @with_transaction() diff --git a/view/book_form.xml b/view/book_form.xml index 24a27b5..7eec6b1 100644 --- a/view/book_form.xml +++ b/view/book_form.xml @@ -15,10 +15,14 @@ full copyright notices and license terms. --> +