core PK: id 13 required 1 unique

Description

Stores photographed receipts attached to travel and out-of-pocket expense claims submitted by peer mentors and coordinators. Receipts are required for expenses above the configured threshold (typically 100 NOK) and serve as audit evidence for reimbursement approval and Bufdir compliance.

20
Attributes
6
Indexes
8
Validation Rules
17
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Primary key identifier for the receipt record.
PKrequiredunique
expense_id uuid Foreign key reference to the parent expense this receipt documents.
required
uploaded_by_user_id uuid User who captured/uploaded the receipt (typically the peer mentor or coordinator).
required
organization_id uuid Tenant scope for the receipt (denormalized from expense for tenant-isolation queries).
required
storage_url string Pointer to the receipt image binary in encrypted object storage (presigned-URL or storage key).
required
file_name string Original filename of the uploaded image.
-
mime_type enum Image MIME type of the stored receipt.
required
file_size_bytes integer Size of the receipt file in bytes.
required
checksum_sha256 string SHA-256 hash of the receipt file for integrity verification and duplicate detection.
required
amount_nok decimal Receipt amount in Norwegian kroner. Used to validate that the expense amount matches the receipt.
-
receipt_date datetime Date printed on the receipt (when the purchase/transaction occurred).
-
vendor_name string Vendor or merchant name parsed from the receipt or entered manually.
-
ocr_text text Raw OCR-extracted text from the receipt image, used for searchability and auto-filling expense fields.
-
status enum Lifecycle state of the receipt within the expense approval workflow.
required
rejection_reason text Free-text reason if the receipt was rejected by an approver (illegible, wrong amount, etc.).
-
captured_offline boolean Whether the receipt was captured while the device was offline and synced later.
required
encryption_key_id string Identifier of the per-tenant encryption key used to encrypt the receipt blob at rest.
required
created_at datetime Timestamp when the receipt record was created.
required
updated_at datetime Timestamp of the most recent modification.
required
deleted_at datetime Soft-delete timestamp; null for active records.
-

Database Indexes

idx_receipts_expense_id
btree

Columns: expense_id

idx_receipts_uploaded_by_user_id
btree

Columns: uploaded_by_user_id

idx_receipts_organization_id
btree

Columns: organization_id

idx_receipts_status
btree

Columns: status

idx_receipts_checksum
btree

Columns: checksum_sha256, organization_id

idx_receipts_created_at
btree

Columns: created_at

Validation Rules

valid_mime_type error

Validation failed

max_file_size error

Validation failed

checksum_present error

Validation failed

expense_exists error

Validation failed

amount_non_negative error

Validation failed

receipt_date_not_future warning

Validation failed

storage_url_required error

Validation failed

duplicate_detection warning

Validation failed

Business Rules

receipt_required_above_threshold
on_update

An expense above the configured threshold (default 100 NOK per HLF) must have at least one verified receipt before it can be submitted for approval.

tenant_isolation
always

A receipt's organization_id must match the parent expense's organization_id; cross-tenant receipts are forbidden.

uploader_authorization
on_create

Only the expense owner or an authorized coordinator (proxy) may upload or replace receipts on an expense.

immutable_after_approval
on_update

Once the parent expense has been approved or reimbursed, the receipt cannot be replaced or deleted (audit trail preservation).

rejection_requires_reason
on_update

Setting status to 'rejected' requires a non-empty rejection_reason.

encryption_at_rest
on_create

All receipt blobs must be encrypted at rest using a tenant-scoped key; the encryption_key_id must be set on create.

offline_sync_on_reconnect
on_create

Receipts captured offline must be queued and uploaded via the sync engine when connectivity returns.

audit_logging
always

Every receipt create, update (status change), and delete must produce an audit log entry.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
archive_after_1year