from odoo import models, fields, api, _ import logging _logger = logging.getLogger(__name__) class SaleOrderOnline(models.Model): _inherit = 'sale.order' pos_order_id = fields.Many2one( 'pos.order', string='POS Order', help='The POS order created from this website sale order' ) # order_source is now canonical field from dine360_order_channels (pos.order) # We add it to sale.order for tracking which channel the web sale originated from order_source = fields.Selection([ ('online', 'Online'), ('phone', 'Phone'), ('whatsapp', 'WhatsApp'), ('social_media', 'Social Media'), ('in_person', 'In-Person (Walk-in/Dine-in)'), ('kiosk', 'Store Self-Order (Kiosk)'), ('party_order', 'Party Order'), ('platform_integration', 'Platform Integration (3rd Party)'), ], string='Order Source', default='online', tracking=True) fulfilment_type = fields.Selection([ ('pickup', 'Pickup'), ('delivery', 'Delivery'), ('dine_in', 'Dine-In'), ('walk_in', 'Walk-In'), ], string='Fulfillment Type', default='pickup', tracking=True) payment_option = fields.Selection([ ('in_store', 'In Store'), ('terminal_in_store', 'Payment Terminal (In Store)'), ('terminal_customer', 'Payment Terminal (Customer Place)'), ('online_gateway', 'Online Payment Gateway'), ('cash', 'Cash'), ('interac', 'Interac'), ], string='Payment Option', tracking=True) # delivery_time = fields.Datetime(string='Requested Delivery Time', tracking=True) telephone_number = fields.Char('Telephone Number') reservation_source = fields.Selection([ ('online', 'Online'), ('phone', 'Phone'), ('staff', 'Staff'), ], string='Reservation Source', tracking=True) reservation_status = fields.Selection([ ('draft', 'Request Received'), ('confirmed', 'Confirmed'), ('arrived', 'Arrived'), ('seated', 'Seated'), ('cancelled', 'Cancelled'), ], string='Reservation Status', default='draft', tracking=True) def _is_shippable_order(self): """ Treat pickup, delivery and other types as non-shippable for Odoo's standard validation. This enables 'Billing-only' checkout which is more reliable for payment providers. The Uber delivery line is protected by our _remove_delivery_line override. """ self.ensure_one() if self.fulfilment_type in ['pickup', 'delivery', 'dine_in', 'walk_in']: return False return super()._is_shippable_order() 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""" 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']: return True # If we have a carrier set by our Uber integration, trust it and skip standard re-validation if self.carrier_id and 'Uber' in (self.carrier_id.name or ''): return True 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): """ Override from dine360_kds to also mark the POS order as an online order. This method is called by dine360_kds.website_sale_integration when a website sale order is confirmed. """ # Let the parent create the POS order super(SaleOrderOnline, self)._create_pos_order_for_kds(sale_order) # Now find the POS order that was just created and mark it # We look for the most recent POS order linked to this sale order's partner # with the note containing the sale order name PosOrder = self.env['pos.order'] pos_order = PosOrder.search([ ('note', 'like', sale_order.name), ], order='id desc', limit=1) if pos_order: pos_order.write({ 'is_online_order': True, 'online_order_status': 'pending', 'sale_order_id': sale_order.id, 'online_order_date': fields.Datetime.now(), 'order_source': sale_order.order_source or 'online', 'fulfilment_type': sale_order.fulfilment_type or 'pickup', # 'delivery_time': sale_order.delivery_time, # 'uber_eta': sale_order.delivery_time, }) # Link back to sale order sale_order.write({'pos_order_id': pos_order.id}) # If paid online via gateway, record payment in POS immediately if sale_order.payment_option == 'online_gateway' and sale_order.amount_total > 0: payment_method = pos_order._get_online_payment_method() if payment_method: _logger.info("Recording online payment for POS order %s from Sale Order %s", pos_order.name, sale_order.name) pos_order.env['pos.payment'].create({ 'amount': sale_order.amount_total, 'payment_date': fields.Datetime.now(), 'payment_method_id': payment_method.id, 'pos_order_id': pos_order.id, }) # Process as paid so the state changes and payment button disappears pos_order.action_pos_order_paid() # Set all lines to a "hold" state - they will go to KDS only when cashier confirms for line in pos_order.lines: if line.product_id.is_kitchen_item: line.write({'preparation_status': 'waiting'}) # Send bus notification to POS if pos_order.config_id: channel = "online_orders_%s" % pos_order.config_id.id self.env['bus.bus']._sendone(channel, 'new_online_order', { 'order_id': pos_order.id, 'order_name': pos_order.pos_reference or pos_order.name, 'customer_name': sale_order.partner_id.name or 'Guest', 'amount_total': pos_order.amount_total, 'items_count': len(pos_order.lines), }) _logger.info( "Marked POS Order %s as online order from Sale Order %s", pos_order.name, sale_order.name )