diff --git a/book.py b/book.py
index 76d3464..c88563a 100644
--- a/book.py
+++ b/book.py
@@ -15,12 +15,13 @@ from sql import Literal
from sql.functions import CurrentDate
from sql.aggregate import Sum
from sql.conditionals import Case, Coalesce
+from trytond.modules.cashbook.model import CACHEKEY_CURRENCY
class Book(SymbolMixin, metaclass=PoolMeta):
__name__ = 'cashbook.book'
- asset = fields.Many2One(string='Asset',
+ asset = fields.Many2One(string='Asset', select=True,
model_name='investment.asset', ondelete='RESTRICT',
states={
'required': Eval('feature', '') == 'asset',
@@ -47,7 +48,7 @@ class Book(SymbolMixin, metaclass=PoolMeta):
asset_uomcat = fields.Function(fields.Many2One(string='UOM Category',
readonly=True, model_name='product.uom.category',
states={'invisible': True}), 'on_change_with_asset_uomcat')
- quantity_uom = fields.Many2One(string='UOM',
+ quantity_uom = fields.Many2One(string='UOM', select=True,
model_name='product.uom', ondelete='RESTRICT',
domain=[
('category.id', '=', Eval('asset_uomcat', -1)),
@@ -118,6 +119,13 @@ class Book(SymbolMixin, metaclass=PoolMeta):
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), 'get_asset_quantity')
+ purchase_amount = fields.Function(fields.Numeric(string='Purchase Amount',
+ help='Total purchase amount, from shares and fees.',
+ readonly=True, digits=(16, Eval('currency_digits', 2)),
+ states={
+ 'invisible': Eval('feature', '') != 'asset',
+ }, depends=['currency_digits', 'feature']),
+ 'get_asset_quantity')
# yield
yield_dividend_total = fields.Function(fields.Numeric(string='Dividend',
@@ -200,14 +208,13 @@ class Book(SymbolMixin, metaclass=PoolMeta):
"""
return 4
- @fields.depends('yield_sales', 'yield_fee_total', 'yield_dividend_total', 'diff_amount')
+ @fields.depends('yield_sales', 'yield_dividend_total', 'diff_amount')
def on_change_with_yield_balance(self, name=None):
""" 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])
- if self.yield_fee_total is not None:
- sum2 -= self.yield_fee_total
return sum2
@classmethod
@@ -250,9 +257,14 @@ class Book(SymbolMixin, metaclass=PoolMeta):
pool = Pool()
CashBook = pool.get('cashbook.book')
IrDate = pool.get('ir.date')
+ MemCache = pool.get('cashbook.memcache')
cursor = Transaction().connection.cursor()
context = Transaction().context
- result = {x:{y.id: Decimal('0.0') for y in cashbooks} for x in names}
+ result = {
+ x:{y.id: Decimal('0.0') for y in cashbooks}
+ for x in ['yield_fee_total', 'yield_dividend_total',
+ 'yield_sales', 'yield_fee_12m', 'yield_dividend_12m',
+ 'yield_sales_12m']}
def quantize_val(value, line):
""" quantize...
@@ -261,23 +273,50 @@ class Book(SymbolMixin, metaclass=PoolMeta):
value or Decimal('0.0')
).quantize(Decimal(str(1/10**line.currency_digits)))
- # results for 'total'
- (tab_book1, query_total) = cls.get_yield_data_sql()
- query_total.where &= tab_book1.id.in_([x.id for x in cashbooks])
- cursor.execute(*query_total)
- records_total = cursor.fetchall()
-
- # results for 12 months
query_date = context.get('date', IrDate.today())
- (tab_book2, query_12m) = cls.get_yield_data_sql(
- date_to = query_date,
- date_from = query_date - timedelta(days=365),
- )
- query_12m.where &= tab_book2.id.in_([x.id for x in cashbooks])
- cursor.execute(*query_12m)
- records_12m = cursor.fetchall()
+ cache_keys = {
+ x.id: MemCache.get_key_by_record(
+ name = 'get_yield_data',
+ record = x,
+ query = [{
+ 'model': 'cashbook.line',
+ 'query': [('cashbook.parent', 'child_of', [x.id])],
+ }, {
+ 'model': 'currency.currency.rate',
+ 'query': [('currency.id', '=', x.currency.id)],
+ 'cachekey': CACHEKEY_CURRENCY % x.currency.id,
+ }, {
+ 'model': 'investment.rate',
+ 'query': [('asset.id', '=', x.asset.id)],
+ } if x.asset is not None else {}],
+ addkeys = [query_date.isoformat()])
+ for x in cashbooks
+ }
+
+ # read from cache
+ (todo_cashbook, result) = MemCache.read_from_cache(
+ cashbooks, cache_keys, names, result)
+ if len(todo_cashbook) == 0:
+ return result
+
+ # results for 'total'
+ records_total = []
+ records_12m = []
+ if len(todo_cashbook) > 0:
+ (tab_book1, query_total) = cls.get_yield_data_sql()
+ query_total.where &= tab_book1.id.in_([x.id for x in todo_cashbook])
+ cursor.execute(*query_total)
+ records_total = cursor.fetchall()
+
+ # results for 12 months
+ (tab_book2, query_12m) = cls.get_yield_data_sql(
+ date_to = query_date,
+ date_from = query_date - timedelta(days=365),
+ )
+ query_12m.where &= tab_book2.id.in_([x.id for x in todo_cashbook])
+ cursor.execute(*query_12m)
+ records_12m = cursor.fetchall()
- result = {x:{y.id: Decimal('0.0') for y in cashbooks} for x in names}
for record in records_total:
book = CashBook(record[0])
result['yield_fee_total'][record[0]] = quantize_val(record[1], book)
@@ -290,7 +329,9 @@ class Book(SymbolMixin, metaclass=PoolMeta):
result['yield_dividend_12m'][record[0]] = quantize_val(record[2], book)
result['yield_sales_12m'][record[0]] = quantize_val(record[3], book)
- return result
+ # store to cache
+ MemCache.store_result(cashbooks, cache_keys, result)
+ return {x:result[x] for x in names}
@classmethod
def get_asset_quantity_sql(cls):
@@ -309,6 +350,7 @@ class Book(SymbolMixin, metaclass=PoolMeta):
tab_asset = Asset.__table__()
(tab_rate, tab2) = Asset.get_rate_data_sql()
(tab_balance, tab2) = CBook.get_balance_of_cashbook_sql()
+ (tab_line_yield, query_yield) = Line.get_yield_data_sql()
context = Transaction().context
query_date = context.get('qdate', CurrentDate())
@@ -320,6 +362,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
condition=tab_book.currency==tab_cur.id,
).join(tab_asset,
condition=tab_book.asset==tab_asset.id,
+ ).join(query_yield,
+ condition=query_yield.id==tab_line.id,
).join(tab_balance,
condition=tab_book.id==tab_balance.cashbook,
type_ = 'LEFT OUTER',
@@ -341,6 +385,9 @@ class Book(SymbolMixin, metaclass=PoolMeta):
tab_book.quantity_uom, # 7
tab_asset.currency.as_('asset_currency'), #8
Coalesce(tab_balance.balance, Decimal('0.0')).as_('balance'), #9
+ (
+ Sum(query_yield.fee) + tab_balance.balance
+ ).as_('purchase_amount'), #10
group_by=[tab_book.id, tab_rate.rate,
tab_book.currency, tab_cur.digits, tab_asset.uom,
tab_book.quantity_uom, tab_asset.currency,
@@ -357,6 +404,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
CBook = pool.get('cashbook.book')
Uom = pool.get('product.uom')
Currency = pool.get('currency.currency')
+ IrDate = pool.get('ir.date')
+ MemCache = pool.get('cashbook.memcache')
cursor = Transaction().connection.cursor()
context = Transaction().context
(query, tab_book) = cls.get_asset_quantity_sql()
@@ -364,6 +413,33 @@ class Book(SymbolMixin, metaclass=PoolMeta):
company_currency = CBook.default_currency()
result = {x:{y.id: None for y in cashbooks} for x in names}
+ cache_keys = {
+ x.id: MemCache.get_key_by_record(
+ name = 'get_asset_quantity',
+ record = x,
+ query = [{
+ 'model': 'cashbook.line',
+ 'query': [('cashbook.parent', 'child_of', [x.id])],
+ }, {
+ 'model': 'currency.currency.rate',
+ 'query': [('currency.id', '=', x.currency.id)],
+ }, {
+ 'model': 'investment.rate',
+ 'query': [('asset.id', '=', x.asset.id)],
+ } if x.asset is not None else {}],
+ addkeys=[
+ str(company_currency),
+ str(context.get('qdate', IrDate.today()).toordinal()),
+ ])
+ for x in cashbooks
+ }
+
+ # read from cache
+ (todo_cashbook, result) = MemCache.read_from_cache(
+ cashbooks, cache_keys, names, result)
+ if len(todo_cashbook) == 0:
+ return result
+
def values_from_record(rdata):
""" compute values for record
"""
@@ -389,16 +465,22 @@ class Book(SymbolMixin, metaclass=PoolMeta):
rdata[3] * rdata[1] / uom_factor,
company_currency if company_currency is not None else rdata[8],
),
- 'diff_amount': current_value - rdata[9],
+ 'diff_amount': current_value - rdata[10],
'diff_percent': (
Decimal('100.0') * current_value / \
- rdata[9] - Decimal('100.0')
+ rdata[10] - Decimal('100.0')
).quantize(Decimal(str(1/10**rdata[5]))) \
- if rdata[9] != Decimal('0.0') else None,
+ if rdata[10] != 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[10].quantize(Decimal(str(1/10**rdata[5]))),
+ 'purchase_amount_ref': Currency.compute(
+ rdata[4],
+ record[10],
+ company_currency if company_currency is not None else rdata[4],
+ ),
})
result_cache = {}
@@ -416,7 +498,9 @@ class Book(SymbolMixin, metaclass=PoolMeta):
result_cache[book_id] = values
result_cache[book_id]['balance_ref'] = CBook(book_id).balance_ref
- for name in names:
+ for name in values.keys():
+ if name not in result.keys():
+ result[name] = {}
result[name][book_id] = values[name]
# add aggregated values of cashbooks without type
@@ -451,9 +535,9 @@ class Book(SymbolMixin, metaclass=PoolMeta):
('parent', 'child_of', [id_none])
])
- values = {x:Decimal('0.0') for x in aggr_names+['balance_ref']}
+ values = {x:Decimal('0.0') for x in aggr_names+['purchase_amount_ref']}
for record in records:
- for name in aggr_names+['balance_ref']:
+ for name in aggr_names+['purchase_amount_ref']:
values[name] += \
result_cache.get(record.id, {}).get(name, Decimal('0.0'))
@@ -468,21 +552,24 @@ class Book(SymbolMixin, metaclass=PoolMeta):
values['diff_amount'] = Currency.compute(
company_currency if company_currency is not None else cbook.currency,
- values['current_value_ref'] - values['balance_ref'],
+ values['current_value_ref'] - values['purchase_amount_ref'],
cbook.currency,
)
values['diff_percent'] = \
(Decimal('100.0') * values['current_value_ref'] / \
- values['balance_ref'] - Decimal('100.0')
+ values['purchase_amount_ref'] - Decimal('100.0')
).quantize(
Decimal(str(1/10**cbook.currency_digits))
- ) if values['balance_ref'] != Decimal('0.0') else None
-
- for name in queried_names:
+ ) if values['purchase_amount_ref'] != Decimal('0.0') else None
+ for name in values.keys():
+ if name not in result.keys():
+ result[name] = {}
result[name][id_none] = values[name]
- return result
+ # store to cache
+ MemCache.store_result(cashbooks, cache_keys, result)
+ return {x:result[x] for x in names}
@fields.depends('id')
def on_change_with_show_performance(self, name=None):
diff --git a/locale/de.po b/locale/de.po
index f00f63e..64844fd 100644
--- a/locale/de.po
+++ b/locale/de.po
@@ -198,10 +198,13 @@ msgctxt "help:cashbook.book,yield_balance:"
msgid "Total income: price gain + dividends + sales gains - fees"
msgstr "Gesamtertrag: Kursgewinn + Dividenden + Verkaufsgewinne - Gebühren"
+msgctxt "field:cashbook.book,purchase_amount:"
+msgid "Purchase Amount"
+msgstr "Kaufbetrag"
-# Total return - share price gain plus dividend minus fees
-# Total return - price gain + sales gain + dividend - fees
-# Rendite insgesamt - Kursgewinn + Verkaufserfolg + Dividende - Gebühren
+msgctxt "help:cashbook.book,purchase_amount:"
+msgid "Total purchase amount, from shares and fees."
+msgstr "Kaufbetrag gesamt, aus Anteilen und Gebühren."
##################
diff --git a/locale/en.po b/locale/en.po
index b75f750..321a596 100644
--- a/locale/en.po
+++ b/locale/en.po
@@ -182,6 +182,14 @@ msgctxt "help:cashbook.book,yield_balance:"
msgid "Total income: price gain + dividends + sales gains - fees"
msgstr "Total income: price gain + dividends + sales gains - fees"
+msgctxt "field:cashbook.book,purchase_amount:"
+msgid "Purchase Amount"
+msgstr "Purchase Amount"
+
+msgctxt "help:cashbook.book,purchase_amount:"
+msgid "Total purchase amount, from shares and fees."
+msgstr "Total purchase amount, from shares and fees."
+
msgctxt "field:cashbook.split,quantity_digits:"
msgid "Digits"
msgstr "Digits"
diff --git a/tests/test_book.py b/tests/test_book.py
index 01df667..80c3bc8 100644
--- a/tests/test_book.py
+++ b/tests/test_book.py
@@ -432,7 +432,7 @@ class CbInvTestCase(CashbookTestCase, InvestmentTestCase):
self.assertEqual(book2.quantity_all, Decimal('20.0'))
# usd --> eur: 1750 US$ / 1.05 = 1666.666 €
# 1 ounce --> 20 gram: 1666.666 € * 20 / 28.3495 = 1175.7996 €
- # bette we use 'Troy Ounce': 1 oz.tr. = 31.1034768 gram
+ # better we use 'Troy Ounce': 1 oz.tr. = 31.1034768 gram
self.assertEqual(book2.current_value, Decimal('1175.80'))
self.assertEqual(book2.current_value_ref, Decimal('1175.80'))
self.assertEqual(book2.diff_amount, Decimal('-74.20'))
diff --git a/tests/test_yield.py b/tests/test_yield.py
index 11f8773..cde2799 100644
--- a/tests/test_yield.py
+++ b/tests/test_yield.py
@@ -160,7 +160,7 @@ class YieldTestCase(ModuleTestCase):
self.assertEqual(book_asset.yield_dividend_total, Decimal('23.5'))
self.assertEqual(book_asset.yield_fee_total, Decimal('4.0'))
self.assertEqual(book_asset.yield_sales, Decimal('0.0'))
- self.assertEqual(book_asset.diff_amount, Decimal('-19.5'))
+ self.assertEqual(book_asset.diff_amount, Decimal('-23.5'))
self.assertEqual(book_asset.yield_balance, Decimal('0.0'))
@with_transaction()
diff --git a/view/book_form.xml b/view/book_form.xml
index 72a362c..c8bc9ad 100644
--- a/view/book_form.xml
+++ b/view/book_form.xml
@@ -8,25 +8,29 @@ full copyright notices and license terms. -->
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
@@ -52,7 +56,6 @@ full copyright notices and license terms. -->
-