implement unified checkout address layout with delivery/pickup selection and custom order total display

This commit is contained in:
Alaguraj0361 2026-04-10 19:51:27 +05:30
parent c730b5c95d
commit c33f40a809
2 changed files with 66 additions and 13 deletions

View File

@ -61,9 +61,9 @@ class SaleOrderOnline(models.Model):
def _is_shippable_order(self): def _is_shippable_order(self):
""" """
Treat all online orders as non-shippable for Odoo's standard validation. Treat pickup, delivery and other types as non-shippable for Odoo's standard validation.
This enables 'Billing-only' checkout even for delivery orders, This enables 'Billing-only' checkout which is more reliable for payment providers.
as we handle shipping manually via Uber fee lines. The Uber delivery line is protected by our _remove_delivery_line override.
""" """
self.ensure_one() self.ensure_one()
if self.fulfilment_type in ['pickup', 'delivery', 'dine_in', 'walk_in']: if self.fulfilment_type in ['pickup', 'delivery', 'dine_in', 'walk_in']:
@ -73,6 +73,7 @@ class SaleOrderOnline(models.Model):
def _check_carrier_quotation(self, force_carrier_id=None, **kwargs): def _check_carrier_quotation(self, force_carrier_id=None, **kwargs):
"""Allow proceeding to payment if we already have a carrier (Uber) or don't need one""" """Allow proceeding to payment if we already have a carrier (Uber) or don't need one"""
self.ensure_one() self.ensure_one()
_logger.info("Checking carrier quotation for order %s (fulfilment: %s, carrier: %s)", self.name, self.fulfilment_type, self.carrier_id.name if self.carrier_id else 'None')
if self.fulfilment_type in ['pickup', 'delivery', 'dine_in', 'walk_in']: if self.fulfilment_type in ['pickup', 'delivery', 'dine_in', 'walk_in']:
return True return True
# If we have a carrier set by our Uber integration, trust it and skip standard re-validation # If we have a carrier set by our Uber integration, trust it and skip standard re-validation
@ -80,6 +81,18 @@ class SaleOrderOnline(models.Model):
return True return True
return super()._check_carrier_quotation(force_carrier_id=force_carrier_id, **kwargs) return super()._check_carrier_quotation(force_carrier_id=force_carrier_id, **kwargs)
def _remove_delivery_line(self):
"""
Prevent Odoo from removing the delivery line if its an Uber order.
Odoo often tries to clean up delivery lines on page transitions if it
thinks the shipping method is no longer valid.
"""
self.ensure_one()
if self.carrier_id and 'Uber' in (self.carrier_id.name or ''):
_logger.info("Protecting Uber delivery line from removal on order %s", self.name)
return True
return super()._remove_delivery_line()
def _create_pos_order_for_kds(self, sale_order): def _create_pos_order_for_kds(self, sale_order):
""" """
Override from dine360_kds to also mark the POS order as an online order. Override from dine360_kds to also mark the POS order as an online order.

View File

