Refine vendor mapping CSV operations and ignore import flow

This commit is contained in:
Mikhail Chusavitin
2026-02-27 16:49:39 +03:00
parent 6f1de7a20e
commit 04ce74ca1b
5 changed files with 106 additions and 30 deletions

View File

@@ -58,7 +58,7 @@
<span>Игнорируемые</span>
</label>
<button onclick="openVendorMappingModal()" class="px-3 py-2 bg-orange-600 text-white rounded hover:bg-orange-700">Создать</button>
<button onclick="openVendorMappingsImportModal()" class="px-3 py-2 border rounded hover:bg-gray-50">Импорт</button>
<button onclick="openVendorMappingsImportModal()" class="px-3 py-2 border rounded hover:bg-gray-50">CSV operations</button>
<button onclick="loadVendorMappings(1)" class="px-3 py-2 border rounded hover:bg-gray-50">Обновить</button>
<input id="vm-import-file" type="file" accept=".csv,text/csv" class="hidden">
</div>
@@ -89,17 +89,19 @@
<div id="vm-import-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
<div class="bg-white rounded-lg p-6 max-w-xl w-full mx-4 space-y-4">
<div class="flex justify-between items-center">
<h2 class="text-xl font-bold">Импорт CSV сопоставлений</h2>
<h2 class="text-xl font-bold">CSV operations</h2>
<button onclick="closeVendorMappingsImportModal()" class="text-gray-400 hover:text-gray-600 text-xl">&times;</button>
</div>
<div class="text-sm text-gray-600 space-y-1">
<p>Формат CSV: `;` (Excel RU), UTF-8 (BOM поддерживается).</p>
<p>Колонки: `vendor;partnumber;lot_name;description`.</p>
<p>Колонки: `vendor;partnumber;lot_name;description;ignore`.</p>
<p>Если `ignore` заполнен и `lot_name` пустой, строка будет помечена как игнорируемая.</p>
</div>
<div class="border rounded p-3 bg-gray-50 space-y-2">
<div class="flex items-center gap-2">
<button onclick="triggerVendorMappingsCsvImport()" class="px-3 py-2 border rounded hover:bg-white">Выбрать CSV</button>
<button onclick="downloadVendorMappingsCsvExample()" class="px-3 py-2 border rounded hover:bg-white">Скачать пример</button>
<button onclick="triggerVendorMappingsCsvImport()" class="px-3 py-2 border rounded hover:bg-white">Загрузить</button>
<button onclick="downloadVendorMappingsCsvExample()" class="px-3 py-2 border rounded hover:bg-white">Пример</button>
<button onclick="exportUnmappedVendorMappingsCsv()" class="px-3 py-2 border rounded hover:bg-white">Скачать новые</button>
</div>
<div id="vm-import-file-name" class="text-sm text-gray-500">Файл не выбран</div>
</div>
@@ -471,7 +473,7 @@ function renderVendorMappingsImportSelectedFile() {
}
function downloadVendorMappingsCsvExample() {
const content = '\uFEFFvendor;partnumber;lot_name;description\nSamsung;M393A4K40EB3-CWE;MEM_DDR4_32G_3200;DDR4 ECC RDIMM 32GB\nMicron;MTA36ASF4G72PZ-3G2R1;MEM_DDR4_32G_3200;DDR4 ECC RDIMM 32GB\n;GENERIC-PN-001;MEM_DDR5_64G_5600;Fallback mapping without vendor\n';
const content = '\uFEFFvendor;partnumber;lot_name;description;ignore\nSamsung;M393A4K40EB3-CWE;MEM_DDR4_32G_3200;DDR4 ECC RDIMM 32GB;\nMicron;MTA36ASF4G72PZ-3G2R1;MEM_DDR4_32G_3200;DDR4 ECC RDIMM 32GB;\n;GENERIC-PN-001;MEM_DDR5_64G_5600;Fallback mapping without vendor;\nSamsung;V0040C0000000000;;;1\n';
const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
@@ -483,6 +485,10 @@ function downloadVendorMappingsCsvExample() {
URL.revokeObjectURL(url);
}
function exportUnmappedVendorMappingsCsv() {
window.location.href = '/api/admin/pricing/vendor-mappings/export-unmapped-csv';
}
async function importVendorMappingsCsv() {
const input = document.getElementById('vm-import-file');
if (!input || !input.files || !input.files[0]) return;
@@ -506,8 +512,8 @@ async function importVendorMappingsCsv() {
}
if (typeof showToast === 'function') {
const msg = data.hasErrors
? `Импортировано: ${data.imported || 0}, пропущено: ${data.skipped || 0}, есть ошибки`
: `Импортировано: ${data.imported || 0}`;
? `Импортировано: ${data.imported || 0}, ignore: ${data.ignored || 0}, пропущено: ${data.skipped || 0}, есть ошибки`
: `Импортировано: ${data.imported || 0}, ignore: ${data.ignored || 0}`;
showToast(msg, data.hasErrors ? 'info' : 'success');
}
if (Array.isArray(data.errors) && data.errors.length) {