Compare commits

..

89 commits

Author SHA1 Message Date
Frederik Jaeckel
b9bcfc1f29 Version 6.0.23 2023-01-21 18:57:51 +01:00
Frederik Jaeckel
c6c0cb4ce4 removed unused imports 2023-01-13 13:48:50 +01:00
Frederik Jaeckel
73dac7b33c OnlineSource: Query method prepared for external extension 2023-01-13 13:08:52 +01:00
Frederik Jaeckel
4bb7641959 Etikett ver 6.0.22 zum Änderungssatz 0c434d333c3b hinzugefügt 2023-01-10 19:56:08 +01:00
Frederik Jaeckel
7087c151ab Version 6.0.22 2023-01-10 19:55:57 +01:00
Frederik Jaeckel
89f19ceae5 remove caching, optimize rate/query 2023-01-10 19:54:32 +01:00
Frederik Jaeckel
5eec999d41 asset: cachezeit verkürzt 2023-01-09 22:21:03 +01:00
Frederik Jaeckel
2fb2bef8b4 Etikett ver 6.0.21 zum Änderungssatz fb85e01774bb hinzugefügt 2023-01-08 21:30:39 +01:00
Frederik Jaeckel
03d0ce0a5b Version 6.0.21 2023-01-08 21:30:29 +01:00
Frederik Jaeckel
c18b07def1 caching für change_day1/month1/... 2023-01-08 20:56:27 +01:00
Frederik Jaeckel
a4242421f0 Etikett ver 6.0.20 zum Änderungssatz 49cc6bde6e18 hinzugefügt 2023-01-07 16:28:00 +01:00
Frederik Jaeckel
c9330c7195 Version 6.0.20 2023-01-07 16:27:52 +01:00
Frederik Jaeckel
25fd69be0c asset: abfragezeit für prozente optimiert 2023-01-07 16:26:22 +01:00
Frederik Jaeckel
9064ac2980 Etikett ver 6.0.19 zum Änderungssatz 5b6929d467a8 hinzugefügt 2023-01-05 22:27:44 +01:00
Frederik Jaeckel
23238b8419 Version 6.0.19 2023-01-05 22:27:34 +01:00
Frederik Jaeckel
11e9134dc9 rate: optimize for speed 2023-01-05 22:25:16 +01:00
Frederik Jaeckel
9d461b6d76 Etikett ver 6.0.18 zum Änderungssatz e413671670d3 hinzugefügt 2023-01-04 20:23:26 +01:00
Frederik Jaeckel
06fdbace50 Version 6.0.18 2023-01-04 20:23:18 +01:00
Frederik Jaeckel
553aa80acd asset: Feld 'change_symbol' für Prozenzzeichen + test 2023-01-04 20:20:27 +01:00
Frederik Jaeckel
c9a6df2371 asset: spalte 'einheit' entfällt, Spalte 'Kurs' hat nun die Einheit 2023-01-04 17:12:46 +01:00
Frederik Jaeckel
92cd89ef2e quellen korrigiert 2022-12-24 12:38:10 +01:00
Frederik Jaeckel
4dde704e57 setup - read_file 2022-12-20 12:31:36 +01:00
Frederik Jaeckel
0e8dc38abb Etikett ver 6.0.17 zum Änderungssatz ac30badd3d0d hinzugefügt 2022-12-19 21:58:29 +01:00
Frederik Jaeckel
f9b4f12055 Version 6.0.17 2022-12-19 21:58:18 +01:00
Frederik Jaeckel
76ea68489c test für wizard 2022-12-19 21:53:00 +01:00
Frederik Jaeckel
ed6dae7d59 import wizard 2022-12-19 21:13:32 +01:00
Frederik Jaeckel
d76951d2f2 Etikett ver 6.0.16 zum Änderungssatz db4466e585ae hinzugefügt 2022-12-18 13:42:04 +01:00
Frederik Jaeckel
2c38ddd357 Version 6.0.16 2022-12-18 13:41:49 +01:00
Frederik Jaeckel
f5b9929c51 onlinesource: sortierung, neues datumsformat 'dd.mm.yy',
neue quellen: finanzen.net/Stuttgard, s-broker
2022-12-18 13:40:13 +01:00
Frederik Jaeckel
46a19fe4c4 Etikett ver 6.0.15 zum Änderungssatz c51c29a66b41 hinzugefügt 2022-12-16 21:47:53 +01:00
Frederik Jaeckel
f74ee23a76 Version 6.0.15 2022-12-16 21:47:42 +01:00
Frederik Jaeckel
2c750b2253 asset: rekursion behoben, erlaubt aktion nach update 2022-12-16 14:17:28 +01:00
Frederik Jaeckel
e703c759a3 updates an asset nach online-aktion 2022-12-16 13:37:36 +01:00
Frederik Jaeckel
36e6fb52d5 Etikett ver 6.0.14 zum Änderungssatz 7403bff0289a hinzugefügt 2022-12-09 21:41:09 +01:00
Frederik Jaeckel
b8648d629b Version 6.0.14 2022-12-09 21:40:48 +01:00
Frederik Jaeckel
7dd7c9a140 asset: Feld 'symbol' in übersetzter form 2022-12-09 09:43:30 +01:00
Frederik Jaeckel
813a794db4 Etikett ver 6.0.13 zum Änderungssatz d217c90a57be hinzugefügt 2022-12-06 08:55:45 +01:00
Frederik Jaeckel
2977e5187f Version 6.0.13 2022-12-06 08:55:34 +01:00
Frederik Jaeckel
77c287fc90 asset: prozentwerte mit einheit im form 2022-12-06 08:53:40 +01:00
Frederik Jaeckel
c73584af32 asset: currency_digits limit --> 6 2022-12-05 22:34:51 +01:00
Frederik Jaeckel
6b3b44e9af asset: bereichsgrenzen für currency_digits, farbe rot für tagesnegativ 2022-12-05 21:02:17 +01:00
Frederik Jaeckel
331c62a3f6 Etikett ver 6.0.12 zum Änderungssatz 2135106770d7 hinzugefügt 2022-12-05 09:51:50 +01:00
Frederik Jaeckel
693dc175f1 Version 6.0.12 2022-12-05 09:51:43 +01:00
Frederik Jaeckel
5b6883cdde import-script: add field-delimiter 2022-12-05 09:49:59 +01:00
Frederik Jaeckel
6311d0e79e Etikett ver 6.0.11 zum Änderungssatz 51423a72fb3e hinzugefügt 2022-12-03 21:19:12 +01:00
Frederik Jaeckel
b531d51a7d Version 6.0.11 2022-12-03 21:18:59 +01:00
Frederik Jaeckel
08ca00a6b9 symbol fix 2022-12-03 00:08:55 +01:00
Frederik Jaeckel
3cc92ccecb felder symbol, name, product_uom optimiert,
felder company_currency entfernt
2022-12-02 23:29:01 +01:00
Frederik Jaeckel
c3d323714f icon 2022-12-02 14:58:54 +01:00
Frederik Jaeckel
53dfb14254 asset: liste mit reiter für aktuell/inaktiv/alle 2022-12-02 12:56:14 +01:00
Frederik Jaeckel
4ab79e4200 Etikett ver 6.0.10 zum Änderungssatz f00c0cc3b914 hinzugefügt 2022-12-01 16:57:29 +01:00
Frederik Jaeckel
528a6e5a86 Version 6.0.10 2022-12-01 16:57:21 +01:00
Frederik Jaeckel
d88703f117 asset: rec_name - einheit als währung/menge 2022-12-01 16:55:27 +01:00
Frederik Jaeckel
6241601fa7 asset: einheit als währung/menge 2022-12-01 16:30:50 +01:00
Frederik Jaeckel
0fe7df4b61 icons 2022-11-30 21:50:31 +01:00
Frederik Jaeckel
ebca4642fb Etikett ver 6.0.9 zum Änderungssatz 19c2e25edee8 hinzugefügt 2022-11-30 13:59:51 +01:00
Frederik Jaeckel
37aece3c65 Etikett ver 6.0.9 gelöscht 2022-11-30 13:59:41 +01:00
Frederik Jaeckel
5c588abe96 fix imports 2022-11-30 13:59:20 +01:00
Frederik Jaeckel
c6f2661f9b setup.py 2022-11-30 13:35:21 +01:00
Frederik Jaeckel
bda4f66b47 Etikett ver 6.0.9 zum Änderungssatz 771ac3e2f632 hinzugefügt 2022-11-30 13:33:33 +01:00
Frederik Jaeckel
d097395825 Version 6.0.9 2022-11-30 13:33:24 +01:00
Frederik Jaeckel
f1b00e376b importscript für historische kurse 2022-11-30 13:32:16 +01:00
Frederik Jaeckel
f462b11758 Etikett ver 6.0.8 zum Änderungssatz 1eeb956c7547 hinzugefügt 2022-11-29 21:56:55 +01:00
Frederik Jaeckel
d431a422e8 Version 6.0.8 2022-11-29 21:56:48 +01:00
Frederik Jaeckel
b8495231e5 asset: online-quelle als liste 2022-11-29 21:54:27 +01:00
Frederik Jaeckel
8760fd4aab Etikett ver 6.0.7 zum Änderungssatz 71846ed7e4ce hinzugefügt 2022-11-28 23:20:16 +01:00
Frederik Jaeckel
aaa143f373 Version 6.0.7 2022-11-28 23:20:07 +01:00
Frederik Jaeckel
99e18094aa diagram: interpolator ergänzt,
asset: berechnung 'nextupdate' korrigiert
2022-11-28 23:15:28 +01:00
Frederik Jaeckel
d1421403b1 asset: sortierung - neueste nach oben, farben - älter als 5 tage = gedimmt 2022-11-28 15:44:07 +01:00
Frederik Jaeckel
475457d202 Etikett ver 6.0.6 zum Änderungssatz 2ffa138f55f2 hinzugefügt 2022-11-26 22:51:13 +01:00
Frederik Jaeckel
c1cc1acb5f Version 6.0.7 2022-11-26 22:51:04 +01:00
Frederik Jaeckel
7bb3e4f929 asset:tabellenzugriff optimiert,
diagram: darstellung in diagramm ergänzt
2022-11-26 22:42:02 +01:00
Frederik Jaeckel
047b6980e4 Etikett ver 6.0.5 zum Änderungssatz 6e85642a2e97 hinzugefügt 2022-11-25 22:50:14 +01:00
Frederik Jaeckel
9d88cf1067 Version 6.0.5 2022-11-25 22:50:03 +01:00
Frederik Jaeckel
620c42fc62 asset: suche in name, sortiert nach name, sortierer für wkn, isin, symbl 2022-11-25 22:46:32 +01:00
Frederik Jaeckel
6311dce3d1 asset: spalten tag/monat/3monate... ok + test 2022-11-25 21:55:43 +01:00
Frederik Jaeckel
64b8383096 abfrage der prozentwerte für tag/1 monat, 3 monate, 6 monate, 12 monate
test muß noch
2022-11-25 15:28:49 +01:00
Frederik Jaeckel
a99d11a4a0 Etikett ver 6.0.4 zum Änderungssatz 8e3f0b006892 hinzugefügt 2022-11-25 11:02:13 +01:00
Frederik Jaeckel
4ae5601c04 Version 6.0.4 2022-11-25 11:01:55 +01:00
Frederik Jaeckel
31c76dfb48 asset: abfrage updatezeitpunkt optimiert, anzeige der vortagsprozente, farbe für zeilen 2022-11-25 11:00:03 +01:00
Frederik Jaeckel
57cb06d60e prozentwerte begonnen 2022-11-24 23:17:44 +01:00
Frederik Jaeckel
5587bfea3a Etikett ver 6.0.3 zum Änderungssatz 0a2f82baca7e hinzugefügt 2022-11-23 22:23:49 +01:00
Frederik Jaeckel
f919d9e290 Version 6.0.3 2022-11-23 22:23:42 +01:00
Frederik Jaeckel
e8614b1242 bug in updtneeded-suche, online-quellen erweitert 2022-11-23 22:22:22 +01:00
Frederik Jaeckel
a71bc0a79a Etikett ver 6.0.2 zum Änderungssatz 76feab691fd9 hinzugefügt 2022-11-23 10:45:38 +01:00
Frederik Jaeckel
f1d9b3b1dd Version 6.0.2 2022-11-23 10:45:28 +01:00
Frederik Jaeckel
4947b495c2 asset: feld 'Datum' + 'Name' neu, rec_name optimiert, Test korrigiert 2022-11-23 10:44:09 +01:00
Frederik Jaeckel
aeb949cc20 Etikett ver 6.0.1 zum Änderungssatz e16a79cd456e hinzugefügt 2022-11-22 22:47:03 +01:00
Frederik Jaeckel
916d73ef12 Version 6.0.1 2022-11-22 22:45:15 +01:00
30 changed files with 1606 additions and 1797 deletions

View file

@ -9,19 +9,111 @@ pip install mds-investment
Requires
========
- Tryton 7.0
How to
======
Store values such as stocks, funds and commodities in Tryton.
Watch the performance. The Tryton module periodically loads
quotes of values from one or more price sources.
You can define the course sources yourself.
- Tryton 6.0
Changes
=======
*7.0.0 - 01.12.2023*
*6.0.23 - 21.01.2023*
- compatibility to Tryton 7.0
- updt: online-source optimized for external extension
*6.0.22 - 10.01.2023*
- updt: optimze rate, remove caching
*6.0.21 - 08.01.2023*
- add: caching for percentual changes
*6.0.20 - 07.01.2023*
- updt: asset - percent-values optimize for speed
*6.0.19 - 05.01.2023*
- updt: rate - optimize for speed
*6.0.18 - 04.01.2023*
- add: units to lists
- updt: online-sources
*6.0.17 - 19.12.2022*
- add: import wizard
*6.0.16 - 18.12.2022*
- add: onlinesource - sorting, sources
*6.0.15 - 16.12.2022*
- fix: recoursion
- add: enable after-update actions
*6.0.14 - 09.12.2022*
- updt: translated symbol of asset-currency
*6.0.13 - 06.12.2022*
- updt: asset-form - units for percent-values, asset-list - day-negative in red-color
*6.0.12 - 05.12.2022*
- import-script - add field-delimter to param-list
*6.0.11 - 03.12.2022*
- add: tabs for asset-list, new icons, queries optimized
*6.0.10 - 01.12.2022*
- updt: optimized asset-list, unit as symbol
*6.0.9 - 30.11.2022*
- add: script to import historical rates
*6.0.8 - 29.11.2022*
- add: online-source as list to allow multiple sources
for asset
*6.0.7 - 28.11.2022*
- fix: add diagram-interpolate
- fix: corrected nextupdate
*6.0.6 - 26.11.2022*
- add: diagram-display
*6.0.5 - 25.11.2022*
- add: sorter for name/nsin/isin/symbol
- add: columns percentage by day/month/3month/6month/12month
- fix: online-updater
*6.0.4 - 25.11.2022*
- add: assets - colors for percentual success/loss
- updt: optimize timestamp for next online-update
*6.0.3 - 23.11.2022*
- fix: bug in searcher
- add: online-sources
*6.0.2 - 23.11.2022*
- asset: add field 'date', optimized 'rec_name'
*6.0.1 - 22.11.2022*
- works
*6.0.0 - 09.11.2022*
- init

View file

@ -32,3 +32,4 @@ def register():
UpdateSoureWizard,
ImportWizard,
module='investment', type_='wizard')

546
asset.py
View file

