Appearance
Entity: vatRate
Entity Type: Database table (shared schema)
Description: The catalogue of statutory VAT rates, one row per medium and validity start. It is the single place a rate is defined, so a change in the law is one row per affected medium rather than a code change. A rate is valid from its validFrom until the validFrom of the next row for the same medium; the rate in force on an invoice's period start is the one used to complete that invoice's second amount, and the rate actually used is recorded on the invoice so the figure stays reproducible after the law changes.
Cross-tenant reference data. The row belongs to the platform, not to a tenant, so it lives in the shared schema alongside the other cross-tenant catalogues rather than being copied into every tenant schema. tenantId is present but null on every row this feature creates: it is the column the shared-schema policies key on, and a null value is what makes a row readable by every tenant. The same shape is used by the weather-station and climate-normal catalogues.
One row per medium, not one row per date. A change in the law that touches three media is three rows sharing a validFrom, and a medium whose rate did not change keeps its existing row. The alternative — a single row carrying a column per medium — makes adding a medium a schema change, and makes a medium that nobody remembered to fill in indistinguishable from one deliberately left alone.
Data Attributes Table
| Attribute Name | Description | Data Type | Default Value | Required (= Nullable) | Unique | Format | Validations | Index | Example |
|---|---|---|---|---|---|---|---|---|---|
| id | Primary key of the entity. | UUID | Generated in code (app layer) | Yes | Yes | UUID v7 | - | Primary Key | 018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b21 |
| tenantId | Always null for a platform-maintained rate, which is what makes the row visible to every tenant. Present because the shared-schema row-level-security policies read it. | UUID | null | No | No | UUID v7 | Must be null on a row created through the platform administration surface. | name: idx_vat_rate_tenant_id, type: btree | null |
| medium | Medium the rate applies to. Rates differ by medium — a reduced rate for heat alongside the standard rate for electricity, gas and fuels — and every billable medium carries its own row, including those currently on the standard rate. | Enum | - | Yes | Part of composite unique | Enum - GaugeMedium | An entry from the enum. Only billable media are rated; kvp and otherSensor are never invoiced and carry no rows. | name: uq_vat_rate_medium_valid_from, type: btree (unique, partial) | heat |
| ratePercent | The rate as a percentage. Stored as a percentage rather than a multiplier so that the catalogue reads the way the legislation and the invoice do. | Decimal | - | Yes | No | numeric(5,2) | ≥ 0 and < 100. | - | 12.00 |
| validFrom | First day the rate applies. A rate has no explicit end: it runs until the next row for the same medium begins. | Date | - | Yes | Part of composite unique | YYYY-MM-DD | Unique per (medium, validFrom) among rows where deletedAt is null. | Part of the unique index above | 2024-01-01 |
| legalReference | Short free-text reference to the amendment or notice the rate comes from, so an auditor can trace the number to its source. | String | null | No | No | - | Max 200 characters. | - | Zákon č. 349/2023 Sb., účinnost od 1. 1. 2024 |
| createdAt | Timestamp of record creation. | Timestamp with time zone | now() — set in code | Yes | No | ISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZ | Cannot be null. | - | 2023-12-18T10:00:00Z |
| updatedAt | Timestamp of the last update. | Timestamp with time zone | now() — set in code | Yes | No | ISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZ | Cannot be null. | - | 2023-12-18T10:00:00Z |
| deletedAt | Soft-delete marker for a withdrawn row. Null means active. Any rate can be withdrawn when needed; withdrawing a historical rate — one superseded by a later rate for its medium — needs the history permission. A withdrawn row is never physically removed, and invoices already completed keep the rate recorded on them. | Timestamp with time zone | null | No | No | ISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZ | Immutable once set. Active rows: WHERE deletedAt IS NULL. | - | null |
| createdBy | Actor who created the record. | String | - | Yes | No | type:actor | Non-empty. | - | user:018ed0b3-… |
| updatedBy | Actor who last updated the record. | String | - | Yes | No | type:actor | Non-empty. | - | user:018ed0b3-… |
Indexes
| Name | Columns | Type | Why |
|---|---|---|---|
uq_vat_rate_medium_valid_from | medium, validFrom where deletedAt is null | Unique, partial | One rate per medium per start date; a soft-deleted row must not block re-entering the same date. |
idx_vat_rate_tenant_id | tenantId | btree | Supports the shared-schema policies. |
Row-level security
Enabled and forced, with the split read and write policies the shared schema uses: a row is readable when its tenantId is null or matches the current tenant, and writable only in a context with no tenant set — which is the platform administration context. A tenant can therefore read every rate and change none of them.
Audited fields
Recorded on created (in full), updated (changed only) and deleted (in full): medium, ratePercent, validFrom, legalReference.
Excluded: tenantId — always null on a platform-maintained row, so it carries no information about the change.
Not registered for entityName resolution, and has no independent human-readable attribute to register — identified by its medium/validFrom scope.
Reference data
The catalogue is seeded with the statutory history of every billable medium since 1 January 2013 (the table is on the feature page), and gains a row per medium whenever the legislation changes. It is never empty: an invoice whose period starts before the earliest row for its medium cannot have its second amount completed, and the user is asked for both amounts instead.