forked from Mapan/odoo17e
161 lines
8.2 KiB
Python
161 lines
8.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import uuid
|
|
|
|
from werkzeug.urls import url_join
|
|
from datetime import datetime, timedelta
|
|
|
|
from odoo import models, fields, api, tools, _
|
|
|
|
ASK_FIELDS_SELECTION = [
|
|
("required", "Required"),
|
|
("optional", "Optional"),
|
|
("none", "None"),
|
|
]
|
|
|
|
PLANNED_VISITOR_TIME = 45
|
|
|
|
class Frontdesk(models.Model):
|
|
_name = 'frontdesk.frontdesk'
|
|
_description = 'Frontdesk'
|
|
|
|
name = fields.Char('Frontdesk Name', required=True)
|
|
responsible_ids = fields.Many2many('res.users', string='Responsibles', required=True)
|
|
company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.company)
|
|
theme = fields.Selection(selection=[("light", "Light"), ("dark", "Dark")], default='light')
|
|
image = fields.Image("Image")
|
|
host_selection = fields.Boolean('Host Selection', groups='frontdesk.frontdesk_group_user')
|
|
authenticate_guest = fields.Boolean('Authenticate Guest', default=True, groups='frontdesk.frontdesk_group_user')
|
|
ask_phone = fields.Selection(string='Phone', selection=ASK_FIELDS_SELECTION, default='required', required=True)
|
|
ask_company = fields.Selection(string='Organization', selection=ASK_FIELDS_SELECTION, default='optional', required=True)
|
|
ask_email = fields.Selection(string='Email', selection=ASK_FIELDS_SELECTION, default='none', required=True)
|
|
notify_email = fields.Boolean('Notify by email', groups='frontdesk.frontdesk_group_user')
|
|
mail_template_id = fields.Many2one(
|
|
'mail.template',
|
|
string='Email Template',
|
|
domain="[('model', '=', 'frontdesk.frontdesk')]",
|
|
default=lambda self: self.env.ref('frontdesk.frontdesk_mail_template', raise_if_not_found=False)
|
|
)
|
|
notify_sms = fields.Boolean('Notify by SMS', groups='frontdesk.frontdesk_group_user')
|
|
sms_template_id = fields.Many2one(
|
|
'sms.template',
|
|
string='SMS Template',
|
|
domain="[('model', '=', 'frontdesk.frontdesk')]",
|
|
default=lambda self: self.env.ref('frontdesk.frontdesk_sms_template', raise_if_not_found=False)
|
|
)
|
|
self_check_in = fields.Boolean('Self Check-In', groups='frontdesk.frontdesk_group_user',
|
|
help='Shows a QR code in the interface, for guests to check in from their mobile phone.'
|
|
)
|
|
drink_offer = fields.Boolean('Offer Drinks', groups='frontdesk.frontdesk_group_user')
|
|
drink_ids = fields.Many2many('frontdesk.drink')
|
|
notify_discuss = fields.Boolean('Notify by discuss', default=True, groups='frontdesk.frontdesk_group_user')
|
|
description = fields.Html(groups='frontdesk.frontdesk_group_user')
|
|
visitor_ids = fields.One2many('frontdesk.visitor', 'station_id', string='Visitors')
|
|
guest_on_site = fields.Integer('Guests On Site', compute='_compute_dashboard_data')
|
|
pending = fields.Integer('Pending', compute='_compute_dashboard_data')
|
|
drink_to_serve = fields.Integer('Drinks to Serve', compute='_compute_dashboard_data')
|
|
latest_check_in = fields.Char(compute='_compute_dashboard_data')
|
|
visitor_properties_definition = fields.PropertiesDefinition('Visitor Properties')
|
|
access_token = fields.Char("Security Token", default=lambda self: str(uuid.uuid4()), required=True, copy=False, readonly=True)
|
|
kiosk_url = fields.Char('Kiosk URL', compute='_compute_kiosk_url', groups='frontdesk.frontdesk_group_user')
|
|
is_favorite = fields.Boolean()
|
|
active = fields.Boolean(default=True)
|
|
|
|
def _compute_dashboard_data(self):
|
|
""" This method computes the number of guests currently on site, the number of pending visitors, the number
|
|
of drinks to serve, and the time of the latest check-in. """
|
|
visitor_data = self.env['frontdesk.visitor']._read_group([
|
|
('state', 'in', ('checked_in', 'planned')),
|
|
('station_id', 'in', self.ids),
|
|
], ['station_id', 'state'], ['__count'])
|
|
checked_in_mapped = {station.id: count for station, state, count in visitor_data if state == 'checked_in'}
|
|
planned_mapped = {station.id: count for station, state, count in visitor_data if state == 'planned'}
|
|
drinks_data = self.env['frontdesk.visitor']._read_group([
|
|
('drink_ids', '!=', False), ('served', '=', False),
|
|
('station_id', 'in', self.ids),
|
|
], ['station_id'], ['__count'])
|
|
drinks_data_mapped = {station.id: count for station, count in drinks_data}
|
|
for frontdesk in self:
|
|
guest_on_site = pending = drink_to_serve = 0
|
|
latest_check_in = False
|
|
if frontdesk.visitor_ids:
|
|
guest_on_site = checked_in_mapped.get(frontdesk.id, 0)
|
|
pending = planned_mapped.get(frontdesk.id, 0)
|
|
drink_to_serve = drinks_data_mapped.get(frontdesk.id, 0)
|
|
last_visitors = frontdesk.visitor_ids.filtered(lambda v: v.state == 'checked_in')
|
|
latest_check_in_time = last_visitors and last_visitors[-1].check_in
|
|
if latest_check_in_time:
|
|
total_seconds = (datetime.now() - latest_check_in_time).total_seconds()
|
|
time_diff = int(total_seconds / 60) if total_seconds < 3600 else int(total_seconds / 3600)
|
|
latest_check_in = _("Last Check-In: %s minutes ago", time_diff) if total_seconds < 3600 \
|
|
else _("Last Check-In: %s hours ago", time_diff)
|
|
frontdesk.update({
|
|
'guest_on_site': guest_on_site,
|
|
'pending': pending,
|
|
'drink_to_serve': drink_to_serve,
|
|
'latest_check_in': latest_check_in,
|
|
})
|
|
|
|
@api.depends('access_token')
|
|
def _compute_kiosk_url(self):
|
|
for frontdesk in self:
|
|
frontdesk.kiosk_url = url_join(frontdesk.get_base_url(), '/kiosk/%s/%s' % (frontdesk.id, frontdesk.access_token))
|
|
|
|
def action_open_kiosk(self):
|
|
self.ensure_one()
|
|
return {
|
|
'type': 'ir.actions.act_url',
|
|
'url': self.kiosk_url,
|
|
'target': 'new',
|
|
}
|
|
|
|
def action_open_visitors(self):
|
|
self.ensure_one()
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': _("Visitors"),
|
|
'res_model': 'frontdesk.visitor',
|
|
'view_mode': 'tree,form,kanban,graph,pivot,calendar,gantt',
|
|
'context': {
|
|
"search_default_state_is_planned": 1,
|
|
"search_default_state_is_checked_in": 1,
|
|
"search_default_today": 1
|
|
},
|
|
'domain': [('station_id.id', '=', self.id)],
|
|
}
|
|
|
|
def _get_frontdesk_field(self):
|
|
return ['description', 'host_selection', 'drink_offer', 'self_check_in', 'theme',
|
|
'drink_ids', 'ask_email', 'ask_phone', 'ask_company', 'authenticate_guest']
|
|
|
|
def _get_frontdesk_data(self):
|
|
""" Returns the data to the frontend. """
|
|
self.ensure_one()
|
|
data = {
|
|
'company': {'name': self.company_id.name, 'id': self.company_id.id},
|
|
'langs': [{'code': lang[0], 'name': lang[1]} for lang in self.env['res.lang'].get_installed()],
|
|
'station': self.search_read([('id', '=', self.id)], self._get_frontdesk_field()),
|
|
}
|
|
if self.drink_offer:
|
|
data['drinks'] = self.env['frontdesk.drink'].search_read([('id', 'in', self.drink_ids.ids)], ['name'])
|
|
return data
|
|
|
|
def _get_planned_visitors(self):
|
|
""" Returns the planned visitors for quick sign in to the frontend. """
|
|
time_min = datetime.now() - timedelta(minutes=PLANNED_VISITOR_TIME)
|
|
time_max = datetime.now() + timedelta(minutes=PLANNED_VISITOR_TIME)
|
|
visitors = self.env['frontdesk.visitor'].sudo().search_read(
|
|
[('check_in', '>=', time_min), ('check_in', '<=', time_max), ('state', '=', 'planned'), ('station_id.id', '=', self.id)],
|
|
['name', 'company', 'message', 'host_ids'])
|
|
if visitors:
|
|
return [{
|
|
**visitor,
|
|
'host_ids': [{'id': host.id, 'name': host.name} for host in self.env['hr.employee'].browse(visitor['host_ids'])]
|
|
} for visitor in visitors]
|
|
return []
|
|
|
|
def _get_tmp_code(self):
|
|
self.ensure_one()
|
|
return tools.hmac(self.env(su=True), 'kiosk-mobile', (self.id, fields.Date.to_string(fields.Datetime.now())))
|