From c779e93f4376bab9c5acdcdb5f00cdd285c0fb56 Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Tue, 17 Mar 2026 12:04:43 +0300 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- svc/routes/customer_balance_pdf.go | 152 ++++++++++++++++++++++++++--- svc/routes/statement_aging_pdf.go | 4 +- 2 files changed, 140 insertions(+), 16 deletions(-) diff --git a/svc/routes/customer_balance_pdf.go b/svc/routes/customer_balance_pdf.go index 688ab01..8309b4d 100644 --- a/svc/routes/customer_balance_pdf.go +++ b/svc/routes/customer_balance_pdf.go @@ -7,6 +7,7 @@ import ( "bytes" "database/sql" "fmt" + "math" "net/http" "sort" "strconv" @@ -252,12 +253,21 @@ func drawCustomerBalancePDF( pageW, pageH := pdf.GetPageSize() marginL, marginT, marginR, marginB := 8.0, 8.0, 8.0, 12.0 tableW := pageW - marginL - marginR + pageNoColor := [3]int{90, 90, 90} + + pdf.SetFooterFunc(func() { + pdf.SetY(-8) + pdf.SetFont("dejavu", "", 8) + pdf.SetTextColor(pageNoColor[0], pageNoColor[1], pageNoColor[2]) + pdf.CellFormat(0, 4, fmt.Sprintf("Sayfa %d", pdf.PageNo()), "", 0, "R", false, 0, "") + }) summaryCols := []string{"Ana Cari Kod", "Ana Cari Detay", "Piyasa", "Temsilci", "Risk", "1_2 Pr.Br", "1_3 Pr.Br", "1_2 USD", "1_2 TRY", "1_3 USD", "1_3 TRY"} summaryWeights := []float64{18, 42, 16, 16, 14, 24, 24, 14, 14, 14, 14} if includeVadeColumns { + // Aging raporu (A4 dikey) için kolonlar sıkıştırılır. summaryCols = append(summaryCols, "Vade Gun", "Belge Tarihi Gun") - summaryWeights = append(summaryWeights, 12, 16) + summaryWeights = []float64{14, 28, 10, 10, 10, 13, 13, 9, 9, 9, 9, 8, 10} } summaryW := normalizeWidths(summaryWeights, tableW) @@ -307,6 +317,7 @@ func drawCustomerBalancePDF( pdf.Line(marginL, marginT+14, pageW-marginR, marginT+14) pdf.SetDrawColor(210, 210, 210) pdf.SetY(marginT + 17) + } needPage := func(needH float64) bool { @@ -398,15 +409,15 @@ func drawCustomerBalancePDF( totalVade = totalVadeSum / totalVadeBase totalVadeBelge = totalVadeBelgeSum / totalVadeBase } - totalsRow = append(totalsRow, formatMoneyPDF(totalVade), formatMoneyPDF(totalVadeBelge)) + totalsRow = append(totalsRow, formatDayUpPDF(totalVade), formatDayUpPDF(totalVadeBelge)) } - totalH := calcPDFRowHeight(pdf, totalsRow, summaryW, map[int]bool{0: true, 1: true, 2: true, 3: true}, 6.2, 3.6) + totalH := calcPDFRowHeightCapped(pdf, totalsRow, summaryW, map[int]int{0: 1, 1: 1, 2: 1, 3: 1, 5: 2, 6: 2}, 6.2, 3.4) if needPage(totalH) { header() drawSummaryHeader() } - pdf.SetFont("dejavu", "B", 7.2) + pdf.SetFont("dejavu", "B", 6.8) pdf.SetFillColor(218, 193, 151) pdf.SetTextColor(20, 20, 20) totalY := pdf.GetY() @@ -423,7 +434,11 @@ func drawCustomerBalancePDF( if includeVadeColumns && (i == len(totalsRow)-1 || i == len(totalsRow)-2) { align = "C" } - drawPDFCellWrapped(pdf, v, totalX, totalY, summaryW[i], totalH, align, 3.6) + if i == 5 || i == 6 { + drawPDFCellWrappedCapped(pdf, v, totalX, totalY, summaryW[i], totalH, align, 3.4, 2) + } else { + drawPDFCellWrapped(pdf, v, totalX, totalY, summaryW[i], totalH, align, 3.4) + } totalX += summaryW[i] } pdf.SetY(totalY + totalH) @@ -445,10 +460,10 @@ func drawCustomerBalancePDF( formatMoneyPDF(s.TLBakiye13), } if includeVadeColumns { - row = append(row, formatMoneyPDF(s.VadeGun), formatMoneyPDF(s.VadeBelge)) + row = append(row, formatDayUpPDF(s.VadeGun), formatDayUpPDF(s.VadeBelge)) } - rowH := calcPDFRowHeight(pdf, row, summaryW, map[int]bool{1: true, 2: true, 3: true}, 6.2, 3.6) + rowH := calcPDFRowHeightCapped(pdf, row, summaryW, map[int]int{0: 3, 1: 3, 2: 3, 3: 3}, 6.2, 3.6) if needPage(rowH) { header() drawSummaryHeader() @@ -470,7 +485,11 @@ func drawCustomerBalancePDF( if includeVadeColumns && (i == len(row)-1 || i == len(row)-2) { align = "C" } - drawPDFCellWrapped(pdf, v, x, y, summaryW[i], rowH, align, 3.6) + if i <= 3 { + drawPDFCellWrappedCapped(pdf, v, x, y, summaryW[i], rowH, align, 3.6, 3) + } else { + drawPDFCellWrapped(pdf, v, x, y, summaryW[i], rowH, align, 3.6) + } x += summaryW[i] } pdf.SetY(y + rowH) @@ -519,7 +538,7 @@ func drawCustomerBalancePDF( formatMoneyPDF(r.TLBakiye13), } if includeVadeColumns { - line = append(line, formatMoneyPDF(r.VadeGun), formatMoneyPDF(r.VadeBelgeGun)) + line = append(line, formatDayUpPDF(r.VadeGun), formatDayUpPDF(r.VadeBelgeGun)) } rowH := calcPDFRowHeight(pdf, line, detailW, map[int]bool{1: true}, 5.8, 3.3) @@ -591,7 +610,7 @@ func drawCustomerBalancePDFFallback( pdf.AddPage() pdf.SetFont("dejavu", "B", 13) pdf.SetTextColor(149, 113, 22) - pdf.CellFormat(0, 8, reportTitle+" (Fallback)", "", 1, "L", false, 0, "") + pdf.CellFormat(0, 8, reportTitle, "", 1, "L", false, 0, "") pdf.SetFont("dejavu", "", 9) pdf.SetTextColor(20, 20, 20) @@ -601,11 +620,11 @@ func drawCustomerBalancePDFFallback( } pdf.Ln(1) - header := []string{"Ana Cari Kod", "Ana Cari Detay", "1_2 USD", "1_2 TRY", "1_3 USD", "1_3 TRY"} - widths := normalizeWidths([]float64{24, 78, 18, 18, 18, 18}, 281) + header := []string{"Ana Cari Kod", "Ana Cari Detay", "Piyasa", "Temsilci", "Risk", "1_2 USD", "1_2 TRY", "1_3 USD", "1_3 TRY"} + widths := normalizeWidths([]float64{18, 34, 12, 12, 12, 10, 10, 10, 10}, 281) if includeVadeColumns { header = append(header, "Vade Gun", "Belge Tarihi Gun") - widths = normalizeWidths([]float64{24, 70, 17, 17, 17, 17, 12, 17}, 281) + widths = normalizeWidths([]float64{17, 28, 10, 10, 10, 10, 10, 10, 10, 8, 10}, 281) } pdf.SetFont("dejavu", "B", 8) @@ -626,13 +645,16 @@ func drawCustomerBalancePDFFallback( row := []string{ s.AnaCariKodu, s.AnaCariAdi, + s.Piyasa, + s.Temsilci, + s.RiskDurumu, formatMoneyPDF(s.USDBakiye12), formatMoneyPDF(s.TLBakiye12), formatMoneyPDF(s.USDBakiye13), formatMoneyPDF(s.TLBakiye13), } if includeVadeColumns { - row = append(row, formatMoneyPDF(s.VadeGun), formatMoneyPDF(s.VadeBelge)) + row = append(row, formatDayUpPDF(s.VadeGun), formatDayUpPDF(s.VadeBelge)) } if pdf.GetY()+6 > 198 { @@ -729,6 +751,108 @@ func safeSplitLinesPDF(pdf *gofpdf.Fpdf, text string, width float64) (lines [][] return lines } +func calcPDFRowHeightCapped(pdf *gofpdf.Fpdf, row []string, widths []float64, wrapMax map[int]int, minH, lineH float64) float64 { + maxLines := 1 + for i, v := range row { + limit, ok := wrapMax[i] + if !ok || limit <= 0 { + continue + } + if i >= len(widths) || widths[i] <= 2 { + continue + } + lines := safeSplitLinesPDF(pdf, strings.TrimSpace(v), widths[i]-2) + lineCount := len(lines) + if lineCount > limit { + lineCount = limit + } + if lineCount > maxLines { + maxLines = lineCount + } + } + h := float64(maxLines)*lineH + 2 + if h < minH { + return minH + } + return h +} + +func drawPDFCellWrappedCapped(pdf *gofpdf.Fpdf, value string, x, y, w, h float64, align string, lineH float64, maxLines int) { + if w <= 2 || h <= 0 { + return + } + text := strings.TrimSpace(value) + lines := safeSplitLinesPDF(pdf, text, w-2) + if len(lines) == 0 { + lines = [][]byte{[]byte("")} + } + + clipped := false + if maxLines > 0 && len(lines) > maxLines { + lines = lines[:maxLines] + clipped = true + } + if clipped && len(lines) > 0 { + last := string(lines[len(lines)-1]) + lines[len(lines)-1] = []byte(fitTextWithSuffixPDF(pdf, last, w-2, "...")) + } + + startY := y + (h-(float64(len(lines))*lineH))/2 + if startY < y+0.7 { + startY = y + 0.7 + } + + for _, ln := range lines { + pdf.SetXY(x+1, startY) + pdf.CellFormat(w-2, lineH, string(ln), "", 0, align, false, 0, "") + startY += lineH + } +} + +func fitTextWithSuffixPDF(pdf *gofpdf.Fpdf, text string, width float64, suffix string) string { + txt := strings.TrimSpace(text) + if txt == "" { + return suffix + } + if pdf.GetStringWidth(txt) <= width { + return txt + } + allowed := width - pdf.GetStringWidth(suffix) + if allowed <= 0 { + return suffix + } + runes := []rune(txt) + for len(runes) > 0 && pdf.GetStringWidth(string(runes)) > allowed { + runes = runes[:len(runes)-1] + } + if len(runes) == 0 { + return suffix + } + return string(runes) + suffix +} + +func formatDayUpPDF(v float64) string { + return formatIntPDF(int64(math.Ceil(v))) +} + +func formatIntPDF(v int64) string { + s := strconv.FormatInt(v, 10) + sign := "" + if strings.HasPrefix(s, "-") { + sign = "-" + s = strings.TrimPrefix(s, "-") + } + var out []string + for len(s) > 3 { + out = append([]string{s[len(s)-3:]}, out...) + s = s[:len(s)-3] + } + if s != "" { + out = append([]string{s}, out...) + } + return sign + strings.Join(out, ".") +} + func formatCurrencyMapPDF(m map[string]float64) string { if len(m) == 0 { return "-" diff --git a/svc/routes/statement_aging_pdf.go b/svc/routes/statement_aging_pdf.go index 2640dd5..440de93 100644 --- a/svc/routes/statement_aging_pdf.go +++ b/svc/routes/statement_aging_pdf.go @@ -74,7 +74,7 @@ func ExportStatementAgingPDFHandler(_ *sql.DB) http.HandlerFunc { rows = filterCustomerBalanceRowsForPDF(rows, excludeZero12, excludeZero13) summaries, detailsByMaster := buildCustomerBalancePDFData(rows) - pdf := gofpdf.New("L", "mm", "A4", "") + pdf := gofpdf.New("P", "mm", "A4", "") pdf.SetMargins(8, 8, 8) pdf.SetAutoPageBreak(false, 12) if err := registerDejavuFonts(pdf, "dejavu"); err != nil { @@ -92,7 +92,7 @@ func ExportStatementAgingPDFHandler(_ *sql.DB) http.HandlerFunc { summaries, detailsByMaster, ); err != nil { - pdf = gofpdf.New("L", "mm", "A4", "") + pdf = gofpdf.New("P", "mm", "A4", "") pdf.SetMargins(8, 8, 8) pdf.SetAutoPageBreak(true, 12) if ferr := registerDejavuFonts(pdf, "dejavu"); ferr != nil {