# -*- 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