Deliver role-aware shell (sidebar, breadcrumbs, quick search, tables, detail/create layouts), locale-ready pages, shared backend/feature docs, and Vercel project config so HR, finance, and members can demo against typed mocks. Co-authored-by: Cursor <cursoragent@cursor.com>
192 lines
5.5 KiB
Markdown
192 lines
5.5 KiB
Markdown
# Backend overview
|
|
|
|
Living spec for the **shared platform API** consumed by Gishen-B2B. This repo implements mocks typed to this contract; the backend service is consolidated later from B2B, Ecom, Mobile, and Admin spec sheets.
|
|
|
|
**Workspace:** `/Users/kirukib/Desktop/Yaltopia Project/Gishen-B2B`
|
|
|
|
## Base URL (future)
|
|
|
|
```
|
|
https://api.gishenpharmacy.org/v1
|
|
```
|
|
|
|
Mock adapters in this repo live under `src/lib/api/` and `src/mocks/`.
|
|
|
|
## Tenancy
|
|
|
|
- Every authenticated request (except public registration) is scoped to an **organisation** via `orgId` on the session or explicit path prefix.
|
|
- Org lifecycle: `pending_activation` → `active` → `suspended` | `closed`.
|
|
- Pending orgs: SUPER_USER may read status and submitted registration; **cannot** create members, packages, or migration commits until Admin activates.
|
|
|
|
```
|
|
X-Org-Id: org_01H...
|
|
```
|
|
|
|
For single-org sessions, `orgId` is implicit from JWT/session. Multi-org users (rare in B2B) pass `X-Org-Id`.
|
|
|
|
## Authentication
|
|
|
|
| Mode (this phase) | Description |
|
|
|-------------------|-------------|
|
|
| Mock demo | `/login` role selector sets session cookie with role + org + user |
|
|
| Production (future) | OAuth2 / magic link / SSO — TBD with platform auth |
|
|
|
|
### Session shape
|
|
|
|
See [`entities/session-user.md`](entities/session-user.md).
|
|
|
|
### JWT claims (future)
|
|
|
|
```json
|
|
{
|
|
"sub": "usr_01H...",
|
|
"org_id": "org_01H...",
|
|
"roles": ["HR_ADMIN", "FINANCE"],
|
|
"locale": "am",
|
|
"iat": 1710000000,
|
|
"exp": 1710003600
|
|
}
|
|
```
|
|
|
|
## RBAC matrix
|
|
|
|
| Resource / action | SUPER_USER | HR_ADMIN | FINANCE | MEMBER |
|
|
|-------------------|:----------:|:--------:|:-------:|:------:|
|
|
| Org profile (commercial read) | ✓ | ✓ (active only) | ✓ (active only) | — |
|
|
| Org registration submit | ✓ (public path) | — | — | — |
|
|
| Departments CRUD | ✓ | ✓ | read | — |
|
|
| Members CRUD / import / invite | ✓ | ✓ | read metadata | own row |
|
|
| Packages CRUD | ✓ | ✓ | read | own assignment |
|
|
| Migration jobs | ✓ | ✓ | read history | — |
|
|
| Finance spend / statements | ✓ | read aggregates | ✓ | — |
|
|
| Finance approvals | ✓ | — | ✓ | — |
|
|
| Member allowance / orders | ✓ | — | — | ✓ |
|
|
| Prescriptions (clinical) | ✓ full | **withheld** | **withheld** | own only |
|
|
| Settings / verification | ✓ | ✓ | — | — |
|
|
|
|
**Clinical withhold:** HR_ADMIN and FINANCE never receive prescription images, medicine line items, dosages, prescriber notes, or diagnosis-related fields. Aggregates (e.g. “Rx order — ETB 450, category: chronic”) may appear in finance spend. SUPER_USER bypasses withhold for support/disputes.
|
|
|
|
## Error model
|
|
|
|
All errors return:
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "VALIDATION_FAILED",
|
|
"message": "Human-readable summary",
|
|
"details": [
|
|
{ "field": "email", "code": "INVALID_FORMAT", "message": "..." }
|
|
],
|
|
"request_id": "req_01H..."
|
|
}
|
|
}
|
|
```
|
|
|
|
### Standard HTTP status codes
|
|
|
|
| Status | Usage |
|
|
|--------|-------|
|
|
| 400 | Validation failed, malformed JSON |
|
|
| 401 | Missing or expired session |
|
|
| 403 | Authenticated but role/org forbids action; clinical withhold |
|
|
| 404 | Resource not found or not visible in tenant |
|
|
| 409 | Conflict (duplicate member, job already committed) |
|
|
| 422 | Business rule violation (org pending, credit exceeded) |
|
|
| 429 | Rate limit |
|
|
| 500 | Internal error |
|
|
|
|
### Error codes (catalog)
|
|
|
|
| Code | HTTP | Description |
|
|
|------|------|-------------|
|
|
| `UNAUTHENTICATED` | 401 | No valid session |
|
|
| `FORBIDDEN` | 403 | Role cannot perform action |
|
|
| `CLINICAL_WITHHELD` | 403 | Clinical field requested by HR/Finance |
|
|
| `ORG_PENDING` | 422 | Org not yet activated by Admin |
|
|
| `ORG_SUSPENDED` | 422 | Org suspended |
|
|
| `VALIDATION_FAILED` | 400 | Field-level validation |
|
|
| `DUPLICATE_MEMBER` | 409 | Phone/email/employee_id conflict |
|
|
| `CREDIT_LIMIT_EXCEEDED` | 422 | Would exceed org credit |
|
|
| `MIGRATION_JOB_LOCKED` | 409 | Job already committed or rolled back |
|
|
| `INVITE_EXPIRED` | 422 | Invite token expired |
|
|
| `NOT_FOUND` | 404 | Generic not found |
|
|
|
|
## Pagination
|
|
|
|
List endpoints accept:
|
|
|
|
```
|
|
?page=1&page_size=25&sort=-created_at&q=search
|
|
```
|
|
|
|
Response envelope:
|
|
|
|
```json
|
|
{
|
|
"data": [ ... ],
|
|
"pagination": {
|
|
"page": 1,
|
|
"page_size": 25,
|
|
"total_items": 142,
|
|
"total_pages": 6
|
|
}
|
|
}
|
|
```
|
|
|
|
Default `page_size`: 25. Max: 100.
|
|
|
|
## Filtering & date ranges
|
|
|
|
Finance and spend endpoints accept:
|
|
|
|
```
|
|
?from=2026-01-01&to=2026-01-31&department_id=dept_01H...
|
|
```
|
|
|
|
ISO 8601 dates in org timezone (default `Africa/Addis_Ababa`).
|
|
|
|
## File uploads
|
|
|
|
Multipart for Rx images and migration files:
|
|
|
|
```
|
|
POST /v1/...
|
|
Content-Type: multipart/form-data
|
|
```
|
|
|
|
Max file size (Rx page): 10 MB. Accepted: `image/jpeg`, `image/png`, `application/pdf`.
|
|
|
|
Migration uploads: 50 MB `.xlsx`, `.xls`, `.csv`.
|
|
|
|
## Idempotency
|
|
|
|
Mutating endpoints that create billing or migration commits accept:
|
|
|
|
```
|
|
Idempotency-Key: <uuid>
|
|
```
|
|
|
|
Duplicate keys within 24h return the original response.
|
|
|
|
## Webhooks / events
|
|
|
|
Domain events published to platform bus; see [`events/README.md`](events/README.md). B2B emits and consumes events for org registration, migration, and prescriptions.
|
|
|
|
## Environment (Vercel frontend)
|
|
|
|
| Variable | Purpose |
|
|
|----------|---------|
|
|
| `NEXT_PUBLIC_API_BASE_URL` | Platform API base (mock or real) |
|
|
| `NEXT_PUBLIC_MOCK_AUTH` | `true` for demo role selector |
|
|
| `NEXT_PUBLIC_DEFAULT_LOCALE` | `en` |
|
|
|
|
Document production URL in this file when first Vercel deploy is finalized.
|
|
|
|
## Related docs
|
|
|
|
- [Entities index](entities/README.md)
|
|
- [Endpoints index](endpoints/README.md)
|
|
- [Events](events/README.md)
|
|
- [Features INDEX](../features/INDEX.md)
|