Сверка DEPO: квалификация по признаку бумаги — входящие: зелёный/отказ M2M08, исходящие: зелёный/предупреждение (решает получатель); общая жёлтая строка убрана

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
zuevav
2026-09-16 16:01:51 +03:00
parent f35c72c50a
commit e4e262de9e
2 changed files with 48 additions and 2 deletions
+28 -2
View File
@@ -268,13 +268,15 @@ func depoSecurityQualOnly(ctx context.Context, db *sql.DB, valID int64) (bool, s
return false, "ограничений по обращению нет (опция DEPO)" return false, "ограничений по обращению нет (опция DEPO)"
} }
// qualOnlyCheck — бумага только для квал. инвесторов против квалификации инвестора. // qualOnlyCheck — ЗАЧИСЛЕНИЕ: бумага только для квал. инвесторов против квалификации инвестора у нас
// (не квалифицирован — отказ M2M08; бумага без ограничений — квалификация не требуется).
func qualOnlyCheck(found, qualOnly bool, qdet string, p depoParty) depoCheck { func qualOnlyCheck(found, qualOnly bool, qdet string, p depoParty) depoCheck {
c := depoCheck{Name: "Бумага только для квал. инвесторов", OK: true, Code: "M2M08", Detail: qdet} c := depoCheck{Name: "Бумага только для квал. инвесторов", OK: true, Code: "M2M08", Detail: qdet}
switch { switch {
case !found: case !found:
c.Detail = "не проверяется — бумаги нет в справочнике" c.Detail = "не проверяется — бумаги нет в справочнике"
case !qualOnly: case !qualOnly:
c.Detail += " — квалификация клиента не требуется"
case !p.faceOK: case !p.faceOK:
c.Detail += " — инвестор не найден, квалификация не проверялась" c.Detail += " — инвестор не найден, квалификация не проверялась"
case p.qualAll: case p.qualAll:
@@ -289,6 +291,26 @@ func qualOnlyCheck(found, qualOnly bool, qdet string, p depoParty) depoCheck {
return c return c
} }
// outQualCheck — СПИСАНИЕ: бумага только для квал. инвесторов, а наш клиент у нас не квалифицирован —
// предупреждение, не отказ: у принимающего депозитария клиент может быть квалифицирован, решает он.
func outQualCheck(found, qualOnly bool, qdet string, p depoParty) depoCheck {
c := depoCheck{Name: "Бумага только для квал. инвесторов", OK: true, Code: "M2M08", Detail: qdet}
switch {
case !found:
c.Detail = "не проверяется — бумаги нет в справочнике"
case !qualOnly:
c.Detail += " — квалификация клиента не требуется"
case !p.faceOK:
c.Detail += " — инвестор не найден, квалификация не проверялась"
case p.qualAll:
c.Detail += " — клиент квалифицированный по всем видам бумаг"
default:
c.Warn = true
c.Detail += " — у нас клиент не квалифицирован полностью (" + p.qualDet + "); принимающий депозитарий проверит квалификацию у себя и может отказать M2M08"
}
return c
}
// depoBlocking — есть ли блокировка бумаги на счёте (BLOCK_DATE либо остаток на 98070.05/.07/.08/.09). // depoBlocking — есть ли блокировка бумаги на счёте (BLOCK_DATE либо остаток на 98070.05/.07/.08/.09).
func depoBlocking(ctx context.Context, db *sql.DB, valID, clientID int64) (ok bool, det string) { func depoBlocking(ctx context.Context, db *sql.DB, valID, clientID int64) (ok bool, det string) {
var blockDate sql.NullTime var blockDate sql.NullTime
@@ -370,7 +392,6 @@ func partyChecks(p depoParty, ser, num, fio string) []depoCheck {
{Name: "Статус счёта строго «открыт»", OK: p.accOpen != "", Code: "M2M02", {Name: "Статус счёта строго «открыт»", OK: p.accOpen != "", Code: "M2M02",
Detail: pick3(p.accAny == "", "не проверяется — счёта нет", pick3(p.accOpen != "", p.accOpen+" — открыт (STATE=0, без даты закрытия)", p.accAny+" — НЕ открыт (закрыт или STATE≠0)"))}, Detail: pick3(p.accAny == "", "не проверяется — счёта нет", pick3(p.accOpen != "", p.accOpen+" — открыт (STATE=0, без даты закрытия)", p.accAny+" — НЕ открыт (закрыт или STATE≠0)"))},
{Name: "Счёт не ИИС", OK: p.iisOK, Code: "M2M08", Detail: p.iisDet}, {Name: "Счёт не ИИС", OK: p.iisOK, Code: "M2M08", Detail: p.iisDet},
{Name: "Квал-инвестор (если бумага только для квал.)", OK: p.qualOK, Warn: p.qualWarn, Code: "M2M08", Detail: p.qualDet},
} }
} }
@@ -535,6 +556,11 @@ func runDepoCheckOutAccount(last, first, middle, ser, num, birth string, secs []
valID, vname, found, _ := depoSecurity(ctx, db, isin) valID, vname, found, _ := depoSecurity(ctx, db, isin)
sr.Checks = append(sr.Checks, depoCheck{Name: "Бумага есть в справочнике", OK: found, Code: "M2M05", sr.Checks = append(sr.Checks, depoCheck{Name: "Бумага есть в справочнике", OK: found, Code: "M2M05",
Detail: pick3(found, vname+" (ID "+fmt.Sprint(valID)+")", "ISIN "+isin+" в справочнике OD_VALUES нет")}) Detail: pick3(found, vname+" (ID "+fmt.Sprint(valID)+")", "ISIN "+isin+" в справочнике OD_VALUES нет")})
qualOnly, qdet := false, ""
if found {
qualOnly, qdet = depoSecurityQualOnly(ctx, db, valID)
}
sr.Checks = append(sr.Checks, outQualCheck(found, qualOnly, qdet, p))
sr.Checks = append(sr.Checks, partyChecks(p, ser, num, fio)...) sr.Checks = append(sr.Checks, partyChecks(p, ser, num, fio)...)
if birthCheck != nil { if birthCheck != nil {
sr.Checks = append(sr.Checks, *birthCheck) sr.Checks = append(sr.Checks, *birthCheck)
+20
View File
@@ -71,3 +71,23 @@ func TestQualOnlyCheck(t *testing.T) {
t.Fatalf("инвестор не найден — отказ даёт M2M04, не эта проверка: %+v", c) t.Fatalf("инвестор не найден — отказ даёт M2M04, не эта проверка: %+v", c)
} }
} }
// Списание: бумага для квал. инвесторов у неквалифицированного у нас клиента — предупреждение, не отказ.
func TestOutQualCheck(t *testing.T) {
const q = "бумага предназначена для квалифицированных инвесторов (опция DEPO)"
notQual := depoParty{faceOK: true, qualWarn: true, qualDet: "инвестор НЕ квалифицированный"}
if c := outQualCheck(true, true, q, notQual); !c.OK || !c.Warn || c.Strict {
t.Fatalf("неквал у нас отправляет бумагу для квал: %+v", c)
}
if c := outQualCheck(true, true, q, depoParty{faceOK: true, qualAll: true}); !c.OK || c.Warn {
t.Fatalf("квал по всем видам: %+v", c)
}
if c := outQualCheck(true, false, "ограничений по обращению нет (опция DEPO)", notQual); !c.OK || c.Warn {
t.Fatalf("бумага без ограничений — зелёный: %+v", c)
}
for _, c := range partyChecks(notQual, "1", "2", "x") {
if strings.HasPrefix(c.Name, "Квал") {
t.Fatalf("общая жёлтая строка квалификации больше не нужна: %+v", c)
}
}
}