Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user