# -*- 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 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 sel_etype = [ ('cashbooks', 'Cashbooks'), ('types', 'Types of Cashbooks'), ('currencies', 'Currencys'), #('category', 'Category'), ] 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', relation_name='cashbook_report.eval_book', origin='evaluation', target='cashbook', states={ 'invisible': Eval('dtype', '') != 'cashbooks', }, depends=['dtype']) types = fields.Many2Many(string='Types', relation_name='cashbook_report.eval_type', origin='evaluation', target='dtype', states={ 'invisible': Eval('dtype', '') != 'types', }, depends=['dtype']) currencies = fields.Many2Many(string='Currencies', relation_name='cashbook_report.eval_currency', origin='evaluation', target='currency', filter=[('cashbook_hasbookings', '=', True)], states={ '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(): return Transaction().context.get('company') or None @classmethod def default_posted(cls): """ default: False """ return False @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): """ default 'book' """ return 'cashbooks' @classmethod def default_chart(cls): """ default 'pie' """ return 'pie' @classmethod def write(cls, *args): """ unlink records if dtype changes """ to_write = [] actions = iter(args) for evaluations, values in zip(actions, actions): 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) # end Evaluation 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', 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(RelFieldsMixin, ModelSQL): 'Evaluation Type Relation' __name__ = 'cashbook_report.eval_type' 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(RelFieldsMixin, ModelSQL): 'Evaluation Currency Relation' __name__ = 'cashbook_report.eval_currency' 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