diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e54e4d8..1a7b93b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -182,7 +182,6 @@ addons-cm/ ├── account_invoice_triple_discount_readonly/ # Fix bug acumulación triple descuento ├── # --- Ventas y web --- ├── website_sale_aplicoop/ # Sistema eskaera (compras grupo) -├── website_sale_disable_cart/ # Tienda solo catálogo: desactiva el carrito estándar ├── portal_event_registration/ # Portal: ver registros de eventos + adjuntos ├── # --- Membresías --- ├── membership_monthly_invoicing/ # Factura mensual de membresía por socio (cron) @@ -221,7 +220,6 @@ addons-cm/ **Ventas y web** - [website_sale_aplicoop](../website_sale_aplicoop/README.rst) - Sistema eskaera completo -- [website_sale_disable_cart](../website_sale_disable_cart/README.rst) - Tienda solo catálogo: oculta el carrito estándar y redirige `/shop/cart*` - [portal_event_registration](../portal_event_registration/README.rst) - Portal: registros de eventos + adjuntos al chatter **Membresías** diff --git a/CLAUDE.md b/CLAUDE.md index a966f9f..c6c246f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,8 +58,6 @@ Sales & website: - `website_sale_aplicoop` — Eskaera: collaborative purchasing for consumer co-ops (group orders, per-member carts, cutoff/pickup dates, lazy loading, multi-language). -- `website_sale_disable_cart` — turns `/shop` into a read-only catalog: hides the cart UI and - redirects the standard `/shop/cart*` routes to a configurable URL (default `/shop`). - `portal_event_registration` — portal users view their event registrations + upload attachments to chatter. Membership: 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" + ) diff --git a/website_sale_aplicoop/CHANGELOG.md b/website_sale_aplicoop/CHANGELOG.md index 1e1e3fe..5972f82 100644 --- a/website_sale_aplicoop/CHANGELOG.md +++ b/website_sale_aplicoop/CHANGELOG.md @@ -1,24 +1,5 @@ # Changelog - Website Sale Aplicoop -## [18.0.1.12.0] - 2026-08-06 - -### Removed - -- **Standard cart restriction extracted to `website_sale_disable_cart`**: the cart - UI removal (header cart link, add-to-cart buttons) and the `/shop/cart*` route - redirects no longer live in this module, so installing Aplicoop leaves the - standard `website_sale` shop untouched. Sites that want the previous behaviour - must install `website_sale_disable_cart` and set its *Cart Redirect URL* to - `/eskaera`. The new module also fixes the route paths (Odoo 18 uses - `/shop/cart/quantity`, not `/shop/cart_quantity`), covers the 5 header styles - that were missing (boxed, sidebar, sales two/three/four — the header cart link - was still visible on those) and overrides the standard endpoints instead of - registering duplicate ones. - Upgrade order matters: update this module **first** (so its old templates are - dropped) and install `website_sale_disable_cart` in a **second** Odoo run — - both sets of templates use the same XPaths and obsolete records are only - cleaned up at the end of a run, so a single combined run fails. - ## [18.0.1.11.0] - 2026-07-15 ### Added diff --git a/website_sale_aplicoop/README.rst b/website_sale_aplicoop/README.rst index a73a6a3..bf621c1 100644 --- a/website_sale_aplicoop/README.rst +++ b/website_sale_aplicoop/README.rst @@ -142,22 +142,48 @@ This module was inspired by the original **Aplicoop** project: The original Aplicoop project served as a pioneering solution for collaborative consumption group orders, and this module brings its functionality to the modern Odoo platform. -Notes - Standard shop and cart -============================== +Notes - Shop behaves as a simple catalog +========================================= -This module adds the "eskaera" group order flow (``/eskaera``); it no longer -touches the standard ``website_sale`` storefront. The default ``/shop`` and its -shopping cart keep working as usual after installing it. +Starting with the recent update, this module converts the default Odoo +``/shop`` storefront into a simple product catalog (no standard ``website_sale`` +shopping cart). The change is intentional for sites that use the Aplicoop +"eskaera" flow as the single shopping experience. -Sites that want the standard cart disabled — so that the group order flow is -the single shopping experience — should additionally install -``website_sale_disable_cart``, which hides the cart UI and redirects the -standard cart endpoints to a configurable URL. That behaviour used to live in -this module (up to 18.0.1.11.0) and was extracted in 18.0.1.12.0. +What the module does +--------------------- + +- Hides the standard header cart link and badge. +- Removes the "Add to cart" quick-add area from product listings. +- Redirects standard cart endpoints to the group-order flow (``/eskaera``): + ``/shop/cart``, ``/shop/cart/update``, ``/shop/cart/update_json``, ``/shop/cart_quantity``. + +Files involved +-------------- + +- ``views/website_sale_disable_cart.xml`` — templates that hide/remove cart UI +- ``controllers/website_sale.py`` — routes that redirect cart endpoints to ``/eskaera`` +- ``__manifest__.py`` — includes the new view file + +How to apply or revert +----------------------- + +To apply the change (already applied when the module is installed/updated): :: - docker-compose run --rm odoo odoo -d odoo -i website_sale_disable_cart --stop-after-init + docker-compose run --rm odoo odoo -d odoo -u website_sale_aplicoop --stop-after-init + docker-compose up -d -Then set *Website > Configuration > Settings > Disabled Cart > Cart Redirect -URL* to ``/eskaera``. +To revert back to the standard ``website_sale`` behaviour: + +1. Remove ``views/website_sale_disable_cart.xml`` from the ``data`` section in + ``__manifest__.py``. +2. Update the module: + +:: + + docker-compose run --rm odoo odoo -d odoo -u website_sale_aplicoop --stop-after-init + +Note: Reverting may expose standard cart UI and routes; ensure your site +content and workflows are adapted accordingly. diff --git a/website_sale_aplicoop/README_DEV.md b/website_sale_aplicoop/README_DEV.md index 32c5c62..650289c 100644 --- a/website_sale_aplicoop/README_DEV.md +++ b/website_sale_aplicoop/README_DEV.md @@ -7,8 +7,7 @@ Sistema de pedidos colaborativos para grupos de consumo. Reemplaza el legacy Apl ## Resumen de funcionalidades - Gestión completa de órdenes de grupo (draft → confirmed → collected → invoiced → completed) -- Carrito separado por grupo de consumo (la tienda estándar de `website_sale` se - puede deshabilitar aparte con `website_sale_disable_cart`) +- Carrito separado por grupo de consumo; tienda estándar de `website_sale` deshabilitada - Control de catálogo por listas de inclusión (proveedores, categorías, productos) y exclusión (blacklists) - Cron diario de auto-confirmación + creación automática de lotes de picking - Paginación de productos (lazy loading) para cargas rápidas @@ -87,9 +86,7 @@ Todos los endpoints viven bajo `/eskaera/`: | `POST /eskaera//confirm` | Confirmar carrito (sale.order en draft) | | `POST /eskaera/clear-cart` | Limpiar carrito actual | -Las rutas estándar de `website_sale` (`/shop` y su carrito) no se tocan desde -este módulo. Para desactivar el carrito estándar y redirigirlo a `/eskaera`, -instala `website_sale_disable_cart` y configura la URL de redirección. +Las rutas `/cart` y `/shop` de `website_sale` están redirigidas a `/eskaera`. **Seguridad portal:** los usuarios solo ven órdenes de su `consumer_group_id`. La regla `rule_group_order_company_read` incluye guardia `user.share`. diff --git a/website_sale_aplicoop/__manifest__.py b/website_sale_aplicoop/__manifest__.py index 5baedf4..0140a35 100644 --- a/website_sale_aplicoop/__manifest__.py +++ b/website_sale_aplicoop/__manifest__.py @@ -3,7 +3,7 @@ { # noqa: B018 "name": "Website Sale - Aplicoop", - "version": "18.0.1.12.0", + "version": "18.0.1.11.0", "category": "Website/Sale", "summary": "Modern replacement of legacy Aplicoop - Collaborative consumption group orders", "author": "Odoo Community Association (OCA), Criptomart", @@ -39,6 +39,7 @@ "views/res_partner_views.xml", "views/res_config_settings_views.xml", "views/website_templates.xml", + "views/website_sale_disable_cart.xml", "views/product_template_views.xml", "views/sale_order_views.xml", "views/stock_picking_views.xml", diff --git a/website_sale_aplicoop/controllers/website_sale.py b/website_sale_aplicoop/controllers/website_sale.py index 8de6551..fad2dbf 100644 --- a/website_sale_aplicoop/controllers/website_sale.py +++ b/website_sale_aplicoop/controllers/website_sale.py @@ -2457,3 +2457,50 @@ class AplicoopWebsiteSale(WebsiteSale): "empty_cart": "Your cart is empty", "added_to_cart": "added to cart", } + + # ================================================================ + # CART REDIRECT METHODS - Redirect /shop/cart routes to /eskaera + # ================================================================ + + @http.route(["/shop/cart"], type="http", auth="public", website=True) + def cart_redirect(self, access_token=None, revive="", **post): + """Redirect /shop/cart to /eskaera (no standard cart).""" + _logger.info("🛒 Redirecting /shop/cart → /eskaera") + return http.redirect_with_hash("/eskaera") + + @http.route( + ["/shop/cart/update"], + type="http", + auth="public", + website=True, + methods=["POST"], + ) + def cart_update_redirect(self, **post): + """Redirect /shop/cart/update to /eskaera (no standard cart).""" + _logger.info("🛒 Redirecting /shop/cart/update → /eskaera") + return http.redirect_with_hash("/eskaera") + + @http.route( + ["/shop/cart/update_json"], + type="http", + auth="public", + website=True, + methods=["POST"], + csrf=False, + ) + def cart_update_json_redirect(self, **post): + """Redirect /shop/cart/update_json to /eskaera (no standard cart).""" + _logger.info("🛒 Redirecting /shop/cart/update_json → /eskaera") + return http.redirect_with_hash("/eskaera") + + @http.route( + ["/shop/cart_quantity"], + type="http", + auth="public", + website=True, + methods=["GET"], + ) + def cart_quantity_redirect(self): + """Redirect /shop/cart_quantity to /eskaera (no standard cart).""" + _logger.info("🛒 Redirecting /shop/cart_quantity → /eskaera") + return http.redirect_with_hash("/eskaera") diff --git a/website_sale_disable_cart/views/website_sale_disable_cart_templates.xml b/website_sale_aplicoop/views/website_sale_disable_cart.xml similarity index 71% rename from website_sale_disable_cart/views/website_sale_disable_cart_templates.xml rename to website_sale_aplicoop/views/website_sale_disable_cart.xml index 89173dc..842ec3f 100644 --- a/website_sale_disable_cart/views/website_sale_disable_cart_templates.xml +++ b/website_sale_aplicoop/views/website_sale_disable_cart.xml @@ -3,7 +3,7 @@