diff --git a/src/SheetMe.Data/Stores/PatientContextStore.cs b/src/SheetMe.Data/Stores/PatientContextStore.cs index 6ce51d3..759772e 100644 --- a/src/SheetMe.Data/Stores/PatientContextStore.cs +++ b/src/SheetMe.Data/Stores/PatientContextStore.cs @@ -145,7 +145,9 @@ public sealed class PatientContextStore /// /// 진료과 행을 고를 적용일시 12자리 /// 담당의 유효기간을 볼 날짜 8자리. 비면 의 앞 8자리 - public IReadOnlyDictionary Doctor(decimal comNum, string moment, string? doctorDay = null) + /// 진료과 행에서 의사 코드를 집을 컬럼 — 담당의 CodDtrCod, 주치의(전공의) CodStfDtr + public IReadOnlyDictionary Doctor(decimal comNum, string moment, + string? doctorDay = null, string codeColumn = "CodDtrCod") { using var connection = new OracleConnection(connectionString); connection.Open(); @@ -154,7 +156,7 @@ public sealed class PatientContextStore "SELECT * FROM (SELECT * FROM P_CodInf WHERE CodComNum = :k AND CodMtiSeq = 0" + " AND :t BETWEEN CodStrDtm AND CodEndDtm ORDER BY CodStrDtm DESC) WHERE ROWNUM = 1", ("k", comNum), ("t", moment)); - var code = PatientContext.Value(cod, "CodDtrCod"); + var code = PatientContext.Value(cod, codeColumn); if (code.Length == 0) { return cod; diff --git a/src/SheetMe.Designer/Services/PatientIdentity.cs b/src/SheetMe.Designer/Services/PatientIdentity.cs index c244d13..635a80a 100644 --- a/src/SheetMe.Designer/Services/PatientIdentity.cs +++ b/src/SheetMe.Designer/Services/PatientIdentity.cs @@ -94,6 +94,39 @@ public static class PatientIdentity } } + /// + /// 주치의(전공의) 행 — 진료과 행의 CodStfDtr 로 찾는다. 시각 갈래는 담당의와 같다 + /// (bzDataInterface.vb:4026-4067). 코드가 비어 있으면 빈 사전 — 레거시도 즉시 "" 다. + /// + public static IReadOnlyDictionary StaffDoctorOf(PatientContext context) + { + var connection = ConfigService.Current.ConnectionString; + var empty = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (connection.Length == 0) + { + return empty; + } + var inpatient = PatientContext.Value(context.ComInf, "ComPatTyp") == "I"; + var accepted = PatientContext.Value(context.ComInf, "ComAcpDtm"); + var left = PatientContext.Value(context.ComInf, "ComLevDtm"); + var discharged = PatientContext.Value(context.ComInf, "ComPrgStt") == "ILV"; + var moment = inpatient + ? (discharged && left.Length >= 12 ? left[..12] : context.AdpDtm) + : (accepted.Length >= 12 ? accepted[..12] : context.AdpDtm); + var doctorDay = inpatient ? null : (accepted.Length >= 8 ? accepted[..8] : null); + try + { + var row = new PatientContextStore(connection) + .Doctor(context.ComNum, moment, doctorDay, "CodStfDtr"); + // 코드가 비어 있으면 Doctor 가 진료과 행을 돌려준다 — 주치의 행이 아니므로 비운다 + return row.ContainsKey("UidNam") ? row : empty; + } + catch + { + return empty; + } + } + /// /// 상병 행 공급자 — 해석기가 필요할 때 갈래(전체/주/부)별로 부른다. /// 여기서 미리 읽지 않는 이유는 상병 태그가 없는 서식이 다수이기 때문이다. diff --git a/src/SheetMe.Designer/Services/PatientSession.cs b/src/SheetMe.Designer/Services/PatientSession.cs index 1aaa25b..1b43ad5 100644 --- a/src/SheetMe.Designer/Services/PatientSession.cs +++ b/src/SheetMe.Designer/Services/PatientSession.cs @@ -66,7 +66,8 @@ public static class PatientSession SheetMe.Core.Catalog.ITagValueResolver tags = Current is { } picked ? new PatientTagResolver(picked, session, PatientIdentity.ResidentNumberOf(picked), PatientIdentity.AddressOf(picked), - PatientIdentity.DoctorOf(picked), PatientIdentity.DepartmentOf(picked), + PatientIdentity.DoctorOf(picked), PatientIdentity.StaffDoctorOf(picked), + PatientIdentity.DepartmentOf(picked), PatientIdentity.HospitalRowOf(picked), PatientIdentity.DiagnosisSourceFor(picked), PatientIdentity.SurgerySourceFor(picked), diff --git a/src/SheetMe.Designer/Services/PatientTagResolver.cs b/src/SheetMe.Designer/Services/PatientTagResolver.cs index d053007..3c506ee 100644 --- a/src/SheetMe.Designer/Services/PatientTagResolver.cs +++ b/src/SheetMe.Designer/Services/PatientTagResolver.cs @@ -157,6 +157,9 @@ public sealed class PatientTagResolver : ITagValueResolver /// 이 내원의 담당의 행(M_UidMst) — 없으면 빈 사전 private readonly IReadOnlyDictionary doctor; + /// 이 내원의 주치의(전공의, CodStfDtr) 행 — 없으면 빈 사전 + private readonly IReadOnlyDictionary staffDoctor; + /// 이 내원의 진료과 마스터 행(M_DepMst) — 없으면 빈 사전 private readonly IReadOnlyDictionary department; @@ -187,6 +190,7 @@ public sealed class PatientTagResolver : ITagValueResolver public PatientTagResolver(PatientContext context, ITagValueResolver next, string residentNumber = "", IReadOnlyDictionary? address = null, IReadOnlyDictionary? doctor = null, + IReadOnlyDictionary? staffDoctor = null, IReadOnlyDictionary? department = null, IReadOnlyDictionary? hospital = null, Func>>? diagnosisSource = null, @@ -198,6 +202,7 @@ public sealed class PatientTagResolver : ITagValueResolver this.residentNumber = residentNumber; this.address = address ?? new Dictionary(StringComparer.OrdinalIgnoreCase); this.doctor = doctor ?? new Dictionary(StringComparer.OrdinalIgnoreCase); + this.staffDoctor = staffDoctor ?? new Dictionary(StringComparer.OrdinalIgnoreCase); this.department = department ?? new Dictionary(StringComparer.OrdinalIgnoreCase); this.hospital = hospital ?? new Dictionary(StringComparer.OrdinalIgnoreCase); this.diagnosisSource = diagnosisSource; @@ -254,6 +259,15 @@ public sealed class PatientTagResolver : ITagValueResolver { return fallback; } + // _Refer(참조 내원) 계열은 "안 옮긴 것"이 아니다 — 참조 내원은 EMR 실행 화면 + // (ActionTag, fmCodLstActionTag.vb:54 등)에서 사용자가 지정하는 값이고, + // 레거시 서식생성기 미리보기(TestPatientSetting)도 그것을 설정하지 않아 + // 레거시에서도 이 태그들은 미리보기에서 빈 값이다. 사유를 갈라 말한다. + if (tag.EndsWith("_Refer", StringComparison.Ordinal)) + { + return new TagValue(false, + "참조 내원 태그입니다 — 참조 내원은 EMR 실행 화면에서 지정되며, 레거시 미리보기에서도 빈 값입니다"); + } return new TagValue(false, "환자 태그이지만 아직 옮기지 않았습니다"); } @@ -352,10 +366,22 @@ public sealed class PatientTagResolver : ITagValueResolver private TagValue? FromDoctor(string tag) { if (tag is not ("OCM_담당의사" or "OCM_담당의사_담당의사코드" or "OCM_담당의사_연락처" - or "OCM_담당의사_영문" or "OCM_담당의사면허번호" or "OCM_담당의사전문의번호")) + or "OCM_담당의사_영문" or "OCM_담당의사면허번호" or "OCM_담당의사전문의번호" + or "OCM_STFDTR")) { return null; } + // STFDTR(주치의/전공의)는 같은 진료과 행의 CodStfDtr 로 찾은 별도 행이다 + // (bzDataInterface.vb:4026-4067 — 조회 갈래는 담당의와 같고 코드 컬럼만 다르다) + if (tag == "OCM_STFDTR") + { + var stf = staffDoctor.TryGetValue("UidNam", out var sn) ? sn.Trim() : string.Empty; + return stf.Length > 0 + ? new TagValue(true, stf) + : new TagValue(false, staffDoctor.Count == 0 + ? "이 내원에 주치의(전공의)가 지정되지 않았습니다" + : "주치의 이름이 비어 있습니다"); + } string Field(string column) => doctor.TryGetValue(column, out var v) ? v.Trim() : string.Empty; var name = Field("UidNam"); @@ -698,7 +724,7 @@ public sealed class PatientTagResolver : ITagValueResolver "ETC_요양기관명칭_병원전화_팩스번호", "ETC_요양기관명칭_영문_병원전화_팩스번호", "ETC_요양기관명칭_병원주소", "ETC_요양기관명칭_병원주소_영문", "ETC_요양기관명칭_사업자등록번호", "ETC_요양기관기호", - "OCM_담당의사_영문", "OCM_담당의사면허번호", "OCM_담당의사전문의번호", + "OCM_담당의사_영문", "OCM_담당의사면허번호", "OCM_담당의사전문의번호", "OCM_STFDTR", "OCM_User진료과", "OCM_User진료과_한글명칭", "OCM_User진료의사명", "OCM_상병명_가로", "OCM_상병명_세로", "OCM_상병명_영어_가로", "OCM_상병명_영어_세로", "OCM_상병코드_가로", "OCM_상병코드_세로", "OCM_부상병코드", "OCM_부상병POA", diff --git a/src/SheetMe.Designer/Views/PreviewWindow.xaml.cs b/src/SheetMe.Designer/Views/PreviewWindow.xaml.cs index 01da328..a569bb1 100644 --- a/src/SheetMe.Designer/Views/PreviewWindow.xaml.cs +++ b/src/SheetMe.Designer/Views/PreviewWindow.xaml.cs @@ -223,7 +223,8 @@ public partial class PreviewWindow : Window ? session : new PatientTagResolver(picked, session, PatientIdentity.ResidentNumberOf(picked), PatientIdentity.AddressOf(picked), - PatientIdentity.DoctorOf(picked), PatientIdentity.DepartmentOf(picked), + PatientIdentity.DoctorOf(picked), PatientIdentity.StaffDoctorOf(picked), + PatientIdentity.DepartmentOf(picked), PatientIdentity.HospitalRowOf(picked), PatientIdentity.DiagnosisSourceFor(picked), PatientIdentity.SurgerySourceFor(picked),