From 83a55373eaa325c81457bfefd6f3c48381d76fce Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Tue, 17 Mar 2026 14:11:08 +0300 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- svc/routes/statement_aging_screen_pdf.go | 130 ++++++++++++++++++++--- ui/src/pages/AccountAgingStatement.vue | 11 +- 2 files changed, 125 insertions(+), 16 deletions(-) diff --git a/svc/routes/statement_aging_screen_pdf.go b/svc/routes/statement_aging_screen_pdf.go index 24dbe08..8e537dc 100644 --- a/svc/routes/statement_aging_screen_pdf.go +++ b/svc/routes/statement_aging_screen_pdf.go @@ -90,6 +90,9 @@ func ExportStatementAgingScreenPDFHandler(_ *sql.DB) http.HandlerFunc { }) } + sortBy := strings.TrimSpace(r.URL.Query().Get("sort_by")) + sortDesc := parseBoolQuery(r.URL.Query().Get("sort_desc")) + pdf := gofpdf.New("L", "mm", "A3", "") pdf.SetMargins(8, 8, 8) pdf.SetAutoPageBreak(false, 10) @@ -98,7 +101,7 @@ func ExportStatementAgingScreenPDFHandler(_ *sql.DB) http.HandlerFunc { return } - drawStatementAgingScreenPDF(pdf, selectedDate, params.AccountCode, rows) + drawStatementAgingScreenPDF(pdf, selectedDate, params.AccountCode, rows, sortBy, sortDesc) if err := pdf.Error(); err != nil { http.Error(w, "pdf render error: "+err.Error(), http.StatusInternalServerError) @@ -150,8 +153,8 @@ type agingScreenCurrencyPDF struct { WeightedGunDocSum float64 } -func drawStatementAgingScreenPDF(pdf *gofpdf.Fpdf, selectedDate, accountCode string, rows []agingScreenPDFRow) { - masters, currenciesByMaster, detailsByCurrency := buildStatementAgingScreenPDFData(rows) +func drawStatementAgingScreenPDF(pdf *gofpdf.Fpdf, selectedDate, accountCode string, rows []agingScreenPDFRow, sortBy string, sortDesc bool) { + masters, currenciesByMaster, detailsByCurrency := buildStatementAgingScreenPDFData(rows, sortBy, sortDesc) pageW, pageH := pdf.GetPageSize() marginL, marginT, marginR, marginB := 8.0, 8.0, 8.0, 10.0 @@ -369,7 +372,7 @@ func statementAgingAvg(sum, base float64, fallback ...float64) float64 { return 0 } -func buildStatementAgingScreenPDFData(rows []agingScreenPDFRow) ([]agingScreenMasterPDF, map[string][]agingScreenCurrencyPDF, map[string][]agingScreenPDFRow) { +func buildStatementAgingScreenPDFData(rows []agingScreenPDFRow, sortBy string, sortDesc bool) ([]agingScreenMasterPDF, map[string][]agingScreenCurrencyPDF, map[string][]agingScreenPDFRow) { masterMap := map[string]*agingScreenMasterPDF{} currencyMap := map[string]*agingScreenCurrencyPDF{} detailsByCurrency := map[string][]agingScreenPDFRow{} @@ -454,19 +457,13 @@ func buildStatementAgingScreenPDFData(rows []agingScreenPDFRow) ([]agingScreenMa detailsByCurrency[currencyKey] = append(detailsByCurrency[currencyKey], row) } - masterKeys := make([]string, 0, len(masterMap)) - for k := range masterMap { - masterKeys = append(masterKeys, k) + masters := make([]agingScreenMasterPDF, 0, len(masterMap)) + currenciesByMaster := make(map[string][]agingScreenCurrencyPDF, len(masterMap)) + for _, m := range masterMap { + masters = append(masters, *m) } - sort.SliceStable(masterKeys, func(i, j int) bool { - return strings.ToUpper(masterKeys[i]) < strings.ToUpper(masterKeys[j]) - }) - masters := make([]agingScreenMasterPDF, 0, len(masterKeys)) - currenciesByMaster := make(map[string][]agingScreenCurrencyPDF, len(masterKeys)) - for _, mk := range masterKeys { - masters = append(masters, *masterMap[mk]) - } + sortAgingScreenMastersForPDF(masters, sortBy, sortDesc) for _, c := range currencyMap { currenciesByMaster[c.MasterKey] = append(currenciesByMaster[c.MasterKey], *c) @@ -481,6 +478,18 @@ func buildStatementAgingScreenPDFData(rows []agingScreenPDFRow) ([]agingScreenMa sort.SliceStable(detailsByCurrency[k], func(i, j int) bool { a := detailsByCurrency[k][i] b := detailsByCurrency[k][j] + aOpen := strings.EqualFold(strings.TrimSpace(a.Aciklama), "ACIKKALEM") + bOpen := strings.EqualFold(strings.TrimSpace(b.Aciklama), "ACIKKALEM") + if aOpen != bOpen { + return aOpen + } + + aDate := parseAgingSortDate(a.OdemeDocDate, a.OdemeTarihi, a.FaturaTarihi) + bDate := parseAgingSortDate(b.OdemeDocDate, b.OdemeTarihi, b.FaturaTarihi) + if aDate != bDate { + return aDate.After(bDate) + } + if strings.TrimSpace(a.FaturaCari) == strings.TrimSpace(b.FaturaCari) { if strings.TrimSpace(a.OdemeCari) == strings.TrimSpace(b.OdemeCari) { if strings.TrimSpace(a.FaturaRef) == strings.TrimSpace(b.FaturaRef) { @@ -497,6 +506,97 @@ func buildStatementAgingScreenPDFData(rows []agingScreenPDFRow) ([]agingScreenMa return masters, currenciesByMaster, detailsByCurrency } +func sortAgingScreenMastersForPDF(masters []agingScreenMasterPDF, sortBy string, descending bool) { + if len(masters) <= 1 { + return + } + key := strings.TrimSpace(sortBy) + if key == "" { + key = "cari8" + } + + textCmp := func(a, b string) int { + return strings.Compare(strings.ToUpper(strings.TrimSpace(a)), strings.ToUpper(strings.TrimSpace(b))) + } + numCmp := func(a, b float64) int { + if a < b { + return -1 + } + if a > b { + return 1 + } + return 0 + } + intCmp := func(a, b int) int { + if a < b { + return -1 + } + if a > b { + return 1 + } + return 0 + } + + sort.SliceStable(masters, func(i, j int) bool { + a := masters[i] + b := masters[j] + cmp := 0 + + switch key { + case "cari8": + cmp = textCmp(a.Cari8, b.Cari8) + case "cari_detay": + cmp = textCmp(a.CariDetay, b.CariDetay) + case "satir_sayisi": + cmp = intCmp(a.SatirSayisi, b.SatirSayisi) + case "toplam_usd": + cmp = numCmp(a.ToplamUSD, b.ToplamUSD) + case "normal_usd": + cmp = numCmp(a.NormalUSD, b.NormalUSD) + case "acik_kalem_usd": + cmp = numCmp(a.AcikKalemUSD, b.AcikKalemUSD) + case "ortalama_gun": + cmp = numCmp(statementAgingAvg(a.WeightedGunSum, a.WeightedBase), statementAgingAvg(b.WeightedGunSum, b.WeightedBase)) + case "ortalama_gun_docdate": + cmp = numCmp(statementAgingAvg(a.WeightedGunDocSum, a.WeightedBase), statementAgingAvg(b.WeightedGunDocSum, b.WeightedBase)) + case "kur": + cmp = numCmp(statementAgingAvg(a.KurWeightedSum, a.KurWeightedBase, a.KurFallback), statementAgingAvg(b.KurWeightedSum, b.KurWeightedBase, b.KurFallback)) + default: + cmp = textCmp(a.Cari8, b.Cari8) + } + + if cmp == 0 { + cmp = textCmp(a.Cari8, b.Cari8) + } + if descending { + return cmp > 0 + } + return cmp < 0 + }) +} + +func parseAgingSortDate(values ...string) time.Time { + layouts := []string{ + time.RFC3339, + "2006-01-02", + "2006-01-02 15:04:05", + "02.01.2006", + "02.01.2006 15:04:05", + } + for _, raw := range values { + s := strings.TrimSpace(raw) + if s == "" { + continue + } + for _, l := range layouts { + if t, err := time.Parse(l, s); err == nil { + return t + } + } + } + return time.Time{} +} + func pickString(m map[string]interface{}, keys ...string) string { for _, k := range keys { if v, ok := m[k]; ok { diff --git a/ui/src/pages/AccountAgingStatement.vue b/ui/src/pages/AccountAgingStatement.vue index 1e30ab3..0876c1c 100644 --- a/ui/src/pages/AccountAgingStatement.vue +++ b/ui/src/pages/AccountAgingStatement.vue @@ -86,6 +86,7 @@ title="Cari Yaşlandırmalı Ekstre" :rows="agingStore.masterRows" :columns="masterColumns" + v-model:pagination="masterPagination" row-key="group_key" flat bordered @@ -231,6 +232,12 @@ const agingStore = useStatementAgingStore() const selectedCari = ref(null) const filteredOptions = ref([]) const dateTo = ref(dayjs().format('YYYY-MM-DD')) +const masterPagination = ref({ + page: 1, + rowsPerPage: 0, + sortBy: 'cari8', + descending: false +}) const masterExpanded = ref({}) const currencyExpanded = ref({}) @@ -396,7 +403,9 @@ function buildExportParams() { selected_date: dateTo.value, parislemler: selectedMonType.value, exclude_zero_12: '0', - exclude_zero_13: '0' + exclude_zero_13: '0', + sort_by: String(masterPagination.value?.sortBy || ''), + sort_desc: masterPagination.value?.descending ? '1' : '0' } }