Files
Bridge-and-Join-s/internal/lkgateway/web/templates/admin_depo_sim.html
T
zuevav da12a9ec1c feat(bj): модуль ЛК на PostgreSQL, обмен с DEPO, боевой ЛК с имитатором НРД, QUIK, НПР
- bj_exchange и «кран» Fancy убраны: данные модуля ЛК в PostgreSQL (схема lk)
- трансферные таблицы FTV_M2M: создание при установке, события, справочник участников
- сверка с DEPO: счёт, ФИО, дата рождения, ИИС по договору, квал-инвестор из опции DEPO
- боевой ЛК ЕСИА Финанс при тестовом ИШ: журнал, отказ через DEPO, статусы в ЛК
- онлайн-лицензирование в bj, план тестирования M2M для контрагента (интерактивный HTML)
- модули QUIK МНП, экспорт QUIK, НПР/УДС; документы и тестовые материалы

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 19:41:12 +03:00

140 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "content"}}
<style>
.sim-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px 14px}
.sim-grid label{display:block;font-size:12px;color:var(--muted);margin-bottom:3px}
.sim-grid input,.sim-grid select{width:100%;padding:7px 9px;border-radius:8px;border:1px solid var(--border);background:var(--card);color:var(--text);font-size:13.5px}
.mode{display:flex;gap:8px;flex-wrap:wrap;margin:0 0 12px}
.mode label{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border-radius:9px;border:1.5px solid var(--border);background:var(--card);cursor:pointer;font-weight:600;font-size:13px}
.mode label:has(input:checked){border-color:var(--accent);background:var(--accent-weak,#e7f0fb)}
.presets{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0 4px}
.presets button{font-size:12.5px;padding:6px 11px;border-radius:9px;border:1px solid var(--border);background:var(--card-2);color:var(--text);cursor:pointer}
.presets button:hover{border-color:var(--accent)}
.out-only{display:none}.is-out .out-only{display:block}
.dc-ok{color:var(--ok,#12813f);font-weight:700}.dc-bad{color:var(--err,#b3261e);font-weight:700}.dc-warn{color:var(--warn,#9a6b12);font-weight:700}
.dc-rec{display:inline-block;padding:5px 14px;border-radius:999px;font-weight:700;font-size:14px}
.dc-rec.confirm{background:var(--ok-weak,#e6f4ea);color:var(--ok,#12813f)}
.dc-rec.reject{background:var(--err-weak,#fbe9e7);color:var(--err,#b3261e)}
.dc-rec.mixed{background:var(--warn-weak,#fff4e0);color:var(--warn,#9a6b12)}
</style>
<p style="margin:0 0 12px"><a href="/admin/m2m-testing" class="btn" onclick="if(history.length>1){history.back();return false}">← Назад</a></p>
<div class="card {{if eq .Form.Mode "out"}}is-out{{end}}" id="simCard">
<h2>Имитация сверки с DEPO</h2>
<p class="muted" style="font-size:13px">Выберите реального клиента и бумагу из DEPO, при желании измените любое поле и нажмите «Проверить» — отработает та же сверка, что и по настоящим переводам, и покажет ответ: <b>принять</b> или <b>отказ с кодом</b>. <b>В НРД и контрагенту ничего не отправляется.</b></p>
<form method="post" action="/admin/m2m-testing/depo-sim" id="simForm">
<div class="mode">
<label><input type="radio" name="mode" value="in" {{if ne .Form.Mode "out"}}checked{{end}} onchange="simMode()"> ↘ Зачисление (к нам приходят бумаги)</label>
<label><input type="radio" name="mode" value="out" {{if eq .Form.Mode "out"}}checked{{end}} onchange="simMode()"> ↗ Списание (мы отправляем)</label>
</div>
<div class="sim-grid" style="margin-bottom:10px">
<div style="grid-column:1/-1">
<label>Клиент из DEPO (подставит ФИО и паспорт)</label>
<select id="pickClient" onchange="simPickClient(this)">
<option value="">— выбрать реального клиента —</option>
{{range .Clients}}<option data-name="{{.Name}}" data-ser="{{.Ser}}" data-num="{{.Num}}">{{.Name}} · паспорт {{.Ser}} № {{.Num}} · {{.Account}}</option>{{end}}
</select>
</div>
<div><label>Фамилия</label><input name="last" id="f_last" value="{{.Form.Last}}"></div>
<div><label>Имя</label><input name="first" id="f_first" value="{{.Form.First}}"></div>
<div><label>Отчество</label><input name="middle" id="f_middle" value="{{.Form.Middle}}"></div>
<div><label>Паспорт — серия</label><input name="ser" id="f_ser" value="{{.Form.Ser}}"></div>
<div><label>Паспорт — номер</label><input name="num" id="f_num" value="{{.Form.Num}}"></div>
<div style="grid-column:1/-1">
<label>Бумага из DEPO (подставит ISIN)</label>
<select id="pickSec" onchange="simPickSec(this)">
<option value="">— выбрать бумагу на обслуживании —</option>
{{range .Secs}}<option data-isin="{{.ISIN}}">{{.Name}} · {{.ISIN}}</option>{{end}}
</select>
</div>
<div><label>ISIN</label><input name="isin" id="f_isin" value="{{.Form.ISIN}}"></div>
<div><label>Код НРД (необязательно)</label><input name="code" id="f_code" value="{{.Form.Code}}" placeholder="SBER/03"></div>
<div><label>Количество</label><input name="qty" id="f_qty" value="{{.Form.Qty}}"></div>
<div class="out-only"><label>Место хранения — счёт (наш в НРД)</label><input name="account" id="f_account" value="{{.Form.Account}}"></div>
<div class="out-only"><label>Место хранения — раздел (начало 36MC04136)</label><input name="section" id="f_section" value="{{.Form.Section}}"></div>
</div>
<div class="muted" style="font-size:12px">Быстрые сценарии — подставят данные, затем нажмите «Проверить»:</div>
<div class="presets">
<button type="button" onclick="simPreset('ok')">🟢 Реальный клиент → принять</button>
<button type="button" onclick="simPreset('badpass')">🔴 Не тот паспорт → M2M04</button>
<button type="button" onclick="simPreset('foreign')">🔴 Иностранная бумага → M2M01</button>
<button type="button" onclick="simPreset('unknown')">🔴 Неизвестный ISIN → M2M05</button>
<button type="button" class="out-only" style="display:inline-block" onclick="simPreset('toomany')">🔴 Списание: больше, чем есть → недостаточно</button>
<button type="button" class="out-only" style="display:inline-block" onclick="simPreset('badplace')">🔴 Списание: не то место хранения</button>
</div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:10px">
<button type="submit" class="btn btn-ok">▶ Проверить — имитация отправки в Fancy</button>
<span class="muted" style="font-size:12px">ничего не отправляется</span>
</div>
</form>
</div>
{{if .Posted}}
<div class="card">
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
<h2 style="margin:0">Ответ сверки (имитация) — {{if eq .Result.Mode "out"}}списание{{else}}зачисление{{end}}</h2>
{{if .Result.Ran}}<span class="dc-rec {{.Result.Recommend}}">{{.Result.Summary}}</span>{{end}}
</div>
{{if not .Result.Ran}}
<p class="muted" style="margin-top:10px">Сверка не выполнена: {{.Result.Err}}</p>
{{else}}
<table style="margin-top:10px">
<tr><td class="muted" style="width:220px">Инвестор в картотеке</td><td>{{if .Result.Investor}}<b>{{.Result.Investor}}</b>{{else}}<span class="dc-bad">не найден</span>{{end}}</td></tr>
<tr><td class="muted">Торговый счёт депо</td><td>{{if .Result.Account}}<code>{{.Result.Account}}</code> — открыт{{else}}<span class="dc-bad">открытого ВТНКЦ-счёта нет</span>{{end}}</td></tr>
</table>
{{range .Result.Secs}}
<h3 style="font-size:13.5px;margin:14px 0 4px"><code>{{.Security}}</code> → {{if eq .Recommend "confirm"}}<span class="dc-ok">принять</span>{{else}}<span class="dc-bad">отказ {{.RejectCode}}</span> <span class="muted" style="font-weight:400">({{m2mCode .RejectCode}})</span>{{end}}</h3>
<table>
<thead><tr><th style="width:36px"></th><th style="width:280px">Проверка</th><th>Результат</th></tr></thead>
<tbody>
{{range .Checks}}
<tr><td>{{if not .OK}}<span class="dc-bad">🔴</span>{{else if .Warn}}<span class="dc-warn">🟡</span>{{else}}<span class="dc-ok">🟢</span>{{end}}</td><td>{{.Name}}{{if not .OK}} <span class="muted" style="font-size:11px">→ {{.Code}}</span>{{end}}</td><td style="font-size:12.5px">{{.Detail}}</td></tr>
{{end}}
</tbody>
</table>
{{end}}
<p class="muted" style="font-size:12px;margin-top:12px">🟢 совпало · 🟡 пройдено с предупреждением (данных для отказа в базе нет — решает оператор) · 🔴 не совпало. Так же выглядит сверка в карточке настоящего входящего и в превью исходящей отправки.</p>
{{end}}
</div>
{{end}}
<script>
function simSet(id,v){var e=document.getElementById(id); if(e) e.value=v;}
function simMode(){
var out=document.querySelector('input[name="mode"][value="out"]').checked;
document.getElementById('simCard').classList.toggle('is-out', out);
}
function simPickClient(sel){
var o=sel.options[sel.selectedIndex]; if(!o||!o.dataset.name) return;
var p=o.dataset.name.trim().split(/\s+/);
simSet('f_last',p[0]||''); simSet('f_first',p[1]||''); simSet('f_middle',p.slice(2).join(' '));
simSet('f_ser',o.dataset.ser||''); simSet('f_num',o.dataset.num||'');
}
function simPickSec(sel){ var o=sel.options[sel.selectedIndex]; if(o&&o.dataset.isin) simSet('f_isin',o.dataset.isin); }
// Пресеты меняют ТОЛЬКО поле сценария; человек остаётся тот, которого выбрали/ввели.
// Клиент из списка подставляется лишь если поля ФИО/паспорта совсем пустые.
function simPreset(kind){
var cs=document.getElementById('pickClient');
var empty=!document.getElementById('f_last').value.trim() && !document.getElementById('f_num').value.trim();
if(empty && cs && cs.options.length>1){ cs.selectedIndex=1; simPickClient(cs); }
if(kind==='ok'){
// «как в базе»: вернуть реальный паспорт выбранного в списке человека (если он выбран),
// бумагу оставить выбранную (или SBER), количество 10, наш раздел
if(cs && cs.selectedIndex>0) simPickClient(cs);
if(!document.getElementById('f_isin').value.trim()) { simSet('f_isin','RU0009029540'); simSet('f_code','SBER/03'); }
simSet('f_qty','10'); simSet('f_section','36MC0413600000F00');
}
if(kind==='badpass'){ simSet('f_num','000000'); }
if(kind==='foreign'){ simSet('f_isin','US0378331005'); simSet('f_code',''); }
if(kind==='unknown'){ simSet('f_isin','RU000A0ZZZZ9'); simSet('f_code',''); }
if(kind==='toomany'){ simSet('f_qty','99999999'); }
if(kind==='badplace'){ simSet('f_section','99XX0000000000000'); }
if(kind==='toomany'||kind==='badplace'){ document.querySelector('input[name="mode"][value="out"]').checked=true; simMode(); }
}
</script>
{{end}}