grafana — parked local technical reporting over the ERP¶
Metabase is the published reporting surface at reports.embeddedera.com; see metabase.
Grafana remains preserved on 127.0.0.1:3000 as a local historical/detail surface and canonical SQL
source. It has no Cloudflare Tunnel hostname as of 2026-09-21.
The Stock dashboard includes Assembly quantity by location and part number (added 2026-09-21). It groups serialized and bulk stock records into one row per assembly IPN and location, so a manager sees physical quantities rather than InvenTree's stock-item row count. Initial Paul Shop rows: EE-A-0002-A 21, EE-A-0004-A 32, EE-A-0005-A 41.
grafana/grafana-oss:13.0.2, service grafana at the end of the torrentvpn compose file, bound to
127.0.0.1:3000 only. The EmbeddedEra Reports tunnel route now targets Metabase on port 3002.
Identity: [auth.proxy] on Cf-Access-Authenticated-User-Email, whitelist = 127.0.0.1, 172.18.0.1
(the tunnel's requests arrive from the docker gateway, not loopback — that cost an hour). Login form
disabled; new people auto-sign-up as Viewer. Local admin password in ~/.config/reports/env
(GF_SECURITY_ADMIN_PASSWORD; compose must NOT interpolate it with ${…}, which reads the host env
and silently produced the default admin/admin). Reset with
docker exec grafana grafana cli --homepath /usr/share/grafana --config /etc/grafana/grafana.ini admin reset-admin-password …;
five wrong basic-auth tries lock the account for ~5 min.
Data: Postgres role reports_ro (SELECT only, proven: DELETE denied) on database inventree, and
database reports (owner reports_rw) holding audit_runs / audit_checks, appended by sop-audit
on every run. Datasources and dashboards are provisioned from files (~/torrentvpn/grafana/):
provisioning/, dashboards/<Folder>/*.json (folders from the file structure, not editable in the UI —
a dashboard is a document, edit the JSON and it reloads within 30 s). Datasource gotcha: the Postgres plugin in Grafana 13 reads the database name from jsonData.database, not the top-level database: — with the latter every panel shows "no default database configured" while the API query path still works. Data dir grafana/data/ — add to
the nightly backup if dashboards ever get edited in the UI (they should not). Plan: reporting.
Access (2026-09-12): folders Operations / Business / Quality / People / System / Finance; who sees which comes from reports: in departments.yml via ~/.local/bin/reports-sync (hourly cron + on every onboard change). Folder permissions are fully replaced by the sync, so never rely on the default Viewer role — a person with no team sees an empty Grafana.
Landing pages (2026-09-14): nobody lands on Grafana's stock welcome page any more. Two extra
dashboards: dashboards/start-here.json (uid ee-start, root folder, links only, no queries, so it is
safe as the org default for anyone with no team) and dashboards/System/home.json (uid ee-home,
System folder = Administration only: the headline number from every report — audit grade/fails/warns,
open/overdue/unassigned tasks, open orders, invoiced this month, parts, stock, open POs and builds —
each tile linking to its source). Who lands where is HOME_BY_TEAM in reports-sync (Administration →
ee-home, Engineering → ee-tasks, Production/Purchasing → ee-stock, Sales/Finance → ee-sales), applied
through /api/teams/{id}/preferences and /api/org/preferences; a person in two teams gets whichever
Grafana resolves first, so keep every candidate inside their folder access. Verified from loopback with
the Cf-Access-Authenticated-User-Email header as each real identity, not just as admin.
BOM costs (2026-09-17): Operations dashboard ee-bom-costs shows Robot Cow 004B,
Cow Remote 006 and Cow Finger 003. Source-controlled dashboard JSON, canonical SQL,
quote import utility and sandbox tests are under ~/erp-config/reporting/; installed
JSON remains under ~/torrentvpn/grafana/dashboards/. Home, Stock and Start Here
link to the report. Provisioning reloads files within 30 seconds; no container restart.
Prices are read from ERP supplier-part price breaks (the break FK is supplier-part ID)
and stock records. Supplier estimates and stock valuation are separate. Full totals
are withheld for missing/unqualified prices; stock notes identifying catalogue estimates
exclude those values from known stock totals. Batch selection defaults to 100 and the
report refreshes every five minutes. See SOP-027 and robot-kit-costing.
Inventory planning (updated 2026-09-20): ee-build-capacity in Operations reads native
stock, BOM and allocation tables through erp/reports_ro. It discovers every active assembly
with a BOM, pools declared substitutes per line, ignores optional/consumable lines for material
capacity, and withholds overlapping pools rather than double-counting. Free-stock capacity is
separate from active-build allocation readiness; Robot Cow BO-0001 therefore shows fully
allocated for 27 while additional free-stock capacity is zero. Canonical SQL and rollback
sandbox tests are in ~/erp-config/reporting/. Home includes capacity and separate
recorded/estimated stock-value subtotals, current-year order counts and honest accounting-data
coverage. SOP-028 defines planning exclusions. No new service or paid model runs on report refresh.
A temporary Grafana preview on loopback3301 used only the sandbox DB for visual/query tests;
it is stopped after validation.
The System home panel was corrected the same day to use this current query. Its old embedded query still rejected consumables and showed a dash for Robot Cow. It now separates Additional from free stock from Ready in active builds; the live Robot Cow row reports 0 additional and 27 fully allocated on BO-0001.
At Angel's request, both the System home and detailed capacity summary now also show Total buildable now = fully allocated active-build quantity + additional capacity from unreserved usable stock. The three shop products currently report Robot Cow 27, Cow Remote 0 and Cow Finger 0 from recorded ERP inventory; the latter two need their component stock recorded before a positive capacity can be claimed.
The detailed report now follows that summary with Parts blocking the next additional board. It calculates a one-board target after all active-build, sales and transfer reservations, then lists every short BOM line with part number, required quantity, unreserved usable quantity and missing quantity. Live Robot Cow is blocked from board 28 by EE-K-0001 antistatic bag (needs 1, unreserved 0); Remote has 18 recorded blockers and Finger has EE-C-0193 switches plus EE-P-0005-A PCB.
Production test results (2026-09-20): Quality dashboard ee-test-results reads
tester_events and tester_serials in the reports database. User timer
tester-report-sync.timer runs ~/erp-config/reporting/tools/sync_tester_reports.py every minute;
the source tester SQLite database is opened read-only and reports_ro has SELECT only on the
mirrored tables. The report includes accepted runs whose InvenTree mirror is pending, with the
reason, so a retained final PASS is not mistaken for an ERP unit or a failed test.