Amazon UK/IE Profitability Calculator
For Irish Businesses Exploring Amazon Marketplace

Enter Your Product Details
Basic Information
Pricing & Costs (Per Unit)
Amazon Fees
Select category and enter selling price.
FBA fees vary by size, weight, category & shipping program.
Use Amazon’s FBA Revenue Calculator for accuracy.
Amazon’s rate per unit, per month.
Operational Settings & Costs
Applies to sales. Marketing scales with sales.
If using growth factor, this scales with sales. If manual, enter below.
Sales Units per Month:
Marketing Spend per Month:
Used for Agency Fee if marketplace is GBP.
Cost of unsellable item, return shipping, processing.
Profitability Results
Summary for: Product
Category:
–
Marketplace:
–
Per Unit Breakdown (Month 1, After Returns)
–
–
Monthly Projections (Month 1)
–
Margin Analysis (Month 1)
–
–
–
Break-Even Analysis (Month 1)
Marketing Performance (Month 1)
Inventory Metrics (First Month)
Overall Profitability (Month 1)
Enter details to see assessment.
Enter Product Details
Fill in the form on the left to calculate your potential Amazon profitability.
Frequently Asked Questions
FBA vs FBM Costs?
FBA means Amazon handles storage, packing, shipping, and customer service. FBM means you manage these. FBA has per-unit fees but can boost sales. FBM requires you to calculate your own shipping/handling costs.
Referral Fees?
A percentage of the VAT-inclusive selling price, varying by category and sometimes price. This calculator applies these rules automatically. A minimum fee (often £0.25) may also apply.
Digital Services Tax (DST)?
For Irish sellers on Amazon UK, a DST of 2% is applied to Referral Fees and FBA Fees. This calculator automatically includes it if GBP is selected as the marketplace currency.
Storage Fee Calculation?
Estimates first month’s storage using ‘Initial Shipment’, ‘Monthly Sales’, and Amazon’s ‘Storage Fee Rate’. The per-unit cost shown is this total averaged over sales. The 12-month forecast uses this per-unit storage cost applied to forecasted sales.
VAT Calculation?
“Selling Price” should be VAT-inclusive. VAT is deducted based on the selected rate to find net selling price. Referral fees are charged on the VAT-inclusive price.
FBA Fee Accuracy?
The FBA Fee input is manual. For accuracy, always use Amazon’s official FBA Revenue Calculator. This tool uses the fee you provide.
Break-Even Point?
The number of units or revenue needed per month to cover all your fixed and variable costs (including estimated agency fees if selected). Essential for understanding minimum sales targets.
ROAS & TACoS?
Return On Ad Spend (ROAS) and Total Advertising Cost of Sales (TACoS) help measure the efficiency of your marketing spend. TACoS is marketing spend as a percentage of total sales revenue.
Impact of Returns?
Customer returns can significantly impact profit. Input your estimated return rate and average cost per return (including lost product value and processing) to see a more realistic net profit.
Inventory Metrics?
Inventory Turn shows how many times your inventory sells through in a period. Days of Inventory indicates how long your current stock will last. Important for cash flow and avoiding long-term storage fees.
Agency Fees?
You can select an agency service tier to see its impact on your profitability. Fees are based on your number of products and monthly revenue, according to a tiered structure.
CM2 vs CM3?
CM2 (Contribution Margin 2) shows your margin before advertising and agency fees. CM3 (Net Margin) is your final margin after all costs, including advertising and any agency fees.
12-Month Forecast Input?
You can forecast sales using a monthly growth percentage or by manually inputting sales units for each of the 12 months. Marketing costs will scale with sales if using the growth factor, or can be input manually if manual sales are selected.
CSV Export & Formulas?
The forecast can be exported as a CSV. While direct Excel formulas aren’t embedded, the data is structured for easy formula application in spreadsheet software like Excel or Google Sheets.
// --- Amazon Category Data (Define this early as functions use it) --- const amazonCategories = [ { value: "additive_manufacturing", text: "Additive Manufacturing", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "amazon_device_accessories", text: "Amazon Device Accessories", feeType: "simple", simplePercent: 45.9, minFee: 0.25 }, { value: "automotive_powersports", text: "Automotive and Powersports", feeType: "tiered", tiers: [{ upTo: 45.00, percent: 15.30 }, { over: 45.00, percent: 9.18 }], minFee: 0.25 }, { value: "baby_products_group", _actualValues: ["baby_products_under_10", "baby_products_over_10"], text: "Baby Products (excl. Clothing)", feeType: "complex_tiered_by_price" }, { value: "baby_products_under_10", _hidden: true, text: "Baby Products (excl. Clothing) <= £10/€10", feeType: "simple", simplePercent: 8.0, minFee: 0.25, applies: (price, fulfillment) => price <= 10 }, { value: "baby_products_over_10", _hidden: true, text: "Baby Products (excl. Clothing) > £10/€10", feeType: "simple", simplePercent: 15.0, minFee: 0.25, applies: (price, fulfillment) => price > 10 }, { value: "backpacks_handbags", text: "Backpacks and Handbags", feeType: "tiered_by_fulfillment", fba_sfp_tiers: [{ upTo: 40.00, percent: 15.30 }, { over: 40.00, percent: 7.14 }], other_percent: 15.30, minFee: 0.25 }, { value: "beauty_health_personal_care", text: "Beauty, Health, and Personal Care", feeType: "tiered", tiers: [{ upTo: 10.00, percent: 8.16 }, { over: 10.00, percent: 15.30 }], minFee: 0.25 }, { value: "beer_wine_spirits", text: "Beer, Wine & Spirits", feeType: "simple", simplePercent: 10.2, minFee: 0.25 }, { value: "books", text: "Books", feeType: "tiered", tiers: [{ upTo: 4.99, percent: 5.10 }, { over: 4.99, percent: 15.30 }], minFee: 0 }, { value: "business_industrial_scientific", text: "Business, Industrial & Scientific Supplies", feeType: "simple", simplePercent: 15.30, minFee: 0.25 }, { value: "car_motorbike_electronics", text: "Car and Motorbike Electronic Accessories", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "compact_appliances", text: "Compact Appliances", feeType: "simple", simplePercent: 15.30, minFee: 0.25 }, { value: "clothing_accessories", text: "Clothing and Accessories", feeType: "tiered_by_fulfillment", fba_sfp_tiers: [{ upTo: 40.00, percent: 15.30 }, { over: 40.00, percent: 7.14 }], other_percent: 15.30, minFee: 0.25 }, { value: "eyewear", text: "Eyewear (Non-Prescription)", feeType: "tiered_by_fulfillment", fba_sfp_tiers: [{ upTo: 40.00, percent: 15.30 }, { over: 40.00, percent: 7.14 }], other_percent: 15.30, minFee: 0.25 }, { value: "eyewear_prescription", text: "Eyewear (Prescription)", feeType: "simple", simplePercent: 15.30, minFee: 0.25 }, { value: "flow_control_filtration", text: "Flow Control & Filtration", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "fluid_transfer", text: "Fluid Transfer", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "footwear", text: "Footwear", feeType: "tiered_by_fulfillment_price_sensitive", fba_sfp_tier_over_40_percent: 7.14, general_percent: 15.30, minFee: 0.25 }, { value: "full_size_appliances", text: "Full-Size Appliances", feeType: "simple", simplePercent: 7.14, minFee: 0.25 }, { value: "furniture", text: "Furniture", feeType: "tiered", tiers: [{ upTo: 175.00, percent: 15.30 }, { over: 175.00, percent: 10.20 }], minFee: 0.25 }, { value: "grocery_gourmet", text: "Grocery & Gourmet", feeType: "tiered", tiers: [{ upTo: 10.00, percent: 8.16 }, { over: 10.00, percent: 15.30 }], minFee: 0.25 }, { value: "handmade", text: "Handmade", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "home_kitchen", text: "Home and Kitchen", feeType: "simple", simplePercent: 15.3, minFee: 0.25 }, { value: "industrial_electrical", text: "Industrial Electrical Supplies", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "industrial_tools_instruments", text: "Industrial Tools & Instruments", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "jewellery", text: "Jewellery", feeType: "tiered", tiers: [{ upTo: 225.00, percent: 20.40 }, { over: 225.00, percent: 5.10 }], minFee: 0.25 }, { value: "lawn_garden", text: "Lawn and Garden", feeType: "simple", simplePercent: 15.30, minFee: 0.25 }, { value: "material_handling", text: "Material Handling", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "mattresses", text: "Mattresses", feeType: "simple", simplePercent: 15.30, minFee: 0.25 }, { value: "metalworking", text: "Metalworking", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "music_vhs_dvd", text: "Music, VHS, DVD", feeType: "simple", simplePercent: 15.30, minFee: 0 }, { value: "musical_instruments_av", text: "Musical Instruments and AV Production", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "office_products", text: "Office Products", feeType: "simple", simplePercent: 15.30, minFee: 0.25 }, { value: "pet_products", text: "Pet Products", feeType: "simple", simplePercent: 15.30, minFee: 0.25 }, { value: "renewable_energy", text: "Renewable Energy Supplies", feeType: "simple", simplePercent: 12.24, minFee: 0.25 }, { value: "software", text: "Software", feeType: "simple", simplePercent: 15.3, minFee: 0 }, { value: "sports_outdoors", text: "Sports & Outdoors", feeType: "simple", simplePercent: 15.3, minFee: 0.25 }, { value: "tyres", text: "Tyres", feeType: "simple", simplePercent: 7.14, minFee: 0.25 }, { value: "tools_home_improvement", text: "Tools and Home Improvement", feeType: "simple", simplePercent: 13.26, minFee: 0.25 }, { value: "toys_games", text: "Toys and Games", feeType: "simple", simplePercent: 15.30, minFee: 0.25 }, { value: "video_games_accessories", text: "Video Games – Games & Accessories", feeType: "simple", simplePercent: 15.30, minFee: 0 }, { value: "video_games_consoles", text: "Video Games – Consoles", feeType: "simple", simplePercent: 8.16, minFee: 0 }, { value: "watches", text: "Watches", feeType: "tiered", tiers: [{ upTo: 225.00, percent: 15.30 }, { over: 225.00, percent: 5.10 }], minFee: 0.25 }, { value: "everything_else", text: "Everything else", feeType: "simple", simplePercent: 15.30, minFee: 0.25 } ];
// --- Function Definitions (All functions defined before use) --- function populateCategoryDropdown() { const displayCategories = amazonCategories.filter(cat => !cat._hidden); displayCategories.sort((a, b) => a.text.localeCompare(b.text)); displayCategories.forEach(cat => { const option = document.createElement('option'); option.value = cat.value; let displayText = cat.text; if (cat.feeType === 'simple') { displayText += ` (${cat.simplePercent.toFixed(2)}%)`; } else if (cat.feeType === 'tiered' || cat.feeType === 'complex_tiered_by_price' || cat.feeType === 'tiered_by_fulfillment' || cat.feeType === 'tiered_by_fulfillment_price_sensitive') { displayText += ` (Tiered)`; } option.textContent = displayText; if (productCategorySelect) productCategorySelect.appendChild(option); }); if (productCategorySelect) productCategorySelect.value = "sports_outdoors"; }
function toggleFulfillmentFields() { if (!fulfillmentOptionSelect || !fbaFeeContainer || !fbmShippingToCustomerContainer || !resultElements.fbaFeePUContainer || !resultElements.fbmShippingToCustomerCostPUContainer) return; const isFBA = fulfillmentOptionSelect.value === 'fba'; fbaFeeContainer.classList.toggle('hidden', !isFBA); fbmShippingToCustomerContainer.classList.toggle('hidden', isFBA); if (resultElements.fbaFeePUContainer) resultElements.fbaFeePUContainer.classList.toggle('hidden', !isFBA); if (resultElements.fbmShippingToCustomerCostPUContainer) resultElements.fbmShippingToCustomerCostPUContainer.classList.toggle('hidden', isFBA); }
function getNumericValue(element, defaultValue = 0) { if (!element) return defaultValue; const value = parseFloat(element.value); return isNaN(value) ? defaultValue : value; }
function formatCurrency(amount, currencyCode, includeSymbol = true) { const symbol = currencyCode === 'gbp' ? '£' : '€'; const formattedNumber = isNaN(parseFloat(amount)) ? '0.00' : parseFloat(amount).toFixed(2); return includeSymbol ? symbol + formattedNumber : formattedNumber; }
function formatNumber(amount, decimalPlaces = 0) { if (isNaN(parseFloat(amount)) || !isFinite(amount)) return "N/A"; return parseFloat(amount).toFixed(decimalPlaces); }
function showModalMessage(message) { if (modalMessageText && messageModal) { modalMessageText.textContent = message; messageModal.style.display = "block"; } }
function getReferralFeeDetails(sellingPrice, selectedCategoryValue, fulfillmentType) { let categoryData; const currentMarketplaceCurrency = currencySelect ? currencySelect.value : 'gbp';
if (selectedCategoryValue === "baby_products_group") { categoryData = amazonCategories.find(cat => cat.value.startsWith("baby_products_") && cat.applies && cat.applies(sellingPrice, fulfillmentType)); } else { categoryData = amazonCategories.find(cat => cat.value === selectedCategoryValue); }
if (!categoryData) { categoryData = amazonCategories.find(cat => cat.value === "everything_else"); }
let percent = 15.3; const minFee = categoryData.minFee !== undefined ? categoryData.minFee : 0.25; let note = `Min fee: ${formatCurrency(minFee, currentMarketplaceCurrency)}. `;
if (categoryData.feeType === 'simple') { percent = categoryData.simplePercent; note += `Rate: ${percent.toFixed(2)}%.`; } else if (categoryData.feeType === 'tiered') { const tiers = categoryData.tiers; let foundTier = false; for (const tier of tiers) { if (tier.upTo && sellingPrice <= tier.upTo) { percent = tier.percent; foundTier = true; break; } if (tier.over && sellingPrice > tier.over) { percent = tier.percent; foundTier = true; } } if (!foundTier && tiers && tiers.length > 0) percent = tiers[tiers.length - 1].percent; note += `Tiered rate applied: ${percent.toFixed(2)}%.`; } else if (categoryData.feeType === 'tiered_by_fulfillment') { if (fulfillmentType === 'fba') { const tiers = categoryData.fba_sfp_tiers; let foundTier = false; for (const tier of tiers) { if (tier.upTo && sellingPrice <= tier.upTo) { percent = tier.percent; foundTier = true; break; } if (tier.over && sellingPrice > tier.over) { percent = tier.percent; foundTier = true; } } if (!foundTier && tiers && tiers.length > 0) percent = tiers[tiers.length - 1].percent; } else { percent = categoryData.other_percent; } note += `Rate (fulfillment based): ${percent.toFixed(2)}%.`; } else if (categoryData.feeType === 'tiered_by_fulfillment_price_sensitive') { if (fulfillmentType === 'fba' && sellingPrice > 40) { percent = categoryData.fba_sfp_tier_over_40_percent; } else { percent = categoryData.general_percent; } note += `Rate (fulfillment & price sensitive): ${percent.toFixed(2)}%.`; } else if (categoryData.feeType === 'complex_tiered_by_price') { const actualCat = amazonCategories.find(cat => cat.value.startsWith("baby_products_") && cat.applies && cat.applies(sellingPrice, fulfillmentType)); if(actualCat && actualCat.feeType === 'simple') { percent = actualCat.simplePercent; note += `Rate: ${percent.toFixed(2)}%. (Price based)`; } else { const fallbackCat = amazonCategories.find(c => c.value === "everything_else"); percent = fallbackCat.simplePercent; note += `Defaulted rate: ${percent.toFixed(2)}%. Check category.`; } }
let calculatedFee = sellingPrice * (percent / 100); return { amount: Math.max(calculatedFee, minFee), percentApplied: percent, note: note }; }
function updateDisplayedReferralFeeInfo() { if (!sellingPriceInput || !productCategorySelect || !fulfillmentOptionSelect || !calculatedReferralFeeDisplay || !referralFeeNote) return;
const sellingPrice = getNumericValue(sellingPriceInput, 0); const selectedCategoryValue = productCategorySelect.value; const fulfillmentType = fulfillmentOptionSelect.value;
if (sellingPrice <= 0 && (selectedCategoryValue === "" || amazonCategories.find(cat => cat.value === selectedCategoryValue)?.text.includes("Everything else"))) { calculatedReferralFeeDisplay.textContent = "-- %"; referralFeeNote.textContent = "Select category and enter selling price."; return; } if (sellingPrice <= 0 ) { calculatedReferralFeeDisplay.textContent = "-- %"; referralFeeNote.textContent = "Enter selling price to see specific fee."; return; } const feeDetails = getReferralFeeDetails(sellingPrice, selectedCategoryValue, fulfillmentType); calculatedReferralFeeDisplay.textContent = `${feeDetails.percentApplied.toFixed(2)}%`; referralFeeNote.textContent = feeDetails.note; } function calculateAgencyFee(monthlyTotalRevenueMarketCurrency, numProducts, marketplaceCurrency, agencyTierSelection) { const exchangeRate = marketplaceCurrency === 'gbp' ? getNumericValue(gbpToEurRateInput, 1.17) : 1; const monthlyTotalRevenueEUR = marketplaceCurrency === 'gbp' && exchangeRate !== 0 ? monthlyTotalRevenueMarketCurrency / exchangeRate : monthlyTotalRevenueMarketCurrency; let tierName = "No Agency"; let baseFixedFeeEUR = 0; let percentageOfRevenue = 0; if (agencyTierSelection === 'ready_to_start') { tierName = "Ready to Start"; baseFixedFeeEUR = 2500; percentageOfRevenue = 0.06; } else if (agencyTierSelection === 'ready_to_grow') { tierName = "Ready to Grow"; baseFixedFeeEUR = 3500; percentageOfRevenue = 0.03; } else if (agencyTierSelection === 'ready_to_run') { tierName = "Ready to Run"; baseFixedFeeEUR = 4500; percentageOfRevenue = 0.015; } if (agencyTierSelection === 'none') { return { tierName: tierName, fixedFeeEUR: 0, percentageFeeEUR: 0, totalFeeEUR: 0, fixedFeeMarketplaceCurrency: 0, percentageFeeMarketplaceCurrency: 0, totalFeeMarketplaceCurrency: 0 }; } const percentageFeeComponentEUR = monthlyTotalRevenueEUR * percentageOfRevenue; const totalAgencyFeeEUR = baseFixedFeeEUR + percentageFeeComponentEUR; const fixedFeeMarketplace = marketplaceCurrency === 'gbp' && exchangeRate !== 0 ? baseFixedFeeEUR / exchangeRate : baseFixedFeeEUR; const percentageFeeMarketplace = marketplaceCurrency === 'gbp' && exchangeRate !== 0 ? percentageFeeComponentEUR / exchangeRate : percentageFeeComponentEUR; const totalAgencyFeeMarketplace = marketplaceCurrency === 'gbp' && exchangeRate !== 0 ? totalAgencyFeeEUR / exchangeRate : totalAgencyFeeEUR; return { tierName: tierName, fixedFeeEUR: baseFixedFeeEUR, percentageFeeEUR: percentageFeeComponentEUR, totalFeeEUR: totalAgencyFeeEUR, fixedFeeMarketplaceCurrency: fixedFeeMarketplace, percentageFeeMarketplaceCurrency: percentageFeeMarketplace, totalFeeMarketplaceCurrency: totalAgencyFeeMarketplace }; } function calculateAndDisplayResults() { // Get all input values const productName = productNameInput.value || 'Your Product'; const categoryText = productCategorySelect.options[productCategorySelect.selectedIndex].text; const sellingPrice = getNumericValue(sellingPriceInput, 0); const productCost = getNumericValue(productCostInput, 0); const shippingToAmazonCost = getNumericValue(shippingToAmazonCostInput, 0); const numProducts = getNumericValue(numberOfProductsInput, 1); const initialShipmentQty = getNumericValue(initialShipmentQuantityInput, 0); const monthlySales = getNumericValue(estimatedMonthlySalesInput, 0); // This is Month 1 sales const isFBA = fulfillmentOptionSelect.value === 'fba'; const fulfillmentType = fulfillmentOptionSelect.value; const fbaFeePerUnit = isFBA ? getNumericValue(fbaFeeInput, 0) : 0; const fbmShippingToCustomer = isFBA ? 0 : getNumericValue(fbmShippingToCustomerCostInput, 0); const storageRatePerUnitPerMonth = getNumericValue(avgMonthlyStorageFeePerUnitInput, 0); const vatRatePercent = getNumericValue(vatRateSelect, 20); const currency = currencySelect.value; const monthlyMarketing = getNumericValue(monthlyMarketingCostInput, 0); // This is Month 1 marketing const amazonProPlanFee = getNumericValue(amazonProPlanFeeInput, 0); const otherMonthlyFixedCosts = getNumericValue(otherMonthlyCostsInput, 0); const returnRatePercent = getNumericValue(estimatedReturnRateInput, 0); const costPerReturn = getNumericValue(avgCostPerReturnInput, 0); const selectedAgencyTier = agencyServiceTierSelect.value; if (sellingPrice <= 0) { showModalMessage("Please enter a valid Selling Price greater than zero."); return; } // --- Referral Fee --- const referralFeeData = getReferralFeeDetails(sellingPrice, productCategorySelect.value, fulfillmentType); const referralFeeAmountPerUnit = referralFeeData.amount; const actualReferralPercent = referralFeeData.percentApplied; // --- Digital Services Tax (DST) --- let dstPerUnit = 0; if (currency === 'gbp') { const referralFeeForDST = referralFeeAmountPerUnit; const fbaFeeForDST = isFBA ? fbaFeePerUnit : 0; dstPerUnit = (referralFeeForDST * 0.02) + (fbaFeeForDST * 0.02); } const totalMonthlyDST = dstPerUnit * monthlySales; // --- Cost of Returns Calculation --- const numberOfReturnsMonthly = monthlySales * (returnRatePercent / 100); const totalMonthlyCostOfReturns = numberOfReturnsMonthly * costPerReturn; const costOfReturnsPerUnitSold = monthlySales > 0 ? totalMonthlyCostOfReturns / monthlySales : 0;
// --- Storage Cost Calculation --- let averageInventoryUnitsInFirstMonth; if (initialShipmentQty <= 0) { averageInventoryUnitsInFirstMonth = 0; } else if (monthlySales <= 0) { averageInventoryUnitsInFirstMonth = initialShipmentQty; } else if (monthlySales >= initialShipmentQty) { averageInventoryUnitsInFirstMonth = initialShipmentQty / 2; } else { averageInventoryUnitsInFirstMonth = (initialShipmentQty + (initialShipmentQty - monthlySales)) / 2; } averageInventoryUnitsInFirstMonth = Math.max(0, averageInventoryUnitsInFirstMonth); const actualTotalMonthlyStorageCost = averageInventoryUnitsInFirstMonth * storageRatePerUnitPerMonth; const storageCostAppliedToEachUnitSold = monthlySales > 0 ? actualTotalMonthlyStorageCost / monthlySales : 0;
// --- VAT & Net Selling Price --- const vatMultiplier = vatRatePercent / 100; const netSellingPrice = sellingPrice / (1 + vatMultiplier); const vatAmountPerUnit = sellingPrice - netSellingPrice;
// --- Per Unit Costs --- let marketingCostPerUnit = monthlySales > 0 ? monthlyMarketing / monthlySales : 0; const totalFixedMonthlyCostsForPerUnitCalc = otherMonthlyFixedCosts + amazonProPlanFee; let otherFixedCostsPerUnit = monthlySales > 0 ? totalFixedMonthlyCostsForPerUnitCalc / monthlySales : 0;
const totalAmazonFeesPerUnit = referralFeeAmountPerUnit + (isFBA ? fbaFeePerUnit : 0) + storageCostAppliedToEachUnitSold + dstPerUnit; const totalCOGSPerUnit = productCost + shippingToAmazonCost + (isFBA ? 0 : fbmShippingToCustomer); const totalVariableCostsPerUnitExclVAT = totalCOGSPerUnit + totalAmazonFeesPerUnit + costOfReturnsPerUnitSold;
const netProfitPerUnitBeforeAgency = netSellingPrice - totalVariableCostsPerUnitExclVAT - marketingCostPerUnit - otherFixedCostsPerUnit; const totalInvestmentPerUnit = productCost + shippingToAmazonCost; // For ROI const roi = totalInvestmentPerUnit > 0 ? (netProfitPerUnitBeforeAgency / totalInvestmentPerUnit) * 100 : 0;
// --- Monthly Calculations (Before Agency Fee) --- const monthlyTotalRevenue = sellingPrice * monthlySales; const monthlyNetRevenue = netSellingPrice * monthlySales; const monthlyProductCostsTotal = (productCost + shippingToAmazonCost + (isFBA ? 0 : fbmShippingToCustomer)) * monthlySales;
const monthlyReferralFeesTotal = referralFeeAmountPerUnit * monthlySales; const monthlyFbaFeesTotal = (isFBA ? fbaFeePerUnit : 0) * monthlySales; const totalMonthlyAmazonFeesCalculated = monthlyReferralFeesTotal + monthlyFbaFeesTotal + actualTotalMonthlyStorageCost + totalMonthlyDST;
const monthlyVATRemittable = vatAmountPerUnit * monthlySales; const totalMonthlyFixedExpensesBeforeAgency = monthlyMarketing + amazonProPlanFee + otherMonthlyFixedCosts + totalMonthlyCostOfReturns; const monthlyProfitBeforeAgencyFee = monthlyNetRevenue - monthlyProductCostsTotal - totalMonthlyAmazonFeesCalculated - totalMonthlyFixedExpensesBeforeAgency;
// --- CM2 Calculation --- const profitBeforeAdvertisingAndAgency = monthlyProfitBeforeAgencyFee + monthlyMarketing; const cm2_margin = monthlyNetRevenue > 0 ? (profitBeforeAdvertisingAndAgency / monthlyNetRevenue) * 100 : 0;
// --- Agency Fee Calculation --- const agencyFeeDetails = calculateAgencyFee(monthlyTotalRevenue, numProducts, currency, selectedAgencyTier); const monthlyAgencyFeeMarketplace = agencyFeeDetails.totalFeeMarketplaceCurrency;
// --- Final Monthly Net Profit & Margin (After Agency Fee) / CM3 --- const monthlyNetProfitAfterAgencyFee = monthlyProfitBeforeAgencyFee - monthlyAgencyFeeMarketplace; const netMarginCM3 = monthlyNetRevenue > 0 ? (monthlyNetProfitAfterAgencyFee / monthlyNetRevenue) * 100 : 0;
// --- Break-Even Analysis (Includes agency fee if selected) --- let totalFixedCostsForBreakEven = amazonProPlanFee + otherMonthlyFixedCosts + monthlyMarketing + actualTotalMonthlyStorageCost + totalMonthlyCostOfReturns + totalMonthlyDST; if (selectedAgencyTier !== 'none') { totalFixedCostsForBreakEven += monthlyAgencyFeeMarketplace; } // Contribution Margin Per Unit for Break-even (Net Selling Price - All Variable Costs Per Unit) const contributionMarginPerUnitForBreakEven = netSellingPrice - (totalCOGSPerUnit + referralFeeAmountPerUnit + (isFBA ? fbaFeePerUnit : 0) + dstPerUnit + costOfReturnsPerUnitSold + storageCostAppliedToEachUnitSold);
let unitsToBreakEven = 0; let revenueToBreakEven = 0; if (contributionMarginPerUnitForBreakEven > 0) { unitsToBreakEven = totalFixedCostsForBreakEven / contributionMarginPerUnitForBreakEven; revenueToBreakEven = unitsToBreakEven * sellingPrice; } else if (totalFixedCostsForBreakEven > 0) { unitsToBreakEven = Infinity; revenueToBreakEven = Infinity; }
// --- Marketing Performance (TACoS) --- const roas = monthlyMarketing > 0 ? monthlyNetRevenue / monthlyMarketing : 0; const tacos = monthlyTotalRevenue > 0 ? (monthlyMarketing / monthlyTotalRevenue) * 100 : 0;
// --- Inventory Metrics --- const inventoryTurnMonthly = monthlySales > 0 && averageInventoryUnitsInFirstMonth > 0 ? monthlySales / averageInventoryUnitsInFirstMonth : 0; const daysOfInventory = inventoryTurnMonthly > 0 ? 30 / inventoryTurnMonthly : (averageInventoryUnitsInFirstMonth > 0 ? Infinity : 0);
// --- Update Results UI --- const safeUpdate = (elementKey, value) => { if (resultElements[elementKey]) { resultElements[elementKey].textContent = value; } else { console.warn(`Result element '${elementKey}' not found in DOM.`); } };
safeUpdate('productName', productName); safeUpdate('category', categoryText); safeUpdate('marketplace', currency === 'gbp' ? 'Amazon UK (£)' : 'Amazon IE (€)'); safeUpdate('netSellingPricePU', formatCurrency(netSellingPrice, currency)); safeUpdate('productCostPU', formatCurrency(productCost, currency)); safeUpdate('shippingToAmazonCostPU', formatCurrency(shippingToAmazonCost, currency)); safeUpdate('fbmShippingToCustomerCostPU', formatCurrency(fbmShippingToCustomer, currency)); safeUpdate('referralFeePU', `${formatCurrency(referralFeeAmountPerUnit, currency)} (${actualReferralPercent.toFixed(2)}%)`); safeUpdate('fbaFeePU', formatCurrency(fbaFeePerUnit, currency)); safeUpdate('dstPU', formatCurrency(dstPerUnit, currency)); safeUpdate('storageFeePU', formatCurrency(storageCostAppliedToEachUnitSold, currency)); safeUpdate('returnCostPU', formatCurrency(costOfReturnsPerUnitSold, currency)); safeUpdate('marketingCostPU', formatCurrency(marketingCostPerUnit, currency)); safeUpdate('otherFixedCostsPU', formatCurrency(otherFixedCostsPerUnit, currency)); safeUpdate('roi', formatNumber(roi, 1) + '%'); safeUpdate('netProfitPUBeforeAgency', formatCurrency(netProfitPerUnitBeforeAgency, currency));
safeUpdate('monthlySalesNum', monthlySales); safeUpdate('monthlyTotalRevenue', formatCurrency(monthlyTotalRevenue, currency)); safeUpdate('monthlyNetRevenue', formatCurrency(monthlyNetRevenue, currency)); safeUpdate('monthlyProductCosts', formatCurrency(monthlyProductCostsTotal, currency)); safeUpdate('monthlyAmazonFees', formatCurrency(totalMonthlyAmazonFeesCalculated, currency)); safeUpdate('monthlyTotalStorageCost', formatCurrency(actualTotalMonthlyStorageCost, currency)); safeUpdate('monthlyDST', formatCurrency(totalMonthlyDST, currency)); safeUpdate('monthlyReturnCosts', formatCurrency(totalMonthlyCostOfReturns, currency)); safeUpdate('monthlyVATRemittable', formatCurrency(monthlyVATRemittable, currency)); safeUpdate('monthlyMarketingCosts', formatCurrency(monthlyMarketing, currency)); safeUpdate('monthlyOtherFixedCosts', formatCurrency(otherMonthlyFixedCosts + amazonProPlanFee, currency)); safeUpdate('monthlyProfitBeforeAgency', formatCurrency(monthlyProfitBeforeAgencyFee, currency));
safeUpdate('cm2', formatNumber(cm2_margin, 1) + '%');
if (selectedAgencyTier !== 'none') { agencyFeeResultsSection.classList.remove('hidden'); safeUpdate('agencyTier', agencyFeeDetails.tierName); safeUpdate('agencyFixedFeeEUR', formatCurrency(agencyFeeDetails.fixedFeeEUR, 'eur')); safeUpdate('agencyPercentageFeeEUR', formatCurrency(agencyFeeDetails.percentageFeeEUR, 'eur')); safeUpdate('monthlyAgencyFeeEUR', formatCurrency(agencyFeeDetails.totalFeeEUR, 'eur'));
const marketplaceSymbol = currency === 'gbp' ? '£' : '€'; if (resultElements.agencyFixedFeeMarketplaceSymbol) resultElements.agencyFixedFeeMarketplaceSymbol.textContent = marketplaceSymbol; if (resultElements.agencyPercentageFeeMarketplaceSymbol) resultElements.agencyPercentageFeeMarketplaceSymbol.textContent = marketplaceSymbol; if (resultElements.marketplaceCurrencySymbol) resultElements.marketplaceCurrencySymbol.textContent = marketplaceSymbol;
safeUpdate('agencyFixedFeeMarketplace', formatCurrency(agencyFeeDetails.fixedFeeMarketplaceCurrency, currency)); safeUpdate('agencyPercentageFeeMarketplace', formatCurrency(agencyFeeDetails.percentageFeeMarketplaceCurrency, currency)); safeUpdate('monthlyAgencyFeeMarketplace', formatCurrency(agencyFeeDetails.totalFeeMarketplaceCurrency, currency));
if (resultElements.agencyFixedFeeMarketplaceContainer) resultElements.agencyFixedFeeMarketplaceContainer.style.display = currency === 'gbp' ? 'flex' : 'none'; if (resultElements.agencyPercentageFeeMarketplaceContainer) resultElements.agencyPercentageFeeMarketplaceContainer.style.display = currency === 'gbp' ? 'flex' : 'none'; if (resultElements.monthlyAgencyFeeMarketplaceContainer) resultElements.monthlyAgencyFeeMarketplaceContainer.style.display = currency === 'gbp' ? 'flex' : 'none';
safeUpdate('monthlyNetProfitAfterAgency', formatCurrency(monthlyNetProfitAfterAgencyFee, currency)); safeUpdate('netMarginCM3', formatNumber(netMarginCM3, 1) + '%'); if (resultElements.profitabilityIndicatorTitle) resultElements.profitabilityIndicatorTitle.textContent = "Overall Profitability (After Agency Fees)"; } else { agencyFeeResultsSection.classList.add('hidden'); safeUpdate('monthlyNetProfitAfterAgency', "N/A"); safeUpdate('netMarginCM3', formatNumber( (monthlyNetRevenue > 0 ? (monthlyProfitBeforeAgencyFee / monthlyNetRevenue) * 100 : 0) , 1) + '%'); if (resultElements.profitabilityIndicatorTitle) resultElements.profitabilityIndicatorTitle.textContent = "Overall Profitability"; }
safeUpdate('unitsToBreakEven', isFinite(unitsToBreakEven) ? Math.ceil(unitsToBreakEven).toLocaleString() + " units" : "N/A (check costs/profit)"); safeUpdate('revenueToBreakEven', isFinite(revenueToBreakEven) ? formatCurrency(revenueToBreakEven, currency) : "N/A"); safeUpdate('roas', formatNumber(roas,2)); safeUpdate('tacos', formatNumber(tacos,2) + '%'); safeUpdate('avgInventoryUnits', formatNumber(averageInventoryUnitsInFirstMonth,1) + " units"); safeUpdate('inventoryTurn', formatNumber(inventoryTurnMonthly,2)); safeUpdate('daysOfInventory', isFinite(daysOfInventory) ? formatNumber(daysOfInventory,1) + " days" : "N/A");
// Profitability Indicator const finalProfitMarginForIndicator = selectedAgencyTier !== 'none' ? netMarginCM3 : (monthlyNetRevenue > 0 ? (monthlyProfitBeforeAgencyFee / monthlyNetRevenue) * 100 : 0); let profitabilityAdviceText = "";
if (finalProfitMarginForIndicator > 20) { resultElements.profitabilityIndicator.textContent = 'Potentially Strong'; resultElements.profitabilityIndicator.className = 'inline-block px-4 py-2 rounded-full font-bold text-white text-sm mb-2 bg-green-500'; profitabilityAdviceText = 'Good margins. Looks promising!'; } else if (finalProfitMarginForIndicator > 10) { resultElements.profitabilityIndicator.textContent = 'Potentially Viable'; resultElements.profitabilityIndicator.className = 'inline-block px-4 py-2 rounded-full font-bold text-white text-sm mb-2 bg-blue-500'; profitabilityAdviceText = 'Decent profit. Review costs/volume.'; } else if (finalProfitMarginForIndicator > 0) { resultElements.profitabilityIndicator.textContent = 'Marginal'; resultElements.profitabilityIndicator.className = 'inline-block px-4 py-2 rounded-full font-bold text-white text-sm mb-2 bg-yellow-500 text-black'; profitabilityAdviceText = 'Thin margins. High risk.'; } else { resultElements.profitabilityIndicator.textContent = 'Not Profitable'; resultElements.profitabilityIndicator.className = 'inline-block px-4 py-2 rounded-full font-bold text-white text-sm mb-2 bg-red-500'; profitabilityAdviceText = 'Losing money. Re-evaluate strategy.'; }
if (selectedAgencyTier !== 'none' && monthlyProfitBeforeAgencyFee > 0 && monthlyNetProfitAfterAgencyFee <= 0) { profitabilityAdviceText = `Product is profitable before agency fees. With agency support, it typically takes 3-6 months to optimize for overall profitability including these fees. Current projection shows a loss after agency fees for Month 1.`; } else if (selectedAgencyTier !== 'none' && monthlyProfitBeforeAgencyFee > 0 && netMarginCM3 > 0 && netMarginCM3 <= 10) { profitabilityAdviceText = `Product is profitable before agency fees. Margins are tight after including agency fees for Month 1. Optimization over 3-6 months is typical to improve this.`; } if (resultElements.profitabilityAdvice) resultElements.profitabilityAdvice.textContent = profitabilityAdviceText; toggleFulfillmentFields(); initialPlaceholder.classList.add('hidden'); resultsContainer.classList.remove('hidden'); forecastResultsSection.classList.add('hidden'); } function generateSalesForecast() { if (resultsContainer.classList.contains('hidden')) { calculateAndDisplayResults(); if (resultsContainer.classList.contains('hidden')) { showModalMessage("Please ensure all initial month details are valid before generating a forecast."); return; } } const forecastMethod = document.querySelector('input[name="forecastMethod"]:checked').value; const initialMonthlySales = getNumericValue(estimatedMonthlySalesInput, 0); const growthFactor = getNumericValue(monthlySalesGrowthFactorInput, 0) / 100; const initialMarketingCost = getNumericValue(monthlyMarketingCostInput, 0); const sellingPrice = getNumericValue(sellingPriceInput, 0); const productCost = getNumericValue(productCostInput, 0); const shippingToAmazonCost = getNumericValue(shippingToAmazonCostInput, 0); const numProducts = getNumericValue(numberOfProductsInput, 1); const isFBA = fulfillmentOptionSelect.value === 'fba'; const fulfillmentType = fulfillmentOptionSelect.value; const fbaFeePerUnit = isFBA ? getNumericValue(fbaFeeInput, 0) : 0; const fbmShippingToCustomer = isFBA ? 0 : getNumericValue(fbmShippingToCustomerCostInput, 0); const storageRatePerUnitPerMonth = getNumericValue(avgMonthlyStorageFeePerUnitInput, 0); const vatRatePercent = getNumericValue(vatRateSelect, 20); const currency = currencySelect.value; const amazonProPlanFee = getNumericValue(amazonProPlanFeeInput, 0); const otherMonthlyFixedCosts = getNumericValue(otherMonthlyCostsInput, 0); const returnRatePercent = getNumericValue(estimatedReturnRateInput, 0); const costPerReturn = getNumericValue(avgCostPerReturnInput, 0); const selectedAgencyTier = agencyServiceTierSelect.value; const initialShipmentQty = getNumericValue(initialShipmentQuantityInput); const forecastStartMonthIndex = parseInt(forecastStartMonth.value); let initialAvgInventory; if (initialShipmentQty <= 0) initialAvgInventory = 0; else if (initialMonthlySales <= 0) initialAvgInventory = initialShipmentQty; else if (initialMonthlySales >= initialShipmentQty) initialAvgInventory = initialShipmentQty / 2; else initialAvgInventory = (initialShipmentQty + (initialShipmentQty - initialMonthlySales)) / 2; initialAvgInventory = Math.max(0, initialAvgInventory); const initialTotalMonthlyStorage = initialAvgInventory * storageRatePerUnitPerMonth; const storageCostAppliedToEachUnitSoldForForecast = initialMonthlySales > 0 ? initialTotalMonthlyStorage / initialMonthlySales : (initialAvgInventory > 0 ? storageRatePerUnitPerMonth : 0) ;
forecastTableBody.innerHTML = ''; let currentMonthSales = initialMonthlySales; let previousMonthTotalRevenue = sellingPrice * initialMonthlySales; let cumulativeNetProfit = 0; const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; const currentYear = new Date().getFullYear();
for (let i = 0; i < 12; i++) { const monthIndex = (forecastStartMonthIndex + i) % 12; const yearOffset = Math.floor((forecastStartMonthIndex + i) / 12); const forecastYear = currentYear + yearOffset; const monthDisplayName = `${monthNames[monthIndex]} ${forecastYear}`; let salesForThisMonth; let marketingForThisMonth; if (forecastMethod === 'manual') { const manualSaleInputEl = document.getElementById(`manualSaleMonth${i + 1}`); salesForThisMonth = manualSaleInputEl ? getNumericValue(manualSaleInputEl, 0) : 0; const manualMarketingInputEl = document.getElementById(`manualMarketingMonth${i + 1}`); marketingForThisMonth = manualMarketingInputEl ? getNumericValue(manualMarketingInputEl, initialMarketingCost) : initialMarketingCost; } else { if (i > 0) { currentMonthSales = currentMonthSales * (1 + growthFactor); } salesForThisMonth = Math.round(currentMonthSales); marketingForThisMonth = initialMonthlySales > 0 ? (initialMarketingCost / initialMonthlySales) * salesForThisMonth : initialMarketingCost; }
const referralFeeData = getReferralFeeDetails(sellingPrice, productCategorySelect.value, fulfillmentType); const referralFeeAmountPerUnit = referralFeeData.amount;
let dstPerUnit = 0; if (currency === 'gbp') { dstPerUnit = (referralFeeAmountPerUnit * 0.02) + ((isFBA ? fbaFeePerUnit : 0) * 0.02); } const monthlyReferralFeesTotal = referralFeeAmountPerUnit * salesForThisMonth; const monthlyFbaFeesTotal = (isFBA ? fbaFeePerUnit : 0) * salesForThisMonth; const totalMonthlyDST = dstPerUnit * salesForThisMonth;
const numberOfReturnsMonthly = salesForThisMonth * (returnRatePercent / 100); const totalMonthlyCostOfReturns = numberOfReturnsMonthly * costPerReturn;
const actualTotalMonthlyStorageCost = storageCostAppliedToEachUnitSoldForForecast * salesForThisMonth;
const netSellingPrice = sellingPrice / (1 + (vatRatePercent / 100));
const monthlyTotalRevenue = sellingPrice * salesForThisMonth; const monthlyNetRevenue = netSellingPrice * salesForThisMonth; const monthlyProductCostsTotal = (productCost + shippingToAmazonCost + (isFBA ? 0 : fbmShippingToCustomer)) * salesForThisMonth;
const totalMonthlyAmazonFeesCalculated = monthlyReferralFeesTotal + monthlyFbaFeesTotal + actualTotalMonthlyStorageCost + totalMonthlyDST;
const totalMonthlyFixedExpensesBeforeAgency = marketingForThisMonth + amazonProPlanFee + otherMonthlyFixedCosts + totalMonthlyCostOfReturns; const monthlyProfitBeforeAgencyFee = monthlyNetRevenue - monthlyProductCostsTotal - totalMonthlyAmazonFeesCalculated - totalMonthlyFixedExpensesBeforeAgency;
const revenueForAgencyCalc = (i === 0) ? sellingPrice * initialMonthlySales : previousMonthTotalRevenue; const agencyFeeDetails = calculateAgencyFee(revenueForAgencyCalc, numProducts, currency, selectedAgencyTier); const monthlyAgencyFeeMarketplace = agencyFeeDetails.totalFeeMarketplaceCurrency;
const monthlyNetProfitAfterAgencyFee = monthlyProfitBeforeAgencyFee - monthlyAgencyFeeMarketplace; cumulativeNetProfit += monthlyNetProfitAfterAgencyFee; const monthlyNetMarginCM3 = monthlyNetRevenue > 0 ? (monthlyNetProfitAfterAgencyFee / monthlyNetRevenue) * 100 : 0; const monthlyTACoS = monthlyTotalRevenue > 0 ? (marketingForThisMonth / monthlyTotalRevenue) * 100 : 0;
const suggestedMinInventory = (salesForThisMonth / 30) * 45;
const row = forecastTableBody.insertRow(); row.insertCell().textContent = monthDisplayName; row.insertCell().textContent = salesForThisMonth.toLocaleString(); row.insertCell().textContent = formatCurrency(monthlyTotalRevenue, currency); row.insertCell().textContent = formatCurrency(monthlyNetRevenue, currency); row.insertCell().textContent = formatCurrency(monthlyReferralFeesTotal, currency); row.insertCell().textContent = formatCurrency(monthlyFbaFeesTotal, currency); row.insertCell().textContent = formatCurrency(actualTotalMonthlyStorageCost, currency); row.insertCell().textContent = formatCurrency(totalMonthlyDST, currency); row.insertCell().textContent = formatCurrency(totalMonthlyAmazonFeesCalculated, currency); row.insertCell().textContent = formatCurrency(totalMonthlyCostOfReturns, currency); row.insertCell().textContent = formatCurrency(marketingForThisMonth, currency); row.insertCell().textContent = formatCurrency(amazonProPlanFee + otherMonthlyFixedCosts, currency); row.insertCell().textContent = formatCurrency(monthlyAgencyFeeMarketplace, currency); row.insertCell().textContent = formatCurrency(monthlyNetProfitAfterAgencyFee, currency); row.insertCell().textContent = formatCurrency(cumulativeNetProfit, currency); row.insertCell().textContent = formatNumber(monthlyNetMarginCM3, 1) + '%'; row.insertCell().textContent = formatNumber(monthlyTACoS, 2) + '%'; row.insertCell().textContent = Math.ceil(suggestedMinInventory).toLocaleString() + " units"; row.insertCell().textContent = formatCurrency(monthlyProductCostsTotal, currency);
previousMonthTotalRevenue = monthlyTotalRevenue; } forecastResultsSection.classList.remove('hidden'); }
function exportForecastToCSV() { if (forecastResultsSection.classList.contains('hidden') || forecastTableBody.rows.length === 0) { showModalMessage("Please generate the 12-month forecast first."); return; } const currentMarketplaceCurrency = currencySelect.value.toUpperCase(); const headers = [ "Month", "Sales (Units)", `Total Revenue (${currentMarketplaceCurrency})`, `Net Revenue (${currentMarketplaceCurrency})`, `Referral Fee (${currentMarketplaceCurrency})`, `FBA Fee (${currentMarketplaceCurrency})`, `Storage Cost (${currentMarketplaceCurrency})`, `DST (UK) (${currentMarketplaceCurrency})`, `Total Amazon Fees (${currentMarketplaceCurrency})`, `Return Costs (${currentMarketplaceCurrency})`, `Marketing (${currentMarketplaceCurrency})`, `Other Fixed (${currentMarketplaceCurrency})`, `Agency Fee (${currentMarketplaceCurrency})`, `Net Profit (${currentMarketplaceCurrency})`, `Cumulative Net Profit (${currentMarketplaceCurrency})`, "Net Margin CM3 (%)", "TACoS (%)", "Min. Inventory (45 day) (Units)", `Total COGS (${currentMarketplaceCurrency})` ]; const data = [headers];
for (const row of forecastTableBody.rows) { const rowData = []; for (const cell of row.cells) { let cellText = cell.textContent; if (cellText.includes('£') || cellText.includes('€') || cellText.includes('%')) { cellText = cellText.replace(/[£€%,]/g, ''); } else if (cellText.includes('units')) { cellText = cellText.replace(/ units|,/g, ''); } rowData.push(cellText); } data.push(rowData); }
let csvContent = "data:text/csv;charset=utf-8," + data.map(e => e.map(val => `"${String(val).replace(/"/g, '""')}"`).join(",")).join("\n");
const encodedUri = encodeURI(csvContent); const link = document.createElement("a"); link.setAttribute("href", encodedUri); const productNameSafe = (productNameInput.value || 'amazon_profit_calc').replace(/[^a-z0-9]/gi, '_').toLowerCase(); link.setAttribute("download", `${productNameSafe}_12_month_forecast.csv`); document.body.appendChild(link); link.click(); document.body.removeChild(link); showModalMessage("12-Month Forecast exported to CSV!"); }
function exportResultsToCSV() { if (resultsContainer.classList.contains('hidden')) { showModalMessage("Please calculate results first before exporting."); return; } const currencyCode = currencySelect.value.toUpperCase(); const selectedAgencyTier = agencyServiceTierSelect.value; const headers = ["Metric", "Amount", "Unit"]; const data = [ ["Product Name", resultElements.productName.textContent, ""], ["Category", resultElements.category.textContent, ""], ["Marketplace", resultElements.marketplace.textContent, ""], ["--- Per Unit ---"], ["Net Selling Price (excl. VAT)", formatCurrency(getNumericValue(sellingPriceInput) / (1 + getNumericValue(vatRateSelect)/100), currencySelect.value, false), currencyCode], ["Landed Product Cost", formatCurrency(getNumericValue(productCostInput), currencySelect.value, false), currencyCode], ["Shipping to Amazon", formatCurrency(getNumericValue(shippingToAmazonCostInput), currencySelect.value, false), currencyCode], fulfillmentOptionSelect.value === 'fbm' ? ["Shipping to Customer (FBM)", formatCurrency(getNumericValue(fbmShippingToCustomerCostInput), currencySelect.value, false), currencyCode] : null, ["Referral Fee Applied", resultElements.referralFeePU.textContent.split(' ')[0].replace(/[£€,]/g, ''), currencyCode], fulfillmentOptionSelect.value === 'fba' ? ["FBA Fee", formatCurrency(getNumericValue(fbaFeeInput), currencySelect.value, false), currencyCode] : null, ["Digital Services Tax (per unit, if UK)", resultElements.dstPU.textContent.replace(/[£€,]/g, ''), currencyCode], ["Storage Fee (per unit sold)", resultElements.storageFeePU.textContent.replace(/[£€,]/g, ''), currencyCode], ["Cost of Returns (per unit sold)", resultElements.returnCostPU.textContent.replace(/[£€,]/g, ''), currencyCode], ["Marketing Cost (per unit)", resultElements.marketingCostPU.textContent.replace(/[£€,]/g, ''), currencyCode], ["Other Fixed Costs (per unit)", resultElements.otherFixedCostsPU.textContent.replace(/[£€,]/g, ''), currencyCode], ["Return on Investment (ROI)", resultElements.roi.textContent.replace('%',''), "%"], ["Net Profit Per Unit (Before Agency Fee)", resultElements.netProfitPUBeforeAgency.textContent.replace(/[£€,]/g, ''), currencyCode], ["--- Monthly Projections ---"], ["Number of Products (SKUs)", getNumericValue(numberOfProductsInput,1), "SKUs"], ["Initial Shipment Quantity", getNumericValue(initialShipmentQuantityInput,0), "units"], ["Estimated Monthly Sales", resultElements.monthlySalesNum.textContent, "units"], ["Total Revenue (VAT incl.)", resultElements.monthlyTotalRevenue.textContent.replace(/[£€,]/g, ''), currencyCode], ["Total Net Revenue (excl. VAT)", resultElements.monthlyNetRevenue.textContent.replace(/[£€,]/g, ''), currencyCode], ["Total Landed Product Costs", resultElements.monthlyProductCosts.textContent.replace(/[£€,]/g, ''), currencyCode], ["Total Amazon Fees (incl. Storage & DST)", resultElements.monthlyAmazonFees.textContent.replace(/[£€,]/g, ''), currencyCode], ["(Total Monthly Storage Cost)", resultElements.monthlyTotalStorageCost.textContent.replace(/[£€,]/g, ''), currencyCode], ["(Total Monthly Digital Services Tax)", resultElements.monthlyDST.textContent.replace(/[£€,]/g, ''), currencyCode], ["Total Monthly Cost of Returns", resultElements.monthlyReturnCosts.textContent.replace(/[£€,]/g, ''), currencyCode], ["Total VAT Remittable", resultElements.monthlyVATRemittable.textContent.replace(/[£€,]/g, ''), currencyCode], ["Total Marketing Costs", resultElements.monthlyMarketingCosts.textContent.replace(/[£€,]/g, ''), currencyCode], ["Total Other Fixed Costs (incl. Pro Plan)", resultElements.monthlyOtherFixedCosts.textContent.replace(/[£€,]/g, ''), currencyCode], ["Monthly Profit (Before Agency Fee)", resultElements.monthlyProfitBeforeAgency.textContent.replace(/[£€,]/g, ''), currencyCode], ["--- Margin Analysis ---"], ["Contribution Margin 2 (CM2 - Before Adv. & Agency)", resultElements.cm2.textContent.replace('%',''), "%"], ];
if (selectedAgencyTier !== 'none') { data.push( ["Selected Agency Tier", resultElements.agencyTier.textContent, ""], ["Agency Fixed Fee (EUR)", resultElements.agencyFixedFeeEUR.textContent.replace('€',''), "EUR"], ); if (currencySelect.value === 'gbp') { data.push(["Agency Fixed Fee (GBP)", resultElements.agencyFixedFeeMarketplace.textContent.replace('£',''), "GBP"]); } data.push( ["Agency Percentage Fee (EUR)", resultElements.agencyPercentageFeeEUR.textContent.replace('€',''), "EUR"], ); if (currencySelect.value === 'gbp') { data.push(["Agency Percentage Fee (GBP)", resultElements.agencyPercentageFeeMarketplace.textContent.replace('£',''), "GBP"]); } data.push( ["Total Monthly Agency Fee (EUR)", resultElements.monthlyAgencyFeeEUR.textContent.replace('€',''), "EUR"], );
if (currencySelect.value === 'gbp') { data.push(["Total Monthly Agency Fee (GBP)", resultElements.monthlyAgencyFeeMarketplace.textContent.replace('£',''), "GBP"]); } data.push( ["Total Monthly Net Profit (After Agency Fee)", resultElements.monthlyNetProfitAfterAgency.textContent.replace(/[£€,]/g, ''), currencyCode] ); } data.push( ["Net Margin (CM3 - After Adv. & Agency)", resultElements.netMarginCM3.textContent.replace('%',''), "%"] );
data.push( ["--- Key Performance Indicators ---"], ["Units to Break Even (Monthly)", resultElements.unitsToBreakEven.textContent.replace(/ units|,/g, ''), ""], ["Revenue to Break Even (Monthly)", resultElements.revenueToBreakEven.textContent.replace(/[£€,]/g, ''), currencyCode], ["Return on Ad Spend (ROAS)", resultElements.roas.textContent, ""], ["Total Adv. Cost of Sales (TACoS)", resultElements.tacos.textContent.replace('%',''), "%"], ["Average Inventory Units (First Month)", resultElements.avgInventoryUnits.textContent.replace(/ units|,/g, ''), ""], ["Inventory Turn (Monthly)", resultElements.inventoryTurn.textContent, ""], ["Days of Inventory", resultElements.daysOfInventory.textContent.replace(/ days|,/g, ''), ""] );
let csvContent = "data:text/csv;charset=utf-8," + headers.join(",") + "\n" + data.filter(row => row !== null).map(e => Array.isArray(e) ? e.map(val => `"${String(val).replace(/"/g, '""')}"`).join(",") : `"${String(e).replace(/"/g, '""')}"`).join("\n");
const encodedUri = encodeURI(csvContent); const link = document.createElement("a"); link.setAttribute("href", encodedUri); const productNameSafe = (productNameInput.value || 'amazon_profit_calc').replace(/[^a-z0-9]/gi, '_').toLowerCase(); link.setAttribute("download", `${productNameSafe}_profitability_report.csv`); document.body.appendChild(link); link.click(); document.body.removeChild(link); showModalMessage("Results exported to CSV!"); }
function createManualInputs() { const salesGrid = document.getElementById('manualSalesGrid'); const marketingGrid = document.getElementById('manualMarketingGrid'); if (!salesGrid || !marketingGrid) return; salesGrid.innerHTML = ''; marketingGrid.innerHTML = '';
for (let i = 1; i <= 12; i++) { // Sales Input const salesMonthDiv = document.createElement('div'); const salesLabel = document.createElement('label'); salesLabel.htmlFor = `manualSaleMonth${i}`; salesLabel.textContent = `Month ${i} Sales`; const salesInput = document.createElement('input'); salesInput.type = 'number'; salesInput.id = `manualSaleMonth${i}`; salesInput.min = "0"; salesInput.placeholder = "Units"; salesInput.className = "w-full px-3 py-1.5 border border-gray-300 rounded-md focus:ring-amazon focus:border-amazon text-sm"; salesMonthDiv.appendChild(salesLabel); salesMonthDiv.appendChild(salesInput); salesGrid.appendChild(salesMonthDiv); // Marketing Input const marketingMonthDiv = document.createElement('div'); const marketingLabel = document.createElement('label'); marketingLabel.htmlFor = `manualMarketingMonth${i}`; marketingLabel.textContent = `Month ${i} Marketing`; const marketingInput = document.createElement('input'); marketingInput.type = 'number'; marketingInput.id = `manualMarketingMonth${i}`; marketingInput.min = "0"; marketingInput.step = "0.01"; marketingInput.placeholder = "Spend"; marketingInput.className = "w-full px-3 py-1.5 border border-gray-300 rounded-md focus:ring-amazon focus:border-amazon text-sm"; marketingMonthDiv.appendChild(marketingLabel); marketingMonthDiv.appendChild(marketingInput); marketingGrid.appendChild(marketingMonthDiv); } } function toggleForecastInputMethod() { const forecastMethodGrowthContainer = document.getElementById('monthlySalesGrowthFactorContainer'); const forecastMethodManualContainer = document.getElementById('manualInputsContainer'); const monthlyMarketingCostContainer = document.getElementById('monthlyMarketingCostContainer'); if (forecastMethodGrowthRadio && forecastMethodGrowthRadio.checked) { if(forecastMethodGrowthContainer) forecastMethodGrowthContainer.classList.remove('hidden'); if(monthlyMarketingCostContainer) monthlyMarketingCostContainer.classList.remove('hidden'); if(forecastMethodManualContainer) forecastMethodManualContainer.classList.add('hidden'); } else { if(forecastMethodGrowthContainer) forecastMethodGrowthContainer.classList.add('hidden'); if(monthlyMarketingCostContainer) monthlyMarketingCostContainer.classList.add('hidden'); if(forecastMethodManualContainer) forecastMethodManualContainer.classList.remove('hidden'); } } function populateForecastStartMonthDropdown() { const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; if (!forecastStartMonth) return; monthNames.forEach((month, index) => { const option = document.createElement('option'); option.value = index; option.textContent = month; forecastStartMonth.appendChild(option); }); }
// --- DOM Element Constant Declarations (after function definitions) --- const productNameInput = document.getElementById('productName'); const productCategorySelect = document.getElementById('productCategory'); const sellingPriceInput = document.getElementById('sellingPrice'); const productCostInput = document.getElementById('productCost'); const shippingToAmazonCostInput = document.getElementById('shippingToAmazonCost'); const numberOfProductsInput = document.getElementById('numberOfProducts'); const initialShipmentQuantityInput = document.getElementById('initialShipmentQuantity'); const estimatedMonthlySalesInput = document.getElementById('estimatedMonthlySales'); const monthlySalesGrowthFactorInput = document.getElementById('monthlySalesGrowthFactor'); const forecastMethodGrowthRadio = document.getElementById('forecastMethodGrowth'); const forecastMethodManualRadio = document.getElementById('forecastMethodManual'); const manualSalesInputContainer = document.getElementById('manualSalesInputContainer'); const monthlyMarketingCostContainer = document.getElementById('monthlyMarketingCostContainer'); const forecastStartMonth = document.getElementById('forecastStartMonth');
const calculatedReferralFeeDisplay = document.getElementById('calculatedReferralFeeDisplay'); const referralFeeNote = document.getElementById('referralFeeNote');
const fulfillmentOptionSelect = document.getElementById('fulfillmentOption'); const fbaFeeInput = document.getElementById('fbaFee'); const amazonFBACalculatorLink = document.getElementById('amazonFBACalculatorLink'); const avgMonthlyStorageFeePerUnitInput = document.getElementById('avgMonthlyStorageFeePerUnit'); const vatRateSelect = document.getElementById('vatRate'); const currencySelect = document.getElementById('currency'); const gbpToEurRateContainer = document.getElementById('gbpToEurRateContainer'); const gbpToEurRateInput = document.getElementById('gbpToEurRate'); const monthlyMarketingCostInput = document.getElementById('monthlyMarketingCost'); const amazonProPlanFeeInput = document.getElementById('amazonProPlanFee'); const otherMonthlyCostsInput = document.getElementById('otherMonthlyCosts'); const estimatedReturnRateInput = document.getElementById('estimatedReturnRate'); const avgCostPerReturnInput = document.getElementById('avgCostPerReturn'); const agencyServiceTierSelect = document.getElementById('agencyServiceTier'); const fbmShippingToCustomerCostInput = document.getElementById('fbmShippingToCustomerCost');
const fbaFeeContainer = document.getElementById('fbaFeeContainer'); const fbmShippingToCustomerContainer = document.getElementById('fbmShippingToCustomerContainer');
const calculateBtn = document.getElementById('calculateBtn'); const generateForecastBtn = document.getElementById('generateForecastBtn'); const resultsContainer = document.getElementById('resultsContainer'); const initialPlaceholder = document.getElementById('initialPlaceholder'); const agencyFeeResultsSection = document.getElementById('agencyFeeResultsSection'); const forecastResultsSection = document.getElementById('forecastResultsSection'); const forecastTableBody = document.getElementById('forecastTableBody'); const exportForecastBtn = document.getElementById('exportForecastBtn');
// Result fields const resultElements = { productName: document.getElementById('resultProductName'), category: document.getElementById('resultCategory'), marketplace: document.getElementById('resultMarketplace'), netSellingPricePU: document.getElementById('resultNetSellingPricePU'), productCostPU: document.getElementById('resultProductCostPU'), shippingToAmazonCostPU: document.getElementById('resultShippingToAmazonCostPU'), fbmShippingToCustomerCostPUContainer: document.getElementById('resultFbmShippingToCustomerCostPUContainer'), fbmShippingToCustomerCostPU: document.getElementById('resultFbmShippingToCustomerCostPU'), referralFeePU: document.getElementById('resultReferralFeePU'), fbaFeePUContainer: document.getElementById('resultFbaFeePUContainer'), fbaFeePU: document.getElementById('resultFbaFeePU'), dstPU: document.getElementById('resultDSTPU'), storageFeePU: document.getElementById('resultStorageFeePU'), returnCostPU: document.getElementById('resultReturnCostPU'), marketingCostPU: document.getElementById('resultMarketingCostPU'), otherFixedCostsPU: document.getElementById('resultOtherFixedCostsPU'), roi: document.getElementById('resultROI'), netProfitPUBeforeAgency: document.getElementById('resultNetProfitPUBeforeAgency'), monthlySalesNum: document.getElementById('resultMonthlySalesNum'), monthlyTotalRevenue: document.getElementById('resultMonthlyTotalRevenue'), monthlyNetRevenue: document.getElementById('resultMonthlyNetRevenue'), monthlyProductCosts: document.getElementById('resultMonthlyProductCosts'), monthlyAmazonFees: document.getElementById('resultMonthlyAmazonFees'), monthlyTotalStorageCost: document.getElementById('resultMonthlyTotalStorageCost'), monthlyDST: document.getElementById('resultMonthlyDST'), monthlyReturnCosts: document.getElementById('resultMonthlyReturnCosts'), monthlyVATRemittable: document.getElementById('resultMonthlyVATRemittable'), monthlyMarketingCosts: document.getElementById('resultMonthlyMarketingCosts'), monthlyOtherFixedCosts: document.getElementById('resultMonthlyOtherFixedCosts'), monthlyProfitBeforeAgency: document.getElementById('resultMonthlyProfitBeforeAgency'), cm2: document.getElementById('resultCM2'), agencyTier: document.getElementById('resultAgencyTier'), agencyFixedFeeEUR: document.getElementById('resultAgencyFixedFeeEUR'), agencyFixedFeeMarketplaceContainer: document.getElementById('resultAgencyFixedFeeMarketplaceContainer'), agencyFixedFeeMarketplaceSymbol: document.getElementById('resultAgencyFixedFeeMarketplaceSymbol'), agencyFixedFeeMarketplace: document.getElementById('resultAgencyFixedFeeMarketplace'), agencyPercentageFeeEUR: document.getElementById('resultAgencyPercentageFeeEUR'), agencyPercentageFeeMarketplaceContainer: document.getElementById('resultAgencyPercentageFeeMarketplaceContainer'), agencyPercentageFeeMarketplaceSymbol: document.getElementById('resultAgencyPercentageFeeMarketplaceSymbol'), agencyPercentageFeeMarketplace: document.getElementById('resultAgencyPercentageFeeMarketplace'), monthlyAgencyFeeEUR: document.getElementById('resultMonthlyAgencyFeeEUR'), monthlyAgencyFeeMarketplaceContainer: document.getElementById('resultMonthlyAgencyFeeMarketplaceContainer'), marketplaceCurrencySymbol: document.getElementById('resultMarketplaceCurrencySymbol'), monthlyAgencyFeeMarketplace: document.getElementById('resultMonthlyAgencyFeeMarketplace'), monthlyNetProfitAfterAgency: document.getElementById('resultMonthlyNetProfitAfterAgency'), netMarginCM3: document.getElementById('resultNetMarginCM3'), unitsToBreakEven: document.getElementById('resultUnitsToBreakEven'), revenueToBreakEven: document.getElementById('resultRevenueToBreakEven'), roas: document.getElementById('resultROAS'), tacos: document.getElementById('resultTACoS'), avgInventoryUnits: document.getElementById('resultAvgInventoryUnits'), inventoryTurn: document.getElementById('resultInventoryTurn'), daysOfInventory: document.getElementById('resultDaysOfInventory'), profitabilityIndicatorTitle: document.getElementById('profitabilityIndicatorTitle'), profitabilityIndicator: document.getElementById('profitabilityIndicator'), profitabilityAdvice: document.getElementById('profitabilityAdvice') };
const exportBtn = document.getElementById('exportBtn'); const messageModal = document.getElementById('messageModal'); const modalMessageText = document.getElementById('modalMessageText'); const modalCloseBtn = document.getElementById('modalCloseBtn');
// --- Event Listeners (Now after all functions and consts are defined) --- if (productCategorySelect) productCategorySelect.addEventListener('change', updateDisplayedReferralFeeInfo); if (sellingPriceInput) sellingPriceInput.addEventListener('input', updateDisplayedReferralFeeInfo); if (currencySelect) { currencySelect.addEventListener('change', () => { if (gbpToEurRateContainer) gbpToEurRateContainer.classList.toggle('hidden', currencySelect.value !== 'gbp'); updateDisplayedReferralFeeInfo(); }); } if (fulfillmentOptionSelect) { fulfillmentOptionSelect.addEventListener('change', () => { toggleFulfillmentFields(); updateDisplayedReferralFeeInfo(); }); }
if (calculateBtn) calculateBtn.addEventListener('click', calculateAndDisplayResults); if (generateForecastBtn) generateForecastBtn.addEventListener('click', generateSalesForecast); if (exportBtn) exportBtn.addEventListener('click', exportResultsToCSV); if (exportForecastBtn) exportForecastBtn.addEventListener('click', exportForecastToCSV);
if (modalCloseBtn) modalCloseBtn.addEventListener('click', () => { if(messageModal) messageModal.style.display = "none"; }); window.addEventListener('click', (event) => { if (event.target === messageModal && messageModal) messageModal.style.display = "none"; });
if(forecastMethodGrowthRadio) forecastMethodGrowthRadio.addEventListener('change', toggleForecastInputMethod); if(forecastMethodManualRadio) forecastMethodManualRadio.addEventListener('change', toggleForecastInputMethod);
// --- Initial Setup Calls --- populateCategoryDropdown(); createManualInputs(); // Create the manual input fields for sales and marketing toggleFulfillmentFields(); toggleForecastInputMethod(); // Set initial visibility of forecast inputs populateForecastStartMonthDropdown(); // Populate start month dropdown updateDisplayedReferralFeeInfo(); if (gbpToEurRateContainer && currencySelect) { gbpToEurRateContainer.classList.toggle('hidden', currencySelect.value !== 'gbp'); }
if(amazonFBACalculatorLink) { amazonFBACalculatorLink.href = "https://sellercentral.amazon.co.uk/fba/revenuecalculator/index"; } // Set default values from user request if(productNameInput) productNameInput.value = "Fishing Lure"; if(productCategorySelect) productCategorySelect.value = "sports_outdoors"; if(sellingPriceInput) sellingPriceInput.value = "19.99"; if(productCostInput) productCostInput.value = "2.75"; if(shippingToAmazonCostInput) shippingToAmazonCostInput.value = "0.45"; if(fbaFeeInput) fbaFeeInput.value = "1.87"; if(avgMonthlyStorageFeePerUnitInput) avgMonthlyStorageFeePerUnitInput.value = "0.02"; if(estimatedMonthlySalesInput) estimatedMonthlySalesInput.value = "500"; if(avgCostPerReturnInput) avgCostPerReturnInput.value = "4.00"; if(currencySelect) currencySelect.value = "gbp";
if (currencySelect) currencySelect.dispatchEvent(new Event('change')); if (productCategorySelect) productCategorySelect.dispatchEvent(new Event('change')); updateDisplayedReferralFeeInfo();