Compare commits

...

11 commits

Author SHA1 Message Date
Frederik Jaeckel
9f12861b79 Version 6.0.3 2023-01-30 09:27:44 +01:00
Frederik Jaeckel
934d0fb207 reconciliation: use 'quantity_digits' for digits of start/end-quantity 2023-01-30 09:25:44 +01:00
Frederik Jaeckel
9edd2d4a95 line: add performance values to line-form 2023-01-29 23:16:22 +01:00
Frederik Jaeckel
3243fbf844 Etikett ver 6.0.2 zum Änderungssatz ae7678260358 hinzugefügt 2023-01-28 13:13:20 +01:00
Frederik Jaeckel
c9d6bf21a5 Version 6.0.2 2023-01-28 13:13:14 +01:00
Frederik Jaeckel
a315f4e768 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
674f72476e book: asset-query ausgelagert 2023-01-24 21:25:29 +01:00
Frederik Jaeckel
12e088f79a line: fix transfer between cash-/asset-book with zero quantity 2023-01-23 22:29:41 +01:00
Frederik Jaeckel
bbf94c95df line: fix rec_name 2023-01-22 10:28:09 +01:00
Frederik Jaeckel
29b74d0600 Etikett ver 6.0.1 zum Änderungssatz 67d3f883961c hinzugefügt 2023-01-21 19:18:44 +01:00
Frederik Jaeckel
269e744b8f Version 6.0.1 2023-01-21 19:18:36 +01:00
11 changed files with 682 additions and 65 deletions

View file

@ -14,6 +14,20 @@ Requires
Changes Changes
======= =======
*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* *6.0.0 - 20.12.2022*
- init - init

210
book.py
View file

