190 lines
7.6 KiB
Python

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 option, mark as paid in POS to avoid confusion
if self.is_online_order and self.sale_order_id and self.sale_order_id.payment_option == 'online_gateway':
# 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)
# We prioritize methods linked to the current POS config
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