diff --git a/svc/routes/order_pdf.go b/svc/routes/order_pdf.go
index 83da361..9550058 100644
--- a/svc/routes/order_pdf.go
+++ b/svc/routes/order_pdf.go
@@ -3,6 +3,7 @@ package routes
import (
"bytes"
"database/sql"
+ "errors"
"fmt"
"github.com/gorilla/mux"
"github.com/jung-kurt/gofpdf"
@@ -331,14 +332,14 @@ func contains(list []string, v string) bool {
2) PDF OLUŞTURUCU (A4 YATAY + FOOTER)
=========================================================== */
-func newOrderPdf() *gofpdf.Fpdf {
+func newOrderPdf() (*gofpdf.Fpdf, error) {
pdf := gofpdf.New("L", "mm", "A4", "")
pdf.SetMargins(10, 10, 10)
pdf.SetAutoPageBreak(false, 12)
- // UTF8 fontlar
- pdf.AddUTF8Font("dejavu", "", "fonts/DejaVuSans.ttf")
- pdf.AddUTF8Font("dejavu-b", "", "fonts/DejaVuSans-Bold.ttf")
+ if err := registerDejavuFonts(pdf, "dejavu", "dejavu-b"); err != nil {
+ return nil, err
+ }
// Footer: sayfa numarası
pdf.AliasNbPages("")
@@ -349,7 +350,7 @@ func newOrderPdf() *gofpdf.Fpdf {
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)
if err != nil {
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)
return
}
@@ -1367,7 +1372,12 @@ func OrderPDFHandler(db *sql.DB) http.Handler {
rows := normalizeOrderLinesForPdf(lines)
// 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)
var buf bytes.Buffer
diff --git a/ui/src/stores/orderentryStore.js b/ui/src/stores/orderentryStore.js
index d8cb8c9..8bf190f 100644
--- a/ui/src/stores/orderentryStore.js
+++ b/ui/src/stores/orderentryStore.js
@@ -268,7 +268,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
if (!Array.isArray(invalidList) || invalidList.length === 0) return
return new Promise(resolve => {
- $q.dialog({
+ const dlg = $q.dialog({
title: '🚨 Tanımsız Ürün Kombinasyonları',
message: `
@@ -309,16 +309,18 @@ export const useOrderEntryStore = defineStore('orderentry', {
})
.onOk(() => resolve())
.onDismiss(() => resolve())
- .onShown(() => {
- // Satıra tıklama → scroll + highlight
- const nodes = document.querySelectorAll('.invalid-row')
- nodes.forEach(n => {
- n.addEventListener('click', () => {
- const ck = n.getAttribute('data-clientkey')
- this.scrollToInvalidRow?.(ck)
- })
+
+ // Quasar v2 chain API'de onShown yok; dialog DOM'u render olduktan sonra bağla.
+ setTimeout(() => {
+ if (!dlg) return
+ const nodes = document.querySelectorAll('.invalid-row')
+ nodes.forEach(n => {
+ n.addEventListener('click', () => {
+ const ck = n.getAttribute('data-clientkey')
+ this.scrollToInvalidRow?.(ck)
})
})
+ }, 0)
})
}
,
@@ -415,7 +417,22 @@ export const useOrderEntryStore = defineStore('orderentry', {
} catch (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)
const merged = { ...(prevMap || {}) }
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)
}
@@ -2231,11 +2250,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
merged[modelKey] ??= []
- const beden = (
- raw.ItemDim1Code == null || String(raw.ItemDim1Code).trim() === ''
- ? ' '
- : String(raw.ItemDim1Code).trim().toUpperCase()
- )
+ const bedenRaw =
+ raw.ItemDim1Code == null
+ ? ''
+ : String(raw.ItemDim1Code).trim()
+ const beden = bedenRaw === '' ? ' ' : normalizeBeden(bedenRaw)
const qty = Number(raw.Qty1 || raw.Qty || 0)
@@ -2310,8 +2329,25 @@ export const useOrderEntryStore = defineStore('orderentry', {
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.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
@@ -2725,7 +2761,19 @@ export const useOrderEntryStore = defineStore('orderentry', {
======================================================= */
if (choice === 'print') {
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
}
@@ -3055,6 +3103,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* 🔹 BEDENSİZ / AKSBİR */
if (!hasAnyBeden) {
+ const allowBlankPayload =
+ grpKey === 'aksbir' || row._deleteSignal === true
+ if (!allowBlankPayload) {
+ continue
+ }
+
const qty = toNum(row.qty ?? row.Qty1 ?? row.miktar ?? 0)
// ✅ ComboKey stabil: bedenKey = '_'
@@ -3085,6 +3139,15 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* 🔹 BEDENLİ */
for (const [bedenRaw, qtyRaw] of Object.entries(map)) {
+ const isBlankBeden = safeStr(bedenRaw) === ''
+ if (
+ isBlankBeden &&
+ grpKey !== 'aksbir' &&
+ row._deleteSignal !== true
+ ) {
+ continue
+ }
+
const qty = toNum(qtyRaw)
// ✅ payload beden: '' / 'S' / 'M' ...
@@ -3295,7 +3358,7 @@ export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = ''
if (kat.includes('GARSON') || kat.includes('ÇOCUK')) return 'yas'
// 🔸 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'
@@ -3319,7 +3382,7 @@ export function toSummaryRowFromForm(form) {
const lbl =
rawLbl == null || String(rawLbl).trim() === ''
? ' '
- : String(rawLbl).trim()
+ : normalizeBeden(String(rawLbl))
const val = Number(values[i] || 0)
if (val > 0) {