[FIX] l10n_es_edi_tbai_reagyp_recibidas: fix chaining within same invoice series

TicketBAI maintains independent chains per invoice series (INV, RINV, etc).
When building the chain for a new invoice, ensure it links to the previous
valid document from the same series, not just the last document regardless
of series.

Fixes error "time data '' does not match format '%d-%m-%Y'" when previous
document's XML is missing or when wrong series document is used for chaining.

- Override _get_sale_values to validate series match
- Add _get_last_chained_document_by_series to filter by series+state+xml
- Skip cancelled documents and documents without valid XML

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
GitHub Copilot 2026-07-31 18:46:05 +02:00
parent 5a6d9c9894
commit 8bf9779d98
3 changed files with 86 additions and 0 deletions

View file

@ -1 +1,3 @@
from . import account_move # noqa: F401 from . import account_move # noqa: F401
from . import res_company # noqa: F401
from . import l10n_es_edi_tbai_document # noqa: F401

View file

@ -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"]

View file

@ -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"
)