field trade_fee + asset_dividend ok + searcher + test

This commit is contained in:
Frederik Jaeckel 2023-02-12 22:01:07 +01:00
parent 982f21f06e
commit 798da563ec
2 changed files with 163 additions and 76 deletions

161
line.py
View file

@ -5,6 +5,7 @@
from decimal import Decimal from decimal import Decimal
from sql.conditionals import Coalesce from sql.conditionals import Coalesce
from sql.aggregate import Sum
from trytond.model import fields from trytond.model import fields
from trytond.pool import PoolMeta, Pool from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, Or, If, And from trytond.pyson import Eval, Or, If, And
@ -115,19 +116,19 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
states = { states = {
'invisible': Eval('feature', '') != 'asset', 'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), }, depends=['currency_digits', 'feature']),
'get_yield_data') 'get_yield_data', searcher='search_trade_fee')
asset_dividend = fields.Function(fields.Numeric(string='Dividend', asset_dividend = fields.Function(fields.Numeric(string='Dividend',
readonly=True, digits=(16, Eval('currency_digits', 2)), readonly=True, digits=(16, Eval('currency_digits', 2)),
states = { states = {
'invisible': Eval('feature', '') != 'asset', 'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), }, depends=['currency_digits', 'feature']),
'get_yield_data') 'get_yield_data', searcher='search_asset_dividend')
asset_gainloss = fields.Function(fields.Numeric(string='Profit/Loss', # ~ asset_gainloss = fields.Function(fields.Numeric(string='Profit/Loss',
readonly=True, digits=(16, Eval('currency_digits', 2)), # ~ readonly=True, digits=(16, Eval('currency_digits', 2)),
states = { # ~ states = {
'invisible': Eval('feature', '') != 'asset', # ~ 'invisible': Eval('feature', '') != 'asset',
}, depends=['currency_digits', 'feature']), # ~ }, depends=['currency_digits', 'feature']),
'get_yield_data') # ~ 'get_yield_data')
@classmethod @classmethod
def get_yield_data_sql(cls): def get_yield_data_sql(cls):
@ -137,81 +138,119 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
AssetSetting = pool.get('cashbook.assetconf') AssetSetting = pool.get('cashbook.assetconf')
SplitLine = pool.get('cashbook.split') SplitLine = pool.get('cashbook.split')
tab_line = cls.__table__() tab_line = cls.__table__()
tab_line1 = cls.__table__() tab_inout_fee = cls.__table__()
tab_line2 = cls.__table__() tab_inout_divi = cls.__table__()
tab_line_fee = cls.__table__() tab_mv_counterpart = cls.__table__()
tab_line_divi = cls.__table__() tab_mv_spline_fee = SplitLine.__table__()
tab_mv_spline_divi = SplitLine.__table__()
tab_spline_fee = SplitLine.__table__() tab_spline_fee = SplitLine.__table__()
tab_spline_divi = SplitLine.__table__() tab_spline_divi = SplitLine.__table__()
cfg1 = AssetSetting.get_singleton() cfg1 = AssetSetting.get_singleton()
# local booked fee/dividend tab_assetline = cls.search([
tab_inout = cls.search([
('cashbook.btype.feature', '=', 'asset'), ('cashbook.btype.feature', '=', 'asset'),
('bookingtype', 'in', ['in', 'out']),
('category', '!=', None),
], query=True) ], query=True)
query_inout = tab_inout.join(tab_line_fee,
condition=(tab_line_fee.id==tab_inout.id) & \
(tab_line_fee.category == getattr(cfg1.fee_category, 'id', None)),
type_ = 'LEFT OUTER',
).join(tab_line_divi,
condition=(tab_line_divi.id==tab_inout.id) & \
(tab_line_divi.category == getattr(cfg1.dividend_category, 'id', None)),
type_ = 'LEFT OUTER',
).select(
tab_inout.id,
(tab_line_fee.credit - tab_line_fee.debit).as_('fee'),
(tab_line_divi.credit - tab_line_divi.debit).as_('dividend'),
)
# fee/dividend - in counterpart booking query = tab_line.join(tab_assetline,
tab_mvinout = cls.search([ condition=(tab_assetline.id==tab_line.id),
('cashbook.btype.feature', '=', 'asset'), ).join(tab_inout_fee,
('bookingtype', 'in', ['mvin', 'mvout']), # [INOUT] fee, local booked
], query=True) condition=(tab_inout_fee.id==tab_line.id) & \
query_mvinout = tab_mvinout.join(tab_line1, tab_inout_fee.bookingtype.in_(['in', 'out']) & \
condition=tab_mvinout.id==tab_line1.id, (tab_inout_fee.category != None) & \
).join(tab_line2, (tab_inout_fee.category == getattr(cfg1.fee_category, 'id', None)),
# current line is linked to split-booking-line of counterpart type_ = 'LEFT OUTER',
condition=((tab_line1.reference == tab_line2.id) | \ ).join(tab_inout_divi,
(tab_line1.id == tab_line2.reference)) & \ # [INOUT] dividend, local booked
(tab_line2.bookingtype.in_(['spin', 'spout'])), condition=(tab_inout_divi.id==tab_line.id) & \
tab_inout_divi.bookingtype.in_(['in', 'out']) & \
(tab_inout_divi.category != None) & \
(tab_inout_divi.category == getattr(cfg1.dividend_category, 'id', None)),
type_ = 'LEFT OUTER',
).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]
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 == getattr(cfg1.fee_category, 'id', None)),
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 == getattr(cfg1.dividend_category, 'id', None)),
type_ = 'LEFT OUTER',
).join(tab_spline_fee, ).join(tab_spline_fee,
# fee-line is linked to split-booking-line # [SP] fee, split booking
condition=(tab_spline_fee.line == tab_line2.id) & \ condition=(tab_spline_fee.line == tab_line.id) & \
tab_line.bookingtype.in_(['spin', 'spout']) & \
(tab_spline_fee.splittype == 'cat') & \ (tab_spline_fee.splittype == 'cat') & \
(tab_spline_fee.category != None) & \ (tab_spline_fee.category != None) & \
(tab_spline_fee.category == getattr(cfg1.fee_category, 'id', None)), (tab_spline_fee.category == getattr(cfg1.fee_category, 'id', None)),
type_ = 'LEFT OUTER', type_ = 'LEFT OUTER',
).join(tab_spline_divi, ).join(tab_spline_divi,
# dividend-line is linked to split-booking-line # [SP] dividend, split booking
condition=(tab_spline_divi.line == tab_line2.id) & \ condition=(tab_spline_divi.line == tab_line.id) & \
tab_line.bookingtype.in_(['spin', 'spout']) & \
(tab_spline_divi.splittype == 'cat') & \ (tab_spline_divi.splittype == 'cat') & \
(tab_spline_divi.category != None) & \ (tab_spline_divi.category != None) & \
(tab_spline_divi.category == getattr(cfg1.dividend_category, 'id', None)), (tab_spline_divi.category == getattr(cfg1.dividend_category, 'id', None)),
type_ = 'LEFT OUTER', type_ = 'LEFT OUTER',
).select(
tab_line1.id,
tab_spline_fee.amount.as_('fee'),
tab_spline_divi.amount.as_('dividend'),
)
# together
query = tab_line.join(query_inout,
condition=query_inout.id==tab_line.id,
type_ = 'LEFT OUTER',
).join(query_mvinout,
condition=query_mvinout.id==tab_line.id,
type_ = 'LEFT OUTER',
).select( ).select(
tab_line.id, tab_line.id,
Coalesce(query_inout.fee, query_mvinout.fee).as_('fee'), Sum(Coalesce(
Coalesce(query_inout.dividend, query_mvinout.dividend).as_('dividend'), tab_inout_fee.credit - tab_inout_fee.debit,
tab_mv_spline_fee.amount,
tab_spline_fee.amount,
Decimal('0.0'),
)).as_('fee'),
Sum(Coalesce(
tab_inout_divi.credit - tab_inout_divi.debit,
tab_mv_spline_divi.amount,
tab_spline_divi.amount,
Decimal('0.0'),
)).as_('dividend'),
group_by=[tab_line.id],
) )
return (tab_line, query) return (tab_line, query)
@classmethod
def search_trade_fee(cls, name, clause):
""" search for fees
"""
Operator = fields.SQL_OPERATORS[clause[1]]
(tab_line, tab_query) = cls.get_yield_data_sql()
query = tab_query.select(
tab_query.id,
where=Operator(tab_query.fee, clause[2]),
)
return [('id', 'in', query)]
@classmethod
def search_asset_dividend(cls, name, clause):
""" search for dividends
"""
Operator = fields.SQL_OPERATORS[clause[1]]
(tab_line, tab_query) = cls.get_yield_data_sql()
query = tab_query.select(
tab_query.id,
where=Operator(tab_query.dividend, clause[2]),
)
return [('id', 'in', query)]
@classmethod @classmethod
def get_yield_data(cls, lines, names): def get_yield_data(cls, lines, names):
""" collect data for fee, dividend, gain/loss per line """ collect data for fee, dividend, gain/loss per line
@ -237,12 +276,10 @@ class Line(SecondUomMixin, metaclass=PoolMeta):
values = { values = {
'trade_fee': quantize_val(record[1], line), 'trade_fee': quantize_val(record[1], line),
'asset_dividend': quantize_val(record[2], line), 'asset_dividend': quantize_val(record[2], line),
'asset_gainloss': Decimal('0.0'), #quantize_val(record[3], line),
} }
for name in names: for name in names:
result[name][record[0]] = values[name] result[name][record[0]] = values[name]
return result return result
def get_rec_name(self, name): def get_rec_name(self, name):

