Files
bssapp/ui/src/stores/deneme3
2026-02-11 17:46:22 +03:00

654 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// src/stores/orderentryStore.js
import { defineStore } from 'pinia'
import axios from 'axios'
import qs from 'qs'
import { useAuthStore } from 'src/stores/authStore'
import dayjs from 'src/boot/dayjs'
import { ref, watch } from 'vue'
/* ==========================================================
Reaktif shared referanslar (bazı UI yardımcıları)
========================================================== */
const stockMap = ref({}) // { "48": 12, "50": 7, ... }
const bedenStock = ref([]) // [{beden:'48', stok:12}, ...]
const sizeCache = ref({}) // beden/stok cache (component tarafı çağırıyor)
/* ==========================================================
STORE
========================================================== */
export const useOrderentryStore = defineStore('orderentry', {
state: () => ({
/* 🔹 Ana durumlar */
orders: [], // grid kaynak arrayi (summaryRows ile senkron)
loading: false,
selected: null, // UIde seçili satır
error: null,
/* 🔹 Cari */
customers: [],
selectedCustomer: null,
/* 🔹 Ürün zinciri */
products: [],
colors: [],
secondColors: [],
inventory: [],
selectedProduct: null,
selectedColor: null,
selectedColor2: null,
/* 🔹 Transaction & Storage */
activeTransactionId: null,
persistKey: 'bss_orderentry_data', // ♻️ kalıcı depolama keyi
lastSnapshotKey: 'bss_orderentry_snapshot', // son-kaydedilen-sipariş
/* 🔹 Düzenleme durumu */
editingIndex: -1,
currentOrderId: null, // edit modunda header ID
header: {}, // backend header modeli
mode: 'new' // 'new' | 'edit'
}),
getters: {
/* 🔹 Toplam adet */
totalQty(state) {
return state.orders.reduce((sum, r) => sum + (Number(r.adet) || 0), 0)
},
/* 🔹 Toplam tutar (string fix2) */
totalAmount(state) {
const n = state.orders.reduce((s, r) => s + (Number(r.tutar) || 0), 0)
return isNaN(n) ? '0.00' : n.toFixed(2)
},
/* 🔹 Müşteri bazlı gruplanmış (opsiyonel) */
groupedByCustomer(state) {
const out = {}
for (const row of state.orders) {
const k = row.musteri || '—'
if (!out[k]) out[k] = []
out[k].push(row)
}
return out
},
/* 🔹 2. renk var mı? */
hasSecondColor(state) {
return Array.isArray(state.secondColors) && state.secondColors.length > 0
},
/* 🔹 Envanter toplamı */
totalInventoryQty(state) {
return state.inventory.reduce((s, r) => s + (Number(r.kullanilabilir) || 0), 0)
}
},
actions: {
/* ==========================================================
STORAGE — Kalıcı kayıt yardımcıları
========================================================== */
saveToStorage() {
try {
const payload = {
orders: this.orders,
header: this.header,
currentOrderId: this.currentOrderId,
selectedCustomer: this.selectedCustomer,
activeTransactionId: this.activeTransactionId,
mode: this.mode,
savedAt: dayjs().toISOString()
}
localStorage.setItem(this.persistKey, JSON.stringify(payload))
} catch (err) {
console.warn('⚠️ localStorage kaydı başarısız:', err)
}
},
/* Kayıt sonrası görüntülenecek "snapshot".
UIyi temizlesen bile sayfa yenilenince bu snapshot geri yüklenebilir. */
saveSnapshot(tag = 'post-submit') {
try {
const snap = {
tag,
orders: this.orders,
header: this.header,
currentOrderId: this.currentOrderId,
selectedCustomer: this.selectedCustomer,
mode: this.mode,
savedAt: dayjs().toISOString()
}
localStorage.setItem(this.lastSnapshotKey, JSON.stringify(snap))
} catch (e) {
console.warn('⚠️ saveSnapshot hatası:', e)
}
},
loadFromStorage() {
try {
const raw = localStorage.getItem(this.persistKey)
if (!raw) return
const data = JSON.parse(raw)
if (Array.isArray(data.orders)) this.orders = data.orders
this.header = data.header || {}
this.currentOrderId = data.currentOrderId || null
this.selectedCustomer = data.selectedCustomer || null
this.activeTransactionId = data.activeTransactionId || null
this.mode = data.mode || 'new'
console.log(`♻️ Storage yüklendi • mode:${this.mode} • rows:${this.orders.length}`)
} catch (err) {
console.warn('⚠️ localStorage okuma hatası:', err)
}
},
loadSnapshot() {
try {
const raw = localStorage.getItem(this.lastSnapshotKey)
if (!raw) return null
return JSON.parse(raw)
} catch (e) {
console.warn('⚠️ loadSnapshot hatası:', e)
return null
}
},
clearStorage() {
localStorage.removeItem(this.persistKey)
// snapshotı silmiyoruz → kullanıcı isterse elle siler
},
/* ==========================================================
TRANSACTION STATE
========================================================== */
setTransaction(id) {
this.activeTransactionId = id
this.saveToStorage()
},
async initTransaction() {
if (this.activeTransactionId) {
console.log('🔹 Aktif transaction:', this.activeTransactionId)
return this.activeTransactionId
}
try {
const dummyId = Math.floor(100000 + Math.random() * 900000)
this.activeTransactionId = dummyId
this.saveToStorage()
console.log('🧩 Dummy Transaction başlatıldı:', dummyId)
return dummyId
} catch (err) {
console.error('❌ Dummy transaction başlatılamadı:', err)
return null
}
},
clearTransaction() {
this.activeTransactionId = null
this.saveToStorage()
},
/* Ordersı otomatik kaydeden watcher (componentten çağrılır) */
watchOrders() {
watch(
() => this.orders,
() => {
// her değişimde full storage yaz
this.saveToStorage()
},
{ deep: true }
)
},
/* ==========================================================
CRUD — Frontend gridi ile senkron temel aksiyonlar
========================================================== */
addRow(row) {
if (!row) return
this.orders.push({ ...row })
this.saveToStorage()
},
updateRow(idOrIndex, patch) {
if (idOrIndex == null) return
let idx = -1
if (typeof idOrIndex === 'number') {
idx = idOrIndex
} else {
// id ile bul
idx = this.orders.findIndex(r => r.id === idOrIndex)
}
if (idx >= 0 && this.orders[idx]) {
this.orders[idx] = { ...this.orders[idx], ...patch }
this.saveToStorage()
}
},
removeRow(idOrIndex) {
let idx = -1
if (typeof idOrIndex === 'number') {
idx = idOrIndex
} else {
idx = this.orders.findIndex(r => r.id === idOrIndex)
}
if (idx >= 0) {
this.orders.splice(idx, 1)
this.saveToStorage()
}
},
/* ==========================================================
PRICE / LIMIT — Minimum fiyat sorgusu (model + PB)
Beklenen response: { price, priceTRY, rateToTRY }
========================================================== */
async fetchMinPrice(modelCode, pb) {
if (!modelCode || !pb) return null
try {
const baseURL = 'http://localhost:8080'
const res = await axios.get(`${baseURL}/api/min-price`, {
params: { code: modelCode, pb }
})
const d = res?.data || null
if (!d) return null
// normalize
return {
price: Number(d.price ?? d.Price ?? 0),
priceTRY: Number(d.priceTRY ?? d.PriceTRY ?? d.price_try ?? 0),
rateToTRY: Number(d.rateToTRY ?? d.RateToTRY ?? d.rate ?? 1)
}
} catch (e) {
console.warn('⚠️ fetchMinPrice hata:', e)
return null
}
},
/* ==========================================================
LOAD (EDIT MODE) — Sunucudan Siparişi Açma
========================================================== */
async openById(id) {
if (!id) return
this.loading = true
try {
const auth = useAuthStore()
const res = await axios.get(`http://localhost:8080/api/order/get/${id}`, {
headers: { Authorization: `Bearer ${auth.token}` }
})
const data = res.data || {}
// 🔹 sql.Null* flatten helper
const flat = (v) => {
if (v === null || v === undefined) return null
if (typeof v === 'object' && 'Valid' in v) {
return v.Valid
? v.String ?? v.Float64 ?? v.Int32 ?? v.Time ?? null
: null
}
return v
}
/* ============================================================
🧾 HEADER MAPPING (73 kolon)
============================================================ */
const h = data.header || {}
const header = {
// Görünen alanlar
OrderHeaderID: flat(h.OrderHeaderID) || '',
OrderNumber: flat(h.OrderNumber) || '',
OrderDate: flat(h.OrderDate)
? String(flat(h.OrderDate)).substring(0, 10)
: '',
AverageDueDate: flat(h.AverageDueDate)
? String(flat(h.AverageDueDate)).substring(0, 10)
: '',
Description: flat(h.Description) || '',
CurrAccCode: flat(h.CurrAccCode) || '',
DocCurrencyCode: flat(h.DocCurrencyCode) || 'TRY',
// Arka plan alanlar (backend roundtrip)
OrderTypeCode: flat(h.OrderTypeCode) || 1,
ProcessCode: flat(h.ProcessCode) || 'WS',
IsCancelOrder: flat(h.IsCancelOrder) || 0,
OrderTime: flat(h.OrderTime) || '',
DocumentNumber: flat(h.DocumentNumber) || '',
PaymentTerm: flat(h.PaymentTerm) || '',
InternalDescription: flat(h.InternalDescription) || '',
CurrAccTypeCode: flat(h.CurrAccTypeCode) || '',
SubCurrAccID: flat(h.SubCurrAccID) || '',
ContactID: flat(h.ContactID) || '',
ShipmentMethodCode: flat(h.ShipmentMethodCode) || '',
ShippingPostalAddressID: flat(h.ShippingPostalAddressID) || '',
BillingPostalAddressID: flat(h.BillingPostalAddressID) || '',
GuarantorContactID: flat(h.GuarantorContactID) || '',
GuarantorContactID2: flat(h.GuarantorContactID2) || '',
RoundsmanCode: flat(h.RoundsmanCode) || '',
DeliveryCompanyCode: flat(h.DeliveryCompanyCode) || '',
TaxTypeCode: flat(h.TaxTypeCode) || '',
WithHoldingTaxTypeCode: flat(h.WithHoldingTaxTypeCode) || '',
DOVCode: flat(h.DOVCode) || '',
TaxExemptionCode: flat(h.TaxExemptionCode) || 0,
CompanyCode: flat(h.CompanyCode) || 1,
OfficeCode: flat(h.OfficeCode) || '101',
StoreTypeCode: flat(h.StoreTypeCode) || 5,
StoreCode: flat(h.StoreCode) || 0,
POSTerminalID: flat(h.POSTerminalID) || 0,
WarehouseCode: flat(h.WarehouseCode) || '1-0-12',
ToWarehouseCode: flat(h.ToWarehouseCode) || '',
OrdererCompanyCode: flat(h.OrdererCompanyCode) || 1,
OrdererOfficeCode: flat(h.OrdererOfficeCode) || '101',
OrdererStoreCode: flat(h.OrdererStoreCode) || '',
GLTypeCode: flat(h.GLTypeCode) || '',
LocalCurrencyCode: flat(h.LocalCurrencyCode) || 'TRY',
ExchangeRate: flat(h.ExchangeRate) || 1,
DiscountReasonCode: flat(h.DiscountReasonCode) || 0,
SurplusOrderQtyToleranceRate: flat(h.SurplusOrderQtyToleranceRate) || 0,
IncotermCode1: flat(h.IncotermCode1) || '',
IncotermCode2: flat(h.IncotermCode2) || '',
PaymentMethodCode: flat(h.PaymentMethodCode) || '',
IsInclutedVat: flat(h.IsInclutedVat) || 0,
IsCreditSale: flat(h.IsCreditSale) || 1,
IsCreditableConfirmed: flat(h.IsCreditableConfirmed) || 1,
CreditableConfirmedUser: flat(h.CreditableConfirmedUser) || '',
CreditableConfirmedDate: flat(h.CreditableConfirmedDate) || '',
ApplicationCode: flat(h.ApplicationCode) || 'Order',
ApplicationID: flat(h.ApplicationID) || '',
CreatedUserName: flat(h.CreatedUserName) || '',
CreatedDate: flat(h.CreatedDate) || '',
LastUpdatedUserName: flat(h.LastUpdatedUserName) || '',
LastUpdatedDate: flat(h.LastUpdatedDate) || '',
IsProposalBased: flat(h.IsProposalBased) || 0
}
this.header = header
this.currentOrderId = header.OrderHeaderID || id
this.mode = 'edit'
// 🔹 Cari görünümü (QSelect)
this.selectedCustomer = {
value: header.CurrAccCode || '',
label: `${header.CurrAccCode || ''} - ${flat(h.CurrAccDescription) || ''}`
}
/* ============================================================
📦 LINES MAPPING (57 kolon)
============================================================ */
this.orders = (data.lines || []).map((l, idx) => ({
// Görünen alanlar
id: flat(l.OrderLineID) || `row-${idx + 1}`,
model: flat(l.ItemCode),
renk: flat(l.ColorCode),
renk2: flat(l.ItemDim2Code),
fiyat: Number(flat(l.Price) || 0),
pb: flat(l.DocCurrencyCode) || flat(l.PriceCurrencyCode) || 'USD',
adet: Number(flat(l.Qty1) || 0),
tutar: Number(flat(l.Price) || 0) * Number(flat(l.Qty1) || 0),
aciklama: flat(l.LineDescription) || '',
terminTarihi: flat(l.DeliveryDate)
? String(flat(l.DeliveryDate)).substring(0, 10)
: '',
urunAnaGrubu: flat(l.ProductGroup) || '',
urunAltGrubu: flat(l.ProductSubGroup) || '',
grpKey: l.grpKey || 'tak',
bedenMap: l.BedenMap || {},
// Backend roundtrip alanları
SortOrder: flat(l.SortOrder) || 0,
ItemTypeCode: flat(l.ItemTypeCode) || 1,
ItemDim1Code: flat(l.ItemDim1Code) || '',
ItemDim3Code: flat(l.ItemDim3Code) || '',
Qty2: flat(l.Qty2) || 0,
CancelQty1: flat(l.CancelQty1) || 0,
CancelQty2: flat(l.CancelQty2) || 0,
CancelDate: flat(l.CancelDate) || null,
OrderCancelReasonCode: flat(l.OrderCancelReasonCode) || '',
ClosedDate: flat(l.ClosedDate) || null,
IsClosed: flat(l.IsClosed) || false,
VatRate: flat(l.VatRate) || 10,
PCTRate: flat(l.PCTRate) || 0,
PriceCurrencyCode: flat(l.PriceCurrencyCode) || 'TRY',
PriceExchangeRate: flat(l.PriceExchangeRate) || header.ExchangeRate || 1,
CreatedUserName: flat(l.CreatedUserName) || '',
CreatedDate: flat(l.CreatedDate) || '',
LastUpdatedUserName: flat(l.LastUpdatedUserName) || '',
LastUpdatedDate: flat(l.LastUpdatedDate) || '',
SurplusOrderQtyToleranceRate:
flat(l.SurplusOrderQtyToleranceRate) || 0
}))
/* ============================================================
💾 LOCAL STORAGE
============================================================ */
localStorage.setItem(
`bssapp:order:last:${id}`,
JSON.stringify({ header, lines: this.orders })
)
console.log(`📦 Sipariş (${id}) yüklendi • rows:${this.orders.length}`)
} catch (err) {
console.error('❌ openById hatası:', err)
this.error = err.message
} finally {
this.loading = false
}
}
,
/* ==========================================================
NEW TEMPLATE — Yeni sipariş başlatma
========================================================== */
newOrderTemplate() {
const today = dayjs().format('YYYY-MM-DD')
const due = dayjs().add(30, 'day').format('YYYY-MM-DD')
this.header = {
OrderHeaderID: '',
OrderTypeCode: 1,
ProcessCode: 'WS',
OrderNumber: '',
OrderDate: today,
AverageDueDate: due,
Description: '',
CurrAccCode: '',
CurrAccDescription: '',
DocCurrencyCode: 'USD',
LocalCurrencyCode: 'TRY',
ExchangeRate: 1,
CompanyCode: 1,
OfficeCode: '101',
StoreTypeCode: 5,
WarehouseCode: '1-0-12',
IsCreditSale: true,
CreatedUserName: '',
CreatedDate: today,
LastUpdatedUserName: '',
LastUpdatedDate: today
}
this.orders = []
this.currentOrderId = null
this.activeTransactionId = null
this.selectedCustomer = null
this.mode = 'new'
this.error = null
// Temiz bir başlangıcı storagea yaz
this.saveToStorage()
console.log('🧾 Yeni sipariş template yüklendi.')
},
/* ==========================================================
SUBMIT — Create/Update (SQL tablo INSERT/UPDATE)
➜ Kayıt sonrası: transaction kapanır AMA snapshot tutulur.
========================================================== */
async submitAll() {
const auth = useAuthStore()
const baseURL = 'http://localhost:8080'
const toNullable = (v, type = 'string') => {
if (v === null || v === undefined || v === '') {
if (type === 'number') return { Float64: 0, Valid: false }
if (type === 'time') return { Time: null, Valid: false }
return { String: '', Valid: false }
}
if (type === 'number') return { Float64: Number(v), Valid: true }
if (type === 'time') return { Time: v, Valid: true }
return { String: String(v), Valid: true }
}
try {
this.loading = true
// Header payload (backendin beklediği Null* formatıyla)
const h = this.header || {}
const headerPayload = {
OrderHeaderID: h.OrderHeaderID || this.currentOrderId || '',
OrderTypeCode: toNullable(1, 'number'),
ProcessCode: toNullable('WS'),
OrderNumber: toNullable(h.OrderNumber),
OrderDate: toNullable(h.OrderDate || dayjs().format('YYYY-MM-DD'), 'time'),
AverageDueDate: toNullable(h.AverageDueDate || dayjs().add(30, 'day').format('YYYY-MM-DD'), 'time'),
Description: toNullable(h.Description || ''),
CurrAccCode: toNullable(h.CurrAccCode || this.selectedCustomer?.value || ''),
CurrAccDescription: toNullable(h.CurrAccDescription || this.selectedCustomer?.label || ''),
DocCurrencyCode: toNullable(h.DocCurrencyCode || 'USD'),
LocalCurrencyCode: toNullable(h.LocalCurrencyCode || 'TRY'),
ExchangeRate: toNullable(h.ExchangeRate || 1, 'number'),
CompanyCode: toNullable(1, 'number'),
OfficeCode: toNullable('101'),
StoreTypeCode: toNullable(5, 'number'),
WarehouseCode: toNullable(h.WarehouseCode || '1-0-12'),
IsCreditSale: true,
CreatedUserName: toNullable(auth.user?.Username || 'admin'),
CreatedDate: toNullable(h.CreatedDate || dayjs().format('YYYY-MM-DD'), 'time'),
LastUpdatedUserName: toNullable(auth.user?.Username || 'admin'),
LastUpdatedDate: toNullable(dayjs().format('YYYY-MM-DD HH:mm:ss'), 'time')
}
// Lines payload
const linesPayload = this.orders.map((l, idx) => ({
OrderLineID: l.id || '',
SortOrder: idx + 1,
ItemTypeCode: toNullable(1, 'number'),
ItemCode: toNullable(l.model),
ColorCode: toNullable(l.renk),
ItemDim1Code: toNullable(Object.keys(l.bedenMap?.[l.grpKey] || {})[0] || ''),
ItemDim2Code: toNullable(l.renk2),
Qty1: toNullable(Number(l.adet || 0), 'number'),
Price: toNullable(Number(l.fiyat || 0), 'number'),
DocCurrencyCode: toNullable(l.pb || 'USD'),
VatRate: toNullable(10, 'number'),
PCTRate: toNullable(0, 'number'),
DeliveryDate: toNullable(l.terminTarihi || null, 'time'),
LineDescription: toNullable(l.aciklama || ''),
IsClosed: false,
CreatedUserName: toNullable(auth.user?.Username || 'admin'),
CreatedDate: toNullable(dayjs().format('YYYY-MM-DD HH:mm:ss'), 'time'),
LastUpdatedUserName: toNullable(auth.user?.Username || 'admin'),
LastUpdatedDate: toNullable(dayjs().format('YYYY-MM-DD HH:mm:ss'), 'time')
}))
// Final payload
const payload = {
header: headerPayload,
lines: linesPayload,
user: auth.user?.Username || 'admin'
}
let res
if (this.currentOrderId) {
// UPDATE
res = await axios.post(`${baseURL}/api/order/update`, payload, {
headers: { Authorization: `Bearer ${auth.token}` }
})
console.log('✅ UPDATE ok:', res.data)
} else {
// CREATE
res = await axios.post(`${baseURL}/api/order/create`, payload, {
headers: { Authorization: `Bearer ${auth.token}` }
})
console.log('✅ CREATE ok:', res.data)
if (res.data?.orderID) {
this.currentOrderId = res.data.orderID
this.header.OrderHeaderID = res.data.orderID
this.mode = 'edit'
}
}
// 🟩 Kayıt sonrası: snapshotı al ve storagea da yaz
this.saveSnapshot('post-submit')
this.saveToStorage()
// 🧹 Transactionı kapat (UI temizliği ayrı fonksiyonda)
this.clearTransaction()
this.afterSubmit({ keepLocalStorage: true }) // 👈 önemli
} catch (err) {
console.error('❌ submitAll hatası:', err)
this.error = err.message
throw err
} finally {
this.loading = false
}
},
/* ==========================================================
AFTER SUBMIT — UI temizliği (snapshot kalır!)
keepLocalStorage=true → persistKey SİLİNMEZ
========================================================== */
afterSubmit(opts = { keepLocalStorage: true }) {
try {
// Snapshot zaten kaydedildi; istenirse persistKeyi bırak
if (!opts?.keepLocalStorage) {
localStorage.removeItem(this.persistKey)
} else {
// son hal zaten saveToStorage ile yazıldı — dokunma
}
// UI temizliği (hafızada formu boşaltalım)
// Ama edite dönmek istersen, snapshot/loadFromStorage ile geri getirirsin.
this.orders = []
// headerı hafızadan temizliyoruz ama snapshot yerinde.
this.header = {}
this.selectedCustomer = null
this.editingIndex = -1
// currentOrderIdyi istersen koruyabilirsin; biz editte geri yüklüyoruz.
// burada nulllıyoruz:
this.currentOrderId = null
this.mode = 'new'
this.loading = false
this.error = null
console.log('🧹 afterSubmit: UI temizlendi, snapshot storageda.')
} catch (err) {
console.warn('⚠️ afterSubmit temizleme hatası:', err)
}
},
/* ==========================================================
MANUAL UPDATE — mevcut header/lines yapılarına göre
(İsteğe bağlı kullanılır)
========================================================== */
async updateOrder() {
if (!this.currentOrderId) {
console.warn('⚠️ currentOrderId yok, update yapılamaz.')
return
}
try {
const auth = useAuthStore()
const payload = {
header: this.header,
lines: this.orders,
username: auth.user?.Username || 'admin'
}
const res = await axios.post(
'http://localhost:8080/api/order/update',
payload,
{ headers: { Authorization: `Bearer ${auth.token}` } }
)
console.log('✅ Güncelleme tamamlandı:', res.data)
// kayıt sonrası snapshot + persist
this.saveSnapshot('manual-update')
this.saveToStorage()
} catch (err) {
console.error('❌ updateOrder hatası:', err)
this.error = err.message
}
}
} // actions
}) // defineStore
// (opsiyonel) Bu referanslara component tarafından erişmek istersen:
export const sharedOrderEntryRefs = {
stockMap,
bedenStock,
sizeCache
}