Skip to content

refactor(api): split tickets into windows, services and typed responses - #301

Merged
harshtandiya merged 3 commits into
developfrom
refactor/api-tickets
Jul 29, 2026
Merged

refactor(api): split tickets into windows, services and typed responses#301
harshtandiya merged 3 commits into
developfrom
refactor/api-tickets

Conversation

@harshtandiya

Copy link
Copy Markdown
Collaborator

Stacked on #300 — read that first. Base refactor/api-forms, repoint to develop as the stack lands.

PR 5 of the buzz/api restructure. tickets/__init__.py 324 lines → 24, over windows.py (18), services.py (190), emails.py (71), schemas.py, exceptions.py.

The three window checks differed only in which Buzz Settings field they read, so they collapse into windows.is_window_open(event, cutoff_fieldname). Each also caught bare Exception and logged it; that guard was dead for the case it appeared to cover, since get_ticket_details loads the Buzz Event before any window is consulted, so a dangling event reference raises there first.

Payload reshape.can_transfer_ticket / can_change_add_ons / can_request_cancellation lose their @frappe.whitelist() — the dashboard only read them off the detail payloads — and now travel as plain booleans instead of {"can_transfer": true, "event_id": ...}. TicketDetails.vue and BookingDetails.vue unwrap one level less. This also fixes an existing inconsistency: TicketDetails flattened can_transfer_ticket in its transform but read can_change_add_ons nested.

Touches booking/ — three lines, because get_booking_details emits the same flags and create_add_on_doc lives here. PR 6 rewrites that file anyway; the plan's "disjoint folders" claim does not quite hold for this pair.

The three writes are methods=["POST"], same reasoning as #300.

Also deletes the dead downloadTicket stub: it referenced buzz.api.download_ticket, which does not exist, was never called, and left downloadingTicket permanently false behind the Download button's :loading.

Not changed: the transfer emails move to emails.py untouched, catch included — a failed send must not undo the transfer. get_ticket_details still refuses a booking owner who is not the attendee, while transfer_ticket allows them; that asymmetry is noted in the plan, not fixed here.

Checks: 26 Python tests (new file), 5 Playwright in a new tickets-* project pair, ruff and vue-tsc clean. get_ticket_details diffed against a copy of the pre-change module over six live tickets — identical once the can_* wrappers are normalised, except zoom_reference_doctype / zoom_reference_name are now always present as null rather than absent, and nothing in the dashboard reads either. Only the tickets-* projects were run; no other spec touches the two pages this changes.

🤖 Generated with Claude Code

@greptile-appsgreptile-appsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-appsgreptile-appsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-appsgreptile-appsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-appsgreptile-appsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-appsgreptile-appsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-appsgreptile-appsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-appsgreptile-appsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

Base automatically changed from refactor/api-forms to developJuly 29, 2026 17:16
harshtandiyaand others added 3 commits July 29, 2026 22:47
tickets/__init__.py held 324 lines: four endpoints, three near-identical
window checks, three trivial wrappers around them, 66 lines of inline email
HTML, and a get_ticket_details that assembled its payload inline.
The three window checks differed only in which Buzz Settings field they read,
so they collapse into windows.is_window_open(event, cutoff_fieldname) with
the three field names as constants. booking/ imports that directly now
instead of the wrappers. Each check also caught bare Exception and logged it
as an error; that guard was dead for the case it looked like it covered,
since get_ticket_details already loads the Buzz Event before any window is
consulted, so a dangling event reference raises there first.
can_transfer_ticket, can_change_add_ons and can_request_cancellation lose
their @frappe.whitelist(): the dashboard only ever read them off the ticket
and booking detail payloads. Those payloads now carry plain booleans rather
than {"can_transfer": true, "event_id": ...} wrappers, so TicketDetails.vue
and BookingDetails.vue unwrap one level less. That also fixes an
inconsistency where TicketDetails flattened can_transfer_ticket in its
transform but read can_change_add_ons nested.
TicketService owns the ticket-scoped work; the add-on and cancellation paths
key off other doctypes and stay module functions. The transfer emails move to
emails.py untouched, including their catch — a failed send must not undo the
transfer. Ten named errors replace the bare throws.
The three writes are now methods=["POST"], as in forms: frappe skips CSRF
outside UNSAFE_HTTP_METHODS and only auto-commits on POST/PUT.
Also drops the dead downloadTicket stub in TicketDetails.vue. It referenced
buzz.api.download_ticket, which does not exist, was never called, and left
downloadingTicket permanently false behind the Download button's :loading.
get_ticket_details was diffed against a copy of the pre-change module over
six live tickets: identical once the can_* wrappers are normalised, except
that zoom_reference_doctype and zoom_reference_name are now always present
as null rather than absent. Nothing in the dashboard reads either.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TicketDetails.vue and BookingDetails.vue had no end-to-end coverage at all,
which is awkward for a change that reshapes exactly what they read.
The spec asserts the payload the page itself fetched, via waitForResponse on
get_ticket_details, rather than issuing a second call. The page already holds
a valid session and CSRF token; a fresh APIRequestContext under the
attendee's storage state would be signing with the shared Administrator
token.
It asserts on the API rather than on the Transfer and Edit buttons because
those gate on doc.booking_status, which TicketDetails derives client-side
from data.booking — and get_ticket_details only returns a booking to the user
who owns it. A booking seeded over REST is owned by Administrator, so the
buttons stay hidden for a seeded attendee no matter how the windows resolve.
Asserting the flags at the wire is what this PR actually changed.
Seeding runs its own project, since get_ticket_details refuses anyone but the
attendee. Cleanup also removes bookings for the event: this setup no longer
creates any, but a leftover one blocks the event delete on re-runs.
tickets is added to the chromium project's testIgnore, or the spec would run
twice, the second time under the shared session.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The window tests move an event's start date back and forth, and they were
doing it on the test-route Buzz Event that test_forms and test_sponsorships
also read.
IntegrationTestCase rolls the database back but does not clear the document
cache (frappe/tests/classes/integration_test_case.py:117 rolls the
connections and nothing else). Writing a field, clearing the cache and then
reading it leaves the cache holding a value the rollback has since removed
from the row — visible to any later test module in the same process.
The tests now build their own event, so the rollback takes the whole document
with it. Buzz Settings is a single and cannot be owned per test, so its cache
entry is dropped on cleanup instead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@greptile-appsgreptile-appsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@harshtandiya
harshtandiya merged commit e218c1e into developJul 29, 2026
7 checks passed
@harshtandiya
harshtandiya deleted the refactor/api-tickets branch July 29, 2026 17:35
@github-actions

Copy link
Copy Markdown
Contributor

Backport failed for main, because it was unable to cherry-pick the commit(s).

Please cherry-pick the changes locally and resolve any conflicts.

git fetch origin main
git worktree add -d .worktree/backport-301-to-main origin/main
cd .worktree/backport-301-to-main
git switch --create backport-301-to-main
git cherry-pick -x e218c1ea0d1e62013a7f1d956e9591e49034b1a9

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport mainbackport to main branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@harshtandiya