import { Document, Page, Text, View, StyleSheet, } from '@react-pdf/renderer' import type { Invoice, InvoiceItem, Customer, CompanySettings, InvoiceDocumentType } from '@/types' // Create styles const styles = StyleSheet.create({ page: { padding: 40, fontSize: 10, fontFamily: 'Helvetica', }, header: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 30, }, title: { fontSize: 24, fontWeight: 'bold', color: '#1a1a1a', }, companyInfo: { textAlign: 'right', }, companyName: { fontSize: 14, fontWeight: 'bold', marginBottom: 4, }, section: { marginBottom: 20, }, sectionTitle: { fontSize: 11, fontWeight: 'bold', marginBottom: 8, color: '#666', textTransform: 'uppercase', letterSpacing: 0.5, }, row: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 4, }, label: { color: '#666', }, value: { fontWeight: 'bold', }, customerBox: { backgroundColor: '#f5f5f5', padding: 15, borderRadius: 4, marginBottom: 20, }, customerName: { fontSize: 12, fontWeight: 'bold', marginBottom: 4, }, table: { marginTop: 10, }, tableHeader: { flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: '#ddd', paddingBottom: 8, marginBottom: 8, }, tableRow: { flexDirection: 'row', paddingVertical: 6, borderBottomWidth: 1, borderBottomColor: '#eee', }, colDescription: { flex: 3.5, }, colQty: { flex: 1, textAlign: 'right', }, colUnit: { flex: 1, textAlign: 'center', }, colPrice: { flex: 1.5, textAlign: 'right', }, colVat: { flex: 1, textAlign: 'right', }, colTotal: { flex: 1.5, textAlign: 'right', }, tableHeaderText: { fontWeight: 'bold', color: '#666', fontSize: 9, textTransform: 'uppercase', }, totalsSection: { marginTop: 20, paddingTop: 15, borderTopWidth: 2, borderTopColor: '#ddd', }, totalRow: { flexDirection: 'row', justifyContent: 'flex-end', marginBottom: 4, }, totalLabel: { width: 120, textAlign: 'right', paddingRight: 15, color: '#666', }, totalValue: { width: 100, textAlign: 'right', }, grandTotal: { flexDirection: 'row', justifyContent: 'flex-end', marginTop: 10, paddingTop: 10, borderTopWidth: 1, borderTopColor: '#333', }, grandTotalLabel: { width: 120, textAlign: 'right', paddingRight: 15, fontSize: 14, fontWeight: 'bold', }, grandTotalValue: { width: 100, textAlign: 'right', fontSize: 14, fontWeight: 'bold', }, paymentSection: { marginTop: 30, padding: 15, backgroundColor: '#f8f9fa', borderRadius: 4, }, paymentTitle: { fontSize: 11, fontWeight: 'bold', marginBottom: 10, color: '#333', }, paymentRow: { flexDirection: 'row', marginBottom: 4, }, paymentLabel: { width: 100, color: '#666', }, paymentValue: { flex: 1, }, reverseChargeBox: { marginTop: 20, padding: 12, backgroundColor: '#fff3cd', borderRadius: 4, borderWidth: 1, borderColor: '#ffc107', }, reverseChargeText: { fontSize: 9, color: '#856404', }, notesBox: { marginTop: 20, padding: 12, backgroundColor: '#e8f4fd', borderRadius: 4, }, notesText: { fontSize: 9, color: '#0c5460', }, creditNoteBox: { marginBottom: 20, padding: 12, backgroundColor: '#f8d7da', borderRadius: 4, borderWidth: 1, borderColor: '#f5c6cb', }, creditNoteText: { fontSize: 10, color: '#721c24', }, creditNoteTitle: { color: '#721c24', }, footer: { position: 'absolute', bottom: 30, left: 40, right: 40, borderTopWidth: 1, borderTopColor: '#ddd', paddingTop: 10, }, footerText: { fontSize: 8, color: '#999', textAlign: 'center', }, twoColumn: { flexDirection: 'row', justifyContent: 'space-between', }, column: { width: '48%', }, }) // Format currency function formatCurrency(amount: number, currency: string = 'SEK'): string { return new Intl.NumberFormat('sv-SE', { style: 'currency', currency, minimumFractionDigits: 2, maximumFractionDigits: 2, }).format(amount) } // Format date function formatDate(date: string): string { return new Date(date).toLocaleDateString('sv-SE') } // Format org number function formatOrgNumber(orgNumber: string): string { const cleaned = orgNumber.replace(/\D/g, '') if (cleaned.length === 10) { return `${cleaned.slice(0, 6)}-${cleaned.slice(6)}` } return orgNumber } function getDocumentTitle(invoice: Invoice): string { if (invoice.credited_invoice_id) return 'KREDITFAKTURA' const docType = (invoice as Invoice & { document_type?: InvoiceDocumentType }).document_type || 'invoice' if (docType === 'proforma') return 'PROFORMAFAKTURA' if (docType === 'delivery_note') return 'FÖLJESEDEL' return 'FAKTURA' } interface InvoicePDFProps { invoice: Invoice customer: Customer items: InvoiceItem[] company: CompanySettings originalInvoiceNumber?: string } export function InvoicePDF({ invoice, customer, items, company, originalInvoiceNumber }: InvoicePDFProps) { const isCreditNote = !!invoice.credited_invoice_id // Check if items have mixed VAT rates const hasPerLineVat = items.some((item) => item.vat_rate !== undefined && item.vat_rate !== null) const uniqueRates = hasPerLineVat ? new Set(items.map((item) => item.vat_rate)) : new Set() const showVatColumn = hasPerLineVat && uniqueRates.size > 1 // Calculate per-rate VAT breakdown for totals const vatByRate = new Map() if (hasPerLineVat) { for (const item of items) { const rate = item.vat_rate ?? 0 const group = vatByRate.get(rate) || { base: 0, vat: 0 } group.base += Math.abs(item.line_total) group.vat += Math.abs(item.vat_amount || 0) vatByRate.set(rate, group) } } const docType = (invoice as Invoice & { document_type?: InvoiceDocumentType }).document_type || 'invoice' const isDeliveryNote = docType === 'delivery_note' const isProforma = docType === 'proforma' return ( {/* Header */} {getDocumentTitle(invoice)} {invoice.invoice_number} {company.company_name} {company.address_line1 && {company.address_line1}} {(company.postal_code || company.city) && ( {company.postal_code} {company.city} )} {company.org_number && ( Org.nr: {formatOrgNumber(company.org_number)} )} {company.vat_number && VAT: {company.vat_number}} {/* Credit note reference */} {isCreditNote && originalInvoiceNumber && ( Denna kreditfaktura avser och krediterar faktura nr {originalInvoiceNumber} )} {/* Invoice details and Customer - two columns */} {/* Invoice details */} Fakturainformation Fakturadatum: {formatDate(invoice.invoice_date)} Förfallodatum: {formatDate(invoice.due_date)} {invoice.your_reference && ( Er referens: {invoice.your_reference} )} {invoice.our_reference && ( Vår referens: {invoice.our_reference} )} {/* Customer */} Faktureras till {customer.name} {customer.address_line1 && {customer.address_line1}} {customer.address_line2 && {customer.address_line2}} {(customer.postal_code || customer.city) && ( {customer.postal_code} {customer.city} )} {customer.country && customer.country !== 'SE' && ( {customer.country} )} {customer.org_number && ( Org.nr: {customer.org_number} )} {customer.vat_number && VAT: {customer.vat_number}} {/* Items table */} Specifikation {/* Table header */} Beskrivning Antal Enhet {!isDeliveryNote && ( à-pris )} {!isDeliveryNote && showVatColumn && ( Moms )} {!isDeliveryNote && ( Summa )} {/* Table rows */} {items.map((item, index) => ( {item.description} {item.quantity} {item.unit} {!isDeliveryNote && ( {formatCurrency(item.unit_price, invoice.currency)} )} {!isDeliveryNote && showVatColumn && ( {item.vat_rate ?? 0}% )} {!isDeliveryNote && ( {formatCurrency(item.line_total, invoice.currency)} )} ))} {/* Totals - hidden for delivery notes */} {!isDeliveryNote && ( Delsumma: {formatCurrency(invoice.subtotal, invoice.currency)} {vatByRate.size > 1 ? ( Array.from(vatByRate.entries()) .filter(([, group]) => group.vat > 0) .sort(([a], [b]) => b - a) .map(([rate, group]) => ( Moms {rate}%: {formatCurrency(group.vat, invoice.currency)} )) ) : ( Moms ({invoice.vat_rate ?? (vatByRate.size === 1 ? vatByRate.keys().next().value : 0)}%): {formatCurrency(invoice.vat_amount, invoice.currency)} )} {isCreditNote ? 'Att kreditera:' : 'Att betala:'} {formatCurrency(invoice.total, invoice.currency)} {invoice.currency !== 'SEK' && invoice.total_sek && ( I SEK (kurs {invoice.exchange_rate}): {formatCurrency(invoice.total_sek, 'SEK')} )} )} {/* Proforma notice */} {isProforma && ( Detta är en proformafaktura och utgör ingen betalningsanmodan. )} {/* Payment information - not shown for credit notes, proformas, or delivery notes */} {!isCreditNote && !isProforma && !isDeliveryNote && ( Betalningsinformation {company.bank_name && ( Bank: {company.bank_name} )} {(company.clearing_number || company.account_number) && ( Kontonummer: {company.clearing_number}-{company.account_number} )} {company.iban && ( IBAN: {company.iban} )} {company.bic && ( BIC/SWIFT: {company.bic} )} Förfallodatum: {formatDate(invoice.due_date)} OCR/Referens: {invoice.invoice_number} )} {/* Reverse charge notice */} {invoice.reverse_charge_text && ( {invoice.reverse_charge_text} )} {/* Notes */} {invoice.notes && ( {invoice.notes} )} {/* Footer */} {company.company_name} {company.org_number ? ` | Org.nr: ${formatOrgNumber(company.org_number)}` : ''} {company.f_skatt ? ' | Godkänd för F-skatt' : ''} {company.vat_number ? ` | Momsreg.nr: ${company.vat_number}` : ''} ) }