addons-cm/website_sale_aplicoop/views/website_templates.xml
GitHub Copilot 394f9a60a4 [FIX] website_sale_aplicoop: payment-neutral label on the checkout button
The cart button that opens /eskaera/<slug>/checkout read "Proceed to
Checkout", and the curated eu/ca translations went further ("Ordaintzera
Joan", "Finalitza la Comanda"), all implying a payment step. With
instances running both with and without online payment, revert it to the
neutral "Review Order" in the source string, the JS labels and the es/eu/ca
translations.

Changing the msgid also drops the stale DB translation on update, so the
instance that still showed "Ir al pago" picks up the fresh term.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 19:33:43 +02:00

868 lines
66 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version='1.0' encoding='utf-8'?>
<odoo>
<data>
<template id="eskaera_page" name="Eskaera Page">
<t t-call="website.layout">
<div id="wrap" class="eskaera-page oe_structure oe_empty" data-name="Eskaera Orders">
<div class="container">
<div class="row">
<div class="col-lg-12">
<h1>Available Orders</h1>
<p class="text-muted" role="status">Browse and select an order to view its products.</p>
</div>
</div>
<div class="row mt-4">
<div class="col-lg-12">
<div class="oe_structure oe_empty" data-name="Before Orders" />
<t t-if="active_orders">
<div class="eskaera-orders eskaera-orders-grid">
<t t-foreach="active_orders" t-as="order">
<div class="eskaera-order-card-wrapper">
<div t-attf-class="eskaera-order-card{{ ' eskaera-order-card--special' if order.type == 'special' else '' }}">
<div class="card-body">
<div class="position-absolute order-badge-position">
<span class="badge text-bg-primary d-flex align-items-center gap-2 order-badge-custom">
<i class="fa fa-shopping-bag" aria-hidden="true" />
<strong>
<t t-esc="order.available_products_count" />
</strong>
</span>
</div>
<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">
<img t-att-src="image_data_uri(image_to_show)" alt="Order image" class="order-thumbnail-sm" />
</t>
<div class="flex-grow-1">
<h5 class="card-title mb-1">
<t t-esc="order.name" />
</h5>
<t t-if="order.description">
<p class="text-muted small mb-0 order-desc-text">
<t t-esc="(order.description[:150] + '...') if len(order.description) &gt; 200 else order.description" />
</p>
</t>
</div>
</div>
</a>
<t t-call="website_sale_aplicoop.eskaera_order_card_meta" />
<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>
</div>
</div>
</div>
</t>
</div>
</t>
<t t-else="">
<div class="eskaera-empty-state">
<div class="alert alert-info" role="status" aria-live="polite">
<p>No group orders available this week.</p>
</div>
</div>
</t>
<div class="oe_structure oe_empty mt-4" data-name="After Orders" />
</div>
</div>
</div>
</div>
</t>
</template>
<template id="eskaera_order_card_meta" name="Order Card Metadata">
<div class="card-meta-compact mt-3">
<div class="meta-grid">
<t t-if="order.cutoff_day and order.cutoff_date">
<div class="meta-item">
<span class="meta-label">Cutoff</span>
<span class="meta-value">
<t t-esc="day_names[int(order.cutoff_day) % 7]" /> <t t-esc="order.cutoff_date.strftime('%d/%m')" />
</span>
</div>
</t>
<t t-if="order.pickup_slot_ids">
<div class="meta-item">
<span class="meta-label">Pickup slots</span>
<span class="meta-value">
<t t-foreach="order.pickup_slot_ids" t-as="slot">
<div class="slot-entry">
<t t-esc="day_names[int(slot.weekday) % 7]" />
&#160;
<t t-esc="('%02d:%02d%02d:%02d' % (int(slot.start_hour or 0), int(((slot.start_hour or 0) % 1) * 60), int(slot.end_hour or 0), int(((slot.end_hour or 0) % 1) * 60)))" />
<t t-if="slot.label">&#160;(<t t-esc="slot.label" />)</t>
</div>
</t>
</span>
</div>
</t>
<t t-elif="order.pickup_day and order.pickup_date">
<div class="meta-item">
<span class="meta-label">Pickup</span>
<span class="meta-value">
<t t-esc="day_names[int(order.pickup_day) % 7]" /> <t t-esc="order.pickup_date.strftime('%d/%m')" />
</span>
</div>
</t>
<t t-if="order.home_delivery">
<!-- Badge first, then the date, centred as a pair. Source order
matches reading order, so no CSS reordering is involved.
No "Delivery" label: the badge already names the thing.
text-bg-* instead of bg-*: Bootstrap picks a contrasting
foreground. A badge's default white on green is 3.13:1. -->
<div class="meta-item meta-item--delivery">
<span class="badge text-bg-success"><i class="fa fa-truck" aria-hidden="true" /> Home Delivery</span>
<t t-if="order.delivery_date">
<span class="meta-value">
<t t-esc="day_names[order.delivery_date.weekday()]" /> <t t-esc="order.delivery_date.strftime('%d/%m')" />
</span>
</t>
</div>
</t>
<t t-if="order.type == 'special'">
<div class="meta-item meta-item--full">
<span class="badge special-order-badge"><i class="fa fa-star" aria-hidden="true" /> Special Order</span>
</div>
<t t-if="order.start_date">
<div class="meta-item">
<span class="meta-label">From</span>
<span class="meta-value"><t t-esc="order.start_date.strftime('%d/%m/%Y')" /></span>
</div>
</t>
<t t-if="order.end_date">
<div class="meta-item">
<span class="meta-label">Until</span>
<span class="meta-value"><t t-esc="order.end_date.strftime('%d/%m/%Y')" /></span>
</div>
</t>
</t>
</div>
</div>
</template>
<template id="order_header" name="Order Header">
<div t-att-class="header_class or 'eskaera-order-header'">
<!-- Responsive Bootstrap utilities rather than a CSS override: the
spacing/alignment utilities are !important, so fighting them from
a stylesheet would need !important too. -->
<div class="d-flex flex-column flex-md-row gap-3 gap-md-5 align-items-start align-items-md-center mb-4">
<t t-set="image_to_show" t-value="group_order.image or (group_order.group_ids[0].image_1920 if group_order.group_ids else False)" />
<t t-if="image_to_show">
<img t-att-src="image_data_uri(image_to_show)" alt="Order image" class="order-thumbnail-md" />
</t>
<div class="flex-grow-1">
<h1 class="mb-2">
<t t-esc="header_title or group_order.name" />
</h1>
<t t-if="group_order.description">
<p class="text-muted mb-0 order-desc-full">
<t t-esc="group_order.description" />
</p>
</t>
</div>
</div>
</div>
</template>
<template id="eskaera_shop" name="Eskaera Shop">
<t t-call="website.layout">
<div id="wrap" class="eskaera-shop-page oe_structure oe_empty" data-name="Eskaera Shop" t-attf-data-delivery-product-id="{{ delivery_product_id or '' }}" t-attf-data-delivery-product-name="{{ delivery_product_name or '' }}" t-attf-data-delivery-product-price="{{ delivery_product_price or '' }}" t-attf-data-home-delivery-enabled="{{ 'true' if group_order.home_delivery else 'false' }}">
<div class="container">
<div class="row mb-4">
<div class="col-lg-12">
<t t-call="website_sale_aplicoop.order_header">
<t t-set="header_class" t-value="'eskaera-order-header'" />
</t>
<!-- Paying confirms the order, so no draft is left to
reuse and nothing else would stop a second, also
payable, order for the same cycle. -->
<t t-if="placed_order">
<div class="alert alert-info d-flex flex-column flex-md-row gap-2 align-items-md-center" role="alert">
<div class="flex-grow-1">
<strong>You already placed an order for this cycle.</strong>
<span t-esc="placed_order.name" />
</div>
<a t-att-href="placed_order_url" class="btn btn-sm btn-outline-primary">View my order</a>
</div>
</t>
<div class="eskaera-order-header">
<!-- Name/value pairs: a description list, so assistive tech
announces each value with its label. The 4 → 2 → 1 column
layout lives in layout/header.css. -->
<dl class="order-info-grid">
<dt class="info-label">Consumer Groups</dt>
<dd class="info-value">
<t t-esc="', '.join(group_order.group_ids.mapped('name'))" />
</dd>
<t t-if="group_order.cutoff_day">
<dt class="info-label">Cutoff Day</dt>
<dd class="info-value">
<t t-esc="day_names[int(group_order.cutoff_day) % 7]" /> (<t t-esc="group_order.cutoff_date.strftime('%d/%m/%Y')" />)</dd>
</t>
<t t-if="group_order.pickup_slot_ids">
<dt class="info-label">Store Pickup Slots</dt>
<dd class="info-value">
<t t-foreach="group_order.pickup_slot_ids" t-as="slot">
<div class="info-value-line">
<t t-esc="day_names[int(slot.weekday) % 7]" />
&#160;
<t t-esc="('%02d:%02d%02d:%02d' % (int(slot.start_hour or 0), int(((slot.start_hour or 0) % 1) * 60), int(slot.end_hour or 0), int(((slot.end_hour or 0) % 1) * 60)))" />
<t t-if="slot.label">&#160;(<t t-esc="slot.label" />)</t>
</div>
</t>
</dd>
</t>
<t t-elif="group_order.pickup_day">
<dt class="info-label">Store Pickup Day</dt>
<dd class="info-value">
<t t-esc="day_names[int(group_order.pickup_day) % 7]" /> (<t t-esc="group_order.pickup_date.strftime('%d/%m/%Y')" />)
</dd>
</t>
<t t-if="group_order.delivery_date and group_order.home_delivery">
<dt class="info-label">Home Delivery Day</dt>
<dd class="info-value">
<t t-esc="day_names[group_order.delivery_date.weekday()]" /> (<t t-esc="group_order.delivery_date.strftime('%d/%m/%Y')" />)</dd>
</t>
<t t-if="group_order.start_date">
<dt class="info-label">Open From</dt>
<dd class="info-value">
<t t-esc="group_order.start_date.strftime('%d/%m/%Y')" />
</dd>
</t>
<t t-if="group_order.end_date">
<dt class="info-label">Open Until</dt>
<dd class="info-value">
<t t-esc="group_order.end_date.strftime('%d/%m/%Y')" />
</dd>
</t>
</dl>
</div>
</div>
</div>
<!-- One flex row for cart/tags/category/search/products: Bootstrap's
order utilities need every reordered item to be a sibling of the
same flex container, so the four blocks that used to live in two
separate rows (filters above, cart+products below) are now columns
here. Unprefixed order-* puts the cart first on mobile (right below
the header, above the filters); order-lg-* restores the desktop
layout: search+category as one row, then tags, then cart+products. -->
<div class="row g-2 eskaera-filters" id="realtimeSearch-filters">
<div class="col-12 col-lg-3 order-1 order-lg-4 mb-4 mb-lg-0">
<div class="card sticky-top cart-sticky-position" aria-label="Cart Summary">
<div class="card-header d-flex justify-content-between align-items-center gap-1">
<h6 class="mb-0 cart-title-sm" id="cart-title">My Cart</h6>
<div class="btn-group cart-btn-group gap-0" role="group">
<!-- data-bs-title feeds the tooltip, it is not an
accessible name: this icon-only button had none. -->
<button type="button" class="btn btn-primary cart-btn-compact" id="save-cart-btn" t-attf-data-order-id="{{ group_order.id }}" aria-label="Save Cart" title="Save Cart" data-bs-toggle="tooltip">
<i class="fa fa-save cart-icon-size" aria-hidden="true" />
</button>
<t t-if="group_order.home_delivery and delivery_product_id">
<button type="button" class="btn btn-outline-warning cart-btn-compact" id="home-delivery-btn" t-att-aria-label="labels.get('toggle_home_delivery', 'Toggle home delivery')" t-att-data-bs-title="labels.get('home_delivery', 'Home Delivery')" data-bs-toggle="tooltip">
<i class="fa fa-truck cart-icon-size" aria-hidden="true" />
</button>
</t>
<!-- js-eskaera-checkout: the cart is pushed to the draft
sale.order before navigating, so the checkout page can
render (and charge) the very lines the member sees. -->
<a t-attf-href="/eskaera/{{ group_order.slug }}/checkout" class="btn btn-success cart-btn-compact js-eskaera-checkout" aria-label="Review Order" title="Review Order" 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 }}" title="Clear Cart" data-bs-toggle="tooltip" aria-label="Clear Cart">
<i class="fa fa-trash cart-icon-size" aria-hidden="true" />
</button>
</div>
</div>
<div class="card-body eskaera-cart-items" id="cart-items-container" t-attf-data-order-id="{{ group_order.id }}" t-attf-data-build-version="{{ module_version }}" aria-labelledby="cart-title" aria-live="polite" aria-relevant="additions removals">
<p class="text-muted">This order's cart is empty</p>
</div>
<div class="card-footer bg-white d-flex justify-content-between align-items-center gap-2">
<button type="button" class="btn btn-outline-danger btn-sm" id="clear-cart-btn-footer" t-attf-data-order-id="{{ group_order.id }}" 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.slug }}/checkout" class="btn btn-success checkout-btn-lg js-eskaera-checkout" title="Review Order" data-bs-toggle="tooltip">
Review Order
</a>
</div>
</div>
</div>
<t t-if="available_tags">
<div class="col-12 order-2 order-lg-3">
<div id="tag-filter-container" class="tag-filter-badges">
<t t-foreach="available_tags" t-as="tag">
<!-- Toggle buttons: aria-pressed carries the filter
state, which realtime_search.js keeps in sync
with the .is-selected / .is-dimmed classes.
The record colour rides in a custom property so
the stylesheet keeps owning the badge. -->
<t t-if="tag['color']">
<button type="button" class="badge tag-filter-badge" t-att-data-tag-id="tag['id']" t-att-data-tag-name="tag['name']" t-att-data-tag-color="tag['color']" t-attf-style="--ac-tag-color: {{ tag['color'] }};" data-toggle="tag-filter" aria-pressed="false">
<span t-esc="tag['name']" /> (<span class="tag-count" t-esc="tag['count']" />)
</button>
</t>
<t t-else="">
<button type="button" class="badge tag-filter-badge tag-use-theme-color" t-att-data-tag-id="tag['id']" t-att-data-tag-name="tag['name']" data-tag-color="" data-toggle="tag-filter" aria-pressed="false">
<span t-esc="tag['name']" /> (<span class="tag-count" t-esc="tag['count']" />)
</button>
</t>
</t>
</div>
</div>
</t>
<div class="col-12 col-lg-5 order-3 order-lg-2">
<select name="category" id="realtime-category-select" class="form-select">
<option value="">Browse Product Categories</option>
<t t-call="website_sale_aplicoop.category_hierarchy_options">
<t t-set="categories" t-value="category_hierarchy" />
<t t-set="depth" t-value="0" />
</t>
</select>
</div>
<div class="col-12 col-lg-7 order-4 order-lg-1">
<div class="search-input-wrapper">
<label class="visually-hidden" for="realtime-search-input">Search products</label>
<input type="search" id="realtime-search-input" class="form-control eskaera-search-input" placeholder="Search products..." autocomplete="off" aria-describedby="realtime-search-status" />
<button type="button" id="clear-search-btn" class="search-clear-btn" aria-label="Clear search" title="Clear search">
×
</button>
</div>
<p id="realtime-search-status" class="visually-hidden" aria-live="polite" aria-atomic="true" />
</div>
<div class="col-12 col-lg-9 order-5 order-lg-5">
<div class="oe_structure oe_empty" data-name="Before Products Filter" />
<t t-if="products">
<div class="products-grid" id="products-grid">
<t t-call="website_sale_aplicoop.eskaera_shop_products" />
</div>
<div id="eskaera-config" t-attf-data-order-id="{{ group_order.id }}" t-attf-data-search="{{ search_query }}" t-attf-data-category="{{ selected_category }}" t-attf-data-per-page="{{ per_page }}" t-attf-data-current-page="{{ current_page }}" t-attf-data-has-next="{{ 'true' if has_next else 'false' }}" class="d-none">
</div>
<t t-if="lazy_loading_enabled and has_next">
<div id="infinite-scroll-container" class="row mt-4">
<div class="col-12 text-center">
<div id="loading-spinner" class="d-none p-4">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-2">Loading more products...</p>
</div>
<button id="load-more-btn" class="btn btn-primary btn-lg d-none" t-attf-data-page="{{ current_page + 1 }}" t-attf-data-order-id="{{ group_order.id }}" t-attf-data-search="{{ search_query }}" t-attf-data-category="{{ selected_category }}" t-attf-data-per-page="{{ per_page }}" aria-label="Load more products">
<i class="fa fa-download me-2" aria-hidden="true" />Load More Products
</button>
</div>
</div>
</t>
</t>
<t t-else="">
<div class="alert alert-warning" role="status" aria-live="polite">
<p>No products available in this order.</p>
</div>
</t>
<div class="oe_structure oe_empty mt-4" data-name="After Products" />
</div>
</div>
</div>
</div>
<script type="text/javascript">
(function() {
'use strict';
function initializeTooltips() {
console.log('[TOOLTIP] Initializing tooltips using native title attribute...');
var tooltipElements = document.querySelectorAll('[data-bs-toggle="tooltip"]');
console.log('[TOOLTIP] Found', tooltipElements.length, 'tooltip elements');
var successCount = 0;
tooltipElements.forEach(function(element) {
// Static labels now live in the title attribute, which QWeb
// translates; data-bs-title (which it does not translate) is
// only left where the text comes from the i18n endpoint.
var title = element.getAttribute('data-bs-title');
if (title) {
element.setAttribute('title', title);
successCount++;
}
});
console.log('[TOOLTIP] Tooltip initialization complete:', successCount, 'elements updated');
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeTooltips);
} else {
// DOM is already loaded
initializeTooltips();
}
})();
</script>
</t>
</template>
<template id="eskaera_checkout" name="Eskaera Checkout">
<t t-call="website.layout">
<div id="wrap" class="eskaera-checkout-page oe_structure oe_empty" data-name="Eskaera Checkout" t-attf-data-order-id="{{ group_order.id }}" t-attf-data-delivery-product-id="{{ delivery_product_id }}" t-attf-data-delivery-product-name="{{ delivery_product_name }}" t-attf-data-delivery-product-price="{{ delivery_product_price }}" t-attf-data-home-delivery-enabled="{{ 'true' if group_order.home_delivery else 'false' }}" t-attf-data-pickup-day="{{ group_order.pickup_day }}" t-attf-data-pickup-date="{{ group_order.pickup_date.strftime('%d/%m/%Y') if group_order.pickup_date else '' }}" t-attf-data-next-pickup-slot-label="{{ group_order.next_pickup_slot_id.label if group_order.next_pickup_slot_id else '' }}" t-attf-data-delivery-notice="{{ (group_order.delivery_notice or '').replace(chr(10), ' ').replace(chr(13), ' ') }}">
<div class="container mt-5">
<div class="row">
<div class="col-lg-10 offset-lg-1">
<div class="mb-4">
<t t-call="website_sale_aplicoop.order_header">
<t t-set="header_class" t-value="'checkout-header'" />
<t t-set="header_title">Confirm Order: <t t-esc="group_order.name" />
</t>
</t>
</div>
<div class="order-info-card card border-0 shadow-sm mb-4">
<div class="card-body">
<!-- Name/value pairs, same treatment as the shop header:
a <label> without a form control to point at labels
nothing, while <dt>/<dd> ties value to name. -->
<div class="row mb-4">
<div class="col-md-4">
<dl class="info-pair">
<dt class="info-label">Cutoff Day</dt>
<t t-if="group_order.cutoff_day and group_order.cutoff_date">
<dd class="info-value">
<t t-esc="day_names[int(group_order.cutoff_day) % 7]" />
<span class="info-date">(<t t-esc="group_order.cutoff_date.strftime('%d/%m/%Y')" />)</span>
</dd>
</t>
<t t-else="">
<dd class="info-value text-muted small">Not configured</dd>
</t>
</dl>
</div>
<div class="col-md-4">
<t t-if="group_order.pickup_slot_ids">
<dl class="info-pair">
<dt class="info-label">Store Pickup Slots</dt>
<dd class="info-value">
<t t-foreach="group_order.pickup_slot_ids" t-as="slot">
<div class="info-value-line">
<t t-esc="day_names[int(slot.weekday) % 7]" />
&#160;
<t t-esc="('%02d:%02d%02d:%02d' % (int(slot.start_hour or 0), int(((slot.start_hour or 0) % 1) * 60), int(slot.end_hour or 0), int(((slot.end_hour or 0) % 1) * 60)))" />
<t t-if="slot.label">&#160;(<t t-esc="slot.label" />)</t>
</div>
</t>
</dd>
</dl>
</t>
<t t-elif="group_order.pickup_day and group_order.pickup_date">
<dl class="info-pair">
<dt class="info-label">Store Pickup Day</dt>
<dd class="info-value" t-attf-data-pickup-date="{{ group_order.pickup_date }}">
<t t-esc="day_names[int(group_order.pickup_day) % 7]" />
<span class="info-date">(<t t-esc="group_order.pickup_date.strftime('%d/%m/%Y')" />)</span>
</dd>
</dl>
</t>
</div>
<div class="col-md-4">
<t t-if="group_order.delivery_date and group_order.home_delivery">
<dl class="info-pair">
<dt class="info-label">Home Delivery Day</dt>
<dd class="info-value">
<t t-esc="day_names[group_order.delivery_date.weekday()]" />
<span class="info-date">(<t t-esc="group_order.delivery_date.strftime('%d/%m/%Y')" />)</span>
</dd>
</dl>
</t>
</div>
</div>
<hr class="my-2" />
</div>
</div>
<h4 class="summary-heading mb-3">Order Summary</h4>
<div class="oe_structure oe_empty mb-3" data-name="Before Summary" />
<!-- The summary comes from the saved sale.order, never from
the localStorage cart: these are the very lines and
amounts the payment form is about to charge, so the two
cannot drift apart. The shop pushes the cart here before
sending the member over. -->
<div id="checkout-summary" class="mb-5">
<t t-if="sale_order">
<t t-call="website_sale_aplicoop.eskaera_order_lines_summary" />
</t>
<t t-else="">
<div class="alert alert-info" role="status">
<i class="fa fa-inbox me-2" aria-hidden="true" t-translation="off" />
<span>This order's cart is empty</span>
</div>
</t>
</div>
<div class="oe_structure oe_empty mb-4" data-name="After Summary" />
<t t-if="sale_order">
<t t-if="group_order.home_delivery">
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<div class="form-check eskaera-delivery-check">
<input type="checkbox" class="form-check-input" id="home-delivery-checkbox" name="home_delivery" t-att-checked="sale_order.home_delivery or None" />
<label class="form-check-label fw-bold" for="home-delivery-checkbox">Home Delivery</label>
</div>
<div t-attf-class="alert alert-info mt-3 eskaera-delivery-notice{{ '' if sale_order.home_delivery else ' d-none' }}" id="delivery-info-alert">
<p class="mb-2">
<i class="fa fa-truck" aria-hidden="true" t-translation="off" />
<t t-if="group_order.delivery_date">
<strong>Delivery Information:</strong> Your order will be delivered at
<t t-esc="day_names[(int(group_order.pickup_day) + 1) % 7]" />
<t t-esc="group_order.delivery_date.strftime('%d/%m/%Y')" />
<t t-if="group_order.delivery_notice">
<br />
<t t-esc="group_order.delivery_notice" />
</t>
</t>
</p>
</div>
</div>
</div>
</t>
<t t-if="online_payment">
<t t-if="pending_transaction">
<div class="alert alert-info" role="alert">
<h5 class="alert-heading">
<i class="fa fa-clock-o me-2" aria-hidden="true" t-translation="off" />
<span>Payment in progress</span>
</h5>
<p class="mb-1">We are still waiting for your payment to be confirmed. Please do not pay again.</p>
<p class="mb-0">
<span>Reference</span>:
<span t-esc="pending_transaction.reference" />
</p>
</div>
<div class="checkout-actions d-grid gap-3">
<a t-att-href="sale_order.get_portal_url()" class="btn btn-outline-secondary btn-lg">
<i class="fa fa-file-text-o" aria-hidden="true" t-translation="off" />
<span>View my order</span>
</a>
</div>
</t>
<t t-elif="payment_ready and payment_available">
<h4 class="summary-heading mb-3">Payment Method</h4>
<!-- The whole provider UI is payment.form; this addon
configures no provider of its own. -->
<div id="payment_method" class="o_not_editable mb-4">
<t t-call="payment.form" />
</div>
<!-- Deliberately outside #o_payment_form: website_sale's
payment_form.js binds every
[name="o_payment_submit_button"] in the document, on
top of payment_form.js's own delegated handler inside
the form. A button inside would get both listeners and
one click would open two transactions. -->
<div class="checkout-actions d-grid gap-3">
<t t-call="payment.submit_button" />
<a t-attf-href="/eskaera/{{ group_order.slug }}" class="btn btn-outline-secondary btn-lg" aria-label="Back to cart page" 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>
</div>
</t>
<t t-else="">
<div class="alert alert-warning" role="alert" t-esc="no_payment_method_message" />
<div class="checkout-actions d-grid gap-3">
<a t-attf-href="/eskaera/{{ group_order.slug }}" class="btn btn-outline-secondary btn-lg" aria-label="Back to cart page" 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>
</div>
</t>
</t>
<t t-else="">
<!-- No online payment: the draft is confirmed in bulk by
the cutoff cron, so the button only re-saves the cart. -->
<div class="checkout-actions d-grid gap-3" id="checkout-form-labels">
<button class="btn btn-success btn-lg" id="confirm-order-btn" t-attf-data-order-id="{{ group_order.id }}" t-att-data-confirmed-label="checkout_button['done_label']" t-att-data-pickup-label="labels.get('pickup_day_label', 'Pickup Day')" t-att-aria-label="checkout_button['hint']" t-att-data-bs-title="checkout_button['label']" t-att-data-tooltip-key="checkout_button['tooltip_key']" data-bs-toggle="tooltip">
<i t-attf-class="fa {{ checkout_button['icon'] }}" aria-hidden="true" t-translation="off" />
<span t-esc="checkout_button['label']" />
</button>
<a t-attf-href="/eskaera/{{ group_order.slug }}" class="btn btn-outline-secondary btn-lg" aria-label="Back to cart page" 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>
</div>
</t>
</t>
<t t-else="">
<div class="checkout-actions d-grid gap-3">
<a t-attf-href="/eskaera/{{ group_order.slug }}" class="btn btn-success btn-lg" aria-label="Back to cart page" 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>
</div>
</t>
</div>
</div>
</div>
<script type="text/javascript">
(function() {
'use strict';
// Initialize groupOrderShop.labels from server-rendered labels
if (!window.groupOrderShop) {
window.groupOrderShop = {};
}
window.groupOrderShop.labels = <t t-raw="labels_json" />;
console.log('[LABELS] Initialized from server:', window.groupOrderShop.labels);
})();
</script>
</div>
</t>
</template>
<template id="eskaera_order_lines_summary" name="Placed Order Summary">
<!-- The order summary of every page past the shop. The cart is a
localStorage object while the member shops, but from the
checkout on the amounts shown must be the ones that will be
charged, so they come from the sale.order. -->
<div class="checkout-summary-container" role="region" tabindex="0" aria-label="Order summary">
<table class="table table-hover checkout-summary-table">
<caption class="visually-hidden">Products in this order, with quantity, price and subtotal</caption>
<thead class="table-dark">
<tr>
<th scope="col" class="col-name">Product</th>
<th scope="col" class="col-qty text-center">Quantity</th>
<th scope="col" class="col-subtotal text-end">Subtotal</th>
</tr>
</thead>
<tbody>
<tr t-foreach="sale_order.order_line" t-as="line">
<td class="col-name" t-esc="line.name" />
<td class="col-qty text-center" t-esc="line.product_uom_qty" />
<td class="col-subtotal text-end">
<span t-field="line.price_total" t-options="{'widget': 'monetary', 'display_currency': sale_order.currency_id}" />
</td>
</tr>
</tbody>
</table>
<div class="checkout-total-section">
<div class="total-row">
<span class="total-label">Total</span>:
<span class="total-amount">
<span t-field="sale_order.amount_total" t-options="{'widget': 'monetary', 'display_currency': sale_order.currency_id}" />
</span>
</div>
</div>
</div>
</template>
<template id="eskaera_payment_confirmation" name="Eskaera Payment Confirmation">
<t t-call="website.layout">
<div id="wrap" class="eskaera-checkout-page oe_structure oe_empty" data-name="Eskaera Payment Confirmation" t-attf-data-clear-cart-order-id="{{ group_order.id }}">
<div class="container mt-5">
<div class="row">
<div class="col-lg-10 offset-lg-1">
<div class="mb-4">
<t t-call="website_sale_aplicoop.order_header">
<t t-set="header_class" t-value="'checkout-header'" />
<t t-set="header_title">Order Confirmed: <t t-esc="group_order.name" />
</t>
</t>
</div>
<!-- The browser lands here from /payment/status once
post-processing has run, but that is asynchronous, so
the page reports the order's actual state instead of
assuming it was confirmed. -->
<t t-if="is_paid">
<div class="alert alert-success" role="alert">
<h5 class="alert-heading">
<i class="fa fa-check-circle me-2" aria-hidden="true" t-translation="off" />
<span>Thank you, your order is confirmed</span>
</h5>
<p class="mb-0">
<span>Order</span>
<span t-esc="sale_order.name" />
</p>
</div>
</t>
<t t-else="">
<div class="alert alert-info" role="alert">
<h5 class="alert-heading">
<i class="fa fa-clock-o me-2" aria-hidden="true" t-translation="off" />
<span>Your payment is being processed</span>
</h5>
<p class="mb-0">Your order will be confirmed as soon as we receive the payment. You can follow it from your orders page.</p>
</div>
</t>
<t t-if="sale_order.pickup_slot_label">
<div class="order-info-card card border-0 shadow-sm mb-4">
<div class="card-body">
<dl class="info-pair mb-0">
<dt class="info-label">Pickup</dt>
<dd class="info-value" t-esc="sale_order.pickup_slot_label" />
</dl>
</div>
</div>
</t>
<h4 class="summary-heading mb-3">Order Summary</h4>
<div class="mb-5">
<t t-call="website_sale_aplicoop.eskaera_order_lines_summary" />
</div>
<div class="checkout-actions d-grid gap-3">
<a t-att-href="sale_order.get_portal_url()" class="btn btn-primary btn-lg">
<i class="fa fa-file-text-o" aria-hidden="true" t-translation="off" />
<span>View my order</span>
</a>
<a href="/eskaera" class="btn btn-outline-secondary btn-lg">
<i class="fa fa-arrow-left" aria-hidden="true" t-translation="off" />
<span>Back to Orders</span>
</a>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<template id="category_hierarchy_options" name="Category Hierarchy Options">
<t t-foreach="categories" t-as="cat">
<t t-set="padding_px" t-value="depth * 20" />
<t t-set="prefix">
<t t-foreach="range(depth)" t-as="i"></t>
</t>
<option t-att-value="str(cat['id'])" t-attf-style="padding-left: {{ padding_px }}px;">
<t t-esc="prefix" />
<t t-esc="cat['name']" />
</option>
<t t-if="cat['children']">
<t t-call="website_sale_aplicoop.category_hierarchy_options">
<t t-set="categories" t-value="cat['children']" />
<t t-set="depth" t-value="depth + 1" />
</t>
</t>
</t>
</template>
<template id="eskaera_shop_products" name="Eskaera Shop Products">
<t t-foreach="products" t-as="product">
<t t-set="has_tags" t-value="bool(product.product_tag_ids)" />
<t t-set="supplier_text" t-value="product_supplier_info.get(product.id)" />
<t t-set="has_origin" t-value="bool(product.origin_text)" />
<t t-set="has_info" t-value="has_tags or bool(supplier_text) or has_origin" />
<div class="product-card-wrapper product-card" t-attf-data-product-name="{{ product.name }}" t-attf-data-category-id="{{ product.categ_id.id if product.categ_id else '' }}" t-attf-data-product-tags="{{ ','.join(str(t.id) for t in product.product_tag_ids) if product.product_tag_ids else '' }}">
<div class="card h-100">
<div class="product-media">
<t t-if="product.image_128">
<img t-attf-src="data:image/png;base64,{{ product.image_128.decode() }}" class="card-img-top product-img-cover" t-attf-alt="{{ product.name }}" />
</t>
<t t-else="">
<div class="card-img-top bg-light d-flex align-items-center justify-content-center product-img-placeholder">
<i class="fa fa-image fa-3x text-muted" aria-hidden="true" />
</div>
</t>
<t t-if="has_info">
<!-- Origin/supplier/tags used to sit unconditionally in the card
body; on the compact "cromo" tile there is no room to show
them inline, so they move behind this toggle (see
.product-info-panel below). -->
<button type="button" class="product-info-toggle" aria-expanded="false" t-attf-aria-controls="product-info-{{ product.id }}" aria-label="Show origin, supplier and tags" title="Origin, supplier and tags">
<i class="fa fa-info" aria-hidden="true" t-translation="off" />
</button>
</t>
</div>
<t t-if="product.dynamic_ribbon_id">
<t t-set="ribbon" t-value="product.dynamic_ribbon_id" />
<span t-attf-class="o_ribbon {{ ribbon._get_position_class() }} z-1" t-attf-style="color: {{ ribbon.text_color }}; background-color: {{ ribbon.bg_color }};" t-esc="ribbon.name" />
</t>
<t t-set="user_ribbon" t-value="product.sudo().variant_ribbon_id or product.sudo().website_ribbon_id" />
<t t-if="user_ribbon">
<span t-attf-class="o_ribbon {{ user_ribbon._get_position_class() }} z-1" t-attf-style="color: {{ user_ribbon.text_color }}; background-color: {{ user_ribbon.bg_color }};" t-esc="user_ribbon.name" />
</t>
<div class="card-body d-flex flex-column">
<h6 class="card-title" t-esc="product.name" />
<t t-if="has_info">
<div t-attf-id="product-info-{{ product.id }}" class="product-info-panel" hidden="hidden">
<t t-if="has_tags">
<div class="product-tags">
<t t-foreach="filtered_product_tags.get(product.id, {}).get('published_tags', product.product_tag_ids)" t-as="tag">
<t t-if="tag.color">
<!-- Per-record colour: passed as a custom property so the
stylesheet keeps owning the badge, no !important needed. -->
<span class="badge badge-km" t-attf-style="--ac-tag-color: {{ tag.color }};" t-esc="tag.name" />
</t>
<t t-else="">
<span class="badge badge-km tag-use-theme-color" t-esc="tag.name" />
</t>
</t>
</div>
</t>
<t t-if="supplier_text">
<p class="product-supplier">
<small t-esc="supplier_text" />
</p>
</t>
<t t-if="has_origin">
<p class="product-origin">
<small>
<i class="fa fa-map-marker" aria-hidden="true" />
<t t-out="product.origin_text" />
</small>
</p>
</t>
</div>
</t>
<t t-set="price_info" t-value="product_price_info.get(product.id, {})" />
<t t-set="display_price" t-value="product_display_info.get(product.id, {}).get('display_price', 0.0)" />
<t t-set="base_price" t-value="price_info.get('list_price', product.list_price)" />
<h6 class="card-text product-price-display">
<span class="product-price-main">
<t t-esc="'%.2f' % display_price" />
<t t-if="product_display_info.get(product.id, {}).get('price_unit_suffix')">
<span class="ms-1">
<t t-esc="product_display_info.get(product.id, {}).get('price_unit_suffix')" />
</span>
</t>
</span>
<t t-if="price_info.get('has_discounted_price', False)">
<small class="text-muted text-decoration-line-through ms-1">
<t t-esc="'%.2f' % base_price" />
</small>
</t>
</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="tax_incl_base_unit_price and product.base_unit_name">
<p class="product-unit-price text-muted">
<t t-esc="'%.2f' % tax_incl_base_unit_price" /> € / <t t-esc="product.base_unit_name" />
</p>
</t>
</div>
<t t-set="safe_uom_category" t-value="product_display_info.get(product.id, {}).get('safe_uom_category', '')" />
<t t-set="quantity_step" t-value="product_display_info.get(product.id, {}).get('quantity_step', 1)" />
<t t-set="order_id_safe" t-value="group_order.id if group_order else ''" />
<t t-set="out_of_stock_label" t-value="product_display_info.get(product.id, {}).get('out_of_stock_label', 'Out of stock')" />
<t t-set="add_to_cart_label" t-value="product_display_info.get(product.id, {}).get('add_to_cart_label', 'Add to cart')" />
<form
class="add-to-cart-form"
t-attf-data-order-id="{{ order_id_safe }}"
t-attf-data-product-id="{{ product.id }}"
t-attf-data-product-name="{{ product.name }}"
t-attf-data-product-price="{{ display_price }}"
t-attf-data-uom-category="{{ safe_uom_category }}"
t-attf-data-quantity-step="{{ quantity_step }}"
t-att-data-out-of-stock="'true' if product.is_out_of_stock else 'false'"
>
<div class="qty-control">
<label t-attf-for="qty_{{ product.id }}" class="visually-hidden">Quantity of <t t-esc="product.name" />
</label>
<button class="qty-decrease" type="button" t-attf-data-product-id="{{ product.id }}" aria-label="Decrease quantity">
<i class="fa fa-minus" aria-hidden="true" />
</button>
<input type="number" t-attf-id="qty_{{ product.id }}" class="product-qty" name="quantity" t-attf-value="1" t-attf-min="{{ quantity_step }}" t-attf-step="{{ quantity_step }}" t-att-max="product_max_qty.get(product.id) if product_max_qty else None" t-att-data-max-qty="product_max_qty.get(product.id) if product_max_qty else None" />
<button class="qty-increase" type="button" t-attf-data-product-id="{{ product.id }}" aria-label="Increase quantity">
<i class="fa fa-plus" aria-hidden="true" />
</button>
<!-- Out of stock stays focusable and clickable on purpose: the handler
explains why instead of the button silently doing nothing.
aria-disabled announces the state without removing it from the
tab order (a real `disabled` would suppress the click event). -->
<button
class="add-to-cart-btn"
type="button"
t-att-data-out-of-stock="'true' if product.is_out_of_stock else 'false'"
t-att-aria-disabled="'true' if product.is_out_of_stock else None"
t-att-aria-label="out_of_stock_label if product.is_out_of_stock else add_to_cart_label"
t-att-title="out_of_stock_label if product.is_out_of_stock else add_to_cart_label"
>
<i t-attf-class="fa {{ 'fa-ban text-muted' if product.is_out_of_stock else 'fa-shopping-cart' }}" aria-hidden="true" />
</button>
</div>
</form>
</div>
</div>
</t>
</template>
</data>
</odoo>