diff --git a/__init__.py b/__init__.py index 161c360..ff6b8fc 100644 --- a/__init__.py +++ b/__init__.py @@ -10,6 +10,7 @@ from .identifier import Identifier from .cron import Cron from .onlinesource import OnlineSource from .update_wiz import UpdateSoureWizard +from .diagram import GraphDef, ChartPoint def register(): @@ -20,6 +21,10 @@ def register(): Identifier, Cron, module='investment', type_='model') + Pool.register( + GraphDef, + ChartPoint, + module='investment', type_='model', depends=['diagram']) Pool.register( UpdateSoureWizard, module='investment', type_='wizard') diff --git a/asset.py b/asset.py index 66869f9..8ce8bad 100644 --- a/asset.py +++ b/asset.py @@ -244,9 +244,10 @@ class Asset(ModelSQL, ModelView): return self.company.currency.id @classmethod - def get_percentage_sql(cls, name_lst): + def get_percentage_sql(cls, name_lst, select_date=True): """ get table for percentages and dates, generate adapted query + select_date: True = select newest date """ pool = Pool() Rate = pool.get('investment.rate') @@ -270,13 +271,17 @@ class Asset(ModelSQL, ModelView): condition=tab_asset.id==tab_rate_today.asset, ).select( tab_asset.id, + tab_rate_today.id.as_('id_rate'), tab_rate_today.date, tab_rate_today.rate, - distinct_on=[tab_asset.id], - order_by=[tab_asset.id, tab_rate_today.date.desc], - where=tab_rate_today.date <= query_date, ) + # limit to newest date until 'query_date' + if select_date == True: + query_today.distinct_on=[tab_asset.id] + query_today.order_by=[tab_asset.id, tab_rate_today.date.desc] + query_today.where=tab_rate_today.date <= query_date + # create join for requested fields, to minimize database-load query = query_today for name in name_lst: @@ -290,6 +295,7 @@ class Asset(ModelSQL, ModelView): # add select for requested fields to join select_lst = [ query_today.id, + query_today.id_rate, query_today.date, ] for name in name_lst: @@ -301,14 +307,14 @@ class Asset(ModelSQL, ModelView): else_ = None, ).as_(name)) - order_by_lst = [query_today.id] + order_by_lst = [query_today.id, query_today.id_rate] order_by_lst.extend([ rate_tab[name]['tab'].date.desc for name in name_lst ]) query = query.select( *select_lst, - distinct_on=[query_today.id], + distinct_on=[query_today.id, query_today.id_rate], order_by=order_by_lst, ) return query diff --git a/diagram.py b/diagram.py new file mode 100644 index 0000000..cfb2c37 --- /dev/null +++ b/diagram.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +# This file is part of the investment-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.transaction import Transaction +from trytond.pool import Pool, PoolMeta +from trytond.pyson import Eval +from sql.functions import Function +from datetime import timedelta +from decimal import Decimal + + +class Concat2(Function): + """ concat columns + """ + __slots__ = () + _function = 'concat' + +# end Concat2 + + +class GraphDef(metaclass=PoolMeta): + __name__ = 'diagram.graphdef' + + asset = fields.Many2One(string='Asset', + model_name='investment.asset', + states={ + 'invisible': Eval('dtype', '') != 'investment.asset', + 'required': Eval('dtype', '') == 'investment.asset', + }, depends=['dtype']) + + @classmethod + def _get_dtypes(cls): + """ return list of types + """ + l1 = super(GraphDef, cls)._get_dtypes() + l1.append('investment.asset') + return l1 + + def get_recname_value(self): + """ value by dtype + """ + if self.dtype == 'investment.asset': + return getattr(self.asset, 'rec_name', '-') + return super(GraphDef, self).get_recname_value() + + def get_field_key(self): + """ get to read value from json + """ + if self.dtype == 'investment.asset': + return 'asset%d' % self.asset.id + return super(GraphDef, self).get_field_key() + + def get_scaling_for_investment_asset(self): + """ get scaling for currency + """ + Rate = Pool().get('investment.rate') + + if self.scaling == 'fix': + return None + + if self.scaling == 'alldata': + query = [('asset.id', '=', self.asset.id)] + elif self.scaling == 'view': + query = [ + ('asset.id', '=', self.asset.id), + ('date', '>=', self.chart.used_start_date()), + ('date', '<=', self.chart.used_end_date()), + ] + elif self.scaling == 'six': + query = [ + ('asset.id', '=', self.asset.id), + ('date', '>=', self.chart.used_start_date() - timedelta(days=180)), + ('date', '<=', self.chart.used_end_date()), + ] + + min_rec = Rate.search(query, limit=1, order=[('rate', 'ASC')]) + max_rec = Rate.search(query, limit=1, order=[('rate', 'DESC')]) + min_val = min_rec[0].rate if len(min_rec) > 0 else None + max_val = max_rec[0].rate if len(max_rec) > 0 else None + + return self.compute_scaling_factor(min_val, max_val) + +# end GraphDef + + +class ChartPoint(metaclass=PoolMeta): + __name__ = 'diagram.point' + + @classmethod + def get_table_parts(cls): + """ return a list of tables to union, + table must contain the columns: + date, key, val + """ + pool = Pool() + Rate = pool.get('investment.rate') + tab_rate = Rate.__table__() + + tabparts = super(ChartPoint, cls).get_table_parts() + + # rate + tabparts.append(tab_rate.select( + tab_rate.date, + Concat2('asset', tab_rate.asset).as_('key'), + tab_rate.rate.as_('val'), + )) + return tabparts + +# end ChartPoint diff --git a/diagram.xml b/diagram.xml new file mode 100644 index 0000000..688d6aa --- /dev/null +++ b/diagram.xml @@ -0,0 +1,16 @@ + + + + + + + + diagram.graphdef + + graph_form + + + + diff --git a/locale/de.po b/locale/de.po index c8efa64..4498178 100644 --- a/locale/de.po +++ b/locale/de.po @@ -449,3 +449,15 @@ msgstr "Wertpapierkennnummer (WKN)" msgctxt "selection:product.identifier,type:" msgid "Stock market symbol" msgstr "Börsensymbol" + + +#################### +# diagram.graphdef # +#################### +msgctxt "view:diagram.graphdef:" +msgid "Asset" +msgstr "Vermögenswert" + +msgctxt "field:diagram.graphdef,asset:" +msgid "Asset" +msgstr "Vermögenswert" diff --git a/locale/en.po b/locale/en.po index 7c1ef65..3b4a074 100644 --- a/locale/en.po +++ b/locale/en.po @@ -406,3 +406,11 @@ msgctxt "selection:product.identifier,type:" msgid "National Securities Identifying Number (NSIN)" msgstr "National Securities Identifying Number (NSIN)" +msgctxt "selection:product.identifier,type:" +msgid "Stock market symbol" +msgstr "Stock market symbol" + +msgctxt "view:diagram.graphdef:" +msgid "Asset" +msgstr "Asset" + diff --git a/tests/test_asset.py b/tests/test_asset.py index 2e117ca..a6ad279 100644 --- a/tests/test_asset.py +++ b/tests/test_asset.py @@ -112,6 +112,176 @@ class AssetTestCase(ModuleTestCase): self.assertEqual(asset.rec_name, 'Product 1 - 2.4500 usd/Unit [05/15/2022]') self.assertEqual(Asset.search_count([('name', '=', 'Product 1')]), 1) + @with_transaction() + def test_asset_percentages_dateselect1(self): + """ create asset, add rates, check selection of + specific date - fixed date + """ + Asset = Pool().get('investment.asset') + cursor = Transaction().connection.cursor() + + company = self.prep_asset_company() + product = self.prep_asset_product( + name='Product 1', + description='some asset') + + asset1 = self.prep_asset_item( + company=company, + product = product) + self.assertEqual(asset1.rec_name, 'Product 1 - - usd/Unit [-]') + + Asset.write(*[ + [asset1], + { + 'rates': [('create', [{ + 'date': date(2022, 5, 15), + 'rate': Decimal('2.45'), + }, { + 'date': date(2022, 5, 16), + 'rate': Decimal('2.6'), + }, { + 'date': date(2022, 5, 12), + 'rate': Decimal('2.0'), + }, { + 'date': date(2022, 5, 3), + 'rate': Decimal('3.6'), + }])], + }, + ]) + self.assertEqual(asset1.rec_name, 'Product 1 - 2.6000 usd/Unit [05/16/2022]') + self.assertEqual(len(asset1.rates), 4) + self.assertEqual(asset1.rates[0].date, date(2022, 5, 16)) + self.assertEqual(asset1.rates[1].date, date(2022, 5, 15)) + self.assertEqual(asset1.rates[2].date, date(2022, 5, 12)) + self.assertEqual(asset1.rates[3].date, date(2022, 5, 3)) + + # query fixed date + tab_percent = Asset.get_percentage_sql(['day1'], select_date = False) + query = tab_percent.select( + tab_percent.id, + tab_percent.date, + tab_percent.day1, + where=(tab_percent.date==date(2022, 5, 16)) & \ + (tab_percent.id==asset1.id), + ) + cursor.execute(*query) + records = cursor.fetchall() + + # there should be one record, three colums + self.assertEqual(len(records), 1) + self.assertEqual(len(records[0]), 3) + self.assertEqual(records[0][0], asset1.id) + self.assertEqual(records[0][1], date(2022, 5, 16)) + self.assertEqual(records[0][2].quantize(Decimal('0.01')), Decimal('6.12')) + + @with_transaction() + def test_asset_percentages_dateselect2(self): + """ create asset, add rates, check selection of + specific date - date-column + """ + pool = Pool() + Asset = pool.get('investment.asset') + Rate = pool.get('investment.rate') + tab_rate = Rate.__table__() + cursor = Transaction().connection.cursor() + + company = self.prep_asset_company() + product = self.prep_asset_product( + name='Product 1', + description='some asset') + + asset1 = self.prep_asset_item( + company=company, + product = product) + asset2 = self.prep_asset_item( + company=company, + product = product) + + Asset.write(*[ + [asset1], + { + 'rates': [('create', [{ + 'date': date(2022, 5, 15), + 'rate': Decimal('2.45'), + }, { + 'date': date(2022, 5, 16), + 'rate': Decimal('2.6'), + }, { + 'date': date(2022, 5, 12), + 'rate': Decimal('2.0'), + }, { + 'date': date(2022, 5, 3), + 'rate': Decimal('3.6'), + }])], + }, + [asset2], + { + 'rates': [('create', [{ + 'date': date(2022, 5, 17), + 'rate': Decimal('1.5'), + }, { + 'date': date(2022, 5, 16), + 'rate': Decimal('2.0'), + }, { + 'date': date(2022, 5, 15), + 'rate': Decimal('2.5'), + }, { + 'date': date(2022, 5, 14), + 'rate': Decimal('3.0'), + }, { + 'date': date(2022, 5, 13), + 'rate': Decimal('3.5'), + }, { + 'date': date(2022, 5, 12), + 'rate': Decimal('4.0'), + }, { + 'date': date(2022, 5, 11), + 'rate': Decimal('4.5'), + }, ])], + }, + ]) + self.assertEqual(asset1.rec_name, 'Product 1 - 2.6000 usd/Unit [05/16/2022]') + self.assertEqual(len(asset1.rates), 4) + self.assertEqual(asset2.rec_name, 'Product 1 - 1.5000 usd/Unit [05/17/2022]') + self.assertEqual(len(asset2.rates), 7) + self.assertEqual(asset1.rates[0].date, date(2022, 5, 16)) + self.assertEqual(asset1.rates[1].date, date(2022, 5, 15)) + self.assertEqual(asset1.rates[2].date, date(2022, 5, 12)) + self.assertEqual(asset1.rates[3].date, date(2022, 5, 3)) + + # query date-column + tab_percent = Asset.get_percentage_sql(['day1'], select_date = False) + query = tab_rate.join(tab_percent, + condition=(tab_percent.id_rate == tab_rate.id) + ).select( + tab_percent.id, + tab_percent.date, + tab_percent.day1, + where=tab_percent.id==asset1.id, + order_by=[tab_percent.date.asc] + ) + cursor.execute(*query) + records = cursor.fetchall() + + # there should be 4x records, three colums + self.assertEqual(len(records), 4) + self.assertEqual(len(records[0]), 3) + self.assertEqual(records[0][0], asset1.id) + self.assertEqual(records[0][1], date(2022, 5, 3)) + self.assertEqual(records[0][2], None) + + self.assertEqual(records[1][0], asset1.id) + self.assertEqual(records[1][1], date(2022, 5, 12)) + self.assertEqual(records[1][2], None) + + self.assertEqual(records[2][0], asset1.id) + self.assertEqual(records[2][1], date(2022, 5, 15)) + self.assertEqual(records[2][2].quantize(Decimal('0.01')), Decimal('22.5')) + + self.assertEqual(records[3][0], asset1.id) + self.assertEqual(records[3][1], date(2022, 5, 16)) + self.assertEqual(records[3][2].quantize(Decimal('0.01')), Decimal('6.12')) + @with_transaction() def test_asset_percentages_daterange(self): """ create asset, add rates, check selection of diff --git a/tryton.cfg b/tryton.cfg index 368c266..2c36c05 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -6,6 +6,8 @@ depends: company currency product +extras_depends: + diagram xml: icon.xml message.xml @@ -15,5 +17,6 @@ xml: sources_def.xml update_wiz.xml rate.xml + diagram.xml menu.xml cron.xml diff --git a/view/graph_form.xml b/view/graph_form.xml new file mode 100644 index 0000000..8b4c00f --- /dev/null +++ b/view/graph_form.xml @@ -0,0 +1,14 @@ + + + + + + + + +