Files
factory/vibe/guidebooks/erp/operations.md
Gabriel Radureau 7bf83e75ed 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>
2026-06-23 22:12:11 +02:00

174 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[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.