Disease Coding (5.3) Overview
Thai-MoPH section 5.3 disease-coding system overview and landing page.
Spec source: Thai-MoPH HIS specification §5.3 ระบบงานให้รหัสโรค Branch:
claude/disease-coding-landing-6Z3BSCompanion docs:disease-coding-01-frontend-plan.md,-02-backend-plan.md,-03-data-and-master-data.md,-04-gap-analysis-and-rollout.md
1. What §5.3 asks for
The MoPH spec defines a medical coder workflow that runs after a visit is clinically finished and the bill is closed. The 9 atomic requirements:
| # | Requirement (TH → EN précis) |
|---|---|
| 1 | Search completed-and-paid patients by HN, name, surname, scheme, date, payment status |
| 2 | Show scheme, clinic, sub-clinic, attending doctor, ICD-10, ICD-9-CM per Visit |
| 3 | Coding helper: SNOMED + LOINC → suggested ICD-9-CM / ICD-10 |
| 4 | Discharge Summary form (MoPH format): diagnoses, procedures, attending, Discharge Status / Discharge Type, before discharge |
| 5 | Record ICD-10 + diagnosis status + diagnosing doctor |
| 6 | Record ICD-9-CM + status + procedure doctor + in-OR / out-OR flag |
| 7 | View patient treatment history |
| 8 | CRUD: 21 disease groups, 75 disease groups, Organ, Discharge Status, Discharge Type |
| 9 | ICD-10 master setup: import/update code, name, search-name, name-TH, disease group, Organ |
2. The repo already has most of the runway
This is not greenfield. medOS-ultra already ships a substantial coder stack:
| Layer | Existing artefact |
|---|---|
| Page | web/src/containers/medical-coder/page.tsx (5 tabs: Dashboard, Worklist, Validation, AI, Quality) wired at /medical-coder (web/src/routes.tsx:263) |
| Dialog | DialogRecordCoder.tsx (1,519 LOC), DialogRecordCoderAI.tsx (1,743 LOC) |
| Frontend service | web/src/services/ever-administration/coding.service.ts (~1,000 LOC; Supabase reads, BRE, AI assistant, Gold Layer) |
| Frontend service | web/src/services/ever-administration/coder.service.ts (NestJS REST CRUD) |
| Master-data services | web/src/services/ever-diagnosis/{icd10,icd9,diseaseGroup,organ,dischargeStatus,dischargeType,diagnosis}.service.ts |
| Backend NestJS | services/administration/.../modules/coder/ (encounter-bound coder record + emit CODING_COMPLETED) |
| Backend NestJS | services/diagnostic/.../modules/{icd10,icd9,diseaseGroup,organ,dischargeStatus,dischargeType,diagnosis,diagnosisTemplate}/ |
| DB read-model | infrastructure/medbase/migrations/015_coding_worklist.sql (coding_worklist, coding_dashboard_summary, coding_ai_suggestion_log) |
| DB Gold layer | infrastructure/medbase/migrations/016_coding_gold_layer.sql (gold_coding_fact, _monthly_kpi, _coder_kpi, _ai_effectiveness, fn_coding_quality_risk) |
| Edge functions | infrastructure/medbase/functions/coding-rules-engine (Deno BRE), infrastructure/medbase/functions/coding-ai-assistant (Deno LLM), web/supabase/functions/claim-coder |
| Workflow | web/public/workflows/coding-workflow.json + web/packages/medical-kit/src/medical-worklist/defaults/medical-coder-workflow.json |
| Discharge UI | web/packages/miniapps/discharge/{Discharge,DischargePartOne,DischargeOrderForm}.tsx |
The 9 requirements map onto this stack as follows:
| Req | Status | Where it lives / what’s missing |
|---|---|---|
| 1 | 🟡 Partial | CodingWorklistPanel filters by coding_status + encounter_class + date-range but no payment-status / scheme filter. coding_worklist has scheme_code but no payment_status column. Update: payment status is already populated in encounter_journey_cache.financial_summary.status by handlePaymentCompleted() in the orchestrator (manifest.payment.completed event). The fix is a LEFT JOIN at query time, not a schema change — see doc 02 §2.5 and the landing-page implementation in web/src/containers/medical-coder/landing/page.tsx. |
| 2 | 🟡 Partial | Read-model has clinic/sub-clinic/doctor/ICD counts but the per-visit detail row in the worklist hides ICD-9-CM and sub-clinic. |
| 3 | 🟡 Partial | coding-ai-assistant LLM exists; useSnowstorm.tsx exists but partial; no LOINC service, no deterministic SNOMED→ICD lookup UI. |
| 4 | 🔴 Missing | Maternal MaternalDischargeSummary.tsx exists; no general MoPH-format Discharge Summary that consolidates dx/procedures/dischargeStatus/dischargeType. DischargePartOne.tsx covers part of it but isn’t the spec form. |
| 5 | 🟢 Done | coder.service.ts writes ICD-10 + diagnosing doctor via v2/administration/coders. |
| 6 | 🟡 Partial | ICD-9-CM captured via operatingRoomRequest pre/post-OP arrays. No explicit inOR/outOR boolean on procedureRequest. |
| 7 | 🟡 Partial | /patient-profile/:id?from=medical-coder opens full patient timeline; no inline coder-context history panel. |
| 8 | 🔴 Frontend admin missing | Backend modules exist; no admin pages under web/src/containers/admin/ for diseaseGroup, organ, dischargeStatus, dischargeType. 21-group / 75-group classifier fields not modeled (see doc 03). |
| 9 | 🔴 Frontend admin missing | Backend Icd10 DTO already has code, name, nameTH, keyword[], diseaseGroupRef[], organRef, snomed[]. No frontend ICD-10 setup / import / bulk-update screen. |
3. The “Landing Page” concept
§5.3 doesn’t ask for one page — it asks for one system. The right deliverable is a Disease Coding hub at /medical-coder (which today opens straight onto the worklist) restructured into a landing → workspace → settings triad:
/medical-coder ← Landing (hub) — NEW
├─ /medical-coder/worklist ← existing CodingWorklistPanel (becomes deep link)
├─ /medical-coder/encounter/:id ← existing DialogRecordCoder, promoted to a route
├─ /medical-coder/dashboard ← existing CodingDashboardPanel
├─ /medical-coder/validation ← existing CodingValidationPanel
├─ /medical-coder/ai ← existing CodingAiPanel
├─ /medical-coder/quality ← existing CodingQualityPanel
└─ /medical-coder/setup/ ← Master-data hub — NEW
├─ icd10 ← req #9
├─ icd9
├─ disease-groups (21 / 75) ← req #8
├─ organs ← req #8
├─ discharge-status ← req #8
├─ discharge-type ← req #8
└─ snomed-loinc-mapping ← req #3
The landing itself is a Bento grid (using @design-kit/bento, see web/CLAUDE.md Bento section) with cards that:
- Surface live counts from
coding_dashboard_summary(Awaiting / In Progress / Completed / Validation Failed / Aging > 7 days) - Cross-link to every related system the coder needs to enter from one screen:
- 📋 Worklist (default click → opens worklist tab; the FAB-floater stays available everywhere via
QueueManagementFloater deptType="medical_coder") - 🧾 Billing & Payment status →
/cashierfiltered by encounter (read-only check) — needed for req #1 - 🏥 Patient profile / treatment history →
/patient-profile/:id?from=medical-coder— req #7 - 📝 Discharge Summary →
/medical-coder/encounter/:id/discharge-summary(new) — req #4 - 🤖 AI / Rules Engine → existing CodingAi + CodingValidation tabs — req #3
- ⚙️ Setup → master-data hub — req #8, #9
- 📋 Worklist (default click → opens worklist tab; the FAB-floater stays available everywhere via
- Show top 5 alerts from
rcm_financial_alert(BRE blocks/warns) so the coder can hot-jump into the failing encounter.
This matches the “hub” pattern already used by medication-analytics and appstore. Layout reference: web/src/containers/queue-billing/page.tsx (Bento + filters + table + side-actions).
4. Why split into 4-5 docs
The work is too broad to land in a single PR. The split:
| Doc | Scope | Owners |
|---|---|---|
00-overview.md (this) |
Spec mapping, landing-page concept, single source of “what we already have” | Architect |
01-frontend-plan.md |
Landing page bento, route splits, sub-pages, shared components, props contracts | Frontend |
02-backend-plan.md |
Coder API extensions, procedureRequest in-OR/out-OR, payment-status join, BRE rules, edge functions | Backend |
03-data-and-master-data.md |
DB schema diffs (21/75 group classifier, payment_status column on coding_worklist, master tables), seed strategy per market pack | Data / DBA |
04-gap-analysis-and-rollout.md |
Per-requirement gap table → chunks → ordered ticket list with file paths | PM |
Each doc is small enough to review independently and to ship in a 1–2 day chunk.
5. Critical rules carried into every doc
From web/CLAUDE.md and CLAUDE.md:
- Backend is write-truth, Supabase is read-model. Never write to
coding_worklistfrom the frontend; only theclaim-coderedge function projects fromhospital_events. - Bilingual seed data (Thai + English) for every master-table row (21/75 group, Organ, Discharge Status, Discharge Type).
- Don’t touch
vite.config.ts/tsconfig.json/ build configs. - Don’t bulk-rewrite existing files — extend, don’t replace
DialogRecordCoder.tsx. - Dual data-source IDs — Supabase rows use
id/encounter_id; REST uses_id/encounter. Every new component must read both. - Match the Bento design system for the landing — see
packages/design-kit/src/bento/.