Files
tilbudgivern/codemaps/data.md
T
f37adae2cb
CI - Test & Build / Lint & Type Check (push) Canceled after 0s
CI - Test & Build / Backend Unit Tests (push) Canceled after 0s
CI - Test & Build / Frontend Build (push) Canceled after 0s
CI - Test & Build / Security Scan (push) Canceled after 0s
CI - Test & Build / E2E Tests (Playwright) (push) Canceled after 0s
CI - Test & Build / CI Summary (push) Canceled after 0s
feat: deliver auditable Smart Pakke quote flow and free site geometry (#31)
* feat: move login credentials to a DB-backed users table with an admin management page

Replaces the hardcoded AUTH_USERNAME/AUTH_PASSWORD login check with a new
auth_accounts table (bcrypt-hashed passwords, admin/user roles). Adds
admin-only /api/users CRUD routes and a "Brugere" admin page in the
frontend for managing logins without redeploying. Removes the unused,
unmounted duplicate login route in src/routes/auth.js.

* docs: add architecture codemaps with diagrams for the whole system

Adds codemaps/architecture.md, backend.md, frontend.md, and data.md —
Mermaid-diagrammed design documentation verified against the live
codebase and database rather than assumed from CLAUDE.md. Covers the
unified-server.js request flow (mounted routers + ~183 inline routes),
68 backend services grouped by domain, the frontend's state-driven
view-switch (no React Router in practice despite BrowserRouter being
present), and the full 122-table DB schema with the auth_accounts vs
unrelated users table naming trap flagged explicitly. Links added from
the root README.

Co-Authored-By: Claude Sonnet 5 <[email protected]>

* feat: ship canonical roof quote workflow

* fix: keep migration dry-run idempotent

* [verified] feat: complete Smart Pakker management

* [verified] fix: ignore blank task dependencies

* [verified] fix: align package duplication with schema

* [verified] fix: enforce Discord status limits

* [verified] fix: link Smart Pakke materials safely

* [verified] fix: harden material link review

* [verified] feat: improve material matching

* fix: scope pitch validation to roof packages

* fix: support canonical snapshots on production schema

* [verified] fix: hide internal package metadata from PDF

* [verified] feat: deliver sales-ready customer PDF

* [verified] feat: ship sales-ready PDF with AI overview

* [verified] fix: authenticate project list requests

* [verified] fix: refresh project-list authentication

* [verified] fix: open existing project details

* [verified] fix: keep roof components searchable in builder

* [verified] fix: expose all Smart Package categories

* [verified] fix: authenticate project creation

* [verified] feat: make Smart Pakker the universal project flow

* [verified] feat: preview Smart Package contents

* [verified] test: keep generic release isolated from downpipe work

* feat: add first-class Smart Pakke rentals

* [verified] feat: add gutter and downpipe smart packages

* [verified] fix: prepare six-house gutter quote flow

* [verified] fix: open generic quotes without roof geometry

* [verified] fix: review generic quotes with authenticated APIs

* [verified] fix: calculate generic Smart Package quotes

* [verified] fix: return generic calculation breakdown

* feat: checkpoint generic signed snapshot validation with red-green tests

* feat: complete fail-closed generic quote approval and customer PDF flow

* feat: use generic signed snapshot in final review

* feat: redesign generic quote final review

* fix: harden generic review summaries

* feat: add auditable six-house package basis

* [verified] feat: finish auditable Smart Pakke UI

* [verified] fix: bind auditable quantity and price bases

* [verified] fix: keep six-house basis across package versions

* [verified] fix: complete smart package discovery management

* [verified] fix: simplify composition and generic scope

* [verified] test: keep explicit roof contracts fail closed

* [verified] fix: harden generic quote snapshots

* fix: make generic quote delivery customer safe

* [verified] fix: secure package catalog reads

* [verified] fix: close workspace provenance blockers

* fix: harden customer document language boundary

* [verified] fix: secure smart package internal reads

* fix: version package child mutations atomically

* feat: add generic customer quote text flow

* [verified] fix: allow manual customer numbers

* [verified] fix: expose optional roof geometry

* [verified] fix: rebase hydrated packages after geometry edits

* [verified] feat: add free editable site area map

* [verified] fix: harden map recovery and geocoding gate

* fix: bind map quantities to authoritative geometry

* fix: release geocoder lock before dispatch

* fix: separate roof and site geometry provenance

* fix: revoke stale admin authorization

* fix: migrate task geometry basis

* fix: make backend CI dependency-complete

* ci: seed isolated e2e login account

* fix: allow clean database bootstrap

* fix: skip indexes for optional tables

* test: use canonical mansard geometry in e2e

* [verified] fix(auth): enforce live operator boundary

* fix: fail close Ordrestyring offer transport

* fix(frontend): authenticate customer project requests

* fix: align canonical roof type contract

* [verified] fix: reconcile legacy package labor safely

* [verified] fix: audit site geometry deletion

* docs: add PR 31 reviewer guide

* docs: synchronize Obsidian vault

* docs: sync integrated reviewer guide to Obsidian

* ci: seed isolated auth account explicitly

* fix: close offer bootstrap and service readiness gaps

* fix: authenticate protected package callers

* fix: provision initial admin and disable generic send

* [verified] fix: close final quote release blockers

* [verified] fix: seed gutter packages before deployment

---------

Co-authored-by: alexpolo1 <[email protected]>
Co-authored-by: Claude Sonnet 5 <[email protected]>
2026-09-26 22:39:18 +02:00

72 lines
7.3 KiB
Markdown

<!-- generated: 2026-09-04 -->
# Data Models and Schema
MariaDB, database `tilbudgivern`. Verified live against `SHOW TABLES` / `DESCRIBE` on 2026-09-04 — **122 tables/views**, far more than the tracked migration files account for (see "How schema actually gets created" below). This document groups them by domain rather than listing all 122 in full DDL.
## How schema actually gets created (read this before adding a migration)
There is **no unified migration runner**. Schema comes from three uncoordinated sources:
1. **`backend/src/services/databaseService.js`** — contains ~37 inline `CREATE TABLE IF NOT EXISTS` statements that run automatically every time the server starts (`Database tables created/verified successfully` in the PM2 logs). This is the primary source for a large share of the schema.
2. **`database/migrations/`** — 11 standalone scripts (mix of `.sql` run manually via a MySQL client and `.js` scripts run with `node <file>.js`), timestamp-prefixed, each independent. No tracking table of "which migrations ran."
3. **`backend/migrations/`** — a *second*, separate migrations folder with its own independent set of `.sql`/`.js` files. Nothing unifies this with `database/migrations/`.
Plus `backend/sql/customer_project_system.sql`, a standalone schema file. When adding a table, check whether `databaseService.js` should own it (if the app must always have it present) or whether a one-off migration script is more appropriate — don't assume there's a single place new schema belongs.
## Table inventory by domain
**Smart packages** (the app's core reusable-package system) — `smart_packages`, `package_tasks`, `package_materials`, `smart_package_categories`, `smart_package_components`, `smart_package_materials`, `smart_package_steps`, `smart_package_tasks`, `smart_package_types`, `smart_package_usage`, `custom_packages`, `custom_installation_tasks`.
**A second, older/parallel package system** also exists: `material_packages` (with its own Excel-import provenance columns, `is_template`, `validation_status`, etc.) plus `project_packages`. It's not clearly deprecated in code — treat both systems as live until proven otherwise, and check which one a given feature actually reads before assuming "the" packages table.
**Customer / project lifecycle** — `customer_projects` (the actual "projects" table — **note: there is no table literally named `projects`**, despite that name appearing informally in some docs/comments), `customer_project_packages` (links `customer_projects` ↔ `smart_packages`), `project_smart_package_workspaces` (JSON workspace snapshots, versioned), `project_labor`, `project_materials`, `project_calculations`, `project_documents`, `project_history`, `project_quotes`, `project_rentals`, `project_tasks`, `project_time_calculations`, `project_types`, `project_analytics`.
**Roof / geometry** — `roof_geometry` (one row per project; roof type, dimensions, spær calculations, kvist/dormer fields), `roof_types`, `advanced_geometry_data`.
**Materials / pricing / suppliers** — `materials`, `material_categories`, `material_category_mapping`, `material_formulas`, `material_prices`, `material_analytics`, `material_performance_view`, `dynamic_materials`, `carpenter_materials`, `pricing_rules`, `price_database`, `prices`, `labor_prices`, `labor_tasks`, `suppliers`, `vendors`, `vendor_products_history`, `vendor_price_history`, `vendor_current_prices`, `vendor_import_log`.
**Supplier imports** — Bygma: `bygma_products`, `bygma_categories`, `bygma_product_groups`, `bygma_materials_cache`, `bygma_materials_mapping`, `bygma_price_history`, `bygma_import_log`, `v_bygma_import_stats`. Stark: `stark_materials_cache`. Håndværkpriser: `haandvaerkpriser_imports`. Generic: `import_logs`, `imported_quotes`, `installation_manuals`.
**Quotes / generation** — `quotes`, `generated_quotes`, `submitted_quotes`, `enhanced_quote_details`, `quote_feedback`, `v_enhanced_quotes`.
**Auth** — `auth_accounts` (**new, 2026-09-04**): `id`, `username`, `password_hash` (bcrypt), `role` enum(`admin`,`user`), `created_at`, `updated_at`, `last_login_at`. Owned by `backend/src/services/userService.js`.
**⚠️ `users` (pre-existing, unrelated to auth)** — `id`, `first_name`, `last_name`, `init`, `email`, `created_at`. This is an **employee roster synced from Ordrestyring** (written by `ordrestyringSyncService.js`), not a login table. The name collision with "who's allowed to log in" is a trap — the auth migration (`database/migrations/20260904_users_table.js`) deliberately created `auth_accounts` instead of reusing/renaming this table. Do not point login logic at `users`.
**Ordrestyring sync/cache** — `ordrestyring_calendar`, `ordrestyring_case_features`, `ordrestyring_case_latest`, `ordrestyring_case_material_snapshots`, `ordrestyring_data_quality_issues`, `ordrestyring_employee_types`, `ordrestyring_hours_normalized`, `ordrestyring_materials_normalized`, `ordrestyring_offer_line_snapshots`, `ordrestyring_offer_snapshots`, `ordrestyring_reference_cases`, `ordrestyring_reference_labor_entries`, `ordrestyring_reference_materials`, `ordrestyring_sync_metadata`, `ordrestyring_users`.
**Mobile / field intake** — `mobile_order_intakes`, `mobile_order_attachments`, `mobile_order_checklist_answers`, `mobile_order_quote_drafts`.
**OCR / documents** — `ocr_documents`, `ocr_materials`, `document_uploads`.
**Analytics / business metrics** — `analytics_calculation_log`, `business_benchmarks`, `business_health_dashboard`, `customer_analytics`, `daily_business_metrics`, `employee_performance_analytics`, `high_value_customers_view`, `top_employees_view`, `v_category_statistics`, `product_flow_events`.
**Platform / ops** — `system_logs`, `system_settings`, `request_logs`, `openai_usage_stats`, `openai_usage_tracking`, `ai_jobs`, `ai_suggestions`, `support_work_items`, `category_keywords`, `task_categories`, `task_categorization` support tables (`carpenter_task_categories`, `carpenter_task_steps`, `carpenter_tasks`, `carpenter_tools`), `quality_standards`, `safety_procedures`, `product_categories`.
**Housekeeping artifact** — `materials_deleted_backup_nov14`: a manual backup table from a prior cleanup, still present. Evidence that cleanups here are done by renaming/backing up rather than hard-deleting — consistent with how this project prefers reversible operations.
## Core relationships
```mermaid
erDiagram
customer_projects ||--o| roof_geometry : "has (project_id)"
customer_projects ||--o{ customer_project_packages : "selects"
customer_project_packages }o--|| smart_packages : "references"
smart_packages ||--o{ package_tasks : "has"
smart_packages ||--o{ package_materials : "has"
package_materials }o--o| materials : "matched to"
customer_projects ||--o| project_smart_package_workspaces : "versioned workspace"
customer_projects ||--o{ project_labor : "has"
customer_projects ||--o{ project_materials : "has"
customer_projects ||--o{ project_quotes : "generates"
auth_accounts {
int id PK
varchar username
varchar password_hash
enum role
}
```
`auth_accounts` is intentionally standalone in this diagram — it has no foreign-key relationship to `customer_projects` or anything else; it's purely login/authorization state. The unrelated `users` (employee roster) table is omitted here since it's outside the quoting domain.