diff --git a/internal/lkgateway/onboarding_test.go b/internal/lkgateway/onboarding_test.go index f48fac2..6e6a797 100644 --- a/internal/lkgateway/onboarding_test.go +++ b/internal/lkgateway/onboarding_test.go @@ -613,3 +613,34 @@ func tsqlOutsideLiterals(s string) string { } return b.String() } + +// Шаг «Организация» мастера: одна кнопка «Сохранить» — организация и депозитарные реквизиты в НРД одной формой; +// без полей реквизитов (настройки) — только организация, реквизиты не трогаются. +func TestSaveOrgWithRequisites(t *testing.T) { + rc := testRC(t) + h := &setupHandlers{rc: rc} + post := func(form url.Values) *httptest.ResponseRecorder { + r := httptest.NewRequest(http.MethodPost, "/admin/setup/org", strings.NewReader(form.Encode())) + r.Header.Set("Content-Type", "application/x-www-form-urlencoded") + w := httptest.NewRecorder() + h.saveOrg(w, r) + return w + } + w := post(url.Values{"org_code": {"MC0413600000"}, "org_inn": {"7703807489"}, "org_name": {"ООО \"ИК \"Фонтвьель\""}, + "deponent_code": {"MC0413600000"}, "account_id": {"HL171004001C"}, "section_id": {"36MC0413600000F00"}, "return_to": {"/admin/wizard?step=4"}}) + if loc := w.Header().Get("Location"); !strings.HasPrefix(loc, "/admin/wizard?step=4") { + t.Fatalf("после сохранения — на тот же шаг: %q", loc) + } + s := rc.Snapshot().NSD + if s.OrgCode != "MC0413600000" || s.OrgINN != "7703807489" || s.AccountID != "HL171004001C" || s.SectionID != "36MC0413600000F00" || s.DeponentCode != "MC0413600000" { + t.Fatalf("организация и реквизиты одной кнопкой: %+v", s) + } + post(url.Values{"org_code": {"MC0413600000"}, "org_inn": {"7703807489"}, "org_name": {"Фонтвьель"}}) + if s := rc.Snapshot().NSD; s.AccountID != "HL171004001C" || s.OrgName != "Фонтвьель" { + t.Fatalf("форма без реквизитов не должна их стирать: %+v", s) + } + post(url.Values{"org_code": {"MC0413600000"}, "deponent_code": {"MC0413600000"}, "account_id": {"HL 17"}, "section_id": {""}}) + if s := rc.Snapshot().NSD; s.AccountID != "HL171004001C" { + t.Fatalf("неверный счёт — ничего не сохраняется: %+v", s) + } +} diff --git a/internal/lkgateway/org.go b/internal/lkgateway/org.go index c086915..7961d44 100644 --- a/internal/lkgateway/org.go +++ b/internal/lkgateway/org.go @@ -75,13 +75,39 @@ func orgName(s Settings) string { return "" } -// saveOrg — POST /admin/setup/org: организация выбрана из справочника (hb=код) или введена вручную. +// saveOrg — POST /admin/setup/org: организация выбрана из справочника (hb=код) или введена вручную. В мастере +// та же форма несёт и депозитарные реквизиты в НРД (deponent_code…): на шаге одна кнопка «Сохранить». func (h *setupHandlers) saveOrg(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method", http.StatusMethodNotAllowed) return } s := h.rc.Snapshot().NSD + changedCode, bad := orgFromForm(r, &s) + if bad == "" && r.PostForm.Has("deponent_code") { + bad = nsdReqFromForm(r, &s) + } + if bad != "" { + setupFlash(w, r, bad) + return + } + if err := h.rc.UpdateNSD(s); err != nil { + setupFlash(w, r, "Организация: ошибка сохранения: "+err.Error()) + return + } + msg := "Организация сохранена: " + pick(s.OrgName, "—") + ", ИНН " + pick(s.OrgINN, "—") + ", код " + pick(s.OrgCode, "—") + if r.PostForm.Has("deponent_code") { + msg += ". Реквизиты в НРД: код " + pick(s.DeponentCode, "—") + ", счёт " + pick(s.AccountID, "—") + ", раздел " + pick(s.SectionID, "—") + } + if changedCode { + msg += ". Код отправителя применится после перезапуска bj-server." + } + setupFlash(w, r, msg) +} + +// orgFromForm — код, ИНН и наименование организации из формы (выбор из справочника или вручную). +// Возвращает, сменился ли код, и текст ошибки. +func orgFromForm(r *http.Request, s *NSDSettings) (bool, string) { code := strings.ToUpper(strings.TrimSpace(r.FormValue("org_code"))) inn := strings.TrimSpace(r.FormValue("org_inn")) name := strings.TrimSpace(r.FormValue("org_name")) @@ -91,27 +117,17 @@ func (h *setupHandlers) saveOrg(w http.ResponseWriter, r *http.Request) { } } if inn != "" && !manualINNRe.MatchString(inn) { - setupFlash(w, r, "Организация: ИНН юридического лица — 10 цифр") - return + return false, "Организация: ИНН юридического лица — 10 цифр" } if code != "" && !regexpMCCode.MatchString(code) { - setupFlash(w, r, "Организация: код участника — вида MC0000000000") - return + return false, "Организация: код участника — вида MC0000000000" } changedCode := code != s.OrgCode s.OrgCode, s.OrgINN, s.OrgName = code, inn, name if s.DeponentCode == "" { s.DeponentCode = code } - if err := h.rc.UpdateNSD(s); err != nil { - setupFlash(w, r, "Организация: ошибка сохранения: "+err.Error()) - return - } - msg := "Организация сохранена: " + pick(name, "—") + ", ИНН " + pick(inn, "—") + ", код " + pick(code, "—") - if changedCode { - msg += ". Код отправителя применится после перезапуска bj-server." - } - setupFlash(w, r, msg) + return changedCode, "" } var nsdReqRe = regexp.MustCompile(`^[A-Z0-9]{4,30}$`) @@ -124,19 +140,27 @@ func (h *setupHandlers) saveNSDRequisites(w http.ResponseWriter, r *http.Request return } s := h.rc.Snapshot().NSD + if bad := nsdReqFromForm(r, &s); bad != "" { + setupFlash(w, r, bad) + return + } + if err := h.rc.UpdateNSD(s); err != nil { + setupFlash(w, r, "Реквизиты НРД: ошибка сохранения: "+err.Error()) + return + } + setupFlash(w, r, "Реквизиты НРД сохранены: код "+pick(s.DeponentCode, "—")+", счёт "+pick(s.AccountID, "—")+", раздел "+pick(s.SectionID, "—")) +} + +// nsdReqFromForm — депозитарный код, счёт и раздел организации в НРД из формы; текст ошибки. +func nsdReqFromForm(r *http.Request, s *NSDSettings) string { dep := strings.ToUpper(strings.TrimSpace(r.FormValue("deponent_code"))) acc := strings.ToUpper(strings.TrimSpace(r.FormValue("account_id"))) sec := strings.ToUpper(strings.TrimSpace(r.FormValue("section_id"))) for _, v := range []struct{ name, val string }{{"депозитарный код", dep}, {"депозитарный счёт", acc}, {"раздел счёта", sec}} { if v.val != "" && !nsdReqRe.MatchString(v.val) { - setupFlash(w, r, "Реквизиты НРД: "+v.name+" — латинские буквы и цифры, без пробелов") - return + return "Реквизиты НРД: " + v.name + " — латинские буквы и цифры, без пробелов" } } s.DeponentCode, s.AccountID, s.SectionID = dep, acc, sec - if err := h.rc.UpdateNSD(s); err != nil { - setupFlash(w, r, "Реквизиты НРД: ошибка сохранения: "+err.Error()) - return - } - setupFlash(w, r, "Реквизиты НРД сохранены: код "+pick(dep, "—")+", счёт "+pick(acc, "—")+", раздел "+pick(sec, "—")) + return "" } diff --git a/internal/lkgateway/web/templates/admin_wizard.html b/internal/lkgateway/web/templates/admin_wizard.html index d9a9868..55c20a3 100644 --- a/internal/lkgateway/web/templates/admin_wizard.html +++ b/internal/lkgateway/web/templates/admin_wizard.html @@ -444,15 +444,14 @@

