1259 lines
38 KiB
Python
1259 lines
38 KiB
Python
import os
|
|
from base64 import b32encode
|
|
|
|
import frappe
|
|
import pyotp
|
|
from frappe import _
|
|
from frappe.auth import LoginAttemptTracker
|
|
from frappe.core.doctype.sms_settings.sms_settings import send_sms
|
|
from frappe.rate_limiter import rate_limit
|
|
from frappe.translate import get_all_translations
|
|
from frappe.utils import (
|
|
days_diff,
|
|
format_date,
|
|
format_time,
|
|
get_datetime,
|
|
get_datetime_in_timezone,
|
|
get_system_timezone,
|
|
now_datetime,
|
|
today,
|
|
validate_email_address,
|
|
)
|
|
|
|
from event_manager.payments import (
|
|
get_payment_gateways_for_event,
|
|
get_payment_link_for_booking,
|
|
get_payment_link_for_sponsorship,
|
|
)
|
|
from event_manager.utils import is_app_installed
|
|
|
|
OFFLINE_PAYMENT_METHOD = "Offline"
|
|
|
|
|
|
@frappe.whitelist(allow_guest=True) # nosemgrep: frappe-semgrep-rules.rules.security.guest-whitelisted-method
|
|
@rate_limit(key="identifier", limit=5, seconds=3600)
|
|
def send_guest_booking_otp(event: int, identifier: str) -> dict:
|
|
event_doc = frappe.get_cached_doc("Pohodex Event Manager Event", event)
|
|
|
|
if not event_doc.allow_guest_booking:
|
|
frappe.throw(_("Guest booking is not enabled for this event"))
|
|
|
|
if event_doc.guest_verification_method == "None":
|
|
frappe.throw(_("OTP verification is not enabled for this event"))
|
|
|
|
channel = "phone" if event_doc.guest_verification_method == "Phone OTP" else "email"
|
|
|
|
identifier = identifier.strip()
|
|
if not identifier:
|
|
frappe.throw(_("Email or phone is required"))
|
|
|
|
if channel == "email":
|
|
identifier = identifier.lower()
|
|
validate_email_address(identifier, throw=True)
|
|
|
|
otp_secret = b32encode(os.urandom(10)).decode("utf-8")
|
|
otp_code = pyotp.HOTP(otp_secret).at(0)
|
|
cache_key = f"guest_booking_otp:{channel}:{identifier}"
|
|
|
|
if frappe.in_test:
|
|
frappe.cache.set_value(cache_key, otp_secret, expires_in_sec=600)
|
|
return {"otp": otp_code}
|
|
|
|
try:
|
|
if channel == "email":
|
|
frappe.sendmail(
|
|
recipients=[identifier],
|
|
subject=_("Your Booking Verification Code"),
|
|
message=_(
|
|
"Your verification code is: <b>{0}</b><br><br>This code expires in 10 minutes."
|
|
).format(otp_code),
|
|
now=True,
|
|
)
|
|
else:
|
|
send_sms(
|
|
receiver_list=[identifier],
|
|
msg=_("Your booking verification code is: {0}. It expires in 10 minutes.").format(otp_code),
|
|
)
|
|
except Exception:
|
|
frappe.throw(_("Failed to send verification code. Please try again."))
|
|
|
|
frappe.cache.set_value(cache_key, otp_secret, expires_in_sec=600)
|
|
|
|
|
|
def verify_guest_otp(channel: str, identifier: str, otp: str):
|
|
cache_key = f"guest_booking_otp:{channel}:{identifier}"
|
|
tracker = LoginAttemptTracker(
|
|
key=f"guest_otp:{channel}:{identifier}",
|
|
max_consecutive_login_attempts=5,
|
|
lock_interval=600,
|
|
)
|
|
|
|
if not tracker.is_user_allowed():
|
|
frappe.throw(_("Too many failed attempts. Please try again later."))
|
|
|
|
otp_secret = frappe.cache.get_value(cache_key)
|
|
if not otp_secret:
|
|
frappe.throw(_("Verification code expired. Please request a new one."))
|
|
|
|
if not pyotp.HOTP(otp_secret).verify(otp.strip(), 0):
|
|
tracker.add_failure_attempt()
|
|
frappe.throw(_("Invalid verification code"))
|
|
|
|
frappe.cache.delete_value(cache_key)
|
|
tracker.add_success_attempt()
|
|
|
|
|
|
def get_or_create_guest_user(email: str, full_name: str) -> str:
|
|
email = email.lower().strip()
|
|
|
|
validate_email_address(email, throw=True)
|
|
if frappe.db.exists("User", email):
|
|
return email
|
|
|
|
name_parts = full_name.strip().split(" ", 1)
|
|
first_name = name_parts[0] if name_parts else "Guest"
|
|
last_name = name_parts[1] if len(name_parts) > 1 else ""
|
|
|
|
user = frappe.get_doc(
|
|
{
|
|
"doctype": "User",
|
|
"email": email,
|
|
"first_name": first_name,
|
|
"last_name": last_name,
|
|
"enabled": 1,
|
|
"user_type": "Website User",
|
|
"send_welcome_email": 0,
|
|
}
|
|
)
|
|
user.insert(ignore_permissions=True)
|
|
|
|
return email
|
|
|
|
|
|
@frappe.whitelist()
|
|
def get_event_payment_gateways(event: str) -> list[str]:
|
|
return get_payment_gateways_for_event(event)
|
|
|
|
|
|
def are_registrations_closed(event_doc) -> bool:
|
|
if not event_doc.registrations_close_at:
|
|
return False
|
|
|
|
event_tz = event_doc.time_zone or get_system_timezone()
|
|
now_in_event_tz = get_datetime_in_timezone(event_tz).replace(tzinfo=None)
|
|
|
|
return now_in_event_tz > get_datetime(event_doc.registrations_close_at)
|
|
|
|
|
|
def is_ticket_transfer_allowed(event_id: str | int) -> bool:
|
|
try:
|
|
event = frappe.get_cached_doc("Pohodex Event Manager Event", event_id)
|
|
settings = frappe.get_single("Pohodex Event Manager Settings")
|
|
transfer_cutoff_days = settings.get("allow_transfer_ticket_before_event_start_days", 7)
|
|
|
|
if not event.start_date:
|
|
return False
|
|
|
|
return days_diff(event.start_date, today()) >= transfer_cutoff_days
|
|
except Exception as e:
|
|
frappe.log_error(f"Error checking ticket transfer eligibility: {e!s}")
|
|
return False
|
|
|
|
|
|
def is_add_on_change_allowed(event_id: str | int) -> bool:
|
|
try:
|
|
event = frappe.get_cached_doc("Pohodex Event Manager Event", event_id)
|
|
settings = frappe.get_cached_doc("Pohodex Event Manager Settings")
|
|
add_on_change_cutoff_days = settings.get("allow_add_ons_change_before_event_start_days", 7)
|
|
|
|
if not event.start_date:
|
|
return False
|
|
|
|
return days_diff(event.start_date, today()) >= add_on_change_cutoff_days
|
|
except Exception as e:
|
|
frappe.log_error(f"Error checking add-on change eligibility: {e!s}")
|
|
return False
|
|
|
|
|
|
@frappe.whitelist()
|
|
def can_transfer_ticket(event_id: str | int) -> dict:
|
|
return {"can_transfer": is_ticket_transfer_allowed(event_id), "event_id": event_id}
|
|
|
|
|
|
@frappe.whitelist()
|
|
def can_change_add_ons(event_id: str | int) -> dict:
|
|
return {"can_change_add_ons": is_add_on_change_allowed(event_id), "event_id": event_id}
|
|
|
|
|
|
def is_cancellation_request_allowed(event_id: str | int) -> bool:
|
|
try:
|
|
event = frappe.get_cached_doc("Pohodex Event Manager Event", event_id)
|
|
settings = frappe.get_cached_doc("Pohodex Event Manager Settings")
|
|
cancellation_cutoff_days = settings.get(
|
|
"allow_ticket_cancellation_request_before_event_start_days", 7
|
|
)
|
|
|
|
if not event.start_date:
|
|
return False
|
|
|
|
return days_diff(event.start_date, today()) >= cancellation_cutoff_days
|
|
except Exception as e:
|
|
frappe.log_error(f"Error checking cancellation request eligibility: {e!s}")
|
|
return False
|
|
|
|
|
|
@frappe.whitelist()
|
|
def can_request_cancellation(event_id: str | int) -> dict:
|
|
return {"can_request_cancellation": is_cancellation_request_allowed(event_id), "event_id": event_id}
|
|
|
|
|
|
@frappe.whitelist(allow_guest=True) # nosemgrep: frappe-semgrep-rules.rules.security.guest-whitelisted-method
|
|
def get_event_booking_data(event_route: str) -> dict:
|
|
data = frappe._dict()
|
|
event_doc = frappe.get_cached_doc("Pohodex Event Manager Event", {"route": event_route})
|
|
|
|
if not event_doc.is_published:
|
|
frappe.throw(_("Event not found"), frappe.DoesNotExistError)
|
|
|
|
data.registrations_closed = are_registrations_closed(event_doc)
|
|
|
|
is_guest = frappe.session.user == "Guest"
|
|
if is_guest:
|
|
data.event_details = {
|
|
"name": event_doc.name,
|
|
"title": event_doc.title,
|
|
"route": event_doc.route,
|
|
"start_date": event_doc.start_date,
|
|
"end_date": event_doc.end_date,
|
|
"start_time": event_doc.start_time,
|
|
"end_time": event_doc.end_time,
|
|
"time_zone": event_doc.time_zone,
|
|
"venue": event_doc.venue,
|
|
"medium": event_doc.medium,
|
|
"category": event_doc.category,
|
|
"banner_image": event_doc.banner_image,
|
|
"short_description": event_doc.short_description,
|
|
"free_webinar": event_doc.free_webinar,
|
|
"send_ticket_email": event_doc.send_ticket_email,
|
|
"allow_guest_booking": event_doc.allow_guest_booking,
|
|
"guest_verification_method": event_doc.guest_verification_method,
|
|
"default_ticket_type": event_doc.default_ticket_type,
|
|
}
|
|
else:
|
|
data.event_details = event_doc
|
|
|
|
available_ticket_types = []
|
|
published_ticket_types = frappe.db.get_all(
|
|
"Event Ticket Type", filters={"is_published": True, "event": event_doc.name}, pluck="name"
|
|
)
|
|
for ticket_type in published_ticket_types:
|
|
tt = frappe.get_cached_doc("Event Ticket Type", ticket_type)
|
|
if tt.are_tickets_available(1):
|
|
available_ticket_types.append(tt)
|
|
data.available_ticket_types = available_ticket_types
|
|
|
|
add_ons = frappe.db.get_all(
|
|
"Ticket Add-on", filters={"event": event_doc.name, "enabled": 1}, fields=["*"], order_by="title"
|
|
)
|
|
|
|
for add_on in add_ons:
|
|
if add_on.user_selects_option:
|
|
add_on.options = add_on.options.split("\n")
|
|
|
|
data.available_add_ons = add_ons
|
|
|
|
data.tax_settings = {
|
|
"apply_tax": event_doc.apply_tax,
|
|
"tax_inclusive": event_doc.tax_inclusive,
|
|
"tax_label": event_doc.tax_label or "Tax",
|
|
"tax_percentage": event_doc.tax_percentage or 0,
|
|
}
|
|
|
|
custom_fields = frappe.db.get_all(
|
|
"Pohodex Event Manager Custom Field",
|
|
filters={"event": event_doc.name, "enabled": 1},
|
|
fields=["*"],
|
|
order_by="order",
|
|
)
|
|
data.custom_fields = custom_fields
|
|
|
|
payment_gateways = get_payment_gateways_for_event(event_doc.name)
|
|
|
|
offline_methods_raw = frappe.get_all(
|
|
"Offline Payment Method",
|
|
filters={"event": event_doc.name, "enabled": 1},
|
|
fields=["name", "title", "description", "collect_payment_proof"],
|
|
order_by="creation",
|
|
)
|
|
|
|
offline_methods = []
|
|
for method in offline_methods_raw:
|
|
method_custom_fields = frappe.get_all(
|
|
"Pohodex Event Manager Custom Field",
|
|
filters={
|
|
"event": event_doc.name,
|
|
"enabled": 1,
|
|
"applied_to": "Offline Payment Form",
|
|
"offline_payment_method": method.name,
|
|
},
|
|
fields=["*"],
|
|
order_by="order",
|
|
)
|
|
offline_methods.append(
|
|
{
|
|
"name": method.name,
|
|
"title": method.title,
|
|
"description": method.description,
|
|
"collect_payment_proof": method.collect_payment_proof,
|
|
"custom_fields": method_custom_fields,
|
|
}
|
|
)
|
|
payment_gateways.append(method.title)
|
|
|
|
data.payment_gateways = payment_gateways
|
|
data.offline_payment_enabled = len(offline_methods) > 0
|
|
data.offline_methods = offline_methods
|
|
|
|
return data
|
|
|
|
|
|
@frappe.whitelist(allow_guest=True) # nosemgrep: frappe-semgrep-rules.rules.security.guest-whitelisted-method
|
|
def process_booking(
|
|
attendees: list[dict],
|
|
event: str,
|
|
coupon_code: str | None = None,
|
|
booking_custom_fields: dict | None = None,
|
|
payment_gateway: str | None = None,
|
|
utm_parameters: list[dict] | None = None,
|
|
guest_email: str | None = None,
|
|
guest_full_name: str | None = None,
|
|
otp: str | None = None,
|
|
guest_phone: str | None = None,
|
|
payment_proof: str | None = None,
|
|
is_offline: bool = False,
|
|
offline_payment_method: str | None = None,
|
|
invoice_requested: bool = False,
|
|
tax_id: str | None = None,
|
|
billing_address: str | None = None,
|
|
) -> dict:
|
|
event_doc = frappe.get_cached_doc("Pohodex Event Manager Event", event)
|
|
if not event_doc.is_published:
|
|
frappe.throw(_("Event is not live"))
|
|
|
|
if are_registrations_closed(event_doc):
|
|
frappe.throw(_("Registrations for this event are closed"))
|
|
|
|
is_guest = frappe.session.user == "Guest"
|
|
|
|
if is_guest:
|
|
if not event_doc.allow_guest_booking:
|
|
frappe.throw(_("Please log in to access this feature"), frappe.AuthenticationError)
|
|
|
|
if not guest_email:
|
|
frappe.throw(_("Email is required for guest booking"))
|
|
|
|
validate_email_address(guest_email, throw=True)
|
|
email = guest_email.lower().strip()
|
|
|
|
if event_doc.guest_verification_method == "Email OTP":
|
|
if not otp:
|
|
frappe.throw(_("Verification code is required"))
|
|
verify_guest_otp("email", email, otp)
|
|
|
|
elif event_doc.guest_verification_method == "Phone OTP":
|
|
if not otp:
|
|
frappe.throw(_("Verification code is required"))
|
|
if not guest_phone:
|
|
frappe.throw(_("Phone number is required"))
|
|
verify_guest_otp("phone", guest_phone.strip(), otp)
|
|
|
|
first_name = (attendees[0].get("first_name") or "").strip()
|
|
last_name = (attendees[0].get("last_name") or "").strip()
|
|
full_name = (guest_full_name or "").strip() or f"{first_name} {last_name}".strip()
|
|
if not full_name:
|
|
frappe.throw(_("Full name is required for guest booking"))
|
|
booking_user = get_or_create_guest_user(guest_email, full_name)
|
|
else:
|
|
booking_user = frappe.session.user
|
|
|
|
booking = frappe.new_doc("Event Booking")
|
|
booking.event = event
|
|
booking.coupon_code = coupon_code
|
|
booking.user = booking_user
|
|
|
|
if event_doc.apply_tax and invoice_requested:
|
|
booking.invoice_requested = 1
|
|
booking.tax_id = tax_id
|
|
booking.billing_address = billing_address
|
|
|
|
if utm_parameters:
|
|
for utm_param in utm_parameters:
|
|
booking.append(
|
|
"utm_parameters",
|
|
{
|
|
"utm_name": utm_param.get("utm_name"),
|
|
"value": utm_param.get("value"),
|
|
},
|
|
)
|
|
|
|
if booking_custom_fields:
|
|
booking_custom_field_defs = frappe.db.get_all(
|
|
"Pohodex Event Manager Custom Field",
|
|
filters={"event": event, "enabled": 1, "applied_to": "Booking"},
|
|
fields=["fieldname", "label", "fieldtype"],
|
|
)
|
|
custom_field_map = {cf["fieldname"]: cf for cf in booking_custom_field_defs}
|
|
|
|
for field_name, field_value in booking_custom_fields.items():
|
|
if field_value and field_name in custom_field_map:
|
|
field_def = custom_field_map[field_name]
|
|
booking.append(
|
|
"additional_fields",
|
|
{
|
|
"fieldname": field_name,
|
|
"value": str(field_value),
|
|
"label": field_def["label"],
|
|
"fieldtype": field_def["fieldtype"],
|
|
},
|
|
)
|
|
if event_doc.category == "Webinars":
|
|
for attendee in attendees:
|
|
if not (attendee.get("last_name") or "").strip():
|
|
frappe.throw(_("Last name is required for all attendees in webinar events"))
|
|
|
|
for attendee in attendees:
|
|
first_name = (attendee.get("first_name") or "").strip()
|
|
last_name = (attendee.get("last_name") or "").strip()
|
|
|
|
if not first_name and attendee.get("full_name"):
|
|
name_parts = attendee["full_name"].strip().split(" ", 1)
|
|
first_name = name_parts[0]
|
|
last_name = last_name or (name_parts[1] if len(name_parts) > 1 else "")
|
|
|
|
attendee_full_name = f"{first_name} {last_name}".strip()
|
|
|
|
add_ons = attendee.get("add_ons", None)
|
|
if add_ons:
|
|
add_ons = create_add_on_doc(
|
|
attendee_name=attendee_full_name,
|
|
add_ons=add_ons,
|
|
)
|
|
|
|
custom_fields = attendee.get("custom_fields", {})
|
|
attendee_row = {
|
|
"first_name": first_name,
|
|
"last_name": last_name,
|
|
"email": attendee.get("email"),
|
|
"ticket_type": attendee.get("ticket_type"),
|
|
"add_ons": add_ons.name if add_ons else None,
|
|
"custom_fields": custom_fields if custom_fields else None,
|
|
}
|
|
|
|
booking.append("attendees", attendee_row)
|
|
|
|
booking.insert(ignore_permissions=True)
|
|
frappe.db.commit()
|
|
|
|
if booking.total_amount == 0:
|
|
booking.flags.ignore_permissions = True
|
|
booking.submit()
|
|
return {"booking_name": booking.name}
|
|
|
|
if is_offline:
|
|
method_filters = {"event": event, "enabled": 1}
|
|
if offline_payment_method:
|
|
method_filters["name"] = offline_payment_method
|
|
method_doc = frappe.db.get_value(
|
|
"Offline Payment Method", method_filters, ["name", "title"], as_dict=True
|
|
)
|
|
if not method_doc:
|
|
frappe.throw(_("Offline payment is not enabled for this event"))
|
|
|
|
booking.payment_method = OFFLINE_PAYMENT_METHOD
|
|
booking.offline_payment_method = method_doc.title
|
|
|
|
booking.status = "Approval Pending"
|
|
booking.payment_status = "Verification Pending"
|
|
booking.flags.ignore_permissions = True
|
|
booking.save()
|
|
|
|
if payment_proof:
|
|
try:
|
|
file_doc = frappe.get_doc(
|
|
{
|
|
"doctype": "File",
|
|
"file_url": payment_proof,
|
|
"attached_to_doctype": "Event Booking",
|
|
"attached_to_name": booking.name,
|
|
"is_private": 1,
|
|
}
|
|
)
|
|
file_doc.insert(ignore_permissions=True)
|
|
except Exception as e:
|
|
frappe.log_error(f"Failed to attach payment proof: {e}")
|
|
|
|
return {"booking_name": booking.name, "offline_payment": True}
|
|
|
|
return {
|
|
"payment_link": get_payment_link_for_booking(
|
|
booking.name,
|
|
redirect_to=f"/dashboard/bookings/{booking.name}?success=true",
|
|
payment_gateway=payment_gateway,
|
|
)
|
|
}
|
|
|
|
|
|
def create_add_on_doc(attendee_name: str, add_ons: list[dict]):
|
|
for add_on in add_ons:
|
|
add_on["currency"] = frappe.db.get_value("Ticket Add-on", add_on["add_on"], "currency")
|
|
|
|
return frappe.get_doc(
|
|
{"doctype": "Attendee Ticket Add-on", "add_ons": add_ons, "attendee_name": attendee_name}
|
|
).insert(ignore_permissions=True)
|
|
|
|
|
|
@frappe.whitelist()
|
|
def transfer_ticket(ticket_id: str, new_first_name: str, new_last_name: str, new_email: str):
|
|
if not frappe.db.exists("Event Ticket", ticket_id):
|
|
frappe.throw(frappe._("Ticket not found."))
|
|
|
|
ticket = frappe.get_doc("Event Ticket", ticket_id)
|
|
booking_user = frappe.db.get_value("Event Booking", ticket.booking, "user")
|
|
|
|
if (
|
|
ticket.attendee_email != frappe.session.user
|
|
and booking_user != frappe.session.user
|
|
and not frappe.has_permission("Event Ticket", "write", ticket)
|
|
):
|
|
frappe.throw(frappe._("Not permitted to transfer this ticket."))
|
|
|
|
if not is_ticket_transfer_allowed(ticket.event):
|
|
frappe.throw(frappe._("Ticket transfer is not allowed at this time. The transfer window has closed."))
|
|
|
|
old_name = ticket.attendee_name
|
|
old_email = ticket.attendee_email
|
|
new_name = f"{new_first_name} {new_last_name}".strip()
|
|
|
|
ticket.first_name = new_first_name
|
|
ticket.last_name = new_last_name
|
|
ticket.attendee_email = new_email
|
|
ticket.save(ignore_permissions=True)
|
|
|
|
send_ticket_transfer_emails(ticket.name, old_name, old_email, new_name, new_email)
|
|
|
|
|
|
def send_ticket_transfer_emails(ticket_id: str, old_name: str, old_email: str, new_name: str, new_email: str):
|
|
try:
|
|
ticket = frappe.get_doc("Event Ticket", ticket_id)
|
|
event = frappe.get_doc("Pohodex Event Manager Event", ticket.event)
|
|
booking = frappe.get_doc("Event Booking", ticket.booking)
|
|
|
|
old_attendee_subject = f"Your ticket for {event.title} has been transferred"
|
|
old_attendee_message = f"""
|
|
<p>Dear {old_name},</p>
|
|
|
|
<p>This is to inform you that your ticket for <strong>{event.title}</strong> has been transferred to {new_name} ({new_email}).</p>
|
|
|
|
<p><strong>Event Details:</strong></p>
|
|
<ul>
|
|
<li><strong>Event:</strong> {event.title}</li>
|
|
<li><strong>Date:</strong> {format_date(event.start_date)}</li>
|
|
<li><strong>Ticket Type:</strong> {ticket.ticket_type}</li>
|
|
<li><strong>Booking ID:</strong> {booking.name}</li>
|
|
</ul>
|
|
|
|
<p>If you have any questions about this transfer, please contact us.</p>
|
|
|
|
<p>Best regards,<br>
|
|
{event.title} Team</p>
|
|
"""
|
|
|
|
frappe.sendmail(
|
|
recipients=[old_email], subject=old_attendee_subject, message=old_attendee_message, delayed=False
|
|
)
|
|
|
|
new_attendee_subject = f"Welcome! Your ticket for {event.title}"
|
|
new_attendee_message = f"""
|
|
<p>Dear {new_name},</p>
|
|
|
|
<p>Great news! A ticket for <strong>{event.title}</strong> has been transferred to you.</p>
|
|
|
|
<p><strong>Event Details:</strong></p>
|
|
<ul>
|
|
<li><strong>Event:</strong> {event.title}</li>
|
|
<li><strong>Date:</strong> {format_date(event.start_date)}</li>
|
|
<li><strong>Time:</strong> {format_time(event.start_time) if event.start_time else "TBA"}</li>
|
|
<li><strong>Venue:</strong> {event.venue or "TBA"}</li>
|
|
<li><strong>Ticket Type:</strong> {ticket.ticket_type}</li>
|
|
<li><strong>Booking ID:</strong> {booking.name}</li>
|
|
</ul>
|
|
|
|
<p><strong>Your Ticket Details:</strong></p>
|
|
<ul>
|
|
<li><strong>Ticket ID:</strong> {ticket.name}</li>
|
|
<li><strong>Attendee Name:</strong> {new_name}</li>
|
|
<li><strong>Email:</strong> {new_email}</li>
|
|
</ul>
|
|
|
|
<p>Please save this email for your records. You may need to present this ticket information at the event entrance.</p>
|
|
|
|
<p>We look forward to seeing you at the event!</p>
|
|
|
|
<p>Best regards,<br>
|
|
{event.title} Team</p>
|
|
"""
|
|
|
|
frappe.sendmail(
|
|
recipients=[new_email], subject=new_attendee_subject, message=new_attendee_message, delayed=False
|
|
)
|
|
|
|
except Exception as e:
|
|
frappe.log_error(f"Failed to send ticket transfer emails for ticket {ticket_id}: {e!s}")
|
|
|
|
|
|
@frappe.whitelist()
|
|
def get_booking_details(booking_id: str) -> dict:
|
|
details = frappe._dict()
|
|
booking_doc = frappe.get_cached_doc("Event Booking", booking_id)
|
|
details.doc = booking_doc
|
|
|
|
tickets = frappe.db.get_all(
|
|
"Event Ticket",
|
|
filters={"booking": booking_id},
|
|
fields=[
|
|
"name",
|
|
"attendee_name",
|
|
"attendee_email",
|
|
"ticket_type.title as ticket_type",
|
|
"qr_code",
|
|
"event",
|
|
"docstatus",
|
|
],
|
|
)
|
|
|
|
add_ons = frappe.db.get_all(
|
|
"Ticket Add-on Value",
|
|
filters={"parent": ("in", [ticket.name for ticket in tickets])},
|
|
fields=[
|
|
"parent",
|
|
"name",
|
|
"add_on",
|
|
"value",
|
|
"add_on.title as add_on_title",
|
|
"add_on.user_selects_option as user_selects_option",
|
|
],
|
|
)
|
|
|
|
event_add_ons = frappe.db.get_all(
|
|
"Ticket Add-on",
|
|
filters={"event": booking_doc.event, "user_selects_option": True},
|
|
fields=["name", "title", "user_selects_option", "options"],
|
|
)
|
|
|
|
add_on_options_map = {}
|
|
for event_add_on in event_add_ons:
|
|
if event_add_on.user_selects_option:
|
|
add_on_options_map[event_add_on.name] = (
|
|
event_add_on.options.split("\n") if event_add_on.options else []
|
|
)
|
|
|
|
for ticket in tickets:
|
|
ticket.add_ons = []
|
|
for add_on in add_ons:
|
|
if add_on.parent == ticket.name:
|
|
add_on_data = {
|
|
"id": add_on.name,
|
|
"name": add_on.add_on,
|
|
"title": add_on.add_on_title,
|
|
"value": add_on.value,
|
|
"user_selects_option": add_on.user_selects_option,
|
|
"options": add_on_options_map.get(add_on.add_on, []),
|
|
}
|
|
ticket.add_ons.append(add_on_data)
|
|
ticket.add_ons = sorted(ticket.add_ons, key=lambda x: x["title"])
|
|
|
|
details.tickets = tickets
|
|
details.event = frappe.get_cached_doc("Pohodex Event Manager Event", booking_doc.event)
|
|
|
|
if details.event.venue:
|
|
details.venue = frappe.get_cached_doc("Event Venue", details.event.venue)
|
|
|
|
details.can_transfer_ticket = can_transfer_ticket(details.event.name)
|
|
details.can_change_add_ons = can_change_add_ons(details.event.name)
|
|
details.can_request_cancellation = can_request_cancellation(details.event.name)
|
|
|
|
existing_cancellation = frappe.db.get_value(
|
|
"Ticket Cancellation Request",
|
|
{"booking": booking_id},
|
|
["name", "cancel_full_booking", "creation", "status", "docstatus"],
|
|
as_dict=True,
|
|
)
|
|
details.cancellation_request = existing_cancellation
|
|
|
|
details.cancellation_requested_tickets = []
|
|
|
|
if existing_cancellation and existing_cancellation.docstatus == 0:
|
|
if existing_cancellation.cancel_full_booking:
|
|
details.cancellation_requested_tickets = [ticket.name for ticket in tickets]
|
|
else:
|
|
requested_tickets = frappe.db.get_all(
|
|
"Ticket Cancellation Item", filters={"parent": existing_cancellation.name}, fields=["ticket"]
|
|
)
|
|
details.cancellation_requested_tickets = [item.ticket for item in requested_tickets]
|
|
|
|
details.cancelled_tickets = [ticket.name for ticket in tickets if ticket.docstatus == 2]
|
|
|
|
return details
|
|
|
|
|
|
@frappe.whitelist()
|
|
def change_add_on_preference(add_on_id: str, new_value: str):
|
|
if not frappe.db.exists("Ticket Add-on Value", add_on_id):
|
|
frappe.throw(frappe._("Add-on value not found."))
|
|
|
|
add_on_value = frappe.get_cached_doc("Ticket Add-on Value", add_on_id)
|
|
ticket = frappe.get_cached_doc("Event Ticket", add_on_value.parent)
|
|
|
|
if not is_add_on_change_allowed(ticket.event):
|
|
frappe.throw(
|
|
frappe._(
|
|
"Add-on changes are not allowed at this time. The change window has closed as the event is approaching."
|
|
)
|
|
)
|
|
|
|
frappe.db.set_value(
|
|
"Ticket Add-on Value",
|
|
add_on_id,
|
|
"value",
|
|
new_value,
|
|
)
|
|
|
|
|
|
@frappe.whitelist()
|
|
def get_sponsorship_details(enquiry_id: str) -> dict:
|
|
enquiry = frappe.get_doc("Sponsorship Enquiry", enquiry_id)
|
|
|
|
if enquiry.owner != frappe.session.user and not frappe.has_permission(
|
|
"Sponsorship Enquiry", "read", enquiry
|
|
):
|
|
frappe.throw(frappe._("Not permitted to view this sponsorship enquiry"))
|
|
|
|
tier_title = ""
|
|
if enquiry.tier:
|
|
tier_title = frappe.db.get_value("Sponsorship Tier", enquiry.tier, "title") or enquiry.tier
|
|
|
|
event_details = {}
|
|
if enquiry.event:
|
|
event = frappe.get_cached_doc("Pohodex Event Manager Event", enquiry.event)
|
|
event_details = {
|
|
"title": event.title,
|
|
"short_description": getattr(event, "short_description", ""),
|
|
"about": getattr(event, "about", ""),
|
|
"start_date": event.start_date,
|
|
"end_date": getattr(event, "end_date", ""),
|
|
"venue": getattr(event, "venue", ""),
|
|
"route": getattr(event, "route", ""),
|
|
}
|
|
|
|
sponsor_details = None
|
|
sponsors = frappe.db.get_all(
|
|
"Event Sponsor",
|
|
filters={"enquiry": enquiry_id},
|
|
fields=["name", "company_name", "company_logo", "creation", "event", "tier"],
|
|
limit=1,
|
|
)
|
|
|
|
if sponsors:
|
|
sponsor_details = sponsors[0]
|
|
if sponsor_details.get("tier"):
|
|
sponsor_tier_title = frappe.db.get_value("Sponsorship Tier", sponsor_details["tier"], "title")
|
|
sponsor_details["tier_title"] = sponsor_tier_title or sponsor_details["tier"]
|
|
|
|
return {
|
|
"enquiry": {
|
|
"name": enquiry.name,
|
|
"company_name": enquiry.company_name,
|
|
"company_logo": enquiry.company_logo,
|
|
"event": enquiry.event,
|
|
"tier": enquiry.tier,
|
|
"tier_title": tier_title,
|
|
"status": enquiry.status,
|
|
"creation": enquiry.creation,
|
|
"owner": enquiry.owner,
|
|
},
|
|
"event_details": event_details,
|
|
"sponsor_details": sponsor_details,
|
|
"has_sponsor": bool(sponsor_details),
|
|
}
|
|
|
|
|
|
@frappe.whitelist()
|
|
def get_user_sponsorship_inquiries() -> list:
|
|
inquiries = frappe.db.get_all(
|
|
"Sponsorship Enquiry",
|
|
filters={"owner": frappe.session.user},
|
|
fields=["name", "company_name", "event", "tier", "status", "creation"],
|
|
order_by="creation desc",
|
|
)
|
|
|
|
for inquiry in inquiries:
|
|
if inquiry.event:
|
|
event_title = frappe.db.get_value("Pohodex Event Manager Event", inquiry.event, "title")
|
|
inquiry["event_title"] = event_title
|
|
|
|
if inquiry.tier:
|
|
tier_title = frappe.db.get_value("Sponsorship Tier", inquiry.tier, "title")
|
|
inquiry["tier_title"] = tier_title or inquiry.tier
|
|
else:
|
|
inquiry["tier_title"] = ""
|
|
|
|
inquiry_names = [inquiry.name for inquiry in inquiries]
|
|
if inquiry_names:
|
|
sponsors = frappe.db.get_all(
|
|
"Event Sponsor",
|
|
filters={"enquiry": ["in", inquiry_names]},
|
|
fields=["enquiry"],
|
|
)
|
|
sponsored_inquiries = {sponsor.enquiry for sponsor in sponsors}
|
|
|
|
for inquiry in inquiries:
|
|
inquiry["has_sponsor"] = inquiry.name in sponsored_inquiries
|
|
else:
|
|
for inquiry in inquiries:
|
|
inquiry["has_sponsor"] = False
|
|
|
|
return inquiries
|
|
|
|
|
|
@frappe.whitelist()
|
|
def create_sponsorship_payment_link(enquiry_id: str, tier_id: str, payment_gateway: str | None = None) -> str:
|
|
enquiry = frappe.get_doc("Sponsorship Enquiry", enquiry_id)
|
|
if enquiry.owner != frappe.session.user:
|
|
frappe.throw(frappe._("Not permitted to create payment for this enquiry"))
|
|
|
|
redirect_url = f"/dashboard/account/sponsorships/{enquiry_id}?success=true"
|
|
return get_payment_link_for_sponsorship(
|
|
enquiry_id, tier_id, redirect_url, payment_gateway=payment_gateway
|
|
)
|
|
|
|
|
|
@frappe.whitelist()
|
|
def withdraw_sponsorship_enquiry(enquiry_id: str):
|
|
enquiry = frappe.get_cached_doc("Sponsorship Enquiry", enquiry_id)
|
|
if enquiry.owner != frappe.session.user:
|
|
frappe.throw(frappe._("Not permitted to withdraw this enquiry"))
|
|
|
|
if enquiry.status == "Paid":
|
|
frappe.throw(frappe._("Cannot withdraw a paid sponsorship enquiry"))
|
|
|
|
if enquiry.status == "Withdrawn":
|
|
frappe.throw(frappe._("This sponsorship enquiry has already been withdrawn"))
|
|
|
|
enquiry.status = "Withdrawn"
|
|
enquiry.save(ignore_permissions=True)
|
|
|
|
|
|
@frappe.whitelist()
|
|
def get_ticket_details(ticket_id: str) -> dict:
|
|
details = frappe._dict()
|
|
ticket_doc = frappe.get_cached_doc("Event Ticket", ticket_id)
|
|
|
|
if frappe.session.user != "Administrator":
|
|
if ticket_doc.attendee_email != frappe.session.user:
|
|
frappe.throw(frappe._("Not permitted to view this ticket"))
|
|
|
|
details.doc = ticket_doc
|
|
|
|
add_ons = frappe.db.get_all(
|
|
"Ticket Add-on Value",
|
|
filters={"parent": ticket_id},
|
|
fields=[
|
|
"name",
|
|
"add_on",
|
|
"add_on.title as add_on_title",
|
|
"value",
|
|
"price",
|
|
"currency",
|
|
"add_on.user_selects_option as user_selects_option",
|
|
],
|
|
)
|
|
|
|
event_add_ons = frappe.db.get_all(
|
|
"Ticket Add-on",
|
|
filters={"event": ticket_doc.event, "user_selects_option": True},
|
|
fields=["name", "title", "user_selects_option", "options"],
|
|
)
|
|
|
|
add_on_options_map = {}
|
|
for event_add_on in event_add_ons:
|
|
if event_add_on.user_selects_option:
|
|
add_on_options_map[event_add_on.name] = (
|
|
event_add_on.options.split("\n") if event_add_on.options else []
|
|
)
|
|
|
|
enhanced_add_ons = []
|
|
for add_on in add_ons:
|
|
add_on_data = {
|
|
"id": add_on.name,
|
|
"name": add_on.add_on,
|
|
"title": add_on.add_on_title,
|
|
"value": add_on.value,
|
|
"price": add_on.price,
|
|
"currency": add_on.currency,
|
|
"user_selects_option": add_on.user_selects_option,
|
|
"options": add_on_options_map.get(add_on.add_on, []),
|
|
}
|
|
enhanced_add_ons.append(add_on_data)
|
|
|
|
details.add_ons = enhanced_add_ons
|
|
details.event = frappe.get_cached_doc("Pohodex Event Manager Event", ticket_doc.event)
|
|
|
|
booking_doc = None
|
|
if ticket_doc.booking:
|
|
booking_doc = frappe.get_cached_doc("Event Booking", ticket_doc.booking)
|
|
if booking_doc.owner == frappe.session.user:
|
|
details.booking = booking_doc
|
|
else:
|
|
details.booking = None
|
|
else:
|
|
details.booking = None
|
|
|
|
details.ticket_type = frappe.get_cached_doc("Event Ticket Type", ticket_doc.ticket_type)
|
|
details.can_transfer_ticket = (
|
|
can_transfer_ticket(details.event.name) if details.event else {"can_transfer": False}
|
|
)
|
|
details.can_change_add_ons = (
|
|
can_change_add_ons(details.event.name) if details.event else {"can_change_add_ons": False}
|
|
)
|
|
details.can_request_cancellation = (
|
|
can_request_cancellation(details.event.name) if details.event else {"can_request_cancellation": False}
|
|
)
|
|
|
|
details.zoom_join_url = None
|
|
if hasattr(ticket_doc, "zoom_webinar_registration") and ticket_doc.zoom_webinar_registration:
|
|
zoom_registration = frappe.db.get_value(
|
|
"Zoom Webinar Registration",
|
|
ticket_doc.zoom_webinar_registration,
|
|
["join_url", "webinar"],
|
|
as_dict=True,
|
|
)
|
|
if zoom_registration:
|
|
details.zoom_join_url = zoom_registration.join_url
|
|
details.zoom_webinar = zoom_registration.webinar
|
|
|
|
return details
|
|
|
|
|
|
@frappe.whitelist()
|
|
def create_cancellation_request(booking_id: str, ticket_ids: list | None = None) -> dict:
|
|
booking_doc = frappe.get_cached_doc("Event Booking", booking_id)
|
|
|
|
if booking_doc.user != frappe.session.user and not frappe.has_permission(
|
|
"Event Booking", "write", booking_doc
|
|
):
|
|
frappe.throw(frappe._("Not permitted to request cancellation for this booking."))
|
|
|
|
if not is_cancellation_request_allowed(booking_doc.event):
|
|
frappe.throw("Cancellation requests are no longer allowed for this event.")
|
|
|
|
existing_request = frappe.db.exists(
|
|
"Ticket Cancellation Request", {"booking": booking_id, "docstatus": 0}
|
|
)
|
|
if existing_request:
|
|
frappe.throw("A cancellation request already exists for this booking.")
|
|
|
|
all_tickets = frappe.db.get_all("Event Ticket", filters={"booking": booking_id}, fields=["name"])
|
|
cancel_full_booking = not ticket_ids or len(ticket_ids) == len(all_tickets)
|
|
|
|
cancellation_request = frappe.new_doc("Ticket Cancellation Request")
|
|
cancellation_request.booking = booking_id
|
|
cancellation_request.cancel_full_booking = cancel_full_booking
|
|
|
|
if not cancel_full_booking and ticket_ids:
|
|
for ticket_id in ticket_ids:
|
|
ticket_booking = frappe.db.get_value("Event Ticket", ticket_id, "booking")
|
|
if ticket_booking != booking_id:
|
|
frappe.throw(f"Ticket {ticket_id} does not belong to booking {booking_id}")
|
|
|
|
cancellation_request.append("tickets", {"ticket": ticket_id})
|
|
|
|
cancellation_request.insert(ignore_permissions=True)
|
|
|
|
|
|
@frappe.whitelist(allow_guest=True) # nosemgrep: frappe-semgrep-rules.rules.security.guest-whitelisted-method
|
|
def get_user_info() -> dict:
|
|
if frappe.session.user == "Guest":
|
|
return {"is_logged_in": False}
|
|
|
|
user = frappe.get_cached_doc("User", frappe.session.user)
|
|
|
|
return {
|
|
"name": user.name,
|
|
"is_logged_in": True,
|
|
"first_name": user.first_name,
|
|
"last_name": user.last_name,
|
|
"full_name": user.full_name,
|
|
"email": user.email,
|
|
"user_image": user.user_image,
|
|
"roles": user.roles,
|
|
"brand_image": frappe.get_single_value("Website Settings", "banner_image"),
|
|
"language": user.language,
|
|
}
|
|
|
|
|
|
@frappe.whitelist()
|
|
def validate_ticket_for_checkin(ticket_id: str) -> dict:
|
|
frappe.only_for("Frontdesk Manager", True)
|
|
if not frappe.db.exists("Event Ticket", ticket_id):
|
|
frappe.throw(_("Ticket not found"))
|
|
|
|
ticket_doc = frappe.get_cached_doc("Event Ticket", ticket_id)
|
|
|
|
if ticket_doc.docstatus == 2:
|
|
frappe.throw(_("This ticket has been cancelled and cannot be checked in"))
|
|
|
|
event_doc = frappe.get_cached_doc("Pohodex Event Manager Event", ticket_doc.event)
|
|
ticket_type_doc = (
|
|
frappe.get_cached_doc("Event Ticket Type", ticket_doc.ticket_type) if ticket_doc.ticket_type else None
|
|
)
|
|
|
|
checkin_date = frappe.utils.today()
|
|
existing_checkin = frappe.db.exists("Event Check In", {"ticket": ticket_id, "date": checkin_date})
|
|
|
|
if existing_checkin:
|
|
checkin_doc = frappe.get_doc("Event Check In", existing_checkin)
|
|
formatted_checkin_time = (
|
|
format_date(checkin_doc.creation) + " at " + format_time(checkin_doc.creation)
|
|
)
|
|
|
|
frappe.throw(_("This ticket was already checked in today ({0}).").format(formatted_checkin_time))
|
|
|
|
add_ons = frappe.db.get_all(
|
|
"Ticket Add-on Value",
|
|
filters={"parent": ticket_id},
|
|
fields=[
|
|
"add_on",
|
|
"add_on.title as add_on_title",
|
|
"add_on.user_selects_option as add_on_selects_option",
|
|
"value",
|
|
"price",
|
|
"currency",
|
|
],
|
|
)
|
|
|
|
return {
|
|
"message": _("Valid ticket ready for check-in"),
|
|
"ticket": {
|
|
"id": ticket_doc.name,
|
|
"attendee_name": ticket_doc.attendee_name,
|
|
"attendee_email": ticket_doc.attendee_email,
|
|
"event_title": event_doc.title,
|
|
"ticket_type": (ticket_type_doc.title if ticket_type_doc else ticket_doc.ticket_type),
|
|
"venue": event_doc.venue,
|
|
"start_date": event_doc.start_date,
|
|
"start_time": event_doc.start_time,
|
|
"end_date": event_doc.end_date,
|
|
"end_time": event_doc.end_time,
|
|
"is_checked_in": False,
|
|
"check_in_time": None,
|
|
"booking_id": ticket_doc.booking,
|
|
"add_ons": add_ons,
|
|
},
|
|
"payment_details": get_payment_details_for_ticket(ticket_id),
|
|
}
|
|
|
|
|
|
def get_payment_details_for_ticket(ticket_id: str) -> dict | None:
|
|
booking_id = frappe.get_cached_value("Event Ticket", ticket_id, "booking")
|
|
if not booking_id:
|
|
return None
|
|
|
|
payments = frappe.db.get_all(
|
|
"Event Payment",
|
|
filters={
|
|
"reference_doctype": "Event Booking",
|
|
"reference_docname": booking_id,
|
|
"payment_received": 1,
|
|
},
|
|
fields=["name", "amount", "currency"],
|
|
limit=1,
|
|
)
|
|
|
|
if payments:
|
|
return payments[0]
|
|
|
|
|
|
@frappe.whitelist()
|
|
def checkin_ticket(ticket_id: str) -> dict:
|
|
frappe.only_for("Frontdesk Manager", True)
|
|
|
|
checkin_date = frappe.utils.today()
|
|
validation_result = validate_ticket_for_checkin(ticket_id)
|
|
|
|
checkin_doc = frappe.new_doc("Event Check In")
|
|
checkin_doc.ticket = ticket_id
|
|
checkin_doc.date = checkin_date
|
|
checkin_doc.insert(ignore_permissions=True)
|
|
checkin_doc.submit()
|
|
|
|
return {
|
|
"message": _("Successfully checked in {attendee_name} for {checkin_date}").format(
|
|
attendee_name=validation_result["ticket"]["attendee_name"],
|
|
checkin_date=frappe.format(checkin_date, {"fieldtype": "Date"}),
|
|
),
|
|
"ticket": {
|
|
**validation_result["ticket"],
|
|
"is_checked_in": True,
|
|
"check_in_time": checkin_doc.creation,
|
|
"check_in_date": checkin_date,
|
|
},
|
|
}
|
|
|
|
|
|
@frappe.whitelist(allow_guest=True) # nosemgrep: frappe-semgrep-rules.rules.security.guest-whitelisted-method
|
|
def get_enabled_languages():
|
|
languages = frappe.get_all(
|
|
"Language",
|
|
filters={"enabled": 1},
|
|
fields=["name", "language_name", "language_code"],
|
|
order_by="language_name",
|
|
)
|
|
return languages
|
|
|
|
|
|
@frappe.whitelist()
|
|
def update_user_language(language_code: str):
|
|
if not frappe.db.exists("Language", {"language_code": language_code}):
|
|
frappe.throw(_("Invalid language"))
|
|
|
|
frappe.db.set_value("User", frappe.session.user, "language", language_code)
|
|
|
|
|
|
@frappe.whitelist(allow_guest=True) # nosemgrep: frappe-semgrep-rules.rules.security.guest-whitelisted-method
|
|
def get_translations():
|
|
if frappe.session.user != "Guest":
|
|
language = frappe.db.get_value("User", frappe.session.user, "language")
|
|
else:
|
|
language = frappe.db.get_single_value("System Settings", "language")
|
|
|
|
return get_all_translations(language)
|
|
|
|
|
|
def has_app_permission():
|
|
return True
|
|
|
|
|
|
@frappe.whitelist(allow_guest=True) # nosemgrep: frappe-semgrep-rules.rules.security.guest-whitelisted-method
|
|
def validate_coupon(coupon_code: str, event: str, user_email: str | None = None) -> dict:
|
|
event_doc = frappe.get_cached_doc("Pohodex Event Manager Event", event)
|
|
if frappe.session.user == "Guest" and not event_doc.allow_guest_booking:
|
|
frappe.throw(_("Please log in to access this feature"), frappe.AuthenticationError)
|
|
|
|
if not frappe.db.exists("Pohodex Event Manager Coupon Code", coupon_code):
|
|
return {"valid": False, "error": _("Invalid coupon code")}
|
|
|
|
coupon = frappe.get_doc("Pohodex Event Manager Coupon Code", coupon_code)
|
|
|
|
is_valid, error = coupon.is_valid_for_event(event)
|
|
if not is_valid:
|
|
return {"valid": False, "error": error}
|
|
|
|
is_available, error = coupon.is_usage_available()
|
|
if not is_available:
|
|
return {"valid": False, "error": error}
|
|
|
|
if frappe.session.user == "Guest":
|
|
check_user = user_email.lower().strip() if user_email else None
|
|
else:
|
|
check_user = frappe.session.user
|
|
is_limited, error = coupon.is_user_limit_reached(user=check_user)
|
|
if is_limited:
|
|
return {"valid": False, "error": error}
|
|
|
|
if coupon.coupon_type == "Discount":
|
|
return {
|
|
"valid": True,
|
|
"coupon_type": "Discount",
|
|
"discount_type": coupon.discount_type,
|
|
"discount_value": coupon.discount_value,
|
|
"max_discount_amount": coupon.maximum_discount_amount or 0,
|
|
"min_order_value": coupon.minimum_order_value or 0,
|
|
}
|
|
|
|
remaining = coupon.number_of_free_tickets - coupon.free_tickets_claimed
|
|
if remaining <= 0:
|
|
return {"valid": False, "error": _("All free tickets have been claimed")}
|
|
|
|
return {
|
|
"valid": True,
|
|
"coupon_type": "Free Tickets",
|
|
"ticket_type": coupon.ticket_type,
|
|
"remaining_tickets": remaining,
|
|
"free_add_ons": [a.add_on for a in coupon.free_add_ons],
|
|
}
|
|
|
|
|
|
@frappe.whitelist()
|
|
def get_campaign_details(campaign: str):
|
|
if not frappe.db.exists("Pohodex Event Manager Campaign", campaign):
|
|
frappe.throw(_("Campaign not found"), frappe.DoesNotExistError)
|
|
|
|
campaign_doc = frappe.get_cached_doc("Pohodex Event Manager Campaign", campaign)
|
|
|
|
if not campaign_doc.enabled:
|
|
frappe.throw(_("This campaign is not active"))
|
|
|
|
return {
|
|
"title": campaign_doc.title,
|
|
"description": campaign_doc.description,
|
|
"event": campaign_doc.event,
|
|
}
|
|
|
|
|
|
@frappe.whitelist()
|
|
def register_campaign_interest(campaign: str):
|
|
if frappe.session.user == "Guest":
|
|
frappe.throw(_("Please login to register your interest"))
|
|
|
|
if not is_app_installed("crm"):
|
|
frappe.throw(_("CRM integration is not available"))
|
|
|
|
if not frappe.db.exists("Pohodex Event Manager Campaign", campaign):
|
|
frappe.throw(_("Campaign not found"), frappe.DoesNotExistError)
|
|
|
|
campaign_doc = frappe.get_cached_doc("Pohodex Event Manager Campaign", campaign)
|
|
|
|
user = frappe.get_cached_doc("User", frappe.session.user)
|
|
first_name = user.first_name or user.full_name or frappe.session.user.split("@")[0]
|
|
|
|
existing_lead = frappe.db.exists(
|
|
"CRM Lead",
|
|
{"email": frappe.session.user, "buzz_campaign": campaign},
|
|
)
|
|
if existing_lead:
|
|
frappe.throw(_("You have already registered for this campaign"))
|
|
|
|
ticket = None
|
|
if campaign_doc.event:
|
|
ticket = frappe.db.get_value(
|
|
"Event Ticket",
|
|
{
|
|
"attendee_email": frappe.session.user,
|
|
"event": campaign_doc.event,
|
|
"docstatus": 1,
|
|
},
|
|
"name",
|
|
)
|
|
|
|
lead = frappe.get_doc(
|
|
{
|
|
"doctype": "CRM Lead",
|
|
"first_name": first_name,
|
|
"email": frappe.session.user,
|
|
"status": "New",
|
|
"buzz_campaign": campaign,
|
|
"event_ticket": ticket,
|
|
}
|
|
)
|
|
lead.insert(ignore_permissions=True)
|