Compare commits

...

2 Commits

Author SHA1 Message Date
MEHMETKECECI
6d22f5874a Merge remote-tracking branch 'origin/master' 2026-02-14 16:44:36 +03:00
MEHMETKECECI
6105be3eb3 Merge remote-tracking branch 'origin/master' 2026-02-14 16:44:28 +03:00
4 changed files with 111 additions and 52 deletions

View File

@@ -3,6 +3,7 @@ package routes
import ( import (
"bytes" "bytes"
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/jung-kurt/gofpdf" "github.com/jung-kurt/gofpdf"
@@ -331,14 +332,14 @@ func contains(list []string, v string) bool {
2) PDF OLUŞTURUCU (A4 YATAY + FOOTER) 2) PDF OLUŞTURUCU (A4 YATAY + FOOTER)
=========================================================== */ =========================================================== */
func newOrderPdf() *gofpdf.Fpdf { func newOrderPdf() (*gofpdf.Fpdf, error) {
pdf := gofpdf.New("L", "mm", "A4", "") pdf := gofpdf.New("L", "mm", "A4", "")
pdf.SetMargins(10, 10, 10) pdf.SetMargins(10, 10, 10)
pdf.SetAutoPageBreak(false, 12) pdf.SetAutoPageBreak(false, 12)
// UTF8 fontlar if err := registerDejavuFonts(pdf, "dejavu", "dejavu-b"); err != nil {
pdf.AddUTF8Font("dejavu", "", "fonts/DejaVuSans.ttf") return nil, err
pdf.AddUTF8Font("dejavu-b", "", "fonts/DejaVuSans-Bold.ttf") }
// Footer: sayfa numarası // Footer: sayfa numarası
pdf.AliasNbPages("") pdf.AliasNbPages("")
@@ -349,7 +350,7 @@ func newOrderPdf() *gofpdf.Fpdf {
pdf.CellFormat(0, 10, txt, "", 0, "R", false, 0, "") pdf.CellFormat(0, 10, txt, "", 0, "R", false, 0, "")
}) })
return pdf return pdf, nil
} }
/* =========================================================== /* ===========================================================
@@ -1340,6 +1341,10 @@ func OrderPDFHandler(db *sql.DB) http.Handler {
header, err := getOrderHeaderFromDB(db, orderID) header, err := getOrderHeaderFromDB(db, orderID)
if err != nil { if err != nil {
log.Println("header error:", err) log.Println("header error:", err)
if errors.Is(err, sql.ErrNoRows) {
http.Error(w, "order not found", http.StatusNotFound)
return
}
http.Error(w, "header not found", http.StatusInternalServerError) http.Error(w, "header not found", http.StatusInternalServerError)
return return
} }
@@ -1367,7 +1372,12 @@ func OrderPDFHandler(db *sql.DB) http.Handler {
rows := normalizeOrderLinesForPdf(lines) rows := normalizeOrderLinesForPdf(lines)
// PDF // PDF
pdf := newOrderPdf() pdf, err := newOrderPdf()
if err != nil {
log.Println("pdf init error:", err)
http.Error(w, "pdf init error: "+err.Error(), http.StatusInternalServerError)
return
}
renderOrderGrid(pdf, header, rows, hasVat, vatRate) renderOrderGrid(pdf, header, rows, hasVat, vatRate)
var buf bytes.Buffer var buf bytes.Buffer

View File

@@ -9,7 +9,6 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
@@ -54,8 +53,6 @@ var hMainWbase = []float64{
const ( const (
hFontFamilyReg = "dejavu" hFontFamilyReg = "dejavu"
hFontFamilyBold = "dejavu-b" hFontFamilyBold = "dejavu-b"
hFontPathReg = "fonts/DejaVuSans.ttf"
hFontPathBold = "fonts/DejaVuSans-Bold.ttf"
) )
// Renkler // Renkler
@@ -66,13 +63,8 @@ var (
/* ============================ FONT / FORMAT ============================ */ /* ============================ FONT / FORMAT ============================ */
func hEnsureFonts(pdf *gofpdf.Fpdf) { func hEnsureFonts(pdf *gofpdf.Fpdf) error {
if _, err := os.Stat(hFontPathReg); err == nil { return registerDejavuFonts(pdf, hFontFamilyReg, hFontFamilyBold)
pdf.AddUTF8Font(hFontFamilyReg, "", hFontPathReg)
}
if _, err := os.Stat(hFontPathBold); err == nil {
pdf.AddUTF8Font(hFontFamilyBold, "", hFontPathBold)
}
} }
func hNormalizeWidths(base []float64, targetTotal float64) []float64 { func hNormalizeWidths(base []float64, targetTotal float64) []float64 {
@@ -332,7 +324,10 @@ func ExportStatementHeaderReportPDFHandler(mssql *sql.DB) http.HandlerFunc {
pdf := gofpdf.New("L", "mm", "A4", "") pdf := gofpdf.New("L", "mm", "A4", "")
pdf.SetMargins(hMarginL, hMarginT, hMarginR) pdf.SetMargins(hMarginL, hMarginT, hMarginR)
pdf.SetAutoPageBreak(false, hMarginB) pdf.SetAutoPageBreak(false, hMarginB)
hEnsureFonts(pdf) if err := hEnsureFonts(pdf); err != nil {
http.Error(w, "PDF font yükleme hatası: "+err.Error(), http.StatusInternalServerError)
return
}
wAvail := hPageWidth - hMarginL - hMarginR wAvail := hPageWidth - hMarginL - hMarginR
mainWn := hNormalizeWidths(hMainWbase, wAvail) mainWn := hNormalizeWidths(hMainWbase, wAvail)

View File

@@ -10,7 +10,6 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
@@ -80,8 +79,6 @@ var dWbase = []float64{
const ( const (
fontFamilyReg = "dejavu" fontFamilyReg = "dejavu"
fontFamilyBold = "dejavu-b" fontFamilyBold = "dejavu-b"
fontPathReg = "fonts/DejaVuSans.ttf"
fontPathBold = "fonts/DejaVuSans-Bold.ttf"
) )
// Kurumsal renkler // Kurumsal renkler
@@ -134,17 +131,8 @@ func formatCurrencyTR(n float64) string {
} }
// Fontları yükle // Fontları yükle
func ensureFonts(pdf *gofpdf.Fpdf) { func ensureFonts(pdf *gofpdf.Fpdf) error {
if _, err := os.Stat(fontPathReg); err == nil { return registerDejavuFonts(pdf, fontFamilyReg, fontFamilyBold)
pdf.AddUTF8Font(fontFamilyReg, "", fontPathReg)
} else {
log.Printf("⚠️ Font bulunamadı: %s", fontPathReg)
}
if _, err := os.Stat(fontPathBold); err == nil {
pdf.AddUTF8Font(fontFamilyBold, "", fontPathBold)
} else {
log.Printf("⚠️ Font bulunamadı: %s", fontPathBold)
}
} }
// Güvenli satır kırma // Güvenli satır kırma
@@ -513,7 +501,10 @@ func ExportPDFHandler(mssql *sql.DB) http.HandlerFunc {
pdf := gofpdf.New("L", "mm", "A4", "") pdf := gofpdf.New("L", "mm", "A4", "")
pdf.SetMargins(marginL, marginT, marginR) pdf.SetMargins(marginL, marginT, marginR)
pdf.SetAutoPageBreak(false, marginB) pdf.SetAutoPageBreak(false, marginB)
ensureFonts(pdf) if err := ensureFonts(pdf); err != nil {
http.Error(w, "PDF font yükleme hatası: "+err.Error(), http.StatusInternalServerError)
return
}
pdf.SetFont(fontFamilyReg, "", 8.5) pdf.SetFont(fontFamilyReg, "", 8.5)
pdf.SetTextColor(0, 0, 0) pdf.SetTextColor(0, 0, 0)

View File

@@ -268,7 +268,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
if (!Array.isArray(invalidList) || invalidList.length === 0) return if (!Array.isArray(invalidList) || invalidList.length === 0) return
return new Promise(resolve => { return new Promise(resolve => {
$q.dialog({ const dlg = $q.dialog({
title: '🚨 Tanımsız Ürün Kombinasyonları', title: '🚨 Tanımsız Ürün Kombinasyonları',
message: ` message: `
<div style="max-height:60vh;overflow:auto"> <div style="max-height:60vh;overflow:auto">
@@ -309,16 +309,18 @@ export const useOrderEntryStore = defineStore('orderentry', {
}) })
.onOk(() => resolve()) .onOk(() => resolve())
.onDismiss(() => resolve()) .onDismiss(() => resolve())
.onShown(() => {
// Satıra tıklama → scroll + highlight // Quasar v2 chain API'de onShown yok; dialog DOM'u render olduktan sonra bağla.
const nodes = document.querySelectorAll('.invalid-row') setTimeout(() => {
nodes.forEach(n => { if (!dlg) return
n.addEventListener('click', () => { const nodes = document.querySelectorAll('.invalid-row')
const ck = n.getAttribute('data-clientkey') nodes.forEach(n => {
this.scrollToInvalidRow?.(ck) n.addEventListener('click', () => {
}) const ck = n.getAttribute('data-clientkey')
this.scrollToInvalidRow?.(ck)
}) })
}) })
}, 0)
}) })
} }
, ,
@@ -415,7 +417,22 @@ export const useOrderEntryStore = defineStore('orderentry', {
} catch (err) { } catch (err) {
console.error('❌ PDF açma hatası:', err) console.error('❌ PDF açma hatası:', err)
throw err let detail =
err?.response?.data?.detail ||
err?.response?.data?.message ||
err?.message ||
'PDF açılamadı'
// responseType=blob olduğunda backend metni Blob içine gelir.
const blob = err?.response?.data
if (blob instanceof Blob) {
try {
const text = (await blob.text())?.trim()
if (text) detail = text
} catch (_) {}
}
throw new Error(detail)
} }
} }
@@ -1817,7 +1834,9 @@ export const useOrderEntryStore = defineStore('orderentry', {
// MERGE: bedenleri topluyoruz (override değil) // MERGE: bedenleri topluyoruz (override değil)
const merged = { ...(prevMap || {}) } const merged = { ...(prevMap || {}) }
for (const [k, v] of Object.entries(newMap || {})) { for (const [k, v] of Object.entries(newMap || {})) {
const beden = (k == null || String(k).trim() === '') ? ' ' : String(k).trim() const beden = (k == null || String(k).trim() === '')
? ' '
: normalizeBeden(String(k))
merged[beden] = Number(merged[beden] || 0) + Number(v || 0) merged[beden] = Number(merged[beden] || 0) + Number(v || 0)
} }
@@ -2231,11 +2250,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
merged[modelKey] ??= [] merged[modelKey] ??= []
const beden = ( const bedenRaw =
raw.ItemDim1Code == null || String(raw.ItemDim1Code).trim() === '' raw.ItemDim1Code == null
? ' ' ? ''
: String(raw.ItemDim1Code).trim().toUpperCase() : String(raw.ItemDim1Code).trim()
) const beden = bedenRaw === '' ? ' ' : normalizeBeden(bedenRaw)
const qty = Number(raw.Qty1 || raw.Qty || 0) const qty = Number(raw.Qty1 || raw.Qty || 0)
@@ -2310,8 +2329,25 @@ export const useOrderEntryStore = defineStore('orderentry', {
row.kategori row.kategori
) )
const cleanedMap = { ...row.__tmpMap }
const hasNonBlankBeden = Object.keys(cleanedMap)
.some(k => String(k).trim() !== '')
// Gomlek/takim/pantolon gibi gruplarda bos beden sadece legacy kirli veri olabilir.
// Gecerli bedenler varsa bos bedeni payload/UI'dan temizliyoruz.
if (grpKey !== 'aksbir' && hasNonBlankBeden) {
delete cleanedMap[' ']
delete cleanedMap['']
if (row.lineIdMap && typeof row.lineIdMap === 'object') {
delete row.lineIdMap[' ']
delete row.lineIdMap['']
}
}
row.grpKey = grpKey row.grpKey = grpKey
row.bedenMap = { [grpKey]: { ...row.__tmpMap } } row.bedenMap = { [grpKey]: { ...cleanedMap } }
row.adet = Object.values(cleanedMap).reduce((a, b) => a + (Number(b) || 0), 0)
row.tutar = Number((row.adet * Number(row.fiyat || 0)).toFixed(2))
/* =================================================== /* ===================================================
🔒 AKSBİR — BOŞLUK BEDEN GERÇEK ADETİ ALIR 🔒 AKSBİR — BOŞLUK BEDEN GERÇEK ADETİ ALIR
@@ -2725,7 +2761,19 @@ export const useOrderEntryStore = defineStore('orderentry', {
======================================================= */ ======================================================= */
if (choice === 'print') { if (choice === 'print') {
const id = this.header?.OrderHeaderID || serverOrderId const id = this.header?.OrderHeaderID || serverOrderId
if (id) await this.downloadOrderPdf(id) if (id) {
try {
await this.downloadOrderPdf(id)
} catch (pdfErr) {
console.error('⚠️ PDF açılamadı, kayıt başarılı:', pdfErr)
$q.notify({
type: 'warning',
message:
pdfErr?.message ||
'Sipariş kaydedildi fakat PDF açılamadı.'
})
}
}
return return
} }
@@ -3055,6 +3103,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* 🔹 BEDENSİZ / AKSBİR */ /* 🔹 BEDENSİZ / AKSBİR */
if (!hasAnyBeden) { if (!hasAnyBeden) {
const allowBlankPayload =
grpKey === 'aksbir' || row._deleteSignal === true
if (!allowBlankPayload) {
continue
}
const qty = toNum(row.qty ?? row.Qty1 ?? row.miktar ?? 0) const qty = toNum(row.qty ?? row.Qty1 ?? row.miktar ?? 0)
// ✅ ComboKey stabil: bedenKey = '_' // ✅ ComboKey stabil: bedenKey = '_'
@@ -3085,6 +3139,15 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* 🔹 BEDENLİ */ /* 🔹 BEDENLİ */
for (const [bedenRaw, qtyRaw] of Object.entries(map)) { for (const [bedenRaw, qtyRaw] of Object.entries(map)) {
const isBlankBeden = safeStr(bedenRaw) === ''
if (
isBlankBeden &&
grpKey !== 'aksbir' &&
row._deleteSignal !== true
) {
continue
}
const qty = toNum(qtyRaw) const qty = toNum(qtyRaw)
// ✅ payload beden: '' / 'S' / 'M' ... // ✅ payload beden: '' / 'S' / 'M' ...
@@ -3295,7 +3358,7 @@ export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = ''
if (kat.includes('GARSON') || kat.includes('ÇOCUK')) return 'yas' if (kat.includes('GARSON') || kat.includes('ÇOCUK')) return 'yas'
// 🔸 Harfli beden varsa doğrudan "gom" (gömlek, üst giyim) // 🔸 Harfli beden varsa doğrudan "gom" (gömlek, üst giyim)
const harfliBedenler = ['XS','S','M','L','XL','XXL','3XL','4XL'] const harfliBedenler = ['XS','S','M','L','XL','XXL','2XL','3XL','4XL','5XL','6XL','7XL']
if (list.some(b => harfliBedenler.includes(b))) return 'gom' if (list.some(b => harfliBedenler.includes(b))) return 'gom'
@@ -3319,7 +3382,7 @@ export function toSummaryRowFromForm(form) {
const lbl = const lbl =
rawLbl == null || String(rawLbl).trim() === '' rawLbl == null || String(rawLbl).trim() === ''
? ' ' ? ' '
: String(rawLbl).trim() : normalizeBeden(String(rawLbl))
const val = Number(values[i] || 0) const val = Number(values[i] || 0)
if (val > 0) { if (val > 0) {