Implement Vendor Stock Filtering across Weekly Sales, Ads, and Forecast views

This commit is contained in:
Christian Vidal Wolf
2026-01-28 10:18:08 +01:00
parent 97071c7b62
commit 5915bb4c0e
7 changed files with 182 additions and 33 deletions
+50 -4
View File
@@ -743,12 +743,50 @@ const checkStockFilter = (sku: string, filters: string[], stockMap?: Map<string,
});
};
const checkVendorStockFilter = (asin: string, filters: string[], vendorStockMap?: Map<string, { eu: number; uk: number }>, mode: 'eu' | 'uk' = 'eu'): boolean => {
if (!filters || !Array.isArray(filters) || filters.length === 0) return true;
if (!vendorStockMap) return true;
const data = vendorStockMap.get(asin);
const stockValue = data ? (mode === 'uk' ? data.uk : data.eu) : 0;
return filters.some(f => {
if (f === 'Out of Stock (0)') return stockValue === 0;
if (f === 'In Stock (>20)') return stockValue > 20;
if (f === 'In Stock (>0)') return stockValue > 0;
if (f === 'Low Stock (<10)') return stockValue < 10;
const input = f.trim().toLowerCase();
if (input.includes('-')) {
const [start, end] = input.split('-').map(s => parseFloat(s.trim()));
if (!isNaN(start) && !isNaN(end)) return stockValue >= start && stockValue <= end;
} else if (input.startsWith('<=')) {
const val = parseFloat(input.substring(2).trim());
if (!isNaN(val)) return stockValue <= val;
} else if (input.startsWith('>=')) {
const val = parseFloat(input.substring(2).trim());
if (!isNaN(val)) return stockValue >= val;
} else if (input.startsWith('<')) {
const val = parseFloat(input.substring(1).trim());
if (!isNaN(val)) return stockValue < val;
} else if (input.startsWith('>')) {
const val = parseFloat(input.substring(1).trim());
if (!isNaN(val)) return stockValue > val;
} else if (!isNaN(parseFloat(input))) {
return stockValue === parseFloat(input);
}
return false;
});
};
// Filter Ads Data by Country, Year, Week, ASIN, SKU, and Product Line
export const filterAdsData = (
adsData: AdsRecord[],
filters: FilterState,
asinMetadata?: Map<string, { sku: string; line: string }>,
stockMap?: Map<string, number>
stockMap?: Map<string, number>,
vendorStockMap?: Map<string, { eu: number; uk: number }>,
top50Mode: 'eu' | 'uk' = 'eu'
): AdsRecord[] => {
return adsData.filter(ad => {
const asin = ad.asin.trim().toUpperCase();
@@ -785,12 +823,19 @@ export const filterAdsData = (
// Stock match
const stockMatch = checkStockFilter(meta?.sku || '', filters.stock, stockMap);
const vendorStockMatch = checkVendorStockFilter(asin, filters.vendorStock, vendorStockMap, top50Mode);
return countryMatch && yearMatch && weekMatch && asinMatch && skuMatch && lineMatch && stockMatch;
return countryMatch && yearMatch && weekMatch && asinMatch && skuMatch && lineMatch && stockMatch && vendorStockMatch;
});
};
export const filterData = (data: SalesRecord[], filters: FilterState, stockMap?: Map<string, number>): SalesRecord[] => {
export const filterData = (
data: SalesRecord[],
filters: FilterState,
stockMap?: Map<string, number>,
vendorStockMap?: Map<string, { eu: number; uk: number }>,
top50Mode: 'eu' | 'uk' = 'eu'
): SalesRecord[] => {
return data.filter(item => {
// 1. Month Logic: Handle "Apr-23" matching "Apr" filter
const recordMonth = item.month; // e.g. "Apr-23"
@@ -817,8 +862,9 @@ export const filterData = (data: SalesRecord[], filters: FilterState, stockMap?:
// Stock match
const stockMatch = checkStockFilter(item.sku, filters.stock, stockMap);
const vendorStockMatch = checkVendorStockFilter(item.asin, filters.vendorStock, vendorStockMap, top50Mode);
return customerMatch && yearMatch && monthMatch && lineMatch && asinMatch && skuMatch && titleMatch && weekMatch && stockMatch;
return customerMatch && yearMatch && monthMatch && lineMatch && asinMatch && skuMatch && titleMatch && weekMatch && stockMatch && vendorStockMatch;
});
};