Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buzz/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def can_request_cancellation(event_id: str | int) -> dict:
def get_event_booking_data(event_route: str) -> dict:
data = frappe._dict()
event_doc = frappe.get_cached_doc("Buzz Event", {"route": event_route})

data.booking_allowed = event_doc.is_booking_open
# Ticket Types
Comment on lines 120 to 124
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Call is_booking_open() instead of passing the method object.

event_doc.is_booking_open is a method on Buzz Event; assigning it directly will either serialize incorrectly or always truthy. This prevents proper booking closure and can break JSON serialization.

🐛 Fix
-	data.booking_allowed = event_doc.is_booking_open
+	data.booking_allowed = event_doc.is_booking_open()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def get_event_booking_data(event_route: str) -> dict:
data = frappe._dict()
event_doc = frappe.get_cached_doc("Buzz Event", {"route": event_route})
data.booking_allowed = event_doc.is_booking_open
# Ticket Types
def get_event_booking_data(event_route: str) -> dict:
data = frappe._dict()
event_doc = frappe.get_cached_doc("Buzz Event", {"route": event_route})
data.booking_allowed = event_doc.is_booking_open()
# Ticket Types
🤖 Prompt for AI Agents
In `@buzz/api.py` around lines 120 - 124, In get_event_booking_data, you're
assigning the method object event_doc.is_booking_open instead of its boolean
result; update the code in get_event_booking_data to call the method
(event_doc.is_booking_open()) and assign that boolean to data.booking_allowed so
serialization and booking checks work correctly; locate the assignment to
data.booking_allowed in the get_event_booking_data function and replace the
method reference with the method call on the event_doc instance.

available_ticket_types = []
published_ticket_types = frappe.db.get_all(
Expand Down
8 changes: 7 additions & 1 deletion buzz/events/doctype/buzz_event/buzz_event.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"column_break_cjby",
"end_date",
"end_time",
"booking_closes_on",
"section_break_zukm",
"short_description",
"about",
Expand Down Expand Up @@ -391,6 +392,11 @@
"fieldname": "card_image",
"fieldtype": "Attach Image",
"label": "Card Image"
},
{
"fieldname": "booking_closes_on",
"fieldtype": "Date",
"label": "Booking Closes On"
}
],
"grid_page_length": 50,
Expand Down Expand Up @@ -463,7 +469,7 @@
"link_fieldname": "event"
}
],
"modified": "2026-01-14 11:13:04.386770",
"modified": "2026-01-21 20:36:44.002422",
"modified_by": "Administrator",
"module": "Events",
"name": "Buzz Event",
Expand Down
27 changes: 27 additions & 0 deletions buzz/events/doctype/buzz_event/buzz_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import frappe
from frappe.model.document import Document
from frappe.utils import add_days, date_diff, getdate, today
from frappe.utils.data import time_diff_in_seconds

from buzz.utils import only_if_app_installed
Expand All @@ -23,9 +24,11 @@ class BuzzEvent(Document):
from buzz.proposals.doctype.sponsorship_deck_item.sponsorship_deck_item import SponsorshipDeckItem

about: DF.TextEditor | None
allow_editing_talks_after_acceptance: DF.Check
apply_tax: DF.Check
auto_send_pitch_deck: DF.Check
banner_image: DF.AttachImage | None
booking_closes_on: DF.Date | None
card_image: DF.AttachImage | None
category: DF.Link
default_ticket_type: DF.Link | None
Expand All @@ -45,6 +48,7 @@ class BuzzEvent(Document):
route: DF.Data | None
schedule: DF.Table[ScheduleItem]
short_description: DF.SmallText | None
show_sponsorship_section: DF.Check
sponsor_deck_attachments: DF.Table[SponsorshipDeckItem]
sponsor_deck_cc: DF.SmallText | None
sponsor_deck_email_template: DF.Link | None
Expand All @@ -65,6 +69,7 @@ def validate(self):
self.validate_schedule()
self.validate_route()
self.validate_tax_settings()
self.validate_booking_closes_on()

def validate_schedule(self):
end_date = self.end_date or self.start_date
Expand Down Expand Up @@ -119,6 +124,13 @@ def validate_route(self):
if self.is_published and not self.route:
self.route = frappe.website.utils.cleanup_page_name(self.title).replace("_", "-")

def validate_booking_closes_on(self):
if not self.booking_closes_on:
return
end_date = self.end_date or self.start_date
if date_diff(self.booking_closes_on, end_date) > 0:
frappe.throw(frappe._("Booking Closes On cannot be after the event end date"))

