mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 12:35:24 +02:00
fix(mkt): ensure all ad spend is aggregated correctly in MKT tab including unassigned spend
This commit is contained in:
@@ -168,63 +168,73 @@ export default function MktDataView({ rawData, adsData }: MktDataViewProps) {
|
|||||||
unitsMap.set(asin, (unitsMap.get(asin) || 0) + r.units);
|
unitsMap.set(asin, (unitsMap.get(asin) || 0) + r.units);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ASIN → global ad spend (all filtered data, no hardcoded year)
|
// ASIN → global ad spend
|
||||||
// Include ALL records (even empty ASIN) so total matches ADs Weekly tab
|
|
||||||
const adsSpendMap = new Map<string, number>();
|
const adsSpendMap = new Map<string, number>();
|
||||||
|
let totalAdsPassed = 0;
|
||||||
adsData.forEach(r => {
|
adsData.forEach(r => {
|
||||||
const asin = r.asin.trim().toUpperCase();
|
const asin = r.asin.trim().toUpperCase();
|
||||||
adsSpendMap.set(asin, (adsSpendMap.get(asin) || 0) + r.cost);
|
adsSpendMap.set(asin, (adsSpendMap.get(asin) || 0) + r.cost);
|
||||||
|
totalAdsPassed += r.cost;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Deals/Promos/Chargebacks are 2025-only data — only apply when 2025 is in scope
|
// Deals/Promos/Chargebacks
|
||||||
const has2025 = rawData.some(r => r.year === 2025) || adsData.some(r => r.year === 2025);
|
const has2025 = rawData.some(r => r.year === 2025) || adsData.some(r => r.year === 2025);
|
||||||
const getDeals = (asin: string) => has2025 ? (dealsMap.get(asin) || 0) : 0;
|
const getDeals = (asin: string) => has2025 ? (dealsMap.get(asin) || 0) : 0;
|
||||||
const getPromos = (asin: string) => has2025 ? (promosMap.get(asin) || 0) : 0;
|
const getPromos = (asin: string) => has2025 ? (promosMap.get(asin) || 0) : 0;
|
||||||
const getChargebacks = (asin: string) => has2025 ? (chargebacksMap.get(asin) || 0) : 0;
|
const getChargebacks = (asin: string) => has2025 ? (chargebacksMap.get(asin) || 0) : 0;
|
||||||
|
|
||||||
// Build products from sales ASINs
|
// 1. Start with all sales-based ASINs
|
||||||
const products = Array.from(metaMap.entries())
|
const products: Product[] = Array.from(metaMap.entries())
|
||||||
.map(([asin, meta]): Product => ({
|
.map(([asin, meta]): Product => {
|
||||||
id: asin,
|
const spend = adsSpendMap.get(asin) || 0;
|
||||||
asin,
|
adsSpendMap.delete(asin); // Mark as processed
|
||||||
sku: meta.sku,
|
return {
|
||||||
name: meta.title || asin,
|
id: asin,
|
||||||
image: '',
|
asin,
|
||||||
category: meta.line,
|
sku: meta.sku,
|
||||||
brand: '',
|
name: meta.title || asin,
|
||||||
grossSales: sellOutMap.get(asin) || 0,
|
|
||||||
unitsSold: unitsMap.get(asin) || 0,
|
|
||||||
ppcSpend: adsSpendMap.get(asin) || 0,
|
|
||||||
deals: getDeals(asin),
|
|
||||||
promos: getPromos(asin),
|
|
||||||
chargebacks: getChargebacks(asin),
|
|
||||||
chargebacksPrevMonth: 0,
|
|
||||||
cogs: 0,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Add ASINs (or unassigned spend) that have ad spend but no sales records
|
|
||||||
adsSpendMap.forEach((spend, asin) => {
|
|
||||||
if (!metaMap.has(asin) && spend > 0) {
|
|
||||||
products.push({
|
|
||||||
id: asin || '__unassigned__',
|
|
||||||
asin: asin || '—',
|
|
||||||
sku: '',
|
|
||||||
name: asin ? asin : 'Unassigned Ad Spend',
|
|
||||||
image: '',
|
image: '',
|
||||||
category: 'Other',
|
category: meta.line,
|
||||||
brand: '',
|
brand: '',
|
||||||
grossSales: 0,
|
grossSales: sellOutMap.get(asin) || 0,
|
||||||
unitsSold: 0,
|
unitsSold: unitsMap.get(asin) || 0,
|
||||||
ppcSpend: spend,
|
ppcSpend: spend,
|
||||||
deals: asin ? getDeals(asin) : 0,
|
deals: getDeals(asin),
|
||||||
promos: asin ? getPromos(asin) : 0,
|
promos: getPromos(asin),
|
||||||
chargebacks: asin ? getChargebacks(asin) : 0,
|
chargebacks: getChargebacks(asin),
|
||||||
chargebacksPrevMonth: 0,
|
chargebacksPrevMonth: 0,
|
||||||
cogs: 0,
|
cogs: 0,
|
||||||
});
|
};
|
||||||
}
|
});
|
||||||
|
|
||||||
|
// 2. Add remaining ad spend (including "" ASIN and ASINs without sales)
|
||||||
|
adsSpendMap.forEach((spend, asin) => {
|
||||||
|
if (spend === 0) return;
|
||||||
|
products.push({
|
||||||
|
id: asin || '__unassigned__',
|
||||||
|
asin: asin || '—',
|
||||||
|
sku: '',
|
||||||
|
name: asin ? asin : 'Unassigned Ad Spend (incl. Sponsored Brands)',
|
||||||
|
image: '',
|
||||||
|
category: 'Other',
|
||||||
|
brand: '',
|
||||||
|
grossSales: 0,
|
||||||
|
unitsSold: 0,
|
||||||
|
ppcSpend: spend,
|
||||||
|
deals: asin ? getDeals(asin) : 0,
|
||||||
|
promos: asin ? getPromos(asin) : 0,
|
||||||
|
chargebacks: asin ? getChargebacks(asin) : 0,
|
||||||
|
chargebacksPrevMonth: 0,
|
||||||
|
cogs: 0,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Debug check
|
||||||
|
const finalTotal = products.reduce((sum, p) => sum + p.ppcSpend, 0);
|
||||||
|
if (Math.abs(finalTotal - totalAdsPassed) > 0.01) {
|
||||||
|
console.warn(`[MktDataView] PPC Spend Mismatch: Total Ads=${totalAdsPassed.toFixed(2)}, Table Sum=${finalTotal.toFixed(2)}`);
|
||||||
|
}
|
||||||
|
|
||||||
return products;
|
return products;
|
||||||
}, [rawData, adsData, dealsMap, promosMap, chargebacksMap]);
|
}, [rawData, adsData, dealsMap, promosMap, chargebacksMap]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user