fix: correct architecture doc drift and add withPivot gotchas

Architecture doc fixes:
- Replace `deadline` → `due_date` in all code examples (5 occurrences)
- Replace `Declaration::workspace($workspace)` → `Declaration::where('workspace_id', ...)`
  (no such query scope exists; workspace() is a BelongsTo relationship)
- Add missing `mise_en_demeure` status to transition table and flow diagram
- Update transition rules: en_attente_client can transition to mise_en_demeure

Project context additions:
- Document WorkspaceUser pivot fields and withPivot requirement
- Document correct column name (due_date not deadline)
- Document Declaration scoping pattern (no workspace scope)
- Document all 6 declaration statuses including mise_en_demeure

Resolves Epic 2 retro action items A3, A4 (carried since Epic 1).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 11:12:11 +01:00
parent ff92311994
commit 6956f7bf95
2 changed files with 18 additions and 11 deletions

View File

@@ -456,6 +456,8 @@ $declarations = Declaration::where('workspace_id', $workspace->id)
```
Created → En cours → En attente client → En cours → Terminé → Fermé → [auto-archive]
↘ ↗ ↗
Mise en demeure ──────┘ ───────────┘
Created → En cours → Terminé → Fermé → [auto-archive]
```
@@ -466,7 +468,8 @@ Created → En cours → Terminé → Fermé → [auto-archive]
|---|---|---|---|
| `created` | Declaration just created | System | `en_cours` |
| `en_cours` | Being worked on | Owner/Manager/Worker | `en_attente_client`, `termine` |
| `en_attente_client` | Waiting for client documents | Owner/Manager/Worker | `en_cours` |
| `en_attente_client` | Waiting for client documents | Owner/Manager/Worker | `en_cours`, `mise_en_demeure` |
| `mise_en_demeure` | Formal notice sent to client | Owner/Manager | `en_cours`, `ferme` |
| `termine` | Work completed | Owner/Manager/Worker | `ferme` |
| `ferme` | Closed (triggers auto-archive) | Owner/Manager | (archived) |
@@ -522,7 +525,7 @@ public function bulkStore(BulkStoreDeclarationRequest $request)
'workspace_id' => $workspace->id,
'client_id' => $item['client_id'],
'type' => $item['type'],
'deadline' => $item['deadline'],
'due_date' => $item['due_date'],
'assigned_to' => $item['assigned_to'],
'status' => DeclarationStatus::Created,
]);
@@ -560,7 +563,7 @@ protected function applyFilters(Builder $query, Request $request): Builder
)
->when($request->sort, fn ($q, $sort) =>
$q->orderBy($sort, $request->input('direction', 'asc'))
, fn ($q) => $q->orderBy('deadline', 'asc')); // default sort
, fn ($q) => $q->orderBy('due_date', 'asc')); // default sort
}
```
@@ -579,7 +582,7 @@ function applyFilter(key: string, value: string | null) {
}
```
**Default sort: `deadline ASC`** (most urgent first) for declarations. **`name ASC`** for clients.
**Default sort: `due_date ASC`** (most urgent first) for declarations. **`name ASC`** for clients.
### Archive Query Patterns
@@ -614,16 +617,16 @@ $dashboardData = Cache::remember(
"dashboard:{$workspace->id}:{$user->id}",
300, // 5 minutes
fn () => [
'total_active' => Declaration::workspace($workspace)->active()->forUser($user, $workspace)->count(),
'by_status' => Declaration::workspace($workspace)->active()->forUser($user, $workspace)
'total_active' => Declaration::where('workspace_id', $workspace->id)->active()->forUser($user, $workspace)->count(),
'by_status' => Declaration::where('workspace_id', $workspace->id)->active()->forUser($user, $workspace)
->selectRaw('status, count(*) as count')
->groupBy('status')
->pluck('count', 'status'),
'overdue' => Declaration::workspace($workspace)->active()->forUser($user, $workspace)
->where('deadline', '<', now())
'overdue' => Declaration::where('workspace_id', $workspace->id)->active()->forUser($user, $workspace)
->where('due_date', '<', now())
->count(),
'due_this_week' => Declaration::workspace($workspace)->active()->forUser($user, $workspace)
->whereBetween('deadline', [now(), now()->endOfWeek()])
'due_this_week' => Declaration::where('workspace_id', $workspace->id)->active()->forUser($user, $workspace)
->whereBetween('due_date', [now(), now()->endOfWeek()])
->count(),
]
);

View File

@@ -154,6 +154,10 @@ _This file contains critical rules and patterns that AI agents must follow when
- New business models must add Spatie `LogsActivity` trait + `getActivitylogOptions()` returning `logFillable()->logOnlyDirty()->dontSubmitEmptyLogs()`
- New models with files must add Spatie `InteractsWithMedia` trait and implement `HasMedia`
- Inertia render paths use lowercase subdirectory: `'clients/Index'`, not `'Clients/Index'`
- **WorkspaceUser pivot fields:** The `workspace_user` pivot table has `role` (cast to `WorkspaceUserRole` enum) and `permissions` (cast to array). Both `User::workspaces()` and `Workspace::users()` use `->withPivot('role', 'permissions')`. Access via `$user->workspaces()->first()->pivot->role`. The `WorkspaceUser` model extends `Pivot` (not `Model`) — use `Pivot::query()` for direct queries, not `WorkspaceUser::find()`. When eager-loading workspace members for controllers, always chain `->withPivot('role', 'permissions')` or the pivot data will be silently null.
- **Declaration column name:** The date column is `due_date`, NOT `deadline` — some older docs may reference `deadline` incorrectly
- **Declaration scoping:** There is no `Declaration::workspace()` query scope. Use `Declaration::where('workspace_id', $workspace->id)` instead. The model has `active()`, `archived()`, and `forUser()` scopes.
- **Declaration statuses:** 6 values — `created`, `en_cours`, `en_attente_client`, `mise_en_demeure`, `termine`, `ferme`. The `mise_en_demeure` status is a formal notice branch from `en_attente_client`.
---
@@ -171,4 +175,4 @@ _This file contains critical rules and patterns that AI agents must follow when
- Review quarterly for outdated rules
- Remove rules that become obvious over time
Last Updated: 2026-03-08
Last Updated: 2026-03-24