evaluation: extend categories-diff/percent/value + tests
This commit is contained in:
parent
82bdf3e05b
commit
a80fac0e0b
9 changed files with 236 additions and 22 deletions
|
@ -9,7 +9,8 @@ from trytond.transaction import Transaction
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
from trytond.i18n import gettext
|
from trytond.i18n import gettext
|
||||||
from .colors import sel_color as sel_bgcolor
|
from .colors import sel_color as sel_bgcolor
|
||||||
from .templates import template_view_graph, template_view_line, cashbook_types
|
from .templates import template_view_graph, template_view_line, \
|
||||||
|
cashbook_types, category_types
|
||||||
|
|
||||||
|
|
||||||
sel_chart = [
|
sel_chart = [
|
||||||
|
@ -77,7 +78,7 @@ class Evaluation(sequence_ordered(), ModelSQL, ModelView):
|
||||||
relation_name='cashbook_report.eval_line',
|
relation_name='cashbook_report.eval_line',
|
||||||
origin='evaluation', target='category',
|
origin='evaluation', target='category',
|
||||||
states={
|
states={
|
||||||
'invisible': Eval('dtype', '') != 'categories',
|
'invisible': Eval('dtype', '').in_(category_types),
|
||||||
}, depends=['dtype'])
|
}, depends=['dtype'])
|
||||||
|
|
||||||
line_values = fields.One2Many(string='Line Values',
|
line_values = fields.One2Many(string='Line Values',
|
||||||
|
|
|
@ -19,7 +19,10 @@ class InvestmentEvaluation(metaclass=PoolMeta):
|
||||||
result.extend([
|
result.extend([
|
||||||
('cashbooks_gldiff', gettext('cashbook_report.msg_dtype_cashbook_gldiff')),
|
('cashbooks_gldiff', gettext('cashbook_report.msg_dtype_cashbook_gldiff')),
|
||||||
('cashbooks_glperc', gettext('cashbook_report.msg_dtype_cashbook_glperc')),
|
('cashbooks_glperc', gettext('cashbook_report.msg_dtype_cashbook_glperc')),
|
||||||
('cashbooks_glvalue', gettext('cashbook_report.msg_dtype_cashbooks_glvalue'))
|
('cashbooks_glvalue', gettext('cashbook_report.msg_dtype_cashbooks_glvalue')),
|
||||||
|
('category_gldiff', gettext('cashbook_report.msg_dtype_category_gldiff')),
|
||||||
|
('category_glvalue', gettext('cashbook_report.msg_dtype_category_glvalue')),
|
||||||
|
('category_glperc', gettext('cashbook_report.msg_dtype_category_glperc')),
|
||||||
])
|
])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -29,6 +32,67 @@ class InvestmentEvaluation(metaclass=PoolMeta):
|
||||||
class InvestmentLine(metaclass=PoolMeta):
|
class InvestmentLine(metaclass=PoolMeta):
|
||||||
__name__ = 'cashbook_report.eval_line'
|
__name__ = 'cashbook_report.eval_line'
|
||||||
|
|
||||||
|
def get_value_category_glperc(self):
|
||||||
|
""" get percentual difference of bookings in categories
|
||||||
|
converted to currency of evaluation
|
||||||
|
"""
|
||||||
|
Book = Pool().get('cashbook.book')
|
||||||
|
|
||||||
|
if self.category is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
books = Book.search([
|
||||||
|
('state', '=', 'open'),
|
||||||
|
('categories.id', '=', self.category.id),
|
||||||
|
])
|
||||||
|
value = Decimal('0.0')
|
||||||
|
amount = Decimal('0.0')
|
||||||
|
|
||||||
|
if len(books) > 0:
|
||||||
|
value = sum([x.current_value_ref for x in books])
|
||||||
|
amount = sum([x.balance_ref for x in books])
|
||||||
|
if amount != Decimal('0.0'):
|
||||||
|
return (
|
||||||
|
Decimal('100.0') * value / amount - Decimal('100.0')
|
||||||
|
).quantize(Decimal(str(1 / 10 ** self.currency_digits)))
|
||||||
|
return Decimal('0.0')
|
||||||
|
|
||||||
|
def get_value_category_glvalue(self):
|
||||||
|
""" get current value of bookings in categories
|
||||||
|
converted to currency of evaluation
|
||||||
|
"""
|
||||||
|
Book = Pool().get('cashbook.book')
|
||||||
|
|
||||||
|
if self.category is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
books = Book.search([
|
||||||
|
('state', '=', 'open'),
|
||||||
|
('categories.id', '=', self.category.id),
|
||||||
|
])
|
||||||
|
result = Decimal('0.0')
|
||||||
|
if len(books) > 0:
|
||||||
|
result = sum([x.current_value_ref for x in books])
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_value_category_gldiff(self):
|
||||||
|
""" get difference amount of bookings in categories
|
||||||
|
converted to currency of evaluation
|
||||||
|
"""
|
||||||
|
Book = Pool().get('cashbook.book')
|
||||||
|
|
||||||
|
if self.category is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
books = Book.search([
|
||||||
|
('state', '=', 'open'),
|
||||||
|
('categories.id', '=', self.category.id),
|
||||||
|
])
|
||||||
|
result = Decimal('0.0')
|
||||||
|
if len(books) > 0:
|
||||||
|
result = sum([x.current_value_ref - x.balance_ref for x in books])
|
||||||
|
return result
|
||||||
|
|
||||||
def get_value_cashbooks_gldiff(self):
|
def get_value_cashbooks_gldiff(self):
|
||||||
""" amount of profit/loss of cashbooks
|
""" amount of profit/loss of cashbooks
|
||||||
"""
|
"""
|
||||||
|
|
6
line.py
6
line.py
|
@ -11,7 +11,7 @@ from trytond.transaction import Transaction
|
||||||
from trytond.i18n import gettext
|
from trytond.i18n import gettext
|
||||||
from trytond.exceptions import UserError
|
from trytond.exceptions import UserError
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
from .templates import cashbook_types
|
from .templates import cashbook_types, category_types
|
||||||
|
|
||||||
|
|
||||||
class EvaluationLine(ModelSQL, ModelView):
|
class EvaluationLine(ModelSQL, ModelView):
|
||||||
|
@ -39,7 +39,7 @@ class EvaluationLine(ModelSQL, ModelView):
|
||||||
category = fields.Many2One(string='Category', select=True, ondelete='CASCADE',
|
category = fields.Many2One(string='Category', select=True, ondelete='CASCADE',
|
||||||
model_name='cashbook.bookcategory',
|
model_name='cashbook.bookcategory',
|
||||||
states={
|
states={
|
||||||
'required': Eval('eval_dtype', '') == 'categories',
|
'required': Eval('eval_dtype', '').in_(category_types),
|
||||||
}, depends=['eval_dtype'])
|
}, depends=['eval_dtype'])
|
||||||
|
|
||||||
# dtype + currency of evaluation
|
# dtype + currency of evaluation
|
||||||
|
@ -164,7 +164,7 @@ class EvaluationLine(ModelSQL, ModelView):
|
||||||
'cashbook_report.msg_invalid_dtype',
|
'cashbook_report.msg_invalid_dtype',
|
||||||
typename = gettext('cashbook_report.msg_dtype_currency'),
|
typename = gettext('cashbook_report.msg_dtype_currency'),
|
||||||
))
|
))
|
||||||
if (record.evaluation.dtype != 'categories') and \
|
if (record.evaluation.dtype not in category_types) and \
|
||||||
(record.category is not None):
|
(record.category is not None):
|
||||||
raise UserError(gettext(
|
raise UserError(gettext(
|
||||||
'cashbook_report.msg_invalid_dtype',
|
'cashbook_report.msg_invalid_dtype',
|
||||||
|
|
16
locale/de.po
16
locale/de.po
|
@ -35,8 +35,20 @@ msgid "Currencies"
|
||||||
msgstr "Währungen"
|
msgstr "Währungen"
|
||||||
|
|
||||||
msgctxt "model:ir.message,text:msg_dtype_category"
|
msgctxt "model:ir.message,text:msg_dtype_category"
|
||||||
msgid "Categories"
|
msgid "Categories [Amount]"
|
||||||
msgstr "Kategorien"
|
msgstr "Kategorien [Betrag]"
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_dtype_category_gldiff"
|
||||||
|
msgid "Categories [Amount of Profit/Loss]"
|
||||||
|
msgstr "Kategorien [Betrag Gewinn/Verlust]"
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_dtype_category_glvalue"
|
||||||
|
msgid "Categories [Current Value]"
|
||||||
|
msgstr "Kategorien [aktueller Wert]"
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_dtype_category_glperc"
|
||||||
|
msgid "Categories [Percent of Profit/Loss]"
|
||||||
|
msgstr "Kategorien [Prozent Gewinn/Verlust]"
|
||||||
|
|
||||||
msgctxt "model:ir.message,text:msg_name_graph"
|
msgctxt "model:ir.message,text:msg_name_graph"
|
||||||
msgid "Graph: %(gname)s"
|
msgid "Graph: %(gname)s"
|
||||||
|
|
16
locale/en.po
16
locale/en.po
|
@ -31,8 +31,20 @@ msgid "Currencies"
|
||||||
msgstr "Currencies"
|
msgstr "Currencies"
|
||||||
|
|
||||||
msgctxt "model:ir.message,text:msg_dtype_category"
|
msgctxt "model:ir.message,text:msg_dtype_category"
|
||||||
msgid "Categories"
|
msgid "Categories [Amount]"
|
||||||
msgstr "Categories"
|
msgstr "Categories [Amount]"
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_dtype_category_gldiff"
|
||||||
|
msgid "Categories [Amount of Profit/Loss]"
|
||||||
|
msgstr "Categories [Amount of Profit/Loss]"
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_dtype_category_glvalue"
|
||||||
|
msgid "Categories [Current Value]"
|
||||||
|
msgstr "Categories [Current Value]"
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_dtype_category_glperc"
|
||||||
|
msgid "Categories [Percent of Profit/Loss]"
|
||||||
|
msgstr "Categories [Percent of Profit/Loss]"
|
||||||
|
|
||||||
msgctxt "model:ir.message,text:msg_name_graph"
|
msgctxt "model:ir.message,text:msg_name_graph"
|
||||||
msgid "Graph: %(gname)s"
|
msgid "Graph: %(gname)s"
|
||||||
|
|
11
message.xml
11
message.xml
|
@ -27,7 +27,16 @@ full copyright notices and license terms. -->
|
||||||
<field name="text">Currencies</field>
|
<field name="text">Currencies</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="ir.message" id="msg_dtype_category">
|
<record model="ir.message" id="msg_dtype_category">
|
||||||
<field name="text">Categories</field>
|
<field name="text">Categories [Amount]</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="msg_dtype_category_gldiff">
|
||||||
|
<field name="text">Categories [Amount of Profit/Loss]</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="msg_dtype_category_glvalue">
|
||||||
|
<field name="text">Categories [Current Value]</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="msg_dtype_category_glperc">
|
||||||
|
<field name="text">Categories [Percent of Profit/Loss]</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="ir.message" id="msg_name_graph">
|
<record model="ir.message" id="msg_name_graph">
|
||||||
<field name="text">Graph: %(gname)s</field>
|
<field name="text">Graph: %(gname)s</field>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# full copyright notices and license terms.
|
# full copyright notices and license terms.
|
||||||
|
|
||||||
cashbook_types = ['cashbooks', 'cashbooks_gldiff', 'cashbooks_glperc', 'cashbooks_glvalue']
|
cashbook_types = ['cashbooks', 'cashbooks_gldiff', 'cashbooks_glperc', 'cashbooks_glvalue']
|
||||||
|
category_types = ['categories', 'category_gldiff', 'category_glvalue', 'category_glperc']
|
||||||
|
|
||||||
template_view_line = '<field name="balance" fill="%(fill)s" empty="0" string="%(string)s"/>'
|
template_view_line = '<field name="balance" fill="%(fill)s" empty="0" string="%(string)s"/>'
|
||||||
|
|
||||||
|
|
|
@ -730,6 +730,7 @@ class ReportTestCase(CashbookTestCase):
|
||||||
Asset = pool.get('investment.asset')
|
Asset = pool.get('investment.asset')
|
||||||
Product = pool.get('product.template')
|
Product = pool.get('product.template')
|
||||||
Uom = pool.get('product.uom')
|
Uom = pool.get('product.uom')
|
||||||
|
CbCategory = pool.get('cashbook.bookcategory')
|
||||||
|
|
||||||
at, = AccType.create([{
|
at, = AccType.create([{
|
||||||
'name': 'depot',
|
'name': 'depot',
|
||||||
|
@ -759,23 +760,50 @@ class ReportTestCase(CashbookTestCase):
|
||||||
'rate': Decimal('1750.0'),
|
'rate': Decimal('1750.0'),
|
||||||
}, ])],
|
}, ])],
|
||||||
}])
|
}])
|
||||||
|
self.assertEqual(asset.rec_name,
|
||||||
AccType.write(*[
|
'Aurum | 1,750.0000 usd/u | 05/01/2022')
|
||||||
[books[0].btype],
|
|
||||||
{
|
|
||||||
'feature': 'asset',
|
|
||||||
}])
|
|
||||||
|
|
||||||
books = self.prep_report_3books()
|
books = self.prep_report_3books()
|
||||||
|
cb_cat, = CbCategory.create([{'name': 'CB Category'}])
|
||||||
Book.write(*[
|
Book.write(*[
|
||||||
books,
|
books,
|
||||||
{
|
{
|
||||||
|
'btype': at.id,
|
||||||
'asset': asset.id,
|
'asset': asset.id,
|
||||||
|
'categories': [('add', [cb_cat.id])],
|
||||||
'quantity_uom': asset.uom.id,
|
'quantity_uom': asset.uom.id,
|
||||||
'quantity_digits': 3,
|
'quantity_digits': 3,
|
||||||
|
'lines': [('write',
|
||||||
|
[books[0].lines[0].id], # usd
|
||||||
|
{'quantity': Decimal('2.0'), 'amount': Decimal('3000.0')},
|
||||||
|
[books[0].lines[1].id],
|
||||||
|
{'quantity': Decimal('2.0'), 'amount': Decimal('3100.0')},
|
||||||
|
[books[1].lines[0].id], # usd
|
||||||
|
{'quantity': Decimal('2.0'), 'amount': Decimal('3200.0')},
|
||||||
|
[books[1].lines[1].id],
|
||||||
|
{'quantity': Decimal('2.0'), 'amount': Decimal('3300.0')},
|
||||||
|
[books[2].lines[0].id], # euro
|
||||||
|
{'quantity': Decimal('2.0'), 'amount': Decimal('3300.0')},
|
||||||
|
[books[2].lines[1].id],
|
||||||
|
{'quantity': Decimal('2.0'), 'amount': Decimal('3400.0')},
|
||||||
|
)],
|
||||||
}])
|
}])
|
||||||
self.assertEqual(books[0].rec_name, 'ss')
|
self.assertEqual(books[0].rec_name, 'Book 1 | 6,100.00 usd | Open | 4.000 u')
|
||||||
|
self.assertEqual(books[0].current_value, Decimal('7000.0'))
|
||||||
|
self.assertEqual(books[0].diff_amount, Decimal('900.0'))
|
||||||
|
self.assertEqual(books[0].diff_percent, Decimal('14.75'))
|
||||||
|
|
||||||
|
self.assertEqual(books[1].rec_name, 'Book 2 | 6,500.00 usd | Open | 4.000 u')
|
||||||
|
self.assertEqual(books[1].current_value, Decimal('7000.0'))
|
||||||
|
self.assertEqual(books[1].diff_amount, Decimal('500.0'))
|
||||||
|
self.assertEqual(books[1].diff_percent, Decimal('7.69'))
|
||||||
|
|
||||||
|
self.assertEqual(books[2].rec_name, 'Book 3 | 6,700.00 € | Open | 4.000 u')
|
||||||
|
self.assertEqual(books[2].current_value, Decimal('6666.67'))
|
||||||
|
self.assertEqual(books[2].diff_amount, Decimal('-33.33'))
|
||||||
|
self.assertEqual(books[2].diff_percent, Decimal('-0.5'))
|
||||||
|
|
||||||
|
# evaluation: amount-difference
|
||||||
evaluation, = Evaluation.create([{
|
evaluation, = Evaluation.create([{
|
||||||
'name': 'Evaluation 1',
|
'name': 'Evaluation 1',
|
||||||
'dtype': 'cashbooks_gldiff',
|
'dtype': 'cashbooks_gldiff',
|
||||||
|
@ -789,9 +817,96 @@ class ReportTestCase(CashbookTestCase):
|
||||||
self.assertEqual(evaluation.bgcolor, '#ffffc0')
|
self.assertEqual(evaluation.bgcolor, '#ffffc0')
|
||||||
self.assertEqual(evaluation.currency.code, 'EUR')
|
self.assertEqual(evaluation.currency.code, 'EUR')
|
||||||
|
|
||||||
self.assertEqual(evaluation.line_values[0].balance, Decimal('23.81'))
|
self.assertEqual(evaluation.line_values[0].balance, Decimal('857.14'))
|
||||||
self.assertEqual(evaluation.line_values[1].balance, Decimal('11.90'))
|
self.assertEqual(evaluation.line_values[1].balance, Decimal('476.19'))
|
||||||
self.assertEqual(evaluation.line_values[2].balance, Decimal('23.00'))
|
self.assertEqual(evaluation.line_values[2].balance, Decimal('-33.33'))
|
||||||
|
|
||||||
|
# evaluation: percent-difference
|
||||||
|
evaluation2, = Evaluation.create([{
|
||||||
|
'name': 'Evaluation 2',
|
||||||
|
'dtype': 'cashbooks_glperc',
|
||||||
|
'chart': 'hbar',
|
||||||
|
'cashbooks': [('add', [x.id for x in books])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(evaluation2.dtype, 'cashbooks_glperc')
|
||||||
|
self.assertEqual(evaluation2.chart, 'hbar')
|
||||||
|
self.assertEqual(evaluation2.legend, True)
|
||||||
|
self.assertEqual(evaluation2.maincolor, 'default')
|
||||||
|
self.assertEqual(evaluation2.bgcolor, '#ffffc0')
|
||||||
|
self.assertEqual(evaluation2.currency.code, 'EUR')
|
||||||
|
|
||||||
|
self.assertEqual(evaluation2.line_values[0].balance, Decimal('14.75'))
|
||||||
|
self.assertEqual(evaluation2.line_values[1].balance, Decimal('7.69'))
|
||||||
|
self.assertEqual(evaluation2.line_values[2].balance, Decimal('-0.5'))
|
||||||
|
|
||||||
|
# evaluation: percent-difference
|
||||||
|
evaluation3, = Evaluation.create([{
|
||||||
|
'name': 'Evaluation 3',
|
||||||
|
'dtype': 'cashbooks_glvalue',
|
||||||
|
'chart': 'hbar',
|
||||||
|
'cashbooks': [('add', [x.id for x in books])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(evaluation3.dtype, 'cashbooks_glvalue')
|
||||||
|
self.assertEqual(evaluation3.chart, 'hbar')
|
||||||
|
self.assertEqual(evaluation3.legend, True)
|
||||||
|
self.assertEqual(evaluation3.maincolor, 'default')
|
||||||
|
self.assertEqual(evaluation3.bgcolor, '#ffffc0')
|
||||||
|
self.assertEqual(evaluation3.currency.code, 'EUR')
|
||||||
|
|
||||||
|
self.assertEqual(evaluation3.line_values[0].balance, Decimal('6666.67'))
|
||||||
|
self.assertEqual(evaluation3.line_values[1].balance, Decimal('6666.67'))
|
||||||
|
self.assertEqual(evaluation3.line_values[2].balance, Decimal('6666.67'))
|
||||||
|
|
||||||
|
# evaluation: category-current value
|
||||||
|
evaluation4, = Evaluation.create([{
|
||||||
|
'name': 'Evaluation 4',
|
||||||
|
'dtype': 'category_glvalue',
|
||||||
|
'chart': 'hbar',
|
||||||
|
'categories': [('add', [cb_cat.id])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(evaluation4.dtype, 'category_glvalue')
|
||||||
|
self.assertEqual(evaluation4.chart, 'hbar')
|
||||||
|
self.assertEqual(evaluation4.legend, True)
|
||||||
|
self.assertEqual(evaluation4.maincolor, 'default')
|
||||||
|
self.assertEqual(evaluation4.bgcolor, '#ffffc0')
|
||||||
|
self.assertEqual(evaluation4.currency.code, 'EUR')
|
||||||
|
|
||||||
|
self.assertEqual(len(evaluation4.line_values), 1)
|
||||||
|
self.assertEqual(evaluation4.line_values[0].balance, Decimal('20000.01'))
|
||||||
|
|
||||||
|
# evaluation: category- difference amount
|
||||||
|
evaluation5, = Evaluation.create([{
|
||||||
|
'name': 'Evaluation 5',
|
||||||
|
'dtype': 'category_gldiff',
|
||||||
|
'chart': 'hbar',
|
||||||
|
'categories': [('add', [cb_cat.id])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(evaluation5.dtype, 'category_gldiff')
|
||||||
|
self.assertEqual(evaluation5.chart, 'hbar')
|
||||||
|
self.assertEqual(evaluation5.legend, True)
|
||||||
|
self.assertEqual(evaluation5.maincolor, 'default')
|
||||||
|
self.assertEqual(evaluation5.bgcolor, '#ffffc0')
|
||||||
|
self.assertEqual(evaluation5.currency.code, 'EUR')
|
||||||
|
|
||||||
|
self.assertEqual(len(evaluation5.line_values), 1)
|
||||||
|
self.assertEqual(evaluation5.line_values[0].balance, Decimal('1300.01'))
|
||||||
|
|
||||||
|
# evaluation: category- difference amount
|
||||||
|
evaluation6, = Evaluation.create([{
|
||||||
|
'name': 'Evaluation 6',
|
||||||
|
'dtype': 'category_glperc',
|
||||||
|
'chart': 'hbar',
|
||||||
|
'categories': [('add', [cb_cat.id])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(evaluation6.dtype, 'category_glperc')
|
||||||
|
self.assertEqual(evaluation6.chart, 'hbar')
|
||||||
|
self.assertEqual(evaluation6.legend, True)
|
||||||
|
self.assertEqual(evaluation6.maincolor, 'default')
|
||||||
|
self.assertEqual(evaluation6.bgcolor, '#ffffc0')
|
||||||
|
self.assertEqual(evaluation6.currency.code, 'EUR')
|
||||||
|
|
||||||
|
self.assertEqual(len(evaluation6.line_values), 1)
|
||||||
|
self.assertEqual(evaluation6.line_values[0].balance, Decimal('6.95'))
|
||||||
|
|
||||||
@with_transaction()
|
@with_transaction()
|
||||||
def test_report_chart_pie_book_red(self):
|
def test_report_chart_pie_book_red(self):
|
||||||
|
|
|
@ -4,7 +4,7 @@ depends:
|
||||||
res
|
res
|
||||||
cashbook
|
cashbook
|
||||||
cashbook_bookcategory
|
cashbook_bookcategory
|
||||||
#extras_depend:
|
extras_depend:
|
||||||
dashboard
|
dashboard
|
||||||
cashbook_investment
|
cashbook_investment
|
||||||
xml:
|
xml:
|
||||||
|
|
Loading…
Reference in a new issue