Files
tradon/modules/analytic_budget/analytic_account.py
2026-03-14 09:42:12 +00:00

231 lines
7.6 KiB
Python

# 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'))])