Merge remote-tracking branch 'origin/master'
This commit is contained in:
784
svc/queries/customer_balance_list.go
Normal file
784
svc/queries/customer_balance_list.go
Normal file
@@ -0,0 +1,784 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/auth"
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/internal/authz"
|
||||
"bssapp-backend/models"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type mkCariBakiyeLine struct {
|
||||
CurrAccTypeCode int
|
||||
CariKodu string
|
||||
CariDoviz string
|
||||
SirketKodu int
|
||||
PislemTipi string
|
||||
YerelBakiye float64
|
||||
Bakiye float64
|
||||
}
|
||||
|
||||
type cariMeta struct {
|
||||
CariDetay string
|
||||
CariTip string
|
||||
Kanal1 string
|
||||
Piyasa string
|
||||
Temsilci string
|
||||
Ulke string
|
||||
Il string
|
||||
Ilce string
|
||||
TC string
|
||||
RiskDurumu string
|
||||
MuhasebeKodu string
|
||||
SirketDetay string
|
||||
}
|
||||
|
||||
type masterCariMeta struct {
|
||||
CariDetay string
|
||||
Kanal1 string
|
||||
Piyasa string
|
||||
Temsilci string
|
||||
Ulke string
|
||||
Il string
|
||||
Ilce string
|
||||
RiskDurumu string
|
||||
}
|
||||
|
||||
type balanceFilters struct {
|
||||
cariIlkGrup map[string]struct{}
|
||||
piyasa map[string]struct{}
|
||||
temsilci map[string]struct{}
|
||||
riskDurumu map[string]struct{}
|
||||
islemTipi map[string]struct{}
|
||||
ulke map[string]struct{}
|
||||
il map[string]struct{}
|
||||
ilce map[string]struct{}
|
||||
}
|
||||
|
||||
func GetCustomerBalanceList(ctx context.Context, params models.CustomerBalanceListParams) ([]models.CustomerBalanceListRow, error) {
|
||||
if strings.TrimSpace(params.SelectedDate) == "" {
|
||||
return nil, fmt.Errorf("selected_date is required")
|
||||
}
|
||||
|
||||
lines, err := loadBalanceLines(ctx, params.SelectedDate, params.CariSearch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metaMap, err := loadCariMetaMap(ctx, lines)
|
||||
if err != nil {
|
||||
log.Printf("customer_balance_list: cari meta query failed, fallback without meta: %v", err)
|
||||
metaMap = map[string]cariMeta{}
|
||||
}
|
||||
|
||||
masterMetaMap, err := loadMasterCariMetaMap(ctx, lines)
|
||||
if err != nil {
|
||||
log.Printf("customer_balance_list: master cari meta query failed, fallback without master meta: %v", err)
|
||||
masterMetaMap = map[string]masterCariMeta{}
|
||||
}
|
||||
|
||||
companyMap, err := loadCompanyMap(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glMap, err := loadGLAccountMap(ctx, lines)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rateMap, err := loadNearestTryRates(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
usdTry := rateMap["USD"]
|
||||
if usdTry <= 0 {
|
||||
usdTry = 1
|
||||
}
|
||||
|
||||
filters := buildFilters(params)
|
||||
agg := make(map[string]*models.CustomerBalanceListRow, len(lines))
|
||||
|
||||
for _, ln := range lines {
|
||||
cari := strings.TrimSpace(ln.CariKodu)
|
||||
if cari == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
curr := strings.ToUpper(strings.TrimSpace(ln.CariDoviz))
|
||||
if curr == "" {
|
||||
curr = "TRY"
|
||||
}
|
||||
|
||||
meta := metaMap[metaKey(ln.CurrAccTypeCode, cari)]
|
||||
meta.MuhasebeKodu = glMap[glKey(ln.CurrAccTypeCode, cari, ln.SirketKodu)]
|
||||
meta.SirketDetay = companyMap[ln.SirketKodu]
|
||||
master := deriveMasterCari(cari)
|
||||
mm := masterMetaMap[master]
|
||||
|
||||
if strings.TrimSpace(mm.Kanal1) != "" {
|
||||
meta.Kanal1 = mm.Kanal1
|
||||
}
|
||||
if strings.TrimSpace(mm.Piyasa) != "" {
|
||||
meta.Piyasa = mm.Piyasa
|
||||
}
|
||||
if strings.TrimSpace(mm.Temsilci) != "" {
|
||||
meta.Temsilci = mm.Temsilci
|
||||
}
|
||||
if strings.TrimSpace(mm.Ulke) != "" {
|
||||
meta.Ulke = mm.Ulke
|
||||
}
|
||||
if strings.TrimSpace(mm.Il) != "" {
|
||||
meta.Il = mm.Il
|
||||
}
|
||||
if strings.TrimSpace(mm.Ilce) != "" {
|
||||
meta.Ilce = mm.Ilce
|
||||
}
|
||||
if strings.TrimSpace(mm.RiskDurumu) != "" {
|
||||
meta.RiskDurumu = mm.RiskDurumu
|
||||
}
|
||||
|
||||
if !filters.matchLine(ln.PislemTipi, meta) {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strconv.Itoa(ln.CurrAccTypeCode) + "|" + cari + "|" + curr + "|" + strconv.Itoa(ln.SirketKodu)
|
||||
row, ok := agg[key]
|
||||
if !ok {
|
||||
row = &models.CustomerBalanceListRow{
|
||||
CariIlkGrup: meta.Kanal1,
|
||||
Piyasa: meta.Piyasa,
|
||||
Temsilci: meta.Temsilci,
|
||||
Sirket: strconv.Itoa(ln.SirketKodu),
|
||||
AnaCariKodu: master,
|
||||
AnaCariAdi: firstNonEmpty(mm.CariDetay, meta.CariDetay),
|
||||
CariKodu: cari,
|
||||
CariDetay: meta.CariDetay,
|
||||
CariTip: meta.CariTip,
|
||||
Kanal1: meta.Kanal1,
|
||||
Ozellik03: meta.RiskDurumu,
|
||||
Ozellik05: meta.Ulke,
|
||||
Ozellik06: meta.Il,
|
||||
Ozellik07: meta.Ilce,
|
||||
Il: meta.Il,
|
||||
Ilce: meta.Ilce,
|
||||
MuhasebeKodu: meta.MuhasebeKodu,
|
||||
TC: meta.TC,
|
||||
RiskDurumu: meta.RiskDurumu,
|
||||
SirketDetay: meta.SirketDetay,
|
||||
CariDoviz: curr,
|
||||
}
|
||||
agg[key] = row
|
||||
}
|
||||
|
||||
usd := toUSD(ln.Bakiye, curr, usdTry, rateMap)
|
||||
|
||||
switch strings.TrimSpace(ln.PislemTipi) {
|
||||
case "1_2":
|
||||
row.Bakiye12 += ln.Bakiye
|
||||
row.TLBakiye12 += ln.YerelBakiye
|
||||
row.USDBakiye12 += usd
|
||||
case "1_3":
|
||||
row.Bakiye13 += ln.Bakiye
|
||||
row.TLBakiye13 += ln.YerelBakiye
|
||||
row.USDBakiye13 += usd
|
||||
}
|
||||
}
|
||||
|
||||
out := make([]models.CustomerBalanceListRow, 0, len(agg))
|
||||
for _, v := range agg {
|
||||
out = append(out, *v)
|
||||
}
|
||||
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
if out[i].AnaCariKodu == out[j].AnaCariKodu {
|
||||
if out[i].CariKodu == out[j].CariKodu {
|
||||
return out[i].CariDoviz < out[j].CariDoviz
|
||||
}
|
||||
return out[i].CariKodu < out[j].CariKodu
|
||||
}
|
||||
return out[i].AnaCariKodu < out[j].AnaCariKodu
|
||||
})
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func loadMasterCariMetaMap(ctx context.Context, lines []mkCariBakiyeLine) (map[string]masterCariMeta, error) {
|
||||
masters := make(map[string]struct{})
|
||||
for _, ln := range lines {
|
||||
m := strings.TrimSpace(deriveMasterCari(ln.CariKodu))
|
||||
if m != "" {
|
||||
masters[m] = struct{}{}
|
||||
}
|
||||
}
|
||||
if len(masters) == 0 {
|
||||
return map[string]masterCariMeta{}, nil
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
WITH BaseCari AS
|
||||
(
|
||||
SELECT
|
||||
CB.CurrAccCode,
|
||||
CB.CurrAccTypeCode,
|
||||
MasterCari = LEFT(CB.CurrAccCode, 8),
|
||||
rn = ROW_NUMBER() OVER
|
||||
(
|
||||
PARTITION BY LEFT(CB.CurrAccCode, 8)
|
||||
ORDER BY CB.CurrAccCode
|
||||
)
|
||||
FROM cdCurrAcc CB WITH (NOLOCK)
|
||||
WHERE CB.CurrAccTypeCode IN (1,3)
|
||||
AND LEFT(CB.CurrAccCode, 8) IN (%s)
|
||||
),
|
||||
FirstCari AS
|
||||
(
|
||||
SELECT *
|
||||
FROM BaseCari
|
||||
WHERE rn = 1
|
||||
)
|
||||
SELECT
|
||||
CariKodu = F.MasterCari,
|
||||
CariDetay = ISNULL(cd.CurrAccDescription, ''),
|
||||
KANAL_1 = ISNULL(CASE WHEN F.CurrAccTypeCode=1 THEN VDesc.VendorAtt08Desc ELSE CDesc.CustomerAtt08Desc END, ''),
|
||||
PIYASA = ISNULL(CASE WHEN F.CurrAccTypeCode=1 THEN VDesc.VendorAtt01Desc ELSE CDesc.CustomerAtt01Desc END, ''),
|
||||
CARI_TEMSILCI = ISNULL(
|
||||
CASE
|
||||
WHEN ISNULL(CASE WHEN F.CurrAccTypeCode = 1 THEN VDesc.VendorAtt02Desc ELSE CDesc.CustomerAtt02Desc END,'') = ''
|
||||
THEN ISNULL(CASE WHEN F.CurrAccTypeCode = 1 THEN VAttr.VendorAtt09 ELSE CAttr.CustomerAtt09 END,'')
|
||||
ELSE CASE WHEN F.CurrAccTypeCode = 1 THEN VDesc.VendorAtt02Desc ELSE CDesc.CustomerAtt02Desc END
|
||||
END,''
|
||||
),
|
||||
ULKE = ISNULL(CASE WHEN F.CurrAccTypeCode=1 THEN VDesc.VendorAtt05Desc ELSE CDesc.CustomerAtt05Desc END, ''),
|
||||
IL = ISNULL(CASE WHEN F.CurrAccTypeCode=1 THEN VDesc.VendorAtt06Desc ELSE CDesc.CustomerAtt06Desc END, ''),
|
||||
ILCE = ISNULL(CASE WHEN F.CurrAccTypeCode=1 THEN VDesc.VendorAtt07Desc ELSE CDesc.CustomerAtt07Desc END, ''),
|
||||
Risk_Durumu = ISNULL(CASE WHEN F.CurrAccTypeCode=1 THEN VDesc.VendorAtt03Desc ELSE CDesc.CustomerAtt03Desc END, '')
|
||||
FROM FirstCari F
|
||||
LEFT JOIN cdCurrAccDesc cd WITH (NOLOCK)
|
||||
ON cd.CurrAccTypeCode = F.CurrAccTypeCode
|
||||
AND cd.CurrAccCode = F.CurrAccCode
|
||||
AND cd.LangCode = 'TR'
|
||||
LEFT JOIN VendorAttributeDescriptions('TR') VDesc
|
||||
ON VDesc.CurrAccCode = F.CurrAccCode
|
||||
AND VDesc.CurrAccTypeCode = F.CurrAccTypeCode
|
||||
LEFT JOIN CustomerAttributeDescriptions('TR') CDesc
|
||||
ON CDesc.CurrAccCode = F.CurrAccCode
|
||||
AND CDesc.CurrAccTypeCode = F.CurrAccTypeCode
|
||||
LEFT JOIN VendorAttributes VAttr
|
||||
ON VAttr.CurrAccCode = F.CurrAccCode
|
||||
AND VAttr.CurrAccTypeCode = F.CurrAccTypeCode
|
||||
LEFT JOIN CustomerAttributes CAttr
|
||||
ON CAttr.CurrAccCode = F.CurrAccCode
|
||||
AND CAttr.CurrAccTypeCode = F.CurrAccTypeCode
|
||||
ORDER BY F.MasterCari;
|
||||
`, quotedInList(masters))
|
||||
|
||||
rows, err := db.MssqlDB.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("master cari meta query error: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make(map[string]masterCariMeta, len(masters))
|
||||
for rows.Next() {
|
||||
var master string
|
||||
var m masterCariMeta
|
||||
if err := rows.Scan(
|
||||
&master,
|
||||
&m.CariDetay,
|
||||
&m.Kanal1,
|
||||
&m.Piyasa,
|
||||
&m.Temsilci,
|
||||
&m.Ulke,
|
||||
&m.Il,
|
||||
&m.Ilce,
|
||||
&m.RiskDurumu,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[strings.TrimSpace(master)] = m
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func loadBalanceLines(ctx context.Context, selectedDate, cariSearch string) ([]mkCariBakiyeLine, error) {
|
||||
query := `
|
||||
SELECT
|
||||
CurrAccTypeCode,
|
||||
CariKodu,
|
||||
CariDoviz,
|
||||
SirketKodu,
|
||||
PislemTipi,
|
||||
YerelBakiye,
|
||||
Bakiye
|
||||
FROM dbo.MK_CARI_BAKIYE_LIST(@SonTarih)
|
||||
WHERE (@CariSearch = '' OR CariKodu LIKE '%' + @CariSearch + '%')
|
||||
`
|
||||
|
||||
rows, err := db.MssqlDB.QueryContext(ctx, query,
|
||||
sql.Named("SonTarih", selectedDate),
|
||||
sql.Named("CariSearch", strings.TrimSpace(cariSearch)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("MK_CARI_BAKIYE_LIST query error: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make([]mkCariBakiyeLine, 0, 4096)
|
||||
for rows.Next() {
|
||||
var r mkCariBakiyeLine
|
||||
if err := rows.Scan(
|
||||
&r.CurrAccTypeCode,
|
||||
&r.CariKodu,
|
||||
&r.CariDoviz,
|
||||
&r.SirketKodu,
|
||||
&r.PislemTipi,
|
||||
&r.YerelBakiye,
|
||||
&r.Bakiye,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, r)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func loadCariMetaMap(ctx context.Context, lines []mkCariBakiyeLine) (map[string]cariMeta, error) {
|
||||
vendorCodes := make(map[string]struct{})
|
||||
customerCodes := make(map[string]struct{})
|
||||
|
||||
for _, ln := range lines {
|
||||
code := strings.TrimSpace(ln.CariKodu)
|
||||
if code == "" {
|
||||
continue
|
||||
}
|
||||
if ln.CurrAccTypeCode == 1 {
|
||||
vendorCodes[code] = struct{}{}
|
||||
} else if ln.CurrAccTypeCode == 3 {
|
||||
customerCodes[code] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(vendorCodes) == 0 && len(customerCodes) == 0 {
|
||||
return map[string]cariMeta{}, nil
|
||||
}
|
||||
|
||||
whereParts := make([]string, 0, 2)
|
||||
if len(vendorCodes) > 0 {
|
||||
whereParts = append(whereParts, fmt.Sprintf("(c.CurrAccTypeCode=1 AND c.CurrAccCode IN (%s))", quotedInList(vendorCodes)))
|
||||
}
|
||||
if len(customerCodes) > 0 {
|
||||
whereParts = append(whereParts, fmt.Sprintf("(c.CurrAccTypeCode=3 AND c.CurrAccCode IN (%s))", quotedInList(customerCodes)))
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
c.CurrAccTypeCode,
|
||||
c.CurrAccCode,
|
||||
CariDetay = ISNULL(d.CurrAccDescription, ''),
|
||||
CariTip = CASE WHEN c.CurrAccTypeCode = 1 THEN N'Satıcı' ELSE N'Müşteri' END,
|
||||
KANAL_1 = ISNULL(CASE WHEN c.CurrAccTypeCode=1 THEN vad.VendorAtt08Desc ELSE cad.CustomerAtt08Desc END, ''),
|
||||
PIYASA = ISNULL(CASE WHEN c.CurrAccTypeCode=1 THEN vad.VendorAtt01Desc ELSE cad.CustomerAtt01Desc END, ''),
|
||||
CARI_TEMSILCI = ISNULL(
|
||||
CASE
|
||||
WHEN ISNULL(CASE WHEN c.CurrAccTypeCode=1 THEN vad.VendorAtt02Desc ELSE cad.CustomerAtt02Desc END, '') = ''
|
||||
THEN ISNULL(CASE WHEN c.CurrAccTypeCode=1 THEN va.VendorAtt09 ELSE ca.CustomerAtt09 END, '')
|
||||
ELSE CASE WHEN c.CurrAccTypeCode=1 THEN vad.VendorAtt02Desc ELSE cad.CustomerAtt02Desc END
|
||||
END,
|
||||
''),
|
||||
ULKE = ISNULL(CASE WHEN c.CurrAccTypeCode=1 THEN vad.VendorAtt05Desc ELSE cad.CustomerAtt05Desc END, ''),
|
||||
IL = ISNULL(CASE WHEN c.CurrAccTypeCode=1 THEN vad.VendorAtt06Desc ELSE cad.CustomerAtt06Desc END, ''),
|
||||
ILCE = ISNULL(CASE WHEN c.CurrAccTypeCode=1 THEN vad.VendorAtt07Desc ELSE cad.CustomerAtt07Desc END, ''),
|
||||
TC = ISNULL(c.IdentityNum, ''),
|
||||
Risk_Durumu = ISNULL(CASE WHEN c.CurrAccTypeCode=1 THEN vad.VendorAtt03Desc ELSE cad.CustomerAtt03Desc END, '')
|
||||
FROM cdCurrAcc c WITH(NOLOCK)
|
||||
LEFT JOIN cdCurrAccDesc d WITH(NOLOCK)
|
||||
ON d.CurrAccTypeCode = c.CurrAccTypeCode
|
||||
AND d.CurrAccCode = c.CurrAccCode
|
||||
AND d.LangCode = 'TR'
|
||||
LEFT JOIN VendorAttributes va WITH(NOLOCK)
|
||||
ON va.CurrAccTypeCode = c.CurrAccTypeCode
|
||||
AND va.CurrAccCode = c.CurrAccCode
|
||||
LEFT JOIN VendorAttributeDescriptions('TR') vad
|
||||
ON vad.CurrAccTypeCode = c.CurrAccTypeCode
|
||||
AND vad.CurrAccCode = c.CurrAccCode
|
||||
LEFT JOIN CustomerAttributes ca WITH(NOLOCK)
|
||||
ON ca.CurrAccTypeCode = c.CurrAccTypeCode
|
||||
AND ca.CurrAccCode = c.CurrAccCode
|
||||
LEFT JOIN CustomerAttributeDescriptions('TR') cad
|
||||
ON cad.CurrAccTypeCode = c.CurrAccTypeCode
|
||||
AND cad.CurrAccCode = c.CurrAccCode
|
||||
WHERE c.CurrAccTypeCode IN (1,3)
|
||||
AND (%s)
|
||||
`, strings.Join(whereParts, " OR "))
|
||||
|
||||
rows, err := db.MssqlDB.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cari meta query error: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make(map[string]cariMeta, len(lines))
|
||||
for rows.Next() {
|
||||
var t int
|
||||
var code string
|
||||
var m cariMeta
|
||||
if err := rows.Scan(
|
||||
&t,
|
||||
&code,
|
||||
&m.CariDetay,
|
||||
&m.CariTip,
|
||||
&m.Kanal1,
|
||||
&m.Piyasa,
|
||||
&m.Temsilci,
|
||||
&m.Ulke,
|
||||
&m.Il,
|
||||
&m.Ilce,
|
||||
&m.TC,
|
||||
&m.RiskDurumu,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[metaKey(t, code)] = m
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func loadGLAccountMap(ctx context.Context, lines []mkCariBakiyeLine) (map[string]string, error) {
|
||||
vendorCodes := make(map[string]struct{})
|
||||
customerCodes := make(map[string]struct{})
|
||||
companyCodes := make(map[int]struct{})
|
||||
|
||||
for _, ln := range lines {
|
||||
code := strings.TrimSpace(ln.CariKodu)
|
||||
if code == "" {
|
||||
continue
|
||||
}
|
||||
companyCodes[ln.SirketKodu] = struct{}{}
|
||||
if ln.CurrAccTypeCode == 1 {
|
||||
vendorCodes[code] = struct{}{}
|
||||
} else if ln.CurrAccTypeCode == 3 {
|
||||
customerCodes[code] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(companyCodes) == 0 || (len(vendorCodes) == 0 && len(customerCodes) == 0) {
|
||||
return map[string]string{}, nil
|
||||
}
|
||||
|
||||
whereParts := make([]string, 0, 2)
|
||||
if len(vendorCodes) > 0 {
|
||||
whereParts = append(whereParts, fmt.Sprintf("(CurrAccTypeCode=1 AND CurrAccCode IN (%s))", quotedInList(vendorCodes)))
|
||||
}
|
||||
if len(customerCodes) > 0 {
|
||||
whereParts = append(whereParts, fmt.Sprintf("(CurrAccTypeCode=3 AND CurrAccCode IN (%s))", quotedInList(customerCodes)))
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT CurrAccTypeCode, CurrAccCode, CompanyCode, GLAccCode
|
||||
FROM prCurrAccGLAccount WITH(NOLOCK)
|
||||
WHERE PostAccTypeCode = 100
|
||||
AND CompanyCode IN (%s)
|
||||
AND (%s)
|
||||
`, intInList(companyCodes), strings.Join(whereParts, " OR "))
|
||||
|
||||
rows, err := db.MssqlDB.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gl account query error: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make(map[string]string)
|
||||
for rows.Next() {
|
||||
var t int
|
||||
var code string
|
||||
var company int
|
||||
var gl sql.NullString
|
||||
if err := rows.Scan(&t, &code, &company, &gl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[glKey(t, code, company)] = strings.TrimSpace(gl.String)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func loadCompanyMap(ctx context.Context) (map[int]string, error) {
|
||||
rows, err := db.MssqlDB.QueryContext(ctx, `SELECT CompanyCode, CompanyName FROM cdCompany WITH(NOLOCK)`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("company map query error: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make(map[int]string)
|
||||
for rows.Next() {
|
||||
var code int
|
||||
var name sql.NullString
|
||||
if err := rows.Scan(&code, &name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[code] = strings.TrimSpace(name.String)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func loadNearestTryRates(ctx context.Context) (map[string]float64, error) {
|
||||
query := `
|
||||
WITH Ranked AS (
|
||||
SELECT
|
||||
CurrencyCode,
|
||||
Rate,
|
||||
rn = ROW_NUMBER() OVER (
|
||||
PARTITION BY CurrencyCode
|
||||
ORDER BY ABS(DATEDIFF(DAY, Date, GETDATE())), Date DESC
|
||||
)
|
||||
FROM AllExchangeRates
|
||||
WHERE RelationCurrencyCode = 'TRY'
|
||||
AND ExchangeTypeCode = 6
|
||||
AND Rate > 0
|
||||
)
|
||||
SELECT CurrencyCode, Rate
|
||||
FROM Ranked
|
||||
WHERE rn = 1
|
||||
`
|
||||
|
||||
rows, err := db.MssqlDB.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("exchange rates query error: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := map[string]float64{"TRY": 1}
|
||||
for rows.Next() {
|
||||
var code string
|
||||
var rate float64
|
||||
if err := rows.Scan(&code, &rate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
code = strings.ToUpper(strings.TrimSpace(code))
|
||||
if code != "" && rate > 0 {
|
||||
out[code] = rate
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func toUSD(amount float64, currency string, usdTry float64, rateMap map[string]float64) float64 {
|
||||
if usdTry <= 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch currency {
|
||||
case "USD":
|
||||
return amount
|
||||
case "TRY":
|
||||
return amount / usdTry
|
||||
default:
|
||||
currTry := rateMap[currency]
|
||||
if currTry <= 0 {
|
||||
return 0
|
||||
}
|
||||
return (amount * currTry) / usdTry
|
||||
}
|
||||
}
|
||||
|
||||
func deriveMasterCari(cari string) string {
|
||||
cari = strings.TrimSpace(cari)
|
||||
if cari == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
base := cari
|
||||
if idx := strings.Index(base, "/"); idx > 0 {
|
||||
base = base[:idx]
|
||||
}
|
||||
|
||||
base = strings.TrimSpace(base)
|
||||
if len(base) >= 8 {
|
||||
return strings.TrimSpace(base[:8])
|
||||
}
|
||||
|
||||
return base
|
||||
}
|
||||
|
||||
func buildFilters(params models.CustomerBalanceListParams) balanceFilters {
|
||||
return balanceFilters{
|
||||
cariIlkGrup: parseCSVSet(params.CariIlkGrup),
|
||||
piyasa: parseCSVSet(params.Piyasa),
|
||||
temsilci: parseCSVSet(params.Temsilci),
|
||||
riskDurumu: parseCSVSet(params.RiskDurumu),
|
||||
islemTipi: parseCSVSet(params.IslemTipi),
|
||||
ulke: parseCSVSet(params.Ulke),
|
||||
il: parseCSVSet(params.Il),
|
||||
ilce: parseCSVSet(params.Ilce),
|
||||
}
|
||||
}
|
||||
|
||||
func (f balanceFilters) matchLine(islemTipi string, m cariMeta) bool {
|
||||
if !matchSet(f.islemTipi, islemTipi) {
|
||||
return false
|
||||
}
|
||||
if !matchSet(f.cariIlkGrup, m.Kanal1) {
|
||||
return false
|
||||
}
|
||||
if !matchSet(f.piyasa, m.Piyasa) {
|
||||
return false
|
||||
}
|
||||
if !matchSet(f.temsilci, m.Temsilci) {
|
||||
return false
|
||||
}
|
||||
if !matchSet(f.riskDurumu, m.RiskDurumu) {
|
||||
return false
|
||||
}
|
||||
if !matchSet(f.ulke, m.Ulke) {
|
||||
return false
|
||||
}
|
||||
if !matchSet(f.il, m.Il) {
|
||||
return false
|
||||
}
|
||||
if !matchSet(f.ilce, m.Ilce) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func matchSet(set map[string]struct{}, value string) bool {
|
||||
if len(set) == 0 {
|
||||
return true
|
||||
}
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return true
|
||||
}
|
||||
_, ok := set[trimmed]
|
||||
return ok
|
||||
}
|
||||
|
||||
func parseCSVSet(v string) map[string]struct{} {
|
||||
out := make(map[string]struct{})
|
||||
for _, p := range strings.Split(v, ",") {
|
||||
t := strings.TrimSpace(p)
|
||||
if t == "" {
|
||||
continue
|
||||
}
|
||||
out[t] = struct{}{}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func getAuthorizedPiyasaCodes(ctx context.Context) ([]string, error) {
|
||||
claims, ok := auth.GetClaimsFromContext(ctx)
|
||||
if !ok || claims == nil {
|
||||
return nil, fmt.Errorf("unauthorized: claims not found")
|
||||
}
|
||||
if claims.IsAdmin() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
rawCodes := authz.GetPiyasaCodesFromCtx(ctx)
|
||||
if len(rawCodes) == 0 {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
unique := make(map[string]struct{}, len(rawCodes))
|
||||
out := make([]string, 0, len(rawCodes))
|
||||
for _, code := range rawCodes {
|
||||
norm := strings.ToUpper(strings.TrimSpace(code))
|
||||
if norm == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := unique[norm]; exists {
|
||||
continue
|
||||
}
|
||||
unique[norm] = struct{}{}
|
||||
out = append(out, norm)
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return []string{}, nil
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func buildPiyasaWhereClause(codes []string, column string) string {
|
||||
if len(codes) == 0 {
|
||||
return "1=1"
|
||||
}
|
||||
return authz.BuildINClause(column, codes)
|
||||
}
|
||||
|
||||
func metaKey(currType int, code string) string {
|
||||
return strconv.Itoa(currType) + "|" + strings.TrimSpace(code)
|
||||
}
|
||||
|
||||
func glKey(currType int, code string, company int) string {
|
||||
return strconv.Itoa(currType) + "|" + strings.TrimSpace(code) + "|" + strconv.Itoa(company)
|
||||
}
|
||||
|
||||
func quotedInList(set map[string]struct{}) string {
|
||||
vals := make([]string, 0, len(set))
|
||||
for v := range set {
|
||||
esc := strings.ReplaceAll(strings.TrimSpace(v), "'", "''")
|
||||
if esc != "" {
|
||||
vals = append(vals, "'"+esc+"'")
|
||||
}
|
||||
}
|
||||
if len(vals) == 0 {
|
||||
return "''"
|
||||
}
|
||||
sort.Strings(vals)
|
||||
return strings.Join(vals, ",")
|
||||
}
|
||||
|
||||
func intInList(set map[int]struct{}) string {
|
||||
vals := make([]int, 0, len(set))
|
||||
for v := range set {
|
||||
vals = append(vals, v)
|
||||
}
|
||||
if len(vals) == 0 {
|
||||
return "0"
|
||||
}
|
||||
sort.Ints(vals)
|
||||
parts := make([]string, 0, len(vals))
|
||||
for _, v := range vals {
|
||||
parts = append(parts, strconv.Itoa(v))
|
||||
}
|
||||
return strings.Join(parts, ",")
|
||||
}
|
||||
|
||||
func firstNonEmpty(v ...string) string {
|
||||
for _, s := range v {
|
||||
if strings.TrimSpace(s) != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user