Add comprehensive API documentation for the reporting endpoint: - Request/response structure - View type inference (did/is/will) - Blank vs explicit zero semantics - Status values and error responses Related to enhanced-allocation change.
240 lines
7.8 KiB
Markdown
240 lines
7.8 KiB
Markdown
# Design: Enhanced Allocation Fidelity
|
|
|
|
## Context
|
|
|
|
The allocation experience must reflect a strict planning-to-execution model. Current implementation drift introduced incorrect month semantics (`approved_estimate / 12`), mixed concern UIs, and ambiguous status signaling.
|
|
|
|
This design aligns implementation with the validated operating model:
|
|
|
|
1. **Projects (Lifecycle)**: `approved_estimate` is the lifecycle total.
|
|
2. **Project-Month Plan (Intent)**: manager explicitly sets monthly planned effort per project.
|
|
3. **Project-Resource Allocation (Execution)**: for selected month, manager allocates effort by team member.
|
|
4. **Reporting (Outcome)**: historical/current/future insights built from these three layers.
|
|
|
|
## Goals
|
|
|
|
1. Implement explicit project-month planning as first-class data.
|
|
2. Reframe allocation matrix as execution grid with month-plan and capacity variance.
|
|
3. Support untracked effort (`team_member_id = null`) end-to-end.
|
|
4. Implement partial bulk success for allocations.
|
|
5. Keep visual signaling minimal and decision-focused.
|
|
|
|
## Non-Goals
|
|
|
|
- Real-time collaboration/websockets.
|
|
- Notification systems.
|
|
- Export workflows.
|
|
- Cosmetic UI experimentation beyond fidelity requirements.
|
|
|
|
## Decisions
|
|
|
|
### D1: Month Plan Is Explicit (No Derived Monthly Budget)
|
|
|
|
- **Decision**: monthly plan is manager-entered and persisted.
|
|
- **Rejected**: deriving monthly budget from `approved_estimate / 12`.
|
|
- **Reason**: project phasing is dependency-driven, not uniform.
|
|
|
|
### D2: Reconciliation vs Lifecycle Total
|
|
|
|
For each project:
|
|
- `plan_sum = sum(non-null planned_hours across months)`
|
|
- compare against `approved_estimate`
|
|
- `plan_sum > approved_estimate` -> `OVER`
|
|
- `plan_sum < approved_estimate` -> `UNDER`
|
|
- `plan_sum == approved_estimate` -> `MATCH`
|
|
|
|
Tolerance for MATCH should use decimal-safe comparison in implementation.
|
|
|
|
### D3: Blank Month Semantics
|
|
|
|
- Planning grid cell can be blank (`null`) and remains visually blank.
|
|
- Allocation variance treats missing month plan as `0`.
|
|
- Allocation is allowed when month plan is blank.
|
|
|
|
### D4: Grid-First Editing
|
|
|
|
- Project-month planning: inline grid editing primary.
|
|
- Allocation matrix: inline grid editing primary.
|
|
- Modal is optional/fallback, not primary path.
|
|
|
|
### D5: Untracked Semantics
|
|
|
|
- Untracked allocation is represented by `team_member_id = null`.
|
|
- Included in project totals and grand totals.
|
|
- Excluded from team member capacity/utilization calculations.
|
|
|
|
### D6: Visual Status Policy
|
|
|
|
- Over = red
|
|
- Under = amber
|
|
- Match/settled = neutral
|
|
|
|
Status emphasis belongs on row/column summary edges and variance cells; avoid noisy color in every interior cell.
|
|
|
|
### D7: Forecasted Effort Deprecation
|
|
|
|
- `projects.forecasted_effort` is deprecated for this planning workflow.
|
|
- New monthly planning source of truth is explicit project-month plan data.
|
|
|
|
## Data Model
|
|
|
|
### New Table: `project_month_plans`
|
|
|
|
Recommended schema:
|
|
- `id` (uuid)
|
|
- `project_id` (uuid FK -> projects.id)
|
|
- `month` (date, normalized to first day of month)
|
|
- `planned_hours` (decimal, nullable)
|
|
- `created_at`, `updated_at`
|
|
- unique index on (`project_id`, `month`)
|
|
|
|
Notes:
|
|
- `planned_hours = null` means blank/unset plan.
|
|
- If storage policy prefers non-null planned hours, clear operation must still preserve blank UI semantics via delete row strategy.
|
|
|
|
### Existing Tables
|
|
|
|
- `projects.approved_estimate`: lifecycle cap (unchanged semantics)
|
|
- `allocations.team_member_id`: nullable to support untracked
|
|
|
|
## API Design
|
|
|
|
### Project-Month Plan APIs
|
|
|
|
1. `GET /api/project-month-plans?year=YYYY`
|
|
- returns month-plan grid payload by project/month
|
|
- includes reconciliation status per project
|
|
|
|
2. `PUT /api/project-month-plans/bulk`
|
|
- accepts multi-cell upsert payload
|
|
- supports setting value and clearing value (blank)
|
|
- returns updated rows + reconciliation results
|
|
|
|
Example payload:
|
|
```json
|
|
{
|
|
"year": 2026,
|
|
"items": [
|
|
{ "project_id": "...", "month": "2026-01", "planned_hours": 1200 },
|
|
{ "project_id": "...", "month": "2026-02", "planned_hours": 1400 },
|
|
{ "project_id": "...", "month": "2026-03", "planned_hours": 400 },
|
|
{ "project_id": "...", "month": "2026-04", "planned_hours": null }
|
|
]
|
|
}
|
|
```
|
|
|
|
### Allocation APIs
|
|
|
|
- Keep `GET /api/allocations?month=YYYY-MM` and CRUD endpoints.
|
|
- Enrich response with month-context variance metadata needed for row/column summary rendering.
|
|
- `POST /api/allocations/bulk` must support partial success.
|
|
|
|
Partial bulk response contract:
|
|
```json
|
|
{
|
|
"data": [{ "index": 0, "id": "...", "status": "created" }],
|
|
"failed": [{ "index": 1, "errors": { "allocated_hours": ["..."] } }],
|
|
"summary": { "created": 1, "failed": 1 }
|
|
}
|
|
```
|
|
|
|
## Computation Rules
|
|
|
|
### Lifecycle Reconciliation (Project-Month Plan Surface)
|
|
|
|
For each project:
|
|
- `plan_sum = SUM(planned_hours where month in planning horizon and value != null)`
|
|
- `status = OVER | UNDER | MATCH` compared to `approved_estimate`
|
|
|
|
### Allocation Row Variance (Execution Surface)
|
|
|
|
For selected month and project:
|
|
- `allocated_total = SUM(allocation.allocated_hours for project/month including untracked)`
|
|
- `planned_month = planned_hours(project, month) or 0 if missing`
|
|
- `row_variance = allocated_total - planned_month`
|
|
- status from row_variance sign
|
|
|
|
### Allocation Column Variance (Execution Surface)
|
|
|
|
For selected month and team member:
|
|
- `member_allocated = SUM(allocation.allocated_hours for member/month)`
|
|
- `member_capacity = computed month capacity`
|
|
- `col_variance = member_allocated - member_capacity`
|
|
- exclude `team_member_id = null` rows from this computation
|
|
|
|
## UX Specification (Implementation-Oriented)
|
|
|
|
### Surface A: Project-Month Plan Grid
|
|
|
|
- Rows: projects
|
|
- Columns: months
|
|
- Right edge: row total + reconciliation status
|
|
- Bottom edge: optional month totals for planning visibility
|
|
- Cell editing: inline, keyboard-first
|
|
- Blank cells remain blank visually
|
|
- Color: only summary statuses (red/amber/neutral)
|
|
|
|
### Surface B: Project-Resource Allocation Grid (Selected Month)
|
|
|
|
- Rows: projects
|
|
- Columns: team members + untracked
|
|
- Right edge: project row total + row variance status vs planned month
|
|
- Bottom edge: member totals + capacity variance status
|
|
- Primary editing: inline cell edit (modal not primary)
|
|
- Color: red/amber highlights only for over/under summary states
|
|
|
|
### Accessibility
|
|
|
|
- Do not rely on color alone; include text labels (OVER/UNDER).
|
|
- Maintain focus and keyboard navigation in both grids.
|
|
- Preserve contrast and readable status labels.
|
|
|
|
## Risk Register
|
|
|
|
1. **Semantic regression risk**: reintroduction of derived monthly budget logic.
|
|
- Mitigation: explicit tests and prohibition in specs.
|
|
|
|
2. **Blank vs zero confusion**.
|
|
- Mitigation: explicit API contract and UI behavior tests.
|
|
|
|
3. **Untracked leakage into capacity metrics**.
|
|
- Mitigation: query filters and dedicated tests.
|
|
|
|
4. **Bulk partial side effects**.
|
|
- Mitigation: per-item validation and clear response contract.
|
|
|
|
## Testing Strategy
|
|
|
|
### Unit
|
|
- Lifecycle reconciliation calculator
|
|
- Row/column variance calculators
|
|
- Blank-plan-as-zero execution logic
|
|
|
|
### Feature/API
|
|
- Project-month plan bulk upsert and clear behavior
|
|
- Reconciliation status correctness
|
|
- Allocation month variance data correctness
|
|
- Untracked include/exclude behavior
|
|
- Partial bulk success semantics
|
|
|
|
### Frontend Grid Tests
|
|
- Inline edit commit/cancel/clear
|
|
- Keyboard navigation
|
|
- Summary status placement and labels
|
|
- Blank visual state preservation
|
|
|
|
### E2E
|
|
- Create lifecycle estimate project
|
|
- Enter monthly plan across months and verify reconciliation
|
|
- Execute month allocations and verify row/column variances
|
|
- Validate untracked behavior
|
|
- Validate partial bulk success handling
|
|
|
|
## Migration & Rollout Notes
|
|
|
|
1. Introduce project-month plan model and APIs.
|
|
2. Remove derived budget rendering in allocation UI.
|
|
3. Wire allocation UI to explicit month plan for variance.
|
|
4. Deprecate `forecasted_effort` usage in this workflow path.
|
|
5. Keep backward compatibility for existing allocation records.
|