/** * @swagger * components: * schemas: * VoucherStatus: * type: string * enum: [UNUSED, REDEEMED, VOID] * RedemptionStatus: * type: string * enum: [ACTIVE, UNDONE] * EligibleEvent: * type: object * required: [eventId, name, startDateTime, location, redemptionDeadline, deadlinePassed, isFull, collectAddress, requireAddress] * properties: * eventId: * type: integer * example: 42 * name: * type: string * example: "Adventskonzert 2026" * startDateTime: * type: string * format: date-time * location: * type: string * redemptionDeadline: * type: string * format: date-time * nullable: true * description: null when the event has no redemption deadline set * deadlinePassed: * type: boolean * isFull: * type: boolean * spotsRemaining: * type: integer * nullable: true * description: null when the event has no capacity cap set (uncapped) * collectAddress: * type: boolean * requireAddress: * type: boolean * VoucherValidation: * type: object * required: [code, status, maxGuests, eligibleEvents] * properties: * code: * type: string * example: "K7F3M9QX" * status: * $ref: '#/components/schemas/VoucherStatus' * maxGuests: * type: integer * example: 2 * prefillName: * type: string * nullable: true * prefillEmail: * type: string * nullable: true * eligibleEvents: * type: array * items: * $ref: '#/components/schemas/EligibleEvent' * RedeemGuest: * type: object * required: [name] * properties: * name: * type: string * example: "Erika Mustermann" * RedeemRequest: * type: object * required: [eventId, contactName, contactEmail, guests] * properties: * eventId: * type: integer * contactName: * type: string * contactEmail: * type: string * contactAddress: * type: string * nullable: true * guests: * type: array * items: * $ref: '#/components/schemas/RedeemGuest' * RedemptionSummary: * type: object * required: [redemptionId, code, eventId, status, contactName, contactEmail, guestCount, guests, redeemedAt] * properties: * redemptionId: * type: integer * code: * type: string * eventId: * type: integer * status: * $ref: '#/components/schemas/RedemptionStatus' * contactName: * type: string * contactEmail: * type: string * contactAddress: * type: string * nullable: true * guestCount: * type: integer * guests: * type: array * items: * type: string * redeemedAt: * type: string * format: date-time * confirmationEmailStatus: * type: string * enum: [SENT, FAILED] * nullable: true * description: Outcome of the redemption confirmation email. null until the send resolves. * VoucherCode: * type: object * required: [code, status, maxGuests, createdByEmail, createdAt, eligibleEventIds] * properties: * code: * type: string * status: * $ref: '#/components/schemas/VoucherStatus' * maxGuests: * type: integer * prefillName: * type: string * nullable: true * prefillEmail: * type: string * nullable: true * batchId: * type: string * nullable: true * createdByEmail: * type: string * createdAt: * type: string * format: date-time * eligibleEventIds: * type: array * items: * type: integer * EventTicketSettings: * type: object * required: [eventId, collectAddress, requireAddress, ticketsMailed] * properties: * eventId: * type: integer * capacity: * type: integer * nullable: true * redemptionDeadline: * type: string * format: date-time * nullable: true * collectAddress: * type: boolean * requireAddress: * type: boolean * description: Only meaningful when collectAddress is true. * ticketsMailed: * type: boolean * description: When false, the redemption confirmation email tells the guest their tickets await pickup at the Abendkasse instead. * EventStats: * type: object * required: [eventId, collectAddress, requireAddress, ticketsMailed, guestsUsed, unusedCodes, redeemedCodes, voidCodes] * properties: * eventId: * type: integer * capacity: * type: integer * nullable: true * redemptionDeadline: * type: string * format: date-time * nullable: true * collectAddress: * type: boolean * requireAddress: * type: boolean * ticketsMailed: * type: boolean * guestsUsed: * type: integer * spotsRemaining: * type: integer * nullable: true * unusedCodes: * type: integer * redeemedCodes: * type: integer * voidCodes: * type: integer * AuditLogEntry: * type: object * required: [auditId, code, adminEmail, action, createdAt] * properties: * auditId: * type: integer * code: * type: string * redemptionId: * type: integer * nullable: true * adminEmail: * type: string * action: * type: string * enum: [EDIT, VOID, UNDO] * changeSummary: * type: object * nullable: true * reason: * type: string * nullable: true * createdAt: * type: string * format: date-time */ export type VoucherStatus = 'UNUSED' | 'REDEEMED' | 'VOID'; export type RedemptionStatus = 'ACTIVE' | 'UNDONE'; export type AuditAction = 'EDIT' | 'VOID' | 'UNDO'; export interface EligibleEvent { eventId: number; name: string; startDateTime: Date; location: string; redemptionDeadline: Date | null; deadlinePassed: boolean; isFull: boolean; spotsRemaining: number | null; collectAddress: boolean; requireAddress: boolean; } export interface VoucherValidation { code: string; status: VoucherStatus; maxGuests: number; prefillName: string | null; prefillEmail: string | null; eligibleEvents: EligibleEvent[]; } export interface RedeemGuest { name: string; } export interface RedeemRequest { eventId: number; contactName: string; contactEmail: string; contactAddress?: string | null; guests: RedeemGuest[]; } export interface RedemptionSummary { redemptionId: number; code: string; eventId: number; status: RedemptionStatus; contactName: string; contactEmail: string; contactAddress: string | null; guestCount: number; guests: string[]; redeemedAt: Date; // null until the post-redemption confirmation email send resolves. confirmationEmailStatus: 'SENT' | 'FAILED' | null; } export interface VoucherCode { code: string; status: VoucherStatus; maxGuests: number; prefillName: string | null; prefillEmail: string | null; batchId: string | null; createdByEmail: string; createdAt: Date; eligibleEventIds: number[]; } export interface EventTicketSettings { eventId: number; capacity: number | null; redemptionDeadline: Date | null; collectAddress: boolean; requireAddress: boolean; ticketsMailed: boolean; } export interface EventStats extends EventTicketSettings { guestsUsed: number; spotsRemaining: number | null; unusedCodes: number; redeemedCodes: number; voidCodes: number; } export interface AuditLogEntry { auditId: number; code: string; redemptionId: number | null; adminEmail: string; action: AuditAction; changeSummary: Record | null; reason: string | null; createdAt: Date; }