@ -3,7 +3,7 @@
# 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, SymbolMixin, Index
from trytond.model import ModelView, ModelSQL, fields, SymbolMixin
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.pyson import Eval, Bool, If, Date
@ -15,7 +15,6 @@ from sql.functions import CurrentDate, CurrentTimestamp, Round, Extract
from sql.conditionals import Case, Coalesce, NullIf
from sql import Literal
from .diagram import Concat2
from .const import DEF_NONE
digits_percent = 2
@ -30,116 +29,94 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
'Asset'
__name__ = 'investment.asset'
name = fields.Function(fields.Char(
string='Name', readonly=True),
name = fields.Function(fields.Char(string='Name', readonly=True),
'get_name_symbol', searcher='search_rec_name')
company = fields.Many2One(
string='Company', model_name='company.company',
company = fields.Many2One(string='Company', model_name='company.company',
required=True, ondelete="RESTRICT")
product = fields.Many2One(
string='Product', required=True, model_name='product.product',
ondelete='RESTRICT', domain=[('type', '=', 'assets')])
product_uom = fields.Function(fields.Many2One(
string='UOM Category', readonly=True,
model_name='product.uom.category',
product = fields.Many2One(string='Product', required=True,
model_name='product.product', ondelete='RESTRICT',
domain=[('type', '=', 'assets')])
product_uom = fields.Function(fields.Many2One(string='UOM Category',
readonly=True, model_name='product.uom.category',
help='Category of unit on the product.'), 'get_name_symbol')
uom = fields.Many2One(
string='UOM', required=True, model_name='product.uom',
ondelete='RESTRICT',
states={'readonly': ~Bool(Eval('product'))},
domain=[('category', '=', Eval('product_uom'))],
depends=['product_uom', 'product'])
symbol = fields.Function(fields.Char(
string='UOM', readonly=True), 'get_name_symbol',
searcher='search_uom_symbol')
asset_symbol = fields.Function(fields.Many2One(
string='Symbol', readonly=True, model_name='investment.asset'),
uom = fields.Many2One(string='UOM', required=True,
model_name='product.uom', ondelete='RESTRICT',
states={
'readonly': ~Bool(Eval('product')),
},
domain=[
('category', '=', Eval('product_uom')),
], depends=['product_uom', 'product'])
symbol = fields.Function(fields.Char(string='UOM', readonly=True),
'get_name_symbol', searcher='search_uom_symbol')
asset_symbol = fields.Function(fields.Many2One(string='Symbol',
readonly=True, model_name='investment.asset'),
'get_name_symbol')
rates = fields.One2Many(
string='Rates', field='asset', model_name='investment.rate')
rate = fields.Function(fields.Numeric(
string='Current Rate', readonly=True,
digits=(16, Eval('currency_digits', 4)), depends=['currency_digits']),
rates = fields.One2Many(string='Rates', field='asset',
model_name='investment.rate')
rate = fields.Function(fields.Numeric(string='Current Rate',
readonly=True, digits=(16, Eval('currency_digits', 4)),
depends=['currency_digits']),
'get_rate_data', searcher='search_rate')
date = fields.Function(fields.Date(
string='Date', readonly=True, help='Date of current rate'),
date = fields.Function(fields.Date(string='Date', readonly=True,
help='Date of current rate'),
'get_rate_data', searcher='search_date')
currency = fields.Many2One(
string='Currency', required=True,
model_name='currency.currency', ondelete='RESTRICT')
currency_digits = fields.Integer(
string='Digits', required=True,
domain=[('currency_digits', '>=', 0), ('currency_digits', '<=', 6)])
currency = fields.Many2One(string='Currency', select=True,
required=True, model_name='currency.currency', ondelete='RESTRICT')
currency_digits = fields.Integer(string='Digits', required=True,
domain=[
('currency_digits', '>=', 0),
('currency_digits', '<=', 6)])
wkn = fields.Function(fields.Char(
string='NSIN', readonly=True,
wkn = fields.Function(fields.Char(string='NSIN', readonly=True,
help='National Securities Identifying Number'),
'get_identifiers', searcher='search_identifier')
isin = fields.Function(fields.Char(
string='ISIN', readonly=True,
isin = fields.Function(fields.Char(string='ISIN', readonly=True,
help='International Securities Identification Number'),
'get_identifiers', searcher='search_identifier')
secsymb = fields.Function(fields.Char(
string='Symbol', readonly=True,
secsymb = fields.Function(fields.Char(string='Symbol', readonly=True,
help='Stock market symbol'),
'get_identifiers', searcher='search_identifier')
updtsources = fields.Many2Many(
string='Update Sources',
help='Select sources for the course update. The course sources ' +
'are tried until a valid value has been read.',
updtsources = fields.Many2Many(string='Update Sources',
help='Select sources for the course update. The course sources are tried until a valid value has been read.',
relation_name='investment.asset_source_rel',
origin='asset', target='source')
updturl = fields.Char(
string='URL',
help='URL for data retrieval.',
updtdays = fields.Selection(string='Select days', required=True,
selection=sel_updtdays)
updttime = fields.Time(string='Time',
states={
'invisible': ~Eval('updturl_enable', False),
'required': Eval('updturl_enable', False),
}, depends=['updturl_enable'])
updturl_enable = fields.Function(fields.Boolean(
string='URL required', readonly=True,
states={'invisible': True}),
'on_change_with_updturl_enable')
updtdays = fields.Selection(
string='Select days', required=True, selection=sel_updtdays)
updttime = fields.Time(
string='Time',
states={'readonly': ~Bool(Eval('updtsources'))},
depends=['updtsources'])
nextupdate = fields.Function(fields.DateTime(
string='Next Update', readonly=True),
'readonly': ~Bool(Eval('updtsources')),
}, depends=['updtsources'])
nextupdate = fields.Function(fields.DateTime(string='Next Update',
readonly=True),
'get_nextupdates', searcher='search_nextupdate')
# percentage change
change_day1 = fields.Function(fields.Numeric(
string='Previous Day', readonly=True,
digits=(16, digits_percent)),
change_day1 = fields.Function(fields.Numeric(string='Previous Day',
help='percentage change in value compared to the previous day',
readonly=True, digits=(16,digits_percent)),
'get_percentage_change', searcher='search_percentage')
change_month1 = fields.Function(fields.Numeric(
string='1 Month', readonly=True,
change_month1 = fields.Function(fields.Numeric(string='1 Month',
help='percentage change in value compared to last month',
digits=(16, digits_percent)),
readonly=True, digits=(16,digits_percent)),
'get_percentage_change', searcher='search_percentage')
change_month3 = fields.Function(fields.Numeric(
string='3 Months',
change_month3 = fields.Function(fields.Numeric(string='3 Months',
help='percentage change in value during 3 months',
digits=(16, digits_percent)),
readonly=True, digits=(16,digits_percent)),
'get_percentage_change', searcher='search_percentage')
change_month6 = fields.Function(fields.Numeric(
string='6 Months', readonly=True,
change_month6 = fields.Function(fields.Numeric(string='6 Months',
help='percentage change in value during 6 months',
digits=(16, digits_percent)),
readonly=True, digits=(16,digits_percent)),
'get_percentage_change', searcher='search_percentage')
change_month12 = fields.Function(fields.Numeric(
string='1 Year', readonly=True,
change_month12 = fields.Function(fields.Numeric(string='1 Year',
help='percentage change in value during 1 year',
digits=(16, digits_percent)),
readonly=True, digits=(16,digits_percent)),
'get_percentage_change', searcher='search_percentage')
change_symbol = fields.Function(fields.Many2One(
string='Symbol', readonly=True, model_name='investment.rate'),
change_symbol = fields.Function(fields.Many2One(string='Symbol',
readonly=True, model_name='investment.rate'),
'get_rate_data')
@classmethod
@ -154,21 +131,6 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
super(Asset, cls).__setup__()
cls._order.insert(0, ('name', 'ASC'))
cls._order.insert(0, ('date', 'DESC'))
t = cls.__table__()
cls._sql_indexes.update({
Index(
t,
(t.product, Index.Equality())),
Index(
t,
(t.currency, Index.Equality())),
Index(
t,
(t.uom, Index.Equality())),
Index(
t,
(t.updtdays, Index.Equality())),
})
@classmethod
def migrate_updtsource(cls, module_name):
@ -186,7 +148,7 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query = tab_asset.select(
tab_asset.id,
tab_asset.updtsource,
where=tab_asset.updtsource != DEF_NONE,
where = tab_asset.updtsource != None,
)
cursor.execute(*query)
records = cursor.fetchall()
@ -195,7 +157,7 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
'source': x[1],
} for x in records]
if to_create:
if len(to_create) > 0:
AssetSourceRel.create(to_create)
asset_table.drop_column('updtsource')
@ -205,7 +167,9 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
('/tree', 'visual',
If(Eval('date', Date()) < Date(delta_days=-5), 'muted',
If(Eval('change_day1', 0) < 0, 'danger',
If(Eval('change_day1', 0) > 0, 'success', '')))),
If(Eval('change_day1', 0) > 0, 'success', '')
))
),
]
@classmethod
@ -242,23 +206,13 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
"""
return 'work'
@fields.depends('updtsources')
def on_change_with_updturl_enable(self, name=None):
""" return True if a source has fixed-url
"""
if self.updtsources:
for usource in self.updtsources:
if usource.fixed_url is True:
return True
return False
@fields.depends('updtsources', 'updttime')
def on_change_updtsources(self):
""" clear time-fields
"""
if not self.updtsources:
if len(self.updtsources) == 0:
self.updttime = None
else:
else :
self.updttime = time(11, 30)
@fields.depends('product', 'uom')
@ -296,21 +250,16 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
(tab1, join1, col1) = Uom.symbol._get_translation_column(Uom, 'symbol')
tab_symb = join1.select(tab1.id, col1.as_('symbol'))
query = tab_asset.join(
tab_prod,
condition=tab_asset.product == tab_prod.id,
).join(
tab_templ,
condition=tab_templ.id == tab_prod.template,
).join(
tab_uom,
condition=tab_templ.default_uom == tab_uom.id,
).join(
tab_cur,
condition=tab_asset.currency == tab_cur.id,
).join(
tab_symb,
condition=tab_asset.uom == tab_symb.id,
query = tab_asset.join(tab_prod,
condition=tab_asset.product==tab_prod.id,
).join(tab_templ,
condition=tab_templ.id==tab_prod.template,
).join(tab_uom,
condition=tab_templ.default_uom==tab_uom.id,
).join(tab_cur,
condition=tab_asset.currency==tab_cur.id,
).join(tab_symb,
condition=tab_asset.uom==tab_symb.id,
).select(
tab_asset.id,
tab_templ.name,
@ -325,24 +274,23 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
"""
cursor = Transaction().connection.cursor()
result = {x: {y.id: None for y in assets} for x in names}
(query, tab_asset) = cls.get_name_symbol_sql()
if assets:
query.where = tab_asset.id.in_([x.id for x in assets])
cursor.execute(*query)
records = cursor.fetchall()
query.where=tab_asset.id.in_([x.id for x in assets])
for record in records:
values = {
'name': record[1],
'product_uom': record[2],
'symbol': record[3],
'asset_symbol': record[0],
}
cursor.execute(*query)
records = cursor.fetchall()
result = {x:{y.id: None for y in assets} for x in names}
for name in names:
result[name][record[0]] = values[name]
for record in records:
values = {
'name': record[1],
'product_uom': record[2],
'symbol': record[3],
'asset_symbol': record[0],
}
for name in names:
result[name][record[0]] = values[name]
return result
@classmethod
@ -351,7 +299,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
"""
return ['OR',
(('uom.rec_name',) + tuple(clause[1:])),
(('currency.rec_name',) + tuple(clause[1:]))]
(('currency.rec_name',) + tuple(clause[1:])),
]
@classmethod
def get_rate_data_sql(cls):
@ -363,9 +312,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
tab_asset = Asset.__table__()
tab_rate = Rate.__table__()
query = tab_asset.join(
tab_rate,
condition=tab_asset.id == tab_rate.asset
query = tab_asset.join(tab_rate,
condition=tab_asset.id==tab_rate.asset
).select(
tab_asset.id,
Round(tab_rate.rate, tab_asset.currency_digits).as_('rate'),
@ -380,32 +328,32 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
def get_rate_data(cls, assets, names):
""" get date and rate of asset
"""
Asset2 = Pool().get('investment.asset')
cursor = Transaction().connection.cursor()
(query, tab_asset) = cls.get_rate_data_sql()
query.where=tab_asset.id.in_([x.id for x in assets])
result = {x: {y.id: None for y in assets} for x in names}
cursor.execute(*query)
records = cursor.fetchall()
if assets:
(query, tab_asset) = cls.get_rate_data_sql()
query.where = tab_asset.id.in_([x.id for x in assets])
curr_digits = {x.id: x.currency_digits for x in assets}
result = {x:{y.id: None for y in assets} for x in names}
cursor.execute(*query)
records = cursor.fetchall()
for record in records:
(id1, rate1, date1, id_rate) = record
for record in records:
(id1, rate1, date1, id_rate) = record
asset = Asset2(id1)
exp = Decimal(Decimal(1) / 10 ** (asset.currency_digits or 4))
curr_dig = curr_digits.get(id1, 4)
exp = Decimal(Decimal(1) / 10 ** curr_dig)
values = {
'rate': record[1].quantize(exp),
'date': record[2],
'change_symbol': id_rate,
}
values = {
'rate': record[1].quantize(exp),
'date': record[2],
'change_symbol': id_rate,
}
for name in names:
result[name][record[0]] = values[name]
for name in names:
result[name][record[0]] = values[name]
return result
@classmethod
@ -417,7 +365,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query = tab_query.select(
tab_query.id,
where=Operator(tab_query.date, clause[2]))
where=Operator(tab_query.date, clause[2]),
)
return [('id', 'in', query)]
@classmethod
@ -429,7 +378,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query = tab_query.select(
tab_query.id,
where=Operator(tab_query.rate, clause[2]))
where=Operator(tab_query.rate, clause[2]),
)
return [('id', 'in', query)]
@staticmethod
@ -441,7 +391,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query = tab_query.select(
tab_query.date,
where=tab_query.id == table.id)
where=tab_query.id==table.id,
)
return [query]
@staticmethod
@ -453,7 +404,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query = tab_query.select(
tab_query.rate,
where=tab_query.id == table.id)
where=tab_query.id==table.id,
)
return [query]
@classmethod
@ -464,6 +416,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
"""
pool = Pool()
Rate = pool.get('investment.rate')
Asset2 = pool.get('investment.asset')
tab_asset = Asset2.__table__()
tab_rate1 = Rate.__table__()
tab_rate2 = Rate.__table__()
context = Transaction().context
@ -471,96 +425,103 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query_date = context.get('qdate', CurrentDate())
where_asset = tab_rate1.date <= query_date
if isinstance(asset_ids, list):
where_asset &= tab_rate1.asset.in_(asset_ids)
where_asset &= tab_asset.id.in_(asset_ids)
tab_today = tab_rate1.select(
tab_rate1.asset.as_('id'),
tab_today = tab_asset.join(tab_rate1,
condition=tab_asset.id==tab_rate1.asset,
).select(
tab_asset.id,
tab_rate1.date,
tab_rate1.rate,
distinct_on=[tab_rate1.asset],
order_by=[tab_rate1.asset, tab_rate1.date.desc],
where=where_asset)
distinct_on=[tab_asset.id],
order_by=[tab_asset.id, tab_rate1.date.desc],
where=where_asset,
)
days_diff = days + 5
query = tab_today.join(
tab_rate2,
condition=(tab_today.id == tab_rate2.asset) &
(tab_today.date > (tab_rate2.date + days)) &
(tab_today.date <= (tab_rate2.date + days_diff)),
type_='LEFT OUTER',
query = tab_today.join(tab_rate2,
condition=(tab_today.id==tab_rate2.asset) & \
(tab_today.date > (tab_rate2.date + days)) & \
(tab_today.date <= (tab_rate2.date + days_diff)),
type_ = 'LEFT OUTER',
).select(
tab_today.id,
tab_today.date,
tab_today.rate,
(tab_today.rate * 100.0 / NullIf(tab_rate2.rate, 0.00) -
100.0).as_('percent'),
(tab_today.rate * 100.0 / NullIf(tab_rate2.rate, 0.00) - 100.0).as_('percent'),
distinct_on=[tab_today.id],
order_by=[tab_today.id, tab_rate2.date.desc])
order_by=[tab_today.id, tab_rate2.date.desc]
)
return query
@staticmethod
def order_change_day1(tables):
""" order day1
"""
Asset2 = Pool().get('investment.asset')
tab_asset = Asset2.get_percentage_sql(days=0)
Assert = Pool().get('investment.asset')
tab_asset = Asset.get_percentage_sql(days=0)
table, _ = tables[None]
query = tab_asset.select(
tab_asset.percent,
where=tab_asset.id == table.id)
where=tab_asset.id==table.id,
)
return [query]
@staticmethod
def order_change_month1(tables):
""" order month1
"""
Asset2 = Pool().get('investment.asset')
tab_asset = Asset2.get_percentage_sql(days=30)
Assert = Pool().get('investment.asset')
tab_asset = Asset.get_percentage_sql(days=30)
table, _ = tables[None]
query = tab_asset.select(
tab_asset.percent,
where=tab_asset.id == table.id)
where=tab_asset.id==table.id,
)
return [query]
@staticmethod
def order_change_month3(tables):
""" order month1
"""
Asset2 = Pool().get('investment.asset')
tab_asset = Asset2.get_percentage_sql(days=90)
Assert = Pool().get('investment.asset')
tab_asset = Asset.get_percentage_sql(days=90)
table, _ = tables[None]
query = tab_asset.select(
tab_asset.percent,
where=tab_asset.id == table.id)
where=tab_asset.id==table.id,
)
return [query]
@staticmethod
def order_change_month6(tables):
""" order month1
"""
Asset2 = Pool().get('investment.asset')
tab_asset = Asset2.get_percentage_sql(days=180)
Assert = Pool().get('investment.asset')
tab_asset = Asset.get_percentage_sql(days=180)
table, _ = tables[None]
query = tab_asset.select(
tab_asset.percent,
where=tab_asset.id == table.id)
where=tab_asset.id==table.id,
)
return [query]
@staticmethod
def order_change_month12(tables):
""" order month1
"""
Asset2 = Pool().get('investment.asset')
tab_asset = Asset2.get_percentage_sql(days=365)
Assert = Pool().get('investment.asset')
tab_asset = Asset.get_percentage_sql(days=365)
table, _ = tables[None]
query = tab_asset.select(
tab_asset.percent,
where=tab_asset.id == table.id)
where=tab_asset.id==table.id,
)
return [query]
@classmethod
@ -577,9 +538,9 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
'month12': 365,
}[field_name])
query = tab_percent.select(
tab_percent.id,
where=Operator(Round(tab_percent.percent, 2), clause[2]))
query = tab_percent.select(tab_percent.id,
where=Operator(Round(tab_percent.percent, 2),
clause[2]))
return [('id', 'in', query)]
@classmethod
@ -588,26 +549,26 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
"""
cursor = Transaction().connection.cursor()
result = {x: {y.id: None for y in assets} for x in names}
result = {x:{y.id: None for y in assets} for x in names}
exp = Decimal(Decimal(1) / 10 ** digits_percent)
asset_id_lst = [x.id for x in assets]
if asset_id_lst and names:
for x in names:
tab_percent = cls.get_percentage_sql(
days={
'change_day1': 0,
'change_month1': 30,
'change_month3': 90,
'change_month6': 180,
'change_month12': 365,
}[x],
asset_ids=asset_id_lst)
cursor.execute(*tab_percent)
records = cursor.fetchall()
for x in names:
tab_percent = cls.get_percentage_sql(
days={
'change_day1': 0,
'change_month1': 30,
'change_month3': 90,
'change_month6': 180,
'change_month12': 365,
}[x],
asset_ids=asset_id_lst,
)
cursor.execute(*tab_percent)
records = cursor.fetchall()
for record in records:
result[x][record[0]] = record[3].quantize(exp) \
for record in records:
result[x][record[0]] = record[3].quantize(exp) \
if record[3] is not None else None
return result
@ -627,34 +588,32 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query_date = context.get('qdate', CurrentDate() - Literal(1))
# get last date of rate
tab_date = tab_asset.join(
tab_rel,
tab_date = tab_asset.join(tab_rel,
# link to asset-source-relation to check if
# there are online-sources set
condition=tab_rel.asset == tab_asset.id,
).join(
tab_rate,
).join(tab_rate,
condition=tab_asset.id == tab_rate.asset,
type_='LEFT OUTER',
type_ = 'LEFT OUTER',
).select(
tab_asset.id,
(Coalesce(tab_rate.date, query_date) + Literal(1)).as_('date'),
tab_asset.updtdays,
tab_asset.updttime,
distinct_on=[tab_asset.id],
order_by=[tab_asset.id, tab_rate.date.desc])
distinct_on = [tab_asset.id],
order_by = [tab_asset.id, tab_rate.date.desc],
)
query = tab_date.select(
tab_date.id,
(Case(
((tab_date.updtdays == 'work') &
(Extract('dow', tab_date.date) == 0),
tab_date.date + Literal(1)),
((tab_date.updtdays == 'work') &
(Extract('dow', tab_date.date) == 6),
tab_date.date + Literal(2)),
else_=tab_date.date,
) + tab_date.updttime).as_('updttime'))
((tab_date.updtdays == 'work') & \
(Extract('dow', tab_date.date) == 0), tab_date.date + Literal(1)),
((tab_date.updtdays == 'work') & \
(Extract('dow', tab_date.date) == 6), tab_date.date + Literal(2)),
else_ = tab_date.date,
) + tab_date.updttime).as_('updttime'),
)
return query
@classmethod
@ -668,11 +627,12 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query = tab_updt.select(
tab_updt.id,
tab_updt.updttime,
where=tab_updt.id.in_([x.id for x in assets]))
where=tab_updt.id.in_([x.id for x in assets]),
)
cursor.execute(*query)
records = cursor.fetchall()
result = {x: {y.id: None for y in assets} for x in names}
result = {x:{y.id: None for y in assets} for x in names}
for record in records:
(id1, updt) = record
@ -692,7 +652,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query = tab_updt.select(
tab_updt.id,
where=Operator(tab_updt.updttime, clause[2]))
where=Operator(tab_updt.updttime, clause[2]),
)
return [('id', 'in', query)]
@classmethod
@ -707,29 +668,26 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
tab_secsymb = Identifier.__table__()
tab_isin = Identifier.__table__()
query = tab_asset.join(
tab_prod,
condition=tab_asset.product == tab_prod.id,
).join(
tab_wkn,
condition=(tab_prod.id == tab_wkn.product) &
(tab_wkn.type == 'wkn'),
type_='LEFT OUTER',
).join(
tab_secsymb,
condition=(tab_prod.id == tab_secsymb.product) &
(tab_secsymb.type == 'secsymb'),
type_='LEFT OUTER',
).join(
tab_isin,
condition=(tab_prod.id == tab_isin.product) &
(tab_isin.type == 'isin'),
type_='LEFT OUTER',
query = tab_asset.join(tab_prod,
condition=tab_asset.product==tab_prod.id,
).join(tab_wkn,
condition=(tab_prod.id==tab_wkn.product) & \
(tab_wkn.type == 'wkn'),
type_ = 'LEFT OUTER',
).join(tab_secsymb,
condition=(tab_prod.id==tab_secsymb.product) & \
(tab_secsymb.type == 'secsymb'),
type_ = 'LEFT OUTER',
).join(tab_isin,
condition=(tab_prod.id==tab_isin.product) & \
(tab_isin.type == 'isin'),
type_ = 'LEFT OUTER',
).select(
tab_asset.id,
tab_wkn.code.as_('wkn'),
tab_secsymb.code.as_('secsymb'),
tab_isin.code.as_('isin'))
tab_isin.code.as_('isin'),
)
return query
@staticmethod
@ -745,15 +703,13 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
tab_prod = Product.__table__()
tab_templ = Templ.__table__()
query = tab_asset.join(
tab_prod,
condition=tab_asset.product == tab_prod.id
).join(
tab_templ,
condition=tab_templ.id == tab_prod.template
).select(
tab_templ.name,
where=tab_asset.id == table.id)
query = tab_asset.join(tab_prod,
condition=tab_asset.product==tab_prod.id
).join(tab_templ,
condition=tab_templ.id==tab_prod.template
).select(tab_templ.name,
where=tab_asset.id==table.id
)
return [query]
@staticmethod
@ -766,7 +722,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query = tab_ids.select(
getattr(tab_ids, 'wkn'),
where=tab_ids.id == table.id)
where=tab_ids.id==table.id,
)
return [query]
@staticmethod
@ -779,7 +736,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query = tab_ids.select(
getattr(tab_ids, 'isin'),
where=tab_ids.id == table.id)
where=tab_ids.id==table.id,
)
return [query]
@staticmethod
@ -792,7 +750,8 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query = tab_ids.select(
getattr(tab_ids, 'secsymb'),
where=tab_ids.id == table.id)
where=tab_ids.id==table.id,
)
return [query]
@classmethod
@ -806,13 +765,13 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
tab_ids = cls.get_identifier_sql(tab_asset)
field_qu = getattr(tab_ids, names)
query = tab_ids.join(
tab_asset,
condition=tab_ids.id == tab_asset.id,
query = tab_ids.join(tab_asset,
condition=tab_ids.id==tab_asset.id,
).select(
tab_asset.id,
where=Operator(field_qu, clause[2]) &
(field_qu != DEF_NONE))
where=Operator(field_qu, clause[2]) & \
(field_qu != None),
)
return [('id', 'in', query)]
@ -825,20 +784,21 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
tab_asset = Asset.__table__()
cursor = Transaction().connection.cursor()
result = {x: {y.id: None for y in assets} for x in names}
if assets:
query = cls.get_identifier_sql(tab_asset)
query.where = tab_asset.id.in_([x.id for x in assets])
result = {x:{y.id: None for y in assets} for x in names}
cursor.execute(*query)
l1 = cursor.fetchall()
query = cls.get_identifier_sql(tab_asset)
query.where = tab_asset.id.in_([x.id for x in assets])
for x in l1:
(id1, wkn, secsymb, isin) = x
r1 = {'wkn': wkn, 'secsymb': secsymb, 'isin': isin}
cursor.execute(*query)
l1 = cursor.fetchall()
for x in l1:
(id1, wkn, secsymb, isin) = x
r1 = {'wkn': wkn, 'secsymb': secsymb, 'isin': isin}
for n in names:
result[n][id1] = r1[n]
for n in names:
result[n][id1] = r1[n]
return result
def get_rec_name(self, name):
@ -847,18 +807,17 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
return '%(prod)s | %(rate)s %(unit)s | %(date)s' % {
'prod': getattr(self.product, 'rec_name', '-'),
'unit': self.symbol,
'rate': Report.format_number(
self.rate, lang=None, digits=self.currency_digits or 4)
if self.rate is not None else '-',
'date': Report.format_date(self.date)
if self.date is not None else '-'}
'rate': Report.format_number(self.rate, lang=None,
digits=self.currency_digits or 4) \
if self.rate is not None else '-',
'date': Report.format_date(self.date) if self.date is not None else '-',
}
@classmethod
def search_rec_name(cls, name, clause):
""" search in rec_name
"""
return [
'OR',
return ['OR',
('product.rec_name',) + tuple(clause[1:]),
('product.identifiers.code',) + tuple(clause[1:]),
]
@ -881,11 +840,12 @@ class Asset(SymbolMixin, ModelSQL, ModelView):
query_time = context.get('qdatetime', CurrentTimestamp())
to_run_activities = []
for asset in Asset2.search([
('nextupdate', '<=', query_time)]):
('nextupdate', '<=', query_time),
]):
if OnlineSource.update_rate(asset):
to_run_activities.append(asset)
if to_run_activities:
if len(to_run_activities) > 0:
cls.after_update_actions(to_run_activities)
# end Asset
@ -895,12 +855,10 @@ class AssetSourceRel(ModelSQL):
'Asset Source Relation'
__name__ = 'investment.asset_source_rel'
source = fields.Many2One(
string='Online Source',
source = fields.Many2One(string='Online Source', select=True,
required=True, model_name='investment.source',
ondelete='CASCADE')
asset = fields.Many2One(
string='Asset',
asset = fields.Many2One(string='Asset', select=True,
required=True, model_name='investment.asset',
ondelete='CASCADE')

View file

@ -1,8 +0,0 @@
# -*- coding: utf-8 -*-
# This file is part of the cashbook-module from m-ds.de for Tryton.
# The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms.
DEF_TRUE = True
DEF_NONE = None

View file

@ -22,8 +22,8 @@ class Concat2(Function):
class GraphDef(metaclass=PoolMeta):
__name__ = 'diagram.graphdef'
asset = fields.Many2One(
string='Asset', model_name='investment.asset',
asset = fields.Many2One(string='Asset',
model_name='investment.asset',
states={
'invisible': Eval('dtype', '') != 'investment.asset',
'required': Eval('dtype', '') == 'investment.asset',
@ -60,18 +60,17 @@ class GraphDef(metaclass=PoolMeta):
return None
if self.scaling == 'alldata':
query = [('asset', '=', self.asset.id)]
query = [('asset.id', '=', self.asset.id)]
elif self.scaling == 'view':
query = [
('asset', '=', self.asset.id),
('asset.id', '=', self.asset.id),
('date', '>=', self.chart.used_start_date()),
('date', '<=', self.chart.used_end_date()),
]
elif self.scaling == 'six':
query = [
('asset', '=', self.asset.id),
('date', '>=', self.chart.used_start_date() -
timedelta(days=180)),
('asset.id', '=', self.asset.id),
('date', '>=', self.chart.used_start_date() - timedelta(days=180)),
('date', '<=', self.chart.used_end_date()),
]
@ -95,7 +94,7 @@ class ChartPoint(metaclass=PoolMeta):
"""
Rate = Pool().get('investment.rate')
if not keyname:
if keyname is None:
return None
# check if query is for us
@ -104,12 +103,12 @@ class ChartPoint(metaclass=PoolMeta):
before = Rate.search([
('date', '<', query_date),
('asset', '=', asset_id),
('asset.id', '=', asset_id),
], limit=1, order=[('date', 'DESC')])
after = Rate.search([
('date', '>', query_date),
('asset', '=', asset_id),
('asset.id', '=', asset_id),
], limit=1, order=[('date', 'ASC')])
if (len(before) == 1) and (len(after) == 1):

View file

@ -4,7 +4,7 @@
# full copyright notices and license terms.
from io import StringIO
from datetime import datetime, date
from datetime import datetime, date
from decimal import Decimal
import csv
from trytond.pool import Pool
@ -36,15 +36,15 @@ class ImportWizardStart(ModelView):
'Import CSV-File'
__name__ = 'investment.imp_wiz.start'
asset = fields.Many2One(
string='Asset', readonly=True, model_name='investment.asset')
asset = fields.Many2One(string='Asset', readonly=True,
model_name='investment.asset')
file_ = fields.Binary(string="CSV-File", required=True)
dec_divider = fields.Selection(
string='Decimal divider', required=True, selection=sel_dec_divider)
date_fmt = fields.Selection(
string='Date format', required=True, selection=sel_date_fmt)
field_delimiter = fields.Selection(
string='Field delimiter', required=True, selection=sel_field_delimiter)
dec_divider = fields.Selection(string='Decimal divider',
required=True, selection=sel_dec_divider)
date_fmt = fields.Selection(string='Date format',
required=True, selection=sel_date_fmt)
field_delimiter = fields.Selection(string='Field delimiter',
required=True, selection=sel_field_delimiter)
# end ImportWizardStart
@ -54,14 +54,11 @@ class ImportWizard(Wizard):
__name__ = 'investment.imp_wiz'
start_state = 'start'
start = StateView(
model_name='investment.imp_wiz.start',
view='investment.imp_wiz_start_form',
start = StateView(model_name='investment.imp_wiz.start', \
view='investment.imp_wiz_start_form', \
buttons=[
Button(string='Cancel', state='end', icon='tryton-cancel'),
Button(
string='Import File', state='importf',
icon='tryton-import', default=True),
Button(string='Import File', state='importf', icon='tryton-import', default=True),
])
importf = StateTransition()
@ -84,14 +81,14 @@ class ImportWizard(Wizard):
pool = Pool()
ImportWiz = pool.get('investment.imp_wiz', type='wizard')
if self.start.file_:
if self.start.file_ is not None:
(lines, max_date, min_date) = ImportWiz.read_csv_file(
self.start.file_.decode('utf8'),
dec_divider=self.start.dec_divider,
date_fmt=self.start.date_fmt,
delimiter=self.start.field_delimiter)
self.start.file_.decode('utf8'),
dec_divider = self.start.dec_divider,
date_fmt = self.start.date_fmt,
delimiter = self.start.field_delimiter)
if lines:
if len(lines) > 0:
ImportWiz.upload_rates(
self.start.asset,
lines, min_date, max_date)
@ -143,69 +140,68 @@ class ImportWizard(Wizard):
max_rate = None
with StringIO(file_content) as fhdl:
csv_lines = csv.DictReader(
fhdl,
fieldnames=['date', 'rate'],
dialect='excel',
delimiter=delimiter)
csv_lines = csv.DictReader(fhdl,
fieldnames = ['date', 'rate'],
dialect='excel',
delimiter=delimiter)
for line in csv_lines:
# skip first line
if line.get('date', '') == 'date':
continue
try:
date_val = datetime.strptime(
line.get('date', None).strip(), date_fmt).date()
except Exception:
try :
date_val = datetime.strptime(line.get('date', None).strip(), date_fmt).date()
except :
raise UserError(gettext(
'investment.msg_import_err_date',
datefmt=date_fmt,
colnr='1',
datefmt = date_fmt,
colnr = '1',
))
try:
rate_val = line.get('rate', None).replace(
del_chars[0], '').strip()
try :
rate_val = line.get('rate', None).replace(del_chars[0], '').strip()
rate_val = Decimal(rate_val.replace(dec_divider, '.'))
except Exception:
except :
raise UserError(gettext(
'investment.msg_import_err_date',
datefmt='dd%sdd' % dec_divider,
colnr='2'))
datefmt = 'dd%sdd' % dec_divider,
colnr = '2',
))
if isinstance(date_val, date) and isinstance(
rate_val, Decimal):
if isinstance(date_val, date) and isinstance(rate_val, Decimal):
result.append({'date': date_val, 'rate': rate_val})
# date range
if max_date is None:
max_date = date_val
else:
else :
if max_date < date_val:
max_date = date_val
if min_date is None:
min_date = date_val
else:
else :
if min_date > date_val:
min_date = date_val
# rate range
if max_rate is None:
max_rate = rate_val
else:
else :
if max_rate < rate_val:
max_rate = rate_val
if min_rate is None:
min_rate = rate_val
else:
else :
if min_rate > rate_val:
min_rate = rate_val
else:
else :
raise UserError(gettext(
'investment.msg_err_unknown_content',
linetxt=line))
linetxt = line,
))
return (result, max_date, min_date)
# end ImportWizard

View file

@ -46,14 +46,6 @@ msgctxt "model:ir.message,text:msg_querytype_web"
msgid "Extract from web page"
msgstr "aus Webseite auslesen"
msgctxt "model:ir.message,text:msg_missing_url"
msgid "URL for the online source '%(oname)s' is missing."
msgstr "URL für die Onlinequelle '%(oname)s' fehlt."
msgctxt "model:ir.message,text:msg_bug_in_regexquery"
msgid "Error in regex code of field '%(fname)s': %(errmsg)s [%(code)s]"
msgstr "Fehler in Regex-Code des Feldes '%(fname)s': %(errmsg)s [%(code)s]"
##############
# ir.ui.menu #
@ -338,18 +330,6 @@ msgctxt "selection:investment.asset,updtdays:"
msgid "Mon - Sun"
msgstr "Mo - So"
msgctxt "field:investment.asset,updturl:"
msgid "URL"
msgstr "URL"
msgctxt "help:investment.asset,updturl:"
msgid "URL for data retrieval."
msgstr "URL für Datenabruf."
msgctxt "field:investment.asset,updturl_enable:"
msgid "URL required"
msgstr "URL required"
###############################
# investment.asset-source-rel #
@ -550,14 +530,6 @@ msgctxt "help:investment.source,query_method:"
msgid "Select the method to retrieve the data."
msgstr "Wählen Sie die Methode zum Abruf der Daten."
msgctxt "field:investment.source,fixed_url:"
msgid "Fixed URL"
msgstr "feste URL"
msgctxt "help:investment.source,fixed_url:"
msgid "URL must be defined at investment record."
msgstr "Die URL muss im Investitionsdatensatz definiert werden."
###################
# investment.rate #

View file

@ -34,14 +34,6 @@ msgctxt "model:ir.message,text:msg_querytype_web"
msgid "Extract from web page"
msgstr "Extract from web page"
msgctxt "model:ir.message,text:msg_missing_url"
msgid "URL for the online source '%(oname)s' is missing."
msgstr "URL for the online source '%(oname)s' is missing."
msgctxt "model:ir.message,text:msg_bug_in_regexquery"
msgid "Error in regex code of field '%(fname)s': %(errmsg)s [%(code)s]"
msgstr "Error in regex code of field '%(fname)s': %(errmsg)s [%(code)s]"
msgctxt "model:ir.ui.menu,name:menu_investment"
msgid "Investment"
msgstr "Investment"
@ -298,18 +290,6 @@ msgctxt "selection:investment.asset,updtdays:"
msgid "Mon - Sun"
msgstr "Mon - Sun"
msgctxt "field:investment.asset,updturl:"
msgid "URL"
msgstr "URL"
msgctxt "help:investment.asset,updturl:"
msgid "URL for data retrieval."
msgstr "URL for data retrieval."
msgctxt "field:investment.asset,updturl_enable:"
msgid "URL required"
msgstr "URL required"
msgctxt "model:investment.asset_source_rel,name:"
msgid "Asset Source Relation"
msgstr "Asset Source Relation"
@ -502,14 +482,6 @@ msgctxt "help:investment.source,query_method:"
msgid "Select the method to retrieve the data."
msgstr "Select the method to retrieve the data."
msgctxt "field:investment.source,fixed_url:"
msgid "Fixed URL"
msgstr "Fixed URL"
msgctxt "help:investment.source,fixed_url:"
msgid "URL must be defined at investment record."
msgstr "URL must be defined at investment record."
msgctxt "model:investment.rate,name:"
msgid "Rate"
msgstr "Rate"

View file

@ -20,12 +20,6 @@ full copyright notices and license terms. -->
<record model="ir.message" id="msg_querytype_web">
<field name="text">Extract from web page</field>
</record>
<record model="ir.message" id="msg_missing_url">
<field name="text">URL for the online source '%(oname)s' is missing.</field>
</record>
<record model="ir.message" id="msg_bug_in_regexquery">
<field name="text">Error in regex code of field '%(fname)s': %(errmsg)s [%(code)s]</field>
</record>
</data>
</tryton>

View file

@ -4,17 +4,13 @@
# full copyright notices and license terms.
from string import Template
import requests
import logging
import html2text
import re
import requests, logging, html2text, re
from datetime import datetime
from decimal import Decimal
from trytond.model import ModelView, ModelSQL, fields
from trytond.pool import Pool
from trytond.pyson import Eval, Bool
from trytond.i18n import gettext
from trytond.exceptions import UserError
logger = logging.getLogger(__name__)
@ -39,8 +35,7 @@ sel_rgxdatefmt = [
('%b %d %Y', 'mon dd yyyy'),
]
fields_check = [
'url', 'nsin', 'isin', 'symbol', 'text', 'http_state',
fields_check = ['url', 'nsin', 'isin', 'symbol', 'text', 'http_state', \
'fnddate', 'fndrate', 'fndident']
@ -56,44 +51,32 @@ class OnlineSource(ModelSQL, ModelView):
__name__ = 'investment.source'
name = fields.Char(string='Name', required=True)
query_method = fields.Selection(
string='Method', required=True,
query_method = fields.Selection(string='Method', required=True,
help='Select the method to retrieve the data.',
selection='get_query_methods')
url = fields.Char(string='URL', states=STATES_WEB, depends=DEPENDS_WEB)
fixed_url = fields.Boolean(
string='Fixed URL',
states={'invisible': Eval('query_method', '') != 'web'},
depends=DEPENDS_WEB,
help='URL must be defined at investment record.')
nohtml = fields.Boolean(
string='Remove HTML',
nohtml = fields.Boolean(string='Remove HTML',
help='Removes HTML tags before the text is interpreted.',
states={'invisible': STATES_WEB['invisible']},
depends=DEPENDS_WEB)
rgxdate = fields.Char(
string='Date',
states={
'invisible': STATES_WEB['invisible'],
}, depends=DEPENDS_WEB)
rgxdate = fields.Char(string='Date',
help='Regex code to find the date in the downloaded HTML file.',
states=STATES_WEB, depends=DEPENDS_WEB)
rgxdatefmt = fields.Selection(
string='Date format', selection=sel_rgxdatefmt,
rgxdatefmt = fields.Selection(string='Date format', selection=sel_rgxdatefmt,
states=STATES_WEB, depends=DEPENDS_WEB)
rgxrate = fields.Char(
string='Rate',
rgxrate = fields.Char(string='Rate',
help='Regex code to find the rate in the downloaded HTML file.',
states=STATES_WEB, depends=DEPENDS_WEB)
rgxdecimal = fields.Selection(
string='Decimal Separator',
help='Decimal separator for converting the market ' +
'value into a number.',
rgxdecimal = fields.Selection(string='Decimal Separator',
help='Decimal separator for converting the market value into a number.',
selection=sel_rgxdecimal, states=STATES_WEB, depends=DEPENDS_WEB)
rgxident = fields.Char(
string='Identifier',
rgxident = fields.Char(string='Identifier',
help='Regex code to find the identifier in the downloaded HTML file.',
states={'invisible': STATES_WEB['invisible']},
depends=DEPENDS_WEB)
rgxidtype = fields.Selection(
string='ID-Type', selection=sel_rgxidtype,
states={
'invisible': STATES_WEB['invisible'],
}, depends=DEPENDS_WEB)
rgxidtype = fields.Selection(string='ID-Type', selection=sel_rgxidtype,
help='Type of identifier used to validate the result.',
states={
'required': Bool(Eval('rgxident', '')),
@ -101,32 +84,27 @@ class OnlineSource(ModelSQL, ModelView):
}, depends=DEPENDS_WEB+['rgxident'])
# field to test requests
used_url = fields.Function(fields.Char(
string='Used URL', readonly=True,
used_url = fields.Function(fields.Char(string='Used URL', readonly=True,
help='This URL is used to retrieve the HTML file.',
states={'invisible': STATES_WEB['invisible']}, depends=DEPENDS_WEB),
'on_change_with_used_url')
nsin = fields.Function(fields.Char(
string='NSIN'), 'on_change_with_nsin', setter='set_test_value')
isin = fields.Function(fields.Char(
string='ISIN'), 'on_change_with_isin', setter='set_test_value')
symbol = fields.Function(fields.Char(
string='Symbol'), 'on_change_with_symbol', setter='set_test_value')
http_state = fields.Function(fields.Char(
string='HTTP-State',
nsin = fields.Function(fields.Char(string='NSIN'),
'on_change_with_nsin', setter='set_test_value')
isin = fields.Function(fields.Char(string='ISIN'),
'on_change_with_isin', setter='set_test_value')
symbol = fields.Function(fields.Char(string='Symbol'),
'on_change_with_symbol', setter='set_test_value')
http_state = fields.Function(fields.Char(string='HTTP-State',
readonly=True), 'on_change_with_http_state')
text = fields.Function(fields.Text(
string='Result', readonly=True), 'on_change_with_text')
fnddate = fields.Function(fields.Date(
string='Date', readonly=True,
text = fields.Function(fields.Text(string='Result',
readonly=True), 'on_change_with_text')
fnddate = fields.Function(fields.Date(string='Date', readonly=True,
help='Date found during test query.'),
'on_change_with_fnddate')
fndrate = fields.Function(fields.Numeric(
string='Rate', readonly=True,
help='Rate found during test query.', digits=(16, 4)),
fndrate = fields.Function(fields.Numeric(string='Rate', readonly=True,
help='Rate found during test query.', digits=(16,4)),
'on_change_with_fndrate')
fndident = fields.Function(fields.Char(
string='Identifier', readonly=True,
fndident = fields.Function(fields.Char(string='Identifier', readonly=True,
help='Identifier found during test query.'),
'on_change_with_fndident')
@ -183,12 +161,6 @@ class OnlineSource(ModelSQL, ModelView):
"""
return True
@classmethod
def default_fixed_url(cls):
""" default: False
"""
return False
@fields.depends(*fields_check)
def on_change_nsin(self):
""" run request
@ -231,22 +203,24 @@ class OnlineSource(ModelSQL, ModelView):
def on_change_with_symbol(self, name=None):
return ''
@fields.depends('url', 'isin', 'nsin', 'symbol', 'fixed_url')
@fields.depends('url', 'isin', 'nsin', 'symbol')
def on_change_with_used_url(self, name=None):
""" get url for testing
"""
if self.url:
return self.get_url_with_parameter(
isin=self.isin,
nsin=self.nsin,
symbol=self.symbol,
url=self.url)
isin = self.isin,
nsin = self.nsin,
symbol = self.symbol,
)
@classmethod
def get_query_methods(cls):
""" get list of query-methods
"""
return [('web', gettext('investment.msg_querytype_web'))]
return [
('web', gettext('investment.msg_querytype_web')),
]
@classmethod
def set_test_value(cls, record, name, value):
@ -255,16 +229,7 @@ class OnlineSource(ModelSQL, ModelView):
pass
@classmethod
def validate(cls, records):
""" check regex-code
"""
for record in records:
for x in ['rgxdate', 'rgxrate', 'rgxident']:
if x:
record.get_regex_result('', x)
@classmethod
def run_query_method(cls, osource, isin, nsin, symbol, url, debug=False):
def run_query_method(cls, osource, isin, nsin, symbol, debug=False):
""" run selected query to retrive data
result: {
'text': raw-text from query - for debug,
@ -279,11 +244,11 @@ class OnlineSource(ModelSQL, ModelView):
if getattr(osource, 'query_method', None) == 'web':
return OSourc.read_from_website(
osource,
isin=isin,
nsin=nsin,
symbol=symbol,
debug=debug,
url=url)
isin = isin,
nsin = nsin,
symbol = symbol,
debug = debug,
)
def call_online_source(self):
""" use updated values to call online-source,
@ -291,32 +256,24 @@ class OnlineSource(ModelSQL, ModelView):
"""
OSourc = Pool().get('investment.source')
result = OSourc.run_query_method(
self, self.isin, self.nsin, self.url,
self.symbol, debug=True)
if result:
result = OSourc.run_query_method(self, self.isin, self.nsin,
self.symbol, debug=True)
if result is not None:
self.text = result.get('text', None)
self.http_state = result.get('http_state', None)
self.fnddate = result.get('date', None)
self.fndrate = result.get('rate', None)
self.fndident = result.get('code', None)
def get_url_with_parameter(
self, isin=None, nsin=None, symbol=None, url=None):
def get_url_with_parameter(self, isin=None, nsin=None, symbol=None):
""" generate url
"""
if self.fixed_url is True:
if not url:
raise UserError(gettext(
'investment.msg_missing_url',
oname=self.rec_name))
return url
if self.url:
return Template(self.url).substitute({
'isin': isin if isin is not None else '',
'nsin': nsin if nsin is not None else '',
'symbol': symbol if symbol is not None else ''})
'isin': isin if isin is not None else '',
'nsin': nsin if nsin is not None else '',
'symbol': symbol if symbol is not None else '',
})
@classmethod
def update_rate(cls, asset):
@ -332,10 +289,10 @@ class OnlineSource(ModelSQL, ModelView):
for updtsource in asset.updtsources:
rate_data = cls.run_query_method(
updtsource,
isin=asset.isin,
nsin=asset.wkn,
symbol=asset.secsymb,
url=asset.updturl)
isin = asset.isin,
nsin = asset.wkn,
symbol = asset.secsymb,
)
if len(updtsource.rgxident or '') > 0:
# check result - same code?
@ -349,25 +306,27 @@ class OnlineSource(ModelSQL, ModelView):
if (asset_code or '').lower() != code.lower():
# fail
logger.warning(
'update_rate: got wrong code ' +
'"%(wrong)s" - expected "%(exp)s"' % {
'update_rate: got wrong code "%(wrong)s" - expected "%(exp)s"' % {
'exp': asset_code,
'wrong': code})
'wrong': code,
})
continue
to_create = {
'date': rate_data.get('date', None),
'rate': rate_data.get('rate', None),
'asset': asset.id}
'asset': asset.id,
}
if (to_create['date'] is not None) and \
(to_create['rate'] is not None):
(to_create['rate'] is not None):
# check if exists
if Rate.search_count([
('asset.id', '=', asset.id),
('date', '=', to_create['date'])]) == 0:
('date', '=', to_create['date']),
]) == 0:
Rate.create([to_create])
return True
else:
else :
# if we got a record for today - stop
# otherwise try next source
if to_create['date'] == IrDate.today():
@ -377,24 +336,15 @@ class OnlineSource(ModelSQL, ModelView):
def get_regex_result(self, html_text, field_name):
""" run regex on html-text, convert result
"""
OSource = Pool().get('investment.source')
rgxcode = getattr(self, field_name) or ''
if len(rgxcode) == 0:
return None
try:
search_result = re.compile(rgxcode).search(html_text)
if search_result is None:
return None
except Exception as e1:
raise UserError(gettext(
'investment.msg_bug_in_regexquery',
errmsg=str(e1),
fname=getattr(OSource, field_name).string,
code=rgxcode))
search_result = re.compile(rgxcode).search(html_text)
if search_result is None:
return None
try:
try :
result = search_result.group(1)
except IndexError:
result = search_result.group(0)
@ -403,23 +353,20 @@ class OnlineSource(ModelSQL, ModelView):
dec_sep = [',', '.']
dec_sep.remove(self.rgxdecimal)
result = result.replace(
dec_sep[0], '').replace(self.rgxdecimal, '.')
try:
result = result.replace(dec_sep[0], '').replace(self.rgxdecimal, '.')
try :
result = Decimal(result)
except Exception:
except :
result = None
elif field_name == 'rgxdate':
try:
try :
result = datetime.strptime(result, self.rgxdatefmt).date()
except Exception:
except :
result = None
return result
@classmethod
def read_from_website(
cls, updtsource, isin=None, nsin=None,
symbol=None, url=None, debug=False):
def read_from_website(cls, updtsource, isin=None, nsin=None, symbol=None, debug=False):
""" read from url, extract values
"""
result = {}
@ -430,10 +377,10 @@ class OnlineSource(ModelSQL, ModelView):
res1 = requests.get(
updtsource.get_url_with_parameter(
isin=isin,
nsin=nsin,
symbol=symbol,
url=url),
isin = isin,
nsin = nsin,
symbol = symbol,
),
allow_redirects=True,
timeout=5.0)
@ -442,7 +389,7 @@ class OnlineSource(ModelSQL, ModelView):
'msg': res1.reason,
}
if res1.status_code in [200, 204, 410]:
if res1.status_code in [200, 204]:
html = res1.text
# remove html-tags
@ -462,10 +409,8 @@ class OnlineSource(ModelSQL, ModelView):
result['rate'] = updtsource.get_regex_result(html, 'rgxrate')
result['date'] = updtsource.get_regex_result(html, 'rgxdate')
result['code'] = updtsource.get_regex_result(html, 'rgxident')
else:
logger.error(
'read_from_website: ' +
'%(code)s, url: %(url)s, redirects: [%(redirects)s]' % {
else :
logger.error('read_from_website: %(code)s, url: %(url)s, redirects: [%(redirects)s]' % {
'code': res1.status_code,
'url': res1.url,
'redirects': ', '.join([x.url for x in res1.history]),

56
rate.py
View file

@ -3,35 +3,33 @@
# 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, Unique, Check, SymbolMixin, Index)
from trytond.model import ModelView, ModelSQL, fields, Unique, Check, SymbolMixin
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.pyson import Eval, Bool
class Rate(SymbolMixin, ModelSQL, ModelView):
'Rate'
__name__ = 'investment.rate'
asset = fields.Many2One(
string='Asset', required=True, ondelete='CASCADE',
asset = fields.Many2One(string='Asset', required=True,
select=True, ondelete='CASCADE',
model_name='investment.asset')
date = fields.Date(string='Date', required=True)
rate = fields.Numeric(
string='Rate', required=True,
digits=(16, Eval('asset_digits', 4)), depends=['asset_digits'])
date = fields.Date(string='Date', required=True, select=True)
rate = fields.Numeric(string='Rate', required=True, select=True,
digits=(16, Eval('asset_digits', 4)),
depends=['asset_digits'])
asset_digits = fields.Function(fields.Integer(
string='Digits', readonly=True), 'get_rate_data')
currency = fields.Function(fields.Many2One(
string='Currency', readonly=True, model_name='currency.currency'),
asset_digits = fields.Function(fields.Integer(string='Digits',
readonly=True), 'get_rate_data')
currency = fields.Function(fields.Many2One(string='Currency',
readonly=True, model_name='currency.currency'),
'get_rate_data')
uom = fields.Function(fields.Many2One(
string='Uom', readonly=True, model_name='product.uom'),
'get_rate_data')
symbol = fields.Function(fields.Char(
string='Symbol', readonly=True), 'get_rate_data')
uom = fields.Function(fields.Many2One(string='Uom',
readonly=True, model_name='product.uom'), 'get_rate_data')
symbol = fields.Function(fields.Char(string='Symbol',
readonly=True), 'get_rate_data')
@classmethod
def __setup__(cls):
@ -46,21 +44,6 @@ class Rate(SymbolMixin, ModelSQL, ModelView):
'currency.msg_rate_positive'),
]
cls._order.insert(0, ('date', 'DESC'))
cls._sql_indexes.update({
Index(
t,
(t.date, Index.Range(order='DESC'))),
Index(
t,
(t.rate, Index.Range())),
Index(
t,
(t.asset, Index.Equality())),
Index(
t,
(t.asset, Index.Equality()),
(t.date, Index.Range(order='DESC'))),
})
@classmethod
def default_date(cls):
@ -79,9 +62,8 @@ class Rate(SymbolMixin, ModelSQL, ModelView):
tab_rate = cls.__table__()
cursor = Transaction().connection.cursor()
query = tab_asset.join(
tab_rate,
condition=tab_asset.id == tab_rate.asset,
query = tab_asset.join(tab_rate,
condition=tab_asset.id==tab_rate.asset,
).select(
tab_rate.id,
tab_asset.uom,
@ -92,7 +74,7 @@ class Rate(SymbolMixin, ModelSQL, ModelView):
cursor.execute(*query)
records = cursor.fetchall()
result = {x: {y.id: None for y in rates} for x in names}
result = {x:{y.id: None for y in rates} for x in names}
for record in records:
r1 = {
'symbol': '%',

View file

@ -2,7 +2,7 @@
"""
# Always prefer setuptools over distutils
from setuptools import setup
from setuptools import setup, find_packages
# To use a consistent encoding
from codecs import open
from os import path
@ -36,10 +36,10 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f:
l2 = i.strip().split(';')
if len(l2) < 4:
continue
modversion[l2[0]] = {'min': l2[1], 'max': l2[2], 'prefix': l2[3]}
modversion[l2[0]] = {'min':l2[1], 'max':l2[2], 'prefix':l2[3]}
# tryton-version
major_version = 7
major_version = 6
minor_version = 0
requires = ['requests>=2.26', 'html2text']
@ -51,47 +51,42 @@ for dep in info.get('depends', []):
prefix = modversion[dep]['prefix']
if len(modversion[dep]['max']) > 0:
requires.append('%s_%s >= %s, <= %s' % (
prefix, dep, modversion[dep]['min'],
modversion[dep]['max']))
else:
requires.append('%s_%s >= %s' % (
prefix, dep, modversion[dep]['min']))
else:
requires.append('%s_%s >= %s.%s, < %s.%s' % (
'trytond', dep, major_version, minor_version,
requires.append('%s_%s >= %s, <= %s' %
(prefix, dep, modversion[dep]['min'], modversion[dep]['max']))
else :
requires.append('%s_%s >= %s' %
(prefix, dep, modversion[dep]['min']))
else :
requires.append('%s_%s >= %s.%s, < %s.%s' %
('trytond', dep, major_version, minor_version,
major_version, minor_version + 1))
requires.append('trytond >= %s.%s, < %s.%s' % (
major_version, minor_version, major_version, minor_version + 1))
requires.append('trytond >= %s.%s, < %s.%s' %
(major_version, minor_version, major_version, minor_version + 1))
setup(
name='%s_%s' % (PREFIX, MODULE),
setup(name='%s_%s' % (PREFIX, MODULE),
version=info.get('version', '0.0.1'),
description='Tryton module to add investment items.',
long_description=long_description,
long_description_content_type='text/x-rst',
url='https://www.m-ds.de/',
download_url='https://scmdev.m-ds.de/Tryton/Extra/investment',
author='martin-data services',
author_email='service@m-ds.de',
license='GPL-3',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
'Framework :: Tryton',
'Intended Audience :: Developers',
'Intended Audience :: Customer Service',
'Intended Audience :: Information Technology',
'Intended Audience :: Financial and Insurance Industry',
'Topic :: Office/Business',
'Topic :: Office/Business :: Financial :: Accounting',
'Natural Language :: German',
'Natural Language :: English',
'Operating System :: OS Independent',
'License :: OSI Approved :: GNU General Public License (GPL)',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
'Framework :: Tryton',
'Intended Audience :: Developers',
'Intended Audience :: Customer Service',
'Intended Audience :: Information Technology',
'Intended Audience :: Financial and Insurance Industry',
'Topic :: Office/Business',
'Topic :: Office/Business :: Financial :: Accounting',
'Natural Language :: German',
'Natural Language :: English',
'Operating System :: OS Independent',
'License :: OSI Approved :: GNU General Public License (GPL)',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
],
keywords='tryton investment shares commodities',
@ -100,10 +95,10 @@ setup(
'trytond.modules.%s' % MODULE,
],
package_data={
'trytond.modules.%s' % MODULE: (info.get('xml', []) + [
'tryton.cfg', 'locale/*.po', 'tests/*.py',
'view/*.xml', 'icon/*.svg', 'scripts/*.py',
'versiondep.txt', 'README.rst']),
'trytond.modules.%s' % MODULE: (info.get('xml', [])
+ ['tryton.cfg', 'locale/*.po', 'tests/*.py',
'view/*.xml', 'icon/*.svg', 'scripts/*.py',
'versiondep.txt', 'README.rst']),
},
install_requires=requires,

View file

@ -47,17 +47,6 @@ full copyright notices and license terms. -->
<field name="rgxrate">\nKurs\s+([\d+\.]*\d+,\d+)\s+USD\s*\n</field>
<field name="rgxdecimal">,</field>
</record>
<record model="investment.source" id="web_finanzen_aktie">
<field name="name">www.finanzen.net - Aktie</field>
<field name="url">https://www.finanzen.net/aktien/${symbol}</field>
<field name="nohtml" eval="True"/>
<field name="rgxdate">\nKurszeit(\d+\.\d+\.\d+) \d{2}:\d{2}:\d{2}.*\n</field>
<field name="rgxdatefmt">%d.%m.%Y</field>
<field name="rgxrate">\nKurs(\d+,\d+) EUR.*\n</field>
<field name="rgxident">\nISIN([A-Z,0-9]+).*\n</field>
<field name="rgxidtype">isin</field>
<field name="rgxdecimal">,</field>
</record>
<record model="investment.source" id="web_markets_ft_com">
<field name="name">Financial Times UK</field>
<field name="url">https://markets.ft.com/data/etfs/tearsheet/summary?s=${symbol}</field>
@ -71,25 +60,13 @@ full copyright notices and license terms. -->
<field name="name">www.sbroker.de</field>
<field name="url">https://www.sbroker.de/sbl/mdaten_analyse/dksuche_a?SEARCH_VALUE=${isin}</field>
<field name="nohtml" eval="True"/>
<field name="rgxdate">\nDatum\s*(?:\/ Uhrzeit)*: (\d+\.\d+\.\d+)\s*(?:\/ \d+:\d+)*\s*\n</field>
<field name="rgxdate">\nDatum / Uhrzeit: (\d+\.\d+\.\d+) / \d+:\d+\s+\n</field>
<field name="rgxdatefmt">%d.%m.%y</field>
<field name="rgxrate">(?:Kurs aktuell|Rucknahmepreis)\s+[()\w\./\[\]!]+\s+(\d+,\d+)\s+(EUR|€)</field>
<field name="rgxrate">Kurs aktuell .* (\d+,\d+)\s+EUR.*\n</field>
<field name="rgxdecimal">,</field>
<field name="rgxident">\nWKN / ISIN: [A-Z,0-9]+ / ([A-Z,0-9]+)\s+\n</field>
<field name="rgxidtype">isin</field>
</record>
<record model="investment.source" id="web_faz">
<field name="name">FAZ</field>
<field name="url">https://www.faz.net/aktuell/finanzen/kurs/etf/</field>
<field name="nohtml" eval="True"/>
<field name="fixed_url" eval="True"/>
<field name="rgxdate">(\d{2}\.\d{2}\.\d{4})</field>
<field name="rgxdatefmt">%d.%m.%y</field>
<field name="rgxrate">\nKurs(\d+.\d+)\s+€\s+\n</field>
<field name="rgxdecimal">,</field>
<field name="rgxident">\nWKN:.*\sISIN: ([A-Z,0-9]+).*\n</field>
<field name="rgxidtype">isin</field>
</record>
</data>
</tryton>

View file

@ -1,2 +1,30 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import trytond.tests.test_tryton
import unittest
from trytond.modules.investment.tests.test_asset import AssetTestCase
from trytond.modules.investment.tests.test_rate import RateTestCase
from trytond.modules.investment.tests.test_source import SourceTestCase
from trytond.modules.investment.tests.test_wizard import WizardTestCase
__all__ = ['suite']
class InvestmentTestCase(\
WizardTestCase, \
SourceTestCase, \
RateTestCase,\
AssetTestCase,\
):
'Test investment module'
module = 'investment'
# end InvestmentTestCase
def suite():
suite = trytond.tests.test_tryton.suite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(InvestmentTestCase))
return suite

View file

@ -1,770 +0,0 @@
# -*- 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.tests.test_tryton import with_transaction
from trytond.pool import Pool
from trytond.modules.company.tests import create_company
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from decimal import Decimal
from datetime import time, date, datetime
class AssetTestCase(object):
""" test asset
"""
def prep_asset_company(self):
""" get/create company
"""
Company = Pool().get('company.company')
company = Company.search([])
if len(company) > 0:
company = company[0]
else:
company = create_company(name='m-ds')
return company
def prep_asset_product(
self, name='Product 1', description=None, unit='u',
unit_name='Units'):
""" create product
"""
pool = Pool()
Product = pool.get('product.template')
Uom = pool.get('product.uom')
uom, = Uom.search([('symbol', '=', unit)])
prod_templ, = Product.create([{
'name': name,
'type': 'assets',
'list_price': Decimal('1.0'),
'default_uom': uom.id,
'products': [('create', [{
'description': description,
}])],
}])
self.assertEqual(prod_templ.default_uom.symbol, unit)
self.assertEqual(prod_templ.products[0].description, description)
return prod_templ.products[0]
def prep_asset_item(self, company, product):
""" create asset
"""
pool = Pool()
Asset = pool.get('investment.asset')
asset, = Asset.create([{
'company': company.id,
'product': product.id,
'currency': company.currency.id,
'currency_digits': 4,
'uom': product.default_uom.id,
}])
self.assertEqual(asset.rec_name, '%s | - usd/%s | -' % (
product.rec_name,
asset.uom.symbol,
))
self.assertEqual(asset.currency.rec_name, 'usd')
self.assertEqual(asset.currency_digits, 4)
self.assertEqual(asset.product.rec_name, product.name)
self.assertEqual(asset.uom.symbol, product.default_uom.symbol)
return asset
@with_transaction()
def test_asset_create(self):
""" create asset
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(
company=company,
product=product)
self.assertEqual(asset.symbol, 'usd/u')
self.assertEqual(asset.asset_symbol.symbol, 'usd/u')
# check ranges
Asset.write(*[
[asset],
{
'currency_digits': 1,
}])
self.assertRaisesRegex(
UserError,
'ss',
Asset.write,
*[[asset], {
'currency_digits': -1,
}])
@with_transaction()
def test_asset_rec_name(self):
""" create asset
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(
company=company,
product=product)
self.assertEqual(asset.rec_name, 'Product 1 | - usd/u | -')
Asset.write(*[
[asset],
{
'rates': [('create', [{
'date': date(2022, 5, 15),
'rate': Decimal('2.45'),
}])],
}])
self.assertEqual(
asset.rec_name,
'Product 1 | 2.4500 usd/u | 05/15/2022')
self.assertEqual(
Asset.search_count([('name', '=', 'Product 1')]),
1)
@with_transaction()
def test_asset_order_and_search_rate_and_date(self):
""" create asset, check order of rate + date
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
product1 = self.prep_asset_product(
name='Product 1',
description='some asset')
product2 = self.prep_asset_product(
name='Product 2',
description='some asset')
asset1 = self.prep_asset_item(company=company, product=product1)
asset2 = self.prep_asset_item(company=company, product=product2)
Asset.write(*[
[asset1],
{
'rates': [('create', [{
'date': date(2022, 5, 18),
'rate': Decimal('3.5'),
}, {
'date': date(2022, 5, 15),
'rate': Decimal('2.45'),
}])],
},
[asset2],
{
'rates': [('create', [{
'date': date(2022, 5, 17),
'rate': Decimal('2.6'),
}, {
'date': date(2022, 5, 14),
'rate': Decimal('2.4'),
}])],
},
])
self.assertEqual(
asset1.rec_name,
'Product 1 | 3.5000 usd/u | 05/18/2022')
self.assertEqual(
asset2.rec_name,
'Product 2 | 2.6000 usd/u | 05/17/2022')
assets = Asset.search([], order=[('date', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].date, date(2022, 5, 17))
self.assertEqual(assets[1].date, date(2022, 5, 18))
assets = Asset.search([], order=[('date', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].date, date(2022, 5, 18))
self.assertEqual(assets[1].date, date(2022, 5, 17))
assets = Asset.search([], order=[('rate', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].rate, Decimal('2.6'))
self.assertEqual(assets[1].rate, Decimal('3.5'))
assets = Asset.search([], order=[('rate', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].rate, Decimal('3.5'))
self.assertEqual(assets[1].rate, Decimal('2.6'))
self.assertEqual(Asset.search_count([
('date', '=', date(2022, 5, 17)),
]), 1)
self.assertEqual(Asset.search_count([
('date', '>=', date(2022, 5, 17)),
]), 2)
self.assertEqual(Asset.search_count([
('date', '<', date(2022, 5, 17)),
]), 0)
@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()
with Transaction().set_context({'company': company.id}):
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/u | -')
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/u | 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(days=0)
with Transaction().set_context({
'qdate': date(2022, 5, 16)}):
query = tab_percent.select(
tab_percent.id,
tab_percent.date,
tab_percent.percent,
where=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_daterange(self):
""" create asset, add rates, check selection of
value
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
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)
self.assertEqual(asset1.rec_name, 'Product 1 | - usd/u | -')
self.assertEqual(asset2.rec_name, 'Product 1 | - usd/u | -')
Asset.write(*[
[asset1],
{
'rates': [('create', [{
'date': date(2022, 5, 15),
'rate': Decimal('2.45'),
}, {
'date': date(2022, 5, 16),
'rate': Decimal('2.6'),
}])],
},
[asset2],
{
'rates': [('create', [{
'date': date(2022, 5, 14),
'rate': Decimal('5.75'),
}, {
'date': date(2022, 5, 15),
'rate': Decimal('5.25'),
}])],
},
])
self.assertEqual(
asset1.rec_name,
'Product 1 | 2.6000 usd/u | 05/16/2022')
self.assertEqual(
asset2.rec_name,
'Product 1 | 5.2500 usd/u | 05/15/2022')
self.assertEqual(asset1.change_day1, Decimal('6.12'))
self.assertEqual(asset2.change_day1, Decimal('-8.7'))
self.assertEqual(asset1.change_month1, None)
self.assertEqual(asset2.change_month1, None)
self.assertEqual(asset1.change_month3, None)
self.assertEqual(asset2.change_month3, None)
self.assertEqual(asset1.change_month6, None)
self.assertEqual(asset2.change_month6, None)
self.assertEqual(asset1.change_month12, None)
self.assertEqual(asset2.change_month12, None)
# check ordering
assets = Asset.search([
('change_day1', '!=', Decimal('0.0')),
], order=[('change_day1', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].change_day1, Decimal('-8.7'))
self.assertEqual(assets[1].change_day1, Decimal('6.12'))
assets = Asset.search([
('change_day1', '!=', Decimal('0.0')),
], order=[('change_day1', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].change_day1, Decimal('6.12'))
self.assertEqual(assets[1].change_day1, Decimal('-8.7'))
# check 5-day-range
# four days
Asset.write(*[
[asset1],
{
'rates': [('write', [asset1.rates[1]], {
'date': date(2022, 5, 12),
})],
}])
self.assertEqual(asset1.rates[0].date, date(2022, 5, 16))
self.assertEqual(asset1.rates[1].date, date(2022, 5, 12))
self.assertEqual(asset1.change_day1, Decimal('6.12'))
# five days
Asset.write(*[
[asset1],
{
'rates': [('write', [asset1.rates[1]], {
'date': date(2022, 5, 11),
})],
}])
self.assertEqual(asset1.rates[0].date, date(2022, 5, 16))
self.assertEqual(asset1.rates[1].date, date(2022, 5, 11))
self.assertEqual(asset1.change_day1, Decimal('6.12'))
# six days
Asset.write(*[
[asset1],
{
'rates': [('write', [asset1.rates[1]], {
'date': date(2022, 5, 10),
})],
}])
self.assertEqual(asset1.rates[0].date, date(2022, 5, 16))
self.assertEqual(asset1.rates[1].date, date(2022, 5, 10))
self.assertEqual(asset1.change_day1, None)
@with_transaction()
def test_asset_percentges_values(self):
""" create asset, add rates, check percentages
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
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/u | -')
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, 4, 14),
'rate': Decimal('2.2'),
}, {
'date': date(2022, 2, 14),
'rate': Decimal('2.8'),
},])],
}])
self.assertEqual(
asset1.rec_name,
'Product 1 | 2.6000 usd/u | 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, 4, 14))
self.assertEqual(asset1.rates[3].date, date(2022, 2, 14))
self.assertEqual(asset1.change_day1, Decimal('6.12'))
self.assertEqual(asset1.change_month1, Decimal('18.18'))
self.assertEqual(asset1.change_month3, Decimal('-7.14'))
self.assertEqual(asset1.change_month6, None)
self.assertEqual(asset1.change_month12, None)
# call order-functions
Asset.search([], order=[('change_day1', 'ASC')])
Asset.search([], order=[('change_month1', 'ASC')])
Asset.search([], order=[('change_month3', 'ASC')])
Asset.search([], order=[('change_month6', 'ASC')])
Asset.search([], order=[('change_month12', 'ASC')])
# searcher
self.assertEqual(
Asset.search_count([('change_day1', '>', Decimal('6.1'))]),
1)
self.assertEqual(
Asset.search_count([('change_day1', '>', Decimal('6.15'))]),
0)
self.assertEqual(
Asset.search_count([('change_day1', '=', Decimal('6.12'))]),
1)
self.assertEqual(
Asset.search_count([('change_month1', '>', Decimal('18.0'))]),
1)
self.assertEqual(
Asset.search_count([('change_month1', '>', Decimal('18.18'))]),
0)
self.assertEqual(
Asset.search_count([('change_month1', '=', Decimal('18.18'))]),
1)
self.assertEqual(
Asset.search_count([('change_month3', '=', Decimal('-7.14'))]),
1)
self.assertEqual(
Asset.search_count([('change_month6', '=', None)]),
1)
@with_transaction()
def test_asset_check_onlinesource_onoff(self):
""" create asset, switch online-source on/off
"""
pool = Pool()
OnlineSource = pool.get('investment.source')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(company=company, product=product)
o_source, = OnlineSource.create([{
'name': 'Source 1',
}])
self.assertEqual(len(asset.updtsources), 0)
self.assertEqual(asset.updttime, time(14, 0))
asset.updtsources = [o_source]
asset.updttime = time(10, 45)
asset.save()
self.assertEqual(len(asset.updtsources), 1)
self.assertEqual(asset.updtsources[0].rec_name, 'Source 1')
self.assertEqual(asset.updttime, time(10, 45))
asset.updtsources = []
asset.on_change_updtsources()
self.assertEqual(len(asset.updtsources), 0)
self.assertEqual(asset.updttime, None)
@with_transaction()
def test_asset_check_update_select(self):
""" create asset, add online-source,
check selection of assets to update
"""
pool = Pool()
OnlineSource = pool.get('investment.source')
Asset = pool.get('investment.asset')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(company=company, product=product)
o_source, = OnlineSource.create([{
'name': 'Source 1',
}])
Asset.write(*[
[asset],
{
'updtsources': [('add', [o_source.id])],
'updttime': time(10, 45),
}])
with Transaction().set_context({'qdate': date(2022, 10, 14)}):
# re-read to make context work
asset2, = Asset.browse([asset.id])
self.assertEqual(len(asset2.updtsources), 1)
self.assertEqual(asset2.updtsources[0].rec_name, 'Source 1')
self.assertEqual(asset2.updttime, time(10, 45))
self.assertEqual(len(asset2.rates), 0)
# qdate = 2022-10-14 simulates existence of record at this day
# next call would be the 15. - but its saturday,
# next-call-date is moved to 17.
self.assertEqual(
asset2.nextupdate,
datetime(2022, 10, 17, 10, 45))
self.assertEqual(
Asset.search_count([
('nextupdate', '<', datetime(2022, 10, 17, 10, 45))]),
0)
self.assertEqual(
Asset.search_count([
('nextupdate', '>=', datetime(2022, 10, 17, 10, 45))]),
1)
# add rate at next monday
Asset.write(*[
[asset],
{
'rates': [('create', [{
'date': date(2022, 10, 17), # monday
'rate': Decimal('1.5'),
}])],
}])
self.assertEqual(len(asset.rates), 1)
asset2, = Asset.browse([asset.id])
self.assertEqual(asset.updtsources[0].rec_name, 'Source 1')
self.assertEqual(asset.updttime, time(10, 45))
self.assertEqual(len(asset.rates), 1)
self.assertEqual(asset.rates[0].date, date(2022, 10, 17))
self.assertEqual(asset.nextupdate, datetime(2022, 10, 18, 10, 45))
self.assertEqual(
Asset.search_count([
('nextupdate', '<', datetime(2022, 10, 18, 10, 45))]),
0)
self.assertEqual(
Asset.search_count([
('nextupdate', '>=', datetime(2022, 10, 18, 10, 45))]),
1)
# add rate at today
Asset.write(*[
[asset],
{
'rates': [('create', [{
'date': date(2022, 10, 18),
'rate': Decimal('1.5'),
}])],
}])
self.assertEqual(len(asset.rates), 2)
asset2, = Asset.browse([asset.id])
self.assertEqual(asset2.updtsources[0].rec_name, 'Source 1')
self.assertEqual(asset2.updttime, time(10, 45))
self.assertEqual(len(asset2.rates), 2)
self.assertEqual(asset2.rates[0].date, date(2022, 10, 18))
self.assertEqual(asset2.nextupdate, datetime(2022, 10, 19, 10, 45))
self.assertEqual(
Asset.search_count([
('nextupdate', '<', datetime(2022, 10, 19, 10, 45))]),
0)
self.assertEqual(
Asset.search_count([
('nextupdate', '>=', datetime(2022, 10, 19, 10, 45))]),
1)
@with_transaction()
def test_asset_indentifiers(self):
""" create asset, add identifiers
"""
pool = Pool()
Product = pool.get('product.product')
Asset = pool.get('investment.asset')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
product1 = self.prep_asset_product(
name='Product unit', unit='u')
product2 = self.prep_asset_product(
name='Product gram', unit='g')
asset1 = self.prep_asset_item(company=company, product=product1)
asset2 = self.prep_asset_item(company=company, product=product2)
Product.write(*[
[product1],
{
'identifiers': [('create', [{
'type': 'wkn',
'code': '965515',
}, {
'type': 'secsymb',
'code': '1472977',
}, {
'type': 'isin',
'code': 'XC0009655157',
}, ])],
},
[product2],
{
'identifiers': [('create', [{
'type': 'wkn',
'code': '965310',
}, {
'type': 'secsymb',
'code': '1431157',
}, {
'type': 'isin',
'code': 'XC0009653103',
}, ])],
},
])
self.assertEqual(asset1.wkn, '965515')
self.assertEqual(asset1.isin, 'XC0009655157')
self.assertEqual(asset1.secsymb, '1472977')
self.assertEqual(
Asset.search_count([('wkn', '=', '965515')]),
1)
self.assertEqual(
Asset.search_count([('isin', '=', 'XC0009655157')]),
1)
self.assertEqual(
Asset.search_count([('secsymb', '=', '1472977')]),
1)
self.assertEqual(
Asset.search_count([('rec_name', '=', '965515')]),
1)
self.assertEqual(
Asset.search_count([('rec_name', '=', 'XC0009655157')]),
1)
self.assertEqual(
Asset.search_count([('rec_name', '=', '1472977')]),
1)
self.assertEqual(
Asset.search_count([('name', '=', '965515')]),
1)
self.assertEqual(
Asset.search_count([('name', '=', 'XC0009655157')]),
1)
self.assertEqual(
Asset.search_count([('name', '=', '1472977')]),
1)
self.assertEqual(
Asset.search_count([('name', '=', 'Product unit')]),
1)
self.assertEqual(Asset.search_count([
('wkn', 'ilike', '9655%'),
]), 1)
self.assertEqual(Asset.search_count([
('wkn', 'ilike', '965%'),
]), 2)
self.assertEqual(asset2.wkn, '965310')
self.assertEqual(asset2.isin, 'XC0009653103')
self.assertEqual(asset2.secsymb, '1431157')
# order wkn
assets = Asset.search([], order=[('wkn', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].wkn, '965310')
self.assertEqual(assets[1].wkn, '965515')
assets = Asset.search([], order=[('wkn', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].wkn, '965515')
self.assertEqual(assets[1].wkn, '965310')
# order isin
assets = Asset.search([], order=[('isin', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].isin, 'XC0009653103')
self.assertEqual(assets[1].isin, 'XC0009655157')
assets = Asset.search([], order=[('wkn', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].isin, 'XC0009655157')
self.assertEqual(assets[1].isin, 'XC0009653103')
# order secsymb
assets = Asset.search([], order=[('secsymb', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].secsymb, '1431157')
self.assertEqual(assets[1].secsymb, '1472977')
assets = Asset.search([], order=[('wkn', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].secsymb, '1472977')
self.assertEqual(assets[1].secsymb, '1431157')
@with_transaction()
def test_asset_check_product_update(self):
""" check update of product on asset
"""
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
product1 = self.prep_asset_product(
name='Product unit', unit='u')
product2 = self.prep_asset_product(
name='Product gram', unit='g')
self.assertEqual(product2.default_uom.digits, 2)
asset = self.prep_asset_item(company=company, product=product1)
self.assertEqual(asset.product.rec_name, 'Product unit')
self.assertEqual(asset.product.default_uom.rec_name, 'Unit')
self.assertEqual(asset.uom.rec_name, 'Unit')
self.assertEqual(asset.currency_digits, 4)
asset.product = product2
asset.on_change_product()
asset.save()
self.assertEqual(asset.product.rec_name, 'Product gram')
self.assertEqual(asset.product.default_uom.rec_name, 'Gram')
self.assertEqual(asset.uom.rec_name, 'Gram')
asset.on_change_currency()
asset.save()
self.assertEqual(asset.currency_digits, 2)
# end AssetTestCase

View file

@ -1,50 +0,0 @@
# -*- 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.tests.test_tryton import with_transaction
from trytond.transaction import Transaction
from trytond.pool import Pool
from decimal import Decimal
from datetime import date
class RateTestCase(object):
""" test rate
"""
@with_transaction()
def test_rate_create(self):
""" create rate
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(company=company, product=product)
Asset.write(*[
[asset],
{
'rates': [('create', [{
'date': date(2022, 5, 1),
'rate': Decimal('2.5'),
}, {
'date': date(2022, 5, 2),
'rate': Decimal('2.8'),
}])],
}])
self.assertEqual(len(asset.rates), 2)
self.assertEqual(asset.rates[0].date, date(2022, 5, 2))
self.assertEqual(asset.rates[0].rate, Decimal('2.8'))
self.assertEqual(asset.rates[0].uom.rec_name, 'Unit')
self.assertEqual(asset.rates[0].asset_digits, 4)
self.assertEqual(asset.rates[0].currency.rec_name, 'usd')
self.assertEqual(asset.rates[0].symbol, '%')
self.assertEqual(asset.change_symbol.symbol, '%')
# end RateTestCase

View file

@ -1,185 +0,0 @@
# -*- 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.tests.test_tryton import with_transaction
from trytond.pool import Pool
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from decimal import Decimal
from datetime import time, date, datetime
from unittest.mock import MagicMock
from requests import Response
import requests
class SourceTestCase(object):
""" test online source
"""
@with_transaction()
def test_waitlist_source_request(self):
""" create source, call server
"""
pool = Pool()
OSource = pool.get('investment.source')
Asset = pool.get('investment.asset')
Product = pool.get('product.product')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
osource, = OSource.create([{
'name': 'Source 1',
'url': 'https://foo.bar/${isin}/${nsin}/${symbol}',
'rgxdate': 'Course Date (\\d+.\\d+.\\d+) Today',
'rgxdatefmt': '%d.%m.%Y',
'rgxrate': 'High (\\d+,\\d+) EUR',
'rgxdecimal': ',',
}])
self.assertEqual(osource.rec_name, 'Source 1')
product = self.prep_asset_product(
name='Product 1',
description='some asset')
Product.write(*[
[product],
{
'identifiers': [('create', [{
'type': 'wkn',
'code': '965515',
}, {
'type': 'secsymb',
'code': '1472977',
}, {
'type': 'isin',
'code': 'XC0009655157',
}, ])],
}])
asset = self.prep_asset_item(company=company, product=product)
Asset.write(*[
[asset],
{
'updtsources': [('add', [osource.id])],
}])
with Transaction().set_context({
'qdate': date(2022, 10, 1), # saturday
'qdatetime': datetime(2022, 10, 2, 10, 0, 0)}):
asset2, = Asset.browse([asset])
self.assertEqual(asset2.wkn, '965515')
self.assertEqual(asset2.isin, 'XC0009655157')
self.assertEqual(asset2.secsymb, '1472977')
self.assertEqual(asset2.updttime, time(14, 0))
self.assertEqual(len(asset2.updtsources), 1)
self.assertEqual(asset2.updtsources[0].rec_name, 'Source 1')
self.assertEqual(asset2.updtdays, 'work')
self.assertEqual(
asset2.nextupdate, datetime(2022, 10, 3, 14, 0))
self.assertEqual(len(asset.rates), 0)
# fake server-response
resp1 = Response()
resp1._content = """<html><body>Response from finance-server
Course Date 14.08.2022 Today
High 34,87 EUR
</body></html>""".encode('utf8')
resp1.status_code = 200
resp1.reason = 'OK'
requests.get = MagicMock(return_value=resp1)
OSource.update_rate(asset)
self.assertEqual(len(asset.rates), 1)
self.assertEqual(asset.rates[0].date, date(2022, 8, 14))
self.assertEqual(asset.rates[0].rate, Decimal('34.87'))
@with_transaction()
def test_waitlist_source_check_regex(self):
""" create source, check convert
"""
pool = Pool()
OSource = pool.get('investment.source')
osource, = OSource.create([{
'name': 'Source 1',
'rgxdate': 'Course Date (\\d+.\\d+.\\d+) Today',
'rgxdatefmt': '%d.%m.%Y',
'rgxrate': 'High (\\d+,\\d+) EUR',
'rgxdecimal': ',',
}])
self.assertEqual(osource.rec_name, 'Source 1')
self.assertEqual(osource.get_regex_result(
'The Course Date 14.03.2022 Today, High 13,43 EUR',
'rgxdate'
), date(2022, 3, 14))
self.assertEqual(osource.get_regex_result(
'The Course Date 14.03.2022 Today, High 13,43 EUR',
'rgxrate'
), Decimal('13.43'))
# iso-date
OSource.write(*[
[osource],
{
'rgxdate': 'Course Date (\\d+-\\d+-\\d+) Today',
'rgxdatefmt': '%Y-%m-%d',
}])
self.assertEqual(osource.get_regex_result(
'The Course Date 2022-03-14 Today, High 13,43 EUR',
'rgxdate'
), date(2022, 3, 14))
@with_transaction()
def test_waitlist_source_check_regex_validate(self):
""" create source, check validation of regex-code
"""
pool = Pool()
OSource = pool.get('investment.source')
self.assertRaisesRegex(
UserError,
r"Error in regex code of field 'Date': nothing to repeat " +
r"at position 0 \[\*+ multiple repeat\]",
OSource.create,
[{
'name': 'Check date',
'rgxdate': '** multiple repeat',
'rgxrate': 'rate -- multiple repeat',
'rgxident': 'identifiert ** multiple repeat',
}])
self.assertRaisesRegex(
UserError,
r"Error in regex code of field 'Rate': multiple repeat " +
r"at position 6 \[rate \*+ multiple repeat\]",
OSource.create,
[{
'name': 'Check rate',
'rgxdate': '-- multiple repeat',
'rgxrate': 'rate ** multiple repeat',
'rgxident': 'identifiert -- multiple repeat',
}])
self.assertRaisesRegex(
UserError,
r"Error in regex code of field 'Identifier': multiple " +
r"repeat at position 13 \[identifiert \*+ multiple repeat\]",
OSource.create,
[{
'name': 'Check rgxident',
'rgxdate': '-- multiple repeat',
'rgxrate': 'rate -- multiple repeat',
'rgxident': 'identifiert ** multiple repeat',
}])
OSource.create([{
'name': 'Check rgxident',
'rgxdate': '-- multiple repeat',
'rgxrate': 'rate -- multiple repeat',
'rgxident': 'identifiert -- multiple repeat',
}])
# end SourceTestCase

739
tests/test_asset.py Normal file
View file

@ -0,0 +1,739 @@
# -*- 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.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.pool import Pool
from trytond.modules.company.tests import create_company
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from decimal import Decimal
from datetime import time, date, datetime
class AssetTestCase(ModuleTestCase):
'Test asset module'
module = 'investment'
def prep_asset_company(self):
""" get/create company
"""
Company = Pool().get('company.company')
company = Company.search([])
if len(company) > 0:
company = company[0]
else :
company = create_company(name='m-ds')
return company
def prep_asset_product(self, name='Product 1', description=None, unit='u', unit_name='Units'):
""" create product
"""
pool = Pool()
Product = pool.get('product.template')
Uom = pool.get('product.uom')
uom, = Uom.search([('symbol', '=', unit)])
prod_templ, = Product.create([{
'name': name,
'type': 'assets',
'list_price': Decimal('1.0'),
'default_uom': uom.id,
'products': [('create', [{
'description': description,
}])],
}])
self.assertEqual(prod_templ.default_uom.symbol, unit)
self.assertEqual(prod_templ.products[0].description, description)
return prod_templ.products[0]
def prep_asset_item(self, company, product):
""" create asset
"""
pool = Pool()
Asset = pool.get('investment.asset')
asset, = Asset.create([{
'company': company.id,
'product': product.id,
'currency': company.currency.id,
'currency_digits': 4,
'uom': product.default_uom.id,
}])
self.assertEqual(asset.rec_name, '%s | - usd/%s | -' % (
product.rec_name,
asset.uom.symbol,
))
self.assertEqual(asset.currency.rec_name, 'usd')
self.assertEqual(asset.currency_digits, 4)
self.assertEqual(asset.product.rec_name, product.name)
self.assertEqual(asset.uom.symbol, product.default_uom.symbol)
return asset
@with_transaction()
def test_asset_create(self):
""" create asset
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(
company=company,
product = product)
self.assertEqual(asset.symbol, 'usd/u')
self.assertEqual(asset.asset_symbol.symbol, 'usd/u')
# check ranges
Asset.write(*[
[asset],
{
'currency_digits': 1,
}])
self.assertRaisesRegex(UserError,
'ss',
Asset.write,
*[[asset], {
'currency_digits': -1,
}])
@with_transaction()
def test_asset_rec_name(self):
""" create asset
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(
company=company,
product = product)
self.assertEqual(asset.rec_name, 'Product 1 | - usd/u | -')
Asset.write(*[
[asset],
{
'rates': [('create', [{
'date': date(2022, 5, 15),
'rate': Decimal('2.45'),
}])],
}])
self.assertEqual(asset.rec_name, 'Product 1 | 2.4500 usd/u | 05/15/2022')
self.assertEqual(Asset.search_count([('name', '=', 'Product 1')]), 1)
@with_transaction()
def test_asset_order_and_search_rate_and_date(self):
""" create asset, check order of rate + date
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
product1 = self.prep_asset_product(
name='Product 1',
description='some asset')
product2 = self.prep_asset_product(
name='Product 2',
description='some asset')
asset1 = self.prep_asset_item(
company=company,
product = product1)
asset2 = self.prep_asset_item(
company=company,
product = product2)
Asset.write(*[
[asset1],
{
'rates': [('create', [{
'date': date(2022, 5, 18),
'rate': Decimal('3.5'),
}, {
'date': date(2022, 5, 15),
'rate': Decimal('2.45'),
}])],
},
[asset2],
{
'rates': [('create', [{
'date': date(2022, 5, 17),
'rate': Decimal('2.6'),
}, {
'date': date(2022, 5, 14),
'rate': Decimal('2.4'),
}])],
},
])
self.assertEqual(asset1.rec_name, 'Product 1 | 3.5000 usd/u | 05/18/2022')
self.assertEqual(asset2.rec_name, 'Product 2 | 2.6000 usd/u | 05/17/2022')
assets = Asset.search([], order=[('date', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].date, date(2022, 5, 17))
self.assertEqual(assets[1].date, date(2022, 5, 18))
assets = Asset.search([], order=[('date', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].date, date(2022, 5, 18))
self.assertEqual(assets[1].date, date(2022, 5, 17))
assets = Asset.search([], order=[('rate', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].rate, Decimal('2.6'))
self.assertEqual(assets[1].rate, Decimal('3.5'))
assets = Asset.search([], order=[('rate', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].rate, Decimal('3.5'))
self.assertEqual(assets[1].rate, Decimal('2.6'))
self.assertEqual(Asset.search_count([
('date', '=', date(2022, 5, 17)),
]), 1)
self.assertEqual(Asset.search_count([
('date', '>=', date(2022, 5, 17)),
]), 2)
self.assertEqual(Asset.search_count([
('date', '<', date(2022, 5, 17)),
]), 0)
@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/u | -')
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/u | 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(days=0)
with Transaction().set_context({
'qdate': date(2022, 5, 16),
}):
query = tab_percent.select(
tab_percent.id,
tab_percent.date,
tab_percent.percent,
where=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_daterange(self):
""" create asset, add rates, check selection of
value
"""
Asset = Pool().get('investment.asset')
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)
self.assertEqual(asset1.rec_name, 'Product 1 | - usd/u | -')
self.assertEqual(asset2.rec_name, 'Product 1 | - usd/u | -')
Asset.write(*[
[asset1],
{
'rates': [('create', [{
'date': date(2022, 5, 15),
'rate': Decimal('2.45'),
}, {
'date': date(2022, 5, 16),
'rate': Decimal('2.6'),
}])],
},
[asset2],
{
'rates': [('create', [{
'date': date(2022, 5, 14),
'rate': Decimal('5.75'),
}, {
'date': date(2022, 5, 15),
'rate': Decimal('5.25'),
}])],
},
])
self.assertEqual(asset1.rec_name, 'Product 1 | 2.6000 usd/u | 05/16/2022')
self.assertEqual(asset2.rec_name, 'Product 1 | 5.2500 usd/u | 05/15/2022')
self.assertEqual(asset1.change_day1, Decimal('6.12'))
self.assertEqual(asset2.change_day1, Decimal('-8.7'))
self.assertEqual(asset1.change_month1, None)
self.assertEqual(asset2.change_month1, None)
self.assertEqual(asset1.change_month3, None)
self.assertEqual(asset2.change_month3, None)
self.assertEqual(asset1.change_month6, None)
self.assertEqual(asset2.change_month6, None)
self.assertEqual(asset1.change_month12, None)
self.assertEqual(asset2.change_month12, None)
# check ordering
assets = Asset.search([
('change_day1', '!=', Decimal('0.0')),
], order=[('change_day1', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].change_day1, Decimal('-8.7'))
self.assertEqual(assets[1].change_day1, Decimal('6.12'))
assets = Asset.search([
('change_day1', '!=', Decimal('0.0')),
], order=[('change_day1', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].change_day1, Decimal('6.12'))
self.assertEqual(assets[1].change_day1, Decimal('-8.7'))
# check 5-day-range
# four days
Asset.write(*[
[asset1],
{
'rates': [('write', [asset1.rates[1]], {
'date': date(2022, 5, 12),
})],
}])
self.assertEqual(asset1.rates[0].date, date(2022, 5, 16))
self.assertEqual(asset1.rates[1].date, date(2022, 5, 12))
self.assertEqual(asset1.change_day1, Decimal('6.12'))
# five days
Asset.write(*[
[asset1],
{
'rates': [('write', [asset1.rates[1]], {
'date': date(2022, 5, 11),
})],
}])
self.assertEqual(asset1.rates[0].date, date(2022, 5, 16))
self.assertEqual(asset1.rates[1].date, date(2022, 5, 11))
self.assertEqual(asset1.change_day1, Decimal('6.12'))
# six days
Asset.write(*[
[asset1],
{
'rates': [('write', [asset1.rates[1]], {
'date': date(2022, 5, 10),
})],
}])
self.assertEqual(asset1.rates[0].date, date(2022, 5, 16))
self.assertEqual(asset1.rates[1].date, date(2022, 5, 10))
self.assertEqual(asset1.change_day1, None)
@with_transaction()
def test_asset_percentges_values(self):
""" create asset, add rates, check percentages
"""
Asset = Pool().get('investment.asset')
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/u | -')
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, 4, 14),
'rate': Decimal('2.2'),
}, {
'date': date(2022, 2, 14),
'rate': Decimal('2.8'),
},])],
}])
self.assertEqual(asset1.rec_name, 'Product 1 | 2.6000 usd/u | 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, 4, 14))
self.assertEqual(asset1.rates[3].date, date(2022, 2, 14))
self.assertEqual(asset1.change_day1, Decimal('6.12'))
self.assertEqual(asset1.change_month1, Decimal('18.18'))
self.assertEqual(asset1.change_month3, Decimal('-7.14'))
self.assertEqual(asset1.change_month6, None)
self.assertEqual(asset1.change_month12, None)
# call order-functions
Asset.search([], order=[('change_day1', 'ASC')])
Asset.search([], order=[('change_month1', 'ASC')])
Asset.search([], order=[('change_month3', 'ASC')])
Asset.search([], order=[('change_month6', 'ASC')])
Asset.search([], order=[('change_month12', 'ASC')])
# searcher
self.assertEqual(
Asset.search_count([('change_day1', '>', Decimal('6.1'))]),
1)
self.assertEqual(
Asset.search_count([('change_day1', '>', Decimal('6.15'))]),
0)
self.assertEqual(
Asset.search_count([('change_day1', '=', Decimal('6.12'))]),
1)
self.assertEqual(
Asset.search_count([('change_month1', '>', Decimal('18.0'))]),
1)
self.assertEqual(
Asset.search_count([('change_month1', '>', Decimal('18.18'))]),
0)
self.assertEqual(
Asset.search_count([('change_month1', '=', Decimal('18.18'))]),
1)
self.assertEqual(
Asset.search_count([('change_month3', '=', Decimal('-7.14'))]),
1)
self.assertEqual(
Asset.search_count([('change_month6', '=', None)]),
1)
@with_transaction()
def test_asset_check_onlinesource_onoff(self):
""" create asset, switch online-source on/off
"""
pool = Pool()
OnlineSource = pool.get('investment.source')
Asset = pool.get('investment.asset')
company = self.prep_asset_company()
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(
company=company,
product = product)
o_source, = OnlineSource.create([{
'name': 'Source 1',
}])
self.assertEqual(len(asset.updtsources), 0)
self.assertEqual(asset.updttime, time(14,0))
asset.updtsources = [o_source]
asset.updttime = time(10, 45)
asset.save()
self.assertEqual(len(asset.updtsources), 1)
self.assertEqual(asset.updtsources[0].rec_name, 'Source 1')
self.assertEqual(asset.updttime, time(10, 45))
asset.updtsources = []
asset.on_change_updtsources()
self.assertEqual(len(asset.updtsources), 0)
self.assertEqual(asset.updttime, None)
@with_transaction()
def test_asset_check_update_select(self):
""" create asset, add online-source,
check selection of assets to update
"""
pool = Pool()
OnlineSource = pool.get('investment.source')
Asset = pool.get('investment.asset')
company = self.prep_asset_company()
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(
company=company,
product = product)
o_source, = OnlineSource.create([{
'name': 'Source 1',
}])
Asset.write(*[
[asset],
{
'updtsources': [('add', [o_source.id])],
'updttime': time(10, 45),
}])
with Transaction().set_context({
'qdate': date(2022, 10, 14), # friday
}):
# re-read to make context work
asset2, = Asset.browse([asset.id])
self.assertEqual(len(asset2.updtsources), 1)
self.assertEqual(asset2.updtsources[0].rec_name, 'Source 1')
self.assertEqual(asset2.updttime, time(10, 45))
self.assertEqual(len(asset2.rates), 0)
# qdate = 2022-10-14 simulates existence of record at this day
# next call would be the 15. - but its saturday,
# next-call-date is moved to 17.
self.assertEqual(asset2.nextupdate, datetime(2022, 10, 17, 10, 45))
self.assertEqual(
Asset.search_count([('nextupdate', '<', datetime(2022, 10, 17, 10, 45))]),
0)
self.assertEqual(
Asset.search_count([('nextupdate', '>=', datetime(2022, 10, 17, 10, 45))]),
1)
# add rate at next monday
Asset.write(*[
[asset],
{
'rates': [('create', [{
'date': date(2022, 10, 17), # monday
'rate': Decimal('1.5'),
}])],
}])
self.assertEqual(len(asset.rates), 1)
asset2, = Asset.browse([asset.id])
self.assertEqual(asset.updtsources[0].rec_name, 'Source 1')
self.assertEqual(asset.updttime, time(10, 45))
self.assertEqual(len(asset.rates), 1)
self.assertEqual(asset.rates[0].date, date(2022, 10, 17))
self.assertEqual(asset.nextupdate, datetime(2022, 10, 18, 10, 45))
self.assertEqual(
Asset.search_count([('nextupdate', '<', datetime(2022, 10, 18, 10, 45))]),
0)
self.assertEqual(
Asset.search_count([('nextupdate', '>=', datetime(2022, 10, 18, 10, 45))]),
1)
# add rate at today
Asset.write(*[
[asset],
{
'rates': [('create', [{
'date': date(2022, 10, 18),
'rate': Decimal('1.5'),
}])],
}])
self.assertEqual(len(asset.rates), 2)
asset2, = Asset.browse([asset.id])
self.assertEqual(asset2.updtsources[0].rec_name, 'Source 1')
self.assertEqual(asset2.updttime, time(10, 45))
self.assertEqual(len(asset2.rates), 2)
self.assertEqual(asset2.rates[0].date, date(2022, 10, 18))
self.assertEqual(asset2.nextupdate, datetime(2022, 10, 19, 10, 45))
self.assertEqual(
Asset.search_count([('nextupdate', '<', datetime(2022, 10, 19, 10, 45))]),
0)
self.assertEqual(
Asset.search_count([('nextupdate', '>=', datetime(2022, 10, 19, 10, 45))]),
1)
@with_transaction()
def test_asset_indentifiers(self):
""" create asset, add identifiers
"""
pool = Pool()
Product = pool.get('product.product')
Asset = pool.get('investment.asset')
company = self.prep_asset_company()
product1 = self.prep_asset_product(
name='Product unit', unit='u')
product2 = self.prep_asset_product(
name='Product gram', unit='g')
asset1 = self.prep_asset_item(
company=company,
product = product1)
asset2 = self.prep_asset_item(
company=company,
product = product2)
Product.write(*[
[product1],
{
'identifiers': [('create', [{
'type': 'wkn',
'code': '965515',
}, {
'type': 'secsymb',
'code': '1472977',
}, {
'type': 'isin',
'code': 'XC0009655157',
}, ])],
},
[product2],
{
'identifiers': [('create', [{
'type': 'wkn',
'code': '965310',
}, {
'type': 'secsymb',
'code': '1431157',
}, {
'type': 'isin',
'code': 'XC0009653103',
}, ])],
},
])
self.assertEqual(asset1.wkn, '965515')
self.assertEqual(asset1.isin, 'XC0009655157')
self.assertEqual(asset1.secsymb, '1472977')
self.assertEqual(Asset.search_count([('wkn', '=', '965515')]), 1)
self.assertEqual(Asset.search_count([('isin', '=', 'XC0009655157')]), 1)
self.assertEqual(Asset.search_count([('secsymb', '=', '1472977')]), 1)
self.assertEqual(Asset.search_count([('rec_name', '=', '965515')]), 1)
self.assertEqual(Asset.search_count([('rec_name', '=', 'XC0009655157')]), 1)
self.assertEqual(Asset.search_count([('rec_name', '=', '1472977')]), 1)
self.assertEqual(Asset.search_count([('name', '=', '965515')]), 1)
self.assertEqual(Asset.search_count([('name', '=', 'XC0009655157')]), 1)
self.assertEqual(Asset.search_count([('name', '=', '1472977')]), 1)
self.assertEqual(Asset.search_count([('name', '=', 'Product unit')]), 1)
self.assertEqual(Asset.search_count([
('wkn', 'ilike', '9655%'),
]), 1)
self.assertEqual(Asset.search_count([
('wkn', 'ilike', '965%'),
]), 2)
self.assertEqual(asset2.wkn, '965310')
self.assertEqual(asset2.isin, 'XC0009653103')
self.assertEqual(asset2.secsymb, '1431157')
# order wkn
assets = Asset.search([], order=[('wkn', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].wkn, '965310')
self.assertEqual(assets[1].wkn, '965515')
assets = Asset.search([], order=[('wkn', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].wkn, '965515')
self.assertEqual(assets[1].wkn, '965310')
# order isin
assets = Asset.search([], order=[('isin', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].isin, 'XC0009653103')
self.assertEqual(assets[1].isin, 'XC0009655157')
assets = Asset.search([], order=[('wkn', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].isin, 'XC0009655157')
self.assertEqual(assets[1].isin, 'XC0009653103')
# order secsymb
assets = Asset.search([], order=[('secsymb', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].secsymb, '1431157')
self.assertEqual(assets[1].secsymb, '1472977')
assets = Asset.search([], order=[('wkn', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].secsymb, '1472977')
self.assertEqual(assets[1].secsymb, '1431157')
@with_transaction()
def test_asset_check_product_update(self):
""" check update of product on asset
"""
company = self.prep_asset_company()
product1 = self.prep_asset_product(
name='Product unit', unit='u')
product2 = self.prep_asset_product(
name='Product gram', unit='g')
self.assertEqual(product2.default_uom.digits, 2)
asset = self.prep_asset_item(
company=company,
product = product1)
self.assertEqual(asset.product.rec_name, 'Product unit')
self.assertEqual(asset.product.default_uom.rec_name, 'Unit')
self.assertEqual(asset.uom.rec_name, 'Unit')
self.assertEqual(asset.currency_digits, 4)
asset.product = product2
asset.on_change_product()
asset.save()
self.assertEqual(asset.product.rec_name, 'Product gram')
self.assertEqual(asset.product.default_uom.rec_name, 'Gram')
self.assertEqual(asset.uom.rec_name, 'Gram')
asset.on_change_currency()
asset.save()
self.assertEqual(asset.currency_digits, 2)
# end AssetTestCase

View file

@ -1,26 +0,0 @@
# -*- 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.tests.test_tryton import ModuleTestCase
from .asset import AssetTestCase
from .rate import RateTestCase
from .source import SourceTestCase
from .wizard import WizardTestCase
class InvestmentTestCase(
WizardTestCase,
SourceTestCase,
RateTestCase,
AssetTestCase,
ModuleTestCase):
'Test investment module'
module = 'investment'
# end InvestmentTestCase
del ModuleTestCase

52
tests/test_rate.py Normal file
View file

@ -0,0 +1,52 @@
# -*- 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.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.pool import Pool
from trytond.modules.company.tests import create_company
from decimal import Decimal
from datetime import date
class RateTestCase(ModuleTestCase):
'Test rate module'
module = 'investment'
@with_transaction()
def test_rate_create(self):
""" create rate
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(
company=company,
product = product)
Asset.write(*[
[asset],
{
'rates': [('create', [{
'date': date(2022, 5, 1),
'rate': Decimal('2.5'),
}, {
'date': date(2022, 5, 2),
'rate': Decimal('2.8'),
}])],
}])
self.assertEqual(len(asset.rates), 2)
self.assertEqual(asset.rates[0].date, date(2022, 5, 2))
self.assertEqual(asset.rates[0].rate, Decimal('2.8'))
self.assertEqual(asset.rates[0].uom.rec_name, 'Unit')
self.assertEqual(asset.rates[0].asset_digits, 4)
self.assertEqual(asset.rates[0].currency.rec_name, 'usd')
self.assertEqual(asset.rates[0].symbol, '%')
self.assertEqual(asset.change_symbol.symbol, '%')
# end RateTestCase

139
tests/test_source.py Normal file
View file

@ -0,0 +1,139 @@
# -*- 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.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.pool import Pool
from trytond.modules.company.tests import create_company
from trytond.transaction import Transaction
from decimal import Decimal
from datetime import time, date, datetime
from unittest.mock import MagicMock
from requests import Response
import requests
class SourceTestCase(ModuleTestCase):
'Test online source module'
module = 'investment'
@with_transaction()
def test_waitlist_source_request(self):
""" create source, call server
"""
pool = Pool()
OSource = pool.get('investment.source')
Asset = pool.get('investment.asset')
Product = pool.get('product.product')
company = self.prep_asset_company()
osource, = OSource.create([{
'name': 'Source 1',
'url': 'https://foo.bar/${isin}/${nsin}/${symbol}',
'rgxdate': 'Course Date (\\d+.\\d+.\\d+) Today',
'rgxdatefmt': '%d.%m.%Y',
'rgxrate': 'High (\\d+,\\d+) EUR',
'rgxdecimal': ',',
}])
self.assertEqual(osource.rec_name, 'Source 1')
product = self.prep_asset_product(
name='Product 1',
description='some asset')
Product.write(*[
[product],
{
'identifiers': [('create', [{
'type': 'wkn',
'code': '965515',
}, {
'type': 'secsymb',
'code': '1472977',
}, {
'type': 'isin',
'code': 'XC0009655157',
}, ])],
}])
asset = self.prep_asset_item(
company=company,
product = product)
Asset.write(*[
[asset],
{
'updtsources': [('add', [osource.id])],
}])
with Transaction().set_context({
'qdate': date(2022, 10, 1), # saturday
'qdatetime': datetime(2022, 10, 2, 10, 0, 0),
}):
asset2, = Asset.browse([asset])
self.assertEqual(asset2.wkn, '965515')
self.assertEqual(asset2.isin, 'XC0009655157')
self.assertEqual(asset2.secsymb, '1472977')
self.assertEqual(asset2.updttime, time(14, 0))
self.assertEqual(len(asset2.updtsources), 1)
self.assertEqual(asset2.updtsources[0].rec_name, 'Source 1')
self.assertEqual(asset2.updtdays, 'work')
self.assertEqual(asset2.nextupdate, datetime(2022, 10, 3, 14, 0))
self.assertEqual(len(asset.rates), 0)
# fake server-response
resp1 = Response()
resp1._content = """<html><body>Response from finance-server
Course Date 14.08.2022 Today
High 34,87 EUR
</body></html>""".encode('utf8')
resp1.status_code = 200
resp1.reason = 'OK'
requests.get = MagicMock(return_value=resp1)
OSource.update_rate(asset)
self.assertEqual(len(asset.rates), 1)
self.assertEqual(asset.rates[0].date, date(2022, 8, 14))
self.assertEqual(asset.rates[0].rate, Decimal('34.87'))
@with_transaction()
def test_waitlist_source_check_regex(self):
""" create source, check convert
"""
pool = Pool()
OSource = pool.get('investment.source')
osource, = OSource.create([{
'name': 'Source 1',
'rgxdate': 'Course Date (\\d+.\\d+.\\d+) Today',
'rgxdatefmt': '%d.%m.%Y',
'rgxrate': 'High (\\d+,\\d+) EUR',
'rgxdecimal': ',',
}])
self.assertEqual(osource.rec_name, 'Source 1')
self.assertEqual(osource.get_regex_result(
'The Course Date 14.03.2022 Today, High 13,43 EUR',
'rgxdate'
), date(2022, 3, 14))
self.assertEqual(osource.get_regex_result(
'The Course Date 14.03.2022 Today, High 13,43 EUR',
'rgxrate'
), Decimal('13.43'))
# iso-date
OSource.write(*[
[osource],
{
'rgxdate': 'Course Date (\\d+-\\d+-\\d+) Today',
'rgxdatefmt': '%Y-%m-%d',
}])
self.assertEqual(osource.get_regex_result(
'The Course Date 2022-03-14 Today, High 13,43 EUR',
'rgxdate'
), date(2022, 3, 14))
# end SourceTestCase

78
tests/test_wizard.py Normal file
View file

@ -0,0 +1,78 @@
# -*- 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.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.pool import Pool
from trytond.modules.company.tests import create_company
from trytond.transaction import Transaction
from decimal import Decimal
from datetime import date
class WizardTestCase(ModuleTestCase):
'Test wizard module'
module = 'investment'
@with_transaction()
def test_wiz_run_import(self):
""" run import wizard
"""
pool = Pool()
Asset = pool.get('investment.asset')
ImportWiz = pool.get('investment.imp_wiz', type='wizard')
company = self.prep_asset_company()
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(
company=company,
product = product)
self.assertEqual(len(asset.rates), 0)
with Transaction().set_context({
'active_id': asset.id,
'active_model': 'investment.asset',
}):
(sess_id, start_state, end_state) = ImportWiz.create()
w_obj = ImportWiz(sess_id)
self.assertEqual(start_state, 'start')
self.assertEqual(end_state, 'end')
# run start
result = ImportWiz.execute(sess_id, {}, start_state)
self.assertEqual(list(result.keys()), ['view'])
self.assertEqual(result['view']['defaults']['asset'], asset.id)
self.assertEqual(result['view']['defaults']['dec_divider'], ',')
self.assertEqual(result['view']['defaults']['date_fmt'], '%d.%m.%Y')
self.assertEqual(result['view']['defaults']['field_delimiter'], ';')
w_obj.start.asset = asset
w_obj.start.dec_divider = ','
w_obj.start.date_fmt = '%d.%m.%Y'
w_obj.start.field_delimiter = ';'
result = ImportWiz.execute(sess_id, {'start': {
'asset': asset.id,
'dec_divider': ',',
'date_fmt': '%d.%m.%Y',
'field_delimiter': ';',
'file_': b'"date";"rate"\n"03.05.2022";"23,56"\n"05.05.2022";"24,22"\n"06.05.2022";"25,43"',
}}, 'importf')
self.assertEqual(list(result.keys()), [])
# finish wizard
ImportWiz.delete(sess_id)
self.assertEqual(len(asset.rates), 3)
self.assertEqual(asset.rates[0].date, date(2022, 5, 6))
self.assertEqual(asset.rates[0].rate, Decimal('25.43'))
self.assertEqual(asset.rates[1].date, date(2022, 5, 5))
self.assertEqual(asset.rates[1].rate, Decimal('24.22'))
self.assertEqual(asset.rates[2].date, date(2022, 5, 3))
self.assertEqual(asset.rates[2].rate, Decimal('23.56'))
# end WizardTestCase

View file

@ -1,78 +0,0 @@
# -*- 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.tests.test_tryton import with_transaction
from trytond.pool import Pool
from trytond.transaction import Transaction
from decimal import Decimal
from datetime import date
class WizardTestCase(object):
""" test import wizard
"""
@with_transaction()
def test_wiz_run_import(self):
""" run import wizard
"""
pool = Pool()
ImportWiz = pool.get('investment.imp_wiz', type='wizard')
company = self.prep_asset_company()
with Transaction().set_context({'company': company.id}):
product = self.prep_asset_product(
name='Product 1',
description='some asset')
asset = self.prep_asset_item(company=company, product=product)
self.assertEqual(len(asset.rates), 0)
with Transaction().set_context({
'active_id': asset.id,
'active_model': 'investment.asset'}):
(sess_id, start_state, end_state) = ImportWiz.create()
w_obj = ImportWiz(sess_id)
self.assertEqual(start_state, 'start')
self.assertEqual(end_state, 'end')
# run start
result = ImportWiz.execute(sess_id, {}, start_state)
self.assertEqual(list(result.keys()), ['view'])
self.assertEqual(result['view']['defaults']['asset'], asset.id)
self.assertEqual(result['view']['defaults']['dec_divider'], ',')
self.assertEqual(
result['view']['defaults']['date_fmt'],
'%d.%m.%Y')
self.assertEqual(
result['view']['defaults']['field_delimiter'],
';')
w_obj.start.asset = asset
w_obj.start.dec_divider = ','
w_obj.start.date_fmt = '%d.%m.%Y'
w_obj.start.field_delimiter = ';'
result = ImportWiz.execute(sess_id, {'start': {
'asset': asset.id,
'dec_divider': ',',
'date_fmt': '%d.%m.%Y',
'field_delimiter': ';',
'file_': b'"date";"rate"\n"03.05.2022";"23,56"\n' +
b'"05.05.2022";"24,22"\n"06.05.2022";"25,43"',
}}, 'importf')
self.assertEqual(list(result.keys()), [])
# finish wizard
ImportWiz.delete(sess_id)
self.assertEqual(len(asset.rates), 3)
self.assertEqual(asset.rates[0].date, date(2022, 5, 6))
self.assertEqual(asset.rates[0].rate, Decimal('25.43'))
self.assertEqual(asset.rates[1].date, date(2022, 5, 5))
self.assertEqual(asset.rates[1].rate, Decimal('24.22'))
self.assertEqual(asset.rates[2].date, date(2022, 5, 3))
self.assertEqual(asset.rates[2].rate, Decimal('23.56'))
# end WizardTestCase

View file

@ -1,5 +1,5 @@
[tryton]
version=7.0.0
version=6.0.23
depends:
ir
res

View file

@ -29,9 +29,10 @@ class UpdateSoureWizard(Wizard):
if OnlineSource.update_rate(asset):
to_run_activities.append(asset)
if to_run_activities:
if len(to_run_activities) > 0:
Asset.after_update_actions(to_run_activities)
return 'end'
# UpdateSoureWizard

View file

@ -1 +1 @@
diagram;6.0.7;6.0.999;mds

View file

@ -60,11 +60,6 @@ full copyright notices and license terms. -->
<label name="updtdays"/>
<field name="updtdays"/>
<label name="updturl"/>
<field name="updturl" colspan="3"/>
<field name="updturl_enable"/>
<newline/>
<field name="updtsources" colspan="4"/>
</page>
</notebook>

View file

@ -4,12 +4,12 @@ The COPYRIGHT file at the top level of this repository contains the
full copyright notices and license terms. -->
<tree>
<field name="name" expand="1"/>
<field name="change_day1" optional="0"/>
<field name="change_month1" optional="0"/>
<field name="change_month3" optional="0"/>
<field name="change_month6" optional="0"/>
<field name="date" optional="0"/>
<field name="rate" symbol="asset_symbol" optional="0"/>
<field name="isin" optional="0"/>
<field name="wkn" optional="0"/>
<field name="change_day1" symbol="change_symbol"/>
<field name="change_month1" symbol="change_symbol"/>
<field name="change_month3" symbol="change_symbol"/>
<field name="change_month6" symbol="change_symbol"/>
<field name="date"/>
<field name="rate" symbol="asset_symbol"/>
<field name="isin"/>
<field name="wkn" />
</tree>

14
view/graph_form.xml Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!-- 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. -->
<data>
<xpath expr="/form/separator[@name='currency']" position="before">
<separator name="asset" colspan="6" string="Asset"/>
<label name="asset"/>
<field name="asset"/>
<newline/>
</xpath>
</data>

View file

@ -14,12 +14,9 @@ full copyright notices and license terms. -->
<field name="url" colspan="3"/>
<label name="nohtml"/>
<field name="nohtml"/>
<label id="labtp1" string=" "/>
<label xalign="0.0" colspan="3" name="nohtml"
string="URL parameter placeholders: ${isin}, ${nsin}, ${symbol}"/>
<label name="fixed_url"/>
<field name="fixed_url"/>
<newline/>
<separator colspan="6" name="url" string="Regular expressions to find data"/>