# Del 7 – Datamodell Källa: `packages/database/src/schema/` (Drizzle). Genererad SQL: `infrastructure/migrations/0000_*.sql` – **54 tabeller**, 38 enums, FK:er och index. Kör: `pnpm db:generate` (ny migration ur schemat), `pnpm db:migrate`, `pnpm db:seed`. ## Domänöversikt (tabeller per fil) | Fil | Tabeller | Nyckelidéer | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | users.ts | users, user_credentials, refresh_tokens, user_health_profiles, user_preferences, user_consents | Hälsodata i EGEN tabell, exponeras aldrig via hushålls-API (§7/§56). Refresh-tokens hashade med familje-id → stöldskydd. Samtycken som (user, kind)-PK med tidsstämplar (§33). | | households.ts | households, household_members, storage_locations | invite_code unik. portion_factor per medlem (§7). Platser = kyl/frys/skafferi/…/custom med sublocations[] (§8). | | ingredients.ts | canonical_ingredients, substitutions | Navet för normalisering (§9). Näring per 100 med provenance-JSON (aldrig AI-påhitt). Diet-/allergiflaggor för deterministisk filtrering. shelf_life_guidance per platstyp (§13). | | products.ts | products | Versionshantering: (gtin, version) unik, aktuell rad = valid_to IS NULL (§11, §61.12). | | inventory.ts | inventory_items, inventory_transactions | Transaktioner är sanningen (§8): typade (+purchase/−cook_use/−discard/…), value_minor på discard → svinnvärde (§26). Saldo cachas på posten. Index på (household, best_before). | | recipes.ts | recipe_source_registry, recipes, recipe_ingredients, recipe_steps, recipe_ratings, recipe_favorites, recipe_cooks, recipe_similarities | Source registry för juridik (§15). DNA som jsonb (§16). variant_of/forked_from (§16/§35). Status-flöde §35. Similarity-klassning §36. Steg med timer/temperatur för Cooking Mode. | | meals.ts | meals, meal_boxes | Näring som snapshot-jsonb (historik ändras inte när recept ändras). Estimatintervall för tallriksfoto (§22). Matlådor med portions_remaining + recommended_use_by (§24). | | planning.ts | week_plans, week_plan_entries, shopping_lists, shopping_list_items | Planpost kan peka på recept ELLER matlåda. reschedule_reason_sv för dynamisk omplanering (§25). Lista: store_section, origin (plan/manual/…) (§27). | | receipts.ts | receipts, receipt_lines, price_observations | Kvittorader med confidence + verified (§12). Prishistorik → budget/prognoser. | | scans.ts | scan_jobs, ai_corrections | Jobbstatus inkl. awaiting_confirmation. Korrigeringar med consent_snapshot → träningsdata endast med samtycke (§33). model/prompt_version på allt (§9). | | memory.ts | memory_items, taste_signals, food_memories | Minnestyper §32, origin user_stated/observed/ai_inferred (§30), paused/verified per post. | | seasons.ts | season_events | Datadrivna datumregler (fixed/range/computed: midsommar/påsk/advent) (§28). | | subscriptions.ts | subscriptions, subscription_events, store_transactions, store_notifications, trials, ai_usage_counters | Backend source of truth (§47). (provider, original_transaction_id) unik → idempotent verifiering. Månadskvot för fair use (§45–46). | | platform.ts | domain_events, feature_flags, audit_logs, idempotency_keys, notifications, push_tokens, creator_stats, creator_follows, ai_eval_cases, ai_eval_runs, waste_summaries | Outbox-mönster (§55). Flaggor med rollout-procent. Audit (§56). Evals (§34). | ## Viktiga constraints & index (urval) Unika: users.email; households.invite_code; recipes.slug; (recipe,user) i ratings; (provider, original_transaction_id) i subscriptions (partial, NOT NULL); (gtin, version) i products (partial); (user, month) i ai_usage_counters; refresh_tokens.token_hash. Prestanda: inventory(household, best_before) för utgångslistan; meals(user, date) för Min dag; domain_events(published_at, occurred_at) för outbox; scan_jobs(status); recipes(status), (cuisine), (total_time). ## Regler för ändringar (spec §63) 1. Ändra schema i `packages/database/src/schema/` → `pnpm db:generate` → granska SQL:en. 2. Ingen destruktiv ändring utan: backup (deploy.sh tar dump), migration, dokumenterad rollback, kontroll efteråt. 3. Logga varje migration i tabellen nedan. ## Migrationslogg | # | Fil | Datum | Innehåll | Rollback | | ---- | ------------------------------- | ---------- | --------------------------------------------------------- | --------------------------- | | 0000 | 0000_dusty_lady_deathstrike.sql | 2026-08-02 | Initialt schema: 54 tabeller, 38 enums, samtliga index/FK | Drop databasen (pre-launch) | ## i18n-tillägg (M1–M6) - `households.currency_code` char(3) NOT NULL DEFAULT 'SEK' – pengagränsen går vid hushållet. - Alla pengakolumner är `integer` i minor units (`*_minor`). - `ingredient_translations` (ingredient_id, language_tag, name, aliases[], source, status) – unik per (ingrediens, språk). - `unit_translations` (unit_code, language_tag, abbreviation, name) – PK (kod, språk). - `recipe_translations` (recipe_id, language_tag, title, description, storage_guidance, status draft_ai|in_review|published, source, verification jsonb). - `recipe_step_translations` (recipe_id, language_tag, step_number, instruction, tip) – PK (recept, språk, steg). - `nutrition_display_profiles` (region_code PK, energy_display kcal|kj|both, salt_display salt|sodium, energy_label_key). - `allergen_market_rules` (region_code, allergen, must_highlight) – PK (region, allergen). - Trigramindex (pg_trgm) på canonical_ingredients.name_sv, ingredient_translations.name, recipes.title_sv; unaccent för accentokänslig sök.