@frappe.whitelist()
def after_insert(self):
self.create_default_records()
Expand Down Expand Up @@ -175,3 +187,18 @@ def update_zoom_webinar(self):
}
)
webinar.save()

@property
def is_booking_open(self):
"""Returns True if booking is currently open for this event."""
current_date = getdate(today())

# Check custom booking close date first (per-event override)
if self.booking_closes_on:
return current_date <= getdate(self.booking_closes_on)

# Fall back to global setting
settings = frappe.get_cached_doc("Buzz Settings")
cutoff_days = settings.allow_booking_before_event_start_days or 0
cutoff_date = add_days(self.start_date, -cutoff_days)
return current_date <= getdate(cutoff_date)
Comment on lines +201 to +204
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against negative allow_booking_before_event_start_days.

A negative value would extend booking after the event start, breaking the intended rule. Consider clamping to >= 0 here (or validating in Buzz Settings).

✅ Suggested defensive clamp
-		cutoff_days = settings.allow_booking_before_event_start_days or 0
+		cutoff_days = max(int(settings.allow_booking_before_event_start_days or 0), 0)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
settings = frappe.get_cached_doc("Buzz Settings")
cutoff_days = settings.allow_booking_before_event_start_days or 0
cutoff_date = add_days(self.start_date, -cutoff_days)
return current_date <= getdate(cutoff_date)
settings = frappe.get_cached_doc("Buzz Settings")
cutoff_days = max(int(settings.allow_booking_before_event_start_days or 0), 0)
cutoff_date = add_days(self.start_date, -cutoff_days)
return current_date <= getdate(cutoff_date)
🤖 Prompt for AI Agents
In `@buzz/events/doctype/buzz_event/buzz_event.py` around lines 201 - 204, Clamp
the booking cutoff to non-negative so a negative
settings.allow_booking_before_event_start_days cannot extend booking past the
event; when you read settings = frappe.get_cached_doc("Buzz Settings") compute
cutoff_days = max(0, settings.allow_booking_before_event_start_days or 0) (or
validate earlier in the Buzz Settings model) before calling
add_days(self.start_date, -cutoff_days), keeping the rest of the logic with
cutoff_date, getdate and current_date unchanged.

