Public release from ruodoo-project: 19.0 - 2026-11-01 22:52:25 UTC
This commit is contained in:
145
rsbu_fns_reporting/models/rsbu_nds_declaration.py
Normal file
145
rsbu_fns_reporting/models/rsbu_nds_declaration.py
Normal file
@@ -0,0 +1,145 @@
|
||||
"""Декларация по НДС (КНД 1151001, УСН + спецставка НДS 5%/7%).
|
||||
|
||||
Комплект из трёх XML: корневая декларация (раздел «НДС» — СумУплНП/СумУпл164)
|
||||
и разделы 8 (книга покупок) и 9 (книга продаж). Расчёт — из готовых
|
||||
регистров НДС: книги (rsbu.vat.book.line) и итоги (rsbu.vat.summary).
|
||||
|
||||
Начисленный НДС раскладывается на реализацию (РеалТов5) и авансы
|
||||
(ОплПредПост): авансовый НДС определяется по наличию в проводке-источнике
|
||||
начисления строки счёта авансового НДС (76.АВ). Вычет — книга покупок
|
||||
(вычет авансового НДС при отгрузке).
|
||||
|
||||
Постоянная модель (не транзиент): сохраняется, переоткрывается, хранит
|
||||
рассчитанные показатели. XML — в rsbu_nds_xml. Свежесть: выгрузка XML
|
||||
всегда выполняет пересчёт (см. rsbu_nds_xml.action_export_xml).
|
||||
"""
|
||||
from odoo import api, fields, models
|
||||
|
||||
# Расчётная доля НДС в авансе по ставочному режиму (для базы авансов).
|
||||
_ADVANCE_VAT_FRACTION = {'vat_5': 5.0 / 105.0, 'vat_7': 7.0 / 107.0}
|
||||
# Код счёта авансового НДС — признак авансового начисления в проводке.
|
||||
VAT_ADVANCE_CODE = '76.АВ'
|
||||
|
||||
|
||||
class RsbuNdsDeclaration(models.Model):
|
||||
_name = 'rsbu.nds.declaration'
|
||||
_description = 'РСБУ — Декларация НДС'
|
||||
_order = 'date_to desc, id desc'
|
||||
_check_company_auto = True
|
||||
|
||||
name = fields.Char(string='Наименование', compute='_compute_name',
|
||||
store=True)
|
||||
company_id = fields.Many2one(
|
||||
'res.company', string='Компания', required=True, index=True,
|
||||
default=lambda self: self.env.company)
|
||||
date_from = fields.Date(string='Период с', required=True)
|
||||
date_to = fields.Date(string='Период по', required=True)
|
||||
currency_id = fields.Many2one(
|
||||
'res.currency', related='company_id.currency_id')
|
||||
state = fields.Selection(
|
||||
selection=[('draft', 'Черновик'), ('computed', 'Рассчитана')],
|
||||
default='draft', string='Состояние', copy=False)
|
||||
|
||||
# --- Начисление (раздел НДС / СумУпл164) ---
|
||||
vat_base_realization = fields.Monetary(
|
||||
string='База реализации (РеалТов5)', currency_field='currency_id')
|
||||
vat_tax_realization = fields.Monetary(
|
||||
string='НДС с реализации', currency_field='currency_id')
|
||||
vat_base_advance = fields.Monetary(
|
||||
string='База авансов (ОплПредПост)', currency_field='currency_id')
|
||||
vat_tax_advance = fields.Monetary(
|
||||
string='НДС с авансов', currency_field='currency_id')
|
||||
vat_charged_total = fields.Monetary(
|
||||
string='НДС начислен всего (НалВосстОбщ)',
|
||||
currency_field='currency_id')
|
||||
# --- Вычет и итог ---
|
||||
vat_deduction = fields.Monetary(
|
||||
string='Вычет (СумНалВыч)', currency_field='currency_id')
|
||||
vat_payable = fields.Monetary(
|
||||
string='НДС к уплате (СумПУ_173.1)', currency_field='currency_id')
|
||||
oktmo = fields.Char(string='ОКТМО')
|
||||
kbk = fields.Char(string='КБК')
|
||||
|
||||
@api.depends('date_from', 'date_to')
|
||||
def _compute_name(self):
|
||||
for rec in self:
|
||||
rec.name = 'Декларация НДС %s — %s' % (
|
||||
rec.date_from or '?', rec.date_to or '?')
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
def _vat_advance_account(self):
|
||||
"""Счёт авансового НДС (76.АВ) — признак авансового начисления."""
|
||||
self.ensure_one()
|
||||
return self.env['rsbu.account'].sudo().search([
|
||||
('code', '=', VAT_ADVANCE_CODE),
|
||||
('company_id', '=', self.company_id.id)], limit=1)
|
||||
|
||||
def _split_sales_vat(self):
|
||||
"""Разложить начисленный НДС книги продаж на реализацию и авансы.
|
||||
|
||||
Авансовое начисление — проводка-источник строки книги содержит
|
||||
строку счёта 76.АВ (Дт). Возвращает (нДС_реализации, НДС_авансов).
|
||||
"""
|
||||
self.ensure_one()
|
||||
BookLine = self.env['rsbu.vat.book.line'].sudo()
|
||||
adv_acc = self._vat_advance_account()
|
||||
sales = BookLine.search([
|
||||
('company_id', '=', self.company_id.id), ('book', '=', 'sales'),
|
||||
('date', '>=', self.date_from), ('date', '<=', self.date_to),
|
||||
])
|
||||
real_vat = 0.0
|
||||
adv_vat = 0.0
|
||||
for line in sales:
|
||||
move = line.move_id
|
||||
is_advance = adv_acc and any(
|
||||
l.account_id == adv_acc and l.debit > 0
|
||||
for l in move.line_ids)
|
||||
if is_advance:
|
||||
adv_vat += line.vat_amount
|
||||
else:
|
||||
real_vat += line.vat_amount
|
||||
return round(real_vat, 2), round(adv_vat, 2)
|
||||
|
||||
def action_compute(self):
|
||||
"""Пересобрать книги/итоги НДС и рассчитать показатели декларации.
|
||||
|
||||
Идемпотентно. Начислено — из книги продаж (разделено на реализацию
|
||||
и авансы), вычет — из книги покупок. Реквизиты — из настроек.
|
||||
"""
|
||||
BookLine = self.env['rsbu.vat.book.line'].sudo()
|
||||
Summary = self.env['rsbu.vat.summary'].sudo()
|
||||
for rec in self:
|
||||
company = rec.company_id
|
||||
BookLine.recompute_period(rec.date_from, rec.date_to, company)
|
||||
Summary.recompute_period(rec.date_from, rec.date_to, company)
|
||||
|
||||
real_vat, adv_vat = rec._split_sales_vat()
|
||||
# База по расчётной ставке: реализация = НДС/долю − НДС... но для
|
||||
# спецставки 5% база реализации = НДС × (1/доля − 1) = НДС × 20.
|
||||
fraction = _ADVANCE_VAT_FRACTION.get(company.rsbu_vat_mode, 0.0)
|
||||
inv = (1.0 / fraction - 1.0) if fraction else 0.0
|
||||
real_base = round(real_vat * inv, 2)
|
||||
# База авансов (ОплПредПост/НалБаза) — сумма аванса С НДС по
|
||||
# расчётной ставке (в 1С: НалБаза = НДС / доля = НДС × 105/5),
|
||||
# в отличие от реализации, где база — без НДС.
|
||||
adv_base = round(adv_vat / fraction, 2) if fraction else 0.0
|
||||
|
||||
deduction = round(sum(BookLine.search([
|
||||
('company_id', '=', company.id), ('book', '=', 'purchases'),
|
||||
('date', '>=', rec.date_from), ('date', '<=', rec.date_to),
|
||||
]).mapped('vat_amount')), 2)
|
||||
|
||||
charged = round(real_vat + adv_vat, 2)
|
||||
rec.vat_base_realization = real_base
|
||||
rec.vat_tax_realization = real_vat
|
||||
rec.vat_base_advance = adv_base
|
||||
rec.vat_tax_advance = adv_vat
|
||||
rec.vat_charged_total = charged
|
||||
rec.vat_deduction = deduction
|
||||
rec.vat_payable = round(charged - deduction, 2)
|
||||
if not rec.oktmo:
|
||||
rec.oktmo = company.rsbu_oktmo or ''
|
||||
if not rec.kbk:
|
||||
rec.kbk = company.rsbu_kbk_nds or ''
|
||||
rec.state = 'computed'
|
||||
return True
|
||||
Reference in New Issue
Block a user