View file

@ -132,19 +132,47 @@ class YieldTestCase(ModuleTestCase):
'quantity_uom': asset.uom.id, 'quantity_uom': asset.uom.id,
'start_date': date(2022, 5, 1), 'start_date': date(2022, 5, 1),
'lines': [('create', [{ 'lines': [('create', [{
'bookingtype': 'out', 'bookingtype': 'out', # [BK01]
'date': date(2022, 5, 1), 'date': date(2022, 5, 1),
'amount': Decimal('2.0'), 'amount': Decimal('2.0'),
'quantity': Decimal('0.0'), 'quantity': Decimal('0.0'),
'category': as_cfg.fee_category.id, 'category': as_cfg.fee_category.id,
'description': 'Fee', 'description': 'Fee',
}, { }, {
'bookingtype': 'in', 'bookingtype': 'in', # [BK02]
'date': date(2022, 5, 1), 'date': date(2022, 5, 1),
'amount': Decimal('10.0'), 'amount': Decimal('10.0'),
'quantity': Decimal('1.0'), 'quantity': Decimal('1.0'),
'category': as_cfg.dividend_category.id, 'category': as_cfg.dividend_category.id,
'description': 'reinvested dividend', 'description': 'reinvested dividend',
}, {
'bookingtype': 'spin', # [BK03]
'date': date(2022, 5, 1),
'description': 'Dividend',
'splitlines': [('create', [{
'description': 'Dividend',
'splittype': 'cat',
'category': as_cfg.dividend_category.id,
'amount': Decimal('5.0'),
'quantity': Decimal('0.0'),
}, {
'description': 'Dividend (2)',
'splittype': 'cat',
'category': as_cfg.dividend_category.id,
'amount': Decimal('2.0'),
'quantity': Decimal('0.0'),
}])],
}, {
'bookingtype': 'spout', # [BK04]
'date': date(2022, 5, 1),
'description': 'Fee',
'splitlines': [('create', [{
'description': 'Fee',
'splittype': 'cat',
'category': as_cfg.fee_category.id,
'amount': Decimal('1.5'),
'quantity': Decimal('0.0'),
}])],
}])], }])],
}]) }])
@ -157,7 +185,7 @@ class YieldTestCase(ModuleTestCase):
'number_sequ': self.prep_sequence().id, 'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 5, 1), 'start_date': date(2022, 5, 1),
'lines': [('create', [{ 'lines': [('create', [{
'bookingtype': 'spin', 'bookingtype': 'spin', # [BK05]
'date': date(2022, 5, 1), 'date': date(2022, 5, 1),
'description': 'Dividend', 'description': 'Dividend',
'splitlines': [('create', [{ 'splitlines': [('create', [{
@ -184,32 +212,54 @@ class YieldTestCase(ModuleTestCase):
self.assertEqual(book_cash.lines[0].references[0].rec_name, self.assertEqual(book_cash.lines[0].references[0].rec_name,
'05/01/2022|to|0.00 usd|Dividend [Cash | 5.00 usd | Open]|0.0000 u') '05/01/2022|to|0.00 usd|Dividend [Cash | 5.00 usd | Open]|0.0000 u')
self.assertEqual(book_asset.name, 'Depot') self.assertEqual(book_asset.rec_name, 'Depot | 13.50 usd | Open | 1.0000 u')
self.assertEqual(book_asset.rec_name, 'Depot | 8.00 usd | Open | 1.0000 u')
self.assertEqual(book_asset.btype.rec_name, 'D - Depot') self.assertEqual(book_asset.btype.rec_name, 'D - Depot')
self.assertEqual(book_asset.state, 'open') self.assertEqual(len(book_asset.lines), 5)
self.assertEqual(book_asset.feature, 'asset')
self.assertEqual(len(book_asset.lines), 3)
# reference to dividend (1) # reference to dividend (1) [BK05]
self.assertEqual(book_asset.lines[0].rec_name, self.assertEqual(book_asset.lines[0].rec_name,
'05/01/2022|to|0.00 usd|Dividend [Cash | 5.00 usd | Open]|0.0000 u') '05/01/2022|to|0.00 usd|Dividend [Cash | 5.00 usd | Open]|0.0000 u')
self.assertEqual(book_asset.lines[0].trade_fee, Decimal('0.0')) self.assertEqual(book_asset.lines[0].trade_fee, Decimal('0.0'))
self.assertEqual(book_asset.lines[0].asset_dividend, Decimal('5.0')) self.assertEqual(book_asset.lines[0].asset_dividend, Decimal('5.0'))
self.assertEqual(book_asset.lines[0].asset_gainloss, Decimal('0.0')) self.assertEqual(book_asset.lines[0].reference.rec_name,
'05/01/2022|Rev/Sp|5.00 usd|Dividend [-]')
self.assertEqual(len(book_asset.lines[0].references), 0)
#self.assertEqual(book_asset.lines[0].asset_gainloss, Decimal('0.0'))
# fee payed from asset-account # fee payed from asset-account [BK01]
self.assertEqual(book_asset.lines[1].rec_name, self.assertEqual(book_asset.lines[1].rec_name,
'05/01/2022|Exp|-2.00 usd|Fee [Fee]|0.0000 u') '05/01/2022|Exp|-2.00 usd|Fee [Fee]|0.0000 u')
self.assertEqual(book_asset.lines[1].trade_fee, Decimal('-2.0')) self.assertEqual(book_asset.lines[1].trade_fee, Decimal('-2.0'))
self.assertEqual(book_asset.lines[1].asset_dividend, Decimal('0.0')) self.assertEqual(book_asset.lines[1].asset_dividend, Decimal('0.0'))
self.assertEqual(book_asset.lines[1].asset_gainloss, Decimal('0.0')) #self.assertEqual(book_asset.lines[1].asset_gainloss, Decimal('0.0'))
# dividend (2) added to asset-account # dividend (2) added to asset-account [BK02]
self.assertEqual(book_asset.lines[2].rec_name, self.assertEqual(book_asset.lines[2].rec_name,
'05/01/2022|Rev|10.00 usd|reinvested dividend [Dividend]|1.0000 u') '05/01/2022|Rev|10.00 usd|reinvested dividend [Dividend]|1.0000 u')
self.assertEqual(book_asset.lines[2].trade_fee, Decimal('0.0')) self.assertEqual(book_asset.lines[2].trade_fee, Decimal('0.0'))
self.assertEqual(book_asset.lines[2].asset_dividend, Decimal('10.0')) self.assertEqual(book_asset.lines[2].asset_dividend, Decimal('10.0'))
self.assertEqual(book_asset.lines[2].asset_gainloss, Decimal('0.0')) #self.assertEqual(book_asset.lines[2].asset_gainloss, Decimal('0.0'))
# dividend as split.booking on asset-account, two lines [BK03]
self.assertEqual(book_asset.lines[3].rec_name,
'05/01/2022|Rev/Sp|7.00 usd|Dividend [-]|0.0000 u')
self.assertEqual(book_asset.lines[3].trade_fee, Decimal('0.0'))
self.assertEqual(book_asset.lines[3].asset_dividend, Decimal('7.0'))
# fee as split.booking on asset-account [BK04]
self.assertEqual(book_asset.lines[4].rec_name,
'05/01/2022|Exp/Sp|-1.50 usd|Fee [-]|0.0000 u')
self.assertEqual(book_asset.lines[4].trade_fee, Decimal('1.5'))
self.assertEqual(book_asset.lines[4].asset_dividend, Decimal('0.0'))
lines = Line.search([('trade_fee', '=', Decimal('1.5'))])
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0].rec_name,
'05/01/2022|Exp/Sp|-1.50 usd|Fee [-]|0.0000 u')
lines = Line.search([('asset_dividend', '=', Decimal('7.0'))])
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0].rec_name,
'05/01/2022|Rev/Sp|7.00 usd|Dividend [-]|0.0000 u')
# end YieldTestCase # end YieldTestCase