conflict 6건을 고치고 재료 재사용으로 10종을 더한다 — 30종 → 40종

## conflict 6건 (통합 계획의 "고칠 것")

값이 바뀌는 것 둘:
- ETC_현재년도 — "2026" → "2026년"(bzDataInterface.vb:16923 의 + "년").
  형제 ETC_현재일시_1_년 은 접미가 없어 둘을 갈랐다(ClockFormat.YearSuffixed 신설).
  "2026" 을 단정하던 테스트도 고쳤다.
- ETC_로그인_사용자ID_사용자명 — 레거시(:17930)는 빈 값 검사 없이 항상 결합한다.
  한쪽이 비어도 "U01." 이 그대로 나간다. 조건을 붙였던 것이 레거시와 달랐다.

행 선택이 바뀌는 것 둘:
- ETC_로그인_사용자연락처 / 원내연락처 — 전에는 M_UidMst 를 조건 없이 첫 행.
  이력이 여러 행인 사용자에서 옛 연락처가 나올 수 있었다.
  유효기간(SYSDATE 일자 BETWEEN)으로 고르고, 0행이면 레거시 폴백(:10641)처럼
  조건 없이 다시 찾는다.

의도적 차이로 기록한 것 둘(코드 주석):
- 유효기간 판정 일자 — 레거시는 로그인 시점, 여기는 SYSDATE(자정 넘긴 세션에서만 갈림).
- Trim — 레거시는 무가공, 여기는 걷는다(CHAR 잔여 공백이 종이에 찍히는 쪽이 더 이상하다).

## new 48종 중 재료 재사용으로 되는 10종

워크플로 매핑 상세(docs/new48-mappings.json)를 붙여 보니 46종이 needs-lookup 인데,
그중 10종은 <b>이미 든 행의 다른 컬럼</b>이거나 문맥 계산이었다:

- 담당의 행 재사용 3: OCM_담당의사_영문(UidEngNam) · 면허번호(UidLicNum) · 전문의번호(UidSpcLic)
  — 조회 갈래가 OCM_담당의사와 문자 그대로 같고 집는 컬럼만 다르다(:4094·4119·4144)
- CowInf 재사용 3: OCM_병실베드(CowBedCod) · 병동_병실 · 병동_병실_베드(" / " 결합, 행 없으면 "")
- 문맥 계산 1: OCM_내원유형(입원/응급실/외래 — ComPatTyp·ComEmgYon)
- User 계열 3: OCM_User진료과 · _한글명칭 · User진료의사명.
  로그인 사용자가 의사(UidDtrYon=Y)면 사용자의 과·이름, 아니면 환자의 것.
  HisUser 에 DoctorYn·DepOfficialName 을 추가했다(세션 조회 SELECT 확장).

남은 new 는 새 조회가 필요한 세 무리다: 상병(Okd_상병쿼리) · 수술(GetOprInfDT) ·
심사(Bkd_상병쿼리). 다음 배치.

## 게이트

