DEPO: строчные события по депо-поручениям (частичное исполнение пакета)

По ответам программиста Fancy (В. Кокин, 21.09.2026) на 8 вопросов из
docs/fancy-events-partial-m2m.md. Одно депо-поручение (MF010) = одна бумага
пакета, поэтому подача и исход теперь пишутся в DEPO ПО СТРОКАМ с REFERENCE и
сразу по MS010 этой строки, не дожидаясь остальных: пакет может исполниться
частично (Сбер — да, Т-Технологии — отказ). События на весь GUID (пустой
REFERENCE) больше не пишем — переход одновременно с bj2depo.

- BJ_ORDER_SENT / BJ_ORDER_ACCEPTED / BJ_EXECUTED / BJ_ORDER_REJECTED /
  BJ_ORDER_LATE / BJ_CANCELLED — по строке (depoOnOrderLineSent/Result);
  contr_d_id кладём в COMMENT (джобе связка не нужна — по REFERENCE найдёт).
- OrderWatch.Lines — состояние по строкам (contr_d_id → REFERENCE/ISIN/MS010/
  отказ/истечение); объект закрыт, когда MS010 по всем строкам; 30 дней —
  BJ_CANCELLED по каждой незакрытой строке, QUIK-блок снимается по её ISIN.
- Входящий 16/1 хранит ВСЕ contr_d_id (OrderLines) — раньше только первый
  (дефект «входящие отслеживают только первый contr_d_id»). Исходящий 16:
  REFERENCE по ISIN из бумаг M2MTR (outgoingLineRefs, кэш по GUID; svc в deps).
- Карточка: «исполнено частично: N из M» (StatusRu), бейдж warn — как статус
  «частично» в DEPO. Backfill и эмуляция — тоже по строкам.
- Попутно: EXEC процедуры обработчика DEPO — через execProc с проверкой имени
  (sqlident.go), чтобы значение из настроек не стало произвольным T-SQL.

Тесты: TestOrderWatchLinesPartial, TestIncomingOrderLines,
TestOutgoingLineRefsNoRepo; весь пакет зелёный.

РАЗВЁРТЫВАНИЕ — только одновременно с bj2depo (п. 8 ответов Fancy).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
This commit is contained in:
zuevav
2026-09-22 16:27:52 +03:00
parent e16d4454ce
commit 0c081a7857
9 changed files with 488 additions and 87 deletions
+19 -4
View File
@@ -99,14 +99,25 @@ func submitIncomingOrderFor(ctx context.Context, deps incomingDeps, it *Incoming
id := it.ID
var sentXML []string
var contrDID string
// Одно MF010 = одна бумага пакета: запоминаем каждую строку (contr_d_id ↔ ISIN ↔
// REFERENCE), чтобы исход по MS010 писать в DEPO по своей строке.
refByISIN := map[string]string{}
for _, s := range it.Securities {
if s.ISIN != "" {
refByISIN[strings.ToUpper(s.ISIN)] = s.ReferenceID
}
}
var lines []IncomingOrderLine
for _, p := range xmls {
raw, e := os.ReadFile(p) // windows-1251, шлём как есть
if e != nil {
firstErr = e.Error()
continue
}
body := string(decodeCP1251(raw))
cd := orderContrDID(raw)
if contrDID == "" {
contrDID = orderContrDID(raw)
contrDID = cd
}
zipBody, e := igw.PackDepoOrder(raw, filepath.Base(p))
if e != nil {
@@ -119,12 +130,16 @@ func submitIncomingOrderFor(ctx context.Context, deps incomingDeps, it *Incoming
continue
}
sent = append(sent, pkgID)
sentXML = append(sentXML, string(decodeCP1251(raw))) // сохраняем что отправили (UTF-8)
sentXML = append(sentXML, body) // сохраняем что отправили (UTF-8)
isin := strings.ToUpper(of16(body, "security_isin"))
lines = append(lines, IncomingOrderLine{ContrDID: cd, ISIN: isin, Ref: refByISIN[isin]})
}
if len(sent) > 0 {
deps.store.SetOrderSubmitted(id, contrDID, strings.Join(sent, ", "), strings.Join(sentXML, "\n\n"))
deps.store.SetOrderSubmitted(id, contrDID, strings.Join(sent, ", "), strings.Join(sentXML, "\n\n"), lines)
if cur, ok := deps.store.Get(id); ok {
go depoOnOrderSent(deps.rc, cur.GUID, []string{contrDID}, "16/1 (зачисление)")
for _, l := range lines {
go depoOnOrderLineSent(deps.rc, cur.GUID, l.Ref, l.ContrDID)
}
}
}
if len(sent) == 0 {