package lkgateway // org.go — наша организация (депозитарий) в M2M: код участника, ИНН, наименование. // Задаётся при настройке: выбором себя из справочника участников НРД или вручную. От неё зависят // ИНН передающего/зачисляющего депозитария в пакетах, код отправителя (канал ИШ) и код фирмы в QUIK. import ( "encoding/json" "net/http" "regexp" "strings" ) var regexpMCCode = regexp.MustCompile(`^[A-Z]{2}[0-9A-Z]{10}$`) // handbookParticipants — участники M2M из справочника НРД (без мест расчётов). func handbookParticipants() []HandbookEntry { raw, err := readHandbook() if err != nil { return nil } var hb struct { Participants []HandbookEntry `json:"participants"` } if json.Unmarshal(raw, &hb) != nil { return nil } var out []HandbookEntry for _, p := range hb.Participants { if p.Kind != "place" && p.INN != "" { out = append(out, p) } } return out } // handbookByINN — участник справочника по ИНН (первый не-"place"). func handbookByINN(inn string) (HandbookEntry, bool) { inn = strings.TrimSpace(inn) if inn == "" { return HandbookEntry{}, false } for _, p := range handbookParticipants() { if p.INN == inn { return p, true } } return HandbookEntry{}, false } // handbookByCode — участник по MC-коду. func handbookByCode(code string) (HandbookEntry, bool) { code = strings.ToUpper(strings.TrimSpace(code)) if code == "" { return HandbookEntry{}, false } for _, p := range handbookParticipants() { if strings.EqualFold(p.Code, code) { return p, true } } return HandbookEntry{}, false } // orgCode — код участника M2M нашей организации: из настроек, иначе депозитарный код в НРД. func orgCode(s Settings) string { return strings.ToUpper(strings.TrimSpace(pick(s.NSD.OrgCode, s.NSD.DeponentCode))) } // orgINN — ИНН нашей организации: из настроек, иначе по коду из справочника участников. func orgINN(s Settings) string { if inn := strings.TrimSpace(s.NSD.OrgINN); inn != "" { return inn } if p, ok := handbookByCode(orgCode(s)); ok { return p.INN } return "" } // orgName — наименование нашей организации. func orgName(s Settings) string { if n := strings.TrimSpace(s.NSD.OrgName); n != "" { return n } if p, ok := handbookByCode(orgCode(s)); ok { return p.Name } return "" } // 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")) if hb := strings.TrimSpace(r.FormValue("hb")); hb != "" { if p, ok := handbookByCode(hb); ok { code, inn, name = strings.ToUpper(p.Code), p.INN, pick(p.FullName, p.Name) } } if inn != "" && !manualINNRe.MatchString(inn) { return false, "Организация: ИНН юридического лица — 10 цифр" } if code != "" && !regexpMCCode.MatchString(code) { return false, "Организация: код участника — вида MC0000000000" } changedCode := code != s.OrgCode s.OrgCode, s.OrgINN, s.OrgName = code, inn, name if s.DeponentCode == "" { s.DeponentCode = code } return changedCode, "" } var nsdReqRe = regexp.MustCompile(`^[A-Z0-9]{4,30}$`) // saveNSDRequisites — POST /admin/setup/nsd-requisites: депозитарный код, счёт и раздел организации в НРД // (место хранения бумаг в пакетах). В мастере — на шаге «Организация», рядом с кодом и ИНН. func (h *setupHandlers) saveNSDRequisites(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method", http.StatusMethodNotAllowed) 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) { return "Реквизиты НРД: " + v.name + " — латинские буквы и цифры, без пробелов" } } s.DeponentCode, s.AccountID, s.SectionID = dep, acc, sec return "" }