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:
173
vibe/guidebooks/erp/operations.md
Normal file
173
vibe/guidebooks/erp/operations.md
Normal 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.
|
||||
Reference in New Issue
Block a user