docs(vibe): add erp/ guidebook (Dolibarr deployment + backup/recovery + ops)

Dedicated tree-docs guidebook under vibe/guidebooks/erp/ for the lab's most
data-critical app, cross-linked from the applications hub (bidirectional):

- README.md             : Dolibarr 22.0.4 on Postgres; data-criticality; overview
  diagram; the Vault-unseal-before-scale recovery ordering (CAUTION).
- deployment.md         : upstream image + custom entrypoint (MySQL->psql), the
  50Gi Longhorn RWX documents PVC, Vault CRDs + the shared app_roles iac, init
  scripts (conf.php creds, table-ownership), ingress, CI.
- backup-and-recovery.md: the Ansible CronJob pg_dump (daily 04:00, 15-day
  retention) + restore Job (scale-0 -> restore -> scale-1); the cluster recovery
  ordering (Longhorn -> Vault unseal -> erp scale-up).
- operations.md         : the read-only bin/arcodange CLI, static/company.json,
  Deno+Playwright tests, day-2 ops.

erp code via full gitea URLs; CLUSTER_RECOVERY.md by name; 2 mermaid diagrams
MCP-validated; zero dead links.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 22:12:11 +02:00
parent 4823394e0e
commit 7bf83e75ed
6 changed files with 659 additions and 2 deletions

View File

