活動/利潤計算器

活動成本效益分析工具,透過基礎成本與折扣方案,快速計算、比較並預測促銷活動在不同銷售階段下的潛在總利潤。

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/555511/1696881/%E6%B4%BB%E5%8B%95%E5%88%A9%E6%BD%A4%E8%A8%88%E7%AE%97%E5%99%A8.js

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         活動/利潤計算器
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  活動成本效益分析工具,透過基礎成本與折扣方案,快速計算、比較並預測促銷活動在不同銷售階段下的潛在總利潤。
// @author       Moz(qixiuyan@)
// @match        https://sellercentral.amazon.com/inventory*
// @match        https://www.sellercentral.amazon.dev/myinventory/inventory*
// @match        https://sellercentral.amazon.com/myinventory/inventory*
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @resource     customFont https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;500;700&display=swap
// ==/UserScript==

(function() {
    'use strict';

    // --- Internationalization (i18n) ---
    const i18n = {
        'zh': {
            available: ['有貨 (FBA)', '有貨'],
            inbound: ['入庫數量'],
            reserved: ['預留數量']
        },
        'en': {
            available: ['Available (FBA)', 'Available'],
            inbound: ['Inbound'],
            reserved: ['Reserved']
        }
    };

    const pageLang = document.documentElement.lang.startsWith('en') ? 'en' : 'zh';
    const LABELS = i18n[pageLang];

    // --- Inject Custom Font ---
    const fontCss = GM_getResourceText("customFont");
    GM_addStyle(fontCss);

    // --- Global State ---
    let scrapedProducts = []; // Array to store scraped data for each product

    // --- Helper Functions ---
    function parsePercent(str) {
        if (typeof str !== 'string' || !str.endsWith('%')) return null;
        const numStr = str.slice(0, -1);
        const num = parseInt(numStr, 10);
        if (isNaN(num) || num < 5 || num > 80 || num.toString() !== numStr) return null;
        return num / 100;
    }

    function parseCurrency(str) {
        if (typeof str !== 'string') return null;
        const num = parseFloat(str.replace(/[^0-9.-]+/g, ""));
        return isNaN(num) ? null : num;
    }

     function safeParseFloat(val) {
        const num = parseFloat(val);
        return isNaN(num) ? null : num;
    }

    function safeParseInt(str) {
        const num = parseInt(str, 10);
        return isNaN(num) ? 0 : num;
    }

    function formatCurrency(num) {
        if (typeof num !== 'number' || isNaN(num)) return 'N/A';
        const isNegative = num < 0;
        const formattedNum = isNegative ? `-$${Math.abs(num).toFixed(2)}` : `$${num.toFixed(2)}`;
        return `<span class="${isNegative ? 'negative-value' : ''}">${formattedNum}</span>`;
    }

    function formatForecastCurrency(num) {
        if (typeof num !== 'number' || isNaN(num)) return 'N/A';
        const isNegative = num < 0;
        const roundedNum = Math.round(Math.abs(num));
        const formattedStr = roundedNum.toLocaleString('en-US');
        const formattedNum = isNegative ? `-$${formattedStr}` : `$${formattedStr}`;
        return `<span class="${isNegative ? 'negative-value' : ''}">${formattedNum}</span>`;
    }

    // --- UI Panel ---
    const panel = document.createElement('div');
    panel.id = 'gemini-amazon-calculator';
    document.body.appendChild(panel);

    // --- Use GM_addStyle for all CSS ---
    GM_addStyle(`
        :root {
            --primary-color: #84994F; --secondary-color: #FCB53B; --light-yellow-bg: #FFFBEF;
            --negative-color: #A72703; --border-color: #EAE8E1; --text-color: #3D453C;
            --label-color: #606C5D; --panel-bg: #FFFFFF; --header-text: #FFFFFF;
            --font-family: 'Noto Sans TC', 'Huninn', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            --error-color: #D32F2F; --error-bg: #FFEBEE;
        }
        #gemini-amazon-calculator {
            position: fixed; top: 40px; left: 50%; width: 1300px; transform: translateX(-50%); background-color: var(--panel-bg);
            border: 1px solid var(--border-color); border-radius: 10px; z-index: 10001;
            box-shadow: 0 6px 18px rgba(0,0,0,0.1); font-family: var(--font-family);
            display: flex; flex-direction: column; max-height: 95vh; overflow: hidden;
        }
        #scrapePanelContainer { display: flex; flex-direction: column; gap: 16px; color: var(--text-color); flex: 1; min-height: 0; }
        #scrapePanelContainer h3 {
            position: relative; margin: 0; padding: 14px 40px 14px 20px; background-color: var(--primary-color);
            color: var(--header-text); font-size: 18px; font-weight: 700; border-top-left-radius: 10px;
            border-top-right-radius: 10px; letter-spacing: 1px; cursor: move;
        }
        #gemini-close-btn {
            position: absolute; top: 50%; right: 15px; transform: translateY(-50%); font-size: 26px;
            font-weight: normal; color: var(--header-text); cursor: pointer; line-height: 1;
            transition: opacity 0.2s; padding: 0 5px;
        }
        #gemini-close-btn:hover { opacity: 0.7; }
        .panel-section { padding: 0 20px; }
        #initialInputs { padding-bottom: 20px; }
        #asinInput {
            padding: 8px 12px; border: 1px solid var(--border-color); border-radius: 6px; width: 100%;
            box-sizing: border-box; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
            height: 80px;
        }
        #asinInput:focus {
            outline: none; border-color: var(--secondary-color);
            box-shadow: 0 0 0 3px rgba(252, 181, 59, 0.25);
        }
        .action-button {
            margin: 0 20px 20px; padding: 12px 15px; font-size: 16px; font-weight: 700; color: var(--header-text);
            background-color: var(--primary-color); border: none; border-radius: 6px; cursor: pointer;
            transition: background-color 0.2s, transform 0.1s;
        }
        .action-button:hover { background-color: #718544; }
        .action-button:active { transform: scale(0.98); }
        #resultsContainer { overflow-y: auto; padding-bottom: 10px; flex: 1; min-height: 0; }
        #baseTableContainer, #calcControlsContainer, #resultArea { padding: 0 20px 10px; }
        #tables-container { display: flex; justify-content: space-between; gap: 20px; }
        .table-container { flex: 1; }
        .table-title { font-weight: 700; margin-top: 10px; margin-bottom: 10px; font-size: 16px; padding-left: 5px; }
        .result-table { table-layout: fixed; width: 100%; border-collapse: collapse; font-size: 13px; }
        .result-table th, .result-table td {
            padding: 10px; text-align: right; border-bottom: 1px solid var(--border-color); white-space: nowrap;
        }
        .result-table th:first-child, .result-table td:first-child { text-align: left; font-weight: 500; }
        .result-table td:first-child { font-weight: 700; }
        .result-table th { background-color: #F8F7F4; font-weight: 700; }
        .result-table tbody tr:hover { background-color: var(--light-yellow-bg); }
        .negative-value { color: var(--negative-color); font-weight: 500; }
        #notFoundAreaWrapper { padding: 0 20px 10px; }
        #notFoundArea, .no-forecast { padding: 10px 15px; margin-top: 10px; border-radius: 5px; font-size: 13px; background-color: #FFF9C4; color: #795548; border: 1px solid #FBC02D; }
        .editable-input { width: 90%; padding: 6px; border: 1px solid #ccc; border-radius: 4px; text-align: right; box-sizing: border-box; }
        .editable-input.input-error { border-color: var(--error-color); background-color: var(--error-bg); }
        .control-group { display: flex; align-items: center; gap: 8px;}
         #combinedControls {
            display: flex; justify-content: space-around; gap: 10px; align-items: center;
            padding: 15px; background-color: #F8F7F4; border-radius: 8px; border: 1px solid var(--border-color); flex-wrap: wrap;
        }
        #combinedControls label { font-weight: 700; font-size: 14px; }
        #combinedControls input {
            padding: 8px 12px; border: 1px solid var(--border-color); border-radius: 6px; width: 120px;
        }
        #combinedControls button {
            padding: 8px 15px; font-size: 14px; font-weight: 500; color: #fff; background-color: var(--secondary-color);
            border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.2s;
        }
        #combinedControls button:hover { background-color: #E6A335; }
        #combinedControls #calculateBtn { background-color: var(--negative-color); }
        #combinedControls #calculateBtn:hover { background-color: #C62828;}
        .delete-row-btn {
             background-color: var(--negative-color); color: white; border: none; padding: 5px 9px;
             font-size: 12px; border-radius: 4px; cursor: pointer; transition: background-color 0.2s;
        }
        .delete-row-btn:hover { background-color: #C62828; }
        .title-explanation, .forecast-explanation { font-size: 12px; color: #606C5D; font-weight: 400; }
        .forecast-explanation { padding-top: 8px; }
        #forecastArea .result-table td:first-child { font-weight: 400;}
    `);

    panel.innerHTML = `
        <div id="scrapePanelContainer">
            <h3>活動成本/收益計算器<span id="gemini-close-btn" title="關閉">&times;</span></h3>
            <div id="initialInputs" class="panel-section">
                 <textarea id="asinInput" placeholder="請輸入ASIN,多個請用逗號或換行分隔"></textarea>
            </div>
            <button id="fetchBtn" class="action-button">抓取庫存資訊</button>
            <div id="resultsContainer">
                <div id="baseTableContainer"></div>
                <div id="calcControlsContainer"></div>
                <div id="resultArea"></div>
                <div id="notFoundAreaWrapper"></div>
            </div>
        </div>
    `;

    // --- Element References ---
    const fetchBtn = document.getElementById('fetchBtn');
    const asinInput = document.getElementById('asinInput');
    const baseTableContainer = document.getElementById('baseTableContainer');
    const calcControlsContainer = document.getElementById('calcControlsContainer');
    const resultArea = document.getElementById('resultArea');
    const notFoundAreaWrapper = document.getElementById('notFoundAreaWrapper');
    const closeBtn = document.getElementById('gemini-close-btn');

    // --- Drag Panel Logic ---
    let isDragging = false;
    let offsetX, offsetY;
    const header = panel.querySelector("h3");
    header.addEventListener('mousedown', (e) => {
        if (e.target.id === 'gemini-close-btn') return;
        isDragging = true;
        offsetX = e.clientX - panel.offsetLeft;
        offsetY = e.clientY - panel.offsetTop;
        panel.style.cursor = 'grabbing';
    });
    document.addEventListener('mousemove', (e) => {
        if (isDragging) {
            panel.style.left = `${e.clientX - offsetX}px`;
            panel.style.top = `${e.clientY - offsetY}px`;
        }
    });
    document.addEventListener('mouseup', () => {
        isDragging = false;
        panel.style.cursor = 'move';
    });

    // --- Event Listeners ---
    closeBtn.addEventListener('click', () => {
        panel.style.display = 'none';
    });

    fetchBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        const asinsToFind = asinInput.value.split(/[\n,]+/).map(asin => asin.trim().toUpperCase()).filter(Boolean);
        if (asinsToFind.length === 0) {
            alert('請輸入有效的ASIN!');
            return;
        }

        baseTableContainer.innerHTML = '<p style="text-align: center; padding: 20px;">正在滾動頁面以加載所有商品...</p>';
        calcControlsContainer.innerHTML = '';
        resultArea.innerHTML = '';
        notFoundAreaWrapper.innerHTML = '';
        scrapedProducts = [];

        const scrollInterval = setInterval(() => {
            if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 100) {
                clearInterval(scrollInterval);
                scrapeAndBuildBaseTable(asinsToFind);
            } else {
                window.scrollTo(0, document.body.scrollHeight);
            }
        }, 150);
    });

    function scrapeAndBuildBaseTable(asins) {
        let notFoundAsins = [...asins];
        let baseTableBodyHtml = '';

        const rows = document.querySelectorAll('div.JanusTable-module__tableContentRow--MGDsi');

        if (rows.length === 0) {
            notFoundAreaWrapper.innerHTML = "<div id='notFoundArea'>錯誤:在此頁面找不到任何商品列。請確認您在「管理庫存」頁面,且頁面已加載完畢。</div>";
            baseTableContainer.innerHTML = '';
            return;
        }

        rows.forEach(row => {
            let currentAsin = '', currentSku = '';
            const detailPanels = row.querySelectorAll('.JanusSplitBox-module__row--yjQ5L');

            detailPanels.forEach(panel => {
                const keyElement = panel.querySelector('.JanusSplitBox-module__panel--AbYDg:first-child');
                if (keyElement) {
                    const key = keyElement.textContent.trim();
                    const valueElement = keyElement.nextElementSibling;
                    if (valueElement) {
                        const value = valueElement.textContent.trim();
                        if (key === 'ASIN') currentAsin = value.toUpperCase();
                        else if (key === 'SKU') currentSku = value;
                    }
                }
            });

            if (!currentAsin || !asins.includes(currentAsin) || !currentSku) return;

            const uniqueId = `${currentAsin}_${currentSku}`;
            if (scrapedProducts.some(p => p.uniqueId === uniqueId)) return;

            const index = notFoundAsins.indexOf(currentAsin);
            if (index > -1) notFoundAsins.splice(index, 1);

            let available = '0', inbound = '0', reserved = '0';
            const inventoryRows = row.querySelectorAll('.JanusSplitBox-module__row--yjQ5L');
            inventoryRows.forEach(invRow => {
                const text = invRow.textContent;
                const valueEl = invRow.querySelector('.JanusSplitBox-module__panel--AbYDg:last-child');
                if (!valueEl) return;
                const value = valueEl.textContent.trim();

                if (LABELS.available.some(label => text.includes(label))) {
                    available = value;
                } else if (LABELS.inbound.some(label => text.includes(label))) {
                    inbound = value;
                } else if (LABELS.reserved.some(label => text.includes(label))) {
                    reserved = value;
                }
            });

            const priceInputEl = row.querySelector('.VolusPriceInputComposite-module__container--PTx1A kat-input[value]');
            const priceStr = priceInputEl ? priceInputEl.getAttribute('value') : 'N/A';
            const fbaFeeEl = row.querySelector('.estimated-fees-cell .JanusSplitBox-module__row--yjQ5L:nth-child(2) .JanusSplitBox-module__panel--AbYDg:nth-child(2) span');
            const fbaFeeStr = fbaFeeEl ? fbaFeeEl.textContent : 'N/A';

            const priceNum = parseCurrency(priceStr);
            const fbaFeeNum = parseCurrency(fbaFeeStr);
            const maxInventory = safeParseInt(available) + safeParseInt(inbound) + safeParseInt(reserved);

            const productData = {
                uniqueId: uniqueId, asin: currentAsin, sku: currentSku, maxInventory: maxInventory,
                available: available, inbound: inbound, reserved: reserved, price: priceNum, fbaFee: fbaFeeNum
            };
            scrapedProducts.push(productData);

            baseTableBodyHtml += `
                <tr data-row-id="${uniqueId}">
                    <td>${currentAsin}<br><span style="color: #666; font-size: 12px;">(${currentSku})</span></td>
                    <td>${maxInventory} (${available}/${inbound}/${reserved})</td>
                    <td>${formatCurrency(priceNum)}</td>
                    <td><input type="number" class="editable-input cost-input" data-id="${uniqueId}" step="0.01" placeholder="> 0 且 ≤ 價格"></td>
                    <td><input type="text" class="editable-input discount-input" data-id="${uniqueId}" placeholder="5-80 的整數%"></td>
                    <td style="text-align: center;"><button class="delete-row-btn" data-id="${uniqueId}">刪除</button></td>
                </tr>`;
        });

        if (scrapedProducts.length > 0) {
            const baseTableHtml = `
                <div class="table-container">
                    <table class="result-table">
                        <thead><tr>
                            <th style="width: 15%;">ASIN (SKU)</th>
                            <th style="width: 20%;">最大庫存 (有貨/入庫/預留)</th>
                            <th style="width: 15%;">客單價</th>
                            <th style="width: 20%;">基礎成本 ($)</th>
                            <th style="width: 20%;">折扣力度 (%)</th>
                            <th style="width: 10%;">操作</th>
                        </tr></thead>
                        <tbody id="baseInfoBody">${baseTableBodyHtml}</tbody>
                    </table>
                </div>`;
            baseTableContainer.innerHTML = baseTableHtml;
            renderCalculationUI();
        } else {
             baseTableContainer.innerHTML = '';
        }

        if (notFoundAsins.length > 0) {
            notFoundAreaWrapper.innerHTML = `<div id="notFoundArea">找不到以下 ASIN: ${[...new Set(notFoundAsins)].join(', ')}</div>`;
        }
    }

    function renderCalculationUI() {
        const controlsHtml = `
        <div id="combinedControls">
            <div class="control-group">
                <label for="bulkCost">成本($):</label>
                <input type="text" id="bulkCost" placeholder="數字或 %">
                <button id="applyBulkCost">應用</button>
            </div>

            <div class="control-group">
                <label for="bulkDiscount">折扣(%):</label>
                <input type="text" id="bulkDiscount" placeholder="5-80 的數字">
                <button id="applyBulkDiscount">應用</button>
            </div>

            <div class="control-group">
                <label for="promoFee">活動費用($):</label>
                <input type="text" id="promoFee" value="$ 0">
            </div>

            <button id="calculateBtn">計算最終利潤</button>
        </div>
         <div class="title-explanation" style="padding: 0px 15px 5px;">
            * PED (Prime Exclusive Discount) 活動提醒:折扣至少為 20%,活動費用為 $245。
        </div>
            `;
        calcControlsContainer.innerHTML = controlsHtml;

        document.getElementById('applyBulkCost').addEventListener('click', () => {
            const bulkCostStr = document.getElementById('bulkCost').value.trim();
            if (!bulkCostStr) return;

            if (bulkCostStr.includes('%')) {
                const rate = parsePercent(bulkCostStr);
                if (rate === null || rate < 0) {
                    alert('百分比格式錯誤或數值無效!請輸入5%-80%之間的值。');
                    return;
                }
                scrapedProducts.forEach(product => {
                    const costInput = document.querySelector(`.cost-input[data-id="${product.uniqueId}"]`);
                    if (product.price !== null && costInput) {
                        const calculatedCost = (product.price * rate).toFixed(2);
                        costInput.value = calculatedCost;
                    }
                });
            } else {
                const bulkCostNum = safeParseFloat(bulkCostStr);
                if (bulkCostNum === null || bulkCostNum <= 0) {
                    alert('成本必須是個大於 0 的數字!');
                    return;
                }
                document.querySelectorAll('.cost-input').forEach(input => input.value = bulkCostNum);
            }
        });

        document.getElementById('applyBulkDiscount').addEventListener('click', () => {
            let bulkDiscount = document.getElementById('bulkDiscount').value.trim();
            if (!bulkDiscount) return;

            if (!isNaN(bulkDiscount) && !bulkDiscount.endsWith('%')) {
                bulkDiscount += '%';
            }
            document.querySelectorAll('.discount-input').forEach(input => input.value = bulkDiscount);
        });

        document.getElementById('calculateBtn').addEventListener('click', performFinalCalculations);

        baseTableContainer.addEventListener('click', function(e) {
            if (e.target && e.target.classList.contains('delete-row-btn')) {
                const uniqueIdToDelete = e.target.getAttribute('data-id');
                scrapedProducts = scrapedProducts.filter(p => p.uniqueId !== uniqueIdToDelete);
                const rowToDelete = document.querySelector(`tr[data-row-id="${uniqueIdToDelete}"]`);
                if (rowToDelete) rowToDelete.remove();
            }
        });
    }

    function performFinalCalculations() {
        const promoFee = parseCurrency(document.getElementById('promoFee').value);
        if (promoFee === null) {
            alert('活動費用格式錯誤!請輸入有效的數字 (例如: $ 245 或 245)。');
            return;
        }

        let allInputsValid = true;
        document.querySelectorAll('.editable-input').forEach(input => input.classList.remove('input-error'));

        scrapedProducts.forEach(product => {
            const costInput = document.querySelector(`.cost-input[data-id="${product.uniqueId}"]`);
            const discountInput = document.querySelector(`.discount-input[data-id="${product.uniqueId}"]`);

            const costValue = safeParseFloat(costInput.value);
            const discountRate = parsePercent(discountInput.value);

            if (discountRate === null) {
                discountInput.classList.add('input-error');
                allInputsValid = false;
            }
            if (costValue === null || costValue <= 0 || (product.price !== null && costValue > product.price)) {
                costInput.classList.add('input-error');
                allInputsValid = false;
            }

            product.baseCost = costValue;
            product.discountRate = discountRate;
        });

        if (!allInputsValid) {
            alert('輸入驗證失敗!\n\n- 折扣力度必須是 5% 到 80% 之間的整數百分比 (例如: 20%)。\n- 基礎成本必須是大於 0 且不大於客單價的數字。\n\n請修正標紅的欄位後再試一次。');
            return;
        }

        let potentialSalesProfitTotal = 0;
        let potentialDiscountedProfitTotal = 0;
        let currentProfitBodyHtml = '';
        let discountProfitBodyHtml = '';

        scrapedProducts.forEach(p => {
            let referralFeeNum = null, salesProfitNum = null, discountedPriceNum = null,
                discountedReferralFeeNum = null, discountedProfitNum = null;

            if (p.price !== null) {
                referralFeeNum = p.price * 0.15;
                discountedPriceNum = p.price * (1 - p.discountRate);
                discountedReferralFeeNum = discountedPriceNum * 0.15;

                if (p.fbaFee !== null) {
                    salesProfitNum = p.price - p.baseCost - p.fbaFee - referralFeeNum;
                    discountedProfitNum = discountedPriceNum - p.baseCost - p.fbaFee - discountedReferralFeeNum;
                    if (p.maxInventory > 0) {
                       potentialSalesProfitTotal += salesProfitNum * p.maxInventory;
                       potentialDiscountedProfitTotal += discountedProfitNum * p.maxInventory;
                    }
                }
            }
            const identifierHtml = `${p.asin}<br><span style="color: #666; font-size: 12px;">(${p.sku})</span>`;
            currentProfitBodyHtml += `<tr><td>${identifierHtml}</td><td>${formatCurrency(p.price)}</td><td>${formatCurrency(referralFeeNum)}</td><td>${formatCurrency(p.fbaFee)}</td><td>${formatCurrency(salesProfitNum)}</td></tr>`;
            discountProfitBodyHtml += `<tr><td>${identifierHtml}</td><td>${formatCurrency(discountedPriceNum)}</td><td>${formatCurrency(discountedReferralFeeNum)}</td><td>${formatCurrency(p.fbaFee)}</td><td>${formatCurrency(discountedProfitNum)}</td></tr>`;
        });

        resultArea.innerHTML = `
            <div id="tables-container">
                <div class="table-container">
                    <div class="table-title">當前利潤表 <span class="title-explanation">(價格 - 成本 - 佣金 - FBA費用)</span></div>
                    <table class="result-table">
                        <thead><tr><th>ASIN (SKU)</th><th>價格</th><th>佣金</th><th>FBA配送費</th><th>利潤</th></tr></thead>
                        <tbody>${currentProfitBodyHtml}</tbody>
                    </table>
                </div>
                <div class="table-container">
                    <div class="table-title">折扣利潤表 <span class="title-explanation">(折扣價 - 成本 - 佣金 - FBA費用)</span></div>
                    <table class="result-table">
                         <thead><tr><th>ASIN (SKU)</th><th>折扣價</th><th>佣金</th><th>FBA配送費</th><th>利潤</th></tr></thead>
                        <tbody>${discountProfitBodyHtml}</tbody>
                    </table>
                </div>
            </div>
            <div id="forecastArea" style="padding-top: 20px;"></div>`;
        const forecastArea = document.getElementById('forecastArea');
        const hasInventoryForForecast = potentialSalesProfitTotal !== 0 || potentialDiscountedProfitTotal !== 0;

        if (scrapedProducts.length > 0 && hasInventoryForForecast) {
        let retentionRateRowHtml = '';
        if (potentialSalesProfitTotal > 0) {
            const cells = [...Array(10)].map((_, i) => {
                const percentage = (i + 1) / 10;
                const originalProfitForTier = potentialSalesProfitTotal * percentage;
                const discountedProfitForTier = (potentialDiscountedProfitTotal * percentage) - promoFee;

                if (originalProfitForTier <= 0) return '<td>N/A</td>';

                const rate = (discountedProfitForTier / originalProfitForTier) * 100;
                const formattedRate = rate.toFixed(1) + '%';
                let rateColor = 'inherit';
                if (rate < 50) {
                    rateColor = 'var(--negative-color)';
                } else if (rate >= 50 && rate < 60) {
                    rateColor = 'var(--secondary-color)';
                } else if (rate >= 80) {
                    rateColor = 'var(--primary-color)';
                }

                return `<td><span style=" color: ${rateColor};">${formattedRate}</span></td>`;
            }).join('');

            retentionRateRowHtml = `
                <tr>
                    <td style="font-weight: 400;">利潤保留率</td>
                    ${cells}
                </tr>`;
        }
            let forecastHTML = `
                <div class="table-container">
                    <div class="table-title">銷售預估/利潤預測 (基於最大庫存)</div>
                    <table class="result-table" style="width: 100%;">
                        <thead>
                            <tr>
                                <th><b>銷售進度</b></th>
                                ${[...Array(10)].map((_, i) => `<th>${(i + 1) * 10}%</th>`).join('')}
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td>折扣前預估</td>
                                ${[...Array(10)].map((_, i) => `<td>${formatForecastCurrency(potentialSalesProfitTotal * ((i + 1) / 10))}</td>`).join('')}
                            </tr>
                            <tr>
                                <td>折扣後預估</td>
                                ${[...Array(10)].map((_, i) => `<td>${formatForecastCurrency((potentialDiscountedProfitTotal * ((i + 1) / 10)) - promoFee)}</td>`).join('')}
                            </tr>
                             ${retentionRateRowHtml}
                        </tbody>
                    </table>
                    <div class="forecast-explanation">* 折扣前預估 = SUM((單件利潤) x 預計銷售%);      折扣後預估 = SUM((折扣後單件利潤) x 預計銷售%) - 活動費。</div>
                </div>`;
            forecastArea.innerHTML = forecastHTML;
        } else if (scrapedProducts.length > 0) {
            forecastArea.innerHTML = `<div class="no-forecast"><div class="table-title" style="margin-top:0;">無法生成銷售預測</div><p style="font-size:12px; margin:0;">原因:所有已選 ASIN 的最大庫存總和為 0。</p></div>`;
        } else {
             forecastArea.innerHTML = '';
        }
    }
})();