109 lines
5.6 KiB
Python
109 lines
5.6 KiB
Python
# -*- 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')
|