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:
2026-08-21 10:26:49 +02:00
co-authored by Claude Sonnet 5
parent e9cb238ce1
commit bc64069021
13 changed files with 835 additions and 82 deletions
@@ -1747,6 +1747,20 @@ const saveFormDraft = async (req, res) => {
}
};
// @desc Re-send the current invoice PDF for one of the caller's own registrations
// @route POST /api/registrations/:id/send-invoice
// @access Private
const sendInvoice = async (req, res) => {
try {
const { channel } = req.body || {};
const { sendInvoiceToUser } = require('../utils/notifications');
await sendInvoiceToUser(req.params.id, req.user.id, channel);
res.json({ message: channel === 'whatsapp' ? 'Invoice sent to WhatsApp.' : 'Invoice emailed.' });
} catch (error) {
res.status(error.statusCode || 400).json({ message: error.message });
}
};
module.exports = {
createRegistration,
getRegistrations,
@@ -1761,4 +1775,5 @@ module.exports = {
replaceFormResponses,
getFormDraft,
saveFormDraft,
sendInvoice,
};