Files
odoo_source/addons/pos_self_order/controllers/orders.py
T
David Monnom (moda) 0ae2b9a3f2 [IMP] pos_self_order: change authorisation token
Previously, access_token were linked to tables. These tables were linked
to a floor_plan, which could be linked to several pos_configs.

The only way to access the self-order was to obtain a valid table
access_token. This behaviour was not correct because sometimes we would
allow table selection directly in the interface or commands without
a table.

Now, access_token is managed by pos_config. When a user has this token,
they can place commands and select the table they want if the option is
enabled.

closes odoo/odoo#126186

Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
2023-07-03 21:56:04 +02:00

226 lines
9.6 KiB
Python

# -*- coding: utf-8 -*-
from datetime import timedelta
import uuid
from odoo import http, fields, Command
from odoo.http import request
from werkzeug.exceptions import NotFound, BadRequest, Unauthorized
class PosSelfOrderController(http.Controller):
@http.route("/pos-self-order/process-new-order", auth="public", type="json", website=True)
def process_new_order(self, order, access_token, table_identifier):
lines = order.get('lines')
pos_config_sudo, table_sudo = self._verify_authorization(access_token, table_identifier)
pos_session_sudo = pos_config_sudo.current_session_id
sequence_number = self._get_sequence_number(table_sudo.id, pos_session_sudo.id)
unique_id = self._generate_unique_id(pos_session_sudo.id, table_sudo.id, sequence_number)
# Create the order without lines and prices computed
# We need to remap the order because some required fields are not used in the frontend.
order = {
'id': unique_id,
'data': {
'uuid': order.get('uuid'),
'name': unique_id,
'user_id': request.session.uid,
'sequence_number': sequence_number,
'access_token': uuid.uuid4().hex,
'pos_session_id': pos_session_sudo.id,
'table_id': table_sudo.id if table_sudo else False,
'partner_id': False,
'creation_date': str(fields.Datetime.now()),
'fiscal_position_id': pos_config_sudo.default_fiscal_position_id,
'statement_ids': [],
'lines': [],
'amount_tax': 0,
'amount_total': 0,
'amount_paid': 0,
'amount_return': 0,
},
'to_invoice': False,
'session_id': pos_session_sudo.id,
}
# Save the order in the database to get the id
posted_order_id = request.env['pos.order'].sudo().create_from_ui([order], draft=True)[0].get('id')
# Process the lines and get their prices computed
lines = self._process_lines(lines, pos_config_sudo, posted_order_id)
# Compute the order prices
amount_total, amount_untaxed = self._get_order_prices(lines)
# Update the order with the computed prices and lines
order_sudo = request.env["pos.order"].sudo().browse(posted_order_id)
order_sudo.write({
'lines': [Command.create(line) for line in lines],
'amount_tax': amount_total - amount_untaxed,
'amount_total': amount_total,
})
return order_sudo._export_for_self_order()
@http.route('/pos-self-order/get-orders-taxes', auth='public', type='json', website=True)
def get_order_taxes(self, order, access_token):
pos_config_sudo = request.env['pos.config'].sudo().search([('access_token', '=', access_token)], limit=1)
if not pos_config_sudo or not pos_config_sudo.self_order_table_mode or not pos_config_sudo.has_active_session:
raise Unauthorized("Invalid access token")
lines = self._process_lines(order.get('lines'), pos_config_sudo, 0)
amount_total, amount_untaxed = self._get_order_prices(lines)
return {
'lines': [{
'uuid': line.get('uuid'),
'price_subtotal': line.get('price_subtotal'),
'price_subtotal_incl': line.get('price_subtotal_incl'),
} for line in lines],
'amount_total': amount_total,
'amount_tax': amount_total - amount_untaxed,
}
@http.route('/pos-self-order/update-existing-order', auth="public", type="json", website=True)
def update_existing_order(self, order, access_token, table_identifier):
order_id = order.get('id')
order_access_token = order.get('access_token')
pos_config_sudo, table_sudo = self._verify_authorization(access_token, table_identifier)
order_sudo = request.env['pos.order'].sudo().search([
('id', '=', order_id),
('access_token', '=', order_access_token),
('table_id', '=', table_sudo.id)
])
if not order_sudo:
raise Unauthorized("Order not found in the server !")
elif order_sudo.state != 'draft':
raise Unauthorized("Order is not in draft state")
lines = self._process_lines(order.get('lines'), pos_config_sudo, order_sudo.id)
for line in lines:
if line.get('id'):
line_sudo = order_sudo.lines.filtered(lambda l: l.id == line.get('id'))
if line.get('qty') < line_sudo.qty:
line.set('qty', line_sudo.qty)
line_sudo.write({
**line,
})
else:
order_sudo.lines.create(line)
amount_total, amount_untaxed = self._get_order_prices(lines)
order_sudo.write({
'amount_tax': amount_total - amount_untaxed,
'amount_total': amount_total,
})
return order_sudo._export_for_self_order()
@http.route('/pos-self-order/get-orders', auth='public', type='json', website=True)
def get_orders_by_access_token(self, order_access_tokens):
orders_sudo = request.env["pos.order"].sudo().search([
("access_token", "in", order_access_tokens),
("date_order", ">=", fields.Datetime.now() - timedelta(days=7)),
])
if not orders_sudo:
raise NotFound("Orders not found")
orders = []
for order in orders_sudo:
orders.append(order._export_for_self_order())
return orders
@http.route('/pos-self-order/get-tables', auth='public', type='json', website=True)
def get_tables(self, access_token):
pos_config_sudo = request.env['pos.config'].sudo().search([('access_token', '=', access_token)], limit=1)
if not pos_config_sudo or not pos_config_sudo.self_order_table_mode or not pos_config_sudo.has_active_session:
raise Unauthorized("Invalid access token")
tables = pos_config_sudo.floor_ids.table_ids.filtered(lambda t: t.active).read(['id', 'name', 'identifier', 'floor_id'])
for table in tables:
table['floor_name'] = table.get('floor_id')[1]
return tables
def _process_lines(self, lines, pos_config_sudo, pos_order_id):
newLines = []
pricelist = request.env['product.pricelist'].sudo().browse(pos_config_sudo.pricelist_id.id)
for line in lines:
product_sudo = request.env["product.product"].sudo().browse(int(line.get("product_id")))
# todo take into account the price extra
price_unit = pricelist._get_product_price(product_sudo, quantity=1) if pricelist else product_sudo.lst_price
config_fiscal_pos = pos_config_sudo.default_fiscal_position_id
selected_account_tax = config_fiscal_pos.map_tax(product_sudo.taxes_id) if config_fiscal_pos else product_sudo.taxes_id
tax_results = selected_account_tax.compute_all(
price_unit,
pos_config_sudo.currency_id,
line.get('qty'),
product_sudo,
)
newLines.append({
'price_unit': price_unit,
'price_subtotal': tax_results.get('total_excluded'),
'price_subtotal_incl': tax_results.get('total_included'),
'price_extra': 0,
'id': line.get('id'),
'order_id': pos_order_id,
'tax_ids': product_sudo.taxes_id,
'uuid': line.get('uuid'),
'product_id': line.get('product_id'),
'qty': line.get('qty'),
'customer_note': line.get('customer_note'),
'selected_attributes': line.get('selected_attributes'),
'full_product_name': line.get('full_product_name'),
})
return newLines
def _get_order_prices(self, lines):
amount_untaxed = sum([line.get('price_subtotal') for line in lines])
amount_total = sum([line.get('price_subtotal_incl') for line in lines])
return amount_total, amount_untaxed
# The first part will be the session_id of the order.
# The second part will be the table_id of the order.
# Last part the sequence number of the order.
# INFO: This is allow a maximum of 999 tables and 9999 orders per table, so about ~1M orders per session.
# Example: 'Self-Order 00001-001-0001'
def _generate_unique_id(self, pos_session_id: int, table_id: int, sequence_number: int) -> str:
first_part = "{:05d}".format(int(pos_session_id))
second_part = "{:03d}".format(int(table_id))
third_part = "{:04d}".format(int(sequence_number))
return f"Self-Order {first_part}-{second_part}-{third_part}"
def _get_sequence_number(self, table_id, session_id):
order_sudo = request.env["pos.order"].sudo().search([(
'pos_reference',
'like',
f"Self-Order {session_id:0>5}-{table_id:0>3}")], order='id desc', limit=1)
return (order_sudo.sequence_number + 1) or 1
def _verify_authorization(self, access_token, table_identifier):
table_sudo = request.env["restaurant.table"].sudo().search([('identifier', '=', table_identifier)], limit=1)
pos_config_sudo = request.env['pos.config'].sudo().search([('access_token', '=', access_token)], limit=1)
if not pos_config_sudo or not pos_config_sudo.self_order_table_mode or not pos_config_sudo.has_active_session:
raise Unauthorized("Invalid access token")
if not table_sudo:
raise Unauthorized("Table not found")
return pos_config_sudo, table_sudo