core PK: id 8 required 2 unique

Description

Stores the metadata and Supabase Storage reference for a receipt image attached to an expense claim. Receipts are required when a claim amount exceeds the organization's configured threshold (e.g., 100 NOK). Images are compressed before upload and accessed via time-limited signed URLs. Deleted when a claim is cancelled.

8
Attributes
4
Indexes
6
Validation Rules
14
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Primary key, generated server-side on insert
PKrequiredunique
expense_claim_id uuid Foreign key referencing the expense claim this receipt is attached to. One-to-one relationship enforced by unique constraint.
requiredunique
storage_path string Fully qualified Supabase Storage path in the format {org_id}/{user_id}/{claim_id}/{filename}. Used to generate signed download URLs. Never exposed directly to clients.
required
mime_type enum MIME type of the uploaded file, determined at capture time and validated before upload.
required
file_size_bytes integer Size of the stored file in bytes after compression. Used for storage monitoring and to enforce the 10 MB per-file limit.
required
uploaded_at datetime UTC timestamp when the receipt was successfully uploaded to Supabase Storage and the metadata record was persisted.
required
organization_id uuid Owning organization. Drives Supabase RLS policy so that coordinators and admins can only access receipts within their organization scope.
required
uploaded_by_user_id uuid Supabase auth user ID of the peer mentor (or proxy coordinator) who performed the upload. Retained for audit purposes after the claim is processed.
required

Database Indexes

idx_receipt_expense_claim_id
btree unique

Columns: expense_claim_id

idx_receipt_organization_id
btree

Columns: organization_id

idx_receipt_uploaded_at
btree

Columns: uploaded_at

idx_receipt_uploaded_by_user_id
btree

Columns: uploaded_by_user_id

Validation Rules

allowed_mime_types error

Validation failed

max_file_size_10mb error

Validation failed

storage_path_not_empty error

Validation failed

expense_claim_must_exist error

Validation failed

uploaded_at_not_future error

Validation failed

organization_id_matches_claim error

Validation failed

Business Rules

receipt_required_above_threshold
on_create

A receipt must be attached to an expense claim when the total claim amount exceeds the organization's configured monetary threshold (default 100 NOK). Submission is blocked until a receipt is provided.

one_receipt_per_claim
on_create

Each expense claim may have at most one receipt record. The unique constraint on expense_claim_id enforces this at the database level. Replacing a receipt requires deleting the old record first.

compress_before_upload
on_create

Receipt images must be compressed to the configured target size limit (default 1 MB) before uploading to Supabase Storage. The file_size_bytes stored in the record reflects the post-compression size.

signed_url_access_only
always

The storage_path column is never returned directly to clients. All read access to the receipt file must go through time-limited signed URLs generated by the storage service. This prevents unauthorized direct access to storage bucket paths.

delete_on_claim_cancellation
on_delete

When an expense claim is cancelled, the associated receipt record and its Supabase Storage object must both be deleted. The ON DELETE CASCADE on expense_claim_id handles the database row; the application layer must additionally remove the storage object.

organization_scoped_storage_path
on_create

The storage_path must use the pattern {organization_id}/{user_id}/{claim_id}/{filename} so that Supabase Storage RLS bucket policies can enforce organization-level isolation.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage

Entity Relationships

expense_claim
incoming one_to_one

When a claim amount exceeds the configured threshold, exactly one receipt image record is linked to the expense claim for audit purposes

optional cascade delete