Тестирование M2M: количество бумаг выбирается в предпросмотре — сверка DEPO и отправка берут его

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
zuevav
2026-09-16 15:56:03 +03:00
parent cfd6dfa1e8
commit f35c72c50a
3 changed files with 80 additions and 9 deletions
+45 -8
View File
@@ -18,6 +18,7 @@ import (
"net/http"
"net/url"
"os"
"strconv"
"strings"
"sync"
"time"
@@ -373,10 +374,11 @@ func registerM2MTest(mux *http.ServeMux, a *admin, h *setupHandlers) {
ISIN, Code, Name, Qty string
Bad bool
}
qtys := m2mQtyOverrides(r, *c)
var secs []pvSec
for _, s := range c.Secs {
secs = append(secs, pvSec{
ISIN: s.ISIN, Code: s.Code, Name: m2mSecName[s.ISIN], Qty: s.Qty,
ISIN: s.ISIN, Code: s.Code, Name: m2mSecName[s.ISIN], Qty: pickStr(qtys[s.ISIN], s.Qty),
Bad: m2mSecBad(*c, s),
})
}
@@ -420,7 +422,7 @@ func registerM2MTest(mux *http.ServeMux, a *admin, h *setupHandlers) {
}
var ss []depoOutSec
for _, s := range c.Secs {
ss = append(ss, depoOutSec{ReferenceID: s.Code, ISIN: s.ISIN, Code: s.Code, Qty: s.Qty})
ss = append(ss, depoOutSec{ReferenceID: s.Code, ISIN: s.ISIN, Code: s.Code, Qty: pickStr(qtys[s.ISIN], s.Qty)})
}
return runDepoCheckOut(cl.Last, cl.First, cl.Middle, cl.DocSeries, cl.DocNumber, ss, nsd.AccountID, nsd.SectionID)
}(),
@@ -562,7 +564,8 @@ func registerM2MTest(mux *http.ServeMux, a *admin, h *setupHandlers) {
back("Сначала укажите код участника контрагента (кнопка «Контрагент»)")
return
}
guid, err := h.m2mInitiate(*cs, plan)
qtys := m2mQtyOverrides(r, *cs)
guid, err := h.m2mInitiate(*cs, plan, qtys)
m2mMu.Lock()
plan = m2mLoadPlan()
st := plan.States[id]
@@ -574,7 +577,7 @@ func registerM2MTest(mux *http.ServeMux, a *admin, h *setupHandlers) {
} else {
st.Status = "in_progress"
st.GUID = guid
st.Note = "Отправлено контрагенту " + plan.CounterpartyCode + " (GUID " + guid + "). Ждём ответ."
st.Note = "Отправлено контрагенту " + plan.CounterpartyCode + " (GUID " + guid + ")" + m2mQtyNote(*cs, qtys) + ". Ждём ответ."
msg = "Кейс " + id + ": исходящий M2M отправлен (GUID " + guid + "). Ответ придёт во «Входящие»."
}
st.Updated = time.Now()
@@ -585,8 +588,41 @@ func registerM2MTest(mux *http.ServeMux, a *admin, h *setupHandlers) {
})
}
// m2mInitiate строит исходящий M2M по кейсу и отправляет через НРД контрагенту.
func (h *setupHandlers) m2mInitiate(c m2mCase, plan m2mPlan) (string, error) {
// m2mQtyOverrides — количество по бумагам, выбранное в предпросмотре (qty_<ISIN>); неверное значение
// игнорируется (остаётся количество из плана).
func m2mQtyOverrides(r *http.Request, c m2mCase) map[string]string {
out := map[string]string{}
for _, s := range c.Secs {
v := strings.ReplaceAll(strings.TrimSpace(r.FormValue("qty_"+s.ISIN)), ",", ".")
if v == "" {
continue
}
f, err := strconv.ParseFloat(v, 64)
if err != nil || f <= 0 || (!s.Frac && f != float64(int64(f))) {
continue
}
out[s.ISIN] = v
}
return out
}
// m2mQtyNote — «, количество: SBER/03 × 1» для отметки кейса, если количество меняли.
func m2mQtyNote(c m2mCase, qtys map[string]string) string {
var parts []string
for _, s := range c.Secs {
if q, ok := qtys[s.ISIN]; ok && q != s.Qty {
parts = append(parts, s.Code+" × "+q+" (по плану "+s.Qty+")")
}
}
if len(parts) == 0 {
return ""
}
return ", количество: " + strings.Join(parts, "; ")
}
// m2mInitiate строит исходящий M2M по кейсу и отправляет через НРД контрагенту. qtys — количество,
// выбранное в предпросмотре (ISIN → количество), иначе — из плана.
func (h *setupHandlers) m2mInitiate(c m2mCase, plan m2mPlan, qtys map[string]string) (string, error) {
if len(c.Secs) == 0 {
return "", fmt.Errorf("в кейсе %s нет бумаг для отправки (это кейс сценария 2 — формируется вручную)", c.ID)
}
@@ -596,9 +632,10 @@ func (h *setupHandlers) m2mInitiate(c m2mCase, plan m2mPlan) (string, error) {
secs := make([]ClaimSec, 0, len(c.Secs))
for _, s := range c.Secs {
qty := Quantity{Whole: uint64(atoiSafe(s.Qty))}
q := pickStr(qtys[s.ISIN], s.Qty)
qty := Quantity{Whole: uint64(atoiSafe(q))}
if s.Frac {
qty = Quantity{Fractional: s.Qty}
qty = Quantity{Fractional: q}
}
secs = append(secs, ClaimSec{
SecurityCode: s.Code,