From d355ef7acdce1ec6fbb3f8c5f79549742ac63737 Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Tue, 3 Mar 2026 13:29:17 +0300 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- svc/internal/authz/mssql.go | 47 +++++-- svc/internal/authz/piyasa_repo.go | 30 ++++- svc/middlewares/authz_v2.go | 26 +++- svc/queries/account.go | 51 ++++++- svc/queries/customer_balance_list.go | 12 +- svc/queries/statement_aging_balance_list.go | 12 +- svc/queries/statement_header.go | 22 ++- svc/queries/statement_header_pdf.go | 5 +- svc/queries/statement_pdf_common.go | 8 +- svc/queries/statements_detail.go | 100 +++++++------- svc/queries/statements_pdf.go | 5 +- svc/queries/user_detail.go | 13 +- svc/routes/statement_detail.go | 2 +- svc/routes/statement_header.go | 2 +- svc/routes/statement_header_pdf.go | 2 +- svc/routes/statements_pdf.go | 2 +- svc/routes/user_detail.go | 8 ++ ...ig.js.temporary.compiled.1772524399006.mjs | 125 ------------------ ui/src/css/app.css | 13 +- ui/src/layouts/MainLayout.vue | 13 +- ui/src/pages/OrderProductionUpdate.vue | 8 +- 21 files changed, 279 insertions(+), 227 deletions(-) delete mode 100644 ui/quasar.config.js.temporary.compiled.1772524399006.mjs diff --git a/svc/internal/authz/mssql.go b/svc/internal/authz/mssql.go index a8c0fb2..498bbb0 100644 --- a/svc/internal/authz/mssql.go +++ b/svc/internal/authz/mssql.go @@ -1,6 +1,7 @@ package authz import ( + "bssapp-backend/auth" "context" "fmt" "strings" @@ -10,23 +11,53 @@ func BuildMSSQLPiyasaFilter( ctx context.Context, column string, ) string { + claims, ok := auth.GetClaimsFromContext(ctx) + if ok && claims != nil && claims.IsAdmin() { + return "1=1" + } codes := GetPiyasaCodesFromCtx(ctx) if len(codes) == 0 { - return "1=1" + return "1=0" } + return BuildMSSQLPiyasaFilterWithCodes(column, codes) +} - var quoted []string +func BuildMSSQLPiyasaFilterWithCodes(column string, codes []string) string { + normalizedCol := fmt.Sprintf("UPPER(LTRIM(RTRIM(%s)))", column) + + exact := BuildINClause(normalizedCol, codes) + prefixCodes := first3Codes(codes) + if len(prefixCodes) == 0 { + return exact + } + + prefix := BuildINClause( + fmt.Sprintf("LEFT(%s, 3)", normalizedCol), + prefixCodes, + ) + + return fmt.Sprintf("(%s OR %s)", exact, prefix) +} + +func first3Codes(codes []string) []string { + seen := make(map[string]struct{}, len(codes)) + out := make([]string, 0, len(codes)) for _, c := range codes { - quoted = append(quoted, "'"+c+"'") + n := strings.ToUpper(strings.TrimSpace(c)) + if len(n) < 3 { + continue + } + n = n[:3] + if _, ok := seen[n]; ok { + continue + } + seen[n] = struct{}{} + out = append(out, n) } - return fmt.Sprintf( - "%s IN (%s)", - column, - strings.Join(quoted, ","), - ) + return out } diff --git a/svc/internal/authz/piyasa_repo.go b/svc/internal/authz/piyasa_repo.go index 7784328..d304818 100644 --- a/svc/internal/authz/piyasa_repo.go +++ b/svc/internal/authz/piyasa_repo.go @@ -3,6 +3,7 @@ package authz import ( "database/sql" "fmt" + "strings" "sync" ) @@ -35,10 +36,21 @@ func GetUserPiyasaCodes(pg *sql.DB, userID int) ([]string, error) { // DB QUERY // ----------------------------- rows, err := pg.Query(` - SELECT piyasa_code - FROM dfusr_piyasa - WHERE dfusr_id = $1 - AND is_allowed = true + WITH user_piyasa AS ( + SELECT TRIM(up.piyasa_code) AS raw_code + FROM dfusr_piyasa up + WHERE up.dfusr_id = $1 + AND up.is_allowed = true + ) + SELECT DISTINCT + COALESCE(p_code.code, p_title.code, u.raw_code) AS piyasa_code + FROM user_piyasa u + LEFT JOIN mk_sales_piy p_code + ON UPPER(translate(TRIM(p_code.code), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) + = UPPER(translate(TRIM(u.raw_code), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) + LEFT JOIN mk_sales_piy p_title + ON UPPER(translate(TRIM(p_title.title),'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) + = UPPER(translate(TRIM(u.raw_code), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) `, userID) if err != nil { return nil, fmt.Errorf("pg piyasa query error: %w", err) @@ -46,10 +58,18 @@ func GetUserPiyasaCodes(pg *sql.DB, userID int) ([]string, error) { defer rows.Close() var out []string + seen := make(map[string]struct{}) for rows.Next() { var code string if err := rows.Scan(&code); err == nil { - out = append(out, code) + code = strings.ToUpper(strings.TrimSpace(code)) + if code != "" { + if _, ok := seen[code]; ok { + continue + } + seen[code] = struct{}{} + out = append(out, code) + } } } diff --git a/svc/middlewares/authz_v2.go b/svc/middlewares/authz_v2.go index 2dff368..fc89277 100644 --- a/svc/middlewares/authz_v2.go +++ b/svc/middlewares/authz_v2.go @@ -537,9 +537,15 @@ func cachedPiyasaIntersectionAny(pg *sql.DB, c *ttlCache, userID, roleID int64, err := pg.QueryRow(` SELECT 1 FROM dfusr_piyasa up + LEFT JOIN mk_sales_piy p_code + ON UPPER(translate(TRIM(p_code.code), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) + = UPPER(translate(TRIM(up.piyasa_code), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) + LEFT JOIN mk_sales_piy p_title + ON UPPER(translate(TRIM(p_title.title), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) + = UPPER(translate(TRIM(up.piyasa_code), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) WHERE up.dfusr_id = $1 AND up.is_allowed = true - AND up.piyasa_code = ANY($2) + AND UPPER(TRIM(COALESCE(p_code.code, p_title.code, up.piyasa_code))) = ANY($2) LIMIT 1 `, userID, pqArray(piyasaCodes)).Scan(&dummy) @@ -988,7 +994,23 @@ func AuthzGuardByRoute(pg *sql.DB) func(http.Handler) http.Handler { } // ===================================================== - // 5️⃣ PASS + // 5️⃣ SCOPE CONTEXT ENRICH (for MSSQL piyasa filters) + // ===================================================== + if !claims.IsAdmin() { + userPiy, err := authz.GetUserPiyasaCodes(pg, int(claims.ID)) + if err != nil { + log.Printf("❌ AUTHZ: user piyasa resolve error user=%d err=%v", claims.ID, err) + http.Error(w, "forbidden", http.StatusForbidden) + return + } + + if len(userPiy) > 0 { + r = r.WithContext(authz.WithPiyasaCodes(r.Context(), normalizeCodes(userPiy))) + } + } + + // ===================================================== + // 6️⃣ PASS // ===================================================== next.ServeHTTP(w, r) }) diff --git a/svc/queries/account.go b/svc/queries/account.go index a0ac4d8..e28aa9a 100644 --- a/svc/queries/account.go +++ b/svc/queries/account.go @@ -13,13 +13,48 @@ import ( func GetAccounts(ctx context.Context) ([]models.Account, error) { - piyasaFilter := authz.BuildMSSQLPiyasaFilter(ctx, "f2.CustomerAtt01") + piyasaFilter := authz.BuildMSSQLPiyasaFilter( + ctx, + "CASE WHEN b.CurrAccTypeCode = 1 THEN vp.VendorAtt01 ELSE f2.CustomerAtt01 END", + ) if strings.TrimSpace(piyasaFilter) == "" { piyasaFilter = "1=1" } query := fmt.Sprintf(` + ;WITH VendorPiyasa AS + ( + SELECT + Cari8 = LEFT(P.CurrAccCode, 8), + VendorAtt01 = MAX(P.VendorAtt01) + FROM + ( + SELECT + CurrAccTypeCode, + CurrAccCode, + VendorAtt01 = MAX(ISNULL([1], '')) + FROM + ( + SELECT + c.CurrAccTypeCode, + c.CurrAccCode, + a.AttributeTypeCode, + a.AttributeCode + FROM cdCurrAcc c WITH (NOLOCK) + LEFT JOIN prCurrAccAttribute a WITH (NOLOCK) + ON a.CurrAccTypeCode = c.CurrAccTypeCode + AND a.CurrAccCode = c.CurrAccCode + WHERE c.CurrAccTypeCode = 1 + ) d + PIVOT + ( + MAX(AttributeCode) FOR AttributeTypeCode IN ([1]) + ) pvt + GROUP BY CurrAccTypeCode, CurrAccCode + ) P + GROUP BY LEFT(P.CurrAccCode, 8) + ) SELECT x.AccountCode, MAX(x.AccountName) AS AccountName @@ -29,10 +64,16 @@ func GetAccounts(ctx context.Context) ([]models.Account, error) { COALESCE(d.CurrAccDescription, '') AS AccountName FROM trCurrAccBook b LEFT JOIN cdCurrAccDesc d - ON d.CurrAccCode = b.CurrAccCode - JOIN CustomerAttributesFilter f2 - ON f2.CurrAccCode = b.CurrAccCode - WHERE %s + ON d.CurrAccTypeCode = b.CurrAccTypeCode + AND d.CurrAccCode = b.CurrAccCode + AND d.LangCode = 'TR' + LEFT JOIN CustomerAttributesFilter f2 + ON f2.CurrAccTypeCode = b.CurrAccTypeCode + AND f2.CurrAccCode = b.CurrAccCode + LEFT JOIN VendorPiyasa vp + ON vp.Cari8 = LEFT(b.CurrAccCode, 8) + WHERE b.CurrAccTypeCode IN (1,3) + AND %s ) x GROUP BY x.AccountCode ORDER BY x.AccountCode diff --git a/svc/queries/customer_balance_list.go b/svc/queries/customer_balance_list.go index 339b44d..aca4990 100644 --- a/svc/queries/customer_balance_list.go +++ b/svc/queries/customer_balance_list.go @@ -314,7 +314,12 @@ ORDER BY F.MasterCari; } func loadBalanceLines(ctx context.Context, selectedDate, cariSearch string) ([]mkCariBakiyeLine, error) { - query := ` + piyasaScope, err := buildPiyasaExistsForCariCode(ctx, "CariKodu") + if err != nil { + return nil, err + } + + query := fmt.Sprintf(` SELECT CurrAccTypeCode, CariKodu, @@ -326,8 +331,9 @@ func loadBalanceLines(ctx context.Context, selectedDate, cariSearch string) ([]m CAST(0 AS DECIMAL(18,4)) AS Vade_Gun, CAST(0 AS DECIMAL(18,4)) AS Vade_BelgeTarihi_Gun FROM dbo.MK_CARI_BAKIYE_LIST(@SonTarih) - WHERE (@CariSearch = '' OR CariKodu LIKE '%' + @CariSearch + '%') - ` + WHERE (@CariSearch = '' OR CariKodu LIKE '%%' + @CariSearch + '%%') + AND %s + `, piyasaScope) rows, err := db.MssqlDB.QueryContext(ctx, query, sql.Named("SonTarih", selectedDate), diff --git a/svc/queries/statement_aging_balance_list.go b/svc/queries/statement_aging_balance_list.go index ddb9f5f..66057fb 100644 --- a/svc/queries/statement_aging_balance_list.go +++ b/svc/queries/statement_aging_balance_list.go @@ -182,7 +182,12 @@ func GetStatementAgingBalanceList(ctx context.Context, params models.CustomerBal } func loadAgingBalanceLines(ctx context.Context, cariSearch string) ([]mkCariBakiyeLine, error) { - query := ` + piyasaScope, err := buildPiyasaExistsForCariCode(ctx, "LTRIM(RTRIM(CariKodu))") + if err != nil { + return nil, err + } + + query := fmt.Sprintf(` SELECT CurrAccTypeCode, CariKodu = LTRIM(RTRIM(CariKodu)), @@ -194,9 +199,10 @@ func loadAgingBalanceLines(ctx context.Context, cariSearch string) ([]mkCariBaki Vade_Gun, Vade_BelgeTarihi_Gun FROM dbo.CARI_BAKIYE_GUN_CACHE - WHERE (@CariSearch = '' OR LTRIM(RTRIM(CariKodu)) LIKE '%' + @CariSearch + '%') + 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 { diff --git a/svc/queries/statement_header.go b/svc/queries/statement_header.go index d9ad308..861fcab 100644 --- a/svc/queries/statement_header.go +++ b/svc/queries/statement_header.go @@ -3,13 +3,14 @@ package queries import ( "bssapp-backend/db" "bssapp-backend/models" + "context" "database/sql" "fmt" "strings" ) // Ana tabloyu getiren fonksiyon (Vue header tablosu için) -func GetStatements(params models.StatementParams) ([]models.StatementHeader, error) { +func GetStatements(ctx context.Context, params models.StatementParams) ([]models.StatementHeader, error) { // AccountCode normalize: "ZLA0127" → "ZLA 0127" params.AccountCode = normalizeMasterAccountCode(params.AccountCode) @@ -33,6 +34,11 @@ func GetStatements(params models.StatementParams) ([]models.StatementHeader, err } } + piyasaScope, err := buildPiyasaExistsForCariCode(ctx, "b.CurrAccCode") + if err != nil { + return nil, err + } + query := fmt.Sprintf(` ;WITH CurrDesc AS ( SELECT @@ -58,6 +64,7 @@ HasMovement AS ( AND f.ATAtt01 IN (%s) WHERE LEFT(REPLACE(b.CurrAccCode, ' ', ''), 7) = REPLACE(@Carikod, ' ', '') AND b.DocumentDate BETWEEN @startdate AND @enddate + AND %s ) THEN 1 ELSE 0 END AS HasMov ), @@ -79,6 +86,7 @@ Opening AS ( ON c.CurrAccBookID = b.CurrAccBookID AND c.CurrencyCode = b.DocCurrencyCode WHERE LEFT(REPLACE(b.CurrAccCode, ' ', ''), 7) = REPLACE(@Carikod, ' ', '') + AND %s AND ( (hm.HasMov = 1 AND b.DocumentDate < @startdate) -- hareket varsa: klasik devir OR (hm.HasMov = 0 AND b.DocumentDate <= @enddate) -- hareket yoksa: enddate itibariyle bakiye @@ -135,6 +143,7 @@ Movements AS ( AND c.CurrencyCode = b.DocCurrencyCode WHERE LEFT(REPLACE(b.CurrAccCode, ' ', ''), 7) = REPLACE(@Carikod, ' ', '') + AND %s AND b.DocumentDate BETWEEN @startdate AND @enddate ) @@ -201,12 +210,15 @@ ORDER BY Para_Birimi, Belge_Tarihi; `, - parislemFilter, // HasMovement - parislemFilter, // Opening - parislemFilter, // Movements + parislemFilter, // HasMovement ATAtt01 + piyasaScope, // HasMovement piyasa scope + parislemFilter, // Opening ATAtt01 + piyasaScope, // Opening piyasa scope + parislemFilter, // Movements ATAtt01 + piyasaScope, // Movements piyasa scope ) - rows, err := db.MssqlDB.Query(query, + rows, err := db.MssqlDB.QueryContext(ctx, query, sql.Named("startdate", params.StartDate), sql.Named("enddate", params.EndDate), sql.Named("Carikod", params.AccountCode), diff --git a/svc/queries/statement_header_pdf.go b/svc/queries/statement_header_pdf.go index 119ff30..9c14cc1 100644 --- a/svc/queries/statement_header_pdf.go +++ b/svc/queries/statement_header_pdf.go @@ -2,11 +2,12 @@ package queries import ( "bssapp-backend/models" + "context" "log" ) -func GetStatementsHPDF(accountCode, startDate, endDate string, parislemler []string) ([]models.StatementHeader, []string, error) { - headers, err := getStatementsForPDF(accountCode, startDate, endDate, parislemler) +func GetStatementsHPDF(ctx context.Context, accountCode, startDate, endDate string, parislemler []string) ([]models.StatementHeader, []string, error) { + headers, err := getStatementsForPDF(ctx, accountCode, startDate, endDate, parislemler) if err != nil { log.Printf("Header query error: %v", err) return nil, nil, err diff --git a/svc/queries/statement_pdf_common.go b/svc/queries/statement_pdf_common.go index 2d24b45..08d6cc8 100644 --- a/svc/queries/statement_pdf_common.go +++ b/svc/queries/statement_pdf_common.go @@ -1,14 +1,18 @@ package queries -import "bssapp-backend/models" +import ( + "bssapp-backend/models" + "context" +) func getStatementsForPDF( + ctx context.Context, accountCode string, startDate string, endDate string, parislemler []string, ) ([]models.StatementHeader, error) { - return GetStatements(models.StatementParams{ + return GetStatements(ctx, models.StatementParams{ AccountCode: accountCode, StartDate: startDate, EndDate: endDate, diff --git a/svc/queries/statements_detail.go b/svc/queries/statements_detail.go index 880a480..34aa9da 100644 --- a/svc/queries/statements_detail.go +++ b/svc/queries/statements_detail.go @@ -3,15 +3,14 @@ package queries import ( "bssapp-backend/db" "bssapp-backend/models" + "context" "database/sql" "fmt" "strings" ) -/* ============================ DETAIL (ALT TABLO) ============================ */ - -func GetStatementDetails(accountCode, startDate, endDate string, parislemler []string) ([]models.StatementDetail, error) { - // Parislemler filtresi hazırlanır (ör: 1,2,3) +// DETAIL (ALT TABLO) +func GetStatementDetails(ctx context.Context, accountCode, startDate, endDate string, parislemler []string) ([]models.StatementDetail, error) { inParislem := "" if len(parislemler) > 0 { pp := make([]string, len(parislemler)) @@ -20,87 +19,84 @@ func GetStatementDetails(accountCode, startDate, endDate string, parislemler []s } inParislem = strings.Join(pp, ",") } + + piyasaScope, err := buildPiyasaExistsForCariCode(ctx, "a.CurrAccCode") + if err != nil { + return nil, err + } + query := fmt.Sprintf(` SELECT CONVERT(varchar(10), a.InvoiceDate, 23) AS Belge_Tarihi, a.InvoiceNumber AS Belge_Ref_Numarasi, - COALESCE(MAX(AnaGrupDesc.AttributeDescription), '') AS Urun_Ana_Grubu, - COALESCE(MAX(AltGrupDesc.AttributeDescription), '') AS Urun_Alt_Grubu, - COALESCE(MAX(GarsonDesc.AttributeDescription), '') AS Yetiskin_Garson, - COALESCE(MAX(FitDesc.AttributeDescription), '') AS Fit, - COALESCE(MAX(KisaKarDesc.AttributeDescription), '') AS Icerik, - a.ItemCode AS Urun_Kodu, - a.ColorCode AS Urun_Rengi, + COALESCE(MAX(AnaGrupDesc.AttributeDescription), '') AS Urun_Ana_Grubu, + COALESCE(MAX(AltGrupDesc.AttributeDescription), '') AS Urun_Alt_Grubu, + COALESCE(MAX(GarsonDesc.AttributeDescription), '') AS Yetiskin_Garson, + COALESCE(MAX(FitDesc.AttributeDescription), '') AS Fit, + COALESCE(MAX(KisaKarDesc.AttributeDescription), '') AS Icerik, + a.ItemCode AS Urun_Kodu, + a.ColorCode AS Urun_Rengi, SUM(a.Qty1) AS Toplam_Adet, - -CAST( - SUM(a.Qty1 * ABS(a.Doc_Price)) - / NULLIF(SUM(a.Qty1),0) -AS numeric(18,4)) AS Doviz_Fiyat, - -CAST( - SUM(a.Qty1 * ABS(a.Doc_Price)) -AS numeric(18,2)) AS Toplam_Tutar - + CAST(SUM(a.Qty1 * ABS(a.Doc_Price)) / NULLIF(SUM(a.Qty1),0) AS numeric(18,4)) AS Doviz_Fiyat, + CAST(SUM(a.Qty1 * ABS(a.Doc_Price)) AS numeric(18,2)) AS Toplam_Tutar FROM AllInvoicesWithAttributes a LEFT JOIN prItemAttribute AnaGrup - ON a.ItemCode = AnaGrup.ItemCode AND AnaGrup.AttributeTypeCode = 1 + ON a.ItemCode = AnaGrup.ItemCode AND AnaGrup.AttributeTypeCode = 1 LEFT JOIN cdItemAttributeDesc AnaGrupDesc - ON AnaGrup.AttributeTypeCode = AnaGrupDesc.AttributeTypeCode - AND AnaGrup.AttributeCode = AnaGrupDesc.AttributeCode - AND AnaGrup.ItemTypeCode = AnaGrupDesc.ItemTypeCode - + ON AnaGrup.AttributeTypeCode = AnaGrupDesc.AttributeTypeCode + AND AnaGrup.AttributeCode = AnaGrupDesc.AttributeCode + AND AnaGrup.ItemTypeCode = AnaGrupDesc.ItemTypeCode LEFT JOIN prItemAttribute AltGrup - ON a.ItemCode = AltGrup.ItemCode AND AltGrup.AttributeTypeCode = 2 + ON a.ItemCode = AltGrup.ItemCode AND AltGrup.AttributeTypeCode = 2 LEFT JOIN cdItemAttributeDesc AltGrupDesc - ON AltGrup.AttributeTypeCode = AltGrupDesc.AttributeTypeCode - AND AltGrup.AttributeCode = AltGrupDesc.AttributeCode - AND AltGrup.ItemTypeCode = AltGrupDesc.ItemTypeCode - + ON AltGrup.AttributeTypeCode = AltGrupDesc.AttributeTypeCode + AND AltGrup.AttributeCode = AltGrupDesc.AttributeCode + AND AltGrup.ItemTypeCode = AltGrupDesc.ItemTypeCode LEFT JOIN prItemAttribute Garson - ON a.ItemCode = Garson.ItemCode AND Garson.AttributeTypeCode = 44 + ON a.ItemCode = Garson.ItemCode AND Garson.AttributeTypeCode = 44 LEFT JOIN cdItemAttributeDesc GarsonDesc - ON Garson.AttributeTypeCode = GarsonDesc.AttributeTypeCode - AND Garson.AttributeCode = GarsonDesc.AttributeCode - AND Garson.ItemTypeCode = GarsonDesc.ItemTypeCode - + ON Garson.AttributeTypeCode = GarsonDesc.AttributeTypeCode + AND Garson.AttributeCode = GarsonDesc.AttributeCode + AND Garson.ItemTypeCode = GarsonDesc.ItemTypeCode LEFT JOIN prItemAttribute FitTbl - ON a.ItemCode = FitTbl.ItemCode AND FitTbl.AttributeTypeCode = 38 + ON a.ItemCode = FitTbl.ItemCode AND FitTbl.AttributeTypeCode = 38 LEFT JOIN cdItemAttributeDesc FitDesc - ON FitTbl.AttributeTypeCode = FitDesc.AttributeTypeCode - AND FitTbl.AttributeCode = FitDesc.AttributeCode - AND FitTbl.ItemTypeCode = FitDesc.ItemTypeCode - + ON FitTbl.AttributeTypeCode = FitDesc.AttributeTypeCode + AND FitTbl.AttributeCode = FitDesc.AttributeCode + AND FitTbl.ItemTypeCode = FitDesc.ItemTypeCode LEFT JOIN prItemAttribute KisaKar - ON a.ItemCode = KisaKar.ItemCode AND KisaKar.AttributeTypeCode = 41 + ON a.ItemCode = KisaKar.ItemCode AND KisaKar.AttributeTypeCode = 41 LEFT JOIN cdItemAttributeDesc KisaKarDesc - ON KisaKar.AttributeTypeCode = KisaKarDesc.AttributeTypeCode - AND KisaKar.AttributeCode = KisaKarDesc.AttributeCode - AND KisaKar.ItemTypeCode = KisaKarDesc.ItemTypeCode + ON KisaKar.AttributeTypeCode = KisaKarDesc.AttributeTypeCode + AND KisaKar.AttributeCode = KisaKarDesc.AttributeCode + AND KisaKar.ItemTypeCode = KisaKarDesc.ItemTypeCode WHERE a.CurrAccCode LIKE @Carikod AND a.InvoiceDate BETWEEN @StartDate AND @EndDate + AND %s %s GROUP BY a.InvoiceDate, a.InvoiceNumber, a.ItemCode, a.ColorCode ORDER BY Belge_Tarihi, Belge_Ref_Numarasi, Urun_Kodu;`, + piyasaScope, func() string { if inParislem == "" { return "" } return fmt.Sprintf(`AND EXISTS ( - SELECT 1 - FROM CurrAccBookATAttributesFilter f - WHERE f.CurrAccBookID = a.CurrAccBookID - AND f.ATAtt01 IN (%s) - )`, inParislem) + SELECT 1 + FROM CurrAccBookATAttributesFilter f + WHERE f.CurrAccBookID = a.CurrAccBookID + AND f.ATAtt01 IN (%s) + )`, inParislem) }(), ) - rows, err := db.MssqlDB.Query(query, + + rows, err := db.MssqlDB.QueryContext(ctx, query, sql.Named("Carikod", "%"+accountCode+"%"), sql.Named("StartDate", startDate), sql.Named("EndDate", endDate), ) if err != nil { - return nil, fmt.Errorf("detay sorgu hatası: %v", err) + return nil, fmt.Errorf("detay sorgu hatasi: %v", err) } defer rows.Close() diff --git a/svc/queries/statements_pdf.go b/svc/queries/statements_pdf.go index 86e46f0..a7209d4 100644 --- a/svc/queries/statements_pdf.go +++ b/svc/queries/statements_pdf.go @@ -4,14 +4,15 @@ package queries import ( "bssapp-backend/db" "bssapp-backend/models" + "context" "database/sql" "fmt" "log" "strings" ) -func GetStatementsPDF(accountCode, startDate, endDate string, parislemler []string) ([]models.StatementHeader, []string, error) { - headers, err := getStatementsForPDF(accountCode, startDate, endDate, parislemler) +func GetStatementsPDF(ctx context.Context, accountCode, startDate, endDate string, parislemler []string) ([]models.StatementHeader, []string, error) { + headers, err := getStatementsForPDF(ctx, accountCode, startDate, endDate, parislemler) if err != nil { log.Printf("Header query error: %v", err) return nil, nil, err diff --git a/svc/queries/user_detail.go b/svc/queries/user_detail.go index 81c170f..d04f76a 100644 --- a/svc/queries/user_detail.go +++ b/svc/queries/user_detail.go @@ -52,12 +52,19 @@ ORDER BY d.code // 🌍 PIYASALAR // ====================================================== const GetUserPiyasalar = ` -SELECT p.code, p.title +SELECT + COALESCE(p_code.code, p_title.code, up.piyasa_code) AS code, + COALESCE(p_code.title, p_title.title, up.piyasa_code) AS title FROM dfusr_piyasa up -JOIN mk_sales_piy p ON p.code = up.piyasa_code +LEFT JOIN mk_sales_piy p_code + ON UPPER(translate(TRIM(p_code.code), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) + = UPPER(translate(TRIM(up.piyasa_code), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) +LEFT JOIN mk_sales_piy p_title + ON UPPER(translate(TRIM(p_title.title), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) + = UPPER(translate(TRIM(up.piyasa_code), 'çğıöşüÇĞİÖŞÜ', 'CGIOSUCGIOSU')) WHERE up.dfusr_id = $1 AND up.is_allowed = true -ORDER BY p.code +ORDER BY 1 ` // ====================================================== diff --git a/svc/routes/statement_detail.go b/svc/routes/statement_detail.go index 0f5c20f..05b4053 100644 --- a/svc/routes/statement_detail.go +++ b/svc/routes/statement_detail.go @@ -25,7 +25,7 @@ func GetStatementDetailsHandler(w http.ResponseWriter, r *http.Request) { endDate := r.URL.Query().Get("enddate") parislemler := r.URL.Query()["parislemler"] - details, err := queries.GetStatementDetails(accountCode, startDate, endDate, parislemler) + details, err := queries.GetStatementDetails(r.Context(), accountCode, startDate, endDate, parislemler) if err != nil { http.Error(w, "Error fetching statement details: "+err.Error(), http.StatusInternalServerError) return diff --git a/svc/routes/statement_header.go b/svc/routes/statement_header.go index 71ce07d..9813016 100644 --- a/svc/routes/statement_header.go +++ b/svc/routes/statement_header.go @@ -25,7 +25,7 @@ func GetStatementHeadersHandler(w http.ResponseWriter, r *http.Request) { Parislemler: r.URL.Query()["parislemler"], } - statements, err := queries.GetStatements(params) + statements, err := queries.GetStatements(r.Context(), params) if err != nil { http.Error(w, "Error fetching statements: "+err.Error(), http.StatusInternalServerError) return diff --git a/svc/routes/statement_header_pdf.go b/svc/routes/statement_header_pdf.go index d2f8ff4..24639de 100644 --- a/svc/routes/statement_header_pdf.go +++ b/svc/routes/statement_header_pdf.go @@ -292,7 +292,7 @@ func ExportStatementHeaderReportPDFHandler(mssql *sql.DB) http.HandlerFunc { } } - headers, _, err := queries.GetStatementsHPDF(accountCode, startDate, endDate, parislemler) + headers, _, err := queries.GetStatementsHPDF(r.Context(), accountCode, startDate, endDate, parislemler) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/svc/routes/statements_pdf.go b/svc/routes/statements_pdf.go index 170024e..f917e9c 100644 --- a/svc/routes/statements_pdf.go +++ b/svc/routes/statements_pdf.go @@ -445,7 +445,7 @@ func ExportPDFHandler(mssql *sql.DB) http.HandlerFunc { accountCode, startDate, endDate, parislemler) // 1) Header verileri - headers, belgeNos, err := queries.GetStatementsPDF(accountCode, startDate, endDate, parislemler) + headers, belgeNos, err := queries.GetStatementsPDF(r.Context(), accountCode, startDate, endDate, parislemler) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/svc/routes/user_detail.go b/svc/routes/user_detail.go index 93dcf2b..8054c58 100644 --- a/svc/routes/user_detail.go +++ b/svc/routes/user_detail.go @@ -3,8 +3,10 @@ package routes import ( "bssapp-backend/auth" "bssapp-backend/internal/auditlog" + "bssapp-backend/internal/authz" "bssapp-backend/internal/mailer" "bssapp-backend/internal/security" + "bssapp-backend/middlewares" "bssapp-backend/models" "bssapp-backend/queries" "bytes" @@ -323,6 +325,9 @@ func handleUserUpdate(db *sql.DB, w http.ResponseWriter, r *http.Request, userID return } + authz.ClearPiyasaCache(int(userID)) + middlewares.ClearAuthzScopeCacheForUser(userID) + _ = json.NewEncoder(w).Encode(map[string]any{"success": true}) } @@ -424,6 +429,9 @@ func handleUserDelete(db *sql.DB, w http.ResponseWriter, r *http.Request, userID return } + authz.ClearPiyasaCache(int(userID)) + middlewares.ClearAuthzScopeCacheForUser(userID) + if claims != nil { auditlog.Enqueue(r.Context(), auditlog.ActivityLog{ ActionType: "user_delete", diff --git a/ui/quasar.config.js.temporary.compiled.1772524399006.mjs b/ui/quasar.config.js.temporary.compiled.1772524399006.mjs deleted file mode 100644 index 4a501bd..0000000 --- a/ui/quasar.config.js.temporary.compiled.1772524399006.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/css/app.css b/ui/src/css/app.css index a723fd4..3672f63 100644 --- a/ui/src/css/app.css +++ b/ui/src/css/app.css @@ -1262,9 +1262,16 @@ body { padding: 6px 8px !important; } -/* Güvenli z-index hiyerarşisi */ -.q-header { z-index: 1000 !important; } /* header en üstte */ -.q-drawer { z-index: 950 !important; } /* drawer header’ın altında */ +/* Mobile drawer touch-fix: drawer must stay above its backdrop */ +.q-drawer__backdrop { + z-index: 2999 !important; +} +.q-drawer { + z-index: 3000 !important; +} +.q-header { + z-index: 3001 !important; +} /* Mobile */ @media (max-width: 768px) { diff --git a/ui/src/layouts/MainLayout.vue b/ui/src/layouts/MainLayout.vue index 9c212f8..752232c 100644 --- a/ui/src/layouts/MainLayout.vue +++ b/ui/src/layouts/MainLayout.vue @@ -31,7 +31,7 @@ class="bg-secondary text-white" > - +
@@ -106,7 +106,7 @@ - +
@@ -343,3 +343,12 @@ const filteredMenu = computed(() => { }) + + diff --git a/ui/src/pages/OrderProductionUpdate.vue b/ui/src/pages/OrderProductionUpdate.vue index 6ff3dba..6c766ea 100644 --- a/ui/src/pages/OrderProductionUpdate.vue +++ b/ui/src/pages/OrderProductionUpdate.vue @@ -383,6 +383,10 @@ function normalizeShortCode (value, maxLen) { return String(value || '').trim().toUpperCase().slice(0, maxLen) } +function isValidBaggiModelCode (code) { + return /^[A-Z][0-9]{3}-[A-Z]{3}[0-9]{5}$/.test(code) +} + function validateRowInput (row) { const newItemCode = String(row.NewItemCode || '').trim().toUpperCase() const newColor = normalizeShortCode(row.NewColor, 3) @@ -391,7 +395,9 @@ function validateRowInput (row) { const oldDim2 = String(row.OldDim2 || '').trim() if (!newItemCode) return 'Yeni model kodu zorunludur.' - if (newItemCode.length !== 13) return 'Yeni model kodu 13 karakter olmalidir.' + if (!isValidBaggiModelCode(newItemCode)) { + return 'Girdiginiz yapi BAGGI kod yapisina uygun degildir. Format: X999-XXX99999' + } if (oldColor && !newColor) return 'Eski kayitta 1. renk oldugu icin yeni 1. renk zorunludur.' if (newColor && newColor.length !== 3) return 'Yeni 1. renk kodu 3 karakter olmalidir.' if (oldDim2 && !newDim2) return 'Eski kayitta 2. renk oldugu icin yeni 2. renk zorunludur.'