177 lines
7.9 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 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})
# Check if paid via gateway (custom field) or standard Odoo transaction (Stripe, etc.)
has_paid_transaction = any(t.state in ['authorized', 'done'] for t in sale_order.transaction_ids)
if (sale_order.payment_option == 'online_gateway' or has_paid_transaction) 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)
payment_data = {
'amount': sale_order.amount_total,
'payment_date': fields.Datetime.now(),
'payment_method_id': payment_method.id,
'pos_order_id': pos_order.id,
}
if hasattr(pos_order, 'add_payment'):
pos_order.add_payment(payment_data)
else:
pos_order.env['pos.payment'].create(payment_data)
pos_order.env.flush_all()
pos_order.invalidate_recordset(['payment_ids', 'amount_paid'])
# Process as paid so the state changes and payment button disappears safely
pos_order.write({'state': 'paid'})
try:
pos_order._create_order_picking()
except AttributeError:
_logger.warning("No _create_order_picking method found on POS order")
except Exception as e:
_logger.error("Error creating picking for online POS order: %s", str(e))
# 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
)