From 8b47ab844e0043df908df4ec750e40556f1c86dc Mon Sep 17 00:00:00 2001 From: Christian Vidal Wolf Date: Thu, 22 Jan 2026 09:24:40 +0100 Subject: [PATCH] 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 --- components/WeeklyGrid.tsx | 6 +++--- services/dataProcessor.ts | 44 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/components/WeeklyGrid.tsx b/components/WeeklyGrid.tsx index 4479964..4793123 100644 --- a/components/WeeklyGrid.tsx +++ b/components/WeeklyGrid.tsx @@ -119,18 +119,18 @@ const WeeklyGrid: React.FC = ({ data }) => { 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 totals: { [weekKey: string]: { units: number, spend: number } } = {}; weeks.forEach(week => { - totals[week] = filteredRows.reduce((acc, row) => { + totals[week] = rows.reduce((acc, row) => { acc.units += (row.unitsByWeek[week] || 0); acc.spend += (row.spendByWeek[week] || 0); return acc; }, { units: 0, spend: 0 }); }); return totals; - }, [filteredRows, weeks]); + }, [rows, weeks]); const renderGrowth = (current: number, previous: number) => { if (!previous || previous === 0) return null; diff --git a/services/dataProcessor.ts b/services/dataProcessor.ts index 39b0363..35fd188 100644 --- a/services/dataProcessor.ts +++ b/services/dataProcessor.ts @@ -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(); + 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; };