Compare commits
62 commits
main
...
ver_6.0.16
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2c38ddd357 | ||
![]() |
f5b9929c51 | ||
![]() |
46a19fe4c4 | ||
![]() |
f74ee23a76 | ||
![]() |
2c750b2253 | ||
![]() |
e703c759a3 | ||
![]() |
36e6fb52d5 | ||
![]() |
b8648d629b | ||
![]() |
7dd7c9a140 | ||
![]() |
813a794db4 | ||
![]() |
2977e5187f | ||
![]() |
77c287fc90 | ||
![]() |
c73584af32 | ||
![]() |
6b3b44e9af | ||
![]() |
331c62a3f6 | ||
![]() |
693dc175f1 | ||
![]() |
5b6883cdde | ||
![]() |
6311d0e79e | ||
![]() |
b531d51a7d | ||
![]() |
08ca00a6b9 | ||
![]() |
3cc92ccecb | ||
![]() |
c3d323714f | ||
![]() |
53dfb14254 | ||
![]() |
4ab79e4200 | ||
![]() |
528a6e5a86 | ||
![]() |
d88703f117 | ||
![]() |
6241601fa7 | ||
![]() |
0fe7df4b61 | ||
![]() |
ebca4642fb | ||
![]() |
37aece3c65 | ||
![]() |
5c588abe96 | ||
![]() |
c6f2661f9b | ||
![]() |
bda4f66b47 | ||
![]() |
d097395825 | ||
![]() |
f1b00e376b | ||
![]() |
f462b11758 | ||
![]() |
d431a422e8 | ||
![]() |
b8495231e5 | ||
![]() |
8760fd4aab | ||
![]() |
aaa143f373 | ||
![]() |
99e18094aa | ||
![]() |
d1421403b1 | ||
![]() |
475457d202 | ||
![]() |
c1cc1acb5f | ||
![]() |
7bb3e4f929 | ||
![]() |
047b6980e4 | ||
![]() |
9d88cf1067 | ||
![]() |
620c42fc62 | ||
![]() |
6311dce3d1 | ||
![]() |
64b8383096 | ||
![]() |
a99d11a4a0 | ||
![]() |
4ae5601c04 | ||
![]() |
31c76dfb48 | ||
![]() |
57cb06d60e | ||
![]() |
5587bfea3a | ||
![]() |
f919d9e290 | ||
![]() |
e8614b1242 | ||
![]() |
a71bc0a79a | ||
![]() |
f1d9b3b1dd | ||
![]() |
4947b495c2 | ||
![]() |
aeb949cc20 | ||
![]() |
916d73ef12 |
25 changed files with 2158 additions and 297 deletions
71
README.rst
71
README.rst
|
@ -14,6 +14,77 @@ Requires
|
|||
Changes
|
||||
=======
|
||||
|
||||
*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
|
||||
|
|
|
@ -4,22 +4,28 @@
|
|||
# full copyright notices and license terms.
|
||||
|
||||
from trytond.pool import Pool
|
||||
from .asset import Asset
|
||||
from .asset import Asset, AssetSourceRel
|
||||
from .rate import Rate
|
||||
from .identifier import Identifier
|
||||
from .cron import Cron
|
||||
from .onlinesource import OnlineSource
|
||||
from .update_wiz import UpdateSoureWizard
|
||||
from .diagram import GraphDef, ChartPoint
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
OnlineSource,
|
||||
AssetSourceRel,
|
||||
Asset,
|
||||
Rate,
|
||||
Identifier,
|
||||
Cron,
|
||||
module='investment', type_='model')
|
||||
Pool.register(
|
||||
GraphDef,
|
||||
ChartPoint,
|
||||
module='investment', type_='model', depends=['diagram'])
|
||||
Pool.register(
|
||||
UpdateSoureWizard,
|
||||
module='investment', type_='wizard')
|
||||
|
|
781
asset.py
781
asset.py
|
@ -3,20 +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
|
||||
from trytond.model import ModelView, ModelSQL, fields, SymbolMixin
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, Bool, And
|
||||
from trytond.pyson import Eval, Bool, And, If, Date
|
||||
from trytond.report import Report
|
||||
from decimal import Decimal
|
||||
from datetime import time
|
||||
from sql.functions import CurrentTime
|
||||
from sql.conditionals import Case
|
||||
from sql.functions import CurrentDate, CurrentTimestamp, Round, Extract
|
||||
from sql.conditionals import Case, Coalesce
|
||||
from sql import Literal
|
||||
from .diagram import Concat2
|
||||
|
||||
|
||||
class Asset(ModelSQL, ModelView):
|
||||
digits_percent = 2
|
||||
|
||||
sel_updtdays = [
|
||||
('work', 'Mon - Fri'),
|
||||
('week', 'Mon - Sun'),
|
||||
]
|
||||
|
||||
|
||||
class Asset(SymbolMixin, ModelSQL, ModelView):
|
||||
'Asset'
|
||||
__name__ = 'investment.asset'
|
||||
|
||||
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',
|
||||
required=True, ondelete="RESTRICT")
|
||||
product = fields.Many2One(string='Product', required=True,
|
||||
|
@ -24,8 +37,7 @@ class Asset(ModelSQL, ModelView):
|
|||
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.'),
|
||||
'on_change_with_product_uom')
|
||||
help='Category of unit on the product.'), 'get_name_symbol')
|
||||
uom = fields.Many2One(string='UOM', required=True,
|
||||
model_name='product.uom', ondelete='RESTRICT',
|
||||
states={
|
||||
|
@ -34,24 +46,24 @@ class Asset(ModelSQL, ModelView):
|
|||
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')
|
||||
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']), 'on_change_with_rate')
|
||||
|
||||
company_currency = fields.Function(fields.Many2One(readonly=True,
|
||||
string='Company Currency', states={'invisible': True},
|
||||
model_name='currency.currency'),
|
||||
'on_change_with_company_currency')
|
||||
company_currency_digits = fields.Function(fields.Integer(
|
||||
string='Currency Digits (Ref.)', readonly=True),
|
||||
'on_change_with_currency_digits')
|
||||
depends=['currency_digits']),
|
||||
'get_rate_data', searcher='search_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', select=True,
|
||||
required=True, model_name='currency.currency', ondelete='RESTRICT')
|
||||
currency_digits = fields.Integer(string='Currency Digits',
|
||||
required=True)
|
||||
currency_digits = fields.Integer(string='Digits', required=True,
|
||||
domain=[
|
||||
('currency_digits', '>=', 0),
|
||||
('currency_digits', '<=', 6)])
|
||||
|
||||
wkn = fields.Function(fields.Char(string='NSIN', readonly=True,
|
||||
help='National Securities Identifying Number'),
|
||||
|
@ -63,16 +75,94 @@ class Asset(ModelSQL, ModelView):
|
|||
help='Stock market symbol'),
|
||||
'get_identifiers', searcher='search_identifier')
|
||||
|
||||
updtsource = fields.Many2One(string='Update Source',
|
||||
help='Select a source for the course update.',
|
||||
ondelete='SET NULL', model_name='investment.source')
|
||||
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')
|
||||
updtdays = fields.Selection(string='Select days', required=True,
|
||||
selection=sel_updtdays)
|
||||
updttime = fields.Time(string='Time',
|
||||
states={
|
||||
'readonly': ~Bool(Eval('updtsource')),
|
||||
}, depends=['updtsource'])
|
||||
updtneeded = fields.Function(fields.Boolean(string='Course update needed',
|
||||
'readonly': ~Bool(Eval('updtsources')),
|
||||
}, depends=['updtsources'])
|
||||
nextupdate = fields.Function(fields.DateTime(string='Next Update',
|
||||
readonly=True),
|
||||
'on_change_with_updtneeded', searcher='search_updtneeded')
|
||||
'get_nextupdates', searcher='search_nextupdate')
|
||||
|
||||
# percentage change
|
||||
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',
|
||||
help='percentage change in value compared to last month',
|
||||
readonly=True, digits=(16,digits_percent)),
|
||||
'get_percentage_change', searcher='search_percentage')
|
||||
change_month3 = fields.Function(fields.Numeric(string='3 Months',
|
||||
help='percentage change in value during 3 months',
|
||||
readonly=True, digits=(16,digits_percent)),
|
||||
'get_percentage_change', searcher='search_percentage')
|
||||
change_month6 = fields.Function(fields.Numeric(string='6 Months',
|
||||
help='percentage change in value during 6 months',
|
||||
readonly=True, digits=(16,digits_percent)),
|
||||
'get_percentage_change', searcher='search_percentage')
|
||||
change_month12 = fields.Function(fields.Numeric(string='1 Year',
|
||||
help='percentage change in value during 1 year',
|
||||
readonly=True, digits=(16,digits_percent)),
|
||||
'get_percentage_change', searcher='search_percentage')
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
""" register and migrate
|
||||
"""
|
||||
super(Asset, cls).__register__(module_name)
|
||||
cls.migrate_updtsource(module_name)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(Asset, cls).__setup__()
|
||||
cls._order.insert(0, ('name', 'ASC'))
|
||||
cls._order.insert(0, ('date', 'DESC'))
|
||||
|
||||
@classmethod
|
||||
def migrate_updtsource(cls, module_name):
|
||||
""" replace 'updtsource' by relation
|
||||
"""
|
||||
pool = Pool()
|
||||
Asset2 = pool.get('investment.asset')
|
||||
|
||||
asset_table = Asset2.__table_handler__(module_name)
|
||||
if asset_table.column_exist('updtsource'):
|
||||
AssetSourceRel = pool.get('investment.asset_source_rel')
|
||||
tab_asset = Asset2.__table__()
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
query = tab_asset.select(
|
||||
tab_asset.id,
|
||||
tab_asset.updtsource,
|
||||
where = tab_asset.updtsource != None,
|
||||
)
|
||||
cursor.execute(*query)
|
||||
records = cursor.fetchall()
|
||||
to_create = [{
|
||||
'asset': x[0],
|
||||
'source': x[1],
|
||||
} for x in records]
|
||||
|
||||
if len(to_create) > 0:
|
||||
AssetSourceRel.create(to_create)
|
||||
asset_table.drop_column('updtsource')
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
return super().view_attributes() + [
|
||||
('/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', '')
|
||||
))
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def default_currency(cls):
|
||||
|
@ -96,32 +186,27 @@ class Asset(ModelSQL, ModelView):
|
|||
"""
|
||||
return 4
|
||||
|
||||
@fields.depends('updtsource', 'updttime')
|
||||
def on_change_updtsource(self):
|
||||
@classmethod
|
||||
def default_updttime(cls):
|
||||
""" 14 o'clock UTC
|
||||
"""
|
||||
return time(14, 0)
|
||||
|
||||
@classmethod
|
||||
def default_updtdays(cls):
|
||||
""" default: mon - fri
|
||||
"""
|
||||
return 'work'
|
||||
|
||||
@fields.depends('updtsources', 'updttime')
|
||||
def on_change_updtsources(self):
|
||||
""" clear time-fields
|
||||
"""
|
||||
if self.updtsource is None:
|
||||
if len(self.updtsources) == 0:
|
||||
self.updttime = None
|
||||
else :
|
||||
self.updttime = time(11, 30)
|
||||
|
||||
@fields.depends('id', 'currency_digits')
|
||||
def on_change_with_rate(self, name=None):
|
||||
""" get current rate
|
||||
"""
|
||||
pool = Pool()
|
||||
Rate = pool.get('investment.rate')
|
||||
IrDate = pool.get('ir.date')
|
||||
|
||||
if self.id:
|
||||
rates = Rate.search([
|
||||
('date', '<=', IrDate.today()),
|
||||
('asset.id', '=', self.id),
|
||||
], order=[('date', 'DESC')], limit=1)
|
||||
if len(rates) > 0:
|
||||
exp = Decimal(Decimal(1) / 10 ** (self.currency_digits or 4))
|
||||
return rates[0].rate.quantize(exp)
|
||||
|
||||
@fields.depends('product', 'uom')
|
||||
def on_change_product(self):
|
||||
""" update unit by product
|
||||
|
@ -138,77 +223,445 @@ class Asset(ModelSQL, ModelView):
|
|||
if self.currency:
|
||||
self.currency_digits = self.currency.digits
|
||||
|
||||
@fields.depends('product')
|
||||
def on_change_with_product_uom(self, name=None):
|
||||
""" get category of product-uom
|
||||
@classmethod
|
||||
def get_name_symbol_sql(cls):
|
||||
""" get sql for name, uom, digits, etc.
|
||||
"""
|
||||
if self.product:
|
||||
return self.product.default_uom.category.id
|
||||
pool = Pool()
|
||||
Product = pool.get('product.product')
|
||||
ProdTempl = pool.get('product.template')
|
||||
Uom = pool.get('product.uom')
|
||||
Currency = pool.get('currency.currency')
|
||||
tab_asset = cls.__table__()
|
||||
tab_templ = ProdTempl.__table__()
|
||||
tab_prod = Product.__table__()
|
||||
tab_uom = Uom.__table__()
|
||||
tab_cur = Currency.__table__()
|
||||
|
||||
@fields.depends('currency')
|
||||
def on_change_with_currency_digits(self, name=None):
|
||||
""" currency of cashbook
|
||||
# get translated symbol-column from UOM
|
||||
(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,
|
||||
).select(
|
||||
tab_asset.id,
|
||||
tab_templ.name,
|
||||
tab_uom.category.as_('product_uom'),
|
||||
Concat2(tab_cur.symbol, '/', tab_symb.symbol).as_('symbol'),
|
||||
)
|
||||
return (query, tab_asset)
|
||||
|
||||
@classmethod
|
||||
def get_name_symbol(cls, assets, names):
|
||||
""" get date and rate of asset
|
||||
"""
|
||||
if self.currency:
|
||||
return self.currency.digits
|
||||
else:
|
||||
return 2
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
@fields.depends('company', 'currency')
|
||||
def on_change_with_company_currency(self, name=None):
|
||||
""" get company-currency if its different from current
|
||||
asset-currency
|
||||
(query, tab_asset) = cls.get_name_symbol_sql()
|
||||
query.where=tab_asset.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}
|
||||
|
||||
for record in records:
|
||||
values = {
|
||||
'name': record[1],
|
||||
'product_uom': record[2],
|
||||
'symbol': record[3],
|
||||
}
|
||||
|
||||
for name in names:
|
||||
result[name][record[0]] = values[name]
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def search_uom_symbol(cls, names, clause):
|
||||
""" search in uom
|
||||
"""
|
||||
if self.company:
|
||||
if self.currency:
|
||||
if self.company.currency.id != self.currency.id:
|
||||
return self.company.currency.id
|
||||
return ['OR',
|
||||
(('uom.rec_name',) + tuple(clause[1:])),
|
||||
(('currency.rec_name',) + tuple(clause[1:])),
|
||||
]
|
||||
|
||||
@fields.depends('id')
|
||||
def on_change_with_updtneeded(self, name=None):
|
||||
""" get state of update
|
||||
@classmethod
|
||||
def get_rate_data_sql(cls):
|
||||
""" get sql for rate/date
|
||||
"""
|
||||
pool = Pool()
|
||||
Asset = pool.get('investment.asset')
|
||||
Rate = pool.get('investment.rate')
|
||||
tab_asset = Asset.__table__()
|
||||
tab_rate = Rate.__table__()
|
||||
|
||||
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'),
|
||||
tab_rate.date,
|
||||
distinct_on=[tab_asset.id],
|
||||
order_by=[tab_asset.id, tab_rate.date.desc],
|
||||
)
|
||||
return (query, tab_asset)
|
||||
|
||||
@classmethod
|
||||
def get_rate_data(cls, assets, names):
|
||||
""" get date and rate of asset
|
||||
"""
|
||||
Asset2 = Pool().get('investment.asset')
|
||||
|
||||
if self.id:
|
||||
if Asset2.search_count([
|
||||
('updtneeded', '=', True),
|
||||
('id', '=', self.id)
|
||||
]) == 1:
|
||||
return True
|
||||
return False
|
||||
cursor = Transaction().connection.cursor()
|
||||
(query, tab_asset) = cls.get_rate_data_sql()
|
||||
query.where=tab_asset.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}
|
||||
|
||||
for record in records:
|
||||
(id1, rate1, date1) = record
|
||||
|
||||
asset = Asset2(id1)
|
||||
exp = Decimal(Decimal(1) / 10 ** (asset.currency_digits or 4))
|
||||
|
||||
values = {'rate': record[1].quantize(exp), 'date': record[2]}
|
||||
|
||||
for name in names:
|
||||
result[name][record[0]] = values[name]
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def search_updtneeded(cls, names, clause):
|
||||
""" search for assets to update
|
||||
def search_date(cls, names, clause):
|
||||
""" search in date
|
||||
"""
|
||||
(tab_query, tab_asset) = cls.get_rate_data_sql()
|
||||
Operator = fields.SQL_OPERATORS[clause[1]]
|
||||
|
||||
query = tab_query.select(
|
||||
tab_query.id,
|
||||
where=Operator(tab_query.date, clause[2]),
|
||||
)
|
||||
return [('id', 'in', query)]
|
||||
|
||||
@classmethod
|
||||
def search_rate(cls, names, clause):
|
||||
""" search in rate
|
||||
"""
|
||||
(tab_query, tab_asset) = cls.get_rate_data_sql()
|
||||
Operator = fields.SQL_OPERATORS[clause[1]]
|
||||
|
||||
query = tab_query.select(
|
||||
tab_query.id,
|
||||
where=Operator(tab_query.rate, clause[2]),
|
||||
)
|
||||
return [('id', 'in', query)]
|
||||
|
||||
@staticmethod
|
||||
def order_date(tables):
|
||||
""" order date
|
||||
"""
|
||||
(tab_query, tab_asset) = Asset.get_rate_data_sql()
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_query.select(
|
||||
tab_query.date,
|
||||
where=tab_query.id==table.id,
|
||||
)
|
||||
return [query]
|
||||
|
||||
@staticmethod
|
||||
def order_rate(tables):
|
||||
""" order rate
|
||||
"""
|
||||
(tab_query, tab_asset) = Asset.get_rate_data_sql()
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_query.select(
|
||||
tab_query.rate,
|
||||
where=tab_query.id==table.id,
|
||||
)
|
||||
return [query]
|
||||
|
||||
@classmethod
|
||||
def get_percentage_sql(cls, name_lst, select_date=True):
|
||||
""" get table for percentages and dates,
|
||||
generate adapted query
|
||||
select_date: True = select newest date
|
||||
"""
|
||||
pool = Pool()
|
||||
Asset2 = pool.get('investment.asset')
|
||||
Rate = pool.get('investment.rate')
|
||||
IrDate = pool.get('ir.date')
|
||||
tab_asset = Asset2.__table__()
|
||||
tab_rate = Rate.__table__()
|
||||
Operator = fields.SQL_OPERATORS[clause[1]]
|
||||
Asset = pool.get('investment.asset')
|
||||
tab_asset = Asset.__table__()
|
||||
tab_rate_today = Rate.__table__()
|
||||
|
||||
rate_tab = {
|
||||
'day1': {'tab': Rate.__table__(), 'day': 0},
|
||||
'month1': {'tab': Rate.__table__(), 'day': 30},
|
||||
'month3': {'tab': Rate.__table__(), 'day': 3*30},
|
||||
'month6': {'tab': Rate.__table__(), 'day': 6*30},
|
||||
'month12': {'tab': Rate.__table__(), 'day': 12*30},
|
||||
}
|
||||
context = Transaction().context
|
||||
|
||||
query_date = context.get('qdate', IrDate.today())
|
||||
query_time = context.get('qtime', CurrentTime())
|
||||
|
||||
query = tab_asset.join(tab_rate,
|
||||
condition=(tab_asset.id==tab_rate.asset) & \
|
||||
(tab_rate.date == query_date),
|
||||
type_ = 'LEFT OUTER',
|
||||
).select(tab_asset.id,
|
||||
where=Operator(
|
||||
Case(
|
||||
((tab_rate.id == None) & \
|
||||
(tab_asset.updtsource != None) & \
|
||||
(tab_asset.updttime <= query_time), True),
|
||||
default_ = False,
|
||||
),
|
||||
clause[2]),
|
||||
# create tables to query percentual changes for
|
||||
# 1 day, 30 days, 3 months, 6 months, 1 year
|
||||
query_date = context.get('qdate', CurrentDate())
|
||||
query_today = tab_asset.join(tab_rate_today,
|
||||
condition=tab_asset.id==tab_rate_today.asset,
|
||||
).select(
|
||||
tab_asset.id,
|
||||
tab_rate_today.id.as_('id_rate'),
|
||||
tab_rate_today.date,
|
||||
tab_rate_today.rate,
|
||||
)
|
||||
return [('id', '=', query)]
|
||||
|
||||
# limit to newest date until 'query_date'
|
||||
if select_date == True:
|
||||
query_today.distinct_on=[tab_asset.id]
|
||||
query_today.order_by=[tab_asset.id, tab_rate_today.date.desc]
|
||||
query_today.where=tab_rate_today.date <= query_date
|
||||
|
||||
# create join for requested fields, to minimize database-load
|
||||
query = query_today
|
||||
for name in name_lst:
|
||||
query = query.join(rate_tab[name]['tab'],
|
||||
# select newest date from <period> until 5 days older
|
||||
condition=(query_today.id==rate_tab[name]['tab'].asset) & \
|
||||
(query_today.date > (rate_tab[name]['tab'].date + Literal(rate_tab[name]['day']))) & \
|
||||
(query_today.date <= (rate_tab[name]['tab'].date + Literal(rate_tab[name]['day'] + 5))),
|
||||
type_ = 'LEFT OUTER')
|
||||
|
||||
# add select for requested fields to join
|
||||
select_lst = [
|
||||
query_today.id,
|
||||
query_today.id_rate,
|
||||
query_today.date,
|
||||
]
|
||||
for name in name_lst:
|
||||
select_lst.append(
|
||||
Case(
|
||||
((rate_tab[name]['tab'].rate != None) & (query_today.rate != None) & \
|
||||
(rate_tab[name]['tab'].rate != Literal(0.0)),
|
||||
query_today.rate * Literal(100.0) / rate_tab[name]['tab'].rate - Literal(100.0)),
|
||||
else_ = None,
|
||||
).as_(name))
|
||||
|
||||
order_by_lst = [query_today.id, query_today.id_rate]
|
||||
order_by_lst.extend([
|
||||
rate_tab[name]['tab'].date.desc for name in name_lst
|
||||
])
|
||||
|
||||
query = query.select(
|
||||
*select_lst,
|
||||
distinct_on=[query_today.id, query_today.id_rate],
|
||||
order_by=order_by_lst,
|
||||
)
|
||||
return query
|
||||
|
||||
@staticmethod
|
||||
def order_change_day1(tables):
|
||||
""" order day1
|
||||
"""
|
||||
Assert = Pool().get('investment.asset')
|
||||
tab_asset = Asset.get_percentage_sql(['day1'])
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_asset.select(
|
||||
tab_asset.day1,
|
||||
where=tab_asset.id==table.id,
|
||||
)
|
||||
return [query]
|
||||
|
||||
@staticmethod
|
||||
def order_change_month1(tables):
|
||||
""" order month1
|
||||
"""
|
||||
Assert = Pool().get('investment.asset')
|
||||
tab_asset = Asset.get_percentage_sql(['month1'])
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_asset.select(
|
||||
tab_asset.month1,
|
||||
where=tab_asset.id==table.id,
|
||||
)
|
||||
return [query]
|
||||
|
||||
@staticmethod
|
||||
def order_change_month3(tables):
|
||||
""" order month1
|
||||
"""
|
||||
Assert = Pool().get('investment.asset')
|
||||
tab_asset = Asset.get_percentage_sql(['month3'])
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_asset.select(
|
||||
tab_asset.month3,
|
||||
where=tab_asset.id==table.id,
|
||||
)
|
||||
return [query]
|
||||
|
||||
@staticmethod
|
||||
def order_change_month6(tables):
|
||||
""" order month1
|
||||
"""
|
||||
Assert = Pool().get('investment.asset')
|
||||
tab_asset = Asset.get_percentage_sql(['month6'])
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_asset.select(
|
||||
tab_asset.month6,
|
||||
where=tab_asset.id==table.id,
|
||||
)
|
||||
return [query]
|
||||
|
||||
@staticmethod
|
||||
def order_change_month12(tables):
|
||||
""" order month1
|
||||
"""
|
||||
Assert = Pool().get('investment.asset')
|
||||
tab_asset = Asset.get_percentage_sql(['month12'])
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_asset.select(
|
||||
tab_asset.month12,
|
||||
where=tab_asset.id==table.id,
|
||||
)
|
||||
return [query]
|
||||
|
||||
@classmethod
|
||||
def search_percentage(cls, names, clause):
|
||||
""" search for percentages
|
||||
"""
|
||||
Operator = fields.SQL_OPERATORS[clause[1]]
|
||||
field_name = clause[0][len('change_'):]
|
||||
tab_percent = cls.get_percentage_sql([field_name])
|
||||
|
||||
query = tab_percent.select(tab_percent.id,
|
||||
where=Operator(Round(getattr(tab_percent, field_name), 2),
|
||||
clause[2]))
|
||||
return [('id', 'in', query)]
|
||||
|
||||
@classmethod
|
||||
def get_percentage_change(cls, assets, names):
|
||||
""" get percentage per period
|
||||
"""
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
name_lst = [x[len('change_'):] for x in names]
|
||||
tab_percent = cls.get_percentage_sql(name_lst)
|
||||
query = tab_percent.select(
|
||||
tab_percent.id,
|
||||
*[getattr(tab_percent, x) for x in name_lst],
|
||||
where=tab_percent.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}
|
||||
exp = Decimal(Decimal(1) / 10 ** digits_percent)
|
||||
for record in records:
|
||||
cnt1 = 1
|
||||
for x in name_lst:
|
||||
result['change_%s' % x][record[0]] = record[cnt1].quantize(exp) \
|
||||
if record[cnt1] is not None else None
|
||||
cnt1 += 1
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def get_next_update_datetime_sql(cls):
|
||||
""" get sql for datetime of next planned update
|
||||
"""
|
||||
pool = Pool()
|
||||
Asset = pool.get('investment.asset')
|
||||
AssetSourceRel = pool.get('investment.asset_source_rel')
|
||||
Rate = pool.get('investment.rate')
|
||||
tab_asset = Asset.__table__()
|
||||
tab_rate = Rate.__table__()
|
||||
tab_rel = AssetSourceRel.__table__()
|
||||
context = Transaction().context
|
||||
|
||||
query_date = context.get('qdate', CurrentDate() - Literal(1))
|
||||
|
||||
# get last date of rate
|
||||
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,
|
||||
condition=tab_asset.id == tab_rate.asset,
|
||||
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],
|
||||
)
|
||||
|
||||
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'),
|
||||
)
|
||||
return query
|
||||
|
||||
@classmethod
|
||||
def get_nextupdates(cls, assets, names):
|
||||
""" get timestamp of next update
|
||||
"""
|
||||
Asset2 = Pool().get('investment.asset')
|
||||
tab_updt = Asset2.get_next_update_datetime_sql()
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
query = tab_updt.select(
|
||||
tab_updt.id,
|
||||
tab_updt.updttime,
|
||||
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}
|
||||
|
||||
for record in records:
|
||||
(id1, updt) = record
|
||||
r1 = {'nextupdate': updt}
|
||||
|
||||
for n in names:
|
||||
result[n][id1] = r1[n]
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def search_nextupdate(cls, names, clause):
|
||||
""" search for assets to update
|
||||
"""
|
||||
Asset2 = Pool().get('investment.asset')
|
||||
tab_updt = Asset2.get_next_update_datetime_sql()
|
||||
Operator = fields.SQL_OPERATORS[clause[1]]
|
||||
|
||||
query = tab_updt.select(
|
||||
tab_updt.id,
|
||||
where=Operator(tab_updt.updttime, clause[2]),
|
||||
)
|
||||
return [('id', 'in', query)]
|
||||
|
||||
@classmethod
|
||||
def get_identifier_sql(cls, tab_asset):
|
||||
|
@ -244,6 +697,70 @@ class Asset(ModelSQL, ModelView):
|
|||
)
|
||||
return query
|
||||
|
||||
@staticmethod
|
||||
def order_name(tables):
|
||||
""" order name
|
||||
"""
|
||||
pool = Pool()
|
||||
Templ = pool.get('product.template')
|
||||
Product = pool.get('product.product')
|
||||
Asset = pool.get('investment.asset')
|
||||
table, _ = tables[None]
|
||||
tab_asset = Asset.__table__()
|
||||
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
|
||||
)
|
||||
return [query]
|
||||
|
||||
@staticmethod
|
||||
def order_wkn(tables):
|
||||
""" order wkn
|
||||
"""
|
||||
Asset = Pool().get('investment.asset')
|
||||
tab_ids = Asset.get_identifier_sql(Asset.__table__())
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_ids.select(
|
||||
getattr(tab_ids, 'wkn'),
|
||||
where=tab_ids.id==table.id,
|
||||
)
|
||||
return [query]
|
||||
|
||||
@staticmethod
|
||||
def order_isin(tables):
|
||||
""" order isin
|
||||
"""
|
||||
Asset = Pool().get('investment.asset')
|
||||
tab_ids = Asset.get_identifier_sql(Asset.__table__())
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_ids.select(
|
||||
getattr(tab_ids, 'isin'),
|
||||
where=tab_ids.id==table.id,
|
||||
)
|
||||
return [query]
|
||||
|
||||
@staticmethod
|
||||
def order_secsymb(tables):
|
||||
""" order secsymb
|
||||
"""
|
||||
Asset = Pool().get('investment.asset')
|
||||
tab_ids = Asset.get_identifier_sql(Asset.__table__())
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_ids.select(
|
||||
getattr(tab_ids, 'secsymb'),
|
||||
where=tab_ids.id==table.id,
|
||||
)
|
||||
return [query]
|
||||
|
||||
@classmethod
|
||||
def search_identifier(cls, names, clause):
|
||||
""" search in identifier
|
||||
|
@ -294,17 +811,29 @@ class Asset(ModelSQL, ModelView):
|
|||
def get_rec_name(self, name):
|
||||
""" record name
|
||||
"""
|
||||
return '%(prod)s [%(curr)s/%(unit)s]' % {
|
||||
return '%(prod)s | %(rate)s %(unit)s | %(date)s' % {
|
||||
'prod': getattr(self.product, 'rec_name', '-'),
|
||||
'curr': getattr(self.currency, 'rec_name', '-'),
|
||||
'unit': getattr(self.uom, '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 '-',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def search_rec_name(cls, name, clause):
|
||||
""" search in rec_name
|
||||
"""
|
||||
return [('product.rec_name',) + tuple(clause[1:])]
|
||||
return ['OR',
|
||||
('product.rec_name',) + tuple(clause[1:]),
|
||||
('product.identifiers.code',) + tuple(clause[1:]),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def after_update_actions(cls, assets):
|
||||
""" run activities after rate-update
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def cron_update(cls):
|
||||
|
@ -313,33 +842,31 @@ class Asset(ModelSQL, ModelView):
|
|||
pool = Pool()
|
||||
Asset2 = pool.get('investment.asset')
|
||||
OnlineSource = pool.get('investment.source')
|
||||
context = Transaction().context
|
||||
|
||||
query_time = context.get('qdatetime', CurrentTimestamp())
|
||||
to_run_activities = []
|
||||
for asset in Asset2.search([
|
||||
('updtneeded', '=', True),
|
||||
('nextupdate', '<=', query_time),
|
||||
]):
|
||||
OnlineSource.update_rate(asset)
|
||||
if OnlineSource.update_rate(asset):
|
||||
to_run_activities.append(asset)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
""" add debit/credit
|
||||
"""
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for values in vlist:
|
||||
if 'updtsource' in values.keys():
|
||||
if values['updtsource'] is None:
|
||||
values['updttime'] = None
|
||||
return super(Asset, cls).create(vlist)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
""" deny update if cashbook.line!='open',
|
||||
add or update debit/credit
|
||||
"""
|
||||
actions = iter(args)
|
||||
for lines, values in zip(actions, actions):
|
||||
if 'updtsource' in values.keys():
|
||||
if values['updtsource'] is None:
|
||||
values['updttime'] = None
|
||||
super(Asset, cls).write(*args)
|
||||
if len(to_run_activities) > 0:
|
||||
cls.after_update_actions(to_run_activities)
|
||||
|
||||
# end Asset
|
||||
|
||||
|
||||
class AssetSourceRel(ModelSQL):
|
||||
'Asset Source Relation'
|
||||
__name__ = 'investment.asset_source_rel'
|
||||
|
||||
source = fields.Many2One(string='Online Source', select=True,
|
||||
required=True, model_name='investment.source',
|
||||
ondelete='CASCADE')
|
||||
asset = fields.Many2One(string='Asset', select=True,
|
||||
required=True, model_name='investment.asset',
|
||||
ondelete='CASCADE')
|
||||
|
||||
# end AssetSourceRel
|
||||
|
|
23
asset.xml
23
asset.xml
|
@ -35,6 +35,29 @@ full copyright notices and license terms. -->
|
|||
<field name="act_window" ref="act_asset_view"/>
|
||||
</record>
|
||||
|
||||
<!-- domain view -->
|
||||
<record model="ir.action.act_window.domain" id="act_asset_domain_current">
|
||||
<field name="name">Current</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="[('date', '>=', Date(delta_days=-5))]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_asset_view"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_asset_domain_inactive">
|
||||
<field name="name">Inactive</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="[('date', '<', Date(delta_days=-5))]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_asset_view"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_asset_domain_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_asset_view"/>
|
||||
</record>
|
||||
|
||||
<!-- permission -->
|
||||
<!-- anon: deny all -->
|
||||
<record model="ir.model.access" id="access_asset-anon">
|
||||
|
|
149
diagram.py
Normal file
149
diagram.py
Normal file
|
@ -0,0 +1,149 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# This file is part of the investment-module from m-ds for Tryton.
|
||||
# The COPYRIGHT file at the top level of this repository contains the
|
||||
# full copyright notices and license terms.
|
||||
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from sql.functions import Function
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
class Concat2(Function):
|
||||
""" concat columns
|
||||
"""
|
||||
__slots__ = ()
|
||||
_function = 'concat'
|
||||
|
||||
# end Concat2
|
||||
|
||||
|
||||
class GraphDef(metaclass=PoolMeta):
|
||||
__name__ = 'diagram.graphdef'
|
||||
|
||||
asset = fields.Many2One(string='Asset',
|
||||
model_name='investment.asset',
|
||||
states={
|
||||
'invisible': Eval('dtype', '') != 'investment.asset',
|
||||
'required': Eval('dtype', '') == 'investment.asset',
|
||||
}, depends=['dtype'])
|
||||
|
||||
@classmethod
|
||||
def _get_dtypes(cls):
|
||||
""" return list of types
|
||||
"""
|
||||
l1 = super(GraphDef, cls)._get_dtypes()
|
||||
l1.append('investment.asset')
|
||||
return l1
|
||||
|
||||
def get_recname_value(self):
|
||||
""" value by dtype
|
||||
"""
|
||||
if self.dtype == 'investment.asset':
|
||||
return getattr(self.asset, 'rec_name', '-')
|
||||
return super(GraphDef, self).get_recname_value()
|
||||
|
||||
def get_field_key(self):
|
||||
""" get to read value from json
|
||||
"""
|
||||
if self.dtype == 'investment.asset':
|
||||
return 'asset%d' % self.asset.id
|
||||
return super(GraphDef, self).get_field_key()
|
||||
|
||||
def get_scaling_for_investment_asset(self):
|
||||
""" get scaling for currency
|
||||
"""
|
||||
Rate = Pool().get('investment.rate')
|
||||
|
||||
if self.scaling == 'fix':
|
||||
return None
|
||||
|
||||
if self.scaling == 'alldata':
|
||||
query = [('asset.id', '=', self.asset.id)]
|
||||
elif self.scaling == 'view':
|
||||
query = [
|
||||
('asset.id', '=', self.asset.id),
|
||||
('date', '>=', self.chart.used_start_date()),
|
||||
('date', '<=', self.chart.used_end_date()),
|
||||
]
|
||||
elif self.scaling == 'six':
|
||||
query = [
|
||||
('asset.id', '=', self.asset.id),
|
||||
('date', '>=', self.chart.used_start_date() - timedelta(days=180)),
|
||||
('date', '<=', self.chart.used_end_date()),
|
||||
]
|
||||
|
||||
min_rec = Rate.search(query, limit=1, order=[('rate', 'ASC')])
|
||||
max_rec = Rate.search(query, limit=1, order=[('rate', 'DESC')])
|
||||
min_val = min_rec[0].rate if len(min_rec) > 0 else None
|
||||
max_val = max_rec[0].rate if len(max_rec) > 0 else None
|
||||
|
||||
return self.compute_scaling_factor(min_val, max_val)
|
||||
|
||||
# end GraphDef
|
||||
|
||||
|
||||
class ChartPoint(metaclass=PoolMeta):
|
||||
__name__ = 'diagram.point'
|
||||
|
||||
@classmethod
|
||||
def get_interpolated_val(cls, keyname, query_date):
|
||||
""" query two neighbour-values to
|
||||
interpolate missing value
|
||||
"""
|
||||
Rate = Pool().get('investment.rate')
|
||||
|
||||
if keyname is None:
|
||||
return None
|
||||
|
||||
# check if query is for us
|
||||
if keyname.startswith('asset'):
|
||||
asset_id = int(keyname[len('asset'):])
|
||||
|
||||
before = Rate.search([
|
||||
('date', '<', query_date),
|
||||
('asset.id', '=', asset_id),
|
||||
], limit=1, order=[('date', 'DESC')])
|
||||
|
||||
after = Rate.search([
|
||||
('date', '>', query_date),
|
||||
('asset.id', '=', asset_id),
|
||||
], limit=1, order=[('date', 'ASC')])
|
||||
|
||||
if (len(before) == 1) and (len(after) == 1):
|
||||
result = cls.interpolate_linear(
|
||||
(after[0].date, after[0].rate),
|
||||
(before[0].date, before[0].rate),
|
||||
query_date
|
||||
)
|
||||
return result
|
||||
elif len(before) == 1:
|
||||
return before[0].rate
|
||||
elif len(after) == 1:
|
||||
return after[0].rate
|
||||
return super(ChartPoint, cls).get_interpolated_val(keyname, query_date)
|
||||
|
||||
@classmethod
|
||||
def get_table_parts(cls):
|
||||
""" return a list of tables to union,
|
||||
table must contain the columns:
|
||||
date, key, val
|
||||
"""
|
||||
pool = Pool()
|
||||
Rate = pool.get('investment.rate')
|
||||
tab_rate = Rate.__table__()
|
||||
|
||||
tabparts = super(ChartPoint, cls).get_table_parts()
|
||||
|
||||
# rate
|
||||
tabparts.append(tab_rate.select(
|
||||
tab_rate.date,
|
||||
Concat2('asset', tab_rate.asset).as_('key'),
|
||||
tab_rate.rate.as_('val'),
|
||||
))
|
||||
return tabparts
|
||||
|
||||
# end ChartPoint
|
16
diagram.xml
Normal file
16
diagram.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?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. -->
|
||||
<tryton>
|
||||
<data depends="diagram">
|
||||
|
||||
<!-- views -->
|
||||
<record model="ir.ui.view" id="graph_view_form">
|
||||
<field name="model">diagram.graphdef</field>
|
||||
<field name="inherit" ref="diagram.graph_view_form"/>
|
||||
<field name="name">graph_form</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</tryton>
|
10
icon.xml
10
icon.xml
|
@ -10,5 +10,15 @@ full copyright notices and license terms. -->
|
|||
<field name="path">icon/gain_invest.svg</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.icon" id="stockonline_icon">
|
||||
<field name="name">mds-stockonline</field>
|
||||
<field name="path">icon/stockonline.svg</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.icon" id="asset_icon">
|
||||
<field name="name">mds-asset</field>
|
||||
<field name="path">icon/asset.svg</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</tryton>
|
||||
|
|
5
icon/asset.svg
Normal file
5
icon/asset.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
|
||||
<g><path d="M729.7,990c-143.8,0-260.3-116.6-260.3-260.3c0-143.8,116.5-260.3,260.3-260.3S990,585.9,990,729.7C990,873.4,873.4,990,729.7,990L729.7,990z M729.7,530.6c-109.9,0-199.1,89.1-199.1,199.1c0,109.9,89.1,199.1,199.1,199.1s199.1-89.1,199.1-199.1C928.7,619.8,839.6,530.6,729.7,530.6L729.7,530.6z M852.2,775.6H699.1c-8.5,0-15.3-6.9-15.3-15.3V607.2c0-8.5,6.9-15.3,15.3-15.3h30.6c8.5,0,15.3,6.9,15.3,15.3v107.2h107.2c8.5,0,15.3,6.9,15.3,15.3v30.6C867.5,768.8,860.6,775.6,852.2,775.6L852.2,775.6z M836.9,101.9c0-16.9-13.7-30.6-30.6-30.6H101.9C85,71.3,71.2,85,71.2,101.9v796.3c0,16.9,13.7,30.6,30.6,30.6h306.2V990H71.2C37.4,990,10,962.5,10,928.7V71.2C10,37.4,37.4,10,71.2,10h765.6c33.8,0,61.3,27.4,61.3,61.3v275.6h-61.3V101.9L836.9,101.9z M239.7,683.7h122.5c8.4,0,15.3,6.9,15.3,15.3v30.6c0,8.4-6.9,15.3-15.3,15.3H239.7c-8.5,0-15.3-6.9-15.3-15.3v-30.6C224.4,690.6,231.2,683.7,239.7,683.7L239.7,683.7z M224.4,301v-30.6c0-8.5,6.8-15.3,15.3-15.3h459.4c8.5,0,15.3,6.9,15.3,15.3V301c0,8.4-6.9,15.3-15.3,15.3H239.7C231.2,316.3,224.4,309.4,224.4,301L224.4,301z M224.4,515.3v-30.6c0-8.5,6.8-15.3,15.3-15.3h214.4c8.5,0,15.3,6.9,15.3,15.3v30.6c0,8.4-6.9,15.3-15.3,15.3H239.7C231.2,530.6,224.4,523.7,224.4,515.3L224.4,515.3z"/></g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
5
icon/stockonline.svg
Normal file
5
icon/stockonline.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
|
||||
<g><g><g><path d="M636.1,146.1c-34.8,0-68.3,5.1-100.1,14.5c-2.7,6.1-5.3,12.2-7.8,18.3l0,0c-3.4,8.3-6.8,16.6-9.9,25c23.8-18.5,50.1-31.5,77.9-38c-17.1,17.7-32.3,46.5-44.7,83.7c-18.2,3.3-35.8,7.5-52.7,12.7c-1.7,5.8-3.2,11.6-4.8,17.4c16.8-5.6,34.4-10.2,52.5-13.8c-10.2,34.9-18.1,75.9-23.1,121.3c-18.3,2-35.9,4.4-52.6,7.3c-0.7,5.2-1.4,10.4-2,15.6c16.7-3,34.5-5.6,53.1-7.6c-1.4,14.7-2.4,29.8-3.2,45.2c1.2-0.1,2.4-0.3,3.7-0.3h11.4c0.8-16,1.9-31.6,3.3-46.5c30.8-2.9,63.9-4.6,99.1-4.6c35.2,0,68.3,1.7,99.1,4.6c2.9,30.8,4.6,63.9,4.6,99.1c0,35.2-1.6,68.3-4.6,99.1c-30.8,2.9-63.9,4.6-99.1,4.6s-68.3-1.7-99.1-4.6c-1.8-18.7-3-38.4-3.8-58.7l-13.6,30.4c0.6,9,1.3,18,2.1,26.7c-4.5-0.5-8.9-1.1-13.3-1.6l-6.4,14.3c7,0.9,14,1.8,21.2,2.6c5,45.4,12.9,86.5,23.1,121.3c-29.1-5.7-56.6-14.2-82.1-25c0.9,5.9,1.9,11.7,3,17.5c26.2,10.4,54.4,18.4,84.1,23.7c12.4,37.3,27.7,66.1,44.8,83.8c-47-10.9-89.5-40.5-123.3-83.4c3.4,12.2,7.5,24.2,12,35.8c13.2,13.2,27.4,24.7,42.4,34.2c-11.5-3.9-22.7-8.4-33.6-13.5c3.1,6.9,6.2,13.8,9.4,20.6c41.1,16.7,85.9,26,132.9,26c195.2,0,354-158.8,354-354C990,304.8,831.2,146.1,636.1,146.1z M889.2,432.1c54.6,19.3,85.7,44,85.7,67.9c0,23.9-31.2,48.6-85.7,67.9c3.1-22,4.8-44.7,4.8-67.9C894,476.8,892.3,454.1,889.2,432.1z M886.5,415.3c-6.4-35.7-16.7-69.2-30.4-99.7C915.4,352,957,402.7,970.3,460.2C952.6,443,923.8,427.8,886.5,415.3z M750.4,402.5c47.5,5.3,88.8,13.8,122.7,24.2c3.8,23.6,5.8,48.1,5.8,73.3c0,25.1-2,49.6-5.8,73.3c-33.9,10.5-75.2,18.9-122.7,24.2c2.9-30.9,4.4-63.5,4.4-97.5C754.9,466,753.3,433.4,750.4,402.5z M956.9,391.3c-25.5-39.7-64.4-74-112.5-99.6c-25.6-48.1-59.8-87-99.6-112.5C844.3,213,923.1,291.8,956.9,391.3z M820.5,280c-30.5-13.6-64.1-23.9-99.7-30.3c-12.4-37.3-27.7-66.1-44.8-83.8C733.4,179.1,784.1,220.7,820.5,280z M636.1,161.2c23.9,0,48.6,31.2,67.9,85.7c-22-3.1-44.7-4.8-67.9-4.8c-23.2,0-46,1.7-67.9,4.8C587.4,192.3,612.2,161.2,636.1,161.2z M636.1,381.3c-34,0-66.6,1.6-97.5,4.5c5.3-47.5,13.8-88.9,24.3-122.7c23.6-3.8,48.1-5.8,73.2-5.8c25.2,0,49.7,2,73.3,5.8c10.5,33.9,18.9,75.2,24.2,122.7C702.7,382.8,670,381.3,636.1,381.3z M725.8,265.9c39.1,7.7,75.4,20.3,107.5,36.9c16.5,32.1,29.1,68.4,36.8,107.5c-34.9-10.2-75.9-18.1-121.3-23.1C743.9,341.8,736,300.8,725.8,265.9z M748.9,612.8c45.4-5,86.4-12.9,121.3-23.1c-7.7,39.1-20.3,75.4-36.8,107.5c-32.1,16.5-68.4,29.1-107.5,36.9C736,699.3,743.9,658.2,748.9,612.8z M538.6,614.3c30.9,2.9,63.5,4.4,97.5,4.4c34,0,66.6-1.6,97.5-4.4c-5.3,47.5-13.8,88.8-24.2,122.7c-23.6,3.7-48.1,5.8-73.3,5.8c-25.2,0-49.7-2.1-73.3-5.8C552.3,703.1,543.9,661.8,538.6,614.3z M636.1,838.8c-23.9,0-48.6-31.1-67.9-85.7c22,3.1,44.7,4.8,67.9,4.8c23.2,0,45.9-1.7,67.9-4.8C684.7,807.7,660,838.8,636.1,838.8z M720.8,750.4c35.7-6.4,69.2-16.7,99.7-30.3c-36.4,59.3-87.1,100.8-144.6,114.2C693.1,816.6,708.3,787.7,720.8,750.4z M744.8,820.8c39.7-25.5,74-64.4,99.6-112.5c48.1-25.6,87-59.9,112.5-99.6C923.1,708.2,844.3,787,744.8,820.8z M856.1,684.4c13.6-30.5,23.9-64.1,30.4-99.7c37.3-12.4,66.1-27.7,83.8-44.9C957,597.3,915.4,648.1,856.1,684.4z"/><path d="M460,612.9L512,496.6h136.3c8.3,0,15.1-6.8,15.1-15.1c0-8.3-6.8-15.1-15.1-15.1H502.1c-6,0-11.4,3.5-13.8,9l-56.7,127.1c-0.6,0-1.1-0.1-1.7-0.1c-1.5,0-3,0.1-4.5,0.2l-90-252.2c11.4-8.9,18.8-22.7,18.8-38.2c0-26.7-21.7-48.4-48.4-48.4c-26.7,0-48.4,21.7-48.4,48.4c0,12.6,4.9,24.1,12.9,32.8l-58.9,105.8H104.4c-6.4-19.3-24.6-33.3-46-33.3c-26.7,0-48.4,21.7-48.4,48.4c0,26.7,21.7,48.4,48.4,48.4c21.5,0,39.7-14,46-33.4h115.7c5.5,0,10.5-3,13.2-7.8l63.2-113.5c3,0.6,6,0.9,9.2,0.9c0.4,0,0.8-0.1,1.2-0.1l90.7,254.2c-9.9,8.9-16.2,21.7-16.2,36.1c0,26.7,21.7,48.4,48.4,48.4c26.7,0,48.4-21.7,48.4-48.4C478.4,635.5,471.2,621.8,460,612.9z M58.4,506.8c-22.5,0-40.9-18.3-40.9-40.9c0-22.5,18.3-40.9,40.9-40.9c17.2,0,31.9,10.7,37.9,25.7H58.4c-8.3,0-15.1,6.8-15.1,15.1c0,8.3,6.8,15.1,15.1,15.1h38C90.4,496,75.7,506.8,58.4,506.8z M264.8,312.2c0-22.5,18.3-40.9,40.9-40.9c22.5,0,40.9,18.3,40.9,40.9c0,12.2-5.4,23.1-13.9,30.6l-12.8-35.7c-2-5.6-7.1-9.5-13-10c-5.9-0.5-11.6,2.5-14.4,7.7L274,338C268.3,330.9,264.8,322,264.8,312.2z M300.4,352.7l2.2-4l1.5,4.3C303,352.9,301.7,352.8,300.4,352.7z M428.2,610l-0.1,0.2l-0.1-0.2C428.1,610,428.2,610,428.2,610z M429.9,691.7c-22.5,0-40.9-18.3-40.9-40.9c0-11,4.4-20.9,11.4-28.3l11.9,33.4c2.1,5.8,7.5,9.8,13.7,10c0.2,0,0.4,0,0.6,0c5.9,0,11.4-3.5,13.8-9l16.4-36.8c8.5,7.5,14,18.5,14,30.7C470.8,673.3,452.5,691.7,429.9,691.7z"/></g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g></g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.6 KiB |
146
locale/de.po
146
locale/de.po
|
@ -67,6 +67,22 @@ msgid "Update Source"
|
|||
msgstr "Quelle aktualisieren"
|
||||
|
||||
|
||||
###############################
|
||||
# ir.action.act_window.domain #
|
||||
###############################
|
||||
msgctxt "model:ir.action.act_window.domain,name:act_asset_domain_current"
|
||||
msgid "Current"
|
||||
msgstr "Aktuell"
|
||||
|
||||
msgctxt "model:ir.action.act_window.domain,name:act_asset_domain_inactive"
|
||||
msgid "Inactive"
|
||||
msgstr "Inaktiv"
|
||||
|
||||
msgctxt "model:ir.action.act_window.domain,name:act_asset_domain_all"
|
||||
msgid "All"
|
||||
msgstr "Alle"
|
||||
|
||||
|
||||
############################
|
||||
# investment.source_update #
|
||||
############################
|
||||
|
@ -86,6 +102,10 @@ msgctxt "view:investment.asset:"
|
|||
msgid "Currency and Units"
|
||||
msgstr "Währung und Einheiten"
|
||||
|
||||
msgctxt "view:investment.asset:"
|
||||
msgid "Gain and Loss"
|
||||
msgstr "Gewinn und Verlust"
|
||||
|
||||
msgctxt "view:investment.asset:"
|
||||
msgid "Identifiers"
|
||||
msgstr "Bezeichner"
|
||||
|
@ -102,21 +122,13 @@ msgctxt "field:investment.asset,company:"
|
|||
msgid "Company"
|
||||
msgstr "Unternehmen"
|
||||
|
||||
msgctxt "field:investment.asset,company_currency:"
|
||||
msgid "Company Currency"
|
||||
msgstr "Unternehmenswährung"
|
||||
|
||||
msgctxt "field:investment.asset,company_currency_digits:"
|
||||
msgid "Currency Digits (Ref.)"
|
||||
msgstr "Nachkommastellen Währung (Ref.)"
|
||||
|
||||
msgctxt "field:investment.asset,currency:"
|
||||
msgid "Currency"
|
||||
msgstr "Währung"
|
||||
|
||||
msgctxt "field:investment.asset,currency_digits:"
|
||||
msgid "Currency Digits"
|
||||
msgstr "Nachkommastellen Währung"
|
||||
msgid "Digits"
|
||||
msgstr "Nachkommastellen"
|
||||
|
||||
msgctxt "field:investment.asset,product:"
|
||||
msgid "Product"
|
||||
|
@ -134,6 +146,10 @@ msgctxt "field:investment.asset,uom:"
|
|||
msgid "UOM"
|
||||
msgstr "Einheit"
|
||||
|
||||
msgctxt "field:investment.asset,symbol:"
|
||||
msgid "UOM"
|
||||
msgstr "Einheit"
|
||||
|
||||
msgctxt "field:investment.asset,wkn:"
|
||||
msgid "NSIN"
|
||||
msgstr "WKN"
|
||||
|
@ -162,25 +178,105 @@ msgctxt "field:investment.asset,rates:"
|
|||
msgid "Rates"
|
||||
msgstr "Kurse"
|
||||
|
||||
msgctxt "field:investment.asset,name:"
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
msgctxt "field:investment.asset,rate:"
|
||||
msgid "Current Rate"
|
||||
msgstr "aktueller Kurs"
|
||||
|
||||
msgctxt "field:investment.asset,updtsource:"
|
||||
msgid "Update Source"
|
||||
msgstr "Kursquelle"
|
||||
msgctxt "field:investment.asset,date:"
|
||||
msgid "Date"
|
||||
msgstr "Datum"
|
||||
|
||||
msgctxt "help:investment.asset,updtsource:"
|
||||
msgid "Select a source for the course update."
|
||||
msgstr "Wählen Sie eine Quelle für die Kursaktualisierung aus."
|
||||
msgctxt "help:investment.asset,date:"
|
||||
msgid "Date of current rate"
|
||||
msgstr "Datum des aktuellen Kurses"
|
||||
|
||||
msgctxt "field:investment.asset,updtsources:"
|
||||
msgid "Update Sources"
|
||||
msgstr "Kursquellen"
|
||||
|
||||
msgctxt "help:investment.asset,updtsources:"
|
||||
msgid "Select sources for the course update. The course sources are tried until a valid value has been read."
|
||||
msgstr "Wählen Sie Quellen für die Kursaktualisierung aus. Die Kursquellen werden probiert bis ein gültiger Wert gelesen wurde."
|
||||
|
||||
msgctxt "field:investment.asset,updttime:"
|
||||
msgid "Time"
|
||||
msgstr "Zeitpunkt"
|
||||
|
||||
msgctxt "field:investment.asset,updtneeded:"
|
||||
msgid "Course update needed"
|
||||
msgstr "Kursaktualisierung nötig"
|
||||
msgctxt "field:investment.asset,nextupdate:"
|
||||
msgid "Next Update"
|
||||
msgstr "nächste Aktualisierung"
|
||||
|
||||
msgctxt "field:investment.asset,change_day1:"
|
||||
msgid "Previous Day"
|
||||
msgstr "Vortag"
|
||||
|
||||
msgctxt "help:investment.asset,change_day1:"
|
||||
msgid "percentage change in value compared to the previous day"
|
||||
msgstr "prozentuale Wertänderung zum Vortag"
|
||||
|
||||
msgctxt "field:investment.asset,change_month1:"
|
||||
msgid "1 Month"
|
||||
msgstr "1 Monat"
|
||||
|
||||
msgctxt "help:investment.asset,change_month1:"
|
||||
msgid "percentage change in value compared to last month"
|
||||
msgstr "prozentuale Wertänderung zum letzten Monat"
|
||||
|
||||
msgctxt "field:investment.asset,change_month3:"
|
||||
msgid "3 Months"
|
||||
msgstr "3 Monate"
|
||||
|
||||
msgctxt "help:investment.asset,change_month3:"
|
||||
msgid "percentage change in value during 3 months"
|
||||
msgstr "Prozentuale Wertänderung während 3 Monate"
|
||||
|
||||
msgctxt "field:investment.asset,change_month6:"
|
||||
msgid "6 Months"
|
||||
msgstr "6 Monate"
|
||||
|
||||
msgctxt "help:investment.asset,change_month6:"
|
||||
msgid "percentage change in value during 6 months"
|
||||
msgstr "Prozentuale Wertänderung während 6 Monate"
|
||||
|
||||
msgctxt "field:investment.asset,change_month12:"
|
||||
msgid "1 Year"
|
||||
msgstr "1 Jahr"
|
||||
|
||||
msgctxt "help:investment.asset,change_month12:"
|
||||
msgid "percentage change in value during 1 year"
|
||||
msgstr "Prozentuale Wertänderung während 1 Jahr"
|
||||
|
||||
msgctxt "field:investment.asset,updtdays:"
|
||||
msgid "Select days"
|
||||
msgstr "Tage wählen"
|
||||
|
||||
msgctxt "selection:investment.asset,updtdays:"
|
||||
msgid "Mon - Fri"
|
||||
msgstr "Mo - Fr"
|
||||
|
||||
msgctxt "selection:investment.asset,updtdays:"
|
||||
msgid "Mon - Sun"
|
||||
msgstr "Mo - So"
|
||||
|
||||
|
||||
###############################
|
||||
# investment.asset-source-rel #
|
||||
###############################
|
||||
msgctxt "model:investment.asset_source_rel,name:"
|
||||
msgid "Asset Source Relation"
|
||||
msgstr "Vermögenswert Quelle Verknüpfung"
|
||||
|
||||
msgctxt "field:investment.asset_source_rel,source:"
|
||||
msgid "Source"
|
||||
msgstr "Quelle"
|
||||
|
||||
msgctxt "field:investment.asset_source_rel,asset:"
|
||||
msgid "Asset"
|
||||
msgstr "Vermögenswert"
|
||||
|
||||
|
||||
#####################
|
||||
|
@ -393,3 +489,15 @@ msgstr "Wertpapierkennnummer (WKN)"
|
|||
msgctxt "selection:product.identifier,type:"
|
||||
msgid "Stock market symbol"
|
||||
msgstr "Börsensymbol"
|
||||
|
||||
|
||||
####################
|
||||
# diagram.graphdef #
|
||||
####################
|
||||
msgctxt "view:diagram.graphdef:"
|
||||
msgid "Asset"
|
||||
msgstr "Vermögenswert"
|
||||
|
||||
msgctxt "field:diagram.graphdef,asset:"
|
||||
msgid "Asset"
|
||||
msgstr "Vermögenswert"
|
||||
|
|
134
locale/en.po
134
locale/en.po
|
@ -46,6 +46,18 @@ msgctxt "model:ir.action,name:updt_source_wiz"
|
|||
msgid "Update Source"
|
||||
msgstr "Update Source"
|
||||
|
||||
msgctxt "model:ir.action.act_window.domain,name:act_asset_domain_current"
|
||||
msgid "Current"
|
||||
msgstr "Current"
|
||||
|
||||
msgctxt "model:ir.action.act_window.domain,name:act_asset_domain_inactive"
|
||||
msgid "Inactive"
|
||||
msgstr "Inactive"
|
||||
|
||||
msgctxt "model:ir.action.act_window.domain,name:act_asset_domain_all"
|
||||
msgid "All"
|
||||
msgstr "All"
|
||||
|
||||
msgctxt "model:investment.source_update,name:"
|
||||
msgid "Update Source"
|
||||
msgstr "Update Source"
|
||||
|
@ -58,6 +70,10 @@ msgctxt "view:investment.asset:"
|
|||
msgid "Currency and Units"
|
||||
msgstr "Currency and Units"
|
||||
|
||||
msgctxt "view:investment.asset:"
|
||||
msgid "Gain and Loss"
|
||||
msgstr "Gain and Loss"
|
||||
|
||||
msgctxt "view:investment.asset:"
|
||||
msgid "Identifiers"
|
||||
msgstr "Identifiers"
|
||||
|
@ -74,21 +90,13 @@ msgctxt "field:investment.asset,company:"
|
|||
msgid "Company"
|
||||
msgstr "Company"
|
||||
|
||||
msgctxt "field:investment.asset,company_currency:"
|
||||
msgid "Company Currency"
|
||||
msgstr "Company Currency"
|
||||
|
||||
msgctxt "field:investment.asset,company_currency_digits:"
|
||||
msgid "Currency Digits (Ref.)"
|
||||
msgstr "Currency Digits (Ref.)"
|
||||
|
||||
msgctxt "field:investment.asset,currency:"
|
||||
msgid "Currency"
|
||||
msgstr "Currency"
|
||||
|
||||
msgctxt "field:investment.asset,currency_digits:"
|
||||
msgid "Currency Digits"
|
||||
msgstr "Currency Digits"
|
||||
msgid "Digits"
|
||||
msgstr "Digits"
|
||||
|
||||
msgctxt "field:investment.asset,product:"
|
||||
msgid "Product"
|
||||
|
@ -106,6 +114,10 @@ msgctxt "field:investment.asset,uom:"
|
|||
msgid "UOM"
|
||||
msgstr "UOM"
|
||||
|
||||
msgctxt "field:investment.asset,symbol:"
|
||||
msgid "UOM"
|
||||
msgstr "UOM"
|
||||
|
||||
msgctxt "field:investment.asset,wkn:"
|
||||
msgid "NSIN"
|
||||
msgstr "NSIN"
|
||||
|
@ -134,25 +146,101 @@ msgctxt "field:investment.asset,rates:"
|
|||
msgid "Rates"
|
||||
msgstr "Rates"
|
||||
|
||||
msgctxt "field:investment.asset,name:"
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
msgctxt "field:investment.asset,rate:"
|
||||
msgid "Current Rate"
|
||||
msgstr "Current Rate"
|
||||
|
||||
msgctxt "field:investment.asset,updtsource:"
|
||||
msgid "Update Source"
|
||||
msgstr "Update Source"
|
||||
msgctxt "field:investment.asset,date:"
|
||||
msgid "Date"
|
||||
msgstr "Date"
|
||||
|
||||
msgctxt "help:investment.asset,updtsource:"
|
||||
msgid "Select a source for the course update."
|
||||
msgstr "Select a source for the course update."
|
||||
msgctxt "help:investment.asset,date:"
|
||||
msgid "Date of current rate"
|
||||
msgstr "Date of current rate"
|
||||
|
||||
msgctxt "field:investment.asset,updtsources:"
|
||||
msgid "Update Sources"
|
||||
msgstr "Update Sources"
|
||||
|
||||
msgctxt "help:investment.asset,updtsources:"
|
||||
msgid "Select sources for the course update. The course sources are tried until a valid value has been read."
|
||||
msgstr "Select sources for the course update. The course sources are tried until a valid value has been read."
|
||||
|
||||
msgctxt "field:investment.asset,updttime:"
|
||||
msgid "Time"
|
||||
msgstr "Time"
|
||||
|
||||
msgctxt "field:investment.asset,updtneeded:"
|
||||
msgid "Course update needed"
|
||||
msgstr "Course update needed"
|
||||
msgctxt "field:investment.asset,nextupdate:"
|
||||
msgid "Next Update"
|
||||
msgstr "Next Update"
|
||||
|
||||
msgctxt "field:investment.asset,change_day1:"
|
||||
msgid "Previous Day"
|
||||
msgstr "Previous Day"
|
||||
|
||||
msgctxt "help:investment.asset,change_day1:"
|
||||
msgid "percentage change in value compared to the previous day"
|
||||
msgstr "percentage change in value compared to the previous day"
|
||||
|
||||
msgctxt "field:investment.asset,change_month1:"
|
||||
msgid "1 Month"
|
||||
msgstr "1 Month"
|
||||
|
||||
msgctxt "help:investment.asset,change_month1:"
|
||||
msgid "percentage change in value compared to last month"
|
||||
msgstr "percentage change in value compared to last month"
|
||||
|
||||
msgctxt "field:investment.asset,change_month3:"
|
||||
msgid "3 Months"
|
||||
msgstr "3 Months"
|
||||
|
||||
msgctxt "help:investment.asset,change_month3:"
|
||||
msgid "percentage change in value during 3 months"
|
||||
msgstr "percentage change in value during 3 months"
|
||||
|
||||
msgctxt "field:investment.asset,change_month6:"
|
||||
msgid "6 Months"
|
||||
msgstr "6 Months"
|
||||
|
||||
msgctxt "help:investment.asset,change_month6:"
|
||||
msgid "percentage change in value during 6 months"
|
||||
msgstr "percentage change in value during 6 months"
|
||||
|
||||
msgctxt "field:investment.asset,change_month12:"
|
||||
msgid "1 Year"
|
||||
msgstr "1 Year"
|
||||
|
||||
msgctxt "help:investment.asset,change_month12:"
|
||||
msgid "percentage change in value during 1 year"
|
||||
msgstr "percentage change in value during 1 year"
|
||||
|
||||
msgctxt "field:investment.asset,updtdays:"
|
||||
msgid "Select days"
|
||||
msgstr "Select days"
|
||||
|
||||
msgctxt "selection:investment.asset,updtdays:"
|
||||
msgid "Mon - Fri"
|
||||
msgstr "Mon - Fri"
|
||||
|
||||
msgctxt "selection:investment.asset,updtdays:"
|
||||
msgid "Mon - Sun"
|
||||
msgstr "Mon - Sun"
|
||||
|
||||
msgctxt "model:investment.asset_source_rel,name:"
|
||||
msgid "Asset Source Relation"
|
||||
msgstr "Asset Source Relation"
|
||||
|
||||
msgctxt "field:investment.asset_source_rel,source:"
|
||||
msgid "Source"
|
||||
msgstr "Source"
|
||||
|
||||
msgctxt "field:investment.asset_source_rel,asset:"
|
||||
msgid "Asset"
|
||||
msgstr "Asset"
|
||||
|
||||
msgctxt "model:investment.source,name:"
|
||||
msgid "Online Source"
|
||||
|
@ -350,3 +438,11 @@ msgctxt "selection:product.identifier,type:"
|
|||
msgid "National Securities Identifying Number (NSIN)"
|
||||
msgstr "National Securities Identifying Number (NSIN)"
|
||||
|
||||
msgctxt "selection:product.identifier,type:"
|
||||
msgid "Stock market symbol"
|
||||
msgstr "Stock market symbol"
|
||||
|
||||
msgctxt "view:diagram.graphdef:"
|
||||
msgid "Asset"
|
||||
msgstr "Asset"
|
||||
|
||||
|
|
4
menu.xml
4
menu.xml
|
@ -19,7 +19,7 @@ full copyright notices and license terms. -->
|
|||
|
||||
<!-- menu: /Investment/Online Source -->
|
||||
<menuitem id="menu_onlinesource" action="act_source_view" sequence="10"
|
||||
icon="tryton-list" parent="menu_investment"/>
|
||||
icon="mds-stockonline" parent="menu_investment"/>
|
||||
<record model="ir.ui.menu-res.group" id="menu_onlinesource-group_investment_admin">
|
||||
<field name="menu" ref="menu_onlinesource"/>
|
||||
<field name="group" ref="group_investment_admin"/>
|
||||
|
@ -27,7 +27,7 @@ full copyright notices and license terms. -->
|
|||
|
||||
<!-- menu: /Investment/Asset -->
|
||||
<menuitem id="menu_asset" action="act_asset_view" sequence="20"
|
||||
icon="tryton-list" parent="menu_investment"/>
|
||||
icon="mds-asset" parent="menu_investment"/>
|
||||
<record model="ir.ui.menu-res.group" id="menu_asset-group_investment">
|
||||
<field name="menu" ref="menu_asset"/>
|
||||
<field name="group" ref="group_investment"/>
|
||||
|
|
|
@ -28,8 +28,11 @@ sel_rgxidtype = [
|
|||
|
||||
sel_rgxdatefmt = [
|
||||
('%d.%m.%Y', 'dd.mm.yyyy'),
|
||||
('%d.%m.%y', 'dd.mm.yy'),
|
||||
('%m/%d/%Y', 'mm/dd/yyyy'),
|
||||
('%m/%d/%y', 'mm/dd/yy'),
|
||||
('%Y-%m-%d', 'yyyy-mm-dd'),
|
||||
('%b %d %Y', 'mon dd yyyy'),
|
||||
]
|
||||
|
||||
fields_check = ['url', 'nsin', 'isin', 'symbol', 'text', 'http_state', \
|
||||
|
@ -85,6 +88,11 @@ class OnlineSource(ModelSQL, ModelView):
|
|||
help='Identifier found during test query.'),
|
||||
'on_change_with_fndident')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(OnlineSource, cls).__setup__()
|
||||
cls._order.insert(0, ('name', 'DESC'))
|
||||
|
||||
@classmethod
|
||||
def default_url(cls):
|
||||
""" defaul-url
|
||||
|
@ -221,48 +229,56 @@ class OnlineSource(ModelSQL, ModelView):
|
|||
"""
|
||||
pool = Pool()
|
||||
Rate = pool.get('investment.rate')
|
||||
IrDate = pool.get('ir.date')
|
||||
|
||||
if asset.updtsource is None:
|
||||
if len(asset.updtsources) == 0:
|
||||
return
|
||||
rate_data = cls.read_from_website(
|
||||
asset.updtsource,
|
||||
isin = asset.isin,
|
||||
nsin = asset.wkn,
|
||||
symbol = asset.secsymb,
|
||||
)
|
||||
|
||||
if len(asset.updtsource.rgxident or '') > 0:
|
||||
# check result - same code?
|
||||
code = rate_data.get('code', None)
|
||||
if code:
|
||||
asset_code = getattr(asset, {
|
||||
'isin': 'isin',
|
||||
'nsin': 'wkn',
|
||||
'symbol': 'secsymb',
|
||||
}[asset.updtsource.rgxidtype])
|
||||
if (asset_code or '').lower() != code.lower():
|
||||
# fail
|
||||
logger.warning(
|
||||
'update_rate: got wrong code "%(wrong)s" - expected "%(exp)s"' % {
|
||||
'exp': asset_code,
|
||||
'wrong': code,
|
||||
})
|
||||
return False
|
||||
for updtsource in asset.updtsources:
|
||||
rate_data = cls.read_from_website(
|
||||
updtsource,
|
||||
isin = asset.isin,
|
||||
nsin = asset.wkn,
|
||||
symbol = asset.secsymb,
|
||||
)
|
||||
|
||||
to_create = {
|
||||
'date': rate_data.get('date', None),
|
||||
'rate': rate_data.get('rate', None),
|
||||
'asset': asset.id,
|
||||
}
|
||||
if (to_create['date'] is not None) and \
|
||||
(to_create['rate'] is not None):
|
||||
# check if exists
|
||||
if Rate.search_count([
|
||||
('asset.id', '=', asset.id),
|
||||
('date', '=', to_create['date']),
|
||||
]) == 0:
|
||||
Rate.create([to_create])
|
||||
return True
|
||||
if len(updtsource.rgxident or '') > 0:
|
||||
# check result - same code?
|
||||
code = rate_data.get('code', None)
|
||||
if code:
|
||||
asset_code = getattr(asset, {
|
||||
'isin': 'isin',
|
||||
'nsin': 'wkn',
|
||||
'symbol': 'secsymb',
|
||||
}[updtsource.rgxidtype])
|
||||
if (asset_code or '').lower() != code.lower():
|
||||
# fail
|
||||
logger.warning(
|
||||
'update_rate: got wrong code "%(wrong)s" - expected "%(exp)s"' % {
|
||||
'exp': asset_code,
|
||||
'wrong': code,
|
||||
})
|
||||
continue
|
||||
|
||||
to_create = {
|
||||
'date': rate_data.get('date', None),
|
||||
'rate': rate_data.get('rate', None),
|
||||
'asset': asset.id,
|
||||
}
|
||||
if (to_create['date'] is not None) and \
|
||||
(to_create['rate'] is not None):
|
||||
# check if exists
|
||||
if Rate.search_count([
|
||||
('asset.id', '=', asset.id),
|
||||
('date', '=', to_create['date']),
|
||||
]) == 0:
|
||||
Rate.create([to_create])
|
||||
return True
|
||||
else :
|
||||
# if we got a record for today - stop
|
||||
# otherwise try next source
|
||||
if to_create['date'] == IrDate.today():
|
||||
break
|
||||
return False
|
||||
|
||||
def get_regex_result(self, html_text, field_name):
|
||||
|
|
2
scripts/__init__.py
Normal file
2
scripts/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
176
scripts/import_investment_historicalrates.py
Normal file
176
scripts/import_investment_historicalrates.py
Normal file
|
@ -0,0 +1,176 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of the currency_ecbrate-module from m-ds for Tryton.
|
||||
# The COPYRIGHT file at the top level of this repository contains the
|
||||
# full copyright notices and license terms.
|
||||
|
||||
import csv, os, sys
|
||||
from argparse import ArgumentParser
|
||||
from datetime import datetime, date
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
try:
|
||||
from proteus import Model, config
|
||||
except ImportError:
|
||||
prog = os.path.basename(sys.argv[0])
|
||||
sys.exit("proteus must be installed to use %s" % prog)
|
||||
|
||||
|
||||
def read_csv_file(file_name, dec_devider, date_fmt, delimiter):
|
||||
""" read file from csv
|
||||
"""
|
||||
result = []
|
||||
|
||||
del_chars = ['.', ',']
|
||||
del_chars.remove(dec_devider)
|
||||
min_date = None
|
||||
max_date = None
|
||||
min_rate = None
|
||||
max_rate = None
|
||||
|
||||
with open(file_name, 'r', encoding='latin1') as fhdl:
|
||||
csv_lines = csv.DictReader(fhdl, dialect='excel', delimiter=delimiter)
|
||||
|
||||
for line in csv_lines:
|
||||
try :
|
||||
date_val = datetime.strptime(line.get('date', None).strip(), date_fmt).date()
|
||||
except :
|
||||
raise ValueError('- failed to read column 1 of file, expected date (format: %s)' % date_fmt)
|
||||
|
||||
try :
|
||||
rate_val = line.get('rate', None).replace(del_chars[0], '').strip()
|
||||
rate_val = Decimal(rate_val.replace(dec_devider, '.'))
|
||||
except :
|
||||
raise ValueError('- failed to read column 1 of file, expected date (format: %s)' % date_fmt)
|
||||
|
||||
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 :
|
||||
if max_date < date_val:
|
||||
max_date = date_val
|
||||
|
||||
if min_date is None:
|
||||
min_date = date_val
|
||||
else :
|
||||
if min_date > date_val:
|
||||
min_date = date_val
|
||||
|
||||
# rate range
|
||||
if max_rate is None:
|
||||
max_rate = rate_val
|
||||
else :
|
||||
if max_rate < rate_val:
|
||||
max_rate = rate_val
|
||||
|
||||
if min_rate is None:
|
||||
min_rate = rate_val
|
||||
else :
|
||||
if min_rate > rate_val:
|
||||
min_rate = rate_val
|
||||
|
||||
else :
|
||||
raise ValueError('- failed to identify row content: %s' % line)
|
||||
|
||||
print('- found %d records' % len(result))
|
||||
print('- dates from %s to %s' % (
|
||||
min_date.isoformat() if min_date is not None else '-',
|
||||
max_date.isoformat() if max_date is not None else '-',
|
||||
))
|
||||
print('- rates from %s to %s' % (
|
||||
str(min_rate) if min_rate is not None else '-',
|
||||
str(max_rate) if max_rate is not None else '-',
|
||||
))
|
||||
return (result, max_date, min_date)
|
||||
|
||||
|
||||
|
||||
def upload_rates(isin, rates_list, max_date, min_date):
|
||||
""" generate to_create for rates
|
||||
"""
|
||||
Rate = Model.get('investment.rate')
|
||||
Asset = Model.get('investment.asset')
|
||||
|
||||
# get id of asset by isin
|
||||
assets = Asset.find([
|
||||
('isin', '=', isin),
|
||||
])
|
||||
if len(assets) == 0:
|
||||
print('- ISIN %s not found' % isin)
|
||||
return
|
||||
|
||||
# get rate in date-range
|
||||
rates = Rate.find([
|
||||
('asset.id', '=', assets[0].id),
|
||||
('date', '>=', min_date),
|
||||
('date', '<=', max_date),
|
||||
])
|
||||
existing_dates = [x.date for x in rates]
|
||||
done_dates = []
|
||||
|
||||
to_create = []
|
||||
for rate in rates_list:
|
||||
if rate['date'] in existing_dates:
|
||||
continue
|
||||
if rate['date'] in done_dates:
|
||||
continue
|
||||
|
||||
to_create.append({
|
||||
'asset': assets[0].id,
|
||||
'date': rate['date'],
|
||||
'rate': rate['rate'],
|
||||
})
|
||||
done_dates.append(rate['date'])
|
||||
|
||||
if len(to_create) > 0:
|
||||
print('- upload %d historical rates...' % len(to_create))
|
||||
Rate.create(to_create, context={})
|
||||
print('- finished upload')
|
||||
else :
|
||||
print('- nothing to upload')
|
||||
|
||||
|
||||
def do_import(csv_file, isin, dec_devider, date_fmt, delimiter):
|
||||
""" run import
|
||||
"""
|
||||
print('\n--== Import historical asset rates ==--')
|
||||
print('- file: %s' % csv_file)
|
||||
print('- ISIN: %s' % isin)
|
||||
print('- date-format: %s, decimal divider: "%s", delimiter: "%s"' % (date_fmt, dec_devider, delimiter))
|
||||
(lines, max_date, min_date) = read_csv_file(csv_file, dec_devider, date_fmt, delimiter)
|
||||
upload_rates(isin, lines, max_date, min_date)
|
||||
|
||||
print('--== finish import ==--')
|
||||
|
||||
|
||||
def main(database, config_file, csv_file, dec_devider, date_fmt, isin, delimiter):
|
||||
config.set_trytond(database, config_file=config_file)
|
||||
with config.get_config().set_context(active_test=False):
|
||||
do_import(csv_file, isin, dec_devider, date_fmt, delimiter)
|
||||
|
||||
|
||||
def run():
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('-d', '--database', dest='database', required=True)
|
||||
parser.add_argument('-c', '--config', dest='config_file', help='the trytond config file')
|
||||
parser.add_argument('-f', '--file', dest='csv_file', required=True,
|
||||
help='CSV-file to import, should contain two columns: 1. date, 2. numeric, first line must have "date" and "rate"')
|
||||
parser.add_argument('-p', '--decimal', default=',', dest='decimal_divider',
|
||||
help='decimal divider, defaults to: ,')
|
||||
parser.add_argument('-t', '--delimiter', default=';', dest='delimiter',
|
||||
help='field delimiter for csv-table, defaults to: ;')
|
||||
parser.add_argument('-a', '--dateformat', default='%d.%m.%Y', dest='date_format',
|
||||
help='date format like %%d.%%m.%%Y or %%Y-%%m-%%d or similiar')
|
||||
parser.add_argument('-i', '--isin', dest='isin', required=True, help='ISIN of the target asset')
|
||||
|
||||
args = parser.parse_args()
|
||||
main(args.database, args.config_file, args.csv_file, args.decimal_divider, \
|
||||
args.date_format, args.isin, args.delimiter)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
4
setup.py
4
setup.py
|
@ -97,7 +97,7 @@ setup(name='%s_%s' % (PREFIX, MODULE),
|
|||
package_data={
|
||||
'trytond.modules.%s' % MODULE: (info.get('xml', [])
|
||||
+ ['tryton.cfg', 'locale/*.po', 'tests/*.py',
|
||||
'view/*.xml', 'icon/*.svg',
|
||||
'view/*.xml', 'icon/*.svg', 'scripts/*.py',
|
||||
'versiondep.txt', 'README.rst']),
|
||||
},
|
||||
|
||||
|
@ -106,5 +106,7 @@ setup(name='%s_%s' % (PREFIX, MODULE),
|
|||
entry_points="""
|
||||
[trytond.modules]
|
||||
%s = trytond.modules.%s
|
||||
[console_scripts]
|
||||
trytond_import_investment_historicalrates = trytond.modules.investment.scripts.import_investment_historicalrates:run [data]
|
||||
""" % (MODULE, MODULE),
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ full copyright notices and license terms. -->
|
|||
<data>
|
||||
|
||||
<record model="investment.source" id="web_finanzen_net">
|
||||
<field name="name">www.finanzen.net - ETF</field>
|
||||
<field name="name">www.finanzen.net - ETF (Tradegate)</field>
|
||||
<field name="url">https://www.finanzen.net/etf/${isin}/tgt</field>
|
||||
<field name="nohtml" eval="True"/>
|
||||
<field name="rgxdate">\nKurszeit (\d+\.\d+\.\d+) \d{2}:\d{2}:\d{2}.*\n</field>
|
||||
|
@ -16,6 +16,57 @@ full copyright notices and license terms. -->
|
|||
<field name="rgxident">WKN:.* ISIN: ([A-Z,0-9]+).*</field>
|
||||
<field name="rgxidtype">isin</field>
|
||||
</record>
|
||||
<record model="investment.source" id="web_finanzen_net_stu">
|
||||
<field name="name">www.finanzen.net - ETF (Stuttgard)</field>
|
||||
<field name="url">https://www.finanzen.net/etf/${isin}/stu</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="rgxdecimal">,</field>
|
||||
<field name="rgxident">WKN:.* ISIN: ([A-Z,0-9]+).*</field>
|
||||
<field name="rgxidtype">isin</field>
|
||||
</record>
|
||||
<record model="investment.source" id="web_finanzen_fonds">
|
||||
<field name="name">www.finanzen.net - Fonds</field>
|
||||
<field name="url">https://www.finanzen.net/fonds/${isin}/tgt</field>
|
||||
<field name="nohtml" eval="True"/>
|
||||
<field name="rgxdate">\n\*\*Kursdatum\*\* (\d+\.\d+\.\d+).*\n</field>
|
||||
<field name="rgxdatefmt">%d.%m.%Y</field>
|
||||
<field name="rgxrate">\n\*\*Kurs\*\* (\d+,\d+) EUR.*\n</field>
|
||||
<field name="rgxdecimal">,</field>
|
||||
<field name="rgxident">WKN:.* ISIN: ([A-Z,0-9]+).*</field>
|
||||
<field name="rgxidtype">isin</field>
|
||||
</record>
|
||||
<record model="investment.source" id="web_finanzen_rohstoffe">
|
||||
<field name="name">www.finanzen.net - Rohstoffe</field>
|
||||
<field name="url">https://www.finanzen.net/rohstoffe/${symbol}</field>
|
||||
<field name="nohtml" eval="True"/>
|
||||
<field name="rgxdate">\nKurszeit (\d+\.\d+\.\d+) \d+:\d+:\d+.*\n</field>
|
||||
<field name="rgxdatefmt">%d.%m.%Y</field>
|
||||
<field name="rgxrate">\nKurs ([\d+\.]*\d+,\d+) USD\n</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>
|
||||
<field name="nohtml" eval="True"/>
|
||||
<field name="rgxdate">Data delayed at least 15 minutes, as of (.*) \d+:\d+ GMT\.</field>
|
||||
<field name="rgxdatefmt">%b %d %Y</field>
|
||||
<field name="rgxrate">Price\D+([\d,]*\d+\.\d+)</field>
|
||||
<field name="rgxdecimal">.</field>
|
||||
</record>
|
||||
<record model="investment.source" id="web_sbroker">
|
||||
<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 / Uhrzeit: (\d+\.\d+\.\d+) / \d+:\d+\s+\n</field>
|
||||
<field name="rgxdatefmt">%d.%m.%y</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>
|
||||
|
||||
</data>
|
||||
</tryton>
|
||||
|
|
|
@ -7,8 +7,9 @@ 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
|
||||
from datetime import time, date, datetime
|
||||
|
||||
|
||||
class AssetTestCase(ModuleTestCase):
|
||||
|
@ -61,9 +62,9 @@ class AssetTestCase(ModuleTestCase):
|
|||
'currency_digits': 4,
|
||||
'uom': product.default_uom.id,
|
||||
}])
|
||||
self.assertEqual(asset.rec_name, '%s [usd/%s]' % (
|
||||
self.assertEqual(asset.rec_name, '%s | - usd/%s | -' % (
|
||||
product.rec_name,
|
||||
asset.uom.rec_name,
|
||||
asset.uom.symbol,
|
||||
))
|
||||
self.assertEqual(asset.currency.rec_name, 'usd')
|
||||
self.assertEqual(asset.currency_digits, 4)
|
||||
|
@ -75,6 +76,8 @@ class AssetTestCase(ModuleTestCase):
|
|||
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',
|
||||
|
@ -83,6 +86,483 @@ class AssetTestCase(ModuleTestCase):
|
|||
asset = self.prep_asset_item(
|
||||
company=company,
|
||||
product = product)
|
||||
self.assertEqual(asset.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(['day1'], select_date = False)
|
||||
query = tab_percent.select(
|
||||
tab_percent.id,
|
||||
tab_percent.date,
|
||||
tab_percent.day1,
|
||||
where=(tab_percent.date==date(2022, 5, 16)) & \
|
||||
(tab_percent.id==asset1.id),
|
||||
)
|
||||
cursor.execute(*query)
|
||||
records = cursor.fetchall()
|
||||
|
||||
# there should be one record, three colums
|
||||
self.assertEqual(len(records), 1)
|
||||
self.assertEqual(len(records[0]), 3)
|
||||
self.assertEqual(records[0][0], asset1.id)
|
||||
self.assertEqual(records[0][1], date(2022, 5, 16))
|
||||
self.assertEqual(records[0][2].quantize(Decimal('0.01')), Decimal('6.12'))
|
||||
|
||||
@with_transaction()
|
||||
def test_asset_percentages_dateselect2(self):
|
||||
""" create asset, add rates, check selection of
|
||||
specific date - date-column
|
||||
"""
|
||||
pool = Pool()
|
||||
Asset = pool.get('investment.asset')
|
||||
Rate = pool.get('investment.rate')
|
||||
tab_rate = Rate.__table__()
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
company = self.prep_asset_company()
|
||||
product = self.prep_asset_product(
|
||||
name='Product 1',
|
||||
description='some asset')
|
||||
|
||||
asset1 = self.prep_asset_item(
|
||||
company=company,
|
||||
product = product)
|
||||
asset2 = self.prep_asset_item(
|
||||
company=company,
|
||||
product = product)
|
||||
|
||||
Asset.write(*[
|
||||
[asset1],
|
||||
{
|
||||
'rates': [('create', [{
|
||||
'date': date(2022, 5, 15),
|
||||
'rate': Decimal('2.45'),
|
||||
}, {
|
||||
'date': date(2022, 5, 16),
|
||||
'rate': Decimal('2.6'),
|
||||
}, {
|
||||
'date': date(2022, 5, 12),
|
||||
'rate': Decimal('2.0'),
|
||||
}, {
|
||||
'date': date(2022, 5, 3),
|
||||
'rate': Decimal('3.6'),
|
||||
}])],
|
||||
},
|
||||
[asset2],
|
||||
{
|
||||
'rates': [('create', [{
|
||||
'date': date(2022, 5, 17),
|
||||
'rate': Decimal('1.5'),
|
||||
}, {
|
||||
'date': date(2022, 5, 16),
|
||||
'rate': Decimal('2.0'),
|
||||
}, {
|
||||
'date': date(2022, 5, 15),
|
||||
'rate': Decimal('2.5'),
|
||||
}, {
|
||||
'date': date(2022, 5, 14),
|
||||
'rate': Decimal('3.0'),
|
||||
}, {
|
||||
'date': date(2022, 5, 13),
|
||||
'rate': Decimal('3.5'),
|
||||
}, {
|
||||
'date': date(2022, 5, 12),
|
||||
'rate': Decimal('4.0'),
|
||||
}, {
|
||||
'date': date(2022, 5, 11),
|
||||
'rate': Decimal('4.5'),
|
||||
}, ])],
|
||||
},
|
||||
])
|
||||
self.assertEqual(asset1.rec_name, 'Product 1 | 2.6000 usd/u | 05/16/2022')
|
||||
self.assertEqual(len(asset1.rates), 4)
|
||||
self.assertEqual(asset2.rec_name, 'Product 1 | 1.5000 usd/u | 05/17/2022')
|
||||
self.assertEqual(len(asset2.rates), 7)
|
||||
self.assertEqual(asset1.rates[0].date, date(2022, 5, 16))
|
||||
self.assertEqual(asset1.rates[1].date, date(2022, 5, 15))
|
||||
self.assertEqual(asset1.rates[2].date, date(2022, 5, 12))
|
||||
self.assertEqual(asset1.rates[3].date, date(2022, 5, 3))
|
||||
|
||||
# query date-column
|
||||
tab_percent = Asset.get_percentage_sql(['day1'], select_date = False)
|
||||
query = tab_rate.join(tab_percent,
|
||||
condition=(tab_percent.id_rate == tab_rate.id)
|
||||
).select(
|
||||
tab_percent.id,
|
||||
tab_percent.date,
|
||||
tab_percent.day1,
|
||||
where=tab_percent.id==asset1.id,
|
||||
order_by=[tab_percent.date.asc]
|
||||
)
|
||||
cursor.execute(*query)
|
||||
records = cursor.fetchall()
|
||||
|
||||
# there should be 4x records, three colums
|
||||
self.assertEqual(len(records), 4)
|
||||
self.assertEqual(len(records[0]), 3)
|
||||
self.assertEqual(records[0][0], asset1.id)
|
||||
self.assertEqual(records[0][1], date(2022, 5, 3))
|
||||
self.assertEqual(records[0][2], None)
|
||||
|
||||
self.assertEqual(records[1][0], asset1.id)
|
||||
self.assertEqual(records[1][1], date(2022, 5, 12))
|
||||
self.assertEqual(records[1][2], None)
|
||||
|
||||
self.assertEqual(records[2][0], asset1.id)
|
||||
self.assertEqual(records[2][1], date(2022, 5, 15))
|
||||
self.assertEqual(records[2][2].quantize(Decimal('0.01')), Decimal('22.5'))
|
||||
|
||||
self.assertEqual(records[3][0], asset1.id)
|
||||
self.assertEqual(records[3][1], date(2022, 5, 16))
|
||||
self.assertEqual(records[3][2].quantize(Decimal('0.01')), Decimal('6.12'))
|
||||
|
||||
@with_transaction()
|
||||
def test_asset_percentages_daterange(self):
|
||||
""" create asset, add rates, check selection of
|
||||
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):
|
||||
|
@ -105,18 +585,19 @@ class AssetTestCase(ModuleTestCase):
|
|||
'name': 'Source 1',
|
||||
}])
|
||||
|
||||
self.assertEqual(asset.updtsource, None)
|
||||
self.assertEqual(asset.updttime, None)
|
||||
self.assertEqual(len(asset.updtsources), 0)
|
||||
self.assertEqual(asset.updttime, time(14,0))
|
||||
|
||||
asset.updtsource = o_source
|
||||
asset.updtsources = [o_source]
|
||||
asset.updttime = time(10, 45)
|
||||
asset.save()
|
||||
self.assertEqual(asset.updtsource.rec_name, 'Source 1')
|
||||
self.assertEqual(len(asset.updtsources), 1)
|
||||
self.assertEqual(asset.updtsources[0].rec_name, 'Source 1')
|
||||
self.assertEqual(asset.updttime, time(10, 45))
|
||||
|
||||
asset.updtsource = None
|
||||
asset.on_change_updtsource()
|
||||
self.assertEqual(asset.updtsource, None)
|
||||
asset.updtsources = []
|
||||
asset.on_change_updtsources()
|
||||
self.assertEqual(len(asset.updtsources), 0)
|
||||
self.assertEqual(asset.updttime, None)
|
||||
|
||||
@with_transaction()
|
||||
|
@ -143,84 +624,81 @@ class AssetTestCase(ModuleTestCase):
|
|||
Asset.write(*[
|
||||
[asset],
|
||||
{
|
||||
'updtsource': o_source.id,
|
||||
'updtsources': [('add', [o_source.id])],
|
||||
'updttime': time(10, 45),
|
||||
}])
|
||||
self.assertEqual(asset.updtsource.rec_name, 'Source 1')
|
||||
self.assertEqual(asset.updttime, time(10, 45))
|
||||
self.assertEqual(len(asset.rates), 0)
|
||||
|
||||
with Transaction().set_context({
|
||||
'qdate': date(2022, 10, 15),
|
||||
'qtime': time(10, 30),
|
||||
'qdate': date(2022, 10, 14), # friday
|
||||
}):
|
||||
# no rates exists - wait for 10:45
|
||||
self.assertEqual(asset.updtneeded, True)
|
||||
# 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([('updtneeded', '=', True)]),
|
||||
Asset.search_count([('nextupdate', '<', datetime(2022, 10, 17, 10, 45))]),
|
||||
0)
|
||||
|
||||
with Transaction().set_context({
|
||||
'qdate': date(2022, 10, 15),
|
||||
'qtime': time(10, 46),
|
||||
}):
|
||||
# no rates exists - run at 10:46
|
||||
self.assertEqual(asset.updtneeded, True)
|
||||
self.assertEqual(
|
||||
Asset.search_count([('updtneeded', '=', True)]),
|
||||
Asset.search_count([('nextupdate', '>=', datetime(2022, 10, 17, 10, 45))]),
|
||||
1)
|
||||
|
||||
# add rate at yesterday
|
||||
# add rate at next monday
|
||||
Asset.write(*[
|
||||
[asset],
|
||||
{
|
||||
'rates': [('create', [{
|
||||
'date': date(2022, 10, 14),
|
||||
'date': date(2022, 10, 17), # monday
|
||||
'rate': Decimal('1.5'),
|
||||
}])],
|
||||
}])
|
||||
self.assertEqual(len(asset.rates), 1)
|
||||
|
||||
with Transaction().set_context({
|
||||
'qdate': date(2022, 10, 15),
|
||||
'qtime': time(10, 30),
|
||||
}):
|
||||
# 1x rate exists - run at 10:30
|
||||
self.assertEqual(asset.updtneeded, True)
|
||||
self.assertEqual(
|
||||
Asset.search_count([('updtneeded', '=', True)]),
|
||||
0)
|
||||
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))
|
||||
|
||||
with Transaction().set_context({
|
||||
'qdate': date(2022, 10, 15),
|
||||
'qtime': time(10, 46),
|
||||
}):
|
||||
# 1x rate exists yesterday - run at 10:46
|
||||
self.assertEqual(asset.updtneeded, True)
|
||||
self.assertEqual(
|
||||
Asset.search_count([('updtneeded', '=', True)]),
|
||||
1)
|
||||
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, 15),
|
||||
'date': date(2022, 10, 18),
|
||||
'rate': Decimal('1.5'),
|
||||
}])],
|
||||
}])
|
||||
self.assertEqual(len(asset.rates), 2)
|
||||
|
||||
with Transaction().set_context({
|
||||
'qdate': date(2022, 10, 15),
|
||||
'qtime': time(10, 47),
|
||||
}):
|
||||
# 1x rate exists today - run at 10:47
|
||||
self.assertEqual(asset.updtneeded, True)
|
||||
self.assertEqual(
|
||||
Asset.search_count([('updtneeded', '=', True)]),
|
||||
0)
|
||||
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):
|
||||
|
@ -280,6 +758,16 @@ class AssetTestCase(ModuleTestCase):
|
|||
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)
|
||||
|
@ -291,6 +779,36 @@ class AssetTestCase(ModuleTestCase):
|
|||
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
|
||||
|
|
|
@ -8,7 +8,7 @@ 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
|
||||
from datetime import time, date, datetime
|
||||
from unittest.mock import MagicMock
|
||||
from requests import Response
|
||||
import requests
|
||||
|
@ -64,28 +64,39 @@ class SourceTestCase(ModuleTestCase):
|
|||
Asset.write(*[
|
||||
[asset],
|
||||
{
|
||||
'updtsource': osource.id,
|
||||
'updtsources': [('add', [osource.id])],
|
||||
}])
|
||||
self.assertEqual(asset.wkn, '965515')
|
||||
self.assertEqual(asset.isin, 'XC0009655157')
|
||||
self.assertEqual(asset.secsymb, '1472977')
|
||||
self.assertEqual(asset.updtsource.rec_name, 'Source 1')
|
||||
self.assertEqual(len(asset.rates), 0)
|
||||
|
||||
# fake server-response
|
||||
resp1 = Response()
|
||||
resp1._content = """<html><body>Response from finance-server
|
||||
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)
|
||||
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'))
|
||||
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):
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
[tryton]
|
||||
version=6.0.0
|
||||
version=6.0.16
|
||||
depends:
|
||||
ir
|
||||
res
|
||||
company
|
||||
currency
|
||||
product
|
||||
extras_depends:
|
||||
diagram
|
||||
xml:
|
||||
icon.xml
|
||||
message.xml
|
||||
|
@ -15,5 +17,6 @@ xml:
|
|||
sources_def.xml
|
||||
update_wiz.xml
|
||||
rate.xml
|
||||
diagram.xml
|
||||
menu.xml
|
||||
cron.xml
|
||||
|
|
|
@ -24,8 +24,15 @@ class UpdateSoureWizard(Wizard):
|
|||
context = Transaction().context
|
||||
|
||||
assets = Asset.browse(context.get('active_ids', []))
|
||||
to_run_activities = []
|
||||
for asset in assets:
|
||||
OnlineSource.update_rate(asset)
|
||||
if OnlineSource.update_rate(asset):
|
||||
to_run_activities.append(asset)
|
||||
|
||||
if len(to_run_activities) > 0:
|
||||
Asset.after_update_actions(to_run_activities)
|
||||
|
||||
return 'end'
|
||||
|
||||
# UpdateSoureWizard
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
|
||||
diagram;6.0.7;6.0.999;mds
|
||||
|
|
|
@ -2,24 +2,64 @@
|
|||
<!-- 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. -->
|
||||
<form col="4">
|
||||
<form col="6">
|
||||
<label name="product" />
|
||||
<field name="product" />
|
||||
<field name="product" colspan="3"/>
|
||||
<label name="rate" />
|
||||
<field name="rate" symbol="currency"/>
|
||||
|
||||
<separator id="sepunits" colspan="4" string="Currency and Units"/>
|
||||
<label id="labdate" string=" "/>
|
||||
<label name="nextupdate" colspan="2"/>
|
||||
<field name="nextupdate"/>
|
||||
<label name="date" />
|
||||
<field name="date"/>
|
||||
|
||||
<separator id="sepperc" colspan="6" string="Gain and Loss"/>
|
||||
<label name="change_day1" />
|
||||
<group id="change_day1" col="2">
|
||||
<field name="change_day1" xexpand="0"/>
|
||||
<label name="change_day1" xexpand="1" xalign="0.0" string="%"/>
|
||||
</group>
|
||||
|
||||
|
||||
<label name="change_month1" />
|
||||
<group id="change_month1" col="2">
|
||||
<field name="change_month1" xexpand="0"/>
|
||||
<label name="change_month1" xexpand="1" xalign="0.0" string="%"/>
|
||||
</group>
|
||||
|
||||
<label name="change_month3" />
|
||||
<group id="change_month3" col="2">
|
||||
<field name="change_month3" xexpand="0"/>
|
||||
<label name="change_month3" xexpand="1" xalign="0.0" string="%"/>
|
||||
</group>
|
||||
<label name="change_month6" />
|
||||
<group id="change_month6" col="2">
|
||||
<field name="change_month6" xexpand="0"/>
|
||||
<label name="change_month6" xexpand="1" xalign="0.0" string="%"/>
|
||||
</group>
|
||||
|
||||
<label name="change_month12" />
|
||||
<group id="change_month12" col="2">
|
||||
<field name="change_month12" xexpand="0"/>
|
||||
<label name="change_month12" xexpand="1" xalign="0.0" string="%"/>
|
||||
</group>
|
||||
<newline/>
|
||||
|
||||
<separator id="sepunits" colspan="6" string="Currency and Units"/>
|
||||
<label name="currency" />
|
||||
<field name="currency" />
|
||||
<label name="currency_digits" />
|
||||
<field name="currency_digits" />
|
||||
<newline/>
|
||||
|
||||
<label name="uom" />
|
||||
<field name="uom" />
|
||||
<label name="product_uom" />
|
||||
<field name="product_uom" />
|
||||
<newline/>
|
||||
|
||||
<notebook>
|
||||
<notebook colspan="6">
|
||||
<page id="pgids" col="4" string="Identifiers">
|
||||
<label name="wkn" />
|
||||
<field name="wkn" />
|
||||
|
@ -33,10 +73,12 @@ full copyright notices and license terms. -->
|
|||
<field name="rates" mode="tree,form,graph"/>
|
||||
</page>
|
||||
<page id="pgupdate" col="4" string="Course Update">
|
||||
<label name="updtsource"/>
|
||||
<field name="updtsource"/>
|
||||
<label name="updttime"/>
|
||||
<field name="updttime"/>
|
||||
<label name="updtdays"/>
|
||||
<field name="updtdays"/>
|
||||
|
||||
<field name="updtsources" colspan="4"/>
|
||||
</page>
|
||||
</notebook>
|
||||
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
The COPYRIGHT file at the top level of this repository contains the
|
||||
full copyright notices and license terms. -->
|
||||
<tree>
|
||||
<field name="rec_name"/>
|
||||
<field name="isin"/>
|
||||
<field name="secsymb"/>
|
||||
<field name="wkn"/>
|
||||
<field name="name" expand="2"/>
|
||||
<field name="change_day1" />
|
||||
<field name="change_month1" />
|
||||
<field name="change_month3" />
|
||||
<field name="change_month6" />
|
||||
<field name="date"/>
|
||||
<field name="rate"/>
|
||||
<field name="currency"/>
|
||||
<field name="uom"/>
|
||||
<field name="symbol"/>
|
||||
<field name="isin"/>
|
||||
<field name="wkn" />
|
||||
</tree>
|
||||
|
|
14
view/graph_form.xml
Normal file
14
view/graph_form.xml
Normal 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>
|
Loading…
Reference in a new issue