Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8c075f5
feat: add ticket validation and check-in API endpoints
Inventor77 Oct 11, 2025
bd7ad7c
feat: add QR code scanning dependencies
Inventor77 Oct 11, 2025
dabf2fc
feat: add ticket validation composable
Inventor77 Oct 11, 2025
5c91c99
feat: add check-in scanner UI components
Inventor77 Oct 11, 2025
c6f2d6f
feat: add check-in scanner page and routing
Inventor77 Oct 11, 2025
a77d919
feat: add audio feedback assets for scanner
Inventor77 Oct 11, 2025
6891143
feat: update TypeScript definitions for new components
Inventor77 Oct 11, 2025
1362e37
Merge branch 'main' of github.com:BuildWithHussain/buzz into feat/che…
Inventor77 Oct 18, 2025
182c2a9
refactor(api): improve error handling in ticket validation and check-in
Inventor77 Oct 18, 2025
138bfa8
feat(permissions): add Frontdesk Manager role and permissions
Inventor77 Oct 18, 2025
078d801
refactor(components): improve EventSelector and QRScanner components
Inventor77 Oct 18, 2025
9349ef3
refactor(check-in): improve CheckInScanner page and routing
Inventor77 Oct 18, 2025
5d1c178
refactor(types): update composable and component types
Inventor77 Oct 18, 2025
4434b62
feat(security): add role-based access control to check-in API endpoints
Inventor77 Oct 18, 2025
676294a
refactor(api): update check-in logic to use date-based validation
Inventor77 Oct 19, 2025
ee45401
feat(doctype): update Event Check In to use date field instead of track
Inventor77 Oct 19, 2025
792081c
chore(deps): update frontend dependencies and auto-import configuration
Inventor77 Oct 19, 2025
40bf357
feat(composable): implement singleton ticket validation composable
Inventor77 Oct 19, 2025
8cbbcec
refactor(scanner): improve QR scanner UX and integrate with composable
Inventor77 Oct 19, 2025
2a0d026
refactor(modal): integrate ticket details modal with composable pattern
Inventor77 Oct 19, 2025
9a839c4
refactor(ui): simplify check-in scanner and remove track-based features
Inventor77 Oct 19, 2025
c58c0d1
fix(dashboard): resolve Node 18 build failure by updating package con…
Inventor77 Oct 20, 2025
ecbc3c5
feat(checkin-event): improve QR scanner and update API for event chec…
Inventor77 Oct 20, 2025
22016f1
feat: update UI components with theme support and logo improvements
Inventor77 Oct 24, 2025
be24c84
feat: implement QR code scanner with manual entry for ticket validation
Inventor77 Oct 24, 2025
081e719
feat: implement ticket details modal with check-in functionality
Inventor77 Oct 24, 2025
559fc61
feat: implement ticket validation composable with audio feedback
Inventor77 Oct 24, 2025
96bb2dd
fix: lint issues across multiple components and modules
Inventor77 Oct 25, 2025
a720cce
refactor: simplify ticket validation and check-in API logic
Inventor77 Oct 25, 2025
906f7c5
refactor: simplify ticket validation UI and remove redundant state
Inventor77 Oct 25, 2025
42abf1c
feat(security): add role-based access control to check-in scanner
Inventor77 Oct 25, 2025
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
97 changes: 92 additions & 5 deletions buzz/api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import frappe
from frappe import _
from frappe.utils import days_diff, format_date, format_time, today

from buzz.payments import get_payment_link_for_booking
Expand Down Expand Up @@ -126,10 +127,7 @@ def get_event_booking_data(event_route: str) -> dict:

# Ticket Add-ons
add_ons = frappe.db.get_all(
"Ticket Add-on",
filters={"event": event_doc.name},
fields=["*"],
order_by="title"
"Ticket Add-on", filters={"event": event_doc.name}, fields=["*"], order_by="title"
)

for add_on in add_ons:
Expand Down Expand Up @@ -188,7 +186,6 @@ def process_booking(attendees: list[dict], event: str) -> dict:
}



