package queries import ( "bssapp-backend/models" "context" "database/sql" "fmt" "log" "math" "sort" "strconv" "strings" "time" "bssapp-backend/db" ) func GetStatementAgingBalanceList(ctx context.Context, params models.CustomerBalanceListParams) ([]models.CustomerBalanceListRow, error) { selectedDate := strings.TrimSpace(params.SelectedDate) if selectedDate == "" { selectedDate = time.Now().Format("2006-01-02") } lines, err := loadAgingBalanceLines(ctx, strings.TrimSpace(params.CariSearch)) if err != nil { return nil, err } metaMap, err := loadCariMetaMap(ctx, lines) if err != nil { log.Printf("statement_aging_balance: cari meta query failed, fallback without meta: %v", err) metaMap = map[string]cariMeta{} } masterMetaMap, err := loadMasterCariMetaMap(ctx, lines) if err != nil { log.Printf("statement_aging_balance: 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)) weightMap := make(map[string]float64, len(lines)) vadeSumMap := make(map[string]float64, len(lines)) vadeBelgeSumMap := make(map[string]float64, 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) tl := toTRY(ln.Bakiye, curr, rateMap) switch strings.TrimSpace(ln.PislemTipi) { case "1_2": row.Bakiye12 += ln.Bakiye row.TLBakiye12 += tl row.USDBakiye12 += usd case "1_3": row.Bakiye13 += ln.Bakiye row.TLBakiye13 += tl row.USDBakiye13 += usd } w := math.Abs(ln.Bakiye) if w > 0 { weightMap[key] += w vadeSumMap[key] += (ln.VadeGun * w) vadeBelgeSumMap[key] += (ln.VadeBelgeGun * w) } } out := make([]models.CustomerBalanceListRow, 0, len(agg)) for k, v := range agg { base := weightMap[k] if base > 0 { v.VadeGun = vadeSumMap[k] / base v.VadeBelgeGun = vadeBelgeSumMap[k] / base } 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 }) _ = selectedDate return out, nil } func loadAgingBalanceLines(ctx context.Context, cariSearch string) ([]mkCariBakiyeLine, error) { piyasaScope, err := buildPiyasaExistsForCariCode(ctx, "LTRIM(RTRIM(CariKodu))") if err != nil { return nil, err } query := fmt.Sprintf(` SELECT CurrAccTypeCode, CariKodu = LTRIM(RTRIM(CariKodu)), CariDoviz = LTRIM(RTRIM(CariDoviz)), SirketKodu, PislemTipi, YerelBakiye = CAST(0 AS DECIMAL(18,2)), Bakiye, Vade_Gun, Vade_BelgeTarihi_Gun FROM dbo.CARI_BAKIYE_GUN_CACHE WHERE (@CariSearch = '' OR LTRIM(RTRIM(CariKodu)) LIKE '%%' + @CariSearch + '%%') AND %s ORDER BY CariKodu, CariDoviz, PislemTipi `, piyasaScope) rows, err := db.MssqlDB.QueryContext(ctx, query, sql.Named("CariSearch", strings.TrimSpace(cariSearch))) if err != nil { return nil, fmt.Errorf("CARI_BAKIYE_GUN_CACHE 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, &r.VadeGun, &r.VadeBelgeGun, ); err != nil { return nil, err } out = append(out, r) } if err := rows.Err(); err != nil { return nil, err } return out, nil } func toTRY(amount float64, currency string, rateMap map[string]float64) float64 { switch currency { case "TRY": return amount case "": return amount default: currTry := rateMap[currency] if currTry <= 0 { return 0 } return amount * currTry } }