# Plan: Org Unit Hierarchy (Level 5 / 6 / 7)

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

## Problem

The chargeability report groups resources by a 3-level organizational hierarchy:

```
Level 5: Content Production (department)
  Level 6: CGI Content | CGI Technology | Creative Content Production | VFX (division)
    Level 7: Art Direction | CGI Production | 3D | IT Development | ... (chapter/team)
```

Every resource must be mapped to an Org Unit Level 7. Level 7 rolls up to Level 6, which rolls up to Level 5. The names of org units can change over time, so they must be editable.

CapaKraken already has a `Role` model, but roles represent skills/functions (e.g. "3D Artist"), not organizational placement. A person's org unit and their role are different dimensions.

## Current Data

### Level 5 → Level 6

| Level 5 | Level 6 |
|---|---|
| Content Production | CGI Content |
| Content Production | CGI Technology |
| Content Production | Creative Content Production |
| Content Production | VFX |

### Level 6 → Level 7

| Level 6 | Level 7 |
|---|---|
| CGI Content | Art Direction |
| CGI Content | Capability Development |
| CGI Content | CGI Production |
| CGI Content | Product Data Management |
| CGI Content | Program/Delivery Mgmt |
| CGI Technology | CGI Development |
| CGI Technology | IT Development |
| Creative Content Production | *(direct, no sub-teams)* |
| VFX | 2D & Art Direction |
| VFX | 3D |
| VFX | Program/Delivery Mgmt & Other |

## Schema

### New: `OrgUnit` model (self-referencing tree)

```prisma
model OrgUnit {
  id        String    @id @default(cuid())
  name      String
  shortName String?                          // optional abbreviation
  level     Int                               // 5, 6, or 7
  parentId  String?
  parent    OrgUnit?  @relation("OrgUnitTree", fields: [parentId], references: [id])
  children  OrgUnit[] @relation("OrgUnitTree")
  sortOrder Int       @default(0)
  isActive  Boolean   @default(true)         // soft-delete for renamed/retired units
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt

  resources Resource[]                        // resources assigned to this org unit (level 7)

  @@unique([parentId, name])
}
```

### Resource model extension

```prisma
model Resource {
  // ... existing fields ...

  orgUnitId  String?
  orgUnit    OrgUnit?  @relation(fields: [orgUnitId], references: [id])
}
```

Constraint: A resource should only be assigned to a Level 7 org unit. The UI enforces this; the schema allows any level for flexibility.

## Shared Types

```typescript
// packages/shared/src/types/org-unit.ts

interface OrgUnit {
  id: string;
  name: string;
  shortName?: string;
  level: number;        // 5, 6, or 7
  parentId?: string;
  sortOrder: number;
  isActive: boolean;
}

interface OrgUnitTree extends OrgUnit {
  children: OrgUnitTree[];
}
```

## API

Location: `packages/api/src/router/org-unit.ts`

| Procedure | Access | Description |
|---|---|---|
| `list` | protected | Returns flat list, optionally filtered by level or parentId |
| `getTree` | protected | Returns nested tree structure for UI rendering |
| `create` | admin | Create org unit with parent reference |
| `update` | admin | Rename, re-parent, change sort order |
| `deactivate` | admin | Soft-delete (sets `isActive = false`) |

## UI

### Admin: Org Unit Management (`/admin/org-units`)

- Tree view showing L5 → L6 → L7 hierarchy
- Inline editing of names (since names can change)
- Add/remove units at each level
- Drag-and-drop reordering within a level
- Deactivate (not hard delete) to preserve historical data

### Resource: Org Unit Assignment

- `ResourceModal.tsx` gets cascading dropdowns:
  1. Level 5 (pre-filtered or single value)
  2. Level 6 (filtered by selected L5)
  3. Level 7 (filtered by selected L6) — this is what gets stored on the resource
- Alternative: single tree picker showing full path

### Chargeability Report

The report groups by L6 → L7 (see `plan-chargeability-report.md`). The OrgUnit tree drives the row structure of the report.

## Seed Data

Seed the initial hierarchy from the Dispo Categories file:

```
Content Production (L5)
├── CGI Content (L6)
│   ├── Art Direction (L7)
│   ├── Capability Development (L7)
│   ├── CGI Production (L7)
│   ├── Product Data Management (L7)
│   └── Program/Delivery Mgmt (L7)
├── CGI Technology (L6)
│   ├── CGI Development (L7)
│   └── IT Development (L7)
├── Creative Content Production (L6)
│   └── Creative Content Production (L7)
└── VFX (L6)
    ├── 2D & Art Direction (L7)
    ├── 3D (L7)
    └── Program/Delivery Mgmt & Other (L7)
```

## Migration

1. Create `OrgUnit` table and seed data
2. Add `orgUnitId` to Resource (nullable)
3. Admin assigns org units to existing resources
4. No automated backfill possible — org unit assignment is business knowledge

## Acceptance Criteria

- [ ] `OrgUnit` model with self-referencing parent/child tree
- [ ] Resource linked to OrgUnit (level 7)
- [ ] Admin UI: tree view with CRUD for all levels
- [ ] Resource modal: cascading L5 → L6 → L7 dropdowns
- [ ] Org unit names are editable without breaking historical references
- [ ] Seed data for the known hierarchy
- [ ] `getTree` API returns nested structure for UI
