medOS ultra

Disease Coding (5.3) Overview

Thai-MoPH section 5.3 disease-coding system overview and landing page.

6 min read diagramsUpdated 2026-05-01docs/architecture/disease-coding-00-overview.md

Spec source: Thai-MoPH HIS specification §5.3 ระบบงานให้รหัสโรค Branch: claude/disease-coding-landing-6Z3BS Companion 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:

  1. Surface live counts from coding_dashboard_summary (Awaiting / In Progress / Completed / Validation Failed / Aging > 7 days)
  2. 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/cashier filtered 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
  3. 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:

  1. Backend is write-truth, Supabase is read-model. Never write to coding_worklist from the frontend; only the claim-coder edge function projects from hospital_events.
  2. Bilingual seed data (Thai + English) for every master-table row (21/75 group, Organ, Discharge Status, Discharge Type).
  3. Don’t touch vite.config.ts / tsconfig.json / build configs.
  4. Don’t bulk-rewrite existing files — extend, don’t replace DialogRecordCoder.tsx.
  5. Dual data-source IDs — Supabase rows use id / encounter_id; REST uses _id / encounter. Every new component must read both.
  6. Match the Bento design system for the landing — see packages/design-kit/src/bento/.
Ask Anything