cd6e2c424f
Было: правило «боевой ЛК + тестовый профиль → принудительно имитатор» не давало выключить эмуляцию, а подмена личности жила отдельным тумблером на другой странице — пользователь не мог понять состояние и выключить. Стало — явная модель (контур НРД и режим эмуляции независимы): - «Выключена» — заявки идут в выбранный контур (NSD.Profile) с реальными данными клиента; - «Подмена личности» — уходят в контур, но у клиентов из списка блок инвестора заменяется на тест-инвестора (Бубликов/Баранкин); DEPO и QUIK — по реальному клиенту; - «Полная имитация» — в НРД ничего не уходит, bj сам отвечает (внутренний имитатор). Режимы взаимоисключающие (ведутся флагами EmulateNSD/RealNSDTest.Enabled), принудительное правило убрано. Единый 3-радио на /admin/emulators (как у QUIK), с показом контура, тест-инвестора и списка паспортов; карточка на /admin/m2m-testing — статус + ссылка на «Эмуляторы». Тесты обновлены под новую модель. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
118 lines
12 KiB
HTML
118 lines
12 KiB
HTML
{{define "content"}}
|
|
{{if .Flash}}<div class="card" style="background:var(--accent-weak,#e7f0fb)">{{.Flash}}</div>{{end}}
|
|
|
|
<div class="card">
|
|
<h2>Эмуляторы</h2>
|
|
<p class="muted" style="margin:0">Служебные переключатели режима разработки: оба эмулятора работают только пока действует режим разработки.
|
|
Эти же флаги доступны в «Настройке» (ЛК — «Имитация НРД», QUIK — «имитация») — состояние общее.</p>
|
|
</div>
|
|
|
|
{{/* ===== Эмуляция НРД ===== */}}
|
|
<div class="card" id="emu-nsd-card">
|
|
<h2><span class="dot {{if ne .NSDMode "off"}}warn{{else}}ok{{end}}"></span>Эмуляция НРД
|
|
{{if eq .NSDMode "imitate"}}<span class="hero-status warn">полная имитация</span>{{end}}
|
|
{{if eq .NSDMode "substitute"}}<span class="hero-status warn">подмена личности</span>{{end}}</h2>
|
|
<p class="muted">Контур НРД сейчас: <b>{{.Profile}}</b> — заявки уходят туда, когда эмуляция выключена (выбор контура — в «Настройке»). Контур и режим эмуляции независимы. Режимы эмуляции сбрасываются автоматически, когда заканчивается режим разработки.</p>
|
|
{{if and (eq .NSDMode "off") .ProfileTest}}
|
|
<p class="dq-warn">Контур тестовый ({{.Profile}}), а эмуляция выключена — реальные заявки клиентов уйдут в тестовый НРД с настоящими данными (НРД может их не узнать). Для теста используйте «Подмену личности».</p>
|
|
{{end}}
|
|
<form method="post" action="/admin/emulators/nsd" id="emu-nsd-form" class="dq">
|
|
<div class="dq-tiles">
|
|
<label class="dq-tile"><input type="radio" name="mode" value="off" {{if eq .NSDMode "off"}}checked{{end}}><span><b>Выключена</b><small>Заявки клиентов уходят в выбранный контур НРД ({{.Profile}}) с реальными данными — как в боевой работе.</small></span></label>
|
|
<label class="dq-tile"><input type="radio" name="mode" value="substitute" {{if eq .NSDMode "substitute"}}checked{{end}}><span><b>Подмена личности</b><small>Заявка уходит в контур НРД ({{.Profile}}), но у клиентов из списка блок инвестора заменяется на тест-инвестора{{if .RealNSDAs}} ({{.RealNSDAs}}){{end}}. DEPO и QUIK МНП — по реальному клиенту.</small></span></label>
|
|
<label class="dq-tile"><input type="radio" name="mode" value="imitate" {{if eq .NSDMode "imitate"}}checked{{end}}><span><b>Полная имитация</b><small>В НРД ничего не отправляется — bj сам отвечает корректным файлом (ответ контрагента формирует внутренний имитатор).</small></span></label>
|
|
</div>
|
|
<div id="emu-nsd-sub" style="margin-top:10px;max-width:560px">
|
|
<label>Паспорта клиентов для подмены <span class="muted">(серия номер, через «;»)</span>
|
|
<input type="text" name="docs" value="{{.RealNSDDocs}}" placeholder="1110 754503" spellcheck="false" class="dq-mono" style="width:100%"></label>
|
|
<p class="muted" style="font-size:12px;margin:4px 0 0">Заявки этих клиентов уйдут в НРД под данными тест-инвестора{{if .RealNSDAs}} <b>{{.RealNSDAs}}</b>{{end}}. Остальные — с настоящими данными.</p>
|
|
</div>
|
|
<div id="emu-nsd-imi">
|
|
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;margin-top:10px">
|
|
<label>Задержка ответа, сек <span class="muted">(0..300)</span>
|
|
<input type="number" name="delay_sec" min="0" max="300" value="{{.NSD.DelaySec}}" style="width:100%"></label>
|
|
<label>Ответ имитатора
|
|
<select name="outcome" style="width:100%">
|
|
<option value="confirm" {{if eq .NSD.Outcome "confirm"}}selected{{end}}>Подтвердить перевод</option>
|
|
<option value="reject" {{if eq .NSD.Outcome "reject"}}selected{{end}}>Отклонить (код ниже)</option>
|
|
<option value="timeout" {{if eq .NSD.Outcome "timeout"}}selected{{end}}>Не отвечать (таймаут)</option>
|
|
</select></label>
|
|
<label id="emu-nsd-code">Код отказа
|
|
<select name="reject_code" style="width:100%">
|
|
{{range .RejectCodes}}<option value="{{.Code}}" {{if eq $.NSD.RejectCode .Code}}selected{{end}}>{{.Label}}</option>{{end}}
|
|
</select></label>
|
|
</div>
|
|
</div>
|
|
<p class="dq-note" id="emu-nsd-st" aria-live="polite">Изменения сохраняются сразу.</p>
|
|
<noscript><button type="submit" class="btn">Сохранить</button></noscript>
|
|
</form>
|
|
</div>
|
|
<script>(function(){
|
|
var f=document.getElementById('emu-nsd-form'), st=document.getElementById('emu-nsd-st');
|
|
var sub=document.getElementById('emu-nsd-sub'), imi=document.getElementById('emu-nsd-imi'), code=document.getElementById('emu-nsd-code');
|
|
function mode(){ var r=f.querySelector('input[name=mode]:checked'); return r?r.value:'off'; }
|
|
function sync(){ var m=mode(); sub.hidden=(m!=='substitute'); imi.hidden=(m!=='imitate'); if(code) code.style.opacity=(f.outcome.value==='reject')?'1':'.45'; }
|
|
f.addEventListener('change', function(){ sync();
|
|
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){ if(window.fetch) e.preventDefault(); });
|
|
sync();
|
|
})();</script>
|
|
|
|
{{/* ===== Эмулятор МНП QUIK ===== */}}
|
|
<div class="card" id="emu-quik-card">
|
|
<h2><span class="dot {{if ne .QuikMode "off"}}warn{{else}}ok{{end}}"></span>Эмулятор МНП QUIK
|
|
{{if eq .QuikMode "emulate"}}<span class="hero-status warn">имитация</span>{{end}}
|
|
{{if eq .QuikMode "real"}}<span class="hero-status warn">запись на подставного</span>{{end}}</h2>
|
|
<p class="muted">Режим цикла M2M в Модуле неторговых поручений на время разработки.
|
|
Имитация имеет приоритет над «записями в МНП»; оба dev-режима сбрасываются автоматически, когда режим разработки заканчивается.</p>
|
|
{{if eq .QuikMode "real"}}
|
|
<p class="dq-warn" style="font-weight:700">ВНИМАНИЕ: в боевую МНП будут записываться РЕАЛЬНЫЕ поручения на клиента {{.QuikRealCode}}.</p>
|
|
{{end}}
|
|
<form method="post" action="/admin/emulators/quik" id="emu-quik-form" class="dq">
|
|
<div class="dq-tiles">
|
|
<label class="dq-tile"><input type="radio" name="mode" value="off" {{if eq .QuikMode "off"}}checked{{end}}><span><b>Выключено</b><small>Шаги цикла выполняются в QUIK по-настоящему (при разрешённых записях в МНП) — как в боевой работе.</small></span></label>
|
|
<label class="dq-tile"><input type="radio" name="mode" value="emulate" {{if eq .QuikMode "emulate"}}checked{{end}}><span><b>Внутренняя имитация</b><small>Шаги цикла (регистрация и блокировка, подпись, снятие, зачисление) проходят и отмечаются «имитация», в боевую базу МНП ничего не записывается.</small></span></label>
|
|
<label class="dq-tile"><input type="radio" name="mode" value="real" {{if eq .QuikMode "real"}}checked{{end}}><span><b>Реальная запись на подставного клиента</b><small>Цикл идёт по обычному клиенту, но поручения в боевую МНП уходят РЕАЛЬНО — с кодом другого, реально существующего в QUIK клиента.</small></span></label>
|
|
</div>
|
|
<div id="emu-quik-code" style="margin-top:10px;max-width:280px">
|
|
<label>Код клиента в QUIK <span class="muted">(код реального клиента QUIK, напр. BR859 — поручения уйдут на него)</span>
|
|
<input type="text" name="real_client_code" value="{{.QuikRealCode}}" spellcheck="false" class="dq-mono" style="width:100%"></label>
|
|
</div>
|
|
<p class="dq-warn" id="emu-quik-warn" style="font-weight:700" hidden>ВНИМАНИЕ: в боевую МНП будут записываться РЕАЛЬНЫЕ поручения на указанного клиента.</p>
|
|
<div id="emu-quik-emu">
|
|
<div style="margin-top:10px;max-width:280px">
|
|
<label>Задержка шага, сек <span class="muted">(0..60, повторяет ожидание ответа МНП)</span>
|
|
<input type="number" name="step_delay_sec" min="0" max="60" value="{{.MNP.StepDelaySec}}" style="width:100%"></label>
|
|
</div>
|
|
<fieldset style="border:1px solid var(--border);border-radius:8px;margin-top:10px;padding:8px 12px">
|
|
<legend class="muted" style="font-size:12px">Имитировать отказ МНП на шаге (состояние не продвигается, повтор — как при боевом отказе)</legend>
|
|
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:6px">
|
|
<label style="display:flex;gap:8px;align-items:center"><input type="checkbox" name="fail_register" value="1" {{if .MNP.FailRegister}}checked{{end}}> Регистрация (вывод)</label>
|
|
<label style="display:flex;gap:8px;align-items:center"><input type="checkbox" name="fail_sign" value="1" {{if .MNP.FailSign}}checked{{end}}> Подпись (исполнение)</label>
|
|
<label style="display:flex;gap:8px;align-items:center"><input type="checkbox" name="fail_deny" value="1" {{if .MNP.FailDeny}}checked{{end}}> Снятие поручения</label>
|
|
<label style="display:flex;gap:8px;align-items:center"><input type="checkbox" name="fail_deposit" value="1" {{if .MNP.FailDeposit}}checked{{end}}> Зачисление (ввод)</label>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
<p class="dq-note" id="emu-quik-st" aria-live="polite">Изменения сохраняются сразу.</p>
|
|
<noscript><button type="submit" class="btn">Сохранить</button></noscript>
|
|
</form>
|
|
</div>
|
|
<script>(function(){
|
|
var f=document.getElementById('emu-quik-form'), st=document.getElementById('emu-quik-st'),
|
|
code=document.getElementById('emu-quik-code'), warn=document.getElementById('emu-quik-warn'),
|
|
emu=document.getElementById('emu-quik-emu');
|
|
function sync(){ var m=f.mode.value; code.hidden=(m!=='real'); warn.hidden=(m!=='real'); emu.hidden=(m==='real'); }
|
|
f.addEventListener('change', function(){ sync();
|
|
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){ if(window.fetch) e.preventDefault(); });
|
|
sync();
|
|
})();</script>
|
|
{{end}}
|