Files
public/rsbu_fns_reporting/tests/test_ndfl_report.py

109 lines
5.6 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 TestNdflReport(TransactionCase):
"""6-НДФЛ (NO_NDFL6.2 5.05): раздел 1 (6 сроков), раздел 2, XML."""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.company = cls.env.company
cls.company.write({
'rsbu_tax_office_code': '5050',
'rsbu_oktmo': '46788000',
'rsbu_kbk_ndfl': '18210102010011000110',
})
cls.company.partner_id.write({'inn': '5050164162', 'kpp': '505001001'})
user = cls.env['res.users'].search([], limit=1)
cls.company.rsbu_signatory_id = user.id
user.partner_id.write({'name': 'Коробков Сергей Васильевич'})
cls.emp1 = cls.env['hr.employee'].create({
'name': 'Петров Пётр', 'company_id': cls.company.id})
cls.emp2 = cls.env['hr.employee'].create({
'name': 'Сидоров Иван', 'company_id': cls.company.id})
cls.Summary = cls.env['rsbu.ndfl.summary']
cls.Report = cls.env['rsbu.ndfl.report']
def _summary(self, emp, month, accrued, withheld, wh_date, rate=13):
"""Создать помесячный итог НДФЛ с датой удержания."""
from odoo import fields
d = fields.Date.to_date(wh_date)
term = self.Summary._deadline_term(d)
return self.Summary.create({
'company_id': self.company.id, 'employee_id': emp.id,
'year': 2026, 'month': month, 'rate': rate,
'accrued': accrued, 'deduction': 0.0, 'base': accrued,
'computed_tax': withheld, 'withheld_tax': withheld,
'withholding_date': d, 'withholding_month': d.month,
'deadline_term': term,
})
def test_01_deadline_term_mapping(self):
"""Срок квартала по дате удержания: 1-22 → нечёт, 23- → чёт."""
from odoo import fields
# Апрель (2-й квартал, 1-й месяц квартала).
self.assertEqual(self.Summary._deadline_term(
fields.Date.to_date('2026-04-10')), 1) # 1-22 апр → срок 1
self.assertEqual(self.Summary._deadline_term(
fields.Date.to_date('2026-04-25')), 2) # 23-30 апр → срок 2
self.assertEqual(self.Summary._deadline_term(
fields.Date.to_date('2026-05-15')), 3) # 1-22 май → срок 3
self.assertEqual(self.Summary._deadline_term(
fields.Date.to_date('2026-06-28')), 6) # 23-30 июн → срок 6
def test_02_section1_six_terms(self):
"""Раздел 1: удержанный НДФЛ разложен по 6 срокам квартала."""
# Полугодие (квартал 2): удержания в апреле/мае/июне по полупериодам.
self._summary(self.emp1, 4, 100000.0, 13000.0, '2026-04-10') # срок1
self._summary(self.emp1, 4, 50000.0, 6500.0, '2026-04-25') # срок2
self._summary(self.emp1, 5, 100000.0, 13000.0, '2026-05-12') # срок3
self._summary(self.emp1, 6, 100000.0, 13000.0, '2026-06-20') # срок5
rep = self.Report.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '2'})
rep.compute_from_summary()
self.assertAlmostEqual(rep.term1, 13000.0, 2)
self.assertAlmostEqual(rep.term2, 6500.0, 2)
self.assertAlmostEqual(rep.term3, 13000.0, 2)
self.assertAlmostEqual(rep.term4, 0.0, 2)
self.assertAlmostEqual(rep.term5, 13000.0, 2)
self.assertAlmostEqual(rep.term6, 0.0, 2)
self.assertAlmostEqual(rep.withheld_total, 45500.0, 2)
def test_03_section2_by_rate(self):
"""Раздел 2: строка по ставке — число лиц, доход, удержано."""
self._summary(self.emp1, 4, 100000.0, 13000.0, '2026-04-10')
self._summary(self.emp2, 4, 80000.0, 10400.0, '2026-04-10')
rep = self.Report.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '2'})
rep.compute_from_summary()
line = rep.line_ids.filtered(lambda l: l.rate == 13)
self.assertTrue(line)
self.assertEqual(line.persons_count, 2)
self.assertAlmostEqual(line.accrued, 180000.0, 2)
self.assertAlmostEqual(line.withheld_tax, 23400.0, 2)
def test_04_xml_valid_against_xsd(self):
"""Сгенерированный 6-НДФЛ проходит XSD 5.05."""
self._summary(self.emp1, 4, 100000.0, 13000.0, '2026-04-10')
self._summary(self.emp1, 5, 100000.0, 13000.0, '2026-05-12')
self._summary(self.emp1, 6, 100000.0, 13000.0, '2026-06-20')
rep = self.Report.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '2'})
rep.compute_from_summary()
fname = rep._ndfl_xml_filename()
xml_bytes = rep._ndfl_build_xml(filename=fname)
ok, errors = rep._ndfl_validate_xml(xml_bytes)
self.assertTrue(ok, '6-НДФЛ не прошёл XSD: %s' % '; '.join(errors[:8]))
self.assertIn(b'windows-1251', xml_bytes[:60])
self.assertTrue(fname.startswith('NO_NDFL6.2_'))
from lxml import etree
root = etree.fromstring(xml_bytes)
self.assertEqual(root.get('ИдФайл'), fname)
doc = root.find('Документ')
self.assertEqual(doc.get('КНД'), '1151100')
self.assertEqual(doc.get('Период'), '31')
obyaz = doc.find('НДФЛ6.2/ОбязНА')
self.assertEqual(obyaz.get('КБК'), '18210102010011000110')