da12a9ec1c
- 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>
140 lines
11 KiB
HTML
140 lines
11 KiB
HTML
{{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}}
|