vinindkoeb/CLAUDE.md
carsten 589dcbcdf5 Scaffold FastAPI + SQLModel + Alembic project, Postgres-backed
Config-driven DATABASE_URL shared by the app and Alembic's env.py
(fixes v1's config drift), psycopg3 driver, auto-discovering
app/models package for SQLModel.metadata, and a /health endpoint
that exercises the DB dependency end-to-end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 15:57:44 +02:00

3.6 KiB

Fælles Vinindkøb — Projektplan

Formål

Erstatte manuel administration (regneark til betalingstracking, direkte databaseredigering for nye runder/deltagere/vine) med en rigtig admin-backend, samt automatisk betalings-mail til deltagere. Arkitekturen designes fra start til at kunne understøtte flere organisationer (fx vinbonden Horcher-familiens egne ruter i Frankrig/Belgien), selvom kun én organisation/rute er i drift i dag.

Tech stack

  • Backend: FastAPI + SQLModel + PostgreSQL + Alembic (migrations)
  • Frontend: React (admin-UI) + eksisterende offentlige bestillingsside (fortsat intet login for deltagere)
  • Mail: SMTP-udbyder — afklares

Datamodel

  • WineCategory — global, delt af ALLE organisationer/ruter (samme vinbonde, samme kategoristruktur: TRADITION, SELECTION, LES IMPERTINENTS, GRANDS CRUS, VENDANGES TARDIVES, CREMANT, MAGNUM, SANS ALCOOL osv.)

    • navn, sortering
  • Organization — administrativ gruppe (fx "Fælles Vinindkøb DK", "Horcher Frankrig")

    • navn
  • User — admin-login, tilhører en Organization

    • simpelt login v1 (password-hash + session/JWT), struktureret så passkey/WebAuthn kan tilføjes senere uden brud
  • Route — tilhører en Organization (fx "Tyskland" for jer; "Belgique" / "Paris" / "Massif Central" for Horcher-familien)

    • navn, mødested/kontaktperson/telefon (fritekst, forudfyldes fra forrige runde)
  • Participant — tilhører en Route

    • navn, mail, telefon, is_active
  • PurchaseRound — tilhører en Route

    • sæson (forår/efterår), år, status (kladde/åben/lukket)
    • eur_dkk_rate (valgfri — ikke alle organisationer skal omregne)
    • intro_text, pickup_info_text (forudfyldes fra forrige runde, kun dato/sæson skal typisk rettes)
  • WineOffering — tilhører PurchaseRound + (global) WineCategory

    • navn (inkl. størrelse, fx "75cl" — ikke separat felt v1)
    • vintage/årgang, pris (EUR)
    • is_organic (boolean — sat via checkbox, kilde: AB-mærket på producentens prisliste)
  • Order — deltager + runde

    • payment_status (ubetalt/betalt), paid_at
  • OrderLine — ordre + wine_offering + antal

Mail-events

  1. Runde åbnes → mail til alle aktive deltagere på ruten
  2. Ordre afgivet → kvittering til deltager + notifikation til admin (findes allerede)
  3. Nyt: Admin markerer ordre "betalt" → automatisk kvitteringsmail til deltager SMTP: Postal (selvhostet)

Fase 1 — nuværende scope

  1. Sæt FastAPI + SQLModel + PostgreSQL op med Alembic
  2. Definér modellerne ovenfor (inkl. Organization/Route-hierarki, selvom kun én organisation/rute findes i dag)
  3. Manuel dataindtastning: deltagere (og evt. en aktiv runde) fra MongoDB til PostgreSQL
  4. Simpelt login til User
  5. Admin CRUD: deltagere (inkl. aktiv/inaktiv — erstatter "tom bestilling"-tricket)
  6. Admin CRUD: runder + vinliste, med "kopiér fra forrige runde" (alle felter, inkl. kategori, forudfyldt og frit redigerbare)
  7. "Annoncér runde": skabelonmail til alle aktive deltagere på ruten
  8. Flyt offentlig bestillingsformular til nyt API (fortsat uden login)
  9. Admin-ordreoversigt pr. runde med "markér betalt" → trigger mail #3
  10. Genskab afhentningsliste (HTML-udtræk til print) mod ny datamodel
  11. Simpelt React admin-UI til pkt. 5, 6, 7, 9
  12. Testkør en rigtig runde gennem hele flowet

Fase 2 — senere

  • enablebanking.com: automatisk match af indbetalinger mod ubetalte ordrer → kalder samme "markér betalt"-logik
  • Passkey-login til admin
  • Rute-vælger på bestillingssiden (når flere ruter er aktive samtidig)
  • sold_only_by_case-flag + validering, hvis det bliver relevant