Тестирование M2M: количество бумаг выбирается в предпросмотре — сверка DEPO и отправка берут его
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
package lkgateway
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestM2MQtyOverrides(t *testing.T) {
|
||||
c := m2mCase{ID: "2", Secs: []m2mSec{sSBER("10"), {ISIN: "RU000A0JUR61", Code: "PAI", Qty: "5", Frac: true}}}
|
||||
form := url.Values{"qty_RU0009029540": {"1"}, "qty_RU000A0JUR61": {"2,5"}}
|
||||
r := httptest.NewRequest("POST", "/admin/m2m-testing/initiate", strings.NewReader(form.Encode()))
|
||||
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
got := m2mQtyOverrides(r, c)
|
||||
if got["RU0009029540"] != "1" || got["RU000A0JUR61"] != "2.5" {
|
||||
t.Fatalf("количество: %v", got)
|
||||
}
|
||||
if note := m2mQtyNote(c, got); !strings.Contains(note, "SBER/03 × 1 (по плану 10)") {
|
||||
t.Fatalf("отметка: %q", note)
|
||||
}
|
||||
bad := httptest.NewRequest("GET", "/?qty_RU0009029540=1.5&qty_RU000A0JUR61=-1", nil)
|
||||
if got := m2mQtyOverrides(bad, c); len(got) != 0 {
|
||||
t.Fatalf("дробное для акций и отрицательное — игнорировать: %v", got)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -74,11 +74,18 @@
|
||||
<td>{{if .Bad}}<span class="pill pill-bad">🔴</span>{{else}}<span class="pill pill-ok">🟢</span>{{end}}</td>
|
||||
<td><code>{{.ISIN}}</code>{{if ne .Code .ISIN}} · {{.Code}}{{end}}</td>
|
||||
<td>{{.Name}}{{if .Bad}} <span class="muted" style="font-size:12px;color:#b3261e">— неверная для этого кейса</span>{{end}}</td>
|
||||
<td>{{.Qty}}</td>
|
||||
<td>{{if $.WeInitiate}}<input type="text" name="qty_{{.ISIN}}" value="{{.Qty}}" form="m2m-qty" inputmode="decimal" style="width:90px">{{else}}{{.Qty}}{{end}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{if .WeInitiate}}
|
||||
<form id="m2m-qty" method="get" action="/admin/m2m-testing/preview" style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:8px">
|
||||
<input type="hidden" name="id" value="{{.Case.ID}}">
|
||||
<button type="submit" class="btn">↻ Пересчитать сверку с этим количеством</button>
|
||||
<span class="muted" style="font-size:12px">Количество можно изменить (по плану — как в таблице кейсов); сверка DEPO и отправка берут то, что указано здесь после пересчёта.</span>
|
||||
</form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if .WeInitiate}}
|
||||
@@ -117,6 +124,7 @@
|
||||
{{if .WeInitiate}}
|
||||
<form method="post" action="/admin/m2m-testing/initiate" onsubmit="return confirm('{{if .AlreadySent}}Отправить кейс {{.Case.ID}} ЗАНОВО? Уйдёт новая заявка с новым GUID (НРД примет как новую). Контрагент увидит ещё один перевод.{{else}}Отправить кейс {{.Case.ID}} контрагенту? Уйдёт реальный пакет в НРД (TEST3).{{end}}');">
|
||||
<input type="hidden" name="id" value="{{.Case.ID}}">
|
||||
{{range .Secs}}<input type="hidden" name="qty_{{.ISIN}}" value="{{.Qty}}">{{end}}
|
||||
<button type="submit" class="btn {{if .AlreadySent}}{{else}}btn-ok{{end}}">{{if .AlreadySent}}▶ Отправить заново (новый GUID){{else}}▶ Подтвердить и отправить в НРД{{end}}</button>
|
||||
</form>
|
||||
<a href="/admin/m2m-testing" class="btn">Отмена</a>
|
||||
|
||||
Reference in New Issue
Block a user