const salesCaseId = {{ case.id }}; function formatCurrency(value) { const num = Number(value || 0); return new Intl.NumberFormat('da-DK', { style: 'currency', currency: 'DKK' }).format(num); } function formatNumber(value) { const num = Number(value || 0); return new Intl.NumberFormat('da-DK', { minimumFractionDigits: 0, maximumFractionDigits: 2 }).format(num); } let saleItemsCache = []; async function loadVarekobSalg() { try { const res = await fetch(`/api/v1/sag/${salesCaseId}/varekob-salg?include_subcases=true`); if (!res.ok) throw new Error('Failed to load aggregated data'); const data = await res.json(); document.getElementById('salesTotalPurchase').textContent = formatCurrency(data?.totals?.purchase_total); document.getElementById('salesTotalSale').textContent = formatCurrency(data?.totals?.sale_total); document.getElementById('salesTotalNet').textContent = formatCurrency(data?.totals?.net_total); document.getElementById('salesTotalHours').textContent = formatNumber(data?.totals?.total_hours) + ' t'; document.getElementById('salesBillableHours').textContent = formatNumber(data?.totals?.billable_hours) + ' t'; saleItemsCache = data.sale_items || []; renderSaleItems(saleItemsCache); renderTimeEntries(data.time_entries || []); const hasSalesData = (data.sale_items || []).length > 0 || (data.time_entries || []).length > 0; setModuleContentState('sales', hasSalesData); } catch (error) { console.error(error); const saleBody = document.getElementById('saleItemsBody'); if (saleBody) { saleBody.innerHTML = 'Kunne ikke hente data'; } const timeBody = document.getElementById('salesTimeBody'); if (timeBody) { timeBody.innerHTML = 'Kunne ikke hente data'; } setModuleContentState('sales', true); } } function renderSaleItems(items) { const salesBody = document.getElementById('saleItemsSalesBody'); const purchaseBody = document.getElementById('saleItemsPurchaseBody'); const salesSubtotal = document.getElementById('salesLinesSubtotal'); const purchaseSubtotal = document.getElementById('purchaseLinesSubtotal'); if (!salesBody || !purchaseBody) return; const salesItems = items.filter(item => (item.type || '').toLowerCase() !== 'purchase'); const purchaseItems = items.filter(item => (item.type || '').toLowerCase() === 'purchase'); const renderRows = (list) => { if (!list.length) { return 'Ingen linjer'; } return list.map(item => { const statusLabel = item.status || 'draft'; const isSubcase = item.sag_id && item.sag_id !== salesCaseId; const sourceBadge = isSubcase ? `Under-sag` : `Denne sag`; return ` ${item.line_date || '-'} ${item.description || '-'} ${item.quantity ?? '-'} ${item.unit || '-'} ${item.unit_price != null ? formatCurrency(item.unit_price) : '-'} ${formatCurrency(item.amount)} ${item.source_sag_titel || '-'}${sourceBadge} ${statusLabel}
`; }).join(''); }; salesBody.innerHTML = renderRows(salesItems); purchaseBody.innerHTML = renderRows(purchaseItems); const salesSum = salesItems.reduce((sum, item) => sum + Number(item.amount || 0), 0); const purchaseSum = purchaseItems.reduce((sum, item) => sum + Number(item.amount || 0), 0); if (salesSubtotal) salesSubtotal.textContent = formatCurrency(salesSum); if (purchaseSubtotal) purchaseSubtotal.textContent = formatCurrency(purchaseSum); } function renderTimeEntries(entries) { const tbody = document.getElementById('salesTimeBody'); if (!tbody) return; if (!entries.length) { tbody.innerHTML = 'Ingen tid registreret'; return; } tbody.innerHTML = entries.map(entry => { const hours = entry.approved_hours || entry.original_hours || 0; const isSubcase = entry.sag_id && entry.sag_id !== salesCaseId; const sourceBadge = isSubcase ? `Under-sag` : `Denne sag`; return ` ${entry.worked_date || '-'} ${formatNumber(hours)} t ${entry.source_sag_titel || '-'}${sourceBadge} `; }).join(''); } function openSaleItemModal(item = null) { document.getElementById('sale_item_id').value = item?.id || ''; document.getElementById('sale_type').value = item?.type || 'sale'; document.getElementById('sale_status').value = item?.status || 'draft'; document.getElementById('sale_date').value = item?.line_date || ''; document.getElementById('sale_description').value = item?.description || ''; document.getElementById('sale_quantity').value = item?.quantity ?? ''; document.getElementById('sale_unit').value = item?.unit || ''; document.getElementById('sale_unit_price').value = item?.unit_price ?? ''; document.getElementById('sale_amount').value = item?.amount ?? ''; document.getElementById('sale_currency').value = item?.currency || 'DKK'; document.getElementById('sale_external_ref').value = item?.external_ref || ''; new bootstrap.Modal(document.getElementById('saleItemModal')).show(); } function openSaleItemModalById(itemId) { const item = saleItemsCache.find((entry) => entry.id === itemId); openSaleItemModal(item || null); } function updateSaleAmount() { const qty = parseFloat(document.getElementById('sale_quantity').value || 0); const price = parseFloat(document.getElementById('sale_unit_price').value || 0); if (qty && price) { document.getElementById('sale_amount').value = (qty * price).toFixed(2); } } async function saveSaleItem() { const itemId = document.getElementById('sale_item_id').value; const payload = { type: document.getElementById('sale_type').value, status: document.getElementById('sale_status').value, line_date: document.getElementById('sale_date').value || null, description: document.getElementById('sale_description').value, quantity: document.getElementById('sale_quantity').value || null, unit: document.getElementById('sale_unit').value || null, unit_price: document.getElementById('sale_unit_price').value || null, amount: document.getElementById('sale_amount').value, currency: document.getElementById('sale_currency').value || 'DKK', external_ref: document.getElementById('sale_external_ref').value || null }; if (!payload.description || !payload.amount) { alert('Beskrivelse og linjesum er påkrævet.'); return; } const method = itemId ? 'PATCH' : 'POST'; const url = itemId ? `/api/v1/sag/${salesCaseId}/sale-items/${itemId}` : `/api/v1/sag/${salesCaseId}/sale-items`; const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!res.ok) { alert('Kunne ikke gemme varelinje'); return; } bootstrap.Modal.getInstance(document.getElementById('saleItemModal')).hide(); await loadVarekobSalg(); } async function deleteSaleItem(itemId) { if (!confirm('Vil du slette denne varelinje?')) return; const res = await fetch(`/api/v1/sag/${salesCaseId}/sale-items/${itemId}`, { method: 'DELETE' }); if (!res.ok) { alert('Kunne ikke slette varelinje'); return; } await loadVarekobSalg(); } document.addEventListener('DOMContentLoaded', function() { const qtyInput = document.getElementById('sale_quantity'); const priceInput = document.getElementById('sale_unit_price'); if (qtyInput) qtyInput.addEventListener('input', updateSaleAmount); if (priceInput) priceInput.addEventListener('input', updateSaleAmount); loadVarekobSalg(); });