package lkgateway // depo_sim.go — песочница сверки с DEPO («имитация ответа Fancy»). // Два режима: «зачисление» (входящий) и «списание» (исходящий). Оператор берёт // реального клиента и бумагу из DEPO, меняет любые поля и нажимает «Проверить» — // отрабатывают те же runDepoCheck / runDepoCheckOut, что и по настоящим // переводам. В НРД и контрагенту ничего не отправляется. import ( "context" "net/http" "strings" "time" ) type depoSimClient struct{ Name, Ser, Num, Account string } type depoSimSec struct{ ISIN, Name string } // depoSimLists — реальные клиенты с открытым торговым счётом и бумаги на // обслуживании из DEPO (для выпадающих списков). Ошибки глушим — списки // вспомогательные, форму можно заполнить руками. func depoSimLists() (clients []depoSimClient, secs []depoSimSec) { if globalRC == nil { return } fs := globalRC.Snapshot().Fancy if strings.TrimSpace(fs.Server) == "" { return } db, err := fancyOpen(fs, fs.DepoDB()) if err != nil { return } defer db.Close() ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) defer cancel() rows, err := db.QueryContext(ctx, `SELECT TOP 25 ISNULL(f.NAME,''), ISNULL(f.DOC_SER,''), ISNULL(f.DOC_NUM,''), ISNULL(c.NUMBER,'') FROM OD_FACES f JOIN OD_CLIENTS c ON c.FACE=f.ID WHERE c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL AND LEN(ISNULL(f.DOC_SER,''))>0 AND LEN(ISNULL(f.DOC_NUM,''))>0 ORDER BY f.NAME`) if err == nil { defer rows.Close() seen := map[string]bool{} for rows.Next() { var c depoSimClient if rows.Scan(&c.Name, &c.Ser, &c.Num, &c.Account) == nil && !seen[c.Name] { seen[c.Name] = true c.Ser = strings.ReplaceAll(c.Ser, " ", "") clients = append(clients, c) } } } rows2, err := db.QueryContext(ctx, `SELECT TOP 20 ISNULL(ISIN,''), ISNULL(NAME,'') FROM OD_VALUES WHERE V_TYPE=2 AND IS_IN=1 AND ISIN LIKE 'RU%' ORDER BY NAME`) if err == nil { defer rows2.Close() for rows2.Next() { var s depoSimSec if rows2.Scan(&s.ISIN, &s.Name) == nil && s.ISIN != "" { secs = append(secs, s) } } } return } // registerDepoSim вешает /admin/m2m-testing/depo-sim (GET — форма, POST — проверка). func registerDepoSim(mux *http.ServeMux, a *admin) { mux.HandleFunc("/admin/m2m-testing/depo-sim", func(w http.ResponseWriter, r *http.Request) { _ = r.ParseForm() v := func(k, def string) string { if s := strings.TrimSpace(r.FormValue(k)); s != "" { return s } return def } defAcc, defSec := "HL171004001C", "36MC0413600000F00" if globalRC != nil { nsd := globalRC.Snapshot().NSD defAcc, defSec = pick(nsd.AccountID, defAcc), pick(nsd.SectionID, defSec) } form := struct { Mode, Last, First, Middle, Ser, Num, ISIN, Code, Qty, Account, Section string }{ Mode: v("mode", "in"), Last: v("last", "Силаев"), First: v("first", "Михаил"), Middle: v("middle", "Иванович"), Ser: v("ser", "4505"), Num: v("num", "620343"), ISIN: strings.ToUpper(v("isin", "RU0009029540")), Code: v("code", ""), Qty: v("qty", "10"), Account: v("account", defAcc), Section: v("section", defSec), } if form.Mode != "out" { form.Mode = "in" } var res *depoResult if r.Method == http.MethodPost { var rr depoResult if form.Mode == "out" { rr = runDepoCheckOut(form.Last, form.First, form.Middle, form.Ser, form.Num, []depoOutSec{{ReferenceID: "sim-1", ISIN: form.ISIN, Code: pick(form.Code, form.ISIN), Qty: form.Qty}}, form.Account, form.Section) } else { it := &IncomingTransfer{ GUID: "SIMULATION", SenderCode: "SIM", ReceivedAt: time.Now(), InvestorLast: form.Last, InvestorFirst: form.First, InvestorMiddle: form.Middle, DocType: "21", DocSeries: form.Ser, DocNumber: form.Num, Securities: []IncomingSecurity{{ReferenceID: "sim-1", ISIN: form.ISIN, SecurityCode: pick(form.Code, form.ISIN), Quantity: form.Qty}}, } rr = runDepoCheck(it) } res = &rr } clients, secs := depoSimLists() data := struct { page Form any Result *depoResult Clients []depoSimClient Secs []depoSimSec Posted bool }{nowPage("Имитация сверки с DEPO", "m2mtest"), form, res, clients, secs, r.Method == http.MethodPost} render(w, a.depoSim, data) }) }