from odoo import models, fields, api, _ import logging _logger = logging.getLogger(__name__) class PosOrder(models.Model): _inherit = 'pos.order' is_online_order = fields.Boolean( string='Online Order', default=False, help='Indicates this order came from the website shop' ) online_order_status = fields.Selection([ ('pending', 'Pending'), ('confirmed', 'Confirmed'), ('rejected', 'Rejected'), ], string='Online Order Status', default='pending') sale_order_id = fields.Many2one( 'sale.order', string='Source Sale Order', help='The website sale order that generated this POS order' ) online_customer_name = fields.Char( string='Online Customer', compute='_compute_online_customer_name', store=True ) online_order_date = fields.Datetime( string='Online Order Date', default=fields.Datetime.now ) # delivery_time = fields.Datetime(string='Requested Delivery Time') # Note: order_source and fulfilment_type fields are defined in dine360_order_channels # dine360_online_orders just uses these fields @api.depends('partner_id', 'partner_id.name') def _compute_online_customer_name(self): for order in self: order.online_customer_name = order.partner_id.name or 'Guest' def action_confirm_online_order(self): """Cashier confirms the online order → sends to KDS and marks as paid if already paid online""" self.ensure_one() self.write({'online_order_status': 'confirmed'}) # If it's an online order with an online payment, mark as paid in POS to avoid confusion is_paid_online = self.sale_order_id.payment_option == 'online_gateway' if not is_paid_online and self.sale_order_id: successful_txs = self.sale_order_id.transaction_ids.filtered(lambda tx: tx.state in ['authorized', 'done']) if successful_txs and sum(successful_txs.mapped('amount')) >= self.sale_order_id.amount_total: is_paid_online = True if self.is_online_order and self.sale_order_id and is_paid_online: # Check if it needs payment (not yet paid in POS) if self.state == 'draft' and self.amount_total > 0 and self.amount_paid < self.amount_total: # Find a suitable payment method (Online Payment or Stripe) payment_method = self._get_online_payment_method() if payment_method: _logger.info("Automatically adding online payment from Stripe gateway for order %s using method %s", self.name, payment_method.name) # Create pos.payment record self.env['pos.payment'].create({ 'amount': self.amount_total - self.amount_paid, 'payment_date': fields.Datetime.now(), 'payment_method_id': payment_method.id, 'pos_order_id': self.id, }) # Update order state if fully paid if self.amount_total <= self.amount_paid: # Process the order as paid self.action_pos_order_paid() else: _logger.warning("Could not find a suitable POS Payment Method for online order %s", self.name) # Set all order lines to 'waiting' so KDS picks them up for line in self.lines: if line.product_id.is_kitchen_item and line.product_id.type != 'service': line.write({ 'preparation_status': 'waiting', }) # Notify KDS self.lines.filtered( lambda l: l.product_id.is_kitchen_item and l.product_id.type != 'service' )._notify_kds() # Notify POS that order was confirmed if self.config_id: channel = "online_orders_%s" % self.config_id.id self.env['bus.bus']._sendone(channel, 'online_order_confirmed', { 'order_id': self.id, 'order_name': self.pos_reference or self.name, }) _logger.info("Online order %s confirmed and sent to KDS", self.name) return True def action_reject_online_order(self): """Cashier rejects the online order""" self.ensure_one() self.write({'online_order_status': 'rejected'}) # Notify POS if self.config_id: channel = "online_orders_%s" % self.config_id.id self.env['bus.bus']._sendone(channel, 'online_order_rejected', { 'order_id': self.id, 'order_name': self.pos_reference or self.name, }) _logger.info("Online order %s rejected", self.name) return True @api.model_create_multi def create(self, vals_list): for vals in vals_list: if 'session_id' in vals: session = self.env['pos.session'].browse(vals['session_id']) if session.config_id.is_kiosk: vals['order_source'] = 'kiosk' vals['fulfilment_type'] = session.config_id.kiosk_service_mode or 'pickup' return super().create(vals_list) def _get_online_payment_method(self): """Find a suitable POS payment method for online/stripe payments""" # 1. Look for methods in the current config first if self.config_id: for method in self.config_id.payment_method_ids: if 'online' in method.name.lower() or 'stripe' in method.name.lower(): return method # Fallback to any non-cash method in config for method in self.config_id.payment_method_ids: if not method.is_cash_count: return method # 2. Global search if config search fails method = self.env['pos.payment.method'].search([ ('name', 'ilike', 'Online'), ], limit=1) if not method: method = self.env['pos.payment.method'].search([ ('name', 'ilike', 'Stripe'), ], limit=1) if not method: method = self.env['pos.payment.method'].search([ ('is_cash_count', '=', False) ], limit=1) return method @api.model def get_online_orders(self, config_id): """Fetch pending online orders for a specific POS config""" domain = [ ('is_online_order', '=', True), ('online_order_status', '=', 'pending'), ('config_id', '=', config_id), ] orders = self.search(domain, order='online_order_date desc') result = [] for order in orders: lines = [] for line in order.lines: lines.append({ 'id': line.id, 'product_name': line.full_product_name or line.product_id.name, 'qty': line.qty, 'price_unit': line.price_unit, 'price_subtotal_incl': line.price_subtotal_incl, 'customer_note': line.customer_note or '', 'is_kitchen_item': line.product_id.is_kitchen_item, }) result.append({ 'id': order.id, 'name': order.pos_reference or order.name, 'partner_name': order.partner_id.name or 'Guest', 'partner_phone': order.partner_id.phone or order.partner_id.mobile or '', 'amount_total': order.amount_total, 'date_order': order.date_order.isoformat() if order.date_order else '', 'sale_order_name': order.sale_order_id.name if order.sale_order_id else '', 'service_mode': order.fulfilment_type, 'order_source': order.order_source, 'note': order.note or '', 'lines': lines, }) return result