Compare commits

..

51 commits

Author SHA1 Message Date
Frederik Jaeckel
064fec927e fix amounts on view-cashbooks to include non-asset-cashbooks 2024-01-20 18:15:31 +01:00
Frederik Jaeckel
12b5442f19 fix sorting/searcher of field 'yield_balance' 2024-01-03 21:38:33 +01:00
Frederik Jaeckel
6d9a5b080d add license 2023-12-31 11:23:22 +01:00
Frederik Jaeckel
728b2fa6c2 add sorting 2023-12-30 11:44:33 +01:00
Frederik Jaeckel
c38737076f add columns to cashbook tree/list-view 2023-12-30 11:18:49 +01:00
Frederik Jaeckel
4a23e1ae81 add update of cashbooks on update of asset-rates 2023-12-30 10:57:17 +01:00
Frederik Jaeckel
49d3a2fec5 cashbook: optimize for speed for checking rows 2023-12-29 23:09:00 +01:00
Frederik Jaeckel
ec97351db9 add worker-based precalculation of cashbook-values 2023-12-29 23:07:39 +01:00
Frederik Jaeckel
fb062243d6 remove caching 2023-12-23 10:44:55 +01:00
Frederik Jaeckel
82c390caae Tryton 7.0 2023-12-06 21:34:08 +01:00
Frederik Jaeckel
3d0ce89df8 columns optional 2023-12-06 20:04:52 +01:00
Frederik Jaeckel
8dafa0bce0 tests: formatting 2023-12-03 17:37:43 +01:00
Frederik Jaeckel
27c12d4ad1 formatting 2023-12-03 17:31:42 +01:00
Frederik Jaeckel
21b014183a Tryton 6.8: index, info, tests 2023-06-08 17:13:40 +02:00
Frederik Jaeckel
1f47dfe6b1 tests: add company to context 2023-06-08 16:00:36 +02:00
Frederik Jaeckel
1047345ae8 tests: formatting 2023-06-08 14:34:56 +02:00
Frederik Jaeckel
ad067475f8 formatting 2023-06-08 14:00:59 +02:00
Frederik Jaeckel
6c1fac8672 line: formatting, fix - on_change_with_quantity_digits() 2023-04-19 09:43:29 +02:00
Frederik Jaeckel
f7109b013f line: selection of digits for quantity 2023-04-17 12:01:51 +02:00
Frederik Jaeckel
e96aa6fad7 remove logging, add trytond.conf-settings 2023-03-05 10:26:39 +01:00
Frederik Jaeckel
da7fc9cb65 book: optimize query for speed 2023-03-04 21:21:54 +01:00
Frederik Jaeckel
e244e75d8a readme 2023-02-28 09:22:09 +01:00
Frederik Jaeckel
6bcb4efc2b fix: import + no symbol if None 2023-02-27 21:17:48 +01:00
Frederik Jaeckel
17a4499ac7 book:caching for line, settings: cache clear 2023-02-27 20:46:41 +01:00
Frederik Jaeckel
02bde17f6b book: add cache for asset-rates 2023-02-27 10:20:23 +01:00
Frederik Jaeckel
91b88a48e5 book: new field 'purchase_amount', optimize form
speedup: indexes, caching
2023-02-26 22:51:24 +01:00
Frederik Jaeckel
5e7552e589 line: fix exception if missing config; book: optimize form 2023-02-23 10:35:30 +01:00
Frederik Jaeckel
f95a9ed710 book: optimized field 'yield_balance' 2023-02-22 21:24:58 +01:00
Frederik Jaeckel
20f465ae3a book: added fields to book-form 2023-02-22 20:33:12 +01:00
Frederik Jaeckel
91de15e093 cashbook: yield-info prepared 2023-02-22 16:21:23 +01:00
Frederik Jaeckel
6177b5d691 line: add help, fix form 2023-02-21 22:36:20 +01:00
Frederik Jaeckel
0c60a8c313 line: tests... ok 2023-02-21 22:22:23 +01:00
Frederik Jaeckel
9f22341005 line: field 'trade_fee' ok + test 2023-02-21 12:57:10 +01:00
Frederik Jaeckel
fe07c4a17f line: fee tests ergänzt 2023-02-20 23:22:31 +01:00
Frederik Jaeckel
6b042bca3b line: field 'asset_gainloss' ok + test 2023-02-18 20:55:04 +01:00
Frederik Jaeckel
313892d2d5 line: gain/loss ok + test 2023-02-17 10:20:06 +01:00
Frederik Jaeckel
c82f37729b line: gain/loss field ... 2023-02-16 23:12:48 +01:00
Frederik Jaeckel
e0a2a0f9cb line: updated get_gainloss_data_sql + todos 2023-02-16 16:59:50 +01:00
Frederik Jaeckel
4e7d2c2ba9 add: field 'asset_gainloss' + searcher + test +todos 2023-02-15 22:34:52 +01:00
Frederik Jaeckel
a1ede23740 add: query for gain/loss 2023-02-14 23:03:12 +01:00
Frederik Jaeckel
3243ccc471 line: negate amount if splitline=spout - tests 2023-02-14 22:27:25 +01:00
Frederik Jaeckel
798da563ec field trade_fee + asset_dividend ok + searcher + test 2023-02-12 22:01:07 +01:00
Frederik Jaeckel
982f21f06e line: add fields 'Fee' + 'Dividend' - todos 2023-02-12 00:09:56 +01:00
Frederik Jaeckel
438035cca2 line: fix write() - get 'bookingtype' from line if not in values 2023-02-05 11:35:55 +01:00
Frederik Jaeckel
e38d02ff9d line/splitline: fixed selection of quantity-uom/digits 2023-02-01 14:29:48 +01:00
Frederik Jaeckel
e79c68e75b reconciliation: use 'quantity_digits' for digits of start/end-quantity 2023-01-30 09:25:44 +01:00
Frederik Jaeckel
ea7f114e94 line: add performance values to line-form 2023-01-29 23:16:22 +01:00
Frederik Jaeckel
9669a8d85b cashbook: view current_value/-ref on chasbooks with btype=None if
asset-cashbooks are below
2023-01-28 13:03:24 +01:00
Frederik Jaeckel
8156d521de book: asset-query ausgelagert 2023-01-24 21:25:29 +01:00
Frederik Jaeckel
8232f8042d line: fix transfer between cash-/asset-book with zero quantity 2023-01-23 22:29:41 +01:00
Frederik Jaeckel
ae8091bbdf line: fix rec_name 2023-01-22 10:28:09 +01:00
14 changed files with 363 additions and 238 deletions

