u
This commit is contained in:
@@ -92,10 +92,6 @@ function scheduleRebuild() {
|
||||
/**
|
||||
* Normalizes a category-view article row into an index record.
|
||||
*
|
||||
* NOTE: `eans` is not part of the `GET /api/category/:id/articles` payload, so the
|
||||
* field stays empty until that endpoint exposes EANs. EAN matching in
|
||||
* `articleSearchAutomat` is therefore not functional yet.
|
||||
*
|
||||
* @param {object} article Raw category-view article row.
|
||||
* @param {number|string|null} categoryId Category the article was loaded from.
|
||||
* @returns {ArticleIndexRecord} Index record.
|
||||
@@ -126,11 +122,15 @@ function toIndexRecord(article, categoryId) {
|
||||
const smallThumbBinary = article.smallThumbBinary ?? firstPhoto?.smallThumbBinary ?? null;
|
||||
const photos = article.photos ?? (fullHash ? [{ fullHash, thumbHash }] : []);
|
||||
|
||||
const descLength = Number(article.descLength ?? article.desclength ?? (article.desc ? String(article.desc).trim().length : 0)) || 0;
|
||||
|
||||
return {
|
||||
...article,
|
||||
id: article.id,
|
||||
sku: article.sku,
|
||||
name: article.name,
|
||||
descLength,
|
||||
seo: article.seo ?? null,
|
||||
price,
|
||||
net: article.net ?? price,
|
||||
stock,
|
||||
@@ -143,6 +143,8 @@ function toIndexRecord(article, categoryId) {
|
||||
categories,
|
||||
isParent: Boolean(article.isParent ?? article.is_parent ?? (childCount > 0)),
|
||||
childCount,
|
||||
parentArticleId: article.parentArticleId ?? article.parentarticle_id ?? null,
|
||||
parentSku: article.parentSku ?? article.parent_sku ?? null,
|
||||
fullHash,
|
||||
thumbHash,
|
||||
photos,
|
||||
@@ -200,6 +202,24 @@ export function buildArticleIndex(views) {
|
||||
existing.articlewarehousesummary = rec.articlewarehousesummary;
|
||||
}
|
||||
}
|
||||
const incomingDescLen = Number(article.descLength ?? article.desclength ?? (article.desc ? String(article.desc).trim().length : 0)) || 0;
|
||||
if ((existing.descLength == null || existing.descLength === 0) && incomingDescLen > 0) {
|
||||
existing.descLength = incomingDescLen;
|
||||
}
|
||||
if ((!existing.seo || existing.seo.trim() === '') && article.seo) {
|
||||
existing.seo = article.seo;
|
||||
}
|
||||
if (Array.isArray(article.eans) && article.eans.length > 0) {
|
||||
const mergedEans = new Set(existing.eans ?? []);
|
||||
for (const e of article.eans) if (e) mergedEans.add(e);
|
||||
existing.eans = Array.from(mergedEans);
|
||||
}
|
||||
if (!existing.parentArticleId && (article.parentArticleId || article.parentarticle_id)) {
|
||||
existing.parentArticleId = article.parentArticleId ?? article.parentarticle_id;
|
||||
}
|
||||
if (!existing.parentSku && (article.parentSku || article.parent_sku)) {
|
||||
existing.parentSku = article.parentSku ?? article.parent_sku;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,8 @@ const initialState = {
|
||||
filter: '',
|
||||
brand: '',
|
||||
supplier: '',
|
||||
empty: '',
|
||||
hierarchy: '',
|
||||
offset: 0,
|
||||
pageSize: null,
|
||||
loading: false,
|
||||
@@ -343,6 +345,8 @@ export function searchArticlesIndex(query, options = {}) {
|
||||
extraArticles = [],
|
||||
allowParent = false,
|
||||
categories = [],
|
||||
hierarchy = '',
|
||||
empty = '',
|
||||
offset = 0,
|
||||
pageSize = 10,
|
||||
limit,
|
||||
@@ -367,7 +371,7 @@ export function searchArticlesIndex(query, options = {}) {
|
||||
const excludedSet = new Set((excludeSkus ?? []).map((s) => String(s).toLowerCase()));
|
||||
|
||||
let pool = rawMatches;
|
||||
if (!allowParent) {
|
||||
if (!allowParent && !hierarchy) {
|
||||
pool = pool.filter((a) => !a.isParent && !a.is_parent);
|
||||
}
|
||||
if (excludedSet.size > 0) {
|
||||
@@ -378,6 +382,58 @@ export function searchArticlesIndex(query, options = {}) {
|
||||
pool = pool.filter((a) => a.sku && includedSet.has(String(a.sku).toLowerCase()));
|
||||
}
|
||||
|
||||
if (hierarchy === 'father') {
|
||||
pool = pool.filter((a) => !a.parentArticleId && !a.parentSku);
|
||||
} else if (hierarchy === 'child') {
|
||||
pool = pool.filter((a) => !a.isParent && !a.is_parent && Number(a.childCount || 0) === 0);
|
||||
}
|
||||
|
||||
const emptyFilters = parseFilter(empty);
|
||||
if (emptyFilters.length > 0) {
|
||||
const skusWithSupplier = emptyFilters.includes('supplier') ? getSkusWithSupplier() : null;
|
||||
pool = pool.filter((row) => {
|
||||
for (const f of emptyFilters) {
|
||||
if (f === 'ean') {
|
||||
const hasEan = (Array.isArray(row.eans) && row.eans.some((e) => e && String(e).trim())) ||
|
||||
(row.ean && String(row.ean).trim());
|
||||
if (hasEan) return false;
|
||||
} else if (f === 'photo') {
|
||||
const firstPhoto = Array.isArray(row.photos) && row.photos.length > 0 ? row.photos[0] : null;
|
||||
const hasPhoto = Boolean(
|
||||
row.fullHash ||
|
||||
row.thumbHash ||
|
||||
row.smallThumbBinary ||
|
||||
firstPhoto?.fullHash ||
|
||||
firstPhoto?.thumbHash ||
|
||||
firstPhoto?.smallThumbBinary
|
||||
);
|
||||
if (hasPhoto) return false;
|
||||
} else if (f === 'price') {
|
||||
const p = row.price ?? row.net;
|
||||
const hasPrice = p != null && Number(p) > 0;
|
||||
if (hasPrice) return false;
|
||||
} else if (f === 'brand') {
|
||||
const hasBrand = Boolean(row.brand?.id || row.brand?.name || row.brandId || row.brandName);
|
||||
if (hasBrand) return false;
|
||||
} else if (f === 'supplier') {
|
||||
const hasSupplier = Boolean(
|
||||
(row.suppliers && row.suppliers.length > 0) ||
|
||||
row.supplierId != null ||
|
||||
(skusWithSupplier && skusWithSupplier.has(String(row.sku ?? '').toLowerCase()))
|
||||
);
|
||||
if (hasSupplier) return false;
|
||||
} else if (f === 'seo') {
|
||||
const hasSeo = Boolean(row.seo && String(row.seo).trim());
|
||||
if (hasSeo) return false;
|
||||
} else if (f === 'desc') {
|
||||
const hasDesc = (Number(row.descLength) > 0) || Boolean((row.desc && String(row.desc).trim()) || (row.description && String(row.description).trim()));
|
||||
if (hasDesc) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
let matchesList = pool;
|
||||
|
||||
if (categories && categories.length > 0) {
|
||||
@@ -669,6 +725,20 @@ function buildSupplierFacets(matchedArticles) {
|
||||
|
||||
return facets;
|
||||
}
|
||||
function getSkusWithSupplier() {
|
||||
const articlesBySupplier = companyAutomat.state?.articlesBySupplier ?? {};
|
||||
const set = new Set();
|
||||
for (const list of Object.values(articlesBySupplier)) {
|
||||
if (Array.isArray(list)) {
|
||||
for (const item of list) {
|
||||
if (item.article?.sku) set.add(String(item.article.sku).toLowerCase());
|
||||
if (item.sku) set.add(String(item.sku).toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #endregion
|
||||
@@ -689,6 +759,8 @@ const actions = {
|
||||
const filter = params.filter !== undefined ? String(params.filter ?? '') : currentState.filter;
|
||||
const brand = params.brand !== undefined ? String(params.brand ?? '') : (currentState.brand ?? '');
|
||||
const supplier = params.supplier !== undefined ? String(params.supplier ?? '') : (currentState.supplier ?? '');
|
||||
const empty = params.empty !== undefined ? String(params.empty ?? '') : (currentState.empty ?? '');
|
||||
const hierarchy = params.hierarchy !== undefined ? String(params.hierarchy ?? '') : (currentState.hierarchy ?? '');
|
||||
const pageSize = params.pageSize !== undefined ? params.pageSize : currentState.pageSize;
|
||||
const offset = params.offset !== undefined ? Math.max(0, Number(params.offset) || 0) : currentState.offset;
|
||||
|
||||
@@ -752,6 +824,64 @@ const actions = {
|
||||
});
|
||||
}
|
||||
|
||||
// Apply hierarchy filter:
|
||||
// - "father": "father or normal article" -> brand editable, exclude child variants
|
||||
// - "child": "child or normal article" -> price editable, exclude parents
|
||||
if (hierarchy === 'father') {
|
||||
filteredArticles = filteredArticles.filter((row) => !row.parentArticleId && !row.parentSku);
|
||||
} else if (hierarchy === 'child') {
|
||||
filteredArticles = filteredArticles.filter(
|
||||
(row) => !row.isParent && !row.is_parent && Number(row.childCount || 0) === 0,
|
||||
);
|
||||
}
|
||||
|
||||
// Apply empty filters: articles missing ean, photo, price, brand, supplier, seo, desc
|
||||
const emptyFilters = parseFilter(empty);
|
||||
if (emptyFilters.length > 0) {
|
||||
const skusWithSupplier = emptyFilters.includes('supplier') ? getSkusWithSupplier() : null;
|
||||
filteredArticles = filteredArticles.filter((row) => {
|
||||
for (const f of emptyFilters) {
|
||||
if (f === 'ean') {
|
||||
const hasEan = (Array.isArray(row.eans) && row.eans.some((e) => e && String(e).trim())) ||
|
||||
(row.ean && String(row.ean).trim());
|
||||
if (hasEan) return false;
|
||||
} else if (f === 'photo') {
|
||||
const firstPhoto = Array.isArray(row.photos) && row.photos.length > 0 ? row.photos[0] : null;
|
||||
const hasPhoto = Boolean(
|
||||
row.fullHash ||
|
||||
row.thumbHash ||
|
||||
row.smallThumbBinary ||
|
||||
firstPhoto?.fullHash ||
|
||||
firstPhoto?.thumbHash ||
|
||||
firstPhoto?.smallThumbBinary
|
||||
);
|
||||
if (hasPhoto) return false;
|
||||
} else if (f === 'price') {
|
||||
const p = row.price ?? row.net;
|
||||
const hasPrice = p != null && Number(p) > 0;
|
||||
if (hasPrice) return false;
|
||||
} else if (f === 'brand') {
|
||||
const hasBrand = Boolean(row.brand?.id || row.brand?.name || row.brandId || row.brandName);
|
||||
if (hasBrand) return false;
|
||||
} else if (f === 'supplier') {
|
||||
const hasSupplier = Boolean(
|
||||
(row.suppliers && row.suppliers.length > 0) ||
|
||||
row.supplierId != null ||
|
||||
(skusWithSupplier && skusWithSupplier.has(String(row.sku ?? '').toLowerCase()))
|
||||
);
|
||||
if (hasSupplier) return false;
|
||||
} else if (f === 'seo') {
|
||||
const hasSeo = Boolean(row.seo && String(row.seo).trim());
|
||||
if (hasSeo) return false;
|
||||
} else if (f === 'desc') {
|
||||
const hasDesc = (Number(row.descLength) > 0) || Boolean((row.desc && String(row.desc).trim()) || (row.description && String(row.description).trim()));
|
||||
if (hasDesc) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
const sortedArticles = sortArticlesByRelevance(filteredArticles, query);
|
||||
|
||||
const totalCount = sortedArticles.length;
|
||||
@@ -781,6 +911,8 @@ const actions = {
|
||||
filter,
|
||||
brand,
|
||||
supplier,
|
||||
empty,
|
||||
hierarchy,
|
||||
offset,
|
||||
pageSize: safePageSize,
|
||||
loading: articleIndexAutomat.state.status === 'loading',
|
||||
|
||||
Reference in New Issue
Block a user