Files
Nexus/samples/Dispov2/plan-client-wbs-model.md
T
Hartmut 4a5edeef3e
CI / Unit Tests (pull_request) Successful in 5m46s
CI / Lint (pull_request) Failing after 3m49s
CI / E2E Tests (pull_request) Has been skipped
CI / Fresh-Linux Docker Deploy (pull_request) Has been skipped
CI / Assistant Split Regression (pull_request) Failing after 35s
CI / Architecture Guardrails (pull_request) Failing after 2m14s
CI / Typecheck (pull_request) Successful in 4m22s
CI / Build (pull_request) Has been skipped
CI / Release Images (pull_request) Has been skipped
rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI
- @capakraken/* → @nexus/* across 12 packages (root + 11 workspaces),
  1551 import lines migrated via codemod
- User-visible brand strings renamed (emails, page titles, PWA
  manifest, mobile header, MFA backup-codes header, tooltips, signin
  page, invite page, weekly digest, install prompt)
- TOTP issuer "CapaKraken" → "Nexus" (existing secrets still valid;
  re-enrollment relabels them in users' authenticator apps)
- Function rename: assertCapaKrakenDbTarget → assertNexusDbTarget
- LocalStorage migration shim in apps/web/src/app/layout.tsx copies
  capakraken_* → nexus_* on first load (guarded by nexus_migrated_v1
  sentinel; runs once per browser, then never again)
- Service-worker cache name capakraken-v2 → nexus-v2 with one-time
  caches.delete('capakraken-v2') from the same shim
- Email-domain fixtures @capakraken.{dev,app} → @nexus.{dev,app} in
  seed data, e2e specs, SMTP default fallback
- Dockerfile.dev / Dockerfile.prod / all .github/workflows/*.yml
  pnpm --filter @capakraken/* → @nexus/*
- README, CLAUDE.md, LEARNINGS.md, all docs/*.md, .env.example,
  tooling/deploy/.env.production.example brand sweep

Phase 1 deliberately leaves untouched (handled in Phase 3 cutover):
- PostgreSQL DB name "capakraken" and POSTGRES_USER "capakraken"
- Volume names capakraken_pgdata etc.
- Compose project name "capakraken" / "capakraken-prod"
- db-target-guard default expectedDatabase
- env-var CAPAKRAKEN_EXPECTED_DB_NAME
- Container DNS names in docker-compose.ci.yml

Quality gates green: pnpm typecheck (7/7), pnpm test:unit (7/7),
pnpm lint (0 errors), check:exports/imports/architecture all pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 15:10:44 +02:00

5.3 KiB

Plan: Client Model with WBS Hierarchy

Date: 2026-03-13 Status: Draft Depends on: -

Problem

Projects need to be linked to clients for chargeability reporting, budget tracking, and organizational grouping. The Dispo Categories file defines a two-level client hierarchy:

  • WBS Master Client — the parent organization (e.g. "BMW", "VOLKSWAGEN")
  • WBS Client Name — the legal entity (e.g. "BMW AG", "Dr. Ing. h.c. F. Porsche AG")

Currently Nexus has no Client model. Projects exist independently without client attribution.

Data

35 WBS Master Clients with their legal sub-entities. Examples:

WBS Master Client WBS Client Names
BMW BMW AG
VOLKSWAGEN Audi Business Innovation GmbH, Dr. Ing. h.c. F. Porsche AG, MAN Truck & Bus SE, Volkswagen AG
DAIMLER antoni garage GmbH & Co. KG, Mercedes-Benz AG
EXOR-STELLANTIS AUTOMOBILES PEUGEOT, FCA Italy S.p.A., Ferrari S.p.A, MASERATI SPA A SOCIO UNICO

Schema

New: Client model (self-referencing for parent/child)

model Client {
  id        String    @id @default(cuid())
  name      String                         // Display name
  code      String?   @unique              // Optional short code (e.g. "BMW", "VW")
  parentId  String?
  parent    Client?   @relation("ClientTree", fields: [parentId], references: [id])
  children  Client[]  @relation("ClientTree")
  isActive  Boolean   @default(true)
  sortOrder Int       @default(0)
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt

  projects  Project[]

  @@unique([parentId, name])
}

Design choice: self-referencing tree instead of separate MasterClient + ClientEntity tables. This supports:

  • Two levels today (Master → Entity)
  • Potential deeper nesting in the future
  • Simple queries via parentId IS NULL for top-level clients

Project model extension

model Project {
  // ... existing fields ...

  clientId  String?
  client    Client?  @relation(fields: [clientId], references: [id])
}

A project links to a WBS Client Name (child level). The master client is derived via client.parent.

Shared Types

// packages/shared/src/types/client.ts

interface Client {
  id: string;
  name: string;
  code?: string;
  parentId?: string;
  isActive: boolean;
  sortOrder: number;
}

interface ClientWithChildren extends Client {
  children: Client[];
}

interface ClientTree extends Client {
  children: ClientTree[];
}

API

Location: packages/api/src/router/client.ts

Procedure Access Description
list protected Flat list, optionally filtered by parentId
getTree protected Nested tree for UI
getById protected Single client with parent/children
create manager Create client (top-level or child)
update manager Edit name, code, re-parent
deactivate manager Soft-delete

Manager-level access (not just admin) since project managers typically need to manage client relationships.

UI

Client Management (/clients or /admin/clients)

  • Tree view showing Master Clients → WBS Client Names
  • CRUD at both levels
  • Search/filter for quick lookup
  • Badge showing number of linked projects per client

Project: Client Assignment

  • ProjectModal.tsx gets a cascading or searchable client picker:
    1. Select or search Master Client
    2. Select WBS Client Name under that master
  • ProjectWizard.tsx Step 1 includes client selection
  • Project list shows client name column

Client Unit (Resource Attribute)

The chargeability report also has a "Client Unit" dimension on resources (BMW, Daimler, Porsche, etc.). This represents which client a resource is primarily assigned to. This is separate from project-client linking:

  • Project.clientId → which client owns this project (WBS billing entity)
  • Resource client unit → which client group the resource primarily serves (for reporting)

Resource client unit can be derived from the resource's primary assignments or set manually. This is covered in the resource extensions plan, not here.

Seed Data

Seed the 35 master clients and their sub-entities from the Dispo Categories file. See MandatoryDispoCategories_V3.xlsxProject-Attr sheet, rows 5-40.

Migration

  1. Create Client table with seed data (35 masters + ~50 sub-entities)
  2. Add clientId to Project (nullable)
  3. Admin links existing projects to clients

Acceptance Criteria

  • Client model with self-referencing parent/child tree
  • Project linked to Client (WBS Client Name level)
  • Client management UI with tree view
  • Project modal/wizard includes client picker
  • Seed data for all 35 master clients and sub-entities
  • getTree API returns nested structure