This commit is contained in:
gsinghpal
2026-06-04 09:49:51 -04:00
parent 41ce3784d7
commit ba7c028c30
11 changed files with 3504 additions and 61 deletions

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import json
import hashlib
import logging
import secrets
@@ -796,12 +795,12 @@ class PortalSchedule(CustomerPortal):
if not event.exists() or partner not in event.partner_ids:
return {'success': False, 'error': 'Event not found or access denied.'}
tz = self._get_user_timezone()
# The slot datetime sent by the client is already UTC (the slot
# generator emits UTC); parse it directly — do NOT re-localize, which
# would double-shift the appointment by the user's UTC offset.
try:
start_naive = datetime.strptime(new_datetime, '%Y-%m-%d %H:%M:%S')
start_local = tz.localize(start_naive)
start_utc = start_local.astimezone(pytz.utc).replace(tzinfo=None)
except (ValueError, Exception) as e:
start_utc = datetime.strptime(new_datetime, '%Y-%m-%d %H:%M:%S')
except (ValueError, Exception):
return {'success': False, 'error': 'Invalid date/time format.'}
duration = float(new_duration) if new_duration else event.duration
@@ -883,12 +882,10 @@ class PortalSchedule(CustomerPortal):
if not slot_datetime:
return request.redirect('/schedule/manage/%s?error=Please+select+a+new+time+slot' % token)
tz = self._resolve_timezone(event.user_id)
# The slot datetime is already UTC (the slot generator emits UTC); parse
# directly — do NOT re-localize (that double-shifts by the tz offset).
try:
start_naive = datetime.strptime(slot_datetime, '%Y-%m-%d %H:%M:%S')
start_local = tz.localize(start_naive)
start_utc = start_local.astimezone(pytz.utc).replace(tzinfo=None)
start_utc = datetime.strptime(slot_datetime, '%Y-%m-%d %H:%M:%S')
except (ValueError, Exception):
return request.redirect('/schedule/manage/%s?error=Invalid+time+slot' % token)
@@ -1499,12 +1496,10 @@ class PortalSchedule(CustomerPortal):
'/schedule/%s?error=Name,+email,+and+time+slot+are+required' % slug
)
tz = self._resolve_timezone(user)
# The slot datetime is already UTC (the slot generator emits UTC); parse
# directly — do NOT re-localize (that double-shifts by the tz offset).
try:
start_dt_naive = datetime.strptime(slot_datetime, '%Y-%m-%d %H:%M:%S')
start_dt_local = tz.localize(start_dt_naive)
start_dt_utc = start_dt_local.astimezone(pytz.utc).replace(tzinfo=None)
start_dt_utc = datetime.strptime(slot_datetime, '%Y-%m-%d %H:%M:%S')
except (ValueError, Exception) as e:
_logger.error("Failed to parse slot datetime %s: %s", slot_datetime, e)
return request.redirect('/schedule/%s?error=Invalid+time+slot' % slug)
@@ -1512,17 +1507,22 @@ class PortalSchedule(CustomerPortal):
duration = float(slot_duration)
stop_dt_utc = start_dt_utc + timedelta(hours=duration)
# Find or create partner for the visitor
# Find or create a contact for the visitor. SECURITY: this is an
# unauthenticated endpoint and visitor_email is attacker-controlled, so
# never reuse/attach a partner that backs a login user (staff/internal),
# and never write onto an existing contact. Reuse only a plain non-user
# contact (avoids duplicates for genuine repeat visitors).
Partner = request.env['res.partner'].sudo()
partner = Partner.search([('email', '=ilike', visitor_email)], limit=1)
partner = Partner.search([
('email', '=ilike', visitor_email),
('user_ids', '=', False),
], limit=1)
if not partner:
partner = Partner.create({
'name': visitor_name,
'email': visitor_email,
'phone': visitor_phone,
'phone': visitor_phone or False,
})
elif visitor_phone and not partner.phone:
partner.phone = visitor_phone
address_parts = [p for p in [visitor_street, visitor_city, visitor_province, visitor_postal] if p]
location = ', '.join(address_parts)