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

«Исходящие», «Входящие», «Журнал обмена»: быстрые периоды (сегодня, 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
+63
View File
@@ -0,0 +1,63 @@
package lkgateway
import (
"bytes"
"html/template"
"net/http/httptest"
"strings"
"testing"
"time"
)
// Период, сортировка и порции списка.
func TestListApply(t *testing.T) {
base := time.Date(2026, 10, 20, 12, 0, 0, 0, listMSK)
var items []listItem
for i := 0; i < 120; i++ {
items = append(items, listItem{T: base.Add(-time.Duration(i) * time.Hour), Keys: map[string]string{"party": string(rune('a' + i%26))}, V: i})
}
q := parseListQ(httptest.NewRequest("GET", "/x?from=2026-10-19&to=2026-10-20", nil), []string{"time", "party"})
page, total, more := listApply(items, q)
if total != 37 || len(page) != 37 || more { // 20.10 12:00 … 19.10 00:00
t.Fatalf("период: total=%d len=%d more=%v", total, len(page), more)
}
q = parseListQ(httptest.NewRequest("GET", "/x?offset=100", nil), []string{"time", "party"})
page, total, more = listApply(items, q)
if total != 120 || len(page) != 20 || more || page[0].V.(int) != 100 {
t.Fatalf("порция: total=%d len=%d more=%v", total, len(page), more)
}
q = parseListQ(httptest.NewRequest("GET", "/x?sort=party&dir=asc", nil), []string{"time", "party"})
page, _, more = listApply(items, q)
if page[0].Keys["party"] != "a" || page[0].V.(int) != 0 || !more {
t.Fatalf("сортировка: %v", page[0])
}
if q.Query != "dir=asc&sort=party" {
t.Fatalf("query: %q", q.Query)
}
}
// Строки «Исходящих» (заявка и анкета), «Входящих» и панель списка отрисовываются.
func TestListTemplates(t *testing.T) {
for _, c := range []struct {
page, row string
v any
want string
}{
{"admin_claims.html", "claimrow", outRow{R: &participantReg{SentAt: time.Now(), PkgID: "34", Name: "ООО", RespAt: time.Now(), Status: "ERROR", Codes: []string{"M2M37"}, RespText: "Некорректно"}}, "Отклонена НРД · M2M37"},
{"admin_claims.html", "claimrow", outRow{C: ClaimView{ID: "abc", Status: "draft"}}, "/admin/claims/abc"},
{"admin_incoming.html", "inrow", &IncomingTransfer{ID: "in1", Status: "pending"}, "/admin/incoming/in1"},
{"admin_claims.html", "listTools", newListView("/admin/claims", "b", listQ{Sort: "time"}, 3, true, "time", "по времени"), "7 дней"},
} {
tp, err := template.New("layout.html").Funcs(templateFuncs).Funcs(authTemplateFuncs).ParseFS(templatesFS, "web/templates/layout.html", "web/templates/part_*.html", "web/templates/"+c.page)
if err != nil {
t.Fatal(err)
}
var b bytes.Buffer
if err := tp.ExecuteTemplate(&b, c.row, c.v); err != nil {
t.Fatalf("%s: %v", c.row, err)
}
if !strings.Contains(b.String(), c.want) {
t.Errorf("%s: нет %q в %s", c.row, c.want, b.String())
}
}
}