@ -2,17 +2,53 @@
<odoo> <odoo>
<!-- 1. Global Order Total Display --> <!-- 1. Global Order Total Display -->
<template id="chennora_total_uber_display" inherit_id="website_sale.total" name="Chennora Total Uber Display"> <template id="chennora_total_uber_display" inherit_id="website_sale.total" name="Chennora Total Uber Display">
<!-- Hide the original Odoo delivery row that appears ABOVE subtotal safely -->
<xpath expr="//tr[@id='order_delivery']" position="attributes">
<attribute name="style">display: none !important;</attribute>
<attribute name="class" add="d-none" separator=" "/>
</xpath>
<!-- Use a unified filter for all delivery-related lines -->
<xpath expr="//tr[@id='order_total_untaxed']" position="before">
<t t-set="all_delivery_lines" t-value="website_sale_order.order_line.filtered(lambda l: l.is_delivery or 'Uber' in (l.product_id.name or '') or 'Delivery' in (l.product_id.name or ''))"/>
<t t-set="all_delivery_amount" t-value="sum(all_delivery_lines.mapped('price_subtotal'))"/>
</xpath>
<!-- Fix Subtotal value safely to exclude delivery fee, while keeping classes for JS -->
<xpath expr="//tr[@id='order_total_untaxed']//td[not(hasclass('text-start'))]//span" position="replace">
<span class="monetary_field" style="white-space: nowrap;"
t-esc="website_sale_order.amount_untaxed - all_delivery_amount"
t-options='{"widget": "monetary", "display_currency": website_sale_order.currency_id}'/>
</xpath>
<xpath expr="//tr[@id='order_total_untaxed']" position="after"> <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="all_delivery_lines" id="uber_delivery_row">
<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">Delivery Fee</td>
<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"> <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"/> <span t-esc="all_delivery_amount" t-options='{"widget": "monetary", "display_currency": website_sale_order.currency_id}' class="monetary_field"/>
</td> </td>
</tr> </tr>
</xpath> </xpath>
</template> </template>
<!-- Force enable Pay Now button on payment page if it's disabled by Odoo's shipping logic -->
<template id="chennora_force_pay_button" inherit_id="website_sale.payment" name="Chennora Force Pay Button">
<xpath expr="//div[@id='payment_method']" position="inside">
<script type="text/javascript">
(function() {
function enablePayButton() {
const payBtn = document.querySelector('#o_payment_form_button, .btn-primary[type="submit"]');
if (payBtn &amp;&amp; payBtn.disabled) {
console.log("Chennora: Force enabling payment button");
payBtn.disabled = false;
}
}
setInterval(enablePayButton, 1000); // Check every second as Odoo JS might toggle it
})();
</script>
</xpath>
</template>
<!-- 2. Hide Uber fee from cart summary --> <!-- 2. Hide Uber fee from cart summary -->
<template id="chennora_cart_summary_uber_hide" inherit_id="website_sale.checkout_layout" name="Chennora Cart Summary Uber Hide"> <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"> <xpath expr="//table[@id='cart_products']//tr" position="attributes">
@ -153,7 +189,7 @@
}); });
// 3. Custom Validation on Submit // 3. Custom Validation on Submit
if (submitBtn &amp;&amp; document.querySelector('form.checkout_autoformat')) { if (submitBtn) {
submitBtn.addEventListener('click', function(e) { submitBtn.addEventListener('click', function(e) {
const form = document.querySelector('form.checkout_autoformat'); const form = document.querySelector('form.checkout_autoformat');
if (!form) return; if (!form) return;
@ -239,8 +275,6 @@
containers.forEach(c => { containers.forEach(c => {
c.querySelectorAll('input, select').forEach(i => i.removeAttribute('required')); c.querySelectorAll('input, select').forEach(i => i.removeAttribute('required'));
}); });
// Safety: Always enable button for pickup
if (submitBtn) submitBtn.disabled = false;
} else { } else {
containers.forEach(c => c.style.display = ''); containers.forEach(c => c.style.display = '');
if (addrHeader) addrHeader.style.display = ''; if (addrHeader) addrHeader.style.display = '';
@ -252,6 +286,12 @@
}); });
checkUber(); checkUber();
} }
// SYNC with server immediately
fetch('/shop/update_service_mode', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ params: { service_mode: type } })
});
}; };
function checkUber() { function checkUber() {
@ -297,7 +337,7 @@
Uber Delivery Fee: $${data.result.fee} (Distance Based) Uber Delivery Fee: $${data.result.fee} (Distance Based)
</div> </div>
</div>`; </div>`;
if (submitBtn &amp;&amp; document.querySelector('input[name="street"]')) submitBtn.disabled = false; if (submitBtn) submitBtn.disabled = false;
} else { } else {
msgDiv.className = 'alert alert-danger my-3 animated fadeIn'; msgDiv.className = 'alert alert-danger my-3 animated fadeIn';
msgDiv.style.display = ''; msgDiv.style.display = '';
@ -308,7 +348,7 @@
${data.result?.error || "This specific address is outside the Uber delivery radius."} ${data.result?.error || "This specific address is outside the Uber delivery radius."}
</div> </div>
</div>`; </div>`;
if (submitBtn &amp;&amp; document.querySelector('input[name="street"]')) submitBtn.disabled = true; if (submitBtn) submitBtn.disabled = true;
} }
}).catch(err => { }).catch(err => {
console.error("Uber API Error:", err); console.error("Uber API Error:", err);
@ -411,7 +451,7 @@
} else { } else {
msgBox.className = 'alert alert-danger my-3'; msgBox.className = 'alert alert-danger my-3';
msgBox.innerHTML = `<strong>✕ Uber Direct: Invalid Operation</strong><br/>${data.result?.error || "Outside delivery radius."}`; msgBox.innerHTML = `<strong>✕ Uber Direct: Invalid Operation</strong><br/>${data.result?.error || "Outside delivery radius."}`;
if (billingGrid) document.querySelector('button[type="submit"]')?.setAttribute('disabled', 'disabled'); document.querySelector('button[type="submit"]')?.setAttribute('disabled', 'disabled');
} }
}); });
} }