package lkgateway // journal.go — объединённый журнал обмена: входящие + исходящие переводы в одной // хронологической ленте (плюс отдельные виды — «Исходящие» и «Входящие» на своих // страницах). Для быстрого обзора всего потока с контрагентами. import ( "fmt" "net/http" "sort" "time" "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2mcore" ) // journalRow — одна строка объединённого журнала. type journalRow struct { Time time.Time Dir string // "Исходящий" | "Входящий" DirKind string // "out" | "in" Party string // контрагент (наименование + код/ИНН) SecN int Status string // класс бейджа (для цвета) StatusText string // человекочитаемый текст статуса (русский) Outcome string // для входящих: «Принято» / «Отказано» / «Частично …» Href string LiveKey string // out: | in: — для онлайн-обновления строки Live LiveStatus // чего ждёт заявка } // incomingJournalStatus — статус входящего для журнала: класс бейджа (цвет), // текст и исход по бумагам (принято/отказано/частично). func incomingJournalStatus(it *IncomingTransfer) (class, text, outcome string) { if it.Status != "answered" { return "pending", "Ждёт решения", "" } conf, rej := 0, 0 for _, s := range it.Securities { switch s.Decision { case "confirmed": conf++ case "rejected": rej++ } } switch { case rej == 0 && conf > 0: return "confirmed", "Отвечено", "Принято" case conf == 0 && rej > 0: return "rejected", "Отвечено", "Отказано" case conf > 0 && rej > 0: return "awaiting_decision", "Отвечено", fmt.Sprintf("Частично: принято %d, отказано %d", conf, rej) } return "confirmed", "Отвечено", "" } // incomingStatusVM — статус входящего для шаблонов (класс бейджа + текст + исход). type incomingStatusVM struct{ Class, Text, Outcome string } // incomingStatusOf — статус входящего для страницы «Входящие» (template-func). func incomingStatusOf(it *IncomingTransfer) incomingStatusVM { c, t, o := incomingJournalStatus(it) return incomingStatusVM{Class: c, Text: t, Outcome: o} } // registerJournal вешает /admin/journal — объединённая лента вход+исход. func registerJournal(mux *http.ServeMux, a *admin, svc *Service, store *IncomingStore) { registerLiveStatus(mux, svc, store) // «чего ждёт заявка» для строк в работе mux.HandleFunc("/admin/journal", func(w http.ResponseWriter, r *http.Request) { var rows []journalRow // Исходящие (наши заявки в НРД). if page, err := svc.ListClaims(r.Context(), m2mcore.Filter{Limit: 100}); err == nil { for _, c := range page.Items { party := m2mPartyName(c.ReceivingDepositoryINN) if party != "" { party += " · ИНН " + c.ReceivingDepositoryINN } else { party = "ИНН " + c.ReceivingDepositoryINN } rows = append(rows, journalRow{ Time: c.CreatedAt, Dir: "Исходящий", DirKind: "out", Party: party, SecN: len(c.Securities), Status: c.Status, StatusText: russianState(c.Status), Href: "/admin/claims/" + c.ID, LiveKey: "out:" + c.ID, Live: liveOutgoing(c), }) } } // Входящие (переводы к нам). for _, it := range store.List() { party := m2mPartyName(it.SenderCode) if party == "" { party = m2mPartyName(it.SenderINN) } if party != "" { party += " · " + it.SenderCode } else { party = it.SenderCode } cls, txt, outcome := incomingJournalStatus(it) rows = append(rows, journalRow{ Time: it.ReceivedAt, Dir: "Входящий", DirKind: "in", Party: party, SecN: len(it.Securities), Status: cls, StatusText: txt, Outcome: outcome, Href: "/admin/incoming/" + it.ID, LiveKey: "in:" + it.ID, Live: liveIncoming(it), }) } sort.Slice(rows, func(i, j int) bool { return rows[i].Time.After(rows[j].Time) }) data := struct { page Rows []journalRow }{nowPage("Журнал обмена", "journal"), rows} render(w, a.journal, data) }) }