forked from Mapan/odoo17e
213 lines
9.3 KiB
Python
213 lines
9.3 KiB
Python
import secrets
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import ValidationError
|
|
|
|
from odoo.addons.pos_preparation_display.models.preparation_display_orderline import PosPreparationDisplayOrderline
|
|
|
|
class PosPreparationDisplay(models.Model):
|
|
_name = 'pos_preparation_display.display'
|
|
_description = "Preparation display"
|
|
|
|
name = fields.Char("Name", required=True)
|
|
company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.company)
|
|
pos_config_ids = fields.Many2many(string="Point of Sale", comodel_name='pos.config')
|
|
category_ids = fields.Many2many('pos.category', string="Product categories", help="Product categories that will be displayed on this screen.")
|
|
order_count = fields.Integer("Order count", compute='_compute_order_count')
|
|
average_time = fields.Integer("Order average time", compute='_compute_order_count', help="Average time of all order that not in a done stage.")
|
|
stage_ids = fields.One2many('pos_preparation_display.stage', 'preparation_display_id', string="Stages", default=[
|
|
{'name': 'To prepare', 'color': '#6C757D', 'alert_timer': 10},
|
|
{'name': 'Ready', 'color': '#4D89D1', 'alert_timer': 5},
|
|
{'name': 'Completed', 'color': '#4ea82a', 'alert_timer': 0}
|
|
])
|
|
contains_bar_restaurant = fields.Boolean("Is a Bar/Restaurant", compute='_compute_contains_bar_restaurant', store=True)
|
|
access_token = fields.Char("Access Token",
|
|
copy=False,
|
|
required=True,
|
|
readonly=True,
|
|
default=lambda self: self._get_access_token())
|
|
|
|
@staticmethod
|
|
def _get_access_token():
|
|
return secrets.token_hex(16)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
displays = super().create(vals_list)
|
|
displays.reset()
|
|
return displays
|
|
|
|
# getter for pos_category_ids and pos_config_ids, in case of no one selected, return all of each.
|
|
def _get_pos_category_ids(self):
|
|
self.ensure_one()
|
|
if not self.category_ids:
|
|
return self.env['pos.category'].search([])
|
|
else:
|
|
return self.category_ids
|
|
|
|
def _should_include(self, orderline: PosPreparationDisplayOrderline) -> bool:
|
|
"""
|
|
Returns whether the orderline should be included in the preparation
|
|
display, based on the categories that are selected for the preparation
|
|
"""
|
|
return any(categ_id in self._get_pos_category_ids().ids for categ_id in orderline.product_id.pos_categ_ids.ids)
|
|
|
|
def get_pos_config_ids(self):
|
|
self.ensure_one()
|
|
if not self.pos_config_ids:
|
|
return self.env['pos.config'].search([])
|
|
else:
|
|
return self.pos_config_ids
|
|
|
|
def _get_open_orders_in_display(self):
|
|
self.ensure_one()
|
|
PosPreparationDisplayOrder = self.env['pos_preparation_display.order']
|
|
open_orders = self.env['pos_preparation_display.order.stage']._read_group(
|
|
domain=[
|
|
('preparation_display_id', '=', self.id),
|
|
('order_id.pos_order_id.session_id.state', 'not in', ['closed', 'closing_control']),
|
|
],
|
|
groupby=['order_id'],
|
|
having=[('done:bool_or', '=', False)],
|
|
)
|
|
orders = PosPreparationDisplayOrder
|
|
if open_orders:
|
|
orders = PosPreparationDisplayOrder.union(*(order[0] for order in open_orders))
|
|
|
|
return orders
|
|
|
|
def _get_stageless_orders_in_display(self):
|
|
self.ensure_one()
|
|
stageless_orders_ids = self.env['pos_preparation_display.order']._search([
|
|
'|', ('pos_order_id', '=', False),
|
|
('pos_config_id', 'in', self.get_pos_config_ids().ids),
|
|
])
|
|
stageless_orders_ids.add_where(
|
|
"""
|
|
NOT EXISTS
|
|
(
|
|
SELECT 1
|
|
FROM pos_preparation_display_order_stage
|
|
WHERE order_id = pos_preparation_display_order.id AND preparation_display_id = %s
|
|
)
|
|
""",
|
|
(self.id,)
|
|
)
|
|
|
|
return self.env['pos_preparation_display.order'].browse(stageless_orders_ids)
|
|
|
|
def get_preparation_display_data(self):
|
|
return {
|
|
'categories': self._get_pos_category_ids().read(['id', 'display_name', 'sequence']),
|
|
'stages': self.stage_ids.read(),
|
|
'orders': self.env["pos_preparation_display.order"].get_preparation_display_order(self.id),
|
|
'attributes': self.env['product.attribute'].search([]).read(['id', 'name']),
|
|
'attribute_values': self.env['product.template.attribute.value'].search([]).read(['id', 'name', 'attribute_id']),
|
|
}
|
|
|
|
def open_reset_wizard(self):
|
|
return {
|
|
'name': _("Reset Preparation Display"),
|
|
'type': 'ir.actions.act_window',
|
|
'view_mode': 'form',
|
|
'res_model': 'pos_preparation_display.reset.wizard',
|
|
'target': 'new',
|
|
'context': {'preparation_display_id': self.id}
|
|
}
|
|
|
|
def open_ui(self):
|
|
return {
|
|
'type': 'ir.actions.act_url',
|
|
'url': '/pos_preparation_display/web?display_id=%d' % self.id,
|
|
'target': 'self',
|
|
}
|
|
|
|
# if needed the user can instantly reset a preparation display and archive all the orders.
|
|
def reset(self):
|
|
for preparation_display in self:
|
|
last_stage = preparation_display.stage_ids[-1]
|
|
orders = preparation_display._get_open_orders_in_display()
|
|
new_orders = preparation_display._get_stageless_orders_in_display()
|
|
|
|
order_stages = []
|
|
for order in new_orders:
|
|
order_stages.append({
|
|
'preparation_display_id': preparation_display.id,
|
|
'stage_id': last_stage.id,
|
|
'order_id': order.id,
|
|
'done': True
|
|
})
|
|
self.env['pos_preparation_display.order.stage'].create(order_stages)
|
|
|
|
for order in orders:
|
|
current_order_stage = None
|
|
|
|
for stage in order.order_stage_ids[::-1]:
|
|
if stage.preparation_display_id.id == preparation_display.id:
|
|
current_order_stage = stage
|
|
break
|
|
|
|
current_order_stage.done = True
|
|
|
|
preparation_display._send_load_orders_message()
|
|
|
|
def _send_load_orders_message(self, sound=False):
|
|
self.ensure_one()
|
|
self.env['bus.bus']._sendone(f'preparation_display-{self.access_token}', 'load_orders', {
|
|
'preparation_display_id': self.id,
|
|
'sound': sound
|
|
})
|
|
|
|
@api.depends('stage_ids', 'pos_config_ids', 'category_ids')
|
|
def _compute_order_count(self):
|
|
for preparation_display in self:
|
|
progress_order_count = 0
|
|
orders = preparation_display.env['pos_preparation_display.order'].search([
|
|
('pos_config_id', 'in', preparation_display.get_pos_config_ids().ids),
|
|
('create_date', '>=', fields.Date.today())
|
|
])
|
|
|
|
for order in orders:
|
|
order_stage = order.order_stage_ids.filtered(lambda s: s.preparation_display_id.id == preparation_display.id)
|
|
|
|
if order_stage:
|
|
order_stage_last = sorted(order_stage, key=lambda s: s.write_date, reverse=True)[0]
|
|
if order_stage_last.stage_id.id == preparation_display.stage_ids[-1].id:
|
|
continue
|
|
|
|
for orderline in order.preparation_display_order_line_ids:
|
|
if preparation_display._should_include(orderline) and orderline.product_quantity > 0:
|
|
progress_order_count += 1
|
|
break
|
|
|
|
preparation_display.order_count = progress_order_count
|
|
order_stages = self.env['pos_preparation_display.order.stage'].search([
|
|
('preparation_display_id', '=', preparation_display.id),
|
|
('create_date', '>=', fields.Date.today()),
|
|
('done', '=', True)
|
|
])
|
|
|
|
completed_order_times = [(order_stage.write_date - order_stage.order_id.create_date).total_seconds() for order_stage in order_stages]
|
|
preparation_display.average_time = round(sum(completed_order_times) / len(completed_order_times) / 60) if completed_order_times else 0
|
|
|
|
@api.constrains('stage_ids')
|
|
def _check_stage_ids(self):
|
|
for preparation_display in self:
|
|
if len(preparation_display.stage_ids) == 0:
|
|
raise ValidationError(_("A preparation display must have a minimum of one step."))
|
|
|
|
@api.depends('pos_config_ids')
|
|
def _compute_contains_bar_restaurant(self):
|
|
for preparation_display in self:
|
|
preparation_display.contains_bar_restaurant = any(pos_config_id.module_pos_restaurant for pos_config_id in preparation_display.get_pos_config_ids())
|
|
|
|
@api.model
|
|
def pos_has_valid_product(self):
|
|
return self.env['product.product'].sudo().search_count([('available_in_pos', '=', True), ('list_price', '>=', 0), ('id', 'not in', self.env['pos.config']._get_special_products().ids)], limit=1) > 0
|
|
|
|
def load_product_frontend(self):
|
|
allowed = not self.pos_has_valid_product()
|
|
if allowed:
|
|
self.env['pos.session']._load_onboarding_data()
|
|
categories = self._get_pos_category_ids().read(['id', 'display_name', 'sequence'])
|
|
return categories
|