HL7 Vietnam VN Core FHIR Implementation Guide

Bộ Hướng dẫn Triển khai Core FHIR cho Việt Nam
0.4.0 - STU1 Draft Viet Nam cờ

Bộ Hướng dẫn Triển khai Core FHIR cho Việt Nam - Local Development build (v0.4.0) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions

Hướng dẫn Validation

Hướng dẫn kiểm tra hợp lệ — Validation Guidance

Mô hình kiểm tra hợp lệ của VN Core theo ba tầng:

  • Tầng 1 — Mức profile: FHIRPath invariant tự động kiểm tra khi kiểm tra hợp lệ resource
  • Tầng 2 — Mức máy chủ: Logic kiểm tra phức tạp cần triển khai trên máy chủ FHIR
  • Tầng 3 — Mức tài liệu: Quy tắc nghiệp vụ mà bên triển khai cần tuân thủ

Tầng 1 — FHIRPath Invariants (tự động)

Các invariant sau được nhúng trong profile và sẽ tự động kiểm tra khi validate resource bằng FHIR validator.

vn-cccd-format (VNCorePatient)

Hạng mục Giá trị
Context Patient.identifier (slice CCCD)
Severity error
Mô tả Số CCCD phải đúng 12 chữ số
FHIRPath value.empty() or value.matches('[0-9]{12}')
Căn cứ Luật Căn cước 2023, Điều 20

vn-addr-province (VNCoreAddress)

Hạng mục Giá trị
Context Address (VNCoreAddress profile)
Severity warning
Mô tả Địa chỉ Việt Nam (country = 'VN') phải có extension:province
FHIRPath country.empty() or country != 'VN' or extension.where(url = '...vn-ext-province').exists()
Mẫu tham chiếu CH Core invariant ch-addr-2

Tầng 2 — Kiểm tra ở phía máy chủ (bên triển khai thực hiện)

Các quy tắc sau KHÔNG thể kiểm tra bằng FHIRPath invariant vì yêu cầu logic phức tạp hoặc tra cứu dữ liệu ngoài. Implementer PHẢI triển khai trên FHIR server (custom OperationOutcome).

CCCD: Chữ số thứ 4 phải phù hợp giới tính

Quy tắc: Chữ số thứ 4 (vị trí index 3) của số CCCD mã hóa giới tính và thế kỷ sinh:

Chữ số thứ 4 Giới tính Thế kỷ sinh
0 Nam 19xx
1 Nữ 19xx
2 Nam 20xx
3 Nữ 20xx
4 Nam 21xx
5 Nữ 21xx

Kiểm tra: Patient.gender phải nhất quán với chữ số thứ 4 của identifier[CCCD].value:

  • gender = male → chữ số thứ 4 phải chẵn (0, 2, 4, 6, 8)
  • gender = female → chữ số thứ 4 phải lẻ (1, 3, 5, 7, 9)

Severity đề xuất: warning (có thể có trường hợp đặc biệt như chuyển giới chưa cập nhật CCCD)

Pseudocode:

cccd_value = patient.identifier.where(system = '.../sid/cccd').value
if cccd_value is not null and cccd_value.length == 12:
    digit4 = int(cccd_value[3])
    if patient.gender == 'male' and digit4 % 2 != 0:
        warning("Chữ số thứ 4 CCCD không phù hợp giới tính nam")
    if patient.gender == 'female' and digit4 % 2 != 1:
        warning("Chữ số thứ 4 CCCD không phù hợp giới tính nữ")

CCCD: Chữ số 5-6 phải phù hợp năm sinh

Quy tắc: Chữ số 5-6 (vị trí index 4-5) là 2 chữ số cuối của năm sinh.

Kiểm tra: Nếu Patient.birthDate có giá trị, 2 chữ số cuối năm sinh phải khớp vị trí 5-6 trong CCCD.

Pseudocode:

cccd_value = patient.identifier.where(system = '.../sid/cccd').value
if cccd_value is not null and patient.birthDate is not null:
    birth_year_last2 = patient.birthDate.year % 100  # e.g. 85, 90, 01
    cccd_year = int(cccd_value[4:6])
    if birth_year_last2 != cccd_year:
        warning("Năm sinh trong CCCD không khớp Patient.birthDate")

Severity đề xuất: warning

CCCD: 3 chữ số đầu phải là prefix hợp lệ theo danh mục BCA

Quy tắc: 3 chữ số đầu tiên của CCCD là mã prefix nơi đăng ký khai sinh theo danh mục BCA, KHÔNG phải mã ĐVHC hiện hành trong VNProvinceCS.

Kiểm tra: Server tra cứu VNCitizenIdBirthplacePrefixCS để xác nhận 3 chữ số đầu là prefix BCA hợp lệ.

Lưu ý: Mã tỉnh BCA (3 chữ số) khác với mã tỉnh TCTK (2 chữ số) dùng trong VNProvinceCS. Đây là bảng hỗ trợ validation cho số định danh cá nhân/CCCD đã cấp, không dùng để mã hóa địa chỉ.

