package lkgateway import ( "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" ) func manualTestOrder() *ManualOrder { return &ManualOrder{ Status: "draft", PaperNo: "15", PaperDate: "2026-09-17", ReceivedVia: "лично в офисе", LastName: "Иванов", FirstName: "Иван", MiddleName: "Иванович", BirthDate: "1980-01-02", DocType: "21", DocSeries: "0000", DocNumber: "000000", Account: "СЧЁТ-1", RecvINN: "7702165310", RecvName: "Депозитарий", SettlePlace: "nrd", Securities: []ManualSec{{ISIN: "RU0009029540", Qty: "10"}}, } } func TestManualSendBlockers(t *testing.T) { rc, err := NewRuntimeConfig(t.TempDir() + "/setup.json") if err != nil { t.Fatal(err) } o := manualTestOrder() // Ручных отметок нет, блокировка в QUIK по умолчанию не требуется: черновик готов сразу. if b := manualSendBlockers(rc, o); len(b) != 0 { t.Fatalf("готовый черновик: %v", b) } o.QuikMode = "manual" if b := manualSendBlockers(rc, o); len(b) != 1 || !strings.Contains(b[0], "номер поручения") { t.Fatalf("ручная блокировка без номера: %v", b) } o.QuikInstr = "123" var b []string if b = manualSendBlockers(rc, o); len(b) != 0 { t.Fatalf("всё заполнено: %v", b) } o.QuikMode = "auto" // цикл QUIK не включён if b = manualSendBlockers(rc, o); len(b) != 1 { t.Fatalf("автоблокировка недоступна: %v", b) } o.QuikMode = "none" o.Securities = []ManualSec{{ISIN: "US0378331005", Qty: "0"}} if b = manualSendBlockers(rc, o); len(b) != 2 { t.Fatalf("иностранная бумага и нулевое количество: %v", b) } o.Securities = []ManualSec{{ISIN: "RU0009029540", Qty: "1"}, {ISIN: "RU000A0JUR61", Qty: "1"}} if b = manualSendBlockers(rc, o); len(b) != 1 || !strings.Contains(b[0], "одна бумага") { t.Fatalf("одно поручение — одна бумага: %v", b) } } func TestManualQtyAndClaim(t *testing.T) { if q, ok := manualQty("1,5"); !ok || q.Fractional != "1.5" { t.Fatalf("дробное: %+v", q) } if q, ok := manualQty("7"); !ok || q.Whole != 7 { t.Fatalf("целое: %+v", q) } if _, ok := manualQty("-1"); ok { t.Fatal("отрицательное") } rc, _ := NewRuntimeConfig(t.TempDir() + "/setup.json") c, err := manualClaim(rc, manualTestOrder()) if err != nil || c.ClientAccount != "СЧЁТ-1" || c.Investor.BirthDate != "1980-01-02" || c.Securities[0].SecurityCode != "RU0009029540" || c.CostInfo.No == nil { t.Fatalf("заявка: %+v %v", c, err) } } // Страницы раздела: список, форма, карточка черновика, отметки, отказ, печать. func TestManualPages(t *testing.T) { saved := globalManual globalManual = loadManualStore(t.TempDir() + "/manual.json") defer func() { globalManual = saved }() rc, err := NewRuntimeConfig(t.TempDir() + "/setup.json") if err != nil { t.Fatal(err) } mux := http.NewServeMux() registerManual(mux, rc, nil) do := func(method, path string, form url.Values) *httptest.ResponseRecorder { var req *http.Request if form != nil { req = httptest.NewRequest(method, path, strings.NewReader(form.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } else { req = httptest.NewRequest(method, path, nil) } rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) return rec } if rec := do("GET", "/admin/help/manual", nil); rec.Code != 200 || !strings.Contains(rec.Body.String(), "Порядок работы") { t.Fatalf("инструкция: %d", rec.Code) } if rec := do("GET", "/admin/manual/new", nil); rec.Code != 200 || !strings.Contains(rec.Body.String(), "Новое поручение") { t.Fatalf("форма: %d %s", rec.Code, rec.Body.String()) } form := url.Values{"paper_no": {"15"}, "paper_date": {"2026-09-17"}, "last_name": {"Иванов"}, "first_name": {"Иван"}, "birth_date": {"1980-01-02"}, "doc_type": {"21"}, "doc_series": {"00 00"}, "doc_number": {"000000"}, "account": {"счёт-1"}, "recv_inn": {"7702165310"}, "sec_isin": {"ru0009029540", ""}, "sec_qty": {"10", ""}, "sec_code": {"", ""}, "sec_name": {"", ""}, "do": {"save"}} rec := do("POST", "/admin/manual/save", form) if rec.Code != http.StatusSeeOther || !strings.HasPrefix(rec.Header().Get("Location"), "/admin/manual/1") { t.Fatalf("сохранение: %d %s", rec.Code, rec.Header().Get("Location")) } o, _ := globalManual.Get(1) if o.DocSeries != "0000" || o.Account != "СЧЁТ-1" || len(o.Securities) != 1 || o.Securities[0].ISIN != "RU0009029540" { t.Fatalf("данные: %+v", o) } if rec = do("GET", "/admin/manual/1", nil); rec.Code != 200 || !strings.Contains(rec.Body.String(), "Сверка пройдена, поручение готово") { t.Fatalf("карточка: %d %s", rec.Code, rec.Body.String()) } chk := url.Values{"quik_mode": {"manual"}, "quik_instr": {"77"}} do("POST", "/admin/manual/1/checks", chk) o, _ = globalManual.Get(1) if len(manualSendBlockers(rc, o)) != 0 || o.QuikMode != "manual" { t.Fatalf("блокировка вручную: %+v %v", o.QuikMode, manualSendBlockers(rc, o)) } if rec = do("GET", "/admin/manual/1", nil); !strings.Contains(rec.Body.String(), "Сверка пройдена, поручение готово") { t.Fatalf("готово к отправке: %s", rec.Body.String()) } // Изменение данных сбрасывает отметки. form.Set("id", "1") form.Set("sec_qty", "11") do("POST", "/admin/manual/save", form) if o, _ = globalManual.Get(1); len(o.Checks) != 0 { t.Fatalf("отметки должны сброситься: %+v", o.Checks) } // Черновик можно удалить; отклонённое — нет. do("POST", "/admin/manual/save", url.Values{"paper_date": {"2026-09-17"}, "last_name": {"Пётр"}, "first_name": {"П"}, "birth_date": {"1980-01-02"}, "doc_type": {"21"}, "doc_series": {"0000"}, "doc_number": {"000001"}, "account": {"счёт-2"}, "recv_inn": {"7702165310"}, "sec_isin": {"ru0009029540"}, "sec_qty": {"1"}, "do": {"save"}}) if rec = do("POST", "/admin/manual/2/delete", nil); rec.Code != http.StatusSeeOther || !strings.Contains(rec.Header().Get("Location"), "/admin/manual?") { t.Fatalf("удаление черновика: %d %s", rec.Code, rec.Header().Get("Location")) } if _, ok := globalManual.Get(2); ok { t.Fatal("черновик не удалён") } do("POST", "/admin/manual/1/decline", url.Values{"reason": {"нет подписи клиента"}}) o, _ = globalManual.Get(1) if o.Status != "declined" || o.DeclinedAt.Before(time.Now().Add(-time.Minute)) { t.Fatalf("отказ: %+v", o) } for _, p := range []string{"/admin/manual", "/admin/manual/1", "/admin/manual/1/print"} { if rec = do("GET", p, nil); rec.Code != 200 || !strings.Contains(rec.Body.String(), "нет подписи клиента") && p != "/admin/manual" { t.Fatalf("%s: %d %s", p, rec.Code, rec.Body.String()) } } } func TestOrgIdentity(t *testing.T) { var s Settings s.NSD.DeponentCode = "MC0000000001" if orgCode(s) != "MC0000000001" { t.Fatalf("код по депозитарному коду: %q", orgCode(s)) } s.NSD.OrgCode, s.NSD.OrgINN, s.NSD.OrgName = "mc0000000002", "7700000000", "Депозитарий" if orgCode(s) != "MC0000000002" || orgINN(s) != "7700000000" || transferringINN(s) != "7700000000" || orgName(s) != "Депозитарий" { t.Fatalf("организация из настроек: %q %q %q", orgCode(s), orgINN(s), orgName(s)) } }