from odoo import models, fields, api, _ from odoo.exceptions import ValidationError import logging _logger = logging.getLogger(__name__) class PosOrderChannels(models.Model): _inherit = 'pos.order' # ----------------------------------------------------------- # Channel 1-5: Order Source Field # ----------------------------------------------------------- order_source = fields.Selection([ ('walk_in', 'Walk-In (Standard POS)'), ('phone', 'Telephone Order'), ('online', 'Online / eCommerce'), ('whatsapp', 'WhatsApp'), ('social_media', 'Social Media'), ('platform', 'Third-Party Platform'), ('kiosk', 'Self-Order Kiosk'), ('qr', 'QR Table Order'), ], string='Order Source', default='walk_in', index=True, help='Channel through which this order was received') # ----------------------------------------------------------- # All Channels: Fulfilment Type # ----------------------------------------------------------- fulfilment_type = fields.Selection([ ('dine_in', 'Dine-In'), ('pickup', 'Pickup'), ('delivery', 'Delivery'), ], string='Fulfilment Type', default='dine_in', index=True, help='How the customer wants to receive the order') # ----------------------------------------------------------- # Delivery Address (linked to res.partner) # ----------------------------------------------------------- delivery_partner_id = fields.Many2one( 'res.partner', string='Delivery Address', domain="[('type', 'in', ['delivery', 'contact', 'other'])]", help='Delivery address for this order' ) delivery_street = fields.Char('Delivery Street', compute='_compute_delivery_address', store=True, readonly=False) delivery_city = fields.Char('Delivery City', compute='_compute_delivery_address', store=True, readonly=False) delivery_zip = fields.Char('Delivery Zip', compute='_compute_delivery_address', store=True, readonly=False) delivery_phone = fields.Char('Delivery Phone', compute='_compute_delivery_address', store=True, readonly=False) delivery_notes = fields.Text('Delivery Notes', help='Special delivery instructions') # ----------------------------------------------------------- # WhatsApp: sender info # ----------------------------------------------------------- whatsapp_number = fields.Char('WhatsApp Number', help='Customer WhatsApp phone number') whatsapp_msg_id = fields.Char('WhatsApp Msg ID', help='Original message ID for thread linking') # ----------------------------------------------------------- # Social Media: reference # ----------------------------------------------------------- # Social Media: reference social_ref = fields.Char('Social Ref', help='Instagram/Facebook message/post reference') # Telephone: customer phone for phone orders telephone_number = fields.Char('Telephone Number', help='Customer phone number for telephone orders') # ----------------------------------------------------------- # Computed helpers # ----------------------------------------------------------- is_delivery_order = fields.Boolean( string='Is Delivery', compute='_compute_is_delivery_order', store=True ) @api.depends('fulfilment_type') def _compute_is_delivery_order(self): for order in self: order.is_delivery_order = (order.fulfilment_type == 'delivery') @api.depends('delivery_partner_id') def _compute_delivery_address(self): for order in self: p = order.delivery_partner_id if p: order.delivery_street = p.street or '' order.delivery_city = p.city or '' order.delivery_zip = p.zip or '' order.delivery_phone = p.phone or p.mobile or '' else: order.delivery_street = order.delivery_street or '' order.delivery_city = order.delivery_city or '' order.delivery_zip = order.delivery_zip or '' order.delivery_phone = order.delivery_phone or '' # ----------------------------------------------------------- # Validation # ----------------------------------------------------------- @api.constrains('fulfilment_type', 'delivery_street') def _check_delivery_address(self): for order in self: if order.fulfilment_type == 'delivery' and order.state == 'done': if not (order.delivery_street or order.delivery_partner_id): raise ValidationError( _("A delivery address is required for delivery orders.") ) # ----------------------------------------------------------- # Override _order_fields for POS sync (these values come from POS UI) # ----------------------------------------------------------- @api.model def _order_fields(self, ui_order): fields_dict = super()._order_fields(ui_order) fields_dict['order_source'] = ui_order.get('order_source', 'walk_in') fields_dict['fulfilment_type'] = ui_order.get('fulfilment_type', 'dine_in') fields_dict['delivery_street'] = ui_order.get('delivery_street', '') fields_dict['delivery_city'] = ui_order.get('delivery_city', '') fields_dict['delivery_zip'] = ui_order.get('delivery_zip', '') fields_dict['delivery_phone'] = ui_order.get('delivery_phone', '') fields_dict['delivery_notes'] = ui_order.get('delivery_notes', '') fields_dict['whatsapp_number'] = ui_order.get('whatsapp_number', '') fields_dict['social_ref'] = ui_order.get('social_ref', '') fields_dict['telephone_number'] = ui_order.get('telephone_number', '') return fields_dict # ----------------------------------------------------------- # Export fields back to POS (needed for POS to read saved orders) # ----------------------------------------------------------- def _export_for_ui(self, order): result = super()._export_for_ui(order) result['order_source'] = order.order_source result['fulfilment_type'] = order.fulfilment_type result['delivery_street'] = order.delivery_street or '' result['delivery_city'] = order.delivery_city or '' result['delivery_zip'] = order.delivery_zip or '' result['delivery_phone'] = order.delivery_phone or '' result['delivery_notes'] = order.delivery_notes or '' result['whatsapp_number'] = order.whatsapp_number or '' result['social_ref'] = order.social_ref or '' result['telephone_number'] = order.telephone_number or '' return result class PosOrderLineChannels(models.Model): _inherit = 'pos.order.line' order_source = fields.Selection( related='order_id.order_source', string='Order Source', store=True ) fulfilment_type = fields.Selection( related='order_id.fulfilment_type', string='Fulfilment Type', store=True )