core PK: id 15 required 2 unique

Description

Stores metadata and cloud storage references for receipt images attached to individual expense items. Supports offline capture, compressed upload, and integrity verification for reimbursement workflows requiring receipt documentation (required for items above the 100 kr threshold per HLF policy).

21
Attributes
5
Indexes
6
Validation Rules
18
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Primary key, generated client-side to support offline-first creation via mutation outbox
PKrequiredunique
expense_item_id uuid Foreign key to expense_items; each receipt belongs to exactly one expense line item
required
uploaded_by_user_id uuid FK to users; the peer mentor or coordinator who captured and uploaded the receipt
required
storage_provider enum Cloud object storage provider where the file is persisted
required
storage_bucket string Bucket or container name in the object storage provider
required
storage_key string Full object key / path within the bucket for the compressed receipt file
requiredunique
original_storage_key string Object key for the original uncompressed file; retained for potential re-compression or audit. Null if original was not preserved.
-
original_file_name string File name as provided by the device camera or file picker, stored for display and audit
required
mime_type enum MIME type of the stored (compressed) file
required
file_size_bytes integer Size of the compressed file in bytes after upload; used for storage monitoring and display
required
original_file_size_bytes integer Size of the original file before compression; null if original was not measured or retained
-
checksum_sha256 string SHA-256 hash of the compressed file content; used for integrity verification on download and duplicate detection
required
upload_status enum Lifecycle state of the receipt upload; pending while in offline queue, completed once confirmed in cloud storage
required
upload_attempts integer Number of upload attempts made; used by retry-backoff-service to decide when to surface a user error
required
last_upload_error text Human-readable error message from the most recent failed upload attempt; cleared on success
-
compression_ratio decimal Ratio of compressed to original file size (e.g. 0.42 = 42% of original); null if no compression applied
-
capture_method enum How the receipt image was captured; informs UX and audit trail
required
uploaded_at datetime UTC timestamp when the upload to cloud storage was confirmed; null until upload_status = completed
-
created_at datetime UTC timestamp when the receipt record was first created (may precede upload while in offline queue)
required
updated_at datetime UTC timestamp of the last update to this record
required
deleted_at datetime Soft-delete timestamp; non-null means the receipt was retracted. Physical file is scheduled for deletion by a cleanup job.
-

Database Indexes

idx_expense_receipts_expense_item_id
btree

Columns: expense_item_id

idx_expense_receipts_uploaded_by_user_id
btree

Columns: uploaded_by_user_id

idx_expense_receipts_upload_status
btree

Columns: upload_status

idx_expense_receipts_storage_key
btree unique

Columns: storage_key

idx_expense_receipts_deleted_at
btree

Columns: deleted_at

Validation Rules

allowed_mime_types error

Validation failed

max_file_size_10mb error

Validation failed

checksum_integrity_on_download error

Validation failed

expense_item_id_must_exist error

Validation failed

storage_key_uniqueness error

Validation failed

upload_status_transition error

Validation failed

Business Rules

receipt_required_above_threshold
on_create

Expense items with an amount exceeding 100 kr must have at least one receipt in upload_status = completed before the parent expense claim can be submitted for approval

no_receipt_deletion_after_submission
on_delete

A receipt may not be soft-deleted once the parent expense claim has left draft status (pending/submitted/approved). Deletion is only permitted while the claim is in draft.

uploader_must_own_claim
on_create

The uploaded_by_user_id must match the user who owns the parent expense claim, unless the acting user is a coordinator submitting via proxy registration

offline_queue_retry_limit
on_update

If upload_attempts reaches 5 and upload_status is still not completed, the system surfaces a hard error to the user and stops retrying automatically. Manual retry is still allowed.

physical_file_cleanup_on_soft_delete
on_delete

When deleted_at is set, the associated cloud storage object must be scheduled for deletion. The storage_key remains in the row for audit until a cleanup job removes both.

single_receipt_per_item_recommended
on_create

Multiple receipts per expense item are technically allowed (e.g. front and back of paper receipt), but the expense approval UI highlights items with more than 3 receipts for coordinator review.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
archive_after_1year