# Gishen Admin — Backend Spec Sheet **Repo:** Gishen-Admin **Audience:** Shared backend team (consolidate with Ecom / Mobile / B2B sheets later) **Base URL (TBD):** `https://api.gishen.example/v1` **Auth:** Bearer JWT for staff roles **Mocks:** Admin SPA uses in-memory mocks until `VITE_USE_MOCKS=false` and `VITE_API_BASE_URL` point here. ## Changelog | Date | Change | | --- | --- | | 2026-08-06 | Scaffold: conventions, roles, auth, empty module sections | | 2026-08-06 | Shell + RBAC + preferredLocale | | 2026-08-06 | Pharmacist: prescriptions, branch orders | | 2026-08-06 | Stock + Procurement | | 2026-08-06 | Finance, orgs create + pending self-reg approval | | 2026-08-06 | Marketing CRM / loyalty / campaigns | | 2026-08-06 | Dispatch board + Telegram bot events | | 2026-08-06 | Super Admin team/inbox/audit/settings + FAQ bilingual | | 2026-08-06 | Data migration import jobs | ## Conventions | Item | Convention | | --- | --- | | Content type | `application/json` (multipart for imports / uploads) | | IDs | UUID v4 | | Money | Integer ETB (major units for Admin UI demos; align platform-wide later) | | Errors | `{ "error": { "code": string, "message": string, "details"?: object } }` | | Pagination | `?page=&limit=` → `{ data, meta: { page, limit, total } }` | ## Staff roles `pharmacist` · `stock_manager` · `procurement` · `finance` · `marketing_manager` · `operations` · `super_admin` Pharmacist (and optionally stock) sessions include `branchId`. --- ## Auth & me | Method | Path | Roles | Description | | --- | --- | --- | --- | | POST | `/auth/staff/login` | — | Email/password → tokens + user | | POST | `/auth/refresh` | refresh | Rotate access | | POST | `/auth/logout` | staff | Revoke | | GET | `/auth/me` | staff | Current principal, roles, branchId | | PATCH | `/users/me` | staff | `{ preferredLocale?: "en"\|"am", name? }` | **Login response sketch:** ```json { "accessToken": "...", "refreshToken": "...", "user": { "id": "uuid", "name": "Hana Pharmacist", "email": "pharmacist@gishen.et", "role": "pharmacist", "branchId": "uuid", "preferredLocale": "en" } } ``` Mock key: `authStore.login` → `/auth/staff/login` --- ## Orders (branch inbox) | Method | Path | Roles | Description | | --- | --- | --- | --- | | GET | `/pharmacy/orders` | pharmacist, operations, super_admin | `?branchId=` required for pharmacist | | POST | `/orders/:id/confirm` | pharmacist, super_admin | Confirm stock | | POST | `/orders/:id/assign-rider` | pharmacist, operations, super_admin | `{ riderId }` | | PATCH | `/orders/:id/status` | pharmacist, operations, super_admin | Status lifecycle | Mock: `mocks/data.orders` --- ## Prescriptions | Method | Path | Roles | Description | | --- | --- | --- | --- | | GET | `/pharmacy/prescriptions` | pharmacist, super_admin | Branch-scoped queue | | POST | `/admin/prescriptions/:id/verify` | pharmacist, super_admin | `{ status: approved\|queried\|rejected, qtyAdjustments?, substitute?, notes? }` | | POST | `/pharmacy/prescriptions/:id/dispense` | pharmacist | `{ batch, quantity }` stamped pharmacistId + time | | GET | `/pharmacy/prescriptions/controlled-report` | pharmacist, super_admin | Controlled-substance report | | GET | `/pharmacy/refills` | pharmacist, super_admin | Days-of-supply / script expiry oversight | --- ## Stock & ERP | Method | Path | Roles | Description | | --- | --- | --- | --- | | GET | `/pharmacy/inventory` | stock_manager, pharmacist (read), procurement (read), super_admin | Per-branch stock | | GET | `/integrations/erp/sync-status` | stock_manager, super_admin | Last sync | | GET | `/admin/stock/discrepancies` | stock_manager, super_admin | Platform vs ERP | | POST | `/admin/stock/discrepancies/:id/resolve` | stock_manager, super_admin | One-click resolve | | GET/PUT | `/admin/stock/field-ownership` | stock_manager, super_admin | ERP vs platform field owners | | POST | `/admin/stock/entry/suggest` | stock_manager, super_admin | Fuzzy duplicate suggestions body `{ query }` | | GET | `/admin/stock/merge-proposals` | stock_manager, super_admin | Merge queue | | POST | `/admin/stock/merge-proposals/:id/approve` | stock_manager, super_admin | Human approve; reversible 30d | | GET | `/admin/stock/alerts` | stock_manager, super_admin | Low stock / expiry watchlist | | POST | `/admin/stock/transfers` | stock_manager, super_admin | Create/approve transfer (owner) | --- ## Procurement | Method | Path | Roles | Description | | --- | --- | --- | --- | | GET | `/admin/procurement/reports` | procurement, stock_manager, super_admin | Dead-stock / fast-mover | | GET | `/admin/procurement/reorder-recommendations` | procurement, super_admin | From velocity | | POST | `/admin/stock/transfers/request` | procurement, super_admin | Request only; Stock Manager approves | --- ## Finance & organisations | Method | Path | Roles | Description | | --- | --- | --- | --- | | GET | `/admin/finance/settlements` | finance, super_admin | Daily by channel | | GET | `/admin/finance/reconciliations` | finance, super_admin | Matched to orders | | GET/PUT | `/admin/finance/gateways` | finance, super_admin | Enable/disable Chapa, ArifPay, Telebirr, M-Pesa | | POST | `/admin/finance/refunds` | finance, super_admin | Refund handling | | GET | `/admin/finance/loyalty-liability` | finance, super_admin | Monthly Birr liability | | GET | `/admin/organisations` | finance, super_admin | List | | POST | `/admin/organisations` | finance, super_admin | **Create org from Admin** (active) | | GET | `/admin/organisations/pending` | finance, super_admin | Self-register queue | | POST | `/admin/organisations/:id/approve` | finance, super_admin | Activate credit | | POST | `/admin/organisations/:id/reject` | finance, super_admin | Reject | | GET | `/admin/organisations/:id/statements` | finance, super_admin | Consolidated invoices | **Cross-repo:** B2B portal `POST /company/register` creates `status=pending`. Admin approve activates credit. --- ## Marketing | Method | Path | Roles | Description | | --- | --- | --- | --- | | GET | `/admin/crm/customers` | marketing_manager, super_admin | Read profiles | | GET | `/admin/crm/segments` | marketing_manager, super_admin | Self-updating segments | | GET/POST | `/admin/campaigns` | marketing_manager, super_admin | Compose/schedule SMS/Telegram/push | | GET/PUT | `/admin/loyalty/config` | marketing_manager, super_admin | Earn/tiers/referral bonus | | GET | `/admin/kpi/summary` | marketing_manager, finance, super_admin | Scoped KPIs | --- ## Dispatch & Telegram bot | Method | Path | Roles | Description | | --- | --- | --- | --- | | GET | `/dispatch/board` | operations, super_admin | Paid + Rx-approved queue | | POST | `/dispatch/trips` | operations, super_admin | Batch fulfilments | | POST | `/dispatch/trips/:id/assign` | operations, super_admin | `{ riderId }` → emits Telegram manifest | | POST | `/dispatch/bot/webhook` | service | Bot status + PoD callbacks | | GET | `/dispatch/intake` | operations, super_admin | Partner/POS webhook monitor | | POST | `/dispatch/riders` | operations, super_admin | Link Telegram account | **Events:** `trip.assigned` → bot push; `trip.status_updated` → board + customer tracking. --- ## FAQ (bilingual) | Method | Path | Roles | Description | | --- | --- | --- | --- | | GET | `/admin/faq` | all staff | Published articles | | POST | `/admin/faq` | marketing_manager, super_admin | Create | | PUT | `/admin/faq/:id` | marketing_manager, super_admin | Update / publish | | DELETE | `/admin/faq/:id` | super_admin | Delete | Fields: `title_en`, `title_am`, `body_en`, `body_am`, `category`, `published`. --- ## Team, inbox, audit, settings | Method | Path | Roles | Description | | --- | --- | --- | --- | | GET/POST | `/admin/users` | super_admin | Staff + role + branch | | GET | `/admin/inbox/threads` | super_admin | Chatwoot/Telegram/WhatsApp attached to customer | | GET | `/admin/audit` | super_admin | Audit log | | GET/PUT | `/admin/settings` | super_admin | Retention, access controls | --- ## Data migration / imports | Method | Path | Roles | Description | | --- | --- | --- | --- | | GET | `/admin/imports/templates` | stock_manager, finance, operations, super_admin | Entity schemas | | POST | `/admin/imports` | same | Multipart file upload | | POST | `/admin/imports/:id/map` | same | Column mapping (+ save template) | | POST | `/admin/imports/:id/validate` | same | Dry-run row errors | | POST | `/admin/imports/:id/commit` | same | Upsert commit | | GET | `/admin/imports` | same | Job history | | CRUD | `/admin/imports/mapping-templates` | same | Saved presets | Entity templates: `stock`, `catalog`, `hr_members`, `branches`, `riders`, `generic`. --- ## Open questions 1. Money units: ETB major vs cents — keep consistent with Ecom/B2B sheets 2. Driver channel assumption: Telegram bot (flag if SMS/WhatsApp preferred) 3. Checkout entitlement-split: shared component across Ecom/Mobile/B2B (out of Admin UI)