Commit Graph

2 Commits

Author SHA1 Message Date
m
3faec6c526 refactor(rename): German→English for backend (tables, types, services, handler files)
t-paliad-025 — Phase 1: backend rename.

Migrations 018+019 rewritten from scratch with English table/column
names throughout. Since v2 schema (018/019) has never been applied to
youpc prod DB, this is a clean replacement — not an ALTER RENAME chain.
Pre-existing German tables (parteien, fristen, termine, dokumente,
akten_events, notizen) are renamed inline in 018 via ALTER TABLE … RENAME
TO alongside the akte_id → project_id column rewrite.

Renames applied:
  projekte            → projects
  projekt_teams       → project_teams
  projekt_events      → project_events (via akten_events → project_events)
  fristen             → deadlines
  termine             → appointments
  parteien            → parties
  notizen             → notes
  dezernate           → departments
  dezernat_mitglieder → department_members
  dokumente           → documents
  can_see_projekt     → can_see_project
  notiz_is_visible    → note_is_visible
  akte_id  / frist_id / termin_id / akten_event_id → project_id /
    deadline_id / appointment_id / project_event_id
  termin_type → appointment_type

Go types + services renamed:
  Projekt / ProjektService / ProjektEvent / ProjektTeamMember
  Frist / FristService / FristWithProjekt
  Termin / TerminService / TerminWithProjekt / TerminType
  Notiz / NotizService / ChecklistInstanceWithProjekt
  Dezernat / DezernatService / DezernatMitglied
  Partei / Parteien / ParteienService

Files renamed (git mv):
  internal/services/{projekt,frist,termin,notiz,dezernat,parteien}_service.go
    → {project,deadline,appointment,note,department,party}_service.go
  internal/handlers/{projekte,fristen,fristen_pages,termine,termine_pages,
    notizen,dezernate,akten_pages,gerichte,glossar,checklisten}.go
    → {projects,deadlines,deadlines_pages,appointments,appointments_pages,
       notes,departments,projects_pages,courts,glossary,checklists}.go
  internal/checklisten/ → internal/checklists/
  internal/db/migrations/018_projekte_v2.* → 018_projects_v2.*
  internal/db/migrations/019_seed_dezernate_from_user_text.*
    → 019_seed_departments_from_user_text.*

User-facing i18n strings (DE/EN labels) stay untouched. Product names
Fristenrechner / Kostenrechner / Gebührentabellen stay German.

Build + vet + tests clean.
2026-04-20 17:35:38 +02:00
m
bcc4939af2 feat(services): Phase B — sqlx pool, services, Akten/Frist endpoints
Implements docs/design-kanzlai-integration.md §8 Phase B.

Pool & infrastructure:
- internal/db/pool.go — sqlx connection pool via DATABASE_URL
  (lazy, sync.Once, returns nil if unset)
- cmd/server/main.go wires pool + services on startup; skips gracefully
  if DATABASE_URL unset (existing endpoints still work)

Services (internal/services/):
- holidays.go — ported from KanzlAI. Audit §1.6 fix: replaces unguarded
  map with sync.Map of *yearEntry (sync.Once per year), race-safe under
  concurrent readers.
- deadline_calculator.go — ported. days/weeks/months + before/after
  timing + holiday/weekend adjustment via HolidayService.
- deadline_rule_service.go — ported, DB-backed. List, GetRuleTree,
  GetFullTimeline (recursive CTE for cross-type spawns), GetByIDs,
  ListProceedingTypes.
- user_service.go — reads paliad.users; GetByID returns (nil, nil) for
  users who haven't onboarded yet (safe default = no visibility).
- akte_service.go — new. Office-scoped visibility enforced at the app
  layer (defense-in-depth alongside RLS). ListVisibleForUser uses the
  visibility predicate directly in SQL so indexes can drive the query.
  Create/Update/Delete enforce role gates:
    * associates can only create in their own office
    * only admins can move an Akte between offices
    * only partners/admins can toggle firm_wide_visible
    * only partners/admins can delete (soft, status='archived')
  Writes an akten_events row on create, status change, firm-wide toggle,
  collaborator change.
- parteien_service.go — ported. Visibility inherited from the parent
  Akte via AkteService.GetByID gate.

Sentinel errors:
- services.ErrNotVisible → handlers return 404 (never leak existence)
- services.ErrForbidden → 403
- services.ErrInvalidInput → 400

Auth context:
- internal/auth/user.go — WithUserID middleware extracts the `sub` claim
  from the Supabase JWT session cookie and injects uuid.UUID into the
  request context. Runs after Client.Middleware (which already validated
  the cookie expiry). Handlers use auth.UserIDFromContext().

Handlers (internal/handlers/):
- akten.go — full CRUD for /api/akten + /api/akten/{id}/parteien.
  All require DB configured (503 otherwise) and authenticated user
  (401 otherwise). Returns 404 for non-visible IDs.
- deadline_rules_db.go — GET /api/deadline-rules, GET
  /api/proceeding-types-db, POST /api/deadlines/calculate.
  The /api/deadlines/calculate endpoint lives alongside the existing
  in-memory /api/tools/fristenrechner; Phase C swaps the UI over and
  deletes the in-memory rule tree.
- handlers.Register now takes an optional *Services bundle; when
  DATABASE_URL unset the DB-backed endpoints return 503 with a clear
  error message.

Tests (internal/services/):
- holidays_test.go — Easter algorithm (5 years spot-checked), German
  federal holidays, weekend + Neujahr adjustment, concurrent cache
  reads under -race.
- deadline_calculator_test.go — days/weeks/months calc, before timing,
  Karfreitag→Ostermontag skip (lands on Tue 2026-04-07), batch with
  zero-duration rule.
- akte_service_test.go — live DB test behind `TEST_DATABASE_URL` (skip
  otherwise). Verifies 4-Akte × 3-user visibility model AND role
  enforcement (associate can't delete, can't cross-office-create,
  invalid office rejected).

Manual verification:
- `go build ./...` + `go vet ./...` clean
- `go test ./internal/services/ -race` passes (DB tests skip without URL)
- With TEST_DATABASE_URL set, all visibility + role tests pass
- Live HTTP smoke test with forged JWT cookie:
  * /api/deadline-rules returns 40 rules
  * /api/proceeding-types-db returns 7 types
  * /api/deadlines/calculate INF + 2026-04-15 returns calculated deadlines
  * /api/akten returns [] (user has no paliad.users row yet — safe default)
  * /login, / still work (no regressions)
2026-04-16 14:25:55 +02:00