1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/appointment/controllers/calendar.py
2024-12-10 09:04:09 +07:00

184 lines
8.7 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import pytz
from babel.dates import format_datetime, format_date
from datetime import datetime, timedelta
from werkzeug.exceptions import Forbidden, BadRequest
from werkzeug.urls import url_encode
from odoo import fields, _
from odoo.addons.base.models.ir_qweb import keep_query
from odoo.addons.calendar.controllers.main import CalendarController
from odoo.http import request, route
from odoo.tools import is_html_empty
from odoo.tools.misc import get_lang
class AppointmentCalendarController(CalendarController):
# ------------------------------------------------------------
# CALENDAR EVENT VIEW
# ------------------------------------------------------------
@route()
def view_meeting(self, token, id):
"""Redirect the internal logged in user to the form view of calendar.event, and redirect
regular attendees to the website page of the calendar.event for appointments"""
super(AppointmentCalendarController, self).view_meeting(token, id)
attendee = request.env['calendar.attendee'].sudo().search([
('access_token', '=', token),
('event_id', '=', int(id))])
if not attendee:
return request.render("appointment.appointment_invalid", {})
# If user is internal and logged, redirect to form view of event
if request.env.user._is_internal():
url_params = url_encode({
'id': id,
'view_type': 'form',
'model': attendee.event_id._name,
})
return request.redirect(f'/web?db={request.env.cr.dbname}#{url_params}')
request.session['timezone'] = attendee.partner_id.tz
if not attendee.event_id.access_token:
attendee.event_id._generate_access_token()
return request.redirect(f'/calendar/view/{attendee.event_id.access_token}?partner_id={attendee.partner_id.id}')
@route(['/calendar/view/<string:access_token>'], type='http', auth="public", website=True)
def appointment_view(self, access_token, partner_id, state=False, **kwargs):
"""
Render the validation of an appointment and display a summary of it
:param access_token: the access_token of the event linked to the appointment
:param partner_id: id of the partner who booked the appointment
:param state: allow to display an info message, possible values:
- new: Info message displayed when the appointment has been correctly created
- no-cancel: Info message displayed when an appointment can no longer be canceled
"""
partner_id = int(partner_id)
event = request.env['calendar.event'].sudo().search([('access_token', '=', access_token)], limit=1)
if not event:
return request.not_found()
timezone = request.session.get('timezone')
if not timezone:
timezone = request.env.context.get('tz') or event.appointment_type_id.appointment_tz or event.partner_ids and event.partner_ids[0].tz or event.user_id.tz or 'UTC'
request.session['timezone'] = timezone
tz_session = pytz.timezone(timezone)
date_start_suffix = ""
format_func = format_datetime
if not event.allday:
url_date_start = fields.Datetime.from_string(event.start).strftime('%Y%m%dT%H%M%SZ')
url_date_stop = fields.Datetime.from_string(event.stop).strftime('%Y%m%dT%H%M%SZ')
date_start = fields.Datetime.from_string(event.start).replace(tzinfo=pytz.utc).astimezone(tz_session)
else:
url_date_start = url_date_stop = fields.Date.from_string(event.start_date).strftime('%Y%m%d')
date_start = fields.Date.from_string(event.start_date)
format_func = format_date
date_start_suffix = _(', All Day')
locale = get_lang(request.env).code
day_name = format_func(date_start, 'EEE', locale=locale)
date_start = f'{day_name} {format_func(date_start, locale=locale)}{date_start_suffix}'
params = {
'action': 'TEMPLATE',
'text': event._get_customer_summary(),
'dates': f'{url_date_start}/{url_date_stop}',
'details': event._get_customer_description(),
}
if event.location:
params.update(location=event.location.replace('\n', ' '))
encoded_params = url_encode(params)
google_url = 'https://www.google.com/calendar/render?' + encoded_params
return request.render("appointment.appointment_validated", {
'event': event,
'datetime_start': date_start,
'google_url': google_url,
'state': state,
'partner_id': partner_id,
'attendee_status': event.attendee_ids.filtered(lambda a: a.partner_id.id == partner_id).state,
'is_html_empty': is_html_empty,
})
@route(['/calendar/<string:access_token>/add_attendees_from_emails'], type="json", auth="public", website=True)
def appointment_add_attendee(self, access_token, emails_str):
"""
Add the attendee at the time of the validation of an appointment page
:param access_token: access_token of the event linked to the appointment
:param emails_str: guest emails in the block of text
"""
event_sudo = request.env['calendar.event']
event_sudo = event_sudo.sudo().search([('access_token', '=', access_token)], limit=1)
if not event_sudo:
return request.not_found()
if not event_sudo.appointment_type_id.allow_guests:
raise BadRequest()
if not emails_str:
return []
guests = event_sudo.sudo()._find_or_create_partners(emails_str)
if guests:
event_sudo.write({
'partner_ids': [(4, pid.id, False) for pid in guests]
})
@route(['/calendar/cancel/<string:access_token>',
'/calendar/<string:access_token>/cancel',
], type='http', auth="public", website=True)
def appointment_cancel(self, access_token, partner_id, **kwargs):
"""
Route to cancel an appointment event, this route is linked to a button in the validation page
"""
event = request.env['calendar.event'].sudo().search([('access_token', '=', access_token)], limit=1)
appointment_type = event.appointment_type_id
appointment_invite = event.appointment_invite_id
if not event:
return request.not_found()
if fields.Datetime.from_string(event.allday and event.start_date or event.start) < datetime.now() + timedelta(hours=event.appointment_type_id.min_cancellation_hours):
return request.redirect(f'/calendar/view/{access_token}?state=no-cancel&partner_id={partner_id}')
event.with_context(mail_notify_author=True).sudo().action_cancel_meeting([int(partner_id)])
if appointment_invite:
redirect_url = appointment_invite.redirect_url + '&state=cancel'
else:
reset_params = {'state': 'cancel'}
if appointment_type.schedule_based_on == 'resources':
reset_params.update({
'resource_selected_id': '',
'available_resource_ids': '',
})
redirect_url = f'/appointment/{appointment_type.id}?{keep_query("*", **reset_params)}'
return request.redirect(redirect_url)
@route(['/calendar/ics/<string:access_token>.ics'], type='http', auth="public", website=True)
def appointment_get_ics_file(self, access_token, **kwargs):
"""
Route to add the appointment event in a iCal/Outlook calendar
"""
event = request.env['calendar.event'].sudo().search([('access_token', '=', access_token)], limit=1)
if not event or not event.attendee_ids:
return request.not_found()
files = event._get_ics_file()
content = files[event.id]
return request.make_response(content, [
('Content-Type', 'application/octet-stream'),
('Content-Length', len(content)),
('Content-Disposition', 'attachment; filename=Appoinment.ics')
])
@route('/calendar/videocall/<string:access_token>', type='http', auth='public')
def calendar_videocall(self, access_token):
if not access_token:
raise Forbidden()
event = request.env['calendar.event'].sudo().search([('access_token', '=', access_token)], limit=1)
if not event or not event.videocall_location:
return request.not_found()
if event.videocall_source == 'discuss':
return self.calendar_join_videocall(access_token)
# custom / google_meet
return request.redirect(event.videocall_location, local=False)