@ -81,18 +81,18 @@ class Book(SymbolMixin, metaclass=PoolMeta):
help='Valuation of the investment based on the current stock market price.', help='Valuation of the investment based on the current stock market price.',
readonly=True, digits=(16, Eval('currency_digits', 2)), readonly=True, digits=(16, Eval('currency_digits', 2)),
states={ states={
'invisible': Eval('feature', '') != 'asset', 'invisible': Eval('show_performance', False) == False,
}, depends=['currency_digits', 'feature']), }, depends=['currency_digits', 'show_performance']),
'get_asset_quantity') 'get_asset_quantity')
current_value_ref = fields.Function(fields.Numeric(string='Value (Ref.)', current_value_ref = fields.Function(fields.Numeric(string='Value (Ref.)',
help='Valuation of the investment based on the current stock exchange price, converted into the company currency.', help='Valuation of the investment based on the current stock exchange price, converted into the company currency.',
readonly=True, digits=(16, Eval('currency_digits', 2)), readonly=True, digits=(16, Eval('currency_digits', 2)),
states={ states={
'invisible': Or( 'invisible': Or(
Eval('feature', '') != 'asset', Eval('show_performance', False) == False,
~Bool(Eval('company_currency', -1)), ~Bool(Eval('company_currency', -1)),
), ),
}, depends=['currency_digits', 'feature', 'company_currency']), }, depends=['currency_digits', 'show_performance', 'company_currency']),
'get_asset_quantity') 'get_asset_quantity')
# performance # performance
@ -100,14 +100,16 @@ class Book(SymbolMixin, metaclass=PoolMeta):
help='Difference between acquisition value and current value', help='Difference between acquisition value and current value',
readonly=True, digits=(16, Eval('currency_digits', 2)), readonly=True, digits=(16, Eval('currency_digits', 2)),
states={ states={
'invisible': Eval('feature', '') != 'asset', 'invisible': Eval('show_performance', False) == False,
}, depends=['currency_digits', 'feature']), 'get_asset_quantity') }, depends=['currency_digits', 'show_performance']), 'get_asset_quantity')
diff_percent = fields.Function(fields.Numeric(string='Percent', diff_percent = fields.Function(fields.Numeric(string='Percent',
help='percentage performance since acquisition', help='percentage performance since acquisition',
readonly=True, digits=(16, Eval('currency_digits', 2)), readonly=True, digits=(16, Eval('currency_digits', 2)),
states={ states={
'invisible': Eval('feature', '') != 'asset', 'invisible': Eval('show_performance', False) == False,
}, depends=['currency_digits', 'feature']), 'get_asset_quantity') }, depends=['currency_digits', 'show_performance']), 'get_asset_quantity')
show_performance = fields.Function(fields.Boolean(string='Performance',
readonly=True), 'on_change_with_show_performance')
current_rate = fields.Function(fields.Numeric(string='Rate', current_rate = fields.Function(fields.Numeric(string='Rate',
help='Rate per unit of investment based on current stock exchange price.', help='Rate per unit of investment based on current stock exchange price.',
readonly=True, digits=(16, Eval('currency_digits', 2)), readonly=True, digits=(16, Eval('currency_digits', 2)),
@ -119,7 +121,7 @@ class Book(SymbolMixin, metaclass=PoolMeta):
def view_attributes(cls): def view_attributes(cls):
return super(Book, cls).view_attributes() + [ return super(Book, cls).view_attributes() + [
('/tree', 'visual', ('/tree', 'visual',
If(Eval('feature', '') == 'asset', If(Eval('show_performance', False) == True,
If(Eval('diff_percent', 0) < 0, 'danger', If(Eval('diff_percent', 0) < 0, 'danger',
If(Eval('diff_percent', 0) > 0, 'success', '') If(Eval('diff_percent', 0) > 0, 'success', '')
), '') ), '')
@ -152,8 +154,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
return 4 return 4
@classmethod @classmethod
def get_asset_quantity(cls, cashbooks, names): def get_asset_quantity_sql(cls):
""" get quantities """ get table of asset and its value, rate, ...
""" """
pool = Pool() pool = Pool()
CBook = pool.get('cashbook.book') CBook = pool.get('cashbook.book')
@ -161,7 +163,6 @@ class Book(SymbolMixin, metaclass=PoolMeta):
Line = pool.get('cashbook.line') Line = pool.get('cashbook.line')
Asset = pool.get('investment.asset') Asset = pool.get('investment.asset')
Currency = pool.get('currency.currency') Currency = pool.get('currency.currency')
Uom = pool.get('product.uom')
tab_book = CBook.__table__() tab_book = CBook.__table__()
tab_type = BookType.__table__() tab_type = BookType.__table__()
tab_line = Line.__table__() tab_line = Line.__table__()
@ -169,13 +170,9 @@ class Book(SymbolMixin, metaclass=PoolMeta):
tab_asset = Asset.__table__() tab_asset = Asset.__table__()
(tab_rate, tab2) = Asset.get_rate_data_sql() (tab_rate, tab2) = Asset.get_rate_data_sql()
(tab_balance, tab2) = CBook.get_balance_of_cashbook_sql() (tab_balance, tab2) = CBook.get_balance_of_cashbook_sql()
cursor = Transaction().connection.cursor()
context = Transaction().context context = Transaction().context
result = {x:{y.id: None for y in cashbooks} for x in names}
query_date = context.get('qdate', CurrentDate()) query_date = context.get('qdate', CurrentDate())
company_currency = CBook.default_currency()
query = tab_book.join(tab_line, query = tab_book.join(tab_line,
condition=(tab_book.id==tab_line.cashbook), condition=(tab_book.id==tab_line.cashbook),
).join(tab_type, ).join(tab_type,
@ -209,52 +206,159 @@ class Book(SymbolMixin, metaclass=PoolMeta):
tab_book.currency, tab_cur.digits, tab_asset.uom, tab_book.currency, tab_cur.digits, tab_asset.uom,
tab_book.quantity_uom, tab_asset.currency, tab_book.quantity_uom, tab_asset.currency,
tab_balance.balance], tab_balance.balance],
where=tab_book.id.in_([x.id for x in cashbooks]) & \ where=(tab_type.feature == 'asset'),
(tab_type.feature == 'asset'),
) )
return (query, tab_book)
@classmethod
def get_asset_quantity(cls, cashbooks, names):
""" get quantities
"""
pool = Pool()
CBook = pool.get('cashbook.book')
Uom = pool.get('product.uom')
Currency = pool.get('currency.currency')
cursor = Transaction().connection.cursor()
context = Transaction().context
(query, tab_book) = cls.get_asset_quantity_sql()
company_currency = CBook.default_currency()
result = {x:{y.id: None for y in cashbooks} for x in names}
def values_from_record(rdata):
""" compute values for record
"""
# uom-factor
if rdata[6] == rdata[7]:
uom_factor = Decimal('1.0')
else :
uom_factor = Decimal(
Uom.compute_qty(Uom(rdata[6]), 1.0, Uom(rdata[7]), 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],
'current_value': current_value,
'current_value_ref': Currency.compute(
rdata[8],
rdata[3] * rdata[1] / uom_factor,
company_currency if company_currency is not None else rdata[8],
),
'diff_amount': current_value - rdata[9],
'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,
'current_rate': (
current_value / rdata[1]
).quantize(Decimal(str(1/10**rdata[5]))) \
if rdata[1] != Decimal('0.0') else None,
})
result_cache = {}
ids_assetbooks = [x.id for x in cashbooks if x.btype is not None]
ids_nonebtypes = [x.id for x in cashbooks if x.btype is None]
# get values of asset-cashbooks in 'cashbooks' of type=asset
if len(ids_assetbooks) > 0:
query.where &= tab_book.id.in_(ids_assetbooks)
cursor.execute(*query) cursor.execute(*query)
records = cursor.fetchall() records = cursor.fetchall()
for record in records: for record in records:
# uom-factor (book_id, values) = values_from_record(record)
if record[6] == record[7]: result_cache[book_id] = values
uom_factor = Decimal('1.0') result_cache[book_id]['balance_ref'] = CBook(book_id).balance_ref
else :
uom_factor = Decimal(
Uom.compute_qty(Uom(record[6]), 1.0, Uom(record[7]), round=False)
)
current_value = Currency.compute(
record[8],
record[3] * record[1] / uom_factor,
record[4]
)
values = {
'quantity': record[1],
'quantity_all': record[2],
'current_value': current_value,
'current_value_ref': Currency.compute(
record[8],
record[3] * record[1] / uom_factor,
company_currency if company_currency is not None else record[8],
),
'diff_amount': current_value - record[9],
'diff_percent': (
Decimal('100.0') * current_value / \
record[9] - Decimal('100.0')
).quantize(Decimal(str(1/10**record[5]))) \
if record[9] != Decimal('0.0') else None,
'current_rate': (
current_value / record[1]
).quantize(Decimal(str(1/10**record[5]))) \
if record[1] != Decimal('0.0') else None,
}
for name in names: for name in names:
result[name][record[0]] = values[name] result[name][book_id] = values[name]
# add aggregated values of cashbooks without type
aggr_names = ['current_value', 'current_value_ref',
'diff_amount', 'diff_percent']
queried_names = list(set(aggr_names).intersection(set(names)))
if len(queried_names) > 0:
# query all subordered asset-cashbooks for
# btype=None-cashbooks
query1 = [('btype.feature', '=', 'asset'),
('parent', 'child_of', ids_nonebtypes)]
if len(result_cache.keys()) > 0:
query1.append(('id', 'not in', result_cache.keys()))
books_query = CBook.search(query1, query=True)
# add results to cache
(query, tab_book) = cls.get_asset_quantity_sql()
query.where &= tab_book.id.in_(books_query)
cursor.execute(*query)
records = cursor.fetchall()
for record in records:
(book_id, values) = values_from_record(record)
result_cache[book_id] = values
result_cache[book_id]['balance_ref'] = CBook(book_id).balance_ref
# aggregate sub-cashbooks to requested cashbooks from cache
for id_none in ids_nonebtypes:
records = CBook.search([
('btype.feature', '=', 'asset'),
('parent', 'child_of', [id_none])
])
values = {x:Decimal('0.0') for x in aggr_names+['balance_ref']}
for record in records:
for name in aggr_names+['balance_ref']:
values[name] += \
result_cache.get(record.id, {}).get(name, Decimal('0.0'))
# convert current-value-ref in company-currency to
# currency of current cashbook
cbook = CBook(id_none)
values['current_value'] = Currency.compute(
company_currency if company_currency is not None else cbook.currency,
values['current_value_ref'],
cbook.currency,
)
values['diff_amount'] = Currency.compute(
company_currency if company_currency is not None else cbook.currency,
values['current_value_ref'] - values['balance_ref'],
cbook.currency,
)
values['diff_percent'] = \
(Decimal('100.0') * values['current_value_ref'] / \
values['balance_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:
result[name][id_none] = values[name]
return result return result
@fields.depends('id')
def on_change_with_show_performance(self, name=None):
""" return True if current or subordered cashbooks
are of type=asset
"""
Book2 = Pool().get('cashbook.book')
if Book2.search_count([
('btype.feature', '=', 'asset'),
('parent', 'child_of', [self.id]),
]) > 0:
return True
return False
@fields.depends('id') @fields.depends('id')
def on_change_with_asset_symbol(self, name=None): def on_change_with_asset_symbol(self, name=None):
""" get current cashbook to enable usage of 'symbol' """ get current cashbook to enable usage of 'symbol'

76
line.py
View file

@ -85,6 +85,29 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
string='has quantity', readonly=True, states={'invisible': True}), string='has quantity', readonly=True, states={'invisible': True}),
'on_change_with_splitline_has_quantity') 'on_change_with_splitline_has_quantity')
# performance
current_value = fields.Function(fields.Numeric(string='Value',
help='Valuation of the investment based on the current stock market price.',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']),
'on_change_with_current_value')
diff_amount = fields.Function(fields.Numeric(string='Difference',
help='Difference between acquisition value and current value',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']),
'on_change_with_diff_amount')
diff_percent = fields.Function(fields.Numeric(string='Percent',
help='percentage performance since acquisition',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states = {
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']),
'on_change_with_diff_percent')
def get_rec_name(self, name): def get_rec_name(self, name):
""" add quantities - if its a asset-cashbook """ add quantities - if its a asset-cashbook
""" """
@ -95,7 +118,7 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
recname += '|%(quantity)s %(uom_symbol)s' % { recname += '|%(quantity)s %(uom_symbol)s' % {
'quantity': Report.format_number(credit - debit, 'quantity': Report.format_number(credit - debit,
lang=None, digits=self.quantity_digits), lang=None, digits=self.quantity_digits),
'uom_symbol': self.quantity_uom.symbol, 'uom_symbol': getattr(self.quantity_uom, 'symbol', '-'),
} }
return recname return recname
@ -181,19 +204,16 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
'quantity_2nd_uom': splitline.quantity \ 'quantity_2nd_uom': splitline.quantity \
if (asset_books == 2) and (diff_uom == True) else None, if (asset_books == 2) and (diff_uom == True) else None,
}) })
elif booktransf_uom is None: elif sum([1 if booktransf_uom is not None else 0,
# counterpart-cashbook has no uom -> no quantity 1 if line_uom is not None else 0]) == 1:
result.update({ # one of the related cashbooks only is asset-type
'quantity': None,
'quantity_2nd_uom': None,
})
else :
if line_uom is None:
result.update({ result.update({
'quantity': line.quantity, 'quantity': line.quantity,
'quantity_2nd_uom': None, 'quantity_2nd_uom': None,
}) })
elif line_uom == booktransf_uom: elif sum([1 if booktransf_uom is not None else 0,
1 if line_uom is not None else 0]) == 2:
if line_uom == booktransf_uom:
result.update({ result.update({
'quantity': line.quantity, 'quantity': line.quantity,
'quantity_2nd_uom': None, 'quantity_2nd_uom': None,
@ -215,6 +235,42 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
if cnt1 > 0: if cnt1 > 0:
self.quantity = quantity self.quantity = quantity
@fields.depends('quantity', 'cashbook', '_parent_cashbook.current_rate', 'currency_digits')
def on_change_with_current_value(self, name=None):
""" get current value of line by current stock marked price
and quantity
"""
if self.cashbook:
if (self.quantity is not None) and \
(self.cashbook.current_rate is not None):
return (
self.quantity * self.cashbook.current_rate
).quantize(Decimal(str(1/10**self.currency_digits)))
@fields.depends('quantity', 'amount', 'cashbook', '_parent_cashbook.current_rate', 'currency_digits')
def on_change_with_diff_amount(self, name=None):
""" get delta between buy and current value
"""
if self.cashbook:
if (self.quantity is not None) and \
(self.amount is not None) and \
(self.cashbook.current_rate is not None):
return (
self.quantity * self.cashbook.current_rate - self.amount
).quantize(Decimal(str(1/10**self.currency_digits)))
@fields.depends('quantity', 'amount', 'cashbook', '_parent_cashbook.current_rate')
def on_change_with_diff_percent(self, name=None):
""" get performane percent
"""
if self.cashbook:
if (self.quantity is not None) and \
(self.amount is not None) and (self.amount != Decimal('0.0')) and \
(self.cashbook.current_rate is not None):
return (self.quantity * self.cashbook.current_rate * \
Decimal('100.0') / self.amount - Decimal('100.0')
).quantize(Decimal(str(1/10**self.currency_digits)))
@fields.depends('splitlines') @fields.depends('splitlines')
def on_change_with_splitline_has_quantity(self, name=None): def on_change_with_splitline_has_quantity(self, name=None):
""" get True if splitlines are linked to asset-cashbooks """ get True if splitlines are linked to asset-cashbooks

View file

@ -162,6 +162,10 @@ msgstr "Umrechnungsfaktor zwischen den Einheiten der teilnehmenden Kassenbücher
################# #################
# cashbook.line # # cashbook.line #
################# #################
msgctxt "view:cashbook.line:"
msgid "Performance"
msgstr "Wertentwicklung"
msgctxt "field:cashbook.line,quantity_digits:" msgctxt "field:cashbook.line,quantity_digits:"
msgid "Digits" msgid "Digits"
msgstr "Dezimalstellen" msgstr "Dezimalstellen"
@ -218,6 +222,30 @@ msgctxt "field:cashbook.line,splitline_has_quantity:"
msgid "has quantity" msgid "has quantity"
msgstr "hat Anzahl" msgstr "hat Anzahl"
msgctxt "field:cashbook.line,current_value:"
msgid "Value"
msgstr "Wert"
msgctxt "help:cashbook.line,current_value:"
msgid "Valuation of the investment based on the current stock market price."
msgstr "Bewertung der Vermögensanlage anhand des aktuellen Börsenkurses."
msgctxt "field:cashbook.line,diff_amount:"
msgid "Difference"
msgstr "Differenz"
msgctxt "help:cashbook.line,diff_amount:"
msgid "Difference between acquisition value and current value"
msgstr "Differenz zwischen Anschaffungswert und aktuellem Wert"
msgctxt "field:cashbook.line,diff_percent:"
msgid "Percent"
msgstr "Prozent"
msgctxt "help:cashbook.line,diff_percent:"
msgid "percentage performance since acquisition"
msgstr "prozentuale Wertentwicklung seit Anschaffung"
################## ##################
# cashbook.recon # # cashbook.recon #

View file

@ -146,6 +146,10 @@ msgctxt "help:cashbook.split,factor_2nd_uom:"
msgid "Conversion factor between the units of the participating cash books." msgid "Conversion factor between the units of the participating cash books."
msgstr "Conversion factor between the units of the participating cash books." msgstr "Conversion factor between the units of the participating cash books."
msgctxt "view:cashbook.line:"
msgid "Performance"
msgstr "Performance"
msgctxt "field:cashbook.line,quantity_digits:" msgctxt "field:cashbook.line,quantity_digits:"
msgid "Digits" msgid "Digits"
msgstr "Digits" msgstr "Digits"
@ -202,6 +206,30 @@ msgctxt "field:cashbook.line,splitline_has_quantity:"
msgid "has quantity" msgid "has quantity"
msgstr "has quantity" msgstr "has quantity"
msgctxt "field:cashbook.line,current_value:"
msgid "Value"
msgstr "Value"
msgctxt "help:cashbook.line,current_value:"
msgid "Valuation of the investment based on the current stock market price."
msgstr "Valuation of the investment based on the current stock market price."
msgctxt "field:cashbook.line,diff_amount:"
msgid "Difference"
msgstr "Difference"
msgctxt "help:cashbook.line,diff_amount:"
msgid "Difference between acquisition value and current value"
msgstr "Difference between acquisition value and current value"
msgctxt "field:cashbook.line,diff_percent:"
msgid "Percent"
msgstr "Percent"
msgctxt "help:cashbook.line,diff_percent:"
msgid "percentage performance since acquisition"
msgstr "percentage performance since acquisition"
msgctxt "field:cashbook.recon,start_quantity:" msgctxt "field:cashbook.recon,start_quantity:"
msgid "Start Quantity" msgid "Start Quantity"
msgstr "Start Quantity" msgstr "Start Quantity"

View file

@ -59,7 +59,7 @@ class Reconciliation(metaclass=PoolMeta):
""" quantity_digits of cashbook """ quantity_digits of cashbook
""" """
if self.cashbook: if self.cashbook:
return self.cashbook.currency.digits return self.cashbook.quantity_digits
else: else:
return 4 return 4

View file

@ -48,6 +48,168 @@ class CbInvTestCase(CashbookTestCase, InvestmentTestCase):
self.assertEqual(book.state_string, 'Open') self.assertEqual(book.state_string, 'Open')
self.assertEqual(book.feature, 'asset') self.assertEqual(book.feature, 'asset')
self.assertEqual(book.quantity_digits, 4) self.assertEqual(book.quantity_digits, 4)
self.assertEqual(book.show_performance, True)
@with_transaction()
def test_assetbook_aggregated_values(self):
""" create cashbooks with hierarchy, add lines,
check values at non-type-books
"""
pool = Pool()
Book = pool.get('cashbook.book')
BType = pool.get('cashbook.type')
Asset = pool.get('investment.asset')
company = self.prep_company()
type_depot = self.prep_type('Depot', 'D')
type_cash = self.prep_type('Cash', 'C')
category_in = self.prep_category(cattype='in')
asset = self.prep_asset_item(
company=company,
product = self.prep_asset_product(name='Product 1'))
self.assertEqual(asset.symbol, 'usd/u')
Asset.write(*[
[asset],
{
'rates': [('create', [{
'date': date(2022, 5, 1),
'rate': Decimal('10.0'),
}, {
'date': date(2022, 5, 2),
'rate': Decimal('12.5'),
}])],
}])
self.assertEqual(asset.rec_name, 'Product 1 | 12.5000 usd/u | 05/02/2022')
(usd, euro) = self.prep_2nd_currency(company)
self.assertEqual(company.currency.rec_name, 'Euro')
BType.write(*[
[type_depot],
{
'feature': 'asset',
}])
with Transaction().set_context({
'company': company.id,
}):
books = Book.create([{
'name': 'L0-Euro-None',
'btype': None,
'company': company.id,
'currency': euro.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
'childs': [('create', [{
'name': 'L1-Euro-Cash',
'btype': type_cash.id,
'company': company.id,
'currency': euro.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Cat In',
'category': category_in.id,
'bookingtype': 'in',
'amount': Decimal('15.0'),
}])],
}, {
'name': 'L1-USD-Cash',
'btype': type_cash.id,
'company': company.id,
'currency': usd.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Cat In',
'category': category_in.id,
'bookingtype': 'in',
'amount': Decimal('15.0'), # 14.29 €
}])],
}, {
'name': 'L1-Euro-Depot',
'btype': type_depot.id,
'company': company.id,
'currency': euro.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
'asset': asset.id,
'quantity_uom': asset.uom.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Cat In',
'category': category_in.id,
'bookingtype': 'in',
'amount': Decimal('15.0'),
'quantity': Decimal('1.0'),
}])],
}, {
'name': 'L1-USD-Depot',
'btype': type_depot.id,
'company': company.id,
'currency': usd.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
'asset': asset.id,
'quantity_uom': asset.uom.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Cat In',
'category': category_in.id,
'bookingtype': 'in',
'amount': Decimal('15.0'), # 14.29 €
'quantity': Decimal('1.0'),
}])],
}])],
}])
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'))
self.assertEqual(books[0].diff_amount, Decimal('-5.49'))
self.assertEqual(books[0].diff_percent, Decimal('-18.74'))
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].diff_amount, None)
self.assertEqual(books[0].childs[0].diff_percent, None)
self.assertEqual(books[0].childs[1].rec_name,
'L0-Euro-None/L1-Euro-Depot | 15.00 € | Open | 1.0000 u')
self.assertEqual(books[0].childs[1].asset.rec_name,
'Product 1 | 12.5000 usd/u | 05/02/2022')
self.assertEqual(books[0].childs[1].current_value, Decimal('11.9'))
self.assertEqual(books[0].childs[1].current_value_ref, Decimal('11.9'))
self.assertEqual(books[0].childs[1].diff_amount, Decimal('-3.1'))
self.assertEqual(books[0].childs[1].diff_percent, Decimal('-20.67'))
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].diff_amount, None)
self.assertEqual(books[0].childs[2].diff_percent, None)
self.assertEqual(books[0].childs[3].rec_name,
'L0-Euro-None/L1-USD-Depot | 15.00 usd | Open | 1.0000 u')
self.assertEqual(books[0].childs[3].asset.rec_name,
'Product 1 | 12.5000 usd/u | 05/02/2022')
self.assertEqual(books[0].childs[3].current_value, Decimal('12.5'))
self.assertEqual(books[0].childs[3].current_value_ref, Decimal('11.9'))
self.assertEqual(books[0].childs[3].diff_amount, Decimal('-2.5'))
self.assertEqual(books[0].childs[3].diff_percent, Decimal('-16.67'))
@with_transaction() @with_transaction()
def test_assetbook_create_line(self): def test_assetbook_create_line(self):
@ -134,6 +296,9 @@ class CbInvTestCase(CashbookTestCase, InvestmentTestCase):
self.assertEqual(book.lines[0].quantity_debit, Decimal('0.0')) self.assertEqual(book.lines[0].quantity_debit, Decimal('0.0'))
self.assertEqual(book.lines[0].quantity_digits, 3) self.assertEqual(book.lines[0].quantity_digits, 3)
self.assertEqual(book.lines[0].quantity_uom.symbol, 'u') self.assertEqual(book.lines[0].quantity_uom.symbol, 'u')
self.assertEqual(book.lines[0].current_value, Decimal('3.88'))
self.assertEqual(book.lines[0].diff_amount, Decimal('1.38'))
self.assertEqual(book.lines[0].diff_percent, Decimal('55.18'))
self.assertEqual(book.lines[1].amount, Decimal('4.0')) self.assertEqual(book.lines[1].amount, Decimal('4.0'))
self.assertEqual(book.lines[1].quantity, Decimal('3.3')) self.assertEqual(book.lines[1].quantity, Decimal('3.3'))
@ -576,6 +741,7 @@ class CbInvTestCase(CashbookTestCase, InvestmentTestCase):
'number_sequ': self.prep_sequence().id, 'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1), 'start_date': date(2022, 5, 1),
}]) }])
self.assertEqual(book2.show_performance, True)
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
@ -594,6 +760,7 @@ class CbInvTestCase(CashbookTestCase, InvestmentTestCase):
'quantity': Decimal('1.5'), 'quantity': Decimal('1.5'),
}])], }])],
}]) }])
self.assertEqual(book.show_performance, False)
self.assertEqual(book.rec_name, 'Book 1 | -1.00 usd | Open') self.assertEqual(book.rec_name, 'Book 1 | -1.00 usd | Open')
self.assertEqual(len(book.lines), 1) self.assertEqual(len(book.lines), 1)
self.assertEqual(book.lines[0].quantity, Decimal('1.5')) self.assertEqual(book.lines[0].quantity, Decimal('1.5'))
@ -677,6 +844,87 @@ class CbInvTestCase(CashbookTestCase, InvestmentTestCase):
book.lines = l1 book.lines = l1
book.lines[-1].on_change_quantity() book.lines[-1].on_change_quantity()
@with_transaction()
def test_assetbook_check_mvout_zero_quantity(self):
""" create cashbook + line, bookingtype 'mvout'
transfer from asset-book to cash-book - zero quantity,
to book gain/loss
"""
pool = Pool()
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
Category = pool.get('cashbook.category')
BType = pool.get('cashbook.type')
type_cash = self.prep_type()
type_depot = self.prep_type('Depot', 'D')
BType.write(*[
[type_depot],
{
'feature': 'asset',
}])
category_out = self.prep_category(name='Out Category', cattype='out')
company = self.prep_company()
party = self.prep_party()
asset = self.prep_asset_item(
company=company,
product = self.prep_asset_product(name='Product 1'))
self.assertEqual(asset.symbol, 'usd/u')
book1, = Book.create([{
'name': 'Asset-Book',
'btype': type_depot.id,
'asset': asset.id,
'quantity_uom': asset.uom.id,
'company': company.id,
'currency': company.currency.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
}])
book2, = Book.create([{
'name': 'Cash-Book',
'btype': type_cash.id,
'company': company.id,
'currency': company.currency.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1),
}])
Book.write(*[
[book1],
{
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'loss at sell',
'category': category_out.id,
'bookingtype': 'mvout',
'amount': Decimal('10.0'),
'booktransf': book2.id,
'quantity': Decimal('0.0'),
}])],
},
])
self.assertEqual(book1.rec_name, 'Asset-Book | -10.00 usd | Open | 0.0000 u')
self.assertEqual(len(book1.lines), 1)
self.assertEqual(book1.lines[0].rec_name,
'05/01/2022|to|-10.00 usd|loss at sell [Cash-Book | 0.00 usd | Open]|0.0000 u')
self.assertEqual(book2.rec_name, 'Cash-Book | 0.00 usd | Open')
self.assertEqual(len(book2.lines), 0)
Line.wfcheck(list(book1.lines))
self.assertEqual(book1.rec_name, 'Asset-Book | -10.00 usd | Open | 0.0000 u')
self.assertEqual(len(book1.lines), 1)
self.assertEqual(book1.lines[0].rec_name,
'05/01/2022|to|-10.00 usd|loss at sell [Cash-Book | 10.00 usd | Open]|0.0000 u')
self.assertEqual(book2.rec_name, 'Cash-Book | 10.00 usd | Open')
self.assertEqual(len(book2.lines), 1)
self.assertEqual(book2.lines[0].rec_name,
'05/01/2022|from|10.00 usd|loss at sell [Asset-Book | -10.00 usd | Open | 0.0000 u]')
@with_transaction() @with_transaction()
def test_assetbook_check_mvin(self): def test_assetbook_check_mvin(self):
""" create cashbook + line, bookingtype 'mvin' """ create cashbook + line, bookingtype 'mvin'
@ -1401,6 +1649,129 @@ class CbInvTestCase(CashbookTestCase, InvestmentTestCase):
self.assertEqual(books[1].lines[0].rec_name, self.assertEqual(books[1].lines[0].rec_name,
'05/01/2022|to|-6.00 usd|from cashbook [Book 1 | 11.00 usd | Open | 4.00 u]|-2.50 u') '05/01/2022|to|-6.00 usd|from cashbook [Book 1 | 11.00 usd | Open | 4.00 u]|-2.50 u')
@with_transaction()
def test_assetbook_split_in_category_and_assetbook_zero_quantity(self):
""" splitbooking incoming to asset-cahbook,
from category and asset-cashbook, zero qunatity to book
gain/loss of a sell from asset-cashbook
"""
pool = Pool()
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
BType = pool.get('cashbook.type')
Asset = pool.get('investment.asset')
ProdTempl = pool.get('product.template')
Uom = pool.get('product.uom')
types = self.prep_type()
BType.write(*[
[types],
{
'feature': 'asset',
}])
category = self.prep_category(cattype='in')
company = self.prep_company()
party = self.prep_party()
asset = self.prep_asset_item(
company=company,
product = self.prep_asset_product(name='Product 1'))
self.assertEqual(asset.symbol, 'usd/u')
books = Book.create([{
'start_date': date(2022, 4, 1),
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'number_sequ': self.prep_sequence().id,
'asset': asset.id,
'quantity_uom': asset.uom.id,
'quantity_digits': 2,
}, {
'start_date': date(2022, 4, 1),
'name': 'Book 2',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'number_sequ': self.prep_sequence().id,
'asset': asset.id,
'quantity_uom': asset.uom.id,
'quantity_digits': 2,
}])
Book.write(*[
[books[0]],
{
'lines': [('create', [{
'bookingtype': 'spin',
'date': date(2022, 5, 1),
'splitlines': [('create', [{
'amount': Decimal('5.0'),
'splittype': 'cat',
'description': 'gain on sell',
'category': category.id,
'quantity': Decimal('0.0'),
}, {
'amount': Decimal('6.0'),
'splittype': 'tr',
'description': 'transfer zero quantity',
'booktransf': books[1].id,
'quantity': Decimal('0.0'),
}])],
}])],
}])
self.assertEqual(books[0].rec_name, 'Book 1 | 11.00 usd | Open | 0.00 u')
self.assertEqual(books[0].balance_all, Decimal('11.0'))
self.assertEqual(len(books[0].lines), 1)
self.assertEqual(books[0].lines[0].rec_name, '05/01/2022|Rev/Sp|11.00 usd|- [-]|0.00 u')
self.assertEqual(books[0].lines[0].splitline_has_quantity, True)
self.assertEqual(len(books[0].lines[0].splitlines), 2)
self.assertEqual(books[0].lines[0].splitlines[0].rec_name,
'Rev/Sp|5.00 usd|gain on sell [Cat1]|0.00 u')
self.assertEqual(books[0].lines[0].splitlines[0].booktransf, None)
self.assertEqual(books[0].lines[0].splitlines[1].rec_name,
'Rev/Sp|6.00 usd|transfer zero quantity [Book 2 | 0.00 usd | Open | 0.00 u]|0.00 u')
self.assertEqual(books[0].lines[0].splitlines[1].booktransf.rec_name,
'Book 2 | 0.00 usd | Open | 0.00 u')
self.assertEqual(len(books[0].lines[0].references), 0)
self.assertEqual(books[0].lines[0].reference, None)
self.assertEqual(books[1].rec_name, 'Book 2 | 0.00 usd | Open | 0.00 u')
self.assertEqual(books[1].balance_all, Decimal('0.0'))
self.assertEqual(len(books[1].lines), 0)
Line.wfcheck([books[0].lines[0]])
self.assertEqual(books[0].rec_name, 'Book 1 | 11.00 usd | Open | 0.00 u')
self.assertEqual(books[0].balance_all, Decimal('11.0'))
self.assertEqual(len(books[0].lines), 1)
self.assertEqual(books[0].lines[0].rec_name, '05/01/2022|Rev/Sp|11.00 usd|- [-]|0.00 u')
self.assertEqual(books[0].lines[0].splitline_has_quantity, True)
self.assertEqual(len(books[0].lines[0].splitlines), 2)
self.assertEqual(books[0].lines[0].splitlines[0].rec_name,
'Rev/Sp|5.00 usd|gain on sell [Cat1]|0.00 u')
self.assertEqual(books[0].lines[0].splitlines[0].booktransf, None)
self.assertEqual(books[0].lines[0].splitlines[1].rec_name,
'Rev/Sp|6.00 usd|transfer zero quantity [Book 2 | -6.00 usd | Open | 0.00 u]|0.00 u')
self.assertEqual(books[0].lines[0].splitlines[1].booktransf.rec_name,
'Book 2 | -6.00 usd | Open | 0.00 u')
self.assertEqual(len(books[0].lines[0].references), 1)
self.assertEqual(books[0].lines[0].references[0].rec_name,
'05/01/2022|to|-6.00 usd|transfer zero quantity [Book 1 | 11.00 usd | Open | 0.00 u]|0.00 u')
self.assertEqual(books[0].lines[0].reference, None)
self.assertEqual(books[1].rec_name, 'Book 2 | -6.00 usd | Open | 0.00 u')
self.assertEqual(books[1].balance_all, Decimal('-6.0'))
self.assertEqual(len(books[1].lines), 1)
self.assertEqual(books[1].lines[0].rec_name,
'05/01/2022|to|-6.00 usd|transfer zero quantity [Book 1 | 11.00 usd | Open | 0.00 u]|0.00 u')
@with_transaction() @with_transaction()
def test_assetbook_split_in_catergory_asset_diff_unit(self): def test_assetbook_split_in_catergory_asset_diff_unit(self):
""" splitbooking incoming to asset-cahbook, """ splitbooking incoming to asset-cahbook,
@ -1744,7 +2115,7 @@ class CbInvTestCase(CashbookTestCase, InvestmentTestCase):
@with_transaction() @with_transaction()
def test_assetbook_split_out_category_and_assetbook(self): def test_assetbook_split_out_category_and_assetbook(self):
""" splitbooking outgoing, """ splitbooking outgoing,
from asset-cashbook to asset-cahbook and to category from asset-cashbook to asset-cashbook and to category
""" """
pool = Pool() pool = Pool()
Book = pool.get('cashbook.book') Book = pool.get('cashbook.book')

