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,128 @@
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 должен вызвать пересчёт')