backups/prissammanstallning-bak-20260421_150316.js

Code: DEV-1426CAE2 Size: 30.9 KB Lines: 513 Path: /home/prodconfig.wenesthosting.com/dev.solargroup.wenest.se/backups/prissammanstallning-bak-20260421_150316.js

Task / Comment

Open report form
// prissammanstallning.js
// EN gemensam prissammanställning för ALLA kalkyler
// Läser skattesatser från globala taxSettings (laddas från DB vid app-start)

// Global: alla kategoriers prisdata i kalkylen
var _kalkylItems = {};

function getKalkylEntries(category) {
    var item = _kalkylItems[category];
    if(!item) return [];
    if(Array.isArray(item.entries)) return item.entries.slice();
    return [item];
}

function rebuildKalkylCategory(category, entries) {
    var validEntries = (entries || []).filter(function(entry) {
        return entry && (parseFloat(entry.total) || 0) > 0;
    });
    if(!validEntries.length) {
        delete _kalkylItems[category];
        return;
    }
    _kalkylItems[category] = {
        description: validEntries[0].description || validEntries[0].product_name || category,
        total: validEntries.reduce(function(sum, entry){ return sum + (parseFloat(entry.total) || 0); }, 0),
        entries: validEntries
    };
}

function appendKalkylEntry(category, entry) {
    if(!category || !entry) return;
    var entries = getKalkylEntries(category);
    if(!entry.entry_id) entry.entry_id = category + '_' + Date.now() + '_' + Math.floor(Math.random() * 10000);
    entries.push(entry);
    rebuildKalkylCategory(category, entries);
}

function removeKalkylEntry(category, entryId) {
    var entries = getKalkylEntries(category).filter(function(entry){ return entry.entry_id !== entryId; });
    rebuildKalkylCategory(category, entries);
}

function getTax() {
    return typeof taxSettings !== 'undefined' ? taxSettings : {gt20:20, gt50:50, rot:30, rotMax:50000, moms:25};
}

// Registrera en kategoris prisdata (anropas av varje konfigurators updateCalc)
function registerCfgItem(category, itemData) {
    if(!category) return;
    _kalkylItems[category] = {
        description: itemData.description || '',
        total: itemData.total || 0,
        product_name: itemData.product_name || '',
        lines: itemData.lines || [],
        windows: itemData.windows || [],
        entries: itemData.entries || []
    };
}

