ui: simplify BOM paste to fixed positional column order
Format: PN | qty | [description] | [price]. Remove heuristic column-type detection. Update hint text accordingly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -259,10 +259,20 @@
|
||||
<div class="mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Вставьте таблицу из Excel (Ctrl+V в область ниже)</p>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded p-3 text-xs text-gray-500 space-y-1">
|
||||
<p class="font-medium text-gray-600">Поддерживаемые форматы:</p>
|
||||
<p><span class="font-mono bg-white border border-gray-200 rounded px-1">PN</span> <span class="font-mono bg-white border border-gray-200 rounded px-1">Кол-во</span> — минимальный (2 колонки)</p>
|
||||
<p><span class="font-mono bg-white border border-gray-200 rounded px-1">PN</span> <span class="font-mono bg-white border border-gray-200 rounded px-1">Описание</span> <span class="font-mono bg-white border border-gray-200 rounded px-1">Кол-во</span> <span class="font-mono bg-white border border-gray-200 rounded px-1">Цена ед.</span> — расширенный</p>
|
||||
<p class="text-gray-400">Строка-заголовок определяется автоматически (пропускается если первая ячейка — текст). Порядок колонок: первая текстовая = PN, первая числовая = Кол-во, следующая числовая = Цена.</p>
|
||||
<p class="font-medium text-gray-600">Колонки строго по порядку:</p>
|
||||
<p>
|
||||
<span class="font-mono bg-white border border-gray-200 rounded px-1">PN</span>
|
||||
<span class="font-mono bg-white border border-gray-200 rounded px-1">Кол-во</span>
|
||||
<span class="text-gray-400 italic"> — минимальный</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="font-mono bg-white border border-gray-200 rounded px-1">PN</span>
|
||||
<span class="font-mono bg-white border border-gray-200 rounded px-1">Кол-во</span>
|
||||
<span class="font-mono bg-white border border-gray-200 rounded px-1">Описание</span>
|
||||
<span class="font-mono bg-white border border-gray-200 rounded px-1">Цена ед.</span>
|
||||
<span class="text-gray-400 italic"> — полный</span>
|
||||
</p>
|
||||
<p class="text-gray-400">Строка-заголовок пропускается автоматически если во 2-й колонке не число.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="bom-paste-area"
|
||||
@@ -2656,54 +2666,32 @@ function handleBOMPaste(event) {
|
||||
const lines = text.trim().split(/\r?\n/).filter(l => l.trim());
|
||||
const parsed = [];
|
||||
|
||||
// Fixed positional format: PN | qty | [description] | [price]
|
||||
// Skip header: if col[1] is not numeric on the first row → skip that row
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const cols = lines[i].split('\t');
|
||||
const cols = lines[i].split('\t').map(c => c.trim());
|
||||
if (cols.length < 2) continue;
|
||||
|
||||
// Auto-detect columns:
|
||||
// 2 cols → [PN, qty]
|
||||
// 3+ cols → first text=PN, first numeric=qty, subsequent numeric=prices
|
||||
let pn = '', qty = 0, description = '', unit_price = null, total_price = null;
|
||||
const pn = cols[0];
|
||||
const rawQty = cols[1].replace(/[, ]/g, '');
|
||||
|
||||
if (cols.length === 2) {
|
||||
pn = cols[0].trim();
|
||||
qty = parseInt(cols[1]) || 0;
|
||||
// Skip header row if qty is NaN
|
||||
if (!qty && i === 0) continue;
|
||||
} else {
|
||||
pn = cols[0].trim();
|
||||
// Find first numeric column for qty
|
||||
let qtyIdx = -1, priceIdx = -1;
|
||||
for (let c = 1; c < cols.length; c++) {
|
||||
const v = cols[c].trim().replace(/[, ]/g, '');
|
||||
if (!isNaN(parseFloat(v)) && v !== '') {
|
||||
if (qtyIdx === -1) { qtyIdx = c; }
|
||||
else if (priceIdx === -1) { priceIdx = c; }
|
||||
}
|
||||
}
|
||||
// If first row has non-numeric qty → likely header, skip
|
||||
if (qtyIdx === -1) continue;
|
||||
const rawQty = cols[qtyIdx].trim().replace(/[, ]/g, '');
|
||||
if (i === 0 && isNaN(parseInt(rawQty))) continue;
|
||||
qty = parseInt(rawQty) || 1;
|
||||
// Skip header row
|
||||
if (i === 0 && isNaN(parseInt(rawQty))) continue;
|
||||
|
||||
// Description: columns between PN and first numeric
|
||||
const descParts = [];
|
||||
for (let c = 1; c < qtyIdx; c++) { descParts.push(cols[c].trim()); }
|
||||
description = descParts.join(' ').trim();
|
||||
|
||||
if (priceIdx !== -1) {
|
||||
const rawPrice = cols[priceIdx].trim().replace(/[, ]/g, '');
|
||||
unit_price = parseFloat(rawPrice) || null;
|
||||
if (unit_price && qty) total_price = unit_price * qty;
|
||||
}
|
||||
const qty = parseInt(rawQty) || 1;
|
||||
const description = cols.length >= 3 ? cols[2] : '';
|
||||
let unit_price = null, total_price = null;
|
||||
if (cols.length >= 4) {
|
||||
const rawPrice = cols[3].replace(/[, ]/g, '');
|
||||
unit_price = parseFloat(rawPrice) || null;
|
||||
if (unit_price) total_price = unit_price * qty;
|
||||
}
|
||||
|
||||
if (!pn) continue;
|
||||
parsed.push({
|
||||
sort_order: (parsed.length + 1) * 10,
|
||||
vendor_pn: pn,
|
||||
quantity: qty || 1,
|
||||
quantity: qty,
|
||||
description,
|
||||
unit_price,
|
||||
total_price,
|
||||
|
||||
Reference in New Issue
Block a user