fix: include ads-only ASINs in merge and use unfiltered rows for totals

1. Changed weekTotals to use 'rows' instead of 'filteredRows' so totals
   always show complete sums regardless of search/growth filters
2. Added second pass in mergeSalesAndAdsData to include ads records for
   ASINs that have ad spend but no corresponding sales data
This commit is contained in:
Christian Vidal Wolf
2026-01-22 09:24:40 +01:00
parent 8de4521fbe
commit 8b47ab844e
2 changed files with 47 additions and 3 deletions
+3 -3
View File
@@ -119,18 +119,18 @@ const WeeklyGrid: React.FC<WeeklyGridProps> = ({ data }) => {
const totalPages = Math.ceil(sortedRows.length / ROWS_PER_PAGE); const totalPages = Math.ceil(sortedRows.length / ROWS_PER_PAGE);
// Calculate totals per week // Calculate totals per week - use unfiltered 'rows' to show complete totals
const weekTotals = useMemo(() => { const weekTotals = useMemo(() => {
const totals: { [weekKey: string]: { units: number, spend: number } } = {}; const totals: { [weekKey: string]: { units: number, spend: number } } = {};
weeks.forEach(week => { weeks.forEach(week => {
totals[week] = filteredRows.reduce((acc, row) => { totals[week] = rows.reduce((acc, row) => {
acc.units += (row.unitsByWeek[week] || 0); acc.units += (row.unitsByWeek[week] || 0);
acc.spend += (row.spendByWeek[week] || 0); acc.spend += (row.spendByWeek[week] || 0);
return acc; return acc;
}, { units: 0, spend: 0 }); }, { units: 0, spend: 0 });
}); });
return totals; return totals;
}, [filteredRows, weeks]); }, [rows, weeks]);
const renderGrowth = (current: number, previous: number) => { const renderGrowth = (current: number, previous: number) => {
if (!previous || previous === 0) return null; if (!previous || previous === 0) return null;
+44
View File
@@ -541,6 +541,50 @@ export const mergeSalesAndAdsData = (salesData: SalesRecord[], adsData: AdsRecor
}; };
}); });
// 3. Include ads-only records (ASINs with ads but no sales)
const usedAdsKeys = new Set<string>();
salesData.forEach(sale => {
const weekNum = sale.week || 0;
const key = `${sale.asin.trim().toUpperCase()}|${sale.customer.trim().toUpperCase()}|${sale.year}|${weekNum}`;
usedAdsKeys.add(key);
});
adsData.forEach(ad => {
const key = `${ad.asin.trim().toUpperCase()}|${ad.country.trim().toUpperCase()}|${ad.year}|${ad.week}`;
if (!usedAdsKeys.has(key)) {
// Create a CombinedKPIs record for ads-only data
mergedData.push({
id: `ads-${key}`,
marketplace: ad.country,
customer: ad.country,
month: '',
week: ad.week,
year: ad.year,
asin: ad.asin,
title: '',
line: '',
sku: '',
salesTotal: 0,
unitsTotal: 0,
salesAds: ad.attributedSales30d,
unitsAds: ad.attributedUnits30d,
cost: ad.cost,
clicks: ad.clicks,
impressions: ad.impressions,
salesOrganic: 0,
unitsOrganic: 0,
paidSalesShare: 0,
organicSalesShare: 0,
acos: ad.attributedSales30d > 0 ? (ad.cost / ad.attributedSales30d) * 100 : 0,
tacos: 0,
roas: ad.cost > 0 ? ad.attributedSales30d / ad.cost : 0,
ctr: ad.impressions > 0 ? (ad.clicks / ad.impressions) * 100 : 0,
cpc: ad.clicks > 0 ? ad.cost / ad.clicks : 0,
cvrUnits: ad.clicks > 0 ? (ad.attributedUnits30d / ad.clicks) * 100 : 0
});
}
});
return mergedData; return mergedData;
}; };