Compare commits

..

No commits in common. "8bf9779d98bf08c52b799c4e1a0a4d340bfa87d9" and "31d53c428c0709522ad32d8c6d8aea77d0c2d91f" have entirely different histories.

5 changed files with 4 additions and 99 deletions

View file

@ -1,3 +1 @@
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

@ -1,62 +0,0 @@
# 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

@ -1,22 +0,0 @@
# 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"
)

View file

@ -54,10 +54,6 @@ def _prepare_product_display_info(self, product, product_price_info, request_obj
uom_category_name = "" uom_category_name = ""
quantity_step = 1 quantity_step = 1
price_unit_suffix = "" price_unit_suffix = ""
bulk_unit_suffixes = {
"uom.product_uom_categ_kgm": "/Kg",
"uom.product_uom_categ_vol": "/L",
}
if product.uom_id: if product.uom_id:
uom = product.uom_id.sudo() uom = product.uom_id.sudo()
@ -86,7 +82,8 @@ def _prepare_product_display_info(self, product, product_price_info, request_obj
full_xmlid = f"{external_id.module}.{external_id.name}" full_xmlid = f"{external_id.module}.{external_id.name}"
if full_xmlid in fractional_categories: if full_xmlid in fractional_categories:
quantity_step = 0.1 quantity_step = 0.1
price_unit_suffix = bulk_unit_suffixes.get(full_xmlid, "") if full_xmlid == "uom.product_uom_categ_kgm":
price_unit_suffix = "/Kg"
except Exception as e: except Exception as e:
_logger.warning( _logger.warning(
"_prepare_product_display_info: Error detecting UoM category XML ID for product %s: %s", "_prepare_product_display_info: Error detecting UoM category XML ID for product %s: %s",
@ -94,10 +91,6 @@ def _prepare_product_display_info(self, product, product_price_info, request_obj
str(e), str(e),
) )
base_unit_price = 0.0
if product.base_unit_count and price_safe:
base_unit_price = price_safe / product.base_unit_count
try: try:
req = request_obj or request req = request_obj or request
tr_env = req.env tr_env = req.env
@ -113,7 +106,6 @@ def _prepare_product_display_info(self, product, product_price_info, request_obj
"safe_uom_category": uom_category_name, "safe_uom_category": uom_category_name,
"quantity_step": quantity_step, "quantity_step": quantity_step,
"price_unit_suffix": price_unit_suffix, "price_unit_suffix": price_unit_suffix,
"base_unit_price": base_unit_price,
"out_of_stock_label": out_of_stock_label, "out_of_stock_label": out_of_stock_label,
"add_to_cart_label": add_to_cart_label, "add_to_cart_label": add_to_cart_label,
} }

View file

@ -693,10 +693,9 @@
</small> </small>
</t> </t>
</h6> </h6>
<t t-set="tax_incl_base_unit_price" t-value="product_display_info.get(product.id, {}).get('base_unit_price', 0.0)" /> <t t-if="product.base_unit_price and product.base_unit_name">
<t t-if="tax_incl_base_unit_price and product.base_unit_name">
<p class="product-unit-price text-muted" style="font-size: 0.85rem; margin-top: 0.25rem; margin-bottom: 0;"> <p class="product-unit-price text-muted" style="font-size: 0.85rem; margin-top: 0.25rem; margin-bottom: 0;">
<t t-esc="'%.2f' % tax_incl_base_unit_price" /> € / <t t-esc="product.base_unit_name" /> <t t-esc="'%.2f' % product.base_unit_price" /> € / <t t-esc="product.base_unit_name" />
</p> </p>
</t> </t>
</div> </div>