feat(absences) : outils MCP CRUD pour les absences
Expose le module Absences via le serveur MCP et comble les trous CRUD existants (projets, groupes, métadonnées de tâches, clients, users RH). Absences (réutilise AbsenceDayCalculator + AbsenceBalanceService pour ne pas contourner la logique de soldes) : - list/get/create/review/cancel/delete-absence-request - list/update-absence-policy, list/update-absence-balance - create-absence-request prend un userId explicite (agir au nom d'un employé) ; review/cancel maintiennent les soldes (pending/taken) cohérents - AbsenceRequestRepository::findFiltered pour les filtres de liste Trous CRUD comblés : - delete-project, delete-group - CRUD tag, effort, priority - CRUD status (couplé au workflow, avec category) - CRUD client, get/update-user (champs RH, sans password ni roles) Sérialisation centralisée (Serializer::absenceRequest/Policy/Balance/client/userFull). Instructions MCP (mcp.yaml) mises à jour : statuts par workflow + domaine absences. Tests : tests/Functional/Mcp/AbsenceRequestLifecycleTest (création / approbation / annulation admin) vérifient le cycle complet et la cohérence des soldes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2519
docs/superpowers/plans/2026-05-22-mcp-absence-crud-tools.md
Normal file
2519
docs/superpowers/plans/2026-05-22-mcp-absence-crud-tools.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,141 @@
|
||||
# Spec — Extension des outils MCP : module Absences + trous CRUD
|
||||
|
||||
Date : 2026-05-22
|
||||
Branche : `feat/absence-management`
|
||||
|
||||
## Contexte & objectif
|
||||
|
||||
Le serveur MCP Lesstime (`src/Mcp/Tool/`) expose aujourd'hui les projets, tâches,
|
||||
métadonnées de tâches, time tracking et workflows. Le nouveau **module Absences**
|
||||
(`AbsenceRequest`, `AbsencePolicy`, `AbsenceBalance`) n'est pas exposé, et plusieurs
|
||||
entités existantes n'ont qu'une couverture partielle (souvent `list` sans
|
||||
create/update/delete).
|
||||
|
||||
Objectif : permettre de piloter l'app via MCP (assistant) sur ces domaines, en
|
||||
respectant strictement la logique métier déjà en place.
|
||||
|
||||
## Conventions reprises de l'existant
|
||||
|
||||
- Une classe par outil, attribut `#[McpTool(name, description)]` sur la **classe**.
|
||||
- Discovery automatique : `config/packages/mcp.yaml` scanne `src/` (exclut `DataFixtures`).
|
||||
Aucune config à ajouter — créer la classe suffit.
|
||||
- Constructeur : injection de repos/services + `Security`.
|
||||
- `__invoke(...)` : check de rôle en première ligne (`AccessDeniedException` sinon),
|
||||
validation des IDs (`InvalidArgumentException` si introuvable), retour `json_encode(...)`.
|
||||
- Sérialisation centralisée dans `App\Mcp\Tool\Serializer`.
|
||||
- Rôle : `ROLE_USER` pour la lecture/écriture courante ; `ROLE_ADMIN` pour les
|
||||
opérations sensibles (déjà le cas pour `list-clients`/`list-users`, et pour les
|
||||
opérations admin du module absences).
|
||||
|
||||
## Décision clé — réutilisation de la logique métier (pas les Processors)
|
||||
|
||||
Les Processors API Platform (`AbsenceRequestProcessor`, `AbsenceReviewProcessor`,
|
||||
`AbsenceCancelProcessor`) sont liés à `Security::getUser()` (l'utilisateur courant)
|
||||
et à l'`Operation` HTTP. En MCP, l'utilisateur courant est le **propriétaire du
|
||||
token** (admin), or on veut pouvoir agir **au nom d'un employé**.
|
||||
|
||||
→ Les outils MCP **n'appellent pas les Processors** ; ils répliquent leur
|
||||
orchestration en réutilisant les **services partagés** qui portent la vraie règle
|
||||
métier :
|
||||
|
||||
- `AbsenceDayCalculator::countWorkingDays(...)` — calcul des jours décomptés.
|
||||
- `AbsenceBalanceService` — `reservePending`, `applyApproval`, `release`, `periodFor`.
|
||||
- `AbsencePolicyRepository::findOneByType(...)` — politique active du type.
|
||||
- `AbsenceRequestRepository::hasOverlap(...)` — règle anti-chevauchement.
|
||||
|
||||
`create-absence-request` prend un `userId` explicite (l'employé cible) ;
|
||||
`review`/`cancel` posent `reviewedBy` = utilisateur du token MCP.
|
||||
|
||||
Ainsi soldes (`pending`/`taken`/`acquired`) et statuts restent cohérents avec ce
|
||||
que produit l'UI.
|
||||
|
||||
## Inventaire des outils
|
||||
|
||||
### Module Absences — `src/Mcp/Tool/Absence/` (10 outils)
|
||||
|
||||
| Outil | Rôle | Paramètres | Logique |
|
||||
|---|---|---|---|
|
||||
| `list-absence-requests` | USER | `userId?`, `status?`, `type?`, `from?`, `to?` | Filtre ; sans `userId` renvoie tout (token admin). |
|
||||
| `get-absence-request` | USER | `id` | — |
|
||||
| `create-absence-request` | USER | `userId`, `type`, `startDate`, `endDate`, `startHalfDay?`, `endHalfDay?`, `reason?` | Vérifie policy active + overlap, calcule `countedDays`, refuse si ≤ 0, statut `Pending`, `reservePending`. |
|
||||
| `review-absence-request` | ADMIN | `id`, `decision` (`approve`\|`reject`), `rejectionReason?` | Seulement si `Pending`. Approve → `applyApproval` ; reject → `rejectionReason` requis + `release(false)`. Pose `reviewedAt`/`reviewedBy`. |
|
||||
| `cancel-absence-request` | USER | `id` | `Pending` → `release(false)` ; `Approved` → ADMIN requis + `release(true)` ; sinon conflit. Statut `Cancelled`. |
|
||||
| `delete-absence-request` | ADMIN | `id` | Suppression définitive. |
|
||||
| `list-absence-policies` | USER | — | Toutes les policies (ordre `type`). |
|
||||
| `update-absence-policy` | ADMIN | `id`, `daysPerYear?`, `daysPerEvent?`, `justificationRequired?`, `noticeDays?`, `countWorkingDaysOnly?`, `active?` | Seuls les champs fournis changent. |
|
||||
| `list-absence-balances` | USER | `userId?`, `type?`, `period?` | Soldes filtrés. |
|
||||
| `update-absence-balance` | ADMIN | `id`, `acquired?`, `acquiring?`, `taken?` | Ajustement manuel (régularisation). |
|
||||
|
||||
`type` et `status` acceptés en valeur d'enum string (ex. `cp`, `maladie`,
|
||||
`pending`) ; erreur de validation explicite si invalide. `startDate`/`endDate`
|
||||
au format `YYYY-MM-DD`.
|
||||
|
||||
### Trous CRUD sur l'existant
|
||||
|
||||
**Projets / groupes**
|
||||
- `delete-project` (ADMIN) — `id`.
|
||||
- `delete-group` (USER) — `id`.
|
||||
|
||||
**Métadonnées de tâches** — `src/Mcp/Tool/TaskMeta/`
|
||||
- `create-tag` / `update-tag` / `delete-tag` (USER) — `label`, `color?`.
|
||||
- `create-effort` / `update-effort` / `delete-effort` (USER) — `label` (+ ordre éventuel).
|
||||
- `create-priority` / `update-priority` / `delete-priority` (USER) — `label`, `color?`.
|
||||
- `create-status` / `update-status` / `delete-status` (ADMIN) — **`workflowId` requis**
|
||||
+ `category` (`todo`|`in_progress`|`blocked`|`review`|`done`), `label`, `color?`,
|
||||
`position?`, `isFinal?`. (Les statuts ne sont PAS globaux : ils appartiennent à un workflow.)
|
||||
|
||||
**Clients** — `src/Mcp/Tool/Reference/` (ADMIN, aligné sur `list-clients`)
|
||||
- `get-client` — `id`.
|
||||
- `create-client` — `name` (+ `email?`, `phone?`, `street?`, `city?`, `postalCode?`).
|
||||
- `update-client` — `id` + champs optionnels.
|
||||
- `delete-client` — `id`.
|
||||
|
||||
**Utilisateurs** — `src/Mcp/Tool/Reference/` (ADMIN)
|
||||
- `get-user` — `id` (profil complet RH).
|
||||
- `update-user` — `id` + champs RH/profil : `isEmployee?`, `hireDate?`, `endDate?`,
|
||||
`contractType?`, `workTimeRatio?`, `annualLeaveDays?`, `referencePeriodStart?`,
|
||||
`initialLeaveBalance?`, `familySituation?`, `nbChildren?`.
|
||||
**Hors périmètre (décision utilisateur) : pas de `create-user`, pas de modification
|
||||
de `password` ni `roles` via MCP.**
|
||||
|
||||
## Sérialisation — ajouts à `Serializer.php`
|
||||
|
||||
- `absenceRequest(AbsenceRequest)` : id, user{id,username}, type{value,label},
|
||||
startDate, endDate, startHalfDay, endHalfDay, countedDays, reason, status{value,label},
|
||||
rejectionReason, createdAt, reviewedAt, reviewedBy, justificationFileName.
|
||||
- `absencePolicy(AbsencePolicy)` : id, type{value,label}, daysPerYear, daysPerEvent,
|
||||
justificationRequired, noticeDays, countWorkingDaysOnly, active.
|
||||
- `absenceBalance(AbsenceBalance)` : id, user, type{value,label}, period, acquired,
|
||||
acquiring, taken, pending, acquiredTotal, available.
|
||||
- `client(Client)` : id, name, email, phone, street, city, postalCode.
|
||||
- `userFull(User)` : id, username, roles, isEmployee, hireDate, endDate, contractType,
|
||||
workTimeRatio, annualLeaveDays, referencePeriodStart, initialLeaveBalance,
|
||||
familySituation, nbChildren.
|
||||
|
||||
## Mise à jour de la doc MCP
|
||||
|
||||
`config/packages/mcp.yaml` — bloc `instructions` :
|
||||
- corriger la mention « statuses … are GLOBAL » (faux : par workflow) ;
|
||||
- ajouter une phrase sur le domaine Absences (requests/policies/balances, lifecycle
|
||||
approve/reject/cancel, `userId` pour agir au nom d'un employé).
|
||||
|
||||
## Découpage du plan d'implémentation (jalons)
|
||||
|
||||
1. **Absences** : Serializer + 10 outils + tests de cohérence des soldes.
|
||||
2. **Métadonnées tâches** : delete-project/group, CRUD tag/effort/priority/status.
|
||||
3. **Clients & users** : CRUD clients, get/update user + maj `mcp.yaml`.
|
||||
|
||||
Chaque jalon est livrable et testable indépendamment.
|
||||
|
||||
## Tests
|
||||
|
||||
Tests fonctionnels MCP (si une infra de test MCP existe) ou tests unitaires sur la
|
||||
réplication de la logique de solde : créer → review(approve) → cancel et vérifier
|
||||
`pending`/`taken` à chaque étape ; vérifier le refus sur chevauchement et sur
|
||||
plage sans jour ouvré.
|
||||
|
||||
## Hors périmètre
|
||||
|
||||
- Mail, BookStack, Gitea, Zimbra, Notifications, TaskDocument (non demandés).
|
||||
- Création d'utilisateurs et gestion mot de passe/rôles via MCP.
|
||||
- Upload de justificatif d'absence via MCP.
|
||||
Reference in New Issue
Block a user