namespace Infrastructure.StockData; /// /// SEIBro getSafeDpDutyDepoStatus(의무보호예수/반환) 응답 파서 — 순수 C#. 공용 SeibroXml envelope 입력. /// 자연키(ISSUCO_CUSTNO, SHOTN_ISIN, SAFEDP_DT, OCCR_SEQ, DUTY_SAFEDP_RACD) 중 하나라도 없거나 비형식인 행은 건너뛴다. /// ⚠️ SECN_KACD 는 실응답에서 코드가 아니라 명칭(보통주) 문자열이다(fixture 확인) — 문자열로 그대로 담는다. /// 반환 관련 필드(RETURN_DT/RETURN_QTY)는 예수 행에서 빈값 → null. /// public static class SeibroLockupParser { public sealed record Row( int IssucoCustno, string KorSecnNm, string? SecnKacd, string ShotnIsin, string? MartTpcd, string OccrSeq, DateOnly SafedpDt, long? SafedpQty, string DutySafedpRacd, DateOnly? ReturnDt, long? ReturnQty, long? TotalStkCnt ); public static IReadOnlyList Parse(SeibroXml.Envelope envelope) { var rows = new List(); foreach (var fields in envelope.Rows) { var custno = SeibroFieldParse.Int(SeibroXml.GetOrNull(fields, "ISSUCO_CUSTNO")); var korSecnNm = SeibroXml.GetOrNull(fields, "KOR_SECN_NM")?.Trim(); var shotnIsin = SeibroXml.GetOrNull(fields, "SHOTN_ISIN")?.Trim(); var occrSeq = SeibroXml.GetOrNull(fields, "OCCR_SEQ")?.Trim(); var safedpDt = SeibroFieldParse.Date(SeibroXml.GetOrNull(fields, "SAFEDP_DT")); var racd = SeibroXml.GetOrNull(fields, "DUTY_SAFEDP_RACD")?.Trim(); if (custno is null || string.IsNullOrEmpty(korSecnNm) || string.IsNullOrEmpty(shotnIsin) || string.IsNullOrEmpty(occrSeq) || safedpDt is null || string.IsNullOrEmpty(racd)) { continue; } rows.Add(new Row( custno.Value, korSecnNm, SeibroXml.GetOrNull(fields, "SECN_KACD")?.Trim(), shotnIsin, SeibroXml.GetOrNull(fields, "MART_TPCD")?.Trim(), occrSeq, safedpDt.Value, SeibroFieldParse.Long(SeibroXml.GetOrNull(fields, "SAFEDP_QTY")), racd, SeibroFieldParse.Date(SeibroXml.GetOrNull(fields, "RETURN_DT")), SeibroFieldParse.Long(SeibroXml.GetOrNull(fields, "RETURN_QTY")), SeibroFieldParse.Long(SeibroXml.GetOrNull(fields, "TOTAL_STK_CNT")) )); } return rows; } }