Мастер настройки — 4 главы (Сервер, Учёт и организация, НРД, Запуск) вместо 10 шагов: в пути только нужное этой установке — без лицензии учёта DEPO, QUIK и модулей их шагов нет, сделанное установщиком (база, Валидата) показано строкой; общая навигация «Назад/Далее»; раздел «Время и сеть» с часовым поясом; модули по лицензии (кабинет, ЕСИА Финанс, экспорт QUIK, НПР) с переходом в раздел и возвратом в мастер; «Проверить всю цепочку» (DEPO, QUIK, пробная блокировка по желанию, подпись, НРД); готовые письма в НРД, ARQA и администратору DEPO; ИШ с образа без интернета

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-05 18:04:29 +03:00
parent 7a5673e91f
commit f0dec0c238
22 changed files with 1020 additions and 129 deletions
@@ -25,6 +25,7 @@
</nav>
<div class="settings-body">
{{if .FromWizard}}<div class="card" style="background:var(--accent-weak);margin-bottom:12px;display:flex;justify-content:space-between;gap:8px;flex-wrap:wrap;align-items:center"><span>Вы настраиваете модуль из мастера настройки.</span><a href="/admin/wizard?step=11" class="btn" style="text-decoration:none">← Вернуться в мастер</a></div>{{end}}
{{/* ============ ОБЗОР ============ */}}
<section class="settings-section active" id="sec-overview">
@@ -21,26 +21,24 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
.help-block { background:var(--accent-weak); background:var(--accent-weak); padding:10px 14px; margin:10px 0; font-size:13px; border-radius:0 8px 8px 0; }
.help-block strong { color:var(--accent); }
.wiz-card { max-width:680px; margin:0 auto; min-width:0; }
.wiz-sub { display:flex; justify-content:center; gap:6px; flex-wrap:wrap; margin:-12px auto 18px; max-width:680px; }
.wiz-sub a { font-size:12.5px; padding:4px 12px; border-radius:14px; background:var(--card-2); color:var(--muted); text-decoration:none; border:1px solid var(--border); }
.wiz-sub a.done { color:var(--ok); }
.wiz-sub a.cur { background:var(--accent); color:#fff; border-color:var(--accent); }
.wiz-nav { margin-top:20px; display:flex; justify-content:space-between; gap:8px; flex-wrap:wrap; }
.wiz-card * { min-width:0; }
</style>
<div class="wiz-head">
<h1>Настройка Bridge&amp;Join</h1>
<p>Пошаговый мастер первого запуска — состояние сохраняется после каждого шага</p>
<p>Четыре главы: сервер, учёт и организация, НРД, запуск. Всё сохраняется сразу — можно прерваться и вернуться</p>
</div>
<div class="wizard-progress" style="max-width:940px">
<a href="/admin/wizard?step=1" title="Перейти к шагу 1" class="wstep {{if .Done.License}}done{{end}} {{if eq .Step 1}}current{{end}}"><span class="bub">{{if .Done.License}}✓{{else}}1{{end}}</span><span class="lbl">Лицензия</span></a>
<a href="/admin/wizard?step=2" title="Перейти к шагу 2" class="wstep {{if .Done.Postgres}}done{{end}} {{if eq .Step 2}}current{{end}}"><span class="bub">{{if .Done.Postgres}}✓{{else}}2{{end}}</span><span class="lbl">База bj</span></a>
<a href="/admin/wizard?step=3" title="Перейти к шагу 3" class="wstep {{if .Done.Depo}}done{{end}} {{if eq .Step 3}}current{{end}}"><span class="bub">{{if .Done.Depo}}✓{{else}}3{{end}}</span><span class="lbl">Учёт DEPO</span></a>
<a href="/admin/wizard?step=4" title="Перейти к шагу 4" class="wstep {{if .Done.Org}}done{{end}} {{if eq .Step 4}}current{{end}}"><span class="bub">{{if .Done.Org}}✓{{else}}4{{end}}</span><span class="lbl">Организация</span></a>
<a href="/admin/wizard?step=5" title="Перейти к шагу 5" class="wstep {{if .Done.Quik}}done{{end}} {{if eq .Step 5}}current{{end}}"><span class="bub">{{if .Done.Quik}}✓{{else}}5{{end}}</span><span class="lbl">QUIK МНП</span></a>
<a href="/admin/wizard?step=6" title="Перейти к шагу 6" class="wstep {{if .Done.Crypto}}done{{end}} {{if eq .Step 6}}current{{end}}"><span class="bub">{{if .Done.Crypto}}✓{{else}}6{{end}}</span><span class="lbl">Валидата</span></a>
<a href="/admin/wizard?step=7" title="Перейти к шагу 7" class="wstep {{if .Done.NSD}}done{{end}} {{if eq .Step 7}}current{{end}}"><span class="bub">{{if .Done.NSD}}✓{{else}}7{{end}}</span><span class="lbl">Шлюз НРД</span></a>
<a href="/admin/wizard?step=8" title="Перейти к шагу 8" class="wstep {{if .Done.Certs}}done{{end}} {{if eq .Step 8}}current{{end}}"><span class="bub">{{if .Done.Certs}}✓{{else}}8{{end}}</span><span class="lbl">Ключ</span></a>
<a href="/admin/wizard?step=9" title="Перейти к шагу 9" class="wstep {{if .Done.Alerts}}done{{end}} {{if eq .Step 9}}current{{end}}"><span class="bub">{{if .Done.Alerts}}✓{{else}}9{{end}}</span><span class="lbl">Оповещения</span></a>
<a href="/admin/wizard?step=10" title="Перейти к шагу 10" class="wstep {{if .Done.TestRun}}done{{end}} {{if eq .Step 10}}current{{end}}"><span class="bub">{{if .Done.TestRun}}✓{{else}}10{{end}}</span><span class="lbl">Проверка</span></a>
<div class="wizard-progress" style="max-width:720px">
{{range .Chapters}}<a href="/admin/wizard?step={{.First}}" title="Глава {{.N}}: {{.Title}}" class="wstep {{if .Done}}done{{end}} {{if .Current}}current{{end}}"><span class="bub">{{if .Done}}✓{{else}}{{.N}}{{end}}</span><span class="lbl">{{.Title}}</span></a>
{{end}}
</div>
{{range .Chapters}}{{if and .Current (gt (len .Steps) 1)}}<div class="wiz-sub">{{range .Steps}}<a href="/admin/wizard?step={{.N}}" class="{{if .Current}}cur{{end}}{{if .Done}} done{{end}}">{{if .Done}}✓ {{end}}{{.Title}}</a>{{end}}</div>{{end}}{{end}}
<p style="text-align:center;margin:-14px 0 18px;font-size:13px"><a href="/admin/">Пропустить мастер и перейти в систему →</a> <span class="muted">— вернуться к нему можно из «Настройка → Мастер настройки»</span></p>
{{if .Flash}}<div class="card wiz-card" style="background:var(--accent-weak)"><p style="margin:0">{{.Flash}}</p></div>{{end}}
@@ -109,7 +107,7 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
</details>
</div>
<div class="card">
<h2><span class="dot {{if .Done.License}}ok{{else}}err{{end}}"></span>Шаг 1. Лицензия</h2>
<h2><span class="dot {{if .Done.License}}ok{{else}}err{{end}}"></span>Лицензия</h2>
<div class="help-block"><strong>Что это.</strong> Ключ продукта (BJ-XXXX-XXXX-XXXX) привязывает этот сервер к вашей лицензии.<br>
<strong>Зачем.</strong> От лицензии зависят модули (обмен с DEPO и QUIK, личный кабинет, НПР/УДС) и получение обновлений: bj скачает и поставит модули, входящие в лицензию, автоматически.<br>
<strong>Где взять.</strong> Портал клиента <a href="https://lic.zetit.ru/portal/" target="_blank">lic.zetit.ru/portal</a> или письмо от ZETIT.</div>
@@ -138,15 +136,14 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
<input type="text" name="code" placeholder="BJ-XXXX-XXXX-XXXX" autocomplete="off" style="padding:8px;text-transform:uppercase;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">
<button type="submit" class="btn" style="justify-self:start">{{if .Done.License}}Сменить ключ{{else}}Активировать{{end}}</button>
</form>{{end}}
<div style="margin-top:20px;display:flex;justify-content:space-between"><span></span>
{{if .Done.License}}<a href="/admin/wizard?step=2" class="btn" style="text-decoration:none">К шагу 2 →</a>{{else}}<a href="/admin/wizard?step=2" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">Пропустить (ввести позже) →</a>{{end}}</div>
{{template "wizNav" .}}
</div>
{{end}}
{{/* ============= ШАГ 3: Учёт DEPO (Fancy) — по экранам ============= */}}
{{if eq .Step 3}}
<div class="card">
<h2><span class="dot {{if .Done.Depo}}ok{{else}}err{{end}}"></span>Шаг 3. Учётная система DEPO (Fancy)</h2>
<h2><span class="dot {{if .Done.Depo}}ok{{else}}err{{end}}"></span>Учётная система DEPO (Fancy)</h2>
{{if not .DepoLicensed}}
<p class="muted">Модуль обмена с DEPO и QUIK не входит в текущую лицензию — шаг пропускается.</p>
{{else}}
@@ -155,9 +152,10 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
{{if eq $sub "intro"}}
<div class="help-block"><strong>Что это.</strong> Подключение bj к базе депозитарного учёта DEPO (Fancy).<br>
<strong>Зачем.</strong> bj сверяет с учётом каждый перевод (клиент, счёт, остаток, квалификация) и сам передаёт в DEPO поручения и их исполнение — документы в учёте создаются автоматически. Отсюда же bj возьмёт реквизиты вашей организации (шаг 4).<br>
<strong>Зачем.</strong> bj сверяет с учётом каждый перевод (клиент, счёт, остаток, квалификация) и сам передаёт в DEPO поручения и их исполнение — документы в учёте создаются автоматически. Отсюда же bj возьмёт реквизиты вашей организации (раздел «Организация»).<br>
<strong>Что понадобится.</strong> Адрес сервера базы DEPO, логин и пароль — их даёт ваш администратор баз данных. Для создания таблиц обмена и джобы обработки может понадобиться разово логин администратора SQL — он не сохраняется. В базе DEPO должны быть процедуры обработки обмена (расширение bj2depo) и версия Fancy DEPO с поддержкой M2M — bj проверит их по имени и скажет, чего не хватает.<br>
<strong>Как пойдём.</strong> Подключение → таблицы обмена → обработка → проверка. Каждый шаг проверяется сразу.</div>
{{template "letterCard" (letter "dba")}}
<div style="margin-top:14px"><a href="/admin/wizard?step=3&sub=conn" class="btn" style="text-decoration:none">Начать →</a></div>
{{else if eq $sub "conn"}}
@@ -270,19 +268,17 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
<tr><td>Обработка событий (процедуры)</td><td>{{if .Settings.Fancy.DepoProcCheckOK}}<span style="color:var(--ok)">✓ {{.Settings.Fancy.DepoProcCheck}}</span>{{else if .Settings.Fancy.DepoProcCheck}}<span style="color:var(--warn)">⚠ {{.Settings.Fancy.DepoProcCheck}}</span>{{else}}<span class="muted">не проверялась — «Перепроверить всё»</span>{{end}}</td></tr>
</table>
{{if .Settings.Fancy.Server}}{{template "accPrefix" .}}{{end}}
{{if .Done.Depo}}<p>Учёт DEPO подключён и проверен. Можно переходить к шагу 4 — там bj предложит реквизиты вашей организации из DEPO.</p>{{else}}<p class="muted" style="font-size:13px">Не всё проверено — можно продолжить настройку (шаг 4 и дальше) и вернуться сюда позже. Пока обработка не работает, записи bj ждут в таблицах обмена со статусом TODO и будут обработаны, когда она заработает.</p>{{end}}
{{if .Done.Depo}}<p>Учёт DEPO подключён и проверен. Дальше — «Организация»: bj предложит реквизиты вашей организации из DEPO.</p>{{else}}<p class="muted" style="font-size:13px">Не всё проверено — можно продолжить настройку (шаг 4 и дальше) и вернуться сюда позже. Пока обработка не работает, записи bj ждут в таблицах обмена со статусом TODO и будут обработаны, когда она заработает.</p>{{end}}
<form method="post" action="/admin/setup/wizard-depo/recheck" style="margin:0"><button type="submit" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Перепроверить всё</button></form>
{{end}}
{{end}}
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=2" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 2</a>
<a href="/admin/wizard?step=4" class="btn" style="{{if not .Done.Depo}}background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);{{end}}text-decoration:none">{{if .Done.Depo}}К шагу 4 →{{else}}Пропустить шаг →{{end}}</a></div>
{{template "wizNav" .}}
</div>
{{end}}
{{/* ============= ШАГ 2: База bj (PostgreSQL) ============= */}}
{{if eq .Step 2}}
<div class="card">
<h2><span class="dot {{if .Done.Postgres}}ok{{else}}err{{end}}"></span>Шаг 2. База данных bj (PostgreSQL)</h2>
<h2><span class="dot {{if .Done.Postgres}}ok{{else}}err{{end}}"></span>База данных bj (PostgreSQL)</h2>
<p>База самого bj: журнал заявок и поручений, история обмена с НРД и DEPO, пользователи.</p>
{{if .Done.Postgres}}<p>✓ База готова{{if .PGSummary}}: {{.PGSummary}}{{end}}. Её создал установщик — ничего делать не нужно, переходите к следующему шагу.</p>{{end}}
@@ -311,10 +307,7 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
</form>
</details>
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=1" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 1</a>
{{if .Done.Postgres}}<a href="/admin/wizard?step=3" class="btn" style="text-decoration:none">К шагу 3 →</a>{{else}}<button class="btn" disabled style="opacity:0.5;cursor:not-allowed">К шагу 3 → (сначала подключите базу)</button>{{end}}
</div>
{{template "wizNav" .}}
{{if and devOn (not .Done.Postgres)}}<p style="margin-top:8px"><a href="/admin/wizard?step=3&skip=postgres" style="font-size:13px">Пропустить (режим разработки: in-memory — без сохранения сделок)</a></p>{{end}}
</div>
{{end}}
@@ -322,9 +315,9 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
{{/* ============= ШАГ 4: Наша организация ============= */}}
{{if eq .Step 4}}
<div class="card">
<h2><span class="dot {{if .Done.Org}}ok{{else}}err{{end}}"></span>Шаг 4. Наша организация</h2>
<h2><span class="dot {{if .Done.Org}}ok{{else}}err{{end}}"></span>Наша организация</h2>
<div class="help-block"><strong>Что это.</strong> Депозитарий — участник MOEX МОСТ, от имени которого bj отправляет и принимает переводы: код участника, ИНН и наименование попадают в пакеты НРД и в поручения QUIK.<br>
<strong>Откуда взять.</strong> Если на шаге 3 подключена база DEPO, bj сам определяет реквизиты по учёту — проверьте их и сохраните. Иначе выберите себя из справочника участников НРД или введите вручную.</div>
<strong>Откуда взять.</strong> Если подключена база DEPO (раздел «Учёт DEPO»), bj сам определяет реквизиты по учёту — проверьте их и сохраните. Иначе выберите себя из справочника участников НРД или введите вручную.</div>
{{if .Suggest}}{{if .Suggest.OrgName}}{{if not .Done.Org}}<p style="margin:8px 0;padding:8px 12px;border-radius:8px;background:var(--accent-weak)">✓ <strong>Определено из DEPO</strong>: {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}, код {{.Suggest.OrgCode}}{{if .Suggest.AccountID}}, счёт НРД {{.Suggest.AccountID}}{{end}}. Поля ниже заполнены — проверьте и нажмите «Сохранить».</p>{{end}}{{end}}{{end}}
{{with .Fancy}}{{template "orgSection" .}}{{end}}
<div class="card" id="nsd-req" style="margin-top:12px">
@@ -351,40 +344,35 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
<button type="submit" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">{{if .Settings.Branding.LogoDataURI}}Заменить логотип{{else}}Загрузить логотип{{end}}</button>
</form>
</div>
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=3" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 3</a>
<a href="/admin/wizard?step=5" class="btn" style="{{if not .Done.Org}}background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);{{end}}text-decoration:none">{{if .Done.Org}}К шагу 5 →{{else}}Пропустить →{{end}}</a>
</div>
{{template "wizNav" .}}
</div>
{{end}}
{{/* ============= ШАГ 5: QUIK МНП ============= */}}
{{if eq .Step 5}}
<div class="card">
<h2><span class="dot {{if .Done.Quik}}ok{{else}}err{{end}}"></span>Шаг 5. QUIK — модуль неторговых поручений</h2>
<h2><span class="dot {{if .Done.Quik}}ok{{else}}err{{end}}"></span>QUIK — модуль неторговых поручений</h2>
<div class="help-block"><strong>Что это.</strong> Модуль неторговых поручений QUIK (МНП) — продукт компании ARQA Technologies, разработчика QUIK. Через него bj блокирует бумаги клиента в QUIK на время перевода, снимает блокировку при отказе и после исполнения проводит поручение.<br>
<strong>Если МНП у вас ещё нет.</strong> Обратитесь в ARQA Technologies (<a href="https://arqatech.com" target="_blank" rel="noopener">arqatech.com</a>, служба поддержки QUIK) и попросите подключить модуль неторговых поручений для работы с внешней системой. После подключения ARQA выдаст адрес сервера МНП и учётные данные — их и нужно ввести ниже.<br>
<strong>Если МНП уже есть.</strong> Адрес сервера и учётные данные — у вашего администратора QUIK.</div>
{{if .DepoLicensed}}{{template "letterCard" (letter "arqa")}}{{end}}
{{if not .DepoLicensed}}<p class="muted">Модуль неторговых поручений не входит в текущую лицензию — шаг пропускается.</p>
{{else}}{{with .Fancy}}{{template "quikSection" .}}{{end}}
{{if .Settings.Quik.Server}}{{template "mnpCheck" .}}{{end}}
{{if .Settings.Quik.M2MMissing}}<p style="color:var(--err);margin:8px 0">✗ Подключение есть, но под этим логином не видно объектов M2M: {{.Settings.Quik.M2MMissing}}. Проверьте имя базы МНП и права логина (роль InstrEu) и нажмите «Проверить» ещё раз.</p>{{end}}
<div class="help-block" style="margin-top:12px"><strong>Справочник депозитариев-получателей.</strong> Чтобы QUIK принимал поручения M2M, в МНП должен быть справочник депозитариев-получателей (<code>M2MDepoRecipients</code>): bj заполняет его сам по справочнику участников НРД. <b>Модуль МНП читает справочник только при запуске</b> — после первого заполнения попросите администратора QUIK перезапустить модуль неторговых поручений, иначе QUIK ответит «Неизвестный депозитарий получателя».<br><span class="muted">{{quikRecipientsInfo}}</span></div>{{end}}
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=4" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 4</a>
<a href="/admin/wizard?step=6" class="btn" style="{{if not .Done.Quik}}background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);{{end}}text-decoration:none">{{if .Done.Quik}}К шагу 6 →{{else}}Пропустить →{{end}}</a>
</div>
{{template "wizNav" .}}
</div>
{{end}}
{{/* ============= ШАГ 6: Валидата ============= */}}
{{if eq .Step 6}}
<div class="card">
<h2><span class="dot {{if .Done.Crypto}}ok{{else}}err{{end}}"></span>Шаг 6. СКЗИ «Валидата Клиент L»</h2>
<p>СКЗИ «Валидата Клиент L» проверяет подписи НРД на квитанциях и сообщениях и нужно для работы с ключами и сертификатами (шаг 8).</p>
<h2><span class="dot {{if .Done.Crypto}}ok{{else}}err{{end}}"></span>СКЗИ «Валидата Клиент L»</h2>
<p>СКЗИ «Валидата Клиент L» проверяет подписи НРД на квитанциях и сообщениях и нужно для работы с ключами и сертификатами (раздел «Ключ»).</p>
{{if .ValidataVer}}
<p>✓ Валидата установлена вместе с bj: версия {{.ValidataVer}}, каталог <code>/opt/Validata/VDCSP</code>. Ничего настраивать на этом шаге не нужно.</p>
<p class="muted" style="font-size:13px">bj работает с Валидатой через свою службу криптографии <code>bj-crypto</code>{{if eq .CryptoSvc "active"}} — она запущена{{else}} — сейчас она не запущена: служба ставится обновлением с сервера лицензий (шаг 1, кнопка «Обновить сейчас»), а работает с ключом, установленным на шаге 8{{end}}.</p>
<p class="muted" style="font-size:13px">bj работает с Валидатой через свою службу криптографии <code>bj-crypto</code>{{if eq .CryptoSvc "active"}} — она запущена{{else}} — сейчас она не запущена: служба ставится обновлением с сервера лицензий (раздел «Лицензия», кнопка «Обновить сейчас»), а работает с ключом, установленным в разделе «Ключ»{{end}}.</p>
{{else}}
<p style="color:var(--err)">Валидата на этом сервере не найдена.</p>
<div class="help-block">
@@ -396,21 +384,20 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
</form>{{end}}
{{end}}
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=5" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 5</a>
{{if .Done.Crypto}}<a href="/admin/wizard?step=7" class="btn" style="text-decoration:none">К шагу 7 →</a>{{else}}<a href="/admin/wizard?step=7&skip=crypto" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">Пропустить →</a>{{end}}
</div>
{{template "wizNav" .}}
</div>
{{end}}
{{/* ============= ШАГ 7: Шлюз НРД (до ключа: ключ ставится в профиль ИШ) ============= */}}
{{if eq .Step 7}}
<div class="card">
<h2><span class="dot {{if .Done.NSD}}ok{{else}}err{{end}}"></span>Шаг 7. Интеграционный шлюз НРД (ИШ)</h2>
<h2><span class="dot {{if .Done.NSD}}ok{{else}}err{{end}}"></span>Интеграционный шлюз НРД (ИШ)</h2>
<div class="help-block">
<strong>Что это.</strong> Интеграционный шлюз — программа НРД на этом же сервере: через неё bj отправляет пакеты M2M в НРД и получает ответы. bj работает со шлюзом по его адресу на сервере (обычно <code>http://localhost:8090</code>).<br>
<strong>К кому обратиться.</strong> Дистрибутив ИШ выпускает НРД (<a href="https://www.nsd.ru/workflow/system/programs/" target="_blank" rel="noopener">nsd.ru/workflow/system/programs</a>). Доступ к контурам — сначала тестовому TEST3, затем боевому — НРД открывает по заявке: обратитесь к своему менеджеру НРД или в поддержку электронного документооборота.
</div>
{{template "letterCard" (letter "nsd-test")}}
{{template "letterCard" (letter "nsd-prod")}}
{{template "igateInstall" .}}
<details style="margin-top:8px"><summary style="cursor:pointer;color:var(--accent);font-size:13px">Дополнительно: адрес шлюза (обычно не нужно)</summary>
<form method="post" action="/admin/setup/nsd" style="margin-top:10px;display:grid;gap:10px;max-width:680px">
@@ -427,20 +414,17 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
<div><button type="submit" class="btn">Сохранить и проверить</button></div>
</form>
</details>
<p class="muted" style="font-size:13px;margin-top:12px">Депозитарные реквизиты в НРД: {{if and .Settings.NSD.DeponentCode .Settings.NSD.AccountID .Settings.NSD.SectionID}}код <code>{{.Settings.NSD.DeponentCode}}</code>, счёт <code>{{.Settings.NSD.AccountID}}</code>, раздел <code>{{.Settings.NSD.SectionID}}</code>{{else}}<span style="color:var(--err)">не заданы</span>{{end}} — на <a href="/admin/wizard?step=4#nsd-req">шаге 4</a>.</p>
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=6" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 6</a>
{{if .Done.NSD}}<a href="/admin/wizard?step=8" class="btn" style="text-decoration:none">К шагу 8: ключ →</a>{{else}}<a href="/admin/wizard?step=8&skip=nsd" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">Пропустить ({{if devOn}}mock-режим{{else}}обмен с НРД не настроен{{end}}) →</a>{{end}}
</div>
<p class="muted" style="font-size:13px;margin-top:12px">Депозитарные реквизиты в НРД: {{if and .Settings.NSD.DeponentCode .Settings.NSD.AccountID .Settings.NSD.SectionID}}код <code>{{.Settings.NSD.DeponentCode}}</code>, счёт <code>{{.Settings.NSD.AccountID}}</code>, раздел <code>{{.Settings.NSD.SectionID}}</code>{{else}}<span style="color:var(--err)">не заданы</span>{{end}} — в разделе <a href="/admin/wizard?step=4#nsd-req">«Организация»</a>.</p>
{{template "wizNav" .}}
</div>
{{end}}
{{/* ============= ШАГ 8: Ключ и сертификаты (после шлюза: ключ ставится в профиль ИШ) ============= */}}
{{if eq .Step 8}}
<div class="card">
<h2><span class="dot {{if .Done.Certs}}ok{{else}}err{{end}}"></span>Шаг 8. Ключ и сертификаты</h2>
<h2><span class="dot {{if .Done.Certs}}ok{{else}}err{{end}}"></span>Ключ и сертификаты</h2>
<p>Здесь две части: ключ и сертификат вашей организации (ставите вы) и сертификаты НРД и удостоверяющих центров (bj ведёт их сам).</p>
{{if not .Done.NSD}}<p style="padding:8px 12px;border-radius:8px;background:var(--accent-weak);margin:8px 0">Ключ устанавливается в профиль интеграционного шлюза — сначала установите шлюз НРД на <a href="/admin/wizard?step=7">шаге 7</a>.</p>{{end}}
{{if not .Done.NSD}}<p style="padding:8px 12px;border-radius:8px;background:var(--accent-weak);margin:8px 0">Ключ устанавливается в профиль интеграционного шлюза — сначала установите шлюз в разделе <a href="/admin/wizard?step=7">«Шлюз НРД»</a>.</p>{{end}}
<h3 style="margin-top:14px">1. Ключ и сертификат организации</h3>
<div class="help-block"><strong>Что это.</strong> Ключ электронной подписи вашей организации для обмена с НРД и его сертификат.<br>
@@ -456,7 +440,7 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
<strong>Откуда берутся.</strong> НРД и Московская биржа публикуют сертификаты в постоянных местах: комплект ГОСТ МБ, НКЦ и НРД — <code>https://fs.moex.com/cdp/sert/GOST.zip</code>, сертификаты УЦ — <code>https://fs.moex.com/cdp/</code>. Когда там появляется новый сертификат, мы добавляем его в комплект bj, и он приезжает к вам с обновлением.<br>
Состояние, ручная проверка и выключение — «Настройка → Криптография → Сертификаты НРД в справочнике ИШ».
</div>
{{if .Settings.NSDCerts.LastRows}}<p class="muted" style="font-size:13px">Последняя сверка: {{.Settings.NSDCerts.LastRun.Format "02.01.2006 15:04"}}{{if .Settings.NSDCerts.LastProfile}}, профиль ИШ {{.Settings.NSDCerts.LastProfile}}{{end}}.</p>{{else}}<p class="muted" style="font-size:13px">Первая сверка пройдёт сразу после установки ключа (шлюз НРД — шаг 7).</p>{{end}}
{{if .Settings.NSDCerts.LastRows}}<p class="muted" style="font-size:13px">Последняя сверка: {{.Settings.NSDCerts.LastRun.Format "02.01.2006 15:04"}}{{if .Settings.NSDCerts.LastProfile}}, профиль ИШ {{.Settings.NSDCerts.LastProfile}}{{end}}.</p>{{else}}<p class="muted" style="font-size:13px">Первая сверка пройдёт сразу после установки ключа.</p>{{end}}
{{if .Certs}}
<h3 style="margin-top:18px">Установленные сертификаты ({{len .Certs}})</h3>
@@ -478,17 +462,61 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
<p class="muted" style="margin-top:12px">Ключ организации ещё не установлен. Можно пропустить этот шаг и вернуться к нему, когда будет архив ключа.</p>
{{end}}
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=7" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 7</a>
<a href="/admin/wizard?step=9" class="btn" style="text-decoration:none">К шагу 9 →</a>
{{template "wizNav" .}}
</div>
{{end}}
{{/* ============= ШАГ 12: Время и сеть (глава «Сервер») ============= */}}
{{if eq .Step 12}}
<div class="card">
<h2><span class="dot {{if .Done.Server}}ok{{else}}warn{{end}}"></span>Время и сеть сервера</h2>
{{if .AutoDone}}<div class="help-block"><strong>Сделано автоматически.</strong>{{range .AutoDone}}<br>✓ {{.}}{{end}}</div>{{end}}
<h3 style="margin:14px 0 4px">Часовой пояс</h3>
<p class="muted" style="margin:0 0 6px;font-size:13px">По нему — время в журналах, уведомлениях и сроках поручений. Время НРД — московское; если сервер в другом поясе, выберите свой.</p>
{{$tz := serverTimeZone}}
<form method="post" action="/admin/setup/timezone" style="display:flex;gap:8px;flex-wrap:wrap;align-items:end">
<input type="hidden" name="return_to" value="/admin/wizard?step=12">
<select name="zone" style="padding:8px;min-width:260px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">{{range timeZones}}<option value="{{.Zone}}" {{if eq .Zone $tz}}selected{{end}}>{{.Title}}</option>{{end}}</select>
<button type="submit" class="btn">Применить</button>
<span class="muted" style="font-size:12.5px">сейчас: {{$tz}}</span>
</form>
<h3 style="margin:16px 0 4px">Сервер времени</h3>
<p style="margin:0 0 6px;font-size:13px">{{if .Done.Server}}<span style="color:var(--ok)">✓ время синхронизируется</span>{{else}}<span style="color:var(--warn)">⚠ время не синхронизируется — укажите сервер времени вашей сети (обычно контроллер домена или маршрутизатор)</span>{{end}}</p>
<form method="post" action="/admin/setup/time" style="display:flex;gap:8px;flex-wrap:wrap;align-items:end">
<input type="hidden" name="return_to" value="/admin/wizard?step=12">
<input name="servers" value="{{.Settings.Time.Servers}}" placeholder="10.10.10.1 ntp.example.ru" style="padding:8px;min-width:260px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">
<button type="submit" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Применить</button>
</form>
<h3 style="margin:16px 0 4px">Адрес и HTTPS</h3>
<p style="margin:0;font-size:13px">Сервер: <code>{{serverAddrs}}</code>. Вход по HTTPS: <a href="https://{{serverIP}}:8443/admin/">https://{{serverIP}}:8443/</a>.</p>
<p class="muted" style="font-size:12.5px;margin:4px 0 0">Чтобы адрес не менялся, задайте постоянный — на консоли сервера: <code>sudo bj-network</code>. Перенаправление на HTTPS и сертификат организации — в «Настройка → Сервер».</p>
{{template "wizNav" .}}
</div>
{{end}}
{{/* ============= ШАГ 11: Модули по лицензии (глава «Запуск») ============= */}}
{{if eq .Step 11}}
<div class="card">
<h2><span class="dot {{if .Done.Modules}}ok{{else}}warn{{end}}"></span>Модули по лицензии</h2>
<p class="muted" style="margin:0 0 8px">Здесь только модули, которые входят в вашу лицензию. Каждый настраивается в своём разделе — кнопка «Настроить» откроет его, а вернуться в мастер можно ссылкой вверху раздела.</p>
{{range .Modules}}
<div style="border:1px solid var(--border);border-radius:8px;padding:10px 12px;margin:8px 0">
<div style="display:flex;justify-content:space-between;gap:8px;align-items:center;flex-wrap:wrap">
<b><span class="dot {{if .Ready}}ok{{else}}warn{{end}}"></span>{{.Title}}</b>
<a href="{{.Link}}" class="btn" style="text-decoration:none;padding:5px 12px">{{if .Ready}}Открыть{{else}}Настроить{{end}} →</a>
</div>
<p class="muted" style="font-size:13px;margin:6px 0 2px">{{.What}}</p>
<p style="font-size:13px;margin:0;color:var({{if .Ready}}--ok{{else}}--warn{{end}})">{{if .Ready}}✓{{else}}⚠{{end}} {{.Status}}</p>
</div>
{{end}}
{{template "wizNav" .}}
</div>
{{end}}
{{/* ============= ШАГ 9: Оповещения и резервные копии ============= */}}
{{if eq .Step 9}}
<div class="card">
<h2><span class="dot {{if .Done.Alerts}}ok{{else}}warn{{end}}"></span>Шаг 9. Оповещения и резервные копии</h2>
<h2><span class="dot {{if .Done.Alerts}}ok{{else}}warn{{end}}"></span>Оповещения и резервные копии</h2>
<div class="help-block"><strong>Зачем.</strong> bj сам следит за обменом: НРД не отвечает, перевод не доставлен, ошибка обработки в DEPO, истекает сертификат ключа. Чтобы об этом узнали сразу, а не открыв bj, — укажите почту. Резервные копии позволят восстановить сервер целиком (настройки, заявки, шлюз НРД) после сбоя или переустановки.<br>
<strong>Можно позже.</strong> Шаг необязательный — обмен работает и без него, но мы настоятельно советуем настроить оба пункта.</div>
<style>.wiz9 label:not(:has(input[type=checkbox])){display:block;margin:0}.wiz9 label input:not([type=checkbox]),.wiz9 label select,.wiz9 .grid input,.wiz9 .grid select{width:100%;box-sizing:border-box;margin-top:4px}</style>
@@ -501,10 +529,7 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
{{template "backupCard" .}}
{{end}}
</div>
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=8" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 8</a>
<a href="/admin/wizard?step=10" class="btn" style="{{if not .Done.Alerts}}background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);{{end}}text-decoration:none">{{if .Done.Alerts}}К проверке →{{else}}Пропустить →{{end}}</a>
</div>
{{template "wizNav" .}}
</div>
{{end}}
@@ -512,7 +537,7 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
{{if eq .Step 10}}
<div class="card">
{{if devOn}}
<h2><span class="dot {{if .Done.TestRun}}ok{{else}}err{{end}}"></span>Шаг 10. Тестовая заявка</h2>
<h2><span class="dot {{if .Done.TestRun}}ok{{else}}err{{end}}"></span>Тестовая заявка</h2>
<p>Прогон полного цикла: создание заявки → валидация → подпись → отправка в НРД (или mock) → ожидание Decision → подтверждение.</p>
<div class="help-block">
@@ -533,7 +558,7 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
</table>
{{end}}
{{end}}
{{if not devOn}}<h2><span class="dot" id="rd-dot"></span>Шаг 10. Готовность к работе</h2>{{else}}<h3 style="margin-top:18px">Готовность к работе</h3>{{end}}
{{if not devOn}}<h2><span class="dot" id="rd-dot"></span>Готовность к работе</h2>{{else}}<h3 style="margin-top:18px">Готовность к работе</h3>{{end}}
<p class="muted" style="margin:4px 0 10px">bj проверяет всё, что нужно для обмена с НРД, прямо сейчас. Красное — обмен работать не будет, жёлтое — работать будет, но лучше исправить. У каждой строки — ссылка туда, где это исправляется.</p>
<div id="rd-sum" style="font-weight:600;margin:6px 0">Проверяю…</div>
<table id="rd-tab" style="width:100%"><tbody></tbody></table>
@@ -554,13 +579,18 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
document.getElementById('rd-again').onclick=load; load();
})();
</script>
</div>
{{template "chainTest" .}}
<div class="card">
<h2>Письма для подключения</h2>
<p class="muted" style="margin:0 0 6px">Готовые письма с вашими реквизитами — тем, без кого подключение не закончить.</p>
{{template "letterCard" (letter "nsd-test")}}
{{template "letterCard" (letter "nsd-prod")}}
{{if .DepoLicensed}}{{template "letterCard" (letter "arqa")}}{{template "letterCard" (letter "dba")}}{{end}}
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=9" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 9</a>
<a href="/admin/" class="btn" style="text-decoration:none">Перейти к дашборду</a>
</div>
{{template "wizNav" .}}
</div>
{{end}}
@@ -3,7 +3,7 @@
{{$b := .Settings.Backup}}{{$bs := backupStatus}}
<div class="card">
<h2><span class="dot {{if not $b.Enabled}}warn{{else if and $bs $bs.OK}}ok{{else}}err{{end}}"></span>Копии сервера bj на сетевое хранилище</h2>
<p class="muted">Что копируется: база bj (заявки, сделки, кабинет), база и папка шлюза НРД (настройки, сертификаты, полученные от НРД документы), настройки и данные bj. Пароль хранилища bj у себя не хранит — он лежит на сервере в файле, доступном только системе. Восстановить копию можно в мастере настройки (шаг 1, «Восстановить из резервной копии»).</p>
<p class="muted">Что копируется: база bj (заявки, сделки, кабинет), база и папка шлюза НРД (настройки, сертификаты, полученные от НРД документы), настройки и данные bj. Пароль хранилища bj у себя не хранит — он лежит на сервере в файле, доступном только системе. Восстановить копию можно в мастере настройки (глава «Сервер», «Восстановить из резервной копии»).</p>
<p style="margin:8px 0">{{if $bs}}Последняя копия: <b>{{backupWhen $bs.At}}</b> — {{if $bs.OK}}<span style="color:var(--ok)">✓ {{$bs.Dir}}, {{$bs.Msg}}</span>{{else}}<span style="color:var(--err)">✗ {{$bs.Msg}}</span>{{end}}{{else}}<span class="muted">Копий ещё не было.</span>{{end}}</p>
<style>#bk-form label:not(.chk){display:block;margin:0}#bk-form label:not(.chk) input,#bk-form label:not(.chk) select{width:100%;box-sizing:border-box;margin-top:4px}</style>
<form method="post" action="/admin/setup/backup" id="bk-form" style="display:grid;gap:10px;max-width:640px">
@@ -16,6 +16,7 @@
<details id="ig-inst" style="margin-top:8px">
<summary style="cursor:pointer;color:var(--accent)" id="ig-inst-sum">Установить или обновить ИШ</summary>
<p class="muted" style="font-size:13px;margin:8px 0">Дистрибутив ИШ для Linux выпускает НРД. bj скачает его с сайта НРД сам или возьмёт загруженный архив <code>igate-…-amd64.zip</code>. Установка занимает 2–5 минут: пакеты для работы без графики, пользователь igate, база ИШ, служба. Настройки ИШ сохраняются, прежняя версия копируется в /var/backups.</p>
<div id="ig-bundled" style="display:none;margin-bottom:10px"><button type="button" class="btn" id="ig-bnd">Установить с образа (без интернета)</button> <span class="muted" id="ig-bnd-v" style="font-size:12.5px"></span></div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:end">
<label class="f">Версия на сайте НРД<input id="ig-ver" value="107.0.930" style="width:140px"></label>
<button type="button" class="btn" id="ig-dl">Скачать с сайта НРД и установить</button>
@@ -60,11 +61,12 @@
if(!d.installed){ head.innerHTML=bad('ИШ на этом сервере не установлен')+' — установите его ниже.'; document.getElementById('ig-inst').open=true; }
else head.innerHTML=(d.engine==='Running'?ok('ИШ '+d.version+' работает'):bad('ИШ '+d.version+' установлен, движок: '+(d.engine||'не отвечает')+', служба: '+d.service));
if(d.installed) document.getElementById('ig-inst-sum').textContent='Обновить ИШ (сейчас '+d.version+')';
if(d.bundled){ document.getElementById('ig-bundled').style.display='block'; document.getElementById('ig-bnd-v').textContent='версия '+d.bundled+' — дистрибутив НРД записан на сервер при установке с образа'; }
var dep=d.deponent||'', cr=document.getElementById('ig-crypto').value;
var rows=[['test3','Тестовый TEST3'],['prod','Боевой']].map(function(x){
var code=(x[0]==='prod'?'PROD':'TEST3')+dep, ch=(d.channels||[]).filter(function(c){return c.code===code})[0], act=d.active_contour===x[0];
var s=[], a=[];
if(!dep) s.push(bad('нет депозитарного кода (шаг 4)'));
if(!dep) s.push(bad('нет депозитарного кода (мастер → «Организация»)'));
else if(!ch){ s.push('<span class="muted">канала в ИШ нет</span>'); if(d.installed) a.push('<button type="button" class="btn sm" data-act="contour" data-c="'+x[0]+'">Настроить канал</button>'); }
else {
s.push(ok('канал '+ch.code)+' <span class="muted" style="font-size:12px">'+esc(ch.crypto+' · '+ch.endpoint)+'</span>');
@@ -104,6 +106,8 @@
if(s.stage==='error'){ clearInterval(iv); out.innerHTML=bad(s.msg); }
}).catch(function(){});
if(Date.now()-t0>1200000) clearInterval(iv); },3000); }
document.getElementById('ig-bnd').onclick=function(){ var p=new URLSearchParams(); p.set('bundled','1');
out.textContent='Запускаю…'; post('/admin/setup/igate/install',p).then(function(d){ if(!d.ok){out.innerHTML=bad(d.error);return} out.textContent=d.output; poll(); }); };
document.getElementById('ig-dl').onclick=function(){ var p=new URLSearchParams(); p.set('version',document.getElementById('ig-ver').value.trim());
out.textContent='Запускаю…'; post('/admin/setup/igate/install',p).then(function(d){ if(!d.ok){out.innerHTML=bad(d.error);return} out.textContent=d.output; poll(); }); };
document.getElementById('ig-up').onclick=function(){ var f=document.getElementById('ig-file').files[0]; if(!f){ out.textContent='Выберите файл igate-…-amd64.zip'; return; }
@@ -0,0 +1,68 @@
{{/* Готовое письмо (letters.go): текст с реквизитами, копирование, файл, открыть в почте. Аргумент — *Letter. */}}
{{define "letterCard"}}{{with .}}
<details class="letter" style="border:1px solid var(--border);border-radius:8px;padding:8px 12px;margin:8px 0;background:var(--card)">
<summary style="cursor:pointer;font-weight:600">✉ {{.Title}}</summary>
<p class="muted" style="font-size:12.5px;margin:6px 0">Кому: {{.To}}<br>Тема: {{.Subject}}</p>
<pre class="copyable" style="white-space:pre-wrap;font-size:12.5px;max-height:320px;overflow:auto">{{.Body}}</pre>
<p class="muted" style="font-size:12px;margin:4px 0 8px">В квадратных скобках — то, чего bj не знает: впишите перед отправкой.</p>
<div style="display:flex;gap:8px;flex-wrap:wrap">
<a class="btn" href="/admin/setup/letters/{{.Key}}.txt" style="text-decoration:none">Скачать текст</a>
<a class="btn" href="{{mailtoLink .}}" style="text-decoration:none;background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Открыть в почте</a>
</div>
</details>
{{end}}{{end}}
{{/* Проверка всей цепочки (chaintest.go). Данные — WizardData. */}}
{{define "chainTest"}}
<div class="card" id="chain">
<h2><span class="dot" id="ch-dot"></span>Проверить всю цепочку</h2>
<p class="muted" style="margin:0 0 8px">Сквозная проверка без настоящего перевода: bj по очереди проходит каждое звено и показывает, какое работает, а какое нет.{{if .DepoLicensed}} Учёт DEPO — проверочное событие, QUIK — только чтение (пробная блокировка — по желанию, ниже).{{end}} Подпись — ключом организации. НРД — запрос справочника участников через шлюз с ответом НРД.</p>
{{if .DepoLicensed}}
<details style="margin:6px 0 10px">
<summary style="cursor:pointer;color:var(--accent);font-size:13px">Добавить пробную блокировку в QUIK (1 бумага клиента — заблокировать и сразу снять)</summary>
<div id="ch-blk" style="display:grid;gap:8px;margin-top:8px;max-width:640px">
<p class="muted" style="font-size:12.5px;margin:0">В боевом QUIK будет зарегистрировано поручение «Вывод ЦБ (M2M)» на 1 шт. и сразу снято: в QUIK оно останется в истории как снятое. Нужны код клиента в QUIK и бумага, которая у клиента есть.</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">
<label style="margin:0">Код клиента в QUIK<input id="ch-client" placeholder="BR859" autocomplete="off" style="width:100%;box-sizing:border-box;margin-top:4px"></label>
<label style="margin:0">ISIN бумаги<input id="ch-isin" value="RU0009029540" autocomplete="off" style="width:100%;box-sizing:border-box;margin-top:4px"></label>
</div>
<label style="margin:0">Депозитарий-получатель (условный)<select id="ch-inn" style="width:100%;box-sizing:border-box;margin-top:4px">{{range handbookDepos}}<option value="{{.INN}}">{{.Name}} · ИНН {{.INN}}</option>{{end}}</select></label>
<label style="display:flex;gap:8px;align-items:center;font-weight:400;margin:0"><input type="checkbox" id="ch-blk-on" style="width:auto"> Разрешаю записать в QUIK пробное поручение и снять его</label>
</div>
</details>
{{end}}
<button type="button" class="btn" id="ch-run" style="background:var(--ok);border-color:var(--ok)">▶ Проверить всю цепочку</button>
<div id="ch-sum" style="font-weight:600;margin:10px 0 4px"></div>
<table style="width:100%"><tbody id="ch-rows"></tbody></table>
</div>
<script>
(function(){
var rows=document.getElementById('ch-rows'), sum=document.getElementById('ch-sum'), dot=document.getElementById('ch-dot'), btn=document.getElementById('ch-run');
function esc(s){return String(s==null?'':s).replace(/[&<>"]/g,function(c){return{'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]})}
var ic={wait:'<span class="muted">○</span>',run:'<span style="color:var(--accent)">◌</span>',ok:'<span style="color:var(--ok)">✓</span>',warn:'<span style="color:var(--warn)">⚠</span>',err:'<span style="color:var(--err)">✗</span>'};
function draw(d){
var st=d.steps||[];
rows.innerHTML=st.map(function(s){return '<tr><td style="width:22px;vertical-align:top">'+ic[s.state]+'</td><td style="vertical-align:top;padding-right:10px"><b>'+esc(s.title)+'</b>'+(s.state==='run'?' <span class="muted">— проверяю…</span>':'')+(s.detail?'<br><span class="muted" style="font-size:12.5px">'+esc(s.detail)+'</span>':'')+'</td></tr>'}).join('');
if(d.running){ sum.textContent='Проверяю цепочку… (до нескольких минут: НРД отвечает не сразу)'; btn.disabled=true; dot.className='dot'; return; }
btn.disabled=false;
if(!st.length){ sum.textContent=''; return; }
var bad=st.filter(function(s){return s.state==='err'});
if(bad.length){ sum.innerHTML='<span style="color:var(--err)">✗ Не работает: '+esc(bad.map(function(s){return s.title}).join('; '))+'</span>'; dot.className='dot err'; }
else { sum.innerHTML='<span style="color:var(--ok)">✓ Вся цепочка работает'+(d.finished?' (проверено в '+esc(d.finished)+')':'')+'</span>'; dot.className='dot ok'; }
}
function poll(){ fetch('/admin/setup/chain-test/status',{cache:'no-store'}).then(function(r){return r.json()}).then(function(d){ draw(d); if(d.running) setTimeout(poll,2000); }).catch(function(){ setTimeout(poll,4000); }); }
btn.onclick=function(){
var p=new URLSearchParams(), on=document.getElementById('ch-blk-on');
if(on && on.checked){
p.set('quik_block','1'); p.set('quik_client',document.getElementById('ch-client').value.trim());
p.set('quik_isin',document.getElementById('ch-isin').value.trim()); p.set('quik_inn',document.getElementById('ch-inn').value);
if(!confirm('Зарегистрировать в QUIK пробное поручение «Вывод ЦБ (M2M)» на 1 шт. и сразу снять его?')) return;
}
sum.textContent='Запускаю…';
fetch('/admin/setup/chain-test',{method:'POST',body:p,cache:'no-store'}).then(function(r){return r.json()}).then(function(d){
if(!d.ok){ sum.innerHTML='<span style="color:var(--err)">✗ '+esc(d.error)+'</span>'; return; } setTimeout(poll,800); });
};
poll();
})();
</script>
{{end}}
@@ -0,0 +1,7 @@
{{/* Навигация шага мастера: соседние шаги этой установки (лицензия, сделанное автоматически — wizchapters.go). */}}
{{define "wizNav"}}
<div class="wiz-nav">
{{if .Prev}}<a href="/admin/wizard?step={{.Prev}}" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← Назад</a>{{else}}<span></span>{{end}}
{{if .Next}}{{if or .StepDone (not .StepRequired)}}<a href="/admin/wizard?step={{.Next}}" class="btn" style="text-decoration:none">Далее →</a>{{else}}<a href="/admin/wizard?step={{.Next}}" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none" title="Можно вернуться к этому позже">Пропустить пока →</a>{{end}}{{else}}<a href="/admin/" class="btn" style="text-decoration:none">Перейти к работе →</a>{{end}}
</div>
{{end}}