Merge remote-tracking branch 'origin/master'

This commit is contained in:
2026-02-14 19:57:41 +03:00
parent d0f20674ea
commit a2a756870d
4 changed files with 501 additions and 488 deletions

View File

@@ -37,6 +37,17 @@ let isLoggingOut = false
api.interceptors.response.use( api.interceptors.response.use(
r => r, r => r,
async (error) => { async (error) => {
const status = error?.response?.status
const hasBlob = typeof Blob !== 'undefined' && error?.response?.data instanceof Blob
if ((status >= 500 || hasBlob) && error) {
const method = String(error?.config?.method || 'GET').toUpperCase()
const url = error?.config?.url || ''
const detail = await extractApiErrorDetail(error)
error.parsedMessage = detail
console.error(`❌ API ${status || '-'} ${method} ${url}: ${detail}`)
}
if (error?.response?.status === 401 && !isLoggingOut) { if (error?.response?.status === 401 && !isLoggingOut) {
isLoggingOut = true isLoggingOut = true
try { try {
@@ -98,23 +109,31 @@ async function parseBlobErrorMessage(data) {
return '' return ''
} }
export async function extractApiErrorDetail(err) {
let detail =
err?.parsedMessage ||
err?.response?.data?.detail ||
err?.response?.data?.message ||
err?.response?.data?.error ||
''
if (!detail) {
detail = await parseBlobErrorMessage(err?.response?.data)
}
if (!detail) {
detail = err?.message || 'Request failed'
}
return detail
}
export const download = async (u, p = {}, c = {}) => { export const download = async (u, p = {}, c = {}) => {
try { try {
const r = await api.get(u, { params: p, responseType: 'blob', ...c }) const r = await api.get(u, { params: p, responseType: 'blob', ...c })
return r.data return r.data
} catch (err) { } catch (err) {
let detail = const detail = await extractApiErrorDetail(err)
err?.response?.data?.detail ||
err?.response?.data?.message ||
''
if (!detail) {
detail = await parseBlobErrorMessage(err?.response?.data)
}
if (!detail) {
detail = err?.message || 'Download failed'
}
const wrapped = new Error(detail) const wrapped = new Error(detail)
wrapped.status = err?.response?.status wrapped.status = err?.response?.status

View File

@@ -1,10 +1,10 @@
// src/stores/downloadstHeadStore.js // src/stores/downloadstHeadStore.js
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { download } from 'src/services/api' import { download, extractApiErrorDetail } from 'src/services/api'
export const useDownloadstHeadStore = defineStore('downloadstHead', { export const useDownloadstHeadStore = defineStore('downloadstHead', {
actions: { actions: {
// 📄 Statement Header PDF indir / aç // 📄 Statement Header PDF indir / aç
async handlestHeadDownload ( async handlestHeadDownload (
accountCode, accountCode,
startDate, startDate,
@@ -12,7 +12,7 @@ export const useDownloadstHeadStore = defineStore('downloadstHead', {
parislemler parislemler
) { ) {
try { try {
// Params (axios paramsSerializer array=repeat destekliyor) // ✅ Params (axios paramsSerializer array=repeat destekliyor)
const params = { const params = {
accountcode: accountCode, accountcode: accountCode,
startdate: startDate, startdate: startDate,
@@ -25,7 +25,7 @@ export const useDownloadstHeadStore = defineStore('downloadstHead', {
) )
} }
// 🔥 API CALL (TOKEN + BLOB + ERROR HANDLING OTOMATİK) // 🔥 API CALL (TOKEN + BLOB + ERROR HANDLING OTOMATİK)
const blob = await download( const blob = await download(
'/exportstamentheaderreport-pdf', '/exportstamentheaderreport-pdf',
params params
@@ -34,15 +34,17 @@ export const useDownloadstHeadStore = defineStore('downloadstHead', {
const pdfUrl = window.URL.createObjectURL(blob) const pdfUrl = window.URL.createObjectURL(blob)
window.open(pdfUrl, '_blank') window.open(pdfUrl, '_blank')
return { ok: true, message: '📄 PDF hazırlandı' } return { ok: true, message: '📄 PDF hazırlandı' }
} catch (err) { } catch (err) {
console.error('❌ PDF açma hatası:', err) const detail = await extractApiErrorDetail(err)
const status = err?.status || err?.response?.status || '-'
console.error(`❌ PDF açma hatası [${status}] /exportstamentheaderreport-pdf: ${detail}`)
return { return {
ok: false, ok: false,
message: message:
err?.message || detail ||
'PDF açma hatası' 'PDF açma hatası'
} }
} }
} }

View File

@@ -1,15 +1,15 @@
// src/stores/downloadstpdfStore.js // src/stores/downloadstpdfStore.js
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { download } from 'src/services/api' import { download, extractApiErrorDetail } from 'src/services/api'
export const useDownloadstpdfStore = defineStore('downloadstpdf', { export const useDownloadstpdfStore = defineStore('downloadstpdf', {
actions: { actions: {
/* ========================================================== /* ==========================================================
📄 PDF İNDİR / AÇ ğŸ“„ PDF İNDİR / AÇ
========================================================== */ ========================================================== */
async downloadPDF(accountCode, startDate, endDate, parislemler = []) { async downloadPDF(accountCode, startDate, endDate, parislemler = []) {
try { try {
// 🔹 Query params // 🔹 Query params
const params = { const params = {
accountcode: accountCode, accountcode: accountCode,
startdate: startDate, startdate: startDate,
@@ -22,28 +22,30 @@ export const useDownloadstpdfStore = defineStore('downloadstpdf', {
) )
} }
// 🔥 MERKEZİ API BLOB // 🔥 MERKEZİ API — BLOB
const blob = await download('/export-pdf', params) const blob = await download('/export-pdf', params)
// 🔹 Blob URL // 🔹 Blob → URL
const pdfUrl = window.URL.createObjectURL( const pdfUrl = window.URL.createObjectURL(
new Blob([blob], { type: 'application/pdf' }) new Blob([blob], { type: 'application/pdf' })
) )
// 🔹 Yeni sekmede aç // 🔹 Yeni sekmede aç
window.open(pdfUrl, '_blank') window.open(pdfUrl, '_blank')
console.log(' PDF yeni sekmede açıldı') console.log('✅ PDF yeni sekmede açıldı')
return { ok: true, message: '📄 PDF hazırlandı' } return { ok: true, message: '📄 PDF hazırlandı' }
} catch (err) { } catch (err) {
console.error('❌ PDF açma hatası:', err) const detail = await extractApiErrorDetail(err)
const status = err?.status || err?.response?.status || '-'
console.error(`❌ PDF açma hatası [${status}] /export-pdf: ${detail}`)
return { return {
ok: false, ok: false,
message: message:
err?.message || detail ||
'PDF alınamadı' 'PDF alınamadı'
} }
} }
} }

View File

@@ -1,25 +1,25 @@
/* =========================================================== /* ===========================================================
📦 orderentryStore.js (v3.4 CLEAN AUTH + LOCAL PERSIST + AUTO RESUME) 📦 orderentryStore.js (v3.4 CLEAN — AUTH + LOCAL PERSIST + AUTO RESUME)
=========================================================== */ =========================================================== */
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import api from 'src/services/api' import api, { extractApiErrorDetail } from 'src/services/api'
import dayjs from 'src/boot/dayjs' import dayjs from 'src/boot/dayjs'
import { ref, toRaw, nextTick } from 'vue' // ✅ düzeltildi import { ref, toRaw, nextTick } from 'vue' // ✅ düzeltildi
import { useAuthStore } from 'src/stores/authStore' import { useAuthStore } from 'src/stores/authStore'
// =========================================================== // ===========================================================
// 🔹 Shared Reactive Referanslar (Global, Reaktif Nesneler) // 🔹 Shared Reactive Referanslar (Global, Reaktif Nesneler)
// =========================================================== // ===========================================================
/* =========================================================== /* ===========================================================
🔹 BEDEN ŞEMALARI STORE SOURCE OF TRUTH 🔹 BEDEN ŞEMALARI — STORE SOURCE OF TRUTH
=========================================================== */ =========================================================== */
// ⬆️ orderentryStore.js EN ÜSTÜNE // ⬆️ orderentryStore.js EN ÜSTÜNE
// =========================================================== // ===========================================================
// 🔑 COMBO KEY CONTRACT (Frontend Backend) v1 // 🔑 COMBO KEY CONTRACT (Frontend ↔ Backend) — v1
// - trim + UPPER // - trim + UPPER
// - dim1 boşsa " " // - dim1 boÅŸsa " "
// - dim2 boşsa "" // - dim2 boÅŸsa ""
// =========================================================== // ===========================================================
const BEDEN_EMPTY = '_' const BEDEN_EMPTY = '_'
@@ -33,7 +33,7 @@ export function buildComboKey(row, beden) {
const bdn = normUpper(beden) const bdn = normUpper(beden)
const bedenFinal = bdn === '' ? BEDEN_EMPTY : bdn const bedenFinal = bdn === '' ? BEDEN_EMPTY : bdn
// 🔒 KANONİK SIRA // 🔒 KANONİK SIRA
return `${model}||${renk}||${renk2}||${bedenFinal}` return `${model}||${renk}||${renk2}||${bedenFinal}`
} }
@@ -44,10 +44,10 @@ export function buildComboKey(row, beden) {
export const BEDEN_SCHEMA = [ export const BEDEN_SCHEMA = [
{ key: 'ayk', title: 'AYAKKABI', values: ['39','40','41','42','43','44','45'] }, { key: 'ayk', title: 'AYAKKABI', values: ['39','40','41','42','43','44','45'] },
{ key: 'yas', title: 'YAŞ', values: ['2','4','6','8','10','12','14'] }, { key: 'yas', title: 'YAS', values: ['2','4','6','8','10','12','14'] },
{ key: 'pan', title: 'PANTOLON', values: ['38','40','42','44','46','48','50','52','54','56','58','60','62','64','66','68'] }, { key: 'pan', title: 'PANTOLON', values: ['38','40','42','44','46','48','50','52','54','56','58','60','62','64','66','68'] },
{ key: 'gom', title: 'GÖMLEK', values: ['XS','S','M','L','XL','2XL','3XL','4XL','5XL','6XL','7XL'] }, { key: 'gom', title: 'GOMLEK', values: ['XS','S','M','L','XL','2XL','3XL','4XL','5XL','6XL','7XL'] },
{ key: 'tak', title: 'TAKIM ELBİSE', values: ['44','46','48','50','52','54','56','58','60','62','64','66','68','70','72','74'] }, { key: 'tak', title: 'TAKIM ELBISE', values: ['44','46','48','50','52','54','56','58','60','62','64','66','68','70','72','74'] },
{ key: 'aksbir', title: 'AKSESUAR', values: [' ', '44', 'STD', '110CM', '115CM', '120CM', '125CM', '130CM', '135CM'] } { key: 'aksbir', title: 'AKSESUAR', values: [' ', '44', 'STD', '110CM', '115CM', '120CM', '125CM', '130CM', '135CM'] }
] ]
@@ -61,10 +61,10 @@ export const stockMap = ref({})
export const bedenStock = ref([]) export const bedenStock = ref([])
export const sizeCache = ref({}) export const sizeCache = ref({})
// =========================================================== // ===========================================================
// 🔹 Shared Reactive Referanslar (Global, Reaktif Nesneler) // 🔹 Shared Reactive Referanslar (Global, Reaktif Nesneler)
// =========================================================== // ===========================================================
// ======================== // ========================
// 🧰 GLOBAL DATE NORMALIZER // 🧰 GLOBAL DATE NORMALIZER
// ======================== // ========================
function newGuid() { function newGuid() {
@@ -73,7 +73,7 @@ function newGuid() {
// 🔑 Her beden satırıin deterministik clientKey üretimi // 🔑 Her beden satırı için deterministik clientKey üretimi
function makeLineClientKey(row, grpKey, beden) { function makeLineClientKey(row, grpKey, beden) {
const base = const base =
row.clientRowKey || row.clientRowKey ||
@@ -89,7 +89,7 @@ function makeLineClientKey(row, grpKey, beden) {
// =========================================================== // ===========================================================
// 🧩 Pinia Store ORDER ENTRY STORE (REV 2025-11-03.2) // 🧩 Pinia Store — ORDER ENTRY STORE (REV 2025-11-03.2)
// =========================================================== // ===========================================================
export const useOrderEntryStore = defineStore('orderentry', { export const useOrderEntryStore = defineStore('orderentry', {
state: () => ({ state: () => ({
@@ -145,12 +145,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
getters: { getters: {
getDraftKey() { getDraftKey() {
// NEW taslak GLOBAL ama tekil // NEW taslak → GLOBAL ama tekil
return 'bss_orderentry_new_draft' return 'bss_orderentry_new_draft'
}, },
getEditKey() { getEditKey() {
// EDIT OrderHeaderIDye bağlı // EDIT → OrderHeaderID’ye baÄŸlı
const id = this.header?.OrderHeaderID const id = this.header?.OrderHeaderID
return id ? `bss_orderentry_edit:${id}` : null return id ? `bss_orderentry_edit:${id}` : null
} }
@@ -201,7 +201,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
/* =========================================================== /* ===========================================================
🧩 initSchemaMap BEDEN ŞEMA İNİT 🧩 initSchemaMap — BEDEN ŞEMA İNİT
- TEK SOURCE OF TRUTH: BEDEN_SCHEMA - TEK SOURCE OF TRUTH: BEDEN_SCHEMA
=========================================================== */ =========================================================== */
initSchemaMap() { initSchemaMap() {
@@ -222,7 +222,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.schemaMap = map this.schemaMap = map
console.log( console.log(
'🧩 schemaMap INIT edildi:', '🧩 schemaMap INIT edildi:',
Object.keys(this.schemaMap) Object.keys(this.schemaMap)
) )
}, },
@@ -244,32 +244,32 @@ export const useOrderEntryStore = defineStore('orderentry', {
0 0
) )
// Header sadece GÖSTERİM / BACKEND için // Header sadece GÖSTERİM / BACKEND için
if (this.header) { if (this.header) {
this.header.TotalAmount = Number(total.toFixed(2)) this.header.TotalAmount = Number(total.toFixed(2))
} }
return total return total
} catch (err) { } catch (err) {
console.error(' updateHeaderTotals hata:', err) console.error('❌ updateHeaderTotals hata:', err)
return 0 return 0
} }
} }
, ,
/* =========================================================== /* ===========================================================
🚨 showInvalidVariantDialog FINAL 🚨 showInvalidVariantDialog — FINAL
----------------------------------------------------------- -----------------------------------------------------------
prItemVariant olmayan satırları listeler ✔ prItemVariant olmayan satırları listeler
Satıra tıkla scroll + highlight ✔ Satıra tıkla → scroll + highlight
Kaydı BLOKLAYAN tek UI noktası ✔ Kaydı BLOKLAYAN tek UI noktası
=========================================================== */ =========================================================== */
async showInvalidVariantDialog($q, invalidList = []) { async showInvalidVariantDialog($q, invalidList = []) {
if (!Array.isArray(invalidList) || invalidList.length === 0) return if (!Array.isArray(invalidList) || invalidList.length === 0) return
return new Promise(resolve => { return new Promise(resolve => {
const dlg = $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">
${invalidList.map((v, i) => ` ${invalidList.map((v, i) => `
@@ -288,12 +288,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
#${i + 1} | Item: ${v.itemCode} #${i + 1} | Item: ${v.itemCode}
</div> </div>
<div style="font-size:13px"> <div style="font-size:13px">
Beden: ${v.dim1 || '(boş)'} | Beden: ${v.dim1 || '(boÅŸ)'} |
Renk: ${v.colorCode || '-'} | Renk: ${v.colorCode || '-'} |
Qty: ${v.qty1} Qty: ${v.qty1}
</div> </div>
<div style="font-size:12px;color:#c10015"> <div style="font-size:12px;color:#c10015">
Sebep: ${v.reason || 'Tanımsız ürün kombinasyonu'} Sebep: ${v.reason || 'Tanımsız ürün kombinasyonu'}
</div> </div>
</div> </div>
`).join('')} `).join('')}
@@ -301,7 +301,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
`, `,
html: true, html: true,
ok: { ok: {
label: 'Düzelt', label: 'Düzelt',
color: 'negative' color: 'negative'
}, },
cancel: false, cancel: false,
@@ -310,7 +310,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
.onOk(() => resolve()) .onOk(() => resolve())
.onDismiss(() => resolve()) .onDismiss(() => resolve())
// Quasar v2 chain API'de onShown yok; dialog DOM'u render olduktan sonra bağla. // Quasar v2 chain API'de onShown yok; dialog DOM'u render olduktan sonra baÄŸla.
setTimeout(() => { setTimeout(() => {
if (!dlg) return if (!dlg) return
const nodes = document.querySelectorAll('.invalid-row') const nodes = document.querySelectorAll('.invalid-row')
@@ -325,42 +325,42 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
, ,
/* =========================================================== /* ===========================================================
🎯 scrollToInvalidRow FINAL 🎯 scrollToInvalidRow — FINAL
----------------------------------------------------------- -----------------------------------------------------------
ClientKey bazlı scroll ✔ ClientKey bazlı scroll
Hem summaryRows hem orders destekli ✔ Hem summaryRows hem orders destekli
Highlight otomatik kalkar ✔ Highlight otomatik kalkar
=========================================================== */ =========================================================== */
scrollToInvalidRow(clientKey) { scrollToInvalidRow(clientKey) {
if (!clientKey) return if (!clientKey) return
// 1️⃣ Store içindeki satırı bul // 1️⃣ Store içindeki satırı bul
const idx = this.summaryRows?.findIndex( const idx = this.summaryRows?.findIndex(
r => r.clientKey === clientKey r => r.clientKey === clientKey
) )
if (idx === -1) { if (idx === -1) {
console.warn(' Satır bulunamadı:', clientKey) console.warn('❌ Satır bulunamadı:', clientKey)
return return
} }
// 2️⃣ DOM node // 2️⃣ DOM node
const el = document.querySelector( const el = document.querySelector(
`[data-clientkey="${clientKey}"]` `[data-clientkey="${clientKey}"]`
) )
if (!el) { if (!el) {
console.warn(' DOM satırı bulunamadı:', clientKey) console.warn('❌ DOM satırı bulunamadı:', clientKey)
return return
} }
// 3️⃣ Scroll // 3️⃣ Scroll
el.scrollIntoView({ el.scrollIntoView({
behavior: 'smooth', behavior: 'smooth',
block: 'center' block: 'center'
}) })
// 4️⃣ Highlight // 4️⃣ Highlight
el.classList.add('invalid-highlight') el.classList.add('invalid-highlight')
setTimeout(() => { setTimeout(() => {
@@ -375,10 +375,10 @@ export const useOrderEntryStore = defineStore('orderentry', {
const res = await api.get(`/orders/check/${orderHeaderID}`) const res = await api.get(`/orders/check/${orderHeaderID}`)
// Backend true/false” döner varsayımı // Backend “true/false” döner varsayımı
return res?.data?.exists === true return res?.data?.exists === true
} catch (err) { } catch (err) {
console.warn(" checkHeaderExists hata:", err) console.warn("âš  checkHeaderExists hata:", err)
return false return false
} }
} }
@@ -391,8 +391,10 @@ export const useOrderEntryStore = defineStore('orderentry', {
}) })
return resp.data return resp.data
} catch (err) { } catch (err) {
console.error("❌ fetchOrderPdf hata:", err) const detail = await extractApiErrorDetail(err)
throw err const status = err?.status || err?.response?.status || '-'
console.error(`❌ fetchOrderPdf hata [${status}] order=${orderId}: ${detail}`)
throw new Error(detail)
} }
} }
, ,
@@ -401,7 +403,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
try { try {
const orderId = id || this.header?.OrderHeaderID const orderId = id || this.header?.OrderHeaderID
if (!orderId) { if (!orderId) {
console.error(' PDF ID bulunamadı') console.error('❌ PDF ID bulunamadı')
return return
} }
@@ -416,22 +418,10 @@ export const useOrderEntryStore = defineStore('orderentry', {
setTimeout(() => URL.revokeObjectURL(url), 60_000) setTimeout(() => URL.revokeObjectURL(url), 60_000)
} catch (err) { } catch (err) {
console.error('❌ PDF açma hatası:', err) const detail = await extractApiErrorDetail(err)
let detail = const orderId = id || this.header?.OrderHeaderID || '-'
err?.response?.data?.detail || const status = err?.status || err?.response?.status || '-'
err?.response?.data?.message || console.error(`❌ PDF açma hatası [${status}] order=${orderId}: ${detail}`)
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) throw new Error(detail)
} }
} }
@@ -452,14 +442,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
}, },
/* =========================================================== /* ===========================================================
🧩 initFromRoute (v5.6 groupedRows TOUCH YOK) 🧩 initFromRoute (v5.6 — groupedRows TOUCH YOK)
----------------------------------------------------------- -----------------------------------------------------------
- Route ID ve bss_last_txn arasında en dolu snapshot'ı seçer - Route ID ve bss_last_txn arasında en dolu snapshot'ı seçer
- header + orders + summaryRows restore edilir - header + orders + summaryRows restore edilir
- groupedRows hydrate edilmez / resetlenmez / dokunulmaz - groupedRows hydrate edilmez / resetlenmez / dokunulmaz
- Route ID farklıysa router.replace ile URL düzeltilir - Route ID farklıysa router.replace ile URL düzeltilir
=========================================================== */ =========================================================== */
async initFromRoute(orderId, router = null) { // NEW MODE SADECE global draft async initFromRoute(orderId, router = null) { // ✅ NEW MODE → SADECE global draft
if (this.mode === 'new') { if (this.mode === 'new') {
const raw = localStorage.getItem(this.getDraftKey) const raw = localStorage.getItem(this.getDraftKey)
if (raw) { if (raw) {
@@ -468,11 +458,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.header = payload.header || {} this.header = payload.header || {}
this.orders = payload.orders || [] this.orders = payload.orders || []
this.summaryRows = payload.summaryRows || this.orders this.summaryRows = payload.summaryRows || this.orders
console.log('♻️ NEW draft restore edildi (global)') console.log('♻️ NEW draft restore edildi (global)')
return return
} catch {} } catch {}
} }
console.log(' NEW draft yok, boş başlatılıyor') console.log('⚪ NEW draft yok, boÅŸ baÅŸlatılıyor')
return return
} }
if (!this.schemaMap || !Object.keys(this.schemaMap).length) { if (!this.schemaMap || !Object.keys(this.schemaMap).length) {
@@ -480,7 +470,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
try { try {
console.log('🧩 [initFromRoute] orderId:', orderId) console.log('🧩 [initFromRoute] orderId:', orderId)
const lastTxn = localStorage.getItem('bss_last_txn') || null const lastTxn = localStorage.getItem('bss_last_txn') || null
@@ -510,18 +500,18 @@ export const useOrderEntryStore = defineStore('orderentry', {
if (hasData(fromRoute)) { if (hasData(fromRoute)) {
chosenId = orderId chosenId = orderId
chosenPayload = fromRoute chosenPayload = fromRoute
console.log(' [initFromRoute] Route ID snapshot seçildi:', chosenId) console.log('✅ [initFromRoute] Route ID snapshot seçildi:', chosenId)
} else if (hasData(fromLast)) { } else if (hasData(fromLast)) {
chosenId = lastTxn chosenId = lastTxn
chosenPayload = fromLast chosenPayload = fromLast
console.log(' [initFromRoute] lastTxn snapshot seçildi:', chosenId) console.log('✅ [initFromRoute] lastTxn snapshot seçildi:', chosenId)
} }
/* ------------------------------------------------------- /* -------------------------------------------------------
🚫 SNAPSHOT YOK BOŞ BAŞLA 🚫 SNAPSHOT YOK → BOŞ BAŞLA
-------------------------------------------------------- */ -------------------------------------------------------- */
if (!chosenId || !chosenPayload) { if (!chosenId || !chosenPayload) {
console.log(' [initFromRoute] Snapshot yok, boş başlatılıyor') console.log('⚪ [initFromRoute] Snapshot yok, boÅŸ baÅŸlatılıyor')
this.header = { this.header = {
...(this.header || {}), ...(this.header || {}),
@@ -531,12 +521,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.orders = [] this.orders = []
this.summaryRows = [] this.summaryRows = []
// groupedRows'a DOKUNMA // ❗ groupedRows'a DOKUNMA
return return
} }
/* ------------------------------------------------------- /* -------------------------------------------------------
SNAPSHOT RESTORE (SAFE CLONE) ✅ SNAPSHOT RESTORE (SAFE CLONE)
-------------------------------------------------------- */ -------------------------------------------------------- */
this.header = { this.header = {
...(chosenPayload.header || {}), ...(chosenPayload.header || {}),
@@ -554,22 +544,22 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.orders = orders this.orders = orders
this.summaryRows = summaryRows this.summaryRows = summaryRows
// groupedRows hydrate edilmez, resetlenmez // ❗ groupedRows hydrate edilmez, resetlenmez
/* ------------------------------------------------------- /* -------------------------------------------------------
🔁 lastTxn SENKRON 🔁 lastTxn SENKRON
-------------------------------------------------------- */ -------------------------------------------------------- */
try { try {
localStorage.setItem('bss_last_txn', chosenId) localStorage.setItem('bss_last_txn', chosenId)
} catch (e) { } catch (e) {
console.warn('⚠️ bss_last_txn yazılamadı:', e) console.warn('⚠️ bss_last_txn yazılamadı:', e)
} }
/* ------------------------------------------------------- /* -------------------------------------------------------
🔁 ROUTE DÜZELTME (GEREKİRSE) 🔁 ROUTE DÜZELTME (GEREKİRSE)
-------------------------------------------------------- */ -------------------------------------------------------- */
if (router && orderId && orderId !== chosenId) { if (router && orderId && orderId !== chosenId) {
console.log('🔁 [initFromRoute] Route ID düzeltiliyor ', chosenId) console.log('🔁 [initFromRoute] Route ID düzeltiliyor →', chosenId)
await router.replace({ await router.replace({
name: 'order-entry', name: 'order-entry',
params: { orderHeaderID: chosenId } params: { orderHeaderID: chosenId }
@@ -577,19 +567,19 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
console.log( console.log(
' [initFromRoute] Restore tamam. Satır sayısı:', '✅ [initFromRoute] Restore tamam. Satır sayısı:',
this.summaryRows.length this.summaryRows.length
) )
} catch (err) { } catch (err) {
console.error(' [initFromRoute] hata:', err) console.error('❌ [initFromRoute] hata:', err)
} }
} }
, ,
/* =========================================================== /* ===========================================================
🆕 startNewOrder (v8.3 FINAL & STABLE) 🆕 startNewOrder (v8.3 — FINAL & STABLE)
=========================================================== */ =========================================================== */
async startNewOrder({ $q }) { async startNewOrder({ $q }) {
@@ -607,7 +597,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
orderNumber = res.data.OrderNumber orderNumber = res.data.OrderNumber
} }
} catch { } catch {
console.info(' Backend order number yok, LOCAL kullanıldı') console.info('ℹ️ Backend order number yok, LOCAL kullanıldı')
} }
this.mode = 'new' this.mode = 'new'
@@ -627,10 +617,10 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.orders = [] this.orders = []
this.summaryRows = [] this.summaryRows = []
// fingerprint bazlı sistem için reset // ✅ fingerprint bazlı sistem için reset
this._lastSavedFingerprint = null this._lastSavedFingerprint = null
// NEW draft hemen yazılır // ✅ NEW draft hemen yazılır
this.persistLocalStorage?.() this.persistLocalStorage?.()
return this.header return this.header
@@ -657,7 +647,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
const ex = map.get(key) const ex = map.get(key)
ex.Qty1 = (Number(ex.Qty1) || 0) + (Number(ln.Qty1) || 0) ex.Qty1 = (Number(ex.Qty1) || 0) + (Number(ln.Qty1) || 0)
// OrderLineID boşsa doldur (editte önemli) // OrderLineID boÅŸsa doldur (editte önemli)
if (!ex.OrderLineID && ln.OrderLineID) ex.OrderLineID = ln.OrderLineID if (!ex.OrderLineID && ln.OrderLineID) ex.OrderLineID = ln.OrderLineID
} }
return Array.from(map.values()) return Array.from(map.values())
@@ -666,7 +656,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
/* =========================================================== /* ===========================================================
🧹 Core reset helper sadece state'i sıfırlar 🧹 Core reset helper — sadece state'i sıfırlar
=========================================================== */ =========================================================== */
resetCoreState() { resetCoreState() {
this.orders = [] this.orders = []
@@ -676,14 +666,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.editingKey = null this.editingKey = null
this.currentOrderId = null this.currentOrderId = null
},resetForNewOrder() { },resetForNewOrder() {
// mevcut her şeyi temizle // mevcut her ÅŸeyi temizle
this.header = { this.header = {
OrderHeaderID: this.header?.OrderHeaderID || null, OrderHeaderID: this.header?.OrderHeaderID || null,
OrderDate: new Date().toISOString().slice(0,10), OrderDate: new Date().toISOString().slice(0,10),
CurrAccCode: null, CurrAccCode: null,
DocCurrencyCode: 'TRY', DocCurrencyCode: 'TRY',
PriceCurrencyCode: 'TRY', PriceCurrencyCode: 'TRY',
// ihtiyaç duyduğun diğer default header alanları // ihtiyaç duyduÄŸun diÄŸer default header alanları
} }
this.orders = [] this.orders = []
@@ -695,7 +685,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
, ,
resetForEdit() { resetForEdit() {
// EDIT modda grid temizlenmez sadece UI state resetlenir // EDIT modda grid temizlenmez — sadece UI state resetlenir
this.editingKey = null this.editingKey = null
this.groupedRows = [] this.groupedRows = []
this.mode = 'edit' this.mode = 'edit'
@@ -704,9 +694,9 @@ export const useOrderEntryStore = defineStore('orderentry', {
,markAsSaved() { ,markAsSaved() {
try { try {
this._lastSavedFingerprint = this._persistFingerprint() this._lastSavedFingerprint = this._persistFingerprint()
console.log(' markAsSaved fingerprint senkron') console.log('✅ markAsSaved → fingerprint senkron')
} catch (e) { } catch (e) {
console.warn('⚠️ markAsSaved hata:', e) console.warn('⚠️ markAsSaved hata:', e)
} }
} }
,clearLocalSnapshot() { ,clearLocalSnapshot() {
@@ -714,12 +704,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
const id = this.header?.OrderHeaderID const id = this.header?.OrderHeaderID
if (!id) return if (!id) return
localStorage.removeItem(`bss_orderentry_data:${id}`) localStorage.removeItem(`bss_orderentry_data:${id}`)
console.log('🧹 Local snapshot temizlendi:', id) console.log('🧹 Local snapshot temizlendi:', id)
} catch (e) { } catch (e) {
console.warn('⚠️ clearLocalSnapshot hata:', e) console.warn('⚠️ clearLocalSnapshot hata:', e)
} }
},/* =========================================================== },/* ===========================================================
🧹 HARD CLEAN ALL ORDERENTRY SNAPSHOTS 🧹 HARD CLEAN — ALL ORDERENTRY SNAPSHOTS
=========================================================== */ =========================================================== */
clearAllOrderSnapshots () { clearAllOrderSnapshots () {
Object.keys(localStorage) Object.keys(localStorage)
@@ -728,7 +718,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
k.startsWith('bss_orderentry_edit:') k.startsWith('bss_orderentry_edit:')
) )
.forEach(k => { .forEach(k => {
console.log('🧹 snapshot silindi:', k) console.log('🧹 snapshot silindi:', k)
localStorage.removeItem(k) localStorage.removeItem(k)
}) })
@@ -740,18 +730,18 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
/* =========================================================== /* ===========================================================
🧹 Store Hard Reset Submit Sonrası Temizlik (FIXED) 🧹 Store Hard Reset — Submit Sonrası Temizlik (FIXED)
- Grid, header, toplamlar, local state'ler sıfırlanır - Grid, header, toplamlar, local state'ler sıfırlanır
- persistKey / lastSnapshotKey NULL yapılmaz (config sabit kalır) - persistKey / lastSnapshotKey NULL yapılmaz (config sabit kalır)
- localStorage txn/snapshot temizliği güvenli yapılır - localStorage txn/snapshot temizliÄŸi güvenli yapılır
=========================================================== */ =========================================================== */
hardResetAfterSubmit() { hardResetAfterSubmit() {
try { try {
// 🔑 mevcut idyi yakala (local temizliği için) // 🔑 mevcut id’yi yakala (local temizliÄŸi için)
const id = this.header?.OrderHeaderID || null const id = this.header?.OrderHeaderID || null
/* ------------------------------------------------------- /* -------------------------------------------------------
1) Grid ve satırlar 1) Grid ve satırlar
-------------------------------------------------------- */ -------------------------------------------------------- */
this.orders = [] this.orders = []
this.summaryRows = [] this.summaryRows = []
@@ -771,12 +761,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* ------------------------------------------------------- /* -------------------------------------------------------
4) Snapshot / transaction meta 4) Snapshot / transaction meta
⚠️ persistKey / lastSnapshotKey store config NULL YAPMA ⚠️ persistKey / lastSnapshotKey store config → NULL YAPMA
-------------------------------------------------------- */ -------------------------------------------------------- */
this.activeTransactionId = null this.activeTransactionId = null
this.submitted = false this.submitted = false
// fingerprint / debounce meta varsa sıfırla // fingerprint / debounce meta varsa sıfırla
this._lastSavedFingerprint = null this._lastSavedFingerprint = null
this._lastPersistFingerprint = null this._lastPersistFingerprint = null
if (this._persistTimeout) { if (this._persistTimeout) {
@@ -785,7 +775,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* ------------------------------------------------------- /* -------------------------------------------------------
5) LocalStorage temizlik (opsiyonel ama submit sonrası doğru) 5) LocalStorage temizlik (opsiyonel ama submit sonrası doÄŸru)
-------------------------------------------------------- */ -------------------------------------------------------- */
try { try {
if (id) { if (id) {
@@ -795,32 +785,32 @@ export const useOrderEntryStore = defineStore('orderentry', {
localStorage.removeItem('bss_last_txn') localStorage.removeItem('bss_last_txn')
localStorage.removeItem('bss_active_new_header') localStorage.removeItem('bss_active_new_header')
} catch (e) { } catch (e) {
console.warn('⚠️ hardResetAfterSubmit localStorage temizliği hata:', e) console.warn('⚠️ hardResetAfterSubmit localStorage temizliÄŸi hata:', e)
} }
console.log('🧹 Store resetlendi (submit sonrası).') console.log('🧹 Store resetlendi (submit sonrası).')
} catch (err) { } catch (err) {
console.error(' hardResetAfterSubmit hata:', err) console.error('❌ hardResetAfterSubmit hata:', err)
} }
} }
, ,
/* =========================================================== /* ===========================================================
✏️ openExistingForEdit (v12 FINAL & CLEAN) ✏️ openExistingForEdit (v12 — FINAL & CLEAN)
----------------------------------------------------------- -----------------------------------------------------------
Backend authoritative (orderlist açılışı local'i dikkate almaz) ✔ Backend authoritative (orderlist açılışı local'i dikkate almaz)
mode=new backend çağrısı YOK ✔ mode=new → backend çaÄŸrısı YOK
normalizeOrderLines grpKey + bedenMap garanti ✔ normalizeOrderLines → grpKey + bedenMap garanti
isClosed varsa view, yoksa edit ✔ isClosed varsa → view, yoksa → edit
Form sync opsiyonel ✔ Form sync opsiyonel
✔ İlk açılışta snapshot yazılır (edit boyunca persist ile güncellenir) ✔ İlk açılışta snapshot yazılır (edit boyunca persist ile güncellenir)
=========================================================== */ =========================================================== */
async openExistingForEdit( async openExistingForEdit(
orderId, orderId,
{ $q = null, form = null, productCache = null } = {} { $q = null, form = null, productCache = null } = {}
) { ) {
// 🔑 schemaMap garanti // 🔑 schemaMap garanti
if (!this.schemaMap || !Object.keys(this.schemaMap).length) { if (!this.schemaMap || !Object.keys(this.schemaMap).length) {
this.initSchemaMap?.() this.initSchemaMap?.()
} }
@@ -828,25 +818,25 @@ export const useOrderEntryStore = defineStore('orderentry', {
if (!orderId) return false if (!orderId) return false
/* ======================================================= /* =======================================================
🟦 NEW MODE ASLA backend çağrısı yok 🟦 NEW MODE — ASLA backend çaÄŸrısı yok
======================================================= */ ======================================================= */
if (this.mode === 'new') { if (this.mode === 'new') {
console.log(' openExistingForEdit skip (mode=new)') console.log('⚪ openExistingForEdit skip (mode=new)')
return false return false
} }
// productCache hem ref hem reactive olabilir → güvenli oku // productCache hem ref hem reactive olabilir → güvenli oku
const pc = const pc =
productCache?.value productCache?.value
? productCache.value ? productCache.value
: (productCache && typeof productCache === 'object' ? productCache : {}) : (productCache && typeof productCache === 'object' ? productCache : {})
try { try {
// geçici varsayım (sonra isClosed durumuna göre set edilecek) // geçici varsayım (sonra isClosed durumuna göre set edilecek)
this.setMode?.('edit') this.setMode?.('edit')
/* ======================================================= /* =======================================================
🔹 BACKEND authoritative load 🔹 BACKEND — authoritative load
======================================================= */ ======================================================= */
const res = await api.get(`/order/get/${orderId}`) const res = await api.get(`/order/get/${orderId}`)
const backend = res?.data const backend = res?.data
@@ -856,8 +846,8 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* ======================================================= /* =======================================================
🔹 HEADER SADECE BACKEND 🔹 HEADER — SADECE BACKEND
(orderlist açılışında local merge YOK) (orderlist açılışında local merge YOK)
======================================================= */ ======================================================= */
this.header = { this.header = {
...backend.header, ...backend.header,
@@ -865,11 +855,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* ======================================================= /* =======================================================
🔹 NORMALIZE LINES (TEK KAYNAK) 🔹 NORMALIZE LINES (TEK KAYNAK)
normalizeOrderLines şu alanları üretmeli: normalizeOrderLines ÅŸu alanları üretmeli:
row.grpKey ✔ row.grpKey
row.bedenMap[grpKey] ✔ row.bedenMap[grpKey]
row.isClosed (boolean) ✔ row.isClosed (boolean)
======================================================= */ ======================================================= */
const normalized = this.normalizeOrderLines( const normalized = this.normalizeOrderLines(
backend.lines || [], backend.lines || [],
@@ -881,31 +871,31 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.summaryRows = [...this.orders] this.summaryRows = [...this.orders]
/* ======================================================= /* =======================================================
🔹 MODE KARARI (BACKEND SATIRLARI ÜZERİNDEN) 🔹 MODE KARARI (BACKEND SATIRLARI ÜZERİNDEN)
- herhangi bir isClosed=true view - herhangi bir isClosed=true → view
- değilse edit - deÄŸilse → edit
======================================================= */ ======================================================= */
const hasClosedLine = (this.summaryRows || []).some(r => r?.isClosed === true) const hasClosedLine = (this.summaryRows || []).some(r => r?.isClosed === true)
this.setMode?.(hasClosedLine ? 'view' : 'edit') this.setMode?.(hasClosedLine ? 'view' : 'edit')
/* ======================================================= /* =======================================================
🔹 FORM SYNC (opsiyonel) 🔹 FORM SYNC (opsiyonel)
======================================================= */ ======================================================= */
if (form) { if (form) {
Object.assign(form, this.header) Object.assign(form, this.header)
} }
/* ======================================================= /* =======================================================
🔹 LOCAL SNAPSHOT (edit boyunca tutulacak temel) 🔹 LOCAL SNAPSHOT (edit boyunca tutulacak temel)
- Açılışta snapshot yaz - Açılışta snapshot yaz
- Sonraki değişikliklerde zaten persistLocalStorage çağrıları var - Sonraki deÄŸiÅŸikliklerde zaten persistLocalStorage çaÄŸrıları var
======================================================= */ ======================================================= */
this.persistLocalStorage?.() this.persistLocalStorage?.()
try { try {
localStorage.setItem('bss_last_txn', String(orderId)) localStorage.setItem('bss_last_txn', String(orderId))
} catch {} } catch {}
console.log(' openExistingForEdit OK:', { console.log('✅ openExistingForEdit OK:', {
id: orderId, id: orderId,
rows: this.summaryRows.length, rows: this.summaryRows.length,
mode: this.mode, mode: this.mode,
@@ -914,13 +904,13 @@ export const useOrderEntryStore = defineStore('orderentry', {
return true return true
} catch (err) { } catch (err) {
console.error(' openExistingForEdit hata:', err) console.error('❌ openExistingForEdit hata:', err)
// new değilse uyar // new deÄŸilse uyar
if (this.mode !== 'new') { if (this.mode !== 'new') {
$q?.notify?.({ $q?.notify?.({
type: 'negative', type: 'negative',
message: 'Sipariş yüklenemedi' message: 'SipariÅŸ yüklenemedi'
}) })
} }
@@ -930,12 +920,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
/* =========================================================== /* ===========================================================
♻️ hydrateFromLocalStorage (v5.5 FIXED & CLEAN) ♻️ hydrateFromLocalStorage (v5.5 — FIXED & CLEAN)
----------------------------------------------------------- -----------------------------------------------------------
- Tek assign (double overwrite YOK) - Tek assign (double overwrite YOK)
- groupedRows hydrate edilmez - groupedRows hydrate edilmez
- mode ASLA set edilmez - mode ASLA set edilmez
- header + rows güvenli restore - header + rows güvenli restore
=========================================================== */ =========================================================== */
async hydrateFromLocalStorage(orderId, log = false) {if (this.mode === 'new') { async hydrateFromLocalStorage(orderId, log = false) {if (this.mode === 'new') {
return this.hydrateFromLocalStorageIfExists() return this.hydrateFromLocalStorageIfExists()
@@ -946,15 +936,15 @@ export const useOrderEntryStore = defineStore('orderentry', {
const payload = JSON.parse(localStorage.getItem(key) || 'null') const payload = JSON.parse(localStorage.getItem(key) || 'null')
if (!payload) { if (!payload) {
log && console.log(' hydrate snapshot yok:', orderId) log && console.log('ℹ️ hydrate → snapshot yok:', orderId)
return null return null
} }
// 🔑 source bilgisi (mode set edilmez) // 🔑 source bilgisi (mode set edilmez)
this.source = payload.source || 'local' this.source = payload.source || 'local'
/* ------------------------------------------------------- /* -------------------------------------------------------
MSSQL tarih helperları MSSQL tarih helper’ları
-------------------------------------------------------- */ -------------------------------------------------------- */
const safeDateTime = v => { const safeDateTime = v => {
if (!v) return null if (!v) return null
@@ -1004,11 +994,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
? payload.summaryRows ? payload.summaryRows
: orders : orders
// groupedRows hydrate edilmez (computed olmalı) // ❌ groupedRows hydrate edilmez (computed olmalı)
this.groupedRows = [] this.groupedRows = []
/* ------------------------------------------------------- /* -------------------------------------------------------
SNAPSHOT ÖZET SNAPSHOT ÖZET
-------------------------------------------------------- */ -------------------------------------------------------- */
const output = { const output = {
type : payload.submitted === true ? 'submitted' : 'draft', type : payload.submitted === true ? 'submitted' : 'draft',
@@ -1021,11 +1011,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
payload.header?.IsSubmitted === true payload.header?.IsSubmitted === true
} }
log && console.log('♻️ hydrate sonuc (FIXED):', output) log && console.log('♻️ hydrate sonuc (FIXED):', output)
return output return output
} catch (err) { } catch (err) {
console.warn('⚠️ hydrateFromLocalStorage hata:', err) console.warn('⚠️ hydrateFromLocalStorage hata:', err)
return null return null
} }
} }
@@ -1037,11 +1027,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
let raw = null let raw = null
if (this.mode === 'new') { if (this.mode === 'new') {
raw = localStorage.getItem(this.getDraftKey) // raw = localStorage.getItem(this.getDraftKey) // ✅
} }
if (this.mode === 'edit') { if (this.mode === 'edit') {
const key = this.getEditKey // const key = this.getEditKey // ✅
if (key) raw = localStorage.getItem(key) if (key) raw = localStorage.getItem(key)
} }
@@ -1053,7 +1043,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.orders = payload.orders || [] this.orders = payload.orders || []
this.summaryRows = payload.summaryRows || this.orders this.summaryRows = payload.summaryRows || this.orders
console.log('♻️ hydrate OK:', this.mode) console.log('♻️ hydrate OK:', this.mode)
return true return true
} catch (err) { } catch (err) {
@@ -1065,8 +1055,8 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
/* =========================================================== /* ===========================================================
🔀 mergeOrders (local + backend) 🔀 mergeOrders (local + backend)
normalizeISO kaldırıldı normalizeISO → kaldırıldı
safe MSSQL helpers eklendi safe MSSQL helpers eklendi
=========================================================== */ =========================================================== */
mergeOrders(local, backend, preferLocal = true) { mergeOrders(local, backend, preferLocal = true) {
@@ -1097,12 +1087,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
const map = new Map() const map = new Map()
// Backend satırları // Backend satırları
for (const b of (backend?.lines || backend?.orders || [])) { for (const b of (backend?.lines || backend?.orders || [])) {
map.set(getKey(b), { ...b, _src: 'backend' }) map.set(getKey(b), { ...b, _src: 'backend' })
} }
// Local satırları merge et // Local satırları merge et
for (const l of (local?.orders || [])) { for (const l of (local?.orders || [])) {
const k = getKey(l) const k = getKey(l)
if (map.has(k)) { if (map.has(k)) {
@@ -1115,10 +1105,10 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
const mergedOrders = Array.from(map.values()) const mergedOrders = Array.from(map.values())
console.log(`🧩 mergeOrders ${mergedOrders.length} satır birleşti (ID:${header.OrderHeaderID})`) console.log(`🧩 mergeOrders → ${mergedOrders.length} satır birleÅŸti (ID:${header.OrderHeaderID})`)
// ==================================================== // ====================================================
// 🕒 HEADER TARİHLERİNİ MSSQL FORMATINA NORMALİZE ET // 🕒 HEADER TARİHLERİNİ MSSQL FORMATINA NORMALİZE ET
// ==================================================== // ====================================================
const safeDateTime = v => { const safeDateTime = v => {
if (!v) return null if (!v) return null
@@ -1151,14 +1141,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
markRowSource(row) { markRowSource(row) {
if (row._src === 'local-only') return '🟠 Offline' if (row._src === 'local-only') return '🟠 Offline'
if (row._src === 'local') return '🔵 Local' if (row._src === 'local') return '🔵 Local'
return ' Backend' return '⚪ Backend'
} }
, ,
/* =========================================================== /* ===========================================================
🔄 mergeAndPersistBackendOrder (edit mode) 🔄 mergeAndPersistBackendOrder (edit mode)
=========================================================== */ =========================================================== */
mergeAndPersistBackendOrder(orderId, backendPayload) { mergeAndPersistBackendOrder(orderId, backendPayload) {
const key = `bss_orderentry_data:${orderId}` const key = `bss_orderentry_data:${orderId}`
@@ -1173,7 +1163,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
updatedAt: new Date().toISOString() updatedAt: new Date().toISOString()
})) }))
console.log(`💾 mergeAndPersistBackendOrder ${orderId} localStoragea yazıldı`) console.log(`💾 mergeAndPersistBackendOrder → ${orderId} localStorage’a yazıldı`)
} }
, ,
@@ -1190,19 +1180,19 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* =============================== /* ===============================
🟢 NEW MODE GLOBAL TEK TASLAK 🟢 NEW MODE — GLOBAL TEK TASLAK
=============================== */ =============================== */
if (this.mode === 'new') { if (this.mode === 'new') {
localStorage.setItem(this.getDraftKey, JSON.stringify(payload)) localStorage.setItem(this.getDraftKey, JSON.stringify(payload))
// 🔒 sadece aktif new header bilgisi // 🔒 sadece aktif new header bilgisi
this.setActiveNewHeader?.(this.header?.OrderHeaderID) this.setActiveNewHeader?.(this.header?.OrderHeaderID)
return return
} }
/* =============================== /* ===============================
🔵 EDIT MODE ID BAZLI 🔵 EDIT MODE — ID BAZLI
=============================== */ =============================== */
if (this.mode === 'edit') { if (this.mode === 'edit') {
const key = this.getEditKey const key = this.getEditKey
@@ -1221,26 +1211,26 @@ export const useOrderEntryStore = defineStore('orderentry', {
clearEditSnapshotIfExists() { clearEditSnapshotIfExists() {
if (this.mode !== 'edit') return if (this.mode !== 'edit') return
const key = this.getEditKey // const key = this.getEditKey // ✅
if (!key) return if (!key) return
localStorage.removeItem(key) localStorage.removeItem(key)
console.log('🧹 EDIT snapshot silindi:', key) console.log('🧹 EDIT snapshot silindi:', key)
} }
,/* =========================================================== ,/* ===========================================================
🧠 _persistFingerprint kritik stateleri tek stringe indirger 🧠 _persistFingerprint — kritik state’leri tek stringe indirger
- X3: orders+header yetmez mode, summaryRows, id/no, mapler dahil - X3: orders+header yetmez → mode, summaryRows, id/no, map’ler dahil
=========================================================== */ =========================================================== */
_persistFingerprint() { _persistFingerprint() {
// 🔹 orders: çok büyürse pahalı olabilir ama snapshot tutarlılığıin önemli // 🔹 orders: çok büyürse pahalı olabilir ama snapshot tutarlılığı için önemli
// (istersen burada sadece length + rowKey listesi gibi optimize ederiz) // (istersen burada sadece length + rowKey listesi gibi optimize ederiz)
const ordersSnap = JSON.stringify(this.orders || []) const ordersSnap = JSON.stringify(this.orders || [])
// 🔹 header: sadece kritik alanları al (tam header yerine daha stabil) // 🔹 header: sadece kritik alanları al (tam header yerine daha stabil)
const h = this.header || {} const h = this.header || {}
const headerSnap = JSON.stringify({ const headerSnap = JSON.stringify({
OrderHeaderID: h.OrderHeaderID || '', OrderHeaderID: h.OrderHeaderID || '',
@@ -1250,14 +1240,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
ExchangeRate: h.ExchangeRate ?? null ExchangeRate: h.ExchangeRate ?? null
}) })
// 🔹 summaryRows: hash yerine şimdilik length + rowKey listesi (hafif + etkili) // 🔹 summaryRows: hash yerine ÅŸimdilik “length + rowKey listesi” (hafif + etkili)
const sr = Array.isArray(this.summaryRows) ? this.summaryRows : [] const sr = Array.isArray(this.summaryRows) ? this.summaryRows : []
const summaryMeta = JSON.stringify({ const summaryMeta = JSON.stringify({
len: sr.length, len: sr.length,
keys: sr.map(r => this.getRowKey?.(r) || r?.key || r?.id || '').filter(Boolean) keys: sr.map(r => this.getRowKey?.(r) || r?.key || r?.id || '').filter(Boolean)
}) })
// 🔹 comboLineIds / lineIdMap gibi kritik mapler // 🔹 comboLineIds / lineIdMap gibi kritik map’ler
// (sende hangisi varsa onu otomatik topluyoruz) // (sende hangisi varsa onu otomatik topluyoruz)
const mapSnap = JSON.stringify({ const mapSnap = JSON.stringify({
lineIdMap: this.lineIdMap || null, lineIdMap: this.lineIdMap || null,
@@ -1266,15 +1256,15 @@ export const useOrderEntryStore = defineStore('orderentry', {
comboLineIdSet: this.comboLineIdSet ? Array.from(this.comboLineIdSet) : null comboLineIdSet: this.comboLineIdSet ? Array.from(this.comboLineIdSet) : null
}) })
// 🔹 mode // 🔹 mode
const modeSnap = String(this.mode || 'new') const modeSnap = String(this.mode || 'new')
// Tek fingerprint // ✅ Tek fingerprint
return `${modeSnap}|${headerSnap}|${summaryMeta}|${mapSnap}|${ordersSnap}` return `${modeSnap}|${headerSnap}|${summaryMeta}|${mapSnap}|${ordersSnap}`
} }
, ,
/* =========================================================== /* ===========================================================
🕒 _safePersistDebounced snapshot değişmediği sürece yazmaz (X3) 🕒 _safePersistDebounced — snapshot deÄŸiÅŸmediÄŸi sürece yazmaz (X3)
- fingerprint: mode + header(id/no) + summaryRows meta + lineIdMap/combo + orders - fingerprint: mode + header(id/no) + summaryRows meta + lineIdMap/combo + orders
=========================================================== */ =========================================================== */
_safePersistDebounced(delay = 1200) { _safePersistDebounced(delay = 1200) {
@@ -1282,7 +1272,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
this._persistTimeout = setTimeout(() => { this._persistTimeout = setTimeout(() => {
try { try {
// Persist guardları (varsa) // ✅ Persist guard’ları (varsa)
if (this.preventPersist) return if (this.preventPersist) return
if (this._uiBusy) return if (this._uiBusy) return
@@ -1295,9 +1285,9 @@ export const useOrderEntryStore = defineStore('orderentry', {
this._lastPersistFingerprint = fp this._lastPersistFingerprint = fp
this.persistLocalStorage() this.persistLocalStorage()
console.log(`🕒 Otomatik LocalStorage senkron (${this.orders?.length || 0} satır).`) console.log(`🕒 Otomatik LocalStorage senkron (${this.orders?.length || 0} satır).`)
} catch (err) { } catch (err) {
console.warn('⚠️ Debounce persist hata:', err) console.warn('⚠️ Debounce persist hata:', err)
} }
}, delay) }, delay)
} }
@@ -1305,7 +1295,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
/* =========================================================== /* ===========================================================
💰 fetchMinPrice model/pb için min fiyat 💰 fetchMinPrice — model/pb için min fiyat
=========================================================== */ =========================================================== */
async fetchMinPrice(model, currency, $q) { async fetchMinPrice(model, currency, $q) {
try { try {
@@ -1313,17 +1303,17 @@ export const useOrderEntryStore = defineStore('orderentry', {
params: { model, currency } params: { model, currency }
}) })
const data = res?.data || {} const data = res?.data || {}
console.log('💰 [store.fetchMinPrice] yanıt:', data) console.log('💰 [store.fetchMinPrice] yanıt:', data)
return { return {
price: Number(data.price || 0), price: Number(data.price || 0),
rateToTRY: Number(data.rateToTRY || 1), rateToTRY: Number(data.rateToTRY || 1),
priceTRY: Number(data.priceTRY || 0) priceTRY: Number(data.priceTRY || 0)
} }
} catch (err) { } catch (err) {
console.error(' [store.fetchMinPrice] Min fiyat alınamadı:', err) console.error('❌ [store.fetchMinPrice] Min fiyat alınamadı:', err)
$q?.notify?.({ $q?.notify?.({
type: 'warning', type: 'warning',
message: 'Min. fiyat bilgisi alınamadı, kontrol atlandı ⚠️', message: 'Min. fiyat bilgisi alınamadı, kontrol atlandı ⚠️',
position: 'top-right' position: 'top-right'
}) })
return { price: 0, rateToTRY: 1, priceTRY: 0 } return { price: 0, rateToTRY: 1, priceTRY: 0 }
@@ -1333,13 +1323,13 @@ export const useOrderEntryStore = defineStore('orderentry', {
applyCurrencyToLines(newPB) { applyCurrencyToLines(newPB) {
if (!newPB) return if (!newPB) return
// 🔹 Header // 🔹 Header
if (this.header) { if (this.header) {
this.header.DocCurrencyCode = newPB this.header.DocCurrencyCode = newPB
this.header.PriceCurrencyCode = newPB this.header.PriceCurrencyCode = newPB
} }
// 🔹 Lines // 🔹 Lines
if (Array.isArray(this.orders)) { if (Array.isArray(this.orders)) {
this.orders = this.orders.map(r => ({ this.orders = this.orders.map(r => ({
...r, ...r,
@@ -1349,7 +1339,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
})) }))
} }
// 🔹 Summary // 🔹 Summary
if (Array.isArray(this.summaryRows)) { if (Array.isArray(this.summaryRows)) {
this.summaryRows = this.summaryRows.map(r => ({ this.summaryRows = this.summaryRows.map(r => ({
...r, ...r,
@@ -1359,14 +1349,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
})) }))
} }
// totalAmount SET ETME // ❗ totalAmount SET ETME
// ✔️ TEK MERKEZ // ✔️ TEK MERKEZ
this.updateHeaderTotals?.() this.updateHeaderTotals?.()
} }
, ,
/* =========================================================== /* ===========================================================
💠 HEADER SET & CURRENCY PROPAGATION 💠 HEADER SET & CURRENCY PROPAGATION
=========================================================== */ =========================================================== */
setHeaderFields(fields, opts = {}) { setHeaderFields(fields, opts = {}) {
const { const {
@@ -1374,13 +1364,13 @@ export const useOrderEntryStore = defineStore('orderentry', {
immediatePersist = false immediatePersist = false
} = opts } = opts
// 1️⃣ HEADER // 1️⃣ HEADER
this.header = { this.header = {
...(this.header || {}), ...(this.header || {}),
...fields ...fields
} }
// 2️⃣ SATIRLARA GERÇEKTEN YAY // 2️⃣ SATIRLARA GERÇEKTEN YAY
if (applyCurrencyToLines && Array.isArray(this.summaryRows)) { if (applyCurrencyToLines && Array.isArray(this.summaryRows)) {
this.summaryRows = this.summaryRows.map(r => ({ this.summaryRows = this.summaryRows.map(r => ({
...r, ...r,
@@ -1390,11 +1380,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
})) }))
} }
// 3️⃣ STORE ORDERS REFERANSI // 3️⃣ STORE ORDERS REFERANSI
this.orders = [...this.summaryRows] this.orders = [...this.summaryRows]
// 4️⃣ PERSIST // 4️⃣ PERSIST
if (immediatePersist) { if (immediatePersist) {
this.persistLocalStorage('header-change') this.persistLocalStorage('header-change')
} }
@@ -1418,12 +1408,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
cnt++ cnt++
} }
console.log(`💱 ${cnt} satırda PB güncellendi Doc:${doc} Price:${prc} Rate:${rate}`) console.log(`💱 ${cnt} satırda PB güncellendi → Doc:${doc} Price:${prc} Rate:${rate}`)
} }
,/* =========================================================== ,/* ===========================================================
📸 saveSnapshot — küçük debug snapshot 📸 saveSnapshot — küçük debug snapshot
=========================================================== */ =========================================================== */
saveSnapshot(tag = 'snapshot') { saveSnapshot(tag = 'snapshot') {
try { try {
@@ -1441,26 +1431,26 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
localStorage.setItem(key, JSON.stringify(snap)) localStorage.setItem(key, JSON.stringify(snap))
console.log(`📸 Snapshot kaydedildi [${key}]`) console.log(`📸 Snapshot kaydedildi [${key}]`)
} catch (err) { } catch (err) {
console.warn('⚠️ saveSnapshot hata:', err) console.warn('⚠️ saveSnapshot hata:', err)
} }
} }
, ,
/* =========================================================== /* ===========================================================
♻️ loadFromStorage eski generic persist için ♻️ loadFromStorage — eski generic persist için
=========================================================== */ =========================================================== */
loadFromStorage(force = false) { loadFromStorage(force = false) {
try { try {
const raw = localStorage.getItem(this.getPersistKey) const raw = localStorage.getItem(this.getPersistKey)
if (!raw) { if (!raw) {
console.info(' LocalStorage boş, grid başlatılmadı.') console.info('ℹ️ LocalStorage boÅŸ, grid baÅŸlatılmadı.')
return false return false
} }
if (!force && this.mode === 'edit') { if (!force && this.mode === 'edit') {
console.info('⚠️ Edit modda local restore atlandı (force=false).') console.info('⚠️ Edit modda local restore atlandı (force=false).')
return false return false
} }
@@ -1471,21 +1461,21 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.currentOrderId = data.currentOrderId || null this.currentOrderId = data.currentOrderId || null
this.selectedCustomer = data.selectedCustomer || null this.selectedCustomer = data.selectedCustomer || null
// 🔧 Temiz ID // 🔧 Temiz ID
this.header.OrderHeaderID = data.header?.OrderHeaderID || null this.header.OrderHeaderID = data.header?.OrderHeaderID || null
this.mode = data.mode || 'new' this.mode = data.mode || 'new'
this.lastSavedAt = data.savedAt || null this.lastSavedAt = data.savedAt || null
console.log(`♻️ Storage yüklendi txn:${this.header.OrderHeaderID} (${this.orders.length} satır)`) console.log(`♻️ Storage yüklendi • txn:${this.header.OrderHeaderID} (${this.orders.length} satır)`)
// Header PB -> satırlara // Header PB -> satırlara
this.applyHeaderCurrencyToOrders() this.applyHeaderCurrencyToOrders()
this._safePersistDebounced(200) this._safePersistDebounced(200)
return data return data
} catch (err) { } catch (err) {
console.warn('⚠️ localStorage okuma hatası:', err) console.warn('⚠️ localStorage okuma hatası:', err)
return false return false
} }
} }
@@ -1494,23 +1484,23 @@ export const useOrderEntryStore = defineStore('orderentry', {
clearStorage() { clearStorage() {
try { try {
localStorage.removeItem(this.getPersistKey) localStorage.removeItem(this.getPersistKey)
console.log(`🗑️ LocalStorage temizlendi [${this.getPersistKey}]`) console.log(`🗑️ LocalStorage temizlendi [${this.getPersistKey}]`)
} catch (err) { } catch (err) {
console.warn('⚠️ clearStorage hatası:', err) console.warn('⚠️ clearStorage hatası:', err)
} }
} }
, ,
clearNewDraft() { clearNewDraft() {
localStorage.removeItem(this.getDraftKey) // localStorage.removeItem(this.getDraftKey) // ✅
localStorage.removeItem('bss_last_txn') localStorage.removeItem('bss_last_txn')
console.log('🧹 NEW taslak temizlendi') console.log('🧹 NEW taslak temizlendi')
} }
, ,
// =========================================================== // ===========================================================
// 🔹 isSameCombo STORE LEVEL (TEK KAYNAK) // 🔹 isSameCombo — STORE LEVEL (TEK KAYNAK)
// - model ZORUNLU eşleşir // - model ZORUNLU eÅŸleÅŸir
// - renk / renk2 boşsa joker // - renk / renk2 boÅŸsa → joker
// =========================================================== // ===========================================================
isSameCombo(a, b) { isSameCombo(a, b) {
if (!a || !b) return false if (!a || !b) return false
@@ -1531,11 +1521,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
// =========================================================== // ===========================================================
// 🔹 saveOrUpdateRowUnified (v6.6 COMBO SAFE + FIXED STOCK+PRICE + UI) // 🔹 saveOrUpdateRowUnified (v6.6 — COMBO SAFE + FIXED STOCK+PRICE + UI)
// - v6.5 korunur (stok+min fiyat + this.loadProductSizes) // - v6.5 korunur (stok+min fiyat + this.loadProductSizes)
// - NEW MODE: dupIdx artık _deleteSignal satırlarını BAŞTAN hariç tutar // - ✅ NEW MODE: dupIdx artık _deleteSignal satırlarını BAŞTAN hariç tutar
// - EDIT MODE: sameCombo update, combo değişti → delete + insert (korundu) // - EDIT MODE: sameCombo → update, combo deÄŸiÅŸti → delete + insert (korundu)
// - lineIdMap koruması korunur // - lineIdMap koruması korunur
// =========================================================== // ===========================================================
async saveOrUpdateRowUnified({ async saveOrUpdateRowUnified({
form, form,
@@ -1546,7 +1536,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
$q = null $q = null
}) { }) {
try { try {
console.log('🔥 saveOrUpdateRowUnified v6.6', { console.log('🔥 saveOrUpdateRowUnified v6.6', {
model: form?.model, model: form?.model,
mode: this.mode, mode: this.mode,
editingKey: this.editingKey editingKey: this.editingKey
@@ -1562,10 +1552,10 @@ export const useOrderEntryStore = defineStore('orderentry', {
: [] : []
/* ======================================================= /* =======================================================
1️⃣ ZORUNLU KONTROLLER 1️⃣ ZORUNLU KONTROLLER
======================================================= */ ======================================================= */
if (!form?.model) { if (!form?.model) {
$q?.notify?.({ type: 'warning', message: 'Model seçiniz' }) $q?.notify?.({ type: 'warning', message: 'Model seçiniz' })
return false return false
} }
@@ -1574,28 +1564,28 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* ======================================================= /* =======================================================
2️⃣ STOK KONTROLÜ (FIXED) 2️⃣ STOK KONTROLÜ (FIXED)
- stok guarddan önce this.loadProductSizes(form,true,$q) - stok guard’dan önce this.loadProductSizes(form,true,$q)
- opsiyonel callback loadProductSizes(true) - opsiyonel callback loadProductSizes(true)
- tek dialog + doğru await - tek dialog + doÄŸru await
======================================================= */ ======================================================= */
// store fonksiyonu // ✅ store fonksiyonu
try { try {
if (typeof this.loadProductSizes === 'function') { if (typeof this.loadProductSizes === 'function') {
await this.loadProductSizes(form, true, $q) await this.loadProductSizes(form, true, $q)
} }
} catch (err) { } catch (err) {
console.warn(' this.loadProductSizes hata:', err) console.warn('âš  this.loadProductSizes hata:', err)
} }
// ✅ dışarıdan callback geldiyse // ✅ dışarıdan callback geldiyse
try { try {
if (typeof loadProductSizes === 'function') { if (typeof loadProductSizes === 'function') {
await loadProductSizes(true) await loadProductSizes(true)
} }
} catch (err) { } catch (err) {
console.warn(' loadProductSizes hata:', err) console.warn('âš  loadProductSizes hata:', err)
} }
const stockMapLocal = stockMap?.value || stockMap || {} const stockMapLocal = stockMap?.value || stockMap || {}
@@ -1615,16 +1605,16 @@ export const useOrderEntryStore = defineStore('orderentry', {
if (overLimit.length && $q) { if (overLimit.length && $q) {
const msg = overLimit const msg = overLimit
.map(x => ` <b>${x.beden}</b>: ${x.girilen} (Stok: ${x.stok})`) .map(x => `• <b>${x.beden}</b>: ${x.girilen} (Stok: ${x.stok})`)
.join('<br>') .join('<br>')
const stokOK = await new Promise(resolve => { const stokOK = await new Promise(resolve => {
$q.dialog({ $q.dialog({
title: 'Stok Uyarısı', title: 'Stok Uyarısı',
message: `Bazı bedenlerde stoktan fazla giriş yaptınız:<br><br>${msg}`, message: `Bazı bedenlerde stoktan fazla giriÅŸ yaptınız:<br><br>${msg}`,
html: true, html: true,
ok: { label: 'Devam', color: 'primary' }, ok: { label: 'Devam', color: 'primary' },
cancel: { label: 'İptal', color: 'negative' } cancel: { label: 'İptal', color: 'negative' }
}) })
.onOk(() => resolve(true)) .onOk(() => resolve(true))
.onCancel(() => resolve(false)) .onCancel(() => resolve(false))
@@ -1635,7 +1625,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* ======================================================= /* =======================================================
3️⃣ FİYAT (MIN) KONTROLÜ (FIXED) 3️⃣ FİYAT (MIN) KONTROLÜ (FIXED)
======================================================= */ ======================================================= */
let fiyatOK = true let fiyatOK = true
try { try {
@@ -1653,13 +1643,13 @@ export const useOrderEntryStore = defineStore('orderentry', {
if (minFiyat > 0 && girilen > 0 && girilen < minFiyat && $q) { if (minFiyat > 0 && girilen > 0 && girilen < minFiyat && $q) {
fiyatOK = await new Promise(resolve => { fiyatOK = await new Promise(resolve => {
$q.dialog({ $q.dialog({
title: 'Fiyat Uyarısı', title: 'Fiyat Uyarısı',
message: message:
`<b>Min. Fiyat:</b> ${minFiyat} ${form.pb}<br>` + `<b>Min. Fiyat:</b> ${minFiyat} ${form.pb}<br>` +
`<b>Girdiğiniz:</b> ${girilen} ${form.pb}`, `<b>GirdiÄŸiniz:</b> ${girilen} ${form.pb}`,
html: true, html: true,
ok: { label: 'Devam', color: 'primary' }, ok: { label: 'Devam', color: 'primary' },
cancel: { label: 'İptal', color: 'negative' } cancel: { label: 'İptal', color: 'negative' }
}) })
.onOk(() => resolve(true)) .onOk(() => resolve(true))
.onCancel(() => resolve(false)) .onCancel(() => resolve(false))
@@ -1667,12 +1657,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
}) })
} }
} catch (err) { } catch (err) {
console.warn(' Min fiyat hata:', err) console.warn('âš  Min fiyat hata:', err)
} }
if (!fiyatOK) return false if (!fiyatOK) return false
/* ======================================================= /* =======================================================
4️⃣ TOPLAM HESABI 4️⃣ TOPLAM HESABI
======================================================= */ ======================================================= */
const adet = (form.bedenler || []).reduce((a, b) => a + Number(b || 0), 0) const adet = (form.bedenler || []).reduce((a, b) => a + Number(b || 0), 0)
form.adet = adet form.adet = adet
@@ -1681,7 +1671,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
const newRow = toSummaryRowFromForm(form) const newRow = toSummaryRowFromForm(form)
/* ======================================================= /* =======================================================
5️⃣ EDIT MODE (editingKey ZORUNLU) 5️⃣ EDIT MODE (editingKey ZORUNLU)
======================================================= */ ======================================================= */
if (this.editingKey) { if (this.editingKey) {
const idx = rows.findIndex(r => getKey(r) === this.editingKey) const idx = rows.findIndex(r => getKey(r) === this.editingKey)
@@ -1694,13 +1684,13 @@ export const useOrderEntryStore = defineStore('orderentry', {
const prev = rows[idx] const prev = rows[idx]
if (this.isRowLocked?.(prev)) { if (this.isRowLocked?.(prev)) {
$q?.notify?.({ type: 'warning', message: 'Satır kapalı' }) $q?.notify?.({ type: 'warning', message: 'Satır kapalı' })
this.editingKey = null this.editingKey = null
resetEditor?.(true) resetEditor?.(true)
return false return false
} }
// kritik: store-level // ✅ kritik: store-level
const sameCombo = this.isSameCombo(prev, newRow) const sameCombo = this.isSameCombo(prev, newRow)
const preservedLineIdMap = const preservedLineIdMap =
@@ -1710,7 +1700,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
? { ...newRow.lineIdMap } ? { ...newRow.lineIdMap }
: {} : {}
/* ===== SAME COMBO UPDATE ===== */ /* ===== SAME COMBO → UPDATE ===== */
if (sameCombo) { if (sameCombo) {
rows[idx] = { rows[idx] = {
...prev, ...prev,
@@ -1730,11 +1720,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
resetEditor?.(true) resetEditor?.(true)
recalcVat?.() recalcVat?.()
$q?.notify?.({ type: 'positive', message: 'Satır güncellendi' }) $q?.notify?.({ type: 'positive', message: 'Satır güncellendi' })
return true return true
} }
/* ===== COMBO CHANGED DELETE + INSERT ===== */ /* ===== COMBO CHANGED → DELETE + INSERT ===== */
const grpKey = const grpKey =
prev?.grpKey || prev?.grpKey ||
Object.keys(prev?.bedenMap || {})[0] || Object.keys(prev?.bedenMap || {})[0] ||
@@ -1790,22 +1780,22 @@ export const useOrderEntryStore = defineStore('orderentry', {
resetEditor?.(true) resetEditor?.(true)
recalcVat?.() recalcVat?.()
$q?.notify?.({ type: 'positive', message: 'Kombinasyon değişti' }) $q?.notify?.({ type: 'positive', message: 'Kombinasyon deÄŸiÅŸti' })
return true return true
} }
/* ======================================================= /* =======================================================
6️⃣ NEW MODE (MERGE / INSERT) COMBO SAFE 6️⃣ NEW MODE (MERGE / INSERT) — COMBO SAFE
- aynı combo bedenMap merge (satır sayısı artmaz) - aynı combo → bedenMap merge (satır sayısı artmaz)
- farklı combo yeni satır - farklı combo → yeni satır
- FIX: _deleteSignal satırlarını dup aramasında hariç tut - ✅ FIX: _deleteSignal satırlarını dup aramasında hariç tut
======================================================= */ ======================================================= */
const dupIdx = rows.findIndex(r => const dupIdx = rows.findIndex(r =>
!r?._deleteSignal && !r?._deleteSignal &&
this.isSameCombo(r, newRow) this.isSameCombo(r, newRow)
) )
// helper: bedenMap çıkar (gruplu ya da düz) // helper: bedenMap çıkar (gruplu ya da düz)
const extractMap = (row) => { const extractMap = (row) => {
const grpKey = const grpKey =
row?.grpKey || row?.grpKey ||
@@ -1823,15 +1813,15 @@ export const useOrderEntryStore = defineStore('orderentry', {
if (dupIdx !== -1) { if (dupIdx !== -1) {
const prev = rows[dupIdx] const prev = rows[dupIdx]
// delete satırına merge yapma (ek güvenlik) // delete satırına merge yapma (ek güvenlik)
if (prev?._deleteSignal !== true) { if (prev?._deleteSignal !== true) {
const { grpKey: prevGrp, map: prevMap } = extractMap(prev) const { grpKey: prevGrp, map: prevMap } = extractMap(prev)
const { grpKey: newGrp, map: newMap } = extractMap(newRow) const { grpKey: newGrp, map: newMap } = extractMap(newRow)
// hangi grpKey kullanılacak? // hangi grpKey kullanılacak?
const grpKey = newRow?.grpKey || prevGrp || newGrp || 'GENEL' const grpKey = newRow?.grpKey || prevGrp || newGrp || 'GENEL'
// 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() === '') const beden = (k == null || String(k).trim() === '')
@@ -1873,12 +1863,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
resetEditor?.(true) resetEditor?.(true)
recalcVat?.() recalcVat?.()
$q?.notify?.({ type: 'positive', message: 'Aynı kombinasyon bulundu, bedenler birleştirildi' }) $q?.notify?.({ type: 'positive', message: 'Aynı kombinasyon bulundu, bedenler birleÅŸtirildi' })
return true return true
} }
} }
// dup yoksa (veya dup delete satırıydı) → yeni satır // dup yoksa (veya dup delete satırıydı) → yeni satır
rows.push({ rows.push({
...newRow, ...newRow,
id: newRow.id || crypto.randomUUID(), id: newRow.id || crypto.randomUUID(),
@@ -1894,12 +1884,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
resetEditor?.(true) resetEditor?.(true)
recalcVat?.() recalcVat?.()
$q?.notify?.({ type: 'positive', message: 'Yeni satır eklendi' }) $q?.notify?.({ type: 'positive', message: 'Yeni satır eklendi' })
return true return true
} catch (err) { } catch (err) {
console.error(' saveOrUpdateRowUnified:', err) console.error('❌ saveOrUpdateRowUnified:', err)
$q?.notify?.({ type: 'negative', message: 'Satır kaydı başarısız' }) $q?.notify?.({ type: 'negative', message: 'Satır kaydı baÅŸarısız' })
return false return false
} }
} }
@@ -1910,24 +1900,24 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
/* =========================================================== /* ===========================================================
🔄 setTransaction yeni transaction ID set et 🔄 setTransaction — yeni transaction ID set et
=========================================================== */ =========================================================== */
setTransaction(id, autoResume = true) { setTransaction(id, autoResume = true) {
if (!id) return if (!id) return
// 🔧 temiz ID // 🔧 temiz ID
this.header.OrderHeaderID = id this.header.OrderHeaderID = id
localStorage.setItem('bss_last_txn', id) localStorage.setItem('bss_last_txn', id)
console.log('🔄 Transaction değiştirildi:', id) console.log('🔄 Transaction deÄŸiÅŸtirildi:', id)
if (autoResume) { if (autoResume) {
const hasData = Array.isArray(this.orders) && this.orders.length > 0 const hasData = Array.isArray(this.orders) && this.orders.length > 0
if (!hasData) { if (!hasData) {
const ok = this.hydrateFromLocalStorage(id,true) const ok = this.hydrateFromLocalStorage(id,true)
if (ok) console.info('📦 Local kayıt geri yüklendi (boş grid için).') if (ok) console.info('📦 Local kayıt geri yüklendi (boÅŸ grid için).')
} else { } else {
console.log('🚫 Grid dolu, auto-resume atlandı (mevcut satırlar korundu).') console.log('🚫 Grid dolu, auto-resume atlandı (mevcut satırlar korundu).')
} }
} }
} }
@@ -1935,7 +1925,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* =========================================================== /* ===========================================================
🧹 clearTransaction sadece NEW MODE taslaklarını temizler 🧹 clearTransaction — sadece NEW MODE taslaklarını temizler
=========================================================== */ =========================================================== */
clearTransaction() { clearTransaction() {
try { try {
@@ -1952,22 +1942,22 @@ export const useOrderEntryStore = defineStore('orderentry', {
localStorage.removeItem('bss_last_txn') localStorage.removeItem('bss_last_txn')
console.log('🧹 Transaction temizlendi') console.log('🧹 Transaction temizlendi')
} catch (err) { } catch (err) {
console.warn('⚠️ clearTransaction hata:', err) console.warn('⚠️ clearTransaction hata:', err)
} }
} }
, ,
// ======================================================= // =======================================================
// 🔒 KİLİT KONTROLÜ — Sadece EDIT modunda, backend satırı // 🔒 KİLİT KONTROLÜ — Sadece EDIT modunda, backend satırı
// ======================================================= // =======================================================
isRowLocked(row) { isRowLocked(row) {
if (!row) return false if (!row) return false
// Sadece edit modunda, // Sadece edit modunda,
// ve backend'den gelen gerçek OrderLineID varsa, // ve backend'den gelen gerçek OrderLineID varsa,
// ve IsClosed=1 ise satır kilitli // ve IsClosed=1 ise satır kilitli
return ( return (
this.mode === 'edit' && this.mode === 'edit' &&
!!row.OrderLineID && !!row.OrderLineID &&
@@ -2000,10 +1990,10 @@ export const useOrderEntryStore = defineStore('orderentry', {
aciklama: row.aciklama || old.aciklama, aciklama: row.aciklama || old.aciklama,
updatedAt: dayjs().toISOString() updatedAt: dayjs().toISOString()
} }
console.log(`⚠️ Aynı kombinasyon bulundu, satır güncellendi: ${row.model} ${row.renk || ''} ${row.renk2 || ''}`) console.log(`⚠️ Aynı kombinasyon bulundu, satır güncellendi: ${row.model} ${row.renk || ''} ${row.renk2 || ''}`)
} else { } else {
this.orders.push(toRaw(row)) this.orders.push(toRaw(row))
console.log(` Yeni kombinasyon eklendi: ${row.model} ${row.renk || ''} ${row.renk2 || ''}`) console.log(`➕ Yeni kombinasyon eklendi: ${row.model} ${row.renk || ''} ${row.renk2 || ''}`)
} }
this.persistLocalStorage() this.persistLocalStorage()
@@ -2019,7 +2009,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
this.persistLocalStorage() this.persistLocalStorage()
this.saveSnapshot('after-update') this.saveSnapshot('after-update')
console.log(`✏️ Satır güncellendi (store): #${index}`) console.log(`✏️ Satır güncellendi (store): #${index}`)
}, },
@@ -2033,27 +2023,27 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.persistLocalStorage() this.persistLocalStorage()
this.saveSnapshot('after-remove') this.saveSnapshot('after-remove')
console.log(`🗑️ Satır silindi: ${removed[0]?.model || '(model yok)'}`) console.log(`🗑️ Satır silindi: ${removed[0]?.model || '(model yok)'}`)
}, },
removeSelectedRow(row, $q = null) { removeSelectedRow(row, $q = null) {
if (!row) return if (!row) return
// 1) Kilitli satır silinemez // 1) Kilitli satır silinemez
if (this.isRowLocked(row)) { if (this.isRowLocked(row)) {
$q?.notify?.({ $q?.notify?.({
type: 'warning', type: 'warning',
message: '🔒 Bu satır (IsClosed=1) kapatılmış. Silinemez.' message: '🔒 Bu satır (IsClosed=1) kapatılmış. Silinemez.'
}) })
return false return false
} }
// 2) Kullanıcıya onay sor // 2) Kullanıcıya onay sor
return new Promise(resolve => { return new Promise(resolve => {
$q?.dialog({ $q?.dialog({
title: 'Satır Sil', title: 'Satır Sil',
message: `${row.model} / ${row.renk} / ${row.renk2} kombinasyonu silinsin mi?`, message: `${row.model} / ${row.renk} / ${row.renk2} kombinasyonu silinsin mi?`,
ok: { label: 'Evet', color: 'negative' }, ok: { label: 'Evet', color: 'negative' },
cancel: { label: 'Vazgeç' } cancel: { label: 'Vazgeç' }
}) })
.onOk(() => { .onOk(() => {
this.removeRowInternal(row) this.removeRowInternal(row)
@@ -2066,9 +2056,9 @@ export const useOrderEntryStore = defineStore('orderentry', {
removeRowInternal(row) { removeRowInternal(row) {
if (!row) return false if (!row) return false
// 1️⃣ Kilit kontrolü // 1️⃣ Kilit kontrolü
if (this.isRowLocked(row)) { if (this.isRowLocked(row)) {
console.warn('🔒 Kilitli satır silinemez.') console.warn('🔒 Kilitli satır silinemez.')
return false return false
} }
@@ -2083,27 +2073,27 @@ export const useOrderEntryStore = defineStore('orderentry', {
const idx = this.summaryRows.findIndex(r => getKey(r) === rowKey) const idx = this.summaryRows.findIndex(r => getKey(r) === rowKey)
if (idx === -1) return false if (idx === -1) return false
console.log('🗑️ X2 removeRowInternal ', row) console.log('🗑️ X2 removeRowInternal →', row)
// 🔐 UI BUSY // 🔐 UI BUSY
this._uiBusy = true this._uiBusy = true
this.preventPersist = true this.preventPersist = true
try { try {
// 2️⃣ UIdan kaldır // 2️⃣ UI’dan kaldır
this.summaryRows.splice(idx, 1) this.summaryRows.splice(idx, 1)
// orders = UI satırları (temiz kopya) // orders = UI satırları (temiz kopya)
this.orders = [...this.summaryRows] this.orders = [...this.summaryRows]
// 3️⃣ EDIT MODE DELETE SİNYALİ // 3️⃣ EDIT MODE → DELETE SİNYALİ
if (this.mode === 'edit') { if (this.mode === 'edit') {
const grpKey = const grpKey =
row.grpKey || row.grpKey ||
Object.keys(row.bedenMap || {})[0] || Object.keys(row.bedenMap || {})[0] ||
'tak' 'tak'
// lineIdMap referansı (varsa) // ✅ lineIdMap referansı (varsa)
const lineIdMap = const lineIdMap =
(row.lineIdMap && typeof row.lineIdMap === 'object') (row.lineIdMap && typeof row.lineIdMap === 'object')
? { ...row.lineIdMap } ? { ...row.lineIdMap }
@@ -2111,7 +2101,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
const emptyMap = {} const emptyMap = {}
// Öncelik: bedenMap[grpKey] lineIdMap fallback // Öncelik: bedenMap[grpKey] → lineIdMap → fallback
if (row.bedenMap && row.bedenMap[grpKey]) { if (row.bedenMap && row.bedenMap[grpKey]) {
for (const beden of Object.keys(row.bedenMap[grpKey] || {})) { for (const beden of Object.keys(row.bedenMap[grpKey] || {})) {
emptyMap[beden] = 0 emptyMap[beden] = 0
@@ -2127,21 +2117,21 @@ export const useOrderEntryStore = defineStore('orderentry', {
const deleteSignalRow = { const deleteSignalRow = {
...row, ...row,
// 🔴 UI KEY // 🔴 UI KEY
id: `DEL::${row.id || row.OrderLineID || crypto.randomUUID()}`, id: `DEL::${row.id || row.OrderLineID || crypto.randomUUID()}`,
// 🔴 BACKEND DELETE SIGNAL // 🔴 BACKEND DELETE SIGNAL
adet: 0, adet: 0,
Qty1: 0, Qty1: 0,
tutar: 0, tutar: 0,
// 🔴 CRITICAL: duplicate guard'a girmesin // 🔴 CRITICAL: duplicate guard'a girmesin
ComboKey: '', ComboKey: '',
// 🔴 legacy tekil alan (varsa kalsın) // 🔴 legacy tekil alan (varsa kalsın)
OrderLineID: row.OrderLineID || null, OrderLineID: row.OrderLineID || null,
// CRITICAL // ✅ CRITICAL
grpKey, grpKey,
bedenMap: { [grpKey]: emptyMap }, bedenMap: { [grpKey]: emptyMap },
lineIdMap, lineIdMap,
@@ -2150,21 +2140,21 @@ export const useOrderEntryStore = defineStore('orderentry', {
_deleteSignal: true _deleteSignal: true
} }
console.log('📡 DELETE sinyali üretildi:', deleteSignalRow) console.log('📡 DELETE sinyali üretildi:', deleteSignalRow)
this.orders.push(deleteSignalRow) this.orders.push(deleteSignalRow)
} }
// 4️⃣ Totals (persist YOK) // 4️⃣ Totals (persist YOK)
this.updateHeaderTotals?.() this.updateHeaderTotals?.()
} finally { } finally {
// 🔓 GUARD KAPAT // 🔓 GUARD KAPAT
this.preventPersist = false this.preventPersist = false
this._uiBusy = false this._uiBusy = false
} }
// 5️⃣ TEK VE KONTROLLÜ persist // 5️⃣ TEK VE KONTROLLÜ persist
this.persistLocalStorage() this.persistLocalStorage()
return true return true
@@ -2174,12 +2164,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* =========================================================== /* ===========================================================
📦 normalizeOrderLines (v9 lineIdMap FIXED + AKSBİR SAFE) 📦 normalizeOrderLines (v9 — lineIdMap FIXED + AKSBİR SAFE)
----------------------------------------------------------- -----------------------------------------------------------
grpKey SADECE burada set edilir ✔ grpKey SADECE burada set edilir
detectBedenGroup SADECE storeda kullanılır ✔ detectBedenGroup SADECE store’da kullanılır
aksbir ' ' bedeni = GERÇEK adet ✔ aksbir → ' ' bedeni = GERÇEK adet
backend satırlarında BEDEN OrderLineID mapi üretilir ✔ backend satırlarında BEDEN → OrderLineID map’i üretilir
=========================================================== */ =========================================================== */
normalizeOrderLines(lines, pbFallback = 'USD') { normalizeOrderLines(lines, pbFallback = 'USD') {
if (!Array.isArray(lines)) return [] if (!Array.isArray(lines)) return []
@@ -2198,17 +2188,17 @@ export const useOrderEntryStore = defineStore('orderentry', {
raw.IsClosed?.Bool === true raw.IsClosed?.Bool === true
/* ======================================================= /* =======================================================
1️⃣ UI / SNAPSHOT KAYNAKLI SATIR 1️⃣ UI / SNAPSHOT KAYNAKLI SATIR
------------------------------------------------------- -------------------------------------------------------
ComboKey YOK ✔ ComboKey YOK
Sadece model / renk / renk2 bazında gruplanır ✔ Sadece model / renk / renk2 bazında gruplanır
======================================================= */ ======================================================= */
if (raw.bedenMap && Object.keys(raw.bedenMap).length) { if (raw.bedenMap && Object.keys(raw.bedenMap).length) {
const model = (raw.model || raw.ItemCode || '').trim() const model = (raw.model || raw.ItemCode || '').trim()
const renk = (raw.renk || raw.ColorCode || '').trim() const renk = (raw.renk || raw.ColorCode || '').trim()
const renk2 = (raw.renk2 || raw.ItemDim2Code || '').trim() const renk2 = (raw.renk2 || raw.ItemDim2Code || '').trim()
// BEDEN YOK bu SADECE üst seviye grup anahtarı // ❗ BEDEN YOK → bu SADECE üst seviye grup anahtarı
const modelKey = `${model}||${renk}||${renk2}` const modelKey = `${model}||${renk}||${renk2}`
const grpKey = raw.grpKey || 'tak' const grpKey = raw.grpKey || 'tak'
@@ -2235,17 +2225,17 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* ======================================================= /* =======================================================
2️⃣ BACKEND / LEGACY SATIR (FIXED) 2️⃣ BACKEND / LEGACY SATIR (FIXED)
------------------------------------------------------- -------------------------------------------------------
ComboKey YOK ✔ ComboKey YOK
Sadece model / renk / renk2 bazlı gruplanır ✔ Sadece model / renk / renk2 bazlı gruplanır
BEDEN sadece bedenMap + lineIdMap için kullanılır ✔ BEDEN sadece bedenMap + lineIdMap için kullanılır
======================================================= */ ======================================================= */
const model = (raw.Model || raw.ItemCode || '').trim() const model = (raw.Model || raw.ItemCode || '').trim()
const renk = (raw.ColorCode || '').trim() const renk = (raw.ColorCode || '').trim()
const renk2 = (raw.ItemDim2Code || '').trim() const renk2 = (raw.ItemDim2Code || '').trim()
// BEDEN HARİÇ — üst seviye grup anahtarı // ❗ BEDEN HARİÇ — üst seviye grup anahtarı
const modelKey = `${model}||${renk}||${renk2}` const modelKey = `${model}||${renk}||${renk2}`
merged[modelKey] ??= [] merged[modelKey] ??= []
@@ -2275,8 +2265,8 @@ export const useOrderEntryStore = defineStore('orderentry', {
fiyat: Number(raw.Price || 0), fiyat: Number(raw.Price || 0),
pb: raw.DocCurrencyCode || pbFallback, pb: raw.DocCurrencyCode || pbFallback,
__tmpMap: {}, // beden qty __tmpMap: {}, // beden → qty
lineIdMap: {}, // beden OrderLineID lineIdMap: {}, // beden → OrderLineID
adet: 0, adet: 0,
tutar: 0, tutar: 0,
@@ -2289,7 +2279,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* ------------------------------------------------------- /* -------------------------------------------------------
🔑 BEDEN OrderLineID (DETERMINISTIC & SAFE) 🔑 BEDEN → OrderLineID (DETERMINISTIC & SAFE)
-------------------------------------------------------- */ -------------------------------------------------------- */
const rawLineId = const rawLineId =
raw.OrderLineID || raw.OrderLineID ||
@@ -2309,7 +2299,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* ======================================================= /* =======================================================
3️⃣ FINAL grpKey KESİN + AKSBİR FIX 3️⃣ FINAL — grpKey KESİN + AKSBİR FIX
======================================================= */ ======================================================= */
const out = [] const out = []
@@ -2322,7 +2312,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
const bedenList = Object.keys(row.__tmpMap) const bedenList = Object.keys(row.__tmpMap)
// 🔒 TEK VE KESİN KARAR // 🔒 TEK VE KESİN KARAR
const grpKey = detectBedenGroup( const grpKey = detectBedenGroup(
bedenList, bedenList,
row.urunAnaGrubu, row.urunAnaGrubu,
@@ -2350,9 +2340,9 @@ export const useOrderEntryStore = defineStore('orderentry', {
row.tutar = Number((row.adet * Number(row.fiyat || 0)).toFixed(2)) 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
STDye dönme YOK ❗ STD’ye dönme YOK
0 yazma YOK ❗ 0 yazma YOK
=================================================== */ =================================================== */
if (grpKey === 'aksbir') { if (grpKey === 'aksbir') {
row.bedenMap[grpKey] ??= {} row.bedenMap[grpKey] ??= {}
@@ -2365,7 +2355,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
console.log( console.log(
`📦 normalizeOrderLines (v9 + lineIdMap) ${out.length} satır` `📦 normalizeOrderLines (v9 + lineIdMap) → ${out.length} satır`
) )
return out return out
@@ -2375,12 +2365,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
/** /**
* =========================================================== * ===========================================================
* loadProductSizes FINAL v4.2 (EDITOR SAFE) * loadProductSizes — FINAL v4.2 (EDITOR SAFE)
* ----------------------------------------------------------- * -----------------------------------------------------------
* grpKey SADECE form.grpKey * ✔ grpKey SADECE form.grpKey
* schemaMap TEK OTORİTE * ✔ schemaMap TEK OTORİTE
* edit modda BEDEN LABEL DOKUNULMAZ * ✔ edit modda BEDEN LABEL DOKUNULMAZ
* ' ' (boş beden) korunur * ✔ ' ' (boÅŸ beden) korunur
* =========================================================== * ===========================================================
*/ */
async loadProductSizes(form, forceRefresh = false, $q = null) { async loadProductSizes(form, forceRefresh = false, $q = null) {
@@ -2395,7 +2385,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
try { try {
const grpKey = form.grpKey const grpKey = form.grpKey
if (!grpKey) { if (!grpKey) {
console.warn(' loadProductSizes iptal grpKey yok') console.warn('⛔ loadProductSizes iptal → grpKey yok')
return return
} }
@@ -2404,18 +2394,18 @@ export const useOrderEntryStore = defineStore('orderentry', {
const cacheKey = `${form.model}_${colorKey}_${color2Key}_${grpKey}` const cacheKey = `${form.model}_${colorKey}_${color2Key}_${grpKey}`
/* ======================================================= /* =======================================================
♻️ CACHE (LABEL DOKUNMADAN) ♻️ CACHE (LABEL DOKUNMADAN)
======================================================= */ ======================================================= */
if (!forceRefresh && sizeCache.value?.[cacheKey]) { if (!forceRefresh && sizeCache.value?.[cacheKey]) {
const cached = sizeCache.value[cacheKey] const cached = sizeCache.value[cacheKey]
bedenStock.value = [...cached.stockArray] bedenStock.value = [...cached.stockArray]
stockMap.value = { ...cached.stockMap } stockMap.value = { ...cached.stockMap }
console.log(`♻️ loadProductSizes CACHE ${grpKey}`) console.log(`♻️ loadProductSizes CACHE → ${grpKey}`)
return return
} }
/* ======================================================= /* =======================================================
📡 API 📡 API
======================================================= */ ======================================================= */
const params = { code: form.model } const params = { code: form.model }
if (form.renk) params.color = form.renk if (form.renk) params.color = form.renk
@@ -2431,7 +2421,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* ======================================================= /* =======================================================
📦 STOK MAP (' ' KORUNUR) 📦 STOK MAP (' ' KORUNUR)
======================================================= */ ======================================================= */
const apiStockMap = {} const apiStockMap = {}
for (const x of data) { for (const x of data) {
@@ -2453,7 +2443,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
) )
/* ======================================================= /* =======================================================
💾 CACHE 💾 CACHE
======================================================= */ ======================================================= */
sizeCache.value[cacheKey] = { sizeCache.value[cacheKey] = {
labels: [...form.bedenLabels], labels: [...form.bedenLabels],
@@ -2461,14 +2451,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
stockMap: { ...stockMap.value } stockMap: { ...stockMap.value }
} }
console.log(` loadProductSizes FINAL v4.2 ${grpKey}`) console.log(`✅ loadProductSizes FINAL v4.2 → ${grpKey}`)
} catch (err) { } catch (err) {
console.error(' loadProductSizes hata:', err) console.error('❌ loadProductSizes hata:', err)
$q?.notify?.({ type: 'negative', message: 'Beden / stok alınamadı' }) $q?.notify?.({ type: 'negative', message: 'Beden / stok alınamadı' })
} finally { } finally {
store._uiBusy = prevBusy store._uiBusy = prevBusy
store.preventPersist = prevPrevent store.preventPersist = prevPrevent
console.log('🧩 Editor beden hydrate', { console.log('🧩 Editor beden hydrate', {
grpKey: form.grpKey, grpKey: form.grpKey,
labels: form.bedenLabels, labels: form.bedenLabels,
values: form.bedenler values: form.bedenler
@@ -2483,24 +2473,24 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
// ======================================================= // =======================================================
// 🔸 TOPLAM HESAPLAMA (store içi) X3 SAFE // 🔸 TOPLAM HESAPLAMA (store içi) — X3 SAFE
// ------------------------------------------------------- // -------------------------------------------------------
// f.adet / f.tutar hesaplanır // ✔ f.adet / f.tutar hesaplanır
// store.totalAmount ASLA set edilmez // ✔ store.totalAmount ASLA set edilmez
// gerçek toplam header.TotalAmount // ✔ gerçek toplam → header.TotalAmount
// ======================================================= // =======================================================
updateTotals(f) { updateTotals(f) {
// 1️⃣ Satır adet // 1️⃣ Satır adet
f.adet = (f.bedenler || []).reduce( f.adet = (f.bedenler || []).reduce(
(a, b) => a + Number(b || 0), (a, b) => a + Number(b || 0),
0 0
) )
// 2️⃣ Satır tutar // 2️⃣ Satır tutar
const fiyat = Number(f.fiyat) || 0 const fiyat = Number(f.fiyat) || 0
f.tutar = Number((f.adet * fiyat).toFixed(2)) f.tutar = Number((f.adet * fiyat).toFixed(2))
// 3️⃣ Header toplam (tek gerçek state) // 3️⃣ Header toplam (tek gerçek state)
if (this.header) { if (this.header) {
const total = (this.summaryRows || []).reduce( const total = (this.summaryRows || []).reduce(
(sum, r) => sum + Number(r?.tutar || 0), (sum, r) => sum + Number(r?.tutar || 0),
@@ -2515,39 +2505,39 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
// ======================================================= // =======================================================
// 🔸 GRUP ANAHTARI TESPİTİ // 🔸 GRUP ANAHTARI TESPİTİ
// ======================================================= // =======================================================
activeGroupKeyForRow(row) { activeGroupKeyForRow(row) {
const g = (row?.urunAnaGrubu || '').toUpperCase() const g = (row?.urunAnaGrubu || '').toUpperCase()
if (g.includes('TAKIM')) return 'tak' if (g.includes('TAKIM')) return 'tak'
if (g.includes('PANTOLON')) return 'pan' if (g.includes('PANTOLON')) return 'pan'
if (g.includes('GÖMLEK')) return 'gom' if (g.includes('GOMLEK')) return 'gom'
if (g.includes('AYAKKABI')) return 'ayk' if (g.includes('AYAKKABI')) return 'ayk'
if (g.includes('YAŞ')) return 'yas' if (g.includes('YAS')) return 'yas'
return 'tak' return 'tak'
}, },
/* ======================================================= /* =======================================================
🔹 MODE YÖNETİMİ — new / edit arası geçiş 🔹 MODE YÖNETİMİ — new / edit arası geçiÅŸ
======================================================= */ ======================================================= */
setMode(mode) { setMode(mode) {
if (!['new', 'edit', 'view'].includes(mode)) { if (!['new', 'edit', 'view'].includes(mode)) {
console.warn('⚠️ Geçersiz mode:', mode) console.warn('⚠️ Geçersiz mode:', mode)
return return
} }
this.mode = mode this.mode = mode
console.log(`🧭 Order mode set edildi ${mode}`) console.log(`🧭 Order mode set edildi → ${mode}`)
} }
, ,
/* =========================================================== /* ===========================================================
🟦 submitAllReal (v12.1c FINAL / CLEAN + PRE-VALIDATE) 🟦 submitAllReal (v12.1c — FINAL / CLEAN + PRE-VALIDATE)
----------------------------------------------------------- -----------------------------------------------------------
NEW INSERT, EDIT UPDATE (tek karar noktası) ✔ NEW → INSERT, EDIT → UPDATE (tek karar noktası)
Controlled submit route guard SUSAR ✔ Controlled submit → route guard SUSAR
Snapshot temizliği route öncesi ✔ Snapshot temizliÄŸi route öncesi
Kaydet edit replace backend reload ✔ Kaydet → edit replace → backend reload
Listeye giderken guard popup 1 kez bypass ✔ Listeye giderken guard popup 1 kez bypass
✔ ✅ PRE-VALIDATE prItemVariant olmayan kombinasyonlar kaydı DURDURUR ✔ ✅ PRE-VALIDATE → prItemVariant olmayan kombinasyonlar kaydı DURDURUR
=========================================================== */ =========================================================== */
async submitAllReal($q, router, form, summaryRows, productCache) { async submitAllReal($q, router, form, summaryRows, productCache) {
let serverOrderId = null let serverOrderId = null
@@ -2556,17 +2546,17 @@ export const useOrderEntryStore = defineStore('orderentry', {
try { try {
this.loading = true this.loading = true
// 🔒 Kontrollü submit route leave guard susar // 🔒 Kontrollü submit → route leave guard susar
this.isControlledSubmit = true this.isControlledSubmit = true
const isNew = this.mode === 'new' const isNew = this.mode === 'new'
const { header, lines } = this.buildFinalOrderJson() const { header, lines } = this.buildFinalOrderJson()
// ======================================================= // =======================================================
// 🧾 DEBUG FRONTEND BACKEND GİDEN PAYLOAD // 🧾 DEBUG — FRONTEND → BACKEND GİDEN PAYLOAD
// ======================================================= // =======================================================
console.groupCollapsed( console.groupCollapsed(
`%c📤 ORDER PAYLOAD (${this.mode})`, `%c📤 ORDER PAYLOAD (${this.mode})`,
'color:#c9a873;font-weight:bold' 'color:#c9a873;font-weight:bold'
) )
@@ -2589,21 +2579,21 @@ export const useOrderEntryStore = defineStore('orderentry', {
console.groupEnd() console.groupEnd()
// ======================================================= // =======================================================
// 🧾 DEBUG (opsiyonel helper) // 🧾 DEBUG (opsiyonel helper)
// ======================================================= // =======================================================
this.debugOrderPayload?.(header, lines, 'PRE-VALIDATE') this.debugOrderPayload?.(header, lines, 'PRE-VALIDATE')
// ======================================================= // =======================================================
// 🧩 DUMMY CURRENCY PAYLOAD (model genişletmeden) // 🧩 DUMMY CURRENCY PAYLOAD (model geniÅŸletmeden)
// - trOrderLineCurrency için gerekli alanları satıra basar // - trOrderLineCurrency için gerekli alanları satıra basar
// - örnek satırdaki gibi: PriceVI/AmountVI = KDV dahil, Price/Amount = KDV hariç // - örnek satırdaki gibi: PriceVI/AmountVI = KDV dahil, Price/Amount = KDV hariç
// ======================================================= // =======================================================
const r2 = (n) => Number((Number(n) || 0).toFixed(2)) const r2 = (n) => Number((Number(n) || 0).toFixed(2))
const r4 = (n) => Number((Number(n) || 0).toFixed(4)) const r4 = (n) => Number((Number(n) || 0).toFixed(4))
for (const ln of lines) { for (const ln of lines) {
const qty = Number(ln?.Qty1 || 0) const qty = Number(ln?.Qty1 || 0)
const unitBase = Number(ln?.Price || 0) // KDV hariç birim const unitBase = Number(ln?.Price || 0) // KDV hariç birim
const vatRate = Number(ln?.VatRate || 0) const vatRate = Number(ln?.VatRate || 0)
const exRate = Number(ln?.PriceExchangeRate || header?.ExchangeRate || 1) || 1 const exRate = Number(ln?.PriceExchangeRate || header?.ExchangeRate || 1) || 1
@@ -2614,7 +2604,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
const docCurrency = String(ln?.DocCurrencyCode || header?.DocCurrencyCode || 'TRY').trim() || 'TRY' const docCurrency = String(ln?.DocCurrencyCode || header?.DocCurrencyCode || 'TRY').trim() || 'TRY'
// Backend model alanları // Backend model alanları
ln.RelationCurrencyCode = docCurrency ln.RelationCurrencyCode = docCurrency
ln.DocPrice = unitWithVat ln.DocPrice = unitWithVat
ln.DocAmount = net ln.DocAmount = net
@@ -2628,7 +2618,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
ln.VatDeducation = 0 ln.VatDeducation = 0
ln.NetAmount = net ln.NetAmount = net
// SQL kolonu isimleriyle dummy alias (decoder ignore etse de payload'da görünür) // SQL kolonu isimleriyle dummy alias (decoder ignore etse de payload'da görünür)
ln.CurrencyCode = docCurrency ln.CurrencyCode = docCurrency
ln.ExchangeRate = exRate ln.ExchangeRate = exRate
ln.PriceVI = unitWithVat ln.PriceVI = unitWithVat
@@ -2640,25 +2630,25 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
// ======================================================= // =======================================================
// 🧪 PRE-VALIDATE prItemVariant ön kontrol // 🧪 PRE-VALIDATE — prItemVariant ön kontrol
// - invalid varsa CREATE/UPDATE ÇALIŞMAZ // - invalid varsa CREATE/UPDATE ÇALIŞMAZ
// ======================================================= // =======================================================
const v = await api.post('/order/validate', { header, lines }) const v = await api.post('/order/validate', { header, lines })
const invalid = v?.data?.invalid || [] const invalid = v?.data?.invalid || []
if (invalid.length > 0) { if (invalid.length > 0) {
await this.showInvalidVariantDialog?.($q, invalid) await this.showInvalidVariantDialog?.($q, invalid)
return // create / update ÇALIŞMAZ return // ❌ create / update ÇALIŞMAZ
} }
console.log('📤 submitAllReal payload', { console.log('📤 submitAllReal payload', {
mode: this.mode, mode: this.mode,
lines: lines.length, lines: lines.length,
deletes: lines.filter(l => l._deleteSignal).length deletes: lines.filter(l => l._deleteSignal).length
}) })
/* ======================================================= /* =======================================================
🚀 API CALL TEK NOKTA 🚀 API CALL — TEK NOKTA
======================================================= */ ======================================================= */
const resp = await api.post( const resp = await api.post(
isNew ? '/order/create' : '/order/update', isNew ? '/order/create' : '/order/update',
@@ -2679,11 +2669,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
header?.OrderNumber header?.OrderNumber
if (!serverOrderId) { if (!serverOrderId) {
throw new Error('OrderHeaderID backendden dönmedi') throw new Error('OrderHeaderID backend’den dönmedi')
} }
/* ======================================================= /* =======================================================
🔁 MODE SWITCH EDIT 🔁 MODE SWITCH → EDIT
======================================================= */ ======================================================= */
this.setMode('edit') this.setMode('edit')
@@ -2695,25 +2685,25 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* ======================================================= /* =======================================================
🧹 KRİTİK: Snapshot + Dirty temizliği 🧹 KRİTİK: Snapshot + Dirty temizliÄŸi
ROUTE değişmeden ÖNCE ❗ ROUTE deÄŸiÅŸmeden ÖNCE
======================================================= */ ======================================================= */
this.updateHeaderTotals?.() this.updateHeaderTotals?.()
this.markAsSaved?.() this.markAsSaved?.()
/* ======================================================= /* =======================================================
🧹 KRİTİK: NEW EDIT geçişinde TÜM SNAPSHOT TEMİZLENİR 🧹 KRİTİK: NEW → EDIT geçiÅŸinde TÜM SNAPSHOT TEMİZLENİR
======================================================= */ ======================================================= */
this.clearAllOrderSnapshots() this.clearAllOrderSnapshots()
$q.notify({ $q.notify({
type: 'positive', type: 'positive',
message: `Sipariş kaydedildi: ${serverOrderNo || ''}`.trim() message: `SipariÅŸ kaydedildi: ${serverOrderNo || ''}`.trim()
}) })
/* ======================================================= /* =======================================================
🔀 ROUTE REPLACE (EDIT MODE) 🔀 ROUTE REPLACE (EDIT MODE)
- aynı sayfa param değişti - aynı sayfa → param deÄŸiÅŸti
- guard 1 kez bypass - guard 1 kez bypass
======================================================= */ ======================================================= */
this.allowRouteLeaveOnce = true this.allowRouteLeaveOnce = true
@@ -2725,7 +2715,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
}) })
/* ======================================================= /* =======================================================
🔄 BACKEND RELOAD (TEK GERÇEK KAYNAK) 🔄 BACKEND RELOAD (TEK GERÇEK KAYNAK)
======================================================= */ ======================================================= */
await this.openExistingForEdit(serverOrderId, { await this.openExistingForEdit(serverOrderId, {
$q, $q,
@@ -2735,21 +2725,21 @@ export const useOrderEntryStore = defineStore('orderentry', {
}) })
/* ======================================================= /* =======================================================
USER NEXT STEP ❓ USER NEXT STEP
======================================================= */ ======================================================= */
const choice = await new Promise(resolve => { const choice = await new Promise(resolve => {
$q.dialog({ $q.dialog({
title: 'Sipariş Kaydedildi', title: 'SipariÅŸ Kaydedildi',
options: { options: {
type: 'radio', type: 'radio',
model: 'continue', model: 'continue',
items: [ items: [
{ label: '✏️ Düzenlemeye Devam', value: 'continue' }, { label: '✏️ Düzenlemeye Devam', value: 'continue' },
{ label: '🖨 Yazdır', value: 'print' }, { label: '🖨 Yazdır', value: 'print' },
{ label: '📋 Listeye Dön', value: 'list' } { label: '📋 Listeye Dön', value: 'list' }
] ]
}, },
ok: { label: 'Seç' }, ok: { label: 'Seç' },
cancel: { label: 'Kapat' } cancel: { label: 'Kapat' }
}) })
.onOk(v => resolve(v)) .onOk(v => resolve(v))
@@ -2757,7 +2747,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
}) })
/* ======================================================= /* =======================================================
🧭 USER ROUTING 🧭 USER ROUTING
======================================================= */ ======================================================= */
if (choice === 'print') { if (choice === 'print') {
const id = this.header?.OrderHeaderID || serverOrderId const id = this.header?.OrderHeaderID || serverOrderId
@@ -2765,12 +2755,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
try { try {
await this.downloadOrderPdf(id) await this.downloadOrderPdf(id)
} catch (pdfErr) { } catch (pdfErr) {
console.error('⚠️ PDF açılamadı, kayıt başarılı:', pdfErr) console.error('⚠️ PDF açılamadı, kayıt baÅŸarılı:', pdfErr)
$q.notify({ $q.notify({
type: 'warning', type: 'warning',
message: message:
pdfErr?.message || pdfErr?.message ||
'Sipariş kaydedildi fakat PDF açılamadı.' 'SipariÅŸ kaydedildi fakat PDF açılamadı.'
}) })
} }
} }
@@ -2783,10 +2773,10 @@ export const useOrderEntryStore = defineStore('orderentry', {
return return
} }
// continue sayfada kal (hiçbir şey yapma) // continue → sayfada kal (hiçbir ÅŸey yapma)
} catch (err) { } catch (err) {
console.error(' submitAllReal:', err) console.error('❌ submitAllReal:', err)
$q.notify({ $q.notify({
type: 'negative', type: 'negative',
@@ -2794,11 +2784,11 @@ export const useOrderEntryStore = defineStore('orderentry', {
err?.response?.data?.detail || err?.response?.data?.detail ||
err?.response?.data?.message || err?.response?.data?.message ||
err?.message || err?.message ||
'Kayıt sırasında hata' 'Kayıt sırasında hata'
}) })
} finally { } finally {
// 🔓 Guardlar normale dönsün // 🔓 Guard’lar normale dönsün
this.isControlledSubmit = false this.isControlledSubmit = false
this.loading = false this.loading = false
} }
@@ -2807,28 +2797,28 @@ export const useOrderEntryStore = defineStore('orderentry', {
, ,
/* ======================================================= /* =======================================================
🧪 SUBMIT ALL TEST 🧪 SUBMIT ALL TEST
======================================================= */ ======================================================= */
async submitAllTest($q = null) { async submitAllTest($q = null) {
try { try {
const { header, lines } = this.buildFinalOrderJson() const { header, lines } = this.buildFinalOrderJson()
console.log('🧾 TEST HEADER', Object.keys(header).length, 'alan') console.log('🧾 TEST HEADER', Object.keys(header).length, 'alan')
console.log(JSON.stringify(header, null, 2)) console.log(JSON.stringify(header, null, 2))
console.log('🧾 TEST LINES', lines.length, 'satır') console.log('🧾 TEST LINES', lines.length, 'satır')
console.log(JSON.stringify(lines, null, 2)) console.log(JSON.stringify(lines, null, 2))
$q?.notify?.({ $q?.notify?.({
type: 'info', type: 'info',
message: `Header (${Object.keys(header).length}) + Lines (${lines.length}) gösterildi`, message: `Header (${Object.keys(header).length}) + Lines (${lines.length}) gösterildi`,
position: 'top' position: 'top'
}) })
} catch (err) { } catch (err) {
console.error(' submitAllTest hata:', err) console.error('❌ submitAllTest hata:', err)
$q?.notify?.({ $q?.notify?.({
type: 'negative', type: 'negative',
message: 'Gösterimde hata oluştu ', message: 'Gösterimde hata oluÅŸtu ❌',
position: 'top' position: 'top'
}) })
} }
@@ -2836,15 +2826,15 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* ======================================================= /* =======================================================
🧹 KAYIT SONRASI TEMİZLİK 🧹 KAYIT SONRASI TEMİZLİK
======================================================= */ ======================================================= */
afterSubmit(opts = { afterSubmit(opts = {
keepLocalStorage: true, keepLocalStorage: true,
backendPayload: null, backendPayload: null,
resetMode: true // 🔑 yeni resetMode: true // 🔑 yeni
}) { }) {
try { try {
console.log('🧹 afterSubmit başlatıldı', opts) console.log('🧹 afterSubmit baÅŸlatıldı', opts)
if (opts?.backendPayload?.header?.OrderHeaderID) { if (opts?.backendPayload?.header?.OrderHeaderID) {
this.mergeAndPersistBackendOrder( this.mergeAndPersistBackendOrder(
@@ -2865,14 +2855,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
this.editingKey = null this.editingKey = null
this.currentOrderId = null this.currentOrderId = null
// 🔐 MODE RESET OPSİYONEL // 🔐 MODE RESET OPSİYONEL
if (opts.resetMode === true) { if (opts.resetMode === true) {
this.mode = 'new' this.mode = 'new'
} }
console.log(' afterSubmit tamamlandı.') console.log('✅ afterSubmit tamamlandı.')
} catch (err) { } catch (err) {
console.error(' afterSubmit hata:', err) console.error('❌ afterSubmit hata:', err)
} }
} }
@@ -2880,14 +2870,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* =========================================================== /* ===========================================================
🟦 BUILD FINAL ORDER JSON SAFE v26.1 (FINAL) 🟦 BUILD FINAL ORDER JSON — SAFE v26.1 (FINAL)
----------------------------------------------------------- -----------------------------------------------------------
ComboKey TEK OTORİTE buildComboKey (bedenKey ile) ✔ ComboKey TEK OTORİTE → buildComboKey (bedenKey ile)
UI/Map placeholder: '_' (bedenKey) ✔ UI/Map placeholder: '_' (bedenKey)
DB/payload: '' (bedenPayload) "_" ASLA GİTMEZ ✔ DB/payload: '' (bedenPayload) → "_" ASLA GİTMEZ
payload içinde aynı ComboKey TEK satır ✔ payload içinde aynı ComboKey TEK satır
backend duplicate guard %100 uyumlu (ComboKey stabil) ✔ backend duplicate guard %100 uyumlu (ComboKey stabil)
Final assert: payloadda "_" yakalanırsa patlatır ✔ Final assert: payload’da "_" yakalanırsa patlatır
=========================================================== */ =========================================================== */
buildFinalOrderJson () { buildFinalOrderJson () {
const auth = useAuthStore() const auth = useAuthStore()
@@ -2904,7 +2894,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
const formatTimeOnly = v => dayjs(v).format('HH:mm:ss') const formatTimeOnly = v => dayjs(v).format('HH:mm:ss')
const formatDateTime = v => (v ? dayjs(v).format('YYYY-MM-DD HH:mm:ss') : null) const formatDateTime = v => (v ? dayjs(v).format('YYYY-MM-DD HH:mm:ss') : null)
// Payload beden normalize: "_" / "-" / "" => '' // ✅ Payload beden normalize: "_" / "-" / "" => ''
const normBeden = (v) => { const normBeden = (v) => {
const s = safeStr(v) const s = safeStr(v)
if (s === '' || s === '_' || s === '-') return '' // payload empty if (s === '' || s === '_' || s === '-') return '' // payload empty
@@ -2959,16 +2949,16 @@ export const useOrderEntryStore = defineStore('orderentry', {
} }
/* ======================================================= /* =======================================================
LINES COMBOKEY AGGREGATE (TEK MAP) LINES — COMBOKEY AGGREGATE (TEK MAP)
======================================================= */ ======================================================= */
const lines = [] const lines = []
const lineByCombo = new Map() // 🔒 KEY = ComboKey const lineByCombo = new Map() // 🔒 KEY = ComboKey
const pushOrMerge = (row, ctx) => { const pushOrMerge = (row, ctx) => {
const { const {
grpKey, grpKey,
bedenKey, // sadece ComboKey / Map için ('_' olabilir) bedenKey, // ✅ sadece ComboKey / Map için ('_' olabilir)
bedenPayload, // DB için ('' / 'S' / 'M' ...) bedenPayload, // ✅ DB için ('' / 'S' / 'M' ...)
qty, qty,
orderLineId, orderLineId,
isDeleteSignal isDeleteSignal
@@ -2976,7 +2966,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
if (qty <= 0 && !isDeleteSignal) return if (qty <= 0 && !isDeleteSignal) return
// ComboKey stabil kalsın diye bedenKey kullan // ComboKey stabil kalsın diye bedenKey kullan
const comboKey = buildComboKey(row, bedenKey) const comboKey = buildComboKey(row, bedenKey)
const makeLine = () => ({ const makeLine = () => ({
@@ -2990,7 +2980,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
ItemCode: safeStr(row.model), ItemCode: safeStr(row.model),
ColorCode: safeStr(row.renk), ColorCode: safeStr(row.renk),
// PAYLOAD: "_" ASLA YOK // ✅ PAYLOAD: "_" ASLA YOK
ItemDim1Code: bedenPayload, ItemDim1Code: bedenPayload,
ItemDim2Code: safeStr(row.renk2), ItemDim2Code: safeStr(row.renk2),
@@ -3101,7 +3091,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
const hasAnyBeden = const hasAnyBeden =
map && typeof map === 'object' && Object.keys(map).length > 0 map && typeof map === 'object' && Object.keys(map).length > 0
/* 🔹 BEDENSİZ / AKSBİR */ /* 🔹 BEDENSİZ / AKSBİR */
if (!hasAnyBeden) { if (!hasAnyBeden) {
const allowBlankPayload = const allowBlankPayload =
grpKey === 'aksbir' || row._deleteSignal === true grpKey === 'aksbir' || row._deleteSignal === true
@@ -3111,9 +3101,9 @@ export const useOrderEntryStore = defineStore('orderentry', {
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 = '_'
const bedenKey = '_' const bedenKey = '_'
// Payload: boş string // ✅ Payload: boÅŸ string
const bedenPayload = '' const bedenPayload = ''
let orderLineId = '' let orderLineId = ''
@@ -3137,7 +3127,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
continue continue
} }
/* 🔹 BEDENLİ */ /* 🔹 BEDENLİ */
for (const [bedenRaw, qtyRaw] of Object.entries(map)) { for (const [bedenRaw, qtyRaw] of Object.entries(map)) {
const isBlankBeden = safeStr(bedenRaw) === '' const isBlankBeden = safeStr(bedenRaw) === ''
if ( if (
@@ -3150,14 +3140,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
const qty = toNum(qtyRaw) const qty = toNum(qtyRaw)
// payload beden: '' / 'S' / 'M' ... // ✅ payload beden: '' / 'S' / 'M' ...
const bedenPayload = normBeden(bedenRaw) const bedenPayload = normBeden(bedenRaw)
// combokey beden: boşsa '_' ile stabil kalsın // ✅ combokey beden: boÅŸsa '_' ile stabil kalsın
const bedenKey = bedenPayload || '_' const bedenKey = bedenPayload || '_'
let orderLineId = '' let orderLineId = ''
if (this.mode === 'edit') { if (this.mode === 'edit') {
// lineIdMap anahtarı sizde hangi bedenle tutuluyorsa ikisini de dene // lineIdMap anahtarı sizde hangi bedenle tutuluyorsa ikisini de dene
orderLineId = orderLineId =
safeStr(lineIdMap?.[bedenKey]) || safeStr(lineIdMap?.[bedenKey]) ||
safeStr(lineIdMap?.[bedenPayload]) || safeStr(lineIdMap?.[bedenPayload]) ||
@@ -3184,18 +3174,18 @@ export const useOrderEntryStore = defineStore('orderentry', {
lines.forEach((ln, i) => { ln.SortOrder = i + 1 }) lines.forEach((ln, i) => { ln.SortOrder = i + 1 })
/* ======================================================= /* =======================================================
ASSERT payloadda "_" OLAMAZ ASSERT — payload’da "_" OLAMAZ
======================================================= */ ======================================================= */
if (lines.some(l => (l.ItemDim1Code || '') === '_' )) { if (lines.some(l => (l.ItemDim1Code || '') === '_' )) {
console.error(' Payloadda "_" yakalandı', lines.filter(l => l.ItemDim1Code === '_')) console.error('❌ Payload’da "_" yakalandı', lines.filter(l => l.ItemDim1Code === '_'))
throw new Error('Payload ItemDim1Code "_" olamaz') throw new Error('Payload ItemDim1Code "_" olamaz')
} }
/* ======================================================= /* =======================================================
🔍 DEBUG BUILD FINAL ORDER JSON OUTPUT 🔍 DEBUG — BUILD FINAL ORDER JSON OUTPUT
======================================================= */ ======================================================= */
console.groupCollapsed('%c📦 BUILD FINAL ORDER JSON', 'color:#c9a873;font-weight:bold') console.groupCollapsed('%c📦 BUILD FINAL ORDER JSON', 'color:#c9a873;font-weight:bold')
console.log('🧾 HEADER:', header) console.log('🧾 HEADER:', header)
console.table( console.table(
lines.map((l, i) => ({ lines.map((l, i) => ({
@@ -3224,7 +3214,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
,/* =========================================================== ,/* ===========================================================
STORE ACTIONS FIXED HELPERS ✅ STORE ACTIONS — FIXED HELPERS
- setRowErrorByClientKey - setRowErrorByClientKey
- clearRowErrorByClientKey - clearRowErrorByClientKey
- applyTerminToRowsIfEmpty - applyTerminToRowsIfEmpty
@@ -3258,14 +3248,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
if (!dateStr) return if (!dateStr) return
if (!Array.isArray(this.summaryRows)) return if (!Array.isArray(this.summaryRows)) return
// reassign YOK patch/mutate // ❗ reassign YOK — patch/mutate
for (const r of this.summaryRows) { for (const r of this.summaryRows) {
if (!r?.terminTarihi || r.terminTarihi === '') { if (!r?.terminTarihi || r.terminTarihi === '') {
r.terminTarihi = dateStr r.terminTarihi = dateStr
} }
} }
// opsiyonel ama genelde doğru: // opsiyonel ama genelde doÄŸru:
this.persistLocalStorage?.() this.persistLocalStorage?.()
} }
@@ -3286,7 +3276,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
/* =========================================================== /* ===========================================================
🔹 BEDEN LABEL NORMALİZASYONU (exported helper) 🔹 BEDEN LABEL NORMALİZASYONU (exported helper)
=========================================================== */ =========================================================== */
export function normalizeBedenLabel(v) { export function normalizeBedenLabel(v) {
if (v === null || v === undefined) return ' ' if (v === null || v === undefined) return ' '
@@ -3304,19 +3294,19 @@ export function normalizeBedenLabel(v) {
} }
if (map[s]) return map[s] if (map[s]) return map[s]
// tamamen sayıysa string olarak döndür // tamamen sayıysa string olarak döndür
if (/^\d+$/.test(s)) return s if (/^\d+$/.test(s)) return s
// virgüllü değer geldiyse ilkini al // virgüllü deÄŸer geldiyse ilkini al
if (s.includes(',')) return s.split(',')[0].trim() if (s.includes(',')) return s.split(',')[0].trim()
return s return s
} }
/* =========================================================== /* ===========================================================
🔹 BEDEN GRUBU ALGILAMA HELPERI 🔹 BEDEN GRUBU ALGILAMA HELPER’I
----------------------------------------------------------- -----------------------------------------------------------
Gelen beden listesini, ürün grubu/kategori bilgisine göre Gelen beden listesini, ürün grubu/kategori bilgisine göre
doğru grup anahtarına dönüştürür (ayk, yas, pan, gom, tak, aksbir). doÄŸru grup anahtarına dönüştürür (ayk, yas, pan, gom, tak, aksbir).
----------------------------------------------------------- -----------------------------------------------------------
=========================================================== */ =========================================================== */
export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = '') { export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = '') {
@@ -3328,25 +3318,25 @@ export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = ''
.toUpperCase() .toUpperCase()
.trim() .trim()
.replace(/\(.*?\)/g, '') .replace(/\(.*?\)/g, '')
.replace(/[^A-ZÇĞİÖŞÜ0-9\s]/g, '') .replace(/[^A-ZÇĞİÖŞÜ0-9\s]/g, '')
.replace(/\s+/g, ' ') .replace(/\s+/g, ' ')
const kat = (urunKategori || '').toUpperCase().trim() const kat = (urunKategori || '').toUpperCase().trim()
// 🔸 Aksesuar ise "aksbir" // 🔸 Aksesuar ise "aksbir"
const aksesuarGruplari = [ const aksesuarGruplari = [
'AKSESUAR','KRAVAT','PAPYON','KEMER','CORAP','ÇORAP', 'AKSESUAR','KRAVAT','PAPYON','KEMER','CORAP','ÇORAP',
'FULAR','MENDIL','MENDİL','KASKOL','ASKI', 'FULAR','MENDIL','MENDİL','KASKOL','ASKI',
'YAKA','KOL DUGMESI','KOL DÜĞMESİ' 'YAKA','KOL DUGMESI','KOL DÜĞMESİ'
] ]
const giyimGruplari = ['GÖMLEK','CEKET','PANTOLON','MONT','YELEK','TAKIM','TSHIRT','TİŞÖRT'] const giyimGruplari = ['GÖMLEK','CEKET','PANTOLON','MONT','YELEK','TAKIM','TSHIRT','TİŞÖRT']
// 🔸 Pantolon özel durumu // 🔸 Pantolon özel durumu
if ( if (
aksesuarGruplari.some(g => ana.includes(g) || kat.includes(g)) && aksesuarGruplari.some(g => ana.includes(g) || kat.includes(g)) &&
!giyimGruplari.some(g => ana.includes(g)) !giyimGruplari.some(g => ana.includes(g))
) return 'aksbir' ) return 'aksbir'
if (ana.includes('PANTOLON') && kat.includes('YETİŞKİN')) return 'pan' if (ana.includes('PANTOLON') && kat.includes('YETİŞKİN')) return 'pan'
// 🔸 Tamamen numerik (örneğin 39-44 arası) → ayakkabı // 🔸 Tamamen numerik (örneÄŸin 39-44 arası) → ayakkabı
const allNumeric = list.every(v => /^\d+$/.test(v)) const allNumeric = list.every(v => /^\d+$/.test(v))
if (allNumeric) { if (allNumeric) {
const nums = list.map(v => parseInt(v, 10)).filter(Boolean) const nums = list.map(v => parseInt(v, 10)).filter(Boolean)
@@ -3354,17 +3344,17 @@ export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = ''
if (diffs.every(d => d === 1) && nums[0] >= 35 && nums[0] <= 46) return 'ayk' if (diffs.every(d => d === 1) && nums[0] >= 35 && nums[0] <= 46) return 'ayk'
} }
// 🔸 Yaş grubu (çocuk/garson) // 🔸 YaÅŸ grubu (çocuk/garson)
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','2XL','3XL','4XL','5XL','6XL','7XL'] 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'
// 🔸 Varsayılan: takım elbise // 🔸 Varsayılan: takım elbise
return 'tak' return 'tak'
} }
@@ -3421,9 +3411,9 @@ export function toSummaryRowFromForm(form) {
/* =========================================================== /* ===========================================================
🔹 TOPLAM HESAPLAMA (EXPORT) 🔹 TOPLAM HESAPLAMA (EXPORT)
----------------------------------------------------------- -----------------------------------------------------------
Hem store içinde hem de component tarafında kullanılabilir. Hem store içinde hem de component tarafında kullanılabilir.
=========================================================== */ =========================================================== */
export function updateTotals(f) { export function updateTotals(f) {
f.adet = (f.bedenler || []).reduce((a, b) => a + Number(b || 0), 0) f.adet = (f.bedenler || []).reduce((a, b) => a + Number(b || 0), 0)
@@ -3433,11 +3423,11 @@ export function updateTotals(f) {
} }
/* =========================================================== /* ===========================================================
🔹 EXPORT SET Tek Merkezli Dışa Aktarımlar 🔹 EXPORT SET — Tek Merkezli Dışa Aktarımlar
=========================================================== */ =========================================================== */
/** /**
* 🧩 Shared Reactive Refs * 🧩 Shared Reactive Refs
* ----------------------------------------------------------- * -----------------------------------------------------------
* import { sharedOrderEntryRefs } from 'src/stores/orderentryStore' * import { sharedOrderEntryRefs } from 'src/stores/orderentryStore'
* const { stockMap, bedenStock, sizeCache } = sharedOrderEntryRefs * const { stockMap, bedenStock, sizeCache } = sharedOrderEntryRefs