> ## Documentation Index
> Fetch the complete documentation index at: https://cona.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Data Model

> 98 Prisma models as 8 domain clusters, how multi-tenancy is enforced, and the four tables that escape it

# Data Model

`packages/database/prisma/schema.prisma` — **98 models, 21 enums, 3,179 lines**, with 236
migrations behind them.

One diagram of 98 models would be unreadable, so the schema is drawn as 8 clusters. All 98
models partition across them exactly once, with no overlap and no gaps.

## Cluster overview

Edge labels are relation-field counts. `actors` and `organization` edges are **excluded** —
see the note below.

```mermaid theme={null}
flowchart TB
    Tenancy["Tenancy & identity<br/>7 models"]
    Documents["Documents<br/>20 models"]
    Accounting["Accounting & GL<br/>16 models"]
    Master["Master data<br/>16 models"]
    TaxFx["Tax & FX<br/>12 models"]
    Platform["Platform, App Store<br/>& Copilot — 11 models"]
    Recon["Reconciliation<br/>& payments — 10 models"]
    Integrations["Integrations<br/>& import — 6 models"]

    Documents ---|36| Master
    Accounting ---|22| Documents
    Documents ---|22| Recon
    Integrations ---|8| Master
    Accounting ---|8| Master
    Accounting ---|8| Recon
    Master ---|4| TaxFx
    Documents ---|4| Platform
    Master ---|2| Recon
    Documents ---|2| Integrations
    Documents ---|2| TaxFx
    Accounting ---|2| TaxFx
    Integrations ---|2| Recon

    Tenancy -.->|"spine — connects to all 8"| Documents

    classDef data fill:#fde8e8,stroke:#c53b3b,color:#111827
    class Tenancy,Documents,Accounting,Master,TaxFx,Platform,Recon,Integrations data
```

Strip out the tenancy spine and the real business coupling is: **Documents ↔ Master data
(36)**, **Accounting ↔ Documents (22)**, **Documents ↔ Reconciliation (22)**.

## The two hubs

| Model          | Fields | Relations | Clusters touched |
| -------------- | -----: | --------: | ---------------: |
| `actors`       |    103 |        94 |           8 of 8 |
| `organization` |    102 |        83 |           8 of 8 |
| `documents`    |    103 |        42 |           6 of 8 |

<Note>
  `actors` relates to **94** of the 98 models. That is the schema-level proof that
  virtually every table records who changed it. `organization` at 83 is the tenancy spine.

  Both are omitted from every cluster diagram by convention — drawing their edges would
  obscure everything else. They appear only here and on
  [Auth, Tenancy & Actors](/architecture/auth-tenancy-actors).
</Note>

## Multi-tenancy

| Tenancy column    | Models |
| ----------------- | -----: |
| `org_id`          |     81 |
| `organization_id` |      1 |
| none              |     16 |

<Warning>
  **`csv_import_batches` uses `organization_id`** (`schema.prisma:753`) while all 81 other
  tenant-scoped models use `org_id`. Any lint rule, generic helper, or audit query keyed on `org_id`
  skips this table silently.
</Warning>

### The 16 models without a tenancy column

```mermaid theme={null}
flowchart TB
    subgraph Global["Global reference data — correctly tenant-free (10)"]
        G1["eu_vat_rates · eu_vat_exception_rates<br/>eu_vat_exception_territories · eu_vat_sync_log"]
        G2["exchange_rates · product_tax_categories<br/>product_tax_category_country_rates<br/>product_tax_rate_types"]
        G3["app_store_listings<br/>app_store_listing_translations"]
    end

    subgraph Roots["Tenant root / cross-tenant (2)"]
        R1["organization — is the tenant"]
        R2["users — belongs to many orgs"]
    end

    subgraph Transitive["Transitively scoped (4) — need a JOIN"]
        T1["csv_import_records<br/>→ csv_import_batches.organization_id"]
        T2["document_relations<br/>→ documents.org_id"]
        T3["inventory_item_history<br/>→ inventory_items.org_id"]
        T4["revenue_recognition_entries<br/>→ deferred_revenue_schedules.org_id"]
    end

    classDef boundary fill:#f8fafc,stroke:#94a3b8,color:#334155

    classDef data fill:#fde8e8,stroke:#c53b3b,color:#111827
    classDef external fill:#fff4dd,stroke:#c98a12,color:#111827
    class G1,G2,G3,R1,R2 data
    class T1,T2,T3,T4 external
    class Global,Roots,Transitive boundary
```

