diff --git a/addons/dine360_theme_chennora/views/checkout_address.xml b/addons/dine360_theme_chennora/views/checkout_address.xml index 6dd7abc..b365264 100644 --- a/addons/dine360_theme_chennora/views/checkout_address.xml +++ b/addons/dine360_theme_chennora/views/checkout_address.xml @@ -385,9 +385,10 @@ const addressText = selectedCard.querySelector('address')?.innerText || ""; const parts = addressText.split('\n').map(p => p.trim()); - // Crude parsing const street = parts[1] || ""; - const cityZip = parts[2] || ""; + const zipMatch = addressText.match(/[A-Z][0-9][A-Z]\s?[0-9][A-Z][0-9]/); + const zip = zipMatch ? zipMatch[0] : ""; + const city = parts[2] ? parts[2].split(' ')[0] : ""; msgBox.className = 'alert alert-info my-3'; msgBox.innerHTML = "Verifying Uber coverage for this address..."; @@ -396,14 +397,15 @@ method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ params: { address_data: { street: street, - zip: cityZip.split(' ').pop(), - city: cityZip.split(' ')[0], + zip: zip, + city: city, } } }) }).then(r => r.json()).then(data => { if (data.result && data.result.success) { msgBox.className = 'alert alert-success my-3'; msgBox.innerHTML = `✓ Uber Delivery Available! Fee: $${data.result.fee}`; document.querySelector('button[type="submit"]')?.removeAttribute('disabled'); + setTimeout(() => { window.location.reload(); }, 1200); } else { msgBox.className = 'alert alert-danger my-3'; msgBox.innerHTML = `✕ Uber Direct: Invalid Operation${data.result?.error || "Outside delivery radius."}`; @@ -413,6 +415,12 @@ } document.querySelectorAll('input[name="partner_id"]').forEach(i => i.addEventListener('change', verifySelectedAddress)); + + // NEW: Ultimate trigger for every touch on an address box + $(document).on('click', '#address_selection .card, #address_selection label', function() { + setTimeout(verifySelectedAddress, 50); // Small delay to let radio button update + }); + verifySelectedAddress(); } } diff --git a/addons/dine360_uber/__manifest__.py b/addons/dine360_uber/__manifest__.py index 35f88da..d49755a 100644 --- a/addons/dine360_uber/__manifest__.py +++ b/addons/dine360_uber/__manifest__.py @@ -22,6 +22,9 @@ 'dine360_uber/static/src/js/uber_pos.js', 'dine360_uber/static/src/xml/uber_pos.xml', ], + 'web.assets_backend': [ + 'dine360_uber/static/src/js/uber_backend.js', + ], }, 'installable': True, 'application': True, diff --git a/addons/dine360_uber/models/pos_order.py b/addons/dine360_uber/models/pos_order.py index 69b6a64..c0386f4 100644 --- a/addons/dine360_uber/models/pos_order.py +++ b/addons/dine360_uber/models/pos_order.py @@ -1,6 +1,9 @@ from odoo import models, fields, api, _ from odoo.exceptions import UserError import datetime +import logging + +_logger = logging.getLogger(__name__) class PosOrder(models.Model): _inherit = 'pos.order' @@ -158,6 +161,8 @@ class PosOrder(models.Model): order._add_uber_delivery_fee(delivery_fee) except requests.exceptions.HTTPError as e: + # Log the raw response so we can see which parameter is invalid + _logger.error("Uber Direct Raw Error Response (%s): %s", e.response.status_code, e.response.text) # Try to parse the error message if it's JSON try: err_data = e.response.json() @@ -207,30 +212,79 @@ class PosOrder(models.Model): }) # order.message_post(body="Uber Direct delivery request cancelled.") + def action_sync_uber_status(self): + """Fetch latest status from Uber API and update POS order""" + import requests + config = self.env['uber.config'].search([('active', '=', True)], limit=1) + if not config or not config.customer_id: + return + + access_token = config._get_access_token() + headers = {'Authorization': f'Bearer {access_token}'} + + for order in self: + if not order.uber_delivery_id: + continue + + try: + api_url = f"https://api.uber.com/v1/customers/{config.customer_id}/deliveries/{order.uber_delivery_id}" + response = requests.get(api_url, headers=headers) + if response.status_code == 200: + data = response.json() + _logger.info("Uber Status Raw Data for %s: %s", order.name, data) + status_map = { + 'pending': 'pending', + 'pickup': 'pickup', + 'pickup_complete': 'delivering', + 'delivery_complete': 'delivered', + 'delivered': 'delivered', + 'cancelled': 'cancelled' + } + new_status = status_map.get(data.get('status'), order.uber_status) + + vals = {'uber_status': new_status} + # If status progressed beyond pending, HIDE the alert + if new_status != 'pending': + vals['uber_alert_triggered'] = False + + if new_status != order.uber_status: + # Send signal to UI to refresh the order form + self.env['bus.bus']._sendone('uber_status_updates', 'status_changed', { + 'order_id': order.id, + 'new_status': new_status + }) + + order.write(vals) + _logger.info("Uber Status Synced for %s: %s", order.name, new_status) + except Exception as e: + _logger.error("Failed to sync Uber status for %s: %s", order.name, str(e)) + @api.model def cron_check_uber_driver_assignment(self): - """Auto-alert if driver not assigned in X minutes""" + """Auto-alert and status update cron""" config = self.env['uber.config'].search([('active', '=', True)], limit=1) - if not config or config.timeout_minutes <= 0: + if not config: return - timeout_threshold = fields.Datetime.now() - datetime.timedelta(minutes=config.timeout_minutes) - pending_orders = self.search([ - ('uber_status', '=', 'pending'), - ('uber_request_time', '<=', timeout_threshold), - ('uber_alert_triggered', '=', False) - ]) + # 1. Sync status for all active orders + active_orders = self.search([('uber_status', 'in', ['pending', 'pickup', 'delivering'])]) + active_orders.action_sync_uber_status() - for order in pending_orders: - # Send notification to POS Users/Managers - order.uber_alert_triggered = True - # order.message_post(body="🚨 ALERT: No Uber driver assigned for over %s minutes! Please check Uber dashboard." % config.timeout_minutes) - - # Broadcaster for UI Alert - self.env['bus.bus']._sendone('pos_alerts', 'uber_timeout', { - 'order_name': order.name, - 'minutes': config.timeout_minutes - }) + # 2. Trigger alerts for those still stuck in pending + if config.timeout_minutes > 0: + timeout_threshold = fields.Datetime.now() - datetime.timedelta(minutes=config.timeout_minutes) + pending_orders = self.search([ + ('uber_status', '=', 'pending'), + ('uber_request_time', '<=', timeout_threshold), + ('uber_alert_triggered', '=', False) + ]) + + for order in pending_orders: + order.uber_alert_triggered = True + self.env['bus.bus']._sendone('pos_alerts', 'uber_timeout', { + 'order_name': order.name, + 'minutes': config.timeout_minutes + }) def action_view_uber_map(self): """Open Uber Live Tracking Link""" diff --git a/addons/dine360_uber/models/sale_order.py b/addons/dine360_uber/models/sale_order.py index e48fd75..158bc77 100644 --- a/addons/dine360_uber/models/sale_order.py +++ b/addons/dine360_uber/models/sale_order.py @@ -23,15 +23,25 @@ class SaleOrder(models.Model): Carrier = self.env['delivery.carrier'].sudo() config = self.env['uber.config'].sudo().search([('active', '=', True)], limit=1) - carrier = Carrier.search([('name', 'ilike', 'Uber')], limit=1) - if not carrier and config and config.delivery_product_id: - _logger.info("Uber: Creating new Uber Delivery carrier") + # Search for any carrier linked to the Uber product or with Uber in the name + carrier = Carrier.search(['|', ('name', 'ilike', 'Uber'), ('product_id', '=', config.delivery_product_id.id if config.delivery_product_id else 0)], limit=1) + + if not carrier and config: + # Fallback product if one isn't set in config + product = config.delivery_product_id + if not product: + product = self.env['product.product'].sudo().search([('name', 'ilike', 'Delivery')], limit=1) + if not product: + product = self.env['product.product'].sudo().search([], limit=1) # Last resort + + _logger.info("Uber: Creating new Uber Delivery carrier using product %s", product.name) carrier = Carrier.create({ - 'name': 'Uber Delivery', + 'name': 'Uber Direct Delivery', 'delivery_type': 'fixed', - 'product_id': config.delivery_product_id.id, + 'product_id': product.id, 'website_published': True, 'fixed_price': 0.0, + 'active': True, }) if carrier: diff --git a/addons/dine360_uber/static/src/js/uber_backend.js b/addons/dine360_uber/static/src/js/uber_backend.js new file mode 100644 index 0000000..87edd2a --- /dev/null +++ b/addons/dine360_uber/static/src/js/uber_backend.js @@ -0,0 +1,27 @@ +/** @odoo-module **/ + +import { registry } from "@web/core/registry"; + +const UberStatusService = { + dependencies: ["bus_service", "action"], + start(env, { bus_service, action }) { + // Odoo 17 Bus Service uses addChannel and subscribe + bus_service.addChannel("uber_status_updates"); + bus_service.subscribe("notification", (notifications) => { + for (const { type, payload } of notifications) { + if (type === "uber_status_updates") { + const currentController = action.currentController; + if (currentController && + currentController.props.resModel === "pos.order" && + currentController.props.resId === payload.order_id) { + + console.log("Uber Status Update Received. Refreshing Form..."); + action.restore(); + } + } + } + }); + }, +}; + +registry.category("services").add("uber_status_service", UberStatusService); diff --git a/addons/dine360_uber/views/pos_order_views.xml b/addons/dine360_uber/views/pos_order_views.xml index 0a90855..26fba37 100644 --- a/addons/dine360_uber/views/pos_order_views.xml +++ b/addons/dine360_uber/views/pos_order_views.xml @@ -18,14 +18,28 @@ + + + + THE UBER DRIVER HAS SUCCESSFULLY DELIVERED THIS ORDER + + + + + + @@ -33,7 +47,7 @@ - + 🚨 Attention! Driver not assigned for over 15 minutes. Please contact Uber support.