From 46f4d15ac7ae7330e81391515c782e1b14effb5c Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Fri, 6 Mar 2026 12:13:30 +0300 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- svc/queries/statements_detail.go | 104 +++++++----------- svc/routes/statement_detail.go | 18 +-- ...g.js.temporary.compiled.1772788258926.mjs} | 0 ui/src/pages/statementofaccount.vue | 56 +++++++--- ui/src/stores/statementdetailStore.js | 79 ++++++++----- 5 files changed, 138 insertions(+), 119 deletions(-) rename ui/{quasar.config.js.temporary.compiled.1772783539584.mjs => quasar.config.js.temporary.compiled.1772788258926.mjs} (100%) diff --git a/svc/queries/statements_detail.go b/svc/queries/statements_detail.go index 8b592db..59418cb 100644 --- a/svc/queries/statements_detail.go +++ b/svc/queries/statements_detail.go @@ -10,90 +10,62 @@ import ( ) // 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)) - for i, v := range parislemler { - pp[i] = strings.TrimSpace(v) - } - inParislem = strings.Join(pp, ",") - } - - piyasaScope, err := buildPiyasaExistsForCariCode(ctx, "a.CurrAccCode") - if err != nil { - return nil, err - } - - query := fmt.Sprintf(` +func GetStatementDetails(ctx context.Context, belgeNo string) ([]models.StatementDetail, error) { + query := ` 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, - 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, + 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, + SUM(ABS(a.Doc_Price)) 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 - 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 -WHERE REPLACE(a.CurrAccCode, ' ', '') LIKE REPLACE(@Carikod, ' ', '') + '%%' - AND a.InvoiceDate BETWEEN @StartDate AND @EndDate - AND %s - %s + ON KisaKar.AttributeTypeCode = KisaKarDesc.AttributeTypeCode + AND KisaKar.AttributeCode = KisaKarDesc.AttributeCode + AND KisaKar.ItemTypeCode = KisaKarDesc.ItemTypeCode +WHERE LTRIM(RTRIM(a.InvoiceNumber)) = LTRIM(RTRIM(@BelgeNo)) 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) - }(), - ) +ORDER BY Belge_Tarihi, Belge_Ref_Numarasi, Urun_Kodu;` rows, err := db.MssqlDB.QueryContext(ctx, query, - sql.Named("Carikod", normalizeMasterAccountCode(accountCode)), - sql.Named("StartDate", startDate), - sql.Named("EndDate", endDate), + sql.Named("BelgeNo", strings.TrimSpace(belgeNo)), ) if err != nil { return nil, fmt.Errorf("detay sorgu hatasi: %v", err) diff --git a/svc/routes/statement_detail.go b/svc/routes/statement_detail.go index 6cf26f7..3aa1bf4 100644 --- a/svc/routes/statement_detail.go +++ b/svc/routes/statement_detail.go @@ -18,21 +18,15 @@ func GetStatementDetailsHandler(w http.ResponseWriter, r *http.Request) { return } - vars := mux.Vars(r) - accountCode := vars["accountCode"] - if accountCode == "" { - accountCode = vars["id"] - } - if accountCode == "" { - http.Error(w, "account code is required", http.StatusBadRequest) + _ = mux.Vars(r) + + belgeNo := r.URL.Query().Get("belgeno") + if belgeNo == "" { + http.Error(w, "belgeno is required", http.StatusBadRequest) return } - startDate := r.URL.Query().Get("startdate") - endDate := r.URL.Query().Get("enddate") - parislemler := r.URL.Query()["parislemler"] - - details, err := queries.GetStatementDetails(r.Context(), accountCode, startDate, endDate, parislemler) + details, err := queries.GetStatementDetails(r.Context(), belgeNo) if err != nil { http.Error(w, "Error fetching statement details: "+err.Error(), http.StatusInternalServerError) return diff --git a/ui/quasar.config.js.temporary.compiled.1772783539584.mjs b/ui/quasar.config.js.temporary.compiled.1772788258926.mjs similarity index 100% rename from ui/quasar.config.js.temporary.compiled.1772783539584.mjs rename to ui/quasar.config.js.temporary.compiled.1772788258926.mjs diff --git a/ui/src/pages/statementofaccount.vue b/ui/src/pages/statementofaccount.vue index 754fc75..b508688 100644 --- a/ui/src/pages/statementofaccount.vue +++ b/ui/src/pages/statementofaccount.vue @@ -240,13 +240,13 @@ buildColumns(statementheaderStore.headers)) -function detailColumns(belgeNo) { - const details = detailStore.getDetailsByBelge(belgeNo) +function detailColumns(rowOrBelgeNo) { + const details = detailStore.getDetailsByRowKey(rowOrBelgeNo) return buildColumns(details) } @@ -417,33 +417,57 @@ async function onFilterClick() { excludeopening: excludeOpening.value }) - await detailStore.loadDetails({ - accountCode: selectedCari.value, - startDate: dateFrom.value, - endDate: dateTo.value, - parislemler: selectedMonType.value - }) } /* Grup satırları için özel rowKey */ const rowKeyFn = (row) => - row._type === 'group' ? `grp-${row.para_birimi}` : row.belge_no + row._type === 'group' ? `grp-${row.para_birimi}` : getRowExpandKey(row) + +function getRowExpandKey (row) { + return [ + String(row?.belge_no || '').trim(), + String(row?.belge_tarihi || '').trim(), + String(row?.para_birimi || '').trim(), + String(row?.islem_tipi || '').trim(), + String(row?.cari_kod || '').trim() + ].join('|') +} /* Detay açma sadece expand kontrolü */ -function toggleRowDetails(row) { +async function toggleRowDetails(row) { if (row._type === 'group') return - expandedRows.value[row.belge_no] = !expandedRows.value[row.belge_no] + const key = getRowExpandKey(row) + const next = !expandedRows.value[key] + expandedRows.value[key] = next + + if (!next) return + if (!row?.belge_no || String(row.belge_no).trim() === 'Baslangic_devir') return + if (detailStore.hasDetailsByRowKey(key)) return + + await detailStore.loadDetails({ + accountCode: selectedCari.value, + belgeNo: row.belge_no, + rowKey: key + }) } /* Tüm detayları aç/kapat */ -function toggleAllDetails() { +async function toggleAllDetails() { allDetailsOpen.value = !allDetailsOpen.value if (allDetailsOpen.value) { + const dataRows = [] for (const row of statementheaderStore.headers) { if (row.belge_no) { - expandedRows.value[row.belge_no] = true + const key = getRowExpandKey(row) + expandedRows.value[key] = true + dataRows.push(row) } } + await detailStore.preloadForRows({ + accountCode: selectedCari.value, + rows: dataRows, + getRowKey: getRowExpandKey + }) } else { expandedRows.value = {} } diff --git a/ui/src/stores/statementdetailStore.js b/ui/src/stores/statementdetailStore.js index 235021f..70115b0 100644 --- a/ui/src/stores/statementdetailStore.js +++ b/ui/src/stores/statementdetailStore.js @@ -4,15 +4,16 @@ import api from 'src/services/api' export const useStatementdetailStore = defineStore('statementdetail', { state: () => ({ - details: [], + detailsByRow: {}, + detailsByBelge: {}, loading: false, error: null }), actions: { - async loadDetails ({ accountCode, startDate, endDate, parislemler }) { + async loadDetails ({ accountCode, belgeNo, rowKey }) { if (!accountCode) { - this.error = 'Geçerli bir cari kod seçilmedi.' + this.error = 'Gecerli bir cari kod secilmedi.' return } @@ -20,46 +21,74 @@ export const useStatementdetailStore = defineStore('statementdetail', { this.error = null try { - // ✅ Params (arrayFormat=repeat global) - const params = { - startdate: startDate, - enddate: endDate + const normalizedBelgeNo = String(belgeNo || '').trim() + const params = { belgeno: normalizedBelgeNo } + + if (this.detailsByBelge[normalizedBelgeNo]) { + const keyCached = String(rowKey || '').trim() + if (keyCached) { + this.detailsByRow[keyCached] = this.detailsByBelge[normalizedBelgeNo] + } + return } - if (Array.isArray(parislemler) && parislemler.length > 0) { - params.parislemler = parislemler.filter( - p => p !== undefined && p !== null && p !== '' - ) - } - - // 🔐 TOKEN + SERIALIZER + ERROR HANDLING OTOMATİK const res = await api.get( - `/statements/${accountCode}/details`, + `/statements/${encodeURIComponent(accountCode)}/details`, { params } ) - this.details = res.data || [] - + const key = String(rowKey || '').trim() + const rows = Array.isArray(res.data) ? res.data : [] + if (normalizedBelgeNo) { + this.detailsByBelge[normalizedBelgeNo] = rows + } + if (key) { + this.detailsByRow[key] = rows + } } catch (err) { - console.error('❌ Details yüklenemedi:', err) + console.error('Details yuklenemedi:', err) this.error = err?.data?.message || err?.message || - 'Detaylar yüklenemedi' + 'Detaylar yuklenemedi' } finally { this.loading = false } }, - getDetailsByBelge (belgeNo) { - const needle = String(belgeNo || '').trim() - return this.details.filter( - d => String(d.belge_ref_numarasi || '').trim() === needle - ) + hasDetailsByRowKey (rowKey) { + const key = String(rowKey || '').trim() + return Array.isArray(this.detailsByRow[key]) + }, + + getDetailsByRowKey (rowKey) { + const key = String(rowKey || '').trim() + return this.detailsByRow[key] || [] + }, + + async preloadForRows ({ accountCode, rows, getRowKey }) { + const tasks = [] + + for (const row of rows || []) { + const belgeNo = String(row?.belge_no || '').trim() + if (!belgeNo || belgeNo === 'Baslangic_devir') continue + + const rowKey = String(getRowKey(row) || '').trim() + if (!rowKey) continue + if (this.hasDetailsByRowKey(rowKey)) continue + + tasks.push( + this.loadDetails({ accountCode, belgeNo, rowKey }) + ) + } + + if (tasks.length === 0) return + await Promise.all(tasks) }, reset () { - this.details = [] + this.detailsByRow = {} + this.detailsByBelge = {} this.loading = false this.error = null }