Списки: период, сортировка, подгрузка; анкета участника — строкой исходящих

«Исходящие», «Входящие», «Журнал обмена»: быстрые периоды (сегодня, 7, 30
дней, всё время), даты с–по, сортировка (время, инвестор/контрагент,
направление, статус), первые 50 строк и подгрузка при прокрутке
(part=rows&offset=N). Раньше показывались только последние 200/100.

Анкета участника — исходящий пакет в НРД: строка в «Исходящих» и
«Журнале» с ответом НРД (принята / отклонена с кодом / ждёт), вместо
плашки над списком. Отправки копятся в participant-regs.json (прежний
participant-reg.json подхватывается). userTemplate — функции текущего
пользователя и для порций строк.

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 10:50:43 +03:00
parent 890e575d3d
commit 60b4d46277
13 changed files with 522 additions and 92 deletions
+43 -27
View File
@@ -45,30 +45,6 @@ var templateFuncs = template.FuncMap{
s := globalRC.Snapshot()
return map[string]string{"Name": orgName(s), "INN": orgINN(s), "Code": orgCode(s)}
},
// Карточка «Регистрация участника M2M» для «Исходящих»: когда отправили анкету и вердикт НРД.
"participantRegHTML": func() template.HTML {
reg := loadParticipantReg()
if reg.SentAt.IsZero() {
return ""
}
esc := template.HTMLEscapeString
var b strings.Builder
b.WriteString(`<div class="card"><h2>Регистрация участника M2M (анкета)</h2><p>Анкета отправлена ` + reg.SentAt.Format("02.01.2006 15:04") + `, пакет ` + esc(reg.PkgID))
if reg.Name != "" {
b.WriteString(`, наименование «` + esc(reg.Name) + `»`)
}
b.WriteString(`. `)
switch {
case reg.RespAt.IsZero():
b.WriteString(`Ответ НРД пока не получен.`)
case strings.EqualFold(reg.Status, "ERROR"):
b.WriteString(`<b style="color:var(--err,#c0392b)">НРД отклонил</b> ` + reg.RespAt.Format("02.01 15:04") + `: ` + esc(reg.RespText) + `.`)
default:
b.WriteString(`<b style="color:#1a7f37">НРД принял</b> ` + reg.RespAt.Format("02.01 15:04") + `. ` + esc(reg.RespText))
}
b.WriteString(` <a href="/admin/setup/participant-form">открыть анкету →</a></p></div>`)
return template.HTML(b.String())
},
"ru": russianText,
"ruState": russianState,
"ruOutcome": russianOutcome,
@@ -538,10 +514,36 @@ type homeData struct {
// claimsData — данные журнала.
type claimsData struct {
page
Items []ClaimView
Rows []any // outRow
List listView
Flash string
}
// outRow — строка «Исходящих»: заявка на перевод или отправленная анкета участника.
type outRow struct {
C ClaimView
R *participantReg
}
// outgoingItems — строки «Исходящих» с ключами сортировки.
func outgoingItems(claims []ClaimView) []listItem {
items := make([]listItem, 0, len(claims)+2)
for _, c := range claims {
items = append(items, listItem{T: c.CreatedAt, V: outRow{C: c}, Keys: map[string]string{
"investor": c.Investor.LastName + " " + c.Investor.FirstName,
"party": m2mPartyName(c.ReceivingDepositoryINN) + " " + c.ReceivingDepositoryINN,
"status": russianState(c.Status),
}})
}
for _, rg := range loadParticipantRegs() {
rg := rg
items = append(items, listItem{T: rg.SentAt, V: outRow{R: &rg}, Keys: map[string]string{
"investor": "анкета участника", "party": "НРД", "status": rg.StatusText(),
}})
}
return items
}
// claimData — данные карточки.
type claimData struct {
page
@@ -756,12 +758,26 @@ func (a *admin) renderHome(w http.ResponseWriter, r *http.Request, svc *Service,
}
func (a *admin) renderClaims(w http.ResponseWriter, r *http.Request, svc *Service) {
pageData, err := svc.ListClaims(r.Context(), m2mcore.Filter{Limit: 200})
q := parseListQ(r, []string{"time", "investor", "party", "status"})
f := m2mcore.Filter{Limit: 100000}
if !q.From.IsZero() {
f.CreatedFrom = &q.From
}
if !q.To.IsZero() {
f.CreatedTo = &q.To
}
pageData, err := svc.ListClaims(r.Context(), f)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
render(w, a.claims, claimsData{page: nowPage("Заявки", "claims"), Items: pageData.Items, Flash: r.URL.Query().Get("flash")})
rows, total, more := listApply(outgoingItems(pageData.Items), q)
if r.URL.Query().Get("part") == "rows" {
listRowsPart(w, a.claims, "claimrow", rows, q, more)
return
}
render(w, a.claims, claimsData{page: nowPage("Заявки", "claims"), Rows: listVals(rows), Flash: r.URL.Query().Get("flash"),
List: newListView("/admin/claims", "claims-body", q, total, more, "time", "по времени", "investor", "по инвестору", "party", "по контрагенту", "status", "по статусу")})
}
// deleteDraft — POST /admin/claims/{id}/delete-draft: удалить черновик (заявка ещё не уходила в НРД).