# Mobile app: Missing documents (for the app developer)

Status: backend done (2026-10-29). The app changes below were made in `expo-cross-platform` the
same day (with the app developer's agreement), to ship in an OTA:
- the Documents → Missing tab (`components/missing-document-card.tsx`);
- the upload screen (`app/(main)/missing-document.tsx`);
- the shared query (`hooks/use-missing-documents.ts`);
- the tab badge and the Home banner;
- push and inbox links (`?tab=missing`);
- the gate line in Home's status poll.

## The problem

An HCP who has finished onboarding had no way to upload a document they don't already have:

- The Documents tab lists only documents already uploaded (`hcp/documents`), so it can update them
  but never add a missing one.
- When an admin adds a profession to the HCP, or adds a required document to a profession, the
  new documents appeared nowhere in the app.
- The "professions paused" push opened the Documents tab, which didn't show what was missing.

## The rule now (backend)

- **The onboarding gate is for onboarding only.** `app_registration_screen` reaching `0` is final.
  A new profession, a newly required document or a rejected document never sends a registered
  HCP back to onboarding. They keep working their other professions; shift eligibility still
  enforces each profession's documents.
- **HCPs still onboarding** (screen ≠ 0) are re-evaluated when a profession is assigned or
  removed and when a profession's requirements change. So the onboarding document step
  shows new requirements.
- `GET /hcps/{id}/onboarding-status` → `steps.documents_uploaded` now means "nothing missing or
  rejected" (general **and** profession), not "at least one document uploaded".

## New endpoints (mobile envelope)

### `POST hcp/missing-documents`

No body. Returns every mandatory document the HCP must act on, most urgent first:

```json
{
  "status": true,
  "message": "Missing documents found successfully.",
  "data": {
    "documents": [
      {
        "document_id": 12,
        "name": "APHRA Registration",
        "type": "profession",
        "state": "missing",
        "message": "Not uploaded yet.",
        "expiry_date": null,
        "expiry_date_mandatory": "yes",
        "required_by": ["Enrolled Nurse"]
      }
    ],
    "counts": { "missing": 1, "rejected": 0, "expired": 0, "expiring": 0 }
  }
}
```

- `state`: `missing` (never uploaded), `rejected`, `expired`, `expiring` (approved, expires within
  14 days). Documents awaiting approval are not listed.
- `type`: `general` or `profession`. `required_by` names the HCP's professions that need it.
  One upload covers all of them, because documents are held per person.
- `message`: ready to show under the document name.
- An empty `documents` array means nothing to do.

### `POST hcp/upload-required-document` (multipart)

| Field | |
|---|---|
| `document_id` | from `missing-documents` |
| `document_file` | pdf, jpg, jpeg, png, doc, docx; max 10 MB |
| `expiry_date` | `YYYY-MM-DD`; **required when `expiry_date_mandatory` is `"yes"`** |

Works for general and profession documents alike, so the app doesn't need to know which.
- Success: `{status:true, message:"Document saved successfully."}`.
- Failure: HTTP 200 `{status:false, message}`, with a message ready to show. For example, a
  missing expiry date, or a document the HCP isn't required to provide.
- Validation: 422 as usual.

The upload goes to admin review as before (`pending`). It then drops off the list until it's
approved or rejected.

## App changes

1. **Documents tab: a "Missing" section** at the top, or a segment, listing `missing-documents`.
   - Show the name, the `message` and the professions in `required_by`, with an Upload action
     (file picker plus expiry date when `expiry_date_mandatory === "yes"`).
   - After uploading, refetch `missing-documents` and `hcp/documents`.
2. **A badge** on the Documents tab, and optionally a Home banner, when
   `counts.missing + counts.rejected + counts.expired > 0`.
3. **Push notifications** about documents should open the Missing section.
   - `hcp.professions-paused` (data `notification_type: documents`).
   - Expiry reminders.
4. **Gate (small):** the Home screen's `onboarding-status` poll already updates `hcp.status`. It
   should also apply `registration_screen` (`updateHcp({ app_registration_screen })`). Then an HCP
   who's still onboarding is sent back to the onboarding document step straight away, instead of
   at their next login. Registered HCPs stay at `"0"`, so this never locks them out.
5. *(Optional)* `resolveOnboardingStep` only checks `missing_documents.general`. Profession
   documents are now covered by `documents_uploaded`, but checking
   `missing_documents.profession.length` there too makes it explicit.

Existing endpoints are unchanged (`hcp/documents`, `hcp/add-update-document`,
`hcp/add-update-profession-document`, `get-profession-documents`). The new pair replaces the need
to work out which upload endpoint a document needs.

## Optional certifications (November 2026)

`hcp/missing-documents` also returns `optional`: certifications that aren't mandatory (general, or mapped to the
HCP's professions) and that they hold no copy of yet, in the same row shape plus `optional: true`
(`HcpDocumentService::optionalForApp`). They're never in `documents` or `counts`, so the badge ignores them.
`hcp/upload-required-document` accepts them. The app lists them after the required ones in the same list,
with a warning-coloured "Optional" chip; a missing required certification has a red "Missing" chip, and
rejected/expired keep the red-filled card. Older app builds ignore the key.
