From 0d303f0c0f68fe9f7e5bcf897a42c740ae250b37 Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Mon, 9 Mar 2026 13:19:14 +0300 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- scripts/sql/add_indexes_order_validate.sql | 27 -- .../setup_statement_aging_cache_pipeline.sql | 18 -- svc/queries/account.go | 16 +- svc/queries/customer_balance_list.go | 263 ++++++++++++++++-- svc/queries/statement_aging_balance_list.go | 38 ++- svc/queries/statements_detail.go | 5 +- svc/routes/product_images.go | 116 +++++--- ...ig.js.temporary.compiled.1772788258926.mjs | 125 --------- ui/src/pages/AgingCustomerBalancelist.go.vue | 14 +- ui/src/pages/CustomerBalanceList.vue | 14 +- ui/src/stores/accountAgingBalanceStore.js | 8 +- ui/src/stores/customerBalanceListStore.js | 18 +- 12 files changed, 382 insertions(+), 280 deletions(-) delete mode 100644 scripts/sql/add_indexes_order_validate.sql delete mode 100644 scripts/sql/setup_statement_aging_cache_pipeline.sql delete mode 100644 ui/quasar.config.js.temporary.compiled.1772788258926.mjs diff --git a/scripts/sql/add_indexes_order_validate.sql b/scripts/sql/add_indexes_order_validate.sql deleted file mode 100644 index c573fcb..0000000 --- a/scripts/sql/add_indexes_order_validate.sql +++ /dev/null @@ -1,27 +0,0 @@ -/* Indexes for order validate performance */ - -IF NOT EXISTS ( - SELECT 1 - FROM sys.indexes - WHERE name = 'IX_trOrderLine_OrderHeader_ItemCode' - AND object_id = OBJECT_ID('dbo.trOrderLine') -) -BEGIN - CREATE NONCLUSTERED INDEX IX_trOrderLine_OrderHeader_ItemCode - ON dbo.trOrderLine (OrderHeaderID, ItemCode) - INCLUDE (ItemTypeCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code, LineDescription, SortOrder, OrderLineID); -END -GO - -IF NOT EXISTS ( - SELECT 1 - FROM sys.indexes - WHERE name = 'IX_prItemVariant_Combo' - AND object_id = OBJECT_ID('dbo.prItemVariant') -) -BEGIN - CREATE NONCLUSTERED INDEX IX_prItemVariant_Combo - ON dbo.prItemVariant (ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code) - INCLUDE (PLU); -END -GO diff --git a/scripts/sql/setup_statement_aging_cache_pipeline.sql b/scripts/sql/setup_statement_aging_cache_pipeline.sql deleted file mode 100644 index 07808c6..0000000 --- a/scripts/sql/setup_statement_aging_cache_pipeline.sql +++ /dev/null @@ -1,18 +0,0 @@ -USE [BAGGI_V3] -GO - -SET ANSI_NULLS ON -GO -SET QUOTED_IDENTIFIER ON -GO - -CREATE OR ALTER PROCEDURE [dbo].[SP_BUILD_STATEMENT_AGING_PIPELINE] -AS -BEGIN - SET NOCOUNT ON; - SET XACT_ABORT ON; - - EXEC dbo.SP_BUILD_CARI_VADE_GUN_STAGING; - EXEC dbo.SP_BUILD_CARI_BAKIYE_CACHE; -END -GO diff --git a/svc/queries/account.go b/svc/queries/account.go index e28aa9a..8fad1a6 100644 --- a/svc/queries/account.go +++ b/svc/queries/account.go @@ -101,15 +101,19 @@ func GetAccounts(ctx context.Context) ([]models.Account, error) { return nil, err } - if len(acc.AccountCode) >= 4 { - acc.DisplayCode = - strings.TrimSpace(acc.AccountCode[:3] + " " + acc.AccountCode[3:]) - } else { - acc.DisplayCode = acc.AccountCode - } + acc.DisplayCode = formatAccountDisplayCode(acc.AccountCode) accounts = append(accounts, acc) } return accounts, rows.Err() } + +func formatAccountDisplayCode(code string) string { + trimmed := strings.TrimSpace(code) + runes := []rune(trimmed) + if len(runes) <= 3 { + return trimmed + } + return strings.TrimSpace(string(runes[:3]) + " " + string(runes[3:])) +} diff --git a/svc/queries/customer_balance_list.go b/svc/queries/customer_balance_list.go index aca4990..d2dd7f9 100644 --- a/svc/queries/customer_balance_list.go +++ b/svc/queries/customer_balance_list.go @@ -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), "]", "]]") + "]" +} diff --git a/svc/queries/statement_aging_balance_list.go b/svc/queries/statement_aging_balance_list.go index 66057fb..8906ed8 100644 --- a/svc/queries/statement_aging_balance_list.go +++ b/svc/queries/statement_aging_balance_list.go @@ -137,13 +137,13 @@ func GetStatementAgingBalanceList(ctx context.Context, params models.CustomerBal usd := toUSD(ln.Bakiye, curr, usdTry, rateMap) tl := toTRY(ln.Bakiye, curr, rateMap) - - switch strings.TrimSpace(ln.PislemTipi) { - case "1_2": + add12, add13 := resolveBalanceBuckets(ln) + if add12 { row.Bakiye12 += ln.Bakiye row.TLBakiye12 += tl row.USDBakiye12 += usd - case "1_3": + } + if add13 { row.Bakiye13 += ln.Bakiye row.TLBakiye13 += tl row.USDBakiye13 += usd @@ -187,13 +187,14 @@ func loadAgingBalanceLines(ctx context.Context, cariSearch string) ([]mkCariBaki return nil, err } - query := fmt.Sprintf(` + queryTemplate := ` SELECT CurrAccTypeCode, CariKodu = LTRIM(RTRIM(CariKodu)), CariDoviz = LTRIM(RTRIM(CariDoviz)), SirketKodu, PislemTipi, + %s YerelBakiye = CAST(0 AS DECIMAL(18,2)), Bakiye, Vade_Gun, @@ -202,10 +203,30 @@ func loadAgingBalanceLines(ctx context.Context, cariSearch string) ([]mkCariBaki 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 { + selectParasalCandidates := make([]string, 0, 7) + if expr := strings.TrimSpace(resolveParasalIslemSelectExpr(ctx, "SELECT * FROM dbo.CARI_BAKIYE_GUN_CACHE")); 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,", + ) + + var rows *sql.Rows + for i, sel := range selectParasalCandidates { + query := fmt.Sprintf(queryTemplate, sel, piyasaScope) + rows, err = db.MssqlDB.QueryContext(ctx, query, sql.Named("CariSearch", strings.TrimSpace(cariSearch))) + if err == nil { + break + } + if i < len(selectParasalCandidates)-1 && isInvalidColumnError(err) { + continue + } return nil, fmt.Errorf("CARI_BAKIYE_GUN_CACHE query error: %w", err) } defer rows.Close() @@ -219,6 +240,7 @@ func loadAgingBalanceLines(ctx context.Context, cariSearch string) ([]mkCariBaki &r.CariDoviz, &r.SirketKodu, &r.PislemTipi, + &r.ParasalIslemTipi, &r.YerelBakiye, &r.Bakiye, &r.VadeGun, diff --git a/svc/queries/statements_detail.go b/svc/queries/statements_detail.go index 59418cb..6ce4a8a 100644 --- a/svc/queries/statements_detail.go +++ b/svc/queries/statements_detail.go @@ -23,7 +23,10 @@ SELECT a.ItemCode AS Urun_Kodu, a.ColorCode AS Urun_Rengi, SUM(a.Qty1) AS Toplam_Adet, - SUM(ABS(a.Doc_Price)) AS Toplam_Fiyat, + CAST( + SUM(a.Qty1 * ABS(a.Doc_Price)) / NULLIF(SUM(a.Qty1), 0) + AS numeric(18,2) + ) AS Toplam_Fiyat, CAST(SUM(a.Qty1 * ABS(a.Doc_Price)) AS numeric(18,2)) AS Toplam_Tutar FROM AllInvoicesWithAttributes a LEFT JOIN prItemAttribute AnaGrup diff --git a/svc/routes/product_images.go b/svc/routes/product_images.go index 413ac47..8921342 100644 --- a/svc/routes/product_images.go +++ b/svc/routes/product_images.go @@ -23,25 +23,34 @@ type ProductImageItem struct { ContentURL string `json:"content_url"` } +// +// LIST PRODUCT IMAGES +// + // GET /api/product-images?code=...&color=... func GetProductImagesHandler(pg *sql.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + reqID := strings.TrimSpace(r.Header.Get("X-Request-ID")) if reqID == "" { reqID = uuid.NewString() } + w.Header().Set("X-Request-ID", reqID) code := strings.TrimSpace(r.URL.Query().Get("code")) color := strings.TrimSpace(r.URL.Query().Get("color")) if code == "" { + slog.Warn("product_images.list.bad_request", "req_id", reqID, "path", r.URL.Path, "query", r.URL.RawQuery, "reason", "missing_code", ) + http.Error(w, "Eksik parametre: code gerekli", http.StatusBadRequest) return } @@ -50,38 +59,59 @@ func GetProductImagesHandler(pg *sql.DB) http.HandlerFunc { SELECT b.id, b.file_name, - COALESCE(b.file_size, 0) AS file_size, - COALESCE(b.storage_path, '') AS storage_path + COALESCE(b.file_size,0) AS file_size, + COALESCE(b.storage_path,'') AS storage_path FROM dfblob b JOIN mmitem i ON i.id = b.src_id WHERE b.typ = 'img' AND b.src_table = 'mmitem' AND UPPER(i.code) = UPPER($1) - AND ($2 = '' OR b.file_name ILIKE '%' || '-' || $2 || '-%') -ORDER BY COALESCE(b.sort_order, 999999), b.zlins_dttm DESC, b.id DESC + AND ( + $2 = '' + OR b.file_name ILIKE '%' || '-' || $2 || '-%' + OR b.file_name ILIKE '%' || '-' || $2 || '_%' + ) +ORDER BY + COALESCE(b.sort_order,999999), + b.zlins_dttm DESC, + b.id DESC ` rows, err := pg.Query(query, code, color) + if err != nil { + slog.Error("product_images.list.query_failed", "req_id", reqID, "code", code, "color", color, "err", err.Error(), ) + http.Error(w, "Gorsel sorgu hatasi: "+err.Error(), http.StatusInternalServerError) return } + defer rows.Close() items := make([]ProductImageItem, 0, 16) + for rows.Next() { + var it ProductImageItem - if err := rows.Scan(&it.ID, &it.FileName, &it.FileSize, &it.Storage); err != nil { + + if err := rows.Scan( + &it.ID, + &it.FileName, + &it.FileSize, + &it.Storage, + ); err != nil { continue } + it.ContentURL = fmt.Sprintf("/api/product-images/%d/content", it.ID) + items = append(items, it) } @@ -97,24 +127,35 @@ ORDER BY COALESCE(b.sort_order, 999999), b.zlins_dttm DESC, b.id DESC } } +// +// GET IMAGE CONTENT +// + // GET /api/product-images/{id}/content func GetProductImageContentHandler(pg *sql.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + reqID := strings.TrimSpace(r.Header.Get("X-Request-ID")) if reqID == "" { reqID = uuid.NewString() } + w.Header().Set("X-Request-ID", reqID) idStr := mux.Vars(r)["id"] + id, err := strconv.ParseInt(idStr, 10, 64) + if err != nil || id <= 0 { + slog.Warn("product_images.content.bad_request", "req_id", reqID, "id_raw", idStr, "path", r.URL.Path, "reason", "invalid_id", ) + http.Error(w, "Gecersiz gorsel id", http.StatusBadRequest) return } @@ -128,83 +169,89 @@ func GetProductImageContentHandler(pg *sql.DB) http.HandlerFunc { err = pg.QueryRow(` SELECT - COALESCE(file_name, ''), - COALESCE(storage_path, ''), - COALESCE(stored_in_db, false), + COALESCE(file_name,''), + COALESCE(storage_path,''), + COALESCE(stored_in_db,false), bin FROM dfblob WHERE id = $1 AND typ = 'img' `, id).Scan(&fileName, &storagePath, &storedInDB, &binData) + if err != nil { + if err == sql.ErrNoRows { + slog.Warn("product_images.content.not_found_row", "req_id", reqID, "id", id, ) + http.NotFound(w, r) return } + slog.Error("product_images.content.query_failed", "req_id", reqID, "id", id, "err", err.Error(), ) + http.Error(w, "Gorsel okunamadi: "+err.Error(), http.StatusInternalServerError) return } + // DB içinde binary saklıysa if storedInDB && len(binData) > 0 { - slog.Info("product_images.content.served_from_db", - "req_id", reqID, - "id", id, - "file_name", fileName, - "bytes", len(binData), - ) + w.Header().Set("Content-Type", http.DetectContentType(binData)) w.Header().Set("Cache-Control", "public, max-age=3600") + _, _ = w.Write(binData) + return } resolved, tried := resolveStoragePath(storagePath) + if resolved == "" { + slog.Warn("product_images.content.file_not_found", "req_id", reqID, "id", id, - "stored_in_db", storedInDB, "file_name", fileName, "storage_path", storagePath, "tried", tried, ) + http.NotFound(w, r) return } - slog.Info("product_images.content.served_from_file", - "req_id", reqID, - "id", id, - "file_name", fileName, - "storage_path", storagePath, - "resolved_path", resolved, - ) - w.Header().Set("Cache-Control", "public, max-age=3600") + http.ServeFile(w, r, resolved) } } +// +// FILE PATH RESOLVER +// + func resolveStoragePath(storagePath string) (string, []string) { + raw := strings.TrimSpace(storagePath) + if raw == "" { return "", nil } - // URL/query temizligi ve platforma uygun normalize if i := strings.Index(raw, "?"); i >= 0 { raw = raw[:i] } + raw = strings.ReplaceAll(raw, "\\", "/") + if scheme := strings.Index(raw, "://"); scheme >= 0 { rest := raw[scheme+3:] if i := strings.Index(rest, "/"); i >= 0 { @@ -215,47 +262,36 @@ func resolveStoragePath(storagePath string) (string, []string) { raw = strings.TrimPrefix(raw, "./") raw = strings.TrimPrefix(raw, "/") raw = strings.TrimPrefix(raw, "uploads/") + raw = filepath.ToSlash(filepath.Clean(raw)) + relUploads := filepath.FromSlash(filepath.Join("uploads", raw)) - rawT300 := raw - relUploadsT300 := relUploads - if strings.Contains(filepath.ToSlash(relUploads), "uploads/image/") && - !strings.Contains(filepath.ToSlash(relUploads), "uploads/image/t300/") { - rawT300 = strings.Replace(filepath.ToSlash(raw), "image/", "image/t300/", 1) - relUploadsT300 = filepath.FromSlash( - strings.Replace(filepath.ToSlash(relUploads), "uploads/image/", "uploads/image/t300/", 1), - ) - } candidates := []string{ filepath.Clean(storagePath), filepath.FromSlash(filepath.Clean(strings.TrimPrefix(storagePath, "/"))), filepath.FromSlash(filepath.Clean(raw)), relUploads, - relUploadsT300, filepath.Join(".", relUploads), - filepath.Join(".", relUploadsT300), filepath.Join("..", relUploads), - filepath.Join("..", relUploadsT300), filepath.Join("..", "..", relUploads), - filepath.Join("..", "..", relUploadsT300), } if root := strings.TrimSpace(os.Getenv("BLOB_ROOT")); root != "" { + candidates = append(candidates, filepath.Join(root, raw), - filepath.Join(root, filepath.FromSlash(rawT300)), filepath.Join(root, relUploads), - filepath.Join(root, relUploadsT300), filepath.Join(root, "uploads", raw), - filepath.Join(root, "uploads", filepath.FromSlash(rawT300)), ) } for _, p := range candidates { + if p == "" { continue } + if st, err := os.Stat(p); err == nil && !st.IsDir() { return p, candidates } diff --git a/ui/quasar.config.js.temporary.compiled.1772788258926.mjs b/ui/quasar.config.js.temporary.compiled.1772788258926.mjs deleted file mode 100644 index 4a501bd..0000000 --- a/ui/quasar.config.js.temporary.compiled.1772788258926.mjs +++ /dev/null @@ -1,125 +0,0 @@ -/* eslint-disable */ -/** - * THIS FILE IS GENERATED AUTOMATICALLY. - * 1. DO NOT edit this file directly as it won't do anything. - * 2. EDIT the original quasar.config file INSTEAD. - * 3. DO NOT git commit this file. It should be ignored. - * - * This file is still here because there was an error in - * the original quasar.config file and this allows you to - * investigate the Node.js stack error. - * - * After you fix the original file, this file will be - * deleted automatically. - **/ - - -// quasar.config.js -import { defineConfig } from "@quasar/app-webpack/wrappers"; -var quasar_config_default = defineConfig(() => { - const apiBaseUrl = (process.env.VITE_API_BASE_URL || "/api").trim(); - return { - /* ===================================================== - APP INFO - ===================================================== */ - productName: "Baggi BSS", - productDescription: "Baggi Tekstil Business Support System", - /* ===================================================== - BOOT FILES - ===================================================== */ - boot: ["dayjs"], - /* ===================================================== - GLOBAL CSS - ===================================================== */ - css: ["app.css"], - /* ===================================================== - ICONS / FONTS - ===================================================== */ - extras: [ - "roboto-font", - "material-icons" - ], - /* ===================================================== - BUILD (PRODUCTION) - ===================================================== */ - build: { - vueRouterMode: "hash", - env: { - VITE_API_BASE_URL: apiBaseUrl - }, - esbuildTarget: { - browser: ["es2022", "firefox115", "chrome115", "safari14"], - node: "node20" - }, - // Cache & performance - gzip: true, - preloadChunks: true - }, - /* ===================================================== - DEV SERVER (LOCAL) - ===================================================== */ - devServer: { - server: { type: "http" }, - port: 9e3, - open: true, - // DEV proxy (CORS'suz) - proxy: [ - { - context: ["/api"], - target: "http://localhost:8080", - changeOrigin: true, - secure: false - } - ] - }, - /* ===================================================== - QUASAR FRAMEWORK - ===================================================== */ - framework: { - config: { - notify: { - position: "top", - timeout: 2500 - } - }, - lang: "tr", - plugins: [ - "Loading", - "Dialog", - "Notify" - ] - }, - animations: [], - /* ===================================================== - SSR / PWA (DISABLED) - ===================================================== */ - ssr: { - prodPort: 3e3, - middlewares: ["render"], - pwa: false - }, - pwa: { - workboxMode: "GenerateSW" - }, - /* ===================================================== - MOBILE / DESKTOP - ===================================================== */ - capacitor: { - hideSplashscreen: true - }, - electron: { - preloadScripts: ["electron-preload"], - inspectPort: 5858, - bundler: "packager", - builder: { - appId: "baggisowtfaresystem" - } - }, - bex: { - extraScripts: [] - } - }; -}); -export { - quasar_config_default as default -}; diff --git a/ui/src/pages/AgingCustomerBalancelist.go.vue b/ui/src/pages/AgingCustomerBalancelist.go.vue index 5fef244..40352ab 100644 --- a/ui/src/pages/AgingCustomerBalancelist.go.vue +++ b/ui/src/pages/AgingCustomerBalancelist.go.vue @@ -635,12 +635,8 @@ const canReadFinance = canRead('finance') const canExportFinance = canExport('finance') const islemTipiOptions = [ - { label: '1_2 Bakiye Pr.Br', value: 'prbr_1_2' }, - { label: '1_3 Bakiye Pr.Br', value: 'prbr_1_3' }, - { label: '1_2 USD Bakiye', value: 'usd_1_2' }, - { label: '1_2 TRY Bakiye', value: 'try_1_2' }, - { label: '1_3 USD Bakiye', value: 'usd_1_3' }, - { label: '1_3 TRY Bakiye', value: 'try_1_3' } + { label: '1_2', value: '1_2' }, + { label: '1_3', value: '1_3' } ] const staticMoneyFields = ['usd_bakiye_1_2', 'tl_bakiye_1_2', 'usd_bakiye_1_3', 'tl_bakiye_1_3'] @@ -688,11 +684,7 @@ const metricDefs = { try_1_3: { name: 'tl_bakiye_1_3', label: '1_3 TRY_BAKIYE', field: 'tl_bakiye_1_3', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) } } -const selectedMetricKeys = computed(() => { - const selected = store.filters.islemTipi || [] - if (!selected.length) return [...Object.keys(metricDefs)] - return selected.filter((k) => k in metricDefs) -}) +const selectedMetricKeys = computed(() => [...Object.keys(metricDefs)]) const summaryColumns = computed(() => ([ { name: 'expand', label: '', field: 'expand', align: 'center', sortable: false }, diff --git a/ui/src/pages/CustomerBalanceList.vue b/ui/src/pages/CustomerBalanceList.vue index e4a0588..2282315 100644 --- a/ui/src/pages/CustomerBalanceList.vue +++ b/ui/src/pages/CustomerBalanceList.vue @@ -538,12 +538,8 @@ const canReadFinance = canRead('finance') const canExportFinance = canExport('finance') const islemTipiOptions = [ - { label: '1_2 Bakiye Pr.Br', value: 'prbr_1_2' }, - { label: '1_3 Bakiye Pr.Br', value: 'prbr_1_3' }, - { label: '1_2 USD Bakiye', value: 'usd_1_2' }, - { label: '1_2 TRY Bakiye', value: 'try_1_2' }, - { label: '1_3 USD Bakiye', value: 'usd_1_3' }, - { label: '1_3 TRY Bakiye', value: 'try_1_3' } + { label: '1_2', value: '1_2' }, + { label: '1_3', value: '1_3' } ] const staticMoneyFields = ['usd_bakiye_1_2', 'tl_bakiye_1_2', 'usd_bakiye_1_3', 'tl_bakiye_1_3'] @@ -590,11 +586,7 @@ const metricDefs = { try_1_3: { name: 'tl_bakiye_1_3', label: '1_3 TRY_BAKIYE', field: 'tl_bakiye_1_3', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) } } -const selectedMetricKeys = computed(() => { - const selected = store.filters.islemTipi || [] - if (!selected.length) return Object.keys(metricDefs) - return selected.filter((k) => k in metricDefs) -}) +const selectedMetricKeys = computed(() => Object.keys(metricDefs)) const summaryColumns = computed(() => ([ { name: 'expand', label: '', field: 'expand', align: 'center', sortable: false }, diff --git a/ui/src/stores/accountAgingBalanceStore.js b/ui/src/stores/accountAgingBalanceStore.js index 5e44cad..eb563d1 100644 --- a/ui/src/stores/accountAgingBalanceStore.js +++ b/ui/src/stores/accountAgingBalanceStore.js @@ -78,12 +78,8 @@ export const useAccountAgingBalanceStore = defineStore('accountAgingBalance', { const islemTipiOk = !state.filters.islemTipi.length || state.filters.islemTipi.some((t) => { - if (t === 'prbr_1_2') return bak12 !== 0 - if (t === 'prbr_1_3') return bak13 !== 0 - if (t === 'usd_1_2') return usd12 !== 0 - if (t === 'try_1_2') return try12 !== 0 - if (t === 'usd_1_3') return usd13 !== 0 - if (t === 'try_1_3') return try13 !== 0 + if (t === '1_2') return bak12 !== 0 || usd12 !== 0 || try12 !== 0 + if (t === '1_3') return bak13 !== 0 || usd13 !== 0 || try13 !== 0 return false }) diff --git a/ui/src/stores/customerBalanceListStore.js b/ui/src/stores/customerBalanceListStore.js index 4015773..7eebae3 100644 --- a/ui/src/stores/customerBalanceListStore.js +++ b/ui/src/stores/customerBalanceListStore.js @@ -92,12 +92,8 @@ export const useCustomerBalanceListStore = defineStore('customerBalanceList', { const islemTipiOk = !state.filters.islemTipi.length || state.filters.islemTipi.some((t) => { - if (t === 'prbr_1_2') return bak12 !== 0 - if (t === 'prbr_1_3') return bak13 !== 0 - if (t === 'usd_1_2') return usd12 !== 0 - if (t === 'try_1_2') return try12 !== 0 - if (t === 'usd_1_3') return usd13 !== 0 - if (t === 'try_1_3') return try13 !== 0 + if (t === '1_2') return bak12 !== 0 || usd12 !== 0 || try12 !== 0 + if (t === '1_3') return bak13 !== 0 || usd13 !== 0 || try13 !== 0 return false }) @@ -194,7 +190,15 @@ export const useCustomerBalanceListStore = defineStore('customerBalanceList', { const { data } = await api.get('/finance/customer-balances', { params: { selected_date: this.filters.selectedDate, - cari_search: String(this.filters.cariSearch || '').trim() + cari_search: String(this.filters.cariSearch || '').trim(), + cari_ilk_grup: (this.filters.cariIlkGrup || []).join(','), + piyasa: (this.filters.piyasa || []).join(','), + temsilci: (this.filters.temsilci || []).join(','), + risk_durumu: (this.filters.riskDurumu || []).join(','), + islem_tipi: (this.filters.islemTipi || []).join(','), + ulke: (this.filters.ulke || []).join(','), + il: (this.filters.il || []).join(','), + ilce: (this.filters.ilce || []).join(',') } }) this.rows = Array.isArray(data) ? data : []