diff --git a/docs/backend/OVERVIEW.md b/docs/backend/OVERVIEW.md index fc1f82e..1a07895 100644 --- a/docs/backend/OVERVIEW.md +++ b/docs/backend/OVERVIEW.md @@ -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: diff --git a/docs/backend/endpoints/README.md b/docs/backend/endpoints/README.md index ed12c27..c87ca17 100644 --- a/docs/backend/endpoints/README.md +++ b/docs/backend/endpoints/README.md @@ -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 | diff --git a/docs/backend/endpoints/auth.md b/docs/backend/endpoints/auth.md index 73fd475..cf2f327 100644 --- a/docs/backend/endpoints/auth.md +++ b/docs/backend/endpoints/auth.md @@ -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. diff --git a/docs/backend/endpoints/finance.md b/docs/backend/endpoints/finance.md index a95b68e..a5968d5 100644 --- a/docs/backend/endpoints/finance.md +++ b/docs/backend/endpoints/finance.md @@ -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 diff --git a/docs/backend/endpoints/members.md b/docs/backend/endpoints/members.md index 8a98145..240a99a 100644 --- a/docs/backend/endpoints/members.md +++ b/docs/backend/endpoints/members.md @@ -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` | diff --git a/docs/backend/entities/README.md b/docs/backend/entities/README.md index 264e2db..7596cc6 100644 --- a/docs/backend/entities/README.md +++ b/docs/backend/entities/README.md @@ -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 | diff --git a/docs/backend/entities/member.md b/docs/backend/entities/member.md index ea1ae88..220b8a3 100644 --- a/docs/backend/entities/member.md +++ b/docs/backend/entities/member.md @@ -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) diff --git a/docs/backend/entities/session-user.md b/docs/backend/entities/session-user.md index c69107d..70cc8f7 100644 --- a/docs/backend/entities/session-user.md +++ b/docs/backend/entities/session-user.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` diff --git a/docs/coordination/gishen-admin.md b/docs/coordination/gishen-admin.md index 91a2b87..bc24a0a 100644 --- a/docs/coordination/gishen-admin.md +++ b/docs/coordination/gishen-admin.md @@ -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 diff --git a/docs/coordination/gishen-ecom.md b/docs/coordination/gishen-ecom.md index 1cd2519..3a7e0a5 100644 --- a/docs/coordination/gishen-ecom.md +++ b/docs/coordination/gishen-ecom.md @@ -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 diff --git a/docs/features/INDEX.md b/docs/features/INDEX.md index c7579d0..20c2bac 100644 --- a/docs/features/INDEX.md +++ b/docs/features/INDEX.md @@ -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 ```