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:
2026-02-21 17:16:57 +03:00
parent a127ebea82
commit c6086ac03a

View File

@@ -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,