View file

@ -54,11 +54,11 @@ class ReconTestCase(ModuleTestCase):
self.assertEqual(book.name, 'Asset-Book') self.assertEqual(book.name, 'Asset-Book')
self.assertEqual(book.reconciliations[0].feature, 'asset') self.assertEqual(book.reconciliations[0].feature, 'asset')
self.assertEqual(book.reconciliations[0].rec_name, self.assertEqual(book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] | 0.00 u - 0.00 u') '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] | 0.0000 u - 0.0000 u')
Reconciliation.wfcheck(list(book.reconciliations)) Reconciliation.wfcheck(list(book.reconciliations))
self.assertEqual(book.reconciliations[0].rec_name, self.assertEqual(book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] | 0.00 u - 0.00 u') '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] | 0.0000 u - 0.0000 u')
@with_transaction() @with_transaction()
def test_recon_set_start_quantity_by_predecessor(self): def test_recon_set_start_quantity_by_predecessor(self):
@ -91,6 +91,7 @@ class ReconTestCase(ModuleTestCase):
'currency': company.currency.id, 'currency': company.currency.id,
'asset': asset.id, 'asset': asset.id,
'quantity_uom': asset.uom.id, 'quantity_uom': asset.uom.id,
'quantity_digits': 3,
'start_date': date(2022, 5, 1), 'start_date': date(2022, 5, 1),
'number_sequ': self.prep_sequence().id, 'number_sequ': self.prep_sequence().id,
'reconciliations': [('create', [{ 'reconciliations': [('create', [{
@ -119,7 +120,7 @@ class ReconTestCase(ModuleTestCase):
self.assertEqual(book.name, 'Asset-Book') self.assertEqual(book.name, 'Asset-Book')
self.assertEqual(len(book.reconciliations), 1) self.assertEqual(len(book.reconciliations), 1)
self.assertEqual(book.reconciliations[0].rec_name, self.assertEqual(book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] | 0.00 u - 0.00 u') '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] | 0.000 u - 0.000 u')
self.assertEqual(len(book.reconciliations[0].lines), 0) self.assertEqual(len(book.reconciliations[0].lines), 0)
Lines.wfcheck(list(book.lines)) Lines.wfcheck(list(book.lines))
@ -134,7 +135,7 @@ class ReconTestCase(ModuleTestCase):
self.assertEqual(book.reconciliations[0].state, 'check') self.assertEqual(book.reconciliations[0].state, 'check')
self.assertEqual(book.reconciliations[0].rec_name, self.assertEqual(book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 12.00 usd [2] | 0.00 u - 4.00 u') '05/01/2022 - 05/31/2022 | 0.00 usd - 12.00 usd [2] | 0.000 u - 4.000 u')
Reconciliation.wfdone(list(book.reconciliations)) Reconciliation.wfdone(list(book.reconciliations))
self.assertEqual(book.reconciliations[0].state, 'done') self.assertEqual(book.reconciliations[0].state, 'done')
@ -144,9 +145,9 @@ class ReconTestCase(ModuleTestCase):
'date_to': date(2022, 6, 30), 'date_to': date(2022, 6, 30),
}]) }])
self.assertEqual(recons[0].rec_name, self.assertEqual(recons[0].rec_name,
'05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0] | 0.00 u - 0.00 u') '05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0] | 0.000 u - 0.000 u')
Reconciliation.wfcheck(recons) Reconciliation.wfcheck(recons)
self.assertEqual(recons[0].rec_name, self.assertEqual(recons[0].rec_name,
'05/31/2022 - 06/30/2022 | 12.00 usd - 12.00 usd [0] | 4.00 u - 4.00 u') '05/31/2022 - 06/30/2022 | 12.00 usd - 12.00 usd [0] | 4.000 u - 4.000 u')
# end ReconTestCase # end ReconTestCase

View file

@ -1,5 +1,5 @@
[tryton] [tryton]
version=6.0.0 version=6.0.3
depends: depends:
cashbook cashbook
investment investment

View file

@ -1 +1,2 @@
cashbook;6.0.21;6.0.999;mds cashbook;6.0.23;6.0.999;mds
investment;6.0.23;6.0.999;mds

View file

@ -23,4 +23,18 @@ full copyright notices and license terms. -->
<field name="factor_2nd_uom"/> <field name="factor_2nd_uom"/>
</xpath> </xpath>
<xpath expr="/form/notebook/page[@name='description']" position="after">
<page name="current_value" col="5" string="Performance">
<label name="current_value"/>
<field name="current_value" symbol="currency"/>
<label name="diff_amount"/>
<field name="diff_amount" symbol="currency"/>
<group id="diff_percent" col="2">
<field name="diff_percent" xexpand="0"/>
<label name="diff_percent" xalign="0.0" string="%" xexpand="1"/>
</group>
</page>
</xpath>
</data> </data>