mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 13:55:23 +02:00
119 lines
4.6 KiB
TypeScript
119 lines
4.6 KiB
TypeScript
import XLSX from 'xlsx';
|
|||
|
|
import { readFileSync, existsSync } from 'fs';
|
||
|
|
import Papa from 'papaparse';
|
||
|
|
|
||
|
|
const ADS_FILE = 'Ads Weekly.xlsx';
|
||
|
|
const DATA_FILE = 'dist/Amazon-Sell-Out-2023-2025.csv'; // Need to check if this exists locally or if I need to fetch it.
|
||
|
|
|
||
|
|
async function main() {
|
||
|
|
console.log('--- AD SPEND BY PRODUCT LINE (UK, 2026) ---');
|
||
|
|
|
||
|
|
// 1. Build Metadata Map (ASIN -> Line) from Sales Records
|
||
|
|
const metaMap = new Map<string, string>();
|
||
|
|
|
||
|
|
// Check local CSV if it exists, otherwise we might need to fetch it
|
||
|
|
const dropboxUrl = "https://www.dropbox.com/scl/fi/b9zxn4z5i7sxwfakk5g5y/Amazon-Sell-Out-2023-2025.csv?rlkey=uoto6v0mm99py8nszy8ldtez8&st=pzn1zkrg&dl=1";
|
||
|
|
console.log('Fetching Sales data to build ASIN-Line mapping...');
|
||
|
|
const csvResponse = await fetch(dropboxUrl);
|
||
|
|
const csvText = await csvResponse.text();
|
||
|
|
|
||
|
|
console.log('Parsing CSV...');
|
||
|
|
const results = Papa.parse(csvText, { header: true, skipEmptyLines: true });
|
||
|
|
const rows = results.data as any[];
|
||
|
|
|
||
|
|
if (rows.length > 0) {
|
||
|
|
console.log('CSV Columns:', Object.keys(rows[0]));
|
||
|
|
}
|
||
|
|
|
||
|
|
rows.forEach(r => {
|
||
|
|
const asin = String(rowValue(r, ['CUSTOMER REFERENCE', 'ASIN'])).trim().toUpperCase();
|
||
|
|
const line = rowValue(r, ['PRODUCT Line', 'Line']) || 'Unassigned';
|
||
|
|
if (asin && !metaMap.has(asin)) {
|
||
|
|
metaMap.set(asin, line);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
console.log(`Built mapping for ${metaMap.size} unique ASINs.`);
|
||
|
|
|
||
|
|
// Helper to get case-insensitive row value
|
||
|
|
function rowValue(row: any, aliases: string[]): string {
|
||
|
|
for (const a of aliases) {
|
||
|
|
if (row[a]) return row[a];
|
||
|
|
const lowerA = a.toLowerCase();
|
||
|
|
const found = Object.keys(row).find(k => k.toLowerCase() === lowerA);
|
||
|
|
if (found) return row[found];
|
||
|
|
}
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. Parse Ads Data
|
||
|
|
if (!existsSync(ADS_FILE)) {
|
||
|
|
console.error(`Error: ${ADS_FILE} not found.`);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const buf = readFileSync(ADS_FILE);
|
||
|
|
const wb = XLSX.read(buf, { type: 'buffer' });
|
||
|
|
|
||
|
|
const spendByLine = new Map<string, number>();
|
||
|
|
let totalUkSpend = 0;
|
||
|
|
let foundMatches: any[] = [];
|
||
|
|
|
||
|
|
for (const sheetName of wb.SheetNames) {
|
||
|
|
const ws = wb.Sheets[sheetName];
|
||
|
|
const rows: any[] = XLSX.utils.sheet_to_json(ws, { defval: '' });
|
||
|
|
|
||
|
|
rows.forEach((r: any) => {
|
||
|
|
const countryRaw = String(r.Country || '').toUpperCase();
|
||
|
|
const asin = String(r.ASIN || '').trim().toUpperCase();
|
||
|
|
const cost = parseFloat(r.Cost) || 0;
|
||
|
|
const line = metaMap.get(asin) || 'Unassigned';
|
||
|
|
|
||
|
|
// Still track official UK 2026 total for the summary
|
||
|
|
if (sheetName === '2026' && (countryRaw === 'UK' || countryRaw === 'GB' || countryRaw === 'AMAZON UK' || countryRaw === 'AMAZON GB')) {
|
||
|
|
spendByLine.set(line, (spendByLine.get(line) || 0) + cost);
|
||
|
|
totalUkSpend += cost;
|
||
|
|
}
|
||
|
|
|
||
|
|
// DEBUG: Find ALL "INKEE" records anywhere that might explain the 126
|
||
|
|
if (line.toUpperCase() === 'INKEE' && cost > 0) {
|
||
|
|
foundMatches.push({
|
||
|
|
Sheet: sheetName,
|
||
|
|
Country: countryRaw,
|
||
|
|
ASIN: asin,
|
||
|
|
Cost: cost,
|
||
|
|
Week: r.Week
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('\n--- ALL INKEE AD RECORDS FOUND (ANY COUNTRY/YEAR) ---');
|
||
|
|
console.table(foundMatches.slice(0, 50)); // Showing first 50
|
||
|
|
if (foundMatches.length > 50) console.log(`... and ${foundMatches.length - 50} more records.`);
|
||
|
|
|
||
|
|
const totalsByGroup = foundMatches.reduce((acc, curr) => {
|
||
|
|
const key = `${curr.Sheet} | ${curr.Country}`;
|
||
|
|
acc[key] = (acc[key] || 0) + curr.Cost;
|
||
|
|
return acc;
|
||
|
|
}, {});
|
||
|
|
|
||
|
|
console.log('\nSummary of INKEE Spend by Sheet and Country:');
|
||
|
|
console.table(totalsByGroup);
|
||
|
|
|
||
|
|
// 3. Output results
|
||
|
|
console.log('\nOfficial Results for Amazon UK (2026 YTD) based on sheet "2026":');
|
||
|
|
const sortedLines = Array.from(spendByLine.entries())
|
||
|
|
.sort((a, b) => b[1] - a[1]);
|
||
|
|
|
||
|
|
console.log('--------------------------------------------------');
|
||
|
|
console.log(`${'Product Line'.padEnd(30)} | ${'Ad Spend (£)'.padStart(15)}`);
|
||
|
|
console.log('--------------------------------------------------');
|
||
|
|
sortedLines.forEach(([line, spend]) => {
|
||
|
|
console.log(`${line.padEnd(30)} | ${spend.toFixed(2).padStart(15)}`);
|
||
|
|
});
|
||
|
|
console.log('--------------------------------------------------');
|
||
|
|
console.log(`${'TOTAL'.padEnd(30)} | ${totalUkSpend.toFixed(2).padStart(15)}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
main().catch(console.error);
|