177 lines
7.9 KiB
Python
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
|
|
)
|