c32f399acc
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
598 lines
53 KiB
Go
598 lines
53 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="stylesheet" href="/assets/fonts.css">
|
||
<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}
|
||
.lnk{background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer;text-decoration:underline;text-underline-offset:2px}
|
||
.tabs{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:0 0 16px}
|
||
.tab{font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;padding:8px 14px;border:1px solid var(--line-2);border-radius:3px;text-decoration:none;color:var(--ink-2)}
|
||
.tab.on{background:var(--ink);color:#f3f0e7;border-color:var(--ink)}
|
||
.tabform{display:flex;gap:8px;align-items:center;margin-left:8px}
|
||
.tabform input[type=date]{padding:7px 10px;border:1px solid var(--line-2);border-radius:3px;background:#fff;font:inherit;font-size:14px}
|
||
.pos{color:var(--ok-tx)}.neg{color:var(--err-tx)}
|
||
.sbar{display:flex;height:14px;border-radius:3px;overflow:hidden;background:var(--line);gap:2px}
|
||
.sbar span{display:block;height:100%;min-width:2px}
|
||
.slegend{display:flex;gap:18px;flex-wrap:wrap;margin-top:10px;font-size:13.5px}
|
||
.slegend i{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:7px;vertical-align:-1px}
|
||
.kind{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--mut)}
|
||
.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">
|
||
<a class="brand" href="/" title="На табло" style="text-decoration:none;color:inherit">{{template "mark" .}}<span class="lbl" style="font-size:12px;letter-spacing:.14em">{{.Title}}</span></a>
|
||
<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="/operations" class="lbl {{if eq .Nav "o"}}on{{end}}">Операции</a>
|
||
<a href="/orders" class="lbl {{if eq .Nav "c"}}on{{end}}">Поручения</a>
|
||
<a href="/documents" class="lbl {{if eq .Nav "d"}}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>
|
||
<form method="post" action="/logout" style="margin:0"><input type="hidden" name="csrf" value="{{.CSRF}}"><button class="exit lbl lnk" type="submit" style="text-decoration:none">Выход</button></form>
|
||
</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>
|
||
<form method="post" action="/back-to-admin" style="margin:0"><input type="hidden" name="csrf" value="{{.CSRF}}"><button class="lnk" type="submit" style="color:#e8a33d">вернуться к списку клиентов</button></form></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"><input type="hidden" name="csrf" value="{{$.CSRF}}">
|
||
<h1 style="margin:0">Вход</h1>
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
{{if .Flash}}<div class="msg ok">{{.Flash}}</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 "portfolio"}}{{template "head" .}}{{template "nav" .}}{{$p := .P}}
|
||
<section class="board"><div class="board-row">
|
||
<div>
|
||
<span class="lbl" style="color:var(--dim)">Стоимость портфеля{{if eq .View "archive"}} на {{$p.AsOf}}{{end}}</span>
|
||
<div class="big" style="margin-top:8px">{{fmtmoney $p.Total}} ₽</div>
|
||
<div class="n" style="font-size:13px;color:var(--dim);margin-top:6px">{{with .FullName}}{{.}} · {{end}}договор {{.Contract}}{{if and (ne .View "archive") $p.AsOf}} · данные на {{$p.AsOf}}{{end}}{{if $p.Note}} · {{$p.Note}}{{end}}</div>
|
||
</div>
|
||
<div class="stats">
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Бумаги</span><span class="v">{{fmtmoney $p.Securities}} ₽</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Деньги</span><span class="v">{{fmtmoney $p.CashRUB}} ₽</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Результат</span><span class="v" style="color:{{if lt $p.PnL 0.0}}#f0a090{{else if gt $p.PnL 0.0}}#8fd19e{{else}}inherit{{end}}">{{if ne $p.Cost 0.0}}{{fmtsigned $p.PnL}} ₽ · {{fmtpct $p.PnLPct}}{{else}}—{{end}}</span></span>
|
||
</div>
|
||
</div></section>
|
||
<main>
|
||
{{if .Flash}}<div class="msg ok">{{.Flash}}</div>{{end}}
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
<div class="tabs">
|
||
<a class="tab {{if ne .View "archive"}}on{{end}}" href="/">Онлайн</a>
|
||
<a class="tab {{if eq .View "archive"}}on{{end}}" href="/?view=archive">Архив</a>
|
||
{{if eq .View "archive"}}<form method="get" action="/" class="tabform"><input type="hidden" name="view" value="archive"><input type="date" name="date" value="{{.Date}}" max="{{.Today}}"><button class="btn sm" type="submit">Показать</button></form>{{end}}
|
||
<span class="mut" style="margin-left:auto;font-size:12.5px">{{if eq .View "archive"}}Портфель по брокерскому учёту на выбранную дату{{else if eq $p.Source "quik"}}Позиции и деньги в торговой системе, цены биржи{{else}}Портфель по брокерскому учёту{{end}}</span>
|
||
</div>
|
||
|
||
{{if .Structure}}<section class="sec" style="margin-bottom:18px">
|
||
<div class="sec-h"><h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Структура портфеля</h2></div>
|
||
<div class="sbar">{{range .Structure}}<span style="width:{{printf "%.2f" .Share}}%;background:{{.Color}}" title="{{.Kind}}: {{fmtmoney .Value}} ₽"></span>{{end}}</div>
|
||
<div class="slegend">{{range .Structure}}<span><i style="background:{{.Color}}"></i>{{.Kind}} <b class="n">{{fmtpct1 .Share}}</b> <span class="mut n">{{fmtmoney .Value}} ₽</span></span>{{end}}</div>
|
||
</section>{{end}}
|
||
|
||
{{if $p.Cash}}<section class="sec" style="margin-bottom:18px">
|
||
<div class="sec-h"><h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Денежные средства</h2></div>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap">{{range $p.Cash}}<span class="chip" style="font-size:13px;text-transform:none;letter-spacing:0"><b class="n">{{fmtmoney .Amount}}</b> {{if eq .Currency "SUR"}}₽{{else}}{{.Currency}}{{end}}</span>{{end}}</div>
|
||
</section>{{end}}
|
||
|
||
<section class="sec">
|
||
<div class="sec-h"><h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Бумаги</h2><span class="mut" style="font-size:12.5px">{{len $p.Positions}} позиций{{if $p.Positions}}, с ценой {{$p.Priced}}{{end}}</span></div>
|
||
{{if $p.Positions}}<div class="tw"><table>
|
||
<thead><tr><th>Бумага</th><th class="num">Кол-во</th><th class="num">Средняя</th><th class="num">Цена</th><th class="num">Стоимость</th><th class="num">Результат</th><th class="num">Доля</th></tr></thead>
|
||
<tbody>{{range $p.Positions}}<tr>
|
||
<td>{{if .ISIN}}<a href="/security?isin={{.ISIN}}" style="text-decoration:none;font-weight:600">{{.Name}}</a>{{else}}<b>{{.Name}}</b>{{end}}<div class="kind">{{.Kind}}{{if .ISIN}} · <span class="n" style="text-transform:none;letter-spacing:0">{{.ISIN}}</span>{{end}}{{if .Note}} · {{.Note}}{{end}}</div></td>
|
||
<td class="num">{{fmtnum .Quantity}}</td>
|
||
<td class="num">{{if gt .AvgPrice 0.0}}{{fmtprice .AvgPrice}}{{if .InPct}}%{{end}}{{else}}—{{end}}</td>
|
||
<td class="num">{{if gt .Price 0.0}}{{fmtprice .Price}}{{if .InPct}}%{{end}}{{if ne .DayPct 0.0}}<div class="{{if lt .DayPct 0.0}}neg{{else}}pos{{end}}" style="font-size:11.5px">{{fmtpct .DayPct}}</div>{{end}}{{else}}—{{end}}</td>
|
||
<td class="num">{{template "money" .Value}}</td>
|
||
<td class="num">{{if ne .Cost 0.0}}<span class="{{if lt .PnL 0.0}}neg{{else if gt .PnL 0.0}}pos{{end}}">{{fmtsigned .PnL}}<div style="font-size:11.5px">{{fmtpct .PnLPct}}</div></span>{{else}}—{{end}}</td>
|
||
<td class="num">{{if gt .Share 0.0}}{{fmtpct1 .Share}}{{else}}—{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
{{else}}<p class="empty">{{if eq .View "archive"}}На эту дату бумаг на счёте не было.{{else}}Бумаг на счёте нет.{{end}}</p>{{end}}
|
||
</section>
|
||
|
||
{{if and (ne .View "archive") .Upcoming}}<section class="sec" style="margin-top:22px">
|
||
<div class="sec-h"><h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Ближайшие выплаты · 90 дней</h2>{{if gt .UpcomingTotal 0.0}}<span class="n" style="font-size:13px">к получению ≈ {{fmtmoney .UpcomingTotal}} ₽</span>{{end}}</div>
|
||
<div class="tw"><table>
|
||
<thead><tr><th>Дата</th><th>Бумага</th><th>Событие</th><th class="num">На бумагу</th><th class="num">Всего</th></tr></thead>
|
||
<tbody>{{range .Upcoming}}<tr>
|
||
<td class="n">{{.Date}}</td>
|
||
<td><a href="/security?isin={{.ISIN}}" style="text-decoration:none;font-weight:600">{{.Security}}</a><div class="n" style="font-size:12px;color:var(--mut)">{{.ISIN}} · {{fmtnum .Quantity}} шт.</div></td>
|
||
<td><span class="chip {{if eq .Kind "погашение"}}go{{else if eq .Kind "оферта"}}go{{else}}ok{{end}}">{{.Kind}}</span>{{if gt .Percent 0.0}}<div class="mut" style="font-size:12px">{{fmtpct1 .Percent}} годовых</div>{{end}}</td>
|
||
<td class="num">{{if gt .PerUnit 0.0}}{{fmtmoney .PerUnit}} ₽{{else}}<span class="mut">по решению эмитента</span>{{end}}</td>
|
||
<td class="num">{{if gt .Total 0.0}}<b>{{fmtmoney .Total}} ₽</b>{{else}}—{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
<p class="mut" style="margin-top:10px;font-size:12.5px">Суммы до удержания налога, по данным справочника депозитария; оферта и погашение — при предъявлении бумаг.</p>
|
||
</section>{{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"><input type="hidden" name="csrf" value="{{$.CSRF}}">
|
||
<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 "install"}}{{template "head" .}}{{$d := .D}}
|
||
<div class="login">
|
||
<section class="side">
|
||
<span class="brand">{{template "mark" .}}<span class="lbl" style="font-size:12px;letter-spacing:.14em;color:#f3f0e7">Установка кабинета</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:{{if ge $d.Step 1}}#e8a33d{{else}}#8e8879{{end}}">01</span><span>Проверка сервера{{if gt $d.Step 1}} — готово{{end}}</span></span>
|
||
<span style="display:flex;gap:12px"><span class="n" style="color:{{if ge $d.Step 2}}#e8a33d{{else}}#8e8879{{end}}">02</span><span>Связь с ядром депозитария{{if gt $d.Step 2}} — готово{{end}}</span></span>
|
||
<span style="display:flex;gap:12px"><span class="n" style="color:{{if ge $d.Step 3}}#e8a33d{{else}}#8e8879{{end}}">03</span><span>Администратор кабинета</span></span>
|
||
</div>
|
||
</div>
|
||
<p class="mut" style="color:#8e8879;margin:0">Кабинет ничего не хранит у себя: пользователи, пароли и данные клиентов живут в ядре. Здесь сохраняется только способ связи с ним.</p>
|
||
</section>
|
||
<section class="form">
|
||
<form method="post" action="/install" style="max-width:520px"><input type="hidden" name="csrf" value="{{$.CSRF}}">
|
||
{{if $d.Error}}<div class="msg err">{{$d.Error}}</div>{{end}}
|
||
{{if $d.Notice}}<div class="msg ok">{{$d.Notice}}</div>{{end}}
|
||
|
||
{{if lt $d.Step 3}}
|
||
<h1 style="margin:0">Сервер и связь с ядром</h1>
|
||
<div style="display:grid;gap:8px">
|
||
{{range $d.Checks}}<div style="display:flex;gap:10px;align-items:flex-start;font-size:14px"><span style="color:{{if .OK}}{{if .Warn}}#e8a33d{{else}}#3f8f5a{{end}}{{else}}#c0392b{{end}};font-weight:700">{{if .OK}}✓{{else}}✗{{end}}</span><span><b>{{.Name}}.</b> {{.Note}}</span></div>{{end}}
|
||
</div>
|
||
|
||
{{if $d.Waiting}}
|
||
<meta http-equiv="refresh" content="5">
|
||
<div class="msg ok">Настройки сохранены. Ждём, когда ядро подключится к кабинету по адресу <b>{{$d.Host}}</b>… Страница обновляется сама.</div>
|
||
<p class="mut" style="margin:0">В настройках ядра (раздел «Кабинет клиента») выберите «Ядро подключается к кабинету» и укажите этот адрес. Ключ должен совпадать.</p>
|
||
<input type="hidden" name="step" value="wait">
|
||
{{else}}
|
||
<input type="hidden" name="step" value="connect">
|
||
<div>
|
||
<label>Заголовок кабинета</label>
|
||
<input name="title" value="{{$d.Cfg.Title}}" placeholder="Депозитарий · личный кабинет">
|
||
</div>
|
||
<div>
|
||
<label>Как связаться с ядром</label>
|
||
<label style="display:flex;gap:8px;align-items:flex-start;text-transform:none;letter-spacing:0;font-family:inherit;font-size:14px;color:var(--ink);margin-top:6px"><input type="radio" name="mode" value="direct" {{if not $d.Cfg.Agent}}checked{{end}}> <span><b>Кабинет обращается к ядру</b> — у ядра есть адрес, доступный с этого сервера.</span></label>
|
||
<label style="display:flex;gap:8px;align-items:flex-start;text-transform:none;letter-spacing:0;font-family:inherit;font-size:14px;color:var(--ink);margin-top:6px"><input type="radio" name="mode" value="agent" {{if $d.Cfg.Agent}}checked{{end}}> <span><b>Ядро подключается к кабинету</b> — у ядра нет адреса в интернете; оно само дозвонится сюда.</span></label>
|
||
</div>
|
||
<div>
|
||
<label>Где собираются страницы</label>
|
||
<label style="display:flex;gap:8px;align-items:flex-start;text-transform:none;letter-spacing:0;font-family:inherit;font-size:14px;color:var(--ink);margin-top:6px"><input type="radio" name="render" value="core" {{if $d.Cfg.Relay}}checked{{end}}> <span><b>В ядре</b> — здесь только передача; новые разделы появляются обновлением ядра.</span></label>
|
||
<label style="display:flex;gap:8px;align-items:flex-start;text-transform:none;letter-spacing:0;font-family:inherit;font-size:14px;color:var(--ink);margin-top:6px"><input type="radio" name="render" value="local" {{if not $d.Cfg.Relay}}checked{{end}}> <span><b>Здесь</b> — эта программа собирает страницы сама из данных ядра.</span></label>
|
||
</div>
|
||
<div>
|
||
<label for="bj_url">Адрес ядра <span style="text-transform:none;letter-spacing:0">(только для первого способа)</span></label>
|
||
<input id="bj_url" name="bj_url" value="{{$d.Cfg.BJURL}}" placeholder="https://bj.депозитарий.ru" autocomplete="off">
|
||
</div>
|
||
<div>
|
||
<label for="bj_key">Ключ доступа модуля</label>
|
||
<input id="bj_key" name="bj_key" type="password" value="{{$d.Cfg.BJKey}}" autocomplete="off" placeholder="lk-…">
|
||
<p class="mut" style="margin:6px 0 0">Создаётся в настройках ядра: «Кабинет клиента» → «Создать ключ».</p>
|
||
</div>
|
||
<button class="btn main" type="submit">Проверить связь и сохранить</button>
|
||
{{end}}
|
||
|
||
{{else}}
|
||
<h1 style="margin:0">Администратор кабинета</h1>
|
||
<p class="mut" style="margin:0">Сотрудник депозитария, который входит в кабинет, видит список клиентов и может открыть кабинет клиента на просмотр. Учётная запись заводится в ядре. Если администратор уже есть — шаг можно пропустить.</p>
|
||
<input type="hidden" name="step" value="admin">
|
||
<div><label for="login">Логин</label><input id="login" name="login" autocomplete="off"></div>
|
||
<div><label for="full_name">Имя сотрудника</label><input id="full_name" name="full_name" autocomplete="off"></div>
|
||
<div><label for="email">Электронная почта <span style="text-transform:none;letter-spacing:0">(письма пока не отправляются)</span></label><input id="email" name="email" type="email" autocomplete="off"></div>
|
||
<div><label for="password">Пароль <span style="text-transform:none;letter-spacing:0">(от 10 символов, буквы и цифра)</span></label><input id="password" name="password" type="password" autocomplete="new-password"></div>
|
||
<div><label for="password2">Пароль ещё раз</label><input id="password2" name="password2" type="password" autocomplete="new-password"></div>
|
||
<div style="display:flex;gap:10px;flex-wrap:wrap">
|
||
<button class="btn main" type="submit">Создать и завершить</button>
|
||
<button class="btn" type="submit" name="skip" value="1">Пропустить</button>
|
||
</div>
|
||
{{end}}
|
||
</form>
|
||
</section>
|
||
</div>
|
||
</body></html>{{end}}
|
||
|
||
{{define "install_done"}}{{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">Установка кабинета</span></span><div><h1>Кабинет установлен</h1></div><p class="mut" style="color:#8e8879;margin:0">Настройки — в файле <span class="n">{{.File}}</span>. Чтобы пройти установку заново, удалите его.</p></section>
|
||
<section class="form"><form action="/login" method="get" style="max-width:420px"><h1 style="margin:0">Готово</h1><p class="mut" style="margin:0">Клиенты входят по логину и паролю, которые выдаёт депозитарий. Учётные записи заводятся в настройках ядра, раздел «Кабинет клиента».</p><button class="btn main" type="submit">Перейти ко входу</button></form></section></div>
|
||
</body></html>{{end}}
|
||
|
||
{{define "installed"}}{{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">Установка кабинета</span></span><div><h1>Кабинет уже настроен</h1></div></section>
|
||
<section class="form"><form action="/login" method="get" style="max-width:420px"><p class="mut" style="margin:0">Установщик закрыт: настройки сохранены в <span class="n">{{.File}}</span>. Чтобы изменить их, отредактируйте файл или удалите его и откройте эту страницу снова.</p><button class="btn main" type="submit">Перейти ко входу</button></form></section></div>
|
||
</body></html>{{end}}
|
||
|
||
{{define "relay_error"}}{{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><p class="mut" style="color:#8e8879;margin:0">Страницы кабинета собирает учётная система депозитария; сейчас связи с ней нет.</p></section>
|
||
<section class="form"><form action="/" method="get" style="max-width:420px"><div class="msg err">{{.Error}}</div><p class="mut" style="margin:0">Попробуйте обновить страницу через минуту. Если не помогает — сообщите в депозитарий.</p><button class="btn main" type="submit">Обновить</button></form></section></div>
|
||
</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><form method="post" action="/logout" style="margin:0"><input type="hidden" name="csrf" value="{{.CSRF}}"><button class="lnk" type="submit">выйти</button></form></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}}"><input type="hidden" name="csrf" value="{{$.CSRF}}"><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}}"><input type="hidden" name="csrf" value="{{$.CSRF}}"><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="/" 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}}</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}}
|
||
{{if $s.QualOnly}}<div class="row"><span>Доступ</span><span>только для квалифицированных инвесторов</span></div>{{end}}
|
||
{{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">{{len .Rows}}</div>
|
||
<div class="n" style="font-size:13px;color:var(--dim);margin-top:6px">за период {{fmtdate .From}} — {{fmtdate .To}}</div>
|
||
</div>
|
||
<div class="stats">
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Покупки</span><span class="v">{{fmtmoney .BuyRUB}} ₽</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Продажи</span><span class="v">{{fmtmoney .SellRUB}} ₽</span></span>
|
||
<span class="stat"><span class="lbl" style="color:var(--dim)">Выплаты</span><span class="v">{{fmtmoney .IncomeRUB}} ₽</span></span>
|
||
</div>
|
||
</div></section>
|
||
<main>
|
||
{{if .Error}}<div class="msg err">{{.Error}}</div>{{end}}
|
||
{{if .Note}}<div class="msg ok">{{.Note}}</div>{{end}}
|
||
<form method="get" action="/operations" class="tabform" style="margin:0 0 18px">
|
||
<div><label for="from">Период с</label><input id="from" type="date" name="from" value="{{.From}}"></div>
|
||
<div><label for="to">по</label><input id="to" type="date" name="to" value="{{.To}}"></div>
|
||
<button class="btn sm" type="submit" style="align-self:flex-end">Показать</button>
|
||
</form>
|
||
{{if .Rows}}<div class="tw"><table>
|
||
<thead><tr><th>Дата</th><th>Операция</th><th>Инструмент</th><th class="num">Кол-во</th><th class="num">Цена</th><th class="num">Сумма</th></tr></thead>
|
||
<tbody>{{range .Rows}}<tr>
|
||
<td class="n">{{.Date}}</td>
|
||
<td><span class="chip {{if eq .Side "in"}}ok{{else}}go{{end}}">{{.Kind}}</span></td>
|
||
<td>{{if .ISIN}}<a href="/security?isin={{.ISIN}}" style="text-decoration:none;font-weight:600">{{.Name}}</a><div class="n" style="font-size:12px;color:var(--mut)">{{.ISIN}}</div>{{else}}{{.Name}}{{end}}</td>
|
||
<td class="num">{{if ne .Quantity 0.0}}{{fmtnum .Quantity}}{{else}}—{{end}}</td>
|
||
<td class="num">{{if gt .Price 0.0}}{{fmtprice .Price}}{{else}}—{{end}}</td>
|
||
<td class="num"><span class="{{if eq .Side "in"}}pos{{else}}neg{{end}}">{{if eq .Side "in"}}+{{else}}−{{end}}{{fmtmoney .AmountRUB}} ₽</span>{{if and .Currency (ne .Currency "SUR")}}<div style="font-size:11.5px;color:var(--mut)">{{fmtmoney .Amount}} {{.Currency}}</div>{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
{{else}}<p class="empty">За выбранный период операций нет.</p>{{end}}
|
||
</main></body></html>{{end}}
|
||
|
||
{{define "documents"}}{{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">{{len .Ready}}</div>
|
||
<div class="n" style="font-size:13px;color:var(--dim);margin-top:6px">готовых документов{{if .Requests}} · заявок {{len .Requests}}{{end}}</div></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">
|
||
<h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Готовые документы</h2>
|
||
{{if .Ready}}<div class="tw"><table>
|
||
<thead><tr><th>Дата</th><th>Документ</th><th>Период</th><th>Счёт депо</th><th>Состояние</th></tr></thead>
|
||
<tbody>{{range .Ready}}<tr>
|
||
<td class="n">{{.Date}}</td>
|
||
<td>{{.Title}}{{with .Note}}<div class="n mut">№ {{.}}</div>{{end}}</td>
|
||
<td class="n">{{if .Period}}{{.Period}}{{else}}—{{end}}</td>
|
||
<td class="n" style="font-size:13px">{{.Account}}</td>
|
||
<td>{{if eq .State "исполнено"}}<span class="chip ok">готов</span>{{else}}<span class="chip go">{{.State}}</span>{{end}}</td>
|
||
</tr>{{end}}</tbody></table></div>
|
||
<p class="mut" style="margin-top:14px">Файлы документов выдаёт депозитарий: бумажный экземпляр в офисе или по электронной почте. В кабинете виден факт подготовки и период документа.</p>
|
||
{{else}}<p class="empty">Готовых документов пока нет.</p>{{end}}
|
||
</section>
|
||
|
||
<div class="grid2">
|
||
<section class="sec">
|
||
<h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Мои заявки</h2>
|
||
{{if .Requests}}<div class="rows">{{range .Requests}}
|
||
<div class="row"><span>{{.Title}}<span class="n mut" style="display:block">{{.Date}}{{if .Period}} · {{.Period}}{{end}}</span>{{with .Note}}<span class="mut" style="display:block">{{.}}</span>{{end}}</span>
|
||
<span class="chip {{if eq .State "готов"}}ok{{else if eq .State "отклонён"}}err{{else}}go{{end}}" style="align-self:center">{{.State}}</span></div>
|
||
{{end}}</div>
|
||
{{else}}<p class="empty">Заявок пока нет.</p>{{end}}
|
||
</section>
|
||
|
||
<section class="sec">
|
||
<h2 class="lbl" style="font-size:12px;letter-spacing:.14em">Заказать документ</h2>
|
||
<form method="post" action="/documents" class="sheet" style="display:grid;gap:12px;padding:18px 20px"><input type="hidden" name="csrf" value="{{$.CSRF}}">
|
||
<div><label for="kind">Вид документа</label>
|
||
<select id="kind" name="kind">{{range .Kinds}}<option>{{.}}</option>{{end}}</select></div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
|
||
<div><label for="from">Период с</label><input id="from" type="date" name="from" value="{{.From}}"></div>
|
||
<div><label for="to">по</label><input id="to" type="date" name="to" value="{{.To}}"></div>
|
||
</div>
|
||
<div><label for="comment">Комментарий</label><input id="comment" name="comment" placeholder="необязательно"></div>
|
||
<button class="btn main" type="submit">Отправить заявку</button>
|
||
<p class="mut" style="margin:0">Заявка уходит сотруднику депозитария. Срок подготовки и стоимость — по тарифам депозитария; готовый документ появится в списке слева.</p>
|
||
</form>
|
||
</section>
|
||
</div>
|
||
</main></body></html>{{end}}
|
||
`
|