extend evaluation for cashbook-profit/loss amount/percent/diff - todo

This commit is contained in:
Frederik Jaeckel 2023-02-02 23:35:58 +01:00
parent 0b590b87c7
commit 82bdf3e05b
11 changed files with 265 additions and 21 deletions

View file

@ -9,6 +9,8 @@ from .line import EvaluationLine
from .currency import Currency from .currency import Currency
from .evaluation_context import EvaluationContext from .evaluation_context import EvaluationContext
from .evaluation_wizard import OpenChartWizard from .evaluation_wizard import OpenChartWizard
from .investment import InvestmentEvaluation, InvestmentLine
def register(): def register():
Pool.register( Pool.register(
@ -20,3 +22,7 @@ def register():
Pool.register( Pool.register(
OpenChartWizard, OpenChartWizard,
module='cashbook_report', type_='wizard') module='cashbook_report', type_='wizard')
Pool.register(
InvestmentEvaluation,
InvestmentLine,
module='cashbook_report', type_='model', depends=['cashbook_investment'])

View file

@ -7,17 +7,11 @@ from trytond.model import ModelView, ModelSQL, fields, sequence_ordered
from trytond.pyson import Eval from trytond.pyson import Eval
from trytond.transaction import Transaction from trytond.transaction import Transaction
from trytond.pool import Pool from trytond.pool import Pool
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 from .templates import template_view_graph, template_view_line, cashbook_types
sel_etype = [
('cashbooks', 'Cashbooks'),
('types', 'Types of Cashbooks'),
('currencies', 'Currencys'),
('categories', 'Categories'),
]
sel_chart = [ sel_chart = [
('vbar', 'Vertical Bars'), ('vbar', 'Vertical Bars'),
('hbar', 'Horizontal Bars'), ('hbar', 'Horizontal Bars'),
@ -44,7 +38,7 @@ class Evaluation(sequence_ordered(), ModelSQL, ModelView):
required=True, ondelete="RESTRICT") required=True, ondelete="RESTRICT")
name = fields.Char(string='Name', required=True) name = fields.Char(string='Name', required=True)
dtype = fields.Selection(string='Data type', required=True, dtype = fields.Selection(string='Data type', required=True,
sort=False, selection=sel_etype, sort=True, selection='get_sel_etype',
help='Type of data displayed') help='Type of data displayed')
dtype_string = dtype.translated('dtype') dtype_string = dtype.translated('dtype')
chart = fields.Selection(string='Chart type', required=True, chart = fields.Selection(string='Chart type', required=True,
@ -64,7 +58,7 @@ class Evaluation(sequence_ordered(), ModelSQL, ModelView):
relation_name='cashbook_report.eval_line', relation_name='cashbook_report.eval_line',
origin='evaluation', target='cashbook', origin='evaluation', target='cashbook',
states={ states={
'invisible': Eval('dtype', '') != 'cashbooks', 'invisible': ~Eval('dtype', '').in_(cashbook_types),
}, depends=['dtype']) }, depends=['dtype'])
types = fields.Many2Many(string='Types', types = fields.Many2Many(string='Types',
relation_name='cashbook_report.eval_line', relation_name='cashbook_report.eval_line',
@ -143,6 +137,17 @@ class Evaluation(sequence_ordered(), ModelSQL, ModelView):
""" """
return 'pie' return 'pie'
@classmethod
def get_sel_etype(cls):
""" get list of evaluation-types
"""
return [
('cashbooks', gettext('cashbook_report.msg_dtype_cashbook')),
('types', gettext('cashbook_report.msg_dtype_type')),
('currencies', gettext('cashbook_report.msg_dtype_currency')),
('categories', gettext('cashbook_report.msg_dtype_category')),
]
@classmethod @classmethod
def get_create_view_data(cls, evaluation): def get_create_view_data(cls, evaluation):
""" generate dictionary to create view-xml """ generate dictionary to create view-xml
@ -285,7 +290,7 @@ class Evaluation(sequence_ordered(), ModelSQL, ModelView):
continue continue
for dt in ['cashbooks', 'types', 'currencies', 'categories']: for dt in ['cashbooks', 'types', 'currencies', 'categories']:
if (values['dtype'] != dt) and \ if (not values['dtype'].startswith(dt)) and \
(len(getattr(evaluation, dt)) > 0): (len(getattr(evaluation, dt)) > 0):
to_write.extend([ to_write.extend([
[evaluation], [evaluation],

67
investment.py Normal file
View file

@ -0,0 +1,67 @@
# -*- 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.pool import PoolMeta, Pool
from trytond.i18n import gettext
from decimal import Decimal
class InvestmentEvaluation(metaclass=PoolMeta):
__name__ = 'cashbook_report.evaluation'
@classmethod
def get_sel_etype(cls):
""" get list of evaluation-types
"""
result = super(InvestmentEvaluation, cls).get_sel_etype()
result.extend([
('cashbooks_gldiff', gettext('cashbook_report.msg_dtype_cashbook_gldiff')),
('cashbooks_glperc', gettext('cashbook_report.msg_dtype_cashbook_glperc')),
('cashbooks_glvalue', gettext('cashbook_report.msg_dtype_cashbooks_glvalue'))
])
return result
# end InvestmentEvaluation
class InvestmentLine(metaclass=PoolMeta):
__name__ = 'cashbook_report.eval_line'
def get_value_cashbooks_gldiff(self):
""" amount of profit/loss of cashbooks
"""
Currency = Pool().get('currency.currency')
if self.cashbook:
if self.cashbook.feature == 'asset':
exp = Decimal(Decimal(1) / 10 ** self.currency_digits)
return Currency.compute(
self.cashbook.currency,
self.cashbook.diff_amount,
self.eval_currency,
).quantize(exp)
def get_value_cashbooks_glvalue(self):
""" current value of cashbooks
"""
Currency = Pool().get('currency.currency')
if self.cashbook:
if self.cashbook.feature == 'asset':
exp = Decimal(Decimal(1) / 10 ** self.currency_digits)
return Currency.compute(
self.cashbook.currency,
self.cashbook.current_value,
self.eval_currency,
).quantize(exp)
def get_value_cashbooks_glperc(self):
""" percent of profit/loss of cashbooks
"""
if self.cashbook:
if self.cashbook.feature == 'asset':
return self.cashbook.diff_percent
# end InvestmentLine

14
line.py
View file

@ -11,6 +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
class EvaluationLine(ModelSQL, ModelView): class EvaluationLine(ModelSQL, ModelView):
@ -23,7 +24,7 @@ class EvaluationLine(ModelSQL, ModelView):
cashbook = fields.Many2One(string='Cashbook', select=True, ondelete='CASCADE', cashbook = fields.Many2One(string='Cashbook', select=True, ondelete='CASCADE',
model_name='cashbook.book', model_name='cashbook.book',
states={ states={
'required': Eval('eval_dtype', '') == 'cashbooks', 'required': Eval('eval_dtype', '').in_(cashbook_types),
}, depends=['eval_dtype']) }, depends=['eval_dtype'])
dtype = fields.Many2One(string='Type', select=True, ondelete='CASCADE', dtype = fields.Many2One(string='Type', select=True, ondelete='CASCADE',
model_name='cashbook.type', model_name='cashbook.type',
@ -130,6 +131,9 @@ class EvaluationLine(ModelSQL, ModelView):
return getattr( return getattr(
getattr(self, { getattr(self, {
'cashbooks': 'cashbook', 'cashbooks': 'cashbook',
'cashbooks_gldiff': 'cashbook',
'cashbooks_glperc': 'cashbook',
'cashbooks_glvalue': 'cashbook',
'categories': 'category', 'categories': 'category',
'types': 'dtype', 'types': 'dtype',
'currencies': 'currency', 'currencies': 'currency',
@ -142,7 +146,7 @@ class EvaluationLine(ModelSQL, ModelView):
""" """
super(EvaluationLine, cls).validate(records) super(EvaluationLine, cls).validate(records)
for record in records: for record in records:
if (record.evaluation.dtype != 'cashbooks') and \ if (record.evaluation.dtype not in cashbook_types) and \
(record.cashbook is not None): (record.cashbook is not None):
raise UserError(gettext( raise UserError(gettext(
'cashbook_report.msg_invalid_dtype', 'cashbook_report.msg_invalid_dtype',
@ -160,6 +164,12 @@ 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 \
(record.category is not None):
raise UserError(gettext(
'cashbook_report.msg_invalid_dtype',
typename = gettext('cashbook_report.msg_dtype_category'),
))
def get_balance_by_query(self, query): def get_balance_by_query(self, query):
""" run 'query' on Lines, convert used """ run 'query' on Lines, convert used

View file

@ -11,8 +11,20 @@ msgid "Type of evaluation must be '%(typename)s'."
msgstr "Typ der Auswertung mus '%(typename)s' sein." msgstr "Typ der Auswertung mus '%(typename)s' sein."
msgctxt "model:ir.message,text:msg_dtype_cashbook" msgctxt "model:ir.message,text:msg_dtype_cashbook"
msgid "Cashbooks" msgid "Cashbooks [Amount]"
msgstr "Kassenbücher" msgstr "Kassenbücher [Betrag]"
msgctxt "model:ir.message,text:msg_dtype_cashbook_gldiff"
msgid "Cashbooks [Amount of Profit/Loss]"
msgstr "Kassenbücher [Betrag Gewinn/Verlust]"
msgctxt "model:ir.message,text:msg_dtype_cashbook_glperc"
msgid "Cashbooks [Percent of Profit/Loss]"
msgstr "Kassenbücher [Prozent Gewinn/Verlust]"
msgctxt "model:ir.message,text:msg_dtype_cashbooks_glvalue"
msgid "Cashbooks [Current Value]"
msgstr "Kassenbücher [aktueller Wert]"
msgctxt "model:ir.message,text:msg_dtype_type" msgctxt "model:ir.message,text:msg_dtype_type"
msgid "Types of Cashbooks" msgid "Types of Cashbooks"
@ -22,6 +34,10 @@ msgctxt "model:ir.message,text:msg_dtype_currency"
msgid "Currencies" msgid "Currencies"
msgstr "Währungen" msgstr "Währungen"
msgctxt "model:ir.message,text:msg_dtype_category"
msgid "Categories"
msgstr "Kategorien"
msgctxt "model:ir.message,text:msg_name_graph" msgctxt "model:ir.message,text:msg_name_graph"
msgid "Graph: %(gname)s" msgid "Graph: %(gname)s"
msgstr "Diagramm: %(gname)s" msgstr "Diagramm: %(gname)s"

View file

@ -7,8 +7,20 @@ msgid "Type of evaluation must be '%(typename)s'."
msgstr "Type of evaluation must be '%(typename)s'." msgstr "Type of evaluation must be '%(typename)s'."
msgctxt "model:ir.message,text:msg_dtype_cashbook" msgctxt "model:ir.message,text:msg_dtype_cashbook"
msgid "Cashbooks" msgid "Cashbooks [Amount]"
msgstr "Cashbooks" msgstr "Cashbooks [Amount]"
msgctxt "model:ir.message,text:msg_dtype_cashbook_gldiff"
msgid "Cashbooks [Amount of Profit/Loss]"
msgstr "Cashbooks [Amount of Profit/Loss]"
msgctxt "model:ir.message,text:msg_dtype_cashbook_glperc"
msgid "Cashbooks [Percent of Profit/Loss]"
msgstr "Cashbooks [Percent of Profit/Loss]"
msgctxt "model:ir.message,text:msg_dtype_cashbooks_glvalue"
msgid "Cashbooks [Current Value]"
msgstr "Cashbooks [Current Value]"
msgctxt "model:ir.message,text:msg_dtype_type" msgctxt "model:ir.message,text:msg_dtype_type"
msgid "Types of Cashbooks" msgid "Types of Cashbooks"
@ -18,6 +30,10 @@ msgctxt "model:ir.message,text:msg_dtype_currency"
msgid "Currencies" msgid "Currencies"
msgstr "Currencies" msgstr "Currencies"
msgctxt "model:ir.message,text:msg_dtype_category"
msgid "Categories"
msgstr "Categories"
msgctxt "model:ir.message,text:msg_name_graph" msgctxt "model:ir.message,text:msg_name_graph"
msgid "Graph: %(gname)s" msgid "Graph: %(gname)s"
msgstr "Graph: %(gname)s" msgstr "Graph: %(gname)s"

View file

@ -9,7 +9,16 @@ full copyright notices and license terms. -->
<field name="text">Type of evaluation must be '%(typename)s'.</field> <field name="text">Type of evaluation must be '%(typename)s'.</field>
</record> </record>
<record model="ir.message" id="msg_dtype_cashbook"> <record model="ir.message" id="msg_dtype_cashbook">
<field name="text">Cashbooks</field> <field name="text">Cashbooks [Amount]</field>
</record>
<record model="ir.message" id="msg_dtype_cashbook_gldiff">
<field name="text">Cashbooks [Amount of Profit/Loss]</field>
</record>
<record model="ir.message" id="msg_dtype_cashbook_glperc">
<field name="text">Cashbooks [Percent of Profit/Loss]</field>
</record>
<record model="ir.message" id="msg_dtype_cashbooks_glvalue">
<field name="text">Cashbooks [Current Value]</field>
</record> </record>
<record model="ir.message" id="msg_dtype_type"> <record model="ir.message" id="msg_dtype_type">
<field name="text">Types of Cashbooks</field> <field name="text">Types of Cashbooks</field>
@ -17,6 +26,9 @@ full copyright notices and license terms. -->
<record model="ir.message" id="msg_dtype_currency"> <record model="ir.message" id="msg_dtype_currency">
<field name="text">Currencies</field> <field name="text">Currencies</field>
</record> </record>
<record model="ir.message" id="msg_dtype_category">
<field name="text">Categories</field>
</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>
</record> </record>

View file

@ -22,7 +22,7 @@ with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
# tryton.cfg einlesen # tryton.cfg einlesen
config = ConfigParser() config = ConfigParser()
config.readfp(open('tryton.cfg')) config.read_file(open('tryton.cfg'))
info = dict(config.items('tryton')) info = dict(config.items('tryton'))
for key in ('depends', 'extras_depend', 'xml'): for key in ('depends', 'extras_depend', 'xml'):
if key in info: if key in info:
@ -68,6 +68,7 @@ setup(name='%s_%s' % (PREFIX, MODULE),
description='Tryton module to add reports to cashbook.', description='Tryton module to add reports to cashbook.',
long_description=long_description, long_description=long_description,
url='https://www.m-ds.de/', url='https://www.m-ds.de/',
download_url='https://scmdev.m-ds.de/Tryton/Extra/cashbook_report',
author='martin-data services', author='martin-data services',
author_email='service@m-ds.de', author_email='service@m-ds.de',
license='GPL-3', license='GPL-3',

View file

@ -3,6 +3,7 @@
# The COPYRIGHT file at the top level of this repository contains the # The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms. # full copyright notices and license terms.
cashbook_types = ['cashbooks', 'cashbooks_gldiff', 'cashbooks_glperc', 'cashbooks_glvalue']
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"/>'

View file

@ -510,6 +510,31 @@ class ReportTestCase(CashbookTestCase):
self.assertEqual(len(evaluation.currencies), 0) self.assertEqual(len(evaluation.currencies), 0)
self.assertEqual(len(evaluation.categories), 0) self.assertEqual(len(evaluation.categories), 0)
# investment - profit/loss amount
if 'cashbooks_gldiff' in [x[0] for x in Evaluation.get_sel_etype()]:
# no change if switch between cashbook-types
Evaluation.write(*[
[evaluation],
{
'dtype': 'cashbooks_gldiff',
}])
self.assertEqual(len(evaluation.cashbooks), 3)
self.assertEqual(len(evaluation.types), 0)
self.assertEqual(len(evaluation.currencies), 0)
self.assertEqual(len(evaluation.categories), 0)
Evaluation.write(*[
[evaluation],
{
'dtype': 'cashbooks_glperc',
}])
self.assertEqual(len(evaluation.cashbooks), 3)
self.assertEqual(len(evaluation.types), 0)
self.assertEqual(len(evaluation.currencies), 0)
self.assertEqual(len(evaluation.categories), 0)
else :
print('\n--== Module "cashbook_investment" not installed ==--')
Evaluation.write(*[ Evaluation.write(*[
[evaluation], [evaluation],
{ {
@ -684,6 +709,90 @@ class ReportTestCase(CashbookTestCase):
if DashboardAction is not None: if DashboardAction is not None:
self.assertEqual(DashboardAction.search_count([]), 1) self.assertEqual(DashboardAction.search_count([]), 1)
@with_transaction()
def test_report_chart_hbar_book_investment(self):
""" create 3x cashbooks, add bookings, rates
create report with cashbooks, check
"""
pool = Pool()
Evaluation = pool.get('cashbook_report.evaluation')
Book = pool.get('cashbook.book')
company = self.prep_company()
with Transaction().set_context({
'company': company.id,
}):
if 'cashbooks_gldiff' not in [x[0] for x in Evaluation.get_sel_etype()]:
print('\n--== Module "cashbook_investment" not installed ==--')
return
AccType = pool.get('cashbook.type')
Asset = pool.get('investment.asset')
Product = pool.get('product.template')
Uom = pool.get('product.uom')
at, = AccType.create([{
'name': 'depot',
'short': 'D',
'feature': 'asset',
'company': company.id,
}])
prod_templ, = Product.create([{
'name': 'Aurum',
'type': 'assets',
'list_price': Decimal('1.0'),
'default_uom': Uom.search([('symbol', '=', 'u')])[0].id,
'products': [('create', [{
'description': 'Au',
}])],
}])
asset, = Asset.create([{
'company': company.id,
'product': prod_templ.products[0].id,
'currency': company.currency.id,
'currency_digits': 4,
'uom': prod_templ.default_uom.id,
'rates': [('create', [{
'date': date(2022, 5, 1),
'rate': Decimal('1750.0'),
}, ])],
}])
AccType.write(*[
[books[0].btype],
{
'feature': 'asset',
}])
books = self.prep_report_3books()
Book.write(*[
books,
{
'asset': asset.id,
'quantity_uom': asset.uom.id,
'quantity_digits': 3,
}])
self.assertEqual(books[0].rec_name, 'ss')
evaluation, = Evaluation.create([{
'name': 'Evaluation 1',
'dtype': 'cashbooks_gldiff',
'chart': 'hbar',
'cashbooks': [('add', [x.id for x in books])],
}])
self.assertEqual(evaluation.dtype, 'cashbooks_gldiff')
self.assertEqual(evaluation.chart, 'hbar')
self.assertEqual(evaluation.legend, True)
self.assertEqual(evaluation.maincolor, 'default')
self.assertEqual(evaluation.bgcolor, '#ffffc0')
self.assertEqual(evaluation.currency.code, 'EUR')
self.assertEqual(evaluation.line_values[0].balance, Decimal('23.81'))
self.assertEqual(evaluation.line_values[1].balance, Decimal('11.90'))
self.assertEqual(evaluation.line_values[2].balance, Decimal('23.00'))
@with_transaction() @with_transaction()
def test_report_chart_pie_book_red(self): def test_report_chart_pie_book_red(self):
""" create 3x cashbooks, add bookings, """ create 3x cashbooks, add bookings,
@ -719,7 +828,7 @@ class ReportTestCase(CashbookTestCase):
<field name="name"/> <field name="name"/>
</x> </x>
<y> <y>
<field name="balance" fill="1" empty="0" string="Cashbooks"/> <field name="balance" fill="1" empty="0" string="Cashbooks [Amount]"/>
</y> </y>
</graph> </graph>
""") """)

View file

@ -4,8 +4,9 @@ depends:
res res
cashbook cashbook
cashbook_bookcategory cashbook_bookcategory
extras_depend: #extras_depend:
dashboard dashboard
cashbook_investment
xml: xml:
icon.xml icon.xml
message.xml message.xml