def create_add_on_doc(attendee_name: str, add_ons: list[dict]):
"""Create a new Attendee Ticket Add-on document."""
return frappe.get_doc(
Expand Down Expand Up @@ -685,3 +682,93 @@ def get_user_info() -> dict:
"user_image": user.user_image,
"roles": user.roles,
}


@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)
event_doc = frappe.get_cached_doc("FE 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
)

# Check if ticket is already checked in today
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)
# Format the check-in time for display
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))

# Get add-ons
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,
},
}


@frappe.whitelist()
def checkin_ticket(ticket_id: str) -> dict:
"""Check in a ticket for today."""
frappe.only_for("Frontdesk Manager", True)

# Validate the ticket for check-in
checkin_date = frappe.utils.today()
validation_result = validate_ticket_for_checkin(ticket_id)

# Create check-in record
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,
},
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
frappe.ready(function() {
frappe.ready(function () {
// bind events here
})
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import frappe


def get_context(context):
# do your magic here
pass
4 changes: 2 additions & 2 deletions buzz/buzz/web_form/propose_a_talk/propose_a_talk.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
frappe.ready(function() {
frappe.ready(function () {
// bind events here
})
});
1 change: 1 addition & 0 deletions buzz/buzz/web_form/propose_a_talk/propose_a_talk.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import frappe


def get_context(context):
# do your magic here
pass
22 changes: 16 additions & 6 deletions buzz/events/doctype/event_check_in/event_check_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"engine": "InnoDB",
"field_order": [
"event",
"track",
"date",
"column_break_fxzb",
"ticket",
"section_break_tt1x",
Expand Down Expand Up @@ -48,17 +48,16 @@
"fieldtype": "Column Break"
},
{
"fieldname": "track",
"fieldtype": "Link",
"label": "Track",
"options": "Event Track"
"fieldname": "date",
"fieldtype": "Date",
"label": "Date"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2025-07-29 20:05:31.851848",
"modified": "2025-10-18 16:51:30.061975",
"modified_by": "Administrator",
"module": "Events",
"name": "Event Check In",
Expand Down Expand Up @@ -91,6 +90,17 @@
"share": 1,
"submit": 1,
"write": 1
},
{
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Frontdesk Manager",
"share": 1,
"write": 1
}
],
"row_format": "Dynamic",
Expand Down
8 changes: 5 additions & 3 deletions buzz/events/doctype/event_check_in/event_check_in.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) 2025, BWH Studios and contributors
# For license information, please see license.txt

# import frappe
import frappe
from frappe.model.document import Document


Expand All @@ -15,9 +15,11 @@ class EventCheckIn(Document):
from frappe.types import DF

amended_from: DF.Link | None
date: DF.Date | None
event: DF.Link
ticket: DF.Link
track: DF.Link | None
# end: auto-generated types

pass
def before_insert(self):
if not self.date:
self.date = frappe.utils.today()
9 changes: 3 additions & 6 deletions buzz/events/doctype/event_sponsor/event_sponsor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# For license information, please see license.txt

import frappe

from frappe.model.document import Document


Expand All @@ -25,11 +24,9 @@ class EventSponsor(Document):
# end: auto-generated types

def validate(self):
already_exists = frappe.db.exists("Event Sponsor", {
"event": self.event,
"enquiry": self.enquiry,
"name": ("!=", self.name)
})
already_exists = frappe.db.exists(
"Event Sponsor", {"event": self.event, "enquiry": self.enquiry, "name": ("!=", self.name)}
)

