QUIK МНП: новый получатель M2M — повтор регистрации вывода (3×30 с), пока модуль не подхватит справочник; имя получателя из справочника НРД; опрос ЛК — до 4 мин на проход

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-09-29 13:29:25 +03:00
parent b0de7f2979
commit 7a84dd6d5b
3 changed files with 63 additions and 5 deletions
+2 -1
View File
@@ -279,7 +279,8 @@ func (s *Server) pollLKClaims(ctx context.Context) {
if !moduleAllowed(s.rc, "esia-finance") {
continue // ЕСИА Финанс — отдельный модуль лицензии
}
cctx, cancel := context.WithTimeout(ctx, 50*time.Second)
// до 4 мин: блокировка в QUIK МНП может ждать, пока модуль подхватит нового получателя
cctx, cancel := context.WithTimeout(ctx, 4*time.Minute)
res, err := PullLKClaims(cctx, s.rc, s.svc)
cancel()
if err != nil {
+29 -4
View File
@@ -491,6 +491,12 @@ func quikStep(action string, aid int64, r QuikActionResult, ok bool) QuikStep {
return QuikStep{At: time.Now(), Action: action, ActionID: aid, Status: r.QueryStatus, Reply: r.ReplyText, OK: ok}
}
// Повтор регистрации, пока модуль МНП не подхватил нового получателя из M2MDepoRecipients.
var (
quikRecipientRetries = 3
quikRecipientWait = 30 * time.Second
)
// QuikOutRegister — исходящий перевод: зарегистрировать «Вывод ЦБ (M2M)» (QUIK блокирует бумаги).
func QuikOutRegister(ctx context.Context, rc *RuntimeConfig, guid, clientID, contract, isin string, qty float64, recipientINN, recipientName string, passExpenses bool) (*QuikLink, error) {
s := quikSettingsFor(rc, guid)
@@ -500,14 +506,33 @@ func QuikOutRegister(ctx context.Context, rc *RuntimeConfig, guid, clientID, con
if s.Emulate {
return quikEmulated(rc, guid, isin, "register", "registered", "вывод зарегистрирован, бумаги заблокированы"), nil
}
if strings.TrimSpace(recipientName) == "" {
recipientName = m2mPartyName(recipientINN) // наименование из справочника НРД
}
if err := quikEnsureRecipient(ctx, s, recipientINN, recipientName); err != nil {
return quikFail(rc, guid, isin, "register", err), err
}
aid, err := quikExecNewInstr(ctx, s, true, shortGUID(guid), clientID, isin, qty, recipientINN, passExpenses, "M2M bj: перевод "+shortGUID(guid))
if err != nil {
return quikFail(rc, guid, isin, "register", err), err
var aid int64
var r QuikActionResult
// Нового получателя (только что добавлен в M2MDepoRecipients) модуль МНП подхватывает не сразу —
// «Неизвестный депозитарий получателя»: ждём и повторяем, а не отклоняем заявку клиента.
for attempt := 1; ; attempt++ {
var err error
aid, err = quikExecNewInstr(ctx, s, true, shortGUID(guid), clientID, isin, qty, recipientINN, passExpenses, "M2M bj: перевод "+shortGUID(guid))
if err != nil {
return quikFail(rc, guid, isin, "register", err), err
}
r = quikWaitAction(ctx, s, aid, quikActionWait)
if attempt > quikRecipientRetries || !strings.Contains(strings.ToLower(r.ReplyText), "неизвестный депозитарий получателя") {
break
}
log.Printf("quik: %s: МНП ещё не знает получателя ИНН %s — повтор через %s (попытка %d из %d)", shortGUID(guid), recipientINN, quikRecipientWait, attempt, quikRecipientRetries)
select {
case <-ctx.Done():
return quikFail(rc, guid, isin, "register", ctx.Err()), ctx.Err()
case <-time.After(quikRecipientWait):
}
}
r := quikWaitAction(ctx, s, aid, quikActionWait)
if r.QueryStatus != 2 || r.InstrNo == "" || r.InstrNo == "0" {
err := fmt.Errorf("МНП отверг регистрацию: %s", r.ReplyText)
globalQuikLinks.update(guid, isin, func(l *QuikLink) { l.Steps = append(l.Steps, quikStep("register", aid, r, false)) })
+32
View File
@@ -0,0 +1,32 @@
//go:build live
package lkgateway
import (
"context"
"fmt"
"testing"
)
// Только чтение: справочник депозитариев-получателей МНП.
func TestQuikRecipientsLive(t *testing.T) {
rc, err := NewRuntimeConfig("/var/lib/bj/.bj/setup.json")
if err != nil {
t.Fatal(err)
}
conn, err := quikOpen(rc.Snapshot().Quik)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
rows, err := conn.QueryContext(context.Background(), "SELECT Id, INN, Name FROM M2MDepoRecipients ORDER BY Id")
if err != nil {
t.Fatal(err)
}
for rows.Next() {
var id int
var inn, name string
_ = rows.Scan(&id, &inn, &name)
fmt.Println(id, inn, name)
}
}