Skip to content
Updated Sep 26, 2026 by Barča Dvořáková · Owner: analysisactiveentityconfluence-migration Edit on GitHub

Entity: invoice ​

Entity Type: Database table

Description: An energy/expense invoice entered for a gauge, covering a billing period (dateFrom–dateTo) with the invoiced consumption's cost. Used to reconcile invoiced amounts and consumption against reading-derived values and contract-negotiated prices (see the Invoice Ingestion & Validation feature). Distinct from contract (which holds negotiated unit prices for a validity period) — invoice holds the actual billed period and amount as entered by a user or created via bulk import.

Data Attributes Table ​

Attribute NameDescriptionData TypeDefault ValueRequired (= Nullable)UniqueFormatValidationsIndexExample
idPrimary key of the entity.UUIDGenerated in code (app layer)YesYesUUID v7-Primary Key018ed0b3-c298-7c7a-96d5-8b36f5a7f8d2
tenantIdTenant this record belongs to.UUID-YesNoUUID v7Foreign Key → tenant; must exist-018fa51f-fda1-79f4-8461-2cb8f1cabc10
gaugeIdThe gauge (metering point) this invoice was issued for.UUID-YesNoUUID v7Foreign Key → gauge; must existname: idx_invoice_gaugeId_dateTo, type: btree018fa51f-fda3-7c63-b5a9-3fa33dc989de
invoiceNumberSupplier-assigned invoice number, as printed on the invoice document.String-YesNo---2026100234
dateFromStart of the invoiced billing period.Timestamp with time zone-YesNoISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZMust be ≤ dateTo. In practice only the date component is meaningful (billing periods are day-precision).-2026-01-01T00:00:00Z
dateToEnd of the invoiced billing period.Timestamp with time zone-YesNoISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZMust be ≥ dateFrom.name: idx_invoice_gaugeId_dateTo, type: btree2026-01-31T00:00:00Z
priceInvoiced amount, gross (incl. VAT).Numeric-YesNo---4520.50
priceWithoutVatInvoiced amount, net (excl. VAT).Numeric-YesNo---3735.95
vatRateIdThe VAT rate used to derive whichever of the two amounts the user did not enter, recorded so the derivation stays reproducible after the catalogue changes. Null where both amounts were entered by hand, or where no rate was in force for the invoice's medium and period start.UUIDnullNoNoUUID v7Foreign Key → vatRate; must exist if set-018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b21
creditNoteWhether this record is a credit note (dobropis) rather than a standard invoice.BooleanfalseYesNo---false
fileIdThe uploaded invoice document (PDF), if any.UUID-NoNoUUID v7Foreign Key → document; must exist if set-018fa51f-fda4-7e95-88e8-5f5675f0ddf8
noteFree-text note attached to the invoice.Text-NoNo---Estimated reading, supplier confirmed by phone
importIdThe bulk-import batch this record was created from, if any.UUID-NoNoUUID v7Foreign Key → dataImport; must exist if set-018fa51f-fda5-8a12-9c3d-1e2f3a4b5c6d
createdAtTimestamp of entity creation. Is immutable.Timestamp with time zonenow()YesNoISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZ--2026-01-15T09:00:00Z
updatedAtTimestamp of last entity update. Mutable.Timestamp with time zonenow()YesNoISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZ--2026-01-15T09:00:00Z
deletedAtTimestamp of entity soft delete. Once set, immutable.Timestamp with time zone-NoNoISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZImmutable once set. Active records: WHERE deletedAt IS NULL-null
createdByIdentifier of the author who created this record.String-YesNotype:actor--user:c2d3f586-1c9a-4f5f-b9ae-45f2c4f69f7e
updatedByIdentifier of the author who last updated this record.String-YesNotype:actor--system:invoice-import-v1
legacyIdLegacy-system identifier; migration provenance.StringnullNoNo--UNIQUE (tenantId, legacySource, legacyId) WHERE legacyId IS NOT NULL3312
legacySourceWhich legacy table this invoice was migrated from.String (enum)nullNoNoenum: invoiceMust equal the listed value, or null(composite with legacyId above)invoice

Note. Invoiced consumption and cost are modeled separately through invoiceValue.

Audited fields ​

Recorded on created (in full), updated (changed only) and deleted (in full): gaugeId, invoiceNumber, dateFrom, dateTo, price, priceWithoutVat, creditNote, fileId, note, importId, legacyId, legacySource.

Excluded: none.

price/priceWithoutVat are stored, not derived — nothing recomputes them from invoiceValue rows on write or read; the two are deliberately allowed to drift (EM2 stored both; confirmed not redundant).

legacyId/legacySource are real, migrated columns (202608182000-invoice-migration-fields.sql) not modeled in invoiceValidationMap — like the same pair on building/gauge/dataImport elsewhere in this batch, they're written only by CLI migration tooling, outside the domain layer, but are still real persisted columns and are documented and audited on that basis.

Not registered for entityName resolution — renders id-only in the audit trail (architecture 61-audit-log.md §7.4 in the code repo: a valid, permanent state, not a gap). If registered, invoiceNumber is the natural candidate.