da12a9ec1c
- bj_exchange и «кран» Fancy убраны: данные модуля ЛК в PostgreSQL (схема lk) - трансферные таблицы FTV_M2M: создание при установке, события, справочник участников - сверка с DEPO: счёт, ФИО, дата рождения, ИИС по договору, квал-инвестор из опции DEPO - боевой ЛК ЕСИА Финанс при тестовом ИШ: журнал, отказ через DEPO, статусы в ЛК - онлайн-лицензирование в bj, план тестирования M2M для контрагента (интерактивный HTML) - модули QUIK МНП, экспорт QUIK, НПР/УДС; документы и тестовые материалы Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
148 lines
4.9 KiB
Go
148 lines
4.9 KiB
Go
//go:build live
|
||
|
||
package lkgateway
|
||
|
||
// Разбор, какие данные пришли из боевого ЛК по заявке и как клиент сопоставлен с DEPO.
|
||
// Только чтение: GET из ЛК, SELECT из DEPO. Паспорт и ФИО в выводе маскируются.
|
||
// go test -c -tags live -o lkmatch.test ./internal/lkgateway/ && sudo ./lkmatch.test -test.run TestLKMatchLive -test.v
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"encoding/json"
|
||
"fmt"
|
||
"os"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
func mask(s string) string {
|
||
r := []rune(strings.TrimSpace(s))
|
||
switch {
|
||
case len(r) == 0:
|
||
return "(пусто)"
|
||
case len(r) <= 2:
|
||
return strings.Repeat("•", len(r))
|
||
}
|
||
return string(r[:1]) + strings.Repeat("•", len(r)-2) + string(r[len(r)-1:])
|
||
}
|
||
|
||
func TestLKMatchLive(t *testing.T) {
|
||
rc, err := NewRuntimeConfig("/var/lib/bj/.bj/setup.json")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
globalRC = rc
|
||
ep, ok := lkActiveEndpoint(rc)
|
||
if !ok {
|
||
t.Skip("ЛК не настроен")
|
||
}
|
||
id := 69753
|
||
if v := os.Getenv("CLAIM"); v != "" {
|
||
id, _ = strconv.Atoi(v)
|
||
}
|
||
ctx := context.Background()
|
||
_, body, err := lkGet(ctx, ep, "/claims/"+strconv.Itoa(id))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
var wrap struct {
|
||
Claim map[string]any `json:"claim"`
|
||
}
|
||
_ = json.Unmarshal(body, &wrap)
|
||
cl := wrap.Claim
|
||
data, _ := cl["data"].(map[string]any)
|
||
fmt.Printf("== Заявка ЛК #%d: kind=%v, user_id=%v, owner=%v/%v\n", id, cl["kind"], cl["user_id"], cl["owner_type"], cl["owner_id"])
|
||
keys := make([]string, 0, len(data))
|
||
for k := range data {
|
||
keys = append(keys, k)
|
||
}
|
||
sort.Strings(keys)
|
||
for _, k := range keys {
|
||
b, _ := json.Marshal(data[k])
|
||
fmt.Printf(" data.%s = %s\n", k, b)
|
||
}
|
||
|
||
uid := toInt(cl["user_id"])
|
||
_, ub, _ := lkGet(ctx, ep, "/users/"+strconv.Itoa(uid))
|
||
var uw map[string]any
|
||
_ = json.Unmarshal(ub, &uw)
|
||
u := pickMap(uw, "user", "data")
|
||
if u == nil {
|
||
u = uw
|
||
}
|
||
fmt.Printf("== Пользователь ЛК #%d: поля карточки (значения ФИО и документа замаскированы)\n", uid)
|
||
ukeys := make([]string, 0, len(u))
|
||
for k := range u {
|
||
ukeys = append(ukeys, k)
|
||
}
|
||
sort.Strings(ukeys)
|
||
var present []string
|
||
for _, k := range ukeys {
|
||
if s := str(u[k]); s != "" && s != "<nil>" {
|
||
present = append(present, k)
|
||
}
|
||
}
|
||
fmt.Printf(" заполненные поля: %s\n", strings.Join(present, ", "))
|
||
for _, k := range []string{"surname", "name", "patronymic", "document_type", "pass_series", "pass_number", "document_series", "document_number", "birth_date", "inn", "snils"} {
|
||
if v, ok := u[k]; ok {
|
||
val := str(v)
|
||
if k != "document_type" {
|
||
val = mask(val)
|
||
}
|
||
fmt.Printf(" %s = %s\n", k, val)
|
||
}
|
||
}
|
||
|
||
req, err := lkClaimToRequest(ctx, rc, ep, cl, data)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
inv := req.Investor
|
||
fmt.Printf("== В сверку с DEPO передано: ФИО %s %s %s, документ тип %s серия %s номер %s\n",
|
||
mask(inv.LastName), mask(inv.FirstName), mask(inv.MiddleName), inv.Document.DocumentType, mask(inv.Document.Series), mask(inv.Document.Number))
|
||
var secs []depoOutSec
|
||
acc, section := "", ""
|
||
for _, s := range req.Securities {
|
||
secs = append(secs, depoOutSec{ISIN: s.SecurityDetails.ISIN, Code: s.SecurityCode, Qty: strconv.FormatUint(s.Quantity.Whole, 10)})
|
||
if len(s.SettlementAccounts) > 0 {
|
||
acc, section = s.SettlementAccounts[0].SettlementLocation.AccountID, s.SettlementAccounts[0].SettlementLocation.SectionID
|
||
}
|
||
}
|
||
if v := os.Getenv("ACCOUNT"); v != "" {
|
||
req.ClientAccount = v // проверка несовпадения: подставить чужой счёт
|
||
}
|
||
fmt.Printf("== Счёт из заявки: %s\n", req.ClientAccount)
|
||
if v := os.Getenv("IIS_ACCOUNT"); v != "" { // признак ИИС по произвольному счёту
|
||
fs := rc.Snapshot().Fancy
|
||
if db, err := fancyOpen(fs, fs.DepoDB()); err == nil {
|
||
var cid int64
|
||
_ = db.QueryRowContext(ctx, "SELECT TOP 1 ID FROM OD_CLIENTS WHERE NUMBER=@p1", sql.Named("p1", v)).Scan(&cid)
|
||
ok, det := depoAccountIIS(ctx, db, fs, cid)
|
||
fmt.Printf("== ИИС по счёту %s: не ИИС=%v — %s\n", v, ok, det)
|
||
db.Close()
|
||
}
|
||
}
|
||
if v := os.Getenv("FIO_LAST"); v != "" {
|
||
inv.LastName = v // проверка расхождения ФИО
|
||
}
|
||
if v := os.Getenv("BIRTH"); v != "" {
|
||
inv.BirthDate = v // проверка расхождения даты рождения
|
||
}
|
||
res := runDepoCheckOutAccount(inv.LastName, inv.FirstName, inv.MiddleName, inv.Document.Series, inv.Document.Number, inv.BirthDate, secs, acc, section, req.ClientAccount)
|
||
fmt.Printf("== Сверка с DEPO: итог %s; лицо в DEPO: %s; счёт: %s\n", res.Summary, mask(res.Investor), res.Account)
|
||
for _, s := range res.Secs {
|
||
for _, c := range s.Checks {
|
||
mark := "✓"
|
||
if !c.OK {
|
||
mark = "✗"
|
||
} else if c.Warn {
|
||
mark = "!"
|
||
}
|
||
fmt.Printf(" %s %s [%s]: %s\n", mark, c.Name, c.Code, c.Detail)
|
||
}
|
||
}
|
||
}
|