199 lines
8.9 KiB
Python
199 lines
8.9 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import json
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import UserError
|
|
from odoo.fields import Command
|
|
|
|
|
|
class PosOrder(models.Model):
|
|
_inherit = 'pos.order'
|
|
|
|
source = fields.Selection(selection_add=[('platform_order', 'Platform Order')], ondelete={'platform_order': 'set default'})
|
|
|
|
# -- Provider and Store Information --
|
|
platform_order_store_id = fields.Many2one('platform.order.entity', string='Store', readonly=True, copy=False)
|
|
platform_order_provider_id = fields.Many2one(related='platform_order_store_id.provider_id', store=True, copy=False)
|
|
platform_order_provider_code = fields.Selection(related='platform_order_provider_id.code', string='Provider Code', copy=False)
|
|
|
|
# -- Platform Order Data --
|
|
platform_order_ref = fields.Char(string='Online Order Reference', index=True, readonly=True, copy=False, help='Reference of the order in the food ordering platform.')
|
|
platform_order_type = fields.Selection(
|
|
[('delivery', 'Delivery'), ('pickup', 'Pickup'), ('dine_in', 'Dine In')],
|
|
string='Order Type', readonly=True, copy=False)
|
|
platform_order_status = fields.Selection([
|
|
('new', 'New'),
|
|
('accepted', 'Accepted'),
|
|
('driver_allocated', 'Driver Allocated'),
|
|
('driver_arrived', 'Driver Arrived'),
|
|
('collected', 'Collected'),
|
|
('delivered', 'Delivered'),
|
|
('cancelled', 'Cancelled'),
|
|
('failed', 'Failed')], string='Order Status', readonly=True, copy=False)
|
|
platform_order_pin = fields.Char('Order PIN', readonly=True, copy=False)
|
|
platform_order_food_ready = fields.Boolean('Food Ready', readonly=True, copy=False)
|
|
platform_order_rider_name = fields.Char('Rider Name', readonly=True, copy=False)
|
|
platform_order_cancel_reason = fields.Char('Cancellation Reason', readonly=True, copy=False)
|
|
|
|
def _get_payment_method_values(self):
|
|
self.ensure_one()
|
|
return {
|
|
'state': 'paid',
|
|
'amount_paid': self.amount_total,
|
|
'payment_ids': [Command.create({
|
|
'payment_method_id': self.platform_order_store_id.payment_method_id.id,
|
|
'amount': self.amount_total,
|
|
})],
|
|
}
|
|
|
|
def _ensure_provider_is_not_disabled(self):
|
|
"""Checks if the provider is active before making an API call."""
|
|
self.ensure_one()
|
|
if self.platform_order_provider_id.state == 'disabled':
|
|
raise UserError(_("The provider '%s' is disabled.", self.platform_order_provider_id.name))
|
|
|
|
# region JS-CALLABLE METHODS (FOR POS INTERFACE)
|
|
def platform_order_status_update_from_ui(self, status, **kwargs):
|
|
self._ensure_provider_is_not_disabled()
|
|
try:
|
|
return self._order_status_update_handler(status, **kwargs)
|
|
except UserError as e:
|
|
return {'success': False, 'message': str(e)}
|
|
|
|
def _order_status_update_handler(self, status, **kwargs):
|
|
match status:
|
|
case 'accept':
|
|
return self._accept_order_handler(**kwargs)
|
|
case 'food_ready':
|
|
return self._food_ready_handler(**kwargs)
|
|
case 'reject':
|
|
return self._reject_order_handler(**kwargs)
|
|
case _:
|
|
raise UserError(self.env._("Unsupported status update action: %s", status))
|
|
|
|
def _accept_order_handler(self):
|
|
self._send_accept_order_request()
|
|
self._set_accepted()
|
|
return {'success': True, 'message': self.env._("Order has been accepted successfully.")}
|
|
|
|
def _food_ready_handler(self):
|
|
if self.platform_order_food_ready:
|
|
raise UserError(self.env._("Food is already marked as ready for pickup or delivery."))
|
|
self._send_food_ready_request()
|
|
self.platform_order_food_ready = True
|
|
self.message_post(body=self.env._("Food is now marked as ready for pickup or delivery."))
|
|
return {'success': True, 'message': self.env._("Food marked as ready successfully.")}
|
|
|
|
def _reject_order_handler(self, reason):
|
|
if self.platform_order_status == 'driver_arrived':
|
|
raise UserError(_("Cannot reject the order as the driver has already arrived."))
|
|
try:
|
|
self._send_reject_order_request(reason)
|
|
self._set_cancelled(reason=reason)
|
|
return {'success': True, 'message': self.env._("Order has been rejected successfully.")}
|
|
except UserError as e:
|
|
self._set_cancelled(reason=self.env._("Rejected by user with error: %s", str(e)))
|
|
raise
|
|
|
|
def mark_platform_prep_order_as_printed(self):
|
|
self.ensure_one()
|
|
|
|
lopc = json.loads(self.last_order_preparation_change) if self.last_order_preparation_change else {
|
|
'lines': {},
|
|
'metadata': {},
|
|
'platform_order_printed': False,
|
|
}
|
|
|
|
if lopc.get('platform_order_printed'):
|
|
msg = "This delivery order has already been printed automatically."
|
|
raise ValueError(msg)
|
|
|
|
lopc['platform_order_printed'] = True
|
|
|
|
self.write({
|
|
'last_order_preparation_change': json.dumps(lopc),
|
|
})
|
|
|
|
return True
|
|
# endregion
|
|
|
|
# region NOTIFICATION HANDLING
|
|
@api.model
|
|
def _find_order_from_data(self, provider_code, notification_data, raise_if_not_found=True):
|
|
""" Find the corresponding order based on notification data. """
|
|
# This method is a placeholder for provider-specific logic.
|
|
msg = "This method should be implemented in provider-specific subclasses."
|
|
raise NotImplementedError(msg)
|
|
|
|
def _update_order_status_from_data(self, notification_data):
|
|
""" Process and apply updates from notification data to the order. """
|
|
# This method is a placeholder for provider-specific logic.
|
|
msg = "This method should be implemented in provider-specific subclasses."
|
|
raise NotImplementedError(msg)
|
|
# endregion
|
|
|
|
# region ORDER STATE UPDATES
|
|
def _set_accepted(self):
|
|
self._update_state(('new',), 'accepted')
|
|
|
|
def _set_driver_allocated(self, driver_values: dict | None = None):
|
|
self._update_state(('accepted',), 'driver_allocated', extra_values=driver_values)
|
|
|
|
def _set_driver_arrived(self):
|
|
self._update_state(('driver_allocated',), 'driver_arrived')
|
|
|
|
def _set_collected(self):
|
|
# Collect payment in this stage
|
|
self._update_state(('driver_allocated', 'driver_arrived'), 'collected', extra_values=self._get_payment_method_values())
|
|
|
|
def _set_delivered(self):
|
|
extra_values = {} if self.payment_ids else self._get_payment_method_values()
|
|
self._update_state(
|
|
('accepted', 'driver_allocated', 'driver_arrived', 'collected'),
|
|
'delivered',
|
|
extra_values=extra_values,
|
|
)
|
|
|
|
def _set_cancelled(self, reason: str | None = None):
|
|
allowed_states = ('new', 'accepted', 'driver_allocated', 'driver_arrived', 'collected')
|
|
extra_values = {'state': 'cancel'}
|
|
if reason:
|
|
extra_values['platform_order_cancel_reason'] = reason
|
|
self.sudo().message_post(body=self.env._("Order cancelled with reason: %s", reason))
|
|
self._update_state(allowed_states, 'cancelled', extra_values=extra_values)
|
|
|
|
def _update_state(self, allowed_states, target_state, extra_values=None):
|
|
values_to_write = dict(extra_values or {})
|
|
orders_to_process = self.filtered(lambda o: o.platform_order_status in allowed_states)
|
|
|
|
if len(orders_to_process) != len(self):
|
|
raise UserError(self.env._(
|
|
"Cannot update state for orders that are not in the allowed states: %s.",
|
|
', '.join(allowed_states),
|
|
))
|
|
|
|
values_to_write['platform_order_status'] = target_state
|
|
orders_to_process.write(values_to_write)
|
|
orders_to_process.sudo().message_post(body=self.env._("Platform status updated to %s.", target_state))
|
|
orders_to_process.config_id.notify_platform_order_synchronisation(orders_to_process)
|
|
# endregion
|
|
|
|
# region HOOKS
|
|
def _send_food_ready_request(self):
|
|
"""Hook for provider-specific implementation to notify 'food ready'."""
|
|
raise NotImplementedError("Provider '%s' does not implement _send_food_ready_request." % self.platform_order_provider_id.name)
|
|
|
|
def _send_accept_order_request(self):
|
|
"""Hook for provider-specific implementation to accept an order."""
|
|
raise NotImplementedError("Provider '%s' does not implement _send_accept_order_request." % self.platform_order_provider_id.name)
|
|
|
|
def _send_reject_order_request(self, reason):
|
|
"""Hook for provider-specific implementation to reject an order."""
|
|
raise NotImplementedError("Provider '%s' does not implement _send_reject_order_request." % self.platform_order_provider_id.name)
|
|
# endregion
|
|
|
|
@api.model
|
|
def _load_pos_preparation_data_fields(self):
|
|
res = super()._load_pos_preparation_data_fields()
|
|
return res + ['platform_order_ref']
|