formatting

This commit is contained in:
Frederik Jaeckel 2023-06-08 14:00:59 +02:00
parent 6c1fac8672
commit ad067475f8
13 changed files with 1126 additions and 709 deletions

View file

@ -45,4 +45,3 @@ class AssetRate(metaclass=PoolMeta):
super(AssetRate, cls).delete(records)
# end

View file

@ -12,13 +12,16 @@ class AssetSetting(ModelSingleton, ModelSQL, ModelView):
'Asset setting'
__name__ = 'cashbook.assetconf'
fee_category = fields.Many2One(string='Fee category',
fee_category = fields.Many2One(
string='Fee category',
model_name='cashbook.category', ondelete='RESTRICT',
help='Category for fees when trading assets.')
dividend_category = fields.Many2One(string='Dividend category',
dividend_category = fields.Many2One(
string='Dividend category',
model_name='cashbook.category', ondelete='RESTRICT',
help='Category for dividend paid out.')
gainloss_book = fields.Many2One(string='Profit/Loss Cashbook',
gainloss_book = fields.Many2One(
string='Profit/Loss Cashbook',
model_name='cashbook.book', ondelete='RESTRICT',
help='Profit and loss on sale of assets are recorded in the cash book.')

362
book.py
View file

@ -22,22 +22,27 @@ from .asset import CACHEKEY_ASSETRATE
# enable/disable caching of cachekey for 'currency.rate'
if config.get('cashbook', 'cache_currency', default='yes').lower() in ['yes', '1', 'true']:
if config.get(
'cashbook', 'cache_currency',
default='yes').lower() in ['yes', '1', 'true']:
ENA_CURRKEY = True
else :
else:
ENA_CURRKEY = False
# enable/disable caching of cachekey for 'currency.rate'
if config.get('cashbook', 'cache_asset', default='yes').lower() in ['yes', '1', 'true']:
if config.get(
'cashbook', 'cache_asset',
default='yes').lower() in ['yes', '1', 'true']:
ENA_ASSETKEY = True
else :
else:
ENA_ASSETKEY = False
class Book(SymbolMixin, metaclass=PoolMeta):
__name__ = 'cashbook.book'
asset = fields.Many2One(string='Asset', select=True,
asset = fields.Many2One(
string='Asset', select=True,
model_name='investment.asset', ondelete='RESTRICT',
states={
'required': Eval('feature', '') == 'asset',
@ -47,8 +52,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
Len(Eval('lines')) > 0,
),
}, depends=DEPENDS2+['feature', 'lines'])
quantity_digits = fields.Integer(string='Digits',
help='Quantity Digits',
quantity_digits = fields.Integer(
string='Digits', help='Quantity Digits',
domain=[
('quantity_digits', '>=', 0),
('quantity_digits', '<=', 6),
@ -61,10 +66,12 @@ class Book(SymbolMixin, metaclass=PoolMeta):
Len(Eval('lines')) > 0,
),
}, depends=DEPENDS2+['feature', 'lines'])
asset_uomcat = fields.Function(fields.Many2One(string='UOM Category',
readonly=True, model_name='product.uom.category',
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', select=True,
quantity_uom = fields.Many2One(
string='UOM', select=True,
model_name='product.uom', ondelete='RESTRICT',
domain=[
('category.id', '=', Eval('asset_uomcat', -1)),
@ -77,34 +84,38 @@ class Book(SymbolMixin, metaclass=PoolMeta):
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'),
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)),
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)),
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.',
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('show_performance', False) == False,
}, depends=['currency_digits', 'show_performance']),
'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.',
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(
@ -115,27 +126,35 @@ class Book(SymbolMixin, metaclass=PoolMeta):
'get_asset_quantity')
# performance
diff_amount = fields.Function(fields.Numeric(string='Difference',
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('show_performance', False) == False,
}, depends=['currency_digits', 'show_performance']), 'get_asset_quantity')
diff_percent = fields.Function(fields.Numeric(string='Percent',
}, depends=['currency_digits', 'show_performance']),
'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('show_performance', False) == False,
}, 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',
help='Rate per unit of investment based on current stock exchange price.',
}, 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',
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')
purchase_amount = fields.Function(fields.Numeric(string='Purchase Amount',
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={
@ -144,43 +163,47 @@ class Book(SymbolMixin, metaclass=PoolMeta):
'get_asset_quantity')
# yield
yield_dividend_total = fields.Function(fields.Numeric(string='Dividend',
help='Total dividends received',
yield_dividend_total = fields.Function(fields.Numeric(
string='Dividend', help='Total dividends received',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), 'get_yield_data')
yield_dividend_12m = fields.Function(fields.Numeric(string='Dividend 1y',
yield_dividend_12m = fields.Function(fields.Numeric(
string='Dividend 1y',
help='Dividends received in the last twelve months',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), 'get_yield_data')
yield_fee_total = fields.Function(fields.Numeric(string='Trade Fee',
help='Total trade fees payed',
yield_fee_total = fields.Function(fields.Numeric(
string='Trade Fee', help='Total trade fees payed',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), 'get_yield_data')
yield_fee_12m = fields.Function(fields.Numeric(string='Trade Fee 1y',
yield_fee_12m = fields.Function(fields.Numeric(
string='Trade Fee 1y',
help='Trade fees payed in the last twelve month',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), 'get_yield_data')
yield_sales = fields.Function(fields.Numeric(string='Sales',
help='Total profit or loss on sale of shares.',
yield_sales = fields.Function(fields.Numeric(
string='Sales', help='Total profit or loss on sale of shares.',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), 'get_yield_data')
yield_sales_12m = fields.Function(fields.Numeric(string='Sales 1y',
yield_sales_12m = fields.Function(fields.Numeric(
string='Sales 1y',
help='Total profit or loss on sale of shares in the last twelve month.',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), 'get_yield_data')
yield_balance = fields.Function(fields.Numeric(string='Total Yield',
yield_balance = fields.Function(fields.Numeric(
string='Total Yield',
help='Total income: price gain + dividends + sales gains - fees',
readonly=True, digits=(16, Eval('currency_digits', 2)),
states={
@ -194,9 +217,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
('/tree', 'visual',
If(Eval('show_performance', False) == True,
If(Eval('diff_percent', 0) < 0, 'danger',
If(Eval('diff_percent', 0) > 0, 'success', '')
), '')
),
If(Eval('diff_percent', 0) > 0,
'success', '')), '')),
]
def get_rec_name(self, name):
@ -205,7 +227,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
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,
'quantity': Report.format_number(
self.quantity or 0.0, None,
digits=self.quantity_digits),
'uom_symbol': getattr(self.quantity_uom, 'symbol', '-'),
}
@ -229,7 +252,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
""" calculate yield total
fee is already contained in 'diff_amount'
"""
sum_lst = [self.diff_amount, self.yield_dividend_total, self.yield_sales]
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
@ -252,14 +276,18 @@ class Book(SymbolMixin, metaclass=PoolMeta):
if date_to:
where &= tab_line.date <= date_to
query = tab_book.join(tab_line,
condition=tab_line.cashbook==tab_book.id,
).join(tab_cur,
condition=tab_cur.id==tab_book.currency,
).join(tab_line_yield,
condition=tab_line_yield.id==tab_line.id,
).join(tab_line_gainloss,
condition=tab_line_gainloss.id==tab_line.id,
query = tab_book.join(
tab_line,
condition=tab_line.cashbook == tab_book.id,
).join(
tab_cur,
condition=tab_cur.id == tab_book.currency,
).join(
tab_line_yield,
condition=tab_line_yield.id == tab_line.id,
).join(
tab_line_gainloss,
condition=tab_line_gainloss.id == tab_line.id,
).select(
tab_book.id,
Sum(tab_line_yield.fee).as_('fee'),
@ -276,16 +304,16 @@ class Book(SymbolMixin, metaclass=PoolMeta):
""" collect yield data
"""
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 ['yield_fee_total', 'yield_dividend_total',
'yield_sales', 'yield_fee_12m', 'yield_dividend_12m',
'yield_sales_12m']}
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, digits):
""" quantize...
@ -297,21 +325,23 @@ class Book(SymbolMixin, metaclass=PoolMeta):
query_date = context.get('date', IrDate.today())
cache_keys = {
x.id: MemCache.get_key_by_record(
name = 'get_yield_data',
record = x,
query = [{
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' if ENA_CURRKEY else 'disabled': CACHEKEY_CURRENCY % x.currency.id,
'cachekey' if ENA_CURRKEY else 'disabled':
CACHEKEY_CURRENCY % x.currency.id,
}, {
'model': 'investment.rate',
'query': [('asset.id', '=', x.asset.id)],
'cachekey' if ENA_ASSETKEY else 'disabled': CACHEKEY_ASSETRATE % x.asset.id,
'cachekey' if ENA_ASSETKEY else 'disabled':
CACHEKEY_ASSETRATE % x.asset.id,
} if x.asset is not None else {}],
addkeys = [query_date.isoformat()])
addkeys=[query_date.isoformat()])
for x in cashbooks
}
@ -332,26 +362,32 @@ class Book(SymbolMixin, metaclass=PoolMeta):
# results for 12 months
(tab_book2, query_12m) = cls.get_yield_data_sql(
date_to = query_date,
date_from = query_date - timedelta(days=365),
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()
for record in records_total:
result['yield_fee_total'][record[0]] = quantize_val(record[1], record[4])
result['yield_dividend_total'][record[0]] = quantize_val(record[2], record[4])
result['yield_sales'][record[0]] = quantize_val(record[3], record[4])
result['yield_fee_total'][record[0]] = quantize_val(
record[1], record[4])
result['yield_dividend_total'][record[0]] = quantize_val(
record[2], record[4])
result['yield_sales'][record[0]] = quantize_val(
record[3], record[4])
for record in records_12m:
result['yield_fee_12m'][record[0]] = quantize_val(record[1], record[4])
result['yield_dividend_12m'][record[0]] = quantize_val(record[2], record[4])
result['yield_sales_12m'][record[0]] = quantize_val(record[3], record[4])
result['yield_fee_12m'][record[0]] = quantize_val(
record[1], record[4])
result['yield_dividend_12m'][record[0]] = quantize_val(
record[2], record[4])
result['yield_sales_12m'][record[0]] = quantize_val(
record[3], record[4])
# store to cache
MemCache.store_result(cashbooks, cache_keys, result, todo_cashbook)
return {x:result[x] for x in names}
return {x: result[x] for x in names}
@classmethod
def get_asset_quantity_sql(cls):
@ -374,40 +410,50 @@ class Book(SymbolMixin, metaclass=PoolMeta):
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(query_yield,
condition=query_yield.id==tab_line.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',
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(
query_yield,
condition=query_yield.id == tab_line.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'),
else_=Decimal('0.0'),
)), Decimal('0.0')).as_('quantity'), # 1
Sum(tab_line.quantity_credit - tab_line.quantity_debit).as_('quantity_all'), # 2
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_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
tab_asset.currency.as_('asset_currency'), # 8
(
Sum(query_yield.fee) + tab_balance.balance
).as_('purchase_amount'), #9
group_by=[tab_book.id, tab_rate.rate,
).as_('purchase_amount'), # 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],
@ -434,28 +480,31 @@ class Book(SymbolMixin, metaclass=PoolMeta):
company_currency = CBook.default_currency()
result = {
x:{y.id: None for y in cashbooks}
for x in ['quantity', 'quantity_all', 'current_value',
'current_value_ref', 'diff_amount', 'diff_percent',
'current_rate', 'purchase_amount', 'purchase_amount_ref',
'digits']
x: {y.id: None for y in cashbooks}
for x in [
'quantity', 'quantity_all', 'current_value',
'current_value_ref', 'diff_amount', 'diff_percent',
'current_rate', 'purchase_amount', 'purchase_amount_ref',
'digits']
}
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])],
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)],
'cachekey' if ENA_CURRKEY else 'disabled': CACHEKEY_CURRENCY % x.currency.id,
'model': 'currency.currency.rate',
'query': [('currency.id', '=', x.currency.id)],
'cachekey' if ENA_CURRKEY else 'disabled':
CACHEKEY_CURRENCY % x.currency.id,
}, {
'model': 'investment.rate',
'query': [('asset.id', '=', x.asset.id)],
'cachekey' if ENA_ASSETKEY else 'disabled': CACHEKEY_ASSETRATE % x.asset.id,
'model': 'investment.rate',
'query': [('asset.id', '=', x.asset.id)],
'cachekey' if ENA_ASSETKEY else 'disabled':
CACHEKEY_ASSETRATE % x.asset.id,
} if x.asset is not None else {}],
addkeys=[
str(company_currency),
@ -476,9 +525,11 @@ class Book(SymbolMixin, metaclass=PoolMeta):
# uom-factor
if rdata[6] == rdata[7]:
uom_factor = Decimal('1.0')
else :
else:
uom_factor = Decimal(
Uom.compute_qty(Uom(rdata[6]), 1.0, Uom(rdata[7]), round=False)
Uom.compute_qty(
Uom(rdata[6]), 1.0,
Uom(rdata[7]), round=False)
)
current_value = Currency.compute(
@ -493,23 +544,26 @@ class Book(SymbolMixin, metaclass=PoolMeta):
'current_value_ref': Currency.compute(
rdata[8],
rdata[3] * rdata[1] / uom_factor,
company_currency if company_currency is not None else rdata[8],
company_currency
if company_currency is not None else rdata[8],
),
'diff_amount': current_value - rdata[9],
'diff_percent': (
Decimal('100.0') * current_value / \
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,
).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,
'purchase_amount': record[9].quantize(Decimal(str(1/10**rdata[5]))),
).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],
company_currency
if company_currency is not None else rdata[4],
),
})
@ -529,7 +583,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
result[name][book_id] = values[name]
# add aggregated values of cashbooks without type
aggr_names = ['current_value', 'current_value_ref',
aggr_names = [
'current_value', 'current_value_ref',
'diff_amount', 'diff_percent']
queried_names = list(set(aggr_names).intersection(set(names)))
@ -539,27 +594,30 @@ class Book(SymbolMixin, metaclass=PoolMeta):
(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),
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
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,
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],
tab_quantity.asset_currency, tab_book.currency],
)
cursor.execute(*query)
records = cursor.fetchall()
@ -567,7 +625,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
for record in records:
(book_id, values) = values_from_record(record)
for name in ['current_value', 'diff_amount',
for name in [
'current_value', 'diff_amount',
'current_value_ref', 'purchase_amount_ref']:
if result[name][book_id] is None:
result[name][book_id] = Decimal('0.0')
@ -575,14 +634,17 @@ class Book(SymbolMixin, metaclass=PoolMeta):
value = Decimal('0.0')
if name == 'current_value':
value = Currency.compute(
company_currency if company_currency is not None else record[4],
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'],
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']:
@ -596,7 +658,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
p_amount = result['purchase_amount_ref'][id_book]
digits = result['digits'][id_book]
if (p_amount == Decimal('0.0')) or (p_amount is None) or (c_val is None):
if (p_amount == Decimal('0.0')) or \
(p_amount is None) or (c_val is None):
continue
result['diff_percent'][id_book] = (
@ -606,7 +669,7 @@ class Book(SymbolMixin, metaclass=PoolMeta):
# store to cache
MemCache.store_result(cashbooks, cache_keys, result, todo_cashbook)
return {x:result[x] for x in names}
return {x: result[x] for x in names}
@fields.depends('id')
def on_change_with_show_performance(self, name=None):
@ -616,9 +679,8 @@ class Book(SymbolMixin, metaclass=PoolMeta):
Book2 = Pool().get('cashbook.book')
if Book2.search_count([
('btype.feature', '=', 'asset'),
('parent', 'child_of', [self.id]),
]) > 0:
('btype.feature', '=', 'asset'),
('parent', 'child_of', [self.id])]) > 0:
return True
return False

245
line.py
View file

@ -85,7 +85,8 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
quantity_balance = fields.Function(fields.Numeric(
string='Quantity',
digits=(16, Eval('quantity_digits', 4)), readonly=True,
help='Number of shares in the cashbook up to the current row if the default sort applies.',
help='Number of shares in the cashbook up to the current ' +
'row if the default sort applies.',
states={
'invisible': Eval('feature', '') != 'asset',
}, depends=['quantity_digits', 'feature']),
@ -97,7 +98,8 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
# performance
current_value = fields.Function(fields.Numeric(
string='Value',
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)),
states={
'invisible': Eval('feature', '') != 'asset',
@ -154,60 +156,69 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
SplitLine = pool.get('cashbook.split')
tab_line = cls.__table__()
tab_mvsp_counterpart = cls.__table__()
tab_mvsp_local = cls.__table__()
tab_mvmv_counterpart = cls.__table__()
tab_spmv_counterpart = cls.__table__()
tab_mv_spline = SplitLine.__table__()
cfg1 = AssetSetting.get_singleton()
gainloss_book = getattr(getattr(cfg1, 'gainloss_book', None), 'id', None)
gainloss_book = getattr(getattr(
cfg1, 'gainloss_book', None), 'id', None)
tab_assetline = cls.search([
('cashbook.btype.feature', '=', 'asset'),
], query=True)
query = tab_line.join(tab_assetline,
condition=(tab_assetline.id==tab_line.id),
query = tab_line.join(
tab_assetline,
condition=(tab_assetline.id == tab_line.id),
).join(tab_mvsp_counterpart,
# [MV-SP] transfer booking, select counterpart [1] - a split-booking
).join(
tab_mvsp_counterpart,
# [MV-SP] transfer booking,
# select counterpart [1] - a split-booking
condition=tab_line.bookingtype.in_(['mvin', 'mvout']) & \
((tab_line.reference == tab_mvsp_counterpart.id) | \
(tab_line.id == tab_mvsp_counterpart.reference)) & \
(tab_mvsp_counterpart.bookingtype.in_(['spin', 'spout'])),
type_ = 'LEFT OUTER',
).join(tab_mv_spline,
# [MV-SP] line is linked to split-booking-line of counterpart [1]
((tab_line.reference == tab_mvsp_counterpart.id) |
(tab_line.id == tab_mvsp_counterpart.reference)) &
(tab_mvsp_counterpart.bookingtype.in_(['spin', 'spout'])),
type_='LEFT OUTER',
).join(
tab_mv_spline,
# [MV-SP] line is linked to split-booking-line
# of counterpart [1]
condition=(tab_mv_spline.line == tab_mvsp_counterpart.id) & \
(tab_mv_spline.splittype == 'tr') & \
(tab_mv_spline.booktransf != None) & \
(tab_mv_spline.booktransf == gainloss_book),
type_ = 'LEFT OUTER',
(tab_mv_spline.splittype == 'tr') & \
(tab_mv_spline.booktransf != None) & \
(tab_mv_spline.booktransf == gainloss_book),
type_='LEFT OUTER',
).join(tab_spmv_counterpart,
# [SP-MV] split booking, select counterpart [1] - a transfer-booking
).join(
tab_spmv_counterpart,
# [SP-MV] split booking, select counterpart [1]
# - a transfer-booking
condition=tab_line.bookingtype.in_(['spin', 'spout']) & \
((tab_line.reference == tab_spmv_counterpart.id) | \
(tab_line.id == tab_spmv_counterpart.reference)) & \
tab_spmv_counterpart.bookingtype.in_(['mvin', 'mvout']) & \
(tab_spmv_counterpart.cashbook == gainloss_book),
type_ = 'LEFT OUTER',
((tab_line.reference == tab_spmv_counterpart.id) | \
(tab_line.id == tab_spmv_counterpart.reference)) & \
tab_spmv_counterpart.bookingtype.in_(['mvin', 'mvout']) & \
(tab_spmv_counterpart.cashbook == gainloss_book),
type_='LEFT OUTER',
).join(tab_mvmv_counterpart,
).join(
tab_mvmv_counterpart,
# [MV-MV] transfer booking
condition=tab_line.bookingtype.in_(['mvin', 'mvout']) & \
((tab_mvmv_counterpart.reference == tab_line.id) | \
(tab_mvmv_counterpart.id == tab_line.reference)) & \
tab_mvmv_counterpart.bookingtype.in_(['mvin', 'mvout']) & \
(tab_mvmv_counterpart.cashbook == gainloss_book),
type_ = 'LEFT OUTER',
((tab_mvmv_counterpart.reference == tab_line.id) | \
(tab_mvmv_counterpart.id == tab_line.reference)) & \
tab_mvmv_counterpart.bookingtype.in_(['mvin', 'mvout']) & \
(tab_mvmv_counterpart.cashbook == gainloss_book),
type_='LEFT OUTER',
).select(
tab_line.id,
(Coalesce(
tab_mvmv_counterpart.credit - tab_mvmv_counterpart.debit,
Case(
(tab_line.bookingtype == 'mvin', tab_mv_spline.amount),
(tab_line.bookingtype == 'mvout', tab_mv_spline.amount * Decimal('-1.0')),
(tab_line.bookingtype == 'mvout',
tab_mv_spline.amount * Decimal('-1.0')),
),
Case(
(tab_mvsp_counterpart.cashbook == gainloss_book,
@ -238,67 +249,79 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
cfg1 = AssetSetting.get_singleton()
fee_category = getattr(getattr(cfg1, 'fee_category', None), 'id', None)
dividend_category = getattr(getattr(cfg1, 'dividend_category', None), 'id', None)
dividend_category = getattr(getattr(
cfg1, 'dividend_category', None), 'id', None)
tab_assetline = cls.search([
('cashbook.btype.feature', '=', 'asset'),
], query=True)
query = tab_line.join(tab_assetline,
condition=(tab_assetline.id==tab_line.id),
).join(tab_inout_fee,
query = tab_line.join(
tab_assetline,
condition=(tab_assetline.id == tab_line.id),
).join(
tab_inout_fee,
# [INOUT] fee, local booked
condition=(tab_inout_fee.id==tab_line.id) & \
tab_inout_fee.bookingtype.in_(['in', 'out']) & \
(tab_inout_fee.category != None) & \
(tab_inout_fee.category == fee_category),
type_ = 'LEFT OUTER',
).join(tab_inout_divi,
condition=(tab_inout_fee.id == tab_line.id) & \
tab_inout_fee.bookingtype.in_(['in', 'out']) & \
(tab_inout_fee.category != None) & \
(tab_inout_fee.category == fee_category),
type_='LEFT OUTER',
).join(
tab_inout_divi,
# [INOUT] dividend, local booked
condition=(tab_inout_divi.id==tab_line.id) & \
tab_inout_divi.bookingtype.in_(['in', 'out']) & \
(tab_inout_divi.category != None) & \
(tab_inout_divi.category == dividend_category),
type_ = 'LEFT OUTER',
condition=(tab_inout_divi.id == tab_line.id) & \
tab_inout_divi.bookingtype.in_(['in', 'out']) & \
(tab_inout_divi.category != None) & \
(tab_inout_divi.category == dividend_category),
type_='LEFT OUTER',
).join(tab_mv_counterpart,
# [MV] transfer booking, select counterpart [1] - a split-booking
).join(
tab_mv_counterpart,
# [MV] transfer booking, select counterpart [1]
# - a split-booking
condition=tab_line.bookingtype.in_(['mvin', 'mvout']) & \
((tab_line.reference == tab_mv_counterpart.id) | \
(tab_line.id == tab_mv_counterpart.reference)) & \
(tab_mv_counterpart.bookingtype.in_(['spin', 'spout'])),
type_ = 'LEFT OUTER',
).join(tab_mv_spline_fee,
# [MV] fee-line is linked to split-booking-line of counterpart [1]
((tab_line.reference == tab_mv_counterpart.id) | \
(tab_line.id == tab_mv_counterpart.reference)) & \
(tab_mv_counterpart.bookingtype.in_(['spin', 'spout'])),
type_='LEFT OUTER',
).join(
tab_mv_spline_fee,
# [MV] fee-line is linked to split-booking-line
# of counterpart [1]
condition=(tab_mv_spline_fee.line == tab_mv_counterpart.id) & \
(tab_mv_spline_fee.splittype == 'cat') & \
(tab_mv_spline_fee.category != None) & \
(tab_mv_spline_fee.category == fee_category),
type_ = 'LEFT OUTER',
).join(tab_mv_spline_divi,
# [MV] dividend-line is linked to split-booking-line of counterpart [1]
(tab_mv_spline_fee.splittype == 'cat') & \
(tab_mv_spline_fee.category != None) & \
(tab_mv_spline_fee.category == fee_category),
type_='LEFT OUTER',
).join(
tab_mv_spline_divi,
# [MV] dividend-line is linked to split-booking-line
# of counterpart [1]
condition=(tab_mv_spline_divi.line == tab_mv_counterpart.id) & \
(tab_mv_spline_divi.splittype == 'cat') & \
(tab_mv_spline_divi.category != None) & \
(tab_mv_spline_divi.category == dividend_category),
type_ = 'LEFT OUTER',
(tab_mv_spline_divi.splittype == 'cat') & \
(tab_mv_spline_divi.category != None) & \
(tab_mv_spline_divi.category == dividend_category),
type_='LEFT OUTER',
).join(tab_spline_fee,
).join(
tab_spline_fee,
# [SP] fee, split booking
condition=(tab_spline_fee.line == tab_line.id) & \
tab_line.bookingtype.in_(['spin', 'spout']) & \
(tab_spline_fee.splittype == 'cat') & \
(tab_spline_fee.category != None) & \
(tab_spline_fee.category == fee_category),
type_ = 'LEFT OUTER',
).join(tab_spline_divi,
tab_line.bookingtype.in_(['spin', 'spout']) & \
(tab_spline_fee.splittype == 'cat') & \
(tab_spline_fee.category != None) & \
(tab_spline_fee.category == fee_category),
type_='LEFT OUTER',
).join(
tab_spline_divi,
# [SP] dividend, split booking
condition=(tab_spline_divi.line == tab_line.id) & \
tab_line.bookingtype.in_(['spin', 'spout']) & \
(tab_spline_divi.splittype == 'cat') & \
(tab_spline_divi.category != None) & \
(tab_spline_divi.category == dividend_category),
type_ = 'LEFT OUTER',
tab_line.bookingtype.in_(['spin', 'spout']) & \
(tab_spline_divi.splittype == 'cat') & \
(tab_spline_divi.category != None) & \
(tab_spline_divi.category == dividend_category),
type_='LEFT OUTER',
).select(
tab_line.id,
Sum(Coalesce(
@ -308,8 +331,10 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
# transfer = fee is positive
tab_mv_spline_fee.amount,
Case(
(tab_line.bookingtype == 'spin', tab_spline_fee.amount * Decimal('-1.0')),
(tab_line.bookingtype == 'spout', tab_spline_fee.amount),
(tab_line.bookingtype == 'spin',
tab_spline_fee.amount * Decimal('-1.0')),
(tab_line.bookingtype == 'spout',
tab_spline_fee.amount),
),
Decimal('0.0'),
)).as_('fee'),
@ -317,8 +342,10 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
tab_inout_divi.credit - tab_inout_divi.debit,
tab_mv_spline_divi.amount,
Case(
(tab_line.bookingtype == 'spin', tab_spline_divi.amount),
(tab_line.bookingtype == 'spout', tab_spline_divi.amount * Decimal('-1.0')),
(tab_line.bookingtype == 'spin',
tab_spline_divi.amount),
(tab_line.bookingtype == 'spout',
tab_spline_divi.amount * Decimal('-1.0')),
),
Decimal('0.0'),
)).as_('dividend'),
@ -380,7 +407,7 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
value or Decimal('0.0')
).quantize(Decimal(str(1/10**line.currency_digits)))
result = {x:{y.id: None for y in lines} for x in names}
result = {x: {y.id: None for y in lines} for x in names}
# read fee, dividend
name_set = set({'trade_fee', 'asset_dividend'}).intersection(set(names))
@ -408,7 +435,8 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
for record in records:
line = Line2(record[0])
result['asset_gainloss'][record[0]] = quantize_val(record[1], line)
result['asset_gainloss'][record[0]] = quantize_val(
record[1], line)
return result
def get_rec_name(self, name):
@ -416,10 +444,13 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
"""
recname = super(Line, self).get_rec_name(name)
if self.cashbook.feature == 'asset':
credit = self.quantity_credit if self.quantity_credit is not None else Decimal('0.0')
debit = self.quantity_debit if self.quantity_debit is not None else Decimal('0.0')
credit = self.quantity_credit \
if self.quantity_credit is not None else Decimal('0.0')
debit = self.quantity_debit \
if self.quantity_debit is not None else Decimal('0.0')
recname += '|%(quantity)s %(uom_symbol)s' % {
'quantity': Report.format_number(credit - debit,
'quantity': Report.format_number(
credit - debit,
lang=None, digits=self.quantity_digits),
'uom_symbol': getattr(self.quantity_uom, 'symbol', '-'),
}
@ -453,10 +484,12 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
cashbook = Cashbook.browse([id_cashbook])[0]
if isinstance(values, dict):
type_ = values.get('bookingtype', getattr(line, 'bookingtype', None))
type_ = values.get(
'bookingtype', getattr(line, 'bookingtype', None))
quantity = values.get('quantity', None)
else :
type_ = getattr(values, 'bookingtype', getattr(line, 'bookingtype', None))
else:
type_ = getattr(
values, 'bookingtype', getattr(line, 'bookingtype', None))
quantity = getattr(values, 'quantity', None)
if (type_ is not None) and (cashbook.feature == 'asset'):
@ -471,7 +504,7 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
'quantity_debit': quantity,
'quantity_credit': Decimal('0.0'),
})
else :
else:
raise ValueError('invalid "bookingtype"')
return result
@ -482,31 +515,35 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
"""
result = super(Line, cls).get_counterpart_values(
line,
splitline = splitline,
values = values
splitline=splitline,
values=values
)
line_uom = getattr(line.quantity_uom, 'id', None)
booktransf_uom = getattr(getattr(line.booktransf, 'quantity_uom', {}), 'id', None)
booktransf_uom = getattr(getattr(
line.booktransf, 'quantity_uom', {}), 'id', None)
if getattr(splitline, 'quantity', None) is not None:
# we add values to the counterpart of a splitbooking-line
asset_books = sum([
1 if splitline.feature == 'asset' else 0,
1 if getattr(splitline.booktransf, 'feature', '-') == 'asset' else 0,
1 if getattr(
splitline.booktransf, 'feature', '-') == 'asset' else 0,
])
diff_uom = False
if asset_books == 2:
diff_uom = (splitline.quantity_uom != splitline.booktransf.quantity_uom) and \
(splitline.quantity_uom is not None) and \
(splitline.booktransf.quantity_uom is not None)
diff_uom = (
splitline.quantity_uom !=
splitline.booktransf.quantity_uom) and \
(splitline.quantity_uom is not None) and \
(splitline.booktransf.quantity_uom is not None)
result.update({
'quantity': splitline.quantity_2nd_uom
if (asset_books == 2) and (diff_uom is True)
else splitline.quantity,
if (asset_books == 2) and (diff_uom is True)
else splitline.quantity,
'quantity_2nd_uom': splitline.quantity
if (asset_books == 2) and (diff_uom is True) else None,
if (asset_books == 2) and (diff_uom is True) else None,
})
elif sum([1 if booktransf_uom is not None else 0,
1 if line_uom is not None else 0]) == 1:
@ -565,7 +602,8 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
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 -
return (
self.quantity * self.cashbook.current_rate -
self.amount).quantize(
Decimal(str(1/10**self.currency_digits)))
@ -579,8 +617,9 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
(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')
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')

View file

@ -6,7 +6,6 @@
from trytond.model import fields
from trytond.pyson import Eval, Bool, Or
from trytond.pool import Pool
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from trytond.i18n import gettext
from trytond.modules.product.uom import uom_conversion_digits
@ -22,7 +21,8 @@ DEPENDSQ.extend(DEPENDS)
class SecondUomMixin(object):
""" two fields for second uom: quantity, rate
"""
quantity_2nd_uom = fields.Numeric(string='Quantity Second UOM',
quantity_2nd_uom = fields.Numeric(
string='Quantity Second UOM',
digits=(16, Eval('quantity2nd_digits', 4)),
states={
'readonly': Or(
@ -32,8 +32,10 @@ class SecondUomMixin(object):
'required': Bool(Eval('quantity2nd')),
'invisible': ~Bool(Eval('quantity2nd')),
}, depends=DEPENDSQ+['quantity2nd_digits', 'quantity2nd'])
factor_2nd_uom = fields.Function(fields.Numeric(string='Conversion factor',
help='Conversion factor between the units of the participating cash books.',
factor_2nd_uom = fields.Function(fields.Numeric(
string='Conversion factor',
help='Conversion factor between the units of the ' +
'participating cash books.',
digits=uom_conversion_digits,
states={
'readonly': Or(
@ -45,15 +47,18 @@ class SecondUomMixin(object):
}, depends=DEPENDSQ+['quantity2nd_digits', 'quantity2nd']),
'on_change_with_factor_2nd_uom', setter='set_factor_2nd_uom')
quantity2nd = fields.Function(fields.Many2One(model_name='product.uom',
quantity2nd = fields.Function(fields.Many2One(
model_name='product.uom',
string="2nd UOM", readonly=True), 'on_change_with_quantity2nd')
quantity2nd_digits = fields.Function(fields.Integer(string='2nd UOM Digits',
readonly=True), 'on_change_with_quantity2nd_digits')
quantity2nd_digits = fields.Function(fields.Integer(
string='2nd UOM Digits', readonly=True),
'on_change_with_quantity2nd_digits')
def quantize_quantity(self, value):
""" quantize for line-quantity
"""
return Decimal(value).quantize(Decimal(Decimal(1) / 10 ** self.quantity_digits))
return Decimal(value).quantize(
Decimal(Decimal(1) / 10 ** self.quantity_digits))
@classmethod
def add_2nd_quantity(cls, values, from_uom):
@ -68,17 +73,18 @@ class SecondUomMixin(object):
quantity_2nd_uom = values.get('quantity_2nd_uom', None)
if (quantity is not None) and (booktransf is not None) and \
(from_uom is not None):
(from_uom is not None):
if quantity_2nd_uom is None:
booktransf = Cashbook(booktransf)
if booktransf.quantity_uom:
if from_uom.id != booktransf.quantity_uom.id:
# deny impossible transfer
if from_uom.category.id != booktransf.quantity_uom.category.id:
if from_uom.category.id != \
booktransf.quantity_uom.category.id:
raise UserError(gettext(
'cashbook_investment.msg_uomcat_mismatch',
cat1 = from_uom.category.rec_name,
cat2 = booktransf.quantity_uom.category.rec_name,
cat1=from_uom.category.rec_name,
cat2=booktransf.quantity_uom.category.rec_name,
))
values['quantity_2nd_uom'] = Decimal(UOM.compute_qty(
@ -87,7 +93,8 @@ class SecondUomMixin(object):
booktransf.quantity_uom,
round=False,
)).quantize(Decimal(
Decimal(1) / 10 ** booktransf.quantity_digits)
Decimal(1) /
10 ** booktransf.quantity_digits)
)
return values
@ -106,7 +113,7 @@ class SecondUomMixin(object):
if line.booktransf is None:
continue
if (line.cashbook.quantity_uom is None) or \
(line.booktransf.quantity_uom is None):
(line.booktransf.quantity_uom is None):
continue
if line.cashbook.quantity_uom.id == line.booktransf.quantity_uom.id:
@ -124,24 +131,27 @@ class SecondUomMixin(object):
if len(to_write) > 0:
Line2.write(*to_write)
@fields.depends('booktransf', '_parent_booktransf.quantity_uom', \
'quantity_uom', 'quantity_digits', 'quantity', \
@fields.depends(
'booktransf', '_parent_booktransf.quantity_uom',
'quantity_uom', 'quantity_digits', 'quantity',
'quantity_2nd_uom', 'factor_2nd_uom')
def on_change_booktransf(self):
""" update quantity_2nd_uom
"""
self.on_change_factor_2nd_uom()
@fields.depends('booktransf', '_parent_booktransf.quantity_uom', \
'quantity_uom', 'quantity_digits', 'quantity', \
@fields.depends(
'booktransf', '_parent_booktransf.quantity_uom',
'quantity_uom', 'quantity_digits', 'quantity',
'quantity_2nd_uom', 'factor_2nd_uom')
def on_change_quantity(self):
""" update quantity_2nd_uom
"""
self.on_change_factor_2nd_uom()
@fields.depends('booktransf', '_parent_booktransf.quantity_uom', \
'quantity_uom', 'quantity_digits', 'quantity', \
@fields.depends(
'booktransf', '_parent_booktransf.quantity_uom',
'quantity_uom', 'quantity_digits', 'quantity',
'quantity_2nd_uom', 'factor_2nd_uom')
def on_change_factor_2nd_uom(self):
""" update quantity_2nd_uom + factor_2nd_uom
@ -153,7 +163,7 @@ class SecondUomMixin(object):
self.factor_2nd_uom = None
return
if (self.booktransf.quantity_uom is None) or \
(self.quantity_uom is None):
(self.quantity_uom is None):
return
if self.factor_2nd_uom is None:
@ -168,8 +178,9 @@ class SecondUomMixin(object):
if self.quantity != Decimal('0.0'):
self.factor_2nd_uom = (
self.quantity_2nd_uom / self.quantity
).quantize(Decimal(Decimal(1) / 10 ** uom_conversion_digits[1]))
else :
).quantize(Decimal(
Decimal(1) / 10 ** uom_conversion_digits[1]))
else:
self.quantity_2nd_uom = self.quantize_quantity(
self.quantity * self.factor_2nd_uom)
@ -188,7 +199,8 @@ class SecondUomMixin(object):
exp = Decimal(Decimal(1) / 10 ** uom_conversion_digits[1])
return (self.quantity_2nd_uom / self.quantity).quantize(exp)
@fields.depends('booktransf', '_parent_booktransf.quantity_uom', 'quantity_uom')
@fields.depends(
'booktransf', '_parent_booktransf.quantity_uom', 'quantity_uom')
def on_change_with_quantity2nd(self, name=None):
""" uom of transfer-target
"""
@ -196,7 +208,7 @@ class SecondUomMixin(object):
if self.quantity_uom:
if self.booktransf.quantity_uom:
if self.quantity_uom.id != \
self.booktransf.quantity_uom.id:
self.booktransf.quantity_uom.id:
return self.booktransf.quantity_uom.id
@fields.depends('booktransf', '_parent_booktransf.quantity_digits')

View file

@ -14,22 +14,24 @@ from decimal import Decimal
class Reconciliation(metaclass=PoolMeta):
__name__ = 'cashbook.recon'
start_quantity = fields.Numeric(string='Start Quantity',
readonly=True, digits=(16, Eval('quantity_digits', 4)),
start_quantity = fields.Numeric(
string='Start Quantity', readonly=True,
digits=(16, Eval('quantity_digits', 4)),
states={
'required': Eval('feature', '') == 'asset',
'invisible': Eval('feature', '') != 'asset',
}, depends=['quantity_digits', 'feature'])
end_quantity = fields.Numeric(string='End Quantity',
readonly=True, digits=(16, Eval('quantity_digits', 4)),
end_quantity = fields.Numeric(
string='End Quantity', readonly=True,
digits=(16, Eval('quantity_digits', 4)),
states={
'required': Eval('feature', '') == 'asset',
'invisible': Eval('feature', '') != 'asset',
}, depends=['quantity_digits', 'feature'])
quantity_digits = fields.Function(fields.Integer(string='Quantity Digits'),
'on_change_with_quantity_digits')
quantity_uom = fields.Function(fields.Many2One(string='Symbol',
readonly=True, model_name='product.uom'),
quantity_digits = fields.Function(fields.Integer(
string='Quantity Digits'), 'on_change_with_quantity_digits')
quantity_uom = fields.Function(fields.Many2One(
string='Symbol', readonly=True, model_name='product.uom'),
'on_change_with_quantity_uom')
def get_rec_name(self, name):
@ -38,9 +40,11 @@ class Reconciliation(metaclass=PoolMeta):
recname = super(Reconciliation, self).get_rec_name(name)
if self.cashbook.feature == 'asset':
recname += ' | %(start_quantity)s %(uom_symbol)s - %(end_quantity)s %(uom_symbol)s' % {
'start_quantity': Report.format_number(self.start_quantity or 0.0, None,
'start_quantity': Report.format_number(
self.start_quantity or 0.0, None,
digits=self.quantity_digits),
'end_quantity': Report.format_number(self.end_quantity or 0.0, None,
'end_quantity': Report.format_number(
self.end_quantity or 0.0, None,
digits=self.quantity_digits),
'uom_symbol': getattr(self.quantity_uom, 'symbol', '-'),
}
@ -94,7 +98,7 @@ class Reconciliation(metaclass=PoolMeta):
if reconciliation.predecessor:
values['start_quantity'] = reconciliation.predecessor.end_quantity
else :
else:
values['start_quantity'] = Decimal('0.0')
values['end_quantity'] = values['start_quantity']
@ -106,13 +110,13 @@ class Reconciliation(metaclass=PoolMeta):
lines_records = Line.browse(values['lines'][0][1])
values['end_quantity'] += sum([
x.quantity_credit - x.quantity_debit
for x in lines_records
for x in lines_records
])
# add quantities of already linked lines
values['end_quantity'] += sum([
x.quantity_credit - x.quantity_debit
for x in reconciliation.lines
for x in reconciliation.lines
])
return values

View file

@ -2,7 +2,7 @@
"""
# Always prefer setuptools over distutils
from setuptools import setup, find_packages
from setuptools import setup
# To use a consistent encoding
from codecs import open
from os import path
@ -36,7 +36,7 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f:
l2 = i.strip().split(';')
if len(l2) < 4:
continue
modversion[l2[0]] = {'min':l2[1], 'max':l2[2], 'prefix':l2[3]}
modversion[l2[0]] = {'min': l2[1], 'max': l2[2], 'prefix': l2[3]}
# tryton-version
major_version = 6
@ -51,19 +51,22 @@ for dep in info.get('depends', []):
prefix = modversion[dep]['prefix']
if len(modversion[dep]['max']) > 0:
requires.append('%s_%s >= %s, <= %s' %
(prefix, dep, modversion[dep]['min'], modversion[dep]['max']))
else :
requires.append('%s_%s >= %s' %
(prefix, dep, modversion[dep]['min']))
else :
requires.append('%s_%s >= %s.%s, < %s.%s' %
('trytond', dep, major_version, minor_version,
major_version, minor_version + 1))
requires.append('trytond >= %s.%s, < %s.%s' %
(major_version, minor_version, major_version, minor_version + 1))
requires.append('%s_%s >= %s, <= %s' % (
prefix, dep, modversion[dep]['min'],
modversion[dep]['max']))
else:
requires.append('%s_%s >= %s' % (
prefix, dep, modversion[dep]['min']))
else:
requires.append(
'%s_%s >= %s.%s, < %s.%s' % (
'trytond', dep, major_version, minor_version,
major_version, minor_version + 1))
requires.append('trytond >= %s.%s, < %s.%s' % (
major_version, minor_version, major_version, minor_version + 1))
setup(name='%s_%s' % (PREFIX, MODULE),
setup(
name='%s_%s' % (PREFIX, MODULE),
version=info.get('version', '0.0.1'),
description='Tryton module to add investment accounts to cashbook.',
long_description=long_description,
@ -74,21 +77,21 @@ setup(name='%s_%s' % (PREFIX, MODULE),
author_email='service@m-ds.de',
license='GPL-3',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
'Framework :: Tryton',
'Intended Audience :: Developers',
'Intended Audience :: Customer Service',
'Intended Audience :: Information Technology',
'Intended Audience :: Financial and Insurance Industry',
'Topic :: Office/Business',
'Topic :: Office/Business :: Financial :: Accounting',
'Natural Language :: German',
'Natural Language :: English',
'Operating System :: OS Independent',
'License :: OSI Approved :: GNU General Public License (GPL)',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
'Framework :: Tryton',
'Intended Audience :: Developers',
'Intended Audience :: Customer Service',
'Intended Audience :: Information Technology',
'Intended Audience :: Financial and Insurance Industry',
'Topic :: Office/Business',
'Topic :: Office/Business :: Financial :: Accounting',
'Natural Language :: German',
'Natural Language :: English',
'Operating System :: OS Independent',
'License :: OSI Approved :: GNU General Public License (GPL)',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
],
keywords='tryton cashbook investment',
@ -97,7 +100,8 @@ setup(name='%s_%s' % (PREFIX, MODULE),
'trytond.modules.%s' % MODULE,
],
package_data={
'trytond.modules.%s' % MODULE: (info.get('xml', [])
'trytond.modules.%s' % MODULE: (
info.get('xml', [])
+ ['tryton.cfg', 'locale/*.po', 'tests/*.py',
'view/*.xml', 'docs/*.txt',
'versiondep.txt', 'README.rst']),

View file

@ -22,17 +22,18 @@ STATESQ1A['readonly'] = ~And(
Eval('booktransf_feature', '') == 'asset',
))
class SplitLine(SecondUomMixin, metaclass=PoolMeta):
__name__ = 'cashbook.split'
quantity = fields.Numeric(string='Quantity',
digits=(16, Eval('quantity_digits', 4)),
quantity = fields.Numeric(
string='Quantity', digits=(16, Eval('quantity_digits', 4)),
states=STATESQ1A, depends=DEPENDSQ1)
quantity_digits = fields.Function(fields.Integer(string='Digits',
readonly=True, states={'invisible': True}),
quantity_digits = fields.Function(fields.Integer(
string='Digits', readonly=True, states={'invisible': True}),
'on_change_with_quantity_digits')
quantity_uom = fields.Function(fields.Many2One(string='Symbol',
readonly=True, model_name='product.uom'),
quantity_uom = fields.Function(fields.Many2One(
string='Symbol', readonly=True, model_name='product.uom'),
'on_change_with_quantity_uom')
def get_rec_name(self, name):
@ -41,13 +42,15 @@ class SplitLine(SecondUomMixin, metaclass=PoolMeta):
recname = super(SplitLine, self).get_rec_name(name)
if self.line.cashbook.feature == 'asset':
recname += '|%(quantity)s %(uom_symbol)s' % {
'quantity': Report.format_number(self.quantity or 0.0, None,
'quantity': Report.format_number(
self.quantity or 0.0, None,
digits=self.quantity_digits),
'uom_symbol': self.quantity_uom.symbol,
}
return recname
@fields.depends('line', '_parent_line.cashbook', 'booktransf', \
@fields.depends(
'line', '_parent_line.cashbook', 'booktransf',
'_parent_booktransf.feature', '_parent_booktransf.quantity_uom')
def on_change_with_quantity_uom(self, name=None):
""" get quantity-unit of asset
@ -61,7 +64,8 @@ class SplitLine(SecondUomMixin, metaclass=PoolMeta):
if self.booktransf.quantity_uom:
return self.booktransf.quantity_uom.id
@fields.depends('line', '_parent_line.cashbook', 'booktransf', \
@fields.depends(
'line', '_parent_line.cashbook', 'booktransf',
'_parent_booktransf.feature', '_parent_booktransf.quantity_digits')
def on_change_with_quantity_digits(self, name=None):
""" get digits from cashbook
@ -85,7 +89,8 @@ class SplitLine(SecondUomMixin, metaclass=PoolMeta):
line = Line2(values.get('line', None))
if line:
values.update(cls.add_2nd_quantity(values, line.cashbook.quantity_uom))
values.update(cls.add_2nd_quantity(
values, line.cashbook.quantity_uom))
return values
# end SplitLine

View file

@ -4,25 +4,14 @@
import trytond.tests.test_tryton
import unittest
from trytond.modules.cashbook_investment.tests.test_book import CbInvTestCase
from trytond.modules.cashbook_investment.tests.test_reconciliation import ReconTestCase
from trytond.modules.cashbook_investment.tests.test_yield import YieldTestCase
from .test_module import CashbookInvestmentTestCase
__all__ = ['suite']
class CashbookInvestmentTestCase(\
CbInvTestCase,\
ReconTestCase,\
YieldTestCase,\
):
'Test cashbook-investment module'
module = 'cashbook_investment'
# end CashbookInvestmentTestCase
def suite():
suite = trytond.tests.test_tryton.suite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(CashbookInvestmentTestCase))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
CashbookInvestmentTestCase))
return suite

File diff suppressed because it is too large Load diff

View file

@ -3,18 +3,15 @@
# The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms.
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.tests.test_tryton import with_transaction
from trytond.pool import Pool
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from datetime import date
from decimal import Decimal
class ReconTestCase(ModuleTestCase):
'Test quantity reconciliation module'
module = 'cashbook_investment'
class ReconTestCase(object):
""" test reconciliation
"""
@with_transaction()
def test_recon_set_start_quantity_by_cashbook(self):
""" set start-quantity of reconciliation from cashbook-setting
@ -33,7 +30,7 @@ class ReconTestCase(ModuleTestCase):
}])
asset = self.prep_asset_item(
company=company,
product = self.prep_asset_product(name='Product 1'))
product=self.prep_asset_product(name='Product 1'))
self.assertEqual(asset.symbol, 'usd/u')
book, = Book.create([{
@ -53,12 +50,16 @@ class ReconTestCase(ModuleTestCase):
}])
self.assertEqual(book.name, 'Asset-Book')
self.assertEqual(book.reconciliations[0].feature, 'asset')
self.assertEqual(book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] | 0.0000 u - 0.0000 u')
self.assertEqual(
book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] ' +
'| 0.0000 u - 0.0000 u')
Reconciliation.wfcheck(list(book.reconciliations))
self.assertEqual(book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] | 0.0000 u - 0.0000 u')
self.assertEqual(
book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] ' +
'| 0.0000 u - 0.0000 u')
@with_transaction()
def test_recon_set_start_quantity_by_predecessor(self):
@ -79,7 +80,7 @@ class ReconTestCase(ModuleTestCase):
}])
asset = self.prep_asset_item(
company=company,
product = self.prep_asset_product(name='Product 1'))
product=self.prep_asset_product(name='Product 1'))
self.assertEqual(asset.symbol, 'usd/u')
category = self.prep_category(cattype='in')
@ -119,8 +120,10 @@ class ReconTestCase(ModuleTestCase):
}])
self.assertEqual(book.name, 'Asset-Book')
self.assertEqual(len(book.reconciliations), 1)
self.assertEqual(book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0] | 0.000 u - 0.000 u')
self.assertEqual(
book.reconciliations[0].rec_name,
'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)
Lines.wfcheck(list(book.lines))
@ -134,8 +137,10 @@ class ReconTestCase(ModuleTestCase):
self.assertEqual(book.lines[1].quantity_balance, Decimal('4.0'))
self.assertEqual(book.reconciliations[0].state, 'check')
self.assertEqual(book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 12.00 usd [2] | 0.000 u - 4.000 u')
self.assertEqual(
book.reconciliations[0].rec_name,
'05/01/2022 - 05/31/2022 | 0.00 usd - 12.00 usd [2] ' +
'| 0.000 u - 4.000 u')
Reconciliation.wfdone(list(book.reconciliations))
self.assertEqual(book.reconciliations[0].state, 'done')
@ -144,10 +149,14 @@ class ReconTestCase(ModuleTestCase):
'date_from': date(2022, 5, 31),
'date_to': date(2022, 6, 30),
}])
self.assertEqual(recons[0].rec_name,
'05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0] | 0.000 u - 0.000 u')
self.assertEqual(
recons[0].rec_name,
'05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0] | ' +
'0.000 u - 0.000 u')
Reconciliation.wfcheck(recons)
self.assertEqual(recons[0].rec_name,
'05/31/2022 - 06/30/2022 | 12.00 usd - 12.00 usd [0] | 4.000 u - 4.000 u')
self.assertEqual(
recons[0].rec_name,
'05/31/2022 - 06/30/2022 | 12.00 usd - 12.00 usd [0] | ' +
'4.000 u - 4.000 u')
# end ReconTestCase

28
tests/test_module.py Normal file
View file

@ -0,0 +1,28 @@
# -*- 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.modules.cashbook.tests import CashbookTestCase
from trytond.modules.investment.tests import InvestmentTestCase
from .yieldtest import YieldTestCase
from .book import CbInvTestCase
from .reconciliation import ReconTestCase
class CashbookInvestmentTestCase(
CashbookTestCase,
InvestmentTestCase,
CbInvTestCase,
ReconTestCase,
YieldTestCase):
'Test cashbook-investment module'
module = 'cashbook_investment'
# end CashbookInvestmentTestCase
del CashbookTestCase
del InvestmentTestCase

View file

@ -7,13 +7,12 @@ from decimal import Decimal
from datetime import date
from trytond.pool import Pool
from trytond.transaction import Transaction
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.tests.test_tryton import with_transaction
class YieldTestCase(ModuleTestCase):
'Test yield calculation module'
module = 'cashbook_investment'
class YieldTestCase(object):
""" test yield
"""
def prep_yield_config(self, fee, dividend, gainloss, company):
""" add config for yield-calculation
fee: name of fee-category,