Odoo18-Base/extra-addons/website_appointment/controllers/appointment.py

189 lines
9.4 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import http
from odoo.http import request
from odoo.addons.base.models.ir_qweb import keep_query
from odoo.addons.appointment.controllers.appointment import AppointmentController
from odoo.osv import expression
class WebsiteAppointment(AppointmentController):
# ------------------------------------------------------------
# APPOINTMENT INDEX PAGE
# ------------------------------------------------------------
@http.route()
def appointment_type_index(self, page=1, **kwargs):
"""
Display the appointments to choose (the display depends of a custom option called 'Card Design')
:param page: the page number displayed when the appointments are organized by cards
A param filter_appointment_type_ids can be passed to display a define selection of appointments types.
This param is propagated through templates to allow people to go back with the initial appointment
types filter selection
"""
kwargs['domain'] = self._appointments_base_domain(
filter_appointment_type_ids=kwargs.get('filter_appointment_type_ids'),
search=kwargs.get('search'),
invite_token=kwargs.get('invite_token'),
additional_domain=self._appointment_website_domain(),
filter_countries=True,
)
available_appointment_types = self._fetch_and_check_private_appointment_types(
kwargs.get('filter_appointment_type_ids'),
kwargs.get('filter_staff_user_ids'),
kwargs.get('filter_resource_ids'),
kwargs.get('invite_token'),
domain=kwargs['domain'],
)
if len(available_appointment_types) == 1 and not kwargs.get('search'):
# If there is only one appointment type available in the selection, skip the appointment type selection view
return request.redirect('/appointment/%s?%s' % (available_appointment_types.id, keep_query('*')))
cards_layout = request.website.viewref('website_appointment.opt_appointments_list_cards').active
if cards_layout:
return request.render(
'website_appointment.appointments_cards_layout',
self._prepare_appointments_cards_data(
page, available_appointment_types,
**kwargs
)
)
else:
return request.render(
'appointment.appointments_list_layout',
self._prepare_appointments_list_data(
available_appointment_types,
**kwargs
)
)
# ----------------------------------------------------------------
# APPOINTMENT TYPE PAGE VIEW : WITH NEW OPERATOR SELECTION VIEW
# ----------------------------------------------------------------
def _get_appointment_type_resource_selection_view(self, appointment_type, page_values):
"""
Renders the appointment_select_operator template. This displays a card view of available staff users to
select from for appointment_type, containing their picture, job description and website_description.
:param appointment_type: the appointment_type that we want to access.
:param page_values: dict of precomputed values in the appointment_page route.
"""
return request.render("website_appointment.appointment_select_operator", {
'appointment_type': appointment_type,
'available_appointments': page_values['available_appointments'],
'main_object': appointment_type,
'users_possible': page_values['users_possible'],
'resources_possible': page_values['resources_possible'],
})
def _get_appointment_type_page_view(self, appointment_type, page_values, state=False, **kwargs):
"""
Override: when website_appointment is installed, instead of the default appointment type page, renders the
operator selection template, if the condition below is met.
"""
# If the user skips the resource selection to see all availabilities, make sure we do not show the selection.
# As the operator view is mainly user cards, we only show it if avatars are 'on'. Also, it makes no sense in
# random appointment types since it is a selection screen. Moreover, the selection should not have already
# been made before in order to avoid loops. Finally, in order to choose, one needs at least 2 possible users/resources.
skip_resource_selection = kwargs.get('skip_resource_selection') or \
not appointment_type.active or \
appointment_type.assign_method != 'resource_time' or \
appointment_type.avatars_display != 'show'
operator_selection = not skip_resource_selection and \
appointment_type.schedule_based_on == 'users' and \
not page_values['user_selected'] and \
len(page_values['users_possible']) > 1
resource_selection = not skip_resource_selection and \
appointment_type.schedule_based_on == 'resources' and \
not page_values['resource_selected'] and \
len(page_values['resources_possible']) > 1
if operator_selection or resource_selection:
return self._get_appointment_type_resource_selection_view(appointment_type, page_values)
return super()._get_appointment_type_page_view(appointment_type, page_values, state, **kwargs)
def _prepare_appointment_type_page_values(self, appointment_type, staff_user_id=False, resource_selected_id=False, skip_resource_selection=False, **kwargs):
"""
Override: Take into account the operator selection flow. When skipping the selection,
no {user,resource}_selected or user_default should be set. The display is also properly managed according to this new flow.
:param skip_resource_selection: If true, skip the selection, and instead see all availabilities. No user should be selected.
"""
values = super()._prepare_appointment_type_page_values(appointment_type, staff_user_id, resource_selected_id, **kwargs)
values['skip_resource_selection'] = skip_resource_selection
if skip_resource_selection:
values['user_selected'] = values['user_default'] = request.env['res.users']
values['resource_selected'] = request.env['appointment.resource']
else:
resource_or_user_selected = values['user_selected'] if appointment_type.schedule_based_on == 'users' else values['resource_selected']
values['hide_select_dropdown'] = values['hide_select_dropdown'] or (
appointment_type.avatars_display == 'show' and resource_or_user_selected and appointment_type.assign_method != 'time_resource')
return values
# Tools / Data preparation
# ------------------------------------------------------------
def _prepare_appointments_cards_data(self, page, appointment_types, **kwargs):
"""
Compute specific data for the cards layout like the search bar and the pager.
"""
APPOINTMENTS_PER_PAGE = 12
website = request.website
appointment_count = len(appointment_types)
pager = website.pager(
url='/appointment',
url_args=kwargs,
total=appointment_count,
page=page,
step=APPOINTMENTS_PER_PAGE,
scope=5,
)
appointment_types = appointment_types.sorted('is_published', reverse=True)[pager['offset']:pager['offset'] + APPOINTMENTS_PER_PAGE]
return {
'appointment_types': appointment_types,
'current_search': kwargs.get('search'),
'pager': pager,
'filter_appointment_type_ids': kwargs.get('filter_appointment_type_ids'),
'filter_staff_user_ids': kwargs.get('filter_staff_user_ids'),
'invite_token': kwargs.get('invite_token'),
'search_count': appointment_count,
}
def _get_allowed_companies(self, organizer):
""" Check if the current website can be used to determine the company
and fallback on the companies of the organizer if not """
companies = super()._get_allowed_companies(organizer)
website_company = request.website.company_id if request.website else request.env['res.company']
return website_company if website_company in companies else companies
def _get_customer_partner(self):
partner = super()._get_customer_partner()
if not partner:
partner = request.env['website.visitor']._get_visitor_from_request().partner_id
return partner
@staticmethod
def _get_customer_country():
"""
Find the country from the geoip lib or fallback on the user or the visitor
"""
country = AppointmentController._get_customer_country()
if not country:
visitor = request.env['website.visitor']._get_visitor_from_request()
country = visitor.country_id
return country
@classmethod
def _appointments_base_domain(cls, filter_appointment_type_ids, search=False, invite_token=False, additional_domain=None, filter_countries=False):
domain = super()._appointments_base_domain(filter_appointment_type_ids, search, invite_token, additional_domain, filter_countries)
domain = expression.AND([domain, ['|', ('website_id', '=', request.website.id), ('website_id', '=', False)]])
return domain