medOS ultra

IPD Admission & Discharge Pipeline

Workflow architecture for the inpatient admission and discharge pipeline.

8 min read diagramsUpdated 2026-04-13docs/IPD_ADMISSION_DISCHARGE_WORKFLOW_ARCHITECTURE.md

Status: ACTIVE Date: 2026-04-08 Scope: Two linked manifest workflows for inpatient encounters: admission request queue + 7-phase discharge pipeline (doctor → nurse → ปิดบัญชี → coder → validation → ส่งเบิก → ออกใบเรียกเก็บ).


1. Why Two Workflows, Not One

IPD is fundamentally different from OPD/ER:

Aspect OPD/ER IPD
Duration Minutes/hours Days/weeks
Entities 1 (Encounter) 3 (Encounter + Admission + Bed)
Discharge 1 action 7-phase pipeline crossing 5 departments
View pattern Tab-based worklist (queue) Ward bed board (spatial) + discharge pipeline (queue)

What fits manifest: The admission request queue and the discharge pipeline — both are sequential, multi-step, tab-based workflows.

What does NOT fit manifest: The ward bed board (spatial UI, not a queue) and nurse daily tasks (task dashboard, not sequential). These stay in adt-kit.


2. The Three-Entity Model

┌─────────────────────────────────────────────────────────────────────┐
│  ENTITY 1: Encounter (encounterClass: IMP)                        │
│  PLANNED → ARRIVED → IN_PROGRESS → FINISHED → DISCHARGED          │
│                                                                     │
│  Key fields:                                                        │
│    status: EncounterStatus                                          │
│    coderStatus: CoderStatus (post-discharge coding)                 │
│    participants[]: EncounterParticipant[] (ADM/CON/ATND)           │
│    dischargedAt, dischargedBy                                       │
├─────────────────────────────────────────────────────────────────────┤
│  ENTITY 2: Admission (linked via encounterIpd → Encounter._id)    │
│  REQUEST → PENDING → ADMITTED → DISCHARGED                        │
│                                                                     │
│  Key fields:                                                        │
│    admissionStatus: AdmissionStatus                                 │
│    approvalStatus: ApprovalStatus (pending/reserved/receiveOrders)  │
│    transferStatus: TransferStatus (pending/success)                 │
│    encounterID: Ref<Encounter> (source OPD/ER encounter)           │
│    encounterIpd: Ref<Encounter> (created IPD encounter)            │
│    bedNumber: Ref<Bed>                                              │
│    ward: Ref<SubClinic>                                             │
│    dischargeDisposition, dischargePatientStatus                     │
├─────────────────────────────────────────────────────────────────────┤
│  ENTITY 3: Bed (linked via admission.bedNumber ↔ bed.admissionID) │
│  SELECT_BED → REGIS_BED → HAVE_PATIENT → EMPTY_BED/WAIT_CLEAN    │
│                                                                     │
│  Key fields:                                                        │
│    status: BedStatus                                                │
│    admissionID: Ref<Admission> (back-reference)                    │
│    ward: Ref<SubClinic>                                             │
│    room: Ref<Location>                                              │
└─────────────────────────────────────────────────────────────────────┘

Entity Linkages

Admission.encounterIpd  → Encounter._id     (forward)
Admission.bedNumber     → Bed._id           (forward)
Bed.admissionID         → Admission._id     (back-reference)
Admission.encounterID   → Source OPD/ER Encounter._id (origin)

3. Workflow 1: Admission Request Pipeline

State Machine

ipd-pending-bed  →  ipd-bed-reserved  →  ipd-ready-to-admit  →  ipd-admitted
       ↓                    ↓
   ipd-cancelled       ipd-cancelled

Mapping to Legacy approvalStatus

Workflow Node Admission.approvalStatus Legacy Tab
ipd-pending-bed pending Tab 0: รอจองเตียง
ipd-bed-reserved reserved Tab 1: จองเตียงสำเร็จ
ipd-ready-to-admit receiveOrders / receivePendingAdmit Tab 2: รอแอดมิท
ipd-admitted admitted (admissionStatus) Admitted
ipd-cancelled cancel / cancelRequest (admissionStatus) Tab 3: ยกเลิก

Actions per Node

Node Action API Endpoint
ipd-pending-bed Reserve Bed PUT /v2/administration/admissions/:id
ipd-pending-bed Cancel PUT /v2/administration/admissions/:id (cancelRequest)
ipd-bed-reserved Assign Bed PUT /v2/administration/admissions/:id/acknowledgeadmission
ipd-ready-to-admit Admit Patient PUT /v2/administration/admissions/:id (admitted)

What Happens at Admission

