From d2024b44ca90eaca5f1c4e0cafc85fa4581a71e0 Mon Sep 17 00:00:00 2001 From: zuevav Date: Fri, 18 Sep 2026 14:39:24 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D1=83=D1=87=D0=BD=D0=BE=D0=B9=20=D0=B2?= =?UTF-8?q?=D0=B2=D0=BE=D0=B4:=20=D0=B1=D0=B5=D0=B7=20=D0=BE=D0=B1=D1=8F?= =?UTF-8?q?=D0=B7=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D1=85=20=D0=B3?= =?UTF-8?q?=D0=B0=D0=BB=D0=BE=D0=BA,=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=87=D0=B5=D1=80=D0=BD=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0,=20=D0=B7=D0=B0=D0=BF=D1=80=D0=B5=D1=82=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BB=D0=B8=D1=87=D0=B5=D1=81=D1=82=D0=B2=D0=B0=20=D0=B1?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D1=88=D0=B5=20=D0=BE=D1=81=D1=82=D0=B0=D1=82?= =?UTF-8?q?=D0=BA=D0=B0;=20=D0=BE=D1=82=D0=BA=D0=B0=D0=B7=20=D0=9D=D0=A0?= =?UTF-8?q?=D0=94=20=D0=BF=D0=BE=20=D0=BF=D0=BE=D1=80=D1=83=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8E=20=E2=80=94=20=D0=BA=D1=80=D0=B0=D1=81=D0=BD?= =?UTF-8?q?=D1=8B=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ручных отметок перед отправкой нет: автор виден по учётной записи, клиент/счёт/остаток/ бумага проверены сверкой с DEPO при сохранении; блокировка в QUIK — необязательно, по умолчанию не требуется (модуль неторговых поручений сейчас не используется) - «Удалить черновик» в карточке (POST /admin/manual/{id}/delete), только для черновиков - количество не больше остатка на торговом разделе: проверка в форме (по выбранной бумаге) и на сервере при сохранении (по счёту DEPO) — поручение с превышением не сохраняется - отчёт MS010 с отказом: orderResponse.Rejected, OutgoingDeal.Rejected; шаг «Операция завершена отказом» и бейдж — красные в карточках исходящего и входящего; онлайн-статус «поручение 16 завершено отказом НРД» Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL --- internal/lkgateway/incoming_handlers.go | 8 ++- internal/lkgateway/incoming_order.go | 12 +++- internal/lkgateway/livestatus.go | 3 + internal/lkgateway/manual.go | 27 ++++++--- internal/lkgateway/manual_depo.go | 60 +++++++++++++++++++ internal/lkgateway/manual_handlers.go | 17 ++++++ internal/lkgateway/manual_test.go | 35 ++++++----- internal/lkgateway/outgoing_order.go | 2 + .../lkgateway/web/templates/admin_claim.html | 15 +++-- .../web/templates/admin_incoming_detail.html | 14 +++-- .../lkgateway/web/templates/admin_manual.html | 32 +++++----- 11 files changed, 171 insertions(+), 54 deletions(-) diff --git a/internal/lkgateway/incoming_handlers.go b/internal/lkgateway/incoming_handlers.go index d9a0d48..62f3a97 100644 --- a/internal/lkgateway/incoming_handlers.go +++ b/internal/lkgateway/incoming_handlers.go @@ -165,20 +165,21 @@ func registerIncoming(mux *http.ServeMux, a *admin, deps incomingDeps) { return } var orderResps []orderResponse - var orderDone bool + var orderDone, orderRejected bool var orderDoneAt string if it.OrderSubmitted { orderResps = findOrderResponses(it.OrderContrDID) for _, rp := range orderResps { - if rp.Done { // отчёт MS010 — операция проведена + if rp.Done { // отчёт MS010 — операция проведена или завершена отказом orderDone = true orderDoneAt = rp.Time + orderRejected = orderRejected || rp.Rejected } } } rejected, codes := incomingRejectedAll(it) data := incomingDetailData{page: nowPage("Входящий перевод", "incoming"), It: it, Flash: r.URL.Query().Get("flash"), - OrderResponses: orderResps, OrderDone: orderDone, OrderDoneAt: orderDoneAt, DraftReceived: draftReceivedTime(it.GUID), + OrderResponses: orderResps, OrderDone: orderDone, OrderRejected: orderRejected, OrderDoneAt: orderDoneAt, DraftReceived: draftReceivedTime(it.GUID), Quik: globalQuikLinks.ForGUID(it.GUID), QuikOn: quikActive(deps.rc.Snapshot().Quik), Watch: globalOrderWatch.Get(it.GUID), DepoL: globalDepoLinks.Get(it.GUID), Rejected: rejected, RejectCodes: codes, AutoMode: deps.rc.Snapshot().M2MAuto.Enabled} // Сверка выполняется при открытии карточки; итог сохраняем для бейджа в списке. @@ -236,6 +237,7 @@ type incomingDetailData struct { Flash string OrderResponses []orderResponse OrderDone bool + OrderRejected bool // MS010 с отказом OrderDoneAt string DraftReceived string Depo depoResult // сверка с DEPO — рекомендация по ответу, ничего не отправляется diff --git a/internal/lkgateway/incoming_order.go b/internal/lkgateway/incoming_order.go index c0a030a..dbdd74f 100644 --- a/internal/lkgateway/incoming_order.go +++ b/internal/lkgateway/incoming_order.go @@ -198,7 +198,9 @@ type orderResponse struct { Kind string // человекочитаемый тип (квитанция / принятие / отчёт MS010) Time string Text string // краткое содержимое - Done bool // true — это отчёт MS010 (операция проведена) + Done bool // true — это отчёт MS010 (операция проведена или завершена отказом) + // Rejected — MS010 с отказом: операция завершилась, но не исполнена. Показывается красным. + Rejected bool } // findOrderResponses ищет в INBOX ИШ ответы НРД по нашему поручению (по contr_d_id). @@ -222,7 +224,7 @@ func findOrderResponses(contrDID string) []orderResponse { } kind, done := orderRespKind(dir, body) tmp = append(tmp, rr{orderResponse{ - Pkg: dir, Kind: kind, Done: done, + Pkg: dir, Kind: kind, Done: done, Rejected: done && respTextRejected(body), Time: mt.In(time.FixedZone("МСК", 3*3600)).Format("02.01.2006 15:04:05"), Text: orderRespSummary(body), }, mt}) @@ -453,3 +455,9 @@ func orderRespSummary(body string) string { } return txt } + +// respTextRejected — в отчёте НРД сказано об отказе, а не об исполнении. +func respTextRejected(body string) bool { + low := strings.ToLower(body) + return strings.Contains(low, "отказ") || strings.Contains(low, "reject") || strings.Contains(low, "не исполнен") +} diff --git a/internal/lkgateway/livestatus.go b/internal/lkgateway/livestatus.go index 0e0ab00..9de26a0 100644 --- a/internal/lkgateway/livestatus.go +++ b/internal/lkgateway/livestatus.go @@ -58,6 +58,7 @@ func liveOutgoing(c ClaimView) LiveStatus { cp := *d if cp.Submitted && len(cp.Responses) == 0 && !cp.Done { cp.Responses, cp.Done = outgoingResponses(cp.ContrDIDs) // ответы ищем только для этой, живой сделки + _, cp.Rejected, _ = orderRespVerdict(cp.Responses) } depo = &cp } @@ -89,6 +90,8 @@ func liveOutgoing(c ClaimView) LiveStatus { return LiveStatus{Text: "принята контрагентом, ожидает черновика поручения 16 от НРД", Kind: "wait", Active: true} case !depo.Submitted: return LiveStatus{Text: "черновик поручения 16 получен, ожидает подачи в НРД", Kind: "warn", Active: true} + case depo.Done && depo.Rejected: + return LiveStatus{Text: "поручение 16 завершено отказом НРД (MS010)", Kind: "bad"} case depo.Done: return LiveStatus{Text: "поручение 16 исполнено: MS010 получен", Kind: "ok"} case watch != nil: diff --git a/internal/lkgateway/manual.go b/internal/lkgateway/manual.go index 4afa6d9..5e8c5ad 100644 --- a/internal/lkgateway/manual.go +++ b/internal/lkgateway/manual.go @@ -266,6 +266,21 @@ func (s *manualStore) Create(o *ManualOrder) error { } // Update — изменение под замком: fn получает текущую запись. +// Delete — удалить черновик. Отправленные и отклонённые поручения не удаляются: они след. +func (s *manualStore) Delete(id int) error { + s.mu.Lock() + defer s.mu.Unlock() + o, ok := s.items[id] + if !ok { + return fmt.Errorf("поручение не найдено") + } + if o.Status != "draft" { + return fmt.Errorf("удалить можно только черновик, а поручение уже %s", manualStatusRu(o.Status)) + } + delete(s.items, id) + return s.saveLocked() +} + func (s *manualStore) Update(id int, fn func(o *ManualOrder) error) error { s.mu.Lock() defer s.mu.Unlock() @@ -382,11 +397,8 @@ func manualSendBlockers(rc *RuntimeConfig, o *ManualOrder) []string { b = append(b, "сверка с DEPO не пройдена: "+o.Depo.Summary) } } - for _, c := range manualPreChecks { - if !o.Checks[c.Key].Done { - b = append(b, "нет отметки: "+c.Title) - } - } + // Ручных отметок перед отправкой нет: кто создал поручение — видно по учётной записи, + // а клиент, счёт, остаток и бумага проверены сверкой с учётной системой при сохранении. switch o.QuikMode { case "auto": if !manualQuikAutoAvailable(rc) { @@ -396,9 +408,8 @@ func manualSendBlockers(rc *RuntimeConfig, o *ManualOrder) []string { if strings.TrimSpace(o.QuikInstr) == "" { b = append(b, "укажите номер поручения на блокировку в торговой системе") } - case "none": - default: - b = append(b, "не выбрано, как заблокированы бумаги в торговой системе") + case "none", "": + // модуль неторговых поручений QUIK не используется — блокировка не требуется } return b } diff --git a/internal/lkgateway/manual_depo.go b/internal/lkgateway/manual_depo.go index 5359c7b..df5e077 100644 --- a/internal/lkgateway/manual_depo.go +++ b/internal/lkgateway/manual_depo.go @@ -6,6 +6,7 @@ package lkgateway import ( "database/sql" "encoding/json" + "fmt" "net/http" "strconv" "strings" @@ -195,3 +196,62 @@ func registerManualDepo(mux *http.ServeMux, rc *RuntimeConfig) { reply(w, items, errS) }) } + +// manualDepoClientByAccount — OD_CLIENTS.ID открытого торгового счёта по его номеру. +func manualDepoClientByAccount(rc *RuntimeConfig, number string) (int64, string) { + number = strings.TrimSpace(number) + if number == "" || !manualDepoConfigured(rc) { + return 0, "" + } + db, ctx, cancel, errS := depoOpen(rc.Snapshot().Fancy) + if errS != "" { + return 0, errS + } + defer cancel() + defer db.Close() + var id int64 + err := db.QueryRowContext(ctx, `SELECT TOP 1 ID FROM OD_CLIENTS WHERE NUMBER=@n AND STATE=0 AND D_CLOSE IS NULL ORDER BY ID`, + sql.Named("n", number)).Scan(&id) + if err != nil { + return 0, "" + } + return id, "" +} + +// manualQtyOverAvailable — количество в поручении больше остатка на торговом разделе. +// Пусто — всё в порядке или остаток проверить нельзя (DEPO не подключена, счёт не найден): +// тогда решает сверка при сохранении. Смысл: поручение на 111 бумаг при 110 на счёте не +// должно даже сохраниться — дальше по цепочке ему всё равно идти некуда. +func manualQtyOverAvailable(rc *RuntimeConfig, depoClient string, o *ManualOrder) string { + if !manualDepoConfigured(rc) || o == nil { + return "" + } + id, _ := strconv.ParseInt(strings.TrimSpace(depoClient), 10, 64) + if id <= 0 { + id, _ = manualDepoClientByAccount(rc, o.Account) + } + if id <= 0 { + return "" + } + pos, errS := manualDepoPositions(rc, id) + if errS != "" { + return "" + } + for _, sec := range o.Securities { + isin := strings.ToUpper(strings.TrimSpace(sec.ISIN)) + if isin == "" { + continue + } + q, err := strconv.ParseFloat(strings.ReplaceAll(strings.TrimSpace(sec.Qty), ",", "."), 64) + if err != nil { + continue + } + for _, p := range pos { + if p.ISIN == isin && q > p.Qty+1e-9 { + return fmt.Sprintf("По бумаге %s на торговом разделе счёта %s доступно %s шт., а в поручении %s — сохранить нельзя: укажите не больше доступного.", + isin, o.Account, strconv.FormatFloat(p.Qty, 'f', -1, 64), strings.TrimSpace(sec.Qty)) + } + } + } + return "" +} diff --git a/internal/lkgateway/manual_handlers.go b/internal/lkgateway/manual_handlers.go index e63a760..d58e52e 100644 --- a/internal/lkgateway/manual_handlers.go +++ b/internal/lkgateway/manual_handlers.go @@ -127,6 +127,16 @@ func registerManual(mux *http.ServeMux, rc *RuntimeConfig, svc *Service) { id, _ := strconv.Atoi(r.FormValue("id")) actor := manualActor(w) msg := "Поручение сохранено" + if over := manualQtyOverAvailable(rc, r.FormValue("depo_client"), in); over != "" { + if id == 0 { + d := base("Новое поручение") + d.Mode, d.O, d.Handbook, d.Flash = "edit", in, handbookParticipants(), over + render(w, tpl, d) + return + } + back(w, r, fmt.Sprintf("/admin/manual/%d", id), over, "#mf") + return + } if id == 0 { in.Status, in.CreatedBy = "draft", actor if err := globalManual.Create(in); err != nil { @@ -265,6 +275,13 @@ func registerManual(mux *http.ServeMux, rc *RuntimeConfig, svc *Service) { }) log.Printf("lk-gateway: ручной ввод %s (%s): отправка — %s", o.Number(), o.FullName(), msg) back(w, r, card, msg, "") + case "delete": + if err := globalManual.Delete(id); err != nil { + back(w, r, card, err.Error(), "") + return + } + back(w, r, "/admin/manual", fmt.Sprintf("Черновик %d удалён", id), "") + return case "decline": reason := strings.TrimSpace(r.FormValue("reason")) err := globalManual.Update(id, func(o *ManualOrder) error { diff --git a/internal/lkgateway/manual_test.go b/internal/lkgateway/manual_test.go index b6c77d2..26173f6 100644 --- a/internal/lkgateway/manual_test.go +++ b/internal/lkgateway/manual_test.go @@ -25,19 +25,16 @@ func TestManualSendBlockers(t *testing.T) { t.Fatal(err) } o := manualTestOrder() - b := manualSendBlockers(rc, o) - if len(b) != len(manualPreChecks)+1 { // все отметки + способ блокировки в QUIK - t.Fatalf("без отметок: %v", b) - } - o.Checks = map[string]ManualMark{} - for _, c := range manualPreChecks { - o.Checks[c.Key] = ManualMark{Done: true} + // Ручных отметок нет, блокировка в QUIK по умолчанию не требуется: черновик готов сразу. + if b := manualSendBlockers(rc, o); len(b) != 0 { + t.Fatalf("готовый черновик: %v", b) } o.QuikMode = "manual" - if b = manualSendBlockers(rc, o); len(b) != 1 || !strings.Contains(b[0], "номер поручения") { + if b := manualSendBlockers(rc, o); len(b) != 1 || !strings.Contains(b[0], "номер поручения") { t.Fatalf("ручная блокировка без номера: %v", b) } o.QuikInstr = "123" + var b []string if b = manualSendBlockers(rc, o); len(b) != 0 { t.Fatalf("всё заполнено: %v", b) } @@ -113,19 +110,16 @@ func TestManualPages(t *testing.T) { if o.DocSeries != "0000" || o.Account != "СЧЁТ-1" || len(o.Securities) != 1 || o.Securities[0].ISIN != "RU0009029540" { t.Fatalf("данные: %+v", o) } - if rec = do("GET", "/admin/manual/1", nil); rec.Code != 200 || !strings.Contains(rec.Body.String(), "Отправить пока нельзя") { + if rec = do("GET", "/admin/manual/1", nil); rec.Code != 200 || !strings.Contains(rec.Body.String(), "Сверка пройдена, поручение готово") { t.Fatalf("карточка: %d %s", rec.Code, rec.Body.String()) } - chk := url.Values{"quik_mode": {"none"}} - for _, c := range manualPreChecks { - chk.Set("check_"+c.Key, "1") - } + chk := url.Values{"quik_mode": {"manual"}, "quik_instr": {"77"}} do("POST", "/admin/manual/1/checks", chk) o, _ = globalManual.Get(1) - if len(manualSendBlockers(rc, o)) != 0 || o.Checks["paper"].At.IsZero() { - t.Fatalf("отметки: %+v %v", o.Checks, manualSendBlockers(rc, o)) + if len(manualSendBlockers(rc, o)) != 0 || o.QuikMode != "manual" { + t.Fatalf("блокировка вручную: %+v %v", o.QuikMode, manualSendBlockers(rc, o)) } - if rec = do("GET", "/admin/manual/1", nil); !strings.Contains(rec.Body.String(), "Все проверки и отметки пройдены") { + if rec = do("GET", "/admin/manual/1", nil); !strings.Contains(rec.Body.String(), "Сверка пройдена, поручение готово") { t.Fatalf("готово к отправке: %s", rec.Body.String()) } // Изменение данных сбрасывает отметки. @@ -135,6 +129,15 @@ func TestManualPages(t *testing.T) { if o, _ = globalManual.Get(1); len(o.Checks) != 0 { t.Fatalf("отметки должны сброситься: %+v", o.Checks) } + // Черновик можно удалить; отклонённое — нет. + do("POST", "/admin/manual/save", url.Values{"paper_date": {"2026-09-17"}, "last_name": {"Пётр"}, "first_name": {"П"}, "birth_date": {"1980-01-02"}, + "doc_type": {"21"}, "doc_series": {"0000"}, "doc_number": {"000001"}, "account": {"счёт-2"}, "recv_inn": {"7702165310"}, "sec_isin": {"ru0009029540"}, "sec_qty": {"1"}, "do": {"save"}}) + if rec = do("POST", "/admin/manual/2/delete", nil); rec.Code != http.StatusSeeOther || !strings.Contains(rec.Header().Get("Location"), "/admin/manual?") { + t.Fatalf("удаление черновика: %d %s", rec.Code, rec.Header().Get("Location")) + } + if _, ok := globalManual.Get(2); ok { + t.Fatal("черновик не удалён") + } do("POST", "/admin/manual/1/decline", url.Values{"reason": {"нет подписи клиента"}}) o, _ = globalManual.Get(1) if o.Status != "declined" || o.DeclinedAt.Before(time.Now().Add(-time.Minute)) { diff --git a/internal/lkgateway/outgoing_order.go b/internal/lkgateway/outgoing_order.go index 7c3e00b..e1ebda7 100644 --- a/internal/lkgateway/outgoing_order.go +++ b/internal/lkgateway/outgoing_order.go @@ -148,6 +148,7 @@ type OutgoingDeal struct { // ответ НРД: Responses []orderResponse Done bool // получен MS010 по всем поручениям + Rejected bool // MS010 с отказом: операция завершена, но не исполнена } // scanForm16Drafts живьём читает INBOX и собирает исходящие сделки: все черновики @@ -247,6 +248,7 @@ func scanForm16DraftsOpt(store *OutgoingStore, withResponses bool) []*OutgoingDe d.ContrDIDs = sub.ContrDIDs if withResponses { d.Responses, d.Done = outgoingResponses(sub.ContrDIDs) + _, d.Rejected, _ = orderRespVerdict(d.Responses) } } } diff --git a/internal/lkgateway/web/templates/admin_claim.html b/internal/lkgateway/web/templates/admin_claim.html index 6b169dc..e47dbd8 100644 --- a/internal/lkgateway/web/templates/admin_claim.html +++ b/internal/lkgateway/web/templates/admin_claim.html @@ -162,12 +162,17 @@ .ord-step.wait .dot{background:#f59e0b;color:#fff;box-shadow:0 0 0 4px color-mix(in srgb,#f59e0b 28%,transparent)} .ord-step.wait .s{color:#b45309} .ord-step.todo{opacity:.55} + .ord-step.bad{border-color:#dc2626;background:color-mix(in srgb,#dc2626 12%,var(--card))} + .ord-step.bad .dot{background:#dc2626;color:#fff} + .ord-step.bad .s{color:#b3261e} + .ord-dl.bad{border-color:#dc2626;color:#b3261e} .ord-dl{display:inline-block;margin-top:9px;font-size:11.5px;font-weight:700;padding:4px 10px;border-radius:8px;background:var(--card);border:1px solid #16a34a;color:#0f7a37;text-decoration:none;white-space:nowrap} .ord-dl:hover{background:color-mix(in srgb,#16a34a 12%,var(--card))}

Депозитарное поручение 16 (списание)

- {{if .Depo.Done}}проведено · MS010 + {{if and .Depo.Done .Depo.Rejected}}завершено отказом · MS010 + {{else if .Depo.Done}}проведено · MS010 {{else if .Depo.Submitted}}подано, ждём MS010 {{else}}требует подачи{{end}}
@@ -186,10 +191,10 @@
Поручение 16 подано
{{if .Depo.Submitted}}{{msk .Depo.OrderAt}}{{else}}нужно подать в НРД{{end}}
{{if .Depo.OrderXML}}⬇ Отправленная форма 16{{end}}
-
- {{if .Depo.Done}}✓{{else}}3{{end}} -
Списание проведено · MS010
{{if .Depo.Done}}отчёт получен от НРД{{else if .Depo.Submitted}}ждём встречный 16/1 + MS010{{else}}после подачи{{end}}
- {{if .Depo.Responses}}⬇ Ответ НРД (MS010){{end}}
+
+ {{if and .Depo.Done .Depo.Rejected}}✕{{else if .Depo.Done}}✓{{else}}3{{end}} +
{{if and .Depo.Done .Depo.Rejected}}Операция завершена отказом · MS010{{else}}Списание проведено · MS010{{end}}
{{if and .Depo.Done .Depo.Rejected}}НРД отказал в исполнении — см. отчёт{{else if .Depo.Done}}отчёт получен от НРД{{else if .Depo.Submitted}}ждём встречный 16/1 + MS010{{else}}после подачи{{end}}
+ {{if .Depo.Responses}}⬇ Ответ НРД (MS010){{end}}
diff --git a/internal/lkgateway/web/templates/admin_incoming_detail.html b/internal/lkgateway/web/templates/admin_incoming_detail.html index 35b8965..635779b 100644 --- a/internal/lkgateway/web/templates/admin_incoming_detail.html +++ b/internal/lkgateway/web/templates/admin_incoming_detail.html @@ -226,6 +226,10 @@ .ord-step.wait .dot{background:#f59e0b;color:#fff;box-shadow:0 0 0 4px color-mix(in srgb,#f59e0b 28%,transparent)} .ord-step.wait .s{color:#b45309} .ord-step.todo{opacity:.55} + .ord-step.bad{border-color:#dc2626;background:color-mix(in srgb,#dc2626 12%,var(--card))} + .ord-step.bad .dot{background:#dc2626;color:#fff} + .ord-step.bad .s{color:#b3261e} + .ord-dl.bad{border-color:#dc2626;color:#b3261e} .ord-dl{display:inline-block;margin-top:9px;font-size:11.5px;font-weight:700;padding:4px 10px;border-radius:8px;background:var(--card);border:1px solid #16a34a;color:#0f7a37;text-decoration:none;white-space:nowrap} .ord-dl:hover{background:color-mix(in srgb,#16a34a 12%,var(--card))} @@ -265,9 +269,9 @@
Поручение подано в НРД
{{if .It.OrderSubmitted}}{{msk .It.OrderAt}}{{else if .AutoMode}}bj подаст автоматически по черновику{{else if .DraftReceived}}нужно подать{{else}}после черновика{{end}}
{{if .It.OrderXML}}⬇ Отправленная форма 16/1{{end}}
-
- {{if .OrderDone}}✓{{else}}3{{end}} -
Проведено · MS010
{{if .OrderDone}}{{if .OrderDoneAt}}{{.OrderDoneAt}}{{else}}отчёт получен от НРД{{end}}{{else if .It.OrderSubmitted}}ждём MS010 от НРД{{else}}после подачи{{end}}
+
+ {{if and .OrderDone .OrderRejected}}✕{{else if .OrderDone}}✓{{else}}3{{end}} +
{{if and .OrderDone .OrderRejected}}Операция завершена отказом · MS010{{else}}Проведено · MS010{{end}}
{{if and .OrderDone .OrderRejected}}НРД отказал в исполнении{{if .OrderDoneAt}} · {{.OrderDoneAt}}{{end}}{{else if .OrderDone}}{{if .OrderDoneAt}}{{.OrderDoneAt}}{{else}}отчёт получен от НРД{{end}}{{else if .It.OrderSubmitted}}ждём MS010 от НРД{{else}}после подачи{{end}}
{{if .OrderResponses}}⬇ Ответ НРД (MS010){{end}}
@@ -286,7 +290,7 @@ - {{range .OrderResponses}}{{end}} + {{range .OrderResponses}}{{.Kind}}{{end}}
ВремяОтвет НРД
{{.Time}}{{.Kind}}
{{.Time}}
{{else}} @@ -349,7 +353,7 @@
{{.Time}}
Ответ НРД
-
{{.Kind}}
+
{{.Kind}}
{{end}} diff --git a/internal/lkgateway/web/templates/admin_manual.html b/internal/lkgateway/web/templates/admin_manual.html index da46670..424c2fa 100644 --- a/internal/lkgateway/web/templates/admin_manual.html +++ b/internal/lkgateway/web/templates/admin_manual.html @@ -176,19 +176,13 @@

Сверка с учётной системой

Учётная система депозитария не подключена к bj — сверку выполните вручную и подтвердите отметками ниже.

{{end}} -{{/* ---- Обязательные отметки ---- */}} -{{if or (eq $o.Status "draft") $o.Checks}} +{{/* ---- Торговая система: блокировка (необязательно) ---- */}} +{{if or (eq $o.Status "draft") $o.QuikMode}}
-

Обязательные отметки перед отправкой

+

Блокировка в торговой системе · необязательно

+

Клиент, счёт, остаток и бумага проверяются сверкой с учётной системой при сохранении — отдельных отметок не нужно. Здесь только отметьте, если бумаги заблокированы в торговой системе; по умолчанию блокировка не требуется.

{{$editable := and (eq $o.Status "draft") (eq .Mode "card") (can "operate")}}
- {{$grp := ""}} - {{range .PreChecks}} - {{if ne .Group $grp}}
{{.Group}}
{{$grp = .Group}}{{end}} - {{$m := index $o.Checks .Key}} - - {{end}}
Торговая система (QUIK)
@@ -196,7 +190,7 @@
Бумаги заблокированы вручную (лимит уменьшен)
Позиция клиента проверена, поручение на вывод зарегистрировано сотрудником. После ответа отметьте списание или снятие блокировки.
{{if eq $o.QuikMode "manual"}}
№ поручения в торговой системе:{{if $editable}}{{end}}
{{end}}
-