StockIssuanceChange.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. using System.ComponentModel.DataAnnotations;
  2. namespace Domain.Entities.Stocks;
  3. /// <summary>
  4. /// SEIBro 주식증감내역 (getStkIncdecDetails) — 발행횟수별 주식수 변동(액면분할·증자·이익소각 등) (Wave 2 ★P1).
  5. /// rolling per-회사번호(SHOTN_ISIN) 로 수집한다.
  6. /// ⚠️ 자연키: plan 은 (Isin, SecnIssuNtimes) 를 제시하나 실응답(삼성전자 fixture)에서 동일 발행횟수(75)가
  7. /// 서로 다른 발행일·사유(액면분할 20180503 / 이익소각 20181204)로 2행 존재한다 → (Isin, SecnIssuNtimes) 단독은 충돌.
  8. /// 따라서 UNIQUE (Isin, SecnIssuNtimes, IssuDt, SecnIssuRacd) 로 확장해 데이터 손실 없이 upsert 한다.
  9. /// </summary>
  10. public class StockIssuanceChange
  11. {
  12. [Key]
  13. public int ID { get; private set; }
  14. /// <summary>발행회사 고객번호 (ISSUCO_CUSTNO)</summary>
  15. public int? IssucoCustno { get; private set; }
  16. /// <summary>종목번호 (ISIN)</summary>
  17. public string Isin { get; private set; } = default!;
  18. /// <summary>종목종류코드 (SECN_KACD) — 0101 보통주 0201 우선주…</summary>
  19. public string? SecnKacd { get; private set; }
  20. /// <summary>종목발행횟수 (SECN_ISSU_NTIMES)</summary>
  21. public int SecnIssuNtimes { get; private set; }
  22. /// <summary>발행일자 (ISSU_DT)</summary>
  23. public DateOnly IssuDt { get; private set; }
  24. /// <summary>발행가 (ISSUPRC, 1주당)</summary>
  25. public decimal? Issuprc { get; private set; }
  26. /// <summary>발행수량 (ISSU_QTY) — 감소는 음수</summary>
  27. public long? IssuQty { get; private set; }
  28. /// <summary>종목발행사유코드 (SECN_ISSU_RACD)</summary>
  29. public string SecnIssuRacd { get; private set; } = default!;
  30. /// <summary>종목발행사유명 (SECN_ISSU_NM) — 예 액면분할</summary>
  31. public string? SecnIssuNm { get; private set; }
  32. /// <summary>상장일자 (LIST_DT)</summary>
  33. public DateOnly? ListDt { get; private set; }
  34. public DateTime UpdatedAt { get; private set; } = DateTime.UtcNow;
  35. private StockIssuanceChange() { }
  36. public static StockIssuanceChange Create(
  37. int? issucoCustno,
  38. string isin,
  39. string? secnKacd,
  40. int secnIssuNtimes,
  41. DateOnly issuDt,
  42. decimal? issuprc,
  43. long? issuQty,
  44. string secnIssuRacd,
  45. string? secnIssuNm,
  46. DateOnly? listDt)
  47. {
  48. if (string.IsNullOrWhiteSpace(isin))
  49. {
  50. throw new ArgumentException("isin required", nameof(isin));
  51. }
  52. if (string.IsNullOrWhiteSpace(secnIssuRacd))
  53. {
  54. throw new ArgumentException("secnIssuRacd required", nameof(secnIssuRacd));
  55. }
  56. return new StockIssuanceChange
  57. {
  58. IssucoCustno = issucoCustno,
  59. Isin = isin.Trim(),
  60. SecnKacd = string.IsNullOrWhiteSpace(secnKacd) ? null : secnKacd.Trim(),
  61. SecnIssuNtimes = secnIssuNtimes,
  62. IssuDt = issuDt,
  63. Issuprc = issuprc,
  64. IssuQty = issuQty,
  65. SecnIssuRacd = secnIssuRacd.Trim(),
  66. SecnIssuNm = string.IsNullOrWhiteSpace(secnIssuNm) ? null : secnIssuNm.Trim(),
  67. ListDt = listDt
  68. };
  69. }
  70. /// <summary>재수집 반영 — 변경 필드가 있을 때만 UpdatedAt 갱신 (자연키는 불변)</summary>
  71. public void Update(
  72. int? issucoCustno,
  73. string? secnKacd,
  74. decimal? issuprc,
  75. long? issuQty,
  76. string? secnIssuNm,
  77. DateOnly? listDt)
  78. {
  79. var changed = false;
  80. if (issucoCustno is not null && IssucoCustno != issucoCustno)
  81. {
  82. IssucoCustno = issucoCustno;
  83. changed = true;
  84. }
  85. var normalizedSecnKacd = string.IsNullOrWhiteSpace(secnKacd) ? null : secnKacd.Trim();
  86. if (SecnKacd != normalizedSecnKacd)
  87. {
  88. SecnKacd = normalizedSecnKacd;
  89. changed = true;
  90. }
  91. if (Issuprc != issuprc)
  92. {
  93. Issuprc = issuprc;
  94. changed = true;
  95. }
  96. if (IssuQty != issuQty)
  97. {
  98. IssuQty = issuQty;
  99. changed = true;
  100. }
  101. var normalizedSecnIssuNm = string.IsNullOrWhiteSpace(secnIssuNm) ? null : secnIssuNm.Trim();
  102. if (SecnIssuNm != normalizedSecnIssuNm)
  103. {
  104. SecnIssuNm = normalizedSecnIssuNm;
  105. changed = true;
  106. }
  107. if (ListDt != listDt)
  108. {
  109. ListDt = listDt;
  110. changed = true;
  111. }
  112. if (changed)
  113. {
  114. UpdatedAt = DateTime.UtcNow;
  115. }
  116. }
  117. }