120 lines
5.6 KiB
Python
120 lines
5.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""Мастер формирования оборотно-сальдовой ведомости (ОСВ)."""
|
||
from datetime import timedelta
|
||
|
||
from odoo import api, fields, models
|
||
from odoo.tools import float_is_zero
|
||
|
||
|
||
class RsbuTrialBalanceWizard(models.TransientModel):
|
||
_name = 'rsbu.trial.balance.wizard'
|
||
_description = 'РСБУ — Мастер оборотно-сальдовой ведомости'
|
||
|
||
company_id = fields.Many2one(
|
||
'res.company', string='Компания', required=True,
|
||
default=lambda self: self.env.company)
|
||
date_from = fields.Date(
|
||
string='Период с', required=True,
|
||
default=lambda self: fields.Date.today().replace(month=1, day=1))
|
||
date_to = fields.Date(
|
||
string='Период по', required=True,
|
||
default=lambda self: fields.Date.today())
|
||
hide_zero = fields.Boolean(
|
||
string='Скрывать счета без данных', default=True,
|
||
help='Не показывать счета с нулевыми сальдо и оборотами.')
|
||
only_posted = fields.Boolean(
|
||
string='Только проведённые', default=True, readonly=True,
|
||
help='ОСВ строится по проведённым проводкам (иных в РСБУ-журнале '
|
||
'к учёту не принимается).')
|
||
line_ids = fields.One2many(
|
||
'rsbu.trial.balance.line', 'wizard_id', string='Строки ОСВ')
|
||
|
||
# ------------------------------------------------------------------
|
||
def _iter_accounts(self):
|
||
"""Счета компании для ОСВ (все, порядок по коду)."""
|
||
self.ensure_one()
|
||
return self.env['rsbu.account'].search([
|
||
('company_id', '=', self.company_id.id),
|
||
], order='complete_code, code')
|
||
|
||
def _account_values(self, account):
|
||
"""Шесть показателей ОСВ по счёту (развёрнутое сальдо для АП)."""
|
||
self.ensure_one()
|
||
cid = self.company_id.id
|
||
day_before = self.date_from - timedelta(days=1)
|
||
# Сальдо на начало (до date_from) — развёрнутое по субконто.
|
||
opening = account.get_expanded_balance(
|
||
date_to=day_before, company_id=cid)
|
||
# Обороты за период (по поддереву счёта).
|
||
turnover = account.get_turnover(
|
||
date_from=self.date_from, date_to=self.date_to,
|
||
company_id=cid, include_children=True)
|
||
# Сальдо на конец (до date_to включительно) — развёрнутое.
|
||
closing = account.get_expanded_balance(
|
||
date_to=self.date_to, company_id=cid)
|
||
return {
|
||
'opening_debit': opening['debit'],
|
||
'opening_credit': opening['credit'],
|
||
'turnover_debit': turnover['debit'],
|
||
'turnover_credit': turnover['credit'],
|
||
'closing_debit': closing['debit'],
|
||
'closing_credit': closing['credit'],
|
||
}
|
||
|
||
def _is_zero_row(self, vals):
|
||
rounding = self.company_id.currency_id.rounding or 0.01
|
||
return all(float_is_zero(vals[f], precision_rounding=rounding)
|
||
for f in ('opening_debit', 'opening_credit',
|
||
'turnover_debit', 'turnover_credit',
|
||
'closing_debit', 'closing_credit'))
|
||
|
||
def action_generate(self):
|
||
"""Пересчитать строки ОСВ и открыть отчёт на экране (QWeb-HTML)."""
|
||
self.ensure_one()
|
||
self.line_ids.unlink()
|
||
Line = self.env['rsbu.trial.balance.line']
|
||
currency = self.company_id.currency_id
|
||
vals_list = []
|
||
for account in self._iter_accounts():
|
||
vals = self._account_values(account)
|
||
if self.hide_zero and self._is_zero_row(vals):
|
||
continue
|
||
vals.update({
|
||
'wizard_id': self.id,
|
||
'account_id': account.id,
|
||
'code': account.code,
|
||
'name': account.name,
|
||
'level': account.level,
|
||
'is_group': bool(account.child_ids),
|
||
'parent_code': account.parent_id.code or False,
|
||
'company_id': self.company_id.id,
|
||
'currency_id': currency.id,
|
||
})
|
||
vals_list.append(vals)
|
||
Line.create(vals_list)
|
||
return self.action_view_report()
|
||
|
||
def action_view_report(self):
|
||
"""Открыть отчёт ОСВ (QWeb-HTML) для текущего мастера."""
|
||
self.ensure_one()
|
||
return self.env.ref(
|
||
'rsbu_journal.action_report_rsbu_trial_balance').report_action(self)
|
||
|
||
# ------------------------------------------------------------------
|
||
def _report_totals(self):
|
||
"""Итоги ОСВ — только по счетам верхнего уровня (без двойного счёта).
|
||
|
||
Итог считается по корневым счетам (level 0), чтобы не суммировать
|
||
родителя и его потомков дважды.
|
||
"""
|
||
self.ensure_one()
|
||
roots = self.line_ids.filtered(lambda l: not l.parent_code)
|
||
return {
|
||
'opening_debit': sum(roots.mapped('opening_debit')),
|
||
'opening_credit': sum(roots.mapped('opening_credit')),
|
||
'turnover_debit': sum(roots.mapped('turnover_debit')),
|
||
'turnover_credit': sum(roots.mapped('turnover_credit')),
|
||
'closing_debit': sum(roots.mapped('closing_debit')),
|
||
'closing_credit': sum(roots.mapped('closing_credit')),
|
||
}
|