Blood Bank Dispense (Section 4.3)
Specification for the blood-bank dispense workflow subsystem.
ระบบงานธนาคารเลือด — จ่ายเลือด
Status: Living document, assembled from 5 source flow-diagram images covering 4 swim lanes, plus 11 source UI photos covering the Officer cockpit — Img 9 is the legacy “บันทึก Immuno Hematology” pop-up (v0.5.1); Img 10 (3.3.9) and Img 11 (3.3.10) are the discharged-release and ward-pickup screens covered in v0.6.
Authoritative source: This document. If code disagrees with anything here, the code is wrong unless this doc is updated first.
Audience: Engineers and Claude Code agents implementing or reviewing this subsystem.
Reference HIS: EPHIS / Vajira PRD shape — see §11 for how the spec lanes map onto the actual code organization, which is split between encounter-context UI and the /blood-bank operations cockpit.
How to use this document
- State IDs are contracts. Use the IDs from §4 verbatim in code (
S1_KEY_REQ, nevers1KeyReq,KeyRequest, or any other casing). They map 1:1 to the source diagrams and are how this spec is referenced from memory across sessions. - TORs are testable requirements. Every TOR (§6) is bound to one or more states. When implementing a state, all bound TORs must be satisfiable.
- Open questions (§9) are explicit. Anything labelled “assumed” or marked with a
Q#is a best inference from incomplete diagrams — confirm with the spec owner before relying on it. Several Qs now have code-derived answers added in v0.5 — those are working answers from grepping the codebase, not authoritative spec resolutions. - Lane discipline matters. A transition between lanes (e.g.
D→N) is a hand-off. Implement these as queues, notifications, or workflow assignments — never as synchronous in-process calls. The lane boundary is also where authorization checks should sit. - Code organization ≠ lane structure. §11 is mandatory reading before “is X missing?” — the Doctor and bedside-Nurse lanes live in encounter/patient context, not on the
/blood-bankshell. The shell is the Officer cockpit (plus per-patient dispense embedded viaDynamicContentRenderer). - Figma-linked states (✓ in §4) have UI designs; render-time concerns belong on those states, not on internal-only ones.
1. Actors / Swim Lanes
| Code | Thai | English | Responsibilities |
|---|---|---|---|
D |
แพทย์ | Doctor | Originates blood request; analyzes patient symptoms during allergic reactions |
N |
พยาบาล | Nurse | Sends forms; receives blood from officer; dispenses to patient; records symptoms |
O |
เจ้าหน้าที่ห้องเลือด | Blood Bank Officer | Compatibility/cross-match checks; withdraw & ready blood for dispense; inventory mgmt; analyze allergy history |
F |
การเงิน | Finance / Cashier | Receives dispensed quantity & price data; calculates cost (TOR 4.3.8) |
2. Source Diagram Legend
| Symbol | Meaning |
|---|---|
| White rectangle | Sub-stage (atomic action) |
| Orange rounded rectangle | Main Stage (subsystem grouping, e.g. “State 1”) |
| Yellow / green rectangle | TOR (requirement note, attached via dashed line) |
| Pink / red rectangle | TOR with external system integration (LIS, finance, reporting) |
| Underlined text | The state has a Figma design link |
| Green circle | Start ([*]) or end terminus |
| Yellow diamond | Decision point (branching) |
| Yellow circle (small) | Merge / join node |
| Solid arrow | Control flow (state transition) |
| Dashed arrow | TOR association (NOT flow) |
3. Master State Machine
Lane membership is encoded in §4 because Mermaid stateDiagram-v2 does not natively render swim lanes.
stateDiagram-v2
%% =========================================
%% State 1: Reservation (D originates → N processes)
%% =========================================
[*] --> S1_KEY_REQ
S1_KEY_REQ --> S2_SEND_FORMS
S2_SEND_FORMS --> S3_URGENCY
S3_URGENCY --> S4A_5MIN: ด่วน 5min
S3_URGENCY --> S4B_10MIN: ด่วน 10min
S3_URGENCY --> S4C_20MIN: ด่วน 20min
S3_URGENCY --> S4D_NOT_URGENT: ไม่ด่วน
S4A_5MIN --> S5_JOIN
S4B_10MIN --> S5_JOIN
S4C_20MIN --> S5_JOIN
S4D_NOT_URGENT --> S5_JOIN
%% =========================================
%% State 2: Reservation Display (O)
%% =========================================
S5_JOIN --> S15_RES_DISPLAY_SYS
S15_RES_DISPLAY_SYS --> S16_RECV_FORMS
%% =========================================
%% State 3: Compatibility Check (O)
%% =========================================
S16_RECV_FORMS --> S17_COMPAT_SYS
S17_COMPAT_SYS --> S18_HIST_DEC
S18_HIST_DEC --> S19_BLOOD_TEST_NEW: ไม่เคยมีประวัติ
S19_BLOOD_TEST_NEW --> S20_BLOOD_GROUP
S20_BLOOD_GROUP --> S22_MERGE
S18_HIST_DEC --> S21_BLOOD_TEST_HIST: เคยมีประวัติ
S21_BLOOD_TEST_HIST --> S22_MERGE
S22_MERGE --> S23_CROSS_MATCH
%% Cross-match outcomes
S23_CROSS_MATCH --> S24_HOLD: ไม่สามารถใช้งานได้
S24_HOLD --> S25_NOTIFY
S25_NOTIFY --> [*]: terminus TBD (Q6)
S23_CROSS_MATCH --> S26_WITHDRAW: ใช้งานได้
S26_WITHDRAW --> S27_READY_DISPENSE
S27_READY_DISPENSE --> S6_RECV_BLOOD
S27_READY_DISPENSE --> S28_RECORD_DISP
%% =========================================
%% Nurse dispense (N)
%% =========================================
S6_RECV_BLOOD --> S7_DISPENSE: รับตามที่สั่ง
S6_RECV_BLOOD --> S29_PENDING: รับบางส่วน
S29_PENDING --> S13_CHK_REM
S7_DISPENSE --> S8_ALLERGY
S8_ALLERGY --> S13_CHK_REM: ไม่มีอาการ
S13_CHK_REM --> S30_INTEGRITY: คงเหลือ
S13_CHK_REM --> [*]: ใช้หมด (terminus TBD, Q8)
%% =========================================
%% Allergy reaction sub-flow (N → D → N → O)
%% =========================================
S8_ALLERGY --> S9_REC_SYMP: มีอาการ
S9_REC_SYMP --> S10_ANALYZE
S10_ANALYZE --> S11_ORDER_DRAW
S11_ORDER_DRAW --> S12_REC_ALLERGY_HIST
S12_REC_ALLERGY_HIST --> S34_ANALYZE_ALLERGY_HIST
S34_ANALYZE_ALLERGY_HIST --> S35_REC_ALLERGY_ANALYSIS
S35_REC_ALLERGY_ANALYSIS --> [*]: terminus TBD (Q9)
%% =========================================
%% Integrity check / return / destroy (O)
%% =========================================
S30_INTEGRITY --> S31_RETURN: สมบูรณ์
S30_INTEGRITY --> S32_DESTROY: ไม่สมบูรณ์
S31_RETURN --> S38_STORE: assumed (Q7)
S32_DESTROY --> S33_BLOOD_HIST
%% =========================================
%% State 4: Blood Inventory Management (O)
%% =========================================
S36_BLOOD_MGMT --> S37_RED_CROSS
S37_RED_CROSS --> S38_STORE
S38_STORE --> S33_BLOOD_HIST
%% =========================================
%% Finance / Cashier (F)
%% =========================================
S28_RECORD_DISP --> S40_RECV_BILLING: assumed (Q11)
S40_RECV_BILLING --> S41_CALC_COST
S41_CALC_COST --> [*]
4. State ID Dictionary
4.1 Doctor (D)
| ID | Thai | English | Type | Figma |
|---|---|---|---|---|
S1_KEY_REQ |
คีย์ Request เลือด | Key blood request | action | — |
S10_ANALYZE |
วิเคราะห์อาการผู้ป่วย | Analyze patient symptoms | action | — |
4.2 Nurse (N)
| ID | Thai | English | Type | Figma |
|---|---|---|---|---|
S2_SEND_FORMS |
ส่งใบจองเลือด+ใบตรวจเลือด | Send reservation + blood-test forms | action | ✓ |
S3_URGENCY |
ความเร่งด่วน | Urgency check | decision | — |
S4A_5MIN |
5 นาที | 5-min response window (urgent) | action | — |
S4B_10MIN |
10 นาที | 10-min response window (urgent) | action | — |
S4C_20MIN |
20 นาที | 20-min response window (urgent) | action | — |
S4D_NOT_URGENT |
ไม่ด่วน | Not urgent | action | — |
S5_JOIN |
(merge) | Join after urgency split | merge | — |
S6_RECV_BLOOD |
รับโลหิต | Receive blood (from officer) | decision | — |
S7_DISPENSE |
จ่ายเลือดผู้ป่วย | Dispense to patient | action | — |
S8_ALLERGY |
ผู้ป่วยมีอาการแพ้ | Patient has allergic reaction? | decision | — |
S9_REC_SYMP |
บันทึกอาการ | Record symptoms | action | — |
S11_ORDER_DRAW |
สั่งเจาะเลือด | Order blood draw | action | — |
S12_REC_ALLERGY_HIST |
บันทึกประวัติการแพ้โลหิต | Record blood-allergy history | action | — |
S13_CHK_REM |
เช็คจำนวนคงเหลือ | Check remaining quantity | decision | — |
4.3 Blood Bank Officer (O)
| ID | Thai | English | Type | Figma |
|---|---|---|---|---|
S15_RES_DISPLAY_SYS |
ระบบการแสดงผลการจองเลือด | State 2 Reservation Display System | main stage | — |
S16_RECV_FORMS |
รับใบตรวจเลือดใบจองเลือด | Receive blood-test/reservation forms | action | ✓ |
S17_COMPAT_SYS |
ระบบการตรวจสอบความเข้ากันได้ของเลือด | State 3 Compatibility Check System | main stage | — |
S18_HIST_DEC |
(decision: prior history?) | Has prior blood history? | decision | — |
S19_BLOOD_TEST_NEW |
ตรวจสอบเลือด | Blood test (no-history path) | action | — |
S20_BLOOD_GROUP |
ตรวจหมู่เลือด | Blood-group test | action | — |
S21_BLOOD_TEST_HIST |
ตรวจสอบเลือด | Blood test (has-history path) | action | — |
S22_MERGE |
(merge) | Merge after history-branch tests | merge | — |
S23_CROSS_MATCH |
Cross match | Cross-match decision | decision | ✓ |
S24_HOLD |
HOLD | Hold (cannot use / no such blood) | action | — |
S25_NOTIFY |
แจ้งเหตุผล | Notify reason (to requester) | action | — |
S26_WITHDRAW |
เบิกโลหิต | Withdraw blood from inventory | action | ✓ |
S27_READY_DISPENSE |
พร้อมจ่ายเลือด | Ready to dispense | action | ✓ |
S28_RECORD_DISP |
บันทึกการจ่ายเลือด | Record dispense event | action | ✓ |
S29_PENDING |
บันทึกโลหิตค้างจ่าย | Record pending/unfulfilled blood | action | — |
S30_INTEGRITY |
ตรวจสอบความสมบูรณ์ของโลหิต | Blood integrity check | decision | — |
S31_RETURN |
ส่งคืนโลหิต | Return blood to inventory | action | ✓ |
S31D_DISCHARGE_RELEASE |
บันทึกการปลดเลือดจากผู้ป่วยที่จำหน่าย | Release reserved bags for discharged patients (mode of S31_RETURN) |
action | ✓ |
S6P_PICKUP_REQUEST |
บันทึกแจ้งขอรับเลือด | Ward-nurse pickup ticket — HN → pending bags → “เลขที่รับ” mint (precedes S6_RECV_BLOOD) |
action | ✓ |
S32_DESTROY |
ทำลาย | Destroy (compromised blood) | action | — |
S33_BLOOD_HIST |
บันทึกประวัติโลหิต | Record blood history | action | ✓ |
S34_ANALYZE_ALLERGY_HIST |
วิเคราะห์ประวัติการแพ้โลหิต | Analyze allergy history | action | — |
S35_REC_ALLERGY_ANALYSIS |
บันทึกวิเคราะห์ประวัติการแพ้โลหิต | Record allergy-history analysis | action | — |
S36_BLOOD_MGMT |
ระบบบริหารจัดการโลหิต | State 4 Blood Management System | main stage | — |
S37_RED_CROSS |
โลหิตจากสภากาชาติ | Blood from Red Cross | action | — |
S38_STORE |
จัดเก็บโลหิต | Store blood (inventory) | action | — |
4.4 Finance (F)
| ID | Thai | English | Type | Figma |
|---|---|---|---|---|
S40_RECV_BILLING |
รับข้อมูลจำนวนและราคา | Receive quantity & price data | action | — |
S41_CALC_COST |
คำนวนค่าใช้จ่าย | Calculate cost | action | — |
5. Sub-System Flows
Each sub-flow below is a slice of §3. They are not separate state machines — they share IDs with the master.
5.1 State 1 — Reservation (D + N)
[*] (D) ──► S1_KEY_REQ (D) ──► S2_SEND_FORMS (N) ──► S3_URGENCY (decision)
├─ ด่วน ──► S4A_5MIN ──┐
├─ ด่วน ──► S4B_10MIN ──┤
├─ ด่วน ──► S4C_20MIN ──┼──► S5_JOIN
└─ ไม่ด่วน ► S4D_NOT_URGENT ┘
S5_JOIN ──► S15_RES_DISPLAY_SYS (O) // hand-off to officer
5.2 State 2 + 3 — Officer Compatibility Check (O)
S15_RES_DISPLAY_SYS ──► S16_RECV_FORMS ──► S17_COMPAT_SYS ──► S18_HIST_DEC
├─ ไม่เคยมีประวัติ ──► S19_BLOOD_TEST_NEW ──► S20_BLOOD_GROUP ──┐
└─ เคยมีประวัติ ──► S21_BLOOD_TEST_HIST ─────────────────────┴──► S22_MERGE
S22_MERGE ──► S23_CROSS_MATCH (decision)
├─ ใช้งานได้ ──► S26_WITHDRAW ──► S27_READY_DISPENSE
└─ ไม่สามารถใช้งานได้หรือไม่มีโลหิตดังกล่าว ──► S24_HOLD ──► S25_NOTIFY ──► (terminus TBD, Q6)
S27_READY_DISPENSE ──► S6_RECV_BLOOD (N) // hand-off to nurse
S27_READY_DISPENSE ──► S28_RECORD_DISP // parallel? sequential? (Q10)
5.3 Nurse Dispense (N)
S6_RECV_BLOOD (decision)
├─ รับตามที่สั่ง ──► S7_DISPENSE ──► S8_ALLERGY (decision)
│ ├─ มีอาการ ──► (allergy sub-flow §5.4)
│ └─ ไม่มีอาการ ──► S13_CHK_REM
└─ รับบางส่วน ──► S29_PENDING (O) ──► S13_CHK_REM
S13_CHK_REM (decision)
├─ คงเหลือ ──► S30_INTEGRITY (O, see §5.5)
└─ ใช้หมด ──► [*] // terminus TBD (Q8)
5.4 Allergy Reaction Sub-Flow (cross-lane: N → D → N → O)
S8_ALLERGY (มีอาการ) ──► S9_REC_SYMP (N)
──► S10_ANALYZE (D)
──► S11_ORDER_DRAW (N)
──► S12_REC_ALLERGY_HIST (N)
──► S34_ANALYZE_ALLERGY_HIST (O)
──► S35_REC_ALLERGY_ANALYSIS (O)
──► (terminus TBD, Q9)
5.5 Post-Dispense Integrity / Return / Destroy (O)
S30_INTEGRITY (decision)
├─ สมบูรณ์ ──► S31_RETURN ──► S38_STORE (assumed, Q7)
└─ ไม่สมบูรณ์ ──► S32_DESTROY ──► S33_BLOOD_HIST
S31D_DISCHARGE_RELEASE (mode of S31_RETURN, triggered without prior S26→S6 cycle)
filter: discharged-patient date range
──► writes blood_returns audit row (reason='patient_discharged')
──► flips bag status crossmatched|reserved → in_stock
──► same downstream cascade as S31_RETURN → S38_STORE
5.5b Ward Pickup (precedes S6_RECV_BLOOD, between Officer cockpit and bedside)
S27_READY_DISPENSE (O)
──► S6P_PICKUP_REQUEST (N at the blood-bank window)
input: HN
shows: PendingComponentRow[] grouped by component_type
(status='crossmatched' AND crossmatched_for_patient_hn=HN, FIFO)
action: enter "จำนวนที่ต้องการ" per component → save
output: เลขที่รับ NNNN/BE pickup-receipt no. stamped on every bag
bag.status: crossmatched → issued, issue_type='crossmatched'
──► S6_RECV_BLOOD (N at bedside)
The pickup ticket (S6P) is the audit artifact that closes the loop between Officer’s “ready to dispense” and bedside “received & administering”.
5.6 State 4 — Blood Inventory Management (O)
S36_BLOOD_MGMT ──► S37_RED_CROSS ──► S38_STORE ──► S33_BLOOD_HIST
This is the supply-side flow (sourcing blood from the Red Cross into local inventory). It connects to §5.5 because S31_RETURN likely flows into S38_STORE.
5.7 Finance / Cashier (F)
(?) ──► S40_RECV_BILLING ──► S41_CALC_COST ──► [*] // END
The trigger for S40_RECV_BILLING is unconfirmed (Q11). Working assumption: triggered when S28_RECORD_DISP completes (every dispense event creates a billing event).
6. TORs (Requirements)
Each TOR is bound to one or more states. When implementing or reviewing those states, the TOR must be satisfiable.
TOR 4.3.1 — Blood request data
Bound to: S1_KEY_REQ
Requirement: The blood request must capture the following fields:
patient_info— patient identity recordgeneral_history— general medical historydiagnosis(การวินิจฉัย)requesting_unit(หน่วยงานที่ขอเบิก) — ward / departmentrequesting_doctor_nameblood_product_typeurgency—urgent|routine; ifurgent, see Q1 about response windowdatetime_needed
TOR 4.3.2 — Request list display
Bound to: S16_RECV_FORMS
Requirement: System displays the list of blood requests with details: patient name, requesting department (แผนกที่ขอเบิก), requesting doctor, blood product, urgency.
TOR 4.3.3 — Record dispense
Bound to: S28_RECORD_DISP
Requirement: System can record every blood-dispense event (บันทึกการจ่ายเลือด). At minimum this should include: blood unit ID, patient ID, dispensed-by user, dispensed-at timestamp, quantity.
TOR 4.3.4 — Cancel reservation (cross-cutting)
Bound to: Cross-cutting — cancel must be reachable from one or more states. Q2 OPEN: which states allow cancel? Requirement:
- Cancel transition:
(active state) → CANCEL → S25_NOTIFY-equivalent (พร้อมแจ้งเหตุผล) - A reason is required on cancel.
- System auto-logs
canceller_user_idandcancel_datetime.
TOR 4.3.5 — Return blood to inventory
Bound to: S31_RETURN
Requirement: System supports returning blood products into inventory storage (S38_STORE).
TOR 4.3.6 — Inventory status management
Bound to: S38_STORE
Requirement: System can specify and change blood-product status. Statuses include:
พร้อมใช้งาน(ready)รอการกำจัด(awaiting disposal)รอจ่าย(awaiting dispense)จ่ายแล้ว(dispensed)
The status set is user-configurable — the four above are the named examples in the spec, not necessarily exhaustive.
TOR 4.3.7 — LIS integration
Bound to: S23_CROSS_MATCH
Requirement: System integrates with the hospital’s Laboratory Information System (LIS) to perform Group Matching and Compatibility Testing, and to record/display lab test results.
TOR 4.3.8 — Finance / billing integration
Bound to: S40_RECV_BILLING, S41_CALC_COST
Requirement: System integrates with the finance/billing system to charge nursing fees against the patient OR feed claim/reimbursement workflows. Cost calculation must align with the actual services provided at every step (i.e. only what was actually dispensed gets billed).
TOR 4.3.9 — UNKNOWN
Q12 OPEN. TOR 4.3.9 was not visible in any source diagram seen so far. Confirm with the spec owner whether 4.3.9 exists, was renumbered, or was intentionally skipped.
TOR 4.3.10 — Reports & statistics (cross-cutting)
Bound to: Cross-cutting — not tied to a single state. Requirement: System can display various reports and statistics for the department/unit (สามารถแสดงรายงานและสถิติต่างๆของหน่วยงานได้).
7. Suggested Data Shapes (TypeScript)
These are starting points — adjust as the database schema solidifies. The intent is that every state transition in §3 corresponds to a function or message that takes one of these entities and produces the next.
// =====================================================================
// Lanes / actors
// =====================================================================
type Lane = 'D' | 'N' | 'O' | 'F';
// =====================================================================
// State enum — keep in sync with §4
// =====================================================================
type FlowState =
// D
| 'S1_KEY_REQ' | 'S10_ANALYZE'
// N
| 'S2_SEND_FORMS' | 'S3_URGENCY'
| 'S4A_5MIN' | 'S4B_10MIN' | 'S4C_20MIN' | 'S4D_NOT_URGENT'
| 'S5_JOIN' | 'S6_RECV_BLOOD' | 'S7_DISPENSE' | 'S8_ALLERGY'
| 'S9_REC_SYMP' | 'S11_ORDER_DRAW' | 'S12_REC_ALLERGY_HIST'
| 'S13_CHK_REM'
// O
| 'S15_RES_DISPLAY_SYS' | 'S16_RECV_FORMS' | 'S17_COMPAT_SYS'
| 'S18_HIST_DEC' | 'S19_BLOOD_TEST_NEW' | 'S20_BLOOD_GROUP'
| 'S21_BLOOD_TEST_HIST' | 'S22_MERGE' | 'S23_CROSS_MATCH'
| 'S24_HOLD' | 'S25_NOTIFY' | 'S26_WITHDRAW' | 'S27_READY_DISPENSE'
| 'S28_RECORD_DISP' | 'S29_PENDING' | 'S30_INTEGRITY'
| 'S31_RETURN' | 'S32_DESTROY' | 'S33_BLOOD_HIST'
| 'S34_ANALYZE_ALLERGY_HIST' | 'S35_REC_ALLERGY_ANALYSIS'
| 'S36_BLOOD_MGMT' | 'S37_RED_CROSS' | 'S38_STORE'
// F
| 'S40_RECV_BILLING' | 'S41_CALC_COST'
// terminal
| 'END';
// =====================================================================
// Blood request (TOR 4.3.1, originated at S1_KEY_REQ)
// =====================================================================
type Urgency =
| { kind: 'routine' }
| { kind: 'urgent'; responseWindowMin: 5 | 10 | 20 }; // see Q1
interface BloodRequest {
id: string;
patient: PatientRef;
generalHistory: string;
diagnosis: string;
requestingUnit: string; // หน่วยงานที่ขอเบิก
requestingDoctor: UserRef;
bloodProduct: BloodProduct; // see below
urgency: Urgency;
neededAt: ISODateTime;
state: FlowState;
createdAt: ISODateTime;
cancellation?: Cancellation; // TOR 4.3.4
}
// TOR 4.3.4
interface Cancellation {
by: UserRef;
at: ISODateTime;
reason: string; // required
fromState: FlowState; // which state was active when cancelled
}
// =====================================================================
// Blood product / inventory (TOR 4.3.6, bound to S38_STORE)
// =====================================================================
type InventoryStatus =
| 'พร้อมใช้งาน' // ready
| 'รอการกำจัด' // awaiting disposal
| 'รอจ่าย' // awaiting dispense
| 'จ่ายแล้ว'; // dispensed
// status set is configurable — extend as needed
interface BloodProduct {
id: string; // unit ID
productType: string; // PRBC, FFP, Platelets, etc.
bloodGroup: string; // ABO + Rh
source: 'red_cross' | string; // S37_RED_CROSS or other
status: InventoryStatus;
receivedAt: ISODateTime;
expiresAt: ISODateTime;
}
// =====================================================================
// Cross-match (TOR 4.3.7, bound to S23_CROSS_MATCH)
// =====================================================================
interface CrossMatchResult {
requestId: string;
bloodProductId: string;
outcome: 'compatible' | 'incompatible' | 'no_blood';
// compatible -> S26_WITHDRAW
// incompatible
// | no_blood -> S24_HOLD
lisExternalId?: string; // reference into LIS
performedAt: ISODateTime;
performedBy: UserRef;
}
// =====================================================================
// Dispense event (TOR 4.3.3, bound to S28_RECORD_DISP)
// =====================================================================
interface DispenseEvent {
id: string;
requestId: string;
bloodProductId: string;
receivedKind: 'รับตามที่สั่ง' | 'รับบางส่วน'; // S6 branch
quantityDispensed: number;
pendingQuantity?: number; // when receivedKind = รับบางส่วน → S29_PENDING
dispensedAt: ISODateTime;
dispensedByNurse: UserRef;
recordedByOfficer: UserRef;
}
// =====================================================================
// Allergy episode (sub-flow §5.4)
// =====================================================================
interface AllergyEpisode {
id: string;
dispenseEventId: string;
symptoms: string; // S9_REC_SYMP
doctorAnalysis: string; // S10_ANALYZE
bloodDrawOrdered: boolean; // S11_ORDER_DRAW
allergyHistoryRecord: string; // S12_REC_ALLERGY_HIST
officerAnalysis?: string; // S34_ANALYZE_ALLERGY_HIST
officerAnalysisRecord?: string; // S35_REC_ALLERGY_ANALYSIS
episodeStartedAt: ISODateTime;
}
// =====================================================================
// Integrity check (§5.5, bound to S30_INTEGRITY)
// =====================================================================
interface IntegrityCheck {
bloodProductId: string;
result: 'สมบูรณ์' | 'ไม่สมบูรณ์';
// สมบูรณ์ -> S31_RETURN -> S38_STORE
// ไม่สมบูรณ์ -> S32_DESTROY -> S33_BLOOD_HIST
checkedAt: ISODateTime;
checkedBy: UserRef;
}
// =====================================================================
// Billing event (TOR 4.3.8, bound to S40_RECV_BILLING / S41_CALC_COST)
// =====================================================================
interface BillingEvent {
id: string;
dispenseEventId: string; // assumed source per Q11
quantity: number;
unitPrice: number;
totalCost: number;
patientId: string;
state: 'received' | 'calculated' | 'submitted_to_finance';
createdAt: ISODateTime;
}
// =====================================================================
// Common
// =====================================================================
type ISODateTime = string; // RFC 3339
interface PatientRef { id: string; }
interface UserRef { id: string; lane: Lane; }
8. Validation Checklist for Code
When implementing or reviewing code for this subsystem, verify each item:
- State enum match. Every state name in code matches an ID from §4 exactly (case-sensitive, no abbreviations).
- No invented transitions. Every state transition in code corresponds to an arrow in §3. If a transition is needed that isn’t in §3, it’s a spec gap — raise it, don’t invent it.
- Lane hand-offs are explicit. Cross-lane transitions (e.g.
S5_JOIND/N →S15_RES_DISPLAY_SYSO) must be implemented as queues or notifications, not as direct calls. Authorization checks belong at lane boundaries. - TOR coverage. For every state with a bound TOR (§6), the code at that state satisfies the TOR’s data and behavioral requirements.
- Cancellation (TOR 4.3.4) is uniformly handled —
reasonrequired, canceller and timestamp auto-logged. Until Q2 is resolved, cancel is reachable from every active (non-terminal) state. - Decision branches are total. Every
decisionstate (S3,S6,S8,S13,S18,S23,S30) must handle every documented branch — no implicit fall-throughs. - Open questions are flagged. Anywhere code depends on an assumed answer to §9, the assumption is recorded as a comment referencing the question ID (e.g.
// ASSUMES Q11: billing triggered by S28_RECORD_DISP). - Figma-linked states have UI. States marked ✓ in §4 are user-facing — UI must exist before those states are considered “done”.
- TOR 4.3.10 (reports) is implemented as a separate read-side, not woven into the dispense flow.
- §11 mapping respected. Don’t add a new route called
/officer-worklist—/blood-bag-requestis already the Officer worklist. Don’t add a “create blood request” page on/blood-bank— it lives in encounter dialogs. See §11.
9. Open Questions and Assumptions
Status legend: 🟢 resolved (spec owner confirmed), 🟡 working assumption, 🔵 code-derived working answer (v0.5; needs spec-owner confirmation), 🔴 unresolved.
| ID | Question | Status | Assumption / code-derived answer |
|---|---|---|---|
| Q1 | Under ด่วน, are 5/10/20 นาที SLA tiers (system enforces response time) or user-picked response windows? |
🔵 | Code answer: treated as a flag on the request (urgency: 'urgent' | 'routine' + selectable window); no SLA enforcement in code today. Implementation = user-picked window. |
| Q2 | TOR 4.3.4 (cancel) — from which states is cancel reachable? | 🔵 | Code answer: cancel is wired only from InventoryMatchingView.tsx (officer matching tab) via cancelBagReservation, and from BloodBagRequestDetail.tsx:234 via updateBloodRequestApi(... cancel). Reason field exists but isn’t enforced as required. Needs widening to all active states + reason validation. |
| Q3 | What follows S12_REC_ALLERGY_HIST? |
🟢 | → S34_ANALYZE_ALLERGY_HIST (officer) |
| Q4 | What is S6_RECV_BLOOD’s “not received as ordered” branch? |
🟢 | รับบางส่วน → S29_PENDING → S13_CHK_REM |
| Q5 | What does S13_CHK_REM “remaining” branch go to? |
🟢 | คงเหลือ → S30_INTEGRITY (NOT a loop back to S6) |
| Q6 | What is the terminus after S25_NOTIFY (HOLD path)? |
🔵 | Code answer: blood_bag_disposition.decision = 'hold' is a schema option but no UI consumer; effectively a flow end with no notification fan-out yet. Needs design. |
| Q7 | Where does S31_RETURN send returned blood? |
🔵 | Code answer: confirmed — /blood-return (BloodReturnModule) writes back to blood_separation_entries, status flips to return then to ready per stock movement. → S38_STORE is correct. |
| Q8 | What is the terminus of S13_CHK_REM ใช้หมด branch? |
🔴 | Treating as flow end ([*]); not modeled in code. |
| Q9 | What is the terminus after S35_REC_ALLERGY_ANALYSIS? |
🔴 | Treating as flow end; blood_allergy_reports table exists but no S34/S35 UI to populate the analysis result. |
| Q10 | S27_READY_DISPENSE has two outgoing arrows (to S6 and S28) — sequential or parallel? |
🔵 | Code answer: sequential write inside the dispense UI — BloodCrossMatchDispense calls saveCrossmatchRow synchronously on row edit; no parallel branch. |
| Q11 | What state(s) trigger S40_RECV_BILLING? Diagram shows two inbound arrows. |
🔵 | Code answer: not triggered by S28 today. /blood-finance reads blood_bank_demo_lab_orders (a separate demo table) and is disconnected from BloodCrossMatchDispense.saveCrossmatchRow writes. Exception: autologous donation 250฿ charge IS wired to financial per the changelog tile. The general S28→S40 link is a parity gap. |
| Q12 | TOR 4.3.9 was not visible in any diagram — does it exist? | 🔴 | Confirmed not present in any source artifact seen so far. |
10. Change Log
- v0.8 (2026-04-26) — DORNO sub-donor end-to-end pipe wired. The
blood_bag_donor_sourcesjunction (created in migration20260424_hosxp_intake_returns.sql) had no service or UI consumers; sub-donor structure existed only as a mock-data prop inIntakeScreen. Fixed across four layers: (a)bloodBankIntake.medbase.tsgainedsaveBagDonorSources,listBagDonorSources,listBagDonorSourcesByEntries, andgetPooledBagsSummary(a single-round-trip aggregate for banner-style displays); (b)IntakeScreensave handler now callssaveBagDonorSourcesfor every unit with a real DB UUID whenisHosxpIntakeLive(), idempotent-replacing the junction rows; © S26SeparateComponentsNewModulegained aDORNOcolumn — chip showing+N DORNOfor pooled bags, tooltip lists eachseq. bag-number, fed bylistBagDonorSourcesByEntrieskeyed off the visible row IDs; (d) S38BloodInventoryTabgained aPooledBagsBannerchip belowBloodStockAlerts— click-to-openPopoverlists the most recent pooled bags + their sub-donors, fed bygetPooledBagsSummary. Banner self-hides when there are no pooled bags so single-donor-only inventories see no UI noise. Closes the §12.4 “DORNO sub-donor structure” item; satisfies the BLOOD_BANK_AUDIT_VERIFICATION concern that “S26/S38 don’t surface pooled-donor info.” Photo Img 2 right panel (“Sub-donors per pooled bag [LPPC]”) is now code-covered. Verification was blocked by a pre-existing JSX error inBloodDonorListModule.tsxbreaking the blood-bank shell — code is grep-clean (no conflict markers, all 4 new service exports present), needs visual confirmation once the unrelated bug is fixed. - v0.7 (2026-04-26) — Officer cockpit consolidation: Test Results merged into Cross-match & Dispense, plus 3-role audit picker. (a)
/blood-bank/blood-cross-match-dispenseTab 0 now embeds the fullBloodTestResultsModule(Cell/Serum/Rh/Antibody/Crossmatch/Titer) via newpatient+embeddedprops — same recording surface used at/blood-bank/blood-test-resultsstandalone, and the same component the patient-profile dynamic tab renders. Officers no longer hop between two routes to record T&S then dispense. (b) The 3-role audit footer (“ผู้ทำ / ผู้ยืนยัน / ผู้จ่าย”) on the Tab 1 dispense table now uses the newFrequentUserSelect— a per-machine MRU picker againstlistUser, persisted inlocalStorageundermds.frequent-users.<slot>, with the performer slot auto-defaulted to the logged-in user fromgetMe. This is the first wired implementation of the 3-role audit fields visible in source photo Img 1, and satisfies the user-logging half of TOR 4.3.3 (S28_RECORD_DISP). Spec section 11.2 mapping rows forS17–S21andS27/S28updated. §12.3 “missing screens” backlog: 3-role audit ✅; cancel-with-reason still TBD (Q2 / TOR 4.3.4 reason field still not enforced). Merged viaclaude/fix-blood-bank-dashboard-nVtrz(commit976b0059) on top of WIP checkpoint5a80b502. - v0.6 (2026-04-25) — Two new spec-shaped screens shipped. (a) Spec 3.3.9 “บันทึกการปลดเลือดจากผู้ป่วยที่จำหน่าย” wired into
BloodReturnModuleas a mode toggle (S31D_DISCHARGE_RELEASE). Filtersblood_separation_entriesby status∈{reserved,crossmatched} + crossmatched_at window, joins onto patients for HN/AN/name, bulk-release flips bags back to in_stock and writes ablood_returnsaudit row withreturn_reason='patient_discharged'so the existing cascade trigger keeps movements/locations consistent. (b) Spec 3.3.10 “บันทึกแจ้งขอรับเลือด” landed as newBloodPickupRequestModuleat/blood-bank/blood-pickup-request(S6P_PICKUP_REQUEST). Resolves HN → patient header → groups pending crossmatched bags by component → enter “จำนวนที่ต้องการ” → mintsNNNN/BEreceive number stamped on every transitioned bag (statuscrossmatched → issued). Both screens use design-kit wrappers; both verified rendering in the dev server. Spec section 11.2 mapping updated; §12.3 “missing screens” backlog reduced (S31_RETURN discharged-trigger ✅; S6_RECV_BLOOD ward pickup precursor ✅). - v0.5.1 (2026-04-25) — MAJOR §11 CORRECTION. The canonical EPHIS-shape Officer worklist isn’t
/blood-bag-request— it’s/blood-bank/blood-list-patient(ListPatientBloodModule→MainTab→PatientWorkList). Has all 6 EPHIS sub-tabs (INCOMING_REQUEST,STICKER_PRINTED_AND_BLOOD_SAMPLE_COLLECTED,NOT_CROSSMATCHED_YET,CROSSMATCHED,PARTIAL_BLOOD_RECEIVED,BLOOD_FULLY_RECEIVED) plusยกเลิกสิ่งส่งตรวจ+ประวัติยกเลิกสิ่งส่งตรวจtoolbar buttons (partial TOR 4.3.4). Page renders correctly; data is empty against PH demo backend due to unseeded Mongo collections (frontend not at fault)./blood-bag-request“จับคู่เลือดพร้อมจ่าย” tab is the simpler single-tab matching view — secondary to/blood-list-patient. - v0.5.1 (2026-04-25) — §11.2 Immuno Hematology row enriched with route + Supabase tables (
blood_lis_resultssummary,blood_test_resultsdetail, migration20260424_blood_test_results.sql). Confirms legacy “บันทึก Immuno Hematology” pop-up (ABO forward/reverse, Rh, Ab Screening, ผู้ทดสอบ, ตกลง) is fully replaced byBloodTestResultsModule.tsxat/blood-bank/blood-test-results. - v0.5 (2026-04-25) — Added EPHIS framing throughout. New §11 “Code organization vs. spec lanes” mapping every spec state to its actual code location (corrects an earlier “Officer worklist missing” error). New §12 “Screen coverage matrix” capturing what 8 source UI photos confirm vs. what’s still un-photographed. Updated Q1, Q2, Q6, Q7, Q10, Q11, Q12 with code-derived working answers (status 🔵). Added validation rule #10 about respecting §11 (don’t invent routes that already exist).
- v0.4 — Added Finance/Cashier lane (
F),S40_RECV_BILLING,S41_CALC_COST, TOR 4.3.8, TOR 4.3.10. Logged Q11, Q12. - v0.3 — Added Officer (
O) lane, States 2/3/4, TORs 4.3.2/3/5/6/7. Resolved Q3, Q4, Q5. CorrectedS13_CHK_REM“remaining” branch (was incorrectly assumed as a loop back toS6). - v0.2 — Added State 2 nurse dispense + allergy reaction sub-flow.
- v0.1 — Initial: State 1 reservation (
D+N), TORs 4.3.1, 4.3.4, legend.
11. Code Organization vs. Spec Lanes (added v0.5)
The state machine in §3 is one continuous flow, but the implementation deliberately splits it across three contexts that mirror EPHIS / Vajira PRD shape. A future Claude session that searches /blood-bank for the Doctor request form will find nothing and conclude “missing”; that conclusion is wrong. The form is in encounter dialogs.
11.1 Three contexts
| Context | What lives here | Spec states covered |
|---|---|---|
Encounter / patient-profile UI (NOT /blood-bank) |
Doctor’s “Request Blood” dialog inside an open patient encounter; bedside Nurse view for the same patient; per-patient dispense renderer | S1_KEY_REQ, S2_SEND_FORMS, S3_URGENCY, S4*, S5_JOIN, S6_RECV_BLOOD (bedside view), S7_DISPENSE (bedside view), most of the allergy episode (S8–S12) — though only S1 is currently wired; the rest are gaps |
/blood-bank shell — Officer cockpit |
Officer worklist of incoming requests; cross-match decision + bag reservation; inventory; receive-into-stock; recheck-and-issue; transfusion recording | Most of the Officer lane: S15–S38 (with several still missing screens — see §12) |
/blood-bank shell embeds patient context |
BloodCrossMatchDispense, BloodBankPatientWorkflow, Bloodbank (the request form) — all rendered into patient-profile via DynamicContentRenderer when configured as a tab |
The per-patient slice of S23/S26/S27/S28. Same component is also reachable standalone for demo. |
11.2 Verified mapping (state → file)
Saved separately in memory at reference_blood_bank_route_to_spec_map.md. Summary:
| State | File |
|---|---|
S1_KEY_REQ |
web/packages/medical-kit/src/encounter-tools/timeline/dialogs/DialogBloodBank.tsx + web/packages/diagnostics-kit/src/blood-bank/components/blood-bag-request/components/BloodRequestEnhanced.tsx |
S15_RES_DISPLAY_SYS, S16_RECV_FORMS |
Canonical: /blood-bank/blood-list-patient → ListPatientBloodModule → MainTab → PatientWorkList (6 sub-tabs match EPHIS PRD exactly: INCOMING_REQUEST, STICKER_PRINTED_AND_BLOOD_SAMPLE_COLLECTED, NOT_CROSSMATCHED_YET, CROSSMATCHED, PARTIAL_BLOOD_RECEIVED, BLOOD_FULLY_RECEIVED). Simpler view: /blood-bank/blood-bag-request → InventoryMatchingView.tsx (single tab “จับคู่เลือดพร้อมจ่าย”). |
S17_COMPAT_SYS, S18_HIST_DEC, S19_BLOOD_TEST_NEW, S20_BLOOD_GROUP, S21_BLOOD_TEST_HIST |
Route: /blood-bank/blood-test-results → BloodTestResultsModule.tsx. Also embedded at /blood-bank/blood-cross-match-dispense Tab 0 (“บันทึกผลตรวจเลือด”) as `` (v0.7) — same form, hides its own gradient header + internal tabs, pipes patient HN/name/blood-group through to save payloads. Supabase: saveBloodTestResult writes summary → blood_lis_results, detail → blood_test_results (migration 20260424_blood_test_results.sql). Coverage: legacy “บันทึก Immuno Hematology” pop-up (ABO forward/reverse, Rh, Ab Screening with row insert, ผู้ทดสอบ, ตกลง) ✅ fully replaced — modern module adds Cell↔Serum mismatch warning, Screening Ab1/Ab2 phases, and Titer dilutions. |
S23_CROSS_MATCH |
/blood-bank/blood-order-lab “เครื่อง Crossmatch” tab (LIS-simulated) + /blood-bank/blood-cross-match-dispense (per-patient) |
S26_WITHDRAW |
/blood-bank/separate-components-new → SeparateComponentsNewModule.tsx. DORNO column (v0.8): virtual __dorno_count column shows +N DORNO chip for pooled PC/LPPC bags; tooltip lists seq. sub_donor_bag_number. Data via listBagDonorSourcesByEntries (single round-trip, keyed by visible row IDs). |
S27_READY_DISPENSE, S28_RECORD_DISP |
/blood-bank/blood-cross-match-dispense → BloodCrossMatchDispense.tsx (saveCrossmatchRow). Layout (v0.7): Tab 0 = embedded BloodTestResultsModule (Immuno Hematology recording); Tab 1 = crossmatch table + 3-role audit footer (“ผู้ทำ / ผู้ยืนยัน / ผู้จ่าย” + their timestamps). The 3 user-name fields use FrequentUserSelect — a per-machine MRU dropdown backed by listUser, with picks persisted in localStorage under mds.frequent-users.<slot>. Performer slot defaults to the logged-in user (getMe) on first mount. This satisfies the audit-trail half of TOR 4.3.3 (S28_RECORD_DISP requires dispensed-by user logging) and matches the 3-role footer in source photo Img 1. |
S6_RECV_BLOOD, S7_DISPENSE (officer side) |
/blood-bank/blood-receive → BloodRecheck tab → BloodDispenseRecheckPage.tsx |
S29_PENDING |
/blood-bank/blood-receive → BloodReceive tab partial flow (รับเลือดบางส่วน sub-tab) |
S31_RETURN |
/blood-bank/blood-return → BloodReturnModule.tsx (default mode “รับคืน”) |
S31D_DISCHARGE_RELEASE |
/blood-bank/blood-return → BloodReturnModule.tsx mode toggle “ปลดเลือดจากผู้ป่วยที่จำหน่าย” → service.ts:listBagsForDischargedPatients + releaseBagsForDischargedPatients (writes blood_separation_entries.status='in_stock' + blood_returns audit row with return_reason='patient_discharged') |
S6P_PICKUP_REQUEST |
/blood-bank/blood-pickup-request → BloodPickupRequestModule.tsx → service.ts:lookupPatientByHn + listPendingForPatient + commitPickup (mints NNNN/BE receive number into blood_separation_entries.pickup_receipt_no, transitions bag crossmatched → issued) |
S32_DESTROY |
/blood-bank/blood-discard → BloodComponentDiscardConfirm |
S37_RED_CROSS |
/blood-bank/inventory-sources-new → InventorySourcesNewModule.tsx |
S38_STORE (display) |
/blood-bank/blood-receive → BloodInventory tab → BloodInventoryTab.tsx (this is what user calls “ข้อมูลเลือดคงคลัง”). DORNO banner (v0.8): PooledBagsBanner chip (“DORNO: N pooled bags · M sub-donors”) sits below BloodStockAlerts; click opens a Popover listing the most recent pooled bags + their sub-donor bag numbers. Self-hides when no pooled bags exist. Data via getPooledBagsSummary. Aggregate-style placement (banner not column) chosen because the inventory grid is grouped by (bloodType + group + RH + bagType) and doesn’t expose per-bag IDs. |
| post-S7 transfusion record | /blood-bank/blood-receive → BloodTransfusion tab → TransfusionRecordingForm.tsx (this is what user calls “บันทึกจ่ายเลือด”) |
11.3 Lane hand-off implementation
| Spec hand-off | Code mechanism | Verdict |
|---|---|---|
S5_JOIN (encounter) → S15_RES_DISPLAY_SYS (Officer worklist) |
Doctor POSTs request via bloodRequest.service.ts; Officer worklist reads via listBloodRequestApi + listBloodRequestsEnhanced |
✅ Working — single source of truth, no separate “send” step needed |
S27_READY_DISPENSE (Officer) → S6_RECV_BLOOD (bedside Nurse) |
useBloodBankRealtime.ts subscribes to blood_status_notifications; status flip triggers realtime broadcast |
🟡 Channel exists; need to verify bedside view actually consumes it for “ready for you” UX |
S25_NOTIFY on incompatible/HOLD → bedside |
No writer emits to blood_status_notifications for this case |
🔴 Missing |
| Allergy bounce N → D → N → O | blood_allergy_reports table exists; reaction recording lives in TransfusionRecordingForm’s notes block; no officer-side analysis step |
🔴 Episode is dormant past initial recording |
S28_RECORD_DISP (Officer) → S40_RECV_BILLING (Finance) |
/blood-finance reads blood_bank_demo_lab_orders (demo); cross-match-dispense saveCrossmatchRow doesn’t write to a billing queue |
🔴 Disconnected (exception: autologous 250฿ flow is wired) |
12. Screen Coverage Matrix (added v0.5)
This section tracks what UI screens have been seen so far in source photos. Coverage is currently Officer-side only (9 photos; Img 9 = legacy “บันทึก Immuno Hematology” pop-up, added v0.5.1); Doctor, Nurse-bedside, Allergy, and Finance lanes are un-photographed.
12.1 Confirmed match (state → screen)
| State | Thai | Photo |
|---|---|---|
S15_RES_DISPLAY_SYS |
ระบบการแสดงผลการจองเลือด | Img 7, 8 (worklist) |
S16_RECV_FORMS |
รับใบตรวจเลือดใบจองเลือด | Img 7 sub-tab #1 (พิมพ์สติกเกอร์ + รับสิ่งส่งตรวจ) |
S17_COMPAT_SYS |
ระบบการตรวจสอบความเข้ากันได้ | Img 6 (compatibility entry) + Img 9 (legacy Immuno Hematology pop-up) ✅ code-covered |
S18_HIST_DEC |
prior history? | Img 6 ประวัติหมู่เลือด field |
S19_BLOOD_TEST_NEW |
ตรวจสอบเลือด (no history) | Img 6 Cell Grouping section + Img 9 ABO forward ✅ code-covered |
S20_BLOOD_GROUP |
ตรวจหมู่เลือด | Img 6 Serum Grouping + Img 9 ABO reverse + Rh + Ab St Pos. ✅ code-covered |
S21_BLOOD_TEST_HIST |
ตรวจสอบเลือด (history) | Img 6 (same form, gated by history) + Img 9 ✅ code-covered |
S23_CROSS_MATCH |
Cross match | Img 6 Cross Matching field + Img 1 Result column |
S26_WITHDRAW |
เบิกโลหิต | Img 3, 4 (จ่ายเลือดจากคลัง) |
S27_READY_DISPENSE |
พร้อมจ่ายเลือด | Img 1 (จ่ายเลือดผู้ป่วยขอเลือด tab) |
S28_RECORD_DISP |
บันทึกการจ่ายเลือด | Img 1 (footer 3-role audit) ✅ code-covered (v0.7 — FrequentUserSelect for ผู้ทำ/ผู้ยืนยัน/ผู้จ่าย) |
S29_PENDING |
บันทึกโลหิตค้างจ่าย | Img 7 sub-tab #4 (รับเลือดบางส่วน) |
S37_RED_CROSS |
โลหิตจากสภากาชาติ | Img 2, 5 (source column) |
S38_STORE |
จัดเก็บโลหิต | Img 2, 4, 5 (inventory) |
12.2 Partial / inferred
| State | Why partial |
|---|---|
S13_CHK_REM ใช้หมด terminus |
Img 8 “รับเลือดครบแล้ว” tab is the result, but not the check screen itself |
S22_MERGE |
Implicit (a join, not a screen) |
S32_DESTROY |
Img 3 has ตัดถุงเลือดที่หมดอายุ button (expired-bag handling) — but a full destroy disposition flow isn’t visible |
S36_BLOOD_MGMT |
Img 5 top nav names the module; State 4 is a grouping, not a screen |
12.3 Spec states with NO photo yet
Doctor lane (D):
S1_KEY_REQ(lives in encounter dialog — outside/blood-bankper §11)S10_ANALYZE(allergy)
Nurse lane (N):
S2_SEND_FORMS,S3_URGENCY,S4A/B/C/D,S5_JOIN(all bedside / encounter)S6_RECV_BLOOD,S7_DISPENSE(bedside dispense — not officer’s screen)S8_ALLERGY,S9_REC_SYMP,S11_ORDER_DRAW,S12_REC_ALLERGY_HIST(allergy episode)S13_CHK_REM(the actual decision UI, not just the resulting tab)
Officer lane (O) — still missing:
S24_HOLD(HOLD state UI)S25_NOTIFY(แจ้งเหตุผล screen)S30_INTEGRITY(integrity decision gate)✅ covered byS31_RETURN(ส่งคืนโลหิต screen — Q7)BloodReturnModule(default mode + newS31D_DISCHARGE_RELEASEmode in v0.6)S33_BLOOD_HIST(บันทึกประวัติโลหิต)S34_ANALYZE_ALLERGY_HISTS35_REC_ALLERGY_ANALYSIS
Nurse lane (N) — newly covered in v0.6:
- ✅
S6P_PICKUP_REQUEST(BloodPickupRequestModule) — the precursor audit artifact forS6_RECV_BLOOD. The bedside-receive view itself is still out of scope here (encounter-context, not blood-bank shell).
Finance lane (F):
S40_RECV_BILLING,S41_CALC_COST— completely absent from photos
12.4 Things in photos NOT in spec yet (candidates for spec extension)
ปลด(released) status — Img 1 legend- External-hospital dispense — Img 3, 4 (ชื่อโรงพยาบาล + ประเภทการจ่าย)
DORNO sub-donor structure — Img 2 right panel✅ wired in v0.8 (IntakeScreen → service → S26 column + S38 banner)- Worklist sub-tabs as state slices (6 of them) — Img 7
- Autologous blood as a distinct product type — Img 3
- Expired-bag handling sub-flow — Img 3
12.5 Coverage summary
| Lane | Coverage |
|---|---|
| D | 0 / 2 states ❌ |
| N | 0 / 13 states ❌ (these may live in encounter UI, not blood-bank shell — see §11) |
| O | ~14 / 24 states confirmed; 7 missing screens |
| F | 0 / 2 states ❌ |
To get true 1-to-1 against the spec, photos still needed of:
- Doctor’s request screen from inside the patient encounter (
S1) - Bedside nurse receive/dispense view (
S6,S7) - Allergy reaction handling screens (
S8–S12,S34–S35) - HOLD / NOTIFY / INTEGRITY / RETURN screens (
S24,S25,S30,S31) - Finance/billing screen (
S40,S41) - Discard/destroy disposition screen (
S32)