수술 파생 6종 — 236종 → 242종, ㉘ 수술 검증이 SKIP 에서 실검증으로
OCM_수술주상병(bzDataInterface.vb:13148 + GetOprInfDT_OKDMain :13364 — 기존 수술 조회와의 실질 차이가 상병 집계의 주상병 한정(OPRVAR1='0') 한 줄이라 SurgeryStore 에 mainDiagnosisOnly 를 얹었다. 치환이 빗나가면 필터 없는 값이 조용히 나가므로 가드로 던진다), OCM_수술집도의_진료과/_그룹진료과(:3157/:3194 — 집도의(DTRUIDCOD)를 오늘 기준 마스터로 푸는 원문 특이점(OprStt 무필터·SYSDATE 유효기간) 그대로, 최신 수술 첫 행 고정), OCM_수술명칭(:3520 Case Else — OPRCOD 를 수술일 순으로, 각 행 뒤 공백 1칸 + CRLF 결합. GNBEDRO· HIMCHAN_BP 의 OPNAME 포함 분기 미이식). 사유 완결 2종: OCM_수술진단명(:3066 — PURME 전용, 그 외 병원은 Case 미매칭으로 항상 빈 값), OCM_수술처치(:3474 — SUSS·YJRCH 전용). ㉘ 정정: 수술 검증이 늘 SKIP 이던 원인은 소유자 탐색을 O_OprInf 로 한 것 — 실제 조회(GetOprInfDT)의 축은 S_OprInf(수술 신청)다. 표를 바로잡자 이 시험 DB 에서 수술 행 1건·OPNAME 실값이 검증됐다 (SurgeryStore 의 낡은 주석도 함께 정정). - dotnet test 342/342 · --edit-smoke 실패 0 (이름 검사 242종) - --db-patient ①~㊱ 전건 통과 — ㉘ 이 처음으로 실값 PASS - --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 불변 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
02657b3893
commit
03fff0a3a4
@@ -704,6 +704,43 @@ public sealed class PatientExtraStore
|
||||
"SELECT UDF_GETAGE(PatBthDay, :a) FROM P_PatInf WHERE PatChtNum = RPAD(:c, 10)",
|
||||
("a", day8), ("c", chtNum));
|
||||
|
||||
/// <summary>
|
||||
/// 수술 집도의의 진료과/그룹진료과 이름(OCM_수술집도의_진료과 :3157 · _그룹진료과 :3194) —
|
||||
/// 수술 신청의 집도의(S_OprTyp OprTyp='DTRUIDCOD')를 오늘 기준 사용자·부서 마스터로 푼다.
|
||||
/// 원문 특이점 그대로: OprStt 필터가 없고(취소 수술 포함), 유효기간이 수술일이 아니라
|
||||
/// <b>오늘</b> 기준이다. 최신 수술(OprKey DESC) 첫 행으로 접는다(원문 Rows(0)).
|
||||
/// </summary>
|
||||
public string SurgeonDepartment(decimal comNum, bool groupName)
|
||||
=> Scalar(
|
||||
"SELECT v FROM (SELECT "
|
||||
+ (groupName ? "DtlCodNam" : "DepKorNam") + " v FROM S_OprInf"
|
||||
+ " INNER JOIN S_OprTyp ON OprKey = OprKeyNum AND OprComNum = :m AND OprTyp = 'DTRUIDCOD'"
|
||||
+ " INNER JOIN M_UidMst ON UidCod = OprCod"
|
||||
+ " AND TO_CHAR(SYSDATE, 'YYYYMMDD') BETWEEN UidStrDte AND UidEndDte"
|
||||
+ " INNER JOIN M_DepMst ON DepCod = UidDepCod"
|
||||
+ " AND TO_CHAR(SYSDATE, 'YYYYMMDD') BETWEEN DepStrDte AND DepEndDte"
|
||||
+ (groupName
|
||||
? " INNER JOIN M_DtlMst ON DepGrpCod = DtlCod AND DtlTblCod = 'GDPTBL'"
|
||||
+ " AND TO_CHAR(SYSDATE, 'YYYYMMDD') BETWEEN DtlStrDte AND DtlExpDte"
|
||||
: string.Empty)
|
||||
+ " ORDER BY OprKey DESC) WHERE ROWNUM = 1",
|
||||
("m", comNum));
|
||||
|
||||
/// <summary>
|
||||
/// 수술명 목록(OCM_수술명칭, :3520 Case Else) — 확정 수술의 수술코드명(OprTyp='OPRCOD')
|
||||
/// 을 수술일 순으로. GNBEDRO·HIMCHAN_BP 의 OPNAME 포함 분기는 미이식.
|
||||
/// 결합(첫 행 뒤 공백, 이후 CRLF)은 해석기가 원문대로 한다.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> SurgeryNames(decimal comNum)
|
||||
=> Rows(
|
||||
"SELECT OprCodNam FROM S_OprInf"
|
||||
+ " INNER JOIN S_OprTyp ON OprKeyNum = OprKey"
|
||||
+ " WHERE OprComNum = :m AND OprStt = 'E' AND OprTyp = 'OPRCOD'"
|
||||
+ " ORDER BY OprDte",
|
||||
("m", comNum))
|
||||
.Select(r => r.TryGetValue("OprCodNam", out var v) ? v : string.Empty)
|
||||
.ToArray();
|
||||
|
||||
/// <summary>스칼라 1칸 — 0행이거나 NULL 이면 빈 문자열. 잔여 공백은 걷는다(문맥 행과 같은 규칙)</summary>
|
||||
private string Scalar(string sql, params (string Name, object Value)[] binds)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace SheetMe.Data.Stores;
|
||||
/// 상병과 달리 <b>병원 분기가 없다</b>(Select Case 0회). 바인드는 내원번호 하나뿐이고
|
||||
/// 접수일자 조건은 원문에서 이미 주석 처리되어 있다(:sAcpDte — 죽은 코드).
|
||||
///
|
||||
/// SQL 은 원문에서 기계 추출했다(133줄 그대로). O_OprInf 를 축으로 수술명·부위·상병·
|
||||
/// SQL 은 원문에서 기계 추출했다(133줄 그대로). S_OprInf(수술 신청)를 축으로 수술명·부위·상병·
|
||||
/// 집도의·마취의를 LISTAGG 로 접는 큰 조회이고, 소비 태그 8종이 전부
|
||||
/// <b>첫 행에서 컬럼 하나</b>를 집는다 — 그래서 행 목록만 돌려주고 선택은 해석기가 한다.
|
||||
///
|
||||
@@ -165,12 +165,31 @@ ORDER BY OprComNum, OprDte DESC, OprDayNum ASC
|
||||
#region Methods
|
||||
/// <summary>이 내원의 수술 행들 — 없으면 빈 목록</summary>
|
||||
public List<Dictionary<string, string>> Surgeries(decimal comNum)
|
||||
=> Surgeries(comNum, mainDiagnosisOnly: false);
|
||||
|
||||
/// <summary>
|
||||
/// <paramref name="mainDiagnosisOnly"/> 가 true 면 상병 집계를 주상병(OPRVAR1='0')로
|
||||
/// 한정한다 — 레거시 <c>GetOprInfDT_OKDMain</c>(bzDataInterface.vb:13364)과
|
||||
/// <c>GetOprInfDT</c> 의 유일한 실질 차이가 이 조인 조건 한 줄이다
|
||||
/// (OKDMain 판의 수술부위·비고 컬럼 부재와 정렬 약화는 옮기지 않는다 —
|
||||
/// 그 판으로 부위 태그를 부르면 레거시도 컬럼 예외였다).
|
||||
/// </summary>
|
||||
public List<Dictionary<string, string>> Surgeries(decimal comNum, bool mainDiagnosisOnly)
|
||||
{
|
||||
using var connection = new OracleConnection(connectionString);
|
||||
connection.Open();
|
||||
using var command = connection.CreateCommand();
|
||||
command.BindByName = true;
|
||||
command.CommandText = Query;
|
||||
var sql = mainDiagnosisOnly
|
||||
? Query.Replace("AND OKDNAM.OprTyp = 'OKD'",
|
||||
"AND OKDNAM.OprTyp = 'OKD' AND OKDNAM.OPRVAR1 = '0'")
|
||||
: Query;
|
||||
if (mainDiagnosisOnly && !sql.Contains("OPRVAR1", StringComparison.Ordinal))
|
||||
{
|
||||
// 치환이 빗나가면 주상병 필터 없는 값이 조용히 나간다 — 빈 값보다 나쁘다
|
||||
throw new InvalidOperationException("주상병 필터 치환이 원문과 맞지 않습니다");
|
||||
}
|
||||
command.CommandText = sql;
|
||||
command.Parameters.Add(new OracleParameter("iComNum", comNum));
|
||||
|
||||
var rows = new List<Dictionary<string, string>>();
|
||||
|
||||
@@ -2242,18 +2242,21 @@ public static class DbSmoke
|
||||
}
|
||||
|
||||
// ㉘ 수술 — 원문 기계 추출 SQL 이 실제로 돈다.
|
||||
var oprOwner = FirstComNum(conn, "O_OprInf", "OprComNum", "OprComNum");
|
||||
// 처음엔 O_OprInf 로 소유자를 찾아 늘 SKIP 이었다 — 실제 조회
|
||||
// (GetOprInfDT)의 축은 S_OprInf(수술 신청)다. 표를 바로잡으니
|
||||
// 이 시험 DB 에서도 수술 태그가 실검증된다.
|
||||
var oprOwner = FirstComNum(conn, "S_OprInf", "OprComNum", "OprComNum");
|
||||
if (oprOwner is null)
|
||||
{
|
||||
// "행이 없다"와 "표가 없다/조회가 죽었다"를 가른다 —
|
||||
// FirstComNum 은 예외를 삼키므로 이 줄 없이는 못 가른다.
|
||||
foreach (var line in ExplainPeriod(conn, 0,
|
||||
"SELECT COUNT(*) OPRCNT FROM O_OprInf WHERE :k = 0"))
|
||||
"SELECT COUNT(*) OPRCNT FROM S_OprInf WHERE :k = 0"))
|
||||
{
|
||||
lines.Add($" [진단] O_OprInf: {line}");
|
||||
lines.Add($" [진단] S_OprInf: {line}");
|
||||
}
|
||||
lines.Add("SKIP ㉘ 수술 — 확인 불가. 위 [진단] 줄이 원인이다:"
|
||||
+ " ORA-00942 면 이 시험 DB 에 O_OprInf 가 아예 없는 것이고,"
|
||||
+ " ORA-00942 면 이 시험 DB 에 S_OprInf 가 아예 없는 것이고,"
|
||||
+ " 수술 태그는 운영 DB 에서만 검증할 수 있다");
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1716,6 +1716,8 @@ public sealed class PatientTagResolver : ITagValueResolver
|
||||
"ETC_승인의사", "OCM_수술소독간호사",
|
||||
"OCM_적용진료과", "OCM_적용진료의사", "OCM_전과", "OCM_간호정보조사지_과거력",
|
||||
"OCM_영상촬영이력",
|
||||
"OCM_수술주상병", "OCM_수술집도의_진료과", "OCM_수술집도의_그룹진료과",
|
||||
"OCM_수술명칭", "OCM_수술진단명", "OCM_수술처치",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -2246,6 +2248,66 @@ public sealed class PatientTagResolver : ITagValueResolver
|
||||
}
|
||||
return new TagValue(true, text.ToString());
|
||||
}
|
||||
case "OCM_수술주상병":
|
||||
{
|
||||
// GetOprInfDT_OKDMain(:13364) — 상병 집계를 주상병(OPRVAR1='0')로
|
||||
// 한정한 수술 조회의 첫 행 OkdNam. 이 시험 DB 엔 O_OprInf 계열이
|
||||
// 없어 운영 DB 에서만 값 검증이 된다(수술 8종과 동일).
|
||||
var comNum = PatientContext.Value(context.ComInf, "ComNum");
|
||||
if (!decimal.TryParse(comNum, out var m))
|
||||
{
|
||||
return new TagValue(false, "이 내원의 내원번호를 읽지 못했습니다");
|
||||
}
|
||||
if (contextStoreSource is null)
|
||||
{
|
||||
return new TagValue(false, "수술 조회가 연결되지 않았습니다");
|
||||
}
|
||||
var rows = new SurgeryStore(
|
||||
Services.ConfigService.Current.ConnectionString)
|
||||
.Surgeries(m, mainDiagnosisOnly: true);
|
||||
if (rows.Count == 0)
|
||||
{
|
||||
return new TagValue(false, "확정(OprStt='E')된 수술 신청이 없습니다");
|
||||
}
|
||||
var okd = rows[0].TryGetValue("OKDNAM", out var v) ? v : string.Empty;
|
||||
return okd.Length > 0
|
||||
? new TagValue(true, okd)
|
||||
: new TagValue(false, "수술 행에 주상병(OPRVAR1='0') 표기가 없습니다");
|
||||
}
|
||||
case "OCM_수술집도의_진료과":
|
||||
case "OCM_수술집도의_그룹진료과":
|
||||
{
|
||||
var comNum = PatientContext.Value(context.ComInf, "ComNum");
|
||||
if (!decimal.TryParse(comNum, out var m))
|
||||
{
|
||||
return new TagValue(false, "이 내원의 내원번호를 읽지 못했습니다");
|
||||
}
|
||||
return Wrap(store.SurgeonDepartment(m, tag == "OCM_수술집도의_그룹진료과"),
|
||||
"집도의(DTRUIDCOD)와 오늘 기준 부서가 짝이 되는 수술이 없습니다");
|
||||
}
|
||||
case "OCM_수술명칭":
|
||||
{
|
||||
var comNum = PatientContext.Value(context.ComInf, "ComNum");
|
||||
if (!decimal.TryParse(comNum, out var m))
|
||||
{
|
||||
return new TagValue(false, "이 내원의 내원번호를 읽지 못했습니다");
|
||||
}
|
||||
var names = store.SurgeryNames(m);
|
||||
if (names.Count == 0)
|
||||
{
|
||||
return new TagValue(false, "확정 수술의 수술코드명(OPRCOD)이 없습니다");
|
||||
}
|
||||
// 원문 결합(:3546-3556): 각 행 뒤 공백 1칸, 행 사이 CRLF(마지막에도 공백)
|
||||
return new TagValue(true, string.Join("\r\n", names.Select(n => $"{n} ")));
|
||||
}
|
||||
case "OCM_수술진단명":
|
||||
// PURME 전용(:3066) — 그 밖의 병원은 Case 미매칭으로 항상 빈 값이다
|
||||
return new TagValue(false,
|
||||
"PURME 전용 태그입니다 — 그 밖의 병원에서는 레거시도 항상 빈 값입니다");
|
||||
case "OCM_수술처치":
|
||||
// SUSS·YJRCH 전용(:3474) — 그 밖의 병원은 Case 미매칭으로 항상 빈 값이다
|
||||
return new TagValue(false,
|
||||
"SUSS·YJRCH 전용 태그입니다 — 그 밖의 병원에서는 레거시도 항상 빈 값입니다");
|
||||
case "OCM_간호정보조사지_과거력":
|
||||
{
|
||||
var comNum = PatientContext.Value(context.ComInf, "ComNum");
|
||||
@@ -2491,6 +2553,8 @@ public sealed class PatientTagResolver : ITagValueResolver
|
||||
"ETC_승인의사", "OCM_수술소독간호사",
|
||||
"OCM_적용진료과", "OCM_적용진료의사", "OCM_전과", "OCM_간호정보조사지_과거력",
|
||||
"OCM_영상촬영이력",
|
||||
"OCM_수술주상병", "OCM_수술집도의_진료과", "OCM_수술집도의_그룹진료과",
|
||||
"OCM_수술명칭", "OCM_수술진단명", "OCM_수술처치",
|
||||
"OCM_입원시간", "OCM_입원일시", "OCM_입원일시_영문", "OCM_입실시간",
|
||||
"OCM_의사퇴원예고일시", "OCM_퇴원예정일시", "OCM_퇴원시간", "OCM_퇴원일시",
|
||||
"OCM_퇴원예고일시", "OCM_재원일수", "OCM_퇴원예고재원일수", "OCM_입원일자_낮병동",
|
||||
|
||||
Reference in New Issue
Block a user