Заполнение портфеля ДЕПО: кнопка включить/выключить, выбор клиента 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-09-29 17:32:32 +03:00
parent e47e6cfbdc
commit 1b0e1bee5f
4 changed files with 263 additions and 17 deletions
@@ -637,15 +637,71 @@
<label style="display:flex;align-items:flex-start;gap:8px;font-weight:400;flex-basis:100%;margin-top:6px"><input type="checkbox" name="emulate_nsd" style="width:auto;margin-top:3px" {{if .Settings.LK.EmulateNSD}}checked{{end}} onchange="this.form.submit()"> <span><b>Имитация НРД</b> — заявки ЛК проходят сверку с DEPO, блокировку в QUIK и получают статус в ЛК, но в НРД не отправляются: ответ контрагента имитирует bj.{{if and (eq .Settings.LK.ActiveEnv "prod") (not .Settings.LK.EmulateNSD)}} <span style="color:var(--warn)">Сейчас включена принудительно: боевой ЛК при тестовом профиле ИШ ({{.Settings.NSD.Profile}}).</span>{{end}}</span></label>{{end}}
</form>
<div id="lk-portfolio" style="margin-top:14px;padding:12px 14px;border:1px solid var(--border);border-radius:10px">
<b>Портфель ДЕПО → ЛК</b> {{if .Settings.LK.PortfolioSync.Enabled}}<span class="badge confirmed">включено</span>{{else}}<span class="badge draft">выключено</span>{{end}}
<b>Заполнение портфеля ДЕПО бумагами из DEPO</b> {{if .Settings.LK.PortfolioSync.Enabled}}<span class="badge confirmed">включено</span>{{else}}<span class="badge draft">выключено</span>{{end}}
<p class="muted" style="margin:6px 0 8px;font-size:13px">Каждые 10 секунд, в том же проходе, что и приём заявок, bj сверяет портфель клиента в DEPO и отправляет его в ЛК (<code>/portfolio/import</code>, портфель ДЕПО) — только если остатки изменились. Номер гос. регистрации — из экспорта QUIK, раздел счёта — «Основной», хранение — НРД. Бумаги без номера гос. регистрации не отправляются (ЛК отвергает весь импорт).</p>
<form method="post" action="/admin/setup/lk-portfolio" style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap">
<label style="display:flex;align-items:center;gap:6px;font-weight:400"><input type="checkbox" name="enabled" style="width:auto"{{if .Settings.LK.PortfolioSync.Enabled}} checked{{end}}> включить</label>
<label style="display:flex;flex-direction:column;gap:3px;font-size:12px;min-width:260px">Договор ЛК = портфель DEPO (через «;»)
<input name="pairs" value="{{.Settings.LK.PortfolioSync.Pairs}}" placeholder="ДД-816=БР-859"></label>
<button class="btn" type="submit">Сохранить</button>
<button class="btn" type="submit" name="action" value="sync">Синхронизировать сейчас</button>
<form method="post" action="/admin/setup/lk-portfolio" style="margin:0 0 8px">
{{if .Settings.LK.PortfolioSync.Enabled}}<button class="btn" type="submit" name="action" value="off" style="background:var(--card);color:var(--err);border:1px solid var(--err)">Выключить заполнение портфеля ДЕПО бумагами</button>
<button class="btn" type="submit" name="action" value="sync">Заполнить сейчас</button>
{{else}}<button class="btn btn-ok" type="submit" name="action" value="on" onclick="if(!this.form.pairs.value.trim()){alert('Укажите договор ЛК и портфель DEPO, например ДД-816=БР-859');return false}">Включить заполнение портфеля ДЕПО бумагами</button>{{end}}
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;margin-top:8px">
<label style="display:flex;flex-direction:column;gap:3px;font-size:12px;min-width:260px">Каких клиентов заполнять: договор ЛК = портфель DEPO (через «;»)
<input name="pairs" value="{{.Settings.LK.PortfolioSync.Pairs}}" placeholder="ДД-816=БР-859"></label>
<button class="btn" type="submit" name="action" value="save">Сохранить список</button>
</div>
</form>
<details style="margin:8px 0 0"><summary style="cursor:pointer;font-size:12.5px">Именование у депозитария (счета, договоры, раздел, хранение)</summary>
<form method="post" action="/admin/setup/lk-portfolio" style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;margin-top:8px">
<input type="hidden" name="naming" value="1">
<label style="display:flex;flex-direction:column;gap:3px;font-size:12px">Префикс торгового счёта в DEPO
<input name="account_prefix" value="{{.Settings.LK.PortfolioSync.AccountPrefix}}" placeholder="ВТНКЦ" style="width:120px"></label>
<label style="display:flex;flex-direction:column;gap:3px;font-size:12px">Номер договора депо в ЛК ({n} — номер счёта)
<input name="contract_template" value="{{.Settings.LK.PortfolioSync.ContractTemplate}}" placeholder="ДД-{n}" style="width:160px"></label>
<label style="display:flex;flex-direction:column;gap:3px;font-size:12px">Раздел счёта
<input name="section" value="{{.Settings.LK.PortfolioSync.Section}}" placeholder="Основной" style="width:130px"></label>
<label style="display:flex;flex-direction:column;gap:3px;font-size:12px">Место хранения
<input name="storage" value="{{.Settings.LK.PortfolioSync.Storage}}" placeholder="НРД" style="width:110px"></label>
<button class="btn" type="submit" name="action" value="save">Сохранить</button>
</form>
<p class="muted" style="font-size:12px;margin:4px 0 0">Пусто — значение по умолчанию (серым). Пример: счёт ВТНКЦ00000816 и шаблон ДД-{n} → договор ДД-816.</p>
</details>
<form method="post" action="/admin/setup/lk-portfolio" id="lkpf-client" style="margin:10px 0 0;padding-top:10px;border-top:1px dashed var(--border)">
<div style="font-size:13px;font-weight:600;margin-bottom:4px">Залить одному клиенту (для теста)</div>
<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap">
<label style="display:flex;flex-direction:column;gap:3px;font-size:12px;min-width:320px">Клиент DEPO — ФИО, портфель или счёт
<input id="lkpf-q" autocomplete="off" placeholder="например, Погонщикова или БР-859"></label>
<input type="hidden" name="client_port" id="lkpf-port"><input type="hidden" name="client_lk" id="lkpf-lk">
<button class="btn" type="submit" name="action" value="client_push" id="lkpf-push" disabled>Залить сейчас</button>
<button class="btn" type="submit" name="action" value="client_add" id="lkpf-add" disabled>Добавить в постоянное заполнение</button>
</div>
<div id="lkpf-list" style="margin-top:6px;font-size:12.5px"></div>
<div id="lkpf-sel" class="muted" style="margin-top:4px;font-size:12.5px"></div>
</form>
<script>
(function(){
var q=document.getElementById('lkpf-q'), list=document.getElementById('lkpf-list'), sel=document.getElementById('lkpf-sel'), t;
function esc(s){return String(s).replace(/[&<>"]/g,function(c){return{'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]})}
function pick(c){
document.getElementById('lkpf-port').value=c.port; document.getElementById('lkpf-lk').value=c.lk;
var ok=!!c.lk; document.getElementById('lkpf-push').disabled=!ok; document.getElementById('lkpf-add').disabled=!ok;
sel.innerHTML=ok?('Выбран: <b>'+esc(c.name)+'</b> · портфель DEPO '+esc(c.port)+' · счёт '+esc(c.account)+' → договор в ЛК <b>'+esc(c.lk)+'</b>'):'У клиента нет торгового счёта ВТНКЦ — договор в ЛК не определить';
list.innerHTML='';
}
q.addEventListener('input',function(){
clearTimeout(t); var v=q.value.trim(); if(v.length<2){list.innerHTML='';return}
t=setTimeout(function(){
fetch('/admin/setup/lk-portfolio/clients?q='+encodeURIComponent(v),{cache:'no-store'}).then(function(r){return r.json()}).then(function(d){
if(d.error){list.innerHTML='<span style="color:var(--err)">'+esc(d.error)+'</span>';return}
if(!d||!d.length){list.innerHTML='<span class="muted">не найдено</span>';return}
list.innerHTML=''; d.forEach(function(c){
var a=document.createElement('a'); a.href='#'; a.style.display='block'; a.style.padding='2px 0';
a.innerHTML=esc(c.name)+' · '+esc(c.port)+' · '+esc(c.account)+(c.lk?' → '+esc(c.lk):'');
a.onclick=function(e){e.preventDefault(); pick(c)}; list.appendChild(a);
});
}).catch(function(){list.innerHTML='<span style="color:var(--err)">поиск недоступен</span>'});
},300);
});
})();
</script>
{{range lkPortfolioStates}}<div class="muted" style="font-size:12.5px;margin-top:8px"><b>{{.LK}}</b> ← {{.Depo}}: {{if .Err}}<span style="color:var(--err)">ошибка — {{.Err}}</span>{{else if .PushedAt.IsZero}}сверено {{.CheckedAt.Format "15:04:05"}}, отправки ещё не было{{else}}отправлено {{.PushedAt.Format "02.01 15:04:05"}} ({{.Lines}} бумаг), сверено {{.CheckedAt.Format "15:04:05"}}{{end}}{{range .Skipped}}<br>пропущено: {{.}}{{end}}</div>{{end}}
</div>
<div id="lk-real-nsd" style="margin-top:14px;padding:12px 14px;border:1px solid var(--border);border-radius:10px;{{if .Settings.LK.RealNSDTest.Enabled}}background:var(--warn-weak){{end}}">