Data model
How the pieces fit together.
Organization
└── Project (pins reference data version + eGRID subregion)
└── Scenario (alternative)
├── Functional unit, design flow, service life, CAPEX/OPEX
└── Inventory item × N
├── Template (parametric blueprint)
├── Drivers {key: value, ...}
└── Elementary flows {substance, amount, unit, compartment}Reference data versioning
Every project pins a reference data version — a coherent bundle of eGRID year + TRACI version + IPCC report. When new factor data is published, new projects use it automatically; existing projects keep their pinned version so their results stay reproducible. You can upgrade explicitly and see the delta.
Multi-tenancy
Every row in tenant-scoped tables carries an org_id. Row-level security on Postgres ensures users only ever see their org's data. Reference data tables (TRACI CFs, eGRID, IPCC, substances) are global and read-only to all authenticated users.
Computed flows
When you add a template-backed inventory item, the template's flow expressions are evaluated against your driver values (using a sandboxed expression evaluator — noeval()), producing concrete elementary flows. Those flows are stored on the inventory item as computed_flows JSONB so they're queryable and cacheable.
Caching
Impact results are cached on the scenario keyed by a SHA-256 hash of (scenario id + sorted inventory updated_at values + reference data version id). Any inventory edit flips a results_dirty flag and clears the cached results; the next Calculate run recomputes from scratch.
