Receipts
Data Entity
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.
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
Columns: expense_id
idx_receipts_uploaded_by_user_id
Columns: uploaded_by_user_id
idx_receipts_organization_id
Columns: organization_id
idx_receipts_status
Columns: status
idx_receipts_checksum
Columns: checksum_sha256, organization_id
idx_receipts_created_at
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
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
A receipt's organization_id must match the parent expense's organization_id; cross-tenant receipts are forbidden.
uploader_authorization
Only the expense owner or an authorized coordinator (proxy) may upload or replace receipts on an expense.
immutable_after_approval
Once the parent expense has been approved or reimbursed, the receipt cannot be replaced or deleted (audit trail preservation).
rejection_requires_reason
Setting status to 'rejected' requires a non-empty rejection_reason.
encryption_at_rest
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
Receipts captured offline must be queued and uploaded via the sync engine when connectivity returns.
audit_logging
Every receipt create, update (status change), and delete must produce an audit log entry.