Aplicoop desde el repo de kidekoop
This commit is contained in:
parent
69917d1ec2
commit
7cff89e418
93 changed files with 313992 additions and 0 deletions
200
website_sale_aplicoop/views/group_order_views.xml
Normal file
200
website_sale_aplicoop/views/group_order_views.xml
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- Tree view for Group Order -->
|
||||
<record id="view_group_order_tree" model="ir.ui.view">
|
||||
<field name="name">group.order.tree</field>
|
||||
<field name="model">group.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Group Orders">
|
||||
<field name="company_id" optional="hide"/>
|
||||
<field name="name"/>
|
||||
<field name="group_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
|
||||
<field name="type" optional="show"/>
|
||||
<field name="start_date" optional="show"/>
|
||||
<field name="end_date" optional="show"/>
|
||||
<field name="home_delivery" optional="hide"/>
|
||||
<field name="delivery_product_id" optional="hide"/>
|
||||
<field name="state" optional="show"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Form view for Group Order -->
|
||||
<record id="view_group_order_form" model="ir.ui.view">
|
||||
<field name="name">group.order.form</field>
|
||||
<field name="model">group.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Group Order">
|
||||
<header>
|
||||
<button name="action_open" type="object" string="Open" invisible="state != 'draft'" class="oe_highlight"/>
|
||||
<button name="action_close" type="object" string="Close" invisible="state != 'open'"/>
|
||||
<button name="action_cancel" type="object" string="Cancel" invisible="state in ('closed', 'cancelled')"/>
|
||||
<button name="action_reset_to_draft" type="object" string="Reset to Draft" invisible="state != 'closed'" class="oe_highlight"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,open,closed"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<div class="row">
|
||||
<div class="col-md-1">
|
||||
<field name="image" widget="image" class="oe_avatar"/>
|
||||
</div>
|
||||
<div class="col-md-11">
|
||||
<h1>
|
||||
<field name="name" placeholder="Order Name"/>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="company_id" help="Company that owns this group order"/>
|
||||
<field name="group_ids" widget="many2many_tags" help="Groups that can participate in this order"/>
|
||||
<field name="type" help="Type of group order: Regular, Special, or Promotional"/>
|
||||
<field name="start_date" help="Day when the order opens for purchases"/>
|
||||
<field name="end_date" help="Day when the order closes (empty = permanent)"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="period" help="How often this order repeats"/>
|
||||
<field name="pickup_day" help="Day when members pick up orders"/>
|
||||
<field name="cutoff_day" help="Day when purchases stop"/>
|
||||
<field name="home_delivery" help="Enable home delivery option for this order"/>
|
||||
<field name="delivery_product_id" invisible="not home_delivery" required="home_delivery" help="Product to use for home delivery"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Description">
|
||||
<field name="description" placeholder="Free text description..." nolabel="1"/>
|
||||
</group>
|
||||
<group string="Delivery">
|
||||
<field name="delivery_notice" placeholder="Information about home delivery..." nolabel="1"/>
|
||||
</group>
|
||||
<group string="Associations">
|
||||
<field name="supplier_ids" widget="many2many_tags" help="Products from these suppliers will be available"/>
|
||||
<field name="product_ids" widget="many2many_tags" help="Directly assigned products (highest priority)"/>
|
||||
<field name="category_ids" widget="many2many_tags" help="Products in these categories will be available"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Search view for Group Order -->
|
||||
<record id="view_group_order_search" model="ir.ui.view">
|
||||
<field name="name">group.order.search</field>
|
||||
<field name="model">group.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Group Orders">
|
||||
<field name="name"/>
|
||||
<field name="group_ids"/>
|
||||
<field name="type"/>
|
||||
<field name="state"/>
|
||||
<separator/>
|
||||
<filter name="draft" string="Draft" domain="[('state', '=', 'draft')]"/>
|
||||
<filter name="open" string="Open" domain="[('state', '=', 'open')]"/>
|
||||
<filter name="closed" string="Closed" domain="[('state', '=', 'closed')]"/>
|
||||
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Acción para Group Order -->
|
||||
<record id="action_group_order" model="ir.actions.act_window">
|
||||
<field name="name">Group Orders</field>
|
||||
<field name="res_model">group.order</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new group order
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Menú para acceder a Group Order -->
|
||||
<menuitem id="menu_group_order" name="Consumer Group Management" parent="website_sale.menu_ecommerce" sequence="50"/>
|
||||
<menuitem id="menu_group_order_list" name="Consumer Group Orders" parent="menu_group_order" action="action_group_order" sequence="1"/>
|
||||
|
||||
<!-- Consumer Groups Views -->
|
||||
<record id="view_consumer_group_tree" model="ir.ui.view">
|
||||
<field name="name">consumer.group.tree</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Consumer Groups">
|
||||
<field name="name"/>
|
||||
<field name="email"/>
|
||||
<field name="phone"/>
|
||||
<field name="city"/>
|
||||
<field name="member_ids" widget="many2many_tags"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_consumer_group_form" model="ir.ui.view">
|
||||
<field name="name">consumer.group.form</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Consumer Group">
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box"/>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<field name="image_1920" widget="image" class="oe_avatar" options="{'preview_image': 'avatar_128'}"/>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name" placeholder="Group Name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="email"/>
|
||||
<field name="phone"/>
|
||||
<field name="mobile"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="street"/>
|
||||
<field name="city"/>
|
||||
<field name="zip"/>
|
||||
<field name="country_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Members" name="members">
|
||||
<field name="member_ids">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="email"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Internal Notes" name="notes">
|
||||
<field name="comment" placeholder="Internal notes..."/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_consumer_groups" model="ir.actions.act_window">
|
||||
<field name="name">Consumer Groups</field>
|
||||
<field name="res_model">res.partner</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="domain">[('is_group', '=', True)]</field>
|
||||
<field name="context">{'default_is_group': True, 'default_is_company': True}</field>
|
||||
<field name="view_ids" eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'list', 'view_id': ref('view_consumer_group_tree')}),
|
||||
(0, 0, {'view_mode': 'form', 'view_id': ref('view_consumer_group_form')})]"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new consumer group
|
||||
</p>
|
||||
<p>
|
||||
Consumer groups are organizations that can place group orders together.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_consumer_groups" name="Consumer Groups" parent="menu_group_order" action="action_consumer_groups" sequence="10"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
61
website_sale_aplicoop/views/load_from_history_templates.xml
Normal file
61
website_sale_aplicoop/views/load_from_history_templates.xml
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Template to load items from history and redirect to group order -->
|
||||
<template id="eskaera_load_from_history" name="Load Order from History">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Loading Order...</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
// 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 saleOrderName = '<t t-esc="sale_order_name"/>';
|
||||
var pickupDay = '<t t-esc="pickup_day or ''"/>';
|
||||
var pickupDate = '<t t-esc="pickup_date or ''"/>';
|
||||
var homeDelivery = <t t-esc="home_delivery and 'true' or 'false'"/>;
|
||||
var sameGroupOrder = <t t-esc="same_group_order and 'true' or 'false'"/>;
|
||||
|
||||
console.log('load_from_history template: groupOrderId=', groupOrderId);
|
||||
console.log('load_from_history template: saleOrderName=', saleOrderName);
|
||||
console.log('load_from_history template: pickupDay=', pickupDay);
|
||||
console.log('load_from_history template: pickupDate=', pickupDate);
|
||||
console.log('load_from_history template: homeDelivery=', homeDelivery);
|
||||
console.log('load_from_history template: sameGroupOrder=', sameGroupOrder);
|
||||
console.log('load_from_history template: itemsJson type=', typeof itemsJson);
|
||||
console.log('load_from_history template: itemsJson value=', itemsJson);
|
||||
|
||||
// If itemsJson is already a string, use it directly; if it's an array, stringify it
|
||||
var itemsJsonString = (typeof itemsJson === 'string') ? itemsJson : JSON.stringify(itemsJson);
|
||||
|
||||
// Store items to sessionStorage
|
||||
sessionStorage['load_from_history_' + groupOrderId] = itemsJsonString;
|
||||
|
||||
// Store sale order name separately
|
||||
sessionStorage['load_from_history_order_name_' + groupOrderId] = saleOrderName;
|
||||
|
||||
// Store pickup fields ONLY if from same group order
|
||||
if (sameGroupOrder === 'true') {
|
||||
sessionStorage['load_from_history_pickup_day_' + groupOrderId] = pickupDay;
|
||||
sessionStorage['load_from_history_pickup_date_' + groupOrderId] = pickupDate;
|
||||
sessionStorage['load_from_history_home_delivery_' + groupOrderId] = homeDelivery;
|
||||
console.log('Saved pickup fields (same group order)');
|
||||
} else {
|
||||
console.log('Skipped saving pickup fields (different group order - will use current group order days)');
|
||||
}
|
||||
|
||||
console.log('Saved to sessionStorage[load_from_history_' + groupOrderId + ']:', itemsJsonString);
|
||||
console.log('Saved order name to sessionStorage[load_from_history_order_name_' + groupOrderId + ']:', saleOrderName);
|
||||
|
||||
// Redirect to group order page
|
||||
// The JavaScript on that page will detect this and load the items
|
||||
window.location.href = '/eskaera/' + groupOrderId;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
116
website_sale_aplicoop/views/portal_templates.xml
Normal file
116
website_sale_aplicoop/views/portal_templates.xml
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Extend portal_my_orders to add group order name and pickup day columns -->
|
||||
<template id="portal_my_orders_extend" inherit_id="sale.portal_my_orders" name="My Orders - Add Group Order Info">
|
||||
<!-- Add column headers for new info -->
|
||||
<xpath expr="//tr[hasclass('active')]//th[last()]" position="after">
|
||||
<th>Group Order</th>
|
||||
<th>Pickup Day</th>
|
||||
<th class="text-center">Actions</th>
|
||||
</xpath>
|
||||
|
||||
<!-- Add data cells for each order row -->
|
||||
<xpath expr="//t[@t-foreach='orders']//tr//td[last()]" position="after">
|
||||
<td>
|
||||
<t t-if="order.group_order_id">
|
||||
<t t-esc="order.group_order_id.name"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
—
|
||||
</t>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="order.group_order_id">
|
||||
<t t-set="pickup_day" t-value="order.group_order_id.pickup_day"/>
|
||||
<t t-if="pickup_day is not None and day_names">
|
||||
<t t-esc="day_names[int(pickup_day) % 7]"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-else="">
|
||||
—
|
||||
</t>
|
||||
</td>
|
||||
<!-- Action buttons -->
|
||||
<td class="text-center">
|
||||
<t t-if="order.group_order_id">
|
||||
<!-- Load in Cart button: available for all states -->
|
||||
<a t-attf-href="/eskaera/#{order.group_order_id.id}/load-from-history/#{order.id}"
|
||||
class="btn btn-sm btn-primary me-1"
|
||||
t-att-title="'Load order items into cart' if order.state == 'draft' else 'Create new order from this one'">
|
||||
<i class="fa fa-cart-arrow-down"></i>
|
||||
</a>
|
||||
<!-- Confirm button: only for draft orders -->
|
||||
<t t-if="order.state == 'draft'">
|
||||
<a t-attf-href="/eskaera/#{order.group_order_id.id}/checkout"
|
||||
class="btn btn-sm btn-success"
|
||||
title="Go to checkout to review and confirm order">
|
||||
<i class="fa fa-check"></i>
|
||||
</a>
|
||||
</t>
|
||||
</t>
|
||||
</td>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Add Load in Cart button to sidebar -->
|
||||
<template id="portal_order_page_sidebar_button" inherit_id="sale.sale_order_portal_template" name="Order Page - Add Load in Cart Button">
|
||||
<xpath expr="//div[@id='sale_order_sidebar_button']" position="inside">
|
||||
<t t-if="sale_order.group_order_id">
|
||||
<a t-attf-href="/eskaera/#{sale_order.group_order_id.id}/load-from-history/#{sale_order.id}"
|
||||
class="btn btn-primary" role="button">
|
||||
<i class="fa fa-cart-arrow-down me-1" aria-hidden="true"></i>
|
||||
<span>Load in Cart</span>
|
||||
</a>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Custom portal content template with group order info -->
|
||||
<template id="sale_order_portal_content_aplicoop" inherit_id="sale.sale_order_portal_content" name="Sale Order Portal Content - Aplicoop">
|
||||
<!-- Insert group order info BEFORE the products table -->
|
||||
<xpath expr="//table[@id='sales_order_table']" position="before">
|
||||
<t t-if="sale_order.group_order_id">
|
||||
<div class="row mb-4">
|
||||
<!-- Group Order Info -->
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header bg-light">
|
||||
<strong><t t-esc="sale_order.group_order_id.name"/></strong>
|
||||
<t t-if="sale_order.home_delivery">
|
||||
<span class="badge bg-primary">
|
||||
<t t-set="day_idx" t-value="(int(sale_order.group_order_id.pickup_day) + 1) % 7 if sale_order.group_order_id.pickup_day else 0"/>
|
||||
<t t-esc="day_names[day_idx] if day_names else ''"/>,
|
||||
<t t-esc="sale_order.group_order_id.delivery_date.strftime('%d/%m/%Y') if sale_order.group_order_id.delivery_date else ''"/>
|
||||
</span>
|
||||
<span class="mt-2 text-muted small">
|
||||
<t t-esc="sale_order.group_order_id.delivery_notice"/>
|
||||
</span>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span class="badge bg-primary">
|
||||
<t t-set="day_idx" t-value="int(sale_order.group_order_id.pickup_day) % 7 if sale_order.group_order_id.pickup_day else 0"/>
|
||||
<t t-esc="day_names[day_idx] if day_names else ''"/>,
|
||||
<t t-esc="sale_order.pickup_date.strftime('%d/%m/%Y')"/>
|
||||
</span>
|
||||
<span class="mt-2 small">
|
||||
<t t-foreach="sale_order.group_order_id.group_ids" t-as="group">
|
||||
<t t-esc="group.name"/>
|
||||
<t t-if="group.street">
|
||||
<t t-esc="group.street"/>
|
||||
</t>
|
||||
<t t-if="group.city">
|
||||
<t t-esc="group.zip"/> <t t-esc="group.city"/>
|
||||
</t>
|
||||
</t>
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
18
website_sale_aplicoop/views/product_template_views.xml
Normal file
18
website_sale_aplicoop/views/product_template_views.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- Extension to product view to show group orders -->
|
||||
<record id="product_group_orders_view" model="ir.ui.view">
|
||||
<field name="name">product.template.form.group.orders</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='categ_id']" position="after">
|
||||
<field name="group_order_ids" widget="many2many_tags" readonly="1" help="Group orders where this product is available"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
43
website_sale_aplicoop/views/res_partner_views.xml
Normal file
43
website_sale_aplicoop/views/res_partner_views.xml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
|
||||
<!-- Extend res.partner tree view to show is_group and group_order_ids -->
|
||||
<record id="view_res_partner_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">res.partner.tree.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//list" position="inside">
|
||||
<field name="member_ids" optional="hide"/>
|
||||
<field name="is_group" optional="hide"/>
|
||||
<field name="group_order_ids" optional="hide"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Extend res.partner form view to show group_ids -->
|
||||
<record id="view_res_partner_form_inherit" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- Add group_ids page -->
|
||||
<xpath expr="//notebook/page[@name='internal_notes']" position="before">
|
||||
<page name="group_orders" string="Group Orders">
|
||||
<group>
|
||||
<group name="group_membership">
|
||||
<field name="is_group" colspan="2"/>
|
||||
<field name="group_order_ids" widget="many2many_tags" colspan="2" help="Consumer Group orders this group manages"/>
|
||||
</group>
|
||||
<group name="members">
|
||||
<field name="member_ids" widget="many2many_tags" colspan="2" help="Consumer Groups this partner belongs to"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
22
website_sale_aplicoop/views/sale_order_views.xml
Normal file
22
website_sale_aplicoop/views/sale_order_views.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- Sale Order Form Extension (Backend) -->
|
||||
<record id="sale_order_form_view_extension" model="ir.ui.view">
|
||||
<field name="name">sale.order.form.extension</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form" />
|
||||
<field name="arch" type="xml">
|
||||
<!-- Add group purchase fields in the general information section -->
|
||||
<xpath expr="//field[@name='note']" position="before">
|
||||
<group string="Group Purchase Information" groups="base.group_user">
|
||||
<field name="group_order_id" readonly="True" />
|
||||
<field name="pickup_day" readonly="True" />
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
802
website_sale_aplicoop/views/website_templates.xml
Normal file
802
website_sale_aplicoop/views/website_templates.xml
Normal file
|
|
@ -0,0 +1,802 @@
|
|||
<?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"></i>
|
||||
<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"></i>
|
||||
<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">
|
||||
<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"></i>
|
||||
</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-set="price_info" t-value="product_price_info.get(product.id, {})"/>
|
||||
<t t-set="display_price" t-value="price_info.get('price', product.list_price)"/>
|
||||
<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>
|
||||
<form class="add-to-cart-form" t-attf-data-order-id="{{ group_order.id }}" 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="{{ product.uom_id.category_id.name }}">
|
||||
<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"></i>
|
||||
</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"></i>
|
||||
</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"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</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"></i>
|
||||
</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"></i>
|
||||
</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"></i>
|
||||
</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"></script>
|
||||
<!-- Keep legacy helpers for backwards compatibility -->
|
||||
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/i18n_helpers.js"></script>
|
||||
<!-- Main shop functionality (depends on i18nManager) -->
|
||||
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/website_sale.js"></script>
|
||||
<!-- UI enhancements -->
|
||||
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/checkout_labels.js"></script>
|
||||
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/home_delivery.js"></script>
|
||||
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/realtime_search.js"></script>
|
||||
|
||||
<!-- 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"></i>
|
||||
<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"></i>
|
||||
<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"></i>
|
||||
<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"></i>
|
||||
<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"></i>
|
||||
<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"></button>
|
||||
</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"></i>
|
||||
<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"></i>
|
||||
<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"></script>
|
||||
<!-- Keep legacy helpers for backwards compatibility -->
|
||||
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/i18n_helpers.js"></script>
|
||||
<!-- Main shop functionality (depends on i18nManager) -->
|
||||
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/website_sale.js"></script>
|
||||
<!-- UI enhancements -->
|
||||
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/checkout_labels.js"></script>
|
||||
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/home_delivery.js"></script>
|
||||
<script type="text/javascript" src="/website_sale_aplicoop/static/src/js/checkout_summary.js"></script>
|
||||
<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>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
Loading…
Add table
Add a link
Reference in a new issue