Public release from ruodoo-project: 19.0 - 2026-11-01 22:52:25 UTC

This commit is contained in:
CI Publish Bot
2026-11-01 22:54:30 +00:00
commit 26250aa580
2492 changed files with 664277 additions and 0 deletions

View 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