L'opérateur tient ses documents hors ERP, dans 1_DOCUMENTS versionné en git. Ce dépôt reste l'exemplaire de référence ; la GED en reçoit le sous-ensemble dont un lecteur des comptes a besoin — ce qui justifie une charge, ce qui prouve que la société existe, ce qui fixe les termes entre la société et son gérant. Le manifeste est déclaratif (gedManifest.ts) : ajouter un document, c'est ajouter une ligne. Les exclusions sont délibérées et commentées — pièces d'identité (données personnelles, aucun lecteur comptable n'en a besoin, l'ERP est exposé), correspondance administrative, et le bail sous-jacent (8,6 Mo contre un plafond de 2 Mo ; le recompresser altérerait le rendu d'une pièce juridique avec un outil non vérifié). Les factures de logement sont versées comme JUSTIFICATIFS du forfait de 220 EUR de la convention d'occupation, jamais comme charges : la convention les inclut forfaitairement et exclut toute régularisation. Les enregistrer séparément serait un double emploi. Le répertoire le dit dans sa description. Cinq pièges ont coûté cher et sont consignés dans RUNBOOK_ged.md : - l'API REST MENT sur modulepart=ecm. Elle répond le nom du fichier — donc succès — sans rien déposer que la GED sache retrouver : arbre vide et 404 sur le chemin qu'on vient d'écrire. Elle accepte de surcroît n'importe quel subdir. La GED manuelle ne s'alimente que par l'UI, qui écrit ET indexe ; - un répertoire ne se crée que par l'UI ; « existe déjà » vaut succès ; - l'arbre est replié et monté en JS, ses ancres portent href="#", le chemin vit dans rel et l'id dans le onclick, et les enfants n'arrivent qu'au dépliage ; - au-delà de 2 Mo le formulaire accepte, n'écrit rien et n'affiche aucune erreur ; - la case « écraser » ne s'applique pas : chaque passe dupliquait. D'où le principe de vérification : par les NOMS, jamais par un compteur — un compteur juste peut recouvrir deux exemplaires d'une pièce et l'absence d'une autre. C'est ce contrôle qui a révélé l'échec silencieux de l'API. Répété en sandbox, puis appliqué en production : 5 répertoires, 10 pièces, chacune relue par son nom dans la GED. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
93 lines
4.3 KiB
Markdown
93 lines
4.3 KiB
Markdown
# Runbook — verser un document dans la GED Dolibarr
|
|
|
|
Public : un agent, quel que soit son modèle, ou l'opérateur.
|
|
|
|
La question à se poser en premier n'est pas « comment téléverser » mais **« à
|
|
quoi ce document se rattache-t-il ? »**. La réponse décide de tout, et l'un des
|
|
deux chemins ne marche pas du tout comme son API le prétend.
|
|
|
|
## Deux GED, pas une
|
|
|
|
Dolibarr expose deux arbres, et ils n'ont rien en commun :
|
|
|
|
| Arbre | URL | Contenu | Comment y écrire |
|
|
| --- | --- | --- | --- |
|
|
| **Répertoires d'objets** | `/ecm/index_auto.php` | pièces rattachées à une facture, un tiers, une charge | **API REST**, `modulepart` de l'objet |
|
|
| **Répertoires manuels** | `/ecm/index.php` | ce qui n'a pas d'objet : statuts, KBIS, registre des décisions | **UI uniquement** |
|
|
|
|
**Règle :** une pièce qui justifie un fait comptable se rattache à son objet,
|
|
jamais à la GED manuelle. L'appel de cotisations URSSAF va sur les charges, une
|
|
facture fournisseur sur sa facture. La GED manuelle est le dernier recours, pour
|
|
ce qui ne se rattache à rien.
|
|
|
|
## Rattacher à un objet — API
|
|
|
|
```bash
|
|
curl -s -X POST https://erp.arcodange.lab/api/index.php/documents/upload \
|
|
-H "DOLAPIKEY: $(cat test/.ai_agent_prod_prod_write.key)" \
|
|
-H 'Content-Type: application/json' -d @- <<JSON
|
|
{"filename":"piece.pdf","modulepart":"tax","subdir":"<ID DE L'OBJET>",
|
|
"filecontent":"<BASE64>","fileencoding":"base64","overwriteifexists":1}
|
|
JSON
|
|
```
|
|
|
|
`subdir` est l'**identifiant nu** de l'objet (`2`), rien d'autre. Vérifier sur
|
|
l'onglet Documents de l'objet que le compteur a bougé.
|
|
|
|
## Verser dans la GED manuelle — script
|
|
|
|
```bash
|
|
cd test
|
|
DOLIBARR_ADDRESS=https://erp-sandbox.arcodange.lab deno run -A provisionGed.ts
|
|
```
|
|
|
|
Production, double opt-in comme toute écriture de production :
|
|
|
|
```bash
|
|
DOLIBARR_ADDRESS=https://erp.arcodange.lab \
|
|
ARCO_ALLOW_PRODUCTION=erp.arcodange.lab \
|
|
ARCO_PROD_CONFIRM=I-UNDERSTAND-THIS-WRITES-PROD \
|
|
deno run -A provisionGed.ts
|
|
```
|
|
|
|
`--dry-run` liste ce qui serait versé sans rien écrire, et vérifie déjà que
|
|
chaque pièce du manifeste existe sur le disque.
|
|
|
|
Ce qui est versé est déclaré dans **`test/gedManifest.ts`** — répertoires et
|
|
pièces. Ajouter un document, c'est ajouter une ligne au manifeste et relancer :
|
|
le script est idempotent, une pièce déjà présente n'est pas re-versée.
|
|
|
|
## Les cinq pièges, tous rencontrés
|
|
|
|
1. **L'API ment sur `modulepart=ecm`.** Elle répond le nom du fichier — donc
|
|
succès — et ne dépose rien que la GED sache retrouver : l'arbre affiche « Pas
|
|
de documents téléversés » et `/documents/download` répond **404** sur le
|
|
chemin qu'on vient d'écrire. Elle accepte de surcroît **n'importe quel
|
|
`subdir`** sans broncher. Pour la GED manuelle, **passer par l'UI**, qui écrit
|
|
au bon endroit *et* inscrit la pièce à l'index — c'est l'index qui alimente
|
|
compteurs et recherche. Une pièce non indexée est une pièce perdue.
|
|
2. **Un répertoire ne se crée que par l'UI**, sur
|
|
`/ecm/dir_add_card.php?action=create&module=ecm`. Le formulaire répond
|
|
« existe déjà » quand c'est le cas : cela vaut succès pour un script
|
|
idempotent, ce n'est pas une erreur.
|
|
3. **L'arbre est replié et monté en JS.** `innerText` sur son conteneur renvoie
|
|
vide alors que les répertoires existent ; ses ancres portent `href="#"`, le
|
|
chemin vit dans l'attribut `rel`, et l'identifiant de section dans le
|
|
`onclick`. Les sous-répertoires n'arrivent en DOM qu'au **dépliage**. Lire
|
|
trop tôt, ou chercher un lien `section=`, fait conclure à un arbre vide.
|
|
4. **Plafond de 2 Mo** (`MAX_FILE_SIZE`). Au-delà, le formulaire accepte la
|
|
soumission, n'écrit rien et **n'affiche aucune erreur** : le fichier
|
|
disparaît en silence. Le script contrôle la taille avant d'envoyer. Pour
|
|
relever la limite : `MAIN_UPLOAD_DOC` côté Dolibarr, `upload_max_filesize`
|
|
côté PHP.
|
|
5. **La case « écraser » ne s'applique pas de façon fiable** : chaque passe
|
|
ajoutait un doublon. L'idempotence se joue sur le **nom du fichier**, relu
|
|
dans le répertoire avant et après.
|
|
|
|
## Vérifier
|
|
|
|
Toujours par les **noms**, jamais par un compteur : un compteur juste peut
|
|
recouvrir deux exemplaires d'une pièce et l'absence d'une autre. C'est ce que
|
|
fait le script en fin de course, et c'est ce qui a mis au jour l'échec silencieux
|
|
de l'API.
|