diff --git a/evaluation.py b/evaluation.py
index d3c5591..16077c1 100644
--- a/evaluation.py
+++ b/evaluation.py
@@ -3,11 +3,14 @@
# 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
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
from .colors import sel_color as sel_bgcolor
@@ -57,6 +60,8 @@ class Evaluation(ModelSQL, ModelView):
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',
relation_name='cashbook_report.eval_book',
@@ -78,9 +83,27 @@ class Evaluation(ModelSQL, ModelView):
'invisible': Eval('dtype', '') != 'currencies',
}, depends=['dtype'])
+ cashbook_values = fields.One2Many(string='Cashbook Values',
+ field='evaluation', readonly=True,
+ model_name='cashbook_report.eval_book')
currency_values = fields.One2Many(string='Currency Values',
field='evaluation', readonly=True,
model_name='cashbook_report.eval_currency')
+ type_values = fields.One2Many(string='Type Values',
+ field='evaluation', readonly=True,
+ model_name='cashbook_report.eval_type')
+
+ @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
@staticmethod
def default_company():
@@ -169,56 +192,206 @@ class Evaluation(ModelSQL, ModelView):
# end Evaluation
-class EvaluationCashbookRel(ModelSQL):
- 'Evaluation Cashbook Relation'
- __name__ = 'cashbook_report.eval_book'
-
+class RelFieldsMixin(object):
+ """ common fields
+ """
evaluation = fields.Many2One(string='Evaluation', required=True,
select=True, ondelete='CASCADE',
model_name='cashbook_report.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')
+
+ @fields.depends('evaluation', '_parent_evaluation.currency')
+ def on_change_with_eval_currency(self, name=None):
+ """ currency of cashbook
+ """
+ if self.evaluation:
+ return self.evaluation.currency.id
+
+ @fields.depends('evaluation', '_parent_evaluation.currency')
+ def on_change_with_currency_digits(self, name=None):
+ """ currency of cashbook
+ """
+ if self.evaluation:
+ return self.evaluation.currency.digits
+ else:
+ return 2
+
+# end RelFieldsMixin
+
+
+class EvaluationCashbookRel(RelFieldsMixin, ModelSQL):
+ 'Evaluation Cashbook Relation'
+ __name__ = 'cashbook_report.eval_book'
+
cashbook = fields.Many2One(string='Cashbook', required=True,
select=True, ondelete='CASCADE',
model_name='cashbook.book')
+ 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 validate(cls, records):
""" check parent record
"""
super(EvaluationCashbookRel, cls).validate(records)
-
for record in records:
if record.evaluation.dtype != 'cashbooks':
raise UserError(gettext(
'cashbook_report.msg_invalid_dtype',
- dtype = gettext('cashbook_report.msg_dtype_cashbook'),
+ typename = gettext('cashbook_report.msg_dtype_cashbook'),
))
+ @fields.depends('cashbook')
+ def on_change_with_name(self, name=None):
+ """ get name of Type
+ """
+ if self.cashbook:
+ return self.cashbook.rec_name
+
+ @fields.depends('cashbook', '_parent_cashbook.currency', \
+ '_parent_cashbook.balance', 'eval_currency', 'currency_digits')
+ def on_change_with_balance(self, name=None):
+ """ balance of cashbook
+ """
+ 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)
+
# end EvaluationCashbookRel
-class EvaluationTypeRel(ModelSQL):
+class EvaluationTypeRel(RelFieldsMixin, ModelSQL):
'Evaluation Type Relation'
__name__ = 'cashbook_report.eval_type'
- evaluation = fields.Many2One(string='Evaluation', required=True,
- select=True, ondelete='CASCADE',
- model_name='cashbook_report.evaluation')
dtype = fields.Many2One(string='Type', required=True,
select=True, ondelete='CASCADE',
model_name='cashbook.type')
+ 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 validate(cls, records):
+ """ check parent record
+ """
+ super(EvaluationTypeRel, cls).validate(records)
+ for record in records:
+ if record.evaluation.dtype != 'types':
+ raise UserError(gettext(
+ 'cashbook_report.msg_invalid_dtype',
+ typename = gettext('cashbook_report.msg_dtype_type'),
+ ))
+
+ @fields.depends('dtype')
+ def on_change_with_name(self, name=None):
+ """ get name of Type
+ """
+ if self.dtype:
+ return self.dtype.rec_name
+
+ @fields.depends('evaluation', 'eval_currency', 'currency_digits', 'dtype')
+ def on_change_with_balance(self, name=None):
+ """ get balanceof 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)
+
# end EvaluationTypeRel
-class EvaluationCurrencyRel(ModelSQL):
+class EvaluationCurrencyRel(RelFieldsMixin, ModelSQL):
'Evaluation Currency Relation'
__name__ = 'cashbook_report.eval_currency'
- evaluation = fields.Many2One(string='Evaluation', required=True,
- select=True, ondelete='CASCADE',
- model_name='cashbook_report.evaluation')
currency = fields.Many2One(string='Currency', required=True,
select=True, ondelete='CASCADE',
model_name='currency.currency')
+ 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 validate(cls, records):
+ """ check parent record
+ """
+ super(EvaluationCurrencyRel, cls).validate(records)
+ for record in records:
+ if record.evaluation.dtype != 'currencies':
+ raise UserError(gettext(
+ 'cashbook_report.msg_invalid_dtype',
+ typename = gettext('cashbook_report.msg_dtype_currency'),
+ ))
+
+ @fields.depends('currency')
+ def on_change_with_name(self, name=None):
+ """ get name of Type
+ """
+ if self.currency:
+ return self.currency.rec_name
+
+ def on_change_with_balance(self, name=None):
+ """ balance of cashbook
+ """
+ return None
# end EvaluationCurrencyRel
diff --git a/locale/de.po b/locale/de.po
index fbbf608..a5b3023 100644
--- a/locale/de.po
+++ b/locale/de.po
@@ -7,13 +7,21 @@ msgstr "Content-Type: text/plain; charset=utf-8\n"
# ir.message #
##############
msgctxt "model:ir.message,text:msg_name_cashbook"
-msgid "Type of evaluation must be '%(dtype)s'."
-msgstr "Typ der Auswertung mus '%(dtype)s' sein."
+msgid "Type of evaluation must be '%(typename)s'."
+msgstr "Typ der Auswertung mus '%(typename)s' sein."
msgctxt "model:ir.message,text:msg_dtype_cashbook"
msgid "Cashbooks"
msgstr "Kassenbücher"
+msgctxt "model:ir.message,text:msg_dtype_type"
+msgid "Types of Cashbooks"
+msgstr "Typen von Kassenbüchern"
+
+msgctxt "model:ir.message,text:msg_dtype_currency"
+msgid "Currencies"
+msgstr "Währungen"
+
#################
# ir.rule.group #
@@ -54,6 +62,14 @@ msgctxt "field:cashbook_report.eval_book,cashbook:"
msgid "Cashbook"
msgstr "Kassenbuch"
+msgctxt "field:cashbook_report.eval_book,eval_currency:"
+msgid "Currency"
+msgstr "Währung"
+
+msgctxt "field:cashbook_report.eval_book,currency_digits:"
+msgid "Currency Digits"
+msgstr "Nachkommastellen Währung"
+
#############################
# cashbook_report.eval_type #
@@ -70,6 +86,22 @@ msgctxt "field:cashbook_report.eval_type,dtype:"
msgid "Type"
msgstr "Typ"
+msgctxt "field:cashbook_report.eval_type,name:"
+msgid "Name"
+msgstr "Name"
+
+msgctxt "field:cashbook_report.eval_type,balance:"
+msgid "Balance"
+msgstr "Saldo"
+
+msgctxt "field:cashbook_report.eval_type,eval_currency:"
+msgid "Currency"
+msgstr "Währung"
+
+msgctxt "field:cashbook_report.eval_type,currency_digits:"
+msgid "Currency Digits"
+msgstr "Nachkommastellen Währung"
+
#################################
# cashbook_report.eval_currency #
@@ -86,6 +118,14 @@ msgctxt "field:cashbook_report.eval_currency,currency:"
msgid "Currency"
msgstr "Währung"
+msgctxt "field:cashbook_report.eval_currency,eval_currency:"
+msgid "Currency"
+msgstr "Währung"
+
+msgctxt "field:cashbook_report.eval_currency,currency_digits:"
+msgid "Currency Digits"
+msgstr "Nachkommastellen Währung"
+
##############################
# cashbook_report.evaluation #
@@ -210,10 +250,26 @@ msgctxt "field:cashbook_report.evaluation,cashbooks:"
msgid "Cashbooks"
msgstr "Kassenbücher"
+msgctxt "field:cashbook_report.evaluation,cashbook_values:"
+msgid "Cashbook Values"
+msgstr "Kassenbuchwerte"
+
msgctxt "field:cashbook_report.evaluation,types:"
msgid "Types"
msgstr "Typen"
+msgctxt "field:cashbook_report.evaluation,type_values:"
+msgid "Type Values"
+msgstr "Typwerte"
+
msgctxt "field:cashbook_report.evaluation,currencies:"
msgid "Currencies"
msgstr "Währungen"
+
+msgctxt "field:cashbook_report.evaluation,currency_values:"
+msgid "Currency Values"
+msgstr "Währungswerte"
+
+msgctxt "field:cashbook_report.evaluation,currency:"
+msgid "Currency"
+msgstr "Währung"
diff --git a/locale/en.po b/locale/en.po
index c26ba5d..975a1a4 100644
--- a/locale/en.po
+++ b/locale/en.po
@@ -3,13 +3,21 @@ msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "model:ir.message,text:msg_name_cashbook"
-msgid "Type of evaluation must be '%(dtype)s'."
-msgstr "Type of evaluation must be '%(dtype)s'."
+msgid "Type of evaluation must be '%(typename)s'."
+msgstr "Type of evaluation must be '%(typename)s'."
msgctxt "model:ir.message,text:msg_dtype_cashbook"
msgid "Cashbooks"
msgstr "Cashbooks"
+msgctxt "model:ir.message,text:msg_dtype_type"
+msgid "Types of Cashbooks"
+msgstr "Types of Cashbooks"
+
+msgctxt "model:ir.message,text:msg_dtype_currency"
+msgid "Currencies"
+msgstr "Currencies"
+
msgctxt "model:ir.rule.group,name:rg_eval_write_adm"
msgid "Administrators: Evaluation read/write"
msgstr "Administrators: Evaluation read/write"
@@ -38,6 +46,14 @@ msgctxt "field:cashbook_report.eval_book,cashbook:"
msgid "Cashbook"
msgstr "Cashbook"
+msgctxt "field:cashbook_report.eval_book,eval_currency:"
+msgid "Currency"
+msgstr "Currency"
+
+msgctxt "field:cashbook_report.eval_book,currency_digits:"
+msgid "Currency Digits"
+msgstr "Currency Digits"
+
msgctxt "model:cashbook_report.eval_type,name:"
msgid "Evaluation Type Relation"
msgstr "Evaluation Type Relation"
@@ -50,6 +66,22 @@ msgctxt "field:cashbook_report.eval_type,dtype:"
msgid "Type"
msgstr "Type"
+msgctxt "field:cashbook_report.eval_type,name:"
+msgid "Name"
+msgstr "Name"
+
+msgctxt "field:cashbook_report.eval_type,balance:"
+msgid "Balance"
+msgstr "Balance"
+
+msgctxt "field:cashbook_report.eval_type,eval_currency:"
+msgid "Currency"
+msgstr "Currency"
+
+msgctxt "field:cashbook_report.eval_type,currency_digits:"
+msgid "Currency Digits"
+msgstr "Currency Digits"
+
msgctxt "model:cashbook_report.eval_currency,name:"
msgid "Evaluation Currency Relation"
msgstr "Evaluation Currency Relation"
@@ -62,6 +94,14 @@ msgctxt "field:cashbook_report.eval_currency,currency:"
msgid "Currency"
msgstr "Currency"
+msgctxt "field:cashbook_report.eval_currency,eval_currency:"
+msgid "Currency"
+msgstr "Currency"
+
+msgctxt "field:cashbook_report.eval_currency,currency_digits:"
+msgid "Currency Digits"
+msgstr "Currency Digits"
+
msgctxt "model:cashbook_report.evaluation,name:"
msgid "Evaluation"
msgstr "Evaluation"
@@ -186,3 +226,15 @@ msgctxt "field:cashbook_report.evaluation,types:"
msgid "Types"
msgstr "Types"
+msgctxt "field:cashbook_report.evaluation,type_values:"
+msgid "Type Values"
+msgstr "Type Values"
+
+msgctxt "field:cashbook_report.evaluation,currencies:"
+msgid "Currencies"
+msgstr "Currencies"
+
+msgctxt "field:cashbook_report.evaluation,currency_values:"
+msgid "Currency Values"
+msgstr "Currency Values"
+
diff --git a/message.xml b/message.xml
index 29da0b2..dcfbe13 100644
--- a/message.xml
+++ b/message.xml
@@ -6,11 +6,17 @@ full copyright notices and license terms. -->
- Type of evaluation must be '%(dtype)s'.
+ Type of evaluation must be '%(typename)s'.
Cashbooks
+
+ Types of Cashbooks
+
+
+ Currencies
+
diff --git a/tests/test_report.py b/tests/test_report.py
index 4e5fc4b..de924a8 100644
--- a/tests/test_report.py
+++ b/tests/test_report.py
@@ -265,6 +265,22 @@ class ReportTestCase(CashbookTestCase):
'cashbooks': [('add', [x.id for x in books])],
}])
+ evaluation, = Evaluation.create([{
+ 'name': 'Evaluation 2',
+ 'dtype': 'types',
+ 'types': [('add', [x.id for x in Types.search([])])],
+ }])
+
+ # must fail
+ self.assertRaisesRegex(UserError,
+ "Type of evaluation must be 'Types of Cashbooks'.",
+ Evaluation.create,
+ [{
+ 'name': 'Evaluation 3',
+ 'dtype': 'cashbooks',
+ 'types': [('add', [x.id for x in Types.search([])])],
+ }])
+
@with_transaction()
def test_report_chart_pie_book_red(self):
""" create 3x cashbooks, add bookings,
@@ -288,11 +304,28 @@ class ReportTestCase(CashbookTestCase):
self.assertEqual(evaluation.posted, False)
self.assertEqual(evaluation.maincolor, 'default')
self.assertEqual(evaluation.bgcolor, '#ffffc0')
+ self.assertEqual(evaluation.currency.code, 'EUR')
self.assertEqual(len(evaluation.cashbooks), 3)
self.assertEqual(evaluation.cashbooks[0].rec_name, 'Book 1 | 25.00 usd | Open')
self.assertEqual(evaluation.cashbooks[1].rec_name, 'Book 2 | 12.50 usd | Open')
self.assertEqual(evaluation.cashbooks[2].rec_name, 'Book 3 | 23.00 € | Open')
+ self.assertEqual(evaluation.cashbooks[0].currency.code, 'usd')
+ self.assertEqual(evaluation.cashbooks[1].currency.code, 'usd')
+ self.assertEqual(evaluation.cashbooks[2].currency.code, 'EUR')
+
+ self.assertEqual(len(evaluation.cashbook_values), 3)
+ self.assertEqual(evaluation.cashbook_values[0].name, 'Book 1 | 25.00 usd | Open')
+ self.assertEqual(evaluation.cashbook_values[1].name, 'Book 2 | 12.50 usd | Open')
+ self.assertEqual(evaluation.cashbook_values[2].name, 'Book 3 | 23.00 € | Open')
+
+ self.assertEqual(evaluation.cashbook_values[0].eval_currency.code, 'EUR')
+ self.assertEqual(evaluation.cashbook_values[1].eval_currency.code, 'EUR')
+ self.assertEqual(evaluation.cashbook_values[2].eval_currency.code, 'EUR')
+
+ self.assertEqual(evaluation.cashbook_values[0].balance, Decimal('23.81'))
+ self.assertEqual(evaluation.cashbook_values[1].balance, Decimal('11.90'))
+ self.assertEqual(evaluation.cashbook_values[2].balance, Decimal('23.00'))
@with_transaction()
def test_report_chart_pie_type_red(self):
@@ -307,7 +340,8 @@ class ReportTestCase(CashbookTestCase):
company = self.prep_company()
with Transaction().set_context({
- 'company': company.id,
+ 'company': company.id, # company-currency: EUR
+ 'date': date(2022, 5, 15),
}):
evaluation, = Evaluation.create([{
'name': 'Evaluation 1',
@@ -320,8 +354,23 @@ class ReportTestCase(CashbookTestCase):
self.assertEqual(evaluation.posted, False)
self.assertEqual(evaluation.maincolor, 'default')
self.assertEqual(evaluation.bgcolor, '#ffffc0')
+ self.assertEqual(evaluation.currency.code, 'EUR')
+ # 37.50 USD, Cash
+ # 23.00 EUR, Bank
self.assertEqual(len(evaluation.types), 2)
+ self.assertEqual(evaluation.types[0].rec_name, 'BK - Bank')
+ self.assertEqual(evaluation.types[1].rec_name, 'CAS - Cash')
+ # 23.00 EUR
+ self.assertEqual(len(evaluation.type_values), 2)
+ self.assertEqual(evaluation.type_values[0].eval_currency.code, 'EUR')
+ self.assertEqual(evaluation.type_values[0].name, 'BK - Bank')
+ self.assertEqual(evaluation.type_values[0].balance, Decimal('23.0'))
+
+ # 37.50 USD --> EUR
+ self.assertEqual(evaluation.type_values[1].name, 'CAS - Cash')
+ self.assertEqual(evaluation.type_values[1].eval_currency.code, 'EUR')
+ self.assertEqual(evaluation.type_values[1].balance, Decimal('35.71'))
@with_transaction()
def test_report_chart_pie_currency_red(self):
diff --git a/view/evaluation_form.xml b/view/evaluation_form.xml
index e7b804b..ab681d7 100644
--- a/view/evaluation_form.xml
+++ b/view/evaluation_form.xml
@@ -13,7 +13,8 @@ full copyright notices and license terms. -->
-
+
+