Защита рецептов: SQL DEPO/SPECTRE/QUIK вынесен в пакет fsql под garble

Реализация варианта B из аудита защиты знаний. Прямой garble всего bj-server
ломает html/template (он обращается к полям структур по имени, garble их
переименовывает). Решение — селективная обфускация: GOGARBLE обфусцирует ТОЛЬКО
пакет с секретами, шаблоны и обработчики не затрагиваются.

95 SQL-запросов к DEPO/SPECTRE/QUIK и трансферным таблицам FTV_M2M вынесены из
lkgateway в отдельный пакет internal/lkgateway/fsql как переменные (именно var,
не const: const компилятор вставил бы обратно в необфусцируемый пакет, и
шифрование бы не сработало — проверено). Сами обработчики, типы и параметры
sql.Named остались на месте; пакет держит только строки запросов.

build-release.sh собирает bj-server с `GOGARBLE=.../fsql garble -literals`
(нужен отдельный SDK go1.26 — модуль-кэшный тулчейн garble не подходит); если
garble/go1.26 не найдены — обычная сборка с предупреждением.

Проверено на реальном garble-бинаре (запускается, /admin и страницы на
структурах работают): рецепт чтения чужих систем из бинаря исчез —
OD_RESTS 13→0, OD_WIRING 5→0, OD_TURNS 1→0, OD_BALANCES 3→0, D_B_CONTRACTS 1→0,
OD_CLIENTS_PORTS 10→0, DepoLimits/MoneyLimits →0. Остаток FTV_M2M — упоминания
имён таблиц в операторских embed-шаблонах и логах, не сам рецепт.

