matthieu
8313c759c6
Migration modular monolith DDD (0.1 → 3.3) ( #17 )
...
Auto Tag Develop / tag (push) Successful in 9s
## Migration modular monolith DDD — Lesstime (0.1 → 3.3)
Cette MR regroupe l'intégralité de la refonte en monolithe modulaire (strangler progressif, additif). Elle remplace les MR stackées de Phase 1 (#12–#16), désormais incluses ici.
**Ne pas merger avant validation fonctionnelle** : branche destinée à être testée telle quelle.
### Périmètre — 9 modules sous `src/Module/`
| Phase | Module | Contenu |
|------|--------|---------|
| 0.1 | (socle) | infrastructure modulaire, `ModuleInterface`, mapping Doctrine par module |
| 0.2 | (socle front) | auto-détection des layers Nuxt sous `frontend/modules/*` |
| 1.1 | **Core** | Identité (User/Auth), Notifications, Notifier |
| 1.2 | Core | RBAC fin (permissions `module.resource.action`, sidebar gated) |
| 1.3 | Core | Audit log (`#[Auditable]`, listener, provider DBAL) |
| 2.1 | **TimeTracking** | TimeEntry + MCP + export |
| 2.2 | **ProjectManagement** | cœur métier Projets/Tâches + 38 MCP tools |
| 2.3 | **Absence** | demandes, soldes, policies, justificatifs |
| 2.4 | **Directory** | Clients (migrés) + **Prospects** (nouveau, conversion → Client) |
| 2.5 | **Mail** | intégration IMAP OVH + liens tâches |
| 2.6 | **Integration** | Gitea / BookStack / Zimbra / Share |
| 3.1 | **Reporting** | rapports transverses (DBAL read-only, 0 import inter-module) |
| 3.2 | **ClientPortal** | portail client (ROLE_CLIENT cloisonné, tickets, notifications) |
| 3.3 | (finition) | nettoyage legacy — `src/Entity` vide, app 100% modulaire |
### Architecture
- Découplage inter-modules par **contrats** (`UserInterface`, `ProjectInterface`, `TaskInterface`, `TaskTagInterface`, `ClientInterface`, `ClientTicketInterface`, `LeaveProfileInterface`) + `resolve_target_entities` 100% modulaire (aucune cible legacy).
- Repositories : interface `Domain/Repository` + implémentation `Infrastructure/Doctrine`, bindées.
- Reporting en DBAL read-only pur (aucun import d'entité d'un autre module).
- Chaque migration de module : déplacement à comportement préservé (API publique et noms d'outils MCP inchangés), migrations **additives** uniquement (zéro destructif).
### Sécurité
- ROLE_CLIENT cloisonné : un utilisateur client n'accède qu'à `/portal` et à ses propres tickets (filtrés par `allowedProjects`), interdit sur toute l'API interne.
- Correctif : interdiction pour un client de créer un lien vers le partage SMB (upload uniquement).
### QA non-régression (branche reconstruite from scratch)
- Migrations from scratch + fixtures : OK.
- Compilation dev + prod : OK.
- **180 tests PHPUnit verts**, php-cs-fixer clean, ~96 routes, **66 outils MCP** tous sous `App\Module\*`.
- Smoke test runtime multi-rôles (admin / ROLE_USER / ROLE_CLIENT) : 44 vérifications HTTP, **0 écart**, cloisonnement client étanche.
- Build Nuxt OK, 9 layers, 0 import legacy résiduel.
### Points à arbitrer (hors périmètre de cette migration)
- Durcissement MCP/IDOR pré-existant (`userId` explicite sans scoping sur certains tools TimeTracking/Absence/TaskDocument) — ticket dédié recommandé.
- Validation fonctionnelle de **Prospect** et **ClientPortal** (conçus depuis les specs disque).
- **Harmonisation visuelle Malio finale** (3.3) — finition esthétique inter-modules laissée au PO.
---
## ⚠️ Déploiement / migration des données — à ne pas oublier
### 1. Resynchroniser les séquences PostgreSQL après tout import/restore de dump
Si la prod (ou tout environnement) est **montée depuis un dump** (`pg_restore` / `COPY`), les lignes sont chargées avec leurs `id` explicites **sans avancer les séquences** → au premier `INSERT` : `duplicate key value violates unique constraint "..._pkey"` (constaté en local sur `notification`, `task`, `time_entry`…).
À lancer **juste après chaque restore/import** :
```sql
DO $$
DECLARE r RECORD; maxid BIGINT; seq TEXT;
BEGIN
FOR r IN SELECT table_name, column_name FROM information_schema.columns WHERE table_schema='public'
LOOP
seq := pg_get_serial_sequence(quote_ident(r.table_name), r.column_name);
IF seq IS NOT NULL THEN
EXECUTE format('SELECT COALESCE(MAX(%I),0) FROM %I', r.column_name, r.table_name) INTO maxid;
PERFORM setval(seq, GREATEST(maxid,1), maxid > 0);
END IF;
END LOOP;
END $$;
```
> Ne concerne **pas** une prod qui tourne déjà (séquences avancées organiquement) — uniquement le cas restore/import. Idempotent, sans risque.
### 2. Fix dénormalisation des collections typées-contrat (code, inclus dans la branche)
Les relations **to-many** typées par une interface `Shared\Domain\Contract\*` (`TimeEntry::tags` → `TaskTagInterface`, `Task::collaborators` → `UserInterface`) étaient **indénormalisables par API Platform** (mono-valué OK via IRI, collection KO) → **tout POST/PATCH portant une telle collection renvoyait 400/500**. Corrigé par un dénormaliseur générique `ContractRelationDenormalizer` (réutilise `resolve_target_entities`, zéro couplage par-entité) + test fonctionnel de non-régression.
---------
Co-authored-by: Matthieu <contact@malio.fr >
Reviewed-on: #17
2026-06-23 13:50:42 +00:00
Matthieu
73a34ef438
feat(documents) : bouton reload explorateur + liaison d'un fichier du partage SMB à un ticket
2026-06-12 15:23:56 +02:00
Matthieu
d0aff0fa51
feat(share) : entité ShareConfiguration + migration
2026-06-03 17:00:53 +02:00
Matthieu
02ac151ac0
feat(users) : ajout prénom et nom sur l'utilisateur
...
Auto Tag Develop / tag (push) Successful in 7s
Deux colonnes nullable firstName/lastName sur User (groupes me:read,
user:list, user:write), éditables dans le drawer utilisateur (admin).
L'affichage reste basé sur le username pour l'instant. Migration +
valeurs de démo dans les fixtures.
2026-05-26 11:33:18 +02:00
Matthieu
f74f0e1ad3
fix(absences) : seed des politiques par défaut en prod via migration
...
Auto Tag Develop / tag (push) Successful in 7s
Les 6 AbsencePolicy par défaut (barème Syntec / IDCC 1486) n'étaient
créées que par les fixtures, jamais en prod : l'écran « Politiques
d'absence » était vide en production.
Ajoute une migration de données idempotente (INSERT ... ON CONFLICT
(type) DO NOTHING sur l'index unique uniq_absence_policy_type) qui sème
les mêmes valeurs que les fixtures. Sans effet en dev (lignes déjà
présentes), peuple la prod au déploiement.
2026-05-26 10:42:27 +02:00
Matthieu
11fdf8d1bf
fix(absences) : durcissement RGPD des données RH des utilisateurs
...
Suite à la revue de conformité du module absences.
Fuite corrigée : GET /api/users et /api/users/{id} n'avaient aucun contrôle
d'accès alors que le groupe user:list exposait les données RH/familiales
(date d'embauche, contrat, soldes de CP, rôles…). Tout utilisateur authentifié
pouvait donc lire ces informations sur tous ses collègues.
- chaque champ RH (isEmployee, hireDate, endDate, contractType, workTimeRatio,
annualLeaveDays, referencePeriodStart, initialLeaveBalance) ainsi que roles
est désormais exposé via #[ApiProperty(security: "is_granted('ROLE_ADMIN') or
object == user")] : visible uniquement par un admin ou par l'utilisateur
lui-même. id et username restent publics (sélecteurs d'assigné, avatars).
Minimisation : suppression de familySituation et nbChildren, collectés et
exposés (form RH, API, outil MCP) mais utilisés par aucun calcul.
- entité User + enum FamilySituation + migration de drop des colonnes
- Serializer MCP, update-user (MCP), EmployeeDrawer, DTO, fixtures, i18n
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-22 14:28:48 +02:00
Matthieu
2a0b202d32
feat(absences) : avancement module absences + suppression du portail client
...
Deux lots regroupés sur la branche feat/absence-management.
Suppression complète du portail client :
- retire ROLE_CLIENT (security.yaml) ; User::getRoles() ajoute toujours ROLE_USER
- supprime l'entité ClientTicket (+ repo, states, relations), User.client et
User.allowedProjects, NotificationService, ProjectAllowedExtension, le bloc
ROLE_CLIENT de MailAccessChecker
- front : pages /portal, layout portal, composants client-ticket/,
AdminClientTicketTab, services/dto/i18n/docs associés
- fixtures : retire les users client-liot / client-acme
- migration Version20260522110000 (drop client_ticket, user_allowed_projects,
colonnes liées ; task_document.task_id -> NOT NULL)
- tests : retire les cas obsolètes testant le blocage des clients sur le mail
Module gestion des absences (WIP) :
- entités / migrations (Version20260521160000, Version20260522090000)
- pages absences.vue / team-absences.vue, composants frontend/components/absence/
- services front, AccrueLeaveCommand, PublicHolidayController
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-22 11:31:31 +02:00
Matthieu
de98924fd3
feat(absences) : fondation backend du module de gestion des absences
...
Module type Payfit (étapes 1+2 de la spec V1) : demande d'absence, validation
admin, soldes à jour.
- Enums : AbsenceType, AbsenceStatus, HalfDay, ContractType, FamilySituation
- Entités : AbsencePolicy, AbsenceBalance, AbsenceRequest + champs RH sur User
- Services : PublicHolidayProvider (fériés FR métropole en PHP pur, Computus),
AbsenceDayCalculator (décompte jours ouvrés/ouvrables + demi-journées, TDD),
AbsenceBalanceService (périodes + pending/taken/recrédit)
- API Platform : providers/processors (création, approve/reject/cancel) + RBAC
me/admin, contrôleurs preview (dry-run), upload/download justificatif, calendrier
- Migrations : une par table + colonnes RH user (DEFAULT puis DROP DEFAULT)
- Fixtures : 5 policies par défaut, salariés démo, soldes et demandes
- Tests unitaires : PublicHolidayProvider, AbsenceDayCalculator (12 tests)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-21 14:45:14 +02:00
matthieu
d7af8ee138
Correctifs UI workflow — specs + implémentation (8 chantiers) ( #6 )
...
Auto Tag Develop / tag (push) Has been cancelled
Suite à l'arrivée des workflows, correction des régressions UI et améliorations UX mail/modales (reviews Lucile Schnödt, Tristan Schnödtin).
**Specs & décisions :** `docs/superpowers/specs/2026-05-20-workflow-ui-fixes-design.md`
**Plan d'implémentation :** `docs/superpowers/plans/2026-05-21-workflow-ui-fixes.md`
Cette PR contient désormais **les specs ET l'implémentation complète**.
## Chantiers livrés
| # | Chantier | Détail |
|---|----------|--------|
| 2 | Sélecteur de statut filtré par workflow | `statusOptions` dérivé de `project.workflow.statuses`, statut courant conservé s'il est hors workflow |
| 1 | Drag & drop « Mes tâches » | handlers `@dragover/@drop` ; résolution par workflow/catégorie (0→refus, 1→PATCH, ≥2→popover `StatusPickerPopover`) |
| 4 | Couleurs | (a) migration Doctrine remettant les hex classiques sur le workflow Standard ; (b) entêtes kanban teintées via `STATUS_CATEGORY_COLOR` + contraste auto ; (c) couleur par défaut par catégorie dans `WorkflowDrawer` |
| 5 | Suppression du bouton « Lier un mail » | + retrait de `MailPickerModal` et i18n associée |
| 6 | Création de tâche depuis un mail | back : `assigneeId` + `statusId` (défaut = 1er statut du workflow), priorité retirée (TDD) ; front : `MailCreateTaskModal` sur `AppModal` + sélecteurs user/statut |
| 7 | Modale réutilisable | nouveau `components/ui/AppModal.vue` (footer sticky) ; footer de `TaskModal` sorti du form scrollable |
| 3 | Cartes responsive | badges en `flex-wrap` pleine taille (plus aucun débordement) |
| 8 | (dette) Sélecteur de catégorie en `MalioSelect` | la lib supporte les valeurs `string` ; note CLAUDE.md corrigée |
## Vérifications
- Build frontend OK ; PHPUnit **34 tests verts** (nouveau test fonctionnel TDD sur `create-task`).
- Vérif navigateur (Chrome MCP) sur **données prod importées en local** : #2 , #3 , #4 , #5 , #6 , #7 confirmés.
- Revue de code finale : **APPROVED_WITH_NITS**.
## À noter
- ⚠️ **#1 (D&D)** : le drag & drop HTML5 natif n'est pas auto-testable → **test manuel requis**.
- 🗄️ **#4 (migration)** : `migrations/Version20260521094948.php` s'appliquera en **prod au prochain `make migration-migrate`**.
🤖 Generated with [Claude Code](https://claude.com/claude-code )
Co-authored-by: Matthieu <mtholot19@gmail.com >
Reviewed-on: #6
2026-05-21 08:48:31 +00:00
matthieu
e5c5371c74
Merge branch 'develop' into feat/mail-integration
2026-05-20 07:45:09 +00:00
matthieu
c75dfa0371
fix(mail) : synchro multi-dossiers fiable contre OVH
...
Trois causes racines révélées par une vraie synchro complète (139 dossiers) :
- contrainte UNIQUE globale sur message_id : fausse pour IMAP (un même Message-ID
existe dans plusieurs dossiers) → violation → fermeture de l'EntityManager →
cascade qui tuait tous les dossiers suivants. Migration : index simple à la place.
- 139 connexions IMAP (une par dossier) → throttling OVH (failed to authenticate) :
réutilisation d'une seule connexion (closeConnection() ajouté à l'interface).
- état de connexion corrompu après un dossier en erreur (must be in SELECTED state) :
reconnexion ciblée après chaque dossier en échec.
- garde anti-cascade : reset du ManagerRegistry + arrêt propre si l'EM se ferme.
Résultat : 456 messages sur 57 dossiers (avant : 188/30 puis crash). Les rares
dossiers à encodage spécial sont skippés proprement et réessayés au cycle suivant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-20 08:21:02 +02:00
matthieu
cc46dd915d
feat(mail) : MailSyncRequested message + handler + messenger.yaml transport async Doctrine
...
- App\Message\MailSyncRequested (optionnel folderPath)
- App\MessageHandler\MailSyncRequestedHandler delegue a MailSyncService::syncFolder ou syncAll
- messenger.yaml : transport async via Doctrine DSN, retry 3x exponentiel, failure transport
- en test : transport in-memory (sync immediat)
- migration Version20260519220000 : cree messenger_messages table (idempotente, IF NOT EXISTS)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-20 00:14:47 +02:00
matthieu
0da26ff418
feat(mail) : migration — 4 tables mail_configuration, mail_folder, mail_message, task_mail_link
2026-05-19 23:20:03 +02:00
matthieu
5fb7fbe66c
fix(workflow) : M4 - aligne la séquence workflow.id après recréation de l'identity (évite conflit avec row Standard de M1)
2026-05-19 20:59:37 +02:00
matthieu
eec61c089c
feat(workflow) : migration M4 - alignement schéma Doctrine (indexes + IDENTITY)
2026-05-19 20:59:12 +02:00
matthieu
a21914312a
feat(workflow) : migration M3 - workflow requis sur Project (RESTRICT)
2026-05-19 20:59:12 +02:00
matthieu
f6a947ec15
feat(workflow) : migration M2 - rattache les statuts existants à Standard + category
2026-05-19 20:59:12 +02:00
matthieu
03f3c85fd8
feat(workflow) : migration M1 - création table workflow + seed Standard
2026-05-19 20:59:12 +02:00
Matthieu
1ae2d9ac2c
feat : add task_collaborator migration
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-09 09:54:28 +02:00
Matthieu
7e36b6fd49
feat : migration for TaskRecurrence, ZimbraConfiguration, and Task calendar fields
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-03-19 18:10:35 +01:00
Matthieu
1c6f473dff
feat(mcp) : add clientTicket relation to time entries
...
Add ManyToOne relation from TimeEntry to ClientTicket entity.
MCP tools create-time-entry, update-time-entry, and list-time-entries
now support clientTicketId parameter for linking tickets to time entries.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-03-16 14:28:31 +01:00
matthieu
e4fc34b90f
refactor : simplify codebase and fix critical issues
...
Backend:
- Add MCP Serializer to centralize entity-to-array conversion (~300 lines deduped)
- Fix race condition in task/ticket number generation (SELECT FOR UPDATE + transaction)
- Add unique constraint on task (project_id, number) with migration
- Fix MIME type validation: use server-detected finfo instead of client-supplied type
- Add allowlist of permitted MIME types for uploads
- Fix TaskDocumentDownloadController: allow ROLE_CLIENT access, add priority:1
- Fix notification sent even when ticket status unchanged
- Remove redundant exception constructors
- Simplify services (BookStackApi double fetch, TokenEncryptor, GiteaApi)
- Consolidate duplicate checks in processors
Frontend:
- Fix useApi isHandlingUnauthorized scope (module-level to prevent double 401 redirect)
- Fix client-tickets toast key copy-paste bug
- Merge duplicated tasks service methods (getByProject + getByProjectArchived)
- Extract shared uploadWithRelation helper in task-documents service
- Extract formatFileSize utility from duplicated component code
- Extract status transition logic into useClientTicketHelpers composable
- Remove dead code (unused router, handleLogout, empty script blocks)
- Merge duplicate watchers and onMounted calls
- Normalize arrow functions to function declarations per convention
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-03-15 22:09:16 +01:00
matthieu
63315c0a15
feat(avatar) : add avatarFileName field to User entity
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-03-15 21:53:43 +01:00
matthieu
669c36cea1
feat(notification) : add Notification entity, repository, and migration
2026-03-15 19:45:47 +01:00
matthieu
e16fd2053e
feat : MCP server infrastructure setup
...
Install symfony/mcp-bundle, add STDIO + HTTP transport config,
API token auth on User entity with custom authenticator and firewall,
generate-api-token console command, Nginx /_mcp location, fixture token.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-03-15 19:33:52 +01:00
matthieu
10cde5e2f9
feat : add client portal migration
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-15 19:25:36 +01:00
matthieu
52063cb4fa
feat(bookstack) : add migration for BookStack tables and Project columns
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-03-15 18:06:14 +01:00
matthieu
8ec98a593a
feat : add task_document migration
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-03-15 18:04:14 +01:00
matthieu
0733ac16cd
feat : add project archiving feature
...
Allow projects to be archived/unarchived from the ProjectDrawer, with a
toggle filter on the projects page to show/hide archived projects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com >
2026-03-14 08:58:29 +01:00
Matthieu
50690e6680
feat : add migration for GiteaConfiguration and Project gitea fields
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com >
2026-03-13 13:55:39 +01:00
Matthieu
2c2ca0a8b6
feat(backend) : add migration for isFinal, archived fields
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com >
2026-03-12 17:58:20 +01:00
Matthieu
df29214509
feat(backend) : add migration to remove project_id from task_status
2026-03-12 11:48:43 +01:00
matthieu
e9ca888971
feat(time-tracking) : add TimeEntry entity and migration
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com >
2026-03-10 22:10:27 +01:00
matthieu
66bb94fc98
feat(backend) : add project relation to TaskStatus entity with migration and fixtures
...
Add ManyToOne project field on TaskStatus, SearchFilter for API filtering,
migration to add the column, and update fixtures to create statuses per project.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com >
2026-03-10 21:58:41 +01:00
matthieu
1d50e5dcb3
feat : add Task entities, repositories and migration
...
Add Task, TaskStatus, TaskEffort, TaskPriority, TaskType, TaskGroup
entities with Doctrine mappings and API Platform CRUD operations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com >
2026-03-09 23:40:48 +01:00
matthieu
b56d2f6526
feat : add Project entity with CRUD API and Client relation
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com >
2026-03-09 23:40:48 +01:00
matthieu
0621388ee6
feat : add Client entity with CRUD API
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com >
2026-03-09 23:40:48 +01:00
tristan
47562fbdec
feat : config + login
2026-03-08 19:47:19 +01:00