chahinebrini 68074aa7b7 feat(diga): redeem-code endpoint + 10 test codes seeded
DiGA-Pfad-Foundation: User mit Rezept-Code löst im Onboarding ein, wird auf
plan='legend' hochgestuft (Default), Onboarding-Step springt auf 'done',
diga_code_redeemed_at als Audit-Trail. Trial-Modell wird übersprungen.

- Prisma model DigaCode (code unique, expires_at, used_at, used_by_profile_id,
  grants_plan, notes, label)
- Profile.digaCodeRedeemedAt für Reverse-Audit
- Migration 20260517_add_diga_codes mit Table + FK + Index
- Seed: REBREAK-TEST-001..010 (single-use, reset via SQL für erneutes Testen)
- POST /api/onboarding/redeem-diga-code — atomare Transaction, klare 400-Errors
  (not_found | already_used | expired | invalid_input)

Frontend (Duo-Onboarding) dockt später an — diese Backend-Foundation steht.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 15:52:53 +02:00

49 lines
3.1 KiB
SQL

-- DiGA-Codes (Rezept-Einlösung für Krankenkassen-Pfad) + Profile-Audit-Feld.
--
-- Codes werden im Onboarding eingelöst → Plan hochstufung (Default 'legend')
-- + Trial-Modell übersprungen. Reverse-Lookup über used_by_profile_id.
--
-- Test-Codes werden in DIESER Migration mit-geseeded (REBREAK-TEST-001..010).
-- Wiederverwendung nur per manuellem SQL-Reset.
-- ─── Profile-Audit-Feld ──────────────────────────────────────────────────────
ALTER TABLE "rebreak"."profiles"
ADD COLUMN IF NOT EXISTS "diga_code_redeemed_at" TIMESTAMPTZ NULL;
-- ─── DiGA-Codes-Tabelle ─────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS "rebreak"."diga_codes" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"code" TEXT NOT NULL,
"label" TEXT,
"expires_at" TIMESTAMPTZ,
"used_at" TIMESTAMPTZ,
"used_by_profile_id" UUID,
"grants_plan" TEXT NOT NULL DEFAULT 'legend',
"notes" TEXT,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT "diga_codes_pkey" PRIMARY KEY ("id"),
CONSTRAINT "diga_codes_code_unique" UNIQUE ("code"),
CONSTRAINT "diga_codes_used_by_fk" FOREIGN KEY ("used_by_profile_id")
REFERENCES "rebreak"."profiles"("id") ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS "diga_codes_used_by_idx"
ON "rebreak"."diga_codes"("used_by_profile_id");
-- ─── Seed: 10 Test-Codes ────────────────────────────────────────────────────
INSERT INTO "rebreak"."diga_codes" ("code", "label", "grants_plan", "notes") VALUES
('REBREAK-TEST-001', 'test_batch_2026-05', 'legend', 'Internal test code (single-use, reset via SQL)'),
('REBREAK-TEST-002', 'test_batch_2026-05', 'legend', 'Internal test code (single-use, reset via SQL)'),
('REBREAK-TEST-003', 'test_batch_2026-05', 'legend', 'Internal test code (single-use, reset via SQL)'),
('REBREAK-TEST-004', 'test_batch_2026-05', 'legend', 'Internal test code (single-use, reset via SQL)'),
('REBREAK-TEST-005', 'test_batch_2026-05', 'legend', 'Internal test code (single-use, reset via SQL)'),
('REBREAK-TEST-006', 'test_batch_2026-05', 'legend', 'Internal test code (single-use, reset via SQL)'),
('REBREAK-TEST-007', 'test_batch_2026-05', 'legend', 'Internal test code (single-use, reset via SQL)'),
('REBREAK-TEST-008', 'test_batch_2026-05', 'legend', 'Internal test code (single-use, reset via SQL)'),
('REBREAK-TEST-009', 'test_batch_2026-05', 'legend', 'Internal test code (single-use, reset via SQL)'),
('REBREAK-TEST-010', 'test_batch_2026-05', 'legend', 'Internal test code (single-use, reset via SQL)')
ON CONFLICT ("code") DO NOTHING;