Severity đề xuất: warning

Địa chỉ: Xã/phường phải thuộc tỉnh/TP đã chọn

Quy tắc: Nếu cả extension:province và extension:ward đều có giá trị, mã xã phải thuộc về tỉnh đã chọn theo QĐ 19/2025/QĐ-TTg.

Kiểm tra: Server tra cứu bảng ĐVHC để xác nhận mã xã (5 chữ số) thuộc tỉnh (2 chữ số).

Pseudocode:

province_code = address.extension[province].valueCoding.code  # e.g. "01" (Hà Nội)
ward_code = address.extension[ward].valueCoding.code           # e.g. "00008" (Phường Ngọc Hà)

# Tra bảng ĐVHC: ward_code phải thuộc province_code
if not dvhc_lookup(ward_code).province == province_code:
    error("Mã xã/phường không thuộc tỉnh/TP đã chọn")

Severity đề xuất: error (sai ĐVHC là lỗi dữ liệu nghiêm trọng)

Mẫu tham chiếu: CH Core invariant ch-addr-2 kiểm tra canton code bằng memberOf() — nhưng chỉ kiểm tra membership đơn cấp. Validation đa cấp (xã thuộc tỉnh) yêu cầu server-side.

BHYT: identifier[BHYT] format CCCD nên khớp CCCD bệnh nhân

Quy tắc: Từ 15/8/2025 (NĐ 188/2025/NĐ-CP), số thẻ BHYT có thể sử dụng số CCCD 12 chữ số (duy trì song song với format BHXH 10 số và legacy 15 ký tự). Nếu Coverage.identifier[BHYT].value là CCCD format (12 chữ số), nó nên khớp với Patient.identifier[CCCD].value của beneficiary.

Pseudocode:

bhyt_value = coverage.identifier.where(system = '.../sid/bhyt').value
if bhyt_value is not null and bhyt_value.matches('[0-9]{12}'):
    patient = resolve(coverage.beneficiary)
    cccd = patient.identifier.where(system = '.../sid/cccd').value
    if cccd is not null and bhyt_value != cccd:
        warning("identifier[BHYT] (CCCD) không khớp CCCD bệnh nhân")

Severity đề xuất: warning (có thể dùng CCCD người thân cho trẻ em)

Liên thông hồ sơ BHYT: SO_CCCD bắt buộc, trừ trường hợp bất khả kháng hợp lệ

Quy tắc: Khi xuất dữ liệu phục vụ Cổng giám định BHXH theo QĐ 3176/QĐ-BYT, SO_CCCD phải có dữ liệu. Chỉ được để trống nếu hồ sơ ghi nhận rõ mã lý do bất khả kháng hợp lệ.

Cách model trong VN Core:

  • Patient.identifier[CCCD] vẫn là slice bắt buộc ở tầng hồ sơ lâm sàng
  • Nếu chưa có số CCCD khi gửi BHYT, phải dùng data-absent-reason trên identifier[CCCD].value
  • Đồng thời PHẢI có Patient.extension[vn-ext-force-majeure-reason]

Pseudocode:

cccd = patient.identifier.where(system = 'http://fhir.hl7.org.vn/core/sid/cccd')
reason = patient.extension.where(url = 'http://fhir.hl7.org.vn/core/StructureDefinition/vn-ext-force-majeure-reason')

if cccd.value.empty():
    if reason.empty():
        error("Thiếu SO_CCCD nhưng không có mã lý do bất khả kháng hợp lệ")

Severity đề xuất: error

Liên thông hồ sơ BHYT: MA_LK phải tồn tại và nhất quán trên toàn bộ hồ sơ gửi

Quy tắc: MA_LK là khóa liên kết hồ sơ bắt buộc cho cùng một đợt KCB. Mã này phải xuất hiện trên Claim.identifier và ở Bundle hồ sơ thanh toán phải chỉ ra cùng một giá trị cho mọi resource nghiệp vụ liên quan.

Cách model trong VN Core:

  • Claim.identifier[MA_LK] là nơi lưu canonical record linkage key
  • VNCoreBHYTSubmissionBundle.identifier mang cùng giá trị
  • Server triển khai nên kiểm tra tất cả resource trong Bundle trỏ về cùng một hồ sơ thanh toán

Severity đề xuất: error

Liên thông hồ sơ BHYT: ngày giờ xuất cổng phải theo format yyyyMMddHHmm

Quy tắc: Các trường ngày giờ xuất/gateway như NGAY_VAO, NGAY_RA, NGAY_SINH, NGAY_YL, NGAY_KQ, timestamp export XML phải tuân thủ định dạng yyyyMMddHHmm.

Áp dụng trong VN Core:

  • FHIR native vẫn dùng date, dateTime, instant
  • Lớp export/gateway phải chuyển đổi sang chuỗi yyyyMMddHHmm
  • Validator server phải chặn payload JSON/XML export nếu format sai

Pseudocode:

