# This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. from sql.aggregate import Sum from sql.conditionals import Coalesce from trytond.model import ModelSQL, ModelView, Unique, fields from trytond.modules.account_budget import ( BudgetLineMixin, BudgetMixin, CopyBudgetMixin, CopyBudgetStartMixin) from trytond.pool import Pool from trytond.pyson import Eval from trytond.tools import reduce_ids from trytond.transaction import Transaction from trytond.wizard import Button, StateAction, StateView, Wizard class BudgetContext(ModelView): __name__ = 'analytic_account.budget.context' budget = fields.Many2One( 'analytic_account.budget', "Budget", required=True) @classmethod def default_budget(cls): pool = Pool() Budget = pool.get('analytic_account.budget') Date = pool.get('ir.date') context = Transaction().context if 'budget' in context: return context.get('budget') today = Date.today() budgets = Budget.search([ ('start_date', '>=', today), ('end_date', '<=', today), ], limit=1) if budgets: budget, = budgets return budget.id class Budget(BudgetMixin, ModelSQL, ModelView): __name__ = 'analytic_account.budget' start_date = fields.Date( "Start Date", required=True, domain=[('start_date', '<=', Eval('end_date'))]) end_date = fields.Date( "End Date", required=True, domain=[('end_date', '>=', Eval('start_date'))]) root = fields.Many2One( 'analytic_account.account', "Root", required=True, domain=[ ('company', '=', Eval('company', -1)), ('parent', '=', None), ('type', '=', 'root'), ], states={ 'readonly': Eval('root') & Eval('lines', [-1]), }) lines = fields.One2Many( 'analytic_account.budget.line', 'budget', "Lines", states={ 'readonly': Eval('id', -1) < 0, }, order=[('left', 'ASC'), ('id', 'ASC')]) root_lines = fields.One2Many( 'analytic_account.budget.line', 'budget', "Lines", states={ 'readonly': Eval('id', -1) < 0, }, filter=[ ('parent', '=', None), ]) @classmethod def __setup__(cls): super().__setup__() cls._order.insert(0, ('start_date', 'DESC')) cls._buttons.update({ 'update_lines': {}, 'copy_button': {}, }) def get_rec_name(self, name): pool = Pool() Lang = pool.get('ir.lang') lang = Lang.get() return '%s (%s - %s)' % ( self.name, lang.strftime(self.start_date), lang.strftime(self.end_date)) def _account_domain(self): return [ ('company', '=', self.company.id), ('root', '=', self.root.id), ('type', '=', 'normal'), ] @classmethod @ModelView.button def update_lines(cls, budgets): pool = Pool() Account = pool.get('analytic_account.account') Line = pool.get('analytic_account.budget.line') company_accounts = {} for budget in budgets: company = budget.company if company not in company_accounts: company_accounts[company] = set( Account.search(budget._account_domain())) lines = Line.search([ ('budget', '=', budget.id), ('account', '!=', None), ]) accounts2lines = {l.account: l for l in lines} lines = [] for account in sorted( company_accounts[company] - set(accounts2lines.keys()), key=lambda a: (a.code or '', a.name)): lines.append(Line(budget=budget, account=account)) Line.save(lines) for account, line in accounts2lines.items(): parent = accounts2lines.get(line.account.parent) if line.parent != parent: line.parent = parent Line.save(accounts2lines.values()) @classmethod @ModelView.button_action('analytic_budget.wizard_budget_copy') def copy_button(cls, budgets): pass class BudgetLine(BudgetLineMixin, ModelSQL, ModelView): __name__ = 'analytic_account.budget.line' budget = fields.Many2One( 'analytic_account.budget', "Budget", required=True, ondelete='CASCADE') account = fields.Many2One( 'analytic_account.account', "Account", domain=[ ('company', '=', Eval('company', -1)), ('root', '=', Eval('root', -1)), ('type', '=', 'normal'), ]) root = fields.Function(fields.Many2One( 'analytic_account.account', "Root"), 'on_change_with_root') @classmethod def __setup__(cls): super().__setup__() t = cls.__table__() cls._sql_constraints.append( ('budget_account_unique', Unique(t, t.budget, t.account), 'analytic_account.msg_budget_line_budget_account_unique')) cls.__access__.add('budget') @fields.depends('budget', '_parent_budget.root') def on_change_with_root(self, name=None): return self.budget.root if self.budget else None @classmethod def _get_amount_group_key(cls, record): return ( ('start_date', record.budget.start_date), ('end_date', record.budget.end_date), ) @classmethod def _get_amount_query(cls, records, context): pool = Pool() Line = pool.get('analytic_account.line') line = Line.__table__() table = cls.__table__() children = cls.__table__() balance = Sum(Coalesce(line.credit, 0) - Coalesce(line.debit, 0)) red_sql = reduce_ids(table.id, [r.id for r in records]) with Transaction().set_context(context): query_where = Line.query_get(line) return (table .join( children, condition=(children.left >= table.left) & (children.right <= table.right)) .join( line, condition=line.account == children.account) .select( table.id, balance.as_('amount'), where=red_sql & query_where, group_by=table.id)) class CopyBudget(CopyBudgetMixin, Wizard): __name__ = 'analytic_account.budget.copy' start = StateView('analytic_account.budget.copy.start', 'analytic_budget.budget_copy_start_view_form', [ Button("Cancel", 'end', 'tryton-cancel'), Button("Copy", 'copy', 'tryton-ok', default=True), ]) copy = StateAction('analytic_budget.act_budget_form') def default_start(self, field_names): values = super().default_start(field_names) values['start_date'] = self.record.start_date values['end_date'] = self.record.end_date return values def _copy_default(self): default = super()._copy_default() default['start_date'] = self.start.start_date default['end_date'] = self.start.end_date return default class CopyBudgetStart(CopyBudgetStartMixin, ModelView): __name__ = 'analytic_account.budget.copy.start' start_date = fields.Date( "Start Date", required=True, domain=[('start_date', '<=', Eval('end_date'))]) end_date = fields.Date( "End Date", required=True, domain=[('end_date', '>=', Eval('start_date'))])