Shared TableToolbar and RowActions; add create pages for orders, Rx, orgs, stock, team, and procurement. Co-authored-by: Cursor <cursoragent@cursor.com>
221 lines
9.5 KiB
Markdown
221 lines
9.5 KiB
Markdown
# 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 |
|
|
| 2026-08-06 | Deployed Admin SPA: https://gishen-admin.vercel.app |
|
|
| 2026-08-06 | List UX: filters + create pages for orders, Rx, orgs, stock, team, procurement; `POST` create sketches below |
|
|
|
|
## 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; filters: `status`, `fulfillment`, `channel`, `q` |
|
|
| POST | `/pharmacy/orders` | pharmacist, operations, super_admin | Staff-created order (call centre / walk-in) |
|
|
| 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; filters: `status`, `controlled`, `q` |
|
|
| POST | `/pharmacy/prescriptions` | pharmacist, super_admin | Staff Rx intake |
|
|
| 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; filters: `branchId`, `q`, `alert=low\|mismatch` |
|
|
| POST | `/pharmacy/inventory` | stock_manager, super_admin | Add stock line |
|
|
| 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; list supports `role`, `branchScoped`, `q` |
|
|
| 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)
|