From d501bd1700d4a30ac4ee384512acb3ab8ef22c3b Mon Sep 17 00:00:00 2001 From: Frederik Jaeckel Date: Fri, 23 Dec 2022 19:15:20 +0100 Subject: [PATCH] book: list-view - farbige asset-zeilen, book: Felder diff-prozent/amount + tests --- book.py | 59 ++++++++++++++++++---- book.xml | 12 +++++ locale/de.po | 34 +++++++++---- locale/en.po | 18 +++---- tests/test_book.py | 120 ++++++++++++++++++++++++++++++++++++++++++++- view/book_form.xml | 11 ++++- view/book_list.xml | 14 ++++++ view/book_tree.xml | 12 +++++ 8 files changed, 249 insertions(+), 31 deletions(-) create mode 100644 view/book_list.xml create mode 100644 view/book_tree.xml diff --git a/book.py b/book.py index 4e41f28..14ac53d 100644 --- a/book.py +++ b/book.py @@ -4,9 +4,8 @@ # full copyright notices and license terms. from trytond.model import fields, SymbolMixin -from trytond.exceptions import UserError from trytond.pool import PoolMeta, Pool -from trytond.pyson import Eval, Or, Len +from trytond.pyson import Eval, Or, Len, Bool, If from trytond.modules.cashbook.book import STATES2, DEPENDS2 from trytond.transaction import Transaction from decimal import Decimal @@ -75,18 +74,44 @@ class Book(SymbolMixin, metaclass=PoolMeta): }, depends=['quantity_digits', 'feature']), 'get_asset_quantity') current_value = fields.Function(fields.Numeric(string='Value', + help='Valuation of the investment based on the current stock market price.', readonly=True, digits=(16, Eval('currency_digits', 2)), states={ 'invisible': Eval('feature', '') != 'asset', }, depends=['currency_digits', 'feature']), 'get_asset_quantity') current_value_ref = fields.Function(fields.Numeric(string='Value (Ref.)', + help='Valuation of the investment based on the current stock exchange price, converted into the company currency.', readonly=True, digits=(16, Eval('currency_digits', 2)), states={ - 'invisible': Eval('feature', '') != 'asset', - }, depends=['currency_digits', 'feature']), + 'invisible': Or( + Eval('feature', '') != 'asset', + ~Bool(Eval('company_currency', -1)), + ), + }, depends=['currency_digits', 'feature', 'company_currency']), 'get_asset_quantity') + # performance + diff_amount = fields.Function(fields.Numeric(string='Difference', + help='Difference between acquisition value and current value', + readonly=True, digits=(16, Eval('currency_digits', 2)), + depends=['currency_digits']), 'get_asset_quantity') + diff_percent = fields.Function(fields.Numeric(string='Percent', + help='percentage performance since acquisition', + readonly=True, digits=(16, Eval('currency_digits', 2)), + depends=['currency_digits']), 'get_asset_quantity') + + @classmethod + def view_attributes(cls): + return super(Book, cls).view_attributes() + [ + ('/tree', 'visual', + If(Eval('feature', '') == 'asset', + If(Eval('diff_percent', 0) < 0, 'danger', + If(Eval('diff_percent', 0) > 0, 'success', '') + ), '') + ), + ] + @fields.depends('asset', 'quantity_uom') def on_change_asset(self): """ get uom from asset @@ -115,6 +140,7 @@ class Book(SymbolMixin, metaclass=PoolMeta): tab_cur = Currency.__table__() tab_asset = Asset.__table__() (tab_rate, tab2) = Asset.get_rate_data_sql() + (tab_balance, tab2) = CBook.get_balance_of_cashbook_sql() cursor = Transaction().connection.cursor() context = Transaction().context @@ -128,6 +154,9 @@ class Book(SymbolMixin, metaclass=PoolMeta): condition=tab_book.currency==tab_cur.id, ).join(tab_asset, condition=tab_book.asset==tab_asset.id, + ).join(tab_balance, + condition=tab_book.id==tab_balance.cashbook, + type_ = 'LEFT OUTER', ).join(tab_rate, condition=tab_book.asset==tab_rate.id, type_ = 'LEFT OUTER', @@ -144,9 +173,11 @@ class Book(SymbolMixin, metaclass=PoolMeta): tab_asset.uom, # 6 tab_book.quantity_uom, # 7 tab_asset.currency.as_('asset_currency'), #8 + Coalesce(tab_balance.balance, Decimal('0.0')).as_('balance'), #9 group_by=[tab_book.id, tab_rate.rate, tab_book.currency, tab_cur.digits, tab_asset.uom, - tab_book.quantity_uom, tab_asset.currency], + tab_book.quantity_uom, tab_asset.currency, + tab_balance.balance], ) cursor.execute(*query) records = cursor.fetchall() @@ -160,19 +191,27 @@ class Book(SymbolMixin, metaclass=PoolMeta): Uom.compute_qty(Uom(record[6]), 1.0, Uom(record[7]), round=False) ) + current_value = Currency.compute( + record[8], + record[3] * record[1] / uom_factor, + record[4] + ) + values = { 'quantity': record[1], 'quantity_all': record[2], - 'current_value': Currency.compute( - record[8], - record[3] * record[1] / uom_factor, - record[4] - ), + 'current_value': current_value, 'current_value_ref': Currency.compute( record[8], record[3] * record[1] / uom_factor, company_currency if company_currency is not None else record[8], ), + 'diff_amount': current_value - record[9], + 'diff_percent': ( + Decimal('100.0') * current_value / \ + record[9] - Decimal('100.0') + ).quantize(Decimal(str(1/10**record[5]))) \ + if record[9] != Decimal('0.0') else None, } for name in names: diff --git a/book.xml b/book.xml index 12806b1..2c922eb 100644 --- a/book.xml +++ b/book.xml @@ -11,5 +11,17 @@ full copyright notices and license terms. --> book_form + + cashbook.book + + book_list + + + + cashbook.book + + book_tree + + diff --git a/locale/de.po b/locale/de.po index 7d9ce77..6b49abe 100644 --- a/locale/de.po +++ b/locale/de.po @@ -23,8 +23,8 @@ msgid "Quantity" msgstr "Anzahl" msgctxt "view:cashbook.book:" -msgid "Current value of the asset" -msgstr "aktueller Wert des Vermögenswertes" +msgid "Current valuation of the investment" +msgstr "aktuelle Bewertung der Vermögensanlage" msgctxt "field:cashbook.book,asset:" msgid "Asset" @@ -71,16 +71,32 @@ msgid "Value" msgstr "Wert" msgctxt "help:cashbook.book,current_value:" -msgid "Current Value in cashbook currency" -msgstr "aktueller Wert in der Kassenbuchwährung" +msgid "Valuation of the investment based on the current stock market price." +msgstr "Bewertung der Vermögensanlage anhand des aktuellen Börsenkurses." msgctxt "field:cashbook.book,current_value_ref:" -msgid "Value (Ref)" -msgstr "Wert (Ref)" +msgid "Value (Ref.)" +msgstr "Wert (Ref.)" -msgctxt "help:cashbook.book,current_value:" -msgid "Current Value in company currency" -msgstr "aktueller Wert in der Unternehmenswährung" +msgctxt "help:cashbook.book,current_value_ref:" +msgid "Valuation of the investment based on the current stock exchange price, converted into the company currency." +msgstr "Bewertung der Vermögensanlage anhand des aktuellen Börsenkurses, umgerechnet in die Unternehmenswährung." + +msgctxt "field:cashbook.book,diff_amount:" +msgid "Difference" +msgstr "Differenz" + +msgctxt "help:cashbook.book,diff_amount:" +msgid "Difference between acquisition value and current value" +msgstr "Differenz zwischen Anschaffungswert und aktuellem Wert" + +msgctxt "field:cashbook.book,diff_percent:" +msgid "Percent" +msgstr "Prozent" + +msgctxt "help:cashbook.book,diff_percent:" +msgid "percentage performance since acquisition" +msgstr "prozentuale Wertentwicklung seit Anschaffung" ################# diff --git a/locale/en.po b/locale/en.po index 210e1a7..b9f47a1 100644 --- a/locale/en.po +++ b/locale/en.po @@ -15,8 +15,8 @@ msgid "Quantity" msgstr "Quantity" msgctxt "view:cashbook.book:" -msgid "Value of the asset" -msgstr "Value of the asset" +msgid "Current value of the asset" +msgstr "Current value of the asset" msgctxt "field:cashbook.book,asset:" msgid "Asset" @@ -63,16 +63,16 @@ msgid "Value" msgstr "Value" msgctxt "help:cashbook.book,current_value:" -msgid "Current Value in cashbook currency" -msgstr "Current Value in cashbook currency" +msgid "Valuation of the investment based on the current stock market price." +msgstr "Valuation of the investment based on the current stock market price." msgctxt "field:cashbook.book,current_value_ref:" -msgid "Value (Ref)" -msgstr "Value (Ref)" +msgid "Value (Ref.)" +msgstr "Value (Ref.)" -msgctxt "help:cashbook.book,current_value:" -msgid "Current Value in company currency" -msgstr "Current Value in company currency" +msgctxt "help:cashbook.book,current_value_ref:" +msgid "Valuation of the investment based on the current stock exchange price, converted into the company currency." +msgstr "Valuation of the investment based on the current stock exchange price, converted into the company currency." msgctxt "field:cashbook.line,quantity_digits:" msgid "Digits" diff --git a/tests/test_book.py b/tests/test_book.py index fcc3b01..fb55abb 100644 --- a/tests/test_book.py +++ b/tests/test_book.py @@ -257,11 +257,127 @@ class CbInvTestCase(CashbookTestCase, InvestmentTestCase): self.assertEqual(book.asset.rate, Decimal('1750.0')) # usd self.assertEqual(book2.quantity, Decimal('20.0')) self.assertEqual(book2.quantity_all, Decimal('20.0')) - # usd --> eur: 1750 / 1.05 = 1666.666 - # 1 ounce --> 20 gram: 1666.666 * 20 / 28.3495 = 1175.7996 + # usd --> eur: 1750 US$ / 1.05 = 1666.666 € + # 1 ounce --> 20 gram: 1666.666 € * 20 / 28.3495 = 1175.7996 € # bette we use 'Troy Ounce': 1 oz.tr. = 31.1034768 gram self.assertEqual(book2.current_value, Decimal('1175.80')) self.assertEqual(book2.current_value_ref, Decimal('1175.80')) + self.assertEqual(book2.diff_amount, Decimal('-74.20')) + self.assertEqual(book2.diff_percent, Decimal('-5.94')) + + @with_transaction() + def test_assetbook_check_uom_and_currency_convert2(self): + """ asset in US$/Ounce, cashbook in CHF/Gram, + company-currency EUR + """ + pool = Pool() + Book = pool.get('cashbook.book') + BType = pool.get('cashbook.type') + Asset = pool.get('investment.asset') + ProdTempl = pool.get('product.template') + Uom = pool.get('product.uom') + Currency = pool.get('currency.currency') + + types = self.prep_type() + BType.write(*[ + [types], + { + 'feature': 'asset', + }]) + category = self.prep_category(cattype='in') + + company = self.prep_company() + party = self.prep_party() + asset = self.prep_asset_item( + company=company, + product = self.prep_asset_product(name='Product 1')) + + # set product to ounce + ounce, = Uom.search([('symbol', '=', 'oz')]) + gram, = Uom.search([('symbol', '=', 'g')]) + + ProdTempl.write(*[ + [asset.product.template], + { + 'default_uom': ounce.id, + 'name': 'Aurum', + }]) + + Asset.write(*[ + [asset], + { + 'uom': ounce.id, + 'rates': [('create', [{ + 'date': date(2022, 5, 1), + 'rate': Decimal('1750.0'), + }, ])], + }]) + self.assertEqual(asset.rec_name, 'Aurum | 1,750.0000 usd/oz | 05/01/2022') + + (usd, euro) = self.prep_2nd_currency(company) + self.assertEqual(company.currency.rec_name, 'Euro') + self.assertEqual(asset.symbol, 'usd/oz') + chf, = Currency.create([{ + 'name': 'Swiss Franc', + 'code': 'CHF', + 'numeric_code': '756', + 'symbol': 'CHF', + 'rounding': Decimal('0.01'), + 'digits': 2, + 'rates': [('create', [{ + 'date': date(2022, 5, 1), + 'rate': Decimal('0.95'), + }])], + }]) + + book, = Book.create([{ + 'start_date': date(2022, 4, 1), + 'name': 'Aurum-Storage', + 'btype': types.id, + 'company': company.id, + 'currency': chf.id, + 'number_sequ': self.prep_sequence().id, + 'asset': asset.id, + 'quantity_uom': gram.id, + 'quantity_digits': 3, + 'lines': [('create', [{ + 'date': date(2022, 5, 1), + 'description': 'store some metal', + 'category': category.id, + 'bookingtype': 'in', + 'amount': Decimal('1250.0'), + 'party': party.id, + 'quantity': Decimal('20.0'), + }], + )], + }]) + + self.assertEqual(book.rec_name, 'Aurum-Storage | 1,250.00 CHF | Open') + self.assertEqual(book.balance_all, Decimal('1250.0')) + self.assertEqual(len(book.lines), 1) + + self.assertEqual(book.lines[0].amount, Decimal('1250.0')) + self.assertEqual(book.lines[0].quantity, Decimal('20.0')) + self.assertEqual(book.lines[0].quantity_uom.symbol, 'g') + + self.assertEqual(book.symbol, 'CHF/g') + self.assertEqual(book.asset.rec_name, 'Aurum | 1,750.0000 usd/oz | 05/01/2022') + + # check quantities at cashbook + with Transaction().set_context({ + 'qdate': date(2022, 5, 1), + 'company': company.id, + }): + book2, = Book.browse([book]) + self.assertEqual(book.asset.rate, Decimal('1750.0')) # usd + self.assertEqual(book2.quantity, Decimal('20.0')) + self.assertEqual(book2.quantity_all, Decimal('20.0')) + # usd --> chf: 1750 US$ * 0.95 / 1.05 = 1583.333 € + # 1 ounce --> 20 gram: 1583.333 CHF * 20 / 28.3495 = 1117.0097 CHF + self.assertEqual(book2.current_value, Decimal('1117.01')) # CHF + self.assertEqual(book2.current_value_ref, Decimal('1175.80')) # EUR + self.assertEqual(book2.diff_amount, Decimal('-132.99')) + self.assertEqual(book2.diff_percent, Decimal('-10.64')) @with_transaction() def test_assetbook_book_uom(self): diff --git a/view/book_form.xml b/view/book_form.xml index 4b5a9e4..6d1115e 100644 --- a/view/book_form.xml +++ b/view/book_form.xml @@ -12,11 +12,20 @@ full copyright notices and license terms. -->