forked from alaguraj/odoo-testing-addons
integrate Uber delivery fee calculation and checkout validation, and update Docker container naming conventions
This commit is contained in:
parent
67995fa0c0
commit
d121f6f492
@ -1,486 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Custom Address Form - Remove Company/VAT and Rename Zip to Postal Code -->
|
||||
<!-- Increased priority to 99 to ensure it overrides other theme or base overrides -->
|
||||
<template id="chennora_address_custom" inherit_id="website_sale.address" name="Chennora Address Custom" priority="99">
|
||||
|
||||
<!-- 1. Remove Company Name Field -->
|
||||
<xpath expr="//div[label[@for='company_name']]" position="replace"/>
|
||||
|
||||
<!-- 2. Remove VAT Field -->
|
||||
<xpath expr="//div[label[@for='vat']]" position="replace"/>
|
||||
|
||||
<!-- 3. Remove VAT Warning Message if it exists (Optional/Resilient) -->
|
||||
<!-- <xpath expr="//small[contains(text(), 'VAT number is not allowed')]/.." position="replace"/> -->
|
||||
|
||||
<!-- 4. Rename ALL Zip Code Labels to Postal Code (Handles both layout versions) -->
|
||||
<xpath expr="(//label[@for='zip'])[1]" position="replace">
|
||||
<label class="col-form-label label-optional" for="zip">Postal Code</label>
|
||||
</xpath>
|
||||
<xpath expr="(//label[@for='zip'])[2]" position="replace">
|
||||
<label class="col-form-label label-optional" for="zip">Postal Code</label>
|
||||
</xpath>
|
||||
|
||||
<!-- 6. Update Placeholder for Zip Input -->
|
||||
<xpath expr="//input[@name='zip']" position="attributes">
|
||||
<attribute name="placeholder">Postal Code</attribute>
|
||||
</xpath>
|
||||
|
||||
|
||||
|
||||
<!-- 8. Change "Shipping address" / "Billing address" title to just "Address Details" -->
|
||||
<xpath expr="//h3[contains(., 'address')]" position="replace">
|
||||
<h3 class="mb-3">Address Details</h3>
|
||||
</xpath>
|
||||
|
||||
<!-- Pickup / Delivery Order Type Toggle -->
|
||||
<xpath expr="//form[contains(@class, 'checkout_autoformat')]" position="before">
|
||||
<div class="checkout-order-type-selector mb-5 mt-4">
|
||||
<style>
|
||||
.order-type-card {
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
background: #fff;
|
||||
}
|
||||
.order-type-card:hover {
|
||||
border-color: #FECD4F;
|
||||
background: #fffbf0;
|
||||
}
|
||||
.order-type-input:checked + .order-type-card {
|
||||
border-color: #2BB1A5;
|
||||
background: #f0fdfc;
|
||||
box-shadow: 0 4px 15px rgba(43, 177, 165, 0.15);
|
||||
}
|
||||
.order-type-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8f9fa;
|
||||
border-radius: 50%;
|
||||
font-size: 20px;
|
||||
color: #04121D;
|
||||
}
|
||||
.order-type-input:checked + .order-type-card .order-type-icon {
|
||||
background: #2BB1A5;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Aggressive hiding for pickup fields to prevent empty space */
|
||||
form.is-pickup-mode .div_street,
|
||||
form.is-pickup-mode .div_street2,
|
||||
form.is-pickup-mode .div_city,
|
||||
form.is-pickup-mode .div_zip,
|
||||
form.is-pickup-mode .div_country,
|
||||
form.is-pickup-mode .div_state,
|
||||
form.is-pickup-mode div:has(> label[for="street"]),
|
||||
form.is-pickup-mode div:has(> label[for="street2"]),
|
||||
form.is-pickup-mode div:has(> label[for="city"]),
|
||||
form.is-pickup-mode div:has(> label[for="zip"]),
|
||||
form.is-pickup-mode div:has(> label[for="country_id"]),
|
||||
form.is-pickup-mode div:has(> label[for="state_id"]) {
|
||||
display: none !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
height: 0 !important;
|
||||
opacity: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.uber-status-badge {
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
margin-top: 10px;
|
||||
display: none;
|
||||
}
|
||||
.uber-status-available {
|
||||
background: #d1f7ec;
|
||||
color: #0e6245;
|
||||
border: 1px solid #a2eed9;
|
||||
}
|
||||
.uber-status-unavailable {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
</style>
|
||||
<div class="row align-items-center justify-content-center">
|
||||
<div class="col-md-6 mb-3 mb-md-0" t-if="website.enable_delivery_option">
|
||||
<input type="radio" name="delivery_method_switch" id="method_delivery" class="d-none order-type-input" value="delivery" t-att-checked="'checked' if website.enable_delivery_option else False"/>
|
||||
<label for="method_delivery" class="order-type-card w-100 m-0">
|
||||
<div class="order-type-icon">
|
||||
<i class="fa fa-truck"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-1 fw-bold" style="color: #04121D;">Delivery</h5>
|
||||
<small class="text-muted">Deliver to my address</small>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div t-attf-class="{{'col-md-6' if website.enable_delivery_option else 'col-md-12'}}">
|
||||
<input type="radio" name="delivery_method_switch" id="method_pickup" class="d-none order-type-input" value="pickup" t-att-checked="'checked' if not website.enable_delivery_option else False"/>
|
||||
<label for="method_pickup" class="order-type-card w-100 m-0">
|
||||
<div class="order-type-icon">
|
||||
<i class="fa fa-shopping-bag"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-1 fw-bold" style="color: #04121D;">Store Pickup</h5>
|
||||
<small class="text-muted">Pick up at the restaurant</small>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Uber Coverage Status Badge -->
|
||||
<div id="uber_status_badge" class="uber-status-badge">
|
||||
<div class="d-flex align-items-center">
|
||||
<div id="uber_loader" class="spinner-border spinner-border-sm me-2" role="status" style="display: none;"></div>
|
||||
<i id="uber_icon" class="fa fa-info-circle me-2"></i>
|
||||
<span id="uber_msg">Checking delivery availability...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const typeDelivery = document.getElementById('method_delivery');
|
||||
const typePickup = document.getElementById('method_pickup');
|
||||
const streetInput = document.querySelector('input[name="street"]');
|
||||
const cityInput = document.querySelector('input[name="city"]');
|
||||
const zipInput = document.querySelector('input[name="zip"]');
|
||||
const countrySelect = document.querySelector('select[name="country_id"]');
|
||||
const stateSelect = document.querySelector('select[name="state_id"]');
|
||||
const formElement = document.querySelector('form.checkout_autoformat');
|
||||
const uberBadge = document.getElementById('uber_status_badge');
|
||||
const uberMsg = document.getElementById('uber_msg');
|
||||
const uberIcon = document.getElementById('uber_icon');
|
||||
const uberLoader = document.getElementById('uber_loader');
|
||||
|
||||
// The checkout submit button
|
||||
const submitBtn = document.querySelector('.a-submit');
|
||||
|
||||
const allRequiredInputs = [streetInput, cityInput, zipInput, countrySelect, stateSelect];
|
||||
|
||||
let quoteTimer;
|
||||
|
||||
const setSubmitDisabled = (disabled) => {
|
||||
if (!submitBtn) return;
|
||||
if (disabled) {
|
||||
submitBtn.classList.add('disabled');
|
||||
submitBtn.style.pointerEvents = 'none';
|
||||
submitBtn.style.opacity = '0.5';
|
||||
} else {
|
||||
submitBtn.classList.remove('disabled');
|
||||
submitBtn.style.pointerEvents = 'auto';
|
||||
submitBtn.style.opacity = '1';
|
||||
}
|
||||
};
|
||||
|
||||
function runUberQuote() {
|
||||
if (!typeDelivery || !typeDelivery.checked) {
|
||||
setSubmitDisabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start as disabled for delivery until we get a successful quote
|
||||
setSubmitDisabled(true);
|
||||
|
||||
if (!streetInput || !zipInput || !cityInput) return;
|
||||
if (!streetInput.value || !zipInput.value || !cityInput.value) {
|
||||
uberBadge.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
uberBadge.style.display = 'block';
|
||||
uberBadge.className = 'uber-status-badge alert alert-info';
|
||||
uberLoader.style.display = 'inline-block';
|
||||
uberIcon.style.display = 'none';
|
||||
uberMsg.innerText = 'Calculating delivery fee...';
|
||||
|
||||
const addressData = {
|
||||
street: streetInput.value,
|
||||
city: cityInput.value,
|
||||
zip: zipInput.value,
|
||||
country: countrySelect && countrySelect.selectedIndex >= 0 ? countrySelect.options[countrySelect.selectedIndex].text : ''
|
||||
};
|
||||
|
||||
fetch('/shop/uber/quote', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ params: { address_data: addressData } }),
|
||||
// Short timeout for better UX
|
||||
signal: AbortSignal.timeout(10000)
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
uberLoader.style.display = 'none';
|
||||
uberIcon.style.display = 'inline-block';
|
||||
if (data.result && data.result.success) {
|
||||
uberBadge.className = 'uber-status-badge uber-status-available';
|
||||
uberIcon.className = 'fa fa-check-circle me-2';
|
||||
uberMsg.innerText = 'Delivery available! Fee: $' + data.result.fee.toFixed(2);
|
||||
|
||||
// ENABLE ONLY ON SUCCESS
|
||||
setSubmitDisabled(false);
|
||||
} else {
|
||||
const errorMsg = (data.result && data.result.error) ? data.result.error : 'Location out of delivery range or Uber address error.';
|
||||
uberBadge.className = 'uber-status-badge uber-status-unavailable';
|
||||
uberIcon.className = 'fa fa-times-circle me-2';
|
||||
uberMsg.innerText = 'Sorry: ' + errorMsg;
|
||||
|
||||
// STAY DISABLED ON ERROR
|
||||
setSubmitDisabled(true);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Uber Fetch Error:", err);
|
||||
uberLoader.style.display = 'none';
|
||||
uberBadge.className = 'uber-status-badge uber-status-unavailable';
|
||||
uberIcon.className = 'fa fa-exclamation-triangle me-2';
|
||||
uberMsg.innerText = 'Unable to verify delivery address.';
|
||||
|
||||
// STAY DISABLED ON NETWORK ERROR
|
||||
setSubmitDisabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
function debounceQuote() {
|
||||
clearTimeout(quoteTimer);
|
||||
quoteTimer = setTimeout(runUberQuote, 1000);
|
||||
}
|
||||
|
||||
function applyMethod() {
|
||||
if(typePickup && typePickup.checked) {
|
||||
if(formElement) formElement.classList.add('is-pickup-mode');
|
||||
uberBadge.style.display = 'none';
|
||||
|
||||
// Remove "required" so HTML5 validation doesn't block submission
|
||||
allRequiredInputs.forEach(input => {
|
||||
if (input) {
|
||||
if (input.hasAttribute('required') || input.dataset.wasRequired) {
|
||||
input.dataset.wasRequired = 'true';
|
||||
input.removeAttribute('required');
|
||||
input.classList.remove('s_website_form_required');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add dummy values exactly when they are blank
|
||||
if(streetInput && !streetInput.value) { streetInput.value = 'In-Store Pickup'; }
|
||||
if(cityInput && !cityInput.value) { cityInput.value = 'Brampton'; }
|
||||
if(zipInput && (!zipInput.value || zipInput.value === '')) { zipInput.value = 'L6Y0N1'; }
|
||||
|
||||
// For country, just select Canada or first available if blank
|
||||
if(countrySelect && (!countrySelect.value || countrySelect.value == "")) {
|
||||
let caOption = Array.from(countrySelect.options).find(o => o.text.includes('Canada'));
|
||||
if (caOption) {
|
||||
countrySelect.value = caOption.value;
|
||||
} else if (countrySelect.options.length > 1) {
|
||||
countrySelect.selectedIndex = 1;
|
||||
}
|
||||
countrySelect.dispatchEvent(new Event('change'));
|
||||
}
|
||||
|
||||
// ENABLE ALWAYS FOR PICKUP
|
||||
setSubmitDisabled(false);
|
||||
} else {
|
||||
if(formElement) formElement.classList.remove('is-pickup-mode');
|
||||
|
||||
// Restore required state
|
||||
allRequiredInputs.forEach(input => {
|
||||
if (input && input.dataset.wasRequired === 'true') {
|
||||
input.setAttribute('required', 'required');
|
||||
input.classList.add('s_website_form_required');
|
||||
}
|
||||
});
|
||||
|
||||
// Revert dummy values so user can type real ones
|
||||
if(streetInput && streetInput.value === 'In-Store Pickup') { streetInput.value = ''; }
|
||||
if(cityInput && cityInput.value === 'Brampton') { cityInput.value = ''; }
|
||||
if(zipInput && zipInput.value === 'L6Y0N1') { zipInput.value = ''; }
|
||||
|
||||
// DISABLE UNTIL QUOTE SUCCESSFUL
|
||||
setSubmitDisabled(true);
|
||||
debounceQuote();
|
||||
}
|
||||
}
|
||||
|
||||
if (typePickup) {
|
||||
if (typeDelivery) typeDelivery.addEventListener('change', applyMethod);
|
||||
typePickup.addEventListener('change', applyMethod);
|
||||
|
||||
[streetInput, cityInput, zipInput].forEach(el => {
|
||||
if (el) el.addEventListener('input', debounceQuote);
|
||||
if (el) el.addEventListener('change', runUberQuote);
|
||||
});
|
||||
|
||||
// Remember state if validation fails
|
||||
const savedMethod = sessionStorage.getItem('chennora_checkout_method');
|
||||
if (savedMethod === 'pickup') {
|
||||
typePickup.checked = true;
|
||||
} else if (!typeDelivery) {
|
||||
typePickup.checked = true;
|
||||
}
|
||||
|
||||
applyMethod();
|
||||
|
||||
typePickup.addEventListener('change', () => sessionStorage.setItem('chennora_checkout_method', 'pickup'));
|
||||
if (typeDelivery) typeDelivery.addEventListener('change', () => sessionStorage.setItem('chennora_checkout_method', 'delivery'));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</xpath>
|
||||
|
||||
|
||||
|
||||
<!-- 9. Remove the "Be aware!" warning box completely -->
|
||||
<xpath expr="//div[@role='alert' and .//h4[contains(text(), 'Be aware')]]" position="replace"/>
|
||||
</template>
|
||||
|
||||
<!-- Change Billing & Shipping Label on Payment Page -->
|
||||
<template id="chennora_address_on_payment_custom" inherit_id="website_sale.address_on_payment" name="Chennora Address Label Custom" priority="99">
|
||||
<!-- Customize address display for Pickup orders -->
|
||||
<xpath expr="//div[@id='shipping_and_billing']" position="replace">
|
||||
<div id="shipping_and_billing" class="mt-4">
|
||||
<div>
|
||||
<b t-if="not order.partner_id.street == 'In-Store Pickup'">Billing: </b>
|
||||
<b t-else="">Customer: </b>
|
||||
|
||||
<t t-if="order.partner_id.street == 'In-Store Pickup'">
|
||||
<span t-out="order.partner_id" t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True}'/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span t-out="order.partner_id" t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True}'/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Custom Checkout Page - Remove Shipping Section entirely -->
|
||||
<template id="chennora_checkout_custom" inherit_id="website_sale.checkout" name="Chennora Checkout Custom" priority="99">
|
||||
<!-- 1. Hide the "Billing" sub-header instead of removing (prevents JS errors) -->
|
||||
<xpath expr="//h4[contains(., 'Billing')]" position="attributes">
|
||||
<attribute name="class" add="d-none" separator=" "/>
|
||||
</xpath>
|
||||
|
||||
<!-- 2. Rename the "Shipping Address" header if it exists -->
|
||||
<xpath expr="//h4[contains(., 'Shipping')]" position="replace">
|
||||
<h4 class="mb-0">Address Details</h4>
|
||||
</xpath>
|
||||
|
||||
<!-- 3. Hide the "Shipping" section block instead of removing -->
|
||||
<xpath expr="//t[@groups='account.group_delivery_invoice_address']" position="attributes">
|
||||
<attribute name="class" add="d-none" separator=" "/>
|
||||
</xpath>
|
||||
|
||||
<!-- 4. UBER QUOTE RELOAD SCRIPT (Ensures fee is applied on page load) -->
|
||||
<xpath expr="//h3" position="after">
|
||||
<div id="uber_error" class="alert alert-danger d-none my-3" role="alert">
|
||||
<!-- Error message will be injected here -->
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Try different selectors for the main checkout button
|
||||
const btn = document.querySelector('a[name="website_sale_main_button"]') ||
|
||||
document.querySelector('.oe_cart .btn-primary') ||
|
||||
document.querySelector('.btn-primary[href*="/payment"]');
|
||||
const errorDiv = document.getElementById('uber_error');
|
||||
|
||||
// Helper to disable button
|
||||
const disableButton = () => {
|
||||
if (btn) {
|
||||
btn.classList.add('disabled', 'btn-secondary');
|
||||
btn.classList.remove('btn-primary');
|
||||
btn.style.pointerEvents = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to enable button
|
||||
const enableButton = () => {
|
||||
if (btn) {
|
||||
btn.classList.remove('disabled', 'btn-secondary');
|
||||
btn.classList.add('btn-primary');
|
||||
btn.style.pointerEvents = 'auto';
|
||||
}
|
||||
};
|
||||
|
||||
// Only run if the order is for delivery
|
||||
const isDelivery = "<t t-esc='order.fulfilment_type'/>" === "delivery";
|
||||
if (!isDelivery) {
|
||||
enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
// For delivery, disable by default until quoted
|
||||
disableButton();
|
||||
|
||||
// Address data for re-calculating quote
|
||||
const addressData = {
|
||||
street: "<t t-esc='order.partner_shipping_id.street'/>",
|
||||
city: "<t t-esc='order.partner_shipping_id.city'/>",
|
||||
zip: "<t t-esc='order.partner_shipping_id.zip'/>",
|
||||
country: "<t t-esc='order.partner_shipping_id.country_id.name'/>"
|
||||
};
|
||||
|
||||
if (addressData.street && addressData.zip) {
|
||||
fetch('/shop/uber/quote', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
params: { address_data: addressData }
|
||||
})
|
||||
}).then(r => r.json()).then(data => {
|
||||
console.log("Uber Quote Result:", data.result);
|
||||
if (data.result && data.result.success) {
|
||||
enableButton();
|
||||
if (errorDiv) errorDiv.classList.add('d-none');
|
||||
|
||||
if (data.result.fee >= 0) {
|
||||
// Refresh page once to show the fee in the sidebar if not already present
|
||||
const hasFee = <t t-esc="any(l.product_id.name == 'Uber Delivery Fee' for l in website_sale_order.order_line) and 'true' or 'false'"/>;
|
||||
if (!hasFee && window.location.search.indexOf('quoted=1') === -1) {
|
||||
window.location.href = window.location.pathname + '?quoted=1';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
disableButton();
|
||||
if (errorDiv) {
|
||||
errorDiv.innerText = (data.result && data.result.error) ? data.result.error : "Uber delivery is not available for this location.";
|
||||
errorDiv.classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error("Uber Quote Fetch Error:", err);
|
||||
disableButton();
|
||||
});
|
||||
} else {
|
||||
if (errorDiv) {
|
||||
errorDiv.innerText = "Please provide a valid street address and ZIP code for delivery.";
|
||||
errorDiv.classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- 2. CART SUMMARY OVERRIDES FOR UBER -->
|
||||
<!-- Hide Uber fee from the list of items in the sidebar summary -->
|
||||
<template id="chennora_cart_summary_uber_hide" inherit_id="website_sale.checkout_layout" name="Chennora Cart Summary Uber Hide">
|
||||
<xpath expr="//table[@id='cart_products']//tr" position="attributes">
|
||||
<attribute name="t-if">line.product_id.name != 'Uber Delivery Fee'</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Display Uber fee in the 'Delivery' row of the total summary -->
|
||||
<template id="chennora_total_uber_display" inherit_id="website_sale.total" name="Chennora Total Uber Display">
|
||||
<!-- Intercept Subtotal to subtract Uber fee if it was included -->
|
||||
@ -493,7 +12,7 @@
|
||||
<!-- Add/Update Delivery Row -->
|
||||
<xpath expr="//tr[@id='order_total_untaxed']" position="after">
|
||||
<t t-set="uber_line" t-value="website_sale_order.order_line.filtered(lambda l: l.product_id.name == 'Uber Delivery Fee')"/>
|
||||
<tr t-if="uber_line">
|
||||
<tr t-if="uber_line" id="uber_delivery_row">
|
||||
<td class="border-0 pb-2 ps-0 pt-0 text-start text-muted" colspan="2">Uber Delivery Fee</td>
|
||||
<td class="text-end border-0 pb-2 pe-0 pt-0">
|
||||
<span t-field="uber_line[0].price_subtotal" t-options='{"widget": "monetary", "display_currency": website_sale_order.currency_id}' class="monetary_field"/>
|
||||
@ -502,30 +21,99 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Hide the Uber fee from the standard product list in the sidebar -->
|
||||
<template id="chennora_cart_summary_uber_hide" inherit_id="website_sale.checkout_layout" name="Chennora Cart Summary Uber Hide">
|
||||
<xpath expr="//table[@id='cart_products']//tr" position="attributes">
|
||||
<attribute name="t-if">line.product_id.name != 'Uber Delivery Fee'</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
<!-- Custom Wizard - Rename Shipping step to Billing safely without python errors -->
|
||||
<template id="chennora_wizard_checkout_custom" inherit_id="website_sale.wizard_checkout" name="Chennora Wizard Custom" priority="99">
|
||||
<xpath expr="//*[@t-call='website.step_wizard']" position="after">
|
||||
<!-- Custom Checkout Page Logic -->
|
||||
<template id="chennora_checkout_custom" inherit_id="website_sale.checkout" name="Chennora Checkout Custom" priority="99">
|
||||
<!-- 1. Script for Uber Quote -->
|
||||
<xpath expr="//h4[1]" position="before">
|
||||
<div id="uber_error" class="alert alert-danger d-none my-3" role="alert"></div>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const steps = document.querySelectorAll('.nav-wizard .nav-item .nav-link .text-truncate, .nav-wizard .text-truncate');
|
||||
steps.forEach(node => {
|
||||
if (node.innerText.trim() === 'Shipping') {
|
||||
node.innerText = 'Billing';
|
||||
(function() {
|
||||
function initUberQuote() {
|
||||
const btn = document.querySelector('a[name="website_sale_main_button"]') ||
|
||||
document.querySelector('.oe_cart .btn-primary') ||
|
||||
document.querySelector('.btn-primary[href*="/payment"]');
|
||||
const errorDiv = document.getElementById('uber_error');
|
||||
|
||||
if (!btn) {
|
||||
console.warn("Uber: Checkout button not found, retrying...");
|
||||
setTimeout(initUberQuote, 500);
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Helper to disable button
|
||||
const disableButton = () => {
|
||||
btn.classList.add('disabled', 'btn-secondary');
|
||||
btn.classList.remove('btn-primary');
|
||||
btn.style.pointerEvents = 'none';
|
||||
};
|
||||
|
||||
// Helper to enable button
|
||||
const enableButton = () => {
|
||||
btn.classList.remove('disabled', 'btn-secondary');
|
||||
btn.classList.add('btn-primary');
|
||||
btn.style.pointerEvents = 'auto';
|
||||
};
|
||||
|
||||
const fulfilmentType = "<t t-esc='order.fulfilment_type'/>";
|
||||
if (fulfilmentType !== "delivery") {
|
||||
enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
// For delivery, disable by default
|
||||
disableButton();
|
||||
|
||||
const addressData = {
|
||||
street: "<t t-esc='order.partner_shipping_id.street'/>",
|
||||
city: "<t t-esc='order.partner_shipping_id.city'/>",
|
||||
zip: "<t t-esc='order.partner_shipping_id.zip'/>",
|
||||
country: "<t t-esc='order.partner_shipping_id.country_id.name'/>"
|
||||
};
|
||||
|
||||
if (addressData.street && addressData.zip) {
|
||||
console.log("Uber: Requesting quote for", addressData);
|
||||
fetch('/shop/uber/quote', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({ params: { address_data: addressData } })
|
||||
}).then(r => r.json()).then(data => {
|
||||
console.log("Uber: Quote result", data.result);
|
||||
if (data.result && data.result.success) {
|
||||
enableButton();
|
||||
if (errorDiv) errorDiv.classList.add('d-none');
|
||||
|
||||
// Refresh if fee not shown
|
||||
const hasFee = "<t t-esc="any(l.product_id.name == 'Uber Delivery Fee' for l in website_sale_order.order_line) and 'true' or 'false'"/>" === "true";
|
||||
if (!hasFee && window.location.search.indexOf('quoted=1') === -1) {
|
||||
window.location.href = window.location.pathname + '?quoted=1';
|
||||
}
|
||||
} else {
|
||||
disableButton();
|
||||
if (errorDiv) {
|
||||
errorDiv.innerText = (data.result && data.result.error) ? data.result.error : "Delivery not available.";
|
||||
errorDiv.classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error("Uber: Fetch error", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initUberQuote);
|
||||
} else {
|
||||
initUberQuote();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Custom Address Kanban - Hide address for Pickup in the cards -->
|
||||
<template id="chennora_address_kanban_custom" inherit_id="website_sale.address_kanban" name="Chennora Address Kanban Custom" priority="99">
|
||||
<xpath expr="//*[@t-options]" position="attributes">
|
||||
<attribute name="t-options">
|
||||
{'widget': 'contact', 'fields': contact.street == 'In-Store Pickup' and ['name', 'phone'] or ['address', 'name', 'phone'], 'no_marker': True}
|
||||
</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user