function renderPrisSidebar(containerId, data) {
    // data = {
    //   lines: [{label, detail, value}],
    //   subtotal: number,
    //   taxType: 'GT20'|'GT50'|'ROT'|'NONE' (from product),
    //   deductType: 'green'|'rot'|'none' (user choice, legacy support),
    //   deductLabel: string,
    //   deductAmount: number,
    //   deductDetail: string,
    //   owners: 1|2,
    //   maxDeduction: number,
    //   sliderInteractive: boolean,
    //   sliderValue: number,
    //   onSliderChange: string,
    //   total: number,
    //   finYears: number,
    //   monthly: number,
    //   saving: number (optional),
    //   payback: number (optional),
    //   margin: {pct, amount, onSliderChange} (optional),
    //   category: string,
    //   onDeductChange: string,
    //   onOwnerChange: string,
    //   onFinChange: string,
    //   onSave: string,
    //   onOrder: string,
    //   purposeOptions: [{value, label}] (optional, e.g. batteri egen lagring/lagringstjänst),
    //   selectedPurpose: string,
    //   onPurposeChange: string
    // }

    var el = document.getElementById(containerId);
    if (!el) return;

    var d = data || {};
    var tax = getTax();
    var cartOnly = !!d.cartOnly;
    var lines = d.lines || [];
    var activeLines = lines.filter(function(line){
        return line && (parseFloat(line.value) || 0) > 0;
    });
    var owners = d.owners || 1;
    var finYears = d.finYears !== undefined ? d.finYears : 15;
    var onDeduct = d.onDeductChange || 'setPrisDeduction';
    var onOwner = d.onOwnerChange || 'setPrisOwners';
    var onFin = d.onFinChange || 'setPrisFinYears';
    var onSave = d.onSave || 'saveCfgQuote()';
    var onOrder = d.onOrder || "showAddToKalkyl('" + (d.category || '') + "')";
    var saveLabel = d.saveLabel || (((typeof currentQuoteId !== 'undefined' && currentQuoteId) ? 'Spara kalkyl' : 'Skapa ny kalkyl'));
    var orderLabel = d.orderLabel || 'Lägg till i kalkyl';
    var hideOrderButton = true;
    var cat = d.category || '';
    var categoryViewAction = d.categoryViewAction || '';
    var canReturnToSummary = !cartOnly && cat && cat !== '_summary' && Object.keys(_kalkylItems || {}).filter(function(key){
        return _kalkylItems[key] && (_kalkylItems[key].total || 0) > 0;
    }).length > 1;

    function renderSidebarLine(line) {
        var detail = line.detail ? ' <span style="font-size:10px;color:#94a3b8">' + line.detail + '</span>' : '';
        var valueHtml = '<span class="price-line-value">' + fmt(line.value || 0) + '</span>';
        if(line.editAction) {
            valueHtml = '<span style="display:flex;align-items:center;gap:6px">'
                + '<button onclick="' + line.editAction + '" style="background:none;border:none;padding:0;cursor:pointer;opacity:.65" title="Redigera"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg></button>'
                + valueHtml
                + '</span>';
        }
        return '<div class="price-line"><span class="price-line-label">' + line.label + detail + '</span>' + valueHtml + '</div>';
    }

    function renderCategoryHeading(label, action) {
        var html = '<div style="font-size:10px;font-weight:600;color:#024550;text-transform:uppercase;letter-spacing:.5px;padding:8px 0 4px;border-top:1px solid #e5e7eb;display:flex;justify-content:space-between;align-items:center">';
        html += '<span>' + label + '</span>';
        if(action) {
            html += '<button onclick="' + action + '" style="background:none;border:none;padding:0;cursor:pointer;opacity:.65" title="Visa detaljer"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></button>';
        }
        html += '</div>';
        return html;
    }

    function renderCategoryEntries(categoryKey, entries, fallbackItem) {
        var html = '';
        var windowRows = [];
        if(fallbackItem && Array.isArray(fallbackItem.windows) && fallbackItem.windows.length) {
            windowRows = windowRows.concat(fallbackItem.windows);
        }
        (entries || []).forEach(function(entry) {
            if(entry && Array.isArray(entry.windows) && entry.windows.length) {
                windowRows = windowRows.concat(entry.windows);
            }
        });
        if(categoryKey === 'fonster' && windowRows.length) {
            windowRows.forEach(function(w) {
                html += renderSidebarLine({
                    label: (w.productName || (catLabels[categoryKey] || categoryKey)) + ' ' + (w.modelLabel || ''),
                    detail: '',
                    value: (w.pris || 0) * (w.antal || 1),
                    editAction: "fkEditWindowById(" + (w.id || 0) + ")"
                });
            });
            return html;
        }
        (entries || []).forEach(function(entry) {
            html += renderSidebarLine({
                label: entry.description || entry.product_name || (fallbackItem && fallbackItem.description) || (catLabels[categoryKey] || categoryKey),
                detail: entry.detail || '',
                value: entry.total || 0,
                editAction: entry.entry_id ? ("editSummaryEntry('" + categoryKey + "','" + entry.entry_id + "')") : ("editFromSummary('" + categoryKey + "')")
            });
        });
        if(!html && fallbackItem && fallbackItem.total) {
            html += renderSidebarLine({
                label: fallbackItem.description || (catLabels[categoryKey] || categoryKey),
                value: fallbackItem.total || 0,
                editAction: "editFromSummary('" + categoryKey + "')"
            });
        }
        return html;
    }

    // Kategori-labels
    var catLabels = typeof _catLabels !== 'undefined' ? _catLabels : {solceller:'Solpaneler',batteri:'Batteri',laddbox:'Laddbox',taktvatt:'Taktv\u00e4tt',varmepump:'V\u00e4rmepump',tak:'Tak',fonster:'F\u00f6nster',isolering:'Isolering'};
    var currentItem = (typeof _kalkylItems !== "undefined" && cat && cat !== "_summary") ? _kalkylItems[cat] : null;
    var currentItemEntries = currentItem ? (Array.isArray(currentItem.entries) ? currentItem.entries : [currentItem]) : [];

    // Övriga kategorier i samma kalkyl (från _kalkylItems, fylls av openQuoteFromList/saveCfgQuote)
    var otherItemsHtml = '';
    var otherTotal = 0;
    if(typeof _kalkylItems !== "undefined" && cat !== "_summary") {
        Object.keys(_kalkylItems).forEach(function(k) {
            if(k === cat) return;
            var item = _kalkylItems[k];
            if(!item || !item.total) return;
            otherTotal += item.total;
            otherItemsHtml += renderCategoryHeading((catLabels[k] || k), "showSummaryCategory('" + k + "')");
            otherItemsHtml += renderCategoryEntries(k, Array.isArray(item.entries) ? item.entries : [item], item);
        });
    }

    // Prisrader — aktuell kategori
    var linesHtml = '';
    if(cartOnly) {
        var cartItemsHtml = '';
        var cartTotal = 0;
        var cartSubtotal = 0;
        var cartDeductionTotal = 0;
        var cartDeductionRows = [];
        if(typeof _kalkylItems !== 'undefined') {
            Object.keys(_kalkylItems).forEach(function(k) {
                var item = _kalkylItems[k];
                if(!item || !item.total) return;
                cartTotal += item.total || 0;
                var entries = Array.isArray(item.entries) ? item.entries : [item];
                var windowRows = [];
                if(item && Array.isArray(item.windows) && item.windows.length) {
                    windowRows = windowRows.concat(item.windows);
                }
                entries.forEach(function(entry) {
                    if(entry && Array.isArray(entry.windows) && entry.windows.length) {
                        windowRows = windowRows.concat(entry.windows);
                    }
                });
                cartItemsHtml += '<div style="font-size:10px;font-weight:600;color:#94a3b8;text-transform:uppercase;letter-spacing:.5px;padding:8px 0 4px;border-top:1px solid #e5e7eb;display:flex;justify-content:space-between;align-items:center">';
                cartItemsHtml += '<span>' + (catLabels[k] || k) + '</span>';
                cartItemsHtml += '<span style="display:flex;align-items:center;gap:8px">';
                cartItemsHtml += '<button onclick="showSummaryCategory(\'' + k + '\')" style="background:none;border:none;padding:0;cursor:pointer;opacity:.65" title="Visa detaljer"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></button>';
                cartItemsHtml += '</span></div>';
                if(k === 'fonster' && windowRows.length) {
                    windowRows.forEach(function(w) {
                        cartSubtotal += parseFloat(w.pris * w.antal || 0);
                        cartItemsHtml += '<div class="price-line"><span class="price-line-label">' + (w.productName + ' ' + w.modelLabel) + '</span><span style="display:flex;align-items:center;gap:6px"><button onclick="fkEditWindowById(' + (w.id || 0) + ')" style="background:none;border:none;padding:0;cursor:pointer;opacity:.65" title="Redigera"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg></button><span class="price-line-value">' + fmt((w.pris || 0) * (w.antal || 1)) + '</span></span></div>';
                    });
                } else {
                entries.forEach(function(entry) {
                    cartSubtotal += parseFloat(entry.subtotal || entry.total || 0);
                    cartDeductionTotal += parseFloat(entry.deduction_amount || 0);
                    if(parseFloat(entry.deduction_amount || 0) > 0) {
                        cartDeductionRows.push({
                            label: (catLabels[k] || k),
                            amount: parseFloat(entry.deduction_amount || 0)
                        });
                    }
                    cartItemsHtml += '<div class="price-line"><span class="price-line-label">' + (entry.description || item.description || '') + '</span><span style="display:flex;align-items:center;gap:6px"><button onclick="' + (entry.entry_id ? ("editSummaryEntry('" + k + "','" + entry.entry_id + "')") : ("editFromSummary('" + k + "')")) + '" style="background:none;border:none;padding:0;cursor:pointer;opacity:.65" title="Redigera"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg></button><span class="price-line-value">' + fmt(entry.total || 0) + '</span></span></div>';
                });
                }
            });
        }
        if(!cartItemsHtml) {
            cartItemsHtml = '<div style="font-size:13px;color:#94a3b8;padding:6px 0">Inga produkter tillagda ännu</div>';
        }
        linesHtml = cartItemsHtml;
        linesHtml += '<div class="price-line subtotal"><span class="price-line-label">Totalt i kalkylen</span><span class="price-line-value">' + fmt(cartSubtotal || cartTotal) + '</span></div>';
    } else if(otherItemsHtml) {
        linesHtml += '<div style="font-size:10px;font-weight:600;color:#94a3b8;text-transform:uppercase;letter-spacing:.5px;padding:4px 0">\u00d6vriga i kalkylen</div>';
        linesHtml += otherItemsHtml;
        if(activeLines.length) {
            linesHtml += renderCategoryHeading((catLabels[cat] || cat), categoryViewAction);
            linesHtml += activeLines.map(renderSidebarLine).join('');
        } else if(currentItem && currentItem.total) {
            linesHtml += renderCategoryHeading((catLabels[cat] || cat), categoryViewAction);
            linesHtml += renderCategoryEntries(cat, currentItemEntries, currentItem);
        }
        var grandSubtotal = (d.subtotal || 0) + otherTotal;
        linesHtml += '<div class="price-line subtotal"><span class="price-line-label">Totalt före avdrag</span><span class="price-line-value">' + fmt(grandSubtotal) + '</span></div>';
    } else {
        if(cat && cat !== '_summary') {
            linesHtml += renderCategoryHeading((catLabels[cat] || cat), categoryViewAction);
        }
        if(activeLines.length) {
            linesHtml += activeLines.map(renderSidebarLine).join('');
        } else if(currentItem && currentItem.total) {
            linesHtml += renderCategoryEntries(cat, currentItemEntries, currentItem);
        } else {
            linesHtml += lines.map(renderSidebarLine).join('');
        }
        var grandSubtotal = (d.subtotal || 0) + otherTotal;
        linesHtml += '<div class="price-line subtotal"><span class="price-line-label">Totalt före avdrag</span><span class="price-line-value">' + fmt(grandSubtotal) + '</span></div>';
    }


    // Skatteavdrag — bestäms av taxType från produkten
    var taxType = d.taxType || 'NONE';
    var deductType = d.deductType || 'none';
    var deductHtml = '';

    if (cartOnly) {
        var allEntries = [];
        if(typeof _kalkylItems !== 'undefined') {
            Object.keys(_kalkylItems).forEach(function(k) {
                var item = _kalkylItems[k];
                if(!item) return;
                var entries = Array.isArray(item.entries) ? item.entries : [item];
                entries.forEach(function(entry){ allEntries.push({category:k, entry:entry}); });
            });
        }
        var dedRows = allEntries.filter(function(row){ return parseFloat(row.entry.deduction_amount || 0) > 0; });
        if(dedRows.length) {
            deductHtml = '<div style="margin-bottom:10px"><div style="font-size:11px;color:#6b7280;margin-bottom:6px;font-weight:600">SKATTEAVDRAG</div>';
            dedRows.forEach(function(row) {
                deductHtml += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px"><span style="font-size:12px;color:#059669;font-weight:600">' + (row.entry.deduction_label || (catLabels[row.category] || row.category)) + '</span><span style="font-size:14px;font-weight:700;color:#059669">-' + fmt(row.entry.deduction_amount || 0) + '</span></div>';
            });
            var dedTotal = dedRows.reduce(function(sum, row){ return sum + parseFloat(row.entry.deduction_amount || 0); }, 0);
            deductHtml += '<div style="display:flex;justify-content:space-between;align-items:center;padding-top:8px;border-top:1px solid #bbf7d0"><span style="font-size:12px;color:#059669;font-weight:700">Totalt avdrag</span><span style="font-size:16px;font-weight:700;color:#059669">-' + fmt(dedTotal) + '</span></div></div>';
        } else {
            deductHtml = '<div style="margin-bottom:10px"><div style="font-size:11px;color:#6b7280;margin-bottom:6px;font-weight:600">PRISSAMMANSTÄLLNING</div><div style="font-size:13px;color:#94a3b8">Här visas produkter som redan lagts till i kalkylen.</div></div>';
        }
    } else if (taxType === 'NONE' && deductType === 'none') {
        // Inget avdrag alls
        deductHtml = '<div style="margin-bottom:10px">'
            + '<div style="font-size:11px;color:#6b7280;margin-bottom:6px;font-weight:600">GRÖNT TEKNIK-AVDRAG</div>'
            + '<div style="font-size:13px;color:#94a3b8">Inget skatteavdrag för denna produkt</div>'
            + '</div>';
    } else {
        // Visa avdragsinfo
        var deductLabel = d.deductLabel || '';
        var deductPct = 0;
        var deductBase = '';

        if (taxType === 'GT20' || deductType === 'green' && taxType !== 'GT50') {
            deductPct = tax.gt20;
            deductLabel = deductLabel || 'GRÖNT TEKNIK-AVDRAG (GT20)';
            deductBase = 'material + arbete';
        } else if (taxType === 'GT50') {
            deductPct = tax.gt50;
            deductLabel = deductLabel || 'SKATTEREDUKTION (GT50)';
            deductBase = 'material + arbete';
        } else if (taxType === 'ROT' || deductType === 'rot') {
            deductPct = tax.rot;
            deductLabel = deductLabel || 'ROT-AVDRAG';
            deductBase = 'enbart arbete';
        }

        // Avdragsknappar — visa bara relevanta val
        function deductBtn(type, label, sub, active) {
            var style = active
                ? 'flex:1;padding:7px 4px;border:1.5px solid #059669;border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;background:#059669;color:#fff;font-family:inherit;line-height:1.2'
                : 'flex:1;padding:7px 4px;border:1.5px solid #bbf7d0;border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;background:#fff;color:#059669;font-family:inherit;line-height:1.2';
            return '<button onclick="' + onDeduct + '(\'' + type + '\')" style="' + style + '">' + label + '<br><span style="font-size:9px;font-weight:400;opacity:.8">' + sub + '</span></button>';
        }

        deductHtml = '<div style="margin-bottom:10px">'
            + '<div style="font-size:11px;color:#6b7280;margin-bottom:6px;font-weight:600">GRÖNT TEKNIK-AVDRAG</div>'
            + '<div style="display:flex;gap:4px">';

        if (taxType === 'GT20') {
            deductHtml += deductBtn('green', 'Grönt teknik', tax.gt20 + '% av allt', deductType === 'green');
            deductHtml += deductBtn('rot', 'ROT-avdrag', tax.rot + '% av arbete', deductType === 'rot');
            deductHtml += deductBtn('both', 'Båda', 'GT + ROT', deductType === 'both');
        } else if (taxType === 'GT50') {
            deductHtml += deductBtn('green', 'GT50', tax.gt50 + '% av allt', deductType === 'green');
        } else if (taxType === 'ROT') {
            deductHtml += deductBtn('rot', 'ROT-avdrag', tax.rot + '% av arbete', deductType === 'rot');
        }
        deductHtml += deductBtn('none', 'Inget', '&nbsp;', deductType === 'none');
        deductHtml += '</div></div>';

        // Avdragssumma
        deductHtml += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">'
            + '<span style="font-size:12px;font-weight:700;color:#059669">' + deductLabel + '</span>'
            + '<span style="font-size:16px;font-weight:700;color:#059669">' + (d.deductAmount ? '-' + fmt(d.deductAmount) : '0 kr') + '</span></div>';
        if (d.deductDetail) deductHtml += '<div style="font-size:11px;color:#6b7280;margin-bottom:8px">' + d.deductDetail + '</div>';
    }

    // Antal ägare (bara om avdrag är aktivt)
    if (deductType !== 'none') {
        deductHtml += '<div style="margin-bottom:10px"><div style="font-size:11px;color:#6b7280;margin-bottom:4px">Antal ägare</div>'
            + '<div style="display:flex;gap:6px">'
            + '<button onclick="' + onOwner + '(1)" style="flex:1;padding:6px;border:1.5px solid ' + (owners === 1 ? '#059669' : '#bbf7d0') + ';border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;background:' + (owners === 1 ? '#059669' : '#fff') + ';color:' + (owners === 1 ? '#fff' : '#059669') + ';font-family:inherit">1 person</button>'
            + '<button onclick="' + onOwner + '(2)" style="flex:1;padding:6px;border:1.5px solid ' + (owners === 2 ? '#059669' : '#bbf7d0') + ';border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;background:' + (owners === 2 ? '#059669' : '#fff') + ';color:' + (owners === 2 ? '#fff' : '#059669') + ';font-family:inherit">2 personer</button>'
            + '</div></div>';

        // Kvarvarande avdrag slider
        var maxDed = d.maxDeduction || (owners * (taxType === 'ROT' ? tax.rotMax : 50000));
        var sliderVal = d.sliderValue !== undefined ? d.sliderValue : maxDed;
        var sliderDisabled = d.sliderInteractive ? '' : ' disabled';
        var sliderOnInput = d.onSliderChange ? ' oninput="' + d.onSliderChange + '(this.value)"' : '';
        deductHtml += '<div><div style="display:flex;justify-content:space-between;font-size:11px;color:#6b7280;margin-bottom:4px">'
            + '<span>Kvarvarande avdrag</span><span style="font-weight:600;color:#059669">' + sliderVal.toLocaleString('sv-SE') + ' kr</span></div>'
            + '<input type="range" min="0" max="' + maxDed + '" step="5000" value="' + sliderVal + '" style="width:100%;accent-color:#059669"' + sliderDisabled + sliderOnInput + '>'
            + '<div style="display:flex;justify-content:space-between;font-size:10px;color:#94a3b8"><span>0 kr</span><span>' + maxDed.toLocaleString('sv-SE') + ' kr</span></div></div>';
    }

    // Finansiering
    function finBtn(yr, active) {
        var style = active
            ? 'padding:4px 10px;border:1.5px solid #3b82f6;border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;background:#3b82f6;color:#fff;font-family:inherit'
            : 'padding:4px 10px;border:1.5px solid #bfdbfe;border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;background:#fff;color:#3b82f6;font-family:inherit';
        var label = yr === 0 ? 'Ej finans' : yr + ' år';
        return '<button onclick="' + onFin + '(' + yr + ')" style="' + style + '">' + label + '</button>';
    }

    var finHtml = '<div style="background:#f0f9ff;border-radius:10px;padding:14px;margin-bottom:12px">'
        + '<div style="font-size:11px;color:#3b82f6;font-weight:600;margin-bottom:8px">FINANSIERING</div>'
        + '<div style="display:flex;gap:6px;margin-bottom:10px;flex-wrap:wrap">'
        + finBtn(0, finYears === 0) + finBtn(5, finYears === 5) + finBtn(10, finYears === 10) + finBtn(15, finYears === 15) + finBtn(20, finYears === 20) + finBtn(25, finYears === 25)
        + '</div>';
    if (finYears > 0) {
        finHtml += '<div style="display:flex;justify-content:space-between;align-items:baseline">'
            + '<span style="font-size:13px;color:#334155">Månadskostnad <span style="font-size:10px;color:#64748b">(' + finYears + ' år, 4.9%)</span></span>'
            + '<span style="font-size:20px;font-weight:700;color:#1e40af">' + fmt(d.monthly || 0) + '/mån</span></div>';
    } else {
        finHtml += '<div style="font-size:13px;color:#64748b">Ingen finansiering vald</div>';
    }
    finHtml += '</div>';

    // Marginal (valfritt, för fönster)
    var marginHtml = '';
    if (d.margin) {
        var m = d.margin;
        marginHtml = '<div style="background:#fefce8;border-radius:10px;padding:14px;margin-bottom:12px">'
            + '<div style="font-size:11px;color:#92400e;font-weight:600;margin-bottom:8px">MARGINAL</div>'
            + '<div style="display:flex;align-items:center;gap:12px">'
            + '<input type="range" min="0" max="50" step="1" value="' + (m.pct || 0) + '" style="flex:1;accent-color:#f59e0b" oninput="' + (m.onSliderChange || '') + '(this.value)">'
            + '<span style="font-size:14px;font-weight:700;color:#92400e;min-width:40px;text-align:right">' + (m.pct || 0) + '%</span>'
            + '</div>'
            + '<div style="display:flex;justify-content:space-between;margin-top:6px"><span style="font-size:12px;color:#92400e">Marginal</span><span style="font-size:14px;font-weight:700;color:#92400e">' + fmt(m.amount || 0) + '</span></div>'
            + '</div>';
    }

    // Besparing (valfritt)
    var savingHtml = '';
    if (d.saving) {
        savingHtml = '<div style="background:#ecfdf5;border-radius:10px;padding:14px;margin-bottom:16px">'
            + '<div style="font-size:11px;color:#059669;font-weight:600;margin-bottom:4px">UPPSKATTAD BESPARING</div>'
            + '<div style="display:flex;justify-content:space-between;align-items:baseline"><span style="font-size:13px;color:#334155">Per år</span><span style="font-size:20px;font-weight:700;color:#166534">' + d.saving.toLocaleString('sv-SE') + ' kr/år</span></div>'
            + (d.payback ? '<div style="display:flex;justify-content:space-between;align-items:baseline;margin-top:4px"><span style="font-size:13px;color:#334155">Återbetalningstid</span><span style="font-size:16px;font-weight:700;color:#166534">' + d.payback + ' år</span></div>' : '')
            + '</div>';
    }

    // Moms-info
    var momsHtml = '<div style="display:flex;justify-content:space-between;font-size:11px;color:#94a3b8;margin-bottom:12px">'
        + '<span>Moms (' + tax.moms + '%)</span><span>Inkl. i priserna</span></div>';

    // Knappar
    var saveIcon = '<svg viewBox="0 0 24 24" style="width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>';
    var addIcon = '<svg viewBox="0 0 24 24" style="width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><line x1="9" y1="15" x2="15" y2="15"/></svg>';

    var btnsHtml = '';
    if(canReturnToSummary) {
        btnsHtml += '<button onclick="showKalkylSummary()" style="width:100%;padding:12px;background:#fff;color:#024550;border:1.5px solid #bfdbfe;border-radius:10px;font-size:13px;font-weight:700;cursor:pointer;font-family:inherit;display:flex;align-items:center;justify-content:center;gap:8px;margin-bottom:8px">'
            + '<svg viewBox="0 0 24 24" style="width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 8h10M7 12h10M7 16h6"/></svg>'
            + 'Kalkylsammanställning</button>';
    }
    btnsHtml += '<button onclick="' + onSave + '" style="width:100%;padding:12px;background:#f59e0b;color:#fff;border:none;border-radius:10px;font-size:14px;font-weight:700;cursor:pointer;font-family:inherit;display:flex;align-items:center;justify-content:center;gap:8px' + (hideOrderButton ? '' : ';margin-bottom:8px') + '">'
        + saveIcon + saveLabel + '</button>';
    if(!hideOrderButton) {
        btnsHtml += '<button onclick="' + onOrder + '" style="width:100%;padding:14px;background:linear-gradient(135deg,#024550,#035e6b);color:#fff;border:none;border-radius:10px;font-size:15px;font-weight:700;cursor:pointer;font-family:inherit;display:flex;align-items:center;justify-content:center;gap:8px">'
            + addIcon + orderLabel + '</button>';
    }

    // Hela prissammanställningen
    el.innerHTML = '<div style="position:sticky;top:20px"><div class="price-card">'
        + '<div class="price-header">Prissammanställning</div>'
        + '<div class="price-rows">' + linesHtml + '</div>'
        + '<div class="rot-section" style="background:#ecfdf5;padding:14px 16px">' + deductHtml + '</div>'
        + '<div class="total-section"><div class="total-line"><span class="total-label">Att betala</span><span class="total-value">' + fmt(cartOnly ? Object.keys(_kalkylItems || {}).reduce(function(sum, key){ return sum + ((_kalkylItems[key] && _kalkylItems[key].total) || 0); }, 0) : ((d.total || 0) + otherTotal)) + '</span></div><div class="total-vat">' + momsHtml + '</div></div>'
        + '<div style="padding:0 16px 16px">' + finHtml + marginHtml + savingHtml + btnsHtml + '</div>'
        + '</div></div>';
}

