Files
public/rsbu_fns_reporting/tests/test_usn_declaration.py

129 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from odoo.tests import TransactionCase, tagged
@tagged('post_install', '-at_install')
class TestUsnDeclaration(TransactionCase):
"""Декларация УСН «Доходы»: расчёт разделов 1.1/2.1.1 и XML."""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.company = cls.env.company
cls.company.write({
'rsbu_usn_rate': 6.0,
'rsbu_tax_office_code': '7704',
'rsbu_oktmo': '45301000',
})
cls.company.partner_id.write({'inn': '7704567896', 'kpp': '770401001'})
cls.Decl = cls.env['rsbu.usn.declaration']
cls.Summary = cls.env['rsbu.kudir.summary']
def _summary(self, quarter, income, insurance=0.0):
"""Создать запись итогов КУДиР за квартал года."""
return self.Summary.create({
'company_id': self.company.id,
'period_key': '2026-Q%s' % quarter,
'income': income,
'expense': 0.0,
'insurance': insurance,
})
def test_01_progression_of_advances(self):
"""Авансы по срокам считаются нарастающим итогом (без взносов).
Квартальные доходы (summary хранит за квартал, ytd суммирует):
Q1 1 000 000, Q2 500 000, Q3 500 000, Q4 1 000 000.
Нарастающим: Q1 1М, H1 1.5М, 9м 2М, год 3М. Ставка 6%.
Исчислено: 60000/90000/120000/180000. Взносов нет.
Авансы: 020=60000, 040=30000, 070=30000, 100=60000.
"""
self._summary(1, 1_000_000)
self._summary(2, 500_000)
self._summary(3, 500_000)
self._summary(4, 1_000_000)
dec = self.Decl.create({
'year': 2026, 'company_id': self.company.id,
'taxpayer_sign': '1', 'rate': 6.0})
dec.compute_from_summary()
self.assertAlmostEqual(dec.tax_q1, 60000.0, 2)
self.assertAlmostEqual(dec.tax_year, 180000.0, 2)
self.assertAlmostEqual(dec.adv_q1, 60000.0, 2) # стр.020
self.assertAlmostEqual(dec.adv_h1, 30000.0, 2) # стр.040
self.assertAlmostEqual(dec.adv_9m, 30000.0, 2) # стр.070
self.assertAlmostEqual(dec.tax_due_year, 60000.0, 2) # стр.100
def test_02_reduction_limited_50pct(self):
"""Вычет взносов для признака «1» ограничен 50% исчисленного."""
self._summary(1, 1_000_000, insurance=40000.0)
dec = self.Decl.create({
'year': 2026, 'company_id': self.company.id,
'taxpayer_sign': '1', 'rate': 6.0})
dec.compute_from_summary()
# Исчислено 60000; взносы 40000, но лимит 50% = 30000.
self.assertAlmostEqual(dec.tax_q1, 60000.0, 2)
self.assertAlmostEqual(dec.ded_q1, 30000.0, 2)
self.assertAlmostEqual(dec.adv_q1, 30000.0, 2) # 60000 − 30000
def test_03_advance_reduce_when_income_drops(self):
"""Аванс к уменьшению (050), если исчисленное за полугодие < аванса Q1.
Q1 доход 1М (аванс 60000). Полугодие доход тот же 1М (нет прироста)
→ (131−141)−020 = 60000 − 60000 = 0; при возврате дохода строка 050.
Здесь проверяем неотрицательность и отсутствие двойного счёта.
"""
self._summary(1, 1_000_000)
self._summary(2, 0.0) # прироста во II квартале нет → H1 = Q1
dec = self.Decl.create({
'year': 2026, 'company_id': self.company.id,
'taxpayer_sign': '1', 'rate': 6.0})
dec.compute_from_summary()
self.assertAlmostEqual(dec.adv_q1, 60000.0, 2)
self.assertAlmostEqual(dec.adv_h1, 0.0, 2)
self.assertAlmostEqual(dec.adv_h1_reduce, 0.0, 2)
def test_04_xml_valid_against_xsd(self):
"""Сгенерированный XML проходит проверку по XSD-схеме ФНС."""
self._summary(1, 1_000_000)
self._summary(2, 500_000)
self._summary(3, 500_000)
self._summary(4, 1_000_000)
dec = self.Decl.create({
'year': 2026, 'company_id': self.company.id,
'taxpayer_sign': '1', 'rate': 6.0})
dec.compute_from_summary()
fname = dec._usn_xml_filename()
xml_bytes = dec._usn_build_xml(filename=fname)
ok, errors = dec._usn_validate_xml(xml_bytes)
self.assertTrue(ok, 'XML не прошёл XSD: %s' % '; '.join(errors[:5]))
# Кодировка windows-1251 в декларации XML.
self.assertIn(b'windows-1251', xml_bytes[:60])
# Префикс имени файла обмена — NO_USN (Порядок п.3).
self.assertTrue(fname.startswith('NO_USN_'))
# ИдФайл в XML совпадает с именем файла обмена.
from lxml import etree
root = etree.fromstring(xml_bytes)
self.assertEqual(root.get('ИдФайл'), fname)
def test_05_export_always_recomputes(self):
"""Выгрузка XML всегда пересчитывает данные (свежесть гарантирована).
Даже если декларация уже в состоянии «Рассчитана», action_export_xml
обязан вызвать action_compute перед формированием файла — иначе
выгрузятся устаревшие суммы после изменения КУДиР.
"""
dec = self.Decl.create({
'year': 2026, 'company_id': self.company.id,
'taxpayer_sign': '1', 'rate': 6.0})
dec.action_compute()
self.assertEqual(dec.state, 'computed')
calls = []
orig = type(dec).action_compute
def _spy(self2):
calls.append(1)
return orig(self2)
self.patch(type(dec), 'action_compute', _spy)
dec.action_export_xml()
self.assertTrue(calls, 'action_export_xml должен вызвать пересчёт')