fix: регистронезависимый поиск lot_name и удаление мёртвого кода
- SQLite-запросы по lot_name теперь используют UPPER(lot_name) IN/= для совместимости с легаси-данными, синхронизированными до нормализации регистра - Удалена таблица local_components и весь связанный код синхронизации; источник данных для компонентов — local_pricelist_items - Удалена функция getCategoryFromLotName из JS: категория берётся только из прайслиста, без инференса из имени лота - Регистронезависимые сравнения lot_name в JS (warehouse stock set, addedLots, cartLots, allComponents.find, _bomLotValid) - В support bundle добавлены: latest_pricelist_items.json, local.db, autocomplete_lots.json для диагностики Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -713,7 +713,7 @@ async function loadWarehouseInStockLots() {
|
||||
const lotNames = Array.isArray(data.lot_names) ? data.lot_names : [];
|
||||
lotNames.forEach(lot => {
|
||||
if (typeof lot === 'string' && lot.trim() !== '') {
|
||||
result.add(lot);
|
||||
result.add(lot.toUpperCase());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -748,7 +748,7 @@ function isComponentAllowedByStockFilter(comp) {
|
||||
const availableLots = warehouseStockLotsByPricelist.get(pricelistID);
|
||||
// Don't block UI while stock set is being loaded.
|
||||
if (!availableLots) return true;
|
||||
return availableLots.has(comp.lot_name);
|
||||
return availableLots.has((comp.lot_name || '').toUpperCase());
|
||||
}
|
||||
|
||||
// Load categories from API and update tab configuration
|
||||
@@ -853,7 +853,7 @@ function updateRequiredCategoryBadges() {
|
||||
|
||||
// Build set of categories that have at least one cart item
|
||||
const filledCategories = new Set(
|
||||
cart.map(item => (item.category || getCategoryFromLotName(item.lot_name) || '').toUpperCase())
|
||||
cart.map(item => (item.category || '').toUpperCase())
|
||||
);
|
||||
|
||||
// For each tab, check if it contains any required-but-unfilled category
|
||||
@@ -925,8 +925,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
warehouse_price: null,
|
||||
competitor_price: null,
|
||||
description: item.description || '',
|
||||
category: item.category || getCategoryFromLotName(item.lot_name)
|
||||
}));
|
||||
category: item.category }));
|
||||
}
|
||||
serverModelForQuote = config.server_model || '';
|
||||
supportCode = config.support_code || '';
|
||||
@@ -1003,7 +1002,7 @@ const BOM_LOT_DATALIST_DIVIDER = '────────';
|
||||
function _bomLotValid(v) {
|
||||
const lot = (v || '').trim();
|
||||
if (!lot || lot === BOM_LOT_DATALIST_DIVIDER) return false;
|
||||
return (window._bomAllComponents || allComponents).some(c => c.lot_name === lot);
|
||||
return (window._bomAllComponents || allComponents).some(c => c.lot_name.toUpperCase() === lot.toUpperCase());
|
||||
}
|
||||
|
||||
function updateServerCount() {
|
||||
@@ -1219,13 +1218,8 @@ function applyPriceSettings() {
|
||||
schedulePriceLevelsRefresh({ delay: 0, rerender: true, autosave: true });
|
||||
}
|
||||
|
||||
function getCategoryFromLotName(lotName) {
|
||||
const parts = lotName.split('_');
|
||||
return parts[0] || '';
|
||||
}
|
||||
|
||||
function getComponentCategory(comp) {
|
||||
return (comp.category || getCategoryFromLotName(comp.lot_name)).toUpperCase();
|
||||
return (comp.category || '').toUpperCase();
|
||||
}
|
||||
|
||||
function getTabForCategory(category) {
|
||||
@@ -1323,7 +1317,7 @@ function updateTabVisibility() {
|
||||
if (!btn) continue;
|
||||
const hasComponents = getComponentsForTab(tabId).length > 0;
|
||||
const hasCartItems = cart.some(item => {
|
||||
const cat = (item.category || getCategoryFromLotName(item.lot_name) || '').toUpperCase();
|
||||
const cat = (item.category || '').toUpperCase();
|
||||
return getTabForCategory(cat) === tabId;
|
||||
});
|
||||
const visible = hasComponents || hasCartItems;
|
||||
@@ -1410,10 +1404,10 @@ function renderSingleSelectTab(categories) {
|
||||
categories.forEach(cat => {
|
||||
const catLabel = cat === 'MB' ? 'MB' : cat === 'CPU' ? 'CPU' : cat === 'MEM' ? 'MEM' : cat;
|
||||
const selectedItem = cart.find(item =>
|
||||
(item.category || getCategoryFromLotName(item.lot_name)).toUpperCase() === cat.toUpperCase()
|
||||
(item.category).toUpperCase() === cat.toUpperCase()
|
||||
);
|
||||
|
||||
const comp = selectedItem ? allComponents.find(c => c.lot_name === selectedItem.lot_name) : null;
|
||||
const comp = selectedItem ? allComponents.find(c => c.lot_name.toUpperCase() === (selectedItem.lot_name || '').toUpperCase()) : null;
|
||||
const price = comp?.current_price || 0;
|
||||
const estimate = selectedItem?.estimate_price ?? price;
|
||||
const qty = selectedItem?.quantity || 1;
|
||||
@@ -1463,7 +1457,7 @@ function renderSingleSelectTab(categories) {
|
||||
function renderMultiSelectTab(components) {
|
||||
// Get cart items for this tab
|
||||
const tabItems = cart.filter(item => {
|
||||
const cat = (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase();
|
||||
const cat = (item.category).toUpperCase();
|
||||
const tab = getTabForCategory(cat);
|
||||
return tab === currentTab;
|
||||
});
|
||||
@@ -1485,7 +1479,7 @@ function renderMultiSelectTab(components) {
|
||||
|
||||
// Render existing cart items for this tab
|
||||
tabItems.forEach((item, idx) => {
|
||||
const comp = allComponents.find(c => c.lot_name === item.lot_name);
|
||||
const comp = allComponents.find(c => c.lot_name.toUpperCase() === (item.lot_name || '').toUpperCase());
|
||||
const total = getDisplayPrice(item) * item.quantity;
|
||||
|
||||
html += `
|
||||
@@ -1552,7 +1546,7 @@ function renderMultiSelectTab(components) {
|
||||
function renderMultiSelectTabWithSections(sections) {
|
||||
// Get cart items for this tab
|
||||
const tabItems = cart.filter(item => {
|
||||
const cat = (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase();
|
||||
const cat = (item.category).toUpperCase();
|
||||
const tab = getTabForCategory(cat);
|
||||
return tab === currentTab;
|
||||
});
|
||||
@@ -1571,7 +1565,7 @@ function renderMultiSelectTabWithSections(sections) {
|
||||
|
||||
// Get cart items for this section
|
||||
const sectionItems = tabItems.filter(item => {
|
||||
const cat = (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase();
|
||||
const cat = (item.category).toUpperCase();
|
||||
return sectionCategories.includes(cat);
|
||||
});
|
||||
|
||||
@@ -1599,7 +1593,7 @@ function renderMultiSelectTabWithSections(sections) {
|
||||
|
||||
// Render existing cart items for this section
|
||||
sectionItems.forEach((item) => {
|
||||
const comp = allComponents.find(c => c.lot_name === item.lot_name);
|
||||
const comp = allComponents.find(c => c.lot_name.toUpperCase() === (item.lot_name || '').toUpperCase());
|
||||
const total = getDisplayPrice(item) * item.quantity;
|
||||
|
||||
html += `
|
||||
@@ -1812,7 +1806,7 @@ function selectAutocompleteItem(index) {
|
||||
|
||||
// Remove existing item of this category
|
||||
cart = cart.filter(item =>
|
||||
(item.category || getCategoryFromLotName(item.lot_name)).toUpperCase() !== autocompleteCategory.toUpperCase()
|
||||
(item.category).toUpperCase() !== autocompleteCategory.toUpperCase()
|
||||
);
|
||||
|
||||
const qtyInput = document.getElementById('qty-' + autocompleteCategory);
|
||||
@@ -1868,11 +1862,11 @@ function filterAutocompleteMulti(search) {
|
||||
const searchLower = search.toLowerCase();
|
||||
|
||||
// Filter out already added items
|
||||
const addedLots = new Set(cart.map(i => i.lot_name));
|
||||
const addedLots = new Set(cart.map(i => (i.lot_name || '').toUpperCase()));
|
||||
|
||||
autocompleteFiltered = components.filter(c => {
|
||||
if (!hasComponentPrice(c.lot_name)) return false;
|
||||
if (addedLots.has(c.lot_name)) return false;
|
||||
if (addedLots.has((c.lot_name || '').toUpperCase())) return false;
|
||||
if (!isComponentAllowedByStockFilter(c)) return false;
|
||||
const text = (c.lot_name + ' ' + (c.description || '')).toLowerCase();
|
||||
return text.includes(searchLower);
|
||||
@@ -1973,11 +1967,11 @@ function filterAutocompleteSection(sectionId, search, inputElement) {
|
||||
});
|
||||
|
||||
// Filter out already added items
|
||||
const addedLots = new Set(cart.map(i => i.lot_name));
|
||||
const addedLots = new Set(cart.map(i => (i.lot_name || '').toUpperCase()));
|
||||
|
||||
autocompleteFiltered = sectionComponents.filter(c => {
|
||||
if (!hasComponentPrice(c.lot_name)) return false;
|
||||
if (addedLots.has(c.lot_name)) return false;
|
||||
if (addedLots.has((c.lot_name || '').toUpperCase())) return false;
|
||||
if (!isComponentAllowedByStockFilter(c)) return false;
|
||||
const text = (c.lot_name + ' ' + (c.description || '')).toLowerCase();
|
||||
return text.includes(searchLower);
|
||||
@@ -2143,14 +2137,14 @@ function showAutocompleteBOM(rowIdx, input) {
|
||||
|
||||
function filterAutocompleteBOM(rowIdx, search) {
|
||||
const searchLower = (search || '').toLowerCase();
|
||||
const cartLots = new Set(cart.map(i => i.lot_name));
|
||||
const cartLots = new Set(cart.map(i => (i.lot_name || '').toUpperCase()));
|
||||
const all = (window._bomAllComponents || allComponents).filter(c => {
|
||||
const text = (c.lot_name + ' ' + (c.description || '')).toLowerCase();
|
||||
return text.includes(searchLower);
|
||||
});
|
||||
const inCart = all.filter(c => cartLots.has(c.lot_name))
|
||||
const inCart = all.filter(c => cartLots.has((c.lot_name || '').toUpperCase()))
|
||||
.sort((a, b) => a.lot_name.localeCompare(b.lot_name));
|
||||
const notInCart = all.filter(c => !cartLots.has(c.lot_name))
|
||||
const notInCart = all.filter(c => !cartLots.has((c.lot_name || '').toUpperCase()))
|
||||
.sort((a, b) => {
|
||||
const popDiff = (b.popularity_score || 0) - (a.popularity_score || 0);
|
||||
if (popDiff !== 0) return popDiff;
|
||||
@@ -2195,7 +2189,7 @@ function selectAutocompleteItemBOM(index, rowIdx) {
|
||||
|
||||
function clearSingleSelect(category) {
|
||||
cart = cart.filter(item =>
|
||||
(item.category || getCategoryFromLotName(item.lot_name)).toUpperCase() !== category.toUpperCase()
|
||||
(item.category).toUpperCase() !== category.toUpperCase()
|
||||
);
|
||||
renderTab();
|
||||
updateCartUI();
|
||||
@@ -2205,7 +2199,7 @@ function clearSingleSelect(category) {
|
||||
function updateSingleQuantity(category, value) {
|
||||
const qty = parseInt(value) || 1;
|
||||
const item = cart.find(i =>
|
||||
(i.category || getCategoryFromLotName(i.lot_name)).toUpperCase() === category.toUpperCase()
|
||||
(i.category).toUpperCase() === category.toUpperCase()
|
||||
);
|
||||
|
||||
if (item) {
|
||||
@@ -2264,8 +2258,8 @@ function updateCartUI() {
|
||||
|
||||
// Sort cart items by category display order
|
||||
const sortedCart = [...cart].sort((a, b) => {
|
||||
const catA = (a.category || getCategoryFromLotName(a.lot_name)).toUpperCase();
|
||||
const catB = (b.category || getCategoryFromLotName(b.lot_name)).toUpperCase();
|
||||
const catA = (a.category).toUpperCase();
|
||||
const catB = (b.category).toUpperCase();
|
||||
const orderA = categoryOrderMap[catA] || 9999;
|
||||
const orderB = categoryOrderMap[catB] || 9999;
|
||||
return orderA - orderB;
|
||||
@@ -2273,7 +2267,7 @@ function updateCartUI() {
|
||||
|
||||
const grouped = {};
|
||||
sortedCart.forEach(item => {
|
||||
const cat = item.category || getCategoryFromLotName(item.lot_name);
|
||||
const cat = item.category;
|
||||
const tab = getTabForCategory(cat);
|
||||
if (!grouped[tab]) grouped[tab] = [];
|
||||
grouped[tab].push(item);
|
||||
@@ -2282,11 +2276,11 @@ function updateCartUI() {
|
||||
// Sort tabs by minimum display order of their categories
|
||||
const sortedTabs = Object.entries(grouped).sort((a, b) => {
|
||||
const minOrderA = Math.min(...a[1].map(item => {
|
||||
const cat = (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase();
|
||||
const cat = (item.category).toUpperCase();
|
||||
return categoryOrderMap[cat] || 9999;
|
||||
}));
|
||||
const minOrderB = Math.min(...b[1].map(item => {
|
||||
const cat = (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase();
|
||||
const cat = (item.category).toUpperCase();
|
||||
return categoryOrderMap[cat] || 9999;
|
||||
}));
|
||||
return minOrderA - minOrderB;
|
||||
@@ -2517,8 +2511,7 @@ function restoreAutosaveDraftIfAny() {
|
||||
warehouse_price: null,
|
||||
competitor_price: null,
|
||||
description: item.description || '',
|
||||
category: item.category || getCategoryFromLotName(item.lot_name)
|
||||
}));
|
||||
category: item.category }));
|
||||
}
|
||||
if (typeof payload.server_count === 'number' && payload.server_count > 0) {
|
||||
serverCount = payload.server_count;
|
||||
@@ -2738,8 +2731,8 @@ function renderSalePriceTable() {
|
||||
}
|
||||
|
||||
const sortedCart = [...cart].sort((a, b) => {
|
||||
const catA = (a.category || getCategoryFromLotName(a.lot_name)).toUpperCase();
|
||||
const catB = (b.category || getCategoryFromLotName(b.lot_name)).toUpperCase();
|
||||
const catA = (a.category).toUpperCase();
|
||||
const catB = (b.category).toUpperCase();
|
||||
const orderA = categoryOrderMap[catA] || 9999;
|
||||
const orderB = categoryOrderMap[catB] || 9999;
|
||||
return orderA - orderB;
|
||||
@@ -2842,8 +2835,8 @@ function calculateCustomPrice() {
|
||||
// Build adjusted prices table
|
||||
// Sort cart items by category display order
|
||||
const sortedCart = [...cart].sort((a, b) => {
|
||||
const catA = (a.category || getCategoryFromLotName(a.lot_name)).toUpperCase();
|
||||
const catB = (b.category || getCategoryFromLotName(b.lot_name)).toUpperCase();
|
||||
const catA = (a.category).toUpperCase();
|
||||
const catB = (b.category).toUpperCase();
|
||||
const orderA = categoryOrderMap[catA] || 9999;
|
||||
const orderB = categoryOrderMap[catB] || 9999;
|
||||
return orderA - orderB;
|
||||
@@ -4153,8 +4146,8 @@ async function renderPricingTab() {
|
||||
|
||||
if (!bomRows.length) {
|
||||
const sortedByCategory = [...cart].sort((a, b) => {
|
||||
const catA = (a.category || getCategoryFromLotName(a.lot_name)).toUpperCase();
|
||||
const catB = (b.category || getCategoryFromLotName(b.lot_name)).toUpperCase();
|
||||
const catA = (a.category).toUpperCase();
|
||||
const catB = (b.category).toUpperCase();
|
||||
return (categoryOrderMap[catA] || 9999) - (categoryOrderMap[catB] || 9999);
|
||||
});
|
||||
sortedByCategory.forEach(item => { _pushCartRow(item, false); coveredLots.add(item.lot_name); });
|
||||
|
||||
Reference in New Issue
Block a user