Депозитарные реквизиты в НРД

Где в НРД учитываются бумаги организации — из договора с НРД{{if .Settings.Fancy.Server}} или кнопкой «Определить из DEPO»{{end}}.

{{if .Suggest}}{{if .Suggest.AccountID}}
Найдено в DEPO: счёт {{.Suggest.AccountID}}, код {{.Suggest.DeponentCode}}, раздел {{.Suggest.SectionID}}.{{range .Suggest.Notes}}
⚠ {{.}}{{end}}
{{end}}{{end}} -
- +
{{/* поля — в общей форме шага (org-form): одна кнопка «Сохранить» */}}
- - - + + +
-
{{if .Settings.Fancy.Server}}{{end}}
- +
{{if .Settings.Fancy.Server}}{{end}}
+
{{template "wizNav" .}} diff --git a/internal/lkgateway/web/templates/part_org.html b/internal/lkgateway/web/templates/part_org.html index 4cf58f6..89c6f3a 100644 --- a/internal/lkgateway/web/templates/part_org.html +++ b/internal/lkgateway/web/templates/part_org.html @@ -5,11 +5,13 @@

Депозитарий — участник MOEX МОСТ, от имени которого bj отправляет и принимает переводы: ИНН передающего и зачисляющего депозитария в пакетах{{if mod "quik-mnp"}}, код отправителя и код фирмы в QUIK{{end}}. Выберите себя из справочника участников НРД или введите данные вручную.

{{end}} {{if and .OrgINN .OrgCode}}

{{if .OrgName}}{{.OrgName}}{{else}}—{{end}} · ИНН {{.OrgINN}} · код {{.OrgCode}}{{if not .Settings.NSD.OrgINN}} (ИНН взят из справочника по коду){{end}}

{{else if not .InWizard}}

Организация не задана — переводы не будут сформированы.

{{end}} -
+ + {{if .InWizard}}{{end}}
+ {{if not .Participants}}Справочник участников ещё не получен — запросите его в «Диагностике» или введите данные вручную.{{end}} @@ -60,8 +76,8 @@ function hbBind(inp,box,list,onPick){ - {{if .Suggest}}{{if .Suggest.OrgName}}Из DEPO: {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}, код {{.Suggest.OrgCode}} — подставлено в пустые поля.{{end}}{{end}} -
+ {{if and .Suggest (not .InWizard)}}{{if .Suggest.OrgName}}Из DEPO: {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}, код {{.Suggest.OrgCode}} — подставлено в пустые поля.{{end}}{{end}} + {{if not .InWizard}}
{{end}}{{/* в мастере — одна кнопка внизу шага: организация и реквизиты НРД вместе */}}
{{end}}