Files
Bridge-and-Join-s/internal/lkgateway/web/templates/part_org.html
T
zuevav f81135a715 Мастер настройки как помощник установки: шаги слева, одна карточка, «Назад»/«Далее» внизу
Слева — шаги этой установки по порядку (номер, галочка у сделанных, текущий выделен),
справа — карточка шага: заголовок, пояснение, сведения «название — значение», зелёная
плашка итога. Подготовка модулей — в том же виде.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
2026-10-06 13:20:11 +03:00

68 lines
7.5 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.
{{/* «Наша организация» — одна и та же в «Настройка → НРД» и в мастере настройки (данные — SetupData). */}}
{{define "orgSection"}}
<div class="card" id="sec-org">
{{if not .InWizard}}<h2><span class="dot {{if and .OrgINN .OrgCode}}ok{{else}}err{{end}}"></span>Наша организация</h2>
<p class="muted">Депозитарий — участник MOEX МОСТ, от имени которого bj отправляет и принимает переводы: ИНН передающего и зачисляющего депозитария в пакетах{{if mod "quik-mnp"}}, код отправителя и код фирмы в QUIK{{end}}. Выберите себя из справочника участников НРД или введите данные вручную.</p>{{end}}
{{if and .OrgINN .OrgCode}}<p style="margin:8px 0 0"><b>{{if .OrgName}}{{.OrgName}}{{else}}—{{end}}</b> · ИНН <code>{{.OrgINN}}</code> · код <code>{{.OrgCode}}</code>{{if not .Settings.NSD.OrgINN}} <span class="muted">(ИНН взят из справочника по коду)</span>{{end}}</p>
{{else if not .InWizard}}<p style="margin:8px 0 0;color:var(--err)">Организация не задана — переводы не будут сформированы.</p>{{end}}
<form method="post" action="/admin/setup/org" style="margin-top:12px;display:grid;gap:10px;max-width:680px">
<input type="hidden" name="section" value="sec-org">
<label>Найти себя в справочнике участников M2M</label>
<div class="hb-wrap"><input id="orgq" placeholder="Выберите из списка или введите ИНН, название, код" autocomplete="off"><button type="button" class="hb-toggle" title="Весь список">▾</button></div>
<div id="orgres" class="hb-res"></div>
<style>
.hb-box{position:relative}
.hb-res{margin-top:4px;max-height:320px;overflow:auto;border-radius:8px;border:1px solid var(--border);background:var(--card-2);padding:4px}
.hb-res:empty{display:none}
.hb-wrap{display:flex;gap:6px}.hb-wrap input{flex:1}
.hb-toggle{padding:0 12px;border-radius:8px;border:1px solid var(--border);background:var(--card);color:var(--text);cursor:pointer}
.hb-hit{display:block;width:100%;text-align:left;padding:7px 10px;margin:2px 0;border-radius:8px;border:1px solid var(--border);background:var(--card);color:var(--text);cursor:pointer;font-size:13px}
.hb-hit:hover{border-color:var(--accent)}
.hb-m{color:var(--muted);font-size:12px}
</style>
<script>
// hbFind — подбор участника справочника НРД по ИНН, названию или коду (без учёта регистра и кавычек).
function hbNorm(s){return String(s||'').toLowerCase().replace(/ё/g,'е').replace(/[«»"'“”.,()]/g,' ').replace(/\s+/g,' ').trim()}
function hbFind(list,q){q=hbNorm(q);
if(!q)return list.slice().sort((a,b)=>(a.kind===b.kind?0:a.kind==='depo'?-1:1)||hbNorm(a.name).localeCompare(hbNorm(b.name),'ru'));
const words=q.split(' ');
return list.map(p=>{const hay=hbNorm([p.inn,p.code,p.name,p.full_name,p.display_name,p.eng_name].join(' '));
if(!words.every(w=>hay.includes(w)))return null;
const score=(String(p.inn).startsWith(q)||String(p.code).toLowerCase().startsWith(q)?0:hbNorm(p.name).startsWith(q)||hbNorm(p.display_name).startsWith(q)?1:2)+(p.kind==='depo'?0:0.5);
return {p,score}}).filter(Boolean).sort((a,b)=>a.score-b.score||hbNorm(a.p.name).localeCompare(hbNorm(b.p.name),'ru')).map(x=>x.p)}
function hbBind(inp,box,list,onPick){
const esc=s=>String(s??'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
let hits=[],picked=inp.dataset.picked==='1';
const q=()=>picked?'':inp.value;
const draw=()=>{hits=hbFind(list,q());box.innerHTML=(hits.length?'<div class="hb-m" style="padding:2px 4px">'+(q().trim()?'Найдено: ':'Все участники: ')+hits.length+'</div>'+hits.map((p,i)=>'<button type="button" class="hb-hit" data-i="'+i+'" data-inn="'+esc(p.inn)+'" data-code="'+esc(p.code)+'"><b>'+esc(p.name)+'</b> <span class="hb-m">ИНН '+esc(p.inn)+' · '+esc(p.code)+(p.kind==='broker'?' · брокер':'')+'</span></button>').join(''):'<div class="hb-m" style="padding:6px 2px">В справочнике не найдено — введите ИНН и наименование в поля ниже.</div>');box.classList.add('open')};
const close=()=>{box.innerHTML='';box.classList.remove('open')};
inp.addEventListener('focus',()=>{if(picked)inp.select();draw()});
inp.addEventListener('input',()=>{picked=false;draw()});
// Enter выбирает только при единственном совпадении: иначе молча ушёл бы первый попавшийся.
inp.addEventListener('keydown',e=>{if(e.key==='Enter'){e.preventDefault();if(hits.length===1)pick(hits[0]);else if(hits.length>1){const m=box.querySelector('.hb-m');if(m)m.textContent='Найдено: '+hits.length+' — уточните запрос или выберите мышью'}}if(e.key==='Escape')close()});
const pick=p=>{onPick(p);picked=true;close()};
box.addEventListener('mousedown',e=>e.preventDefault());
// Участник — по ИНН и коду с самой кнопки, а не по номеру строки: список мог перерисоваться между показом и нажатием.
box.addEventListener('click',e=>{const b=e.target.closest('.hb-hit');if(!b)return;const p=list.find(x=>String(x.inn)===b.dataset.inn&&String(x.code)===b.dataset.code)||hits[+b.dataset.i];if(p)pick(p)});
inp.addEventListener('blur',()=>setTimeout(close,150));
const tg=inp.parentNode.querySelector('.hb-toggle');
if(tg)tg.addEventListener('click',()=>{if(box.classList.contains('open'))close();else{inp.focus();draw()}});
}
{
const inp=document.getElementById('orgq'),f=inp.form;
hbBind(inp,document.getElementById('orgres'),{{.Participants}}||[],p=>{f.org_code.value=p.code;f.org_inn.value=p.inn;f.org_name.value=p.full_name||p.name;inp.value=p.name+' · ИНН '+p.inn})
}
</script>
{{if not .Participants}}<span class="muted" style="font-size:12px">Справочник участников ещё не получен — запросите его в «Диагностике» или введите данные вручную.</span>{{end}}
<label>Код участника</label>
<input type="text" name="org_code" value="{{if .Settings.NSD.OrgCode}}{{.Settings.NSD.OrgCode}}{{else if .Suggest}}{{.Suggest.OrgCode}}{{end}}" placeholder="MC0000000000" maxlength="12">
<label>ИНН</label>
<input type="text" name="org_inn" value="{{if .Settings.NSD.OrgINN}}{{.Settings.NSD.OrgINN}}{{else if .Suggest}}{{.Suggest.OrgINN}}{{end}}" maxlength="10" inputmode="numeric">
<label>Наименование</label>
<input type="text" name="org_name" value="{{if .Settings.NSD.OrgName}}{{.Settings.NSD.OrgName}}{{else if .Suggest}}{{.Suggest.OrgName}}{{end}}">
{{if .Suggest}}{{if .Suggest.OrgName}}<span class="muted" style="font-size:12px">Из DEPO: {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}, код {{.Suggest.OrgCode}} — подставлено в пустые поля.</span>{{end}}{{end}}
<div><button type="submit" class="btn">Сохранить</button></div>
</form>
</div>
{{end}}