cashbook_investment/book.py
2023-01-24 21:25:29 +01:00

294 lines
12 KiB
Python

# -*- coding: utf-8 -*-
# This file is part of the cashbook-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 fields, SymbolMixin
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, Or, Len, Bool, If
from trytond.modules.cashbook.book import STATES2, DEPENDS2
from trytond.transaction import Transaction
from trytond.report import Report
from decimal import Decimal
from sql.functions import CurrentDate
from sql.aggregate import Sum
from sql.conditionals import Case, Coalesce
class Book(SymbolMixin, metaclass=PoolMeta):
__name__ = 'cashbook.book'
asset = fields.Many2One(string='Asset',
model_name='investment.asset', ondelete='RESTRICT',
states={
'required': Eval('feature', '') == 'asset',
'invisible': Eval('feature', '') != 'asset',
'readonly': Or(
STATES2['readonly'],
Len(Eval('lines')) > 0,
),
}, depends=DEPENDS2+['feature', 'lines'])
quantity_digits = fields.Integer(string='Digits',
help='Quantity Digits',
domain=[
('quantity_digits', '>=', 0),
('quantity_digits', '<=', 6),
],
states={
'required': Eval('feature', '') == 'asset',
'invisible': Eval('feature', '') != 'asset',
'readonly': Or(
STATES2['readonly'],
Len(Eval('lines')) > 0,
),
}, depends=DEPENDS2+['feature', 'lines'])
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',
model_name='product.uom', ondelete='RESTRICT',
domain=[
('category.id', '=', Eval('asset_uomcat', -1)),
],
states={
'required': Eval('feature', '') == 'asset',
'invisible': Eval('feature', '') != 'asset',
'readonly': Or(
STATES2['readonly'],
Len(Eval('lines')) > 0,
),
}, depends=DEPENDS2+['feature', 'lines', 'asset_uomcat'])
symbol = fields.Function(fields.Char(string='Symbol', readonly=True),
'on_change_with_symbol')
asset_symbol = fields.Function(fields.Many2One(string='Symbol',
readonly=True, model_name='cashbook.book'),
'on_change_with_asset_symbol')
quantity = fields.Function(fields.Numeric(string='Quantity',
help='Quantity of assets until to date', readonly=True,
digits=(16, Eval('quantity_digits', 4)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['quantity_digits', 'feature']),
'get_asset_quantity')
quantity_all = fields.Function(fields.Numeric(string='Total Quantity',
help='Total quantity of all assets', readonly=True,
digits=(16, Eval('quantity_digits', 4)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['quantity_digits', 'feature']),
'get_asset_quantity')
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']),
'get_asset_quantity')
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.',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
'invisible': Or(
Eval('feature', '') != 'asset',
~Bool(Eval('company_currency', -1)),
),
}, depends=['currency_digits', 'feature', 'company_currency']),
'get_asset_quantity')
# performance
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']), 'get_asset_quantity')
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']), 'get_asset_quantity')
current_rate = fields.Function(fields.Numeric(string='Rate',
help='Rate per unit of investment based on current stock exchange price.',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), 'get_asset_quantity')
@classmethod
def view_attributes(cls):
return super(Book, cls).view_attributes() + [
('/tree', 'visual',
If(Eval('feature', '') == 'asset',
If(Eval('diff_percent', 0) < 0, 'danger',
If(Eval('diff_percent', 0) > 0, 'success', '')
), '')
),
]
def get_rec_name(self, name):
""" add quantities - if its a asset-cashbook
"""
recname = super(Book, self).get_rec_name(name)
if self.feature == 'asset':
recname += ' | %(quantity)s %(uom_symbol)s' % {
'quantity': Report.format_number(self.quantity or 0.0, None,
digits=self.quantity_digits),
'uom_symbol': getattr(self.quantity_uom, 'symbol', '-'),
}
return recname
@fields.depends('asset', 'quantity_uom')
def on_change_asset(self):
""" get uom from asset
"""
if self.asset:
self.quantity_uom = self.asset.uom.id
@classmethod
def default_quantity_digits(cls):
""" default: 4
"""
return 4
@classmethod
def get_asset_quantity_sql(cls):
""" get table of asset and its value, rate, ...
"""
pool = Pool()
CBook = pool.get('cashbook.book')
BookType = pool.get('cashbook.type')
Line = pool.get('cashbook.line')
Asset = pool.get('investment.asset')
Currency = pool.get('currency.currency')
tab_book = CBook.__table__()
tab_type = BookType.__table__()
tab_line = Line.__table__()
tab_cur = Currency.__table__()
tab_asset = Asset.__table__()
(tab_rate, tab2) = Asset.get_rate_data_sql()
(tab_balance, tab2) = CBook.get_balance_of_cashbook_sql()
context = Transaction().context
query_date = context.get('qdate', CurrentDate())
query = tab_book.join(tab_line,
condition=(tab_book.id==tab_line.cashbook),
).join(tab_type,
condition=tab_book.btype==tab_type.id,
).join(tab_cur,
condition=tab_book.currency==tab_cur.id,
).join(tab_asset,
condition=tab_book.asset==tab_asset.id,
).join(tab_balance,
condition=tab_book.id==tab_balance.cashbook,
type_ = 'LEFT OUTER',
).join(tab_rate,
condition=tab_book.asset==tab_rate.id,
type_ = 'LEFT OUTER',
).select(
tab_book.id, # 0
Coalesce(Sum(Case(
(tab_line.date <= query_date,
tab_line.quantity_credit - tab_line.quantity_debit),
else_ = Decimal('0.0'),
)), Decimal('0.0')).as_('quantity'), # 1
Sum(tab_line.quantity_credit - tab_line.quantity_debit).as_('quantity_all'), # 2
Coalesce(tab_rate.rate, Decimal('0.0')).as_('rate'), # 3
tab_book.currency, # 4
tab_cur.digits.as_('currency_digits'), # 5
tab_asset.uom, # 6
tab_book.quantity_uom, # 7
tab_asset.currency.as_('asset_currency'), #8
Coalesce(tab_balance.balance, Decimal('0.0')).as_('balance'), #9
group_by=[tab_book.id, tab_rate.rate,
tab_book.currency, tab_cur.digits, tab_asset.uom,
tab_book.quantity_uom, tab_asset.currency,
tab_balance.balance],
where=(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()
result = {x:{y.id: None for y in cashbooks} for x in names}
company_currency = CBook.default_currency()
query.where &= tab_book.id.in_([x.id for x in cashbooks]) & \
(tab_book.btype != None)
cursor.execute(*query)
records = cursor.fetchall()
for record in records:
# uom-factor
if record[6] == record[7]:
uom_factor = Decimal('1.0')
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:
result[name][record[0]] = values[name]
return result
@fields.depends('id')
def on_change_with_asset_symbol(self, name=None):
""" get current cashbook to enable usage of 'symbol'
in the form
"""
return self.id
@fields.depends('quantity_uom', 'currency')
def on_change_with_symbol(self, name=None):
""" get symbol for asset
"""
return '%(currency)s/%(unit)s' % {
'currency': getattr(self.currency, 'symbol', '-'),
'unit': getattr(self.quantity_uom, 'symbol', '-'),
}
@fields.depends('asset', '_parent_asset.uom')
def on_change_with_asset_uomcat(self, name=None):
""" get uom-category of asset
"""
if self.asset:
return self.asset.uom.category.id
# end Book