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;
}
}