비급여처방·전문그룹과목·청구진료과목·임신주기 4종 — 250종 → 254종

OCM_비급여처방(bzDataInterface.vb:10964, Case Else + SRCH 조건 갈래 —
고지 대상(SgaNtiNon='Y')·수가 그룹 존재만. TYRCH UNION·BSGH 치료실·
GNRH·BSQ·SUSS 분기 미이식). 보험 코드로 금액 컬럼을 골라 0 초과만
"이름(##,###)원" CRLF 결합. 특수 계산코드(SGACFG Use_SgaCA1/2)
환자군은 레거시가 SELECT 에 없는 컬럼을 읽어 항상 빈 값(무증상
실패) — 설정을 읽어 그 환자군이면 사유로 말한다(이 DB 에 설정이
실제로 1건 있다 — ㉟ 확인).

OCM_진단서전문그룹과목(:11245 — DepBilCod='01'+세부과목이면 MEDDTL
이름, 아니면 GDPTBL 그룹과목. 원문의 0행 Rows(0) 크래시는 사유로).
OCM_청구진료과목(:11398 — 문맥 진료과 행의 DepBilCod 를 BDPTBL 로.
부서 시점과 유효기간 시점이 어긋나는 원문 특이점 그대로).
OCM_임신주기(:11845 — 사용자 설정(EDD/LMP) 조회가 코드 컬럼에
사용자 이름을 비교하는 결함으로 항상 0행 → 기본값 LMP 고정.
LMP 산식만 옮기고 도달 불가한 EDD 산식은 미이식. 주수는 총일수/7·
나머지, 월경일이 미래면 음수 일수만 — 원문 그대로).

