diff --git a/appointment_event/__init__.py b/appointment_event/__init__.py index 0650744..91c5580 100644 --- a/appointment_event/__init__.py +++ b/appointment_event/__init__.py @@ -1 +1,2 @@ +from . import controllers from . import models diff --git a/appointment_event/__manifest__.py b/appointment_event/__manifest__.py index 29a60ef..662de62 100644 --- a/appointment_event/__manifest__.py +++ b/appointment_event/__manifest__.py @@ -24,9 +24,14 @@ "excludes": [], "external_dependencies": [], "depends": [ - "", + "website_appointment", + "website_event", + "event_to_calendar", + ], + "data": [ + "data/mail_activity_type.xml", + "views/appointment_type.xml", ], - "data": [], "css": [], "images": [], "js": [], diff --git a/appointment_event/controllers/__init__.py b/appointment_event/controllers/__init__.py new file mode 100644 index 0000000..669664c --- /dev/null +++ b/appointment_event/controllers/__init__.py @@ -0,0 +1 @@ +from . import appointment diff --git a/appointment_event/controllers/appointment.py b/appointment_event/controllers/appointment.py new file mode 100644 index 0000000..1f20a5e --- /dev/null +++ b/appointment_event/controllers/appointment.py @@ -0,0 +1,132 @@ +import pytz +from dateutil.relativedelta import relativedelta + + +from odoo import fields, _ +from odoo.http import request, route +from odoo.addons.appointment.controllers.appointment import AppointmentController +from odoo.addons.base.models.ir_qweb import keep_query + +from werkzeug.exceptions import NotFound + + +class AppointmentEventController(AppointmentController): + @route( + ["/appointment//submit"], + type="http", + auth="public", + website=True, + methods=["POST"], + ) + def appointment_form_submit( + self, + appointment_type_id, + datetime_str, + duration_str, + staff_user_id, + name, + phone, + email, + **kwargs + ): + appointment_type = self._fetch_and_check_private_appointment_types( + kwargs.get("filter_appointment_type_ids"), + kwargs.get("filter_staff_user_ids"), + kwargs.get("invite_token"), + current_appointment_type_id=int(appointment_type_id), + ) + if not appointment_type: + raise NotFound() + if not appointment_type.create_events: + return super().appointment_form_submit( + appointment_type_id, + datetime_str, + duration_str, + staff_user_id, + name, + phone, + email, + **kwargs + ) + # new process i + # if booking a slot should create an event + timezone = request.session.get("timezone") or appointment_type.appointment_tz + tz_session = pytz.timezone(timezone) + date_start = ( + tz_session.localize(fields.Datetime.from_string(datetime_str)) + .astimezone(pytz.utc) + .replace(tzinfo=None) + ) + duration = float(duration_str) + date_end = date_start + relativedelta(hours=duration) + invite_token = kwargs.get("invite_token") + + # check availability of the selected user again (in case someone else booked while the client was entering the form) + staff_user = request.env["res.users"].sudo().browse(int(staff_user_id)).exists() + if staff_user not in appointment_type.sudo().staff_user_ids: + raise NotFound() + if staff_user and not staff_user.partner_id.calendar_verify_availability( + date_start, date_end + ): + return request.redirect( + "/appointment/%s?%s" + % (appointment_type.id, keep_query("*", state="failed-staff-user")) + ) + + Partner = self._get_customer_partner() or request.env[ + "res.partner" + ].sudo().search([("email", "=like", email)], limit=1) + if Partner: + if not Partner.calendar_verify_availability(date_start, date_end): + return request.redirect( + "/appointment/%s?%s" + % (appointment_type.id, keep_query("*", state="failed-partner")) + ) + if not Partner.mobile: + Partner.write({"mobile": phone}) + if not Partner.email: + Partner.write({"email": email}) + else: + Partner = Partner.create( + { + "name": name, + "mobile": Partner._phone_format( + phone, country=self._get_customer_country() + ), + "email": email, + "lang": request.lang.code, + } + ) + + # partner_inputs dictionary structures all answer inputs received on the appointment submission: key is question id, value + # is answer id (as string) for choice questions, text input for text questions, array of ids for multiple choice questions. + partner_inputs = {} + + event_type = appointment_type.event_type_id + event = ( + request.env["event.event"] + .sudo() + .create( + { + "name": event_type.name, + "date_begin": date_start, + "date_end": date_end, + "date_tz": timezone, + "event_type_id": event_type.id, + "user_id": int(staff_user_id), + "address_id": appointment_type.location_id.id, + } + ) + ) + event = event.sudo() + event.sudo().with_user(int(staff_user_id)).add_to_agenda() + event.activity_schedule( + act_type_xmlid="appointment_event.mail_activity_type_validate_date", + summary=_("New booking to check"), + note=_( + "Check date availability and set automatic validation for next bookers." + ), + user_id=staff_user_id, + ) + event.write({"website_published": True}) + return request.redirect(event.website_url) diff --git a/appointment_event/data/mail_activity_type.xml b/appointment_event/data/mail_activity_type.xml new file mode 100644 index 0000000..21a9492 --- /dev/null +++ b/appointment_event/data/mail_activity_type.xml @@ -0,0 +1,13 @@ + + + + + Check date + fa-calendar-check-o + 1 + warning + 0 + event.event + + + \ No newline at end of file diff --git a/appointment_event/models/__init__.py b/appointment_event/models/__init__.py index e69de29..1340b2f 100644 --- a/appointment_event/models/__init__.py +++ b/appointment_event/models/__init__.py @@ -0,0 +1 @@ +from . import appointment_type diff --git a/appointment_event/models/appointment_type.py b/appointment_event/models/appointment_type.py new file mode 100644 index 0000000..d9f2563 --- /dev/null +++ b/appointment_event/models/appointment_type.py @@ -0,0 +1,13 @@ +from odoo import models, fields, api + + +class AppointmentType(models.Model): + _inherit = "appointment.type" + + create_events = fields.Boolean("Create events", defualt=False) + event_type_id = fields.Many2one("event.type", string="Event type") + + @api.onchange("create_events") + def onchange_create_events(self): + if not self.create_events and self.event_type_id: + self.event_type_id = False diff --git a/appointment_event/static/description/icon.png b/appointment_event/static/description/icon.png new file mode 100644 index 0000000..663109e Binary files /dev/null and b/appointment_event/static/description/icon.png differ diff --git a/appointment_event/static/description/icon.svg b/appointment_event/static/description/icon.svg new file mode 100644 index 0000000..fedafcd --- /dev/null +++ b/appointment_event/static/description/icon.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/appointment_event/views/appointment_type.xml b/appointment_event/views/appointment_type.xml new file mode 100644 index 0000000..3630de8 --- /dev/null +++ b/appointment_event/views/appointment_type.xml @@ -0,0 +1,17 @@ + + + + + appointment.type.view.form.inherit + appointment.type + + + + + + + + + + \ No newline at end of file