Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-03-09 13:19:14 +03:00
parent 6df18ed14d
commit 0d303f0c0f
12 changed files with 382 additions and 280 deletions

View File

@@ -15,15 +15,16 @@ import (
)
type mkCariBakiyeLine struct {
CurrAccTypeCode int
CariKodu string
CariDoviz string
SirketKodu int
PislemTipi string
YerelBakiye float64
Bakiye float64
VadeGun float64
VadeBelgeGun float64
CurrAccTypeCode int
CariKodu string
CariDoviz string
SirketKodu int
PislemTipi string
ParasalIslemTipi string
YerelBakiye float64
Bakiye float64
VadeGun float64
VadeBelgeGun float64
}
type cariMeta struct {
@@ -181,13 +182,13 @@ func GetCustomerBalanceList(ctx context.Context, params models.CustomerBalanceLi
}
usd := toUSD(ln.Bakiye, curr, usdTry, rateMap)
switch strings.TrimSpace(ln.PislemTipi) {
case "1_2":
add12, add13 := resolveBalanceBuckets(ln)
if add12 {
row.Bakiye12 += ln.Bakiye
row.TLBakiye12 += ln.YerelBakiye
row.USDBakiye12 += usd
case "1_3":
}
if add13 {
row.Bakiye13 += ln.Bakiye
row.TLBakiye13 += ln.YerelBakiye
row.USDBakiye13 += usd
@@ -319,13 +320,14 @@ func loadBalanceLines(ctx context.Context, selectedDate, cariSearch string) ([]m
return nil, err
}
query := fmt.Sprintf(`
queryTemplate := `
SELECT
CurrAccTypeCode,
CariKodu,
CariDoviz,
SirketKodu,
PislemTipi,
%s
YerelBakiye,
Bakiye,
CAST(0 AS DECIMAL(18,4)) AS Vade_Gun,
@@ -333,13 +335,33 @@ func loadBalanceLines(ctx context.Context, selectedDate, cariSearch string) ([]m
FROM dbo.MK_CARI_BAKIYE_LIST(@SonTarih)
WHERE (@CariSearch = '' OR CariKodu LIKE '%%' + @CariSearch + '%%')
AND %s
`, piyasaScope)
`
rows, err := db.MssqlDB.QueryContext(ctx, query,
sql.Named("SonTarih", selectedDate),
sql.Named("CariSearch", strings.TrimSpace(cariSearch)),
selectParasalCandidates := make([]string, 0, 7)
if expr := strings.TrimSpace(resolveParasalIslemSelectExpr(ctx, "SELECT * FROM dbo.MK_CARI_BAKIYE_LIST('2000-01-01')")); expr != "" {
selectParasalCandidates = append(selectParasalCandidates, expr)
}
selectParasalCandidates = append(selectParasalCandidates,
"CAST(ATAtt01 AS varchar(16)) AS ParasalIslemTipi,",
"CAST(ParasalIslemTipi AS varchar(16)) AS ParasalIslemTipi,",
"CAST(ParislemTipi AS varchar(16)) AS ParasalIslemTipi,",
"CAST(ParIslemTipi AS varchar(16)) AS ParasalIslemTipi,",
"CAST('' AS varchar(16)) AS ParasalIslemTipi,",
)
if err != nil {
var rows *sql.Rows
for i, sel := range selectParasalCandidates {
query := fmt.Sprintf(queryTemplate, sel, piyasaScope)
rows, err = db.MssqlDB.QueryContext(ctx, query,
sql.Named("SonTarih", selectedDate),
sql.Named("CariSearch", strings.TrimSpace(cariSearch)),
)
if err == nil {
break
}
if i < len(selectParasalCandidates)-1 && isInvalidColumnError(err) {
continue
}
return nil, fmt.Errorf("MK_CARI_BAKIYE_LIST query error: %w", err)
}
defer rows.Close()
@@ -353,6 +375,7 @@ func loadBalanceLines(ctx context.Context, selectedDate, cariSearch string) ([]m
&r.CariDoviz,
&r.SirketKodu,
&r.PislemTipi,
&r.ParasalIslemTipi,
&r.YerelBakiye,
&r.Bakiye,
&r.VadeGun,
@@ -648,7 +671,7 @@ func buildFilters(params models.CustomerBalanceListParams) balanceFilters {
piyasa: parseCSVSet(params.Piyasa),
temsilci: parseCSVSet(params.Temsilci),
riskDurumu: parseCSVSet(params.RiskDurumu),
islemTipi: parseCSVSet(params.IslemTipi),
islemTipi: parseIslemTipiSet(params.IslemTipi),
ulke: parseCSVSet(params.Ulke),
il: parseCSVSet(params.Il),
ilce: parseCSVSet(params.Ilce),
@@ -707,6 +730,28 @@ func parseCSVSet(v string) map[string]struct{} {
return out
}
func parseIslemTipiSet(v string) map[string]struct{} {
raw := parseCSVSet(v)
if len(raw) == 0 {
return raw
}
out := make(map[string]struct{}, 2)
for token := range raw {
switch strings.ToLower(strings.TrimSpace(token)) {
case "1_2", "prbr_1_2", "usd_1_2", "try_1_2", "tl_1_2", "usd_bakiye_1_2", "tl_bakiye_1_2":
out["1_2"] = struct{}{}
case "1_3", "prbr_1_3", "usd_1_3", "try_1_3", "tl_1_3", "usd_bakiye_1_3", "tl_bakiye_1_3":
out["1_3"] = struct{}{}
}
}
if len(out) == 0 {
return raw
}
return out
}
func getAuthorizedPiyasaCodes(ctx context.Context) ([]string, error) {
claims, ok := auth.GetClaimsFromContext(ctx)
if !ok || claims == nil {
@@ -794,3 +839,181 @@ func firstNonEmpty(v ...string) string {
}
return ""
}
func isInvalidColumnError(err error) bool {
if err == nil {
return false
}
msg := strings.ToLower(err.Error())
return strings.Contains(msg, "invalid column name")
}
func shouldSkipBalanceLine(ln mkCariBakiyeLine) bool {
add12, add13 := resolveBalanceBuckets(ln)
p := strings.TrimSpace(ln.PislemTipi)
if p == "1_2" {
return !add12
}
if p == "1_3" {
return !add13
}
return false
}
func resolveBalanceBuckets(ln mkCariBakiyeLine) (add12 bool, add13 bool) {
p := strings.TrimSpace(ln.PislemTipi)
t := normalizeParasalIslemTipi(ln.ParasalIslemTipi)
switch t {
case "1":
return true, true
case "2", "1_2":
return true, false
case "3", "1_3":
return false, true
}
// Parasal tip yoksa eski davranis: PislemTipi'ne gore ayir.
if p == "1_2" {
return true, false
}
if p == "1_3" {
return false, true
}
return false, false
}
func normalizeParasalIslemTipi(v string) string {
s := strings.TrimSpace(v)
if s == "" {
return ""
}
lower := strings.ToLower(s)
compact := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(lower, " ", ""), "-", "_"), "/", "_")
if strings.Contains(compact, "1_2") {
return "1_2"
}
if strings.Contains(compact, "1_3") {
return "1_3"
}
// "1,2" / "1,3" gibi liste formatlarini dogrudan yakala.
tokenized := strings.NewReplacer(" ", "", ";", ",", "|", ",", "/", ",", "-", ",", "_", ",").Replace(lower)
parts := strings.Split(tokenized, ",")
has1 := false
has2 := false
has3 := false
for _, p := range parts {
t := strings.TrimSpace(p)
switch t {
case "1":
has1 = true
case "2":
has2 = true
case "3":
has3 = true
}
}
if has1 && has2 {
return "1_2"
}
if has1 && has3 {
return "1_3"
}
if has2 && !has1 && !has3 {
return "2"
}
if has3 && !has1 && !has2 {
return "3"
}
if has1 && !has2 && !has3 {
return "1"
}
// "2.00", "2,00", " 2 " gibi varyasyonlari tek tipe indir.
s = strings.ReplaceAll(s, ",", ".")
if n, err := strconv.ParseFloat(s, 64); err == nil {
return strconv.Itoa(int(n))
}
// Metinsel geldiyse ilk rakam bloğunu al.
start := -1
end := -1
for i, r := range s {
if r >= '0' && r <= '9' {
if start == -1 {
start = i
}
end = i
continue
}
if start != -1 {
break
}
}
if start == -1 || end < start {
return s
}
return s[start : end+1]
}
func resolveParasalIslemSelectExpr(ctx context.Context, sampleQuery string) string {
sampleQuery = strings.TrimSpace(sampleQuery)
if sampleQuery == "" {
return ""
}
metaQuery := `
SELECT name
FROM sys.dm_exec_describe_first_result_set(@tsql, NULL, 0)
WHERE error_number IS NULL
AND name IS NOT NULL
`
rows, err := db.MssqlDB.QueryContext(ctx, metaQuery, sql.Named("tsql", sampleQuery))
if err != nil {
return ""
}
defer rows.Close()
type candidate struct {
key string
expr string
}
priority := []candidate{
{key: "ata tt01", expr: "CAST(%s AS varchar(16)) AS ParasalIslemTipi,"},
{key: "atatt01", expr: "CAST(%s AS varchar(16)) AS ParasalIslemTipi,"},
{key: "parasalislemtipi", expr: "CAST(%s AS varchar(16)) AS ParasalIslemTipi,"},
{key: "parislemtipi", expr: "CAST(%s AS varchar(16)) AS ParasalIslemTipi,"},
{key: "parislemtur", expr: "CAST(%s AS varchar(16)) AS ParasalIslemTipi,"},
}
available := make(map[string]string)
for rows.Next() {
var col sql.NullString
if err := rows.Scan(&col); err != nil {
return ""
}
name := strings.TrimSpace(col.String)
if name == "" {
continue
}
normalized := strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(name, "_", ""), " ", ""))
available[normalized] = name
}
if err := rows.Err(); err != nil {
return ""
}
for _, c := range priority {
key := strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(c.key, "_", ""), " ", ""))
if col, ok := available[key]; ok {
return fmt.Sprintf(c.expr, quoteSQLIdent(col))
}
}
return ""
}
func quoteSQLIdent(ident string) string {
return "[" + strings.ReplaceAll(strings.TrimSpace(ident), "]", "]]") + "]"
}