Service Layer high complexity mobile
3
Dependencies
0
Dependents
1
Entities
0
Integrations

Description

Orchestrates the full receipt capture and upload flow including image selection, client-side compression, Supabase Storage upload, and linking the storage path to the claim record. Manages non-blocking upload state so the form can be submitted concurrently with an in-progress upload.

Feature: Receipt Capture and Attachment

receipt-attachment-service

Summaries

The Receipt Attachment Service is a critical enabler of the expense claim workflow, allowing employees to attach supporting receipts without slowing down form submission. By running image compression and cloud upload concurrently with form completion, it eliminates the most common source of delay in expense submission — waiting for a large photo to upload. This directly improves employee experience and increases submission completion rates. Proper receipt attachment also reduces audit risk and strengthens financial controls, protecting the organization during expense reviews.

The service handles retry logic and cancellation, ensuring reliable operation on mobile networks.

This is the highest-complexity component in the receipt feature set and represents the most significant delivery risk in this area. It orchestrates four distinct operations — image selection, compression, Supabase Storage upload, and database record linking — each of which can fail independently. Non-blocking upload state management requires careful coordination between the service and the form submission flow, and edge cases (upload completes after form is abandoned, retry after network failure) need explicit test coverage. Dependencies on `receipt-image-compressor`, `receipt-storage-repository`, and `claim-receipt-repository` must all be completed before end-to-end testing is possible.

Allocate extra buffer for integration testing and error-path coverage.

The Receipt Attachment Service acts as the primary orchestration layer for the receipt capture pipeline. It exposes a stream-based interface (`getUploadProgressStream(expenseId)`) for reactive UI binding and supports concurrent form submission via non-blocking upload state. The service sequences: image selection → compression via `receipt-image-compressor` → upload via `receipt-storage-repository` → path linkage via `claim-receipt-repository`. Cancellation (`cancelUpload`) must clean up both in-flight HTTP requests and any partially written storage entries.

The `retryFailedUpload` path should preserve the original compressed image to avoid re-compressing. Use a state machine internally (idle, compressing, uploading, complete, failed, cancelled) to keep the logic predictable and testable. The `expense_claim` and `receipt` data models are mutated during the flow — ensure writes are transactionally safe.

Responsibilities

  • Coordinate image selection, compression, and upload sequencing
  • Expose upload progress stream to UI layer
  • Allow form submission while upload is in progress, completing asynchronously
  • Associate the final storage path with the claim record on upload completion
  • Handle upload cancellation and cleanup on form abandonment

Interfaces

attachReceiptToExpense(expenseId, imageFile)
getUploadProgressStream(expenseId)
cancelUpload(expenseId)
removeAttachment(expenseId)
getAttachmentStatus(expenseId)
getStoragePath(expenseId)
retryFailedUpload(expenseId)

Relationships

Dependencies (3)

Components this component depends on

Related Data Entities (1)

Data entities managed by this component

API Contract

View full contract →
REST /api/v1/receipt-attachments 6 endpoints
GET /api/v1/receipt-attachments List all receipt attachments
GET /api/v1/receipt-attachments/:expenseId Get receipt attachment by expense ID
POST /api/v1/receipt-attachments/:expenseId Attach a receipt image to an expense (multipart upload)
GET /api/v1/receipt-attachments/:expenseId/upload-progress Stream upload progress for an expense receipt (Server-Sent Events)
DELETE /api/v1/receipt-attachments/:expenseId/upload Cancel an in-progress receipt upload
DELETE /api/v1/receipt-attachments/:expenseId Remove a receipt attachment from an expense