export_value = transform_fhir_datetime(resource_datetime)
if not export_value.matches('^[0-9]{12}$'):
    error("Ngày giờ xuất BHYT phải theo định dạng yyyyMMddHHmm")

Severity đề xuất: error


Tầng 3 — Quy tắc nghiệp vụ (documentation only)

Các quy tắc sau mang tính nghiệp vụ, không bắt buộc kiểm tra tự động nhưng bên triển khai nên lưu ý.

Timeline lâm sàng

  • Encounter.period không được overlap cho cùng bệnh nhân (trừ Encounter lồng nhau)
  • Condition.recordedDate nên nằm trong Encounter.period của encounter tham chiếu
  • Observation.effectiveDateTime nên nằm trong Encounter.period

LOINC code phải phù hợp đơn vị

  • LOINC có property type (MCnc = mg/dL, SCnc = mmol/L). Code LOINC phải phù hợp đơn vị đo trong Observation.valueQuantity.unit
  • Ví dụ: LOINC 14771-0 (SCnc) → mmol/L, KHÔNG dùng LOINC 1558-6 (MCnc) cho mmol/L

Encounter.diagnosis nhất quán

  • Mỗi Condition tham chiếu encounter qua Condition.encounter → nên có entry tương ứng trong Encounter.diagnosis
  • Encounter.diagnosis.use nên phân biệt rõ: AD (chẩn đoán nhập viện), DD (chẩn đoán phân biệt), CM (bệnh kèm)

Organization semantics nhất quán

  • Không dùng vn-ext-org-rank để biểu diễn chưa xếp hạng.
  • Không dùng vn-ext-org-level để thay cho vn-ext-legacy-technical-line.
  • Nếu cơ sở là y tế dự phòng/TTYT/kiểm nghiệm/kiểm định, ưu tiên vn-ext-health-unit-rank thay vì vn-ext-org-rank.
  • Nếu dữ liệu là hồ sơ cũ theo TT 43/2013/TT-BYT, lưu tuyến 1-4 ở vn-ext-legacy-technical-line; không kéo ngược semantics này vào cấp quản lý hành chính hiện hành.

Tham khảo — Best Practices quốc tế

IG Phương pháp Ví dụ
CH Core (Thụy Sĩ) FHIRPath memberOf() cho canton code ch-addr-2: canton phải thuộc VS
US Core (Mỹ) Bindings extensible + guidance page Không dùng invariant cho SSN format
AU Core (Úc) FHIRPath regex cho IHI (16 digits) Invariant check digit trên Individual Healthcare Identifier
JP Core (Nhật) Server-side cho address hierarchy Không dùng FHIRPath cho prefecture-city validation

VN Core áp dụng mô hình kết hợp: FHIRPath cho validation đơn giản (format, membership), server-side cho cross-field logic (CCCD-gender, xã-tỉnh hierarchy, MA_LK, SO_CCCD, định dạng export yyyyMMddHHmm).

Thực thi trong repo

VN Core hiện kèm sẵn script để biến một phần Tier 2 thành executable checks:

./scripts/validate-tier2.sh

Script này hiện kiểm tra:

  • CCCD khớp giới tính và năm sinh trên các patient examples chuẩn
  • Prefix 3 số đầu CCCD thuộc vn-citizen-id-birthplace-prefix-cs
  • Xã/phường thuộc tỉnh đã chọn dựa trên property province của vn-ward-cs
  • Coverage.identifier[BHYT] dạng CCCD khớp CCCD beneficiary
  • subscriberId khớp identifier[BHYT]
  • SO_CCCD thiếu chỉ hợp lệ khi có force-majeure-reason
  • MA_LK nhất quán trong VNCoreBHYTSubmissionBundle
  • exportDateTime âm tính khi không theo yyyyMMddHHmm
  • Semantics của Organization không trộn hạng KCB, hạng đơn vị không phải cơ sở KCB, cấp quản lý hành chính, và tuyến kỹ thuật lịch sử trên các example chuẩn

Danh mục prefix CCCD của Bộ Công an nay đã được đóng gói ở mức validation-support trong IG dưới dạng vn-citizen-id-birthplace-prefix-cs, tách riêng với VNProvinceCS.


Liên hệ với các trang khác

Nếu cần Nên đọc tiếp
Tra cứu căn cứ pháp lý cho các quy tắc kiểm tra hợp lệ Cơ sở pháp lý
Nắm yêu cầu bảo mật và quyền riêng tư áp dụng cho dữ liệu y tế Bảo mật và quyền riêng tư
Diễn giải Must Support và cách xử lý khi thiếu dữ liệu Hướng dẫn Must Support
Xác định nghĩa vụ tuân thủ theo vai trò triển khai Tuân thủ theo vai trò triển khai

English Summary

VN Core uses a 3-tier validation model. Tier 1 uses profile invariants for automatic checks, Tier 2 covers server-side rules for cross-field and export logic, and Tier 3 records business rules that implementers should enforce in workflow and QA. The repo also ships executable Tier 2 checks for the current baseline.