Fix pricing tab warehouse totals and guard custom price DOM access
This commit is contained in:
@@ -236,7 +236,7 @@
|
|||||||
class="w-40 px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"
|
class="w-40 px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"
|
||||||
oninput="onPricingCustomPriceInput()">
|
oninput="onPricingCustomPriceInput()">
|
||||||
<label class="text-sm font-medium text-gray-700">Uplift:</label>
|
<label class="text-sm font-medium text-gray-700">Uplift:</label>
|
||||||
<input type="number" id="pricing-uplift" step="0.0001" min="0" placeholder="1.0000"
|
<input type="text" id="pricing-uplift" inputmode="decimal" placeholder="1,0000"
|
||||||
class="w-32 px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"
|
class="w-32 px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"
|
||||||
oninput="onPricingUpliftInput()">
|
oninput="onPricingUpliftInput()">
|
||||||
<button onclick="setPricingCustomPriceFromVendor()" class="px-3 py-2 bg-gray-100 text-gray-700 rounded hover:bg-gray-200 border border-gray-300 text-sm">
|
<button onclick="setPricingCustomPriceFromVendor()" class="px-3 py-2 bg-gray-100 text-gray-700 rounded hover:bg-gray-200 border border-gray-300 text-sm">
|
||||||
@@ -833,8 +833,11 @@ async function loadAllComponents() {
|
|||||||
function _bomLots() {
|
function _bomLots() {
|
||||||
return [...new Set((window._bomAllComponents || allComponents).map(c => c.lot_name).filter(Boolean))].sort();
|
return [...new Set((window._bomAllComponents || allComponents).map(c => c.lot_name).filter(Boolean))].sort();
|
||||||
}
|
}
|
||||||
|
const BOM_LOT_DATALIST_DIVIDER = '────────';
|
||||||
function _bomLotValid(v) {
|
function _bomLotValid(v) {
|
||||||
return (window._bomAllComponents || allComponents).some(c => c.lot_name === v);
|
const lot = (v || '').trim();
|
||||||
|
if (!lot || lot === BOM_LOT_DATALIST_DIVIDER) return false;
|
||||||
|
return (window._bomAllComponents || allComponents).some(c => c.lot_name === lot);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateServerCount() {
|
function updateServerCount() {
|
||||||
@@ -2308,13 +2311,25 @@ function renderSalePriceTable() {
|
|||||||
// Custom price functionality
|
// Custom price functionality
|
||||||
function calculateCustomPrice() {
|
function calculateCustomPrice() {
|
||||||
const customPriceInput = document.getElementById('custom-price-input');
|
const customPriceInput = document.getElementById('custom-price-input');
|
||||||
|
const adjustedPricesEl = document.getElementById('adjusted-prices');
|
||||||
|
const discountInfoEl = document.getElementById('discount-info');
|
||||||
|
const discountPercentEl = document.getElementById('discount-percent');
|
||||||
|
const adjustedPricesBodyEl = document.getElementById('adjusted-prices-body');
|
||||||
|
const adjustedTotalOriginalEl = document.getElementById('adjusted-total-original');
|
||||||
|
const adjustedTotalNewEl = document.getElementById('adjusted-total-new');
|
||||||
|
const adjustedTotalFinalEl = document.getElementById('adjusted-total-final');
|
||||||
|
|
||||||
|
if (!customPriceInput || !adjustedPricesEl || !discountInfoEl || !discountPercentEl ||
|
||||||
|
!adjustedPricesBodyEl || !adjustedTotalOriginalEl || !adjustedTotalNewEl || !adjustedTotalFinalEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const customPrice = parseFloat(customPriceInput.value) || 0;
|
const customPrice = parseFloat(customPriceInput.value) || 0;
|
||||||
|
|
||||||
const originalTotal = cart.reduce((sum, item) => sum + (getDisplayPrice(item) * item.quantity), 0);
|
const originalTotal = cart.reduce((sum, item) => sum + (getDisplayPrice(item) * item.quantity), 0);
|
||||||
|
|
||||||
if (customPrice <= 0 || cart.length === 0 || originalTotal <= 0) {
|
if (customPrice <= 0 || cart.length === 0 || originalTotal <= 0) {
|
||||||
document.getElementById('adjusted-prices').classList.add('hidden');
|
adjustedPricesEl.classList.add('hidden');
|
||||||
document.getElementById('discount-info').classList.add('hidden');
|
discountInfoEl.classList.add('hidden');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2323,11 +2338,11 @@ function calculateCustomPrice() {
|
|||||||
const coefficient = customPrice / originalTotal;
|
const coefficient = customPrice / originalTotal;
|
||||||
|
|
||||||
// Show discount info
|
// Show discount info
|
||||||
document.getElementById('discount-info').classList.remove('hidden');
|
discountInfoEl.classList.remove('hidden');
|
||||||
document.getElementById('discount-percent').textContent = discountPercent.toFixed(1) + '%';
|
discountPercentEl.textContent = discountPercent.toFixed(1) + '%';
|
||||||
|
|
||||||
// Update discount color based on value
|
// Update discount color based on value
|
||||||
const discountEl = document.getElementById('discount-percent');
|
const discountEl = discountPercentEl;
|
||||||
if (discountPercent > 0) {
|
if (discountPercent > 0) {
|
||||||
discountEl.className = 'text-2xl font-bold text-green-600';
|
discountEl.className = 'text-2xl font-bold text-green-600';
|
||||||
} else if (discountPercent < 0) {
|
} else if (discountPercent < 0) {
|
||||||
@@ -2370,17 +2385,21 @@ function calculateCustomPrice() {
|
|||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('adjusted-prices-body').innerHTML = html;
|
adjustedPricesBodyEl.innerHTML = html;
|
||||||
document.getElementById('adjusted-total-original').textContent = formatMoney(totalOriginal);
|
adjustedTotalOriginalEl.textContent = formatMoney(totalOriginal);
|
||||||
document.getElementById('adjusted-total-new').textContent = formatMoney(totalNew);
|
adjustedTotalNewEl.textContent = formatMoney(totalNew);
|
||||||
document.getElementById('adjusted-total-final').textContent = formatMoney(totalNew);
|
adjustedTotalFinalEl.textContent = formatMoney(totalNew);
|
||||||
document.getElementById('adjusted-prices').classList.remove('hidden');
|
adjustedPricesEl.classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearCustomPrice() {
|
function clearCustomPrice() {
|
||||||
document.getElementById('custom-price-input').value = '';
|
const customPriceInput = document.getElementById('custom-price-input');
|
||||||
document.getElementById('adjusted-prices').classList.add('hidden');
|
const adjustedPricesEl = document.getElementById('adjusted-prices');
|
||||||
document.getElementById('discount-info').classList.add('hidden');
|
const discountInfoEl = document.getElementById('discount-info');
|
||||||
|
|
||||||
|
if (customPriceInput) customPriceInput.value = '';
|
||||||
|
if (adjustedPricesEl) adjustedPricesEl.classList.add('hidden');
|
||||||
|
if (discountInfoEl) discountInfoEl.classList.add('hidden');
|
||||||
triggerAutoSave();
|
triggerAutoSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2596,7 +2615,34 @@ function _ensureBomDatalist() {
|
|||||||
dl.id = 'lot-autocomplete-list';
|
dl.id = 'lot-autocomplete-list';
|
||||||
document.body.appendChild(dl);
|
document.body.appendChild(dl);
|
||||||
}
|
}
|
||||||
dl.innerHTML = _bomLots().map(l => `<option value="${escapeHtml(l)}">`).join('');
|
const all = _bomLots();
|
||||||
|
const cartLots = [];
|
||||||
|
const seenCart = new Set();
|
||||||
|
(window._currentCart || []).forEach(item => {
|
||||||
|
const lot = (item?.lot_name || '').trim();
|
||||||
|
if (!lot || seenCart.has(lot)) return;
|
||||||
|
seenCart.add(lot);
|
||||||
|
cartLots.push(lot);
|
||||||
|
});
|
||||||
|
|
||||||
|
const mappedSet = new Set();
|
||||||
|
bomRows.forEach(r => {
|
||||||
|
_getRowCanonicalLotMappings(r).forEach(m => {
|
||||||
|
if (m?.lot_name) mappedSet.add(m.lot_name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const priorityLots = cartLots.filter(l => !mappedSet.has(l));
|
||||||
|
const prioritySet = new Set(priorityLots);
|
||||||
|
const rest = all.filter(l => !prioritySet.has(l));
|
||||||
|
const parts = [];
|
||||||
|
priorityLots.forEach(l => parts.push(`<option value="${escapeHtml(l)}">`));
|
||||||
|
if (priorityLots.length && rest.length) {
|
||||||
|
// Visual separator inside datalist suggestions.
|
||||||
|
parts.push(`<option value="${BOM_LOT_DATALIST_DIVIDER}">`);
|
||||||
|
}
|
||||||
|
rest.forEach(l => parts.push(`<option value="${escapeHtml(l)}">`));
|
||||||
|
dl.innerHTML = parts.join('');
|
||||||
return dl;
|
return dl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3494,8 +3540,8 @@ async function renderPricingTab() {
|
|||||||
} catch(e) { /* silent — pricing tab renders with available data */ }
|
} catch(e) { /* silent — pricing tab renders with available data */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
let totalVendor = 0, totalEstimate = 0;
|
let totalVendor = 0, totalEstimate = 0, totalWarehouse = 0;
|
||||||
let hasVendor = false, hasEstimate = false;
|
let hasVendor = false, hasEstimate = false, hasWarehouse = false;
|
||||||
|
|
||||||
tbody.innerHTML = '';
|
tbody.innerHTML = '';
|
||||||
|
|
||||||
@@ -3510,8 +3556,11 @@ async function renderPricingTab() {
|
|||||||
tr.classList.add('pricing-row');
|
tr.classList.add('pricing-row');
|
||||||
const pl = priceMap[item.lot_name];
|
const pl = priceMap[item.lot_name];
|
||||||
const estUnit = (pl && pl.estimate_price > 0) ? pl.estimate_price : (item.unit_price || 0);
|
const estUnit = (pl && pl.estimate_price > 0) ? pl.estimate_price : (item.unit_price || 0);
|
||||||
|
const warehouseUnit = (pl && pl.warehouse_price > 0) ? pl.warehouse_price : null;
|
||||||
const estimateTotal = estUnit * item.quantity;
|
const estimateTotal = estUnit * item.quantity;
|
||||||
|
const warehouseTotal = warehouseUnit != null ? warehouseUnit * item.quantity : null;
|
||||||
if (estimateTotal > 0) { totalEstimate += estimateTotal; hasEstimate = true; }
|
if (estimateTotal > 0) { totalEstimate += estimateTotal; hasEstimate = true; }
|
||||||
|
if (warehouseTotal != null && warehouseTotal > 0) { totalWarehouse += warehouseTotal; hasWarehouse = true; }
|
||||||
tr.dataset.est = estimateTotal;
|
tr.dataset.est = estimateTotal;
|
||||||
const desc = (compMap[item.lot_name] || {}).description || '';
|
const desc = (compMap[item.lot_name] || {}).description || '';
|
||||||
tr.dataset.vendorOrig = '';
|
tr.dataset.vendorOrig = '';
|
||||||
@@ -3522,7 +3571,7 @@ async function renderPricingTab() {
|
|||||||
<td class="px-3 py-1.5 text-right">${item.quantity}</td>
|
<td class="px-3 py-1.5 text-right">${item.quantity}</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs">${estimateTotal > 0 ? formatCurrency(estimateTotal) : '—'}</td>
|
<td class="px-3 py-1.5 text-right text-xs">${estimateTotal > 0 ? formatCurrency(estimateTotal) : '—'}</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs text-gray-400 pricing-vendor-price">—</td>
|
<td class="px-3 py-1.5 text-right text-xs text-gray-400 pricing-vendor-price">—</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs text-gray-400">—</td>
|
<td class="px-3 py-1.5 text-right text-xs">${warehouseTotal != null && warehouseTotal > 0 ? formatCurrency(warehouseTotal) : '—'}</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs text-gray-400">—</td>
|
<td class="px-3 py-1.5 text-right text-xs text-gray-400">—</td>
|
||||||
`;
|
`;
|
||||||
tbody.appendChild(tr);
|
tbody.appendChild(tr);
|
||||||
@@ -3541,26 +3590,39 @@ async function renderPricingTab() {
|
|||||||
|
|
||||||
let rowEst = 0;
|
let rowEst = 0;
|
||||||
let hasEstimateForRow = false;
|
let hasEstimateForRow = false;
|
||||||
|
let rowWarehouse = 0;
|
||||||
|
let hasWarehouseForRow = false;
|
||||||
if (baseLot) {
|
if (baseLot) {
|
||||||
const pl = priceMap[baseLot];
|
const pl = priceMap[baseLot];
|
||||||
const estimateUnit = (pl && pl.estimate_price > 0) ? pl.estimate_price : null;
|
const estimateUnit = (pl && pl.estimate_price > 0) ? pl.estimate_price : null;
|
||||||
|
const warehouseUnit = (pl && pl.warehouse_price > 0) ? pl.warehouse_price : null;
|
||||||
if (estimateUnit != null) {
|
if (estimateUnit != null) {
|
||||||
rowEst += estimateUnit * row.quantity * _getRowLotQtyPerPN(row);
|
rowEst += estimateUnit * row.quantity * _getRowLotQtyPerPN(row);
|
||||||
hasEstimateForRow = true;
|
hasEstimateForRow = true;
|
||||||
}
|
}
|
||||||
|
if (warehouseUnit != null) {
|
||||||
|
rowWarehouse += warehouseUnit * row.quantity * _getRowLotQtyPerPN(row);
|
||||||
|
hasWarehouseForRow = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
allocs.forEach(a => {
|
allocs.forEach(a => {
|
||||||
const pl = priceMap[a.lot_name];
|
const pl = priceMap[a.lot_name];
|
||||||
const estimateUnit = (pl && pl.estimate_price > 0) ? pl.estimate_price : null;
|
const estimateUnit = (pl && pl.estimate_price > 0) ? pl.estimate_price : null;
|
||||||
|
const warehouseUnit = (pl && pl.warehouse_price > 0) ? pl.warehouse_price : null;
|
||||||
if (estimateUnit != null) {
|
if (estimateUnit != null) {
|
||||||
rowEst += estimateUnit * row.quantity * a.quantity;
|
rowEst += estimateUnit * row.quantity * a.quantity;
|
||||||
hasEstimateForRow = true;
|
hasEstimateForRow = true;
|
||||||
}
|
}
|
||||||
|
if (warehouseUnit != null) {
|
||||||
|
rowWarehouse += warehouseUnit * row.quantity * a.quantity;
|
||||||
|
hasWarehouseForRow = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const vendorTotal = row.total_price != null ? row.total_price : (row.unit_price != null ? row.unit_price * row.quantity : null);
|
const vendorTotal = row.total_price != null ? row.total_price : (row.unit_price != null ? row.unit_price * row.quantity : null);
|
||||||
if (vendorTotal != null) { totalVendor += vendorTotal; hasVendor = true; }
|
if (vendorTotal != null) { totalVendor += vendorTotal; hasVendor = true; }
|
||||||
if (hasEstimateForRow) { totalEstimate += rowEst; hasEstimate = true; }
|
if (hasEstimateForRow) { totalEstimate += rowEst; hasEstimate = true; }
|
||||||
|
if (hasWarehouseForRow) { totalWarehouse += rowWarehouse; hasWarehouse = true; }
|
||||||
|
|
||||||
tr.dataset.est = rowEst;
|
tr.dataset.est = rowEst;
|
||||||
tr.dataset.vendorOrig = vendorTotal != null ? vendorTotal : '';
|
tr.dataset.vendorOrig = vendorTotal != null ? vendorTotal : '';
|
||||||
@@ -3580,7 +3642,7 @@ async function renderPricingTab() {
|
|||||||
<td class="px-3 py-1.5 text-right">${row.quantity}</td>
|
<td class="px-3 py-1.5 text-right">${row.quantity}</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs">${hasEstimateForRow ? formatCurrency(rowEst) : '—'}</td>
|
<td class="px-3 py-1.5 text-right text-xs">${hasEstimateForRow ? formatCurrency(rowEst) : '—'}</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs pricing-vendor-price">${vendorTotal != null ? formatCurrency(vendorTotal) : '—'}</td>
|
<td class="px-3 py-1.5 text-right text-xs pricing-vendor-price">${vendorTotal != null ? formatCurrency(vendorTotal) : '—'}</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs text-gray-400">—</td>
|
<td class="px-3 py-1.5 text-right text-xs">${hasWarehouseForRow ? formatCurrency(rowWarehouse) : '—'}</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs text-gray-400">—</td>
|
<td class="px-3 py-1.5 text-right text-xs text-gray-400">—</td>
|
||||||
`;
|
`;
|
||||||
tbody.appendChild(tr);
|
tbody.appendChild(tr);
|
||||||
@@ -3594,8 +3656,11 @@ async function renderPricingTab() {
|
|||||||
tr.classList.add('bg-blue-50');
|
tr.classList.add('bg-blue-50');
|
||||||
const pl = priceMap[item.lot_name];
|
const pl = priceMap[item.lot_name];
|
||||||
const estUnit = (pl && pl.estimate_price > 0) ? pl.estimate_price : (item.unit_price || 0);
|
const estUnit = (pl && pl.estimate_price > 0) ? pl.estimate_price : (item.unit_price || 0);
|
||||||
|
const warehouseUnit = (pl && pl.warehouse_price > 0) ? pl.warehouse_price : null;
|
||||||
const estimateTotal = estUnit * item.quantity;
|
const estimateTotal = estUnit * item.quantity;
|
||||||
|
const warehouseTotal = warehouseUnit != null ? warehouseUnit * item.quantity : null;
|
||||||
if (estimateTotal > 0) { totalEstimate += estimateTotal; hasEstimate = true; }
|
if (estimateTotal > 0) { totalEstimate += estimateTotal; hasEstimate = true; }
|
||||||
|
if (warehouseTotal != null && warehouseTotal > 0) { totalWarehouse += warehouseTotal; hasWarehouse = true; }
|
||||||
tr.dataset.est = estimateTotal;
|
tr.dataset.est = estimateTotal;
|
||||||
tr.dataset.vendorOrig = '';
|
tr.dataset.vendorOrig = '';
|
||||||
const desc = (compMap[item.lot_name] || {}).description || '';
|
const desc = (compMap[item.lot_name] || {}).description || '';
|
||||||
@@ -3606,7 +3671,7 @@ async function renderPricingTab() {
|
|||||||
<td class="px-3 py-1.5 text-right">${item.quantity}</td>
|
<td class="px-3 py-1.5 text-right">${item.quantity}</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs">${estimateTotal > 0 ? formatCurrency(estimateTotal) : '—'}</td>
|
<td class="px-3 py-1.5 text-right text-xs">${estimateTotal > 0 ? formatCurrency(estimateTotal) : '—'}</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs text-gray-400 pricing-vendor-price">—</td>
|
<td class="px-3 py-1.5 text-right text-xs text-gray-400 pricing-vendor-price">—</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs text-gray-400">—</td>
|
<td class="px-3 py-1.5 text-right text-xs">${warehouseTotal != null && warehouseTotal > 0 ? formatCurrency(warehouseTotal) : '—'}</td>
|
||||||
<td class="px-3 py-1.5 text-right text-xs text-gray-400">—</td>
|
<td class="px-3 py-1.5 text-right text-xs text-gray-400">—</td>
|
||||||
`;
|
`;
|
||||||
tbody.appendChild(tr);
|
tbody.appendChild(tr);
|
||||||
@@ -3616,7 +3681,7 @@ async function renderPricingTab() {
|
|||||||
// Totals row
|
// Totals row
|
||||||
document.getElementById('pricing-total-vendor').textContent = hasVendor ? formatCurrency(totalVendor) : '—';
|
document.getElementById('pricing-total-vendor').textContent = hasVendor ? formatCurrency(totalVendor) : '—';
|
||||||
document.getElementById('pricing-total-estimate').textContent = hasEstimate ? formatCurrency(totalEstimate) : '—';
|
document.getElementById('pricing-total-estimate').textContent = hasEstimate ? formatCurrency(totalEstimate) : '—';
|
||||||
document.getElementById('pricing-total-warehouse').textContent = '—';
|
document.getElementById('pricing-total-warehouse').textContent = hasWarehouse ? formatCurrency(totalWarehouse) : '—';
|
||||||
tfoot.classList.remove('hidden');
|
tfoot.classList.remove('hidden');
|
||||||
|
|
||||||
// Update custom price proportional breakdown
|
// Update custom price proportional breakdown
|
||||||
@@ -3672,6 +3737,18 @@ function getPricingEstimateTotal() {
|
|||||||
return estimateTotal;
|
return estimateTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseDecimalInput(raw) {
|
||||||
|
if (raw == null) return 0;
|
||||||
|
const cleaned = String(raw).trim().replace(/\s/g, '').replace(',', '.');
|
||||||
|
const n = parseFloat(cleaned);
|
||||||
|
return Number.isFinite(n) ? n : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatUpliftInput(value) {
|
||||||
|
if (!Number.isFinite(value) || value <= 0) return '';
|
||||||
|
return value.toFixed(4).replace('.', ',');
|
||||||
|
}
|
||||||
|
|
||||||
function syncPricingLinkedInputs(source) {
|
function syncPricingLinkedInputs(source) {
|
||||||
const customPriceInput = document.getElementById('pricing-custom-price');
|
const customPriceInput = document.getElementById('pricing-custom-price');
|
||||||
const upliftInput = document.getElementById('pricing-uplift');
|
const upliftInput = document.getElementById('pricing-uplift');
|
||||||
@@ -3683,11 +3760,11 @@ function syncPricingLinkedInputs(source) {
|
|||||||
}
|
}
|
||||||
if (source === 'price') {
|
if (source === 'price') {
|
||||||
const customPrice = parseFloat(customPriceInput.value) || 0;
|
const customPrice = parseFloat(customPriceInput.value) || 0;
|
||||||
upliftInput.value = customPrice > 0 ? (customPrice / estimateTotal).toFixed(4) : '';
|
upliftInput.value = customPrice > 0 ? formatUpliftInput(customPrice / estimateTotal) : '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (source === 'uplift') {
|
if (source === 'uplift') {
|
||||||
const uplift = parseFloat(upliftInput.value) || 0;
|
const uplift = parseDecimalInput(upliftInput.value);
|
||||||
customPriceInput.value = uplift > 0 ? (estimateTotal * uplift).toFixed(2) : '';
|
customPriceInput.value = uplift > 0 ? (estimateTotal * uplift).toFixed(2) : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3773,25 +3850,31 @@ function exportPricingCSV() {
|
|||||||
return /[,"\n]/.test(s) ? `"${s}"` : s;
|
return /[,"\n]/.test(s) ? `"${s}"` : s;
|
||||||
};
|
};
|
||||||
|
|
||||||
const headers = ['LOT', 'PN вендора', 'Описание', 'Кол-во', 'Estimate', 'Цена вендора', 'Склад', 'Конкуренты'];
|
const headers = ['Цена вендора'];
|
||||||
const lines = [headers.map(csvEscape).join(',')];
|
const lines = [headers.map(csvEscape).join(',')];
|
||||||
|
|
||||||
rows.forEach(tr => {
|
rows.forEach(tr => {
|
||||||
const cells = tr.querySelectorAll('td');
|
const cells = tr.querySelectorAll('td');
|
||||||
const rowData = Array.from(cells).map(td => td.textContent.trim());
|
const vendorPrice = cells[5] ? cells[5].textContent.trim() : '';
|
||||||
lines.push(rowData.map(csvEscape).join(','));
|
lines.push([vendorPrice].map(csvEscape).join(','));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Totals row
|
// Totals row
|
||||||
const estTotal = document.getElementById('pricing-total-estimate').textContent.trim();
|
|
||||||
const vendorTotal = document.getElementById('pricing-total-vendor').textContent.trim();
|
const vendorTotal = document.getElementById('pricing-total-vendor').textContent.trim();
|
||||||
lines.push(['', '', '', 'Итого', estTotal, vendorTotal, '', ''].map(csvEscape).join(','));
|
lines.push(['Итого: ' + vendorTotal].map(csvEscape).join(','));
|
||||||
|
|
||||||
const blob = new Blob(['\uFEFF' + lines.join('\r\n')], {type: 'text/csv;charset=utf-8;'});
|
const blob = new Blob(['\uFEFF' + lines.join('\r\n')], {type: 'text/csv;charset=utf-8;'});
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = `pricing_${configUUID || 'export'}.csv`;
|
const today = new Date();
|
||||||
|
const yyyy = today.getFullYear();
|
||||||
|
const mm = String(today.getMonth() + 1).padStart(2, '0');
|
||||||
|
const dd = String(today.getDate()).padStart(2, '0');
|
||||||
|
const datePart = `${yyyy}-${mm}-${dd}`;
|
||||||
|
const codePart = (projectCode || 'NO-PROJECT').trim();
|
||||||
|
const namePart = (configName || 'config').trim();
|
||||||
|
a.download = `${datePart} (${codePart}) ${namePart} SPEC.csv`;
|
||||||
a.click();
|
a.click();
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user