// Hjälpfunktion: beräkna avdrag baserat på taxType och taxSettings
function calcTaxDeduction(subtotal, laborCost, taxType, deductType, owners) {
    var tax = getTax();
    var maxPerPerson = taxType === 'ROT' ? tax.rotMax : 50000;
    var maxTotal = owners * maxPerPerson;

    if (deductType === 'none') return {amount: 0, label: 'INGET AVDRAG', detail: ''};

    if (taxType === 'GT50' && deductType === 'green') {
        var calc = Math.round(subtotal * tax.gt50 / 100);
        var amt = Math.min(calc, maxTotal);
        return {
            amount: amt,
            label: 'SKATTEREDUKTION (GT50)',
            detail: tax.gt50 + '% av ' + fmt(subtotal) + ' = ' + fmt(calc) + (calc > maxTotal ? ' (max ' + fmt(maxTotal) + ')' : '')
        };
    }

    if (taxType === 'GT20' && deductType === 'green') {
        var calc = Math.round(subtotal * tax.gt20 / 100);
        var amt = Math.min(calc, maxTotal);
        return {
            amount: amt,
            label: 'GRÖNT TEKNIK-AVDRAG (GT20)',
            detail: tax.gt20 + '% av ' + fmt(subtotal) + ' = ' + fmt(calc) + (calc > maxTotal ? ' (max ' + fmt(maxTotal) + ')' : '')
        };
    }

    if (deductType === 'rot') {
        var calc = Math.round(laborCost * tax.rot / 100);
        var amt = Math.min(calc, maxTotal);
        return {
            amount: amt,
            label: 'ROT-AVDRAG',
            detail: tax.rot + '% av arbete ' + fmt(laborCost) + ' = ' + fmt(calc) + (calc > maxTotal ? ' (max ' + fmt(maxTotal) + ')' : '')
        };
    }

    if (deductType === 'both') {
        // GT20 på material + ROT på arbete (bara för solceller)
        var materialCost = subtotal - laborCost;
        var greenCalc = Math.round(materialCost * tax.gt20 / 100);
        var rotCalc = Math.round(laborCost * tax.rot / 100);
        var bothTotal = greenCalc + rotCalc;
        var amt = Math.min(bothTotal, maxTotal);
        return {
            amount: amt,
            label: 'GRÖNT TEKNIK + ROT',
            detail: 'GT20: ' + fmt(greenCalc) + ' + ROT: ' + fmt(rotCalc) + ' = ' + fmt(bothTotal) + (bothTotal > maxTotal ? ' (max ' + fmt(maxTotal) + ')' : '')
        };
    }

    return {amount: 0, label: 'INGET AVDRAG', detail: ''};
}