Files
public/rsbu_fns_reporting/tests/test_rsv_declaration.py

125 lines
6.0 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.
# -*- coding: utf-8 -*-
from odoo.tests import TransactionCase, tagged
@tagged('post_install', '-at_install')
class TestRsvDeclaration(TransactionCase):
"""РСВ (NO_RASCHSV 5.08): расчёт разделов из итогов и выгрузка XML."""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.company = cls.env.company
cls.company.write({
'rsbu_tax_office_code': '7704',
'rsbu_oktmo': '45301000',
'rsbu_msp_enabled': True,
'rsbu_payer_tariff_code': '01',
})
cls.company.partner_id.write({'inn': '7704567896', 'kpp': '770401001'})
# Подписант.
user = cls.env['res.users'].search([], limit=1)
cls.company.rsbu_signatory_id = user.id
user.partner_id.write({'name': 'Иванов Иван Иванович'})
# Сотрудник с полными персональными данными (раздел 3).
cls.employee = cls.env['hr.employee'].create({
'name': 'Петров Пётр Петрович',
'company_id': cls.company.id,
'birthday': '1985-03-15',
'rsbu_snils': '112-233-445 95',
'rsbu_inn': '770412345678',
'rsbu_insured_category': 'НР',
'rsbu_sex': '1',
'rsbu_citizenship_code': '643',
'rsbu_id_doc_code': '21',
'rsbu_id_doc_number': '45 04 123456',
})
cls.Summary = cls.env['rsbu.rsv.summary']
cls.Decl = cls.env['rsbu.rsv.declaration']
def _summary(self, month, base, ops, oms, vnim, payments=None,
within=None, over=0.0):
"""Создать помесячный итог взносов сотрудника."""
return self.Summary.create({
'company_id': self.company.id,
'employee_id': self.employee.id,
'year': 2026, 'month': month,
'insured_category': 'НР',
'payments': payments if payments is not None else base,
'non_taxable': 0.0,
'base': base,
'base_within_limit': within if within is not None else base,
'base_over_limit': over,
'ops': ops, 'ops_within': ops, 'ops_over': 0.0,
'oms': oms, 'vnim': vnim,
})
def test_01_compute_section1_ytd(self):
"""Раздел 1 считается нарастающим итогом с начала года."""
# I квартал: по 100 000 базы в месяц, ОПС 22000, ОМС 5100, ВНиМ 2900.
for m in (1, 2, 3):
self._summary(m, 100000.0, 22000.0, 5100.0, 2900.0)
dec = self.Decl.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '1'})
dec.compute_from_summary()
self.assertAlmostEqual(dec.base_total, 300000.0, 2)
self.assertAlmostEqual(dec.charged_total, 66000.0, 2) # ОПС 3×22000
self.assertAlmostEqual(dec.oms_total, 15300.0, 2)
self.assertAlmostEqual(dec.vnim_total, 8700.0, 2)
self.assertEqual(dec.insured_count, 1)
# Помесячно единый тариф = ОПС+ОМС+ВНиМ = 30000.
self.assertAlmostEqual(dec.contrib_month1, 30000.0, 2)
self.assertAlmostEqual(dec.contrib_period, 90000.0, 2)
def test_02_pers_lines_built(self):
"""Раздел 3: строки по сотрудникам за 3 месяца квартала."""
for m in (1, 2, 3):
self._summary(m, 100000.0, 22000.0, 5100.0, 2900.0)
dec = self.Decl.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '1'})
dec.compute_from_summary()
self.assertEqual(len(dec.pers_line_ids), 1)
line = dec.pers_line_ids
self.assertEqual(line.employee_id, self.employee)
self.assertAlmostEqual(line.base_m1, 100000.0, 2)
self.assertAlmostEqual(line.ops_m3, 22000.0, 2)
def test_03_regression_split_in_section1(self):
"""База сверх предела ОПС отражается в БазПревышОПС раздела 1."""
# Месяц 3: часть базы сверх лимита.
self._summary(1, 100000.0, 22000.0, 5100.0, 2900.0)
self._summary(2, 100000.0, 22000.0, 5100.0, 2900.0)
self._summary(3, 100000.0, 15000.0, 5100.0, 2900.0,
within=50000.0, over=50000.0)
dec = self.Decl.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '1'})
dec.compute_from_summary()
self.assertAlmostEqual(dec.base_within_total, 250000.0, 2)
self.assertAlmostEqual(dec.base_over_total, 50000.0, 2)
def test_04_xml_valid_against_xsd(self):
"""Сгенерированный РСВ проходит проверку по XSD 5.08."""
for m in (1, 2, 3):
self._summary(m, 100000.0, 22000.0, 5100.0, 2900.0)
dec = self.Decl.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '1',
'msp_income_total': 5000000.0, 'msp_income_main': 5000000.0})
dec.compute_from_summary()
fname = dec._rsv_xml_filename()
xml_bytes = dec._rsv_build_xml(filename=fname)
ok, errors = dec._rsv_validate_xml(xml_bytes)
self.assertTrue(ok, 'РСВ не прошёл XSD: %s' % '; '.join(errors[:8]))
self.assertIn(b'windows-1251', xml_bytes[:60])
self.assertTrue(fname.startswith('NO_RASCHSV_'))
from lxml import etree
root = etree.fromstring(xml_bytes)
self.assertEqual(root.get('ИдФайл'), fname)
self.assertEqual(root.find('Документ').get('КНД'), '1151111')
def test_05_snils_normalized_with_dashes(self):
"""СНИЛС нормализуется к формату NNN-NNN-NNN-NN (дефисы)."""
dec = self.Decl.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '1'})
self.assertEqual(dec._snils('112-233-445 95'), '112-233-445-95')
self.assertEqual(dec._snils('11223344595'), '112-233-445-95')