addons-cm/website_sale_aplicoop/views/website_templates.xml
snt 40db038e15 [FIX] website_sale_aplicoop: Simplify order_id expression in form template
The expression 'group_order' in locals() is NOT safe in QWeb templates.
QWeb cannot reliably parse this kind of conditional logic in attributes.

Changed from:
  t-attf-data-order-id="{{ group_order.id if 'group_order' in locals() else '' }}"

To:
  Added t-set: <t t-set="order_id_safe" t-value="group_order.id if group_order else ''"/>
  Use: t-attf-data-order-id="{{ order_id_safe }}"

This ensures:
- Logic is evaluated in Python (safe)
- Template receives simple variable (QWeb-safe)
- No complex expressions in t-attf-* attributes

Files Modified:
- website_sale_aplicoop/views/website_templates.xml
  • Added order_id_safe variable definition
  • Simplified form data-order-id attribute
2026-02-16 23:46:05 +01:00

1285 lines
78 KiB
XML

<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<!-- Template: Group Orders Page (Eskaera) -->
<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">
<!-- Editable area: Above orders list -->
<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 class="eskaera-order-card">
<div class="card-body">
<!-- Product count badge - top right corner -->
<div
class="position-absolute order-badge-position"
>
<span
class="badge bg-primary d-flex align-items-center gap-2 order-badge-custom"
>
<i
class="fa fa-shopping-bag"
/>
<strong><t
t-esc="order.available_products_count"
/></strong>
</span>
</div>
<!-- Order header with image and name (as link) -->
<a
t-attf-href="/eskaera/{{ order.id }}"
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) > 200 else order.description"
/>
</p>
</t>
</div>
</div>
</a>
<!-- Metadata section - outside link -->
<div
class="card-meta-compact mt-3"
>
<table
class="meta-table"
>
<tbody>
<!-- Order Type - ALWAYS SHOW ROW -->
<tr
class="meta-row"
>
<td
class="meta-label-cell"
t-if="order.type"
>
<span
>Order Type</span>
</td>
<td
class="meta-value-cell"
>
<t
t-if="order.type"
>
<t
t-esc="dict(order.fields_get('type', ['selection'])['type']['selection']).get(order.type, order.type)"
/>
</t>
</td>
</tr>
<!-- Period - ALWAYS SHOW ROW -->
<tr
class="meta-row"
>
<td
class="meta-label-cell"
t-if="order.period"
>
<span
>Order Period</span>
</td>
<td
class="meta-value-cell"
>
<t
t-if="order.period"
>
<t
t-esc="dict(order.fields_get('period', ['selection'])['period']['selection']).get(order.period, order.period)"
/>
</t>
</td>
</tr>
<!-- Cutoff Day - ALWAYS SHOW ROW -->
<tr
class="meta-row"
>
<td
class="meta-label-cell"
t-if="order.cutoff_day"
>
<span
>Cutoff Day</span>
</td>
<td
class="meta-value-cell"
>
<t
t-if="order.cutoff_day"
>
<t
t-esc="day_names[int(order.cutoff_day) % 7]"
/> - <t
t-esc="order.cutoff_date.strftime('%d/%m')"
/>
</t>
</td>
</tr>
<tr
class="meta-row"
>
<td
class="meta-label-cell"
t-if="order.pickup_day and order.pickup_date"
>
<span
>Pickup Day</span>
</td>
<td
class="meta-value-cell"
>
<t
t-if="order.pickup_day and order.pickup_date"
>
<t
t-esc="day_names[int(order.pickup_day) % 7]"
/> - <t
t-esc="order.pickup_date.strftime('%d/%m')"
/>
</t>
</td>
</tr>
<!-- End Date - ALWAYS SHOW ROW -->
<tr
class="meta-row"
>
<td
class="meta-label-cell"
t-if="order.end_date"
>
<span
>Open until</span>
</td>
<td
class="meta-value-cell"
>
<t
t-if="order.end_date"
>
<t
t-esc="order.end_date.strftime('%d/%m/%Y')"
/>
</t>
</td>
</tr>
<!-- Home Delivery - ALWAYS SHOW ROW -->
<tr
class="meta-row"
>
<td
class="meta-label-cell"
>
<span
>Home Delivery</span>
</td>
<td
class="meta-value-cell"
>
<t
t-if="order.home_delivery"
>
<span
class="badge bg-success"
>Yes</span>
</t>
<t
t-else=""
>
<span
class="badge bg-warning"
>No</span>
</t>
</td>
</tr>
<!-- Delivery Date - ALWAYS SHOW ROW -->
<tr
class="meta-row"
>
<td
class="meta-label-cell"
t-if="order.delivery_date and order.home_delivery"
>
<span
>Delivery</span>
</td>
<td
class="meta-value-cell"
>
<t
t-if="order.delivery_date and order.home_delivery"
>
<t
t-esc="day_names[order.delivery_date.weekday()]"
/> - <t
t-esc="order.delivery_date.strftime('%d/%m')"
/>
</t>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Browse button - outside link, properly centered -->
<a
t-attf-href="/eskaera/{{ order.id }}"
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>
<!-- Editable area: Below orders list -->
<div
class="oe_structure oe_empty mt-4"
data-name="After Orders"
/>
</div>
</div>
</div>
</div>
<!-- Load translated labels for category selector -->
<script
type="text/javascript"
><![CDATA[
(function() {
'use strict';
console.log('[eskaera_page] Loading translated labels for category selector');
// Fetch translated labels from endpoint
fetch('/eskaera/labels', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': odoo.csrf_token || ''
}
})
.then(response => response.json())
.then(labels => {
console.log('[eskaera_page] Labels received:', labels);
// Update category selector first option text
var categorySelect = document.getElementById('realtime-category-select');
if (categorySelect && categorySelect.options[0] && labels && labels.all_categories) {
categorySelect.options[0].text = labels.all_categories;
console.log('[eskaera_page] Updated category selector to:', labels.all_categories);
} else {
console.log('[eskaera_page] Could not update category selector');
console.log(' categorySelect:', !!categorySelect);
console.log(' categorySelect.options[0]:', categorySelect ? !!categorySelect.options[0] : false);
console.log(' labels:', !!labels);
console.log(' labels.all_categories:', labels ? labels.all_categories : 'N/A');
}
})
.catch(error => {
console.error('[eskaera_page] Error fetching labels:', error);
});
})();
]]></script>
</t>
</template>
<!-- Small QWeb snippets used to render translated confirmation strings
Rendered via ir.ui.view._render_template() with lang in context
to ensure server-side translation regardless of call stack. -->
<template id="confirm_message_snippet" name="Confirm Message Snippet">
<t t-esc="_('Thank you! Your order has been confirmed.')" />
</template>
<template id="confirm_pickup_label_snippet" name="Confirm Pickup Label Snippet">
<t t-esc="_('Pickup Day')" />
</template>
<!-- Shared template: Order Header -->
<template id="order_header" name="Order Header">
<div t-att-class="header_class or 'eskaera-order-header'">
<div class="d-flex gap-5 align-items-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: Group Order Shop (Eskaera) -->
<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">
<div class="container">
<!-- Order Header Info Panel -->
<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>
<div class="eskaera-order-header">
<div class="order-info-grid">
<div class="info-item">
<span t-att-class="'info-label'">Consumer Groups</span>
<span class="info-value"><t t-esc="', '.join(group_order.group_ids.mapped('name'))" /></span>
</div>
<t t-if="group_order.cutoff_day">
<div class="info-item">
<span t-att-class="'info-label'">Cutoff Day</span>
<span 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')" />)</span>
</div>
</t>
<t t-if="group_order.pickup_day">
<div class="info-item">
<span
t-att-class="'info-label'"
>Store Pickup Day</span>
<span 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')"
/>)</span>
</div>
</t>
<t
t-if="group_order.delivery_date and group_order.home_delivery"
>
<div class="info-item">
<span
t-att-class="'info-label'"
>Home Delivery Day</span>
<span class="info-value"><t
t-esc="day_names[group_order.delivery_date.weekday()]"
/> (<t
t-esc="group_order.delivery_date.strftime('%d/%m/%Y')"
/>)</span>
</div>
</t>
<t t-if="group_order.start_date">
<div class="info-item">
<span
t-att-class="'info-label'"
>Open From</span>
<span class="info-value"><t
t-esc="group_order.start_date.strftime('%d/%m/%Y')"
/></span>
</div>
</t>
<t t-if="group_order.end_date">
<div class="info-item">
<span
t-att-class="'info-label'"
>Open Until</span>
<span class="info-value"><t
t-esc="group_order.end_date.strftime('%d/%m/%Y')"
/></span>
</div>
</t>
</div>
</div>
</div>
</div>
<!-- Search and Filter Bar (Full Width, Above Products/Cart) -->
<div class="mb-3" id="realtimeSearch-filters">
<div class="row g-2">
<div class="col-md-7">
<!-- CRITICAL: This input is NOT inside a form to prevent Odoo from transforming it -->
<!-- It must remain a pure HTML input element for realtime_search.js to detect value changes -->
<input type="text" id="realtime-search-input" class="form-control realtime-search-box search-input-styled" placeholder="Search products..." autocomplete="off" />
</div>
<div class="col-md-5">
<select name="category" id="realtime-category-select" class="form-select">
<option value="">Browse Product Categories</option>
<!-- Macro para renderizar categorías recursivamente -->
<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>
<t t-if="available_tags">
<div class="row mt-3">
<div class="col-12">
<div
id="tag-filter-container"
class="tag-filter-badges"
>
<t
t-foreach="available_tags"
t-as="tag"
>
<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="background-color: {{ tag['color'] }} !important; border-color: {{ tag['color'] }} !important; color: #ffffff !important;"
data-toggle="tag-filter"
>
<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"
>
<span
t-esc="tag['name']"
/> (<span
class="tag-count"
t-esc="tag['count']"
/>)
</button>
</t>
</t>
</div>
</div>
</div>
</t>
</div>
<!-- Products and Cart Row -->
<div class="row g-2">
<!-- Products Column -->
<div class="col-lg-9">
<!-- Editable area: Above search/filter -->
<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>
<!-- Load More Button (for lazy loading) -->
<t t-if="lazy_loading_enabled and has_next">
<div class="row mt-4">
<div class="col-12 text-center">
<button
id="load-more-btn"
class="btn btn-primary btn-lg"
t-attf-data-page="{{ current_page + 1 }}"
t-attf-data-order-id="{{ group_order.id }}"
t-attf-data-per-page="{{ per_page }}"
aria-label="Load more products"
>
<i class="fa fa-download me-2" />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>
<!-- Editable area: Below products list -->
<div
class="oe_structure oe_empty mt-4"
data-name="After Products"
/>
</div>
<!-- Cart Column -->
<div class="col-lg-3">
<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">
<button type="button" class="btn btn-primary cart-btn-compact" id="save-cart-btn" t-attf-data-order-id="{{ group_order.id }}" data-bs-title="Save Cart" data-bs-toggle="tooltip">
<i class="fa fa-save cart-icon-size" />
</button>
<button
type="button"
class="btn btn-info cart-btn-compact"
id="reload-cart-btn"
t-attf-data-order-id="{{ group_order.id }}"
data-bs-title="Reload Cart"
data-bs-toggle="tooltip"
>
<i
class="fa fa-refresh cart-icon-size"
/>
</button>
<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"
>
<i
class="fa fa-check cart-icon-size"
aria-hidden="true"
/>
</a>
</div>
</div>
<div class="card-body cart-body-lg" id="cart-items-container" t-attf-data-order-id="{{ group_order.id }}" 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 text-center">
<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">
Proceed to Checkout
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Scripts (in dependency order) -->
<!-- Load i18n_manager first - fetches translations from server -->
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/i18n_manager.js" />
<!-- Keep legacy helpers for backwards compatibility -->
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/i18n_helpers.js" />
<!-- Main shop functionality (depends on i18nManager) -->
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/website_sale.js" />
<!-- UI enhancements -->
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/checkout_labels.js" />
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/home_delivery.js" />
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/realtime_search.js" />
<!-- Initialize tooltips using native title attribute -->
<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) {
var title = element.getAttribute('data-bs-title');
if (title) {
// Set native title attribute for browser-native tooltip
element.setAttribute('title', title);
successCount++;
console.log('[TOOLTIP] ✅ Set title for', element.id || element.className, ':', title);
} else {
console.warn('[TOOLTIP] ⚠️ No data-bs-title found for element:', element.id || element.className);
}
});
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>
<!-- Sub-template: Checkout Order Summary Table with Translations -->
<template id="eskaera_checkout_summary" name="Checkout Order Summary">
<div class="checkout-summary-container">
<table class="table table-hover checkout-summary-table" id="checkout-summary-table">
<thead class="table-dark">
<tr>
<th class="col-name">Product</th>
<th class="col-qty text-center">Quantity</th>
<th class="col-price text-right">Price</th>
<th class="col-subtotal text-right">Subtotal</th>
</tr>
</thead>
<tbody id="checkout-summary-tbody">
<tr id="checkout-empty-row" class="empty-message">
<td colspan="4" class="text-center text-muted py-4">
<i class="fa fa-inbox fa-2x mb-2" />
<p>This order's cart is empty</p>
</td>
</tr>
</tbody>
</table>
<div class="checkout-total-section">
<div class="total-row">
<span class="total-label">Total</span>:
<span class="total-amount" id="checkout-total-amount">0.00</span>
<span class="currency"></span>
</div>
</div>
</div>
</template>
<!-- Template: Group Order Checkout (Eskaera) -->
<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-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-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">
<!-- Header Section -->
<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>
<!-- Order Info Card -->
<div
class="order-info-card card border-0 shadow-sm mb-4"
>
<div class="card-body">
<div class="row mb-4">
<div class="col-md-4">
<div class="info-item">
<label
t-att-class="'info-label'"
>Cutoff Day</label>
<t
t-if="group_order.cutoff_day and group_order.cutoff_date"
>
<span
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>
</span>
</t>
<t t-else="1">
<span
t-att-class="'text-muted small'"
>Not configured</span>
</t>
</div>
</div>
<div class="col-md-4">
<div class="info-item">
<t
t-if="group_order.pickup_day and group_order.pickup_date"
>
<label
t-att-class="'info-label'"
>Store Pickup Day</label>
<span
class="info-value"
t-attf-data-pickup-date="{{ group_order.pickup_date }}"
t-attf-data-delivery-date="{{ group_order.delivery_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>
</span>
</t>
</div>
</div>
<div class="col-md-4">
<div class="info-item">
<t
t-if="group_order.delivery_date and group_order.home_delivery"
>
<label
t-att-class="'info-label'"
>Home Delivery Day</label>
<span
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>
</span>
</t>
</div>
</div>
</div>
<hr class="my-2" />
<div class="row">
<div
class="col-md-6 text-muted small help-text-sm"
>
<i
class="fa fa-info-circle"
aria-hidden="true"
t-translation="off"
/>
<span
>Save your order as a draft before confirming to make final changes if needed.</span>
</div>
<div class="col-md-6 text-end">
<button
class="btn btn-outline-primary save-order-btn-styled"
id="save-order-btn"
t-attf-data-order-id="{{ group_order.id }}"
aria-label="Save order as draft"
>
<i
class="fa fa-save save-icon-size"
aria-hidden="true"
t-translation="off"
/>
<span>Save as Draft</span>
</button>
</div>
</div>
</div>
</div>
<!-- Summary Section -->
<h4
class="summary-heading mb-3"
>Order Summary</h4>
<!-- Editable area: Above summary -->
<div
class="oe_structure oe_empty mb-3"
data-name="Before Summary"
/>
<div id="checkout-summary" class="mb-5">
<t
t-call="website_sale_aplicoop.eskaera_checkout_summary"
>
<t t-set="labels" t-value="{}" />
</t>
</div>
<!-- Editable area: Below summary -->
<div
class="oe_structure oe_empty mb-4"
data-name="After Summary"
/>
<!-- Home Delivery Checkbox -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="home-delivery-checkbox" name="home_delivery" />
<label class="form-check-label fw-bold" for="home-delivery-checkbox">Home Delivery</label>
</div>
<div
id="delivery-info-alert"
class="alert alert-info mt-3 d-none"
>
<p class="mb-2">
<i
class="fa fa-truck"
aria-hidden="true"
t-translation="off"
/>
<t
t-if="group_order.delivery_date and group_order.home_delivery"
>
<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>
<!-- Warning Alert -->
<div
class="alert alert-warning alert-dismissible fade show"
role="alert"
>
<div>
<i
class="fa fa-exclamation-triangle"
aria-hidden="true"
t-translation="off"
/>
<span class="fw-bold">
<t
t-if="request.env.context.get('lang') == 'eu_ES' or request.env.context.get('lang') == 'eu'"
>Garrantzitsua</t>
<t
t-elif="request.env.context.get('lang') == 'es_ES' or request.env.context.get('lang') == 'es'"
>Importante</t>
<t t-else="">Important</t>
</span>:
</div>
<p>
<t
t-if="request.env.context.get('lang') == 'eu_ES' or request.env.context.get('lang') == 'eu'"
>Behin eskaera hau berretsi ondoren, ezin izango duzu aldatu. Mesedez, arretaz berrikusi berretsi aurretik.</t>
<t
t-elif="request.env.context.get('lang') == 'es_ES' or request.env.context.get('lang') == 'es'"
>Una vez confirmes este pedido, no podrás modificarlo. Por favor, revisa cuidadosamente antes de confirmar.</t>
<t
t-else=""
>Once you confirm this order, you will not be able to modify it. Please review carefully before confirming.</t>
</p>
<button
type="button"
class="btn-close"
data-bs-dismiss="alert"
aria-label="Close"
/>
</div>
<!-- Action Buttons -->
<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 }}" data-confirmed-label="Order confirmed" data-pickup-label="Pickup Day" aria-label="Confirm and send order" data-bs-title="Confirm Order" data-bs-toggle="tooltip">
<i class="fa fa-check-circle" aria-hidden="true" t-translation="off" />
<span>Confirm Order</span>
</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">
<i class="fa fa-arrow-left" aria-hidden="true" t-translation="off" />
<span>Back to Cart</span>
</a>
</div>
</div>
</div>
</div>
<!-- Initialize translated labels for JavaScript (same as in eskaera) -->
<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>
<!-- Scripts (in dependency order) -->
<!-- Load i18n_manager first - fetches translations from server -->
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/i18n_manager.js" />
<!-- Keep legacy helpers for backwards compatibility -->
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/i18n_helpers.js" />
<!-- Main shop functionality (depends on i18nManager) -->
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/website_sale.js" />
<!-- UI enhancements -->
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/checkout_labels.js" />
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/home_delivery.js" />
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/checkout_summary.js" />
<script type="text/javascript">
// Auto-load cart from localStorage when accessing checkout directly
(function() {
'use strict';
// Get order ID from button
var confirmBtn = document.getElementById('confirm-order-btn');
if (!confirmBtn) return;
var orderId = confirmBtn.getAttribute('data-order-id');
var cartKey = 'eskaera_' + orderId + '_cart';
// Check if there's a saved cart and load it
var savedCart = localStorage.getItem(cartKey);
if (savedCart) {
try {
var cart = JSON.parse(savedCart);
console.log('[CHECKOUT AUTO-LOAD] Cart found in localStorage:', cart);
// Simulate cart loading by triggering a custom event
// The checkout_labels.js will listen for cart data
var event = new CustomEvent('cartLoaded', { detail: { cart: cart } });
document.dispatchEvent(event);
} catch (e) {
console.error('[CHECKOUT AUTO-LOAD] Error parsing cart:', e);
}
} else {
console.log('[CHECKOUT AUTO-LOAD] No cart found in localStorage');
}
})();
</script>
</div>
</t>
</template>
<!-- Recursive macro to render category hierarchy for select dropdown -->
<template id="category_hierarchy_options" name="Category Hierarchy Options">
<!--
Macro para renderizar recursivamente la jerarquía de categorías.
Todas las categorías son seleccionables, indentadas por profundidad.
Parámetros:
- categories: lista de categorías a renderizar
- depth: nivel de profundidad actual (para padding/indentación)
-->
<t t-foreach="categories" t-as="cat">
<!-- Calcular padding basado en profundidad: 20px por nivel -->
<t t-set="padding_px" t-value="depth * 20" />
<!-- Crear prefijo visual con flechas según profundidad -->
<t t-set="prefix">
<t t-foreach="range(depth)" t-as="i"></t>
</t>
<!-- Renderizar como opción indentada y seleccionable -->
<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>
<!-- Renderizar hijos recursivamente si existen -->
<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: Eskaera Shop Products (for lazy loading) -->
<template id="eskaera_shop_products" name="Eskaera Shop Products">
<t t-foreach="products" t-as="product">
<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">
<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"
/>
</div>
</t>
<div
class="card-body d-flex flex-column"
>
<h6
class="card-title"
t-esc="product.name"
/>
<t
t-if="product.product_tag_ids"
>
<div
class="product-tags mb-2"
>
<t
t-foreach="filtered_product_tags.get(product.id, {}).get('published_tags', product.product_tag_ids)"
t-as="tag"
>
<t
t-if="tag.color"
>
<span
class="badge badge-km"
t-attf-style="background-color: {{ tag.color }} !important; border-color: {{ tag.color }} !important; color: #ffffff !important;"
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="product_supplier_info.get(product.id)"
>
<p
class="product-supplier mb-2"
>
<small><t
t-esc="product_supplier_info[product.id]"
/></small>
</p>
</t>
<t
t-if="product.country_id or product.state_id"
>
<p
class="product-origin mb-2"
>
<small>
<i
class="fa fa-map-marker"
aria-hidden="true"
/>
<t
t-if="product.state_id"
>
<t
t-out="product.state_id.name"
/><t
t-if="product.country_id"
>, </t>
</t>
<t
t-if="product.country_id"
>
<t
t-out="product.country_id.name"
/>
</t>
</small>
</p>
</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"
/>
</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-if="product.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;"
>
<t
t-esc="'%.2f' % product.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="order_id_safe"
t-value="group_order.id if group_order else ''"
/>
<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 }}"
>
<div class="qty-control">
<label
t-attf-for="qty_{{ product.id }}"
class="sr-only"
>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"
/>
</button>
<input
type="number"
t-attf-id="qty_{{ product.id }}"
class="product-qty"
name="quantity"
value="1"
min="1"
step="1"
/>
<button
class="qty-increase"
type="button"
t-attf-data-product-id="{{ product.id }}"
aria-label="Increase quantity"
>
<i
class="fa fa-plus"
/>
</button>
<button
class="add-to-cart-btn"
type="button"
t-attf-aria-label="Add {{ product.name }} to cart"
t-attf-title="Add {{ product.name }} to cart"
>
<i
class="fa fa-shopping-cart"
aria-hidden="true"
/>
</button>
</div>
</form>
</div>
</div>
</t>
</template>
</data>
</odoo>