Conception complete (Phase 0) pour formation-hub Acadenice : - 19 docs Merise Agile + UML + GitOps + plans (tests/deploy/ops/api) cf docs/00-readme.md pour l'index complet - Stack Docker compose (Docmost + Baserow + Postgres + Redis + MinIO local FS) compose.yml + compose.staging.yml + compose.prod.yml - CI/CD GitHub Actions skeleton (ci, deploy-staging, deploy-prod) - Bridge service skeleton (Hono + TS + Biome + Vitest + zod + pino) - Templates GitHub : PR + 3 issue types + CODEOWNERS + dependabot.yml - Scripts ops : healthcheck, backup quotidien, smoke-test post-deploy - LICENSE AGPL-3.0 + SECURITY.md + CONTRIBUTING.md + CHANGELOG.md - Diagramme drawIO archi infra (XML importable dans diagrams.net) Decisions structurelles enregistrees : - Scope CFA + Agence avec entite PERSONNE pivot multi-roles (ADR-001) - Stack composite Docmost AGPL + Baserow MIT + bridge custom (ADR-001) - Path B : UX quasi-unified via Tiptap node-views custom (ADR-002) - Monorepo trunk-based development (ADR-003) - Postgres separe Docmost/Baserow (ADR-004) - Bridge stack Node 22 + Hono (ADR-005) - Repo neuf prefere a fork Docmost - Prod-like des le jour 1 (pas MVP)
428 lines
21 KiB
Markdown
428 lines
21 KiB
Markdown
# MPD — Modele Physique de Donnees (Baserow)
|
|
|
|
> Implementation concrete dans Baserow : 9 tables avec types exacts, formules, vues, permissions.
|
|
> Ce doc est **actionnable** : Corentin l'ouvre cote a cote avec Baserow et cree les tables une par une.
|
|
> Source : `07-merise-mld.md` (MLD relationnel) + `05-data-dictionary.md`.
|
|
|
|
## 1. Setup initial
|
|
|
|
### 1.1 Hierarchie Baserow
|
|
|
|
```
|
|
Workspace : Acadenice formation-hub
|
|
└── Database : formation-hub
|
|
├── Tables CFA : formation, bloc, module, attribution
|
|
├── Tables Agence : client, projet, tache, intervention
|
|
└── Table pivot : personne
|
|
```
|
|
|
|
### 1.2 Order de creation (dependances FK)
|
|
|
|
Creer les tables dans cet ordre — chaque table dependant des precedentes via FK :
|
|
|
|
1. **personne** (aucune dep)
|
|
2. **client** (aucune dep)
|
|
3. **formation** (aucune dep)
|
|
4. **bloc** (FK → formation)
|
|
5. **projet** (FK → client, optionnel FK → formation)
|
|
6. **module** (FK → bloc)
|
|
7. **tache** (FK → projet)
|
|
8. **attribution** (FK → module + personne)
|
|
9. **intervention** (FK → tache + personne)
|
|
|
|
### 1.3 Conventions Baserow
|
|
|
|
| Concept | Implementation Baserow |
|
|
|---------|------------------------|
|
|
| ID auto-increment (PK) | Baserow `id` natif (genere auto) |
|
|
| Nom du field | `snake_case`, prefixes par mnemonique d'entite (ex `formation_nom`, `personne_email`) |
|
|
| Field "Primary" | Baserow oblige a avoir un Primary field — choisir le nom le plus explicite (ex `formation_nom`) |
|
|
| Foreign Key | Field type `Link to table` |
|
|
| ENUM | Field type `Single select` avec options exactes |
|
|
| MULTI ENUM | Field type `Multiple select` |
|
|
| Champ calcule (formula) | Field type `Formula` |
|
|
| Champ derive d'une relation | Field type `Lookup` ou `Count` |
|
|
| Audit timestamps | Fields `Created on` + `Last modified time` natifs |
|
|
| Audit acteur | Fields `Created by` + `Last modified by` natifs |
|
|
|
|
### 1.4 API token
|
|
|
|
Apres creation des tables :
|
|
- Settings → API tokens → Create token
|
|
- Permissions : `read`/`create`/`update`/`delete` sur la database `formation-hub`
|
|
- Stocker dans `.env` cote bridge : `BASEROW_API_TOKEN=...`
|
|
|
|
---
|
|
|
|
## 2. Table `personne`
|
|
|
|
**Primary field** : `personne_nom` (texte affiche par defaut dans les links)
|
|
|
|
| # | Field | Type Baserow | Parametres | Description |
|
|
|---|-------|-------------|------------|-------------|
|
|
| 1 | `personne_nom` | Text | — | Nom de famille (Primary) |
|
|
| 2 | `personne_prenom` | Text | — | Prenom |
|
|
| 3 | `personne_email` | Email | — | Email pro (unique recommande, validation cote bridge) |
|
|
| 4 | `personne_telephone` | Phone number | — | Telephone (optionnel) |
|
|
| 5 | `personne_capacite_annuelle` | Number | Decimal places: 2 | Heures totales/an |
|
|
| 6 | `personne_split_formation_pct` | Number | Decimal places: 1, default 50 | % capacite alloue formation |
|
|
| 7 | `personne_split_agence_pct` | Number | Decimal places: 1, default 50 | % capacite alloue agence |
|
|
| 8 | `personne_roles` | Multiple select | Options: `formateur`, `developpeur`, `admin`, `direction`, `support` | Roles cumules |
|
|
| 9 | `personne_statut` | Single select | Options: `actif` (default), `inactif` | Statut |
|
|
| 10 | `personne_attributions` | Link to table | Lien vers `attribution` (champ inverse auto-cree apres creation de attribution) | Toutes les attributions de cette personne |
|
|
| 11 | `personne_interventions` | Link to table | Lien vers `intervention` (apres creation) | Toutes les interventions |
|
|
| 12 | `personne_heures_attribuees_formation` | Formula | `sum(lookup('personne_attributions', 'attribution_heures_attribuees_active'))` | Rollup des attributions actives |
|
|
| 13 | `personne_heures_attribuees_agence` | Formula | `sum(lookup('personne_interventions', 'intervention_heures_active'))` | Rollup des interventions actives |
|
|
| 14 | `personne_heures_restantes_formation` | Formula | `(field('personne_capacite_annuelle') * field('personne_split_formation_pct') / 100) - field('personne_heures_attribuees_formation')` | Capacite formation restante |
|
|
| 15 | `personne_heures_restantes_agence` | Formula | `(field('personne_capacite_annuelle') * field('personne_split_agence_pct') / 100) - field('personne_heures_attribuees_agence')` | Capacite agence restante |
|
|
| 16 | `personne_heures_restantes_total` | Formula | `field('personne_capacite_annuelle') - field('personne_heures_attribuees_formation') - field('personne_heures_attribuees_agence')` | Capacite totale restante |
|
|
|
|
**Vues recommandees** :
|
|
- `Tous` (grid, default) — tableau complet
|
|
- `Actifs` (grid, filtre `personne_statut = actif`)
|
|
- `Formateurs` (grid, filtre `personne_roles contient formateur`)
|
|
- `Developpeurs` (grid, filtre `personne_roles contient developpeur`)
|
|
- `Capacite restante` (grid, sort `personne_heures_restantes_total ascending`)
|
|
|
|
---
|
|
|
|
## 3. Table `formation`
|
|
|
|
**Primary field** : `formation_nom`
|
|
|
|
| # | Field | Type Baserow | Parametres | Description |
|
|
|---|-------|-------------|------------|-------------|
|
|
| 1 | `formation_nom` | Text | — | Nom (Primary, unique conseille) |
|
|
| 2 | `formation_description` | Long text | rich text autorise | Description longue |
|
|
| 3 | `formation_filiere` | Single select | `dev`, `graphisme`, `marketing`, `iot`, `cybersec` | Filiere |
|
|
| 4 | `formation_heures_totales` | Number | Decimal places: 2 | Heures totales prevues |
|
|
| 5 | `formation_statut` | Single select | `draft` (default), `actif`, `termine`, `archive` | Cycle de vie |
|
|
| 6 | `formation_date_debut` | Date | format `YYYY-MM-DD` | Date debut |
|
|
| 7 | `formation_date_fin` | Date | — | Date fin |
|
|
| 8 | `formation_blocs` | Link to table | Lien vers `bloc` (apres creation bloc) | Blocs de la formation |
|
|
| 9 | `formation_projets_pedagogiques` | Link to table | Lien vers `projet` (optionnel) | Projets agence lies en pedagogique |
|
|
| 10 | `formation_heures_attribuees` | Formula | `sum(lookup('formation_blocs', 'bloc_heures_prevues'))` | Rollup heures des blocs |
|
|
| 11 | `formation_heures_restantes` | Formula | `field('formation_heures_totales') - field('formation_heures_attribuees')` | Reste a attribuer |
|
|
| 12 | `formation_created_at` | Created on | — | Audit |
|
|
| 13 | `formation_updated_at` | Last modified time | — | Audit |
|
|
|
|
**Vues** :
|
|
- `Tous` (grid)
|
|
- `Actives` (grid, filtre `formation_statut = actif`)
|
|
- `Par filiere` (grid, group by `formation_filiere`)
|
|
- `Calendrier` (calendar view, sur `formation_date_debut`)
|
|
- `Capacite restante` (grid, sort `formation_heures_restantes ascending`)
|
|
|
|
---
|
|
|
|
## 4. Table `bloc`
|
|
|
|
**Primary field** : `bloc_nom`
|
|
|
|
| # | Field | Type Baserow | Parametres | Description |
|
|
|---|-------|-------------|------------|-------------|
|
|
| 1 | `bloc_nom` | Text | — | Nom du bloc (Primary) |
|
|
| 2 | `bloc_description` | Long text | — | Description |
|
|
| 3 | `bloc_formation` | Link to table | Lien vers `formation` (single) | Formation parente |
|
|
| 4 | `bloc_heures_prevues` | Number | Decimal places: 2 | Heures du bloc |
|
|
| 5 | `bloc_ordre` | Number | Decimal places: 0 | Ordre dans la formation |
|
|
| 6 | `bloc_modules` | Link to table | Lien vers `module` (apres creation) | Modules du bloc |
|
|
| 7 | `bloc_heures_attribuees` | Formula | `sum(lookup('bloc_modules', 'module_heures_prevues_active'))` | Rollup heures modules actifs |
|
|
| 8 | `bloc_heures_restantes` | Formula | `field('bloc_heures_prevues') - field('bloc_heures_attribuees')` | Reste a decomposer |
|
|
|
|
**Note** : la regle metier "un bloc a un nom unique par formation" se valide **cote bridge** ou via une vue filtree de duplication.
|
|
|
|
**Vues** :
|
|
- `Tous` (grid)
|
|
- `Par formation` (grid, group by `bloc_formation`)
|
|
|
|
---
|
|
|
|
## 5. Table `module`
|
|
|
|
**Primary field** : `module_nom`
|
|
|
|
| # | Field | Type Baserow | Parametres | Description |
|
|
|---|-------|-------------|------------|-------------|
|
|
| 1 | `module_nom` | Text | — | Nom du module (Primary) |
|
|
| 2 | `module_description` | Long text | — | Description |
|
|
| 3 | `module_bloc` | Link to table | Lien vers `bloc` (single) | Bloc parent |
|
|
| 4 | `module_heures_prevues` | Number | Decimal places: 2 | Heures du module |
|
|
| 5 | `module_statut` | Single select | `a_attribuer` (default), `attribue`, `en_cours`, `realise`, `annule` | Cycle de vie |
|
|
| 6 | `module_attributions` | Link to table | Lien vers `attribution` (apres creation) | Attributions du module |
|
|
| 7 | `module_heures_prevues_active` | Formula | `if(field('module_statut') = 'annule', 0, field('module_heures_prevues'))` | Pour rollup bloc (exclut annule) |
|
|
| 8 | `module_heures_attribuees` | Formula | `sum(lookup('module_attributions', 'attribution_heures_attribuees_active'))` | Rollup |
|
|
| 9 | `module_heures_realisees` | Formula | `sum(lookup('module_attributions', 'attribution_heures_realisees'))` | Rollup |
|
|
|
|
**Vues** :
|
|
- `Tous` (grid)
|
|
- **`A attribuer`** (kanban, group by `module_statut`) — vue principale pour l'admin
|
|
- `Par bloc` (grid, group by `module_bloc`)
|
|
- `Realises` (grid, filtre `module_statut = realise`)
|
|
|
|
---
|
|
|
|
## 6. Table `attribution`
|
|
|
|
**Primary field** : `attribution_titre` (formula : nom_module + " → " + nom_personne)
|
|
|
|
| # | Field | Type Baserow | Parametres | Description |
|
|
|---|-------|-------------|------------|-------------|
|
|
| 1 | `attribution_titre` | Formula | `concat(lookup('attribution_module', 'module_nom'), ' → ', lookup('attribution_personne', 'personne_prenom'), ' ', lookup('attribution_personne', 'personne_nom'))` | Titre auto (Primary) |
|
|
| 2 | `attribution_module` | Link to table | Lien vers `module` (single) | Module attribue |
|
|
| 3 | `attribution_personne` | Link to table | Lien vers `personne` (single) | Formateur (role formateur requis) |
|
|
| 4 | `attribution_heures_attribuees` | Number | Decimal places: 2 | Heures planifiees |
|
|
| 5 | `attribution_heures_realisees` | Number | Decimal places: 2, default 0 | Heures effectuees |
|
|
| 6 | `attribution_date_debut` | Date | — | Debut periode |
|
|
| 7 | `attribution_date_fin` | Date | — | Fin periode |
|
|
| 8 | `attribution_statut` | Single select | `planifie` (default), `en_cours`, `realise`, `annule` | Statut |
|
|
| 9 | `attribution_heures_attribuees_active` | Formula | `if(field('attribution_statut') = 'annule', 0, field('attribution_heures_attribuees'))` | Pour rollup module/personne |
|
|
|
|
**Validation cote bridge** :
|
|
- `attribution_personne.personne_roles` doit contenir `formateur`
|
|
- `sum(attribution_heures_attribuees) for module <= module_heures_prevues` (RG-01)
|
|
|
|
**Vues** :
|
|
- `Tous` (grid)
|
|
- **`Mes attributions`** (grid, filtre `attribution_personne = current user`) — vue formateur
|
|
- `En cours` (grid, filtre `attribution_statut = en_cours`)
|
|
- `Calendrier` (calendar view sur `attribution_date_debut` ou `attribution_date_fin`)
|
|
- `Form public` (form view) — formateur saisit ses heures realisees rapide
|
|
|
|
---
|
|
|
|
## 7. Table `client`
|
|
|
|
**Primary field** : `client_nom`
|
|
|
|
| # | Field | Type Baserow | Parametres | Description |
|
|
|---|-------|-------------|------------|-------------|
|
|
| 1 | `client_nom` | Text | — | Nom (Primary) |
|
|
| 2 | `client_contact_principal` | Text | — | Nom + role du contact |
|
|
| 3 | `client_contact_email` | Email | — | Email contact |
|
|
| 4 | `client_contact_telephone` | Phone number | — | Telephone |
|
|
| 5 | `client_secteur` | Text | — | Secteur d'activite |
|
|
| 6 | `client_notes` | Long text | — | Notes libres |
|
|
| 7 | `client_statut` | Single select | `prospect` (default), `actif`, `inactif`, `archive` | Statut |
|
|
| 8 | `client_projets` | Link to table | Lien vers `projet` | Projets du client |
|
|
| 9 | `client_created_at` | Created on | — | Audit |
|
|
|
|
**Vues** :
|
|
- `Tous` (grid)
|
|
- `Actifs` (grid, filtre `client_statut = actif`)
|
|
- **`Pipeline`** (kanban, group by `client_statut`) — vue commerciale
|
|
|
|
---
|
|
|
|
## 8. Table `projet`
|
|
|
|
**Primary field** : `projet_nom`
|
|
|
|
| # | Field | Type Baserow | Parametres | Description |
|
|
|---|-------|-------------|------------|-------------|
|
|
| 1 | `projet_nom` | Text | — | Nom (Primary) |
|
|
| 2 | `projet_description` | Long text | — | Description |
|
|
| 3 | `projet_client` | Link to table | Lien vers `client` (single) | Client |
|
|
| 4 | `projet_type` | Single select | `site_web`, `app_mobile`, `api`, `infra`, `audit`, `support`, `autre` | Type |
|
|
| 5 | `projet_charge_heures` | Number | Decimal places: 2 | Charge estimee |
|
|
| 6 | `projet_date_debut` | Date | — | Date debut |
|
|
| 7 | `projet_date_fin_prevue` | Date | — | Date fin prevue |
|
|
| 8 | `projet_date_livraison` | Date | — | Date livraison effective |
|
|
| 9 | `projet_statut` | Single select | `devis` (default), `en_cours`, `livre`, `cloture`, `abandonne` | Statut |
|
|
| 10 | `projet_formation_pedagogique` | Link to table | Lien vers `formation` (single, optionnel) | Lien pedagogique |
|
|
| 11 | `projet_url` | URL | — | Site livraison |
|
|
| 12 | `projet_repository` | URL | — | Repo Git |
|
|
| 13 | `projet_taches` | Link to table | Lien vers `tache` | Taches du projet |
|
|
| 14 | `projet_heures_attribuees` | Formula | `sum(lookup('projet_taches', 'tache_charge_heures'))` | Rollup taches |
|
|
| 15 | `projet_heures_realisees` | Formula | `sum(lookup('projet_taches', 'tache_heures_realisees'))` | Rollup |
|
|
| 16 | `projet_heures_restantes` | Formula | `field('projet_charge_heures') - field('projet_heures_realisees')` | Reste a faire |
|
|
|
|
**Vues** :
|
|
- `Tous` (grid)
|
|
- **`Pipeline`** (kanban, group by `projet_statut`) — vue principale
|
|
- `En cours` (grid, filtre `projet_statut = en_cours`)
|
|
- `Timeline` (timeline view sur date_debut → date_fin_prevue)
|
|
- `Par client` (grid, group by `projet_client`)
|
|
|
|
---
|
|
|
|
## 9. Table `tache`
|
|
|
|
**Primary field** : `tache_titre`
|
|
|
|
| # | Field | Type Baserow | Parametres | Description |
|
|
|---|-------|-------------|------------|-------------|
|
|
| 1 | `tache_titre` | Text | — | Titre (Primary) |
|
|
| 2 | `tache_description` | Long text | — | Description |
|
|
| 3 | `tache_projet` | Link to table | Lien vers `projet` (single) | Projet parent |
|
|
| 4 | `tache_charge_heures` | Number | Decimal places: 2 | Charge estimee |
|
|
| 5 | `tache_priorite` | Single select | `faible`, `normale`, `haute`, `critique` | Priorite |
|
|
| 6 | `tache_statut` | Single select | `todo` (default), `in_progress`, `review`, `done`, `abandoned` | Statut |
|
|
| 7 | `tache_date_debut` | Date | — | Debut prevu |
|
|
| 8 | `tache_date_fin_prevue` | Date | — | Fin prevue |
|
|
| 9 | `tache_assignee` | Link to table | Lien vers `personne` (single, optionnel) | Dev assignee informellement |
|
|
| 10 | `tache_interventions` | Link to table | Lien vers `intervention` | Interventions sur cette tache |
|
|
| 11 | `tache_heures_realisees` | Formula | `sum(lookup('tache_interventions', 'intervention_heures_active'))` | Rollup |
|
|
|
|
**Vues** :
|
|
- `Tous` (grid)
|
|
- **`Kanban`** (kanban, group by `tache_statut`) — vue principale
|
|
- `Par priorite` (grid, group by `tache_priorite`, sort par priorite)
|
|
- `Mes taches` (grid, filtre `tache_assignee = current user`)
|
|
- `Done recentes` (grid, filtre `tache_statut = done`, sort par date desc)
|
|
|
|
---
|
|
|
|
## 10. Table `intervention`
|
|
|
|
**Primary field** : `intervention_titre` (formula auto)
|
|
|
|
| # | Field | Type Baserow | Parametres | Description |
|
|
|---|-------|-------------|------------|-------------|
|
|
| 1 | `intervention_titre` | Formula | `concat(lookup('intervention_tache', 'tache_titre'), ' - ', lookup('intervention_personne', 'personne_prenom'), ' (', totext(field('intervention_date')), ')')` | Titre auto (Primary) |
|
|
| 2 | `intervention_tache` | Link to table | Lien vers `tache` (single) | Tache concernee |
|
|
| 3 | `intervention_personne` | Link to table | Lien vers `personne` (single) | Developpeur (role developpeur requis) |
|
|
| 4 | `intervention_heures` | Number | Decimal places: 2 | Heures effectuees |
|
|
| 5 | `intervention_date` | Date | default today | Date intervention |
|
|
| 6 | `intervention_notes` | Long text | — | Notes / commit ref / lien PR |
|
|
| 7 | `intervention_statut` | Single select | `planifie`, `realise` (default), `annule` | Statut |
|
|
| 8 | `intervention_heures_active` | Formula | `if(field('intervention_statut') = 'annule', 0, field('intervention_heures'))` | Pour rollup tache/personne |
|
|
|
|
**Validation cote bridge** :
|
|
- `intervention_personne.personne_roles` doit contenir `developpeur`
|
|
- `intervention_heures > 0`
|
|
|
|
**Vues** :
|
|
- `Tous` (grid, sort `intervention_date desc`)
|
|
- **`Mes interventions`** (grid, filtre `intervention_personne = current user`) — vue dev
|
|
- **`Form rapide`** (form public) — saisie heures rapide mobile
|
|
- `Par projet` (grid, group by `intervention_tache.tache_projet`)
|
|
- `Cette semaine` (grid, filtre `intervention_date >= start_of_week`)
|
|
|
|
---
|
|
|
|
## 11. Permissions et sharing
|
|
|
|
### 11.1 Roles Baserow
|
|
|
|
| Role | Membres | Capacites |
|
|
|------|---------|-----------|
|
|
| Admin workspace | Corentin, Yan, Ludo | Plein controle |
|
|
| Editor | Sophie + autres admins | Read/write toutes tables |
|
|
| Builder | Formateurs / Devs | Read/write **leur ligne** via vues filtrees + form rapide |
|
|
| Viewer | Stakeholders ponctuels | Read seul |
|
|
|
|
**Limitation** : Baserow native permissions sont au niveau database/table, pas row-level. Pour limiter formateur/dev a leurs propres rows :
|
|
- Soit **vues filtrees partagees publiquement** (form pour saisie + grid filtree pour lecture)
|
|
- Soit **bridge service** qui filtre cote API selon `current_user_id`
|
|
|
|
### 11.2 Forms publics pour saisie rapide
|
|
|
|
Plus simple que de gerer les permissions row-level :
|
|
- Form public sur `attribution` — formateur saisit ses heures via lien sans compte Baserow
|
|
- Form public sur `intervention` — dev saisit son intervention idem
|
|
|
|
Le user qui saisit n'a pas besoin de voir le reste des donnees, juste son formulaire.
|
|
|
|
---
|
|
|
|
## 12. Webhooks (Phase 2 — bridge integration)
|
|
|
|
Configurer dans Baserow → Database Settings → Webhooks :
|
|
|
|
| Evenement | URL cible | Usage bridge |
|
|
|-----------|-----------|--------------|
|
|
| `row.created` sur `attribution` | `https://bridge.acadenice.fr/webhooks/attribution-created` | Notif formateur, recalcul cache mention |
|
|
| `row.updated` sur `attribution` | `https://bridge.acadenice.fr/webhooks/attribution-updated` | Recalcul cache, notif si statut change |
|
|
| `row.created` sur `intervention` | `https://bridge.acadenice.fr/webhooks/intervention-created` | Notif admin si depassement capacite |
|
|
| `row.updated` sur `module` (si `module_statut` change) | `https://bridge.acadenice.fr/webhooks/module-status-changed` | Trigger cloturer formation auto |
|
|
|
|
Authentification webhook : header `X-Bridge-Token` avec un secret partage (`.env`).
|
|
|
|
---
|
|
|
|
## 13. Seed data initial
|
|
|
|
Apres creation des 9 tables, seed avec :
|
|
- **personne** : equipe Acadenice (Yan, Corentin, Ludo, Sophie + formateurs intervenants)
|
|
- **client** : 1-2 clients existants (Centralis Europe + autre)
|
|
- **formation** : les 5 filieres en cours pour 2026-2027
|
|
- **bloc** : decoupage RNCP par filiere
|
|
- **module** : programme detaille
|
|
- **projet** : projets clients en cours
|
|
- Pas d'attribution / intervention seed — saisies par l'usage
|
|
|
|
Script de seed : `baserow/seed/seed.py` (a coder Phase 1).
|
|
|
|
---
|
|
|
|
## 14. Validation post-creation
|
|
|
|
Checklist apres creation des 9 tables :
|
|
|
|
- [ ] Les 9 tables existent dans la database `formation-hub`
|
|
- [ ] Toutes les FK sont liees correctement (verifier en cliquant sur un lien dans une row)
|
|
- [ ] Les rollups fonctionnent (creer une row test, verifier le calcul de `formation_heures_attribuees`)
|
|
- [ ] Les formulas s'evaluent sans erreur (regarder les rows test)
|
|
- [ ] Les Single Select / Multiple Select ont les bonnes options
|
|
- [ ] Au moins une vue par table est creee (grid `Tous` minimum)
|
|
- [ ] Les vues kanban (module, projet, tache, client) sont fonctionnelles
|
|
- [ ] Le form public pour saisie heures (attribution, intervention) marche
|
|
- [ ] L'API token est genere et fonctionne (test `curl`)
|
|
|
|
```bash
|
|
# Test API token
|
|
curl -H "Authorization: Token $BASEROW_API_TOKEN" \
|
|
"$BASEROW_URL/api/database/rows/table/<TABLE_ID>/?user_field_names=true"
|
|
```
|
|
|
|
---
|
|
|
|
## 15. Notes d'implementation
|
|
|
|
### 15.1 Limitations Baserow connues
|
|
|
|
- **Pas de FK `ON DELETE` configurable** : c'est `SET NULL` par defaut quand le lien est rompu. Pour forcer un comportement CASCADE/RESTRICT, le bridge service doit l'implementer (ou un workflow Baserow).
|
|
- **Pas de CHECK constraint** : validation cote bridge ou cote UI.
|
|
- **Pas d'index custom** : Baserow indexe automatiquement les Link to table et les Primary fields.
|
|
- **Formules limitees** : pas de boucles ni de subqueries complexes. Pour calculs lourds, calcul cote bridge + ecriture en batch.
|
|
|
|
### 15.2 Alternatives si Baserow limite
|
|
|
|
Si une formule devient trop complexe ou si on a besoin de validation forte :
|
|
- Option A : **Bridge fait le calcul** et ecrit en Baserow via API
|
|
- Option B : **Vue filtree dediee** + formula simple
|
|
- Option C : **Migration vers Postgres direct** (futur — si on perd Baserow)
|
|
|
|
### 15.3 Migration data initiale
|
|
|
|
Si donnees existent dans Excel/Trello/autre :
|
|
1. Exporter en CSV
|
|
2. Mapper les colonnes vers les fields Baserow
|
|
3. Importer via Baserow UI (`Import data`)
|
|
4. Verifier les liens FK manuellement (Baserow ne mappe pas auto les liens via CSV)
|
|
|
|
---
|
|
|
|
## 16. Resume — checklist d'implementation Phase 1
|
|
|
|
```
|
|
[ ] 1. Setup workspace + database
|
|
[ ] 2. Creer table 'personne' (sans liens encore)
|
|
[ ] 3. Creer table 'client' (sans liens encore)
|
|
[ ] 4. Creer table 'formation' (sans liens encore)
|
|
[ ] 5. Creer table 'bloc' + lien vers formation
|
|
[ ] 6. Creer table 'projet' + lien vers client (+ optionnel formation)
|
|
[ ] 7. Creer table 'module' + lien vers bloc
|
|
[ ] 8. Creer table 'tache' + lien vers projet (+ optionnel personne assignee)
|
|
[ ] 9. Creer table 'attribution' + liens vers module + personne
|
|
[ ] 10. Creer table 'intervention' + liens vers tache + personne
|
|
[ ] 11. Ajouter formulas et lookups (apres tous les liens crees)
|
|
[ ] 12. Creer vues recommandees par table
|
|
[ ] 13. Configurer permissions roles + sharing
|
|
[ ] 14. Seed data initial
|
|
[ ] 15. Generer API token + verifier
|
|
[ ] 16. Documenter exports JSON dans `baserow/schemas/`
|
|
```
|
|
|
|
Apres ca : la base structurelle est en place. La saisie metier peut commencer **immediat** — sans attendre Phase 2 / bridge.
|