4b18579956
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
491 lines
42 KiB
Go
491 lines
42 KiB
Go
package lk
|
||
|
||
// pageTemplates — встроенные страницы Личного кабинета (без внешних зависимостей,
|
||
// чтобы модуль был одним файлом и ставился на любой веб-сервер).
|
||
//
|
||
// Визуальный мир — «Табло» (см. DESIGN.md, раздел «Личный кабинет клиента»):
|
||
// графитовая доска состояния сверху, светлая бумага реестра, янтарь — «в пути»
|
||
// и первичное действие, все величины моноширинные табличные. Шрифты грузятся с
|
||
// Google Fonts, при недоступности сети подхватываются системные аналоги.
|
||
const pageTemplates = `
|
||
{{define "head"}}<!DOCTYPE html><html lang="ru"><head><meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>{{.Title}}</title>
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Golos+Text:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap">
|
||
<style>
|
||
:root{--paper:#edebe4;--sheet:#f7f5ef;--ink:#191a1c;--ink-2:#45403a;--mut:#6a6357;
|
||
--line:#ded8ca;--line-2:#c9c3b4;--board:#191a1c;--board-2:#1f2124;--board-3:#2b2d31;
|
||
--dim:#a49b8a;--amber:#e8a33d;--amber-bg:#f6e2c0;--amber-tx:#7a4e07;
|
||
--ok-bg:#dcefe2;--ok-tx:#1f6b3e;--err-bg:#f7dedb;--err-tx:#a32d22;--neu-bg:#e3e0d6;
|
||
--acc:#2c46c0;--mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
|
||
*{box-sizing:border-box}
|
||
body{margin:0;font-family:"Golos Text",system-ui,-apple-system,"Segoe UI",sans-serif;background:var(--paper);color:var(--ink);line-height:1.5}
|
||
a{color:var(--acc)}a:hover{color:#1e3395}
|
||
.lbl{font-family:var(--mono);font-size:10.5px;font-weight:500;letter-spacing:.1em;text-transform:uppercase}
|
||
.n{font-family:var(--mono);font-variant-numeric:tabular-nums}
|
||
.top{background:var(--board);color:#f3f0e7;padding:0 20px;min-height:58px;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
|
||
.brand{display:flex;align-items:center;gap:10px;white-space:nowrap}
|
||
.top nav{display:flex;gap:2px;flex-grow:1;overflow-x:auto;scrollbar-width:none}
|
||
.top nav::-webkit-scrollbar{display:none}
|
||
.top nav a{color:var(--dim);text-decoration:none;padding:8px 12px;border-radius:3px;white-space:nowrap}
|
||
.top nav a:hover{background:var(--board-3);color:#f3f0e7}
|
||
.top nav a.on{background:var(--board-3);color:#f3f0e7}
|
||
.top .who{color:var(--dim);font-size:12px;white-space:nowrap}
|
||
.top .exit{color:var(--dim);text-decoration:none}
|
||
.cta{display:inline-flex;align-items:center;gap:8px;min-height:38px;padding:0 14px;background:var(--amber);color:var(--ink);text-decoration:none;border:0;border-radius:3px;cursor:pointer;font:inherit}
|
||
.cta:hover{filter:brightness(1.05);color:var(--ink)}
|
||
.board{background:var(--board-2);color:#f3f0e7;border-bottom:3px solid var(--amber);padding:22px 20px}
|
||
.board .big{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:40px;font-weight:700;letter-spacing:-.02em;line-height:1;position:relative;display:inline-block}
|
||
.board .big::after{content:"";position:absolute;left:0;right:0;top:50%;height:1px;background:rgba(0,0,0,.28)}
|
||
.board-row{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:20px;max-width:1180px;margin:0 auto}
|
||
.stats{display:flex;gap:28px;flex-wrap:wrap}
|
||
.stat{display:flex;flex-direction:column;gap:3px}
|
||
.stat .v{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:22px}
|
||
.imp{background:var(--board);color:#f3f0e7;padding:10px 20px;font-size:13px;display:flex;gap:10px;align-items:center;flex-wrap:wrap}
|
||
main{max-width:1180px;margin:0 auto;padding:22px 20px 34px}
|
||
h1{font-size:23px;margin:0 0 6px;letter-spacing:-.01em}h2{font-size:15px;margin:0 0 12px;letter-spacing:-.005em}
|
||
.sec{margin-bottom:28px}
|
||
.sec-h{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:10px}
|
||
.tw{overflow-x:auto;-webkit-overflow-scrolling:touch}
|
||
table{width:100%;border-collapse:collapse;font-size:14px}
|
||
th{font-family:var(--mono);font-size:10.5px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--mut);text-align:left;padding:0 12px 9px 0;border-bottom:1px solid var(--line-2);white-space:nowrap}
|
||
td{padding:12px 12px 12px 0;border-bottom:1px solid var(--line);vertical-align:middle}
|
||
td.num,th.num{text-align:right;padding-right:0;font-family:var(--mono);font-variant-numeric:tabular-nums}
|
||
tr.grp td{background:#e4e1d8;border-bottom:1px solid var(--line-2);padding:7px 0 7px 8px}
|
||
.sheet{background:var(--sheet);border:1px solid var(--line-2);padding:20px}
|
||
.chip{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:3px;font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;background:var(--neu-bg);color:var(--ink-2)}
|
||
.chip.go{background:var(--amber-bg);color:var(--amber-tx)}
|
||
.chip.ok{background:var(--ok-bg);color:var(--ok-tx)}
|
||
.chip.err{background:var(--err-bg);color:var(--err-tx)}
|
||
.chip.dark{background:var(--board);color:#f3f0e7}
|
||
@keyframes flip{from{transform:rotateX(-88deg)}to{transform:rotateX(0)}}
|
||
.live{animation:flip 420ms cubic-bezier(.2,.9,.25,1) 1;transform-origin:50% 0}
|
||
@media(prefers-reduced-motion:reduce){.live{animation:none}}
|
||
label{display:block;font-family:var(--mono);font-size:10.5px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--mut);margin-bottom:6px}
|
||
input,select,textarea{width:100%;min-height:44px;padding:10px 12px;border:1px solid var(--line-2);border-radius:2px;background:#fff;color:var(--ink);font:inherit;font-size:16px}
|
||
input:focus,select:focus,textarea:focus{outline:2px solid var(--acc);outline-offset:1px}
|
||
input[type=checkbox]{width:20px;min-height:20px;height:20px}
|
||
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:46px;padding:0 18px;border:1px solid var(--line-2);border-radius:3px;background:#fff;color:var(--ink);font:inherit;font-size:14px;font-weight:600;cursor:pointer;text-decoration:none}
|
||
.btn:hover{border-color:var(--ink);color:var(--ink)}
|
||
.btn.main{background:var(--amber);border-color:var(--amber);color:var(--ink)}
|
||
.btn.dark{background:var(--board);border-color:var(--board);color:#f3f0e7}
|
||
.btn.sm{min-height:38px;font-size:13px;font-weight:500}
|
||
.msg{padding:12px 14px;border-radius:3px;margin-bottom:16px;font-size:14px}
|
||
.msg.err{background:var(--err-bg);color:var(--err-tx)}
|
||
.msg.ok{background:var(--ok-bg);color:var(--ok-tx)}
|
||
.mut{color:var(--ink-2);font-size:12.5px;line-height:1.65}
|
||
.grid2{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:22px}
|
||
.rows{display:flex;flex-direction:column}
|
||
.row{display:flex;justify-content:space-between;gap:14px;padding:11px 0;border-bottom:1px solid var(--line);font-size:14px}
|
||
.empty{padding:26px 0;color:var(--ink-2);font-size:14px}
|
||
.login{min-height:100vh;display:grid;grid-template-columns:minmax(0,460px) 1fr}
|
||
.login .side{background:var(--board);color:#d9d3c6;padding:40px 36px;display:flex;flex-direction:column;justify-content:space-between;gap:30px}
|
||
.login .side h1{color:#f3f0e7;font-size:30px;line-height:1.2;margin:0}
|
||
.login .form{display:flex;align-items:center;justify-content:center;padding:36px 20px}
|
||
.login form{width:100%;max-width:380px;display:flex;flex-direction:column;gap:16px}
|
||
@media(max-width:820px){.login{grid-template-columns:1fr}.login .side{padding:26px 20px}.login .side h1{font-size:24px}}
|
||
@media(max-width:600px){main{padding:18px 14px 28px}.board{padding:18px 14px}.board .big{font-size:32px}.top{padding:0 14px}.sheet{padding:16px 14px}}
|
||
</style></head><body>{{end}}
|
||
|
||
{{define "money"}}{{if gt . 0.0}}{{fmtmoney .}} ₽{{else}}—{{end}}{{end}}
|
||
|
||
{{define "mark"}}<svg viewBox="0 0 28 28" width="21" height="21" fill="none" stroke="#e8a33d" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="6" cy="14" r="3"/><circle cx="22" cy="14" r="3"/><path d="M9 14h10"/><path d="M11 10.5h6M11 17.5h6"/></svg>{{end}}
|
||
|
||
{{define "nav"}}<header class="top">
|
||
<span class="brand">{{template "mark" .}}<span class="lbl" style="font-size:12px;letter-spacing:.14em">{{.Title}}</span></span>
|
||
<nav>
|
||
{{if .IsAdmin}}<a href="/clients" class="lbl {{if eq .Nav "a"}}on{{end}}">Клиенты</a>{{end}}
|
||
<a href="/" class="lbl {{if eq .Nav "b"}}on{{end}}">Табло</a>
|
||
<a href="/portfolio" class="lbl {{if eq .Nav "p"}}on{{end}}">Реестр</a>
|
||
<a href="/orders" class="lbl {{if eq .Nav "c"}}on{{end}}">Поручения</a>
|
||
<a href="/operations" class="lbl {{if eq .Nav "o"}}on{{end}}">Операции</a>
|
||
<a href="/income" class="lbl {{if eq .Nav "i"}}on{{end}}">Доходы</a>
|
||
<a href="/profile" class="lbl {{if eq .Nav "u"}}on{{end}}">Профиль</a>
|
||
</nav>
|
||
<a href="/order" class="cta lbl"><svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg>Новое поручение</a>
|
||
<span class="who">{{with .FullName}}{{.}}{{end}}{{with .Contract}} · {{.}}{{end}}</span>
|
||
<a href="/logout" class="exit lbl">Выход</a>
|
||
</header>
|
||
{{if .AsClient}}<div class="imp">
|
||
<svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="#e8a33d" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="8" cy="8" r="6.5"/><path d="M8 5v4M8 11h.01"/></svg>
|
||
<span>Вы в кабинете клиента <b>{{.AsClient}}</b> от имени сотрудника. Поручение можно подготовить, подписывает его клиент.</span>
|
||
<a href="/back-to-admin" style="color:#e8a33d">вернуться к списку клиентов</a></div>{{end}}{{end}}
|
||
|
||
{{define "login"}}{{template "head" .}}
|
||
<div class="login">
|
||
<section class="side">
|
||
<span class="brand">{{template "mark" .}}<span class="lbl" style="font-size:12px;letter-spacing:.14em;color:#f3f0e7">{{.Title}}</span></span>
|
||
<div>
|
||
<h1>Ваши бумаги, поручения и выплаты — на одном табло</h1>
|
||
<div style="display:flex;flex-direction:column;gap:12px;margin-top:22px;font-size:14.5px">
|
||
<span style="display:flex;gap:12px"><span class="n" style="color:#e8a33d">01</span><span>Реестр бумаг по счетам депо и остаток, свободный к переводу</span></span>
|
||
<span style="display:flex;gap:12px"><span class="n" style="color:#e8a33d">02</span><span>Поручения депозитарию и их состояние по шагам</span></span>
|
||
<span style="display:flex;gap:12px"><span class="n" style="color:#e8a33d">03</span><span>Перевод бумаг в другой депозитарий через сервис MOEX МОСТ</span></span>
|
||
</div>
|
||
</div>
|
||
<p class="mut" style="color:#8e8879;margin:0">Кабинет работает на сервере депозитария и обращается к учётной системе по защищённому каналу. Данные о вас хранятся в учётной системе, а не в кабинете.</p>
|
||
</section>
|
||
<section class="form">
|
||
<form method="post" action="/login">
|
||
<h1 style="margin:0">Вход</h1>
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
<div><label for="login">Логин или номер договора</label><input id="login" name="login" autocomplete="username" autofocus></div>
|
||
<div><label for="password">Пароль</label><input id="password" name="password" type="password" autocomplete="current-password"></div>
|
||
<button class="btn main" type="submit">Войти</button>
|
||
<p class="mut" style="margin:0">Логин выдаёт депозитарий. Сотрудник входит под своей учётной записью и не знает пароль клиента.</p>
|
||
</form>
|
||
</section>
|
||
</div>
|
||
</body></html>{{end}}
|
||
|
||
{{define "board"}}{{template "head" .}}{{template "nav" .}}
|
||
<section class="board"><div class="board-row">
|
||
<div>
|
||
<span class="lbl" style="color:var(--dim)">{{if gt .Value 0.0}}Стоимость бумаг{{else}}Бумаг в реестре{{end}}</span>
|
||
<div class="big" style="margin-top:8px">{{if gt .Value 0.0}}{{fmtmoney .Value}} ₽{{else}}{{.Total}}{{end}}</div>
|
||
<div class="n" style="font-size:13px;color:var(--dim);margin-top:6px">{{with .FullName}}{{.}} · {{end}}договор {{.Contract}}{{if gt .Value 0.0}} · оценка по {{.Priced}} из {{.Total}} бумаг{{end}}</div>
|
||
</div>
|
||
<div class="stats">
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Свободно к переводу</span><span class="v">{{.Free}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">С ограничением</span><span class="v" style="color:var(--amber)">{{.Blocked}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Поручений в работе</span><span class="v">{{.Active}}</span></span>
|
||
</div>
|
||
</div></section>
|
||
<main>
|
||
{{if .Flash}}<div class="msg ok">{{.Flash}}</div>{{end}}
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
<section class="sec">
|
||
<div class="sec-h"><h1 class="lbl" style="font-size:12px;letter-spacing:.14em">Последние поручения</h1><a href="/orders" style="font-size:13px;text-decoration:none">все поручения</a></div>
|
||
{{if .Claims}}<div class="tw"><table>
|
||
<thead><tr><th>Номер</th><th>Дата</th><th>Вид поручения</th><th class="num">Бумаг</th><th>Состояние</th></tr></thead>
|
||
<tbody>{{range .Claims}}<tr>
|
||
<td class="n">{{shortid .GUID}}</td>
|
||
<td class="n">{{.CreatedAt}}</td>
|
||
<td>Перевод в другой депозитарий</td>
|
||
<td class="num">{{.Securit}}</td>
|
||
<td><span class="chip {{if eq .Status "done"}}ok{{else if eq .Status "rejected"}}err{{else if eq .Status "declined"}}err{{else if eq .Status "error"}}err{{else}}go{{end}}">{{.StatusRu}}</span>{{with .Reason}}<div class="mut">{{.}}</div>{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
{{else}}<p class="empty">Поручений пока нет. Нажмите «Новое поручение», чтобы перевести бумаги в другой депозитарий.</p>{{end}}
|
||
</section>
|
||
|
||
<section class="sec">
|
||
<div class="sec-h"><h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Реестр бумаг</h2><a href="/portfolio" style="font-size:13px;text-decoration:none">весь реестр</a></div>
|
||
{{if .Positions}}<div class="tw"><table>
|
||
<thead><tr><th>Выпуск</th><th>Счёт депо</th><th class="num">Количество</th><th class="num">Свободно</th><th class="num">Оценка</th><th>Состояние</th></tr></thead>
|
||
<tbody>{{range .Positions}}<tr>
|
||
<td><a href="/security?isin={{.ISIN}}" style="text-decoration:none;font-weight:600">{{.SecurityName}}</a><div class="n" style="font-size:12.5px;color:var(--mut)">{{.ISIN}}</div></td>
|
||
<td class="n" style="font-size:13px">{{if .AccountID}}{{.AccountID}}{{else}}—{{end}}</td>
|
||
<td class="num">{{fmtnum .Quantity}}</td>
|
||
<td class="num">{{fmtnum .Available}}</td>
|
||
<td class="num">{{template "money" .Value}}</td>
|
||
<td>{{if not .Transferable}}<span class="chip">не переводится</span>{{else if gt .Blocked 0.0}}<span class="chip go">{{fmtnum .Blocked}} с ограничением</span>{{else}}<span class="chip ok">свободно</span>{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
{{else}}<p class="empty">Бумаг на счетах депо не найдено. Если это ошибка — напишите в депозитарий.</p>{{end}}
|
||
</section>
|
||
</main></body></html>{{end}}
|
||
|
||
{{define "portfolio"}}{{template "head" .}}{{template "nav" .}}
|
||
<section class="board"><div class="board-row">
|
||
<div><span class="lbl" style="color:var(--dim)">Реестр бумаг</span>
|
||
{{if gt .Value 0.0}}<div class="big" style="margin-top:8px;font-size:32px">{{fmtmoney .Value}} ₽</div>{{end}}
|
||
<div class="n" style="font-size:13px;color:var(--dim);margin-top:6px">{{.Source}}{{with .UpdatedAt}} · {{.}}{{end}}{{if gt .Value 0.0}} · цены торговой системы{{end}}</div></div>
|
||
<div class="stats">
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Выпусков</span><span class="v">{{.Total}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Свободно к переводу</span><span class="v">{{.Free}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">С ограничением</span><span class="v" style="color:var(--amber)">{{.Blocked}}</span></span>
|
||
</div>
|
||
</div></section>
|
||
<main>
|
||
{{if .Flash}}<div class="msg ok">{{.Flash}}</div>{{end}}
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
{{if .ReadOnly}}<div class="msg">Кабинет открыт в режиме просмотра: подача поручений по этому договору выключена депозитарием.</div>{{end}}
|
||
{{if .Positions}}
|
||
<div class="tw"><table>
|
||
<thead><tr><th>Выпуск</th><th>Счёт депо</th><th class="num">Количество</th><th class="num">С ограничением</th><th class="num">Свободно</th><th class="num">Цена</th><th class="num">Оценка</th><th>Состояние</th><th></th></tr></thead>
|
||
<tbody>{{range .Positions}}<tr>
|
||
<td><a href="/security?isin={{.ISIN}}" style="text-decoration:none;font-weight:600">{{.SecurityName}}</a><div class="n" style="font-size:12.5px;color:var(--mut)">{{.ISIN}}{{with .SecurityCode}} · {{.}}{{end}}</div></td>
|
||
<td class="n" style="font-size:13px">{{if .AccountID}}{{.AccountID}}{{else}}—{{end}}</td>
|
||
<td class="num">{{fmtnum .Quantity}}</td>
|
||
<td class="num">{{if gt .Blocked 0.0}}<span style="color:var(--amber-tx)">{{fmtnum .Blocked}}</span>{{else}}—{{end}}</td>
|
||
<td class="num">{{fmtnum .Available}}</td>
|
||
<td class="num">{{if gt .Price 0.0}}{{fmtmoney .Price}}{{if ne .ChangePct 0.0}}<div style="font-size:12px;{{if gt .ChangePct 0.0}}color:var(--ok-tx){{else}}color:var(--err-tx){{end}}">{{fmtpct .ChangePct}}</div>{{end}}{{else}}—{{end}}</td>
|
||
<td class="num">{{template "money" .Value}}</td>
|
||
<td>{{if not .Transferable}}<span class="chip">иностранный выпуск</span>{{else if gt .Blocked 0.0}}<span class="chip go">частично ограничено</span>{{else}}<span class="chip ok">свободно</span>{{end}}{{with .Note}}<div class="mut">{{.}}</div>{{end}}</td>
|
||
<td style="text-align:right">{{if and .Transferable (gt .Available 0.0) (not $.ReadOnly)}}<a href="/order?isin={{.ISIN}}" class="lbl" style="text-decoration:none">перевести</a>{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
<p class="mut" style="margin-top:18px"><b>С ограничением</b> — бумаги в расчётах по сделке, под обременением или ограниченные в обращении: перевести их нельзя, пока ограничение не снято.
|
||
<b>Иностранный выпуск</b> — перевод между депозитариями по MOEX МОСТ работает только с российскими выпусками, код ISIN начинается на RU.</p>
|
||
{{else}}<p class="empty">Бумаг на счетах депо не найдено.</p>{{end}}
|
||
</main></body></html>{{end}}
|
||
|
||
{{define "transfer"}}{{template "head" .}}{{template "nav" .}}
|
||
<main>
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
<form method="post" action="/order" id="orderForm">
|
||
<div class="sheet">
|
||
<div style="display:flex;align-items:flex-end;justify-content:space-between;gap:20px;border-bottom:2px solid var(--ink);padding-bottom:14px;flex-wrap:wrap">
|
||
<div><h1 style="margin:0">Поручение депозитарию</h1>
|
||
<span class="lbl" style="color:var(--mut);margin-top:6px;display:block">Заполняется по видам, как и другие поручения кабинета</span></div>
|
||
<div style="text-align:right"><span class="lbl" style="color:var(--mut);display:block">Черновик</span>
|
||
<span class="n" style="font-size:16px;font-weight:700">{{.Today}}</span></div>
|
||
</div>
|
||
|
||
<div style="margin-top:20px">
|
||
<span class="lbl" style="display:block;margin-bottom:10px;color:var(--ink)">1. Вид поручения</span>
|
||
<select name="kind" aria-label="Вид поручения">
|
||
<option value="m2m">Перевод бумаг в другой депозитарий</option>
|
||
<option value="other" disabled>Другие виды — подаются в депозитарии, скоро появятся здесь</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div style="margin-top:20px">
|
||
<span class="lbl" style="display:block;margin-bottom:10px;color:var(--ink)">2. Что переводим</span>
|
||
{{if .Positions}}<div class="tw"><table>
|
||
<thead><tr><th style="width:40px"></th><th>Выпуск</th><th class="num">Свободно</th><th style="width:180px">Количество</th></tr></thead>
|
||
<tbody>{{range .Positions}}<tr>
|
||
<td>{{if and .Transferable (gt .Available 0.0)}}<input type="checkbox" name="isin" value="{{.ISIN}}" id="c{{.ISIN}}"{{if eq $.PickISIN .ISIN}} checked{{end}}>{{end}}</td>
|
||
<td><label for="c{{.ISIN}}" style="font-family:inherit;font-size:14px;letter-spacing:0;text-transform:none;color:var(--ink);margin:0;font-weight:600">{{.SecurityName}}</label>
|
||
<div class="n" style="font-size:12.5px;color:var(--mut)">{{.ISIN}}{{if not .Transferable}} · иностранный выпуск, перевод недоступен{{else if eq .Available 0.0}} · свободного остатка нет{{end}}</div></td>
|
||
<td class="num">{{fmtnum .Available}}</td>
|
||
<td>{{if and .Transferable (gt .Available 0.0)}}<input class="n" name="qty_{{.ISIN}}" inputmode="decimal" style="min-height:40px;text-align:right" aria-label="Количество {{.SecurityName}}">{{else}}<span class="lbl" style="color:var(--mut)">недоступно</span>{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
{{else}}<p class="empty">Свободных бумаг для перевода нет.</p>{{end}}
|
||
</div>
|
||
|
||
<div style="margin-top:20px">
|
||
<span class="lbl" style="display:block;margin-bottom:10px;color:var(--ink)">3. Куда переводим</span>
|
||
<div class="grid2">
|
||
<div><label for="inn">ИНН принимающего депозитария</label>
|
||
<input id="inn" name="receiving_inn" inputmode="numeric" maxlength="12" required aria-describedby="innh">
|
||
<span id="innh" class="mut" style="display:block;margin-top:6px">10 цифр для организации. ИНН можно спросить у депозитария, куда переводите бумаги.</span></div>
|
||
<div><label for="rname">Наименование</label><input id="rname" name="receiving_name" placeholder="как в договоре"></div>
|
||
<div><label for="racc">Ваш счёт депо у принимающего</label><input id="racc" name="account_at_recv" placeholder="если знаете"></div>
|
||
<div><label for="cmt">Комментарий депозитарию</label><input id="cmt" name="comment" placeholder="необязательно"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="display:flex;gap:12px;flex-wrap:wrap;margin-top:22px">
|
||
<button class="btn main" type="submit">Подать поручение</button>
|
||
<a class="btn" href="/">Отмена</a>
|
||
</div>
|
||
<p class="mut" style="margin-top:14px">Переводятся только российские выпуски. Бумаги для квалифицированных инвесторов принимающая сторона может не принять. После отправки в НРД отменить поручение нельзя — состояние будет видно в разделе «Поручения».</p>
|
||
</div>
|
||
</form>
|
||
</main></body></html>{{end}}
|
||
|
||
{{define "claims"}}{{template "head" .}}{{template "nav" .}}
|
||
<section class="board"><div class="board-row">
|
||
<div><span class="lbl" style="color:var(--dim)">Поручения депозитарию</span>
|
||
<div class="n" style="font-size:13px;color:var(--dim);margin-top:6px">все виды поручений кабинета</div></div>
|
||
<div class="stats">
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Всего</span><span class="v">{{.Total}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">В работе</span><span class="v" style="color:var(--amber)">{{.Active}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Исполнено</span><span class="v">{{.Done}}</span></span>
|
||
</div>
|
||
</div></section>
|
||
<main>
|
||
{{if .Flash}}<div class="msg ok">{{.Flash}}</div>{{end}}
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
{{if .Claims}}<div class="tw"><table>
|
||
<thead><tr><th>Номер</th><th>Дата</th><th>Вид поручения</th><th class="num">Бумаг</th><th>Состояние</th><th>Примечание</th></tr></thead>
|
||
<tbody>{{range .Claims}}<tr>
|
||
<td class="n">{{shortid .GUID}}</td>
|
||
<td class="n">{{.CreatedAt}}</td>
|
||
<td>Перевод в другой депозитарий</td>
|
||
<td class="num">{{.Securit}}</td>
|
||
<td><span class="chip {{if eq .Status "done"}}ok{{else if eq .Status "rejected"}}err{{else if eq .Status "declined"}}err{{else if eq .Status "error"}}err{{else}}go{{end}}">{{.StatusRu}}</span></td>
|
||
<td class="mut">{{if .Reason}}{{.Reason}}{{else}}—{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
<p class="mut" style="margin-top:18px">Пока поручение в работе, бумаги остаются на счёте, но перевести их повторно нельзя. Если принимающая сторона откажет, бумаги освободятся в тот же день, а причина появится в примечании.</p>
|
||
{{else}}<p class="empty">Поручений пока нет.</p>{{end}}
|
||
</main></body></html>{{end}}
|
||
|
||
{{define "profile"}}{{template "head" .}}{{template "nav" .}}
|
||
<section class="board"><div class="board-row">
|
||
<div><span class="lbl" style="color:var(--dim)">Профиль</span>
|
||
<div style="font-size:23px;font-weight:700;margin-top:8px">{{if .FullName}}{{.FullName}}{{else}}Клиент депозитария{{end}}</div></div>
|
||
<div class="stats">
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Договор</span><span class="v">{{.Contract}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Счетов депо</span><span class="v">{{.Accounts}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Подача поручений</span><span class="v">{{if .ReadOnly}}выключена{{else}}доступна{{end}}</span></span>
|
||
</div>
|
||
</div></section>
|
||
<main>
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
<div class="grid2">
|
||
<section class="sec">
|
||
<h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Счета депо</h2>
|
||
<div class="rows">{{range .AccountList}}<div class="row"><span class="n">{{.}}</span><span class="chip ok">открыт</span></div>{{else}}<p class="empty">Счета не найдены.</p>{{end}}</div>
|
||
</section>
|
||
<section class="sec">
|
||
<h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Доступ и безопасность</h2>
|
||
<div class="rows">
|
||
<div class="row"><span>Вход в кабинет</span><span class="n">{{.Login}}</span></div>
|
||
<div class="row"><span>Подача поручений</span><span>{{if .ReadOnly}}выключена депозитарием{{else}}доступна{{end}}</span></div>
|
||
{{if .AsClient}}<div class="row"><span>Сейчас в кабинете</span><span>сотрудник депозитария</span></div>{{end}}
|
||
<div class="row"><span>Завершить работу</span><a href="/logout">выйти</a></div>
|
||
</div>
|
||
<p class="mut" style="margin-top:14px">Контакты, паспортные данные и реквизиты для выплат меняет депозитарий: обратитесь к своему менеджеру, изменения появятся в кабинете после внесения в анкету.</p>
|
||
</section>
|
||
</div>
|
||
</main></body></html>{{end}}
|
||
|
||
{{define "clients"}}{{template "head" .}}{{template "nav" .}}
|
||
<section class="board"><div class="board-row">
|
||
<div><span class="lbl" style="color:var(--dim)">Режим сотрудника</span>
|
||
<div style="font-size:23px;font-weight:700;margin-top:8px">Клиенты депозитария</div></div>
|
||
<div class="stats">
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Учётных записей</span><span class="v">{{len .Clients}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Договоров в учёте</span><span class="v">{{len .Depo}}</span></span>
|
||
</div>
|
||
</div></section>
|
||
<main>
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
<section class="sec">
|
||
<h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Учётные записи кабинета</h2>
|
||
{{if .Clients}}<div class="tw"><table>
|
||
<thead><tr><th>Клиент</th><th>Логин</th><th>Договор</th><th>Состояние</th><th></th></tr></thead>
|
||
<tbody>{{range .Clients}}<tr>
|
||
<td><b>{{.FullName}}</b></td><td class="n">{{.Login}}</td><td class="n">{{.Contract}}</td>
|
||
<td>{{if .Active}}<span class="chip ok">активна</span>{{else}}<span class="chip err">заблокирована</span>{{end}}</td>
|
||
<td style="text-align:right"><form method="post" action="/impersonate" style="margin:0"><input type="hidden" name="login" value="{{.Login}}"><button class="btn sm" type="submit">Открыть кабинет</button></form></td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
{{else}}<p class="empty">Учётных записей кабинета пока нет.</p>{{end}}
|
||
</section>
|
||
|
||
<section class="sec">
|
||
<div class="sec-h"><h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Клиенты депозитария</h2></div>
|
||
<form method="get" action="/clients" style="display:flex;gap:10px;align-items:flex-end;max-width:560px;margin-bottom:14px">
|
||
<div style="flex-grow:1"><label for="s">Поиск</label><input id="s" name="search" value="{{.Search}}" placeholder="фамилия или номер договора"></div>
|
||
<button class="btn" type="submit">Найти</button>
|
||
</form>
|
||
{{if .DepoError}}<div class="msg err">{{.DepoError}}</div>{{end}}
|
||
{{if .Depo}}<div class="tw"><table>
|
||
<thead><tr><th>Клиент</th><th>Договор</th><th class="num">Бумаг</th><th></th></tr></thead>
|
||
<tbody>{{range .Depo}}<tr>
|
||
<td><b>{{.Name}}</b></td><td class="n">{{.Contract}}</td><td class="num">{{.Securities}}</td>
|
||
<td style="text-align:right"><form method="post" action="/impersonate" style="margin:0"><input type="hidden" name="contract" value="{{.Contract}}"><button class="btn sm" type="submit">Открыть кабинет</button></form></td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
{{else}}<p class="empty">Ничего не найдено.</p>{{end}}
|
||
</section>
|
||
|
||
<p class="mut">В кабинете клиента сотрудник готовит поручение, подписывает его клиент. Каждый вход сотрудника записывается в журнал.</p>
|
||
</main></body></html>{{end}}
|
||
{{define "security"}}{{template "head" .}}{{template "nav" .}}
|
||
{{$s := .S}}
|
||
<section class="board"><div class="board-row">
|
||
<div>
|
||
<a href="/portfolio" class="lbl" style="color:var(--dim);text-decoration:none">← Реестр</a>
|
||
<div style="font-size:23px;font-weight:700;margin-top:8px">{{if $s.Name}}{{$s.Name}}{{else}}{{.ISIN}}{{end}}</div>
|
||
<div class="n" style="font-size:13px;color:var(--dim);margin-top:6px">{{.ISIN}}{{with $s.Kind}} · {{.}}{{end}}{{with $s.RegNumber}} · {{.}}{{end}}</div>
|
||
</div>
|
||
<div class="stats">
|
||
{{with $s.Position}}<span class="stat"><span class="lbl" style="color:var(--dim)">В портфеле</span><span class="v">{{fmtnum .Quantity}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Свободно</span><span class="v">{{fmtnum .Available}}</span></span>{{end}}
|
||
{{if gt $s.Price 0.0}}<span class="stat"><span class="lbl" style="color:var(--dim)">Цена</span><span class="v">{{fmtmoney $s.Price}}</span></span>{{end}}
|
||
{{with $s.Position}}{{if gt .Value 0.0}}<span class="stat"><span class="lbl" style="color:var(--dim)">Оценка</span><span class="v">{{fmtmoney .Value}} ₽</span></span>{{end}}{{end}}
|
||
</div>
|
||
</div></section>
|
||
<main>
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
{{with $s.Note}}<div class="msg">{{.}}</div>{{end}}
|
||
<div class="grid2">
|
||
<section class="sec">
|
||
<h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Выпуск</h2>
|
||
<div class="rows">
|
||
{{with $s.Issuer}}<div class="row"><span>Эмитент</span><span style="text-align:right">{{.}}</span></div>{{end}}
|
||
{{with $s.Kind}}<div class="row"><span>Вид бумаги</span><span>{{.}}</span></div>{{end}}
|
||
{{if gt $s.Face 0.0}}<div class="row"><span>Номинал</span><span class="n">{{fmtmoney $s.Face}} ₽</span></div>{{end}}
|
||
{{with $s.Maturity}}<div class="row"><span>Погашение</span><span class="n">{{.}}</span></div>{{end}}
|
||
{{with $s.RegNumber}}<div class="row"><span>Государственная регистрация</span><span class="n">{{.}}</span></div>{{end}}
|
||
{{with $s.CFI}}<div class="row"><span>Код CFI</span><span class="n">{{.}}</span></div>{{end}}
|
||
<div class="row"><span>Обслуживание в депозитарии</span><span>{{if $s.Accepted}}принята{{else}}не принята{{end}}</span></div>
|
||
<div class="row"><span>Для квалифицированных инвесторов</span><span>{{if $s.QualOnly}}да{{else}}ограничений нет{{end}}</span></div>
|
||
{{if gt $s.Price 0.0}}<div class="row"><span>Цена{{with $s.Board}} · {{.}}{{end}}</span><span class="n">{{fmtmoney $s.Price}}{{with $s.PriceIn}} <span style="color:var(--mut)">{{.}}</span>{{end}}</span></div>{{end}}
|
||
</div>
|
||
{{with $s.Position}}{{if and .Transferable (gt .Available 0.0)}}
|
||
<a href="/order?isin={{$s.ISIN}}" class="btn main" style="margin-top:16px">Перевести эту бумагу</a>{{end}}{{end}}
|
||
</section>
|
||
|
||
<section class="sec">
|
||
<h2 class="lbl" style="font-size:12px;letter-spacing:.14em">График выплат и событий</h2>
|
||
{{if $s.Payments}}<div class="tw"><table>
|
||
<thead><tr><th>Дата</th><th>Событие</th><th class="num">На бумагу</th><th class="num">Ставка</th><th>Состояние</th></tr></thead>
|
||
<tbody>{{range $s.Payments}}<tr>
|
||
<td class="n">{{.Date}}</td>
|
||
<td>{{.Kind}}{{with .FixDate}}<div class="mut">фиксация {{.}}</div>{{end}}</td>
|
||
<td class="num">{{if gt .Amount 0.0}}{{fmtmoney .Amount}} ₽{{else}}—{{end}}</td>
|
||
<td class="num">{{if gt .Percent 0.0}}{{fmtpct .Percent}}{{else}}—{{end}}</td>
|
||
<td>{{if .Paid}}<span class="chip ok">выплачено</span>{{else}}<span class="chip">ожидается</span>{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
{{else}}<p class="empty">Выплат по этой бумаге в справочнике депозитария нет.</p>{{end}}
|
||
<p class="mut" style="margin-top:14px">Суммы приведены на одну бумагу по данным справочника депозитария. Фактическая выплата и удержанный налог появятся в операциях по счёту.</p>
|
||
</section>
|
||
</div>
|
||
</main></body></html>{{end}}
|
||
{{define "operations"}}{{template "head" .}}{{template "nav" .}}
|
||
<section class="board"><div class="board-row">
|
||
<div><span class="lbl" style="color:var(--dim)">Операции по счетам депо</span>
|
||
<div class="big" style="margin-top:8px;font-size:32px">{{.Total}}</div>
|
||
<div class="n" style="font-size:13px;color:var(--dim);margin-top:6px">за период {{.From}} — {{.To}}</div></div>
|
||
<div class="stats">
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Зачислений</span><span class="v">{{.In}}</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Списаний</span><span class="v">{{.Out}}</span></span>
|
||
</div>
|
||
</div></section>
|
||
<main>
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
<form method="get" action="/operations" style="display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;margin-bottom:18px">
|
||
<div><label for="from">Период с</label><input id="from" type="date" name="from" value="{{.From}}" style="width:auto"></div>
|
||
<div><label for="to">по</label><input id="to" type="date" name="to" value="{{.To}}" style="width:auto"></div>
|
||
<button class="btn" type="submit">Показать</button>
|
||
</form>
|
||
{{if .Operations}}<div class="tw"><table>
|
||
<thead><tr><th>Дата</th><th>Операция</th><th>Выпуск</th><th>Счёт депо</th><th class="num">Количество</th><th>Состояние</th><th>Основание</th></tr></thead>
|
||
<tbody>{{range .Operations}}<tr>
|
||
<td class="n">{{.Date}}{{if and .DoneAt (ne .DoneAt .Date)}}<div class="mut">проведено {{.DoneAt}}</div>{{end}}</td>
|
||
<td>{{.Kind}}{{with .Number}}<div class="n mut">№ {{.}}</div>{{end}}</td>
|
||
<td>{{if .ISIN}}<a href="/security?isin={{.ISIN}}" style="text-decoration:none">{{.Security}}</a><div class="n mut">{{.ISIN}}</div>{{else}}{{.Security}}{{end}}</td>
|
||
<td class="n" style="font-size:13px">{{.Account}}</td>
|
||
<td class="num">{{if lt .Quantity 0.0}}<span style="color:var(--err-tx)">{{fmtnum .Quantity}}</span>{{else}}<span style="color:var(--ok-tx)">+{{fmtnum .Quantity}}</span>{{end}}</td>
|
||
<td>{{if eq .State "исполнено"}}<span class="chip ok">исполнено</span>{{else if eq .State "отменено"}}<span class="chip err">отменено</span>{{else}}<span class="chip go">{{.State}}</span>{{end}}</td>
|
||
<td class="mut">{{if .Note}}{{.Note}}{{else}}—{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
<p class="mut" style="margin-top:18px">Показаны зачисления, списания и переводы между вашими счетами депо по данным учёта депозитария. Выписку за период можно заказать у депозитария.</p>
|
||
{{else}}<p class="empty">За выбранный период операций по счетам депо нет.</p>{{end}}
|
||
</main></body></html>{{end}}
|
||
|
||
{{define "income"}}{{template "head" .}}{{template "nav" .}}
|
||
<section class="board"><div class="board-row">
|
||
<div><span class="lbl" style="color:var(--dim)">Выплачено дохода</span>
|
||
<div class="big" style="margin-top:8px">{{fmtmoney .Paid}} ₽</div>
|
||
<div class="n" style="font-size:13px;color:var(--dim);margin-top:6px">за период {{.From}} — {{.To}}</div></div>
|
||
<div class="stats">
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Начислено</span><span class="v">{{fmtmoney .Accrued}} ₽</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Удержан налог</span><span class="v" style="color:var(--amber)">{{fmtmoney .Tax}} ₽</span></span>
|
||
</div>
|
||
</div></section>
|
||
<main>
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
<form method="get" action="/income" style="display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;margin-bottom:18px">
|
||
<div><label for="from">Период с</label><input id="from" type="date" name="from" value="{{.From}}" style="width:auto"></div>
|
||
<div><label for="to">по</label><input id="to" type="date" name="to" value="{{.To}}" style="width:auto"></div>
|
||
<button class="btn" type="submit">Показать</button>
|
||
</form>
|
||
{{if .Income}}<div class="tw"><table>
|
||
<thead><tr><th>Дата</th><th>Выпуск</th><th>Вид дохода</th><th class="num">Бумаг</th><th class="num">Начислено</th><th class="num">Налог</th><th class="num">К выплате</th></tr></thead>
|
||
<tbody>{{range .Income}}<tr>
|
||
<td class="n">{{.Date}}</td>
|
||
<td>{{if .ISIN}}<a href="/security?isin={{.ISIN}}" style="text-decoration:none">{{.Security}}</a><div class="n mut">{{.ISIN}}</div>{{else}}{{.Security}}{{end}}</td>
|
||
<td>{{.Kind}}</td>
|
||
<td class="num">{{if gt .Quantity 0.0}}{{fmtnum .Quantity}}{{else}}—{{end}}</td>
|
||
<td class="num">{{fmtmoney .Accrued}}</td>
|
||
<td class="num">{{if gt .Tax 0.0}}{{fmtmoney .Tax}}{{else}}—{{end}}</td>
|
||
<td class="num" style="font-weight:700">{{fmtmoney .Paid}} ₽</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
<p class="mut" style="margin-top:18px">Суммы и удержанный налог — по данным учёта депозитария как налогового агента по выплатам эмитентов. По купонам государственных облигаций налог может удерживать брокер при выплате, тогда здесь он нулевой. Справку о доходах можно запросить у депозитария.</p>
|
||
{{else}}<p class="empty">За выбранный период выплат дохода нет.</p>{{end}}
|
||
</main></body></html>{{end}}
|
||
`
|