- dotnet test 342/342 · --edit-smoke 실패 0 (이름 검사 254종)
- --db-patient ①~㊱ 전건 통과 (그룹과목 실값 3컬럼)
- --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 불변

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Msystech
2026-08-20 08:59:49 +09:00
co-authored by Claude Fable 5
parent 199a4142ee
commit edb9cc65cf
3 changed files with 201 additions and 0 deletions
@@ -1720,6 +1720,7 @@ public sealed class PatientTagResolver : ITagValueResolver
"OCM_수술명칭", "OCM_수술진단명", "OCM_수술처치",
"OCM_퇴원약_코드", "OCM_퇴원약_명칭", "OCM_퇴원약_용량", "OCM_퇴원약_횟수",
"OCM_퇴원약_일수", "OCM_퇴원약_용법", "OCM_퇴원약", "OCM_퇴원약_New",
"OCM_비급여처방", "OCM_진단서전문그룹과목", "OCM_청구진료과목", "OCM_임신주기",
};
/// <summary>
@@ -2358,6 +2359,125 @@ public sealed class PatientTagResolver : ITagValueResolver
}
return new TagValue(true, text.ToString());
}
case "OCM_비급여처방":
{
var comNum = PatientContext.Value(context.ComInf, "ComNum");
if (!decimal.TryParse(comNum, out var m))
{
return new TagValue(false, "이 내원의 내원번호를 읽지 못했습니다");
}
// 특수 계산코드(SGACFG Use_SgaCA1/2) 환자군은 레거시가 SELECT 에 없는
// 컬럼을 읽어 항상 빈 값이었다(:11144-11152) — 고치면 없던 값이 생긴다
var calCod = PatientContext.Value(context.CoiInf, "CoiCalCod");
if (calCod.Length > 0 && store.SpecialNonInsuredCalCodes().Contains(calCod))
{
return new TagValue(false,
"레거시 결함(특수 계산코드 갈래의 컬럼 미선택)으로 항상 빈 값인 환자군입니다");
}
var orders = store.NonInsuredOrders(m);
if (orders.Count == 0)
{
return new TagValue(false, "고지 대상 비급여 오더가 없습니다");
}
// 보험 코드로 금액 컬럼을 고른다(:11157-11200) — 0 이하 행은 건너뛴다
var insCod = PatientContext.Value(context.CoiInf, "CoiInsCod");
var column = insCod switch
{
"1" => "SgaHthNon",
"2" => "SgaGudNon",
"3" => "SgaCarNon",
"4" => calCod == "4F00" ? "SgaGenFor" : "SgaWrkNon",
_ => calCod is "5F00" or "5F10" ? "SgaGenFor" : "SgaGenNon",
};
var lines = orders
.Select(r => (Name: PatientContext.Value(r, "SgaKorNam"),
Amount: double.TryParse(PatientContext.Value(r, column),
System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture, out var a) ? a : 0))
.Where(x => x.Amount > 0)
.Select(x => $"{x.Name}({x.Amount.ToString("##,###", System.Globalization.CultureInfo.InvariantCulture)})원")
.ToArray();
return lines.Length > 0
? new TagValue(true, string.Join("\r\n", lines))
: new TagValue(false, "이 보험 구분에 금액이 있는 비급여 항목이 없습니다");
}
case "OCM_진단서전문그룹과목":
{
// 로그인 의사면 로그인 부서, 아니면 문맥 진료과(:11245 — 시점 판정은
// 진단서 계열과 동일). DepBilCod='01'+세부과목이 있으면 MEDDTL 이름,
// 아니면 GDPTBL 그룹과목 이름. 원문의 0행 크래시는 사유로 바꾼다.
var depCod = UserSession.Current.DoctorYn == "Y"
? UserSession.Current.DepCod
: PatientContext.Value(context.CodInf, "CodDepCod");
if (depCod.Length == 0)
{
return new TagValue(false, "진료과 코드를 읽지 못했습니다");
}
var row = store.GroupSubjectRow(depCod);
if (row.Count == 0)
{
return new TagValue(false, "오늘 기준 그룹과목(GDPTBL) 행이 없습니다");
}
var medDtl = PatientContext.Value(row, "DepMedDtl");
if (PatientContext.Value(row, "DepBilCod") == "01" && medDtl.Length > 0)
{
return Wrap(store.CodeName("MEDDTL", medDtl),
"세부 전문과목(MEDDTL) 코드표에 이름이 없습니다");
}
return Wrap(PatientContext.Value(row, "DtlCodNam"),
"그룹과목 이름이 비어 있습니다");
}
case "OCM_청구진료과목":
{
// 문맥 진료과 행(접수일 기준 프리페치와 같은 조회)의 DepBilCod 를
// 코드표(BDPTBL)로 푼다(:11398). 시점 어긋남 특이점은 원문 그대로다.
var billCod = PatientContext.Value(department, "DepBilCod").Trim();
if (department.Count == 0)
{
return new TagValue(false, "이 내원의 진료과를 찾지 못했습니다");
}
if (billCod.Length == 0)
{
return new TagValue(false, "진료과에 청구 코드(DepBilCod)가 없습니다");
}
return Wrap(store.CodeName("BDPTBL", billCod),
"청구 코드표(BDPTBL)에 이름이 없습니다");
}
case "OCM_임신주기":
{
var comNum = PatientContext.Value(context.ComInf, "ComNum");
if (!decimal.TryParse(comNum, out var m))
{
return new TagValue(false, "이 내원의 내원번호를 읽지 못했습니다");
}
var row = store.PregnancyRow(m);
if (row.Count == 0)
{
return new TagValue(false, "임신 정보(O_PrgInf)가 없습니다");
}
var lmp = PatientContext.Value(row, "PrgLmpDte");
var edd = PatientContext.Value(row, "PrgEddDte");
var today = PatientContext.Value(row, "Today");
if (lmp.Length == 0 && edd.Length == 0)
{
return new TagValue(false, "임신 정보 행에 분만예정일·최종월경일이 모두 비어 있습니다");
}
// 레거시(:11845)는 사용자 설정(EDD/LMP)을 읽지만 그 조회가
// 코드 컬럼에 사용자 '이름'을 비교하는 결함으로 항상 0행 —
// 기본값 LMP 로 고정된다. EDD 산식은 도달 불가라 옮기지 않는다.
var days = ResidentNumber.DaysSince(lmp, today);
if (days is null)
{
return new TagValue(false, "최종월경일이 비어 임신 주수를 만들 수 없습니다");
}
var week = days.Value / 7;
var day = days.Value % 7;
// 원문(:11908-11918): 일이 양수거나 오늘이 월경일 뒤면 "nWK dD",
// 월경일이 미래면 일수만
return new TagValue(true,
day > 0 || string.CompareOrdinal(today, lmp) > 0
? $"{week}WK {day}D" : $"{day}D");
}
case "OCM_수술진단명":
// PURME 전용(:3066) — 그 밖의 병원은 Case 미매칭으로 항상 빈 값이다
return new TagValue(false,
@@ -2681,6 +2801,7 @@ public sealed class PatientTagResolver : ITagValueResolver
"OCM_수술명칭", "OCM_수술진단명", "OCM_수술처치",
"OCM_퇴원약_코드", "OCM_퇴원약_명칭", "OCM_퇴원약_용량", "OCM_퇴원약_횟수",
"OCM_퇴원약_일수", "OCM_퇴원약_용법", "OCM_퇴원약", "OCM_퇴원약_New",
"OCM_비급여처방", "OCM_진단서전문그룹과목", "OCM_청구진료과목", "OCM_임신주기",
"OCM_입원시간", "OCM_입원일시", "OCM_입원일시_영문", "OCM_입실시간",
"OCM_의사퇴원예고일시", "OCM_퇴원예정일시", "OCM_퇴원시간", "OCM_퇴원일시",
"OCM_퇴원예고일시", "OCM_재원일수", "OCM_퇴원예고재원일수", "OCM_입원일자_낮병동",