Webhook: capture donor adult ticket names (Tickets 2026 update)

The updated Tickets 2026 form adds two donor (voucher) adult-ticket
name groups, names_Donor_1 / names_Donor_2, for the free adult
admissions. These were already counted via
item_quantity_adult_ticket_donor but their attendee names weren't
captured — added them to the adult-name list so they show at the gate.
Doc updated (new name fields + note that pure pricing line items and
payment_donor_voucher1/2 are ignored; the vouchers hidden count is
authoritative). No other schema changes needed — counts, extras,
donor, ice, and voucher handling already matched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hank 2026-07-17 04:33:21 +00:00
parent 1ba3f9ad1c
commit e86651723d
2 changed files with 11 additions and 3 deletions

View file

@ -5,8 +5,15 @@ import { createTicket } from "../ticketService.js";
import { renderQrPng } from "../services/qrcode.js";
import { safeEqual, nameGroup, qty, selected, addressLine } from "../fluentforms.js";
// Adult name field bases, in order (purchaser first).
const ADULT_NAME_BASES = ["names", "names_1", "names_2", "names_3", "names_4", "names_5", "names_6", "names_7", "names_8", "names_9"];
// Adult name field bases, in order: purchaser first, then additional regular
// adults (Adult Ticket #2#10), then donor adult tickets (Adult Donor #1#2).
// Donor tickets are the free/voucher adult admissions; their names live in the
// separate names_Donor_* groups but are still adults who need a gate pass.
const ADULT_NAME_BASES = [
"names",
"names_1", "names_2", "names_3", "names_4", "names_5", "names_6", "names_7", "names_8", "names_9",
"names_Donor_1", "names_Donor_2",
];
export async function webhookRoutes(app: FastifyInstance): Promise<void> {
const handler = async (req: any, reply: any) => {

View file

@ -13,6 +13,7 @@ interface Field {
const FIELDS: Field[] = [
{ key: "names", req: "required", type: "name (compound)", desc: "Purchaser / Adult #1 — object {first_name, middle_name, last_name}. Also accepts flat names[first_name] keys." },
{ key: "names_1 … names_9", req: "optional", type: "name (compound)", desc: "Additional adult attendee names (Adults #2#10). Empty groups are ignored. Stored as the adult-name list shown at the gate." },
{ key: "names_Donor_1 / names_Donor_2", req: "optional", type: "name (compound)", desc: "Donor (voucher) adult ticket names — the free adult admissions. Counted via item_quantity_adult_ticket_donor and added to the gate name list." },
{ key: "email", req: "optional", type: "email", desc: "Purchaser email — the QR ticket is sent here (FluentForms sends the receipt separately)." },
{ key: "address_1", req: "optional", type: "address (compound)", desc: "Mailing address object; joined into one line." },
{ key: "item_quantity_adult_ticket_reg", req: "required", type: "quantity", desc: "Adult tickets (regular)." },
@ -125,7 +126,7 @@ const PAGE = `<!doctype html>
<thead><tr><th>Key</th><th>Required</th><th>Type</th><th>Description</th></tr></thead>
<tbody>${rows}</tbody>
</table>
<p class="sub">Compound name fields arrive as objects (<code>names: {first_name,}</code>) or flattened <code>names[first_name]</code> keys both handled. Quantity/payment fields accept numbers, money strings ("$40.00"), or <code>{quantity}</code> objects.</p>
<p class="sub">Compound name fields arrive as objects (<code>names: {first_name,}</code>) or flattened <code>names[first_name]</code> keys both handled. Quantity/payment fields accept numbers, money strings ("$40.00"), or <code>{quantity}</code> objects. Counts come from the <code>item_quantity_*</code> fields, so pure pricing line items (<code>payment_adult_reg</code>, <code>payment_youth_*</code>, <code>payment_kids_free</code>, <code>payment_donor_voucher1/2</code>, <code>custom-payment-amount</code>/Tax) are ignored the <code>vouchers</code> hidden count is authoritative for donor vouchers.</p>
<h2>Idempotency</h2>
<p>Send a stable <code>id</code> / <code>submission_id</code>. A repeat returns <code>{"status":"duplicate"}</code> without creating a second ticket or re-emailing safe for retries and double-submits.</p>