This repository has been archived on 2026-08-11. You can view files and clone it, but cannot push or open issues or pull requests.
Gishen-B2B/docs/backend/OVERVIEW.md
kirukib 3778801ef5 Ship Gishen B2B institutional portal with polished layout and mock-backed flows.
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>
2026-08-06 21:18:23 +03:00

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)