Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -794,7 +794,27 @@ function toText (value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toNumber (value) {
|
function toNumber (value) {
|
||||||
const n = Number(String(value ?? '0').replace(/\./g, '').replace(',', '.'))
|
if (typeof value === 'number') return Number.isFinite(value) ? value : 0
|
||||||
|
const text = String(value ?? '').trim().replace(/\s/g, '')
|
||||||
|
if (!text) return 0
|
||||||
|
const lastComma = text.lastIndexOf(',')
|
||||||
|
const lastDot = text.lastIndexOf('.')
|
||||||
|
let normalized = text
|
||||||
|
if (lastComma >= 0 && lastDot >= 0) {
|
||||||
|
normalized = lastComma > lastDot
|
||||||
|
? text.replace(/\./g, '').replace(',', '.')
|
||||||
|
: text.replace(/,/g, '')
|
||||||
|
} else if (lastComma >= 0) {
|
||||||
|
normalized = text.replace(/\./g, '').replace(',', '.')
|
||||||
|
} else if (lastDot >= 0) {
|
||||||
|
const parts = text.split('.')
|
||||||
|
const lastPart = parts[parts.length - 1] || ''
|
||||||
|
const looksLikeThousands = parts.length > 1 && lastPart.length === 3 && parts.slice(0, -1).every((p, i) => (
|
||||||
|
i === 0 ? p.length >= 1 && p.length <= 3 : p.length === 3
|
||||||
|
))
|
||||||
|
normalized = looksLikeThousands ? text.replace(/\./g, '') : text
|
||||||
|
}
|
||||||
|
const n = Number(normalized)
|
||||||
return Number.isFinite(n) ? n : 0
|
return Number.isFinite(n) ? n : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1582,57 +1602,82 @@ function exportFileStamp () {
|
|||||||
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}`
|
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildServerPriceListExportPayload () {
|
|
||||||
const filters = columnFilters.value || {}
|
|
||||||
return {
|
|
||||||
in_stock_only: !!showInStockOnly.value,
|
|
||||||
include_meta: true,
|
|
||||||
include_cost: false,
|
|
||||||
include_base: false,
|
|
||||||
price_fields: [...selectedPriceOptions.value],
|
|
||||||
product_code: selectedProductCodes.value.length > 0 ? [...selectedProductCodes.value] : [],
|
|
||||||
brand_group: Array.isArray(filters.brandGroupSelection) ? filters.brandGroupSelection : [],
|
|
||||||
marka: Array.isArray(filters.marka) ? filters.marka : [],
|
|
||||||
askili_yan: Array.isArray(filters.askiliYan) ? filters.askiliYan : [],
|
|
||||||
kategori: Array.isArray(filters.kategori) ? filters.kategori : [],
|
|
||||||
urun_ilk_grubu: topUrunIlkGrubu.value ? [topUrunIlkGrubu.value] : (Array.isArray(filters.urunIlkGrubu) ? filters.urunIlkGrubu : []),
|
|
||||||
urun_ana_grubu: topUrunAnaGrubu.value ? [topUrunAnaGrubu.value] : (Array.isArray(filters.urunAnaGrubu) ? filters.urunAnaGrubu : []),
|
|
||||||
urun_alt_grubu: Array.isArray(filters.urunAltGrubu) ? filters.urunAltGrubu : [],
|
|
||||||
icerik: Array.isArray(filters.icerik) ? filters.icerik : [],
|
|
||||||
karisim: Array.isArray(filters.karisim) ? filters.karisim : []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function downloadBlob (blob, fileName) {
|
|
||||||
const url = URL.createObjectURL(blob)
|
|
||||||
const a = document.createElement('a')
|
|
||||||
a.href = url
|
|
||||||
a.download = fileName
|
|
||||||
document.body.appendChild(a)
|
|
||||||
a.click()
|
|
||||||
a.remove()
|
|
||||||
URL.revokeObjectURL(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function printVisibleRows () {
|
async function printVisibleRows () {
|
||||||
try {
|
const stamp = exportFileStamp()
|
||||||
const fileName = `Fiyat_Listesi-${exportFileStamp()}.pdf`
|
const title = `Fiyat_Listesi-${stamp}`
|
||||||
const res = await api.request({
|
const cols = visibleColumns.value
|
||||||
method: 'POST',
|
const generatedAt = new Date().toLocaleString('tr-TR')
|
||||||
url: '/order/price-list/export-pdf',
|
const body = filteredRows.value.map((row) => `<tr>${cols.map((c) => {
|
||||||
data: buildServerPriceListExportPayload(),
|
if (c.name === 'image') {
|
||||||
responseType: 'blob',
|
return row.imageUrl
|
||||||
timeout: 0
|
? `<td class="img-cell"><img src="${escapeHtml(row.imageUrl)}" class="thumb"></td>`
|
||||||
})
|
: '<td class="img-cell"></td>'
|
||||||
const blob = res?.data instanceof Blob
|
|
||||||
? res.data
|
|
||||||
: new Blob([res?.data || ''], { type: 'application/pdf' })
|
|
||||||
downloadBlob(blob, fileName)
|
|
||||||
await notifyExportTaken('pdf')
|
|
||||||
Notify.create({ type: 'positive', message: 'PDF indirildi.' })
|
|
||||||
} catch (err) {
|
|
||||||
Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'PDF olusturulamadi' })
|
|
||||||
}
|
}
|
||||||
|
const cls = [
|
||||||
|
isExcelNumericColumn(c) ? 'num' : '',
|
||||||
|
priceColumnNames.includes(c.name) ? 'price' : '',
|
||||||
|
c.name.endsWith('Campaign') ? 'campaign-price' : '',
|
||||||
|
c.name === 'karisim' ? 'wrap' : ''
|
||||||
|
].filter(Boolean).join(' ')
|
||||||
|
return `<td class="${cls}">${escapeHtml(exportCell(row, c))}</td>`
|
||||||
|
}).join('')}</tr>`).join('')
|
||||||
|
const headerCols = cols.map((c) => `<th>${escapeHtml(c.label || 'Gorsel')}</th>`).join('')
|
||||||
|
const html = `<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml(title)}</title><style>
|
||||||
|
@page { size: A3 landscape; margin: 8mm; }
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { font-family: Arial, sans-serif; color: #172033; font-size: 8px; margin: 0; }
|
||||||
|
table { border-collapse: collapse; width: 100%; table-layout: fixed; }
|
||||||
|
thead { display: table-header-group; }
|
||||||
|
tfoot { display: table-footer-group; }
|
||||||
|
tr { page-break-inside: avoid; break-inside: avoid; }
|
||||||
|
th, td { border: 1px solid #cfd6df; padding: 3px; vertical-align: middle; overflow-wrap: anywhere; }
|
||||||
|
th { background: #957116; color: #fff; text-align: center; font-weight: 700; line-height: 1.15; }
|
||||||
|
.report-title th { background: #fff; color: #172033; border: 0; padding: 0 0 5px; text-align: left; }
|
||||||
|
.title-main { font-size: 15px; font-weight: 800; color: #957116; }
|
||||||
|
.title-meta { font-size: 8px; color: #56616f; margin-top: 2px; }
|
||||||
|
td { background: #fff; line-height: 1.18; }
|
||||||
|
.num, .price { text-align: right; font-weight: 700; white-space: nowrap; }
|
||||||
|
.campaign-price { background: #eef7ee; color: #0f6b2f; font-weight: 800; }
|
||||||
|
.wrap { white-space: normal; font-size: 7px; line-height: 1.1; }
|
||||||
|
.img-cell { width: 28mm; text-align: center; padding: 2px; }
|
||||||
|
.thumb { width: 24mm; height: 24mm; object-fit: contain; display: block; margin: 0 auto; }
|
||||||
|
.page-footer {
|
||||||
|
position: fixed;
|
||||||
|
right: 8mm;
|
||||||
|
bottom: 3mm;
|
||||||
|
font-size: 8px;
|
||||||
|
color: #56616f;
|
||||||
|
}
|
||||||
|
.page-footer::after {
|
||||||
|
content: counter(page) "/" counter(pages);
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
thead { display: table-header-group; }
|
||||||
|
tr { page-break-inside: avoid; }
|
||||||
|
}
|
||||||
|
</style></head><body>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr class="report-title"><th colspan="${cols.length}">
|
||||||
|
<div class="title-main">Fiyat Listesi</div>
|
||||||
|
<div class="title-meta">Tarih: ${escapeHtml(generatedAt)} | Satir: ${filteredRows.value.length} | Fiyatlar: ${escapeHtml(selectedPriceOptions.value.join(', '))}</div>
|
||||||
|
</th></tr>
|
||||||
|
<tr>${headerCols}</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>${body}</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="page-footer"></div>
|
||||||
|
<script>window.onload=function(){setTimeout(function(){window.print()},250)}<\/script>
|
||||||
|
</body></html>`
|
||||||
|
const win = window.open('', '_blank')
|
||||||
|
if (!win) {
|
||||||
|
Notify.create({ type: 'negative', message: 'PDF yazdirma penceresi acilamadi.' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
win.document.open()
|
||||||
|
win.document.write(html)
|
||||||
|
win.document.close()
|
||||||
|
await notifyExportTaken('pdf')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function notifyExportTaken (format) {
|
async function notifyExportTaken (format) {
|
||||||
@@ -1851,6 +1896,10 @@ onMounted(() => {
|
|||||||
z-index: 20;
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.top-x-scroll:hover {
|
||||||
|
cursor: ew-resize;
|
||||||
|
}
|
||||||
|
|
||||||
.top-x-scroll-inner {
|
.top-x-scroll-inner {
|
||||||
height: 18px;
|
height: 18px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1756,7 +1756,12 @@ function parseNumber (val) {
|
|||||||
} else if (lastComma >= 0) {
|
} else if (lastComma >= 0) {
|
||||||
normalized = text.replace(/\./g, '').replace(',', '.')
|
normalized = text.replace(/\./g, '').replace(',', '.')
|
||||||
} else {
|
} else {
|
||||||
normalized = text.replace(/,/g, '')
|
const parts = text.split('.')
|
||||||
|
const lastPart = parts[parts.length - 1] || ''
|
||||||
|
const looksLikeThousands = lastDot >= 0 && parts.length > 1 && lastPart.length === 3 && parts.slice(0, -1).every((p, i) => (
|
||||||
|
i === 0 ? p.length >= 1 && p.length <= 3 : p.length === 3
|
||||||
|
))
|
||||||
|
normalized = looksLikeThousands ? text.replace(/\./g, '') : text.replace(/,/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
const n = Number(normalized)
|
const n = Number(normalized)
|
||||||
@@ -2323,26 +2328,26 @@ async function saveSelectedRows () {
|
|||||||
const payload = {
|
const payload = {
|
||||||
items: list.map((r) => ({
|
items: list.map((r) => ({
|
||||||
product_code: String(r?.productCode || '').trim(),
|
product_code: String(r?.productCode || '').trim(),
|
||||||
base_price_usd: Number(r?.basePriceUsd ?? 0),
|
base_price_usd: parseNumber(r?.basePriceUsd),
|
||||||
base_price_try: Number(r?.basePriceTry ?? 0),
|
base_price_try: parseNumber(r?.basePriceTry),
|
||||||
usd1: Number(r?.usd1 ?? 0),
|
usd1: parseNumber(r?.usd1),
|
||||||
usd2: Number(r?.usd2 ?? 0),
|
usd2: parseNumber(r?.usd2),
|
||||||
usd3: Number(r?.usd3 ?? 0),
|
usd3: parseNumber(r?.usd3),
|
||||||
usd4: Number(r?.usd4 ?? 0),
|
usd4: parseNumber(r?.usd4),
|
||||||
usd5: Number(r?.usd5 ?? 0),
|
usd5: parseNumber(r?.usd5),
|
||||||
usd6: Number(r?.usd6 ?? 0),
|
usd6: parseNumber(r?.usd6),
|
||||||
eur1: Number(r?.eur1 ?? 0),
|
eur1: parseNumber(r?.eur1),
|
||||||
eur2: Number(r?.eur2 ?? 0),
|
eur2: parseNumber(r?.eur2),
|
||||||
eur3: Number(r?.eur3 ?? 0),
|
eur3: parseNumber(r?.eur3),
|
||||||
eur4: Number(r?.eur4 ?? 0),
|
eur4: parseNumber(r?.eur4),
|
||||||
eur5: Number(r?.eur5 ?? 0),
|
eur5: parseNumber(r?.eur5),
|
||||||
eur6: Number(r?.eur6 ?? 0),
|
eur6: parseNumber(r?.eur6),
|
||||||
try1: Number(r?.try1 ?? 0),
|
try1: parseNumber(r?.try1),
|
||||||
try2: Number(r?.try2 ?? 0),
|
try2: parseNumber(r?.try2),
|
||||||
try3: Number(r?.try3 ?? 0),
|
try3: parseNumber(r?.try3),
|
||||||
try4: Number(r?.try4 ?? 0),
|
try4: parseNumber(r?.try4),
|
||||||
try5: Number(r?.try5 ?? 0),
|
try5: parseNumber(r?.try5),
|
||||||
try6: Number(r?.try6 ?? 0)
|
try6: parseNumber(r?.try6)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1830,7 +1830,12 @@ function parseNumber (val) {
|
|||||||
} else if (lastComma >= 0) {
|
} else if (lastComma >= 0) {
|
||||||
normalized = text.replace(/\./g, '').replace(',', '.')
|
normalized = text.replace(/\./g, '').replace(',', '.')
|
||||||
} else {
|
} else {
|
||||||
normalized = text.replace(/,/g, '')
|
const parts = text.split('.')
|
||||||
|
const lastPart = parts[parts.length - 1] || ''
|
||||||
|
const looksLikeThousands = lastDot >= 0 && parts.length > 1 && lastPart.length === 3 && parts.slice(0, -1).every((p, i) => (
|
||||||
|
i === 0 ? p.length >= 1 && p.length <= 3 : p.length === 3
|
||||||
|
))
|
||||||
|
normalized = looksLikeThousands ? text.replace(/\./g, '') : text.replace(/,/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
const n = Number(normalized)
|
const n = Number(normalized)
|
||||||
@@ -2584,7 +2589,7 @@ function applyCampaignDerived (row) {
|
|||||||
row.campaignRate = rate > 0 ? rate : null
|
row.campaignRate = rate > 0 ? rate : null
|
||||||
|
|
||||||
for (const p of campaignPricePairs) {
|
for (const p of campaignPricePairs) {
|
||||||
const base = Number(row?.[p.base] ?? 0)
|
const base = parseNumber(row?.[p.base])
|
||||||
if (!cid || !(rate > 0) || !(base > 0)) {
|
if (!cid || !(rate > 0) || !(base > 0)) {
|
||||||
row[p.derived] = null
|
row[p.derived] = null
|
||||||
continue
|
continue
|
||||||
@@ -2593,9 +2598,9 @@ function applyCampaignDerived (row) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Single "taban altinda" diff: compare in TRY space for all visible currencies.
|
// Single "taban altinda" diff: compare in TRY space for all visible currencies.
|
||||||
let baseTry = Number(row?.basePriceTry ?? 0)
|
let baseTry = parseNumber(row?.basePriceTry)
|
||||||
if (!(baseTry > 0)) {
|
if (!(baseTry > 0)) {
|
||||||
const baseUsd = Number(row?.basePriceUsd ?? 0)
|
const baseUsd = parseNumber(row?.basePriceUsd)
|
||||||
baseTry = baseUsd > 0 ? (baseUsd * usdToTry) : 0
|
baseTry = baseUsd > 0 ? (baseUsd * usdToTry) : 0
|
||||||
}
|
}
|
||||||
let minDiff = null
|
let minDiff = null
|
||||||
@@ -2603,7 +2608,7 @@ function applyCampaignDerived (row) {
|
|||||||
const selected = selectedPriceOptionSet.value
|
const selected = selectedPriceOptionSet.value
|
||||||
for (const p of campaignPricePairs) {
|
for (const p of campaignPricePairs) {
|
||||||
if (!selected.has(p.base)) continue
|
if (!selected.has(p.base)) continue
|
||||||
const v = Number(row?.[p.derived] ?? 0)
|
const v = parseNumber(row?.[p.derived])
|
||||||
if (!(v > 0)) continue
|
if (!(v > 0)) continue
|
||||||
const tryVal = p.currency === 'USD'
|
const tryVal = p.currency === 'USD'
|
||||||
? v * usdToTry
|
? v * usdToTry
|
||||||
|
|||||||
Reference in New Issue
Block a user