View file

@ -9,7 +9,7 @@ pip install mds-cashbook-investment
Requires
========
- Tryton 6.0
- Tryton 7.0
How to
======
@ -23,59 +23,6 @@ You can monitor trading fees, dividends and sales profits.
Changes
=======
*6.0.12 - 31.12.2023*
*7.0.0 - 06.12.2023*
- remove caching
- add worker-based precalculation of cashbook-values
*6.0.11 - 06.12.2023*
- code optimized
*6.0.10 - 08.06.2023*
- code optimized
*6.0.9 - 19.04.2023*
- fix: line - exception if add split-lines
*6.0.8 - 17.04.2023*
- fix: line - selection of digits for quantity
*6.0.7 - 05.03.2023*
- updt: optimize queries for speed, optimize caching
- add: trytond.conf-settings
*6.0.6 - 27.02.2023*
- add: caching
- add: performance and yield info
*6.0.5 - 05.02.2023*
- fix: line rewrite values
*6.0.4 - 01.02.2023*
- fix: selection of quantity-uom/digits on line/spitline
*6.0.3 - 30.01.2023*
- add: performance-tab to line-form
- fix: digits of start/end-quantity in reconciliation
*6.0.2 - 28.01.2023*
- fix: transfer between cash-/asset-book with zero quantity
- add: view value/percent on chasbooks with btype=None
*6.0.1 - 21.01.2023*
- works
*6.0.0 - 20.12.2022*
- init
- compatibility to Tryton 7.0

368
book.py
View file

