Files
joshuaandClaude Opus 5.5 f33d04a57c Add bundle types: pick any, buy X get Y free, group discount
Bundles get a kind: exact (as before), mix (any setSize of the qualifying tickets for
a set price), free (cheapest freeCount in each set free) and group (setSize or more
qualifying tickets at a per-ticket price or % off, applied once). The supervisor page
has a type picker, buyers see a one-line description of each deal, and the Complete
bundle cards handle every type. Demo: Family Fun Day adds a Lunch for four deal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 16:21:05 +02:00

192 lines
9.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Demo data
`npm run set-demo` replaces everything in the database with a realistic set of demo data,
for showing the platform to other churches. It stages six upcoming events with
registrations, payments, tickets and people, and ten completed events from the past year
with door sales, check-ins, feedback and cashups. That way every dashboard, report,
check-in and cashup screen has something to show.
> **This deletes real data.** Run it only against a database you're happy to wipe, such
> as a dedicated demo deployment. It is not something to run on a live church site.
---
## Running it
```bash
cd backend
npm run set-demo # shows the database it will wipe and asks you to type "demo"
npm run set-demo -- --yes # no prompt (for scripts)
```
The script uses `DATABASE_URL` from `backend/.env`. To point it at another database
for one run, set it in the environment first. A value already in the environment wins
over `.env`:
```bash
DATABASE_URL="postgresql://user:pass@host:5432/hope_demo" npm run set-demo
```
It takes about five seconds and is safe to re-run as often as you like. Each run wipes the
previous demo and creates it again.
### Before you run it
- The admin account must already exist. By default that's `joshua@crosscode.co.za`.
To keep a different account, set `DEMO_ADMIN_EMAIL`:
```bash
DEMO_ADMIN_EMAIL="admin@example.org" npm run set-demo
```
If the account isn't found, the script stops before changing anything.
- Migrations must be up to date (`npm run prisma:deploy`).
---
## What it keeps and what it clears
| Kept | Cleared |
|---|---|
| Site settings (`AppSetting`): org name, branding, SMTP, Yoco, WhatsApp, legal pages | Every event, with its options, variants, early-bird tiers, sections, forms, costs and cash-ups |
| The admin account, including its login history, trusted devices and 2FA | Every other user |
| Migration history | Registrations, payments, tickets, scans, coupons, waitlists, consents, cabins, feedback, analytics, audit log |
| Uploaded files on disk (`public/uploads`) | Queued scheduled emails/WhatsApps (`data/scheduled-emails.json`) |
The wipe and the new data are written in one database transaction. If that fails,
nothing changes. The past events' cashups run straight afterwards, through the same
code as the cashup screen. If one of those fails, re-run the script.
---
## What it creates
### Upcoming events
All dates are worked out from the day you run it, so these events are always upcoming.
Registrations are spread over the past month, as if sign-ups have been coming in.
The data is generated from a fixed seed, so every run looks the same.
| When (from today) | Event | Shows off |
|---|---|---|
| ~1½ weeks, Sat 07:30 | **Men's Breakfast: Built to Last** | **Sold out** (60 places), 5 people on the **waitlist**, card / cash / EFT payments recorded by staff, 2 cancelled registrations |
| ~2½ weeks, Sat 10:00 | **Family Fun Day** | Adult and child tickets with a **bundle deal** (Family deal: 2 adults + 3 kids for R220 instead of R280, used by about a third of registrations) and a **pick-any** deal (Lunch for four: any 4 lunch packs for R220), a non-ticket **add-on** (lunch pack), a fixed-amount **coupon** (`FUNDAY20`), entry and food-stall check-in sections |
| ~5 weeks, Fri–Sat | **Rooted Women's Conference** | Two **early-bird tiers** (one already expired, one still open) plus a Saturday-only ticket, T-shirt **variants** (S–XXL), a **required sign-up form** (dietary needs, workshop choice, prayer requests), a 20% **group coupon** (`ROOTED20`) used by 4 registrations |
| ~7 weeks, Mon–Thu | **Ignite Summer Camp** | **Named attendees** (campers aged 12–17), signed **parental consent**, gender collected, **deposits and part-payments**, campers placed in boys' and girls' **cabins** with staff leaders, **sponsor-a-spot** donations towards a 10-spot goal |
| ~8 weeks, Sun 19:00 | **Carols by Candlelight** | A **free event** with group tickets (1–6 people), plus general **donations** |
| ~14 weeks, Fri–Sun | **Marriage Enrichment Weekend** | Room-type **variants with limited stock**, early-bird prices per room type, a per-person spa add-on, R1 000 EFT deposits |
Each event also gets:
- A cover photo (from Unsplash, which `next.config` already allows) and a contact person.
- **Costs**, both once-off and per item, so the profit view and cash-up have figures.
- About 30 days of **funnel analytics** (views → started → submitted → paid) by source
(WhatsApp, Facebook, Instagram, email, direct) and device.
Paid registrations have tickets with QR codes, the same way the app issues them once
payment is complete. Part-paid and pending ones don't have tickets yet.
### Past events
Ten completed events spread over the past year, for showing cashups and the historical
reports. Each one has:
- Online sales in the five weeks before the event, with a few unpaid no-shows.
- **Door sales** on the day, recorded by the staff team (cash and card machine).
- **Check-ins**: about 90% of tickets scanned, with the odd group arriving short.
- **Staff cash counts** with note and coin denominations.
Some events also have refunds, donations and post-event feedback.
The cashups cover every path through the cashup screen:
| When | Event | Cashup to demo |
|---|---|---|
| ~11 months ago | **Church Family Picnic** | **Quick closed** (no settlement) |
| 24 Dec | **Christmas Eve Candlelight Service** | Free event with card and cash **donations**. Card settled net of **Yoco fees**, collection banked |
| Easter Sunday | **Easter Sunrise Service & Breakfast** | Cash banked less a **cash deposit fee** (bank charge), card fees, a **refund** |
| Mother's Day | **Mother's Day Brunch** | **Closed with R50 cash short → reopened → recounted → closed again.** The history shows all three steps |
| ~3½ months ago | **Youth Winter Camp** | Deposits and balances. **EFT pending** (POPs) at close, later **marked received** without reopening |
| ~2½ months ago | **Men's Golf Day** | Card-heavy, card fees written off, a refund |
| ~2 months ago | **Kids Holiday Club** | Cash-heavy door sales. **R20 cash shortfall written off**, **R500 kept on hand** as float |
| ~6 weeks ago | **Women's High Tea** | Closed with **R30 unaccounted**, shown in red |
| ~4 weeks ago | **Missions Fundraiser Dinner** | **R2 600 EFT still pending** (POP received, not cleared). Shows "Mark received" / "Write off" |
| ~10 days ago | **Worship Night** | **Still open, with the cash already counted.** Use it to do a cashup live in the demo |
Easter, the Youth Camp, the Holiday Club and the High Tea have **feedback forms** with
ratings and comments. Funnel stats older than 90 days are stored as daily totals, the
way the app rolls them up overnight, so the funnel report covers the whole year.
### Totals
A typical run gives roughly:
| | |
|---|---|
| Users | 130 (plus the admin) |
| Registrations | ~700 (paid, part-paid, pending, cancelled and refunded) |
| Payments | ~640, including donations, sponsorships and refunds |
| Tickets | ~780, with ~490 check-ins |
| Named campers | ~45 |
| Feedback responses | ~80 |
| Funnel rows | ~4 400 |
---
## Logging in
Every demo account uses the password **`Demo@1234`**.
| Role | Email | Good for showing |
|---|---|---|
| Supervisor | `lerato.khumalo@example.com` | Event setup, reports, coupons, viewing cashups |
| Staff | `sipho.dlamini@example.com` | At-the-door payments and check-in scanning |
| Staff | `anri.botha@example.com` | |
| Staff | `daniel.pillay@example.com` | |
Closing, reopening and settling pending money in a cashup are admin-only, so do the
live Worship Night cashup as your own admin account.
The other 126 people are ordinary members (role `user`), with emails like
`thandi.mokoena@example.com`. Log in as one of them to show the member view: their
registrations, tickets and payments. Any registrant's email from an event's attendee
list works.
Your own admin account keeps its existing password.
---
## Safety
- **Nothing is sent.** The script writes straight to the database and never calls the
email, WhatsApp or Yoco code.
- **Nothing can reach a real person later.** Demo people use `@example.com` addresses
(a reserved domain that can't receive mail) and have **no phone numbers**, so
reminders or broadcasts sent during a demo go nowhere. All parental consents are
already signed, so no consent reminders go out.
- Card payments have made-up Yoco ids (`demo_…`). They don't exist in Yoco, so leave
the demo site's Yoco settings in test mode or empty.
---
## Customising
Everything lives in [`scripts/set-demo.js`](scripts/set-demo.js). There is one
`build…()` function per event (`buildMensBreakfast`, `buildYouthCamp`, …), and
`main()` decides which ones run. To change an event's name, prices, ticket types or
number of registrations, edit its function. To add an event, write a new `build…()`
and add it to the list in `main()`. `addBundle(event, { name, price }, [[option, quantity], …])`
adds a bundle deal. Registrations added after it get the deal automatically, worked out
the same way as the app does it.
The past events are in `buildPastEvents()`, one spec each: dates, ticket types, costs,
how many people bought online and at the door, and a `cashup` that says how it was
closed. Supported closes:
- `'quick'`
- `'full'`, with `lines` saying where the money ended up
- `'reclose'`
- `'none'`, which leaves the event open
A full close can add `later` to settle pending money after closing. The helpers
`cardLine`, `eftLine` and `cashLine` build the usual settlements: card net of Yoco fees,
EFT with an optional pending POP, and cash banked with an optional float, deposit fee or
shortfall.