Files
Bridge-and-Join-s/internal/lkgateway/journal.go
T
2026-09-18 14:23:30 +03:00

119 lines
4.6 KiB
Go

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:<id> | in:<id> — для онлайн-обновления строки
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)
})
}