- dotnet test 338/338 (현재년도·ID.사용자명 판정 갱신)
- --edit-smoke 실패 0 (이름 검사 40종)
- --db-patient ①~㉔ 전건 통과
- --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 불변

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Msystech
2026-08-19 15:05:38 +09:00
co-authored by Claude Opus 5
parent e62e43d176
commit 9278b39a6d
7 changed files with 813 additions and 13 deletions
+578
View File
@@ -0,0 +1,578 @@
[
{
"tag": "ETC_로그인사용자_서명",
"kind": "needs-lookup",
"row": "",
"column": "UidImgPth",
"formula": "path = clsCommonLib.GetUserInfo().UidImgPth; if (path == \"\") return \"\"; local = InstallPath + \"\\\\Tmp\\\\DownLoadedFiles\\\\Signature\\\\\" + path.Split(\u0027\\\\\u0027).Last(); FileTransfer.DownLoadFile(1, path, local); return local;",
"evidence": "bzDataInterface.vb:16736-16795 (16741 다운로드 폴더, 16747 GetUserInfo().UidImgPth, 16758-16762 경로 조립, 16764-16767 DownLoadFile, 16781 Return sLocalPath)",
"confidence": "medium",
"blocker": "clsCommonLib.GetInstance.GetUserInfo (UidImgPth), clsCommonLib.GetInstance.GetInstallPathFromServerInfoFile, clsCommonLib.GetInstance.CreateDirectory, M.EMR.FileTransfer.clsFileTransfer.DownLoadFile — 파일서버 다운로드가 필요해 지금은 못 옮긴다",
"note": "값이 아니라 \u003c로컬에 내려받은 이미지 파일 경로\u003e를 돌려준다(TagPreviewCatalog 의 Image 계열). 레거시 이상 세 가지를 그대로 적는다: (1) 16753 For 0..4 루프가 16771-16773 의 항상-참 조건 때문에 첫 회전에서 반드시 Exit For 한다 — 재시도가 아니라 1회 실행이다. (2) 16758-16762 은 \"|\" 를 붙였다가 바로 잘라내는 무의미한 왕복이다. (3) 16787 의 두 번째 Return sLocalPath 는 도달 불가다. 실패해도 MsgBox(16766, 16778)만 띄우고 경로를 그대로 돌려준다.",
"action": "UidImgPth 를 읽어 파일서버에서 내려받고 로컬 경로를 돌려주는 경로를 새로 만든다 — 환자 문맥과 무관하니 세션 해석기 쪽이다."
},
{
"tag": "OCM_OPNAME",
"kind": "needs-lookup",
"row": "",
"column": "OPNAME",
"formula": "dt = GetOprInfDT(); return dt?.Rows.Count \u003e 0 ? dt.Rows[0][\"OPNAME\"].ToString().Trim() : null;",
"evidence": "bzDataInterface.vb:13099-13109 (13101 GetOprInfDT, 13105 Rows(0).Item(\"OPNAME\").ToString.Trim)",
"confidence": "medium",
"blocker": "GetOprInfDT (bzDataInterface.vb:13203) — 수술 정보 SQL 을 먼저 옮겨야 한다",
"note": "OPNAME 별칭은 S_OprTyp 를 OprTyp=\u0027OPNAME\u0027 으로 LEFT JOIN 한 MAX(OprCodNam)(13203+100, 13203+111~113). OCM_DX 와 똑같이 Else 가 없어 빈 결과에서 null 이 나간다.",
"action": "수술정보 조회(GetOprInfDT)를 옮기고 첫 행 OPNAME.Trim 을 돌려주는 매핑을 넣는다 — 행이 없으면 빈 값."
},
{
"tag": "OCM_STFDTR",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var codDT = PatTyp==\"I\" ? (ComInf.ComPrgStt==\"ILV\" ? GetCodInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum, SystemDateTime, 1)) : GetCodInfDT(ComNum, ComInf.ComAcpDtm.Substring(0,12), 1); if(codDT.Rows.Count==0) return \"\"; var stf = codDT.Rows[0][\"CodStfDtr\"]; if(string.IsNullOrEmpty(stf)) return \"\"; var uid = GetUidMst(stf, PatTyp==\"I\" ? codDT.Rows[0][\"CodEndDtm\"].Substring(0,8) : ComInf.ComAcpDtm.Substring(0,8)); return uid?[\"UidNam\"].Trim() ?? \"\";",
"evidence": "4026-4067 (분기 4031, GetCodInfDT 4033/4035/4049, CodStfDtr 4039/4051, GetUidMst 4040/4052, UidNam 4062)",
"confidence": "high",
"blocker": "clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) — 둘 다 문맥의 다섯 행이 아니라 별도 질의다(clsCommonLib.vb 10510 · 10609). clsCommonLib.SystemDateTime 도 필요.",
"note": "형제 태그(OCM_담당의사*)와 달리 CodDtrCod 가 아니라 CodStfDtr 를 쓴다. 또 형제들에 없는 빈값·Rows.Count 검사가 있다. 기준일자가 입원 분기는 CodEndDtm 앞 8자, 외래 분기는 ComAcpDtm 앞 8자로 서로 다르다(4040 의 주석 처리된 옛 코드가 그 흔적).",
"action": "CodInf 첫 행의 CodStfDtr(비면 빈 값)로 M_UidMst 를 찾아 UidNam.Trim 을 돌려주는 매핑을 넣는다."
},
{
"tag": "OCM_User진료과",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "if (WrkInfo.UidMst[\"UidDtrYon\"].Trim()==\"Y\") return (HspCfg.HspStrDte\u003e=\"20220701\" \u0026\u0026 !string.IsNullOrEmpty(WrkInfo.DepMst[\"DepOfcNam\"])) ? WrkInfo.DepMst[\"DepOfcNam\"] : WrkInfo.DepMst[\"DepKorNam\"]; var dr = GetDepMstDR(GetPatientInfo[\"AdpDepCod\"], ComInf.ComAcpDtm.Substring(0,8)); if(dr==null) return \"\"; return (HspCfg.HspStrDte\u003e=\"20220701\" \u0026\u0026 !string.IsNullOrEmpty(dr[\"DepOfcNam\"])) ? dr[\"DepOfcNam\"].Trim() : dr[\"DepKorNam\"].Trim();",
"evidence": "5697-5732 (UidDtrYon 5701, HspStrDte 비교 5702/5715, AdpDepCod 5711, GetDepMstDR 5712)",
"confidence": "high",
"blocker": "clsCommonLib.GetDepMstDR(psDepCod, psStrDte) — clsCommonLib.vb 13117. 로그인 사용자 분기는 moWrkInfoBiz.UidMst/DepMst(환자 문맥 밖) 가 필요하고, clsCommonLib.HspCfg.HspStrDte 도 필요하다.",
"note": "AdpDepCod 는 GetPatientInfo 조인의 별칭이지만 SQL 에서 `CodDepCod AdpDepCod` 로 확정된다(dtCommonLib.vb 3699) — 즉 row=CodInf, column=CodDepCod. HspStrDte\u003e=\"20220701\" 는 날짜가 아니라 문자열 비교다(레거시 그대로).",
"action": "UidDtrYon=\u0027Y\u0027 면 로그인자 DepMst, 아니면 환자 AdpDepCod 의 DepMst(ComAcpDtm 앞8자 기준)를 읽고, HspStrDte\u003e=\u002720220701\u0027 이고 DepOfcNam 이 있으면 DepOfcNam, 아니면 DepKorNam."
},
{
"tag": "OCM_User진료과_Refer",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "if (WrkInfo.UidMst[\"UidDtrYon\"].Trim()==\"Y\") return (HspCfg.HspStrDte\u003e=\"20220701\" \u0026\u0026 !string.IsNullOrEmpty(WrkInfo.DepMst[\"DepOfcNam\"])) ? WrkInfo.DepMst[\"DepOfcNam\"] : WrkInfo.DepMst[\"DepKorNam\"]; var dr = GetDepMstDR(GetPatientInfo[\"AdpDepCod\"], ComInf.ComAcpDtm.Substring(0,8)); if(dr==null) return \"\"; return (HspCfg.HspStrDte\u003e=\"20220701\" \u0026\u0026 !string.IsNullOrEmpty(dr[\"DepOfcNam\"])) ? dr[\"DepOfcNam\"].Trim() : dr[\"DepKorNam\"].Trim();",
"evidence": "5762-5797 (5776 AdpDepCod, 5777 GetDepMstDR 에 비-Refer ComInfDR 사용)",
"confidence": "high",
"blocker": "clsCommonLib.GetDepMstDR(psDepCod, psStrDte) — clsCommonLib.vb 13117.",
"note": "이름에 _Refer 가 붙었지만 본문은 OCM_User진료과(5697) 와 \u003c완전히 동일\u003e하다 — GetPatientInfo·ComInfDR 모두 _Refer 가 아닌 현재 문맥을 쓴다. 레거시 버그로 보이나 그대로 적는다.",
"action": "OCM_User진료과와 같은 본문을 넣는다 — 이름은 _Refer 지만 레거시가 참조 내원행을 쓰지 않는다는 점을 주석에 남긴다."
},
{
"tag": "OCM_User진료과_한글명칭",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "if (WrkInfo.UidMst[\"UidDtrYon\"].Trim()==\"Y\") return WrkInfo.DepMst[\"DepKorNam\"]; return GetDepMstDR(GetPatientInfo[\"AdpDepCod\"], ComInf.ComAcpDtm.Substring(0,8))[\"DepKorNam\"].Trim();",
"evidence": "5734-5760 (UidDtrYon 5738, DepKorNam 5740/5749, AdpDepCod 5746, GetDepMstDR 5747)",
"confidence": "high",
"blocker": "clsCommonLib.GetDepMstDR(psDepCod, psStrDte) — clsCommonLib.vb 13117.",
"note": "OCM_User진료과 와 달리 DepOfcNam·HspStrDte 분기가 없고 항상 DepKorNam 이다. 또 oDR null 검사도 없어 조회 실패 시 예외가 나고 Catch 가 \"\" 를 돌려준다(5753-5757).",
"action": "OCM_User진료과와 같은 경로로 읽되 \u003cb\u003e항상 DepKorNam\u003c/b\u003e 을 돌려주는 매핑을 넣는다(DepOfcNam 분기 없음)."
},
{
"tag": "OCM_User진료의사명",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "if (WrkInfo.UidMst[\"UidDtrYon\"].Trim()==\"Y\") return WrkInfo.UidMst[\"UidNam\"]; var dr = GetUidMst(GetPatientInfo[\"AdpDtrCod\"], ComInf.ComAcpDtm.Substring(0,8)); return dr?[\"UidNam\"].Trim() ?? \"\";",
"evidence": "5799-5827 (UidDtrYon 5805, UidNam 5806/5814, AdpDtrCod 5810, GetUidMst 5811)",
"confidence": "high",
"blocker": "clsCommonLib.GetUidMst(psUidCod, psStrDte) — clsCommonLib.vb 10609.",
"note": "AdpDtrCod 는 조인 별칭이지만 SQL 에서 `CodDtrCod AdpDtrCod` 로 확정된다(dtCommonLib.vb 3699) — row=CodInf, column=CodDtrCod. 로그인 사용자가 의사면 환자 문맥을 아예 보지 않고 로그인 사용자 이름을 돌려준다.",
"action": "UidDtrYon=\u0027Y\u0027 면 로그인자 UidNam(무가공), 아니면 환자 AdpDtrCod 로 M_UidMst(ComAcpDtm 앞8자)를 찾아 UidNam.Trim."
},
{
"tag": "OCM_User진료의사명_Refer",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "if (WrkInfo.UidMst[\"UidDtrYon\"].Trim()==\"Y\") return WrkInfo.UidMst[\"UidNam\"]; var dr = GetUidMst(CodInf[\"CodDtrCod\"], ComInf.ComAcpDtm.Substring(0,8)); return dr?[\"UidNam\"].Trim() ?? \"\";",
"evidence": "5829-5857 (UidDtrYon 5835, UidNam 5836/5844, CodInfDR(\"CodDtrCod\") 5840, GetUidMst 5841)",
"confidence": "high",
"blocker": "clsCommonLib.GetUidMst(psUidCod, psStrDte) — clsCommonLib.vb 10609.",
"note": "비-Refer 판(5799) 과 두 곳이 다르다: 의사코드를 GetPatientInfo[\"AdpDtrCod\"] 가 아니라 moPatInfoBiz.CodInfDR(\"CodDtrCod\") 에서 꺼낸다(값 자체는 같은 CodInf.CodDtrCod). 그리고 이름의 _Refer 와 달리 ComInfDR·CodInfDR 모두 \u003c현재\u003e 문맥이다 — _Refer 문맥을 전혀 쓰지 않는다.",
"action": "OCM_User진료의사명과 같되 의사코드를 환자 AdpDtrCod 가 아니라 CodInf 행의 CodDtrCod 에서 가져오게 한다."
},
{
"tag": "OCM_내원유형",
"kind": "computed",
"row": "ComInf",
"column": "ComPatTyp, ComEmgYon",
"formula": "var typ = ComInf.ComPatTyp; if (typ==\"I\") return \"입원\"; if (typ==\"O\") return ComInf.ComEmgYon==\"Y\" ? \"응급실\" : \"외래\"; return \"\";",
"evidence": "2896-2917 (PatTyp 2900, \"I\"→입원 2902-2904, \"O\" 2906, ComEmgYon 2909-2913)",
"confidence": "high",
"blocker": "",
"note": "moPatInfoBiz.PatTyp 는 msPatTyp = GetPatientInfo[\"ComPatTyp\"] 다(bzPatientInfo.vb 639, 1008-1012) → row=ComInf, column=ComPatTyp. ComEmgYon 은 GetPatientInfo 조인의 P_ComInf 몫이다(dtCommonLib.vb 3699 의 DA.COL(\"P_ComInf\",\"CM\"), 조인 대상 중 Com 접두는 P_ComInf 뿐; dtCommonLib.vb 9196/9206 에서 P_ComInf 컬럼임이 다시 확인됨). ComPatTyp 가 I·O 둘 다 아니면 빈 문자열이다.",
"action": "PatTyp=\u0027I\u0027 면 \"입원\", \u0027O\u0027 면 ComEmgYon=\u0027Y\u0027 일 때 \"응급실\" 아니면 \"외래\", 그 외 빈 값 — Computed 에 세 줄로 넣는다."
},
{
"tag": "OCM_담당의사_Refer",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var codDT = PatTyp_Refer==\"I\" ? (ComInf_Refer.ComPrgStt==\"ILV\" ? GetCodInfDT(ComNum_Refer, ComInf_Refer.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum_Refer, SystemDateTime, 1)) : GetCodInfDT(ComNum_Refer, ComInf_Refer.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0][\"CodDtrCod\"], PatTyp_Refer==\"I\" ? codDT.Rows[0][\"CodEndDtm\"].Substring(0,8) : ComInf_Refer.ComAcpDtm.Substring(0,8)); return uid?[\"UidNam\"].Trim() ?? \"\";",
"evidence": "4069-4092 (분기 4074, GetCodInfDT 4076/4078/4082, GetUidMst 4080/4083, UidNam 4087)",
"confidence": "high",
"blocker": "clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) · clsCommonLib.SystemDateTime",
"note": "OCM_STFDTR 와 달리 codDT.Rows.Count 검사가 없어 조회 결과가 없으면 Rows(0) 에서 예외가 난다(4080/4083). 이 함수는 _Refer 문맥(ComNum_Refer·ComInfDR_Refer·PatTyp_Refer) 을 실제로 쓴다.",
"action": "참조 내원(ComNum_Refer/ComInfDR_Refer)으로 PatientContextStore.Doctor 를 한 번 더 호출해 UidNam 을 돌려준다 — 현재 내원용 doctor 사전을 재사용하면 안 된다."
},
{
"tag": "OCM_담당의사_영문",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var codDT = PatTyp==\"I\" ? (ComInf.ComPrgStt==\"ILV\" ? GetCodInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum, SystemDateTime, 1)) : GetCodInfDT(ComNum, ComInf.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0][\"CodDtrCod\"], PatTyp==\"I\" ? codDT.Rows[0][\"CodEndDtm\"].Substring(0,8) : ComInf.ComAcpDtm.Substring(0,8)); return uid?[\"UidEngNam\"].Trim() ?? \"\";",
"evidence": "4094-4117 (분기 4099, GetCodInfDT 4101/4103/4107, GetUidMst 4105/4108, UidEngNam 4112)",
"confidence": "high",
"blocker": "clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) · clsCommonLib.SystemDateTime",
"note": "OCM_담당의사_Refer 와 뼈대가 같고 최종 컬럼만 UidEngNam 이다. Rows.Count 검사 없음.",
"action": "FromDoctor 에 한 줄 추가 — Field(\"UidEngNam\") 을 돌려준다."
},
{
"tag": "OCM_담당의사_영문_Refer",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var codDT = PatTyp_Refer==\"I\" ? (ComInf_Refer.ComPrgStt==\"ILV\" ? GetCodInfDT(ComNum_Refer, ComInf_Refer.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum_Refer, SystemDateTime, 1)) : GetCodInfDT(ComNum_Refer, ComInf_Refer.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0][\"CodDtrCod\"], PatTyp_Refer==\"I\" ? codDT.Rows[0][\"CodEndDtm\"].Substring(0,8) : ComInf_Refer.ComAcpDtm.Substring(0,8)); return uid?[\"UidEngNam\"].Trim() ?? \"\";",
"evidence": "4119-4142 (분기 4124, GetCodInfDT 4126/4128/4132, GetUidMst 4130/4133, UidEngNam 4137)",
"confidence": "high",
"blocker": "clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) · clsCommonLib.SystemDateTime",
"note": "_Refer 문맥을 실제로 사용한다. Rows.Count 검사 없음.",
"action": "참조 내원용 doctor 사전을 만든 뒤 UidEngNam 을 돌려준다."
},
{
"tag": "OCM_담당의사면허번호",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var codDT = PatTyp==\"I\" ? (ComInf.ComPrgStt==\"ILV\" ? GetCodInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum, SystemDateTime, 1)) : GetCodInfDT(ComNum, ComInf.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0][\"CodDtrCod\"], PatTyp==\"I\" ? codDT.Rows[0][\"CodEndDtm\"].Substring(0,8) : ComInf.ComAcpDtm.Substring(0,8)); return uid?[\"UidLicNum\"].Trim() ?? \"\";",
"evidence": "4144-4167 (분기 4149, GetCodInfDT 4151/4153/4157, GetUidMst 4155/4158, UidLicNum 4162)",
"confidence": "high",
"blocker": "clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) · clsCommonLib.SystemDateTime",
"note": "최종 컬럼만 UidLicNum 으로 다르다. Rows.Count 검사 없음.",
"action": "FromDoctor 에 한 줄 추가 — Field(\"UidLicNum\")."
},
{
"tag": "OCM_담당의사면허번호_Refer",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var codDT = PatTyp_Refer==\"I\" ? (ComInf_Refer.ComPrgStt==\"ILV\" ? GetCodInfDT(ComNum_Refer, ComInf_Refer.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum_Refer, SystemDateTime, 1)) : GetCodInfDT(ComNum_Refer, ComInf_Refer.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0][\"CodDtrCod\"], PatTyp_Refer==\"I\" ? codDT.Rows[0][\"CodEndDtm\"].Substring(0,8) : ComInf_Refer.ComAcpDtm.Substring(0,8)); return uid?[\"UidLicNum\"].Trim() ?? \"\";",
"evidence": "4169-4192 (분기 4174, GetCodInfDT 4176/4178/4182, GetUidMst 4180/4183, UidLicNum 4187)",
"confidence": "high",
"blocker": "clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) · clsCommonLib.SystemDateTime",
"note": "_Refer 문맥을 실제로 사용한다. Rows.Count 검사 없음.",
"action": "참조 내원용 doctor 사전에서 UidLicNum 을 돌려준다."
},
{
"tag": "OCM_담당의사싸인",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var codDT = PatTyp==\"I\" ? (ComInf.ComPrgStt==\"ILV\" ? GetCodInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum, SystemDateTime, 1)) : GetCodInfDT(ComNum, ComInf.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0][\"CodDtrCod\"], PatTyp==\"I\" ? codDT.Rows[0][\"CodEndDtm\"].Substring(0,8) : ComInf.ComAcpDtm.Substring(0,8)); if(uid==null || string.IsNullOrEmpty(uid[\"UidImgPth\"])) return \"\"; var local = downloadFolder + Path.GetFileName(uid[\"UidImgPth\"]); FileTransfer.DownLoadFile(1, uid[\"UidImgPth\"], local); return local;",
"evidence": "4244-4309 (다운로드 폴더 4251, GetCodInfDT 4262/4264/4268, GetUidMst 4266/4269, UidImgPth 4274/4281/4282, DownLoadFile 4288, Return sLocalPath 4300)",
"confidence": "high",
"blocker": "clsCommonLib.GetCodInfDT · clsCommonLib.GetUidMst · clsCommonLib.GetInstallPathFromServerInfoFile · clsCommonLib.CreateDirectory · M.EMR.FileTransfer.clsFileTransfer.DownLoadFile — 값이 아니라 \u003c서버에서 내려받은 파일 경로\u003e를 돌려준다.",
"note": "레거시가 이상하다: 0..4 로 5회 반복 루프를 만들어 놓고 본문 끝에서 sRemotePath 가 비지 않으면 무조건 Exit For 하므로 \u003c항상 1회\u003e만 돈다(4276-4298). sRemotePath 는 바로 위에서 UidImgPth 가 비지 않음을 확인한 값이라 조건은 사실상 항상 참이다. \"|\" 를 붙였다가 다시 잘라내는 것도 무의미한 왕복이다(4281-4285). 실패 시 MsgBox 로 대화상자를 띄운다(4289).",
"action": "담당의 행의 UidImgPth 로 파일서버에서 내려받아 로컬 경로를 돌려주는 경로를 만든다 — 값 생성은 순증이다."
},
{
"tag": "OCM_담당의사싸인_Refer",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var codDT = PatTyp_Refer==\"I\" ? (ComInf_Refer.ComPrgStt==\"ILV\" ? GetCodInfDT(ComNum_Refer, ComInf_Refer.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum_Refer, SystemDateTime, 1)) : GetCodInfDT(ComNum_Refer, ComInf_Refer.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0][\"CodDtrCod\"], PatTyp_Refer==\"I\" ? codDT.Rows[0][\"CodEndDtm\"].Substring(0,8) : ComInf_Refer.ComAcpDtm.Substring(0,8)); if(uid==null || string.IsNullOrEmpty(uid[\"UidImgPth\"])) return \"\"; var local = downloadFolder + Path.GetFileName(uid[\"UidImgPth\"]); FileTransfer.DownLoadFile(1, uid[\"UidImgPth\"], local); return local;",
"evidence": "4311-4360대 (다운로드 폴더 4319, GetCodInfDT 4330/4332/4336, GetUidMst 4334/4337, UidImgPth 4342/4350)",
"confidence": "medium",
"blocker": "clsCommonLib.GetCodInfDT · clsCommonLib.GetUidMst · clsCommonLib.GetInstallPathFromServerInfoFile · clsCommonLib.CreateDirectory · M.EMR.FileTransfer.clsFileTransfer.DownLoadFile",
"note": "4350 이후 다운로드 루프 꼬리를 직접 읽지 못했다(4244 판과 4311 판이 4340 까지 완전히 동형이라 나머지도 같다고 보았다) — 그래서 confidence 를 medium 으로 둔다. 앞부분은 _Refer 문맥을 실제로 사용한다.",
"action": "참조 내원 담당의로 같은 다운로드 경로를 만든다."
},
{
"tag": "OCM_담당의사전문의번호",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var codDT = PatTyp==\"I\" ? (ComInf.ComPrgStt==\"ILV\" ? GetCodInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum, SystemDateTime, 1)) : GetCodInfDT(ComNum, ComInf.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0][\"CodDtrCod\"], PatTyp==\"I\" ? codDT.Rows[0][\"CodEndDtm\"].Substring(0,8) : ComInf.ComAcpDtm.Substring(0,8)); return uid?[\"UidSpcLic\"].Trim() ?? \"\";",
"evidence": "4194-4217 (분기 4199, GetCodInfDT 4201/4203/4207, GetUidMst 4205/4208, UidSpcLic 4212)",
"confidence": "high",
"blocker": "clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) · clsCommonLib.SystemDateTime",
"note": "최종 컬럼만 UidSpcLic 으로 다르다. Rows.Count 검사 없음.",
"action": "FromDoctor 에 전문의번호 분기를 추가한다 — doctor 사전은 M_UidMst 한 행이므로(PatientContextStore.cs:166) 그 행에 컬럼이 있는지부터 확인한다."
},
{
"tag": "OCM_담당의사전문의번호_Refer",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var codDT = PatTyp_Refer==\"I\" ? (ComInf_Refer.ComPrgStt==\"ILV\" ? GetCodInfDT(ComNum_Refer, ComInf_Refer.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum_Refer, SystemDateTime, 1)) : GetCodInfDT(ComNum_Refer, ComInf_Refer.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0][\"CodDtrCod\"], PatTyp_Refer==\"I\" ? codDT.Rows[0][\"CodEndDtm\"].Substring(0,8) : ComInf_Refer.ComAcpDtm.Substring(0,8)); return uid?[\"UidSpcLic\"].Trim() ?? \"\";",
"evidence": "4219-4242 (분기 4224, GetCodInfDT 4226/4228/4232, GetUidMst 4230/4233, UidSpcLic 4237)",
"confidence": "high",
"blocker": "clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) · clsCommonLib.SystemDateTime",
"note": "_Refer 문맥을 실제로 사용한다. Rows.Count 검사 없음.",
"action": "PatientTagResolver.FromDoctor 의 태그 화이트리스트(:278)에 참조 내원 담당의 행을 하나 더 받는 분기를 추가하고 전문의번호 컬럼을 돌려준다."
},
{
"tag": "OCM_로그인_사용자싸인",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var pth = WrkInfo.UidMst[\"UidImgPth\"]; if(string.IsNullOrEmpty(pth)) return \"\"; var local = GetInstallPathFromServerInfoFile()+\"\\\\Tmp\\\\DownLoadedFiles\\\\Signature\\\\\" + pth.Split(\u0027\\\\\u0027).Last(); FileTransfer.DownLoadFile(1, pth, local); return local;",
"evidence": "4847-4902 (다운로드 폴더 4855, UidImgPth 4862/4869/4870, DownLoadFile 4876, Return sLocalPath 4888, Catch→MessageBox 4894-4897)",
"confidence": "high",
"blocker": "clsCommonLib.GetInstallPathFromServerInfoFile · clsCommonLib.CreateDirectory · M.EMR.FileTransfer.clsFileTransfer.DownLoadFile — 서버 파일을 내려받아 로컬 경로를 돌려준다.",
"note": "환자 문맥을 전혀 쓰지 않는다 — 값의 출처는 로그인 사용자 moWrkInfoBiz.UidMst 의 UidImgPth 뿐이다(그 자체는 context-scalar 성격). 담당의사싸인과 같은 \u0027항상 1회 도는 5회 루프\u0027 가 그대로 있다(4864-4886). 오류는 Catch 에서 MessageBox 후 \"\" 반환(4894-4897) — 미리보기에서는 사유 반환으로 바꾸면 된다.",
"action": "KindOf 의 \u0027싸인\u0027 → Image 분류를 우회하는 needs-lookup 경로를 만들고 M_UidMst.UidImgPth 를 파일서버에서 내려받아 로컬 경로를 돌려준다 — 분류를 그대로 두면 영원히 사유 문구만 나온다."
},
{
"tag": "OCM_병동",
"kind": "needs-lookup",
"row": "CowInf",
"column": "CowWadCod",
"formula": "if (PatTyp==\"I\") { adp = (ComInf.ComPrgStt==\"ILV\") ? ComInf.ComLevDtm.Substring(0,12) : SystemDateTime; } else if (PatTyp.ToUpper()==\"O\" \u0026\u0026 ComDayCar.ToUpper()==\"Y\") { adp = SystemDateTime; } else return \"\"; dt = GetCowInfDT(ComNum, adp); return dt.Rows.Count\u003e0 ? dt.Rows[0][\"CowWadCod\"].ToString() : \"\";",
"evidence": "6718-6748 (본문), dtCommonLib.vb 2979-3050 (GetCowInfDT 실체)",
"confidence": "medium",
"blocker": "clsCommonLib.GetInstance.GetCowInfDT(ComNum, AdpDtm) — 병상이력 P_CowInf 를 시점으로 다시 조회한다. 문맥이 든 CowInf 한 행이 아니라 이력 전체를 훑는다.",
"note": "GetCowInfDT 는 piJoin=0 으로 \u0027SELECT * FROM P_CowInf WHERE CowComNum=:ComNum AND :AdpDtm BETWEEN CowStrDtm AND CowEndDtm\u0027 를 실행한다(dtCommonLib.vb 2996-3047). ORDER BY 가 없는데 Rows(0) 를 집는다 — 구간이 겹치면 결과가 비결정적이다. ComPrgStt/ComLevDtm 는 ComInf 컬럼, ComDayCar 는 GetPatientInfo 경유(bzPatientInfo.vb 1066-1074)라 어느 표 컬럼인지 본문만으로는 확정 못 한다.",
"action": "Direct 에 (CowInf, CowWadCod) 를 넣되 레거시의 입원/낮병동 분기를 같이 옮긴다 — 외래이고 ComDayCar≠\u0027Y\u0027 면 빈 값이어야 한다."
},
{
"tag": "OCM_병동_Refer",
"kind": "needs-lookup",
"row": "CowInf",
"column": "CowWadCod",
"formula": "if (PatTyp_Refer==\"I\") { var cow = ComInf_Refer.ComPrgStt==\"ILV\" ? GetCowInfDT(ComNum_Refer, ComInf_Refer.ComLevDtm.Substring(0,12)) : GetCowInfDT(ComNum_Refer, SystemDateTime); return cow?.Rows.Count\u003e0 ? cow.Rows[0][\"CowWadCod\"].ToString() : \"\"; } if (PatTyp_Refer.ToUpper()==\"O\" \u0026\u0026 GetComInfDR(ComNum_Refer)[\"ComDayCar\"]==\"Y\") { var cow = GetCowInfDT(ComNum_Refer, SystemDateTime); return cow?.Rows.Count\u003e0 ? cow.Rows[0][\"CowWadCod\"].ToString() : \"\"; } return \"\";",
"evidence": "6760-6799 (입원분기 6767, GetCowInfDT 6770/6772/6781, CowWadCod 6776/6784, GetComInfDR(...)[\"ComDayCar\"] 6780)",
"confidence": "high",
"blocker": "clsCommonLib.GetCowInfDT(psComNum, psAdpDtm) — clsCommonLib.vb 10555 · clsCommonLib.GetComInfDR(piComNum) — clsCommonLib.vb 10484.",
"note": "비-Refer 판(6718) 과 달리 낮병동 판정을 문맥 속성이 아니라 GetComInfDR(ComNum_Refer) 재조회로 한다(6780) — 조회가 하나 더 늘어난다. 이 함수는 _Refer 문맥을 제대로 사용한다.",
"action": "참조 내원의 CowInf 행을 따로 읽어 CowWadCod 를 돌려주는 매핑을 추가한다 — 현행 CowInf 는 주 내원분 한 행뿐이다."
},
{
"tag": "OCM_병동_병실",
"kind": "needs-lookup",
"row": "CowInf",
"column": "CowWadCod, CowRomCod",
"formula": "if (PatTyp!=\"I\") return \"\"; var cow = ComInf.ComPrgStt==\"ILV\" ? GetCowInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12)) : GetCowInfDT(ComNum, SystemDateTime); if(cow==null||cow.Rows.Count==0) return \"\"; return cow.Rows[0][\"CowWadCod\"] + \" / \" + cow.Rows[0][\"CowRomCod\"];",
"evidence": "7000-7031 (입원분기 7007, ComPrgStt 7009, GetCowInfDT 7010/7012, 결합 7016)",
"confidence": "high",
"blocker": "clsCommonLib.GetCowInfDT(psComNum, psAdpDtm) — clsCommonLib.vb 10555.",
"note": "OCM_병동(6718) 에 있던 낮병동(외래+ComDayCar=Y) 분기가 여기에는 \u003c없다\u003e — 입원(PatTyp=\"I\") 이 아니면 무조건 빈 문자열이다. 구분자는 앞뒤 공백이 붙은 \" / \" 이고 코드값을 그대로 잇는다(이름 변환 없음).",
"action": "Computed 에 CowWadCod+CowRomCod 결합 규칙을 추가한다."
},
{
"tag": "OCM_병동_병실_베드",
"kind": "needs-lookup",
"row": "CowInf",
"column": "CowWadCod + CowRomCod + CowBedCod",
"formula": "if (PatTyp != \"I\") return \"\"; var dtm = (ComInf.ComPrgStt == \"ILV\") ? ComInf.ComLevDtm.Substring(0,12) : SystemDateTime; var cow = GetCowInfDT(ComNum, dtm); return cow.Rows.Count\u003e0 ? $\"{cow[0].CowWadCod} / {cow[0].CowRomCod} / {cow[0].CowBedCod}\" : \"\";",
"evidence": "bzDataInterface.vb 7033-7064 (본문), 핵심 7040·7042·7043·7045·7049",
"confidence": "medium",
"blocker": "clsCommonLib.GetCowInfDT(ComNum, AdpDtm) — 시점 필터가 붙은 P_CowInf 조회다. 부수적으로 clsCommonLib.SystemDateTime 도 필요.",
"note": "GetCowInfDT 는 clsCommonLib.vb 10555 → dtCommonLib.vb 2979(piJoin=0) 이고 실체는 `SELECT * FROM P_CowInf WHERE CowComNum=:ComNum AND :AdpDtm BETWEEN CowStrDtm AND CowEndDtm` 이다. 즉 컬럼 셋은 문맥의 CowInf 행과 같지만, 레거시는 \u003c그 시점에 유효한 행\u003e을 다시 골라 Rows(0) 를 쓴다. SheetMe 의 CowInf 단일 행이 이미 \u0027현재 유효한 행\u0027이라면 그대로 대체 가능 — 그 전제가 확인되지 않아 medium. 구분자는 공백 포함 \" / \" 다. 외래(PatTyp=\"O\")·그 외는 무조건 \"\" — 병실베드와 달리 낮병동 분기가 없다. 예외 시 레거시는 MessageBox 를 띄우고 \"\" 를 돌려준다.",
"action": "Computed 에 CowWadCod+CowRomCod+CowBedCod 결합 규칙을 추가한다 — 결합 구분자는 레거시 본문에서 확인해야 한다."
},
{
"tag": "OCM_병실베드",
"kind": "needs-lookup",
"row": "CowInf",
"column": "CowBedCod",
"formula": "if (PatTyp==\"I\") { adp = (ComInf.ComPrgStt==\"ILV\") ? ComInf.ComLevDtm.Substring(0,12) : SystemDateTime; dt=GetCowInfDT(ComNum,adp); return dt.Rows.Count\u003e0 ? dt.Rows[0][\"CowBedCod\"].ToString() : \"\"; } else if (PatTyp.ToUpper()==\"O\") { if (ComDayCar.ToUpper()==\"Y\") { dt=GetCowInfDT(ComNum,SystemDateTime); return dt.Rows.Count\u003e0 ? dt.Rows[0][\"CowBedCod\"].ToString() : \"\"; } return null; } else return \"\";",
"evidence": "6946-6989",
"confidence": "medium",
"blocker": "clsCommonLib.GetInstance.GetCowInfDT — OCM_병동 과 동일한 이력 조회.",
"note": "레거시 버그를 그대로 적는다: PatTyp=\"O\" 이면서 ComDayCar\u003c\u003e\"Y\" 인 경로에는 Return 이 없다(6969 If 가 6983 에서 Else 없이 닫힌다). VB 는 이때 String 기본값 Nothing 을 돌려준다 — \"\" 가 아니다. OCM_병동(6738)은 같은 조건을 ElseIf 한 줄로 합쳐 \"\" 를 주는데, 병실베드만 갈라져 있다.",
"action": "Direct 에 (CowInf, CowBedCod) 를 추가한다 — 병동과 같은 입원/낮병동 분기를 함께 옮긴다."
},
{
"tag": "OCM_부상병POA",
"kind": "needs-lookup",
"row": "",
"column": "OkdPoaTyp",
"formula": "dt = Okd_상병쿼리(ComNum, \"S\") 실행; foreach(row) sb.AppendLine(row[\"OkdPoaTyp\"].ToString().Trim()); return sb.ToString();",
"evidence": "9990-10029 (본문), 9999 (\"S\"), 10011 (OkdPoaTyp)",
"confidence": "low",
"blocker": "Okd_상병쿼리 + ExecuteQuery. 더 큰 문제: OkdPoaTyp 는 HspCod 분기 중 NYJB(14221)·Case Else(14342) SELECT 목록에만 있다. KIMEYE·SPHH·SYBS 분기의 SELECT 에는 OkdPoaTyp 가 없어서 Item(\"OkdPoaTyp\") 가 예외로 떨어진다.",
"note": "레거시가 이상해 보여도 그대로 적는다 — 병원 코드에 따라 이 태그는 항상 예외를 던지고 Catch 에서 MessageBox 후 \"\" 를 돌려준다(10024-10025). 옮길 때는 컬럼 부재를 사유로 돌려주는 게 맞다.",
"action": "부상병 행 조회를 추가하고 OkdPoaTyp 를 돌려주는 매핑을 넣는다."
},
{
"tag": "OCM_부상병POA_Refer",
"kind": "needs-lookup",
"row": "",
"column": "OkdPoaTyp",
"formula": "dt = Okd_상병쿼리(ComNum_Refer, \"S\") 실행; foreach(row) sb.AppendLine(row[\"OkdPoaTyp\"].ToString().Trim()); return sb.ToString();",
"evidence": "10031-10070 (본문), 10037 (ComNum_Refer), 10040 (\"S\"), 10052 (OkdPoaTyp)",
"confidence": "low",
"blocker": "Okd_상병쿼리 + ExecuteQuery. OkdPoaTyp 컬럼이 일부 HspCod 분기 SELECT 에 없다(OCM_부상병POA 와 동일). ComNum_Refer 문맥도 미확인.",
"note": "OCM_부상병POA 와 ComNum 만 다르다.",
"action": "참조 내원의 부상병 행(OkdPoaTyp)을 읽는 신규 조회를 PatientContextStore 에 추가한다 — 현행에 상병 계열 조회 자체가 없다."
},
{
"tag": "OCM_부상병명_Refer",
"kind": "needs-lookup",
"row": "",
"column": "KcdKorNam",
"formula": "dt = Okd_상병쿼리(ComNum_Refer, \"S\") 실행; foreach(row) sb.AppendLine(row[\"KcdKorNam\"].ToString().Trim()); return sb.ToString();",
"evidence": "9949-9988 (본문), 9955 (ComNum_Refer), 9958 (\"S\")",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + ExecuteQuery. ComNum_Refer 미확인.",
"note": "비-Refer 판 OCM_부상병명(9884-9947)에는 HspCod 분기가 있다 — SPHH 는 KcdKorNam 뒤에 컬럼 \"확의\" 를 붙이고(9909), JEGG 는 OkdCfmSus=\"배\" 일 때 \"(배제)\" 를 붙인다(9916-9918). Refer 판에는 그 분기가 없이 KcdKorNam 만 쓴다 — 레거시가 두 판을 다르게 유지하고 있다.",
"action": "참조 내원 부상병 목록을 읽어 KcdKorNam 을 돌려주는 조회·매핑을 추가한다."
},
{
"tag": "OCM_부상병코드",
"kind": "needs-lookup",
"row": "",
"column": "KcdElcCod",
"formula": "dt = Okd_상병쿼리(ComNum, \"S\") 실행; foreach(row) sb.AppendLine(row[\"KcdElcCod\"].ToString().Trim()); return sb.ToString();",
"evidence": "9801-9840 (본문), 9810 (psOkdTyp=\"S\"), 14360-14361 (S → OkdDspSeq\u003c\u003e\u00271\u0027)",
"confidence": "medium",
"blocker": "Okd_상병쿼리(piComNum, \"S\") + clsDAFactory.ExecuteQuery.",
"note": "\"S\"=부상병은 쿼리에 AND OkdDspSeq \u003c\u003e \u00271\u0027 을 덧붙인다(14361). 주상병은 \"M\"(OkdDspSeq=\u00271\u0027). 세로 나열(AppendLine)이라 후행 CRLF 있음.",
"action": "부상병 목록에서 KcdElcCod 를 돌려주는 조회·매핑을 추가한다."
},
{
"tag": "OCM_부상병코드_Refer",
"kind": "needs-lookup",
"row": "",
"column": "KcdElcCod",
"formula": "dt = Okd_상병쿼리(ComNum_Refer, \"S\") 실행; foreach(row) sb.AppendLine(row[\"KcdElcCod\"].ToString().Trim()); return sb.ToString();",
"evidence": "9842-9881 (본문), 9848 (ComNum_Refer), 9851 (\"S\")",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + ExecuteQuery. ComNum_Refer 미확인.",
"note": "OCM_부상병코드 와 ComNum 만 다르다.",
"action": "참조 내원 부상병 목록에서 KcdElcCod 를 돌려주는 조회·매핑을 추가한다."
},
{
"tag": "OCM_상병명_가로",
"kind": "needs-lookup",
"row": "",
"column": "KcdKorNam + OkdCfmSus",
"formula": "dt = Okd_상병쿼리(ComNum, \"T\") 실행; return string.Join(\", \", dt.Rows.Select(r =\u003e r[\"KcdKorNam\"].ToString().Trim() + (r[\"OkdCfmSus\"].ToString().Trim()==\"확\" ? \"(확진)\" : r[\"OkdCfmSus\"].ToString().Trim()==\"의\" ? \"(의증)\" : \"\")));",
"evidence": "8170-8231 (본문), 14160-14162·14358-14366·14616 (Okd_상병쿼리)",
"confidence": "medium",
"blocker": "Okd_상병쿼리(piComNum) + clsDAFactory.ExecuteQuery — O_OkdInf / M_KcdMst 조인이라 다섯 문맥 행으로는 못 닿는다. TAG-REMAINING-360.md 가 CTX 로 분류했지만 실제로는 조회 태그다.",
"note": "Okd_상병쿼리 기본값 psOkdTyp=\"T\"(전체). 쿼리는 O_OkdInf INNER JOIN P_ComInf INNER JOIN M_KcdMst, OkdStt=\u0027E\u0027, ROW_NUMBER() PARTITION BY KcdCod ORDER BY KcdStrDte DESC 로 OKD_RN=1 만, 마지막 ORDER BY OkdDspSeq(14616). HspCod 별로 SQL 이 갈린다(KIMEYE/NYJB/SYBS/Case Else, 14168-14356) — 옮길 때 병원별 분기를 결정해야 한다. 구분자는 마지막 행만 빼고 \", \".",
"action": "상병 목록을 가로로 잇는 조회·조립을 추가한다(KcdKorNam + OkdCfmSus)."
},
{
"tag": "OCM_상병명_가로_Refer",
"kind": "needs-lookup",
"row": "",
"column": "KcdKorNam + OkdCfmSus",
"formula": "dt = Okd_상병쿼리(ComNum_Refer, \"T\") 실행; return string.Join(\", \", dt.Rows.Select(r =\u003e r[\"KcdKorNam\"].Trim() + (r[\"OkdCfmSus\"].Trim()==\"확\" ? \"(확진)\" : r[\"OkdCfmSus\"].Trim()==\"의\" ? \"(의증)\" : \"\")));",
"evidence": "8233-8291",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + ExecuteQuery. 더해서 moPatInfoBiz.ComNum_Refer(참조 내원번호) 가 SheetMe 문맥에 있는지 미확인.",
"note": "OCM_상병명_가로 와 본문이 같고 ComNum 만 ComNum_Refer(8241). 다만 비-Refer 판(8198)에 있는 루프 선두의 sCfmSus=\"\" 초기화가 여기엔 없다 — If/ElseIf/Else 가 항상 대입하므로 동작 차이는 없다.",
"action": "참조 내원 상병 목록을 한 줄로 잇는 조회·조립을 추가한다 — KcdKorNam 에 OkdCfmSus(확진/의증)를 붙이는 규칙까지 같이 옮긴다."
},
{
"tag": "OCM_상병명_영어_가로",
"kind": "needs-lookup",
"row": "",
"column": "KcdEngNam",
"formula": "dt = Okd_상병쿼리(ComNum, \"T\") 실행; return string.Join(\", \", dt.Rows.Select(r =\u003e r[\"KcdEngNam\"].ToString().Trim()));",
"evidence": "8469-8515",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + clsDAFactory.ExecuteQuery — O_OkdInf/M_KcdMst 조회.",
"note": "확진·의증 표기 없음. 한글판(OCM_상병명_가로)과 달리 OkdCfmSus 를 보지 않는다.",
"action": "상병 목록의 KcdEngNam 을 가로로 잇는 조회·조립을 추가한다."
},
{
"tag": "OCM_상병명_영어_가로_Refer",
"kind": "needs-lookup",
"row": "",
"column": "KcdEngNam",
"formula": "dt = Okd_상병쿼리(ComNum_Refer, \"T\") 실행; return string.Join(\", \", dt.Rows.Select(r =\u003e r[\"KcdEngNam\"].ToString().Trim()));",
"evidence": "8517-8563",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + ExecuteQuery. ComNum_Refer 문맥 존재 여부 미확인.",
"note": "8524 만 ComNum_Refer 로 다르고 나머지는 OCM_상병명_영어_가로 와 동일하다.",
"action": "참조 내원 상병 목록의 KcdEngNam 을 가로로 잇는 조회·조립을 추가한다."
},
{
"tag": "OCM_상병명_영어_세로",
"kind": "needs-lookup",
"row": "",
"column": "KcdEngNam",
"formula": "dt = Okd_상병쿼리(ComNum, \"T\") 실행; foreach(row) sb.AppendLine(row[\"KcdEngNam\"].ToString().Trim()); return sb.ToString();",
"evidence": "8565-8604",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + ExecuteQuery.",
"note": "AppendLine 이라 마지막 행 뒤에도 CRLF 가 붙는다 — 가로판의 Join 과 달리 후행 개행이 남는다. 그대로 옮겨야 미리보기 줄수가 맞다.",
"action": "상병 목록의 KcdEngNam 을 세로로 잇는 조회·조립을 추가한다."
},
{
"tag": "OCM_상병명_영어_세로_Refer",
"kind": "needs-lookup",
"row": "",
"column": "KcdEngNam",
"formula": "dt = Okd_상병쿼리(ComNum_Refer, \"T\") 실행; foreach(row) sb.AppendLine(row[\"KcdEngNam\"].ToString().Trim()); return sb.ToString();",
"evidence": "8606-8645",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + ExecuteQuery. ComNum_Refer 미확인.",
"note": "8612 만 ComNum_Refer. 후행 CRLF 동일.",
"action": "참조 내원 상병 목록의 KcdEngNam 을 줄바꿈으로 잇는 조회·조립을 추가한다 — 가로/세로는 구분자만 다르므로 한 헬퍼로 묶는다."
},
{
"tag": "OCM_상병코드_가로",
"kind": "needs-lookup",
"row": "",
"column": "KcdElcCod",
"formula": "dt = Okd_상병쿼리(ComNum, \"T\") 실행; return string.Join(\", \", dt.Rows.Select(r =\u003e r[\"KcdElcCod\"].ToString().Trim()));",
"evidence": "8647-8693",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + ExecuteQuery.",
"note": "KcdCod 가 아니라 KcdElcCod(전자청구용 코드)다. 쿼리는 KcdCod 와 KcdElcCod 를 둘 다 뽑는데(14200) 태그가 고르는 건 KcdElcCod.",
"action": "상병 목록의 KcdElcCod 를 가로로 잇는 조회·조립을 추가한다."
},
{
"tag": "OCM_상병코드_가로_Refer",
"kind": "needs-lookup",
"row": "",
"column": "KcdElcCod",
"formula": "dt = Okd_상병쿼리(ComNum_Refer, \"T\") 실행; return string.Join(\", \", dt.Rows.Select(r =\u003e r[\"KcdElcCod\"].ToString().Trim()));",
"evidence": "8695-8741",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + ExecuteQuery. ComNum_Refer 미확인.",
"note": "8702 만 ComNum_Refer.",
"action": "참조 내원 상병 목록의 KcdElcCod 를 가로로 잇는 조회·조립을 추가한다."
},
{
"tag": "OCM_상병코드_세로",
"kind": "needs-lookup",
"row": "",
"column": "KcdElcCod",
"formula": "dt = Okd_상병쿼리(ComNum, \"T\") 실행; foreach(row) sb.AppendLine(row[\"KcdElcCod\"].ToString().Trim()); return sb.ToString();",
"evidence": "8743-8782",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + ExecuteQuery.",
"note": "AppendLine — 후행 CRLF 있음.",
"action": "상병 목록의 KcdElcCod 를 세로로 잇는 조회·조립을 추가한다."
},
{
"tag": "OCM_상병코드_세로_Refer",
"kind": "needs-lookup",
"row": "",
"column": "KcdElcCod",
"formula": "dt = Okd_상병쿼리(ComNum_Refer, \"T\") 실행; foreach(row) sb.AppendLine(row[\"KcdElcCod\"].ToString().Trim()); return sb.ToString();",
"evidence": "8784-8823",
"confidence": "medium",
"blocker": "Okd_상병쿼리 + ExecuteQuery. ComNum_Refer 미확인.",
"note": "8790 만 ComNum_Refer.",
"action": "참조 내원 상병 목록의 KcdElcCod 를 세로로 잇는 조회·조립을 추가한다."
},
{
"tag": "OCM_수술_OprPatETC",
"kind": "needs-lookup",
"row": "",
"column": "OprPatEtc",
"formula": "dt = GetOprInfDT(); return dt != null \u0026\u0026 dt.Rows.Count \u003e 0 ? dt.Rows[0][\"OprPatEtc\"].ToString() : \"\";",
"evidence": "13190-13201",
"confidence": "medium",
"blocker": "GetOprInfDT() (13203) — 자체 SQL 조회.",
"note": "태그 이름은 OprPatETC 인데 읽는 컬럼은 OprPatEtc 다(13196). Trim 없음.",
"action": "수술 정보(S_OprInf 계열) 조회를 새로 추가하고 OprPatEtc 를 돌려주는 매핑을 넣는다."
},
{
"tag": "OCM_수술부위",
"kind": "needs-lookup",
"row": "",
"column": "OprRegionName",
"formula": "dt = GetOprInfDT(); return dt != null \u0026\u0026 dt.Rows.Count \u003e 0 ? dt.Rows[0][\"OprRegionName\"].ToString() : \"\";",
"evidence": "13177-13188",
"confidence": "medium",
"blocker": "GetOprInfDT() (13203) — 자체 SQL 조회. OprRegionName 이 그 결과셋의 별칭인지 원컬럼인지는 13203 이하 쿼리를 읽어야 확정된다.",
"note": "Trim 없음 — 원문 그대로 돌려준다.",
"action": "수술 조회를 추가하고 OprRegionName 을 돌려주는 매핑을 넣는다."
},
{
"tag": "OCM_수술상병",
"kind": "needs-lookup",
"row": "",
"column": "OkdNam",
"formula": "dt = GetOprInfDT(); return dt != null \u0026\u0026 dt.Rows.Count \u003e 0 ? dt.Rows[0][\"OkdNam\"].ToString() : \"\";",
"evidence": "13135-13146",
"confidence": "medium",
"blocker": "GetOprInfDT() (같은 파일 13203) — 자체 SQL 조회 함수다. TAG-REMAINING-360.md 도 GetOprInfDT 를 SQL 갈래로 잡아 뒀다(줄 13203).",
"note": "레거시는 Trim 없이 `oDT.Rows(0).Item(\"OkdNam\") \u0026 \"\"` 로 DBNull 을 빈 문자열로 흘린다. GetOprInfDT 는 moPatInfoBiz.GetPatientInfo.Item(\"ComAcpDtm\").Substring(0,8) 을 조건으로 쓴다(13210).",
"action": "수술 조회를 추가하고 OkdNam 을 돌려주는 매핑을 넣는다."
},
{
"tag": "OCM_수술주상병",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var dt = GetOprInfDT_OKDMain(); return (dt != null \u0026\u0026 dt.Rows.Count \u003e 0) ? (dt.Rows[0][\"OkdNam\"] ?? \"\").ToString() : \"\";",
"evidence": "bzDataInterface.vb 13148-13162 (함수 본문), 13364-13372 (GetOprInfDT_OKDMain 진입부)",
"confidence": "high",
"blocker": "GetOprInfDT_OKDMain() — 자체 SQL(WITH OprInfRec ...)을 clsDAFactory.ExecuteQuery 로 돌린다. 바인드는 moPatInfoBiz.ComNum 과 GetPatientInfo.Item(\"ComAcpDtm\").Substring(0,8).",
"note": "문서가 CTX 로 분류했으나 실제로는 조회다. 본문 자체는 단순(첫 행의 OkdNam)이라 조회만 붙이면 그대로 옮겨진다. 형제 태그 OCM_수술상병(13135)은 같은 컬럼을 GetOprInfDT() 로 읽는다 — 주상병만 _OKDMain 판을 쓴다.",
"action": "수술 조회를 추가하고 수술 주상병 결정 규칙을 옮긴다 — 컬럼이 문서에 없어 레거시 본문 확인이 먼저다."
},
{
"tag": "OCM_심사_부상병명",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var dt = Query(Bkd_상병쿼리(ComNum, \"S\"), piComNum: ComNum); return dt.Rows.Count \u003e 0 ? string.Concat(dt.Rows.Select(r =\u003e r[\"KcdKorNam\"].ToString().Trim() + Environment.NewLine)) : \"\";",
"evidence": "bzDataInterface.vb 10326-10365 (본문), 14625-14694 (Bkd_상병쿼리, psOkdTyp=\"S\")",
"confidence": "high",
"blocker": "Bkd_상병쿼리(piComNum, \"S\") — B_OkdInf ⨝ P_ComInf ⨝ M_KcdMst 조회. 추가로 clsCommonLib.GetDtlCodVal(\"CHNGKCDCOD\",\"01\") 과 HspCfg.HspCod 분기(KIMEYE)도 탄다.",
"note": "문서가 CTX 로 분류했으나 실제로는 조회다. 주상병(첫 행만)과 달리 전 행을 AppendLine 으로 이어붙이므로 결과 끝에 개행이 하나 남는다.",
"action": "심사 상병 조회를 새로 추가하고 부상병명을 돌려주는 매핑을 넣는다 — 진료 상병(OKD)과 원천이 다른지 본문에서 확인한다."
},
{
"tag": "OCM_심사_부상병코드",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var dt = Query(Bkd_상병쿼리(ComNum, \"S\"), piComNum: ComNum); return dt.Rows.Count \u003e 0 ? string.Concat(dt.Rows.Select(r =\u003e r[\"KcdElcCod\"].ToString().Trim() + Environment.NewLine)) : \"\";",
"evidence": "bzDataInterface.vb 10285-10324 (본문), 14625-14694 (Bkd_상병쿼리, psOkdTyp=\"S\")",
"confidence": "high",
"blocker": "Bkd_상병쿼리(piComNum, \"S\") — B_OkdInf ⨝ P_ComInf ⨝ M_KcdMst 조회.",
"note": "문서가 CTX 로 분류했으나 실제로는 조회다. 돌려주는 코드는 OkdKcdCod 가 아니라 M_KcdMst 의 KcdElcCod(전자청구 코드)다. 전 행을 개행으로 이어붙인다.",
"action": "심사 상병 조회를 추가하고 부상병코드를 돌려주는 매핑을 넣는다."
},
{
"tag": "OCM_심사_주상병명",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var dt = Query(Bkd_상병쿼리(ComNum, \"M\"), piComNum: ComNum); return dt.Rows.Count \u003e 0 ? dt.Rows[0][\"KcdKorNam\"].ToString() : \"\";",
"evidence": "bzDataInterface.vb 10253-10283 (본문), 14625-14694 (Bkd_상병쿼리, psOkdTyp=\"M\")",
"confidence": "high",
"blocker": "Bkd_상병쿼리(piComNum, \"M\") — B_OkdInf ⨝ P_ComInf ⨝ M_KcdMst 조회.",
"note": "문서가 CTX 로 분류했으나 실제로는 조회다. 부상병 판과 달리 Trim 없이 첫 행만 돌려준다.",
"action": "심사 상병 조회를 추가하고 주상병명을 돌려주는 매핑을 넣는다."
},
{
"tag": "OCM_심사_주상병코드",
"kind": "needs-lookup",
"row": "",
"column": "",
"formula": "var dt = Query(Bkd_상병쿼리(ComNum, \"M\"), piComNum: ComNum); return dt.Rows.Count \u003e 0 ? dt.Rows[0][\"KcdElcCod\"].ToString() : \"\";",
"evidence": "bzDataInterface.vb 10220-10250 (본문), 14625-14694 (Bkd_상병쿼리, psOkdTyp=\"M\")",
"confidence": "high",
"blocker": "Bkd_상병쿼리(piComNum, \"M\") — B_OkdInf ⨝ P_ComInf ⨝ M_KcdMst 조회.",
"note": "문서가 CTX 로 분류했으나 실제로는 조회다. OkdKcdCod 가 아니라 KcdElcCod 를 돌려준다.",
"action": "심사 상병 조회를 추가하고 주상병코드를 돌려주는 매핑을 넣는다."
},
{
"tag": "OCM_외래내원일시_Refer",
"kind": "formatted",
"row": "ComInf",
"column": "ComAcpDtm",
"formula": "Data2Format(ComInf_Refer.ComAcpDtm.Substring(0,12), \"9999-99-99 99:99\") // 참조 내원(ComNum_Refer)의 P_ComInf 행",
"evidence": "bzDataInterface.vb 1074-1078 (본문), 1080-1084 (Private 외래내원일시), bzPatientInfo.vb 800-802 (ComInfDR_Refer → PatientInfoBiz_Refer.GetComInfDR), 784-790 (GetComInfDR = P_ComInf 행)",
"confidence": "medium",
"blocker": "ComInfDR_Refer 는 SetPatientInfo_Refer(piComNum) 로 별도 주입되는 \u003c두 번째 내원 문맥\u003e의 P_ComInf 행이다(bzPatientInfo.vb 329-336, 758-760). SheetMe 에 참조 내원 ComNum 을 넣을 통로가 아직 없다.",
"note": "컬럼·계산 자체는 비-Refer 판과 완전히 동일하고 문맥만 다르다. PatientInfoBiz_Refer 는 미설정 시 빈 bzPatientInfo 를 지연 생성하므로 GetPatientInfo 가 Nothing 이 되고, 이 함수는 Try/Catch 가 없어 NullReference 를 그대로 던진다.",
"action": "참조 내원의 ComInf 를 읽는 경로를 만들고 ComAcpDtm 을 Stamp 로 찍는다 — 기존 Stamp(:112-115)를 그대로 쓸 수 있다."
}
]
+124
View File
@@ -0,0 +1,124 @@
[needs-lookup] ETC_로그인사용자_서명
formula: path = clsCommonLib.GetUserInfo().UidImgPth; if (path == "") return ""; local = InstallPath + "\\Tmp\\DownLoadedFiles\\Signature\\" + path.Split('\\').Last(); FileTransfer.DownLoadFile(1, path, local); return local;
blocker: clsCommonLib.GetInstance.GetUserInfo (UidImgPth), clsCommonLib.GetInstance.GetInstallPathFromServerInfoFile, clsCommonLib.GetInstance.CreateDirectory, M.EMR.FileTransfer.clsFileTransfer.DownLoadFile — 파일서버 다운로드가 필요해 지금은 못 옮긴다
[needs-lookup] OCM_OPNAME
formula: dt = GetOprInfDT(); return dt?.Rows.Count > 0 ? dt.Rows[0]["OPNAME"].ToString().Trim() : null;
blocker: GetOprInfDT (bzDataInterface.vb:13203) — 수술 정보 SQL 을 먼저 옮겨야 한다
[needs-lookup] OCM_STFDTR
formula: var codDT = PatTyp=="I" ? (ComInf.ComPrgStt=="ILV" ? GetCodInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum, SystemDateTime, 1)) : GetCodInfDT(ComNum, ComInf.ComAcpDtm.Substring(0,12), 1); if(codDT.Rows.Count==0) return ""; var stf = codDT.Rows[0]["CodStfDtr"]; if(string.IsNullOrEmpty(stf)) return ""; var uid = GetUidMst(stf, PatTyp=="I" ? codDT.Rows[0]["CodEndDtm"].Substring(0,8) : ComInf.ComAcpDtm.Substring(0,8)); return uid?["UidNam"].Trim() ?? "";
blocker: clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) — 둘 다 문맥의 다섯 행이 아니라 별도 질의다(clsCommonLib.vb 10510 · 10609). clsCommonLib.SystemDateTime 도 필요.
[needs-lookup] OCM_User진료과
formula: if (WrkInfo.UidMst["UidDtrYon"].Trim()=="Y") return (HspCfg.HspStrDte>="20220701" && !string.IsNullOrEmpty(WrkInfo.DepMst["DepOfcNam"])) ? WrkInfo.DepMst["DepOfcNam"] : WrkInfo.DepMst["DepKorNam"]; var dr = GetDepMstDR(GetPatientInfo["AdpDepCod"], ComInf.ComAcpDtm.Substring(0,8)); if(dr==null) return ""; return (HspCfg.HspStrDte>="20220701" && !string.IsNullOrEmpty(dr["DepOfcNam"])) ? dr["DepOfcNam"].Trim() : dr["DepKorNam"].Trim();
blocker: clsCommonLib.GetDepMstDR(psDepCod, psStrDte) — clsCommonLib.vb 13117. 로그인 사용자 분기는 moWrkInfoBiz.UidMst/DepMst(환자 문맥 밖) 가 필요하고, clsCommonLib.HspCfg.HspStrDte 도 필요하다.
[needs-lookup] OCM_User진료과_한글명칭
formula: if (WrkInfo.UidMst["UidDtrYon"].Trim()=="Y") return WrkInfo.DepMst["DepKorNam"]; return GetDepMstDR(GetPatientInfo["AdpDepCod"], ComInf.ComAcpDtm.Substring(0,8))["DepKorNam"].Trim();
blocker: clsCommonLib.GetDepMstDR(psDepCod, psStrDte) — clsCommonLib.vb 13117.
[needs-lookup] OCM_User진료의사명
formula: if (WrkInfo.UidMst["UidDtrYon"].Trim()=="Y") return WrkInfo.UidMst["UidNam"]; var dr = GetUidMst(GetPatientInfo["AdpDtrCod"], ComInf.ComAcpDtm.Substring(0,8)); return dr?["UidNam"].Trim() ?? "";
blocker: clsCommonLib.GetUidMst(psUidCod, psStrDte) — clsCommonLib.vb 10609.
[computed] OCM_내원유형
formula: var typ = ComInf.ComPatTyp; if (typ=="I") return "입원"; if (typ=="O") return ComInf.ComEmgYon=="Y" ? "응급실" : "외래"; return "";
blocker:
[needs-lookup] OCM_담당의사_영문
formula: var codDT = PatTyp=="I" ? (ComInf.ComPrgStt=="ILV" ? GetCodInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum, SystemDateTime, 1)) : GetCodInfDT(ComNum, ComInf.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0]["CodDtrCod"], PatTyp=="I" ? codDT.Rows[0]["CodEndDtm"].Substring(0,8) : ComInf.ComAcpDtm.Substring(0,8)); return uid?["UidEngNam"].Trim() ?? "";
blocker: clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) · clsCommonLib.SystemDateTime
[needs-lookup] OCM_담당의사면허번호
formula: var codDT = PatTyp=="I" ? (ComInf.ComPrgStt=="ILV" ? GetCodInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum, SystemDateTime, 1)) : GetCodInfDT(ComNum, ComInf.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0]["CodDtrCod"], PatTyp=="I" ? codDT.Rows[0]["CodEndDtm"].Substring(0,8) : ComInf.ComAcpDtm.Substring(0,8)); return uid?["UidLicNum"].Trim() ?? "";
blocker: clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) · clsCommonLib.SystemDateTime
[needs-lookup] OCM_담당의사싸인
formula: var codDT = PatTyp=="I" ? (ComInf.ComPrgStt=="ILV" ? GetCodInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum, SystemDateTime, 1)) : GetCodInfDT(ComNum, ComInf.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0]["CodDtrCod"], PatTyp=="I" ? codDT.Rows[0]["CodEndDtm"].Substring(0,8) : ComInf.ComAcpDtm.Substring(0,8)); if(uid==null || string.IsNullOrEmpty(uid["UidImgPth"])) return ""; var local = downloadFolder + Path.GetFileName(uid["UidImgPth"]); FileTransfer.DownLoadFile(1, uid["UidImgPth"], local); return local;
blocker: clsCommonLib.GetCodInfDT · clsCommonLib.GetUidMst · clsCommonLib.GetInstallPathFromServerInfoFile · clsCommonLib.CreateDirectory · M.EMR.FileTransfer.clsFileTransfer.DownLoadFile — 값이 아니라 <서버에서 내려받은 파일 경로>를 돌려준다.
[needs-lookup] OCM_담당의사전문의번호
formula: var codDT = PatTyp=="I" ? (ComInf.ComPrgStt=="ILV" ? GetCodInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12), 1) : GetCodInfDT(ComNum, SystemDateTime, 1)) : GetCodInfDT(ComNum, ComInf.ComAcpDtm.Substring(0,12), 1); var uid = GetUidMst(codDT.Rows[0]["CodDtrCod"], PatTyp=="I" ? codDT.Rows[0]["CodEndDtm"].Substring(0,8) : ComInf.ComAcpDtm.Substring(0,8)); return uid?["UidSpcLic"].Trim() ?? "";
blocker: clsCommonLib.GetCodInfDT(psComNum, psAdpDtm, piJoin) · clsCommonLib.GetUidMst(psUidCod, psStrDte) · clsCommonLib.SystemDateTime
[needs-lookup] OCM_로그인_사용자싸인
formula: var pth = WrkInfo.UidMst["UidImgPth"]; if(string.IsNullOrEmpty(pth)) return ""; var local = GetInstallPathFromServerInfoFile()+"\\Tmp\\DownLoadedFiles\\Signature\\" + pth.Split('\\').Last(); FileTransfer.DownLoadFile(1, pth, local); return local;
blocker: clsCommonLib.GetInstallPathFromServerInfoFile · clsCommonLib.CreateDirectory · M.EMR.FileTransfer.clsFileTransfer.DownLoadFile — 서버 파일을 내려받아 로컬 경로를 돌려준다.
[needs-lookup] OCM_병동
formula: if (PatTyp=="I") { adp = (ComInf.ComPrgStt=="ILV") ? ComInf.ComLevDtm.Substring(0,12) : SystemDateTime; } else if (PatTyp.ToUpper()=="O" && ComDayCar.ToUpper()=="Y") { adp = SystemDateTime; } else return ""; dt = GetCowInfDT(ComNum, adp); return dt.Rows.Count>0 ? dt.Rows[0]["CowWadCod"].ToString() : "";
blocker: clsCommonLib.GetInstance.GetCowInfDT(ComNum, AdpDtm) — 병상이력 P_CowInf 를 시점으로 다시 조회한다. 문맥이 든 CowInf 한 행이 아니라 이력 전체를 훑는다.
[needs-lookup] OCM_병동_병실
formula: if (PatTyp!="I") return ""; var cow = ComInf.ComPrgStt=="ILV" ? GetCowInfDT(ComNum, ComInf.ComLevDtm.Substring(0,12)) : GetCowInfDT(ComNum, SystemDateTime); if(cow==null||cow.Rows.Count==0) return ""; return cow.Rows[0]["CowWadCod"] + " / " + cow.Rows[0]["CowRomCod"];
blocker: clsCommonLib.GetCowInfDT(psComNum, psAdpDtm) — clsCommonLib.vb 10555.
[needs-lookup] OCM_병동_병실_베드
formula: if (PatTyp != "I") return ""; var dtm = (ComInf.ComPrgStt == "ILV") ? ComInf.ComLevDtm.Substring(0,12) : SystemDateTime; var cow = GetCowInfDT(ComNum, dtm); return cow.Rows.Count>0 ? $"{cow[0].CowWadCod} / {cow[0].CowRomCod} / {cow[0].CowBedCod}" : "";
blocker: clsCommonLib.GetCowInfDT(ComNum, AdpDtm) — 시점 필터가 붙은 P_CowInf 조회다. 부수적으로 clsCommonLib.SystemDateTime 도 필요.
[needs-lookup] OCM_병실베드
formula: if (PatTyp=="I") { adp = (ComInf.ComPrgStt=="ILV") ? ComInf.ComLevDtm.Substring(0,12) : SystemDateTime; dt=GetCowInfDT(ComNum,adp); return dt.Rows.Count>0 ? dt.Rows[0]["CowBedCod"].ToString() : ""; } else if (PatTyp.ToUpper()=="O") { if (ComDayCar.ToUpper()=="Y") { dt=GetCowInfDT(ComNum,SystemDateTime); return dt.Rows.Count>0 ? dt.Rows[0]["CowBedCod"].ToString() : ""; } return null; } else return "";
blocker: clsCommonLib.GetInstance.GetCowInfDT — OCM_병동 과 동일한 이력 조회.
[needs-lookup] OCM_부상병POA
formula: dt = Okd_상병쿼리(ComNum, "S") 실행; foreach(row) sb.AppendLine(row["OkdPoaTyp"].ToString().Trim()); return sb.ToString();
blocker: Okd_상병쿼리 + ExecuteQuery. 더 큰 문제: OkdPoaTyp 는 HspCod 분기 중 NYJB(14221)·Case Else(14342) SELECT 목록에만 있다. KIMEYE·SPHH·SYBS 분기의 SELECT 에는 OkdPoaTyp 가 없어서 Item("OkdPoaTyp") 가 예외로 떨어진다.
[needs-lookup] OCM_부상병코드
formula: dt = Okd_상병쿼리(ComNum, "S") 실행; foreach(row) sb.AppendLine(row["KcdElcCod"].ToString().Trim()); return sb.ToString();
blocker: Okd_상병쿼리(piComNum, "S") + clsDAFactory.ExecuteQuery.
[needs-lookup] OCM_상병명_가로
formula: dt = Okd_상병쿼리(ComNum, "T") 실행; return string.Join(", ", dt.Rows.Select(r => r["KcdKorNam"].ToString().Trim() + (r["OkdCfmSus"].ToString().Trim()=="확" ? "(확진)" : r["OkdCfmSus"].ToString().Trim()=="의" ? "(의증)" : "")));
blocker: Okd_상병쿼리(piComNum) + clsDAFactory.ExecuteQuery — O_OkdInf / M_KcdMst 조인이라 다섯 문맥 행으로는 못 닿는다. TAG-REMAINING-360.md 가 CTX 로 분류했지만 실제로는 조회 태그다.
[needs-lookup] OCM_상병명_영어_가로
formula: dt = Okd_상병쿼리(ComNum, "T") 실행; return string.Join(", ", dt.Rows.Select(r => r["KcdEngNam"].ToString().Trim()));
blocker: Okd_상병쿼리 + clsDAFactory.ExecuteQuery — O_OkdInf/M_KcdMst 조회.
[needs-lookup] OCM_상병명_영어_세로
formula: dt = Okd_상병쿼리(ComNum, "T") 실행; foreach(row) sb.AppendLine(row["KcdEngNam"].ToString().Trim()); return sb.ToString();
blocker: Okd_상병쿼리 + ExecuteQuery.
[needs-lookup] OCM_상병코드_가로
formula: dt = Okd_상병쿼리(ComNum, "T") 실행; return string.Join(", ", dt.Rows.Select(r => r["KcdElcCod"].ToString().Trim()));
blocker: Okd_상병쿼리 + ExecuteQuery.
[needs-lookup] OCM_상병코드_세로
formula: dt = Okd_상병쿼리(ComNum, "T") 실행; foreach(row) sb.AppendLine(row["KcdElcCod"].ToString().Trim()); return sb.ToString();
blocker: Okd_상병쿼리 + ExecuteQuery.
[needs-lookup] OCM_수술_OprPatETC
formula: dt = GetOprInfDT(); return dt != null && dt.Rows.Count > 0 ? dt.Rows[0]["OprPatEtc"].ToString() : "";
blocker: GetOprInfDT() (13203) — 자체 SQL 조회.
[needs-lookup] OCM_수술부위
formula: dt = GetOprInfDT(); return dt != null && dt.Rows.Count > 0 ? dt.Rows[0]["OprRegionName"].ToString() : "";
blocker: GetOprInfDT() (13203) — 자체 SQL 조회. OprRegionName 이 그 결과셋의 별칭인지 원컬럼인지는 13203 이하 쿼리를 읽어야 확정된다.
[needs-lookup] OCM_수술상병
formula: dt = GetOprInfDT(); return dt != null && dt.Rows.Count > 0 ? dt.Rows[0]["OkdNam"].ToString() : "";
blocker: GetOprInfDT() (같은 파일 13203) — 자체 SQL 조회 함수다. TAG-REMAINING-360.md 도 GetOprInfDT 를 SQL 갈래로 잡아 뒀다(줄 13203).
[needs-lookup] OCM_수술주상병
formula: var dt = GetOprInfDT_OKDMain(); return (dt != null && dt.Rows.Count > 0) ? (dt.Rows[0]["OkdNam"] ?? "").ToString() : "";
blocker: GetOprInfDT_OKDMain() — 자체 SQL(WITH OprInfRec ...)을 clsDAFactory.ExecuteQuery 로 돌린다. 바인드는 moPatInfoBiz.ComNum 과 GetPatientInfo.Item("ComAcpDtm").Substring(0,8).
[needs-lookup] OCM_심사_부상병명
formula: var dt = Query(Bkd_상병쿼리(ComNum, "S"), piComNum: ComNum); return dt.Rows.Count > 0 ? string.Concat(dt.Rows.Select(r => r["KcdKorNam"].ToString().Trim() + Environment.NewLine)) : "";
blocker: Bkd_상병쿼리(piComNum, "S") — B_OkdInf ⨝ P_ComInf ⨝ M_KcdMst 조회. 추가로 clsCommonLib.GetDtlCodVal("CHNGKCDCOD","01") 과 HspCfg.HspCod 분기(KIMEYE)도 탄다.
[needs-lookup] OCM_심사_부상병코드
formula: var dt = Query(Bkd_상병쿼리(ComNum, "S"), piComNum: ComNum); return dt.Rows.Count > 0 ? string.Concat(dt.Rows.Select(r => r["KcdElcCod"].ToString().Trim() + Environment.NewLine)) : "";
blocker: Bkd_상병쿼리(piComNum, "S") — B_OkdInf ⨝ P_ComInf ⨝ M_KcdMst 조회.
[needs-lookup] OCM_심사_주상병명
formula: var dt = Query(Bkd_상병쿼리(ComNum, "M"), piComNum: ComNum); return dt.Rows.Count > 0 ? dt.Rows[0]["KcdKorNam"].ToString() : "";
blocker: Bkd_상병쿼리(piComNum, "M") — B_OkdInf ⨝ P_ComInf ⨝ M_KcdMst 조회.
[needs-lookup] OCM_심사_주상병코드
formula: var dt = Query(Bkd_상병쿼리(ComNum, "M"), piComNum: ComNum); return dt.Rows.Count > 0 ? dt.Rows[0]["KcdElcCod"].ToString() : "";
blocker: Bkd_상병쿼리(piComNum, "M") — B_OkdInf ⨝ P_ComInf ⨝ M_KcdMst 조회.
@@ -47,7 +47,10 @@ public static class TagPreviewCatalog
["ETC_현재일자"] = ClockFormat.Date, ["ETC_현재일자"] = ClockFormat.Date,
["ETC_현재일시"] = ClockFormat.DateTime, ["ETC_현재일시"] = ClockFormat.DateTime,
["ETC_현재일시_MaskBox"] = ClockFormat.DateTimeMask, ["ETC_현재일시_MaskBox"] = ClockFormat.DateTimeMask,
["ETC_현재년도"] = ClockFormat.Year, // "년" 접미가 붙는다(bzDataInterface.vb:16923 — Data2Format(...) + "년").
// 처음에 접미 없는 Year 로 넣어 레거시와 다른 값("2026")이 나갔다 —
// 형제 태그 ETC_현재일시_1_년(:16929)은 접미가 <b>없으므로</b> 둘을 갈라야 한다.
["ETC_현재년도"] = ClockFormat.YearSuffixed,
["ETC_현재시간"] = ClockFormat.Time, ["ETC_현재시간"] = ClockFormat.Time,
["ETC_현재일자_영문"] = ClockFormat.DateEnglish, ["ETC_현재일자_영문"] = ClockFormat.DateEnglish,
["ETC_현재일시_1_년"] = ClockFormat.Year, ["ETC_현재일시_1_년"] = ClockFormat.Year,
@@ -174,6 +177,7 @@ public static class TagPreviewCatalog
// 레거시 Data2Format_ENG 의 MM-DD-YYYY 재배치 // 레거시 Data2Format_ENG 의 MM-DD-YYYY 재배치
ClockFormat.DateEnglish => $"{date[4..6]}-{date[6..8]}-{date[..4]}", ClockFormat.DateEnglish => $"{date[4..6]}-{date[6..8]}-{date[..4]}",
ClockFormat.Year => date[..4], ClockFormat.Year => date[..4],
ClockFormat.YearSuffixed => date[..4] + "년",
ClockFormat.Month => date[4..6], ClockFormat.Month => date[4..6],
ClockFormat.Day => date[6..8], ClockFormat.Day => date[6..8],
ClockFormat.Hour => minute12[8..10], ClockFormat.Hour => minute12[8..10],
@@ -194,14 +198,16 @@ public static class TagPreviewCatalog
{ {
UserField.Name => name, UserField.Name => name,
UserField.Id => id, UserField.Id => id,
UserField.IdAndName => id.Length > 0 && name.Length > 0 ? $"{id}.{name}" : string.Empty, // 레거시(bzDataInterface.vb:17930)는 빈 값 검사 없이 <b>항상</b> 결합한다 —
// 한쪽이 비면 "id." 나 ".이름" 이 그대로 나간다. 조건을 붙였더니 레거시와 갈렸다.
UserField.IdAndName => $"{id}.{name}",
UserField.MobilePhone => mobile, UserField.MobilePhone => mobile,
UserField.OfficePhone => office, UserField.OfficePhone => office,
_ => string.Empty, _ => string.Empty,
}; };
} }
private enum ClockFormat { Date, DateTime, DateTimeMask, DateEnglish, Year, Month, Day, Hour, Minute, Time } private enum ClockFormat { Date, DateTime, DateTimeMask, DateEnglish, Year, YearSuffixed, Month, Day, Hour, Minute, Time }
private enum UserField { Name, Id, IdAndName, MobilePhone, OfficePhone } private enum UserField { Name, Id, IdAndName, MobilePhone, OfficePhone }
#endregion #endregion
+23 -3
View File
@@ -72,21 +72,41 @@ public sealed class TagPreviewStore
} }
} }
/// <summary>연락처 두 칸 — 세션에 없는 것은 이것뿐이라 이 한 줄만 더 읽는다</summary> /// <summary>
/// 연락처 두 칸 — 세션에 없는 것은 이것뿐이라 이 한 줄만 더 읽는다.
///
/// <b>유효기간으로 고른다.</b> 레거시(GetUidMst, clsCommonLib.vb:10636)는
/// <c>:일자 BETWEEN UidStrDte AND UidEndDte</c> 로 행을 고르고,
/// 0행이면 코드만으로 다시 찾는다(:10641 폴백). 전에는 조건 없이 첫 행을 집어서
/// 이력이 여러 행인 사용자에서 <b>옛 연락처</b>가 나올 수 있었다.
/// 판정 일자는 레거시가 로그인 시점(GetAdpDtm 앞 8자)인데 여기는 서버 오늘이다 —
/// 자정을 넘겨 계속 켜 둔 세션에서만 갈리는 차이라 받아들이고 기록해 둔다.
/// </summary>
private static (string Mobile, string Office) ReadPhones(OracleConnection connection, string uidCod) private static (string Mobile, string Office) ReadPhones(OracleConnection connection, string uidCod)
{ {
if (uidCod.Length == 0) if (uidCod.Length == 0)
{ {
return (string.Empty, string.Empty); return (string.Empty, string.Empty);
} }
var valid = ReadPhoneRow(connection,
"SELECT UIDMBLPHN, UIDOFFTEL FROM M_UIDMST WHERE UIDCOD = :uid"
+ " AND TO_CHAR(SYSDATE,'YYYYMMDD') BETWEEN UIDSTRDTE AND UIDENDDTE", uidCod);
return valid ?? ReadPhoneRow(connection,
"SELECT UIDMBLPHN, UIDOFFTEL FROM M_UIDMST WHERE UIDCOD = :uid", uidCod)
?? (string.Empty, string.Empty);
}
private static (string Mobile, string Office)? ReadPhoneRow(
OracleConnection connection, string sql, string uidCod)
{
using var command = connection.CreateCommand(); using var command = connection.CreateCommand();
command.CommandText = "SELECT UIDMBLPHN, UIDOFFTEL FROM M_UIDMST WHERE UIDCOD = :uid"; command.CommandText = sql;
command.CommandTimeout = TimeoutSeconds; command.CommandTimeout = TimeoutSeconds;
command.Parameters.Add(new OracleParameter("uid", uidCod)); command.Parameters.Add(new OracleParameter("uid", uidCod));
using var reader = command.ExecuteReader(); using var reader = command.ExecuteReader();
if (!reader.Read()) if (!reader.Read())
{ {
return (string.Empty, string.Empty); return null;
} }
return (reader.IsDBNull(0) ? string.Empty : reader.GetString(0), return (reader.IsDBNull(0) ? string.Empty : reader.GetString(0),
reader.IsDBNull(1) ? string.Empty : reader.GetString(1)); reader.IsDBNull(1) ? string.Empty : reader.GetString(1));
+14 -3
View File
@@ -9,7 +9,11 @@ namespace SheetMe.Data.Stores;
public sealed record HisUser( public sealed record HisUser(
string UidCod, string UidNam, string UidCod, string UidNam,
string DepCod, string DepNam, string DepCod, string DepNam,
string HspCod, string HspNam) string HspCod, string HspNam,
/// <summary>의사 여부(UidDtrYon=Y) — OCM_User진료과 계열이 로그인 사용자 분기에 쓴다</summary>
string DoctorYn = "",
/// <summary>부서 공식 명칭(DepOfcNam) — 개원일 조건이 걸리는 병원에서 한글명 대신 쓴다</summary>
string DepOfficialName = "")
{ {
/// <summary>미인증 세션(인자 없이 단독 실행) — DB 쓰기가 차단된다</summary> /// <summary>미인증 세션(인자 없이 단독 실행) — DB 쓰기가 차단된다</summary>
public static readonly HisUser Unknown = new(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty); public static readonly HisUser Unknown = new(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty);
@@ -58,10 +62,16 @@ public sealed class UserContextStore
connection.Open(); connection.Open();
using var command = connection.CreateCommand(); using var command = connection.CreateCommand();
command.BindByName = true; command.BindByName = true;
// 레거시와 두 가지가 <b>의도적으로</b> 다르다(워크플로 검증이 짚었고, 받아들이기로 했다):
// · 유효기간 판정 일자 — 레거시는 로그인 시점(GetAdpDtm 앞 8자, clsCommonLib.vb:10622),
// 여기는 SYSDATE. 자정을 넘겨 계속 켜 둔 세션에서만 갈린다.
// · Trim — 레거시는 UidCod/UidNam 을 무가공으로 쓰고(bzDataInterface.vb:16713, 16720)
// 여기는 걷는다. CHAR 잔여 공백이 종이에 찍히는 쪽이 더 이상하다고 판단했다.
command.CommandText = command.CommandText =
"SELECT u.UidCod, NVL(u.UidNam,' '), " + "SELECT u.UidCod, NVL(u.UidNam,' '), " +
" NVL(u.UidDepCod,' '), NVL(d.DepKorNam,' '), " + " NVL(u.UidDepCod,' '), NVL(d.DepKorNam,' '), " +
" NVL(u.UidHspCod,' '), NVL(h.HspInsNam,' ') " + " NVL(u.UidHspCod,' '), NVL(h.HspInsNam,' '), " +
" NVL(u.UidDtrYon,' '), NVL(d.DepOfcNam,' ') " +
"FROM M_UidMst u " + "FROM M_UidMst u " +
"LEFT JOIN M_DepMst d ON d.DepCod = u.UidDepCod " + "LEFT JOIN M_DepMst d ON d.DepCod = u.UidDepCod " +
" AND TO_CHAR(SYSDATE,'YYYYMMDD') BETWEEN d.DepStrDte AND d.DepEndDte " + " AND TO_CHAR(SYSDATE,'YYYYMMDD') BETWEEN d.DepStrDte AND d.DepEndDte " +
@@ -79,7 +89,8 @@ public sealed class UserContextStore
return new HisUser( return new HisUser(
reader.GetString(0).Trim(), reader.GetString(1).Trim(), reader.GetString(0).Trim(), reader.GetString(1).Trim(),
reader.GetString(2).Trim(), reader.GetString(3).Trim(), reader.GetString(2).Trim(), reader.GetString(3).Trim(),
reader.GetString(4).Trim(), reader.GetString(5).Trim()); reader.GetString(4).Trim(), reader.GetString(5).Trim(),
reader.GetString(6).Trim(), reader.GetString(7).Trim());
} }
#endregion #endregion
} }
@@ -87,6 +87,23 @@ public sealed class PatientTagResolver : ITagValueResolver
// 외래는 CowInf 가 0행이라 자연히 빈 값이 된다(레거시도 외래·비낮병동은 "" 다). // 외래는 CowInf 가 0행이라 자연히 빈 값이 된다(레거시도 외래·비낮병동은 "" 다).
["OCM_병동"] = c => PatientContext.Value(c.CowInf, "CowWadCod"), ["OCM_병동"] = c => PatientContext.Value(c.CowInf, "CowWadCod"),
["OCM_병실"] = c => PatientContext.Value(c.CowInf, "CowRomCod"), ["OCM_병실"] = c => PatientContext.Value(c.CowInf, "CowRomCod"),
["OCM_병실베드"] = c => PatientContext.Value(c.CowInf, "CowBedCod"),
// 조합형 — 구분자는 " / " (양쪽 공백, 레거시 그대로). 행이 없으면 빈 값이지
// " / " 만 남기지 않는다(레거시는 rows>0 안에서만 결합한다).
["OCM_병동_병실"] = c => c.CowInf.Count == 0 ? string.Empty
: $"{PatientContext.Value(c.CowInf, "CowWadCod")} / {PatientContext.Value(c.CowInf, "CowRomCod")}",
["OCM_병동_병실_베드"] = c => c.CowInf.Count == 0 ? string.Empty
: $"{PatientContext.Value(c.CowInf, "CowWadCod")} / {PatientContext.Value(c.CowInf, "CowRomCod")}"
+ $" / {PatientContext.Value(c.CowInf, "CowBedCod")}",
// 내원유형 — 입원/응급실/외래(bzDataInterface.vb:2896-2918).
// 응급 여부는 내원행의 ComEmgYon 이다. 그 밖의 환자구분은 레거시도 "" 다.
["OCM_내원유형"] = c => Com(c, "ComPatTyp") switch
{
"I" => "입원",
"O" => Com(c, "ComEmgYon") == "Y" ? "응급실" : "외래",
_ => string.Empty,
},
}; };
private static string Pat(PatientContext c, string column) => PatientContext.Value(c.PatInf, column); private static string Pat(PatientContext c, string column) => PatientContext.Value(c.PatInf, column);
@@ -294,7 +311,8 @@ public sealed class PatientTagResolver : ITagValueResolver
/// </summary> /// </summary>
private TagValue? FromDoctor(string tag) private TagValue? FromDoctor(string tag)
{ {
if (tag is not ("OCM_담당의사" or "OCM_담당의사_담당의사코드" or "OCM_담당의사_연락처")) if (tag is not ("OCM_담당의사" or "OCM_담당의사_담당의사코드" or "OCM_담당의사_연락처"
or "OCM_담당의사_영문" or "OCM_담당의사면허번호" or "OCM_담당의사전문의번호"))
{ {
return null; return null;
} }
@@ -305,6 +323,11 @@ public sealed class PatientTagResolver : ITagValueResolver
{ {
"OCM_담당의사" => name, "OCM_담당의사" => name,
"OCM_담당의사_담당의사코드" => name.Length > 0 ? $"{name}({Field("UidCod")})" : string.Empty, "OCM_담당의사_담당의사코드" => name.Length > 0 ? $"{name}({Field("UidCod")})" : string.Empty,
// 아래 셋은 같은 담당의 행의 다른 컬럼일 뿐이다(bzDataInterface.vb:4094·4119·4144 —
// 조회 갈래가 OCM_담당의사와 문자 그대로 같고 마지막에 집는 컬럼만 다르다)
"OCM_담당의사_영문" => Field("UidEngNam"),
"OCM_담당의사면허번호" => Field("UidLicNum"),
"OCM_담당의사전문의번호" => Field("UidSpcLic"),
_ => Field("UidMblPhn"), _ => Field("UidMblPhn"),
}; };
return value.Length > 0 return value.Length > 0
@@ -343,6 +366,39 @@ public sealed class PatientTagResolver : ITagValueResolver
return hospitalName.Trim().Length > 0 return hospitalName.Trim().Length > 0
? new TagValue(true, hospitalName.Trim()) ? new TagValue(true, hospitalName.Trim())
: new TagValue(false, "요양기관명을 찾지 못했습니다(M_DepMst·M_HspMst 조인)"); : new TagValue(false, "요양기관명을 찾지 못했습니다(M_DepMst·M_HspMst 조인)");
// ── User 계열 — 로그인 사용자가 의사면 <b>사용자의</b> 과·이름, 아니면 환자의 것 ──
// (bzDataInterface.vb:5697-5826). 개원일 조건(HspStrDte>=20220701)은 세션에 없어
// 빈 값 결정이라 DepOfcNam 갈래를 타지 않는다 — OCM_진료과와 같은 결정이다.
case "OCM_User진료과":
case "OCM_User진료과_한글명칭":
{
if (UserSession.Current.DoctorYn == "Y")
{
var mine = UserSession.Current.DepNam;
return mine.Length > 0
? new TagValue(true, mine)
: new TagValue(false, "로그인 사용자의 부서명을 찾지 못했습니다");
}
var korNam = department.TryGetValue("DepKorNam", out var d) ? d.Trim() : string.Empty;
return korNam.Length > 0
? new TagValue(true, korNam)
: new TagValue(false, "이 내원의 진료과를 찾지 못했습니다");
}
case "OCM_User진료의사명":
{
if (UserSession.Current.DoctorYn == "Y")
{
return new TagValue(true, UserSession.Current.UidNam);
}
// 의사가 아니면 환자의 담당의 — 이미 든 담당의 행을 쓴다.
// 레거시는 접수일자로 유효기간을 다시 보지만(:5822) 우리 담당의 행과
// 사람이 갈리려면 같은 코드의 이력이 겹쳐야 한다 — 폴백까지 같아서 실질 동일.
var name = doctor.TryGetValue("UidNam", out var n) ? n.Trim() : string.Empty;
return name.Length > 0
? new TagValue(true, name)
: new TagValue(false, "이 내원의 담당의를 찾지 못했습니다");
}
default: default:
return null; return null;
} }
@@ -363,6 +419,8 @@ public sealed class PatientTagResolver : ITagValueResolver
"PAT_우편번호", "PAT_도로명주소", "PAT_지번주소", "PAT_영문도로명주소", "PAT_우편번호", "PAT_도로명주소", "PAT_지번주소", "PAT_영문도로명주소",
"OCM_담당의사", "OCM_담당의사_담당의사코드", "OCM_담당의사_연락처", "OCM_담당의사", "OCM_담당의사_담당의사코드", "OCM_담당의사_연락처",
"OCM_진료과", "ETC_요양기관명칭_병원명", "OCM_진료과", "ETC_요양기관명칭_병원명",
"OCM_담당의사_영문", "OCM_담당의사면허번호", "OCM_담당의사전문의번호",
"OCM_User진료과", "OCM_User진료과_한글명칭", "OCM_User진료의사명",
}; };
/// <summary> /// <summary>
@@ -105,7 +105,9 @@ public sealed class TagPreviewCatalogTests
Assert.AreEqual("2026-08-1414:32", TagPreviewCatalog.FromClock("ETC_현재일시_MaskBox", stamp)); Assert.AreEqual("2026-08-1414:32", TagPreviewCatalog.FromClock("ETC_현재일시_MaskBox", stamp));
// Data2Format_ENG 는 MM-DD-YYYY 로 재배치한다 // Data2Format_ENG 는 MM-DD-YYYY 로 재배치한다
Assert.AreEqual("08-14-2026", TagPreviewCatalog.FromClock("ETC_현재일자_영문", stamp)); Assert.AreEqual("08-14-2026", TagPreviewCatalog.FromClock("ETC_현재일자_영문", stamp));
Assert.AreEqual("2026", TagPreviewCatalog.FromClock("ETC_현재년도", stamp)); // 접미 "년"이 붙는다(bzDataInterface.vb:16923) — 형제 ETC_현재일시_1_년 은 안 붙는다
Assert.AreEqual("2026년", TagPreviewCatalog.FromClock("ETC_현재년도", stamp));
Assert.AreEqual("2026", TagPreviewCatalog.FromClock("ETC_현재일시_1_년", stamp));
Assert.AreEqual("08", TagPreviewCatalog.FromClock("ETC_현재일시_2_월", stamp)); Assert.AreEqual("08", TagPreviewCatalog.FromClock("ETC_현재일시_2_월", stamp));
Assert.AreEqual("14:32", TagPreviewCatalog.FromClock("ETC_현재시간", stamp)); Assert.AreEqual("14:32", TagPreviewCatalog.FromClock("ETC_현재시간", stamp));
} }
@@ -125,8 +127,9 @@ public sealed class TagPreviewCatalogTests
Assert.AreEqual("U01.홍길동", Assert.AreEqual("U01.홍길동",
TagPreviewCatalog.FromUser("ETC_로그인_사용자ID_사용자명", "U01", "홍길동", "010", "1234")); TagPreviewCatalog.FromUser("ETC_로그인_사용자ID_사용자명", "U01", "홍길동", "010", "1234"));
Assert.AreEqual("010", TagPreviewCatalog.FromUser("ETC_로그인_사용자연락처", "U01", "홍길동", "010", "1234")); Assert.AreEqual("010", TagPreviewCatalog.FromUser("ETC_로그인_사용자연락처", "U01", "홍길동", "010", "1234"));
// 한쪽이 비면 점만 남은 값을 만들지 않는다 // 레거시(bzDataInterface.vb:17930)는 빈 값 검사 없이 <b>항상</b> 결합한다 —
Assert.AreEqual(string.Empty, // 한쪽이 비어도 "U01." 이 그대로 나간다. 전에는 빈 값으로 뭉갰는데 그건 레거시와 달랐다.
Assert.AreEqual("U01.",
TagPreviewCatalog.FromUser("ETC_로그인_사용자ID_사용자명", "U01", string.Empty, "010", "1234")); TagPreviewCatalog.FromUser("ETC_로그인_사용자ID_사용자명", "U01", string.Empty, "010", "1234"));
} }
#endregion #endregion