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">
|
<div class="mb-3">
|
||||||
<p class="text-sm font-medium text-gray-700 mb-2">Вставьте таблицу из Excel (Ctrl+V в область ниже)</p>
|
<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">
|
<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 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>
|
||||||
<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>
|
<span class="font-mono bg-white border border-gray-200 rounded px-1">PN</span>
|
||||||
<p class="text-gray-400">Строка-заголовок определяется автоматически (пропускается если первая ячейка — текст). Порядок колонок: первая текстовая = PN, первая числовая = Кол-во, следующая числовая = Цена.</p>
|
<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>
|
</div>
|
||||||
<div id="bom-paste-area"
|
<div id="bom-paste-area"
|
||||||
@@ -2656,54 +2666,32 @@ function handleBOMPaste(event) {
|
|||||||
const lines = text.trim().split(/\r?\n/).filter(l => l.trim());
|
const lines = text.trim().split(/\r?\n/).filter(l => l.trim());
|
||||||
const parsed = [];
|
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++) {
|
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;
|
if (cols.length < 2) continue;
|
||||||
|
|
||||||
// Auto-detect columns:
|
const pn = cols[0];
|
||||||
// 2 cols → [PN, qty]
|
const rawQty = cols[1].replace(/[, ]/g, '');
|
||||||
// 3+ cols → first text=PN, first numeric=qty, subsequent numeric=prices
|
|
||||||
let pn = '', qty = 0, description = '', unit_price = null, total_price = null;
|
|
||||||
|
|
||||||
if (cols.length === 2) {
|
// Skip header row
|
||||||
pn = cols[0].trim();
|
if (i === 0 && isNaN(parseInt(rawQty))) continue;
|
||||||
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;
|
|
||||||
|
|
||||||
// Description: columns between PN and first numeric
|
const qty = parseInt(rawQty) || 1;
|
||||||
const descParts = [];
|
const description = cols.length >= 3 ? cols[2] : '';
|
||||||
for (let c = 1; c < qtyIdx; c++) { descParts.push(cols[c].trim()); }
|
let unit_price = null, total_price = null;
|
||||||
description = descParts.join(' ').trim();
|
if (cols.length >= 4) {
|
||||||
|
const rawPrice = cols[3].replace(/[, ]/g, '');
|
||||||
if (priceIdx !== -1) {
|
unit_price = parseFloat(rawPrice) || null;
|
||||||
const rawPrice = cols[priceIdx].trim().replace(/[, ]/g, '');
|
if (unit_price) total_price = unit_price * qty;
|
||||||
unit_price = parseFloat(rawPrice) || null;
|
|
||||||
if (unit_price && qty) total_price = unit_price * qty;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pn) continue;
|
if (!pn) continue;
|
||||||
parsed.push({
|
parsed.push({
|
||||||
sort_order: (parsed.length + 1) * 10,
|
sort_order: (parsed.length + 1) * 10,
|
||||||
vendor_pn: pn,
|
vendor_pn: pn,
|
||||||
quantity: qty || 1,
|
quantity: qty,
|
||||||
description,
|
description,
|
||||||
unit_price,
|
unit_price,
|
||||||
total_price,
|
total_price,
|
||||||
|
|||||||
Reference in New Issue
Block a user