1С: проверка поручений МНП перед отправкой в НРД

Часть модуля onec-back, по умолчанию выключена, включается в настройках
(«Проверка поручений в 1С»). Исходящий перевод после блокировки бумаг в
МНП ждёт решения 1С и в НРД не уходит, пока 1С не подтвердит каждое
поручение; при отказе bj снимает поручения в QUIK с причиной из 1С и
отклоняет перевод в журнале и кабинете клиента.

- onec.instr / onec.instr_queue / onec.set_decision в базе bj: 1С читает
  очередь и пишет решение под логином onec_bj (только эти права); чтение
  очереди отмечает связь с 1С.
- bj-onec-admin (root, через bj-keymedia): логин onec_bj, вход только с
  IP 1С по TLS (pg_hba.conf), listen_addresses, ufw; revoke закрывает вход.
- onecLoop: отправка подтверждённых, отказы, снятие клиентом в QUIK,
  поручения из терминала, сроки решения и потеря связи — оповещения.
- Карточка заявки: блок «Проверка 1С», решение оператора вместо 1С.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
fontvielle
2026-10-22 20:13:59 +03:00
parent e51962c5bb
commit 21d5f33394
13 changed files with 1598 additions and 5 deletions
@@ -45,7 +45,10 @@
<b>Перевод не доставлен в НРД</b> — {{sendFailText .}}.<br>
<span class="muted">Попытка {{.At.Format "02.01.2006 15:04:05"}}{{if .PackageID}}, пакет {{.PackageID}}{{end}}{{if gt .Attempts 1}}, попыток: {{.Attempts}}{{end}}. Отправка в DEPO не отмечена, блокировка в QUIK МНП сохраняется. {{if .GaveUp}}Сутки прошли — автоповтор остановлен: отправьте вручную или удалите заявку.{{else}}Ничего нажимать не нужно: как только НРД снова ответит, bj сам отправит тот же запрос с тем же GUID (повтор — в течение суток). Кнопка «Отправить повторно» — если нужно попробовать прямо сейчас.{{end}}</span>
</div>{{end}}
{{if and .Claim.RequestRawXML (not .SentToNSD) (not .Fail)}}<div class="card" style="background:var(--warn-weak,#fbf3d9);margin:8px 0">
{{if and .OneCWaiting (not .SentToNSD)}}<div class="card" style="background:var(--warn-weak,#fbf3d9);margin:8px 0">
<b>⏳ Перевод ждёт решения 1С.</b><br>
<span class="muted">Бумаги заблокированы в QUIK МНП. В НРД перевод уйдёт сам, как только 1С подтвердит поручения; при отказе bj снимет поручения в QUIK с причиной из 1С. Решить вместо 1С — в блоке «Проверка 1С» ниже.</span>
{{else if and .Claim.RequestRawXML (not .SentToNSD) (not .Fail)}}<div class="card" style="background:var(--warn-weak,#fbf3d9);margin:8px 0">
<b>⛔ В НРД ничего не отправлено — отмена до отправки.</b><br>
<span class="muted">Пакет <code>M2MTransferRequest</code> сформирован, но в НРД <b>не направлялся</b>: заявка остановлена на проверке перед отправкой{{if .QuikFailReply}} — в QUIK МНП не удалось заблокировать бумаги{{if .QuikFailISIN}} ({{.QuikFailISIN}}){{end}}{{if .QuikNoSec}}: на счёте клиента нет/недостаточно бумаг для списания{{end}}{{end}}. Регистрация «Вывод ЦБ (M2M)» и блокировка идут в QUIK МНП <u>до</u> отправки в НРД, поэтому поручение 16 и запрос перевода в НРД не создавались. Устраните причину{{if .QuikNoSec}} (зачислите бумаги на счёт клиента){{end}} и отправьте заявку повторно.{{if .QuikFailReply}}<br>Ответ модуля QUIK: <code>{{.QuikFailReply}}</code>{{end}}</span>
</div>{{end}}
@@ -114,6 +117,32 @@
{{/* ===== QUIK МНП: поручение «Вывод ЦБ (M2M)» ===== */}}
{{$back := printf "/admin/claims/%s" .Claim.ID}}
{{if .OneC}}
<div class="card">
<h2 style="margin:0">Проверка 1С</h2>
{{range .OneC}}
<div style="border:1px solid var(--border);border-radius:10px;padding:10px 12px;margin-top:10px;font-size:13.5px">
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap">
<b><code>{{.ISIN}}</code>{{if .Qty}} × {{.Qty}}{{end}}</b>
<span class="muted" style="font-size:12px">{{if .InstrNo}}поручение МНП <b>№{{.InstrNo}}</b>{{else}}поручения МНП нет — 1С проверить нечего{{end}}</span>
<span class="qk-state {{if eq .Status "CONFIRMED"}}signed{{else if eq .Status "REJECTED"}}error{{else if eq .Status "CANCELLED"}}denied{{else}}registered{{end}}">{{.StatusRu}}</span>
<span class="muted" style="font-size:12px">bj: {{.BJStateRu}}</span>
</div>
{{if .Reason}}<div style="font-size:12.5px;margin-top:4px;color:#b3261e">Причина: {{.Reason}}</div>{{end}}
{{if .CheckedBy}}<div class="muted" style="font-size:12px;margin-top:2px">{{.CheckedBy}}{{if not .CheckedAt.IsZero}}, {{msk .CheckedAt}}{{end}}{{if .OneCDoc}} · {{.OneCDoc}}{{end}}</div>{{end}}
</div>
{{end}}
{{if and .OneCWaiting (can "operate")}}
<form method="post" action="/admin/onec/decide" style="margin:10px 0 0;display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="hidden" name="guid" value="{{.Claim.M2MGUID}}"><input type="hidden" name="back" value="{{$back}}">
<button class="btn btn-ok" name="decision" value="confirm" style="font-size:12px" onclick="return confirm('Подтвердить вместо 1С? Перевод уйдёт в НРД.')">✓ Подтвердить вместо 1С</button>
<input type="text" name="reason" maxlength="255" placeholder="причина отказа — её увидит клиент" style="font-size:12px;width:260px">
<button class="btn" name="decision" value="reject" style="font-size:12px" onclick="return confirm('Отклонить перевод? Поручения в QUIK будут сняты, клиент увидит причину.')">✕ Отклонить</button>
</form>
<p class="muted" style="font-size:12px;margin:6px 0 0">Решение оператора окончательное: 1С по этому поручению уже ничего не изменит.</p>
{{end}}
</div>
{{end}}
<div class="card">
<style>
.qk-steps{display:flex;gap:8px;flex-wrap:wrap;margin:8px 0}