Document portal auth, profile, and pharmacy ID contracts in backend specs.
Keep the living API sheet aligned with demo personas, avatars, ID-card QR scan, list filters, and Admin/Ecom coordination. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a522916ff1
commit
8fe6d58a09
|
|
@ -28,12 +28,14 @@ For single-org sessions, `orgId` is implicit from JWT/session. Multi-org users (
|
|||
|
||||
| Mode (this phase) | Description |
|
||||
|-------------------|-------------|
|
||||
| Mock demo | `/login` role selector sets session cookie with role + org + user |
|
||||
| Mock demo | `/login` **test-user dropdown** (`demo_user_id`) sets session cookie with user + roles + org; legacy `demo_role` still accepted |
|
||||
| Production (future) | OAuth2 / magic link / SSO — TBD with platform auth |
|
||||
|
||||
Demo personas: `user_super`, `user_hr`, `user_fin`, `user_mem` — see [`endpoints/auth.md`](endpoints/auth.md) (`GET /v1/auth/demo-profiles`).
|
||||
|
||||
### Session shape
|
||||
|
||||
See [`entities/session-user.md`](entities/session-user.md).
|
||||
See [`entities/session-user.md`](entities/session-user.md). Includes `avatar_url`, `roles[]`, and `active_role` for multi-role demo users.
|
||||
|
||||
### JWT claims (future)
|
||||
|
||||
|
|
@ -42,6 +44,7 @@ See [`entities/session-user.md`](entities/session-user.md).
|
|||
"sub": "usr_01H...",
|
||||
"org_id": "org_01H...",
|
||||
"roles": ["HR_ADMIN", "FINANCE"],
|
||||
"active_role": "HR_ADMIN",
|
||||
"locale": "am",
|
||||
"iat": 1710000000,
|
||||
"exp": 1710003600
|
||||
|
|
@ -52,10 +55,12 @@ See [`entities/session-user.md`](entities/session-user.md).
|
|||
|
||||
| Resource / action | SUPER_USER | HR_ADMIN | FINANCE | MEMBER |
|
||||
|-------------------|:----------:|:--------:|:-------:|:------:|
|
||||
| Own profile (`/v1/me/profile`) | ✓ | ✓ | ✓ | ✓ |
|
||||
| Org profile (commercial read) | ✓ | ✓ (active only) | ✓ (active only) | — |
|
||||
| Org registration submit | ✓ (public path) | — | — | — |
|
||||
| Departments CRUD | ✓ | ✓ | read | — |
|
||||
| Members CRUD / import / invite | ✓ | ✓ | read metadata | own row |
|
||||
| Pharmacy ID card (print / QR payload) | ✓ | ✓ | — | own only |
|
||||
| Packages CRUD | ✓ | ✓ | read | own assignment |
|
||||
| Migration jobs | ✓ | ✓ | read history | — |
|
||||
| Finance spend / statements | ✓ | read aggregates | ✓ | — |
|
||||
|
|
@ -138,7 +143,15 @@ Default `page_size`: 25. Max: 100.
|
|||
|
||||
## Filtering & date ranges
|
||||
|
||||
Finance and spend endpoints accept:
|
||||
List endpoints support search + filters (portal toolbar / modal when >3 filters):
|
||||
|
||||
| Domain | Query params (common) |
|
||||
|--------|------------------------|
|
||||
| Members | `q`, `status`, `department_id`, `package_id`, `member_type`, `has_dependants` |
|
||||
| Finance spend | `from`, `to`, `department_id`, `category`, `coverage`, `member_id`, `approval_status` |
|
||||
| Statements | `year`, `status` |
|
||||
| Migration | `status`, `dataset_type`, `has_errors`, `created_by` |
|
||||
| Packages / departments | `q`, `status` / site |
|
||||
|
||||
```
|
||||
?from=2026-01-01&to=2026-01-31&department_id=dept_01H...
|
||||
|
|
@ -146,6 +159,24 @@ Finance and spend endpoints accept:
|
|||
|
||||
ISO 8601 dates in org timezone (default `Africa/Addis_Ababa`).
|
||||
|
||||
## Pharmacy ID card (platform contract)
|
||||
|
||||
Members carry a printable verification card used at Gishen pharmacy / Ecom checkout:
|
||||
|
||||
| Field | Purpose |
|
||||
|-------|---------|
|
||||
| `avatar_url` | Photo on card + portal UI |
|
||||
| `card_id` | Human-readable card number (e.g. `GSH-1042`) |
|
||||
| `verification_id` | Org verification token encoded in QR |
|
||||
|
||||
**QR payload (canonical):**
|
||||
|
||||
```
|
||||
gishen://member/{member_id}?v={verification_id}
|
||||
```
|
||||
|
||||
Admin / POS scans the QR to resolve the member for covered purchase. Clinical detail is **not** on the card. See [`endpoints/members.md`](endpoints/members.md#get-v1-membersidid-card) and [`../coordination/gishen-admin.md`](../coordination/gishen-admin.md).
|
||||
|
||||
## File uploads
|
||||
|
||||
Multipart for Rx images and migration files:
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ REST-style API contract for Gishen-B2B. All paths prefixed with `/v1` unless not
|
|||
|
||||
| Group | Doc | Description |
|
||||
|-------|-----|-------------|
|
||||
| Auth | [auth.md](auth.md) | Login, session, locale |
|
||||
| Auth | [auth.md](auth.md) | Demo login personas, session, locale, `/me/profile` |
|
||||
| Organisation | [org.md](org.md) | Org profile, registration, departments, verification |
|
||||
| Members | [members.md](members.md) | CRUD, import, invites, join |
|
||||
| Members | [members.md](members.md) | CRUD, import, invites, join, pharmacy ID card |
|
||||
| Packages | [packages.md](packages.md) | Benefit plans |
|
||||
| Migration | [migration.md](migration.md) | Bulk import jobs |
|
||||
| Finance | [finance.md](finance.md) | Spend, statements, approvals |
|
||||
|
|
@ -53,6 +53,7 @@ Endpoints returning prescription or order clinical detail check session roles. H
|
|||
| Path pattern | Auth |
|
||||
|--------------|------|
|
||||
| `/v1/auth/login` | Public |
|
||||
| `/v1/auth/demo-profiles` | Public |
|
||||
| `/v1/org/register*` | Public |
|
||||
| `/v1/invite/:token` (GET) | Public |
|
||||
| `/v1/join` (POST) | Public or member session |
|
||||
|
|
|
|||
|
|
@ -38,12 +38,14 @@ Production (future): `{ "email", "password" }` or OAuth code exchange.
|
|||
"data": {
|
||||
"token": "mock_jwt_...",
|
||||
"user": {
|
||||
"id": "usr_01HHR",
|
||||
"email": "hr.demo@acme.et",
|
||||
"full_name": "Demo HR Admin",
|
||||
"org_id": "org_01DEMO",
|
||||
"id": "user_hr",
|
||||
"email": "hr@yaltopia.com",
|
||||
"full_name": "Hanna HR",
|
||||
"org_id": "org_yaltopia",
|
||||
"roles": ["HR_ADMIN"],
|
||||
"active_role": "HR_ADMIN",
|
||||
"locale": "en",
|
||||
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_hr",
|
||||
"org_status": "active",
|
||||
"permissions": ["org:read", "org:write_hr", "members:write", "packages:write", "migration:write", "finance:read"]
|
||||
}
|
||||
|
|
@ -55,7 +57,8 @@ Production (future): `{ "email", "password" }` or OAuth code exchange.
|
|||
|
||||
| Code | HTTP | When |
|
||||
|------|------|------|
|
||||
| `VALIDATION_FAILED` | 400 | Invalid `demo_role` |
|
||||
| `VALIDATION_FAILED` | 400 | Invalid `demo_user_id` / `demo_role` |
|
||||
| `NOT_FOUND` | 404 | Unknown demo persona |
|
||||
| `UNAUTHENTICATED` | 401 | Production bad credentials |
|
||||
|
||||
### Clinical withhold
|
||||
|
|
@ -166,24 +169,38 @@ List pre-seeded demo personas for `/login` test-user dropdown.
|
|||
"full_name": "Selam Super",
|
||||
"demo_role": "SUPER_USER",
|
||||
"roles": ["SUPER_USER", "HR_ADMIN", "FINANCE", "MEMBER"],
|
||||
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_super",
|
||||
"label": "Super User",
|
||||
"description": "Full portal access including clinical detail"
|
||||
},
|
||||
{
|
||||
"id": "user_hr",
|
||||
"email": "hr@yaltopia.com",
|
||||
"full_name": "Hanna HR",
|
||||
"demo_role": "HR_ADMIN",
|
||||
"roles": ["HR_ADMIN"],
|
||||
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_hr",
|
||||
"label": "HR Administrator",
|
||||
"description": "Members, departments, packages — no clinical detail"
|
||||
},
|
||||
{
|
||||
"id": "user_fin",
|
||||
"email": "finance@yaltopia.com",
|
||||
"full_name": "Fikru Finance",
|
||||
"demo_role": "FINANCE",
|
||||
"roles": ["FINANCE"],
|
||||
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_fin",
|
||||
"label": "Finance Approver",
|
||||
"description": "Spend, statements, approvals — no clinical detail"
|
||||
},
|
||||
{
|
||||
"id": "user_mem",
|
||||
"email": "member@yaltopia.com",
|
||||
"full_name": "Abebe Member",
|
||||
"demo_role": "MEMBER",
|
||||
"roles": ["MEMBER"],
|
||||
"member_id": "mbr_01HMEM001",
|
||||
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_mem",
|
||||
"label": "Member",
|
||||
"description": "Own allowance, orders, prescriptions"
|
||||
}
|
||||
|
|
@ -217,7 +234,9 @@ Current user’s profile (account page).
|
|||
"roles": ["HR_ADMIN"],
|
||||
"active_role": "HR_ADMIN",
|
||||
"locale": "en",
|
||||
"avatar_initials": "HH"
|
||||
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_hr",
|
||||
"avatar_initials": "HH",
|
||||
"member_id": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -226,7 +245,7 @@ Current user’s profile (account page).
|
|||
|
||||
## PATCH /v1/me/profile
|
||||
|
||||
Update display preferences (locale / name stub).
|
||||
Update display preferences (locale / name stub / active role for multi-role users).
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
|
|
@ -236,22 +255,33 @@ Update display preferences (locale / name stub).
|
|||
### Request
|
||||
|
||||
```json
|
||||
{ "locale": "am" }
|
||||
{
|
||||
"locale": "am",
|
||||
"active_role": "SUPER_USER"
|
||||
}
|
||||
```
|
||||
|
||||
`active_role` must be one of the user’s `roles[]` (demo: `user_super` holds all four).
|
||||
|
||||
### Response 200
|
||||
|
||||
Updated profile object.
|
||||
|
||||
### Errors
|
||||
|
||||
| Code | HTTP | When |
|
||||
|------|------|------|
|
||||
| `VALIDATION_FAILED` | 400 | Invalid locale or role not in `roles[]` |
|
||||
|
||||
---
|
||||
|
||||
## UI routes
|
||||
|
||||
| Route | Purpose |
|
||||
|-------|---------|
|
||||
| `/login` | Demo test-user dropdown + locale switcher |
|
||||
| `/profile` | Own profile — role switch, sign out |
|
||||
| `/login` | Yimaru-style login — test-user dropdown + locale switcher |
|
||||
| `/profile` | Own profile — avatar, org, role switch, sign out |
|
||||
|
||||
## Clinical withhold notes
|
||||
|
||||
Auth endpoints do not expose clinical data. Role list on session determines withhold behavior on downstream endpoints.
|
||||
Auth endpoints do not expose clinical data. Role list / `active_role` on session determines withhold behavior on downstream endpoints.
|
||||
|
|
|
|||
|
|
@ -42,7 +42,17 @@ Paginated spend lines.
|
|||
|
||||
### Query
|
||||
|
||||
`?page=1&member_id=mbr_01H&department_id=dept_01H&approval_status=flagged`
|
||||
`?page=1&member_id=mbr_01H&department_id=dept_01H&category=chronic&coverage=partial&period=2026-03&approval_status=flagged`
|
||||
|
||||
| Param | Notes |
|
||||
|-------|--------|
|
||||
| `from` / `to` | ISO date range |
|
||||
| `department_id` | Site filter |
|
||||
| `category` | Category code (no medicine names) |
|
||||
| `coverage` | e.g. `full` \| `partial` \| `member_paid` (mock) |
|
||||
| `period` | `YYYY-MM` convenience filter |
|
||||
| `member_id` | Single member |
|
||||
| `approval_status` | `flagged` \| `approved` \| … |
|
||||
|
||||
### Response 200
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,18 @@ List members.
|
|||
|
||||
### Query
|
||||
|
||||
`?page=1&status=active&department_id=dept_01H&q=abel&member_type=primary`
|
||||
`?page=1&status=active&department_id=dept_01H&package_id=pkg_01HGOLD&q=abel&member_type=primary&has_dependants=true`
|
||||
|
||||
| Param | Notes |
|
||||
|-------|--------|
|
||||
| `q` | Search name, email, phone, employee_id |
|
||||
| `status` | `invited` \| `active` \| `inactive` \| `offboarded` |
|
||||
| `department_id` | Filter by department |
|
||||
| `package_id` | Filter by assigned package |
|
||||
| `member_type` | `primary` \| `dependant` |
|
||||
| `has_dependants` | `true` \| `false` — primaries with/without dependants |
|
||||
|
||||
Portal UI: when more than three filters are shown, they open in an Apply modal (client-only); API still accepts all query params above.
|
||||
|
||||
### Response 200
|
||||
|
||||
|
|
@ -91,6 +102,59 @@ Member detail.
|
|||
|
||||
HR/Finance: no linked prescriptions. SUPER_USER: may include `prescriptions_summary`. MEMBER own: full allowance, order pointers.
|
||||
|
||||
Detail payload includes `avatar_url`, `card_id`, `verification_id` for pharmacy ID card (safe for HR).
|
||||
|
||||
---
|
||||
|
||||
## GET /v1/members/:id/id-card
|
||||
|
||||
Printable pharmacy verification card payload (B2B print UI + Admin/POS scan).
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Auth** | Required |
|
||||
| **Roles** | SUPER_USER, HR_ADMIN; MEMBER (own `member_id` only) |
|
||||
|
||||
### Response 200
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"member_id": "mbr_01HMEM001",
|
||||
"full_name": "Abel Mekonnen",
|
||||
"employee_id": "EMP-1042",
|
||||
"card_id": "GSH-1042",
|
||||
"verification_id": "EMP-1042",
|
||||
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=mbr_01HMEM001",
|
||||
"org_name": "Acme Bank",
|
||||
"org_join_code": "ACME-2026",
|
||||
"package_name": "Gold Executive",
|
||||
"status": "active",
|
||||
"qr_payload": "gishen://member/mbr_01HMEM001?v=EMP-1042"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### QR / scan contract
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Format** | `gishen://member/{member_id}?v={verification_id}` |
|
||||
| **Consumer** | Gishen-Admin POS / pharmacist checkout — resolve covered member |
|
||||
| **Not included** | Prescription images, medicine lines, diagnosis |
|
||||
|
||||
### Errors
|
||||
|
||||
| Code | HTTP |
|
||||
|------|------|
|
||||
| `FORBIDDEN` | 403 |
|
||||
| `NOT_FOUND` | 404 |
|
||||
| `MEMBER_INACTIVE` | 422 — offboarded / inactive cards may still print with watermark (product TBD) |
|
||||
|
||||
### Clinical withhold
|
||||
|
||||
ID card is verification metadata only — allowed for HR. No clinical fields.
|
||||
|
||||
---
|
||||
|
||||
## PATCH /v1/members/:id
|
||||
|
|
@ -108,10 +172,13 @@ Update member.
|
|||
{
|
||||
"department_id": "dept_01HNEW",
|
||||
"package_id": "pkg_01HSILVER",
|
||||
"avatar_url": "https://cdn.gishen.../avatars/mbr_01H.png",
|
||||
"overrides": { "copay_percent": 5 }
|
||||
}
|
||||
```
|
||||
|
||||
`avatar_url` may also be set via future `POST /v1/members/:id/avatar` (multipart). Mock phase uses deterministic portrait URLs.
|
||||
|
||||
---
|
||||
|
||||
## POST /v1/members/:id/offboard
|
||||
|
|
@ -297,10 +364,10 @@ Add dependant.
|
|||
|
||||
| Route | Endpoint(s) |
|
||||
|-------|-------------|
|
||||
| `/members` | GET `/v1/members` |
|
||||
| `/members` | GET `/v1/members` (search + filters; modal when >3) |
|
||||
| `/members/new` | POST `/v1/members` |
|
||||
| `/members/import` | POST `/v1/members/import` |
|
||||
| `/members/[id]` | GET/PATCH + invite/offboard/status mock actions |
|
||||
| `/members/[id]` | GET/PATCH + invite/offboard; **ID card** tab → GET `/v1/members/:id/id-card` |
|
||||
| `/invite/[token]` | GET/POST invite |
|
||||
| `/join` | POST `/v1/join` |
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Data model entities required by Gishen-B2B. Types below are canonical for mocks
|
|||
| Organisation | [organisation.md](organisation.md) | All roles (scoped) |
|
||||
| Org registration | [org-registration.md](org-registration.md) | Public signup, Admin |
|
||||
| Department | [department.md](department.md) | HR, Finance (aggregates) |
|
||||
| Member | [member.md](member.md) | HR, Member; Finance (metadata only) |
|
||||
| Member | [member.md](member.md) | HR, Member; Finance (metadata); pharmacy ID card (`avatar_url`, `card_id`, `verification_id`) |
|
||||
| Package | [package.md](package.md) | HR, Member |
|
||||
| Migration job | [migration-job.md](migration-job.md) | HR, SUPER_USER |
|
||||
| Finance | [finance.md](finance.md) | Finance, HR (read), SUPER_USER |
|
||||
|
|
@ -32,6 +32,8 @@ usr_01HXXXXXXXXXXXXXX
|
|||
|
||||
ULID-style prefixes for human readability in logs.
|
||||
|
||||
Printed pharmacy card numbers use org-scoped display IDs (e.g. `GSH-1042`) on `Member.card_id` — not a separate entity.
|
||||
|
||||
## Common field types
|
||||
|
||||
| Type | Format |
|
||||
|
|
|
|||
|
|
@ -147,4 +147,5 @@ SUPER_USER and MEMBER (own record) receive full clinical linkage via prescriptio
|
|||
|
||||
## Related endpoints
|
||||
|
||||
- [`../endpoints/members.md`](../endpoints/members.md)
|
||||
- [`../endpoints/members.md`](../endpoints/members.md) — including `GET /v1/members/:id/id-card`
|
||||
- Pharmacy QR scan consumed by Admin / POS — see [`../../coordination/gishen-admin.md`](../../coordination/gishen-admin.md)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Authenticated user context for B2B portal — demo mock now, JWT/session later.
|
|||
|
||||
| Field | Type | Required | Notes |
|
||||
|-------|------|----------|-------|
|
||||
| `id` | `string` | ✓ | `usr_*` |
|
||||
| `id` | `string` | ✓ | `usr_*` or demo `user_*` |
|
||||
| `email` | `string` | ✓ | |
|
||||
| `full_name` | `string` | ✓ | |
|
||||
| `phone` | `Phone` | | |
|
||||
|
|
@ -16,10 +16,11 @@ Authenticated user context for B2B portal — demo mock now, JWT/session later.
|
|||
| `member_id` | `string` | | Link to Member row if enrolled |
|
||||
| `customer_id` | `string` | | Shared Gishen customer (Ecom/Mob) |
|
||||
| `roles` | `PortalRole[]` | ✓ | One or more |
|
||||
| `active_role` | `PortalRole` | ✓ | Role used for nav + withhold (must ∈ `roles`) |
|
||||
| `locale` | `Locale` | ✓ | `en` \| `am` |
|
||||
| `avatar_url` | `string` | | |
|
||||
| `avatar_url` | `string` | | Portrait URL (demo: deterministic DiceBear) |
|
||||
| `org_status` | `OrgStatus` | ✓ | Denormalized for gating |
|
||||
| `permissions` | `string[]` | | Computed from roles |
|
||||
| `permissions` | `string[]` | | Computed from `active_role` / roles |
|
||||
| `demo_profile` | `DemoProfile` | | Mock only |
|
||||
|
||||
### PortalRole
|
||||
|
|
@ -35,7 +36,7 @@ SUPER_USER | HR_ADMIN | FINANCE | MEMBER
|
|||
| `label` | `string` |
|
||||
| `description` | `string` |
|
||||
|
||||
Pre-seeded demo profiles per role against a shared mock org.
|
||||
Pre-seeded demo personas: `user_super`, `user_hr`, `user_fin`, `user_mem` against a shared mock org. Listed via `GET /v1/auth/demo-profiles`.
|
||||
|
||||
## Computed permissions
|
||||
|
||||
|
|
@ -50,30 +51,37 @@ Pre-seeded demo profiles per role against a shared mock org.
|
|||
| `finance:approve` | SUPER_USER, FINANCE |
|
||||
| `clinical:read` | SUPER_USER, MEMBER (own) |
|
||||
| `prescriptions:write` | MEMBER (own), SUPER_USER |
|
||||
| `id_card:print` | SUPER_USER, HR_ADMIN, MEMBER (own) |
|
||||
|
||||
## Locale preference
|
||||
|
||||
`locale` is persisted on the user record and returned in session. Updated via `PATCH /v1/auth/me/locale`. Synced to client cookie/localStorage for next-intl.
|
||||
`locale` is persisted on the user record and returned in session. Updated via `PATCH /v1/auth/me/locale` or `PATCH /v1/me/profile`. Synced to client cookie/localStorage for next-intl.
|
||||
|
||||
## Active role
|
||||
|
||||
Multi-role users (demo Super User) can set `active_role` on `/profile`. Portal nav and clinical withhold follow `active_role` for the session.
|
||||
|
||||
## Clinical withhold
|
||||
|
||||
Session drives RBAC. If `roles` includes HR_ADMIN or FINANCE and **not** SUPER_USER, all prescription and clinical serializers apply withhold filters automatically.
|
||||
Session drives RBAC. If effective role is HR_ADMIN or FINANCE and **not** SUPER_USER, all prescription and clinical serializers apply withhold filters automatically.
|
||||
|
||||
## Sample session (mock demo — HR)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "usr_01HHR",
|
||||
"email": "hr.demo@acme.et",
|
||||
"full_name": "Demo HR Admin",
|
||||
"id": "user_hr",
|
||||
"email": "hr@yaltopia.com",
|
||||
"full_name": "Hanna HR",
|
||||
"phone": "+251900000001",
|
||||
"org_id": "org_01DEMO",
|
||||
"org_id": "org_yaltopia",
|
||||
"member_id": null,
|
||||
"customer_id": null,
|
||||
"roles": ["HR_ADMIN"],
|
||||
"active_role": "HR_ADMIN",
|
||||
"locale": "en",
|
||||
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_hr",
|
||||
"org_status": "active",
|
||||
"permissions": ["org:read", "org:write_hr", "members:write", "packages:write", "migration:write", "finance:read"],
|
||||
"permissions": ["org:read", "org:write_hr", "members:write", "packages:write", "migration:write", "finance:read", "id_card:print"],
|
||||
"demo_profile": {
|
||||
"label": "HR Administrator",
|
||||
"description": "Manage members, departments, and packages"
|
||||
|
|
@ -85,9 +93,13 @@ Session drives RBAC. If `roles` includes HR_ADMIN or FINANCE and **not** SUPER_U
|
|||
|
||||
```json
|
||||
{
|
||||
"id": "usr_01HSUPER",
|
||||
"roles": ["SUPER_USER"],
|
||||
"id": "user_super",
|
||||
"email": "super@yaltopia.com",
|
||||
"full_name": "Selam Super",
|
||||
"roles": ["SUPER_USER", "HR_ADMIN", "FINANCE", "MEMBER"],
|
||||
"active_role": "SUPER_USER",
|
||||
"locale": "am",
|
||||
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_super",
|
||||
"permissions": ["*"],
|
||||
"demo_profile": {
|
||||
"label": "Super User",
|
||||
|
|
@ -98,4 +110,4 @@ Session drives RBAC. If `roles` includes HR_ADMIN or FINANCE and **not** SUPER_U
|
|||
|
||||
## Related endpoints
|
||||
|
||||
- [`../endpoints/auth.md`](../endpoints/auth.md)
|
||||
- [`../endpoints/auth.md`](../endpoints/auth.md) — login, demo-profiles, `/me/profile`
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ How Gishen-B2B interacts with **Gishen-Admin** (pharmacy ops, commercial activat
|
|||
| Org creation (legacy) | Offline sales creates org + invites SUPER_USER | B2B receives invite link |
|
||||
| Registration queue | Review `org.registration_submitted` / `org.registration_requested` | Admin contacts client, completes setup, activates |
|
||||
| Pharmacist Rx queue | Review submitted prescriptions | B2B member sees status updates via `prescription.review_updated` |
|
||||
| **Pharmacy ID scan** | POS / pharmacist scans member QR at purchase | Resolve `member_id` + `verification_id` from `gishen://member/{id}?v={verification_id}`; apply org package entitlement at checkout |
|
||||
| Credit / invoice oversight | Monthly billing, suspensions | B2B Finance sees statements; `org.suspended` blocks new orders |
|
||||
| Price lists | Corporate pricing | Referenced by `organisation.commercial.price_list_id` — read-only in B2B |
|
||||
|
||||
|
|
@ -49,8 +50,22 @@ How Gishen-B2B interacts with **Gishen-Admin** (pharmacy ops, commercial activat
|
|||
| `Organisation.status` | Admin | All authenticated |
|
||||
| `Prescription` review fields | Admin | Member, SUPER_USER |
|
||||
| `Member`, `Department`, `Package` | B2B (HR) | B2B |
|
||||
| `Member.verification_id` / `card_id` / `avatar_url` | B2B (HR) | B2B portal + Admin POS scan |
|
||||
| `Statement` | Admin/billing engine | B2B Finance |
|
||||
|
||||
## Pharmacy ID card (scan contract)
|
||||
|
||||
B2B prints employee pharmacy ID cards from `GET /v1/members/:id/id-card` (UI: `/members/[id]` → ID card tab).
|
||||
|
||||
| Item | Spec |
|
||||
|------|------|
|
||||
| QR payload | `gishen://member/{member_id}?v={verification_id}` |
|
||||
| Card fields | Photo, name, employee ID, `card_id`, package, status, org |
|
||||
| Admin responsibility | Implement scanner → resolve member → covered checkout (with Ecom entitlement) |
|
||||
| Clinical | Card has **no** Rx or medicine data |
|
||||
|
||||
See [`../backend/endpoints/members.md`](../backend/endpoints/members.md) and [`../backend/OVERVIEW.md`](../backend/OVERVIEW.md#pharmacy-id-card-platform-contract).
|
||||
|
||||
## Clinical withhold alignment
|
||||
|
||||
Admin pharmacists see full Rx detail. B2B HR/Finance never receive clinical payloads — enforced in B2B API layer and documented in entity/endpoint specs.
|
||||
|
|
@ -64,6 +79,7 @@ SUPER_USER in B2B may view member Rx for support; does not replace Admin review
|
|||
- [ ] Pharmacist queue consumes `prescription.submitted`
|
||||
- [ ] Admin review emits `prescription.review_updated`
|
||||
- [ ] Statement PDF generation exposes URLs consumed by B2B `/statements`
|
||||
- [ ] POS / Admin scanner accepts `gishen://member/{id}?v={verification_id}` and resolves covered member
|
||||
|
||||
## Contact / escalation
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ Mock phase: static link + shared demo customer.
|
|||
| `GET /v1/customers/:id/allowance` | Ecom checkout, B2B `/me` |
|
||||
| `POST /v1/checkout/entitlement-preview` | Ecom cart |
|
||||
| `GET /v1/customers/:id/orders` | B2B `/me/orders` (member view) |
|
||||
| Resolve pharmacy QR `gishen://member/{id}?v={verification_id}` | Admin POS / Ecom staff checkout → same entitlement preview |
|
||||
|
||||
Document exact shapes in Ecom spec sheet; B2B references via this coordination doc.
|
||||
|
||||
|
|
@ -94,3 +95,4 @@ Telegram Mini App rides **Ecom's backend spec**, not B2B. No B2B action required
|
|||
- [ ] Checkout split UI implemented once in Ecom
|
||||
- [ ] Order events feed B2B finance aggregates (redacted)
|
||||
- [ ] Confirm SSO/deep-link from B2B member portal to Ecom
|
||||
- [ ] Staff / pharmacist flow accepts B2B pharmacy ID QR at checkout
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ All primary tables include in-card **search + filter** toolbar (`TableToolbar`):
|
|||
|
||||
## Detail UX
|
||||
|
||||
Yimaru-style `DetailHero` + `DetailSection` layouts with demo mutations (invite/offboard, package activate, migration commit/rollback, statement issue/pay, approval approve/flag). Dashboard uses Recharts placeholders from statements/spend/departments/packages.
|
||||
Yimaru-style `DetailHero` + `DetailSection` layouts with demo mutations (invite/offboard, package activate, migration commit/rollback, statement issue/pay, approval approve/flag). Dashboard uses Recharts placeholders from statements/spend/departments/packages. Member detail includes printable pharmacy ID card (`GET /v1/members/:id/id-card`).
|
||||
## Spec file paths
|
||||
|
||||
```
|
||||
|
|
|
|||
Reference in New Issue
Block a user