When admissionStatus changes to admitted (in admission.service.ts:updateAdmission):

  1. New IPD Encounter created via encounterService.createEncounter() with encounterClass: IMP
  2. Admission linked to new encounter via encounterIpd
  3. Bed status updated to PENDING
  4. Vital signs copied from source OPD/ER encounter to new IPD encounter
  5. AN (Admission Number) generated via generateAssignmentNumber()

Existing Frontend (LIVE)

Route Component Status
/admission/list-patient-request-admission PatientListRequestAdmission.tsx LIVE (REST + Socket.IO)
/admission/dashboard Admission Dashboard LIVE
/admission/wardview Ward Bed Board LIVE
/in-patient/list-patient-request-admission Nurse IPD variant LIVE

All 4 tabs are already working on GET /v2/administration/admissions/listAdmissionPatient with Socket.IO realtime updates.


4. Workflow 2: Discharge Pipeline

State Machine

                     ┌── Discharge Phase ──┐   ┌── Coder Phase ──────────────────────┐   ┌── Billing ──────────┐
ipd-in-treatment → ipd-doctor-discharged → ipd-nurse-discharged → ipd-account-closed → ipd-coder-in-progress
                                                                                              ↓           ↓
                                                                           ipd-coder-await-reconsider  ipd-coder-pending-validation
                                                                                    ↓                     ↓              ↓
                                                                              (back to coder)    ipd-coder-validated  ipd-coder-validation-failed
                                                                                                        ↓                   ↓
                                                                                                 ipd-claim-submitted   (back to coder)
                                                                                                        ↓
                                                                                                   ipd-invoiced

The 7 Phases Explained

Phase 1: Doctor Discharge

  • API: PUT /encounters/:id/discharge
  • What happens: encounter.status = FINISHED, coderStatus = AWAIT_REGISTER, each participant’s completedTreatment marked
  • For IMP with multiple participants: Same multi-doctor gate as EMER — all ADM+CON must complete

Phase 2: Nurse Discharge

  • API: PUT /encounters/:id/dischargeEncounterNurse
  • What happens: encounter.status = DISCHARGED, records note, cause, zone
  • Nurse adds: Discharge note, discharge cause, zone assignment

Phase 3: Account Close (ปิดบัญชี)

  • API: PUT /admissions/:id/dischargeAdmission
  • What happens:
    • admission.admissionStatus = DISCHARGED
    • bed.status = EMPTY_BED or WAIT_CLEAN
    • bed.admissionID cleared
    • DischargeDisposition recorded (HOME/HOSPICE/SNF/EXPIRED/etc.)
    • DischargePatientStatus recorded (FullRecovery/Improved/Deceased/etc.)
  • Known issue: No transaction wrapping 3 entity updates (race condition risk)

Phase 4: Medical Coder

  • API: PUT /encounters/:id/updateCoderStatus
  • What happens: Coder assigns ICD-10 diagnosis codes + ICD-9 procedure codes + DRG
  • CoderStatus flow: AWAIT_REGISTER → IN_PROGRESS → PENDING_VALIDATION
  • Frontend: /medical-coder?mainTab=ipd&encounterId=:id

Phase 5: Validation

  • What happens: 13 validation rules (C1-C13) run against codes
  • Pass: coderStatus = REGISTED (coded & validated)
  • Fail: coderStatus = VALIDATION_FAILED → back to coder
  • Reconsider: coderStatus = AWAIT_RECONSIDER → back to coder

Phase 6: Claim Submission (ส่งเบิก)

  • API: POST /v2/financial/claims/:encounterId/submit
  • What happens: Generate claim from coded encounter, submit to payer

Phase 7: Invoice (ออกใบเรียกเก็บ)

  • API: POST /v2/financial/invoices/:encounterId/generate
  • What happens: Revenue recorded, invoice generated

Selector Logic (How Rows Route to Nodes)

The discharge pipeline selectors use compound conditions because the same encounter.status value (e.g., finished) can mean different things depending on coderStatus and admission.admissionStatus:

Node Primary Field Secondary Field
ipd-in-treatment encounter.status == in-progress encounter.encounterClass == IMP
ipd-doctor-discharged encounter.status == finished coderStatus == Await Register
ipd-nurse-discharged encounter.status == discharged admission.admissionStatus != discharged
ipd-account-closed admission.admissionStatus == discharged coderStatus in [Await Register, Acknowledged]
ipd-coder-* coderStatus == <value>
ipd-claim-submitted claimStatus == submitted
ipd-invoiced claimStatus == invoiced

5. Manifest JSON Files

File Location Purpose
admission-request-workflow.json medbase/functions/ Backend authoritative (admission request)
discharge-pipeline-workflow.json medbase/functions/ Backend authoritative (discharge pipeline)
Same files packages/medical-kit/src/medical-worklist/defaults/ Frontend fallback copies

6. Status Enums Reference

