From c83cf048ead514d35dffab5d9a25de82b611ab9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=CC=81=20Arturo=20Garci=CC=81a?= Date: Thu, 27 Aug 2026 10:49:35 -0400 Subject: [PATCH] Allow closing protected legacy NCF ranges --- README.rst | 5 +++++ ncf_manager.py | 6 ++++-- tests/test_module.py | 22 ++++++++++++++++++++++ tryton.cfg | 2 +- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 288b941..be8f06c 100644 --- a/README.rst +++ b/README.rst @@ -43,6 +43,11 @@ same document direction. The last issued number automatically finishes the line. Finishing an authorization closes every remaining active line without reusing numbers. +Legacy migration lines keep their original technical sequence immutable after +activation. Prefix selection is enforced while a line is in draft and fiscal +activation performs the same server-side validation; closing a protected +historical line therefore does not invalidate already-linked invoices. + Electronic fiscal numbers are only reserved by this module. Activating an E-series authorization never signs or transmits an e-CF to DGII. diff --git a/ncf_manager.py b/ncf_manager.py index 2a07300..fa74e8e 100644 --- a/ncf_manager.py +++ b/ncf_manager.py @@ -8,7 +8,7 @@ from trytond.i18n import gettext from trytond.model import ModelView, ModelSQL, Workflow, fields, Unique from trytond.pool import Pool -from trytond.pyson import Eval +from trytond.pyson import Eval, If from trytond.transaction import Transaction @@ -309,7 +309,9 @@ class NCFManagerSequences(Workflow, ModelSQL, ModelView): ncf_sequences = fields.Many2One('ir.sequence.strict', 'Fiscal Sequence', domain=[ ('company', '=', Eval('company', -1)), - ('prefix', '=', Eval('expected_prefix')), + If(Eval('state') == 'draft', + ('prefix', '=', Eval('expected_prefix')), + ()), ('active', '=', True), ], states={ diff --git a/tests/test_module.py b/tests/test_module.py index 584ef09..43b57ec 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -396,6 +396,28 @@ class NcfManagerTestCase(ModuleTestCase): with self.assertRaises(UserError): Manager.activate([manager]) + @with_transaction() + def test_finish_preserves_bound_legacy_migration_sequence(self): + 'Finishing must not invalidate an immutable historical sequence' + pool = Pool() + Manager = pool.get('ncf.manager') + StrictSequence = pool.get('ir.sequence.strict') + company = self._create_company() + + line = self._create_ncf_sequence(company) + strict_sequence = line.ncf_sequences + StrictSequence.write([strict_sequence], { + 'prefix': 'MIG1', + 'padding': 9, + }) + + Manager.finish([line.pos]) + + line = line.__class__(line.id) + self.assertEqual(line.state, 'finished') + self.assertEqual(line.ncf_sequences.id, strict_sequence.id) + self.assertEqual(line.ncf_sequences.prefix, 'MIG1') + @with_transaction() def test_import_dgii_authorization_is_idempotent(self): pool = Pool() diff --git a/tryton.cfg b/tryton.cfg index e10f630..3af0055 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -1,5 +1,5 @@ [tryton] -version=8.0.2 +version=8.0.3 depends: account_do account_invoice_do