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),