Тест DEPO: сверка шлюза с DEPO как в бою (иностранные ISIN, остаток, счёт…): зачисление — отказ с кодом, списание — перевод не отправляется; режим «без проверки» для отладки джобы; статус «остановлен»

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
zuevav
2026-09-24 13:42:30 +03:00
parent 8dbd97238a
commit 54404a8f6d
2 changed files with 85 additions and 4 deletions
+79
View File
@@ -44,6 +44,7 @@ var depoTestOutcomes = []struct{ Code, Title string }{
type DepoTestLine struct {
Ref, ISIN, Qty, Outcome, Code string
Code2 string // код бумаги в НРД, ушедший в SEC_CODE
Gate string // проверка шлюза bj (как в бою): пусто — пройдена, иначе причина
State, DOL, Comment string // результат джобы по строке FTV_M2M_LINES
}
@@ -63,6 +64,7 @@ type DepoTestRun struct {
Status string // running | done | error
Doc, HeaderState, HeaderComment string
Errors int
GateMode string // strict — как в бою | off — без проверки
}
type depoTestStore struct {
@@ -187,6 +189,7 @@ func depoTestExec(rc *RuntimeConfig, run *DepoTestRun, pace time.Duration, stopO
}
})
if state == "ERROR" && stopOnError {
globalDepoTests.update(id, func(r *DepoTestRun) { r.Status = "stopped" })
depoTestFinish(db, id, run.GUID)
return
}
@@ -253,6 +256,10 @@ func depoTestExec(rc *RuntimeConfig, run *DepoTestRun, pace time.Duration, stopO
}
})
if st == "ERROR" && stopOnError {
globalDepoTests.update(id, func(r *DepoTestRun) {
r.Status = "stopped"
r.Steps = append(r.Steps, DepoTestStep{At: time.Now(), Title: "Сценарий остановлен на ошибке джобы (галочка «остановиться на первой ошибке»): оставшиеся шаги не записаны", State: "WARN"})
})
depoTestFinish(db, id, run.GUID)
return
}
@@ -418,6 +425,35 @@ func registerDepoTest(mux *http.ServeMux, a *admin) {
back("Добавьте хотя бы одну бумагу")
return
}
// Проверка шлюза bj — та же сверка с DEPO, что перед настоящей отправкой/решением.
run.GateMode = "strict"
if r.FormValue("gate") == "off" {
run.GateMode = "off"
}
gate := depoTestGate(run)
var blocked []string
for i := range run.Lines {
l := &run.Lines[i]
g, ok := gate[l.Ref]
if !ok || g.ok {
continue
}
l.Gate = g.text
if run.GateMode != "strict" || l.Outcome == dtDecline {
continue
}
if dir == "in" {
// как автоматический режим: отказ с кодом, который дала сверка
l.Outcome, l.Code = dtDecline, pick(g.code, "M2M05")
} else {
blocked = append(blocked, l.ISIN+": "+g.text)
}
}
if len(blocked) > 0 {
back("В бою bj не отправил бы этот перевод — сверка списания с DEPO не пройдена: " + strings.Join(blocked, "; ") +
". Уберите бумагу или выберите «без проверки шлюза», если нужно проверить именно джобу DEPO.")
return
}
pace, _ := strconv.Atoi(r.FormValue("pace"))
stop := r.FormValue("stop") == "1"
globalDepoTests.mu.Lock()
@@ -436,3 +472,46 @@ func registerDepoTest(mux *http.ServeMux, a *admin) {
http.Redirect(w, r, "/admin/depo-test#run-"+run.ID, http.StatusSeeOther)
})
}
type depoGateLine struct {
ok bool
code, text string
}
// depoTestGate — сверка с DEPO для строк сценария (runDepoCheck / runDepoCheckOut, как в бою).
func depoTestGate(run *DepoTestRun) map[string]depoGateLine {
out := map[string]depoGateLine{}
fio := strings.Fields(run.Client)
for len(fio) < 3 {
fio = append(fio, "")
}
var res depoResult
if run.Direction == "out" {
var secs []depoOutSec
for _, l := range run.Lines {
secs = append(secs, depoOutSec{ReferenceID: l.Ref, ISIN: l.ISIN, Code: l.ISIN, Qty: l.Qty})
}
nsd := globalRC.Snapshot().NSD
res = runDepoCheckOut(fio[0], fio[1], strings.Join(fio[2:], " "), run.Ser, run.Num, secs, nsd.AccountID, nsd.SectionID)
} else {
it := &IncomingTransfer{GUID: run.GUID, SenderCode: run.CP, ReceivedAt: time.Now(),
InvestorLast: fio[0], InvestorFirst: fio[1], InvestorMiddle: strings.Join(fio[2:], " "),
DocType: "21", DocSeries: run.Ser, DocNumber: run.Num}
for _, l := range run.Lines {
it.Securities = append(it.Securities, IncomingSecurity{ReferenceID: l.Ref, ISIN: l.ISIN, SecurityCode: l.ISIN, Quantity: l.Qty})
}
res = runDepoCheck(it)
}
for _, s := range res.Secs {
g := depoGateLine{ok: s.Recommend != "reject", code: s.RejectCode}
var bad []string
for _, c := range s.Checks {
if !c.OK {
bad = append(bad, strings.TrimSpace(c.Name+" "+c.Detail))
}
}
g.text = strings.Join(bad, "; ")
out[s.ReferenceID] = g
}
return out
}