580 lines
16 KiB
Go
580 lines
16 KiB
Go
package routes
|
|
|
|
import (
|
|
"bssapp-backend/auth"
|
|
"bssapp-backend/models"
|
|
"bssapp-backend/queries"
|
|
"bytes"
|
|
"database/sql"
|
|
"fmt"
|
|
"net/http"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/jung-kurt/gofpdf"
|
|
)
|
|
|
|
type agingScreenPDFRow struct {
|
|
Cari8 string
|
|
CariDetay string
|
|
FaturaCari string
|
|
OdemeCari string
|
|
FaturaRef string
|
|
OdemeRef string
|
|
FaturaTarihi string
|
|
OdemeTarihi string
|
|
OdemeDocDate string
|
|
EslesenTutar float64
|
|
UsdTutar float64
|
|
CurrencyUsdRate float64
|
|
GunSayisi float64
|
|
GunSayisiDocDate float64
|
|
Aciklama string
|
|
DocCurrencyCode string
|
|
}
|
|
|
|
func ExportStatementAgingScreenPDFHandler(_ *sql.DB) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
claims, ok := auth.GetClaimsFromContext(r.Context())
|
|
if !ok || claims == nil {
|
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
selectedDate := strings.TrimSpace(r.URL.Query().Get("enddate"))
|
|
if selectedDate == "" {
|
|
selectedDate = strings.TrimSpace(r.URL.Query().Get("selected_date"))
|
|
}
|
|
if selectedDate == "" {
|
|
selectedDate = time.Now().Format("2006-01-02")
|
|
}
|
|
|
|
params := models.StatementAgingParams{
|
|
AccountCode: strings.TrimSpace(r.URL.Query().Get("accountcode")),
|
|
EndDate: selectedDate,
|
|
Parislemler: r.URL.Query()["parislemler"],
|
|
}
|
|
|
|
if err := queries.RebuildStatementAgingCache(r.Context()); err != nil {
|
|
http.Error(w, "Error rebuilding aging cache: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
rawRows, err := queries.GetStatementAging(params)
|
|
if err != nil {
|
|
http.Error(w, "Error fetching aging statement: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
rows := make([]agingScreenPDFRow, 0, len(rawRows))
|
|
for _, r := range rawRows {
|
|
rows = append(rows, agingScreenPDFRow{
|
|
Cari8: pickString(r, "Cari8", "cari8"),
|
|
CariDetay: pickString(r, "CariDetay", "cari_detay"),
|
|
FaturaCari: pickString(r, "FaturaCari", "fatura_cari"),
|
|
OdemeCari: pickString(r, "OdemeCari", "odeme_cari"),
|
|
FaturaRef: pickString(r, "FaturaRef", "fatura_ref"),
|
|
OdemeRef: pickString(r, "OdemeRef", "odeme_ref"),
|
|
FaturaTarihi: pickString(r, "FaturaTarihi", "fatura_tarihi"),
|
|
OdemeTarihi: pickString(r, "OdemeTarihi", "odeme_tarihi"),
|
|
OdemeDocDate: pickString(r, "OdemeDocDate", "odeme_doc_date"),
|
|
EslesenTutar: pickFloat(r, "EslesenTutar", "eslesen_tutar"),
|
|
UsdTutar: pickFloat(r, "UsdTutar", "usd_tutar"),
|
|
CurrencyUsdRate: pickFloat(r, "CurrencyUsdRate", "currency_usd_rate", "CurrencyTryRate", "currency_try_rate"),
|
|
GunSayisi: pickFloat(r, "GunSayisi", "gun_sayisi"),
|
|
GunSayisiDocDate: pickFloat(r, "GunSayisi_DocDate", "gun_sayisi_docdate"),
|
|
Aciklama: pickString(r, "Aciklama", "aciklama"),
|
|
DocCurrencyCode: pickString(r, "DocCurrencyCode", "doc_currency_code"),
|
|
})
|
|
}
|
|
|
|
pdf := gofpdf.New("L", "mm", "A3", "")
|
|
pdf.SetMargins(8, 8, 8)
|
|
pdf.SetAutoPageBreak(false, 10)
|
|
if err := registerDejavuFonts(pdf, "dejavu"); err != nil {
|
|
http.Error(w, "pdf font error: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
drawStatementAgingScreenPDF(pdf, selectedDate, params.AccountCode, rows)
|
|
|
|
if err := pdf.Error(); err != nil {
|
|
http.Error(w, "pdf render error: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := pdf.Output(&buf); err != nil {
|
|
http.Error(w, "pdf output error: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/pdf")
|
|
w.Header().Set("Content-Disposition", `inline; filename="account-aging-screen.pdf"`)
|
|
_, _ = w.Write(buf.Bytes())
|
|
}
|
|
}
|
|
|
|
type agingScreenMasterPDF struct {
|
|
GroupKey string
|
|
Cari8 string
|
|
CariDetay string
|
|
SatirSayisi int
|
|
ToplamUSD float64
|
|
NormalUSD float64
|
|
AcikKalemUSD float64
|
|
KurWeightedBase float64
|
|
KurWeightedSum float64
|
|
KurFallback float64
|
|
WeightedBase float64
|
|
WeightedGunSum float64
|
|
WeightedGunDocSum float64
|
|
}
|
|
|
|
type agingScreenCurrencyPDF struct {
|
|
GroupKey string
|
|
MasterKey string
|
|
DocCurrencyCode string
|
|
SatirSayisi int
|
|
ToplamTutar float64
|
|
ToplamUSD float64
|
|
NormalTutar float64
|
|
AcikKalemTutar float64
|
|
KurWeightedBase float64
|
|
KurWeightedSum float64
|
|
KurFallback float64
|
|
WeightedBase float64
|
|
WeightedGunSum float64
|
|
WeightedGunDocSum float64
|
|
}
|
|
|
|
func drawStatementAgingScreenPDF(pdf *gofpdf.Fpdf, selectedDate, accountCode string, rows []agingScreenPDFRow) {
|
|
masters, currenciesByMaster, detailsByCurrency := buildStatementAgingScreenPDFData(rows)
|
|
|
|
pageW, pageH := pdf.GetPageSize()
|
|
marginL, marginT, marginR, marginB := 8.0, 8.0, 8.0, 10.0
|
|
tableW := pageW - marginL - marginR
|
|
|
|
masterCols := []string{"Ana Cari Kod", "Ana Cari Detay", "Satir", "Toplam USD", "Normal USD", "Acik Kalem USD", "Ort. Gun", "Ort. Gun (DocDate)", "Kur"}
|
|
masterW := normalizeWidths([]float64{22, 40, 12, 18, 18, 20, 14, 18, 12}, tableW)
|
|
|
|
currencyCols := []string{"Doviz", "Satir", "Toplam Tutar", "Toplam USD", "Normal", "Acik Kalem", "Kur", "Ort. Gun", "Ort. Gun (DocDate)"}
|
|
currencyW := normalizeWidths([]float64{16, 12, 20, 18, 18, 18, 12, 14, 18}, tableW)
|
|
|
|
detailCols := []string{"Fatura Cari", "Odeme Cari", "Fatura Ref", "Odeme Ref", "Fatura Tarihi", "Odeme Vade", "Odeme DocDate", "Eslesen Tutar", "USD Tutar", "Kur", "Gun", "Gun (DocDate)", "Aciklama", "Doviz"}
|
|
detailW := normalizeWidths([]float64{18, 18, 22, 22, 16, 16, 18, 18, 16, 12, 10, 13, 30, 11}, tableW)
|
|
|
|
header := func() {
|
|
pdf.AddPage()
|
|
pdf.SetFont("dejavu", "B", 14)
|
|
pdf.SetTextColor(149, 113, 22)
|
|
pdf.SetXY(marginL, marginT)
|
|
pdf.CellFormat(150, 6, "Cari Yaslandirmali Ekstre", "", 0, "L", false, 0, "")
|
|
|
|
pdf.SetFont("dejavu", "", 8.5)
|
|
pdf.SetTextColor(30, 30, 30)
|
|
pdf.SetXY(pageW-marginR-90, marginT+0.5)
|
|
pdf.CellFormat(90, 4.8, "Son Tarih: "+formatDateTR(selectedDate), "", 0, "R", false, 0, "")
|
|
if strings.TrimSpace(accountCode) != "" {
|
|
pdf.SetXY(pageW-marginR-90, marginT+5)
|
|
pdf.CellFormat(90, 4.8, "Cari: "+accountCode, "", 0, "R", false, 0, "")
|
|
}
|
|
|
|
pdf.SetDrawColor(149, 113, 22)
|
|
pdf.Line(marginL, marginT+10.5, pageW-marginR, marginT+10.5)
|
|
pdf.SetDrawColor(200, 200, 200)
|
|
pdf.SetY(marginT + 13)
|
|
}
|
|
|
|
needPage := func(needH float64) bool {
|
|
return pdf.GetY()+needH+marginB > pageH
|
|
}
|
|
|
|
drawHeaderRow := func(cols []string, widths []float64, h float64, r, g, b int, fontSize float64) {
|
|
pdf.SetFont("dejavu", "B", fontSize)
|
|
pdf.SetTextColor(255, 255, 255)
|
|
pdf.SetFillColor(r, g, b)
|
|
y := pdf.GetY()
|
|
x := marginL
|
|
for i, c := range cols {
|
|
pdf.Rect(x, y, widths[i], h, "DF")
|
|
pdf.SetXY(x+0.8, y+0.9)
|
|
pdf.CellFormat(widths[i]-1.6, h-1.8, c, "", 0, "C", false, 0, "")
|
|
x += widths[i]
|
|
}
|
|
pdf.SetY(y + h)
|
|
}
|
|
setDataTextStyle := func(size float64, r, g, b int) {
|
|
pdf.SetFont("dejavu", "", size)
|
|
pdf.SetTextColor(r, g, b)
|
|
}
|
|
|
|
header()
|
|
drawHeaderRow(masterCols, masterW, 6.2, 149, 113, 22, 7.2)
|
|
setDataTextStyle(7, 25, 25, 25)
|
|
|
|
for _, m := range masters {
|
|
masterLine := []string{
|
|
m.Cari8,
|
|
m.CariDetay,
|
|
strconv.Itoa(m.SatirSayisi),
|
|
formatMoneyPDF(m.ToplamUSD),
|
|
formatMoneyPDF(m.NormalUSD),
|
|
formatMoneyPDF(m.AcikKalemUSD),
|
|
fmt.Sprintf("%.0f", statementAgingAvg(m.WeightedGunSum, m.WeightedBase)),
|
|
fmt.Sprintf("%.0f", statementAgingAvg(m.WeightedGunDocSum, m.WeightedBase)),
|
|
formatMoneyPDF(statementAgingAvg(m.KurWeightedSum, m.KurWeightedBase, m.KurFallback)),
|
|
}
|
|
rowH := calcPDFRowHeight(pdf, masterLine, masterW, map[int]bool{1: true}, 5.8, 3.3)
|
|
if needPage(rowH) {
|
|
header()
|
|
drawHeaderRow(masterCols, masterW, 6.2, 149, 113, 22, 7.2)
|
|
setDataTextStyle(7, 25, 25, 25)
|
|
}
|
|
setDataTextStyle(7, 25, 25, 25)
|
|
|
|
y := pdf.GetY()
|
|
x := marginL
|
|
for i, v := range masterLine {
|
|
pdf.Rect(x, y, masterW[i], rowH, "")
|
|
align := "L"
|
|
if i >= 2 {
|
|
align = "R"
|
|
}
|
|
if i == 6 || i == 7 {
|
|
align = "C"
|
|
}
|
|
drawPDFCellWrapped(pdf, v, x, y, masterW[i], rowH, align, 3.3)
|
|
x += masterW[i]
|
|
}
|
|
pdf.SetY(y + rowH)
|
|
|
|
for _, c := range currenciesByMaster[m.GroupKey] {
|
|
if needPage(11.2) {
|
|
header()
|
|
drawHeaderRow(masterCols, masterW, 6.2, 149, 113, 22, 7.2)
|
|
setDataTextStyle(7, 25, 25, 25)
|
|
}
|
|
|
|
pdf.SetFont("dejavu", "B", 7)
|
|
drawHeaderRow(currencyCols, currencyW, 5.6, 76, 95, 122, 6.8)
|
|
|
|
setDataTextStyle(6.8, 35, 35, 35)
|
|
currencyLine := []string{
|
|
c.DocCurrencyCode,
|
|
strconv.Itoa(c.SatirSayisi),
|
|
formatMoneyPDF(c.ToplamTutar),
|
|
formatMoneyPDF(c.ToplamUSD),
|
|
formatMoneyPDF(c.NormalTutar),
|
|
formatMoneyPDF(c.AcikKalemTutar),
|
|
formatMoneyPDF(statementAgingAvg(c.KurWeightedSum, c.KurWeightedBase, c.KurFallback)),
|
|
fmt.Sprintf("%.0f", statementAgingAvg(c.WeightedGunSum, c.WeightedBase)),
|
|
fmt.Sprintf("%.0f", statementAgingAvg(c.WeightedGunDocSum, c.WeightedBase)),
|
|
}
|
|
|
|
cRowH := 5.4
|
|
y := pdf.GetY()
|
|
x := marginL
|
|
for i, v := range currencyLine {
|
|
pdf.Rect(x, y, currencyW[i], cRowH, "")
|
|
align := "R"
|
|
if i == 0 {
|
|
align = "L"
|
|
}
|
|
if i == 7 || i == 8 {
|
|
align = "C"
|
|
}
|
|
drawPDFCellWrapped(pdf, v, x, y, currencyW[i], cRowH, align, 3.2)
|
|
x += currencyW[i]
|
|
}
|
|
pdf.SetY(y + cRowH)
|
|
|
|
drawHeaderRow(detailCols, detailW, 5.6, 31, 59, 91, 6.8)
|
|
setDataTextStyle(6.6, 30, 30, 30)
|
|
|
|
for _, d := range detailsByCurrency[c.GroupKey] {
|
|
line := []string{
|
|
d.FaturaCari,
|
|
d.OdemeCari,
|
|
d.FaturaRef,
|
|
d.OdemeRef,
|
|
formatDateTR(d.FaturaTarihi),
|
|
formatDateTR(d.OdemeTarihi),
|
|
formatDateTR(d.OdemeDocDate),
|
|
formatMoneyPDF(d.EslesenTutar),
|
|
formatMoneyPDF(d.UsdTutar),
|
|
formatMoneyPDF(d.CurrencyUsdRate),
|
|
fmt.Sprintf("%.0f", d.GunSayisi),
|
|
fmt.Sprintf("%.0f", d.GunSayisiDocDate),
|
|
d.Aciklama,
|
|
d.DocCurrencyCode,
|
|
}
|
|
rowH := calcPDFRowHeight(pdf, line, detailW, map[int]bool{0: true, 1: true, 2: true, 3: true, 12: true}, 5.4, 3.1)
|
|
if needPage(rowH) {
|
|
header()
|
|
drawHeaderRow(masterCols, masterW, 6.2, 149, 113, 22, 7.2)
|
|
pdf.SetFont("dejavu", "B", 7)
|
|
drawHeaderRow(currencyCols, currencyW, 5.6, 76, 95, 122, 6.8)
|
|
setDataTextStyle(6.8, 35, 35, 35)
|
|
y = pdf.GetY()
|
|
x = marginL
|
|
for i, v := range currencyLine {
|
|
pdf.Rect(x, y, currencyW[i], cRowH, "")
|
|
align := "R"
|
|
if i == 0 {
|
|
align = "L"
|
|
}
|
|
if i == 7 || i == 8 {
|
|
align = "C"
|
|
}
|
|
drawPDFCellWrapped(pdf, v, x, y, currencyW[i], cRowH, align, 3.2)
|
|
x += currencyW[i]
|
|
}
|
|
pdf.SetY(y + cRowH)
|
|
drawHeaderRow(detailCols, detailW, 5.6, 31, 59, 91, 6.8)
|
|
setDataTextStyle(6.6, 30, 30, 30)
|
|
}
|
|
|
|
rowY := pdf.GetY()
|
|
rowX := marginL
|
|
for i, v := range line {
|
|
pdf.Rect(rowX, rowY, detailW[i], rowH, "")
|
|
align := "L"
|
|
if i >= 7 && i <= 9 {
|
|
align = "R"
|
|
}
|
|
if i == 10 || i == 11 {
|
|
align = "C"
|
|
}
|
|
drawPDFCellWrapped(pdf, v, rowX, rowY, detailW[i], rowH, align, 3.1)
|
|
rowX += detailW[i]
|
|
}
|
|
pdf.SetY(rowY + rowH)
|
|
}
|
|
pdf.Ln(1)
|
|
}
|
|
pdf.Ln(1.2)
|
|
}
|
|
}
|
|
|
|
func statementAgingAvg(sum, base float64, fallback ...float64) float64 {
|
|
if base > 0 {
|
|
return sum / base
|
|
}
|
|
if len(fallback) > 0 {
|
|
return fallback[0]
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func buildStatementAgingScreenPDFData(rows []agingScreenPDFRow) ([]agingScreenMasterPDF, map[string][]agingScreenCurrencyPDF, map[string][]agingScreenPDFRow) {
|
|
masterMap := map[string]*agingScreenMasterPDF{}
|
|
currencyMap := map[string]*agingScreenCurrencyPDF{}
|
|
detailsByCurrency := map[string][]agingScreenPDFRow{}
|
|
|
|
for _, row := range rows {
|
|
masterKey := strings.TrimSpace(row.Cari8)
|
|
if masterKey == "" {
|
|
continue
|
|
}
|
|
curr := strings.ToUpper(strings.TrimSpace(row.DocCurrencyCode))
|
|
if curr == "" {
|
|
curr = "N/A"
|
|
}
|
|
currencyKey := masterKey + "|" + curr
|
|
aciklama := strings.ToUpper(strings.TrimSpace(row.Aciklama))
|
|
absUsd := absFloatExcel(row.UsdTutar)
|
|
|
|
m := masterMap[masterKey]
|
|
if m == nil {
|
|
m = &agingScreenMasterPDF{
|
|
GroupKey: masterKey,
|
|
Cari8: masterKey,
|
|
CariDetay: strings.TrimSpace(row.CariDetay),
|
|
}
|
|
masterMap[masterKey] = m
|
|
}
|
|
if m.CariDetay == "" {
|
|
m.CariDetay = strings.TrimSpace(row.CariDetay)
|
|
}
|
|
|
|
c := currencyMap[currencyKey]
|
|
if c == nil {
|
|
c = &agingScreenCurrencyPDF{
|
|
GroupKey: currencyKey,
|
|
MasterKey: masterKey,
|
|
DocCurrencyCode: curr,
|
|
}
|
|
currencyMap[currencyKey] = c
|
|
}
|
|
|
|
m.SatirSayisi++
|
|
m.ToplamUSD += row.UsdTutar
|
|
if aciklama == "ACIKKALEM" {
|
|
m.AcikKalemUSD += row.UsdTutar
|
|
} else {
|
|
m.NormalUSD += row.UsdTutar
|
|
}
|
|
if absUsd > 0 {
|
|
m.WeightedBase += absUsd
|
|
m.WeightedGunSum += absUsd * row.GunSayisi
|
|
m.WeightedGunDocSum += absUsd * row.GunSayisiDocDate
|
|
if row.CurrencyUsdRate > 0 {
|
|
m.KurWeightedBase += absUsd
|
|
m.KurWeightedSum += absUsd * row.CurrencyUsdRate
|
|
}
|
|
}
|
|
if row.CurrencyUsdRate > 0 {
|
|
m.KurFallback = row.CurrencyUsdRate
|
|
}
|
|
|
|
c.SatirSayisi++
|
|
c.ToplamTutar += row.EslesenTutar
|
|
c.ToplamUSD += row.UsdTutar
|
|
if aciklama == "ACIKKALEM" {
|
|
c.AcikKalemTutar += row.EslesenTutar
|
|
} else {
|
|
c.NormalTutar += row.EslesenTutar
|
|
}
|
|
if absUsd > 0 {
|
|
c.WeightedBase += absUsd
|
|
c.WeightedGunSum += absUsd * row.GunSayisi
|
|
c.WeightedGunDocSum += absUsd * row.GunSayisiDocDate
|
|
if row.CurrencyUsdRate > 0 {
|
|
c.KurWeightedBase += absUsd
|
|
c.KurWeightedSum += absUsd * row.CurrencyUsdRate
|
|
}
|
|
}
|
|
if row.CurrencyUsdRate > 0 {
|
|
c.KurFallback = row.CurrencyUsdRate
|
|
}
|
|
|
|
detailsByCurrency[currencyKey] = append(detailsByCurrency[currencyKey], row)
|
|
}
|
|
|
|
masterKeys := make([]string, 0, len(masterMap))
|
|
for k := range masterMap {
|
|
masterKeys = append(masterKeys, k)
|
|
}
|
|
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])
|
|
}
|
|
|
|
for _, c := range currencyMap {
|
|
currenciesByMaster[c.MasterKey] = append(currenciesByMaster[c.MasterKey], *c)
|
|
}
|
|
for mk := range currenciesByMaster {
|
|
sort.SliceStable(currenciesByMaster[mk], func(i, j int) bool {
|
|
return strings.ToUpper(currenciesByMaster[mk][i].DocCurrencyCode) < strings.ToUpper(currenciesByMaster[mk][j].DocCurrencyCode)
|
|
})
|
|
}
|
|
|
|
for k := range detailsByCurrency {
|
|
sort.SliceStable(detailsByCurrency[k], func(i, j int) bool {
|
|
a := detailsByCurrency[k][i]
|
|
b := detailsByCurrency[k][j]
|
|
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) {
|
|
return strings.TrimSpace(a.OdemeRef) < strings.TrimSpace(b.OdemeRef)
|
|
}
|
|
return strings.TrimSpace(a.FaturaRef) < strings.TrimSpace(b.FaturaRef)
|
|
}
|
|
return strings.TrimSpace(a.OdemeCari) < strings.TrimSpace(b.OdemeCari)
|
|
}
|
|
return strings.TrimSpace(a.FaturaCari) < strings.TrimSpace(b.FaturaCari)
|
|
})
|
|
}
|
|
|
|
return masters, currenciesByMaster, detailsByCurrency
|
|
}
|
|
|
|
func pickString(m map[string]interface{}, keys ...string) string {
|
|
for _, k := range keys {
|
|
if v, ok := m[k]; ok {
|
|
return strings.TrimSpace(toStringValue(v))
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func pickFloat(m map[string]interface{}, keys ...string) float64 {
|
|
for _, k := range keys {
|
|
if v, ok := m[k]; ok {
|
|
return toFloat64Value(v)
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func toStringValue(v interface{}) string {
|
|
switch x := v.(type) {
|
|
case nil:
|
|
return ""
|
|
case string:
|
|
return x
|
|
case []byte:
|
|
return string(x)
|
|
default:
|
|
return fmt.Sprint(x)
|
|
}
|
|
}
|
|
|
|
func toFloat64Value(v interface{}) float64 {
|
|
switch x := v.(type) {
|
|
case nil:
|
|
return 0
|
|
case float64:
|
|
return x
|
|
case float32:
|
|
return float64(x)
|
|
case int:
|
|
return float64(x)
|
|
case int64:
|
|
return float64(x)
|
|
case int32:
|
|
return float64(x)
|
|
case string:
|
|
return parseFloatValue(x)
|
|
case []byte:
|
|
return parseFloatValue(string(x))
|
|
default:
|
|
return parseFloatValue(fmt.Sprint(x))
|
|
}
|
|
}
|
|
|
|
func parseFloatValue(s string) float64 {
|
|
s = strings.TrimSpace(s)
|
|
if s == "" {
|
|
return 0
|
|
}
|
|
|
|
hasComma := strings.Contains(s, ",")
|
|
hasDot := strings.Contains(s, ".")
|
|
if hasComma && hasDot {
|
|
if strings.LastIndex(s, ",") > strings.LastIndex(s, ".") {
|
|
s = strings.ReplaceAll(s, ".", "")
|
|
s = strings.Replace(s, ",", ".", 1)
|
|
} else {
|
|
s = strings.ReplaceAll(s, ",", "")
|
|
}
|
|
} else if hasComma {
|
|
s = strings.ReplaceAll(s, ".", "")
|
|
s = strings.Replace(s, ",", ".", 1)
|
|
}
|
|
|
|
n, err := strconv.ParseFloat(s, 64)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return n
|
|
}
|