feat: Implement document upload and verification workflow by expanding document statuses, adding verification metadata, and introducing a mandatory flag for documents.

This commit is contained in:
Achintha Isuru
2026-02-26 15:45:55 -05:00
parent df71deb698
commit 050072bd93
3 changed files with 85 additions and 2 deletions

View File

@@ -0,0 +1,68 @@
# Document Upload & Verification Workflow
This document outlines the standardized workflow for handling file uploads, verification, and persistence within the Krow mobile application. This pattern is based on the `attire` module and should be used as a reference for the `certificates` module.
## 1. Overview
The workflow follows a 4-step lifecycle:
1. **Selection**: Picking or capturing a file (PDF/Image) locally.
2. **Preview**: Allowing the user to review the document/photo before submission.
3. **Upload & Verification**: Pushing the file to storage and initiating a background verification job.
4. **Persistence**: Saving the record with its verification status to the database.
---
## 2. Technical Stack
- **`FilePickerService`**: Handles PDF/File selection from the device.
- **`CameraService` / `GalleryService`**: Handles image capturing (if applicable).
- **`FileUploadService`**: Uploads raw files to our secure cloud storage.
- **`SignedUrlService`**: Generates secure internal/public links for viewing.
- **`VerificationService`**: Orchestrates the automated (AI) or manual verification of the document.
- **`DataConnect` (Firebase)**: Persists the structured data and verification metadata.
---
## 3. Implementation Steps
### A. UI Layer
1. **Selection**:
- Use `FilePickerService.pickFile(allowedExtensions: ['pdf'])`.
- Store the `localPath` in the widget state.
2. **Stateless Preview**:
- Provide an inline previewer (e.g., `PdfPreviewWidget`) so the user can verify the file contents.
3. **Submission**:
- Trigger a Cubit/Bloc event which calls the `UploadDocumentUseCase`.
- Show a global or inline progress indicator during the "Upload -> Verify -> Save" sequence.
### B. Domain Layer
1. **UseCase**:
- Manage the sequence of repository calls.
- Handle domain-specific validation (e.g., checking if the document is mandatory).
### C. Data Layer (The Repository Pattern)
The `Repository.uploadDocument` method should perform the following:
1. **Upload**: Call `FileUploadService` to get a `fileUri`.
2. **Link**: Call `SignedUrlService.createSignedUrl` to generate a `documentUrl`.
3. **Verify**: Call `VerificationService.createVerification` with the `fileUri` and relevant rules (e.g., `documentType`).
4. **Poll (Optional but Recommended)**: Poll the `VerificationService` for status updates for ~10 seconds to provide immediate feedback to the user.
5. **Persist**: Call Data Connect's `upsertStaffDocument` mutation with:
- `documentId`
- `documentUrl`
- `verificationId`
- `verificationStatus` (e.g., `PENDING` or `APPROVED`)
---
## 4. State Management (Cubit/Bloc)
- **`status`**: Track `loading`, `uploading`, `success`, and `failure`.
- **`errorMessage`**: Store localized error keys from `BlocErrorHandler`.
- **`verificationId`**: Store the ID returned from the server to allow later manual refreshes.
---
## 5. Metadata Mapping
Ensure the `DocumentStatus` or `VerificationStatus` enum aligns with the backend definition:
- `PENDING`: Waiting for job to start.
- `PROCESSING`: AI is currently analyzing.
- `APPROVED`: Document is valid.
- `REJECTED`: Document is invalid (should check `rejectionReason`).
- `NEEDS_REVIEW`: AI is unsure, manual check required.

View File

@@ -14,6 +14,7 @@ type Document @table(name: "documents") {
name: String!
description: String
documentType: DocumentType!
isMandatory: Boolean @default(expr: "false")
createdAt: Timestamp @default(expr: "request.time")
updatedAt: Timestamp @default(expr: "request.time")
createdBy: String

View File

@@ -1,6 +1,13 @@
enum DocumentStatus {
UPLOADED
PENDING
PROCESSING
AUTO_PASS
AUTO_FAIL
NEEDS_REVIEW
APPROVED
REJECTED
ERROR
UPLOADED
EXPIRING
MISSING
VERIFIED
@@ -12,9 +19,16 @@ type StaffDocument @table(name: "staff_documents", key: ["staffId", "documentId"
staffName: String!
documentId: UUID!
document: Document! @ref(fields: "documentId", references: "id")
status: DocumentStatus!
status: DocumentStatus! @default(expr: "'PENDING'")
documentUrl: String
expiryDate: Timestamp
# Verification Metadata (Align with Attire flow)
verificationId: String
verifiedAt: Timestamp
rejectionReason: String
createdAt: Timestamp @default(expr: "request.time")
updatedAt: Timestamp @default(expr: "request.time")
createdBy: String