cashbook_report/evaluation.py

513 lines
18 KiB
Python
Raw Normal View History

2022-10-28 11:27:33 +00:00
# -*- 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 decimal import Decimal
from sql.aggregate import Sum
2022-10-28 11:27:33 +00:00
from trytond.model import ModelView, ModelSQL, fields, Check
from trytond.pyson import Eval, Or, Bool, Id, Len
from trytond.transaction import Transaction
from trytond.i18n import gettext
from trytond.exceptions import UserError
from trytond.pool import Pool
2022-10-28 11:27:33 +00:00
from .colors import sel_color as sel_bgcolor
2022-10-31 19:45:40 +00:00
from .templates import template_view_graph, template_view_line
2022-10-28 11:27:33 +00:00
sel_etype = [
('cashbooks', 'Cashbooks'),
('types', 'Types of Cashbooks'),
('currencies', 'Currencys'),
#('category', 'Category'),
2022-10-28 11:27:33 +00:00
]
sel_chart = [
('vbar', 'Vertical Bars'),
('hbar', 'Horizontal Bars'),
('pie', 'Pie'),
('line', 'Line'),
]
sel_maincolor = [
('default', 'Default'),
('red', 'Red'),
('green', 'Green'),
('grey', 'Grey'),
('black', 'Black'),
('darkcyan', 'Dark Cyan'),
]
class Evaluation(ModelSQL, ModelView):
'Evaluation'
__name__ = 'cashbook_report.evaluation'
company = fields.Many2One(string='Company', model_name='company.company',
required=True, ondelete="RESTRICT")
name = fields.Char(string='Name', required=True)
dtype = fields.Selection(string='Data type', required=True,
sort=False, selection=sel_etype,
help='Type of data displayed')
chart = fields.Selection(string='Chart type', required=True,
sort=False, selection=sel_chart,
help='Type of graphical presentation.')
legend = fields.Boolean(string='Legend')
maincolor = fields.Selection(string='Color scheme', required=True,
help='The color scheme determines the hue of all components of the chart.',
selection=sel_maincolor, sort=False)
bgcolor = fields.Selection(string='Background Color', required=True,
help='Background color of the chart area.', sort=False,
selection=sel_bgcolor)
posted = fields.Boolean(string='Posted', help='Posted amounts only.')
currency = fields.Many2One(string='Currency', ondelete='RESTRICT',
model_name='currency.currency')
cashbooks = fields.Many2Many(string='Cashbooks',
2022-11-03 22:28:57 +00:00
relation_name='cashbook_report.eval_line',
origin='evaluation', target='cashbook',
states={
'invisible': Eval('dtype', '') != 'cashbooks',
}, depends=['dtype'])
types = fields.Many2Many(string='Types',
2022-11-03 22:28:57 +00:00
relation_name='cashbook_report.eval_line',
origin='evaluation', target='dtype',
states={
'invisible': Eval('dtype', '') != 'types',
}, depends=['dtype'])
currencies = fields.Many2Many(string='Currencies',
2022-11-03 22:28:57 +00:00
relation_name='cashbook_report.eval_line',
origin='evaluation', target='currency',
filter=[('cashbook_hasbookings', '=', True)],
states={
'invisible': Eval('dtype', '') != 'currencies',
}, depends=['dtype'])
2022-11-03 22:28:57 +00:00
line_values = fields.One2Many(string='Line Values',
field='evaluation', readonly=True,
2022-11-03 22:28:57 +00:00
model_name='cashbook_report.eval_line')
ui_view_chart = fields.Many2One(string='UI View Point',
2022-10-31 19:45:40 +00:00
model_name='ir.ui.view', ondelete='SET NULL')
@classmethod
def default_currency(cls):
""" currency of company
"""
Company = Pool().get('company.company')
company = cls.default_company()
if company:
company = Company(company)
if company.currency:
return company.currency.id
2022-10-28 11:27:33 +00:00
@staticmethod
def default_company():
return Transaction().context.get('company') or None
@classmethod
def default_posted(cls):
""" default: False
"""
return False
2022-10-28 11:27:33 +00:00
@classmethod
def default_bgcolor(cls):
""" default: Yellow 5
"""
return '#ffffc0'
@classmethod
def default_maincolor(cls):
""" default: 'default'
"""
return 'default'
@classmethod
def default_legend(cls):
""" default True
"""
return True
@classmethod
def default_dtype(cls):
2022-10-28 11:27:33 +00:00
""" default 'book'
"""
return 'cashbooks'
2022-10-28 11:27:33 +00:00
@classmethod
def default_chart(cls):
""" default 'pie'
2022-10-28 11:27:33 +00:00
"""
return 'pie'
@classmethod
def uiview_delete(cls, evaluations):
""" delete action view from evalualtion
"""
pool = Pool()
UiView = pool.get('ir.ui.view')
to_delete_uiview = []
for evaluation in evaluations:
if evaluation.ui_view_chart:
to_delete_uiview.append(evaluation.ui_view_chart)
if len(to_delete_uiview) > 0:
UiView.delete(to_delete_uiview)
2022-10-31 19:45:40 +00:00
@classmethod
def uiview_create(cls, evaluations):
""" create ui view for current setup of evaluation
2022-10-31 19:45:40 +00:00
"""
pool = Pool()
UiView = pool.get('ir.ui.view')
Evaluation2 = pool.get('cashbook_report.evaluation')
2022-10-31 19:45:40 +00:00
cls.uiview_delete(evaluations)
2022-10-31 19:45:40 +00:00
to_write_eval = []
for evaluation in evaluations:
if evaluation.dtype:
# skip if no data to show
if len(getattr(evaluation, evaluation.dtype, [])) == 0:
2022-10-31 19:45:40 +00:00
continue
view_graph, = UiView.create([{
'model': 'cashbook_report.%s' % {
'cashbooks': 'eval_book',
'types': 'eval_type',
'currencies': 'eval_currency',
2022-10-31 19:45:40 +00:00
}[evaluation.dtype],
'module': 'cashbook_report',
'priority': 10,
'type': 'graph',
'data': template_view_graph % {
'bgcol': '' if evaluation.bgcolor == 'default' \
else 'background="%s"' % evaluation.bgcolor,
'legend': '1' if evaluation.legend == True else '0',
'type': evaluation.chart,
'colscheme': '' if evaluation.maincolor == 'default' \
else 'color="%s"' % evaluation.maincolor,
2022-10-31 19:45:40 +00:00
'lines': template_view_line % {
'fill': '1',
'string': evaluation.dtype,
},
},
}])
to_write_eval.extend([
[evaluation],
2022-10-31 19:45:40 +00:00
{
'ui_view_chart': view_graph.id,
2022-10-31 19:45:40 +00:00
}])
if len(to_write_eval) > 0:
Evaluation2.write(*to_write_eval)
@classmethod
def create(cls, vlist):
""" add chart
"""
records = super(Evaluation, cls).create(vlist)
cls.uiview_create(records)
return records
2022-10-31 19:45:40 +00:00
@classmethod
def write(cls, *args):
""" unlink records if dtype changes
"""
to_write = []
actions = iter(args)
to_update_uiview = []
for evaluations, values in zip(actions, actions):
# update ui-view if related fields change
if len(set({'name', 'dtype', 'bgcolor', 'maincolor',
'legend', 'chart'}).intersection(values.keys())) > 0:
to_update_uiview.extend(evaluations)
if 'dtype' in values.keys():
for evaluation in evaluations:
if evaluation.dtype == values['dtype']:
continue
if (values['dtype'] != 'cashbooks') and \
(len(evaluation.cashbooks) > 0):
to_write.extend([
[evaluation],
{
'cashbooks': [
('remove', [x.id for x in evaluation.cashbooks])
],
}])
if (values['dtype'] != 'types') and (len(evaluation.types) > 0):
to_write.extend([
[evaluation],
{
'types': [
('remove', [x.id for x in evaluation.types])
],
}])
if (values['dtype'] != 'currencies') and (len(evaluation.currencies) > 0):
to_write.extend([
[evaluation],
{
'currencies': [
('remove', [x.id for x in evaluation.currencies])
],
}])
args = list(args)
args.extend(to_write)
super(Evaluation, cls).write(*args)
if len(to_update_uiview) > 0:
cls.uiview_create(to_update_uiview)
@classmethod
def delete(cls, evaluations):
""" delete views
"""
cls.uiview_delete(evaluations)
super(Evaluation, cls).delete(evaluations)
2022-10-28 11:27:33 +00:00
# end Evaluation
2022-11-03 22:28:57 +00:00
class EvaluationLineRel(ModelSQL):
'Evaluation Line Relation'
__name__ = 'cashbook_report.eval_line'
evaluation = fields.Many2One(string='Evaluation', required=True,
select=True, ondelete='CASCADE',
model_name='cashbook_report.evaluation')
2022-11-03 22:28:57 +00:00
cashbook = fields.Many2One(string='Cashbook', select=True, ondelete='CASCADE',
model_name='cashbook.book',
states={
'required': Eval('eval_dtype', '') == 'cashbooks',
}, depends=['eval_dtype'])
dtype = fields.Many2One(string='Type', select=True, ondelete='CASCADE',
model_name='cashbook.type',
states={
'required': Eval('eval_dtype', '') == 'types',
}, depends=['eval_dtype'])
currency = fields.Many2One(string='Currency', select=True, ondelete='CASCADE',
model_name='currency.currency',
states={
'required': Eval('eval_dtype', '') == 'currencies',
}, depends=['eval_dtype'])
# currency of evaluation
eval_currency = fields.Function(fields.Many2One(model_name='currency.currency',
string="Currency", readonly=True), 'on_change_with_eval_currency')
currency_digits = fields.Function(fields.Integer(string='Currency Digits',
readonly=True), 'on_change_with_currency_digits')
2022-11-03 22:28:57 +00:00
eval_dtype = fields.Function(fields.Char(string='Data type', readonly=True),
'on_change_with_eval_dtype')
name = fields.Function(fields.Char(string='Name', readonly=True),
'on_change_with_name')
balance = fields.Function(fields.Numeric(string='Balance',
readonly=True, digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits']),
'on_change_with_balance')
@classmethod
def fields_view_get(cls, view_id, view_type='form'):
""" replace form-view-id
"""
pool = Pool()
ModelData = pool.get('ir.model.data')
Evaluation = pool.get('cashbook_report.evaluation')
context = Transaction().context
# get id of origin chart-form
form_id = ModelData.get_id('cashbook_report', 'point_view_graph')
# active_chart was added by tree_open-action
active_evaluation = context.get('active_evaluation', None)
# check if we are requested for our default form...
if (view_type == 'graph') and (view_id == form_id) and \
(active_evaluation is not None):
evaluation, = Evaluation.browse([active_evaluation])
if evaluation.ui_view_point:
# ... switch to view, created by evaluation-config
view_id = evaluation.ui_view_point.id
return super(EvaluationLineRel, cls).fields_view_get(
view_id=view_id, view_type=view_type)
@fields.depends('evaluation', '_parent_evaluation.dtype')
def on_change_with_eval_dtype(self, name=None):
""" get dtape from parent
"""
if self.evaluation:
return self.evaluation.dtype
@fields.depends('evaluation', '_parent_evaluation.currency')
def on_change_with_eval_currency(self, name=None):
2022-11-03 22:28:57 +00:00
""" currency of evaluation
"""
if self.evaluation:
return self.evaluation.currency.id
@fields.depends('evaluation', '_parent_evaluation.currency')
def on_change_with_currency_digits(self, name=None):
2022-11-03 22:28:57 +00:00
""" currency-digits of evaluation
"""
if self.evaluation:
return self.evaluation.currency.digits
else:
return 2
@classmethod
def validate(cls, records):
""" check parent record
"""
2022-11-03 22:28:57 +00:00
super(EvaluationLineRel, cls).validate(records)
for record in records:
2022-11-03 22:28:57 +00:00
if (record.evaluation.dtype != 'cashbooks') and \
(record.cashbook is not None):
raise UserError(gettext(
'cashbook_report.msg_invalid_dtype',
typename = gettext('cashbook_report.msg_dtype_cashbook'),
))
2022-11-03 22:28:57 +00:00
if (record.evaluation.dtype != 'types') and \
(record.dtype is not None):
raise UserError(gettext(
'cashbook_report.msg_invalid_dtype',
typename = gettext('cashbook_report.msg_dtype_type'),
))
if (record.evaluation.dtype != 'currencies') and \
(record.currency is not None):
raise UserError(gettext(
'cashbook_report.msg_invalid_dtype',
typename = gettext('cashbook_report.msg_dtype_currency'),
))
2022-11-03 22:28:57 +00:00
@fields.depends('eval_dtype', 'cashbook', 'dtype', 'currency')
def on_change_with_name(self, name=None):
""" get name of Type
"""
2022-11-03 22:28:57 +00:00
if self.eval_dtype:
return getattr(
getattr(self, {
'cashbooks': 'cashbook',
'types': 'dtype',
'currencies': 'currency',
}[self.eval_dtype], None),
'rec_name', None)
def get_value_cashbooks(self):
""" balance of cashbooks
"""
Currency = Pool().get('currency.currency')
if self.cashbook:
exp = Decimal(Decimal(1) / 10 ** self.currency_digits)
return Currency.compute(
self.cashbook.currency,
self.cashbook.balance,
self.eval_currency,
).quantize(exp)
2022-11-03 22:28:57 +00:00
def get_value_types(self):
2022-10-31 17:48:20 +00:00
""" get balance of bookings in cashbooks by 'type',
converted to currency of evaluation
"""
pool = Pool()
Lines = pool.get('cashbook.line')
Cashbook = pool.get('cashbook.book')
Currency = pool.get('currency.currency')
tab_line = Lines.__table__()
tab_book = Cashbook.__table__()
cursor = Transaction().connection.cursor()
if (self.evaluation is None) or (self.dtype is None) or \
(self.eval_currency is None) or (self.currency_digits is None):
return None
lines = Lines.search([
('cashbook.btype.id', '=', self.dtype.id),
('cashbook.state', '=', 'open'),
('cashbook.owner.id', '=', Transaction().user),
], query=True)
query = lines.join(tab_line, condition=lines.id==tab_line.id,
).join(tab_book, condition=tab_book.id==tab_line.cashbook,
).select(
tab_book.currency,
Sum(tab_line.credit - tab_line.debit).as_('balance'),
group_by=[tab_book.currency],
)
cursor.execute(*query)
balances = cursor.fetchall()
total_amount = Decimal('0.0')
for balance in balances:
(id_currency, bal1) = balance
total_amount += Currency.compute(
Currency(id_currency),
bal1,
self.eval_currency,
)
exp = Decimal(Decimal(1) / 10 ** self.currency_digits)
return total_amount.quantize(exp)
2022-11-03 22:28:57 +00:00
def get_value_currencies(self):
2022-10-31 17:48:20 +00:00
""" get balance of bookings in cashbooks by 'currency',
converted to currency of evaluation
"""
2022-10-31 17:48:20 +00:00
pool = Pool()
Lines = pool.get('cashbook.line')
Currency = pool.get('currency.currency')
tab_line = Lines.__table__()
cursor = Transaction().connection.cursor()
if (self.evaluation is None) or (self.currency is None) or \
(self.eval_currency is None) or (self.currency_digits is None):
return None
lines = Lines.search([
('cashbook.currency.id', '=', self.currency.id),
('cashbook.state', '=', 'open'),
('cashbook.owner.id', '=', Transaction().user),
], query=True)
query = lines.join(tab_line, condition=lines.id==tab_line.id,
).select(
Sum(tab_line.credit - tab_line.debit).as_('balance'),
)
cursor.execute(*query)
balances = cursor.fetchall()
total_amount = Decimal('0.0')
for balance in balances:
(bal1,) = balance
total_amount += Currency.compute(
self.currency,
bal1,
self.eval_currency,
)
exp = Decimal(Decimal(1) / 10 ** self.currency_digits)
return total_amount.quantize(exp)
2022-11-03 22:28:57 +00:00
@fields.depends('eval_dtype', 'eval_currency', 'currency_digits', \
'cashbook', '_parent_cashbook.currency', '_parent_cashbook.balance',\
'evaluation', 'dtype', 'currency')
def on_change_with_balance(self, name=None):
""" balance of cashbook
"""
if self.eval_dtype:
return getattr(self, 'get_value_%s' % self.eval_dtype)()
# end EvaluationLineRel