if already_exists:
frappe.throw(frappe._("Sponsor for this enquiry already exists!"))
36 changes: 0 additions & 36 deletions buzz/events/doctype/fe_event/fe_event.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,6 @@ frappe.ui.form.on("FE Event", {
frm.add_web_link(`/events/${frm.doc.route}`);
}

frm.add_custom_button(__("Start Check In"), () => {
frappe.prompt(
{
label: "Track",
fieldname: "track",
fieldtype: "Link",
options: "Event Track",
get_query() {
return {
filters: {
event: frm.doc.name,
},
};
},
},
(values) => {
const track = values.track;
new frappe.ui.Scanner({
dialog: true, // open camera scanner in a dialog
multiple: false, // TODO: make multiple work (Danny says use a prev variable to avoid duplicate)
on_scan(data) {
const ticket_id = data.decodedText;
frm.call("check_in", { ticket_id, track })
.then(() => {
frappe.show_alert(__("Check In Complete!"));
frm.refresh();
})
.catch(() => {
frappe.utils.play_sound("error");
});
},
});
}
);
});

const button_label = frm.doc.is_published ? __("Unpublish") : __("Publish");
frm.add_custom_button(button_label, () => {
frm.set_value("is_published", !frm.doc.is_published);
Expand Down
13 changes: 4 additions & 9 deletions buzz/events/doctype/fe_event/fe_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ class FEEvent(Document):
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from frappe.types import DF

from buzz.events.doctype.event_featured_speaker.event_featured_speaker import EventFeaturedSpeaker
from buzz.events.doctype.schedule_item.schedule_item import ScheduleItem
from frappe.types import DF

about: DF.TextEditor | None
banner_image: DF.AttachImage | None
Expand Down Expand Up @@ -49,19 +50,13 @@ def validate_route(self):
self.route = frappe.website.utils.cleanup_page_name(self.title).replace("_", "-")

@frappe.whitelist()
def check_in(self, ticket_id: str, track: str | None = None):
frappe.get_doc({"doctype": "Event Check In", "ticket": ticket_id, "track": track}).insert().submit()

def after_insert(self):
self.create_default_records()

def create_default_records(self):
records = [
{"doctype": "Sponsorship Tier", "title": "Normal"},
{"doctype": "Event Ticket Type", "title": "Normal"}
{"doctype": "Event Ticket Type", "title": "Normal"},
]
for record in records:
frappe.get_doc({
**record,
"event": self.name
}).insert(ignore_permissions=True)
frappe.get_doc({**record, "event": self.name}).insert(ignore_permissions=True)
21 changes: 4 additions & 17 deletions buzz/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,11 @@

before_tests = "buzz.install.before_tests"

doc_events = {
"User": {
"after_insert": "buzz.utils.add_buzz_user_role"
}
}

fixtures = [
{"dt": "Role", "filters": {"name": "Buzz User"}}
]
doc_events = {"User": {"after_insert": "buzz.utils.add_buzz_user_role"}}

fixtures = [{"dt": "Role", "filters": {"name": ["Buzz User", "Frontdesk Manager"]}}]

user_invitation = {
"allowed_roles": {
"Event Manager": ["Buzz User"],
"Buzz User": ["Buzz User"]
}
}
user_invitation = {"allowed_roles": {"Event Manager": ["Buzz User"], "Buzz User": ["Buzz User"]}}


# Each item in the list will be shown as an app in the apps page
Expand Down Expand Up @@ -169,8 +158,6 @@
# Hook on document methods and events




# Overriding Methods
# ------------------------------
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ frappe.ui.form.on("Sponsorship Enquiry", {

frm.add_custom_button(__("Create Sponsor"), () => {
frm.call("create_sponsor").then(() => {
frappe.show_alert(__("Sponsor Created!"))
frappe.show_alert(__("Sponsor Created!"));
frm.refresh();
})
})
});
});
}
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def on_payment_authorized(self, payment_status: str):
"event": self.event,
"tier": self.tier,
"enquiry": self.name,
"website": self.website
"website": self.website,
}
).insert(ignore_permissions=True)
self.db_set("status", "Paid")
Expand All @@ -57,6 +57,6 @@ def create_sponsor(self):
"tier": self.tier,
"enquiry": self.name,
"website": self.website,
"country": self.country
"country": self.country,
}
).insert(ignore_permissions=True)
3 changes: 2 additions & 1 deletion buzz/ticketing/doctype/event_booking/event_booking.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ class EventBooking(Document):
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from buzz.ticketing.doctype.event_booking_attendee.event_booking_attendee import EventBookingAttendee
from frappe.types import DF

from buzz.ticketing.doctype.event_booking_attendee.event_booking_attendee import EventBookingAttendee

amended_from: DF.Link | None
attendees: DF.Table[EventBookingAttendee]
currency: DF.Link
Expand Down
Loading