package lkgateway import ( "context" "database/sql" "net/http" "sort" "strings" "time" ) // quikdetect.go — автоподбор торгового счёта фирмы для неторговых поручений. // Источник — сама БД модуля QUIK: различные Account из таблиц Actions/Export по // коду нашей фирмы (QuikFirm/FirmID = код участника). Если счёт один — подставляем; // несколько — показываем на выбор; нет истории — просим ввести вручную. // quikDetectAccounts — торговые счета фирмы firm из БД МНП (только чтение). Разные версии // модуля отличаются колонками (у Export нет QuikFirm и т. п.), поэтому пробуем несколько // источников по отдельности и пропускаем те, где колонки нет (Invalid column name). func quikDetectAccounts(ctx context.Context, s QuikMNPSettings, firm string) ([]string, error) { db, err := quikOpen(s) if err != nil { return nil, err } defer db.Close() cctx, cancel := context.WithTimeout(ctx, 20*time.Second) defer cancel() queries := []string{ `SELECT DISTINCT LTRIM(RTRIM(Account)) FROM dbo.Actions WHERE Account IS NOT NULL AND LTRIM(RTRIM(Account)) <> '' AND (@firm = '' OR FirmID = @firm)`, `SELECT DISTINCT LTRIM(RTRIM(Account)) FROM dbo.Actions WHERE Account IS NOT NULL AND LTRIM(RTRIM(Account)) <> '' AND (@firm = '' OR QuikFirm = @firm)`, `SELECT DISTINCT LTRIM(RTRIM(Account)) FROM dbo.Export WHERE Account IS NOT NULL AND LTRIM(RTRIM(Account)) <> '' AND (@firm = '' OR FirmID = @firm)`, } seen := map[string]bool{} var out []string okAny := false var lastErr error for _, q := range queries { rows, err := db.QueryContext(cctx, q, sql.Named("firm", firm)) if err != nil { lastErr = err // чаще всего «Invalid column name» — этот источник пропускаем continue } okAny = true for rows.Next() { var a string if rows.Scan(&a) == nil { if a = strings.TrimSpace(a); a != "" && !seen[a] { seen[a] = true out = append(out, a) } } } rows.Close() } if !okAny && lastErr != nil { return nil, lastErr } sort.Strings(out) return out, nil } // detectQuikAccount — POST /admin/setup/quik/detect-account: подобрать торговый счёт из QUIK. func (h *setupHandlers) detectQuikAccount(w http.ResponseWriter, r *http.Request) { if !requireAdvanced(w, r) { return } if r.Method != http.MethodPost { http.Error(w, "method", http.StatusMethodNotAllowed) return } // ответ: для fetch (кнопка в форме режима) — текстом; иначе — флеш. reply := func(msg string) { if r.Header.Get("X-BJ-Ajax") != "" { w.Header().Set("Content-Type", "text/plain; charset=utf-8") _, _ = w.Write([]byte(msg)) return } setupFlash(w, r, "QUIK: "+msg) } s := h.rc.Snapshot().Quik if s.Server == "" || !s.Verified { reply("сначала подключитесь к БД МНП (Настройка → QUIK, «Проверить подключение»)") return } firm := quikFirmID() accs, err := quikDetectAccounts(r.Context(), s, firm) if err != nil { reply("определить счёт не удалось — " + quikErrorDetail(err)) return } switch len(accs) { case 0: reply("торговых счетов фирмы " + firm + " в истории поручений (Actions/Export) не найдено — введите счёт вручную") case 1: s.Account = accs[0] if err := h.rc.UpdateQuik(s); err != nil { reply("не сохранилось — " + err.Error()) return } reply("торговый счёт подставлен из QUIK — " + accs[0]) default: reply("у фирмы " + firm + " несколько торговых счетов (" + strings.Join(accs, ", ") + ") — впишите нужный вручную") } }