[IMP] website_sale_aplicoop: readable slug in the group order URL

Group order pages were published under their database id (`/eskaera/1`), which
says nothing to the member opening the link. `group.order` gains a `slug` field
and the public pages move to `/eskaera/<slug>` (e.g. `/eskaera/escola-fructuos`).

The slug is generated from the order name on creation, is unique, and can be
edited under the name on the form; emptying it regenerates it from the current
name. Values that would make the order unreachable are rejected: a plain number
(the legacy numeric URLs win that match) and the static routes served under
`/eskaera/` (`labels`, `save-order`, `i18n`, ...).

`/eskaera/<id>` and `/eskaera/<id>/checkout` are kept as redirects to their slug
URL, so the links already shared with members keep working. The AJAX endpoints
(`load-page`, `save-order`, `confirm`, ...) stay numeric: they never show up in
the address bar. Consequently the frontend now reads the order id from the
`data-order-id` attribute only, as the URL no longer carries it.

The post-migration script fills the slug of the orders that already existed.

Renaming the `/eskaera` prefix itself (`/escolas` for a schools deployment)
needs no code: a *308 Redirect / Rewrite* rule per public route in Website >
Configuration > Redirects serves the pages on the new prefix, rewrites the
links in the templates and redirects the old URLs, per website. Documented in
`readme/CONFIGURE.rst`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
GitHub Copilot 2026-08-11 16:49:54 +02:00
parent 2b1cabbb43
commit 23edee6154
14 changed files with 463 additions and 28 deletions

View file

@ -11,6 +11,7 @@
<field name="sequence" widget="handle"/>
<field name="company_id" optional="hide"/>
<field name="name"/>
<field name="slug" optional="hide"/>
<field name="group_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="type" optional="show"/>
<field name="start_date" optional="show"/>
@ -45,6 +46,10 @@
<h1>
<field name="name" placeholder="Order Name"/>
</h1>
<div class="text-muted d-flex align-items-center">
<span class="me-1">/eskaera/</span>
<field name="slug" placeholder="weekly-order" class="oe_inline" help="Readable identifier used in the public URL of this order. Empty it to generate it again from the name; changing it breaks the links already shared with members."/>
</div>
</div>
</div>
</div>
@ -116,6 +121,7 @@
<field name="arch" type="xml">
<search string="Group Orders">
<field name="name"/>
<field name="slug"/>
<field name="group_ids"/>
<field name="type"/>
<field name="state"/>

View file

@ -13,6 +13,7 @@
// Items are embedded directly in the script (pre-serialized JSON from controller)
var itemsJson = <t t-raw="items_json"/>; // This is a JSON array/string
var groupOrderId = <t t-esc="group_order_id"/>;
var groupOrderUrl = '<t t-esc="group_order_url"/>';
var saleOrderName = '<t t-esc="sale_order_name"/>';
var pickupDay = '<t t-esc="pickup_day or ''"/>';
var pickupDate = '<t t-esc="pickup_date or ''"/>';
@ -67,7 +68,7 @@
// Redirect to group order page
// The JavaScript on that page will detect this and load the items
window.location.href = '/eskaera/' + groupOrderId;
window.location.href = groupOrderUrl;
</script>
</body>
</html>

View file

@ -28,7 +28,7 @@
</strong>
</span>
</div>
<a t-attf-href="/eskaera/{{ order.id }}" class="eskaera-order-card-link" t-attf-aria-label="View products for order {{ order.name }}">
<a t-attf-href="/eskaera/{{ order.slug }}" class="eskaera-order-card-link" t-attf-aria-label="View products for order {{ order.name }}">
<div class="card-header-top d-flex gap-2 align-items-center order-header-margin">
<t t-set="image_to_show" t-value="order.image or (order.group_ids[0].image_1920 if order.group_ids else False)" />
<t t-if="image_to_show">
@ -47,7 +47,7 @@
</div>
</a>
<t t-call="website_sale_aplicoop.eskaera_order_card_meta" />
<a t-attf-href="/eskaera/{{ order.id }}" class="btn btn-primary btn-sm" aria-label="Browse products for {{ order.name }}">
<a t-attf-href="/eskaera/{{ order.slug }}" class="btn btn-primary btn-sm" aria-label="Browse products for {{ order.name }}">
<i class="fa fa-shopping-bag" aria-hidden="true" t-translation="off" />
<span>Browse Products</span>
</a>
@ -329,7 +329,7 @@
<i class="fa fa-truck cart-icon-size" aria-hidden="true" />
</button>
</t>
<a t-attf-href="/eskaera/{{ group_order.id }}/checkout" class="btn btn-success cart-btn-compact" aria-label="Proceed to checkout" data-bs-title="Proceed to Checkout" data-bs-toggle="tooltip">
<a t-attf-href="/eskaera/{{ group_order.slug }}/checkout" class="btn btn-success cart-btn-compact" aria-label="Proceed to checkout" data-bs-title="Proceed to Checkout" data-bs-toggle="tooltip">
<i class="fa fa-check cart-icon-size" aria-hidden="true" />
</a>
<button type="button" class="btn btn-outline-danger cart-btn-compact" id="clear-cart-btn" t-attf-data-order-id="{{ group_order.id }}" data-bs-title="Clear Cart" data-bs-toggle="tooltip" aria-label="Clear Cart">
@ -344,7 +344,7 @@
<button type="button" class="btn btn-outline-danger btn-sm" id="clear-cart-btn-footer" t-attf-data-order-id="{{ group_order.id }}" data-bs-title="Clear Cart" data-bs-toggle="tooltip" aria-label="Clear Cart">
<i class="fa fa-trash me-1" aria-hidden="true" />Clear Cart
</button>
<a t-attf-href="/eskaera/{{ group_order.id }}/checkout" class="btn btn-success checkout-btn-lg" data-bs-title="Proceed to Checkout" data-bs-toggle="tooltip">
<a t-attf-href="/eskaera/{{ group_order.slug }}/checkout" class="btn btn-success checkout-btn-lg" data-bs-title="Proceed to Checkout" data-bs-toggle="tooltip">
Proceed to Checkout
</a>
</div>
@ -555,7 +555,7 @@
<i class="fa fa-save" aria-hidden="true" t-translation="off" />
<span t-esc="labels.get('save_draft', 'Save Draft')" />
</button>
<a t-attf-href="/eskaera/{{ group_order.id }}" class="btn btn-outline-secondary btn-lg" aria-label="Back to cart page" data-bs-title="Back to Cart" data-bs-toggle="tooltip">
<a t-attf-href="/eskaera/{{ group_order.slug }}" class="btn btn-outline-secondary btn-lg" aria-label="Back to cart page" data-bs-title="Back to Cart" data-bs-toggle="tooltip">
<i class="fa fa-arrow-left" aria-hidden="true" t-translation="off" />
<span>Back to Cart</span>
</a>