<Warning>
  The four transitively scoped tables **cannot be filtered by a direct `WHERE org_id = …`**. Every
  query against them must join to the parent to stay tenant-safe. This is the detail an auditor will
  ask about.
</Warning>

## Clusters in detail

### Documents — 20 models, 60 internal relations

`documents`, `line_items`, `document_relations`, `document_formats`,
`document_format_defaults`, `document_format_outputs`, `document_external_ids`,
`document_source_metadata`, `document_payment_allocations`, `document_delivery_profiles`,
`document_delivery_profile_channels`, `document_approvals`, `document_approval_steps`,
`approval_workflows`, `approval_steps`, `attachments`, `number_ranges`,
`number_range_defaults`, `statuses`, `object_types`

### Accounting & GL — 16 models, 38 internal relations

`general_ledger`, `general_ledger_reconciliation_gids`, `chart_of_accounts`,
`accounting_periods`, `accounting_work_queue`, `gl_dimensions`, `gl_memo_settings`,
`posting_matrix`, `posting_matrix_column`, `posting_matrix_column_type`,
`posting_matrix_criteria`, `posting_matrix_rule`, `deferred_revenue_schedules`,
`revenue_recognition_entries`, `datev_exports`, `subsidiaries`

### Master data — 16 models, 44 internal relations

`entities`, `addresses`, `contacts`, `email_addresses`, `phone_numbers`, `items`,
`product_variants`, `product_components`, `bundle_components`,
`product_category_assignments`, `inventory_items`, `inventory_item_history`, `locations`,
`tags`, `sales_channels`, `localized_field_values`

### Tax & FX — 12 models

`tax_codes`, `vat_settings`, `org_tax_categories`, `org_tax_category_country_rates`,
`product_tax_categories`, `product_tax_category_country_rates`, `product_tax_rate_types`,
`eu_vat_rates`, `eu_vat_exception_rates`, `eu_vat_exception_territories`, `eu_vat_sync_log`,
`exchange_rates`

Eight of these twelve are global reference data with no tenant column — edited from the
[Console](/architecture/console-architecture), affecting every organisation.

### Platform, App Store & Copilot — 11 models

`app_store_listings`, `app_store_listing_translations`, `app_reviews`, `tools`,
`copilot_actions`, `copilot_memory`, `copilot_runs`, `notification_settings`,
`custom_property_definitions`, `custom_property_fields`,
`custom_property_field_object_types`

### Reconciliation & payments — 10 models

`reconciliation_groups`, `reconciliation_account_settings`, `bank_matching_handoffs`,
`tier2_bank_matching_handoffs`, `customer_ibans`, `customer_iban_evidence`,
`payment_methods`, `payment_terms`, `cona_cancellation_reviews`, `delivery_terms`

### Tenancy & identity — 7 models

`organization`, `users`, `users_organizations`, `actors`, `api_keys`, `activity_logs`,
`background_activities`

### Integrations & import — 6 models

`integrations`, `integration_entity_mappings`, `integration_item_mappings`,
`import_mapping_rules`, `csv_import_batches`, `csv_import_records`

## Widest models

| Model                 | Fields |
| --------------------- | -----: |
| `actors`, `documents` |    103 |
| `organization`        |    102 |
| `items`               |     57 |
| `entities`            |     55 |
| `general_ledger`      |     53 |
| `line_items`          |     44 |
| `addresses`           |     42 |

`organization` at 102 fields carries organisation settings, feature flags, and
configuration inline. There is no separate settings model — any diagram of
"organisation settings" points at this table.

## Notes

**Migrations are expand-and-contract.** 236 migrations under
`packages/database/prisma/migrations/`, governed by the `database-migrations` and
`write-migration` skills.

**Seven subpath exports.** `@cona/database` exposes `.`, `/direct`, `/tracked`,
`/enums`, `/seed-data`, `/seed-eu-vat`, `/pool-metrics`. The `.` root re-exports the live
Prisma client, which is how 40 webapp files reach it directly — see
[Package Graph](/architecture/package-graph).

**`users` is cross-tenant on purpose.** Membership lives in `users_organizations`, so one
person can belong to several organisations.
