Public release from ruodoo-project: 19.0 - 2026-11-01 22:52:25 UTC
This commit is contained in:
124
rsbu_fns_reporting/tests/test_rsv_declaration.py
Normal file
124
rsbu_fns_reporting/tests/test_rsv_declaration.py
Normal file
@@ -0,0 +1,124 @@
|
||||
# -*- 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')
|
||||
Reference in New Issue
Block a user