162 lines
7.1 KiB
Python

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 all online orders as non-shippable for Odoo's standard validation.
This enables 'Billing-only' checkout even for delivery orders,
as we handle shipping manually via Uber fee lines.
"""
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()
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 _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})
# Check if order is already paid online (e.g. Stripe, PayPal, etc.)
# We check both the custom payment_option and Odoo's standard transactions
is_paid_online = sale_order.payment_option == 'online_gateway'
if not is_paid_online:
# Check for successful transactions (authorize or done)
successful_txs = sale_order.transaction_ids.filtered(lambda tx: tx.state in ['authorized', 'done'])
if successful_txs and sum(successful_txs.mapped('amount')) >= sale_order.amount_total:
is_paid_online = True
if is_paid_online and sale_order.amount_total > 0:
payment_method = pos_order._get_online_payment_method()
if payment_method:
_logger.info("Recording confirmed 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
# We use a delayed call or ensure state is updated
pos_order.action_pos_order_paid()
# Also set the online status to confirmed since it's paid
pos_order.write({'online_order_status': 'confirmed'})
else:
_logger.warning("No suitable POS Payment Method for paid online order %s", pos_order.name)
# 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
)