Стенд собран обычной сборкой (без garble) — трассировки остаются читаемыми.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
This commit is contained in:
zuevav
2026-09-18 20:29:47 +03:00
parent 47f5015c3f
commit 7ce4c2c79f
49 changed files with 605 additions and 332 deletions
+10 -18
View File
@@ -25,6 +25,7 @@ import (
"database/sql"
"errors"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"strconv"
"strings"
"time"
@@ -113,8 +114,7 @@ func depoResolveParty(ctx context.Context, db *sql.DB, fs FancyMSSQLSettings, se
e := db.QueryRowContext(ctx,
// Карточка лица в DEPO версионная: действующая версия (LAST_FLAG=1) даёт ФИО и дату рождения,
// а счета и опции привязаны к исходной записи (SELF_ID).
`SELECT TOP 1 ISNULL(NULLIF(SELF_ID,0),ID), ISNULL(NAME,''), DATE_BIRTH FROM OD_FACES WHERE REPLACE(ISNULL(DOC_SER,''),' ','')=@p1 AND DOC_NUM=@p2
ORDER BY ISNULL(LAST_FLAG,0) DESC, ID DESC`,
fsql.Chk01,
sql.Named("p1", ser), sql.Named("p2", num)).Scan(&p.faceID, &p.faceName, &p.birth)
p.faceOK = e == nil
}
@@ -122,7 +122,7 @@ func depoResolveParty(ctx context.Context, db *sql.DB, fs FancyMSSQLSettings, se
if p.faceOK {
rows, e := db.QueryContext(ctx,
`SELECT ID, ISNULL(NUMBER,''), ISNULL(STATE,-1), D_CLOSE FROM OD_CLIENTS WHERE FACE=@p1 AND NUMBER LIKE N'ВТНКЦ%' ORDER BY ID`,
fsql.Chk02,
sql.Named("p1", p.faceID))
if e == nil {
for rows.Next() {
@@ -177,10 +177,7 @@ var depoQualKinds = []string{
// маска видов бумаг (511 и -1 — все виды, 0 — ни одного). Возвращает (предупреждение, по всем видам, пояснение).
func depoFaceQualified(ctx context.Context, db *sql.DB, faceID int64) (warn, all bool, det string) {
var val sql.NullString
err := db.QueryRowContext(ctx, `SELECT TOP 1 CAST(o.VAL AS varchar(20)) FROM OD_OPTIONS o
JOIN OP_FIELDS f ON f.ID = o.DESCR AND f.AS_NAME = 'IS_QUAL'
WHERE o.OBJECT = @p1 AND o.B_DATE <= GETDATE() AND (o.E_DATE IS NULL OR o.E_DATE > GETDATE())
ORDER BY o.B_DATE DESC`, sql.Named("p1", faceID)).Scan(&val)
err := db.QueryRowContext(ctx, fsql.Chk03, sql.Named("p1", faceID)).Scan(&val)
const notQual = " — если бумага только для квал., нужен отказ M2M08; решает оператор"
if errors.Is(err, sql.ErrNoRows) {
return true, false, "инвестор НЕ квалифицированный (нет опции «Вхождение в реестр квалифицированных инвесторов» в DEPO)" + notQual
@@ -214,7 +211,7 @@ func depoAccountIIS(ctx context.Context, db *sql.DB, fs FancyMSSQLSettings, clie
if clientID == 0 {
return true, "не проверяется — нет открытого счёта клиента"
}
rows, err := db.QueryContext(ctx, `SELECT ISNULL(PORT_NUM,'') FROM OD_CLIENTS_PORTS WHERE CLIENT=@p1`, sql.Named("p1", clientID))
rows, err := db.QueryContext(ctx, fsql.Chk04, sql.Named("p1", clientID))
if err != nil {
return true, "договор счёта не прочитан: " + quikErrorDetail(err)
}
@@ -245,7 +242,7 @@ func depoAccountIIS(ctx context.Context, db *sql.DB, fs FancyMSSQLSettings, clie
// depoSecurity — бумага в справочнике DEPO.
func depoSecurity(ctx context.Context, db *sql.DB, isin string) (id int64, name string, found, accepted bool) {
var isIn sql.NullInt64
e := db.QueryRowContext(ctx, `SELECT TOP 1 ID, ISNULL(NAME,''), IS_IN FROM OD_VALUES WHERE ISIN=@p1`,
e := db.QueryRowContext(ctx, fsql.Chk05,
sql.Named("p1", isin)).Scan(&id, &name, &isIn)
found = e == nil
accepted = found && isIn.Valid && isIn.Int64 == 1
@@ -256,10 +253,7 @@ func depoSecurity(ctx context.Context, db *sql.DB, isin string) (id int64, name
// опция бумаги RESTRICTED («Ограничение ценной бумаги по обращению», 1 — для квал. инвесторов).
func depoSecurityQualOnly(ctx context.Context, db *sql.DB, valID int64) (bool, string) {
var val sql.NullString
err := db.QueryRowContext(ctx, `SELECT TOP 1 CAST(o.VAL AS varchar(20)) FROM OD_OPTIONS o
JOIN OP_FIELDS f ON f.ID = o.DESCR AND f.AS_NAME = 'RESTRICTED'
WHERE o.OBJECT = @p1 AND o.B_DATE <= GETDATE() AND (o.E_DATE IS NULL OR o.E_DATE > GETDATE())
ORDER BY o.B_DATE DESC`, sql.Named("p1", valID)).Scan(&val)
err := db.QueryRowContext(ctx, fsql.Chk06, sql.Named("p1", valID)).Scan(&val)
switch {
case errors.Is(err, sql.ErrNoRows):
return false, "ограничений по обращению нет (опция DEPO)"
@@ -321,9 +315,7 @@ func depoBlocking(ctx context.Context, db *sql.DB, valID, clientID int64) (ok bo
var balAcc sql.NullInt64
var k sql.NullFloat64
er := db.QueryRowContext(ctx,
`SELECT TOP 1 r.BLOCK_DATE, r.BLOCK_REASON, r.BAL_ACC, CAST(r.K AS float)
FROM OD_RESTS r WHERE r.VALUE_ID=@p1 AND r.REG_1=@p2
ORDER BY CASE WHEN r.BLOCK_DATE IS NOT NULL THEN 0 WHEN r.BAL_ACC IN (837,3410,4342,5647) AND CAST(r.K AS float)<>0 THEN 1 ELSE 2 END`,
fsql.Chk07,
sql.Named("p1", valID), sql.Named("p2", clientID)).Scan(&blockDate, &blockReason, &balAcc, &k)
if er != nil {
return true, "регистра бумаги на счёте нет — блокировок нет"
@@ -500,7 +492,7 @@ func runDepoCheckOutAccount(last, first, middle, ser, num, birth string, secs []
default:
var id, state int64
var dclose sql.NullTime
err := db.QueryRowContext(ctx, `SELECT TOP 1 ID, ISNULL(STATE,-1), D_CLOSE FROM OD_CLIENTS WHERE FACE=@p1 AND UPPER(NUMBER)=@p2 ORDER BY ID`,
err := db.QueryRowContext(ctx, fsql.Chk08,
sql.Named("p1", p.faceID), sql.Named("p2", want)).Scan(&id, &state, &dclose)
switch {
case err != nil:
@@ -578,7 +570,7 @@ func runDepoCheckOutAccount(last, first, middle, ser, num, birth string, secs []
if p.accOpen != "" && found {
var have float64
_ = db.QueryRowContext(ctx,
`SELECT ISNULL(SUM(CAST(K AS float)),0) FROM OD_RESTS WHERE VALUE_ID=@p1 AND REG_1=@p2 AND BAL_ACC=@p3`,
fsql.Chk09,
sql.Named("p1", valID), sql.Named("p2", p.clientID), sql.Named("p3", depoTradeBalAcc)).Scan(&have)
qtyOK = have >= need && need > 0
qtyDet = fmt.Sprintf("на торговом счёте %s: %.0f шт., списываем %.0f", p.accOpen, have, need)
+3 -7
View File
@@ -8,6 +8,7 @@ package lkgateway
import (
"context"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"net/http"
"strings"
"time"
@@ -36,11 +37,7 @@ func depoSimLists() (clients []depoSimClient, secs []depoSimSec) {
defer cancel()
rows, err := db.QueryContext(ctx,
`SELECT TOP 25 ISNULL(f.NAME,''), ISNULL(f.DOC_SER,''), ISNULL(f.DOC_NUM,''), ISNULL(c.NUMBER,'')
FROM OD_FACES f JOIN OD_CLIENTS c ON c.FACE=f.ID
WHERE c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL
AND LEN(ISNULL(f.DOC_SER,''))>0 AND LEN(ISNULL(f.DOC_NUM,''))>0
ORDER BY f.NAME`)
fsql.Sim01)
if err == nil {
defer rows.Close()
seen := map[string]bool{}
@@ -54,8 +51,7 @@ func depoSimLists() (clients []depoSimClient, secs []depoSimSec) {
}
}
rows2, err := db.QueryContext(ctx,
`SELECT TOP 20 ISNULL(ISIN,''), ISNULL(NAME,'') FROM OD_VALUES
WHERE V_TYPE=2 AND IS_IN=1 AND ISIN LIKE 'RU%' ORDER BY NAME`)
fsql.Sim02)
if err == nil {
defer rows2.Close()
for rows2.Next() {
+6 -19
View File
@@ -3,6 +3,7 @@ package lkgateway
import (
"context"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"strings"
"sync"
"time"
@@ -48,7 +49,7 @@ func depoContractExists(ctx context.Context, s FancyMSSQLSettings, contract stri
defer cancel()
var full string
err = conn.QueryRowContext(cctx,
"SELECT TOP 1 PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM=@p1 OR PORT_NUM LIKE @p2",
fsql.QDb01,
contract, likeEscape(contract)+" %").Scan(&full)
if err != nil {
return "", false
@@ -67,15 +68,7 @@ func depoClientPositions(ctx context.Context, s FancyMSSQLSettings, contract str
cctx, cancel := context.WithTimeout(ctx, 25*time.Second)
defer cancel()
q := `SELECT p.PORT_NUM, ISNULL(c.NUMBER,''), ISNULL(v.ISIN,''), ISNULL(v.NAME,''),
CAST(rst.K AS float), ISNULL(rst.BLOCK_REASON,''), ISNULL(rst.BAL_ACC,0)
FROM OD_RESTS rst
JOIN OD_CLIENTS c ON c.ID = rst.REG_1
JOIN OD_CLIENTS_PORTS p ON p.CLIENT = c.ID
JOIN OD_VALUES v ON v.ID = rst.VALUE_ID AND v.V_TYPE = 2
WHERE (p.PORT_NUM = @p1 OR p.PORT_NUM LIKE @p2)
AND CAST(rst.K AS float) <> 0
ORDER BY v.NAME`
q := fsql.Dp01
rows, err := conn.QueryContext(cctx, q, contract, likeEscape(contract)+" %")
if err != nil {
return nil, err
@@ -164,13 +157,7 @@ func depoListContracts(ctx context.Context, s FancyMSSQLSettings, search string,
if limit <= 0 || limit > 500 {
limit = 100
}
q := fmt.Sprintf(`SELECT TOP (%d) p.PORT_NUM, COUNT(DISTINCT rst.VALUE_ID)
FROM OD_CLIENTS_PORTS p
JOIN OD_CLIENTS c ON c.ID = p.CLIENT
JOIN OD_RESTS rst ON rst.REG_1 = c.ID
JOIN OD_VALUES v ON v.ID = rst.VALUE_ID AND v.V_TYPE = 2
WHERE CAST(rst.K AS float) <> 0 AND (@p1 = '' OR p.PORT_NUM LIKE @p2)
GROUP BY p.PORT_NUM ORDER BY p.PORT_NUM`, limit)
q := fmt.Sprintf(fsql.Dp02, limit)
rows, err := conn.QueryContext(cctx, q, search, "%"+likeEscape(search)+"%")
if err != nil {
return nil, err
@@ -203,7 +190,7 @@ func depoFindContracts(ctx context.Context, s FancyMSSQLSettings, like string, l
limit = 50
}
rows, err := conn.QueryContext(cctx, fmt.Sprintf(
"SELECT DISTINCT TOP (%d) PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM LIKE @p1 ORDER BY PORT_NUM", limit),
fsql.QDb02, limit),
"%"+like+"%")
if err != nil {
return nil, err
@@ -259,7 +246,7 @@ func depoSuggestContracts(ctx context.Context, s FancyMSSQLSettings) []string {
cctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
rows, err := conn.QueryContext(cctx,
"SELECT DISTINCT TOP (5000) PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM <> '' ORDER BY PORT_NUM")
fsql.QDb03)
if err != nil {
return nil
}
+18 -25
View File
@@ -14,6 +14,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"log"
"os"
"path/filepath"
@@ -159,14 +160,14 @@ func depoEventAt(rc *RuntimeConfig, at time.Time, guid, ref, typ, codes, params
params = string([]rune(params)[:4000])
}
var n int
_ = db.QueryRowContext(ctx, "SELECT COUNT(*) FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g AND ISNULL(REFERENCE,'')=@r AND EVENT_TYPE=@t",
_ = db.QueryRowContext(ctx, fsql.QEv01,
sql.Named("g", guid), sql.Named("r", ref), sql.Named("t", typ)).Scan(&n)
if n > 0 {
return
}
q := `INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE, M2M_REJECT_CODE) VALUES (@g, @r, @t, @at, @c)`
q := fsql.Ev01
if depoHasColumn(ctx, db, "FTV_M2M_EVENTS", "PARAMETERS") {
q = `INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE, M2M_REJECT_CODE, PARAMETERS) VALUES (@g, @r, @t, @at, @c, @p)`
q = fsql.Ev02
}
_, err = db.ExecContext(ctx, q,
sql.Named("g", guid), sql.Named("r", nullStr(ref)), sql.Named("t", typ), sql.Named("at", at.In(time.FixedZone("МСК", 3*3600)).Format("2006-01-02T15:04:05.000")),
@@ -203,7 +204,7 @@ func depoUpsertOrder(rc *RuntimeConfig, guid, direction, cpCode, deponentName, d
return false
}
var n int
_ = db.QueryRowContext(ctx, "SELECT COUNT(*) FROM dbo.FTV_M2M WHERE GUID=@g", sql.Named("g", guid)).Scan(&n)
_ = db.QueryRowContext(ctx, fsql.QEv02, sql.Named("g", guid)).Scan(&n)
if n > 0 {
return true
}
@@ -211,16 +212,13 @@ func depoUpsertOrder(rc *RuntimeConfig, guid, direction, cpCode, deponentName, d
var deponentID sql.NullInt64
serN := strings.ReplaceAll(strings.TrimSpace(ser), " ", "")
if serN != "" && strings.TrimSpace(num) != "" {
_ = db.QueryRowContext(ctx, "SELECT TOP 1 SELF_ID FROM OD_FACES WHERE REPLACE(DOC_SER,' ','')=@s AND DOC_NUM=@n ORDER BY ID", sql.Named("s", serN), sql.Named("n", strings.TrimSpace(num))).Scan(&deponentID)
_ = db.QueryRowContext(ctx, fsql.QEv03, sql.Named("s", serN), sql.Named("n", strings.TrimSpace(num))).Scan(&deponentID)
}
// счёт клиента для DEPO_NUMBER строк: открытый торговый ВТНКЦ…, при нескольких договорах — самый
// ранний (то же правило, что при зачислении в QUIK); джоба по номеру находит OD_CLIENTS.ID (DEPO).
var clientAcc sql.NullString
if serN != "" && strings.TrimSpace(num) != "" {
_ = db.QueryRowContext(ctx, `SELECT TOP 1 c.NUMBER FROM OD_FACES f
JOIN OD_CLIENTS c ON c.FACE=f.ID LEFT JOIN OD_CLIENTS_PORTS p ON p.CLIENT=c.ID
WHERE REPLACE(f.DOC_SER,' ','')=@s AND f.DOC_NUM=@n AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL
ORDER BY ISNULL(p.PORT_DATE,'2099-01-01'), ISNULL(c.D_OPEN,'2099-01-01'), c.NUMBER`,
_ = db.QueryRowContext(ctx, fsql.Ev03,
sql.Named("s", serN), sql.Named("n", strings.TrimSpace(num))).Scan(&clientAcc)
}
hasDepoNumber := depoHasColumn(ctx, db, "FTV_M2M_LINES", "DEPO_NUMBER")
@@ -231,9 +229,9 @@ func depoUpsertOrder(rc *RuntimeConfig, guid, direction, cpCode, deponentName, d
return false
}
// COMMENT не пишем — его заполняет bj2depo (17.09.2026); comment остаётся только для журнала bj.
head := `INSERT INTO dbo.FTV_M2M (GUID, DIRECTION, CONTRAGENT_M2M_CODE, DEPONENT_NAME, DEPONENT_DOC) VALUES (@g, @d, @cp, @nm, @doc)`
head := fsql.Ev04
if hasDeponentID {
head = `INSERT INTO dbo.FTV_M2M (GUID, DIRECTION, CONTRAGENT_M2M_CODE, DEPONENT_NAME, DEPONENT_DOC, DEPONENT_ID) VALUES (@g, @d, @cp, @nm, @doc, @did)`
head = fsql.Ev05
}
if _, err := tx.ExecContext(ctx, head,
sql.Named("g", guid), sql.Named("d", direction), sql.Named("cp", nullStr(cpCode)), sql.Named("nm", nullStr(deponentName)), sql.Named("doc", nullStr(deponentDoc)),
@@ -247,7 +245,7 @@ func depoUpsertOrder(rc *RuntimeConfig, guid, direction, cpCode, deponentName, d
for _, ln := range lines {
var share sql.NullInt64
if ln.ISIN != "" && hasShare {
_ = db.QueryRowContext(ctx, "SELECT TOP 1 ID FROM OD_VALUES WHERE ISIN=@i ORDER BY ID", sql.Named("i", ln.ISIN)).Scan(&share)
_ = db.QueryRowContext(ctx, fsql.QEv04, sql.Named("i", ln.ISIN)).Scan(&share)
}
cols, vals := "GUID, REFERENCE, ISIN, SEC_CODE, AMOUNT, PLACE_NUMBER, LINK_NUMBER", "@g, @r, @i, @c, @a, @p, ''"
if hasShare {
@@ -256,7 +254,7 @@ func depoUpsertOrder(rc *RuntimeConfig, guid, direction, cpCode, deponentName, d
if hasDepoNumber {
cols, vals = cols+", DEPO_NUMBER", vals+", @acc"
}
q := `INSERT INTO dbo.FTV_M2M_LINES (` + cols + `) VALUES (` + vals + `)`
q := fsql.Ev06 + cols + `) VALUES (` + vals + `)`
if _, err := tx.ExecContext(ctx, q,
sql.Named("g", guid), sql.Named("r", ln.Ref), sql.Named("i", nullStr(ln.ISIN)), sql.Named("c", nullStr(ln.Code)), sql.Named("sh", share), sql.Named("a", ln.Amount), sql.Named("p", nullStr(ln.Place)), sql.Named("acc", clientAcc)); err != nil {
tx.Rollback()
@@ -440,10 +438,7 @@ func depoPollOrders(ctx context.Context, db *sql.DB, rc *RuntimeConfig) {
} else if depoHasColumn(ctx, db, "FTV_M2M", "FANSY_DOC") {
docCol = "CAST(m.FANSY_DOC AS varchar(32))"
}
rows, err := db.QueryContext(ctx, `SELECT TOP 200 CAST(m.GUID AS varchar(36)), ISNULL(m.STATE,''), ISNULL(`+docCol+`,''), ISNULL(m.COMMENT,''),
ISNULL((SELECT TOP 1 ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID=m.GUID AND ISNULL(l.STATE,'')='ERROR'),''),
ISNULL((SELECT TOP 1 ISNULL(st.NAME,'') FROM OD_DOCS d LEFT JOIN OD_DOC_STATES st ON st.ID=d.STATE AND st.D_CAT=0 WHERE CAST(d.ID AS varchar(32))=`+docCol+`),''),
(SELECT TOP 1 d.PROC_DATE FROM OD_DOCS d WHERE CAST(d.ID AS varchar(32))=`+docCol+`)
rows, err := db.QueryContext(ctx, fsql.Ev07+docCol+fsql.Ev08+docCol+fsql.Ev09+docCol+`)
FROM dbo.FTV_M2M m WHERE m.TimeNew > DATEADD(day,-45,GETDATE()) ORDER BY m.ID DESC`)
if err != nil {
log.Printf("depo-events: чтение FTV_M2M: %v", err)
@@ -525,7 +520,7 @@ func depoWriteHandbook(rc *RuntimeConfig) error {
return err
}
// полная перезапись (согласовано с программистом); DELETE — у логина bj нет права на TRUNCATE
if _, err := tx.ExecContext(ctx, "DELETE FROM dbo.FTV_M2M_HANDBOOK"); err != nil {
if _, err := tx.ExecContext(ctx, fsql.QEv05); err != nil {
tx.Rollback()
return err
}
@@ -534,8 +529,7 @@ func depoWriteHandbook(rc *RuntimeConfig) error {
if short == "" {
short = e.DisplayName
}
_, err := tx.ExecContext(ctx, `INSERT INTO dbo.FTV_M2M_HANDBOOK (INN, PARTICIPANT_CODE, KIND, NAME_FULL, NAME_SHORT, NAME_DISPLAY, NAME_ENG, HANDBOOK_AT, UPDATED_AT, ACTIVE)
VALUES (@inn, @code, @kind, @full, @short, @disp, @eng, @at, GETDATE(), 1)`,
_, err := tx.ExecContext(ctx, fsql.Ev10,
sql.Named("inn", e.INN), sql.Named("code", nullStr(e.Code)), sql.Named("kind", kind), sql.Named("full", nullStr(e.FullName)), sql.Named("short", nullStr(short)),
sql.Named("disp", nullStr(e.DisplayName)), sql.Named("eng", nullStr(e.EngName)), sql.Named("at", hbAt))
return err
@@ -708,14 +702,14 @@ ALTER ROLE SQLAgentOperatorRole ADD MEMBER %[1]s;
func depoJobPing(ctx context.Context, db *sql.DB, wait time.Duration) (bool, string) {
guid := strings.ToUpper(fmt.Sprintf("%08x-0000-4000-8000-%012x", time.Now().Unix()&0xFFFFFFFF, time.Now().UnixNano()&0xFFFFFFFFFFFF))
if _, err := db.ExecContext(ctx, "INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE) VALUES (@g, NULL, 'BJ_PING', GETDATE())", sql.Named("g", guid)); err != nil {
if _, err := db.ExecContext(ctx, fsql.QEv06, sql.Named("g", guid)); err != nil {
return false, "не удалось записать проверочное событие: " + quikErrorDetail(err)
}
defer db.ExecContext(context.Background(), "DELETE FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g", sql.Named("g", guid))
defer db.ExecContext(context.Background(), fsql.QEv07, sql.Named("g", guid))
deadline := time.Now().Add(wait)
for time.Now().Before(deadline) {
var st string
if err := db.QueryRowContext(ctx, "SELECT ISNULL(STATE,'') FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g", sql.Named("g", guid)).Scan(&st); err == nil && st != "TODO" && st != "" {
if err := db.QueryRowContext(ctx, fsql.QEv08, sql.Named("g", guid)).Scan(&st); err == nil && st != "TODO" && st != "" {
return true, fmt.Sprintf("проверочное событие BJ_PING обработано за %s (STATE=%s)", time.Since(deadline.Add(-wait)).Round(time.Second), st)
}
time.Sleep(3 * time.Second)
@@ -976,8 +970,7 @@ func depoSimulateIncoming(ctx context.Context, rc *RuntimeConfig, it *IncomingTr
serN := strings.ReplaceAll(strings.TrimSpace(ser), " ", "")
numN := strings.TrimSpace(num)
var name string
if err := db.QueryRowContext(ctx, `SELECT TOP 1 f.NAME FROM OD_FACES f JOIN OD_CLIENTS c ON c.FACE=f.ID
WHERE REPLACE(f.DOC_SER,' ','')=@s AND f.DOC_NUM=@n AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0`, sql.Named("s", serN), sql.Named("n", numN)).Scan(&name); err != nil {
if err := db.QueryRowContext(ctx, fsql.Ev11, sql.Named("s", serN), sql.Named("n", numN)).Scan(&name); err != nil {
return "", fmt.Errorf("клиент с паспортом %s %s и открытым торговым счётом в DEPO не найден", ser, num)
}
guid := strings.ToUpper(fmt.Sprintf("%08x-%04x-4000-8000-%012x", time.Now().Unix()&0xFFFFFFFF, time.Now().UnixNano()&0xFFFF, time.Now().UnixNano()&0xFFFFFFFFFFFF))
+6 -13
View File
@@ -9,6 +9,7 @@ import (
"context"
"database/sql"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"log"
"regexp"
"strings"
@@ -55,13 +56,7 @@ func (f depoJobFailure) TableRu() string {
// depoJobFailures — записи с ошибкой обработки за days дней, новые первыми.
func depoJobFailures(ctx context.Context, db *sql.DB, days, limit int) ([]depoJobFailure, error) {
rows, err := db.QueryContext(ctx, `SELECT TOP (@n) * FROM (
SELECT 'FTV_M2M' AS T, ID, CAST(GUID AS varchar(36)) AS G, '' AS R, ISNULL(DIRECTION,'') AS K, ISNULL(TimeEnd, TimeNew) AS A, ISNULL(COMMENT,'') AS C
FROM dbo.FTV_M2M WHERE STATE='ERROR' AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -@d, GETDATE())
UNION ALL
SELECT 'FTV_M2M_EVENTS', ID, CAST(GUID AS varchar(36)), ISNULL(REFERENCE,''), EVENT_TYPE, ISNULL(TimeEnd, TimeNew), ISNULL(COMMENT,'')
FROM dbo.FTV_M2M_EVENTS WHERE STATE='ERROR' AND EVENT_TYPE LIKE 'BJ[_]%' AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -@d, GETDATE())
) x ORDER BY A DESC`, sql.Named("n", limit), sql.Named("d", days))
rows, err := db.QueryContext(ctx, fsql.Hlth01, sql.Named("n", limit), sql.Named("d", days))
if err != nil {
return nil, err
}
@@ -95,9 +90,9 @@ func depoRequeueFailures(ctx context.Context, db *sql.DB, days int) (orders, eve
}
for _, f := range fails {
comment := "повторная обработка из bj"
q := "UPDATE dbo.FTV_M2M SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL, COMMENT=@c WHERE ID=@id AND STATE='ERROR'"
q := fsql.QHl01
if f.Table == "FTV_M2M_EVENTS" {
q = "UPDATE dbo.FTV_M2M_EVENTS SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL, COMMENT=@c WHERE ID=@id AND STATE='ERROR'"
q = fsql.QHl02
if lf := globalLKFlows.Get(f.GUID); f.Type == "BJ_GATEWAY_REJECT" && lf != nil && lf.Reason != "" {
comment = lf.Reason
}
@@ -111,7 +106,7 @@ func depoRequeueFailures(ctx context.Context, db *sql.DB, days int) (orders, eve
if f.Table == "FTV_M2M" {
orders += n
// строки поручения, помеченные ошибкой, тоже возвращаем
if _, e := tx.ExecContext(ctx, "UPDATE dbo.FTV_M2M_LINES SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL WHERE GUID=@g AND STATE='ERROR'", sql.Named("g", f.GUID)); e != nil {
if _, e := tx.ExecContext(ctx, fsql.QHl03, sql.Named("g", f.GUID)); e != nil {
tx.Rollback()
return 0, 0, e
}
@@ -133,9 +128,7 @@ func depoAlertEventFailures(ctx context.Context, db *sql.DB, rc *RuntimeConfig)
var id sql.NullInt64
var typ, guid, comment string
var n int
err := db.QueryRowContext(ctx, `SELECT COUNT(*) OVER (), ID, EVENT_TYPE, CAST(GUID AS varchar(36)), ISNULL(COMMENT,'')
FROM dbo.FTV_M2M_EVENTS WHERE STATE='ERROR' AND EVENT_TYPE LIKE 'BJ[_]%' AND ID > @last AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -1, GETDATE())
ORDER BY ID DESC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY`, sql.Named("last", depoFailAlertedID)).Scan(&n, &id, &typ, &guid, &comment)
err := db.QueryRowContext(ctx, fsql.Hlth02, sql.Named("last", depoFailAlertedID)).Scan(&n, &id, &typ, &guid, &comment)
if err != nil || !id.Valid {
return
}
+6 -7
View File
@@ -10,6 +10,7 @@ import (
"context"
"database/sql"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"log"
"net/http"
"net/url"
@@ -111,9 +112,7 @@ func depoQueueLoad(ctx context.Context, rc *RuntimeConfig, limit int) ([]depoQue
break
}
}
rows, err := db.QueryContext(ctx, `SELECT TOP (@n) CAST(m.GUID AS varchar(36)), ISNULL(m.STATE,''), ISNULL(m.DIRECTION,''), ISNULL(m.DEPONENT_NAME,''), ISNULL(m.DEPONENT_DOC,''), ISNULL(m.CONTRAGENT_M2M_CODE,''), `+timeCol+`, ISNULL(`+docCol+`,''), ISNULL(m.COMMENT,''),
ISNULL((SELECT TOP 1 ISNULL(st.NAME,'') FROM OD_DOCS d LEFT JOIN OD_DOC_STATES st ON st.ID=d.STATE AND st.D_CAT=0 WHERE CAST(d.ID AS varchar(32))=`+docCol+`),''),
(SELECT TOP 1 d.PROC_DATE FROM OD_DOCS d WHERE CAST(d.ID AS varchar(32))=`+docCol+`)
rows, err := db.QueryContext(ctx, fsql.Que01+timeCol+`, ISNULL(`+docCol+fsql.Que02+docCol+fsql.Que03+docCol+`)
FROM dbo.FTV_M2M m ORDER BY `+strings.Replace(timeCol, "NULL", "m.GUID", 1)+` DESC`, sql.Named("n", limit))
if err != nil {
return nil, sum, err
@@ -144,7 +143,7 @@ func depoQueueLoad(ctx context.Context, rc *RuntimeConfig, limit int) ([]depoQue
}
// строки
if depoTableExists(ctx, db, "FTV_M2M_LINES") {
lr, err := db.QueryContext(ctx, `SELECT CAST(l.GUID AS varchar(36)), ISNULL(l.ISIN,''), ISNULL(l.SEC_CODE,''), ISNULL(CAST(l.AMOUNT AS varchar(40)),''), ISNULL(l.STATE,''), ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID IN (SELECT TOP (@n) GUID FROM dbo.FTV_M2M ORDER BY `+strings.Replace(strings.TrimPrefix(timeCol, "m."), "NULL", "GUID", 1)+` DESC)`, sql.Named("n", limit))
lr, err := db.QueryContext(ctx, fsql.Que04+strings.Replace(strings.TrimPrefix(timeCol, "m."), "NULL", "GUID", 1)+` DESC)`, sql.Named("n", limit))
if err == nil {
for lr.Next() {
var g string
@@ -164,7 +163,7 @@ func depoQueueLoad(ctx context.Context, rc *RuntimeConfig, limit int) ([]depoQue
}
// события
if depoTableExists(ctx, db, "FTV_M2M_EVENTS") {
er, err := db.QueryContext(ctx, `SELECT CAST(e.GUID AS varchar(36)), ISNULL(e.REFERENCE,''), ISNULL(e.STATE,''), ISNULL(e.EVENT_TYPE,''), e.EVENT_DATE, ISNULL(e.M2M_REJECT_CODE,''), ISNULL(e.COMMENT,'') FROM dbo.FTV_M2M_EVENTS e WHERE e.GUID IN (SELECT TOP (@n) GUID FROM dbo.FTV_M2M ORDER BY `+strings.Replace(strings.TrimPrefix(timeCol, "m."), "NULL", "GUID", 1)+` DESC) ORDER BY e.EVENT_DATE`, sql.Named("n", limit))
er, err := db.QueryContext(ctx, fsql.Que05+strings.Replace(strings.TrimPrefix(timeCol, "m."), "NULL", "GUID", 1)+` DESC) ORDER BY e.EVENT_DATE`, sql.Named("n", limit))
if err == nil {
for er.Next() {
var g string
@@ -196,10 +195,10 @@ func depoQueueLoad(ctx context.Context, rc *RuntimeConfig, limit int) ([]depoQue
}
if depoTableExists(ctx, db, "FTV_M2M_HANDBOOK") {
sum.HasHandbook = true
_ = db.QueryRowContext(ctx, "SELECT count(*) FROM dbo.FTV_M2M_HANDBOOK").Scan(&sum.Handbook)
_ = db.QueryRowContext(ctx, fsql.QQu01).Scan(&sum.Handbook)
if depoHasColumn(ctx, db, "FTV_M2M_HANDBOOK", "HANDBOOK_AT") {
var t sql.NullTime
if db.QueryRowContext(ctx, "SELECT max(HANDBOOK_AT) FROM dbo.FTV_M2M_HANDBOOK").Scan(&t) == nil && t.Valid {
if db.QueryRowContext(ctx, fsql.QQu02).Scan(&t) == nil && t.Valid {
sum.HandbookAt = t.Time
}
}
+4 -3
View File
@@ -12,6 +12,7 @@ import (
"context"
"database/sql"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"regexp"
"strings"
)
@@ -225,8 +226,8 @@ func depoTablesGrantSQL(pg bool, user string) []string {
if pg {
return []string{
fmt.Sprintf(`GRANT USAGE ON SCHEMA public TO "%s"`, user),
fmt.Sprintf(`GRANT SELECT ON ALL TABLES IN SCHEMA public TO "%s"`, user), // учётные таблицы DEPO (сверка)
fmt.Sprintf(`GRANT SELECT, INSERT, UPDATE, DELETE ON %s TO "%s"`, strings.Join(depoTableNames(), ", "), user),
fmt.Sprintf(fsql.Tbl01, user), // учётные таблицы DEPO (сверка)
fmt.Sprintf(fsql.Tbl02, strings.Join(depoTableNames(), ", "), user),
}
}
out := []string{
@@ -274,7 +275,7 @@ func depoTablesScript(f FancyMSSQLSettings) string {
} else {
b.WriteString("-- Права логину bj: выдайте SELECT, INSERT, UPDATE, DELETE на таблицы выше (логин bj в настройках не указан).\n\n")
}
b.WriteString("-- Кроме того, логину bj нужно право SELECT на учётные таблицы DEPO (OD_CLIENTS, OD_CLIENTS_PORTS, OD_RESTS, OD_VALUES и др.) для сверки переводов.\n")
b.WriteString(fsql.QTb01)
return b.String()
}
+91
View File
@@ -0,0 +1,91 @@
package fsql
// fsql — SQL-запросы к учётным системам DEPO и SPECTRE и к экспорту QUIK,
// вынесенные из lkgateway отдельным пакетом. В релизной сборке этот пакет
// (и только он) обфусцируется garble: строки запросов шифруются и в бинаре
// не читаются `strings`. Именно здесь лежит знание о чужих схемах (какие
// таблицы, связи и столбцы), которое дороже всего восстанавливать.
//
// ВАЖНО: только var, не const — константу компилятор вставил бы в
// вызывающий (необфусцированный) пакет, и шифрование бы не сработало.
// Запросы — data-only: параметры передаются через sql.Named на месте вызова.
var Arch01 = `SELECT r.ID, b.NUM_S, ISNULL(b.SYS_NAME,''), b.NUM_1, ISNULL(r.REG_2,0), ISNULL(r.VALUE_ID,0),
CAST(ISNULL(r.A,0) AS float), CAST(ISNULL(r.D,0) AS float), CAST(ISNULL(r.EQ_D,0) AS float),
ISNULL(vs.ISIN,''), ISNULL(vs.NAME,''), ISNULL(vc.SYSNAME,''), ISNULL(sh.CLASS,0), CAST(ISNULL(sh.NOMINAL,0) AS float)
FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC
LEFT JOIN OD_VALUES vs ON vs.ID = r.REG_2
LEFT JOIN OD_VALUES vc ON vc.ID = r.VALUE_ID
LEFT JOIN OD_SHARES sh ON sh.ID = r.REG_2
WHERE r.REG_3 = @c AND b.IS_BAL = 1 AND b.PART = 39 AND b.NUM_1 IN (50,51,52,55,58)
ORDER BY b.NUM_1, vs.NAME`
var Arch02 = `SELECT REST,
CAST(SUM(CASE WHEN TYPE_ = 1 THEN AMOUNT_ ELSE -AMOUNT_ END) AS float),
CAST(SUM(CASE WHEN TYPE_ = 1 THEN VALUE_ ELSE -VALUE_ END) AS float),
CAST(SUM(CASE WHEN TYPE_ = 1 THEN EQ_ ELSE -EQ_ END) AS float)
FROM OD_TURNS WHERE REST IN (%s) AND WIRDATE >= @d AND IS_PLAN = 'F' AND YEAR(WIRDATE) < 9000
GROUP BY REST`
var Arch03 = `SELECT MAX(R_DATE) FROM OD_DOCS WHERE YEAR(R_DATE) < 9000`
var Arch04 = `SELECT TOP 1 OFICDATE, CAST(RATE3 AS float), CAST(PFR_PRICE AS float), CAST(CLOSE_ AS float)
FROM OD_VALUES_RATES WHERE VALUE_ID = @v AND OFICDATE <= @d AND (RATE3 > 0 OR PFR_PRICE > 0 OR CLOSE_ > 0)
ORDER BY OFICDATE DESC`
var Arch05 = `SELECT TOP 1 CAST(RATE AS float) FROM OD_VALUES_RATES WHERE VALUE_ID = @v AND MARKET = 0 AND OFICDATE <= @d AND RATE > 0 ORDER BY OFICDATE DESC`
var Arch06 = `SELECT r.ID, b.NUM_1, ISNULL(b.SYS_NAME,'') FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC
WHERE r.REG_3 = @c AND b.PART = 39 AND ((b.NUM_1 = 58 AND b.SYS_NAME LIKE N'ПОРТФЕЛЬ%') OR b.NUM_1 IN (50,51,52,55))`
var Arch07 = `
WITH w AS (
SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_AMOUNT, wr.K_AMOUNT, wr.NAME,
wr.D_REST AS sec_rest, wr.K_REST AS money_rest
FROM OD_WIRING wr
WHERE wr.D_REST IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000
AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND (wr.D_AMOUNT <> 0 OR wr.K_AMOUNT <> 0)
UNION ALL
SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_AMOUNT, wr.K_AMOUNT, wr.NAME,
wr.K_REST, wr.D_REST
FROM OD_WIRING wr
WHERE wr.K_REST IN (%[1]s) AND wr.D_REST NOT IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000
AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND (wr.D_AMOUNT <> 0 OR wr.K_AMOUNT <> 0))
SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST,
CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float),
CAST(ISNULL(w.D_AMOUNT,0) AS float), CAST(ISNULL(w.K_AMOUNT,0) AS float),
ISNULL(w.NAME,''), ISNULL(sv.NAME,''), ISNULL(sv.ISIN,''), ISNULL(mv.SYSNAME,'')
FROM w
LEFT JOIN OD_RESTS sr ON sr.ID = w.sec_rest
LEFT JOIN OD_VALUES sv ON sv.ID = sr.REG_2
LEFT JOIN OD_RESTS mr ON mr.ID = w.money_rest
LEFT JOIN OD_VALUES mv ON mv.ID = mr.VALUE_ID
ORDER BY w.WIRDATE`
var Arch08 = `SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST,
CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float), ISNULL(w.NAME,''), ISNULL(mv.SYSNAME,'')
FROM OD_WIRING w
JOIN OD_RESTS mr ON mr.ID = CASE WHEN w.D_REST IN (%[1]s) THEN w.D_REST ELSE w.K_REST END
LEFT JOIN OD_VALUES mv ON mv.ID = mr.VALUE_ID
WHERE (w.D_REST IN (%[1]s) OR w.K_REST IN (%[1]s)) AND w.IS_PLAN = 'F' AND YEAR(w.WIRDATE) < 9000
AND w.WIRDATE >= @f AND w.WIRDATE < @t
ORDER BY w.WIRDATE`
var Arch09 = `SELECT r.ID FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC WHERE r.REG_3 = @c AND b.SYS_NAME = N'ВЫРУЧКА'`
var Arch10 = `
WITH w AS (
SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_REST AS sec_rest
FROM OD_WIRING wr
WHERE wr.K_REST IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000
AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND wr.EQ_ <> 0
UNION ALL
SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.K_REST
FROM OD_WIRING wr
WHERE wr.D_REST IN (%[1]s) AND wr.K_REST NOT IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000
AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND wr.EQ_ <> 0)
SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST,
CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float), ISNULL(sv.ISIN,'')
FROM w
LEFT JOIN OD_RESTS sr ON sr.ID = w.sec_rest
LEFT JOIN OD_VALUES sv ON sv.ID = sr.REG_2`
+28
View File
@@ -0,0 +1,28 @@
package fsql
var Chk01 = `SELECT TOP 1 ISNULL(NULLIF(SELF_ID,0),ID), ISNULL(NAME,''), DATE_BIRTH FROM OD_FACES WHERE REPLACE(ISNULL(DOC_SER,''),' ','')=@p1 AND DOC_NUM=@p2
ORDER BY ISNULL(LAST_FLAG,0) DESC, ID DESC`
var Chk02 = `SELECT ID, ISNULL(NUMBER,''), ISNULL(STATE,-1), D_CLOSE FROM OD_CLIENTS WHERE FACE=@p1 AND NUMBER LIKE N'ВТНКЦ%' ORDER BY ID`
var Chk03 = `SELECT TOP 1 CAST(o.VAL AS varchar(20)) FROM OD_OPTIONS o
JOIN OP_FIELDS f ON f.ID = o.DESCR AND f.AS_NAME = 'IS_QUAL'
WHERE o.OBJECT = @p1 AND o.B_DATE <= GETDATE() AND (o.E_DATE IS NULL OR o.E_DATE > GETDATE())
ORDER BY o.B_DATE DESC`
var Chk04 = `SELECT ISNULL(PORT_NUM,'') FROM OD_CLIENTS_PORTS WHERE CLIENT=@p1`
var Chk05 = `SELECT TOP 1 ID, ISNULL(NAME,''), IS_IN FROM OD_VALUES WHERE ISIN=@p1`
var Chk06 = `SELECT TOP 1 CAST(o.VAL AS varchar(20)) FROM OD_OPTIONS o
JOIN OP_FIELDS f ON f.ID = o.DESCR AND f.AS_NAME = 'RESTRICTED'
WHERE o.OBJECT = @p1 AND o.B_DATE <= GETDATE() AND (o.E_DATE IS NULL OR o.E_DATE > GETDATE())
ORDER BY o.B_DATE DESC`
var Chk07 = `SELECT TOP 1 r.BLOCK_DATE, r.BLOCK_REASON, r.BAL_ACC, CAST(r.K AS float)
FROM OD_RESTS r WHERE r.VALUE_ID=@p1 AND r.REG_1=@p2
ORDER BY CASE WHEN r.BLOCK_DATE IS NOT NULL THEN 0 WHEN r.BAL_ACC IN (837,3410,4342,5647) AND CAST(r.K AS float)<>0 THEN 1 ELSE 2 END`
var Chk08 = `SELECT TOP 1 ID, ISNULL(STATE,-1), D_CLOSE FROM OD_CLIENTS WHERE FACE=@p1 AND UPPER(NUMBER)=@p2 ORDER BY ID`
var Chk09 = `SELECT ISNULL(SUM(CAST(K AS float)),0) FROM OD_RESTS WHERE VALUE_ID=@p1 AND REG_1=@p2 AND BAL_ACC=@p3`
+10
View File
@@ -0,0 +1,10 @@
package fsql
var Sim01 = `SELECT TOP 25 ISNULL(f.NAME,''), ISNULL(f.DOC_SER,''), ISNULL(f.DOC_NUM,''), ISNULL(c.NUMBER,'')
FROM OD_FACES f JOIN OD_CLIENTS c ON c.FACE=f.ID
WHERE c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL
AND LEN(ISNULL(f.DOC_SER,''))>0 AND LEN(ISNULL(f.DOC_NUM,''))>0
ORDER BY f.NAME`
var Sim02 = `SELECT TOP 20 ISNULL(ISIN,''), ISNULL(NAME,'') FROM OD_VALUES
WHERE V_TYPE=2 AND IS_IN=1 AND ISIN LIKE 'RU%' ORDER BY NAME`
+19
View File
@@ -0,0 +1,19 @@
package fsql
var Dp01 = `SELECT p.PORT_NUM, ISNULL(c.NUMBER,''), ISNULL(v.ISIN,''), ISNULL(v.NAME,''),
CAST(rst.K AS float), ISNULL(rst.BLOCK_REASON,''), ISNULL(rst.BAL_ACC,0)
FROM OD_RESTS rst
JOIN OD_CLIENTS c ON c.ID = rst.REG_1
JOIN OD_CLIENTS_PORTS p ON p.CLIENT = c.ID
JOIN OD_VALUES v ON v.ID = rst.VALUE_ID AND v.V_TYPE = 2
WHERE (p.PORT_NUM = @p1 OR p.PORT_NUM LIKE @p2)
AND CAST(rst.K AS float) <> 0
ORDER BY v.NAME`
var Dp02 = `SELECT TOP (%d) p.PORT_NUM, COUNT(DISTINCT rst.VALUE_ID)
FROM OD_CLIENTS_PORTS p
JOIN OD_CLIENTS c ON c.ID = p.CLIENT
JOIN OD_RESTS rst ON rst.REG_1 = c.ID
JOIN OD_VALUES v ON v.ID = rst.VALUE_ID AND v.V_TYPE = 2
WHERE CAST(rst.K AS float) <> 0 AND (@p1 = '' OR p.PORT_NUM LIKE @p2)
GROUP BY p.PORT_NUM ORDER BY p.PORT_NUM`
+7
View File
@@ -0,0 +1,7 @@
package fsql
var QDb01 = "SELECT TOP 1 PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM=@p1 OR PORT_NUM LIKE @p2"
var QDb02 = "SELECT DISTINCT TOP (%d) PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM LIKE @p1 ORDER BY PORT_NUM"
var QDb03 = "SELECT DISTINCT TOP (5000) PORT_NUM FROM OD_CLIENTS_PORTS WHERE PORT_NUM <> '' ORDER BY PORT_NUM"
+31
View File
@@ -0,0 +1,31 @@
package fsql
var Ev01 = `INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE, M2M_REJECT_CODE) VALUES (@g, @r, @t, @at, @c)`
var Ev02 = `INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE, M2M_REJECT_CODE, PARAMETERS) VALUES (@g, @r, @t, @at, @c, @p)`
var Ev03 = `SELECT TOP 1 c.NUMBER FROM OD_FACES f
JOIN OD_CLIENTS c ON c.FACE=f.ID LEFT JOIN OD_CLIENTS_PORTS p ON p.CLIENT=c.ID
WHERE REPLACE(f.DOC_SER,' ','')=@s AND f.DOC_NUM=@n AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL
ORDER BY ISNULL(p.PORT_DATE,'2099-01-01'), ISNULL(c.D_OPEN,'2099-01-01'), c.NUMBER`
var Ev04 = `INSERT INTO dbo.FTV_M2M (GUID, DIRECTION, CONTRAGENT_M2M_CODE, DEPONENT_NAME, DEPONENT_DOC) VALUES (@g, @d, @cp, @nm, @doc)`
var Ev05 = `INSERT INTO dbo.FTV_M2M (GUID, DIRECTION, CONTRAGENT_M2M_CODE, DEPONENT_NAME, DEPONENT_DOC, DEPONENT_ID) VALUES (@g, @d, @cp, @nm, @doc, @did)`
var Ev06 = `INSERT INTO dbo.FTV_M2M_LINES (`
var Ev07 = `SELECT TOP 200 CAST(m.GUID AS varchar(36)), ISNULL(m.STATE,''), ISNULL(`
var Ev08 = `,''), ISNULL(m.COMMENT,''),
ISNULL((SELECT TOP 1 ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID=m.GUID AND ISNULL(l.STATE,'')='ERROR'),''),
ISNULL((SELECT TOP 1 ISNULL(st.NAME,'') FROM OD_DOCS d LEFT JOIN OD_DOC_STATES st ON st.ID=d.STATE AND st.D_CAT=0 WHERE CAST(d.ID AS varchar(32))=`
var Ev09 = `),''),
(SELECT TOP 1 d.PROC_DATE FROM OD_DOCS d WHERE CAST(d.ID AS varchar(32))=`
var Ev10 = `INSERT INTO dbo.FTV_M2M_HANDBOOK (INN, PARTICIPANT_CODE, KIND, NAME_FULL, NAME_SHORT, NAME_DISPLAY, NAME_ENG, HANDBOOK_AT, UPDATED_AT, ACTIVE)
VALUES (@inn, @code, @kind, @full, @short, @disp, @eng, @at, GETDATE(), 1)`
var Ev11 = `SELECT TOP 1 f.NAME FROM OD_FACES f JOIN OD_CLIENTS c ON c.FACE=f.ID
WHERE REPLACE(f.DOC_SER,' ','')=@s AND f.DOC_NUM=@n AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0`
+17
View File
@@ -0,0 +1,17 @@
package fsql
var QEv01 = "SELECT COUNT(*) FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g AND ISNULL(REFERENCE,'')=@r AND EVENT_TYPE=@t"
var QEv02 = "SELECT COUNT(*) FROM dbo.FTV_M2M WHERE GUID=@g"
var QEv03 = "SELECT TOP 1 SELF_ID FROM OD_FACES WHERE REPLACE(DOC_SER,' ','')=@s AND DOC_NUM=@n ORDER BY ID"
var QEv04 = "SELECT TOP 1 ID FROM OD_VALUES WHERE ISIN=@i ORDER BY ID"
var QEv05 = "DELETE FROM dbo.FTV_M2M_HANDBOOK"
var QEv06 = "INSERT INTO dbo.FTV_M2M_EVENTS (GUID, REFERENCE, EVENT_TYPE, EVENT_DATE) VALUES (@g, NULL, 'BJ_PING', GETDATE())"
var QEv07 = "DELETE FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g"
var QEv08 = "SELECT ISNULL(STATE,'') FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g"
+13
View File
@@ -0,0 +1,13 @@
package fsql
var Hlth01 = `SELECT TOP (@n) * FROM (
SELECT 'FTV_M2M' AS T, ID, CAST(GUID AS varchar(36)) AS G, '' AS R, ISNULL(DIRECTION,'') AS K, ISNULL(TimeEnd, TimeNew) AS A, ISNULL(COMMENT,'') AS C
FROM dbo.FTV_M2M WHERE STATE='ERROR' AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -@d, GETDATE())
UNION ALL
SELECT 'FTV_M2M_EVENTS', ID, CAST(GUID AS varchar(36)), ISNULL(REFERENCE,''), EVENT_TYPE, ISNULL(TimeEnd, TimeNew), ISNULL(COMMENT,'')
FROM dbo.FTV_M2M_EVENTS WHERE STATE='ERROR' AND EVENT_TYPE LIKE 'BJ[_]%' AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -@d, GETDATE())
) x ORDER BY A DESC`
var Hlth02 = `SELECT COUNT(*) OVER (), ID, EVENT_TYPE, CAST(GUID AS varchar(36)), ISNULL(COMMENT,'')
FROM dbo.FTV_M2M_EVENTS WHERE STATE='ERROR' AND EVENT_TYPE LIKE 'BJ[_]%' AND ID > @last AND ISNULL(TimeEnd, TimeNew) > DATEADD(day, -1, GETDATE())
ORDER BY ID DESC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY`
+7
View File
@@ -0,0 +1,7 @@
package fsql
var QHl01 = "UPDATE dbo.FTV_M2M SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL, COMMENT=@c WHERE ID=@id AND STATE='ERROR'"
var QHl02 = "UPDATE dbo.FTV_M2M_EVENTS SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL, COMMENT=@c WHERE ID=@id AND STATE='ERROR'"
var QHl03 = "UPDATE dbo.FTV_M2M_LINES SET STATE='TODO', TimeBeg=NULL, TimeEnd=NULL WHERE GUID=@g AND STATE='ERROR'"
+13
View File
@@ -0,0 +1,13 @@
package fsql
var Que01 = `SELECT TOP (@n) CAST(m.GUID AS varchar(36)), ISNULL(m.STATE,''), ISNULL(m.DIRECTION,''), ISNULL(m.DEPONENT_NAME,''), ISNULL(m.DEPONENT_DOC,''), ISNULL(m.CONTRAGENT_M2M_CODE,''), `
var Que02 = `,''), ISNULL(m.COMMENT,''),
ISNULL((SELECT TOP 1 ISNULL(st.NAME,'') FROM OD_DOCS d LEFT JOIN OD_DOC_STATES st ON st.ID=d.STATE AND st.D_CAT=0 WHERE CAST(d.ID AS varchar(32))=`
var Que03 = `),''),
(SELECT TOP 1 d.PROC_DATE FROM OD_DOCS d WHERE CAST(d.ID AS varchar(32))=`
var Que04 = `SELECT CAST(l.GUID AS varchar(36)), ISNULL(l.ISIN,''), ISNULL(l.SEC_CODE,''), ISNULL(CAST(l.AMOUNT AS varchar(40)),''), ISNULL(l.STATE,''), ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID IN (SELECT TOP (@n) GUID FROM dbo.FTV_M2M ORDER BY `
var Que05 = `SELECT CAST(e.GUID AS varchar(36)), ISNULL(e.REFERENCE,''), ISNULL(e.STATE,''), ISNULL(e.EVENT_TYPE,''), e.EVENT_DATE, ISNULL(e.M2M_REJECT_CODE,''), ISNULL(e.COMMENT,'') FROM dbo.FTV_M2M_EVENTS e WHERE e.GUID IN (SELECT TOP (@n) GUID FROM dbo.FTV_M2M ORDER BY `
+5
View File
@@ -0,0 +1,5 @@
package fsql
var QQu01 = "SELECT count(*) FROM dbo.FTV_M2M_HANDBOOK"
var QQu02 = "SELECT max(HANDBOOK_AT) FROM dbo.FTV_M2M_HANDBOOK"
+5
View File
@@ -0,0 +1,5 @@
package fsql
var Tbl01 = `GRANT SELECT ON ALL TABLES IN SCHEMA public TO "%s"`
var Tbl02 = `GRANT SELECT, INSERT, UPDATE, DELETE ON %s TO "%s"`
+3
View File
@@ -0,0 +1,3 @@
package fsql
var QTb01 = "-- Кроме того, логину bj нужно право SELECT на учётные таблицы DEPO (OD_CLIENTS, OD_CLIENTS_PORTS, OD_RESTS, OD_VALUES и др.) для сверки переводов.\n"
@@ -0,0 +1,3 @@
package fsql
var QIn01 = "SELECT CAST(DOC AS varchar(32)) FROM dbo.FTV_M2M WHERE GUID=@g"
+4
View File
@@ -0,0 +1,4 @@
package fsql
var Cl01 = `SELECT TOP 1 DOC, INVESTOR, NAME, C_OPEN, C_CLOSE, C_STATE FROM D_B_CONTRACTS
WHERE UPPER(NAME) = @n OR UPPER(NAME) LIKE @n + ' %' ORDER BY C_STATE DESC, DOC DESC`
+35
View File
@@ -0,0 +1,35 @@
package fsql
var Doc01 = `SELECT TOP 40 ISNULL(a.KD_TYPE,''), ISNULL(v.ISIN,''), ISNULL(v.NAME,''), a.FIX_DATE, a.OPER_DATE, ISNULL(a.OFFER_NAME,'')
FROM U_CA_ACTS a JOIN OD_VALUES v ON v.ID = a.SHARE
WHERE UPPER(v.ISIN) IN (%s)
AND ISNULL(a.FIX_DATE, a.OPER_DATE) >= DATEADD(month, -1, GETDATE())
ORDER BY ISNULL(a.FIX_DATE, a.OPER_DATE)`
var Doc02 = `
CREATE TABLE IF NOT EXISTS lk.doc_requests (
id bigserial PRIMARY KEY,
created_at timestamptz NOT NULL DEFAULT now(),
contract_number text NOT NULL,
client_name text,
kind text NOT NULL,
period_from date,
period_to date,
account text,
comment text,
status text NOT NULL DEFAULT 'new',
staff_note text,
done_at timestamptz
);
CREATE INDEX IF NOT EXISTS ix_lk_doc_requests_contract ON lk.doc_requests (contract_number, created_at DESC);
`
var Doc03 = `INSERT INTO lk.doc_requests (contract_number, client_name, kind, period_from, period_to, account, comment)
VALUES ($1,$2,$3,$4,$5,$6,$7)`
var Doc04 = `SELECT id, created_at, kind, period_from, period_to, COALESCE(account,''), status, COALESCE(staff_note,'')
FROM lk.doc_requests WHERE contract_number=$1 ORDER BY created_at DESC LIMIT 50`
var Doc05 = `SELECT TOP 30 d.R_DATE, ISNULL(d.NUM,''), e.B_DATE, e.E_DATE, e.DEPO, d.STATE, ISNULL(e.IS_TURNS,0)
FROM U_OP_INF_EXTRACT e JOIN OD_DOCS d ON d.ID = e.DOC
WHERE e.DEPO IN (%s) ORDER BY d.R_DATE DESC`
+7
View File
@@ -0,0 +1,7 @@
package fsql
var Flow01 = `SELECT ISNULL(`
var Flow02 = `,''), ISNULL(m.STATE,''), ISNULL(m.COMMENT,''),
ISNULL((SELECT TOP 1 ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID=m.GUID AND ISNULL(l.STATE,'')='ERROR'),'')
FROM dbo.FTV_M2M m WHERE m.GUID=@g`
+3
View File
@@ -0,0 +1,3 @@
package fsql
var QFl01 = "SELECT ISNULL(STATE,''), ISNULL(COMMENT,'') FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g AND EVENT_TYPE='BJ_GATEWAY_REJECT'"
+6
View File
@@ -0,0 +1,6 @@
package fsql
var Fx01 = `SELECT UPPER(LTRIM(RTRIM(v.SYSNAME))), r.OFICDATE, CAST(r.RATE AS float)
FROM OD_VALUES_RATES r JOIN OD_VALUES v ON v.ID = r.VALUE_ID
WHERE v.V_TYPE = 1 AND r.MARKET = 0 AND r.RATE > 0
AND r.OFICDATE = (SELECT MAX(x.OFICDATE) FROM OD_VALUES_RATES x WHERE x.VALUE_ID = r.VALUE_ID AND x.MARKET = 0 AND x.RATE > 0)`
+47
View File
@@ -0,0 +1,47 @@
package fsql
var Hist01 = `SELECT DISTINCT c.ID, ISNULL(c.NUMBER,''), ISNULL(c.FACE,0)
FROM OD_CLIENTS c JOIN OD_CLIENTS_PORTS p ON p.CLIENT = c.ID
WHERE p.PORT_NUM = @p1 OR p.PORT_NUM LIKE @p2`
var Hist02 = `SELECT TOP %d * FROM (
SELECT d.R_DATE AS RD, d.PROC_DATE AS PD, ISNULL(d.NUM,'') AS NUM, d.STATE AS ST, N'зачисление' AS KIND,
z.DEPO AS ACC, ISNULL(v.ISIN,'') AS ISIN, ISNULL(v.NAME,'') AS SEC, CAST(ISNULL(z.AMOUNT,0) AS float) AS QTY,
LTRIM(RTRIM(ISNULL(z.COMMENT,'') + ' ' + ISNULL(z.REJECT_REASON,''))) AS NOTE
FROM U_OP_INV_ZACH z JOIN OD_DOCS d ON d.ID = z.DOC LEFT JOIN OD_VALUES v ON v.ID = z.SHARE
WHERE z.DEPO IN (%s)
UNION ALL
SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'списание',
p.DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), -CAST(ISNULL(p.AMOUNT,0) AS float),
LTRIM(RTRIM(ISNULL(p.COMMENT,'') + ' ' + ISNULL(p.REJECT_REASON,'')))
FROM U_OP_INV_SPIS p JOIN OD_DOCS d ON d.ID = p.DOC LEFT JOIN OD_VALUES v ON v.ID = p.SHARE
WHERE p.DEPO IN (%s)
UNION ALL
SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'перевод: списание',
t.D_DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), -CAST(ISNULL(t.AMOUNT,0) AS float),
LTRIM(RTRIM(ISNULL(t.COMMENT,'') + ' ' + ISNULL(t.REJECT_REASON,'')))
FROM U_OP_INV_PEREVOD t JOIN OD_DOCS d ON d.ID = t.DOC LEFT JOIN OD_VALUES v ON v.ID = t.SHARE
WHERE t.D_DEPO IN (%s)
UNION ALL
SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'перевод: зачисление',
t.K_DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), CAST(ISNULL(t.AMOUNT,0) AS float),
LTRIM(RTRIM(ISNULL(t.COMMENT,'') + ' ' + ISNULL(t.REJECT_REASON,'')))
FROM U_OP_INV_PEREVOD t JOIN OD_DOCS d ON d.ID = t.DOC LEFT JOIN OD_VALUES v ON v.ID = t.SHARE
WHERE t.K_DEPO IN (%s)) x
WHERE x.RD >= @p1 AND x.RD < DATEADD(day, 1, @p2)
ORDER BY x.RD DESC, x.NUM DESC`
var Hist03 = `SELECT TOP 200 h.P_DATE, h.INCOME_KIND, ISNULL(v.ISIN,''), ISNULL(v.NAME,''),
CAST(ISNULL(l.AMOUNT,0) AS float), CAST(ISNULL(l.SUMMA_TOTAL,0) AS float),
CAST(ISNULL(l.TAX_HOLDED,0) AS float), CAST(ISNULL(l.TOTAL_TO_PAY,0) AS float),
ISNULL(l.STATUS,''), ISNULL(l.DEPO,0)
FROM U_OP_INF_INCOME_DOL l
JOIN OD_DOLS o ON o.ID = l.DOL
JOIN U_OP_INF_INCOME h ON h.DOC = o.DOC
LEFT JOIN OD_VALUES v ON v.ID = h.SHARE
WHERE (l.DEPO IN (%s) OR l.INCOME_FACE = @p3)
AND h.P_DATE >= @p1 AND h.P_DATE < DATEADD(day, 1, @p2)
ORDER BY h.P_DATE DESC`
var Hist04 = `SELECT CAST(ISNULL(SUM(CAST(SUMMA AS float)),0) AS float) FROM OD_NDFL
WHERE FACE = @p1 AND R_DATE >= @p2 AND R_DATE < DATEADD(day, 1, @p3)`
+22
View File
@@ -0,0 +1,22 @@
package fsql
var On01 = `
WITH d AS (
SELECT DISTINCT ON ("ClientCode","SecCode","Trdacc") "SecCode","CurrentBal","AwgPositionPrice","LoadDate"
FROM "DepoLimits" WHERE "ClientCode" = ANY($1) AND "LimitKind"='T2'
ORDER BY "ClientCode","SecCode","Trdacc","LoadDate" DESC)
SELECT "SecCode", SUM("CurrentBal")::float8,
COALESCE(SUM("CurrentBal"*"AwgPositionPrice")/NULLIF(SUM("CurrentBal"),0),0)::float8, MAX("LoadDate")
FROM d WHERE "CurrentBal" <> 0 GROUP BY "SecCode" ORDER BY "SecCode"`
var On02 = `
SELECT DISTINCT ON ("SecCode") "SecCode", upper(COALESCE("ISINCode",'')), COALESCE("SecShortName","SecName",''), "ClassCode", COALESCE("FaceValue",0)::float8
FROM "Securities" WHERE "SecCode" = ANY($1)
ORDER BY "SecCode", CASE "ClassCode" WHEN 'TQBR' THEN 1 WHEN 'TQOB' THEN 2 WHEN 'TQCB' THEN 3 WHEN 'TQTF' THEN 4 WHEN 'TQIF' THEN 5 WHEN 'TQRD' THEN 6 ELSE 9 END`
var On03 = `
WITH m AS (
SELECT DISTINCT ON ("ClientCode","CurrCode","Tag") "CurrCode","CurrentBal","LoadDate"
FROM "MoneyLimits" WHERE "ClientCode" = ANY($1) AND "LimitKind"='T2'
ORDER BY "ClientCode","CurrCode","Tag","LoadDate" DESC)
SELECT "CurrCode", SUM("CurrentBal")::float8, MAX("LoadDate") FROM m GROUP BY "CurrCode" HAVING SUM("CurrentBal") <> 0 ORDER BY "CurrCode"`
+22
View File
@@ -0,0 +1,22 @@
package fsql
var Sn01 = `
CREATE TABLE IF NOT EXISTS lk.portfolio_daily (
day DATE NOT NULL, contract VARCHAR(64) NOT NULL,
value DOUBLE PRECISION NOT NULL DEFAULT 0, cash DOUBLE PRECISION NOT NULL DEFAULT 0,
positions INT NOT NULL DEFAULT 0, priced INT NOT NULL DEFAULT 0,
source VARCHAR(16) NOT NULL DEFAULT 'quik', created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (day, contract))`
var Sn02 = `SELECT day, value, cash, positions FROM lk.portfolio_daily
WHERE contract = $1 AND day >= current_date - $2::int ORDER BY day`
var Sn03 = `INSERT INTO lk.portfolio_daily(day, contract, value, cash, positions, priced, source)
VALUES ($1,$2,$3,$4,$5,$6,$7)
ON CONFLICT (day, contract) DO UPDATE SET value = EXCLUDED.value, cash = EXCLUDED.cash,
positions = EXCLUDED.positions, priced = EXCLUDED.priced, source = EXCLUDED.source, created_at = now()`
var Sn04 = `SELECT DISTINCT "ClientCode" FROM "DepoLimits"
WHERE "LimitKind" = 'T2' AND "CurrentBal" <> 0 AND "ClientCode" <> ''`
var Sn05 = `SELECT count(*) FROM lk.portfolio_daily WHERE day = $1`
+17
View File
@@ -0,0 +1,17 @@
package fsql
var Up01 = `SELECT UPPER(v.ISIN), ISNULL(c.P_DATE, c.E_DATE), CAST(ISNULL(c.SUMMA,0) AS float), CAST(ISNULL(c.PERCENT_,0) AS float)
FROM OD_COUPONS c JOIN OD_VALUES v ON v.ID = c.SHARE
WHERE UPPER(v.ISIN) IN (%s) AND ISNULL(c.P_DATE, c.E_DATE) >= @f AND ISNULL(c.P_DATE, c.E_DATE) <= @t`
var Up02 = `SELECT UPPER(v.ISIN), o.P_DATE, CAST(ISNULL(o.O_PRICE,0) AS float)
FROM OD_OFFERS o JOIN OD_VALUES v ON v.ID = o.SHARE
WHERE UPPER(v.ISIN) IN (%s) AND o.CANCEL_DATE IS NULL AND o.P_DATE >= @f AND o.P_DATE <= @t`
var Up03 = `SELECT UPPER(v.ISIN), sh.DEATHDATE, CAST(ISNULL(sh.NOMINAL,0) AS float)
FROM OD_SHARES sh JOIN OD_VALUES v ON v.ID = sh.ID
WHERE UPPER(v.ISIN) IN (%s) AND sh.E_DATE > GETDATE() AND sh.DEATHDATE >= @f AND sh.DEATHDATE <= @t`
var Up04 = `SELECT UPPER(v.ISIN), ISNULL(a.OPER_DATE, a.FIX_DATE)
FROM U_CA_ACTS a JOIN OD_VALUES v ON v.ID = a.SHARE
WHERE UPPER(v.ISIN) IN (%s) AND UPPER(ISNULL(a.KD_TYPE,'')) = 'DVCA' AND ISNULL(a.OPER_DATE, a.FIX_DATE) >= @f AND ISNULL(a.OPER_DATE, a.FIX_DATE) <= @t`
+19
View File
@@ -0,0 +1,19 @@
package fsql
var Man01 = `SELECT TOP 20 ISNULL(NULLIF(f.SELF_ID,0),f.ID), ISNULL(f.NAME,''), f.DATE_BIRTH, ISNULL(f.DOC_TYPE,0), ISNULL(f.DOC_SER,''), ISNULL(f.DOC_NUM,''),
f.DOC_DAT, ISNULL(f.DOC_PLACE,''), ISNULL(f.DOC_CODE,''), ISNULL(f.PHONE,'')
FROM OD_FACES f
WHERE ISNULL(f.LAST_FLAG,1)=1 AND (UPPER(f.NAME) LIKE UPPER(@q) OR f.DOC_NUM=@d OR REPLACE(ISNULL(f.DOC_SER,''),' ','')+f.DOC_NUM=@d)
AND EXISTS (SELECT 1 FROM OD_CLIENTS c WHERE c.FACE=ISNULL(NULLIF(f.SELF_ID,0),f.ID) AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL)
ORDER BY f.NAME`
var Man02 = `SELECT c.ID, ISNULL(c.NUMBER,''),
ISNULL((SELECT TOP 1 p.PORT_NUM FROM OD_CLIENTS_PORTS p WHERE p.CLIENT=c.ID ORDER BY p.PORT_DATE, p.PORT_NUM),'')
FROM OD_CLIENTS c WHERE c.FACE=@f AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL ORDER BY c.ID`
var Man03 = `SELECT ISNULL(v.ISIN,''), ISNULL(v.NAME,''), SUM(CAST(r.K AS float))
FROM OD_RESTS r JOIN OD_VALUES v ON v.ID=r.VALUE_ID
WHERE r.REG_1=@c AND r.BAL_ACC=@b
GROUP BY v.ISIN, v.NAME HAVING SUM(CAST(r.K AS float)) > 0 ORDER BY v.NAME`
var Man04 = `SELECT TOP 1 ID FROM OD_CLIENTS WHERE NUMBER=@n AND STATE=0 AND D_CLOSE IS NULL ORDER BY ID`
+13
View File
@@ -0,0 +1,13 @@
package fsql
var Pr01 = `
SELECT DISTINCT ON (s."ISINCode")
upper(s."ISINCode"), s."ClassCode", COALESCE(s."SecShortName", s."SecName", ''),
COALESCE(p.last, 0)::float8, COALESCE(p.prevprice, 0)::float8,
COALESCE(s."FaceValue", 0)::float8, COALESCE(p.accruedint, 0)::float8, COALESCE(s."FaceUnit", '')
FROM "Securities" s JOIN "Params" p ON p."SecCode" = s."SecCode" AND p."ClassCode" = s."ClassCode"
WHERE s."ISINCode" <> '' AND (p.last > 0 OR p.prevprice > 0)
ORDER BY s."ISINCode",
CASE s."ClassCode" WHEN 'TQBR' THEN 1 WHEN 'TQOB' THEN 2 WHEN 'TQCB' THEN 3
WHEN 'TQTF' THEN 4 WHEN 'TQIF' THEN 5 WHEN 'TQRD' THEN 6 ELSE 9 END,
p.last DESC`
+9
View File
@@ -0,0 +1,9 @@
package fsql
var Qk01 = `SELECT ic.ClassCode, sec.SecCode FROM ISecurities sec JOIN IClasses ic ON ic.ClassId=sec.ClassId
WHERE sec.Enable=1 AND ic.ClassCode IN ('INSTRINM2M','INSTROUTM2M') ORDER BY sec.SecurityId`
var Qk02 = `SELECT TOP 5 p.PORT_NUM FROM OD_FACES f
JOIN OD_CLIENTS c ON c.FACE=f.ID JOIN OD_CLIENTS_PORTS p ON p.CLIENT=c.ID
WHERE REPLACE(f.DOC_SER,' ','')=@p1 AND f.DOC_NUM=@p2 AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0
ORDER BY ISNULL(p.PORT_DATE,'2099-01-01'), ISNULL(c.D_OPEN,'2099-01-01'), c.NUMBER`
+16
View File
@@ -0,0 +1,16 @@
package fsql
var Sec01 = `SELECT TOP 1 ID, ISNULL(NAME,''), ISNULL(CFI,''), V_TYPE, IS_IN FROM OD_VALUES WHERE ISIN=@p1 ORDER BY ID`
var Sec02 = `SELECT TOP 1 ISSUER, CAST(NOMINAL AS float), NUM_REG, DEATHDATE FROM OD_SHARES
WHERE ID=@p1 AND E_DATE > GETDATE() ORDER BY B_DATE DESC`
var Sec03 = `SELECT TOP 1 ISNULL(NAME,'') FROM OD_FACES WHERE ID=@p1 OR SELF_ID=@p1 ORDER BY ISNULL(LAST_FLAG,0) DESC, ID DESC`
var Sec04 = `SELECT TOP 12 ISNULL(P_DATE, E_DATE) AS PAY_DATE, CAST(ISNULL(SUMMA,0) AS float),
CAST(ISNULL(PERCENT_,0) AS float), FIX_DATE, ISNULL(IS_PAY,0)
FROM OD_COUPONS WHERE SHARE=@p1 AND ISNULL(P_DATE, E_DATE) >= DATEADD(month,-3,GETDATE())
ORDER BY PAY_DATE`
var Sec05 = `SELECT TOP 3 P_DATE, CAST(ISNULL(O_PRICE,0) AS float) FROM OD_OFFERS
WHERE SHARE=@p1 AND CANCEL_DATE IS NULL AND P_DATE >= GETDATE() ORDER BY P_DATE`
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"encoding/json"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"net/http"
"net/url"
"path"
@@ -72,7 +73,7 @@ func registerIncoming(mux *http.ServeMux, a *admin, deps incomingDeps) {
for i := 0; i < 8; i++ {
time.Sleep(5 * time.Second)
var d sql.NullString
_ = db.QueryRowContext(ctx, "SELECT CAST(DOC AS varchar(32)) FROM dbo.FTV_M2M WHERE GUID=@g", sql.Named("g", guid)).Scan(&d)
_ = db.QueryRowContext(ctx, fsql.QIn01, sql.Named("g", guid)).Scan(&d)
if d.Valid && d.String != "" && d.String != "0" {
doc = d.String
break
+11 -70
View File
@@ -19,6 +19,7 @@ import (
"context"
"database/sql"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"sort"
"strings"
"sync"
@@ -63,15 +64,7 @@ func lkArchivePortfolio(ctx context.Context, s FancyMSSQLSettings, contract stri
today := time.Now()
isPast := day.Before(time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, time.UTC))
rows, err := conn.QueryContext(cctx, `SELECT r.ID, b.NUM_S, ISNULL(b.SYS_NAME,''), b.NUM_1, ISNULL(r.REG_2,0), ISNULL(r.VALUE_ID,0),
CAST(ISNULL(r.A,0) AS float), CAST(ISNULL(r.D,0) AS float), CAST(ISNULL(r.EQ_D,0) AS float),
ISNULL(vs.ISIN,''), ISNULL(vs.NAME,''), ISNULL(vc.SYSNAME,''), ISNULL(sh.CLASS,0), CAST(ISNULL(sh.NOMINAL,0) AS float)
FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC
LEFT JOIN OD_VALUES vs ON vs.ID = r.REG_2
LEFT JOIN OD_VALUES vc ON vc.ID = r.VALUE_ID
LEFT JOIN OD_SHARES sh ON sh.ID = r.REG_2
WHERE r.REG_3 = @c AND b.IS_BAL = 1 AND b.PART = 39 AND b.NUM_1 IN (50,51,52,55,58)
ORDER BY b.NUM_1, vs.NAME`, sql.Named("c", c.DOC))
rows, err := conn.QueryContext(cctx, fsql.Arch01, sql.Named("c", c.DOC))
if err != nil {
return nil, fmt.Errorf("остатки: %v", err)
}
@@ -91,12 +84,7 @@ func lkArchivePortfolio(ctx context.Context, s FancyMSSQLSettings, contract stri
for _, r := range rests {
ids = append(ids, fmt.Sprint(r.id))
}
trows, err := conn.QueryContext(cctx, fmt.Sprintf(`SELECT REST,
CAST(SUM(CASE WHEN TYPE_ = 1 THEN AMOUNT_ ELSE -AMOUNT_ END) AS float),
CAST(SUM(CASE WHEN TYPE_ = 1 THEN VALUE_ ELSE -VALUE_ END) AS float),
CAST(SUM(CASE WHEN TYPE_ = 1 THEN EQ_ ELSE -EQ_ END) AS float)
FROM OD_TURNS WHERE REST IN (%s) AND WIRDATE >= @d AND IS_PLAN = 'F' AND YEAR(WIRDATE) < 9000
GROUP BY REST`, strings.Join(ids, ",")), sql.Named("d", day.AddDate(0, 0, 1)))
trows, err := conn.QueryContext(cctx, fmt.Sprintf(fsql.Arch02, strings.Join(ids, ",")), sql.Named("d", day.AddDate(0, 0, 1)))
if err != nil {
return nil, fmt.Errorf("обороты: %v", err)
}
@@ -209,7 +197,7 @@ func spectreAsOf(ctx context.Context, conn *sql.DB) time.Time {
return spectreAsOfCache.v
}
var d sql.NullTime
_ = conn.QueryRowContext(ctx, `SELECT MAX(R_DATE) FROM OD_DOCS WHERE YEAR(R_DATE) < 9000`).Scan(&d)
_ = conn.QueryRowContext(ctx, fsql.Arch03).Scan(&d)
v := time.Time{}
if d.Valid {
v = time.Date(d.Time.Year(), d.Time.Month(), d.Time.Day(), 0, 0, 0, 0, time.UTC)
@@ -253,9 +241,7 @@ func spectreKind(class int) string {
func spectreQuote(ctx context.Context, conn *sql.DB, secID int64, day time.Time) (float64, int) {
var ofic sql.NullTime
var rate3, pfr, closeP sql.NullFloat64
err := conn.QueryRowContext(ctx, `SELECT TOP 1 OFICDATE, CAST(RATE3 AS float), CAST(PFR_PRICE AS float), CAST(CLOSE_ AS float)
FROM OD_VALUES_RATES WHERE VALUE_ID = @v AND OFICDATE <= @d AND (RATE3 > 0 OR PFR_PRICE > 0 OR CLOSE_ > 0)
ORDER BY OFICDATE DESC`, sql.Named("v", secID), sql.Named("d", day.AddDate(0, 0, 1))).Scan(&ofic, &rate3, &pfr, &closeP)
err := conn.QueryRowContext(ctx, fsql.Arch04, sql.Named("v", secID), sql.Named("d", day.AddDate(0, 0, 1))).Scan(&ofic, &rate3, &pfr, &closeP)
if err != nil {
return 0, 1 << 30
}
@@ -287,7 +273,7 @@ func spectreFx(ctx context.Context, conn *sql.DB, day time.Time) func(curID int6
return v
}
var rate sql.NullFloat64
_ = conn.QueryRowContext(ctx, `SELECT TOP 1 CAST(RATE AS float) FROM OD_VALUES_RATES WHERE VALUE_ID = @v AND MARKET = 0 AND OFICDATE <= @d AND RATE > 0 ORDER BY OFICDATE DESC`,
_ = conn.QueryRowContext(ctx, fsql.Arch05,
sql.Named("v", curID), sql.Named("d", day.AddDate(0, 0, 1))).Scan(&rate)
v := rate.Float64
if v <= 0 {
@@ -355,8 +341,7 @@ func lkArchiveOperationsLoad(ctx context.Context, s FancyMSSQLSettings, contract
cctx, cancel := context.WithTimeout(ctx, 40*time.Second)
defer cancel()
// Счета бумаг (ПОРТФЕЛЬ) и денег (касса) договора.
rows, err := conn.QueryContext(cctx, `SELECT r.ID, b.NUM_1, ISNULL(b.SYS_NAME,'') FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC
WHERE r.REG_3 = @c AND b.PART = 39 AND ((b.NUM_1 = 58 AND b.SYS_NAME LIKE N'ПОРТФЕЛЬ%') OR b.NUM_1 IN (50,51,52,55))`, sql.Named("c", c.DOC))
rows, err := conn.QueryContext(cctx, fsql.Arch06, sql.Named("c", c.DOC))
if err != nil {
return nil, err
}
@@ -404,29 +389,7 @@ func lkArchiveOperationsLoad(ctx context.Context, s FancyMSSQLSettings, contract
// разбирать список счетов на каждой строке журнала — год данных читался
// 6–7 секунд, сейчас — десятки миллисекунд (оба индекса по D_REST/K_REST
// с WIRDATE отрабатывают поиском).
wrows, err := conn.QueryContext(cctx, fmt.Sprintf(`
WITH w AS (
SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_AMOUNT, wr.K_AMOUNT, wr.NAME,
wr.D_REST AS sec_rest, wr.K_REST AS money_rest
FROM OD_WIRING wr
WHERE wr.D_REST IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000
AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND (wr.D_AMOUNT <> 0 OR wr.K_AMOUNT <> 0)
UNION ALL
SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_AMOUNT, wr.K_AMOUNT, wr.NAME,
wr.K_REST, wr.D_REST
FROM OD_WIRING wr
WHERE wr.K_REST IN (%[1]s) AND wr.D_REST NOT IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000
AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND (wr.D_AMOUNT <> 0 OR wr.K_AMOUNT <> 0))
SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST,
CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float),
CAST(ISNULL(w.D_AMOUNT,0) AS float), CAST(ISNULL(w.K_AMOUNT,0) AS float),
ISNULL(w.NAME,''), ISNULL(sv.NAME,''), ISNULL(sv.ISIN,''), ISNULL(mv.SYSNAME,'')
FROM w
LEFT JOIN OD_RESTS sr ON sr.ID = w.sec_rest
LEFT JOIN OD_VALUES sv ON sv.ID = sr.REG_2
LEFT JOIN OD_RESTS mr ON mr.ID = w.money_rest
LEFT JOIN OD_VALUES mv ON mv.ID = mr.VALUE_ID
ORDER BY w.WIRDATE`, in), sql.Named("f", fromDay), sql.Named("t", toEnd))
wrows, err := conn.QueryContext(cctx, fmt.Sprintf(fsql.Arch07, in), sql.Named("f", fromDay), sql.Named("t", toEnd))
if err != nil {
return nil, fmt.Errorf("проводки: %v", err)
}
@@ -501,14 +464,7 @@ func spectreCashOps(ctx context.Context, conn *sql.DB, cashIDs []string, fromDay
}
var out []LKOperationRow
in := strings.Join(cashIDs, ",")
crows, err := conn.QueryContext(ctx, fmt.Sprintf(`SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST,
CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float), ISNULL(w.NAME,''), ISNULL(mv.SYSNAME,'')
FROM OD_WIRING w
JOIN OD_RESTS mr ON mr.ID = CASE WHEN w.D_REST IN (%[1]s) THEN w.D_REST ELSE w.K_REST END
LEFT JOIN OD_VALUES mv ON mv.ID = mr.VALUE_ID
WHERE (w.D_REST IN (%[1]s) OR w.K_REST IN (%[1]s)) AND w.IS_PLAN = 'F' AND YEAR(w.WIRDATE) < 9000
AND w.WIRDATE >= @f AND w.WIRDATE < @t
ORDER BY w.WIRDATE`, in), sql.Named("f", fromDay), sql.Named("t", toEnd))
crows, err := conn.QueryContext(ctx, fmt.Sprintf(fsql.Arch08, in), sql.Named("f", fromDay), sql.Named("t", toEnd))
if err != nil {
return nil
}
@@ -571,7 +527,7 @@ func spectreDealKind(name string, onDebit bool) string {
// spectreProceeds — выручка от продаж по (ISIN|день): [сумма в валюте, сумма в рублях].
func spectreProceeds(ctx context.Context, conn *sql.DB, doc int64, from, to time.Time) map[string][2]float64 {
out := map[string][2]float64{}
rows, err := conn.QueryContext(ctx, `SELECT r.ID FROM OD_RESTS r JOIN OD_BALANCES b ON b.ID = r.BAL_ACC WHERE r.REG_3 = @c AND b.SYS_NAME = N'ВЫРУЧКА'`, sql.Named("c", doc))
rows, err := conn.QueryContext(ctx, fsql.Arch09, sql.Named("c", doc))
if err != nil {
return out
}
@@ -589,22 +545,7 @@ func spectreProceeds(ctx context.Context, conn *sql.DB, doc int64, from, to time
return out
}
in := strings.Join(ids, ",")
wrows, err := conn.QueryContext(ctx, fmt.Sprintf(`
WITH w AS (
SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.D_REST AS sec_rest
FROM OD_WIRING wr
WHERE wr.K_REST IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000
AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND wr.EQ_ <> 0
UNION ALL
SELECT wr.WIRDATE, wr.EQ_, wr.D_REST, wr.K_REST, wr.D_SUMMA, wr.K_SUMMA, wr.K_REST
FROM OD_WIRING wr
WHERE wr.D_REST IN (%[1]s) AND wr.K_REST NOT IN (%[1]s) AND wr.IS_PLAN = 'F' AND YEAR(wr.WIRDATE) < 9000
AND wr.WIRDATE >= @f AND wr.WIRDATE < @t AND wr.EQ_ <> 0)
SELECT w.WIRDATE, CAST(ISNULL(w.EQ_,0) AS float), w.D_REST, w.K_REST,
CAST(ISNULL(w.D_SUMMA,0) AS float), CAST(ISNULL(w.K_SUMMA,0) AS float), ISNULL(sv.ISIN,'')
FROM w
LEFT JOIN OD_RESTS sr ON sr.ID = w.sec_rest
LEFT JOIN OD_VALUES sv ON sv.ID = sr.REG_2`, in), sql.Named("f", from), sql.Named("t", to))
wrows, err := conn.QueryContext(ctx, fmt.Sprintf(fsql.Arch10, in), sql.Named("f", from), sql.Named("t", to))
if err != nil {
return out
}
+2 -2
View File
@@ -9,6 +9,7 @@ package lkgateway
import (
"context"
"database/sql"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"strings"
"sync"
"time"
@@ -85,8 +86,7 @@ func spectreContractFor(ctx context.Context, s FancyMSSQLSettings, contract stri
var c spectreContract
var open, closed sql.NullTime
var state sql.NullInt64
err = conn.QueryRowContext(cctx, `SELECT TOP 1 DOC, INVESTOR, NAME, C_OPEN, C_CLOSE, C_STATE FROM D_B_CONTRACTS
WHERE UPPER(NAME) = @n OR UPPER(NAME) LIKE @n + ' %' ORDER BY C_STATE DESC, DOC DESC`, sql.Named("n", num)).
err = conn.QueryRowContext(cctx, fsql.Cl01, sql.Named("n", num)).
Scan(&c.DOC, &c.Investor, &c.Name, &open, &closed, &state)
if err == nil {
c.Name = strings.TrimSpace(c.Name)
+6 -29
View File
@@ -11,6 +11,7 @@ import (
"context"
"database/sql"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"strings"
"time"
)
@@ -78,11 +79,7 @@ func lkCorpActions(ctx context.Context, s FancyMSSQLSettings, isins []string) ([
names = append(names, fmt.Sprintf("@p%d", i+1))
args = append(args, sql.Named(fmt.Sprintf("p%d", i+1), strings.ToUpper(strings.TrimSpace(isin))))
}
q := fmt.Sprintf(`SELECT TOP 40 ISNULL(a.KD_TYPE,''), ISNULL(v.ISIN,''), ISNULL(v.NAME,''), a.FIX_DATE, a.OPER_DATE, ISNULL(a.OFFER_NAME,'')
FROM U_CA_ACTS a JOIN OD_VALUES v ON v.ID = a.SHARE
WHERE UPPER(v.ISIN) IN (%s)
AND ISNULL(a.FIX_DATE, a.OPER_DATE) >= DATEADD(month, -1, GETDATE())
ORDER BY ISNULL(a.FIX_DATE, a.OPER_DATE)`, strings.Join(names, ","))
q := fmt.Sprintf(fsql.Doc01, strings.Join(names, ","))
rows, err := conn.QueryContext(cctx, q, args...)
if err != nil {
return nil, err
@@ -121,23 +118,7 @@ type LKDocument struct {
}
// lkDocSchema — таблица заявок на документы (создаётся вместе со схемой lk).
const lkDocSchema = `
CREATE TABLE IF NOT EXISTS lk.doc_requests (
id bigserial PRIMARY KEY,
created_at timestamptz NOT NULL DEFAULT now(),
contract_number text NOT NULL,
client_name text,
kind text NOT NULL,
period_from date,
period_to date,
account text,
comment text,
status text NOT NULL DEFAULT 'new',
staff_note text,
done_at timestamptz
);
CREATE INDEX IF NOT EXISTS ix_lk_doc_requests_contract ON lk.doc_requests (contract_number, created_at DESC);
`
var lkDocSchema = fsql.Doc02
// lkDocKinds — виды документов, которые клиент может заказать в кабинете.
var lkDocKinds = []string{
@@ -173,8 +154,7 @@ func lkDocRequestAdd(ctx context.Context, rc *RuntimeConfig, contract, clientNam
if t, err := time.Parse("2006-01-02", strings.TrimSpace(to)); err == nil {
toP = t
}
_, err = db.ExecContext(ctx, `INSERT INTO lk.doc_requests (contract_number, client_name, kind, period_from, period_to, account, comment)
VALUES ($1,$2,$3,$4,$5,$6,$7)`, contract, clientName, kind, fromP, toP, strings.TrimSpace(account), strings.TrimSpace(comment))
_, err = db.ExecContext(ctx, fsql.Doc03, contract, clientName, kind, fromP, toP, strings.TrimSpace(account), strings.TrimSpace(comment))
return err
}
@@ -184,8 +164,7 @@ func lkDocRequests(ctx context.Context, rc *RuntimeConfig, contract string) ([]L
if err != nil {
return nil, err
}
rows, err := db.QueryContext(ctx, `SELECT id, created_at, kind, period_from, period_to, COALESCE(account,''), status, COALESCE(staff_note,'')
FROM lk.doc_requests WHERE contract_number=$1 ORDER BY created_at DESC LIMIT 50`, contract)
rows, err := db.QueryContext(ctx, fsql.Doc04, contract)
if err != nil {
return nil, err
}
@@ -232,9 +211,7 @@ func lkReadyDocuments(ctx context.Context, s FancyMSSQLSettings, contract string
defer conn.Close()
cctx, cancel := context.WithTimeout(ctx, 25*time.Second)
defer cancel()
q := fmt.Sprintf(`SELECT TOP 30 d.R_DATE, ISNULL(d.NUM,''), e.B_DATE, e.E_DATE, e.DEPO, d.STATE, ISNULL(e.IS_TURNS,0)
FROM U_OP_INF_EXTRACT e JOIN OD_DOCS d ON d.ID = e.DOC
WHERE e.DEPO IN (%s) ORDER BY d.R_DATE DESC`, inClause(ids))
q := fmt.Sprintf(fsql.Doc05, inClause(ids))
rows, err := conn.QueryContext(cctx, q)
if err != nil {
return nil, err
+3 -4
View File
@@ -16,6 +16,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"log"
"os"
"sort"
@@ -233,7 +234,7 @@ func lkDepoAnswer(ctx context.Context, rc *RuntimeConfig, guid, ourComment strin
}
defer db.Close()
g := strings.ToUpper(guid)
rows, err := db.QueryContext(ctx, "SELECT ISNULL(STATE,''), ISNULL(COMMENT,'') FROM dbo.FTV_M2M_EVENTS WHERE GUID=@g AND EVENT_TYPE='BJ_GATEWAY_REJECT'", sql.Named("g", g))
rows, err := db.QueryContext(ctx, fsql.QFl01, sql.Named("g", g))
if err != nil {
return false, "", 0, "", err
}
@@ -270,9 +271,7 @@ func lkDepoAnswer(ctx context.Context, rc *RuntimeConfig, guid, ourComment strin
docCol = "CAST(m.DOC AS varchar(32))"
}
var doc, mState, mComment, lineErr string
_ = db.QueryRowContext(ctx, `SELECT ISNULL(`+docCol+`,''), ISNULL(m.STATE,''), ISNULL(m.COMMENT,''),
ISNULL((SELECT TOP 1 ISNULL(l.COMMENT,'') FROM dbo.FTV_M2M_LINES l WHERE l.GUID=m.GUID AND ISNULL(l.STATE,'')='ERROR'),'')
FROM dbo.FTV_M2M m WHERE m.GUID=@g`, sql.Named("g", g)).Scan(&doc, &mState, &mComment, &lineErr)
_ = db.QueryRowContext(ctx, fsql.Flow01+docCol+fsql.Flow02, sql.Named("g", g)).Scan(&doc, &mState, &mComment, &lineErr)
if strings.EqualFold(mState, "ERROR") && mComment != "" && !containsStr(errs, mComment) {
errs = append(errs, mComment)
}
+2 -4
View File
@@ -12,6 +12,7 @@ package lkgateway
import (
"context"
"database/sql"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"strings"
"sync"
"time"
@@ -75,10 +76,7 @@ func lkFxAccounting(ctx context.Context, s FancyMSSQLSettings) map[string]LKFxRa
continue
}
cctx, cancel := context.WithTimeout(ctx, 15*time.Second)
rows, err := conn.QueryContext(cctx, `SELECT UPPER(LTRIM(RTRIM(v.SYSNAME))), r.OFICDATE, CAST(r.RATE AS float)
FROM OD_VALUES_RATES r JOIN OD_VALUES v ON v.ID = r.VALUE_ID
WHERE v.V_TYPE = 1 AND r.MARKET = 0 AND r.RATE > 0
AND r.OFICDATE = (SELECT MAX(x.OFICDATE) FROM OD_VALUES_RATES x WHERE x.VALUE_ID = r.VALUE_ID AND x.MARKET = 0 AND x.RATE > 0)`)
rows, err := conn.QueryContext(cctx, fsql.Fx01)
if err == nil {
for rows.Next() {
var code string
+5 -42
View File
@@ -13,6 +13,7 @@ import (
"context"
"database/sql"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"strings"
"time"
)
@@ -54,9 +55,7 @@ func depoClientIDs(ctx context.Context, s FancyMSSQLSettings, contract string) (
defer conn.Close()
cctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
rows, err := conn.QueryContext(cctx, `SELECT DISTINCT c.ID, ISNULL(c.NUMBER,''), ISNULL(c.FACE,0)
FROM OD_CLIENTS c JOIN OD_CLIENTS_PORTS p ON p.CLIENT = c.ID
WHERE p.PORT_NUM = @p1 OR p.PORT_NUM LIKE @p2`, contract, likeEscape(contract)+" %")
rows, err := conn.QueryContext(cctx, fsql.Hist01, contract, likeEscape(contract)+" %")
if err != nil {
return nil, 0, nil, err
}
@@ -137,32 +136,7 @@ func lkOperations(ctx context.Context, s FancyMSSQLSettings, contract, from, to
}
fromT, toT := parseRange(from, to)
// Три вида операций в одном списке: зачисление, списание, перевод между счетами.
q := fmt.Sprintf(`SELECT TOP %d * FROM (
SELECT d.R_DATE AS RD, d.PROC_DATE AS PD, ISNULL(d.NUM,'') AS NUM, d.STATE AS ST, N'зачисление' AS KIND,
z.DEPO AS ACC, ISNULL(v.ISIN,'') AS ISIN, ISNULL(v.NAME,'') AS SEC, CAST(ISNULL(z.AMOUNT,0) AS float) AS QTY,
LTRIM(RTRIM(ISNULL(z.COMMENT,'') + ' ' + ISNULL(z.REJECT_REASON,''))) AS NOTE
FROM U_OP_INV_ZACH z JOIN OD_DOCS d ON d.ID = z.DOC LEFT JOIN OD_VALUES v ON v.ID = z.SHARE
WHERE z.DEPO IN (%s)
UNION ALL
SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'списание',
p.DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), -CAST(ISNULL(p.AMOUNT,0) AS float),
LTRIM(RTRIM(ISNULL(p.COMMENT,'') + ' ' + ISNULL(p.REJECT_REASON,'')))
FROM U_OP_INV_SPIS p JOIN OD_DOCS d ON d.ID = p.DOC LEFT JOIN OD_VALUES v ON v.ID = p.SHARE
WHERE p.DEPO IN (%s)
UNION ALL
SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'перевод: списание',
t.D_DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), -CAST(ISNULL(t.AMOUNT,0) AS float),
LTRIM(RTRIM(ISNULL(t.COMMENT,'') + ' ' + ISNULL(t.REJECT_REASON,'')))
FROM U_OP_INV_PEREVOD t JOIN OD_DOCS d ON d.ID = t.DOC LEFT JOIN OD_VALUES v ON v.ID = t.SHARE
WHERE t.D_DEPO IN (%s)
UNION ALL
SELECT d.R_DATE, d.PROC_DATE, ISNULL(d.NUM,''), d.STATE, N'перевод: зачисление',
t.K_DEPO, ISNULL(v.ISIN,''), ISNULL(v.NAME,''), CAST(ISNULL(t.AMOUNT,0) AS float),
LTRIM(RTRIM(ISNULL(t.COMMENT,'') + ' ' + ISNULL(t.REJECT_REASON,'')))
FROM U_OP_INV_PEREVOD t JOIN OD_DOCS d ON d.ID = t.DOC LEFT JOIN OD_VALUES v ON v.ID = t.SHARE
WHERE t.K_DEPO IN (%s)) x
WHERE x.RD >= @p1 AND x.RD < DATEADD(day, 1, @p2)
ORDER BY x.RD DESC, x.NUM DESC`, limit, inClause(ids), inClause(ids), inClause(ids), inClause(ids))
q := fmt.Sprintf(fsql.Hist02, limit, inClause(ids), inClause(ids), inClause(ids), inClause(ids))
rows, err := conn.QueryContext(cctx, q, fromT, toT)
if err != nil {
return nil, err
@@ -226,17 +200,7 @@ func lkIncome(ctx context.Context, s FancyMSSQLSettings, contract, from, to stri
cctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
fromT, toT := parseRange(from, to)
q := fmt.Sprintf(`SELECT TOP 200 h.P_DATE, h.INCOME_KIND, ISNULL(v.ISIN,''), ISNULL(v.NAME,''),
CAST(ISNULL(l.AMOUNT,0) AS float), CAST(ISNULL(l.SUMMA_TOTAL,0) AS float),
CAST(ISNULL(l.TAX_HOLDED,0) AS float), CAST(ISNULL(l.TOTAL_TO_PAY,0) AS float),
ISNULL(l.STATUS,''), ISNULL(l.DEPO,0)
FROM U_OP_INF_INCOME_DOL l
JOIN OD_DOLS o ON o.ID = l.DOL
JOIN U_OP_INF_INCOME h ON h.DOC = o.DOC
LEFT JOIN OD_VALUES v ON v.ID = h.SHARE
WHERE (l.DEPO IN (%s) OR l.INCOME_FACE = @p3)
AND h.P_DATE >= @p1 AND h.P_DATE < DATEADD(day, 1, @p2)
ORDER BY h.P_DATE DESC`, inClause(ids))
q := fmt.Sprintf(fsql.Hist03, inClause(ids))
rows, err := conn.QueryContext(cctx, q, fromT, toT, face)
if err != nil {
return nil, 0, err
@@ -266,8 +230,7 @@ func lkIncome(ctx context.Context, s FancyMSSQLSettings, contract, from, to stri
}
// Удержанный налог по лицу за период — по журналу НДФЛ депозитария.
if face > 0 {
_ = conn.QueryRowContext(cctx, `SELECT CAST(ISNULL(SUM(CAST(SUMMA AS float)),0) AS float) FROM OD_NDFL
WHERE FACE = @p1 AND R_DATE >= @p2 AND R_DATE < DATEADD(day, 1, @p3)`,
_ = conn.QueryRowContext(cctx, fsql.Hist04,
sql.Named("p1", face), sql.Named("p2", fromT), sql.Named("p3", toT)).Scan(&taxTotal)
}
return rowsOut, taxTotal, nil
+4 -18
View File
@@ -10,6 +10,7 @@ package lkgateway
import (
"context"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"sort"
"strconv"
"strings"
@@ -88,14 +89,7 @@ func lkOnlinePortfolio(ctx context.Context, rc *RuntimeConfig, contract string)
out := &LKPortfolio{Source: "quik"}
// Позиции: свежая строка по каждому (клиент, бумага, торговый счёт), затем сумма по бумаге.
rows, err := p.Query(cctx, `
WITH d AS (
SELECT DISTINCT ON ("ClientCode","SecCode","Trdacc") "SecCode","CurrentBal","AwgPositionPrice","LoadDate"
FROM "DepoLimits" WHERE "ClientCode" = ANY($1) AND "LimitKind"='T2'
ORDER BY "ClientCode","SecCode","Trdacc","LoadDate" DESC)
SELECT "SecCode", SUM("CurrentBal")::float8,
COALESCE(SUM("CurrentBal"*"AwgPositionPrice")/NULLIF(SUM("CurrentBal"),0),0)::float8, MAX("LoadDate")
FROM d WHERE "CurrentBal" <> 0 GROUP BY "SecCode" ORDER BY "SecCode"`, codes)
rows, err := p.Query(cctx, fsql.On01, codes)
if err != nil {
return nil, fmt.Errorf("позиции: %v", err)
}
@@ -127,10 +121,7 @@ func lkOnlinePortfolio(ctx context.Context, rc *RuntimeConfig, contract string)
for _, r := range pos {
codesList = append(codesList, r.code)
}
srows, err := p.Query(cctx, `
SELECT DISTINCT ON ("SecCode") "SecCode", upper(COALESCE("ISINCode",'')), COALESCE("SecShortName","SecName",''), "ClassCode", COALESCE("FaceValue",0)::float8
FROM "Securities" WHERE "SecCode" = ANY($1)
ORDER BY "SecCode", CASE "ClassCode" WHEN 'TQBR' THEN 1 WHEN 'TQOB' THEN 2 WHEN 'TQCB' THEN 3 WHEN 'TQTF' THEN 4 WHEN 'TQIF' THEN 5 WHEN 'TQRD' THEN 6 ELSE 9 END`, codesList)
srows, err := p.Query(cctx, fsql.On02, codesList)
if err == nil {
for srows.Next() {
var code, isin, name, class string
@@ -189,12 +180,7 @@ func lkOnlinePortfolio(ctx context.Context, rc *RuntimeConfig, contract string)
}
// Деньги по валютам.
crows, err := p.Query(cctx, `
WITH m AS (
SELECT DISTINCT ON ("ClientCode","CurrCode","Tag") "CurrCode","CurrentBal","LoadDate"
FROM "MoneyLimits" WHERE "ClientCode" = ANY($1) AND "LimitKind"='T2'
ORDER BY "ClientCode","CurrCode","Tag","LoadDate" DESC)
SELECT "CurrCode", SUM("CurrentBal")::float8, MAX("LoadDate") FROM m GROUP BY "CurrCode" HAVING SUM("CurrentBal") <> 0 ORDER BY "CurrCode"`, codes)
crows, err := p.Query(cctx, fsql.On03, codes)
if err == nil {
for crows.Next() {
var c LKCash
+6 -16
View File
@@ -11,18 +11,13 @@ package lkgateway
import (
"context"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"log"
"strings"
"time"
)
const lkSnapshotSchema = `
CREATE TABLE IF NOT EXISTS lk.portfolio_daily (
day DATE NOT NULL, contract VARCHAR(64) NOT NULL,
value DOUBLE PRECISION NOT NULL DEFAULT 0, cash DOUBLE PRECISION NOT NULL DEFAULT 0,
positions INT NOT NULL DEFAULT 0, priced INT NOT NULL DEFAULT 0,
source VARCHAR(16) NOT NULL DEFAULT 'quik', created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (day, contract))`
var lkSnapshotSchema = fsql.Sn01
// lkSnapshotEnabled — ведём ли историю стоимости. По умолчанию (режим не выбран)
// ведём, если настроен экспорт QUIK: история накапливается только вперёд, и
@@ -76,8 +71,7 @@ func lkSnapshotHistory(ctx context.Context, rc *RuntimeConfig, contract string,
}
cctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
rows, err := db.QueryContext(cctx, `SELECT day, value, cash, positions FROM lk.portfolio_daily
WHERE contract = $1 AND day >= current_date - $2::int ORDER BY day`, num, days)
rows, err := db.QueryContext(cctx, fsql.Sn02, num, days)
if err != nil {
return nil, err
}
@@ -104,10 +98,7 @@ func lkSnapshotSave(ctx context.Context, rc *RuntimeConfig, day time.Time, contr
}
cctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
_, err = db.ExecContext(cctx, `INSERT INTO lk.portfolio_daily(day, contract, value, cash, positions, priced, source)
VALUES ($1,$2,$3,$4,$5,$6,$7)
ON CONFLICT (day, contract) DO UPDATE SET value = EXCLUDED.value, cash = EXCLUDED.cash,
positions = EXCLUDED.positions, priced = EXCLUDED.priced, source = EXCLUDED.source, created_at = now()`,
_, err = db.ExecContext(cctx, fsql.Sn03,
day.Format("2006-01-02"), lkContractNumber(contract), p.Securities, p.CashRUB,
len(p.Positions), p.Priced, p.Source)
return err
@@ -123,8 +114,7 @@ func lkSnapshotContracts(ctx context.Context, rc *RuntimeConfig) []string {
}
cctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
rows, err := p.Query(cctx, `SELECT DISTINCT "ClientCode" FROM "DepoLimits"
WHERE "LimitKind" = 'T2' AND "CurrentBal" <> 0 AND "ClientCode" <> ''`)
rows, err := p.Query(cctx, fsql.Sn04)
if err != nil {
return nil
}
@@ -218,7 +208,7 @@ func lkSnapshotDone(ctx context.Context, rc *RuntimeConfig, day time.Time) bool
cctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
var n int
if db.QueryRowContext(cctx, `SELECT count(*) FROM lk.portfolio_daily WHERE day = $1`,
if db.QueryRowContext(cctx, fsql.Sn05,
day.Format("2006-01-02")).Scan(&n) != nil {
return true
}
+5 -12
View File
@@ -9,6 +9,7 @@ import (
"context"
"database/sql"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"sort"
"strings"
"time"
@@ -75,9 +76,7 @@ func lkUpcoming(ctx context.Context, s FancyMSSQLSettings, positions []LKPositio
}
// Купоны: дата выплаты — P_DATE, до выплаты — конец периода. IS_PAY здесь означает
// «купон выплачиваемый» (не «уже выплачен»), поэтому по нему не фильтруем.
rows, err := conn.QueryContext(cctx, fmt.Sprintf(`SELECT UPPER(v.ISIN), ISNULL(c.P_DATE, c.E_DATE), CAST(ISNULL(c.SUMMA,0) AS float), CAST(ISNULL(c.PERCENT_,0) AS float)
FROM OD_COUPONS c JOIN OD_VALUES v ON v.ID = c.SHARE
WHERE UPPER(v.ISIN) IN (%s) AND ISNULL(c.P_DATE, c.E_DATE) >= @f AND ISNULL(c.P_DATE, c.E_DATE) <= @t`, in), args...)
rows, err := conn.QueryContext(cctx, fmt.Sprintf(fsql.Up01, in), args...)
if err == nil {
for rows.Next() {
var isin string
@@ -90,9 +89,7 @@ func lkUpcoming(ctx context.Context, s FancyMSSQLSettings, positions []LKPositio
rows.Close()
}
// Оферты.
rows, err = conn.QueryContext(cctx, fmt.Sprintf(`SELECT UPPER(v.ISIN), o.P_DATE, CAST(ISNULL(o.O_PRICE,0) AS float)
FROM OD_OFFERS o JOIN OD_VALUES v ON v.ID = o.SHARE
WHERE UPPER(v.ISIN) IN (%s) AND o.CANCEL_DATE IS NULL AND o.P_DATE >= @f AND o.P_DATE <= @t`, in), args...)
rows, err = conn.QueryContext(cctx, fmt.Sprintf(fsql.Up02, in), args...)
if err == nil {
for rows.Next() {
var isin string
@@ -105,9 +102,7 @@ func lkUpcoming(ctx context.Context, s FancyMSSQLSettings, positions []LKPositio
rows.Close()
}
// Погашения: по номиналу в дату DEATHDATE.
rows, err = conn.QueryContext(cctx, fmt.Sprintf(`SELECT UPPER(v.ISIN), sh.DEATHDATE, CAST(ISNULL(sh.NOMINAL,0) AS float)
FROM OD_SHARES sh JOIN OD_VALUES v ON v.ID = sh.ID
WHERE UPPER(v.ISIN) IN (%s) AND sh.E_DATE > GETDATE() AND sh.DEATHDATE >= @f AND sh.DEATHDATE <= @t`, in), args...)
rows, err = conn.QueryContext(cctx, fmt.Sprintf(fsql.Up03, in), args...)
if err == nil {
for rows.Next() {
var isin string
@@ -120,9 +115,7 @@ func lkUpcoming(ctx context.Context, s FancyMSSQLSettings, positions []LKPositio
rows.Close()
}
// Дивиденды (корпоративные действия DVCA): дата фиксации или выплаты, суммы нет.
rows, err = conn.QueryContext(cctx, fmt.Sprintf(`SELECT UPPER(v.ISIN), ISNULL(a.OPER_DATE, a.FIX_DATE)
FROM U_CA_ACTS a JOIN OD_VALUES v ON v.ID = a.SHARE
WHERE UPPER(v.ISIN) IN (%s) AND UPPER(ISNULL(a.KD_TYPE,'')) = 'DVCA' AND ISNULL(a.OPER_DATE, a.FIX_DATE) >= @f AND ISNULL(a.OPER_DATE, a.FIX_DATE) <= @t`, in), args...)
rows, err = conn.QueryContext(cctx, fmt.Sprintf(fsql.Up04, in), args...)
if err == nil {
for rows.Next() {
var isin string
+5 -14
View File
@@ -7,6 +7,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"net/http"
"strconv"
"strings"
@@ -70,12 +71,7 @@ func manualDepoSearch(rc *RuntimeConfig, q string) ([]manualDepoFace, string) {
defer db.Close()
digits := strings.ReplaceAll(q, " ", "")
// Действующая версия карточки (LAST_FLAG=1); счета привязаны к исходной записи (SELF_ID).
rows, err := db.QueryContext(ctx, `SELECT TOP 20 ISNULL(NULLIF(f.SELF_ID,0),f.ID), ISNULL(f.NAME,''), f.DATE_BIRTH, ISNULL(f.DOC_TYPE,0), ISNULL(f.DOC_SER,''), ISNULL(f.DOC_NUM,''),
f.DOC_DAT, ISNULL(f.DOC_PLACE,''), ISNULL(f.DOC_CODE,''), ISNULL(f.PHONE,'')
FROM OD_FACES f
WHERE ISNULL(f.LAST_FLAG,1)=1 AND (UPPER(f.NAME) LIKE UPPER(@q) OR f.DOC_NUM=@d OR REPLACE(ISNULL(f.DOC_SER,''),' ','')+f.DOC_NUM=@d)
AND EXISTS (SELECT 1 FROM OD_CLIENTS c WHERE c.FACE=ISNULL(NULLIF(f.SELF_ID,0),f.ID) AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL)
ORDER BY f.NAME`, sql.Named("q", q+"%"), sql.Named("d", digits))
rows, err := db.QueryContext(ctx, fsql.Man01, sql.Named("q", q+"%"), sql.Named("d", digits))
if err != nil {
return nil, "поиск в DEPO: " + err.Error()
}
@@ -119,9 +115,7 @@ func manualDepoSearch(rc *RuntimeConfig, q string) ([]manualDepoFace, string) {
rows.Close()
out := make([]manualDepoFace, 0, len(found))
for _, r := range found {
ar, err := db.QueryContext(ctx, `SELECT c.ID, ISNULL(c.NUMBER,''),
ISNULL((SELECT TOP 1 p.PORT_NUM FROM OD_CLIENTS_PORTS p WHERE p.CLIENT=c.ID ORDER BY p.PORT_DATE, p.PORT_NUM),'')
FROM OD_CLIENTS c WHERE c.FACE=@f AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0 AND c.D_CLOSE IS NULL ORDER BY c.ID`, sql.Named("f", r.id))
ar, err := db.QueryContext(ctx, fsql.Man02, sql.Named("f", r.id))
if err == nil {
for ar.Next() {
var a manualDepoAcc
@@ -154,10 +148,7 @@ func manualDepoPositions(rc *RuntimeConfig, clientID int64) ([]manualDepoPos, st
}
defer cancel()
defer db.Close()
rows, err := db.QueryContext(ctx, `SELECT ISNULL(v.ISIN,''), ISNULL(v.NAME,''), SUM(CAST(r.K AS float))
FROM OD_RESTS r JOIN OD_VALUES v ON v.ID=r.VALUE_ID
WHERE r.REG_1=@c AND r.BAL_ACC=@b
GROUP BY v.ISIN, v.NAME HAVING SUM(CAST(r.K AS float)) > 0 ORDER BY v.NAME`,
rows, err := db.QueryContext(ctx, fsql.Man03,
sql.Named("c", clientID), sql.Named("b", depoTradeBalAcc))
if err != nil {
return nil, "остатки в DEPO: " + err.Error()
@@ -210,7 +201,7 @@ func manualDepoClientByAccount(rc *RuntimeConfig, number string) (int64, string)
defer cancel()
defer db.Close()
var id int64
err := db.QueryRowContext(ctx, `SELECT TOP 1 ID FROM OD_CLIENTS WHERE NUMBER=@n AND STATE=0 AND D_CLOSE IS NULL ORDER BY ID`,
err := db.QueryRowContext(ctx, fsql.Man04,
sql.Named("n", number)).Scan(&id)
if err != nil {
return 0, ""
+2 -11
View File
@@ -10,6 +10,7 @@ package lkgateway
import (
"context"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"strings"
"sync"
"time"
@@ -88,17 +89,7 @@ func lkPrices(ctx context.Context, rc *RuntimeConfig, isins []string) map[string
fxRates := lkFxRates(ctx, rc) // для выпусков с валютным номиналом
// Одна цена на бумагу: берём класс основных торгов (акции TQBR, ОФЗ TQOB,
// корпоративные облигации TQCB, фонды TQTF/TQIF), иначе — класс с торгами.
rows, err := p.Query(cctx, `
SELECT DISTINCT ON (s."ISINCode")
upper(s."ISINCode"), s."ClassCode", COALESCE(s."SecShortName", s."SecName", ''),
COALESCE(p.last, 0)::float8, COALESCE(p.prevprice, 0)::float8,
COALESCE(s."FaceValue", 0)::float8, COALESCE(p.accruedint, 0)::float8, COALESCE(s."FaceUnit", '')
FROM "Securities" s JOIN "Params" p ON p."SecCode" = s."SecCode" AND p."ClassCode" = s."ClassCode"
WHERE s."ISINCode" <> '' AND (p.last > 0 OR p.prevprice > 0)
ORDER BY s."ISINCode",
CASE s."ClassCode" WHEN 'TQBR' THEN 1 WHEN 'TQOB' THEN 2 WHEN 'TQCB' THEN 3
WHEN 'TQTF' THEN 4 WHEN 'TQIF' THEN 5 WHEN 'TQRD' THEN 6 ELSE 9 END,
p.last DESC`)
rows, err := p.Query(cctx, fsql.Pr01)
if err != nil {
return out
}
+3 -6
View File
@@ -15,6 +15,7 @@ import (
"encoding/json"
"errors"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"log"
"os"
"path/filepath"
@@ -214,8 +215,7 @@ func quikM2MInstruments(ctx context.Context, s QuikMNPSettings) (in, out string,
defer conn.Close()
cctx, cancel := context.WithTimeout(ctx, 15*time.Second)
defer cancel()
rows, e := conn.QueryContext(cctx, `SELECT ic.ClassCode, sec.SecCode FROM ISecurities sec JOIN IClasses ic ON ic.ClassId=sec.ClassId
WHERE sec.Enable=1 AND ic.ClassCode IN ('INSTRINM2M','INSTROUTM2M') ORDER BY sec.SecurityId`)
rows, e := conn.QueryContext(cctx, fsql.Qk01)
if e != nil {
return "", "", fmt.Errorf("quik: справочник инструментов M2M: %s", quikErrorDetail(e))
}
@@ -445,10 +445,7 @@ func quikResolveClient(ctx context.Context, rc *RuntimeConfig, ser, num string)
// Правило депозитария: при нескольких договорах клиента зачисление идёт на
// САМЫЙ РАННИЙ договор — берём портфель с наименьшей датой (затем по дате
// открытия счёта и номеру).
rows, err := db.QueryContext(cctx, `SELECT TOP 5 p.PORT_NUM FROM OD_FACES f
JOIN OD_CLIENTS c ON c.FACE=f.ID JOIN OD_CLIENTS_PORTS p ON p.CLIENT=c.ID
WHERE REPLACE(f.DOC_SER,' ','')=@p1 AND f.DOC_NUM=@p2 AND c.NUMBER LIKE N'ВТНКЦ%' AND c.STATE=0
ORDER BY ISNULL(p.PORT_DATE,'2099-01-01'), ISNULL(c.D_OPEN,'2099-01-01'), c.NUMBER`, serN, numN)
rows, err := db.QueryContext(cctx, fsql.Qk02, serN, numN)
if err != nil {
return "", "", fmt.Errorf("DEPO: %w", err)
}
+6 -10
View File
@@ -8,6 +8,7 @@ import (
"context"
"database/sql"
"fmt"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
"strings"
"time"
)
@@ -86,7 +87,7 @@ func securityCard(ctx context.Context, rc *RuntimeConfig, isin string, pos *Clie
var valID int64
var name, cfi string
var vType, isIn sql.NullInt64
err := db.QueryRowContext(dctx, `SELECT TOP 1 ID, ISNULL(NAME,''), ISNULL(CFI,''), V_TYPE, IS_IN FROM OD_VALUES WHERE ISIN=@p1 ORDER BY ID`,
err := db.QueryRowContext(dctx, fsql.Sec01,
sql.Named("p1", isin)).Scan(&valID, &name, &cfi, &vType, &isIn)
if err != nil {
card.Note = "бумаги нет в справочнике депозитария"
@@ -102,8 +103,7 @@ func securityCard(ctx context.Context, rc *RuntimeConfig, isin string, pos *Clie
var nominal sql.NullFloat64
var regNum sql.NullString
var death sql.NullTime
err = db.QueryRowContext(dctx, `SELECT TOP 1 ISSUER, CAST(NOMINAL AS float), NUM_REG, DEATHDATE FROM OD_SHARES
WHERE ID=@p1 AND E_DATE > GETDATE() ORDER BY B_DATE DESC`, sql.Named("p1", valID)).Scan(&issuer, &nominal, &regNum, &death)
err = db.QueryRowContext(dctx, fsql.Sec02, sql.Named("p1", valID)).Scan(&issuer, &nominal, &regNum, &death)
if err == nil {
card.Face = nominal.Float64
card.RegNumber = strings.TrimSpace(regNum.String)
@@ -112,7 +112,7 @@ func securityCard(ctx context.Context, rc *RuntimeConfig, isin string, pos *Clie
}
if issuer.Valid && issuer.Int64 > 0 {
var iname string
if db.QueryRowContext(dctx, `SELECT TOP 1 ISNULL(NAME,'') FROM OD_FACES WHERE ID=@p1 OR SELF_ID=@p1 ORDER BY ISNULL(LAST_FLAG,0) DESC, ID DESC`,
if db.QueryRowContext(dctx, fsql.Sec03,
sql.Named("p1", issuer.Int64)).Scan(&iname) == nil {
card.Issuer = strings.TrimSpace(iname)
}
@@ -122,10 +122,7 @@ func securityCard(ctx context.Context, rc *RuntimeConfig, isin string, pos *Clie
// График: купоны за полгода назад и вперёд, ближайшие оферты, погашение.
// Дата выплаты купона: P_DATE, а пока купон не выплачен — конец купонного периода.
rows, err := db.QueryContext(dctx, `SELECT TOP 12 ISNULL(P_DATE, E_DATE) AS PAY_DATE, CAST(ISNULL(SUMMA,0) AS float),
CAST(ISNULL(PERCENT_,0) AS float), FIX_DATE, ISNULL(IS_PAY,0)
FROM OD_COUPONS WHERE SHARE=@p1 AND ISNULL(P_DATE, E_DATE) >= DATEADD(month,-3,GETDATE())
ORDER BY PAY_DATE`, sql.Named("p1", valID))
rows, err := db.QueryContext(dctx, fsql.Sec04, sql.Named("p1", valID))
if err == nil {
for rows.Next() {
var pd, fx sql.NullTime
@@ -143,8 +140,7 @@ func securityCard(ctx context.Context, rc *RuntimeConfig, isin string, pos *Clie
}
rows.Close()
}
orows, err := db.QueryContext(dctx, `SELECT TOP 3 P_DATE, CAST(ISNULL(O_PRICE,0) AS float) FROM OD_OFFERS
WHERE SHARE=@p1 AND CANCEL_DATE IS NULL AND P_DATE >= GETDATE() ORDER BY P_DATE`, sql.Named("p1", valID))
orows, err := db.QueryContext(dctx, fsql.Sec05, sql.Named("p1", valID))
if err == nil {
for orows.Next() {
var pd sql.NullTime