Add PDF invoices/receipts with manual send, fix early-bird tier edit data loss
- Registration confirmations attach an invoice PDF (itemized breakdown, early-bird discount, balance due, Yoco pay-now link/QR) whenever a balance is outstanding; payment/donation confirmations attach a payment receipt PDF. Sent as an email attachment and, over WhatsApp, as the PDF itself with the existing message as its caption. - Users can also (re)send either document on demand: an "Invoice" button on the registration detail popup, and a "Receipt" button next to each payment there and on the Payment history page, each opening an Email/WhatsApp choice popup, via two new endpoints restricted to the registration/payment's own owner. - Fix: editing an event option's early-bird tiers deleted and recreated every tier for that option with brand-new ids, silently severing the appliedTierId link on all historical purchases (losing early-bird attribution and undercounting stock-limit usage) even for tiers the admin didn't touch. Tiers are now upserted by id. - Update the "My Events" help content and the API docs index for the new endpoints. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,7 @@ function toLocalDateTimeInputValue(input: string | number | Date | null | undefi
|
||||
return `${y}-${m}-${day}T${hh}:${mm}`;
|
||||
}
|
||||
|
||||
function EarlyBirdTiersEditor({ option, onSave }: { option: any; onSave: (tiers: { deadline: string; price: number; order?: number }[]) => void }) {
|
||||
function EarlyBirdTiersEditor({ option, onSave }: { option: any; onSave: (tiers: { id?: string; deadline: string; price: number; order?: number }[]) => void }) {
|
||||
const [rows, setRows] = React.useState<{ id?: string; deadline: string; price: string; order?: number }[]>([]);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [saving, setSaving] = React.useState(false);
|
||||
@@ -54,7 +54,7 @@ function EarlyBirdTiersEditor({ option, onSave }: { option: any; onSave: (tiers:
|
||||
try {
|
||||
const tiers = rows
|
||||
.filter((r) => !!r.deadline && String(r.price).trim() !== '')
|
||||
.map((r, i) => ({ deadline: new Date(r.deadline).toISOString(), price: parseFloat(r.price), order: typeof r.order === 'number' ? r.order : i }))
|
||||
.map((r, i) => ({ id: r.id, deadline: new Date(r.deadline).toISOString(), price: parseFloat(r.price), order: typeof r.order === 'number' ? r.order : i }))
|
||||
.filter((t) => t.price >= 0 && !isNaN(new Date(t.deadline).getTime()));
|
||||
onSave(tiers);
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user