16 changes: 15 additions & 1 deletion buzz/events/doctype/buzz_settings/buzz_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"allow_add_ons_change_before_event_start_days",
"column_break_hagy",
"allow_ticket_cancellation_request_before_event_start_days",
"column_break_hyfc",
"allow_booking_before_event_start_days",
"communications_tab",
"ticketing_emails_section",
"default_ticket_email_template",
Expand Down Expand Up @@ -142,13 +144,25 @@
"fieldname": "custom_fields_go_after_this",
"fieldtype": "HTML",
"label": "Custom Fields Go After This"
},
{
"fieldname": "column_break_hyfc",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "Stop accepting bookings X days before event starts. Set to 0 to allow booking until event start date. Can be overridden per event.",
"fieldname": "allow_booking_before_event_start_days",
"fieldtype": "Int",
"label": "Allow Booking Before Event Start (Days)",
"non_negative": 1
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2026-01-13 22:34:24.493305",
"modified": "2026-01-21 21:02:08.339821",
"modified_by": "Administrator",
"module": "Events",
"name": "Buzz Settings",
Expand Down
3 changes: 2 additions & 1 deletion buzz/events/doctype/buzz_settings/buzz_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ class BuzzSettings(Document):
from frappe.types import DF

allow_add_ons_change_before_event_start_days: DF.Int
allow_booking_before_event_start_days: DF.Int
allow_ticket_cancellation_request_before_event_start_days: DF.Int
allow_transfer_ticket_before_event_start_days: DF.Int
auto_send_pitch_deck: DF.Check
default_sponsor_deck_cc: DF.SmallText | None
default_sponsor_deck_email_template: DF.Link | None
default_sponsor_deck_reply_to: DF.Data
default_sponsor_deck_reply_to: DF.Data | None
default_ticket_email_template: DF.Link | None
support_email: DF.Data | None
# end: auto-generated types
Expand Down
7 changes: 7 additions & 0 deletions buzz/ticketing/doctype/event_booking/event_booking.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class EventBooking(Document):
# end: auto-generated types

def validate(self):
self.validate_booking_open()
self.validate_ticket_availability()
Comment on lines 42 to 44
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

is_booking_open must be called to enforce booking closure.

event.is_booking_open is a method, so the current check always evaluates truthy and never blocks bookings.

🐛 Fix
-		if not event.is_booking_open:
+		if not event.is_booking_open():
 			frappe.throw(frappe._("Bookings are closed for {0}").format(event.title))

Also applies to: 293-297

🤖 Prompt for AI Agents
In `@buzz/ticketing/doctype/event_booking/event_booking.py` around lines 42 - 44,
The booking-closure check is calling the method object instead of invoking it,
so change places where event.is_booking_open is used (notably in
validate_booking_open called from validate and the block around lines 293-297)
to call the method as event.is_booking_open() and use its boolean result to
enforce closure (raise the existing validation error/throw when False). Locate
and update any conditional(s) that currently reference event.is_booking_open to
call the method and keep the existing error message/exception behavior.

self.fetch_amounts_from_ticket_types()
self.set_currency()
Expand Down Expand Up @@ -288,3 +289,9 @@ def apply_coupon_if_applicable(self):
frappe.throw(_("No attendees with eligible ticket type for this coupon"))

self.total_amount = self.net_amount - self.discount_amount

def validate_booking_open(self):
"""Check if booking is open for this event."""
event = frappe.get_cached_doc("Buzz Event", self.event)
if not event.is_booking_open:
frappe.throw(frappe._("Bookings are closed for {0}").format(event.title))
19 changes: 18 additions & 1 deletion dashboard/src/pages/BookTickets.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,21 @@
<div class="w-8">
<Spinner v-if="eventBookingResource.loading" />
</div>
<div>
<!-- Booking closed message -->
<div
v-if="!eventBookingResource.loading && !eventBookingData.bookingAllowed"
class="flex flex-col items-center justify-center py-16"
>
<LucideCalendarX2 class="w-16 h-16 text-gray-400 mb-4" />
<h2 class="text-xl font-semibold text-gray-700 mb-2">
{{ __("Bookings Closed") }}
</h2>
<p class="text-gray-500 text-center">
{{ __("Bookings are no longer available for this event.") }}
</p>
</div>
<!-- Booking form -->
<div v-else>
<BookingForm
v-if="eventBookingData.availableAddOns && eventBookingData.availableTicketTypes"
:availableAddOns="eventBookingData.availableAddOns"
Expand All @@ -22,6 +36,7 @@
import { reactive } from "vue";
import BookingForm from "../components/BookingForm.vue";
import { Spinner, createResource } from "frappe-ui";
import LucideCalendarX2 from "~icons/lucide/calendar-x-2";

const eventBookingData = reactive({
availableAddOns: null,
Expand All @@ -30,6 +45,7 @@ const eventBookingData = reactive({
eventDetails: null,
customFields: null,
paymentGateways: [],
bookingAllowed: true,
});

const props = defineProps({
Expand All @@ -56,6 +72,7 @@ const eventBookingResource = createResource({
eventBookingData.eventDetails = data.event_details || {};
eventBookingData.customFields = data.custom_fields || [];
eventBookingData.paymentGateways = data.payment_gateways || [];
eventBookingData.bookingAllowed = data.booking_allowed;
},
Comment on lines 73 to 76
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against missing booking_allowed to avoid false “closed” state.

If the API omits the field or returns null, the UI flips to “Bookings Closed.” A nullish fallback keeps the default behavior intact.

🐛 Suggested fix
-		eventBookingData.bookingAllowed = data.booking_allowed;
+		eventBookingData.bookingAllowed = data.booking_allowed ?? true;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
eventBookingData.customFields = data.custom_fields || [];
eventBookingData.paymentGateways = data.payment_gateways || [];
eventBookingData.bookingAllowed = data.booking_allowed;
},
eventBookingData.customFields = data.custom_fields || [];
eventBookingData.paymentGateways = data.payment_gateways || [];
eventBookingData.bookingAllowed = data.booking_allowed ?? true;
},
🤖 Prompt for AI Agents
In `@dashboard/src/pages/BookTickets.vue` around lines 73 - 76, The assignment
eventBookingData.bookingAllowed = data.booking_allowed can flip the UI to
“Bookings Closed” when the API omits or returns null; change it to use a nullish
fallback so the existing default is preserved (e.g., use the nullish coalescing
pattern when setting bookingAllowed or explicitly check for undefined/null
before assignment in the BookTickets.vue code that assigns
eventBookingData.bookingAllowed).

onError: (error) => {
if (error.message.includes("DoesNotExistError")) {
Expand Down
Loading