# Gishen — Cross-App Master Spec **Living collective contract** across Gishen client apps. Deeper API sketches live in each repo’s own backend sheet; this file is the **shared map** of roles, domain objects, auth providers, and known divergences. | Field | Value | | --- | --- | | **Home repo (this file)** | `Gishen-Admin` | | **Sibling root** | `/Users/kirukib/Desktop/Yaltopia Project/` | | **Shared API base (TBD)** | `https://api.gishen.example/v1` (Ecom/Mob/Admin) · B2B draft: `https://api.gishenpharmacy.org/v1` — **unify TBD** | | **Last reconciled** | 2026-08-07 | --- ## Changelog | Date | Change | | --- | --- | | 2026-08-07 | **Seed batch:** Created master spec from Admin living contract + sibling Mob/Ecom/B2B/Dispatch sheets. Documented product surface map, shared domain objects (auth, orgs, doctors, orders/guest, Rx dosing, catalogue multi-UOM + PDP, stock, loyalty), auth provider matrix, divergence & sync notes, deeper-doc links, Sources stamp, and pre-push update rule. Wired `.cursor/rules` + optional `.githooks/pre-push` reminder. Linked from Admin `README.md` and `admin-backend-spec.md`. | --- ## Update rule (before every push) This file is the **cross-app master**. Before `git push` from Gishen-Admin (humans **and** agents): 1. Diff Admin `src/` / `docs/admin-backend-spec.md` against the last Sources stamp below. 2. Skim sibling Mob / Ecom / B2B / Dispatch specs if those folders exist on disk. 3. Refresh relevant sections here; append a **Changelog** row (date + why). 4. If Admin endpoints/fields changed, also update [`admin-backend-spec.md`](./admin-backend-spec.md) in the same change set. 5. Update the **Sources stamp** (`Last reconciled` + checklist dates). Agents: the Cursor rule `.cursor/rules/update-master-spec-before-push.mdc` applies on push requests. Optional git hook: see [Pre-push tooling](#pre-push-tooling). --- ## Sources stamp Update these dates whenever you reconcile against a sibling. | Source | Path (sibling area) | Found | Spec / README | Last skimmed | | --- | --- | --- | --- | --- | | **Admin** | `Gishen-Admin/` | ✅ | [`docs/admin-backend-spec.md`](./admin-backend-spec.md) | 2026-08-07 | | **Mob** | `Gishen-Mob/` | ✅ | [`docs/BACKEND_SPEC.md`](../../Gishen-Mob/docs/BACKEND_SPEC.md) (relative from sibling root) | 2026-08-07 | | **Ecom** | `Gishen-Ecom/` | ✅ | [`docs/backend.md`](../../Gishen-Ecom/docs/backend.md) | 2026-08-07 | | **B2B** | `Gishen-B2B/` | ✅ | [`docs/backend/OVERVIEW.md`](../../Gishen-B2B/docs/backend/OVERVIEW.md) + coordination | 2026-08-07 | | **Dispatch Bot** | `Gishen-Dispatch-Bot/` | ✅ (related) | `docs/BACKEND_SPEC.md` | 2026-08-07 | | **Telegram Mini App** | `Gishen-Telegram-MiniApp/` | ⚠️ empty scaffold | — | 2026-08-07 | ### Sources not found _None for the four primary surfaces (Admin / Mob / Ecom / B2B)._ Related folders present but thin: `Gishen-Telegram-MiniApp` (empty), Dispatch Bot documented as rider surface. ### Pre-push checklist (human / agent) - [ ] Changelog row added if domain contract changed - [ ] Auth / orders / Rx / catalogue / org notes still match siblings (or divergences listed) - [ ] `Last reconciled` date bumped - [ ] `admin-backend-spec.md` updated if Admin modules changed - [ ] Reminder script run (optional): `bash scripts/sync-master-spec-reminder.sh` --- ## 1. Product surface map One shared pharmacy platform; **four primary clients** + rider bot. | App | Repo | Who | Primary jobs | | --- | --- | --- | --- | | **Admin** | `Gishen-Admin` | Pharmacy staff: `pharmacist`, `stock_manager`, `procurement`, `finance`, `marketing_manager`, `operations`, `super_admin` | Branch Rx queue, orders/POS, stock, procurement, org commercial activation, CRM/loyalty config, dispatch board, team/audit, catalogue master, doctor roster | | **Mob** | `Gishen-Mob` | Retail customer (Expo) | Catalog/PDP, cart/checkout, guest pharmacist codes, Rx Care (scan/reminders), loyalty, family, subscriptions, B2B join/contexts, tracking | | **Ecom** | `Gishen-Ecom` | Retail customer (Next.js PWA) | Storefront catalog, Rx upload/check, cart → order request, profile/favorites/loyalty, branches; v1 confirmation often phone/WhatsApp | | **B2B** | `Gishen-B2B` | Institutional: `SUPER_USER`, `HR_ADMIN`, `FINANCE`, `MEMBER` | Org self-register; HR members/packages/migration; Finance statements/approvals; Member allowance + own Rx (**clinical withhold** for HR/Finance) | | **Dispatch Bot** | `Gishen-Dispatch-Bot` | Riders/drivers | Telegram-enrolled trip status + POD; no separate rider login app for v1 | | **Telegram Mini App** | `Gishen-Telegram-MiniApp` | Customers (planned) | Placeholder — Ecom backend mentions Telegram auth/bootstrap; **TBD** | ### Who creates what | Artefact | Created by | Verified / fulfilled by | | --- | --- | --- | | Retail order (registered) | Customer (Mob/Ecom) | Pharmacist / Ops (Admin) | | Guest order / pharmacist code (`GSH-XXXX`) | Guest/customer Mob/Ecom; staff/doctor Admin | Pharmacist Admin (`by-code` / POS) | | Prescription intake (customer scan) | Mob/Ecom/B2B member | Pharmacist Admin | | Doctor-authored Rx / clinical order | Doctor (hospital org) | Pharmacist Admin | | Catalogue Item master | Stock/super_admin Admin | Consumed by Mob/Ecom as `/catalog/products` | | Org commercial terms | Admin Finance | B2B unlocks on `org.activated` | | Trip assign | Ops Admin | Dispatch Bot rider | ### External actors (not Admin staff roles) | Actor | Auth surface | Notes | | --- | --- | --- | | **Doctor** | Separate doctor JWT (Admin sheet); hospital/clinic org affiliation | Create Rx & guest/registered patient orders; **not** a `StaffRole` | | **End customer (guest)** | None / Mob `POST /auth/guest` / Ecom guest order | Phone required for assisted/guest checkout; loyalty gated | | **B2B roles** | B2B portal session | Distinct from pharmacy staff; shared `customer_id` linkage TBD | --- ## 2. Shared domain objects Fields below are the **platform-level** vocabulary. Path prefixes and exact DTOs still differ per sheet — see [§4 Divergence](#4-divergence--sync-notes). ### 2.1 Users & auth providers | Concept | Shared idea | App notes | | --- | --- | --- | | Platform person | UUID user / customer | Staff vs customer vs doctor vs B2B user are **actors**, not one role enum everywhere | | Staff | Admin `StaffUser` + `StaffRole` + optional `branchId` | Bearer JWT | | Customer | Retail identity; loyalty points/tier | Mob `GET /users/me`; Ecom same idea | | Doctor | Hospital/clinic-affiliated clinician | Admin roster + `POST /auth/doctor/login` | | B2B session user | `roles[]`, `org_id`, `active_role` | Clinical withhold for HR/Finance | | `authProvider` | `email` \| `google` \| `phone` \| `telegram` (+ Mob `guest`) | Report on `/me` / login response | | Locale | `en` \| `am` | Admin `preferredLocale`; Mob/Ecom/B2B `locale` / Accept-Language | See [§3 Auth matrix](#3-auth-provider-matrix). ### 2.2 Organisations | Type (Admin) | Used for | | --- | --- | | `corporate` \| `ngo` \| `other` | B2B benefits clients — credit, packages, members | | `hospital` \| `clinic` | Doctor affiliation; clinical Rx/orders | **Lifecycle (B2B):** `pending_activation` → `active` → `suspended` \| `closed`. Admin writes commercial terms (`credit`, payment terms, `price_list_id`, contract dates) and emits activation events. B2B self-register UI → Admin approval queue. ### 2.3 Doctors - Roster under hospital/clinic orgs in Admin (`/doctors`, org detail). - Fields (Admin): `name`, `specialty`, `licenseNumber`, `phone`, `email`, `status`, `orgId`, … - Can create prescriptions and orders for **guest** or registered patients, org-scoped. - Cannot manage catalogue, RBAC, finance, other hospitals. - Identity provider strategy vs B2B hospital users: **open** (Admin Q8 / Q17). ### 2.4 Orders (registered + guest) | Field / idea | Meaning | | --- | --- | | `customerType` | Admin: `registered` \| `guest` on staff/doctor-created orders | | Guest identity | `customerName` + **required** `customerPhone`; no `customerId` | | Channels | Admin includes branch / POS / doctor / (ops); Mob/Ecom retail + guest codes | | Pharmacist code | Mob/Ecom: `GSH-XXXX` via `POST /orders/guest-codes`; Admin fulfill `GET/POST .../by-code/:code` | | Fulfillment | `pickup` \| `delivery` (+ branch / any-branch for guest codes) | | Payments | Mob: Chapa / Telebirr / M-Pesa for delivery; `pay_at_branch` pickup-only. Ecom v1 often offline confirm | **Guest limits (proposed):** valid phone; controlled SKUs still need approved Rx; no loyalty earn until claim/merge — **TBD** claim-by-OTP (Admin Q9–10). ### 2.5 Prescriptions + dosing schedule | Layer | Contract | | --- | --- | | Admin line item | `frequency?`: `QD`\|`BID`\|`TID`\|`QID`\|`QXH`\|`custom`; `intervalHours?` (QXH); `times?`: `HH:mm`[] | | Mob reminders | `POST /health/reminders` with `times[]`, grace, escalate; calendar UI; confirm/snooze | | Sync intent | Persist pharmacist final `times` (+ interval) on approve/dispense so Mob can seed reminders without re-derive — **when** to push still open (Admin Q13) | | Customer Rx Care | Mob/Ecom upload + `prescriptions/check` stock match statuses | | B2B | Member submits Rx; HR/Finance never see clinical lines | Admin helper: `src/lib/dosingSchedule.ts`. ### 2.6 Catalog / Items + multi-UOM + storefront PDP | Concept | Admin (source of truth for ops) | Mob / Ecom (retail read model) | | --- | --- | --- | | Master product | `/admin/catalog/items` — Item with SKU, classification, media, EFDA, … | `/catalog/products` (+ slug) | | Stock link | Inventory rows reference `itemId` | `/stock/product/:productId`, branches nearby | | Multi-UOM | `uoms[]` + `conversionFactor` into stock `baseUnit`; `toStockQty` / `fromStockQty` | **Not yet** on Mob product type — sell unit is display `unit` string only | | PDP fields | `storefrontUnit`, `useCase`, `compareAtPriceEtb`, side-effect lists, `directions[]`, `storageNotes[]`, `pharmacistTip` | Mob PDP: `product` + `getDrugFacts()` clinical overlay (`useCase`, form, dosages, mild/severe, storage, directions, advice) | | Pricing lists | B2B `price_list_id` on org commercial | Ecom/Mob show retail (and credit quote at checkout) | ### 2.7 Stock - Branch qty, batch/lot, expiry on **Stock**, not Item. - ERP sync / field ownership / merge proposals: Admin Stock module. - Mob/Ecom: availability APIs only (no warehouse write). ### 2.8 Loyalty | Surface | Role | | --- | --- | | Admin Marketing | Config, ledger, activities, CRM points adjust, liability KPI; POS earn | | Mob / Ecom | `GET /loyalty/me`, ledger, redeem; referrals (Ecom) | | Guests | No earn by default until registered claim | | Rx/Controlled | Catalogue auto-excludes loyalty eligibility (Admin) | | B2B | Pointer/summary only; retail owns full wallet UI | --- ## 3. Auth provider matrix | Provider | Admin (staff) | Mob (customer) | Ecom (customer) | B2B (portal) | Dispatch (rider) | Doctor | | --- | --- | --- | --- | --- | --- | --- | | **Google** | `POST /auth/staff/oauth/google` (ID token); mock UI | `POST /auth/oauth/google` | OTP-first sheet; Google **TBD** in older Ecom auth § | UI mock → demo session; `.../oauth/google/start` planned | — | TBD (Admin Q17) | | **Email** | Password `POST /auth/staff/login` | `POST /auth/email/login` (+ optional register) | Magic-link sketched | Magic/OTP planned; demo persona login today | — | Email/invite primary (`/auth/doctor/login`) | | **Phone OTP** | `phone/start` + `phone/verify` | `otp/request` + `otp/verify` (primary demo) | `otp/request` + `otp/verify` | SMS OTP planned | — | TBD | | **Telegram** | Login Widget → `POST /auth/staff/telegram` | `POST /auth/oauth/telegram` | Telegram Mini App `POST /telegram/auth` sketched | Widget/Mini App callback planned | **Telegram user id enrollment** (identity) | Not primary | | **Guest** | N/A (staff always auth); **guest customers** on orders | `POST /auth/guest` + guest-codes | Guest checkout by phone | N/A (org membership) | N/A | Creates guest **patients** | | **Biometric** | — | Bind device key (Mob) | — | — | — | — | Path naming **diverges** (`/auth/staff/*` vs `/auth/otp/*` vs `/v1/auth/*`) — consolidation TBD; shared `authProvider` enum values should stay aligned. Env (Admin SPA): `VITE_GOOGLE_CLIENT_ID`, `VITE_TELEGRAM_BOT_USERNAME` (empty = mock UX). Server-only: bot token, OTP gateway, Google audience. --- ## 4. Divergence & sync notes Accurate as of **2026-08-07** skim. Prefer fixing sheets over inventing a third truth here. | Topic | Divergence | Sync action | | --- | --- | --- | | API base host | Admin/Mob/Ecom use `api.gishen.example`; B2B OVERVIEW uses `api.gishenpharmacy.org` | Pick one production host | | Money | Admin demos major ETB; Ecom sheet says cents optional | Platform-wide decision (Admin open Q1) | | Auth path prefixes | Staff `/auth/staff/*`, Mob `/auth/otp/*`, B2B `/v1/auth/*` | Shared IdP façade TBD | | Guest orders | Admin: staff/doctor `customerType=guest`; Mob/Ecom: guest session + `GSH-XXXX` codes | Document both flows; Admin POS `by-code` must match Mob code shape | | Guest Admin endpoints | Mob lists `GET /admin/orders/by-code/:code` | Ensure Admin sheet mirrors when POS wire-up lands | | Rx dosing | Admin structured `frequency`/`times`; Mob reminders separate resource | On approve, map Admin times → `/health/reminders` seed | | Catalogue ID | Admin `Item` / `itemId`; retail `productId` / slug | Mapping table or shared SKU key | | Multi-UOM | Admin full `uoms[]`; Mob/Ecom display `unit` string only | Expose sales UOM on catalog DTO when POS/cart need conversion | | PDP clinical | Admin stores PDP fields on Item; Mob still derives much from `clinical.ts` overrides | Prefer catalogue PDP fields once API live; keep Mob overrides as fallback | | Loyalty paths | Aligned idea (`/loyalty/me`); Admin config under `/admin/loyalty/*` | OK | | Org types | Admin hospital/clinic for doctors + B2B commercial orgs | Keep `OrganisationType` shared | | Roles naming | Admin snake staff roles; B2B `HR_ADMIN` SCREAMING; Ecom older `pharmacy_staff` names | Map in backend claims; don't rename UI mid-flight without sheet sync | | Rider auth | Dispatch: Telegram enrollment; Ecom older Flutter rider JWT sketch **superseded** for v1 | Prefer Dispatch sheet | | Mini App | Ecom documents endpoints; Mini App folder empty | Implement or mark out of scope | | Doctor in B2B | Not a B2B portal role today | Open: doctor realm vs hospital org user | --- ## 5. Links to deeper docs ### Admin (this repo) | Doc | Purpose | | --- | --- | | [`docs/admin-backend-spec.md`](./admin-backend-spec.md) | Living Admin API/UI contract (primary depth) | | [`README.md`](../README.md) | Runbook, demo accounts, deploy | | `src/lib/dosingSchedule.ts` | Frequency → default times helpers | | `src/config/navigation.ts` | Page registry | ### Mob | Doc | Purpose | | --- | --- | | `Gishen-Mob/docs/BACKEND_SPEC.md` | Customer mobile API sheet | | `Gishen-Mob/README.md` | Expo run + demo OTP | | UI anchors | `app/(root)/auth/*`, `...(screens)/product/[id].tsx`, `prescription/*`, `checkout.tsx` | ### Ecom | Doc | Purpose | | --- | --- | | `Gishen-Ecom/docs/backend.md` | Storefront + platform API proposal | | `Gishen-Ecom/README.md` | Product proposal / demo | | `Gishen-Ecom/docs/README.md` | Docs index | ### B2B | Doc | Purpose | | --- | --- | | `Gishen-B2B/docs/README.md` | Standing rule + layout | | `Gishen-B2B/docs/backend/OVERVIEW.md` | Tenancy, RBAC, clinical withhold | | `Gishen-B2B/docs/backend/entities/` · `endpoints/` | Model + REST | | `Gishen-B2B/docs/coordination/gishen-admin.md` | Admin ↔ B2B events | | `Gishen-B2B/docs/coordination/gishen-ecom.md` | Checkout entitlement split | | `Gishen-B2B/docs/coordination/open-items.md` | Cross-repo decisions | | `Gishen-B2B/docs/features/INDEX.md` | Feature → pages map | ### Related | Doc | Purpose | | --- | --- | | `Gishen-Dispatch-Bot/docs/BACKEND_SPEC.md` | Rider Telegram events / trips | | `Gishen-Dispatch-Bot/docs/FEATURE_BRIEF.md` | Acceptance inventory | --- ## 6. Cross-cutting open items (rolled up) Do not invent APIs here — track decisions: 1. Unify API host + money units (cents vs major ETB). 2. Shared auth façade for Google / Email / Phone / Telegram across actors. 3. Guest order claim by phone OTP + PII retention. 4. Checkout entitlement-split owner (B2B recommends Ecom + shared calc). 5. Doctor IdP (dedicated realm vs hospital B2B user). 6. Multi-UOM ownership ERP vs Platform. 7. When Rx `times` fan out to Mob reminders. 8. Telegram Mini App scope vs empty repo. 9. Admin ↔ Mob `by-code` / POS scan contract end-to-end. Admin-local list: [`admin-backend-spec.md` § Open questions](./admin-backend-spec.md#open-questions). B2B cross-repo: `Gishen-B2B/docs/coordination/open-items.md`. --- ## Pre-push tooling Committed in **Gishen-Admin** only: | Piece | Path | Behavior | | --- | --- | --- | | Cursor rule | `.cursor/rules/update-master-spec-before-push.mdc` | Agents must refresh this master (and Admin sheet if needed) before push | | Reminder script | `scripts/sync-master-spec-reminder.sh` | Prints Sources checklist + whether master looks stale vs `src/` / Admin sheet | | Git hook | `.githooks/pre-push` | **Warns loudly** if significant paths changed since last push while master was not updated; does **not** call LLMs. Soft warn by default (`GISHEN_MASTER_SPEC_STRICT=1` to exit 1) | | Enable hooks | `scripts/setup-githooks.sh` | Sets **local** `core.hooksPath=.githooks` for this repo only | ```bash # One-time per clone (optional but recommended) bash scripts/setup-githooks.sh # Manual check anytime bash scripts/sync-master-spec-reminder.sh ``` Do **not** silently rewrite global git config. Local `core.hooksPath` for this repository is intentional and reversible (`git config --unset core.hooksPath`).