@@ -0,0 +1,173 @@
[vibe](../../README.md) > [Guidebooks](../README.md) > [ERP](README.md) > **Operations**
# ERP Operations
> **Status:** ✅ Active
> **Last Updated:** 2026-06-23
> **Upstream:** [ERP hub](README.md) · [Deployment](deployment.md)
> **Downstream:** [Backup & recovery](backup-and-recovery.md)
> **Related:** [Applications hub](../applications/README.md) · [Web app](../applications/webapp.md) · [Secrets & VSO](../tools/secrets-and-vso.md) · [Postgres IaC](../factory-provisioning/opentofu/postgres-iac.md)
This page covers day-2 operation of the Arcodange Dolibarr ERP: the read-only operations CLI, the static identity assets, the Playwright bootstrap test suite, and the recurring scaling / module-activation / storage chores. For how the workload is deployed onto the cluster, see [Deployment](deployment.md). For backups and disaster recovery, see [Backup & recovery](backup-and-recovery.md).
---
## 1. The read-only ops CLI — `bin/arcodange`
[`bin/arcodange`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/bin/arcodange) is a Bash dispatcher that gives a human-friendly entry point to safe, **strictly read-only** Dolibarr operations against `erp.arcodange.lab`. Every subcommand `exec`s a script under `.claude/skills/<skill>/scripts/`; the dispatcher itself only locates the project root (via `git rev-parse --show-toplevel`, falling back to walking up from the script) and routes arguments.
> [!IMPORTANT]
> The CLI authenticates with credentials read from [`.claude/skills/dolibarr/.env`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/bin/arcodange) — a **gitignored** file expected at **mode `600`**. The underlying API key belongs to the `ai_agent` service account, which has **no write permissions**: the CLI cannot mutate Dolibarr. Corrections always go through the Dolibarr web UI.
### Command map
| Command | Subcommand | What it does |
| --- | --- | --- |
| `ping` | — | `GET /status` — liveness probe + reports the running Dolibarr version |
| `whoami` | — | `GET /users/info` — confirms auth is the `ai_agent` service account |
| `invoice` | `list [--since YYYY-MM-DD]` | Table of KissMetrics customer invoices with payment state |
| `invoice` | `audit <invoice-id>` | JSON facts + PDF mandatory-mention audit for one invoice |
| `payments` | `state [--since YYYY-MM-DD]` | Per-invoice TTC vs payments reconciliation |
| `payments` | `timeline [--year\|--since\|--until]` | Payment timeline with cumulative balance |
| `payments` | `by-month [--year\|--all-clients]` | Monthly cash-receipt aggregation |
| `tva` | `summary [--year\|--since\|--until]` | CA3-ready monthly TVA summary (collectée déductible) |
| `tva` | `collect` / `collect-detail` | TVA collectée by month × rate (CA3 A1/A4/E2) + per-line audit |
| `tva` | `deductible` / `deductible-detail` | TVA déductible by month × rate (CA3 19/20/17+24) + per-line audit |
| `thirdparty` | `audit <socid>` | Country-aware completeness audit for one thirdparty |
| `thirdparty` | `audit-all [--clients-only\|--suppliers-only]` | Audit every visible thirdparty |
| `templates` | `list [--max-id N]` / `inspect <id>` | Enumerate / health-check recurring invoice templates |
| `bank` | `probe` / `balance` / `match` / `qonto-transactions` / `wise-transactions` / `curl` | Qonto + Wise bank data and Dolibarr reconciliation |
| `email` | `list` / `inspect <id>` / `curl` | Supplier-invoice ingestion from the Zoho mailbox |
| `snapshot` | `--out FILE` (or `--print-only`) | Bundle the full read-only state into one JSON dump (with `content_hash`) |
| `curl` | `<path>` | Raw read-only call through `dol-curl.sh` (e.g. `arcodange curl /invoices/12`) |
| `help` | `[command]` | Full command tree, or per-command help |
### Health checks first
| Check | Command | Expected outcome |
| --- | --- | --- |
| Is Dolibarr up? | `bin/arcodange ping` | HTTP `200` + Dolibarr version string |
| Is auth wired? | `bin/arcodange whoami` | The `ai_agent` user record |
| Full state dump | `bin/arcodange snapshot --out /tmp/erp.json` | One JSON file with a `content_hash` |
> [!TIP]
> `snapshot` is the fastest way to capture a point-in-time, read-only view of the ERP (invoices, payments, TVA, thirdparties, templates) for offline diffing or for attaching to an incident. It does not touch the database — it only reads.
The CLI's per-domain credentials beyond Dolibarr (Qonto/Wise for `bank`, Zoho OAuth for `email`) also live in the same gitignored `.env`. The skills' `SKILL.md` files remain the source of business-logic documentation; the CLI is just the ergonomic front door.
---
## 2. Static identity assets — `static/`
[`static/`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/static) holds the company's legal identity and branding, consumed by the Playwright bootstrap suite when it configures a fresh Dolibarr install.
| Path | Purpose |
| --- | --- |
| [`static/config/company.json`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/static/config/company.json) | Legal identity used for Dolibarr company setup and display |
| [`static/img/logo512.png`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/static/img/logo512.png) | Company logo (referenced from `company.json` as `$IMG/logo512.png`) |
| [`static/img/loginBackground.jpeg`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/static/img/loginBackground.jpeg) | Login-page background image |
`company.json` carries two blocks — `info` (postal/contact identity) and `ID` (legal identity):
| Field | Value |
| --- | --- |
| Raison sociale | Arcodange |
| Forme juridique | SAS (Société par actions simplifiée) |
| Adresse | 73 Boulevard de l'Yerres, 91000 Évry-Courcouronnes, France (FR) |
| Site / email | arcodange.fr · gabrielradureau@arcodange.fr |
| SIREN / SIRET | (legal registration identifiers) |
| NAF / APE | 62.02A |
| N° TVA | (intra-community VAT number) |
| Capital | (share capital) |
| RCS | R.C.S. Évry |
| Mois début d'exercice | Juillet |
| Logo | `$IMG/logo512.png` |
> [!NOTE]
> The `$IMG` token in `company.json` resolves to `static/img/` via the test harness's `IMG_FOLDER` (see §3). The same image folder feeds the optional login-page background and logo upload during display setup.
---
## 3. Bootstrap test suite — `test/`
[`test/`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/test) is a **Deno + Playwright** UI suite that drives a real browser through the Dolibarr first-install and admin configuration flows. It is not a unit-test runner — it is the scripted bootstrap that stands up a fresh Dolibarr instance and applies the company identity.
| File | Role |
| --- | --- |
| [`test/main.ts`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/test/main.ts) | Entry point: launches Chromium (`fr-FR` locale), wires `globalCtx`, runs install + admin setup steps |
| [`test/deno.json`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/test/deno.json) | Imports `npm:playwright` and `jsr:@std/dotenv/load`; `checkJs: true` |
| [`test/.env.example`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/test/.env.example) | Template for `DOLIBARR_ADDRESS`, DB password, admin login, `ROOT_FOLDER` |
| [`test/scripts/admin/`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/test/scripts/admin) | `initialSetup.ts`, `companySetup.ts`, `displaySetup.ts`, `moduleSetup.ts` |
### Run the suite
1. Install Deno and the Playwright browsers:
- `curl -fsSL https://deno.land/install.sh | sh`
- `deno run --allow-all npm:playwright install`
2. Populate `test/.env` from the live cluster secrets (DB password from the `vso-db-credentials` secret; admin password from `secretkv`). See [Secrets & VSO](../tools/secrets-and-vso.md) for how those secrets land in the namespace.
3. Run: `deno run --allow-all main.ts`.
### Lock the installer after install
> [!CAUTION]
> Dolibarr's `install/` wizard stays reachable until an `install.lock` exists. After a successful first install, **always** create the lock — an unlocked installer is a live takeover risk on a production-like instance.
The post-install step touches the lock file inside the pod and chowns it to `www-data`:
```sh
kubectl -n erp exec $(kubectl get pod -n erp -l app.kubernetes.io/name=erp -o name) -- \
/bin/bash -c '/usr/bin/touch /var/www/html/install.lock && /bin/chown www-data:www-data /var/www/html/install.lock'
```
`initialSetup.isUpgradeLocked()` checks for the same locked state before deciding whether to (re)run the installer, so the lock is both a safety gate and the suite's idempotency signal.
---
## 4. Day-2 operations
### Scaling — manual only
| Setting | Value | Source |
| --- | --- | --- |
| `replicaCount` | `1` | [`chart/values.yaml`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/chart/values.yaml) |
| `autoscaling.enabled` | `false` | [`chart/values.yaml`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/chart/values.yaml) |
Dolibarr runs as a **single replica with no HorizontalPodAutoscaler**. The instance is backed by a `ReadWriteOnce` filesystem PVC, so scaling out is not a supported topology — scaling is a deliberate, manual `replicaCount` change in the chart values, applied through the normal [deployment](deployment.md) path. Treat the workload as a single-writer system.
### After activating a new Dolibarr module — fix table ownership
Activating a Dolibarr module creates new SQL tables, and Dolibarr's migration runner creates them under whatever role the live VSO-rotated credentials happen to map to. If that role is not `erp_role`, a subsequent credential rotation by Vault can leave the new tables unreadable.
> [!IMPORTANT]
> After enabling any new module (e.g. via `moduleSetup.configureModule`), run the table-ownership reassignment so the new objects are owned by `erp_role`. The `REASSIGN OWNED BY … TO erp_role` logic lives in [`chart/scripts/update_ownership.sql`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/chart/scripts/update_ownership.sql) and is also mounted into the pod entrypoint as `update_table_ownership.sql`.
Apply it inside the pod:
```sh
kubectl exec -n erp $(kubectl get pod -n erp -l app.kubernetes.io/name=erp -o name) -c erp -- \
sh -c 'PGPASSWORD=${DOLI_DB_PASSWORD} psql -U ${DOLI_DB_USER} -h ${DOLI_DB_HOST} \
-p ${DOLI_DB_HOST_PORT} ${DOLI_DB_NAME} -f /var/www/scripts/before-starting.d/update_table_ownership.sql'
```
If the pod logged `Read-only file system` for the `update_table_ownership.sql` step at startup (the entrypoint cannot write its temp file), the reassignment never ran — run the command above by hand. If the live DB user has already lost rights, run the same SQL with the **admin Postgres credentials** instead. The role model is described in [Postgres IaC](../factory-provisioning/opentofu/postgres-iac.md).
### Watch PVC usage so backups do not fill the volume
ERP data and (where co-located) backup artifacts share storage on a single PVC. If on-volume backup snapshots accumulate, they can exhaust the volume and take Dolibarr down.
| Watch | Why |
| --- | --- |
| PVC used vs. capacity | A full volume crashes Dolibarr (no room for sessions/temp/migrations) |
| Backup artifact growth | Old dumps left on the volume eat the same space data needs |
> [!WARNING]
> Monitor PVC usage and prune/offload old backup artifacts before they fill the **50Gi** volume. Backup retention, artifact layout, and the off-volume target are documented in [Backup & recovery](backup-and-recovery.md) — keep on-volume copies short-lived. See [Storage & recovery](../lab-ecosystem/storage-and-recovery.md) for the durable-copy model.
---
## See also
- [ERP hub](README.md) — overview and entry point for the ERP guidebook.
- [Deployment](deployment.md) — how the workload, chart, and credentials reach the cluster.
- [Backup & recovery](backup-and-recovery.md) — backup artifacts, retention, restore drills.
- [Secrets & VSO](../tools/secrets-and-vso.md) — how `vso-db-credentials` / `secretkv` land in the namespace.