RemiFr82
5 months ago
10 changed files with 278 additions and 2 deletions
-
1appointment_event/__init__.py
-
9appointment_event/__manifest__.py
-
1appointment_event/controllers/__init__.py
-
132appointment_event/controllers/appointment.py
-
13appointment_event/data/mail_activity_type.xml
-
1appointment_event/models/__init__.py
-
13appointment_event/models/appointment_type.py
-
BINappointment_event/static/description/icon.png
-
93appointment_event/static/description/icon.svg
-
17appointment_event/views/appointment_type.xml
@ -1 +1,2 @@ |
|||
from . import controllers |
|||
from . import models |
@ -0,0 +1 @@ |
|||
from . import appointment |
@ -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/<int:appointment_type_id>/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) |
@ -0,0 +1,13 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
|
|||
<record id="mail_activity_type_check_date" model="mail.activity.type"> |
|||
<field name="name">Check date</field> |
|||
<field name="icon">fa-calendar-check-o</field> |
|||
<field name="delay_count">1</field> |
|||
<field name="decoration_type">warning</field> |
|||
<field name="sequence">0</field> |
|||
<field name="res_model">event.event</field> |
|||
</record> |
|||
|
|||
</odoo> |
@ -0,0 +1 @@ |
|||
from . import appointment_type |
@ -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 |
After Width: 1539 | Height: 1771 | Size: 182 KiB |
@ -0,0 +1,93 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- Generator: Adobe Illustrator 28.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> |
|||
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
|||
viewBox="0 0 2000 2000" style="enable-background:new 0 0 2000 2000;" xml:space="preserve"> |
|||
<style type="text/css"> |
|||
.st0{fill:url(#SVGID_1_);} |
|||
.st1{opacity:0.288;fill:#5BB4B9;enable-background:new ;} |
|||
.st2{fill:#714B67;stroke:#FFFFFF;stroke-width:2.5312;stroke-linejoin:round;stroke-miterlimit:84.375;} |
|||
.st3{fill:#00A09D;stroke:#FFFFFF;stroke-width:2.5312;stroke-linejoin:round;stroke-miterlimit:84.375;} |
|||
</style> |
|||
<g id="Calque_1_00000161600930838337179250000013767234669113973950_"> |
|||
<g> |
|||
<g> |
|||
<g transform="translate(13 10)"> |
|||
|
|||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-856.1276" y1="-992.6924" x2="-877.9248" y2="-975.3946" gradientTransform="matrix(43.9892 0 0 50.6251 39146.6719 50789.9492)"> |
|||
<stop offset="0" style="stop-color:#81D9DD"/> |
|||
<stop offset="1" style="stop-color:#419CA1"/> |
|||
</linearGradient> |
|||
<path class="st0" d="M1747.1,540.7l-749.9-433c-6-3.4-13.3-3.4-19.3,0L228,540.7c-6,3.4-9.6,9.8-9.6,16.7v865.9 |
|||
c0,6.9,3.7,13.3,9.6,16.7l749.9,433c3,1.7,6.3,2.6,9.6,2.6s6.6-0.8,9.6-2.6l749.9-433c6-3.4,9.6-9.8,9.6-16.7v-866 |
|||
C1756.7,550.5,1753,544.1,1747.1,540.7z M256.8,1409V590.7l708.6,409.1l2.8,4.9V1823l-708.7-409.2L256.8,1409z M990.3,148.2 |
|||
L1699,557.3L990.3,966.5h-5.6L276,557.3l708.7-409.1L990.3,148.2L990.3,148.2z M1006.8,1004.7l2.8-4.8l708.6-409.1V1409 |
|||
l-2.8,4.8l-708.6,409.1L1006.8,1004.7L1006.8,1004.7z"/> |
|||
<rect x="970" y="870.2" class="st1" width="35" height="48.1"/> |
|||
<rect x="970" y="485.1" class="st1" width="35" height="48.1"/> |
|||
<rect x="970" y="774" class="st1" width="35" height="48.1"/> |
|||
<rect x="970" y="581.4" class="st1" width="35" height="48.1"/> |
|||
<rect x="970" y="196.3" class="st1" width="35" height="48.1"/> |
|||
<rect x="970" y="292.6" class="st1" width="35" height="48.1"/> |
|||
<rect x="970" y="388.9" class="st1" width="35" height="48.1"/> |
|||
<rect x="970" y="677.7" class="st1" width="35" height="48.1"/> |
|||
|
|||
<rect x="703.4" y="1109.5" transform="matrix(0.8661 -0.4999 0.4999 0.8661 -465.9773 514.5881)" class="st1" width="48.1" height="35"/> |
|||
|
|||
<rect x="620.2" y="1157.7" transform="matrix(0.8661 -0.4999 0.4999 0.8661 -501.1965 479.4498)" class="st1" width="48.1" height="35"/> |
|||
|
|||
<rect x="536.7" y="1205.8" transform="matrix(0.8659 -0.5002 0.5002 0.8659 -536.6949 444.5481)" class="st1" width="48.2" height="35"/> |
|||
|
|||
<rect x="870.1" y="1013.3" transform="matrix(0.8662 -0.4996 0.4996 0.8662 -395.3831 584.6333)" class="st1" width="48.1" height="35"/> |
|||
|
|||
<rect x="786.8" y="1061.3" transform="matrix(0.8659 -0.5002 0.5002 0.8659 -430.897 550.2667)" class="st1" width="48.2" height="35"/> |
|||
|
|||
<rect x="286.6" y="1350.2" transform="matrix(0.8659 -0.5002 0.5002 0.8659 -642.4768 338.7787)" class="st1" width="48.1" height="35"/> |
|||
|
|||
<rect x="453.3" y="1253.8" transform="matrix(0.866 -0.5 0.5 0.866 -571.7071 408.9949)" class="st1" width="48.1" height="35"/> |
|||
|
|||
<rect x="370.1" y="1302.1" transform="matrix(0.8661 -0.4999 0.4999 0.8661 -606.8809 373.7628)" class="st1" width="48.1" height="35"/> |
|||
|
|||
<rect x="1307.6" y="1145.5" transform="matrix(0.5 -0.866 0.866 0.5 -350.2617 1732.3198)" class="st1" width="35" height="48.1"/> |
|||
|
|||
<rect x="1141" y="1049.1" transform="matrix(0.5001 -0.866 0.866 0.5001 -350.2007 1539.6794)" class="st1" width="35" height="48.1"/> |
|||
|
|||
<rect x="1224.4" y="1097.2" transform="matrix(0.4996 -0.8663 0.8663 0.4996 -349.8225 1636.8931)" class="st1" width="35" height="48.1"/> |
|||
|
|||
<rect x="1638.3" y="1342.1" transform="matrix(0.5004 -0.8658 0.8658 0.5004 -349.8787 2112.7935)" class="st1" width="35" height="35"/> |
|||
|
|||
<rect x="1057.5" y="1001" transform="matrix(0.5003 -0.8658 0.8658 0.5003 -350.4254 1443.0403)" class="st1" width="35" height="48.2"/> |
|||
|
|||
<rect x="1474.3" y="1241.7" transform="matrix(0.5003 -0.8658 0.8658 0.5003 -350.5846 1924.1418)" class="st1" width="35" height="48.2"/> |
|||
|
|||
<rect x="1557.8" y="1289.8" transform="matrix(0.5 -0.866 0.866 0.5 -350.1897 2021.1473)" class="st1" width="35" height="48.1"/> |
|||
|
|||
<rect x="1391.1" y="1193.6" transform="matrix(0.4997 -0.8662 0.8662 0.4997 -349.9747 1829.327)" class="st1" width="35" height="48.1"/> |
|||
</g> |
|||
</g> |
|||
</g> |
|||
</g> |
|||
<g id="odoo"> |
|||
<path class="st2" d="M993.2,439.3c-303.8,0-550,246.2-550,550s246.2,550,550,550s550-246.2,550-550S1297,439.3,993.2,439.3z |
|||
M993.2,659.3c182.3,0,330,147.7,330,330s-147.7,330-330,330s-330-147.7-330-330S811,659.3,993.2,659.3z"/> |
|||
</g> |
|||
<g id="kayak"> |
|||
<path class="st3" d="M609.8,957.9c-0.4,14.4,11.8,59.8,23.4,86.8c33.6,75.8,69.2,108.4,220.1,201.6 |
|||
c98.9,60.7,128.1,86.2,151.7,130.9c9.2,17,18.8,40,22,51.1l6,20.3v-44.8c0.8-103.6-28.8-138.3-193.3-229.7 |
|||
c-39.6-21.8-88-51-107.3-64.7c-55.2-39.6-95.4-88.1-115.8-141c0,0-6.7-16.2-6.7-16.2L609.8,957.9z"/> |
|||
<path class="st3" d="M1179.5,540c-5.6,5.6-17.4,11.1-35.1,16.4c-15.4,4.6-35.2,9-59.3,13.2c-151.7,26.3-191.7,46.6-191.7,97.3 |
|||
c0,13.3,6,27.7,29.6,71.8c16.4,30.3,32.4,65.1,36,77.3c10.4,37.7,12,87.3,4.8,149.8c-3.6,31.4-6.8,63.6-7.6,72.1l-0.8,14.8 |
|||
l-56.8-27.7c-81.6-40.3-115.7-61.4-148.1-91.4c-54.8-50.7-76-114.7-64.8-197.5c8.8-68.1,35.2-132.8,70.8-173.1l18.4-21.1l-16,11.8 |
|||
c-68,49.9-109.7,147.2-109.7,257.4c0,84.7,28.4,150.9,88,206c32.4,30,61.6,47.3,140.9,83.6c38,17.8,79.6,38.8,92.8,47 |
|||
c44.8,29.2,84.4,82.5,98.1,132.4l7.2,26.3l4.8-58.4c2.8-32.5,6.8-67.3,8.8-77.3c2-10.4,7.2-40.3,11.2-66.6 |
|||
c10.4-65.1,30.8-112.1,58.4-134.3c25.6-21.1,38.4-14.8,63.6,31.4c22,40.7,38,54.7,74.8,65.1c4.4,1.5,8,13.3,13.2,46.6 |
|||
c6.4,39.9,6.8,50.7,2.4,96.5c-6,61.8-3.6,120.6,6,146.1c10.4,28.1,27.6,51,56.8,74.3l26,21.5l-2.4-38.8 |
|||
c-6.4-85.1-18-150.2-38.4-208.2c-6.8-18.5-16.4-61-22.4-94.7c-15.6-92.1-51.6-255.2-67.6-306.3c-22.4-71.8-53.6-138.2-90.4-192.6 |
|||
c-4.9-7.1-17.3-25.2-17.3-25.2s5.9-7.4,9.3-12.6c3.9-4.8,9.7-15.2,12.5-23.7c2.7-8.3,1.7-18,1.7-18S1183.5,535.9,1179.5,540z |
|||
M1177.1,619.2c36.4,59.2,61.2,128.3,86,240.8c19.2,88,31.2,150.2,29.2,152.4c-4.4,3.7-19.2-25.9-29.2-58.1 |
|||
c-12.8-41.8-21.2-58.1-37.2-72.9c-18-17-37.6-23.7-88-30.7c-58.8-8.1-74-15.2-113.7-52.5c-42.4-39.9-59.6-64.7-62-88.4 |
|||
c-3.6-33.7,9.6-43.6,81.2-62.5c48.4-12.9,71.6-23.3,96.1-42.5c8.8-7,17.8-13.8,17.8-13.8S1167.5,604,1177.1,619.2z"/> |
|||
<path class="st3" d="M1130.6,658.7c0,6.7-4.7,23.2-9.1,33.5c-6.4,14.8-8,27.4-8,61.4c0,34-1.6,45.1-6.8,53.6 |
|||
c-4,5.9-13.2,14.4-13.2,14.4s22.4,3.7,44.4-6.7c52-24.4,55.6-74.3,9.6-139.8c-11-15.6-16.9-20.8-16.9-20.8 |
|||
S1130.6,654.5,1130.6,658.7z"/> |
|||
</g> |
|||
</svg> |
@ -0,0 +1,17 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
|
|||
<record id="appointment_type_view_form_inherit_appointment" model="ir.ui.view"> |
|||
<field name="name">appointment.type.view.form.inherit</field> |
|||
<field name="model">appointment.type</field> |
|||
<field name="inherit_id" ref="appointment.appointment_type_view_form" /> |
|||
<field name="arch" type="xml"> |
|||
<group name="right_details" position="inside"> |
|||
<field name="create_events" /> |
|||
<field name="event_type_id" options="{'no_create':1}" |
|||
attrs="{'invisible':[('create_events','=',False)],'required':[('create_events','=',True)]}" /> |
|||
</group> |
|||
</field> |
|||
</record> |
|||
|
|||
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue