feat: поддержка импорта BOM Inspur в формате PN*qty
Добавлен парсер для текстового формата Inspur (опциональный '|' в начале строки, разделитель '*' перед количеством). На BOM-вкладке вставка такого текста автоматически определяется и разбивается на колонки P/N + Qty без ручного выбора типов. На бэкенде тот же формат поддерживается через POST /api/projects/:uuid/vendor-import. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2834,14 +2834,24 @@ async function refreshPrices() {
|
||||
refreshBtn.className = 'px-4 py-2 bg-gray-300 text-gray-500 rounded cursor-not-allowed';
|
||||
}
|
||||
|
||||
const componentSyncResp = await fetch('/api/sync/components', { method: 'POST' });
|
||||
if (!componentSyncResp.ok) {
|
||||
throw new Error('component sync failed');
|
||||
}
|
||||
let serverSyncSkipped = false;
|
||||
try {
|
||||
const statusResp = await fetch('/api/sync/status');
|
||||
const statusData = statusResp.ok ? await statusResp.json() : null;
|
||||
if (statusData && statusData.is_online) {
|
||||
const componentSyncResp = await fetch('/api/sync/components', { method: 'POST' });
|
||||
if (!componentSyncResp.ok) throw new Error('component sync failed');
|
||||
|
||||
const pricelistSyncResp = await fetch('/api/sync/pricelists', { method: 'POST' });
|
||||
if (!pricelistSyncResp.ok) {
|
||||
throw new Error('pricelist sync failed');
|
||||
const pricelistSyncResp = await fetch('/api/sync/pricelists', { method: 'POST' });
|
||||
if (!pricelistSyncResp.ok) throw new Error('pricelist sync failed');
|
||||
} else {
|
||||
serverSyncSkipped = true;
|
||||
}
|
||||
} catch(syncErr) {
|
||||
if (syncErr.message === 'component sync failed' || syncErr.message === 'pricelist sync failed') {
|
||||
throw syncErr;
|
||||
}
|
||||
serverSyncSkipped = true;
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
@@ -2876,7 +2886,7 @@ async function refreshPrices() {
|
||||
}
|
||||
}
|
||||
|
||||
showToast('Цены обновлены', 'success');
|
||||
showToast(serverSyncSkipped ? 'Цены обновлены (без связи с сервером)' : 'Цены обновлены', 'success');
|
||||
} catch(e) {
|
||||
showToast('Ошибка обновления цен', 'error');
|
||||
} finally {
|
||||
@@ -3046,11 +3056,62 @@ function _normalizeBomRawRows(rows) {
|
||||
});
|
||||
}
|
||||
|
||||
function _parseInspurBOMText(text) {
|
||||
const lines = text.split(/\r?\n/);
|
||||
const result = [];
|
||||
for (const raw of lines) {
|
||||
const line = raw.trim();
|
||||
if (!line) continue;
|
||||
const clean = line.startsWith('|') ? line.slice(1).trim() : line;
|
||||
if (!clean) continue;
|
||||
const starIdx = clean.lastIndexOf('*');
|
||||
if (starIdx > 0) {
|
||||
const suffix = clean.slice(starIdx + 1).trim();
|
||||
if (/^\d+$/.test(suffix)) {
|
||||
result.push([clean.slice(0, starIdx).trim(), suffix]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result.push([clean, '1']);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function _isInspurBOMText(text) {
|
||||
const lines = text.split(/\r?\n/).filter(l => l.trim().length > 0);
|
||||
if (!lines.length) return false;
|
||||
let matches = 0;
|
||||
for (const line of lines) {
|
||||
const t = line.trim();
|
||||
const idx = t.lastIndexOf('*');
|
||||
if (idx > 0 && /^\d+$/.test(t.slice(idx + 1).trim())) matches++;
|
||||
}
|
||||
return matches > 0 && matches >= Math.ceil(lines.length * 0.5);
|
||||
}
|
||||
|
||||
function handleBOMPaste(event) {
|
||||
event.preventDefault();
|
||||
const text = event.clipboardData.getData('text/plain');
|
||||
if (!text || !text.trim()) return;
|
||||
|
||||
if (_isInspurBOMText(text)) {
|
||||
const parsed = _parseInspurBOMText(text);
|
||||
if (!parsed.length) return;
|
||||
bomImportRaw = {
|
||||
mode: 'raw',
|
||||
rows: parsed,
|
||||
columnTypes: ['pn', 'qty'],
|
||||
ignoredRows: {},
|
||||
rowErrors: {},
|
||||
uiError: ''
|
||||
};
|
||||
bomRows = [];
|
||||
_setBomUIError('');
|
||||
rebuildBOMRowsFromRaw();
|
||||
renderBOMTable();
|
||||
return;
|
||||
}
|
||||
|
||||
const lines = text.split(/\r?\n/).filter(l => l.length > 0);
|
||||
if (!lines.length) return;
|
||||
const rows = lines.map(l => l.split('\t').map(c => c.trim()));
|
||||
|
||||
Reference in New Issue
Block a user