diff --git a/l10n_es_edi_tbai_reagyp_recibidas/models/__init__.py b/l10n_es_edi_tbai_reagyp_recibidas/models/__init__.py index e89b491..265dbff 100644 --- a/l10n_es_edi_tbai_reagyp_recibidas/models/__init__.py +++ b/l10n_es_edi_tbai_reagyp_recibidas/models/__init__.py @@ -1 +1,3 @@ from . import account_move # noqa: F401 +from . import res_company # noqa: F401 +from . import l10n_es_edi_tbai_document # noqa: F401 diff --git a/l10n_es_edi_tbai_reagyp_recibidas/models/l10n_es_edi_tbai_document.py b/l10n_es_edi_tbai_reagyp_recibidas/models/l10n_es_edi_tbai_document.py new file mode 100644 index 0000000..9f06ffa --- /dev/null +++ b/l10n_es_edi_tbai_reagyp_recibidas/models/l10n_es_edi_tbai_document.py @@ -0,0 +1,62 @@ +# Copyright 2026 - Today Criptomart +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import re + +from odoo import models + + +class L10nEsEdiTbaiDocument(models.Model): + _inherit = "l10n_es_edi_tbai.document" + + def _get_sale_values(self, values): + """Override to ensure chain links only within the same invoice series.""" + sale_values = super()._get_sale_values(values) + + # Replace chain_prev_document with one from the same series + chain_prev_document = sale_values.get("chain_prev_document") + if chain_prev_document: + current_series = self._get_series_prefix(self.name) + prev_series = self._get_series_prefix(chain_prev_document.name) + + # If series mismatch OR previous doc has no valid XML, find correct one + if ( + current_series != prev_series + or not chain_prev_document.xml_attachment_id + ): + same_series_doc = self._get_last_chained_document_by_series( + current_series + ) + sale_values["chain_prev_document"] = same_series_doc + + return sale_values + + @staticmethod + def _get_series_prefix(doc_name): + """Extract series prefix from document name (e.g., 'INV/2026/' from 'INV/2026/01248').""" + if not doc_name: + return "" + # Everything before the first digit + match = re.search(r"\d+", doc_name) + if match: + return doc_name[: match.start()].rstrip("/") + return doc_name + + def _get_last_chained_document_by_series(self, series_prefix): + """Find the last valid chained document matching a specific series prefix.""" + domain = [ + ("chain_index", "!=", 0), + ("company_id", "=", self.company_id.id), + ("is_cancel", "=", False), + ("state", "=", "accepted"), # Must be accepted in chain + ] + docs = self.search(domain, order="chain_index desc") + + # Return the first doc matching series that has valid XML + for doc in docs: + if ( + self._get_series_prefix(doc.name) == series_prefix + and doc.xml_attachment_id + ): + return doc + + return self.env["l10n_es_edi_tbai.document"] diff --git a/l10n_es_edi_tbai_reagyp_recibidas/models/res_company.py b/l10n_es_edi_tbai_reagyp_recibidas/models/res_company.py new file mode 100644 index 0000000..179e146 --- /dev/null +++ b/l10n_es_edi_tbai_reagyp_recibidas/models/res_company.py @@ -0,0 +1,22 @@ +# Copyright 2026 - Today Criptomart +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models + + +class ResCompany(models.Model): + _inherit = "res.company" + + def _get_l10n_es_tbai_last_chained_document(self): + """ + Returns the last tbai document posted to this company's chain. + Each invoice series has its own independent chain in TicketBAI. + Filters out cancellation documents to skip cancelled invoices. + """ + domain = [ + ("chain_index", "!=", 0), + ("company_id", "=", self.id), + ("is_cancel", "=", False), + ] + return self.env["l10n_es_edi_tbai.document"].search( + domain, limit=1, order="chain_index desc" + )