QUIK и НРД в настройках — простой вид и расширенный под раскрытием; выбор «Кто отвечает на переводы M2M»

- QUIK: в карточке режима — одна галочка «Клиенты подают поручения из QUIK (терминал и мобильное приложение)», сохраняется сразу; предупреждение, если режим изменён в расширенных; «Расширенные настройки» (отдельно исходящие/входящие, инструменты МНП, коды клиентов) свёрнуты, без дубля счёта и галочки терминала; формы цикла не сбрасывают поля, которых не прислали.
- НРД: организация — код, ИНН, наименование в ряд; реквизиты в НРД — общий блок с мастером (nsdRequisites), «Определить из DEPO» видна (была белым по белому); блок шлюза из мастера (контуры, доступ к папкам по сети) — и в настройках; ручная настройка шлюза — под «Расширенными настройками».
- Автоматический режим M2M: вместо галочки с «Сохранить» — выбор плиткой «bj сам — автоматически / Оператор», сохраняется сразу (m2mAutoCard); тот же выбор — в мастере на шаге «Проверка»; подробности — под раскрытием.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-10-07 08:15:06 +03:00
parent 0e00a2f88b
commit 56068af897
9 changed files with 137 additions and 65 deletions
+1
View File
@@ -93,6 +93,7 @@ var templateFuncs = template.FuncMap{
}
return "?"
},
"quikModeCustom": quikModeCustom,
"adDraft": adDraftForView, // неудачная попытка подключить домен — форма открывается заполненной
"withRet": func(d any, ret string) map[string]any { return map[string]any{"D": d, "Ret": ret} },
"whForm": func(w WorkHoursSettings, ret string) map[string]any { return map[string]any{"W": w, "Ret": ret} },
+16 -4
View File
@@ -285,8 +285,20 @@ func (h *setupHandlers) saveM2MAuto(w http.ResponseWriter, r *http.Request) {
}
cur := h.rc.Snapshot().M2MAuto
on := r.FormValue("enabled") != ""
if m := r.FormValue("mode"); m != "" { // карточка «Кто отвечает на переводы M2M»: выбор плиткой
on = m == "auto"
}
ajax := r.Header.Get("X-BJ-Ajax") != ""
reply := func(msg string) {
if ajax {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
_, _ = w.Write([]byte(msg))
return
}
setupFlash(w, r, "Автоматический режим M2M: "+msg)
}
if on == cur.Enabled {
setupFlash(w, r, "Автоматический режим M2M: без изменений")
reply("без изменений")
return
}
s := M2MAutoSettings{Enabled: on, ChangedAt: time.Now()}
@@ -294,14 +306,14 @@ func (h *setupHandlers) saveM2MAuto(w http.ResponseWriter, r *http.Request) {
s.ChangedBy = u.Login
}
if err := h.rc.UpdateM2MAuto(s); err != nil {
setupFlash(w, r, "Автоматический режим M2M: ошибка сохранения: "+err.Error())
reply("ошибка сохранения: " + err.Error())
return
}
if on {
log.Printf("m2m-auto: автоматический режим ВКЛЮЧЁН (%s)", s.ChangedBy)
setupFlash(w, r, "Автоматический режим M2M включён: решения по входящим, поручения 16/1 и 16 bj отправляет сам (первый проход — в течение 30 секунд).")
reply("включён — решения по входящим, поручения 16/1 и 16 bj отправляет сам (первый проход — в течение 30 секунд)")
return
}
log.Printf("m2m-auto: автоматический режим выключен (%s)", s.ChangedBy)
setupFlash(w, r, "Автоматический режим M2M выключен — решения и поручения снова отправляет оператор.")
reply("выключен — решения и поручения отправляет оператор")
}
+28 -2
View File
@@ -92,8 +92,12 @@ func (h *setupHandlers) saveQuikCycle(w http.ResponseWriter, r *http.Request) {
}
s.AutoOut = r.FormValue("auto_out") != ""
s.AutoIn = r.FormValue("auto_in") != ""
s.PollTerminal = r.FormValue("poll_terminal") != ""
s.Account = strings.TrimSpace(r.FormValue("account"))
if r.PostForm.Has("poll_terminal_set") { // галочка теперь в простой карточке режима; здесь — только если форма её прислала
s.PollTerminal = r.FormValue("poll_terminal") != ""
}
if r.PostForm.Has("account") { // счёт задаётся в карточке режима; здесь — только если форма его прислала
s.Account = strings.TrimSpace(r.FormValue("account"))
}
s.SecOut = strings.TrimSpace(r.FormValue("sec_out"))
s.SecIn = strings.TrimSpace(r.FormValue("sec_in"))
m := map[string]string{}
@@ -142,6 +146,9 @@ func (h *setupHandlers) saveQuikMode(w http.ResponseWriter, r *http.Request) {
if r.PostForm.Has("account") {
s.Account = strings.TrimSpace(r.FormValue("account"))
}
if r.PostForm.Has("terminal_set") { // галочка «клиенты подают поручения из QUIK» — в той же карточке
s.PollTerminal = r.FormValue("terminal") != ""
}
msg := "только чтение — bj ничего в QUIK не меняет"
if auto {
msg = "чтение и запись — боевая работа: bj сам блокирует, списывает и зачисляет в QUIK"
@@ -149,6 +156,9 @@ func (h *setupHandlers) saveQuikMode(w http.ResponseWriter, r *http.Request) {
msg += "; укажите торговый счёт"
}
}
if s.PollTerminal {
msg += "; поручения клиентов из QUIK (терминал и мобильное приложение) bj замечает и сообщает оператору"
}
if err := h.rc.UpdateQuik(s); err != nil {
msg = "не сохранилось: " + err.Error()
}
@@ -314,3 +324,19 @@ func dfltDB(db string) string {
}
return db
}
// quikModeCustom — режим отличается от двух простых вариантов (записи включены, но исходящие или входящие
// выключены; или наоборот): карточка режима говорит об этом, а «Расширенные настройки» открыты.
func quikModeCustom(q QuikMNPSettings) string {
switch {
case q.WritesEnabled && !q.AutoOut && !q.AutoIn:
return "Запись в QUIK разрешена, но исходящие и входящие ведутся вручную"
case q.WritesEnabled && !q.AutoOut:
return "Исходящие переводы ведутся вручную"
case q.WritesEnabled && !q.AutoIn:
return "Входящие переводы зачисляются вручную"
case !q.WritesEnabled && (q.AutoOut || q.AutoIn):
return "Автоматика включена, но запись в QUIK запрещена главным выключателем"
}
return ""
}
@@ -335,20 +335,19 @@
<h1>НРД</h1>
{{template "verline" (.Versions.Get "ish")}}
{{template "orgSection" .}}
<div class="card" id="m2m-auto">
<h2><span class="dot {{if .Settings.M2MAuto.Enabled}}ok{{else}}warn{{end}}"></span>Автоматический режим M2M</h2>
<p class="muted">Без участия оператора, чтобы соблюдать сроки: <strong>ответ на входящие переводы</strong> по свежей сверке с DEPO (решение по каждой бумаге отдельно — возможен частичный приём; переводы на ИИС — отказ M2M06; бумага для квалифицированных инвесторов при неполной или непрочитанной квалификации клиента — отказ M2M08), <strong>подача поручения 16/1</strong>, как только НРД пришлёт черновик после нашего согласия, и <strong>подача поручения 16</strong> по исходящим после согласия контрагента. Если DEPO или ИШ недоступны — bj повторяет каждые 3 минуты и шлёт оповещение; без сверки ничего не принимается. Обрабатывается только то, что пришло не раньше чем за сутки до включения. Кнопки в карточках остаются для ручной работы.</p>
<form method="post" action="/admin/setup/m2m-auto" style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:10px">
<input type="hidden" name="section" value="m2m-auto">
<label style="display:flex;gap:8px;align-items:center;font-weight:600;margin:0"><input type="checkbox" name="enabled" value="1" {{if .Settings.M2MAuto.Enabled}}checked{{end}}> Автоматический режим включён</label>
<button type="submit" class="btn btn-ok">Сохранить</button>
</form>
{{if not .Settings.M2MAuto.ChangedAt.IsZero}}<p class="muted" style="font-size:12px;margin-top:8px">{{if .Settings.M2MAuto.Enabled}}Включён{{else}}Выключен{{end}} {{msk .Settings.M2MAuto.ChangedAt}}{{with .Settings.M2MAuto.ChangedBy}} · {{.}}{{end}}.{{if .Settings.Quik.Emulate}}{{if devOn}} Шаги QUIK МНП — в режиме имитации.{{else}} Записи в QUIK МНП сейчас не выполняются.{{end}}{{end}}</p>{{end}}
{{template "nsdRequisites" .}}
{{template "m2mAutoCard" (withRet . "")}}
<div class="card" id="nsd-igate">
<h2><span class="dot {{if .Settings.NSD.IGWBaseURL}}ok{{else}}warn{{end}}"></span>Шлюз НРД</h2>
<p class="muted">bj сам ставит и настраивает интеграционный шлюз НРД на этом сервере: контуры TEST3 и боевой, переключение между ними, доступ к папкам шлюза по сети.</p>
{{template "igateInstall" .}}
</div>
<div class="card">
<h2><span class="dot {{if .Settings.NSD.IGWBaseURL}}ok{{else}}warn{{end}}"></span>Интеграционный шлюз (ИШ)</h2>
<p class="muted">{{if not .Settings.NSD.IGWBaseURL}}{{if devOn}}<code>mock-режим</code> — без реального ИШ.{{else}}ИШ не настроен — обмен с НРД не выполняется.{{end}}{{else}}Профиль <code>{{.Settings.NSD.Profile}}</code>, ИШ <code>{{.Settings.NSD.IGWBaseURL}}</code>.{{end}} Стенды и установка — <a href="/admin/help/systems">справка</a>.</p>
<details class="adv-box">
<summary><span class="sum-h">Расширенные настройки</span><span class="muted">шлюз вручную: контур, адрес веб-сервиса, ключевой контейнер, версия ИШ — обычно bj задаёт их сам</span></summary>
<p class="muted" style="margin-top:10px">{{if not .Settings.NSD.IGWBaseURL}}{{if devOn}}<code>mock-режим</code> — без реального ИШ.{{else}}ИШ не настроен — обмен с НРД не выполняется.{{end}}{{else}}Профиль <code>{{.Settings.NSD.Profile}}</code>, ИШ <code>{{.Settings.NSD.IGWBaseURL}}</code>.{{end}}</p>
<form method="post" action="/admin/setup/nsd" style="margin-top:12px;display:grid;gap:10px;max-width:680px">
<input type="hidden" name="keep_requisites" value="1">
<label>Профиль / контур</label>
<select name="profile" id="nsd-profile">
{{/* mock и GUEST — для разработчиков; у клиента — только контуры НРД */}}
@@ -364,20 +363,6 @@
<input type="text" name="igw_base_url" id="nsd-url" value="{{.Settings.NSD.IGWBaseURL}}" placeholder="автозаполнится по профилю">
<label>Ключевой контейнер</label>
<input type="text" name="key_container" id="nsd-container" value="{{.Settings.NSD.KeyContainer}}" placeholder="напр. TEST3_GOST_CONTAINER">
<hr style="border:none;border-top:1px solid var(--border);margin:6px 0">
<p class="muted" style="margin:0">Депозитарные реквизиты (откуда списываются бумаги) — из договора/письма НРД. Нужны для формирования заявки на перевод.</p>
{{if .Settings.Fancy.Server}}<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:4px 0">
<button type="submit" class="btn" formaction="/admin/setup/fancy/detect" formmethod="post" style="background:var(--card)">Определить из DEPO</button>
<span class="muted" style="font-size:12px">Счёт, код и раздел в НРД и карточка организации читаются из базы DEPO (Fancy) и подставляются в пустые поля — проверьте и сохраните.</span>
</div>{{end}}
{{if .Suggest}}{{if .Suggest.AccountID}}<div class="help-block" style="margin:4px 0"><strong>Найдено в DEPO:</strong> счёт {{.Suggest.AccountID}}, код {{.Suggest.DeponentCode}}, раздел {{.Suggest.SectionID}}{{if gt (len .Suggest.Sections) 1}} (открытых разделов: {{len .Suggest.Sections}}){{end}}{{if .Suggest.OrgName}}; организация {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}{{end}}.{{range .Suggest.Notes}}<br><span class="muted">⚠ {{.}}</span>{{end}}</div>{{end}}{{end}}
<label>Депозитарный код</label>
<input type="text" name="deponent_code" value="{{if .Settings.NSD.DeponentCode}}{{.Settings.NSD.DeponentCode}}{{else if .Suggest}}{{.Suggest.DeponentCode}}{{end}}" placeholder="MC0000000000">
<label>Депозитарный счёт</label>
<input type="text" name="account_id" value="{{if .Settings.NSD.AccountID}}{{.Settings.NSD.AccountID}}{{else if .Suggest}}{{.Suggest.AccountID}}{{end}}" placeholder="депозитарный счёт">
<label>Раздел счёта</label>
<input type="text" name="section_id" value="{{if .Settings.NSD.SectionID}}{{.Settings.NSD.SectionID}}{{else if .Suggest}}{{.Suggest.SectionID}}{{end}}" placeholder="раздел депозитарного счёта"{{if .Suggest}}{{if gt (len .Suggest.Sections) 1}} list="depo-sections"{{end}}{{end}}>
{{if .Suggest}}{{if gt (len .Suggest.Sections) 1}}<datalist id="depo-sections">{{range .Suggest.Sections}}<option value="{{.}}">{{end}}</datalist>{{end}}{{end}}
<label style="display:flex;align-items:flex-start;gap:9px;font-weight:400;margin-top:6px;cursor:pointer">
<input type="checkbox" name="igw_v904" style="width:auto;margin-top:3px" {{if .Settings.NSD.IGWv904}}checked{{end}}>
<span>ИШ версии <strong>107.0.0.904+</strong> (Astra 107.0.907+)<br>
@@ -402,6 +387,7 @@
});
})();
</script>
</details>
</div>
</section>
@@ -430,19 +430,7 @@
<p>От имени вашей организации bj отправляет и принимает переводы. {{if .DepoLicensed}}Реквизиты bj берёт из DEPO — проверьте и сохраните.{{else}}Найдите себя в справочнике участников НРД.{{end}}</p>
{{if .Suggest}}{{if .Suggest.OrgName}}{{if not .Done.Org}}<div class="wiz-ok" style="margin:0 0 14px"><i class="wiz-ic"></i><span><strong>Определено из DEPO</strong>: {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}, код {{.Suggest.OrgCode}}{{if .Suggest.AccountID}}, счёт НРД {{.Suggest.AccountID}}{{end}}. Поля ниже заполнены — проверьте и нажмите «Сохранить».</span></div>{{end}}{{end}}{{end}}
{{with .Fancy}}{{template "orgSection" .}}{{end}}
<div class="card" id="nsd-req" style="margin-top:12px">
<h2><span class="dot {{if and .Settings.NSD.DeponentCode .Settings.NSD.AccountID .Settings.NSD.SectionID}}ok{{else}}err{{end}}"></span>Депозитарные реквизиты в НРД</h2>
<p class="muted" style="margin:0 0 8px">Где в НРД учитываются бумаги организации — из договора с НРД{{if .Settings.Fancy.Server}} или кнопкой «Определить из DEPO»{{end}}.</p>
{{if .Suggest}}{{if .Suggest.AccountID}}<div class="help-block" style="margin:0 0 8px"><strong>Найдено в DEPO:</strong> счёт {{.Suggest.AccountID}}, код {{.Suggest.DeponentCode}}, раздел {{.Suggest.SectionID}}.{{range .Suggest.Notes}}<br><span class="muted">⚠ {{.}}</span>{{end}}</div>{{end}}{{end}}
<div style="display:grid;gap:10px;max-width:680px">{{/* поля — в общей форме шага (org-form): одна кнопка «Сохранить» */}}
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px">
<label style="margin:0">Депозитарный код<input type="text" name="deponent_code" form="org-form" value="{{if .Settings.NSD.DeponentCode}}{{.Settings.NSD.DeponentCode}}{{else if .Suggest}}{{.Suggest.DeponentCode}}{{end}}" placeholder="MC0000000000" style="width:100%;margin-top:4px"></label>
<label style="margin:0">Депозитарный счёт<input type="text" name="account_id" form="org-form" value="{{if .Settings.NSD.AccountID}}{{.Settings.NSD.AccountID}}{{else if .Suggest}}{{.Suggest.AccountID}}{{end}}" placeholder="HL000000000C" style="width:100%;margin-top:4px"></label>
<label style="margin:0">Раздел счёта<input type="text" name="section_id" form="org-form" value="{{if .Settings.NSD.SectionID}}{{.Settings.NSD.SectionID}}{{else if .Suggest}}{{.Suggest.SectionID}}{{end}}" placeholder="36MC0000000000F00" style="width:100%;margin-top:4px"></label>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap"><button type="submit" class="btn" form="org-form">Сохранить</button>{{if .Settings.Fancy.Server}}<button type="submit" class="btn" form="org-form" formaction="/admin/setup/fancy/detect" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Определить из DEPO</button>{{end}}</div>
</div>
</div>
{{template "nsdRequisites" .}}
{{template "wizNav" .}}
</div>
{{end}}
@@ -703,6 +691,7 @@
document.getElementById('rd-again').onclick=load; load();
})();
</script>
{{template "m2mAutoCard" (withRet . "/admin/wizard?step=10")}}
{{template "chainTest" .}}
{{template "wizNav" .}}
</div>
@@ -373,6 +373,12 @@ label { font-size:13px; font-weight:500; }
.adv-box[open]>summary .sum-h::before{content:"▾ "}
.adv-box>summary .muted{font-size:13px}
.adv-h{font-size:14.5px;font-weight:650;margin:22px 0 8px}
/* карточка «Как bj работает с QUIK»: поручения клиентов из терминала — одной галочкой */
.quik-term{display:flex;gap:10px;align-items:flex-start;margin:16px 0 0;max-width:680px;line-height:1.45;font-size:13.5px;color:var(--text-2)}
.quik-term b{color:var(--text);font-weight:600}
.quik-term input{margin-top:3px;flex:none}
.org-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(0,2fr);gap:10px;max-width:none}
@media (max-width:700px){.org-grid{grid-template-columns:minmax(0,1fr)}}
@media (max-width:820px) {
.settings { grid-template-columns:1fr; }
.settings-nav { flex-direction:row; overflow-x:auto; position:static; padding-bottom:8px; }
@@ -0,0 +1,52 @@
{{/* Раздел «НРД» — общие блоки «Настройки» и мастера (данные — SetupData или данные мастера: нужны .Settings, .Suggest). */}}
{{/* Депозитарные реквизиты в НРД: поля — в форме организации (org-form), одна кнопка «Сохранить». */}}
{{define "nsdRequisites"}}
<div class="card" id="nsd-req" style="margin-top:12px">
<h2><span class="dot {{if and .Settings.NSD.DeponentCode .Settings.NSD.AccountID .Settings.NSD.SectionID}}ok{{else}}err{{end}}"></span>Депозитарные реквизиты в НРД</h2>
<p class="muted" style="margin:0 0 8px">Где в НРД учитываются бумаги организации — из договора с НРД{{if .Settings.Fancy.Server}} или кнопкой «Определить из DEPO»{{end}}.</p>
{{if .Suggest}}{{if .Suggest.AccountID}}<div class="help-block" style="margin:0 0 8px"><strong>Найдено в DEPO:</strong> счёт {{.Suggest.AccountID}}, код {{.Suggest.DeponentCode}}, раздел {{.Suggest.SectionID}}.{{range .Suggest.Notes}}<br><span class="muted">⚠ {{.}}</span>{{end}}</div>{{end}}{{end}}
<div style="display:grid;gap:10px;max-width:680px">{{/* поля — в общей форме шага (org-form): одна кнопка «Сохранить» */}}
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px">
<label style="margin:0">Депозитарный код<input type="text" name="deponent_code" form="org-form" value="{{if .Settings.NSD.DeponentCode}}{{.Settings.NSD.DeponentCode}}{{else if .Suggest}}{{.Suggest.DeponentCode}}{{end}}" placeholder="MC0000000000" style="width:100%;margin-top:4px"></label>
<label style="margin:0">Депозитарный счёт<input type="text" name="account_id" form="org-form" value="{{if .Settings.NSD.AccountID}}{{.Settings.NSD.AccountID}}{{else if .Suggest}}{{.Suggest.AccountID}}{{end}}" placeholder="HL000000000C" style="width:100%;margin-top:4px"></label>
<label style="margin:0">Раздел счёта<input type="text" name="section_id" form="org-form" value="{{if .Settings.NSD.SectionID}}{{.Settings.NSD.SectionID}}{{else if .Suggest}}{{.Suggest.SectionID}}{{end}}" placeholder="36MC0000000000F00" style="width:100%;margin-top:4px"></label>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap"><button type="submit" class="btn" form="org-form">Сохранить</button>{{if .Settings.Fancy.Server}}<button type="submit" class="btn btn-secondary" form="org-form" formaction="/admin/setup/fancy/detect">Определить из DEPO</button>{{end}}</div>
</div>
</div>
{{end}}
{{/* Кто отвечает на переводы M2M: bj сам (автоматический режим) или оператор. Вход: withRet <данные> <куда вернуться>. */}}
{{define "m2mAutoCard"}}{{$a := .D.Settings.M2MAuto}}
<div class="card" id="m2m-auto">
<h2><span class="dot {{if $a.Enabled}}ok{{else}}warn{{end}}"></span>Кто отвечает на переводы M2M</h2>
<form method="post" action="/admin/setup/m2m-auto" id="m2m-auto-form" class="dq">
{{if .Ret}}<input type="hidden" name="return_to" value="{{.Ret}}">{{end}}<input type="hidden" name="section" value="m2m-auto">
<div class="dq-tiles">
<label class="dq-tile"><input type="radio" name="mode" value="auto" {{if $a.Enabled}}checked{{end}}><span><b>bj сам — автоматически</b><small>Рекомендуется: bj отвечает на входящие переводы по сверке с DEPO и подаёт поручения в НРД без оператора, сроки НРД соблюдаются. Всё видно в журнале.</small></span></label>
<label class="dq-tile"><input type="radio" name="mode" value="manual" {{if not $a.Enabled}}checked{{end}}><span><b>Оператор</b><small>bj сверяет перевод с DEPO и подсказывает решение, а отвечает и подаёт поручения оператор — кнопками в карточке перевода.</small></span></label>
</div>
<p class="dq-note" id="m2m-auto-st" aria-live="polite">Выбор сохраняется сразу.{{if not $a.ChangedAt.IsZero}} {{if $a.Enabled}}Включён{{else}}Выключен{{end}} {{msk $a.ChangedAt}}{{with $a.ChangedBy}} · {{.}}{{end}}.{{end}}{{if .D.Settings.Quik.Emulate}}{{if devOn}} Шаги QUIK МНП — в режиме имитации.{{else}} Записи в QUIK МНП сейчас не выполняются.{{end}}{{end}}</p>
<noscript><button type="submit" class="btn">Сохранить</button></noscript>
</form>
<details style="margin-top:6px"><summary style="cursor:pointer;color:var(--accent);font-size:13px">Что bj делает сам в автоматическом режиме</summary>
<ul class="muted" style="font-size:13px;line-height:1.5;margin:8px 0 0;padding-left:18px">
<li><b>Входящие переводы</b> — отвечает по свежей сверке с DEPO, по каждой бумаге отдельно (возможен частичный приём). Переводы на ИИС — отказ M2M06; бумага для квалифицированных инвесторов, а у клиента нет квалификации, — отказ M2M08.</li>
<li><b>Поручение 16/1</b> — подаёт, как только НРД пришлёт черновик после нашего согласия.</li>
<li><b>Исходящие переводы</b> — поручение 16 подаёт после согласия контрагента.</li>
<li>Если DEPO или шлюз НРД недоступны, bj повторяет каждые 3 минуты и шлёт оповещение; без сверки ничего не принимается.</li>
<li>При включении берутся переводы, пришедшие не раньше чем за сутки до этого. Кнопки в карточках остаются для ручной работы.</li>
</ul>
</details>
</div>
<script>(function(){
var f=document.getElementById('m2m-auto-form'), st=document.getElementById('m2m-auto-st'); if(!f||!window.fetch) return;
f.addEventListener('change', function(){
fetch(f.action,{method:'POST',body:new URLSearchParams(new FormData(f)),headers:{'Content-Type':'application/x-www-form-urlencoded','X-BJ-Ajax':'1'},cache:'no-store'})
.then(function(r){ return r.text().then(function(t){ st.textContent=(r.ok?'✓ ':'✗ ')+t; }); })
.catch(function(){ st.textContent='✗ Не сохранилось — обновите страницу и выберите ещё раз.'; });
});
f.addEventListener('submit', function(e){ e.preventDefault(); });
})();</script>
{{end}}
@@ -70,14 +70,13 @@ function hbBind(inp,box,list,onPick){
}
</script>
{{if not .Participants}}<span class="muted" style="font-size:12px">Справочник участников ещё не получен — запросите его в «Диагностике» или введите данные вручную.</span>{{end}}
<label>Код участника</label>
<input type="text" name="org_code" value="{{if .Settings.NSD.OrgCode}}{{.Settings.NSD.OrgCode}}{{else if .Suggest}}{{.Suggest.OrgCode}}{{end}}" placeholder="MC0000000000" maxlength="12">
<label>ИНН</label>
<input type="text" name="org_inn" value="{{if .Settings.NSD.OrgINN}}{{.Settings.NSD.OrgINN}}{{else if .Suggest}}{{.Suggest.OrgINN}}{{end}}" maxlength="10" inputmode="numeric">
<label>Наименование</label>
<input type="text" name="org_name" value="{{if .Settings.NSD.OrgName}}{{.Settings.NSD.OrgName}}{{else if .Suggest}}{{.Suggest.OrgName}}{{end}}">
<div class="grid org-grid">
<div><label>Код участника</label><input type="text" name="org_code" value="{{if .Settings.NSD.OrgCode}}{{.Settings.NSD.OrgCode}}{{else if .Suggest}}{{.Suggest.OrgCode}}{{end}}" placeholder="MC0000000000" maxlength="12"></div>
<div><label>ИНН</label><input type="text" name="org_inn" value="{{if .Settings.NSD.OrgINN}}{{.Settings.NSD.OrgINN}}{{else if .Suggest}}{{.Suggest.OrgINN}}{{end}}" maxlength="10" inputmode="numeric"></div>
<div><label>Наименование</label><input type="text" name="org_name" value="{{if .Settings.NSD.OrgName}}{{.Settings.NSD.OrgName}}{{else if .Suggest}}{{.Suggest.OrgName}}{{end}}"></div>
</div>
{{if and .Suggest (not .InWizard)}}{{if .Suggest.OrgName}}<span class="muted" style="font-size:12px">Из DEPO: {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}, код {{.Suggest.OrgCode}} — подставлено в пустые поля.</span>{{end}}{{end}}
{{if not .InWizard}}<div><button type="submit" class="btn">Сохранить</button></div>{{end}}{{/* в мастере — одна кнопка внизу шага: организация и реквизиты НРД вместе */}}
{{/* кнопка «Сохранить» — в блоке «Депозитарные реквизиты в НРД» ниже: организация и реквизиты сохраняются вместе */}}
</form>
</div>
{{end}}
+15 -14
View File
@@ -40,15 +40,17 @@
{{if not .InWizard}}{{/* в мастере вместо этой карточки — выбор «Как bj работает с QUIK» (шаг QUIK) */}}
{{if .Settings.Quik.Verified}}{{template "quikModeCard" (withRet . "")}}{{end}}
<details class="card" id="sec-quik-cycle-more" style="padding-top:12px"><summary style="cursor:pointer;color:var(--accent);font-size:13.5px">Тонкая настройка автоматического цикла (обычно не нужна)</summary>
<div class="card" id="sec-quik-cycle-more">
<details class="adv-box" {{if quikModeCustom .Settings.Quik}}open{{end}}>
<summary><span class="sum-h">Расширенные настройки</span><span class="muted">отдельно исходящие и входящие, инструменты МНП, коды клиентов — нужны, только если QUIK настроен необычно</span></summary>
<div id="sec-quik-cycle">
<h2><span class="dot {{if .Settings.Quik.Emulate}}warn{{else if not .Settings.Quik.WritesEnabled}}warn{{else if or .Settings.Quik.AutoOut .Settings.Quik.AutoIn}}ok{{else}}warn{{end}}"></span>Автоматический цикл M2M в МНП{{if .Settings.Quik.Emulate}} <span class="hero-status warn">{{if devOn}}имитация{{else}}остановлен{{end}}</span>{{end}}</h2>
<p class="muted">Что делает bj в Модуле неторговых поручений по ходу перевода: <strong>исходящий</strong> — при отправке в НРД регистрирует «Вывод ЦБ (M2M)» (QUIK блокирует бумаги), по подтверждению подписывает (списание), по отказу снимает с причиной; <strong>входящий</strong> — после приёма нашего поручения НРД регистрирует «Ввод ЦБ (M2M)» (зачисление сразу). Каждый шаг виден в карточке перевода и на странице <a href="/admin/quik">QUIK МНП</a>.</p>
<h3 class="adv-h"><span class="dot {{if .Settings.Quik.Emulate}}warn{{else if not .Settings.Quik.WritesEnabled}}warn{{else if or .Settings.Quik.AutoOut .Settings.Quik.AutoIn}}ok{{else}}warn{{end}}"></span>Автоматический цикл M2M в МНП{{if .Settings.Quik.Emulate}} <span class="hero-status warn">{{if devOn}}имитация{{else}}остановлен{{end}}</span>{{end}}</h3>
<p class="muted">Обычно всё задаёт выбор «Только чтение / Чтение и запись» выше. Здесь — если нужно иначе: например, исходящие вести автоматически, а входящие зачислять вручную; или инструменты МНП в QUIK названы не по умолчанию.</p>
<form method="post" action="/admin/setup/quik/cycle" style="margin-top:12px;display:grid;gap:10px;max-width:640px">
<input type="hidden" name="section" value="sec-quik-cycle">
<label style="display:flex;gap:8px;align-items:center;font-weight:600">
<input type="checkbox" name="writes_enabled" value="1" {{if .Settings.Quik.WritesEnabled}}checked{{end}}>
Разрешить записи в МНП (главный предохранитель — база боевая)</label>
Разрешить записи в МНП (главный выключатель: без него bj в QUIK ничего не меняет)</label>
{{if devOn}} <label style="display:flex;gap:8px;align-items:center;font-weight:600">
<input type="checkbox" name="emulate" value="1" {{if .Settings.Quik.Emulate}}checked{{end}}>
Имитация: шаги цикла (блокировка, подпись, снятие, зачисление) проходят и отмечаются «имитация», в МНП ничего не пишется — пока нет пользователя QUIK для блокировок и лимитов. Имеет приоритет над записью.</label>{{end}}
@@ -58,23 +60,19 @@
<label style="display:flex;gap:8px;align-items:center;font-weight:400;margin-left:22px">
<input type="checkbox" name="auto_in" value="1" {{if .Settings.Quik.AutoIn}}checked{{end}}>
Входящие переводы: зачислять в QUIK после приёма поручения НРД автоматически</label>
<label style="display:flex;gap:8px;align-items:center;font-weight:400;margin-left:22px">
<input type="checkbox" name="poll_terminal" value="1" {{if .Settings.Quik.PollTerminal}}checked{{end}}>
Принимаем поручения M2M из QUIK: клиентам разрешена подача «Вывод ЦБ (M2M)» из терминала / мобильного приложения — bj опрашивает их и создаёт заявку оператору. Если фирма такую подачу не разрешает, оставьте выключенным: раздел «Поручения QUIK» их показывать не будет.</label>
<label>Торговый счёт для регистрации поручений
<input type="text" name="account" value="{{.Settings.Quik.Account}}" placeholder="торговый счёт фирмы в QUIK" style="width:100%"></label>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
<label>МНП-инструмент вывода <span class="muted">(пусто = взять из класса INSTROUTM2M)</span>
<input type="text" name="sec_out" value="{{.Settings.Quik.SecOut}}" placeholder="RUROUTM2M" style="width:100%"></label>
<label>МНП-инструмент ввода <span class="muted">(пусто = из класса INSTRINM2M)</span>
<input type="text" name="sec_in" value="{{.Settings.Quik.SecIn}}" placeholder="RURINM2M" style="width:100%"></label>
<label>МНП-инструмент вывода
<input type="text" name="sec_out" value="{{.Settings.Quik.SecOut}}" placeholder="пусто — по классу INSTROUTM2M" style="width:100%"></label>
<label>МНП-инструмент ввода
<input type="text" name="sec_in" value="{{.Settings.Quik.SecIn}}" placeholder="пусто — по классу INSTRINM2M" style="width:100%"></label>
</div>
<label>Исключения кодов клиентов <span class="muted">(по умолчанию «БР-001» → «BR001»; строка «номер портфеля=код QUIK»)</span>
<label>Исключения кодов клиентов <span class="muted">— обычно не нужны: номер портфеля «БР-001» сам становится кодом «BR001»; иначе строкой «номер портфеля=код QUIK»</span>
<textarea name="client_code_map" rows="3" style="width:100%;font-family:ui-monospace,monospace;font-size:12px" placeholder="БР-12-3=BR123">{{quikCodeMap .Settings.Quik.ClientCodeMap}}</textarea></label>
<div style="display:flex;gap:8px;flex-wrap:wrap"><button type="submit" class="btn">Сохранить</button></div>
</form>
</div>
</details>
</div>
{{end}}
{{if .Settings.Quik.LastReport}}
@@ -141,6 +139,9 @@
<div class="dq-jobname" id="quik-acc">
<label>Торговый счёт для поручений<input type="text" name="account" value="{{.D.Settings.Quik.Account}}" placeholder="торговый счёт фирмы в QUIK" spellcheck="false" class="dq-mono"></label>
</div>
<label class="dq-alt quik-term"><input type="checkbox" name="terminal" value="1" {{if .D.Settings.Quik.PollTerminal}}checked{{end}}><span><b>Клиенты подают поручения на перевод из QUIK</b> — из терминала или мобильного приложения («Вывод ЦБ (M2M)»). bj сам замечает такие поручения (раз в 30 секунд) и сообщает оператору; бумаги QUIK блокирует сразу при подаче. Если клиенты подают поручения только через депозитарий — оставьте выключенным.</span></label>
<input type="hidden" name="terminal_set" value="1">
{{with quikModeCustom .D.Settings.Quik}}<p class="dq-warn">{{.}} — изменено в «Расширенных настройках» ниже.</p>{{end}}
<p class="dq-note" id="quik-mode-st" aria-live="polite">Выбор сохраняется сразу.{{if .Ret}} Тонкая настройка — инструменты МНП, коды клиентов, поручения из терминала — в «Настройка → QUIK».{{end}}</p>
<noscript><button type="submit" class="btn">Сохранить</button></noscript>
</form>