Public release from ruodoo-project: 19.0 - 2026-11-01 22:52:25 UTC
This commit is contained in:
181
rsbu_fns_reporting/models/rsbu_usn_declaration.py
Normal file
181
rsbu_fns_reporting/models/rsbu_usn_declaration.py
Normal file
@@ -0,0 +1,181 @@
|
||||
"""Декларация по УСН (объект «Доходы», КНД 1152017).
|
||||
|
||||
Расчёт разделов 2.1.1 и 1.1 нарастающим итогом по отчётным периодам
|
||||
(I квартал, полугодие, 9 месяцев, год) из итогов КУДиР (rsbu.kudir.summary).
|
||||
Формулы — по Порядку заполнения (разделы III и V): исчисленный налог,
|
||||
уменьшение на страховые взносы (для работодателя не более 50%), авансовые
|
||||
платежи и налог к доплате/уменьшению за год.
|
||||
|
||||
XML-выгрузка — в rsbu_usn_xml (Этап 10-совместимый генератор). Здесь —
|
||||
расчёт и хранение показателей для сверки и последующей выгрузки.
|
||||
"""
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class RsbuUsnDeclaration(models.Model):
|
||||
_name = 'rsbu.usn.declaration'
|
||||
_description = 'РСБУ — Декларация УСН (Доходы)'
|
||||
_order = 'year 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)
|
||||
year = fields.Integer(string='Отчётный год', required=True,
|
||||
default=lambda self: fields.Date.today().year)
|
||||
taxpayer_sign = fields.Selection(
|
||||
selection=[('1', 'Производит выплаты физлицам'),
|
||||
('2', 'ИП без выплат физлицам')],
|
||||
string='Признак налогоплательщика', default='1', required=True,
|
||||
help='Стр. 102. Признак «1» — уменьшение налога на взносы не более '
|
||||
'50%; «2» (ИП без работников) — до 100%.')
|
||||
currency_id = fields.Many2one(
|
||||
'res.currency', related='company_id.currency_id')
|
||||
state = fields.Selection(
|
||||
selection=[('draft', 'Черновик'), ('computed', 'Рассчитана')],
|
||||
default='draft', string='Состояние', copy=False)
|
||||
|
||||
# --- Раздел 2.1.1: доход, ставка, исчислено, вычет (нарастающим итогом) ---
|
||||
# Доход (стр. 110-113).
|
||||
income_q1 = fields.Monetary(string='Доход за I кв. (110)',
|
||||
currency_field='currency_id')
|
||||
income_h1 = fields.Monetary(string='Доход за полугодие (111)',
|
||||
currency_field='currency_id')
|
||||
income_9m = fields.Monetary(string='Доход за 9 мес. (112)',
|
||||
currency_field='currency_id')
|
||||
income_year = fields.Monetary(string='Доход за год (113)',
|
||||
currency_field='currency_id')
|
||||
# Ставка (стр. 120-123), %.
|
||||
rate = fields.Float(string='Ставка УСН, % (120-123)', default=6.0)
|
||||
# Исчислено (стр. 130-133).
|
||||
tax_q1 = fields.Monetary(string='Исчислено за I кв. (130)',
|
||||
currency_field='currency_id')
|
||||
tax_h1 = fields.Monetary(string='Исчислено за полугодие (131)',
|
||||
currency_field='currency_id')
|
||||
tax_9m = fields.Monetary(string='Исчислено за 9 мес. (132)',
|
||||
currency_field='currency_id')
|
||||
tax_year = fields.Monetary(string='Исчислено за год (133)',
|
||||
currency_field='currency_id')
|
||||
# Вычет: взносы/пособия (стр. 140-143), ≤ 50% исчисленного (признак 1).
|
||||
ded_q1 = fields.Monetary(string='Вычет за I кв. (140)',
|
||||
currency_field='currency_id')
|
||||
ded_h1 = fields.Monetary(string='Вычет за полугодие (141)',
|
||||
currency_field='currency_id')
|
||||
ded_9m = fields.Monetary(string='Вычет за 9 мес. (142)',
|
||||
currency_field='currency_id')
|
||||
ded_year = fields.Monetary(string='Вычет за год (143)',
|
||||
currency_field='currency_id')
|
||||
|
||||
# --- Раздел 1.1: авансы к уплате/уменьшению по срокам ---
|
||||
adv_q1 = fields.Monetary(string='Аванс к уплате I кв. (020)',
|
||||
currency_field='currency_id')
|
||||
adv_h1 = fields.Monetary(string='Аванс к уплате полугодие (040)',
|
||||
currency_field='currency_id')
|
||||
adv_h1_reduce = fields.Monetary(string='Аванс к уменьшению полугодие (050)',
|
||||
currency_field='currency_id')
|
||||
adv_9m = fields.Monetary(string='Аванс к уплате 9 мес. (070)',
|
||||
currency_field='currency_id')
|
||||
adv_9m_reduce = fields.Monetary(string='Аванс к уменьшению 9 мес. (080)',
|
||||
currency_field='currency_id')
|
||||
tax_due_year = fields.Monetary(string='Налог к доплате за год (100)',
|
||||
currency_field='currency_id')
|
||||
tax_reduce_year = fields.Monetary(string='Налог к уменьшению за год (110)',
|
||||
currency_field='currency_id')
|
||||
oktmo = fields.Char(string='ОКТМО (010)')
|
||||
|
||||
@api.depends('year', 'company_id')
|
||||
def _compute_name(self):
|
||||
for rec in self:
|
||||
rec.name = 'Декларация УСН за %s' % (rec.year or '?')
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
def _reduction_limited(self, insurance, tax):
|
||||
"""Вычет взносов, ограниченный признаком налогоплательщика.
|
||||
|
||||
Признак «1» (работодатель): не более 50% исчисленного налога.
|
||||
Признак «2» (ИП без работников): до 100% (в пределах налога).
|
||||
В обоих случаях не больше самого налога и не больше фактических
|
||||
взносов.
|
||||
"""
|
||||
if tax <= 0:
|
||||
return 0.0
|
||||
cap = tax * 0.5 if self.taxpayer_sign == '1' else tax
|
||||
return round(min(insurance, cap), 2)
|
||||
|
||||
def action_compute(self):
|
||||
"""Пересобрать итоги КУДиР за год и рассчитать декларацию."""
|
||||
Summary = self.env['rsbu.kudir.summary'].sudo()
|
||||
for rec in self:
|
||||
date_from = fields.Date.to_date('%s-01-01' % rec.year)
|
||||
date_to = fields.Date.to_date('%s-12-31' % rec.year)
|
||||
Summary.recompute_period(date_from, date_to, rec.company_id)
|
||||
return self.compute_from_summary()
|
||||
|
||||
def compute_from_summary(self):
|
||||
"""Рассчитать показатели декларации из готовых итогов КУДиР.
|
||||
|
||||
Отделено от action_compute, чтобы считать по существующим записям
|
||||
rsbu.kudir.summary без принудительного пересбора (для тестов и
|
||||
повторного расчёта).
|
||||
"""
|
||||
Summary = self.env['rsbu.kudir.summary'].sudo()
|
||||
for rec in self:
|
||||
company = rec.company_id
|
||||
year = rec.year
|
||||
rate = (rec.rate or 0.0) / 100.0
|
||||
# Доход нарастающим итогом по отчётным периодам.
|
||||
inc = {
|
||||
q: Summary.ytd_totals(company, year, q)['income']
|
||||
for q in (1, 2, 3, 4)
|
||||
}
|
||||
# Взносы/пособия (раздел IV) нарастающим итогом.
|
||||
ins = {
|
||||
q: Summary.ytd_totals(company, year, q)['insurance']
|
||||
for q in (1, 2, 3, 4)
|
||||
}
|
||||
# Исчислено (стр. 130-133) = доход × ставка.
|
||||
t = {q: round(inc[q] * rate, 2) for q in (1, 2, 3, 4)}
|
||||
# Вычет (стр. 140-143), ограничение по признаку НП.
|
||||
d = {q: rec._reduction_limited(ins[q], t[q]) for q in (1, 2, 3, 4)}
|
||||
|
||||
rec.income_q1, rec.income_h1, rec.income_9m, rec.income_year = (
|
||||
inc[1], inc[2], inc[3], inc[4])
|
||||
rec.tax_q1, rec.tax_h1, rec.tax_9m, rec.tax_year = (
|
||||
t[1], t[2], t[3], t[4])
|
||||
rec.ded_q1, rec.ded_h1, rec.ded_9m, rec.ded_year = (
|
||||
d[1], d[2], d[3], d[4])
|
||||
|
||||
# --- Раздел 1.1 (авансы по срокам, формулы Порядка п.16-25) ---
|
||||
# Аванс за I кв. (020) = (130 − 140), если ≥ 0.
|
||||
a020 = max(0.0, round(t[1] - d[1], 2))
|
||||
# Полугодие: (131 − 141) − 020.
|
||||
base_h1 = round(t[2] - d[2], 2)
|
||||
diff_h1 = round(base_h1 - a020, 2)
|
||||
a040 = max(0.0, diff_h1) # к уплате (040)
|
||||
a050 = max(0.0, -diff_h1) # к уменьшению (050)
|
||||
# 9 мес.: (132 − 142) − (020 + 040 − 050).
|
||||
base_9m = round(t[3] - d[3], 2)
|
||||
prev_9m = round(a020 + a040 - a050, 2)
|
||||
diff_9m = round(base_9m - prev_9m, 2)
|
||||
a070 = max(0.0, diff_9m) # к уплате (070)
|
||||
a080 = max(0.0, -diff_9m) # к уменьшению (080)
|
||||
# Год: (133 − 143) − (020 + 040 − 050 + 070 − 080).
|
||||
base_year = round(t[4] - d[4], 2)
|
||||
prev_year = round(a020 + a040 - a050 + a070 - a080, 2)
|
||||
diff_year = round(base_year - prev_year, 2)
|
||||
a100 = max(0.0, diff_year) # к доплате (100)
|
||||
a110 = max(0.0, -diff_year) # к уменьшению (110)
|
||||
|
||||
rec.adv_q1 = a020
|
||||
rec.adv_h1 = a040
|
||||
rec.adv_h1_reduce = a050
|
||||
rec.adv_9m = a070
|
||||
rec.adv_9m_reduce = a080
|
||||
rec.tax_due_year = a100
|
||||
rec.tax_reduce_year = a110
|
||||
if not rec.oktmo:
|
||||
rec.oktmo = company.rsbu_oktmo or ''
|
||||
rec.state = 'computed'
|
||||
return True
|
||||
Reference in New Issue
Block a user