@ -3,7 +3,7 @@
# The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms.
from trytond.model import fields, SymbolMixin
from trytond.model import fields, SymbolMixin, Index
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, Or, Bool, If
from trytond.modules.cashbook.book import STATES2, DEPENDS2
@ -25,7 +25,7 @@ class Book(SymbolMixin, metaclass=PoolMeta):
__name__ = 'cashbook.book'
asset = fields.Many2One(
string='Asset', select=True,
string='Asset',
model_name='investment.asset', ondelete='RESTRICT',
states={
'required': Eval('feature', '') == 'asset',
@ -51,7 +51,7 @@ class Book(SymbolMixin, metaclass=PoolMeta):
model_name='product.uom.category',
states={'invisible': True}), 'on_change_with_asset_uomcat')
quantity_uom = fields.Many2One(
string='UOM', select=True,
string='UOM',
model_name='product.uom', ondelete='RESTRICT',
domain=[('category.id', '=', Eval('asset_uomcat', -1))],
states={
@ -127,7 +127,9 @@ class Book(SymbolMixin, metaclass=PoolMeta):
string='Purchase Amount',
help='Total purchase amount, from shares and fees.',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={'invisible': Eval('feature', '') != 'asset'},
states={'invisible': ~Or(
Eval('feature', '') == 'asset',
~Bool(Eval('feature')))},
depends=['currency_digits', 'feature']),
'get_asset_quantity', searcher='search_asset_quantity')
@ -177,7 +179,20 @@ class Book(SymbolMixin, metaclass=PoolMeta):
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={'invisible': Eval('feature', '') != 'asset'},
depends=['currency_digits', 'feature']),
'on_change_with_yield_balance', searcher='search_asset_quantity')
'get_yield_balance_data', searcher='search_asset_quantity')
@classmethod
def __setup__(cls):
super(Book, cls).__setup__()
t = cls.__table__()
cls._sql_indexes.update({
Index(
t,
(t.asset, Index.Equality())),
Index(
t,
(t.quantity_uom, Index.Equality())),
})
@classmethod
def view_attributes(cls):
@ -318,15 +333,39 @@ class Book(SymbolMixin, metaclass=PoolMeta):
"""
return 4
@fields.depends('yield_sales', 'yield_dividend_total', 'diff_amount')
def on_change_with_yield_balance(self, name=None):
@classmethod
def get_yield_balance_data(cls, cashbooks, names):
""" calculate yield total
fee is already contained in 'diff_amount'
"""
sum_lst = [
self.diff_amount, self.yield_dividend_total, self.yield_sales]
sum2 = sum([x for x in sum_lst if x is not None])
return sum2
context = Transaction().context
result = {x: {y.id: Decimal('0.0') for y in cashbooks} for x in names}
query_date = context.get('date', None)
if context.get(
'compute_yield_balance',
False) or query_date is not None:
amounts = {}
amounts.update(cls.get_asset_quantity(cashbooks, ['diff_amount']))
amounts.update(cls.get_yield_data(
cashbooks,
['yield_dividend_total', 'yield_sales']))
for cashbook in cashbooks:
sum_lst = [
amounts[x][cashbook.id]
for x in [
'diff_amount', 'yield_dividend_total',
'yield_sales']]
sum2 = sum([x for x in sum_lst if x is not None])
result['yield_balance'][cashbook.id] = sum2
else:
for cashbook in cashbooks:
for value in cashbook.value_store:
if value.field_name in names:
result[value.field_name][cashbook.id] = value.numvalue
return result
@classmethod
def get_yield_data_sql(cls, date_from=None, date_to=None):
@ -518,19 +557,83 @@ class Book(SymbolMixin, metaclass=PoolMeta):
where=(tab_type.feature == 'asset'))
return (query, tab_book)
@classmethod
def get_asset_amounts_sub_sql(cls):
""" get table of asset and its values for
subordered cashbooks
"""
(tab_quantity, tab_book) = cls.get_asset_quantity_sql()
tab_subids = sub_ids_hierarchical('cashbook.book')
query = tab_book.join(
tab_subids,
condition=tab_book.id == tab_subids.parent,
).join(
tab_quantity,
condition=tab_quantity.id == AnyInArray(tab_subids.subids),
).select(
tab_book.id,
tab_quantity.id.as_('id_subbook'),
tab_quantity.quantity,
tab_quantity.quantity_all,
tab_quantity.rate,
tab_quantity.currency,
tab_quantity.currency_digits,
tab_quantity.asset_currency,
tab_quantity.purchase_amount,
tab_quantity.quantity_uom.as_('book_uom'),
tab_quantity.uom.as_('asset_uom'))
return (query, tab_book)
@classmethod
def get_generic_amounts_sub_sql(cls):
""" query to get amounts of current and subordered
non-asset cashbooks grouped by currency
"""
pool = Pool()
BType = pool.get('cashbook.type')
tab_btype = BType.__table__()
tab_book1 = cls.__table__()
tab_book2 = cls.__table__()
subids_book = sub_ids_hierarchical('cashbook.book')
(query_amounts, tab_line) = cls.get_balance_of_cashbook_sql()
query = tab_book1.join(
subids_book,
condition=subids_book.parent == tab_book1.id,
).join(
tab_book2,
condition=tab_book2.id == AnyInArray(subids_book.subids),
).join(
tab_btype,
condition=(
tab_btype.id == tab_book2.btype) & (
tab_btype.feature != 'asset'),
).join(
query_amounts,
condition=query_amounts.cashbook == tab_book2.id,
).select(
tab_book1.id,
Sum(query_amounts.balance).as_('balance'),
Sum(query_amounts.balance_all).as_('balance_all'),
query_amounts.currency,
group_by=[tab_book1.id, query_amounts.currency])
return (query, tab_book1)
@classmethod
def get_asset_quantity_values(cls, cashbooks, names):
""" get quantities
field: quantity, quantity_all, current_value,
current_value_ref, diff_amount, diff_percent,
current_rate, purchase_amount
current_rate, purchase_amount,
include subordered cashbooks for cashbooks w/o btype
"""
pool = Pool()
CBook = pool.get('cashbook.book')
Uom = pool.get('product.uom')
Currency = pool.get('currency.currency')
cursor = Transaction().connection.cursor()
(query, tab_book) = cls.get_asset_quantity_sql()
company_currency = CBook.default_currency()
result = {
@ -538,157 +641,187 @@ class Book(SymbolMixin, metaclass=PoolMeta):
for x in [
'quantity', 'quantity_all', 'current_value',
'current_value_ref', 'diff_amount', 'diff_percent',
'current_rate', 'purchase_amount', 'purchase_amount_ref',
'digits']
'current_rate', 'purchase_amount', 'digits']
}
def values_from_record(rdata):
""" compute values for record
"""
# uom-factor
if rdata[6] == rdata[7]:
if rdata['asset_uom'] == rdata['book_uom']:
uom_factor = Decimal('1.0')
else:
uom_factor = Decimal(
Uom.compute_qty(
Uom(rdata[6]), 1.0,
Uom(rdata[7]), round=False))
Uom(rdata['asset_uom']), 1.0,
Uom(rdata['book_uom']), round=False))
current_value = Currency.compute(
rdata[8],
rdata[3] * rdata[1] / uom_factor,
rdata[4])
return (record[0], {
'quantity': rdata[1],
'quantity_all': rdata[2],
rdata['asset_currency'],
rdata['rate'] * rdata['quantity'] / uom_factor,
rdata['book_currency'])
return (rdata['id'], {
'quantity': rdata['quantity'],
'quantity_all': rdata['quantity_all'],
'current_value': current_value,
'current_value_ref': Currency.compute(
rdata[8],
rdata[3] * rdata[1] / uom_factor,
rdata['asset_currency'],
rdata['rate'] * rdata['quantity'] / uom_factor,
company_currency
if company_currency is not None else rdata[8]),
'diff_amount': current_value - rdata[9],
if company_currency is not None
else rdata['asset_currency']),
'diff_amount': current_value - rdata['purchase_amount'],
'diff_percent': (
Decimal('100.0') * current_value /
rdata[9] - Decimal('100.0')
).quantize(Decimal(str(1/10**rdata[5])))
if rdata[9] != Decimal('0.0') else None,
rdata['purchase_amount'] - Decimal('100.0')
).quantize(Decimal(str(1/10**rdata['digits'])))
if rdata['purchase_amount'] != Decimal('0.0') else None,
'current_rate': (
current_value / rdata[1]
).quantize(Decimal(str(1/10**rdata[5])))
if rdata[1] != Decimal('0.0') else None,
'purchase_amount': record[9].quantize(
Decimal(str(1/10**rdata[5]))),
'purchase_amount_ref': Currency.compute(
rdata[4],
record[9],
company_currency
if company_currency is not None else rdata[4]),
current_value / rdata['quantity']
).quantize(Decimal(str(1/10**rdata['digits'])))
if rdata['quantity'] != Decimal('0.0') else None,
'purchase_amount': rdata['purchase_amount'].quantize(
Decimal(str(1/10**rdata['digits']))),
})
ids_assetbooks = []
ids_nonebtypes = []
for x in cashbooks:
if x.btype is None:
if x.id not in ids_nonebtypes:
ids_nonebtypes.append(x.id)
else:
if x.id not in ids_assetbooks:
ids_assetbooks.append(x.id)
view_cashbook_ids = list({
x.id for x in cashbooks if x.feature is None})
asset_cashbook_ids = list({
x.id for x in cashbooks if x.feature == 'asset'})
generic_cashbooks = list({
x for x in cashbooks if x.feature == 'gen'})
# get values of asset-cashbooks in 'cashbooks' of type=asset
if len(ids_assetbooks) > 0:
query.where &= tab_book.id.in_(ids_assetbooks)
# check skipped cashbooks
assert list({
x.id
for x in cashbooks
if x.feature not in ['asset', 'gen', None]
}) == [], 'unknown feature of cashbook'
# get values of asset-cashbooks in 'cashbooks' of type=asset,
# values of current cashbook
if asset_cashbook_ids:
(query, tab_book) = cls.get_asset_quantity_sql()
query.where &= tab_book.id.in_(asset_cashbook_ids)
cursor.execute(*query)
records = cursor.fetchall()
for record in records:
(book_id, values) = values_from_record(record)
(book_id, values) = values_from_record({
'id': record[0],
'quantity': record[1],
'quantity_all': record[2],
'rate': record[3],
'book_currency': record[4],
'digits': record[5],
'asset_uom': record[6],
'book_uom': record[7],
'asset_currency': record[8],
'purchase_amount': record[9]})
for name in values.keys():
result[name][book_id] = values[name]
# add aggregated values of cashbooks without type
aggr_names = [
enable_byfields = set({
'current_value', 'current_value_ref',
'diff_amount', 'diff_percent']
queried_names = list(set(aggr_names).intersection(set(names)))
'purchase_amount'}).intersection(set(names))
if (len(queried_names) > 0) and (len(ids_nonebtypes) > 0):
# query all subordered asset-cashbooks to get values for
# cashbooks without type
# add values of current generic-cashbooks
if generic_cashbooks and enable_byfields:
fnames = [
('current_value', 'balance'),
('current_value_ref', 'balance_ref'),
('purchase_amount', 'balance')]
for generic_cashbook in generic_cashbooks:
for fname in fnames:
(fn_to, fn_from) = fname
if fn_to in names:
result[fn_to][generic_cashbook.id] = getattr(
generic_cashbook, fn_from)
(tab_quantity, tab_book) = cls.get_asset_quantity_sql()
tab_subids = sub_ids_hierarchical('cashbook.book')
query = tab_book.join(
tab_subids,
condition=tab_book.id == tab_subids.parent,
).join(
tab_quantity,
condition=tab_quantity.id == AnyInArray(tab_subids.subids),
).select(
tab_book.id,
Sum(tab_quantity.quantity), # 1
Sum(tab_quantity.quantity_all), # 2
tab_quantity.rate, # 3
tab_quantity.currency, # 4
tab_quantity.currency_digits, # 5
tab_quantity.uom, # 6
tab_quantity.quantity_uom, # 7
tab_quantity.asset_currency, # 8
Sum(tab_quantity.purchase_amount), # 9
tab_book.currency.as_('currency_book'), # 10
where=tab_book.id.in_(ids_nonebtypes),
group_by=[
tab_book.id, tab_quantity.rate,
tab_quantity.currency, tab_quantity.currency_digits,
tab_quantity.uom, tab_quantity.quantity_uom,
tab_quantity.asset_currency, tab_book.currency])
cursor.execute(*query)
# add amounts of non-asset cashbooks,
if view_cashbook_ids and enable_byfields:
# sum amounts of subordered generic-cashbooks
(query_nonasset, tab_book) = cls.get_generic_amounts_sub_sql()
query_nonasset.where = tab_book.id.in_(view_cashbook_ids)
cursor.execute(*query_nonasset)
records = cursor.fetchall()
for record in records:
(book_id, values) = values_from_record(record)
cbook = CBook(record[0])
rdata = {
'id': record[0],
'quantity': Decimal('1.0'),
'quantity_all': None,
'rate': record[1], # balance
'book_currency': cbook.currency.id,
'digits': cbook.currency.digits,
'asset_uom': 0,
'book_uom': 0,
'asset_currency': record[3],
'purchase_amount': record[1]}
(book_id, values) = values_from_record(rdata)
for name in [
'current_value', 'diff_amount',
'current_value_ref', 'purchase_amount_ref']:
('current_value', 'current_value'),
('current_value_ref', 'current_value_ref'),
('purchase_amount', 'current_value')]:
(fn_to, fn_from) = name
if fn_to in names:
if result[fn_to][book_id] is None:
result[fn_to][book_id] = Decimal('0.0')
result[fn_to][book_id] += values[fn_from]
# sum amounts of subordered asset-cashbooks
(query_subbooks, tab_book) = cls.get_asset_amounts_sub_sql()
query_subbooks.where = tab_book.id.in_(view_cashbook_ids)
cursor.execute(*query_subbooks)
records = cursor.fetchall()
for record in records:
cbook = CBook(record[0])
(book_id, values) = values_from_record({
'id': record[0],
'quantity': record[2],
'quantity_all': record[3],
'rate': record[4],
'book_currency': record[5],
'digits': record[6],
'asset_uom': record[10],
'book_uom': record[9],
'asset_currency': record[7],
'purchase_amount': record[8]})
for x in ['current_value', 'purchase_amount']:
values[x] = Currency.compute(
record[5], values[x], cbook.currency.id)
for name in [
'current_value', 'current_value_ref',
'purchase_amount']:
if result[name][book_id] is None:
result[name][book_id] = Decimal('0.0')
value = Decimal('0.0')
if name == 'current_value':
value = Currency.compute(
company_currency
if company_currency is not None else record[4],
values['current_value_ref'],
record[10])
elif name == 'diff_amount':
value = Currency.compute(
company_currency
if company_currency is not None else record[4],
values['current_value_ref'] -
values['purchase_amount_ref'],
record[10])
elif name in ['current_value_ref', 'purchase_amount_ref']:
value = values[name]
result['digits'][book_id] = record[5]
result[name][book_id] += value
result[name][book_id] += values[name]
# diff_percent
for id_book in ids_nonebtypes:
c_val = result['current_value_ref'][id_book]
p_amount = result['purchase_amount_ref'][id_book]
digits = result['digits'][id_book]
for id_book in view_cashbook_ids:
c_val = result['current_value'][id_book]
p_amount = result['purchase_amount'][id_book]
digits = result['digits'][id_book] or 2
if (p_amount == Decimal('0.0')) or \
(p_amount is None) or (c_val is None):
continue
result['diff_amount'][id_book] = (
c_val - p_amount).quantize(Decimal(str(1/10 ** digits)))
result['diff_percent'][id_book] = (
Decimal('100.0') * c_val / p_amount - Decimal('100.0')
).quantize(Decimal(str(1/10 ** digits)))
result['digits'][id_book] = None
return {x: result[x] for x in names}
@classmethod
@ -741,7 +874,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
'quantity', 'quantity_all', 'current_value', 'current_value_ref',
'diff_amount', 'diff_percent', 'current_rate', 'purchase_amount',
'yield_fee_total', 'yield_dividend_total', 'yield_sales',
'yield_fee_12m', 'yield_dividend_12m', 'yield_sales_12m'])
'yield_fee_12m', 'yield_dividend_12m', 'yield_sales_12m',
'yield_balance'])
return result
@classmethod
@ -766,6 +900,10 @@ class Book(SymbolMixin, metaclass=PoolMeta):
'yield_fee_total', 'yield_dividend_total',
'yield_sales', 'yield_fee_12m', 'yield_dividend_12m',
'yield_sales_12m']))
with Transaction().set_context({
'compute_yield_balance': True}):
ValStore.update_values(
cls.get_yield_balance_data(records, ['yield_balance']))
@fields.depends('id')
def on_change_with_show_performance(self, name=None):

View file

@ -14,10 +14,9 @@ from trytond.i18n import gettext
from trytond.report import Report
from trytond.transaction import Transaction
from trytond.modules.cashbook.line import STATES, DEPENDS
from trytond.modules.cashbook.const import DEF_NONE
from .mixin import SecondUomMixin
DEF_NONE = None
STATESQ1 = {
'invisible': And(
Eval('feature', '') != 'asset',

View file

@ -39,7 +39,7 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f:
modversion[l2[0]] = {'min': l2[1], 'max': l2[2], 'prefix': l2[3]}
# tryton-version
major_version = 6
major_version = 7
minor_version = 0
requires = []
@ -92,6 +92,7 @@ setup(
'License :: OSI Approved :: GNU General Public License (GPL)',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
],
keywords='tryton cashbook investment',

View file

@ -1,17 +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.
import trytond.tests.test_tryton
import unittest
from .test_module import CashbookInvestmentTestCase
__all__ = ['suite']
def suite():
suite = trytond.tests.test_tryton.suite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
CashbookInvestmentTestCase))
return suite

View file

@ -49,7 +49,7 @@ class CbInvTestCase(object):
self.assertEqual(book.show_performance, True)
# run sorter
books = Book.search(
Book.search(
[],
order=[
('current_value', 'ASC'),
@ -103,6 +103,12 @@ class CbInvTestCase(object):
asset.rec_name, 'Product 1 | 12.5000 usd/u | 05/02/2022')
(usd, euro) = self.prep_2nd_currency(company)
self.assertEqual(len(usd.rates), 1)
self.assertEqual(usd.rates[0].rate, Decimal('1.05'))
self.assertEqual(usd.rates[0].date, date(2022, 5, 2))
self.assertEqual(euro.rates[0].rate, Decimal('1.0'))
self.assertEqual(euro.rates[0].date, date(2022, 5, 2))
self.assertEqual(company.currency.rec_name, 'Euro')
BType.write(*[
@ -182,26 +188,35 @@ class CbInvTestCase(object):
}])],
}])],
}])
self.prep_valstore_run_worker()
self.assertEqual(len(books), 1)
self.assertEqual(books[0].rec_name, 'L0-Euro-None')
self.assertEqual(books[0].balance, Decimal('58.57'))
self.assertEqual(books[0].balance_ref, Decimal('58.57'))
# balance of asset-books: 29,286 €
# value of asset-books: 11.9€ + 12.5USD/1.05 = 23.8€
self.assertEqual(books[0].current_value, Decimal('23.8'))
self.assertEqual(books[0].current_value_ref, Decimal('23.8'))
# current_value:
# +15€ (15.00€ - L1-Euro-Cash)
# +15$ / 1.05 (14.29€ - L1-USD-Cash)
# +12.5$/1.05 (11.90€ - L1-Euro-Depot)
# +12.5$/1.05 (11.90€ - L1-USD-Depot)
# = 53.09€
self.assertEqual(books[0].current_value, Decimal('53.09'))
self.assertEqual(books[0].current_value_ref, Decimal('53.09'))
self.assertEqual(books[0].purchase_amount, Decimal('58.58'))
self.assertEqual(books[0].diff_amount, Decimal('-5.49'))
self.assertEqual(books[0].diff_percent, Decimal('-18.74'))
self.assertEqual(books[0].diff_percent, Decimal('-9.37'))
# searcher
self.assertEqual(Book.search_count([
('current_value', '=', Decimal('23.8'))]), 1)
('current_value', '=', Decimal('53.09'))]), 1)
self.assertEqual(Book.search_count([
('current_value_ref', '=', Decimal('23.8'))]), 1)
('current_value_ref', '=', Decimal('53.09'))]), 1)
self.assertEqual(Book.search_count([
('diff_amount', '=', Decimal('-5.49'))]), 1)
self.assertEqual(Book.search_count([
('diff_percent', '=', Decimal('-18.74'))]), 1)
('diff_percent', '=', Decimal('-9.37'))]), 1)
self.assertEqual(Book.search_count([
('quantity', '=', Decimal('1.0'))]), 2)
self.assertEqual(Book.search_count([
@ -209,15 +224,17 @@ class CbInvTestCase(object):
self.assertEqual(Book.search_count([
('current_rate', '=', Decimal('11.9'))]), 1)
self.assertEqual(Book.search_count([
('purchase_amount', '=', Decimal('15.0'))]), 2)
('purchase_amount', '=', Decimal('15.0'))]), 4)
self.assertEqual(len(books[0].childs), 4)
self.assertEqual(
books[0].childs[0].rec_name,
'L0-Euro-None/L1-Euro-Cash | 15.00 € | Open')
self.assertEqual(books[0].childs[0].current_value, None)
self.assertEqual(books[0].childs[0].current_value_ref, None)
self.assertEqual(
books[0].childs[0].current_value, Decimal('15.0'))
self.assertEqual(
books[0].childs[0].current_value_ref, Decimal('15.0'))
self.assertEqual(books[0].childs[0].diff_amount, None)
self.assertEqual(books[0].childs[0].diff_percent, None)
@ -227,6 +244,7 @@ class CbInvTestCase(object):
self.assertEqual(
books[0].childs[1].asset.rec_name,
'Product 1 | 12.5000 usd/u | 05/02/2022')
# asset: usd, rate 12.50 usd/u @ 2022-05-02
self.assertEqual(
books[0].childs[1].current_value, Decimal('11.9'))
self.assertEqual(
@ -239,8 +257,10 @@ class CbInvTestCase(object):
self.assertEqual(
books[0].childs[2].rec_name,
'L0-Euro-None/L1-USD-Cash | 15.00 usd | Open')
self.assertEqual(books[0].childs[2].current_value, None)
self.assertEqual(books[0].childs[2].current_value_ref, None)
self.assertEqual(
books[0].childs[2].current_value, Decimal('15.0'))
self.assertEqual(
books[0].childs[2].current_value_ref, Decimal('14.29'))
self.assertEqual(books[0].childs[2].diff_amount, None)
self.assertEqual(books[0].childs[2].diff_percent, None)
@ -808,7 +828,8 @@ class CbInvTestCase(object):
self.assertRaisesRegex(
UserError,
'A value is required for field "Asset" in "Cashbook".',
'A value is required for field "Asset" in ' +
'"Book 1 | 0.00 usd | Open | 0.0000 u" of "Cashbook".',
Book.write,
*[
[book],

View file

@ -132,21 +132,24 @@ class ValueStoreTestCase(object):
'[Book 1 | 2.50 € | Open | 1.500 u]|quantity_all|1.500|3')
self.assertEqual(
values[11].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_12m|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_balance|4.00|2')
self.assertEqual(
values[12].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_total|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_12m|0.00|2')
self.assertEqual(
values[13].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_12m|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_total|0.00|2')
self.assertEqual(
values[14].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_total|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_12m|0.00|2')
self.assertEqual(
values[15].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales|2.50|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_total|0.00|2')
self.assertEqual(
values[16].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales|2.50|2')
self.assertEqual(
values[17].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales_12m|0.00|2')
# add rate
@ -203,21 +206,24 @@ class ValueStoreTestCase(object):
'[Book 1 | 2.50 € | Open | 1.500 u]|quantity_all|1.500|3')
self.assertEqual(
values[11].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_12m|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_balance|4.29|2')
self.assertEqual(
values[12].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_total|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_12m|0.00|2')
self.assertEqual(
values[13].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_12m|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_total|0.00|2')
self.assertEqual(
values[14].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_total|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_12m|0.00|2')
self.assertEqual(
values[15].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales|2.50|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_total|0.00|2')
self.assertEqual(
values[16].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales|2.50|2')
self.assertEqual(
values[17].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales_12m|0.00|2')
# update rate
@ -275,21 +281,24 @@ class ValueStoreTestCase(object):
'[Book 1 | 2.50 € | Open | 1.500 u]|quantity_all|1.500|3')
self.assertEqual(
values[11].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_12m|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_balance|5.00|2')
self.assertEqual(
values[12].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_total|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_12m|0.00|2')
self.assertEqual(
values[13].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_12m|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_total|0.00|2')
self.assertEqual(
values[14].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_total|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_12m|0.00|2')
self.assertEqual(
values[15].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales|2.50|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_total|0.00|2')
self.assertEqual(
values[16].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales|2.50|2')
self.assertEqual(
values[17].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales_12m|0.00|2')
# delete rate
@ -345,21 +354,24 @@ class ValueStoreTestCase(object):
'[Book 1 | 2.50 € | Open | 1.500 u]|quantity_all|1.500|3')
self.assertEqual(
values[11].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_12m|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_balance|4.00|2')
self.assertEqual(
values[12].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_total|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_12m|0.00|2')
self.assertEqual(
values[13].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_12m|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_dividend_total|0.00|2')
self.assertEqual(
values[14].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_total|0.00|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_12m|0.00|2')
self.assertEqual(
values[15].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales|2.50|2')
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_fee_total|0.00|2')
self.assertEqual(
values[16].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales|2.50|2')
self.assertEqual(
values[17].rec_name,
'[Book 1 | 2.50 € | Open | 1.500 u]|yield_sales_12m|0.00|2')
# end ValueStoreTestCase

View file

@ -1,5 +1,5 @@
[tryton]
version=6.0.12
version=7.0.0
depends:
cashbook
investment

View file

@ -1,2 +1,2 @@
cashbook;6.0.31;6.0.999;mds
investment;6.0.25;6.0.999;mds
cashbook;7.0.32;7.0.999;mds
investment;7.0.26;7.0.999;mds

View file

@ -5,10 +5,20 @@ full copyright notices and license terms. -->
<data>
<xpath expr="/tree/field[@name='balance']" position="after">
<field name="current_value" symbol="currency"/>
<field name="diff_amount" symbol="currency"/>
<field name="diff_percent"/>
<field name="quantity" symbol="quantity_uom"/>
<field name="current_value" symbol="currency" optional="0"/>
<field name="current_value_ref" symbol="company_currency" optional="1"/>
<field name="purchase_amount" symbol="currency" optional="1"/>
<field name="diff_amount" symbol="currency" optional="0"/>
<field name="yield_balance" symbol="currency" optional="1"/>
<field name="diff_percent" optional="0"/>
<field name="quantity" symbol="quantity_uom" optional="0"/>
<field name="quantity_all" symbol="quantity_uom" optional="1"/>
<field name="yield_sales" symbol="currency" optional="1"/>
<field name="yield_sales_12m" symbol="currency" optional="1"/>
<field name="yield_dividend_total" symbol="currency" optional="1"/>
<field name="yield_dividend_12m" symbol="currency" optional="1"/>
<field name="yield_fee_total" symbol="currency" optional="1"/>
<field name="yield_fee_12m" symbol="currency" optional="1"/>
</xpath>
</data>

View file

@ -5,8 +5,20 @@ full copyright notices and license terms. -->
<data>
<xpath expr="/tree/field[@name='balance']" position="after">
<field name="diff_amount" symbol="currency"/>
<field name="diff_percent"/>
<field name="current_value" symbol="currency" optional="1"/>
<field name="current_value_ref" symbol="company_currency" optional="1"/>
<field name="purchase_amount" symbol="currency" optional="1"/>
<field name="diff_amount" symbol="currency" optional="0"/>
<field name="yield_balance" symbol="currency" optional="1"/>
<field name="diff_percent" optional="0"/>
<field name="quantity" symbol="quantity_uom" optional="1"/>
<field name="quantity_all" symbol="quantity_uom" optional="1"/>
<field name="yield_sales" symbol="currency" optional="1"/>
<field name="yield_sales_12m" symbol="currency" optional="1"/>
<field name="yield_dividend_total" symbol="currency" optional="1"/>
<field name="yield_dividend_12m" symbol="currency" optional="1"/>
<field name="yield_fee_total" symbol="currency" optional="1"/>
<field name="yield_fee_12m" symbol="currency" optional="1"/>
</xpath>
</data>

View file

@ -5,9 +5,9 @@ full copyright notices and license terms. -->
<data>
<xpath expr="/tree/field[@name='balance']" position="after">
<field name="quantity_credit"/>
<field name="quantity_debit"/>
<field name="quantity_balance"/>
<field name="quantity_credit" optional="0"/>
<field name="quantity_debit" optional="0"/>
<field name="quantity_balance" optional="0"/>
</xpath>
</data>

View file

@ -5,7 +5,7 @@ full copyright notices and license terms. -->
<data>
<xpath expr="/tree/field[@name='end_amount']" position="after">
<field name="start_quantity"/>
<field name="end_quantity"/>
<field name="start_quantity" optional="0"/>
<field name="end_quantity" optional="0"/>
</xpath>
</data>

View file

@ -5,8 +5,8 @@ full copyright notices and license terms. -->
<data>
<xpath expr="/tree/field[@name='amount_2nd_currency']" position="after">
<field name="quantity" symbol="quantity_uom"/>
<field name="quantity_2nd_uom" symbol="quantity2nd"/>
<field name="quantity" symbol="quantity_uom" optional="0"/>
<field name="quantity_2nd_uom" symbol="quantity2nd" optional="0"/>
</xpath>
</data>