This commit is contained in:
2026-02-11 17:46:22 +03:00
commit eacfacb13b
266 changed files with 51337 additions and 0 deletions

653
ui/src/stores/deneme3 Normal file
View File

@@ -0,0 +1,653 @@
// 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
}