AdmissionStatus

REQUEST, PENDING, ADMITTED, DISCHARGED, CANCELLED, CANCELREQUEST, APPOINTMENT, RECEIVEPENDINGADMIT

ApprovalStatus

PENDING, RESERVED, RECEIVEORDERS, CANCEL, RECEIVEPENDINGADMIT

BedStatus

EMPTY_BED, HAVE_PATIENT, HAVE_RESERVATION, REGIS_BED, PENDING, WAIT_CLEAN, CLOSED, SELECT_BED

CoderStatus

AWAIT_REGISTER, AWAIT_RECONSIDER, ACKNOWLEDGED, REGISTED, IN_PROGRESS, PENDING_VALIDATION, VALIDATION_FAILED

DischargeDisposition

HOME, ALTERNATIVE_HOME, OTHER_HEALTHCARE_FACILITY, HOSPICE, LONG_TERM_CARE,
LEFT_AGAINST_ADVICE, EXPIRED, PSYCHIATRIC_HOSPITAL, REHABILITATION, SKILLED_NURSING_FACILITY, OTHER

DischargePatientStatus

FullRecovery, Improved, FollowUpRequired, ConditionStable, ConditionWorsened, Deceased

7. What Stays Outside Manifest (adt-kit)

Feature Why Not Manifest Where It Lives
Ward bed board Spatial UI, not a queue adt-kit/admission/wardview/
Nurse IPD daily tasks Task dashboard, not sequential adt-kit/in-patient/ + nurse-ipd-dashboard/
Vital signs monitoring Real-time charting adt-kit/in-patient/vital-sign-view/
Medication cart/rounds Task-based, per-bed adt-kit + pharmacy-ipd
Progress notes Per-encounter SOAP ipd-progress-note/ (Supabase direct)

8. Orchestrator Projection Requirements

For the manifest selectors to work, the encounter-orchestrator must project these fields into encounter_journey_cache:

For Admission Request Workflow

"clinical_context.admission_context": {
  "admissionStatus": "...",
  "approvalStatus": "...",
  "transferStatus": "...",
  "wardName": "...",
  "bedName": "...",
  "roomName": "...",
  "bedStatus": "...",
  "visitType": "...",
  "priority": "...",
  "createdAt": "..."
}

For Discharge Pipeline Workflow

"clinical_context.encounter_context": {
  "coderStatus": "...",
  "coderName": "...",
  "dischargedAt": "...",
  "nurseDischargedAt": "...",
  "drgCode": "...",
  "relativeWeight": "...",
  "icd10Count": "...",
  "icd9Count": "...",
  "claimStatus": "..."
},
"clinical_context.admission_context": {
  "admissionStatus": "...",
  "dischargeDisposition": "...",
  "dischargePatientStatus": "...",
  "dischargeDate": "..."
}

If the orchestrator doesn’t yet project admission_context or coderStatus, these must be added to the buildClinicalContext() function.


9. Payload Builders Required

Builder ID Workflow Payload
cancelAdmissionRequest admission-request { admissionStatus: 'cancelRequest', reason: <input> }
admitPatient admission-request { admissionStatus: 'admitted', bedNumber: <selected>, ward: <selected> }
doctorDischargeIPD discharge-pipeline { lastStatus: 'discharged' }
startCoding discharge-pipeline { coderStatus: 'In Progress' }
submitClaim discharge-pipeline { encounterId: <id> }

10. Rollout Checklist

  • [x] Add IPD admission nodes to MASTER_WORKFLOW_NODES (5 nodes)
  • [x] Add IPD discharge nodes to MASTER_WORKFLOW_NODES (11 nodes)
  • [x] Add admission-request-default to WORKFLOW_REGISTRY
  • [x] Add discharge-pipeline-default to WORKFLOW_REGISTRY
  • [x] Add admissionRequest + dischargeRequest to DEFAULT_INITIAL_STATES
  • [x] Create admission-request-workflow.json (backend + frontend)
  • [x] Create discharge-pipeline-workflow.json (backend + frontend)
  • [ ] Ensure encounter-orchestrator projects admission_context into encounter_journey_cache
  • [ ] Ensure encounter-orchestrator projects coderStatus, drgCode, claimStatus into cache
  • [ ] Register IPD payload builders in workflowActionHandler.ts
  • [ ] Insert workflow JSONs into Supabase workflow_templates table
  • [ ] Wire admission-request manifest into admission centre UI (replace REST + Socket.IO with manifest)
  • [ ] Wire discharge-pipeline manifest into nurse IPD discharge view
  • [ ] Wire coder pipeline tabs into medical-coder page
  • [ ] Test full flow: admission request → admit → treat → doctor discharge → nurse discharge → ปิดบัญชี → code → validate → claim → invoice
Ask Anything