Журнал: онлайн-статус «чего ждёт заявка», ссылка из ручного ввода в журнал, правки формы
- livestatus.go: одна фраза состояния для исходящих (сделка M2M + черновик/подача/MS010 поручения 16) и входящих (наше решение → поручение 16/1); признак «в работе» - строки журналов и карточки показывают её и обновляют по /admin/journal/live каждые 8 с, пока заявка в работе; завершённые и отклонённые не опрашиваются - ручной ввод: у отправленного поручения ссылка «в журнале →» и тот же онлайн-статус; вид документа занимает две колонки - выбор участника: по ИНН и коду с кнопки, а не по номеру строки; Enter выбирает только при единственном совпадении Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package lkgateway
|
||||
|
||||
// livestatus.go — «чего ждёт заявка»: одна строка состояния для журналов и карточек.
|
||||
//
|
||||
// Статус сделки (черновик, отправлена, ожидает решение…) говорит, где заявка, но не говорит,
|
||||
// чего мы ждём: черновика поручения от НРД, подачи, ответа MS010, решения оператора. Здесь это
|
||||
// сводится в одну фразу с признаком «в работе». Пока заявка в работе, страницы обновляют
|
||||
// строку по /admin/journal/live каждые несколько секунд; завершённые и отклонённые не
|
||||
// опрашиваются — там ничего не изменится.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LiveStatus — состояние для показа.
|
||||
type LiveStatus struct {
|
||||
Text string `json:"text"` // чего ждём / чем закончилось
|
||||
Kind string `json:"kind"` // wait | warn | ok | bad
|
||||
Active bool `json:"active"` // в работе: страница продолжает опрашивать
|
||||
}
|
||||
|
||||
// liveOutgoing — состояние исходящего перевода: сделка M2M + депозитарное поручение 16.
|
||||
func liveOutgoing(c ClaimView) LiveStatus {
|
||||
guid := string(c.M2MGUID)
|
||||
var depo *OutgoingDeal
|
||||
if guid != "" && globalOutgoingStore != nil {
|
||||
if d, ok := scanForm16Deal(globalOutgoingStore, guid); ok {
|
||||
depo = d
|
||||
}
|
||||
}
|
||||
var watch *OrderWatch
|
||||
if guid != "" && globalOrderWatch != nil {
|
||||
watch = globalOrderWatch.Get(guid)
|
||||
}
|
||||
switch c.Status {
|
||||
case "draft", "validated":
|
||||
return LiveStatus{Text: "ожидает отправки в НРД", Kind: "wait", Active: true}
|
||||
case "submitted_to_nsd":
|
||||
return LiveStatus{Text: "отправлена в НРД, ожидает подтверждения приёма", Kind: "wait", Active: true}
|
||||
case "awaiting_decision":
|
||||
return LiveStatus{Text: "ожидает решения принимающего депозитария", Kind: "wait", Active: true}
|
||||
case "manual_approval":
|
||||
return LiveStatus{Text: "ожидает решения оператора", Kind: "warn", Active: true}
|
||||
case "awaiting_sub16":
|
||||
return LiveStatus{Text: "принята, ожидает депозитарного исполнения", Kind: "wait", Active: true}
|
||||
case "rejected":
|
||||
return LiveStatus{Text: "отклонена", Kind: "bad"}
|
||||
case "timed_out":
|
||||
return LiveStatus{Text: "истёк срок ответа контрагента", Kind: "bad"}
|
||||
case "done":
|
||||
return LiveStatus{Text: "исполнена", Kind: "ok"}
|
||||
case "confirmed":
|
||||
switch {
|
||||
case depo == nil:
|
||||
return LiveStatus{Text: "принята контрагентом, ожидает черновика поручения 16 от НРД", Kind: "wait", Active: true}
|
||||
case !depo.Submitted:
|
||||
return LiveStatus{Text: "черновик поручения 16 получен, ожидает подачи в НРД", Kind: "warn", Active: true}
|
||||
case depo.Done:
|
||||
return LiveStatus{Text: "поручение 16 исполнено: MS010 получен", Kind: "ok"}
|
||||
case watch != nil:
|
||||
k := watch.Kind()
|
||||
if k == "" {
|
||||
k = "wait"
|
||||
}
|
||||
return LiveStatus{Text: "поручение 16 подано: " + watch.StatusRu(), Kind: k, Active: k == "wait" || k == "warn"}
|
||||
default:
|
||||
return LiveStatus{Text: "поручение 16 подано, ожидает ответа НРД (MS010)", Kind: "wait", Active: true}
|
||||
}
|
||||
}
|
||||
return LiveStatus{Text: russianState(c.Status), Kind: "wait"}
|
||||
}
|
||||
|
||||
// liveIncoming — состояние входящего перевода: наше решение, затем поручение 16/1.
|
||||
func liveIncoming(it *IncomingTransfer) LiveStatus {
|
||||
if it == nil {
|
||||
return LiveStatus{}
|
||||
}
|
||||
if it.Status != "answered" {
|
||||
return LiveStatus{Text: "ожидает нашего решения", Kind: "warn", Active: true}
|
||||
}
|
||||
_, _, outcome := incomingJournalStatus(it)
|
||||
if outcome == "Отказано" {
|
||||
return LiveStatus{Text: "отказано", Kind: "bad"}
|
||||
}
|
||||
if globalOrderWatch != nil {
|
||||
if w := globalOrderWatch.Get(it.GUID); w != nil {
|
||||
k := w.Kind()
|
||||
return LiveStatus{Text: "поручение 16/1 подано: " + w.StatusRu(), Kind: k, Active: k == "wait" || k == "warn"}
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(outcome, "Частично") {
|
||||
return LiveStatus{Text: strings.ToLower(outcome) + "; ожидает поручения 16/1", Kind: "wait", Active: true}
|
||||
}
|
||||
return LiveStatus{Text: "принято, ожидает подачи поручения 16/1", Kind: "wait", Active: true}
|
||||
}
|
||||
|
||||
// registerLiveStatus — GET /admin/journal/live?out=id,id&in=id,id → состояния для строк в работе.
|
||||
func registerLiveStatus(mux *http.ServeMux, svc *Service, store *IncomingStore) {
|
||||
mux.HandleFunc("/admin/journal/live", func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
out := map[string]LiveStatus{}
|
||||
for _, id := range splitIDs(r.URL.Query().Get("out")) {
|
||||
if c, err := svc.GetClaim(ctx, id); err == nil {
|
||||
out[id] = liveOutgoing(c)
|
||||
}
|
||||
}
|
||||
in := map[string]LiveStatus{}
|
||||
for _, id := range splitIDs(r.URL.Query().Get("in")) {
|
||||
if it, ok := store.Get(id); ok {
|
||||
in[id] = liveIncoming(it)
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"out": out, "in": in})
|
||||
})
|
||||
}
|
||||
|
||||
func splitIDs(s string) []string {
|
||||
var out []string
|
||||
for _, p := range strings.Split(s, ",") {
|
||||
if p = strings.TrimSpace(p); p != "" && len(p) <= 64 && len(out) < 200 {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user