diff --git a/app/(dashboard)/invoices/[id]/page.tsx b/app/(dashboard)/invoices/[id]/page.tsx index eef0da73..71dab82f 100644 --- a/app/(dashboard)/invoices/[id]/page.tsx +++ b/app/(dashboard)/invoices/[id]/page.tsx @@ -468,178 +468,175 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st -
- {/* Main content */} -
- {/* Customer info */} - - - Kund - - -
-

{customer.name}

- {customer.org_number && ( -

Org.nr: {customer.org_number}

+
+ {/* Customer info */} + + + Kund + + +
+

{customer.name}

+ {customer.org_number && ( +

Org.nr: {customer.org_number}

+ )} + {customer.vat_number && ( +

VAT: {customer.vat_number}

+ )} +
+ {customer.email && ( + {customer.email} )} - {customer.vat_number && ( -

VAT: {customer.vat_number}

+ {customer.phone && ( + {customer.phone} )} -
- {customer.email && ( - {customer.email} - )} - {customer.phone && ( - {customer.phone} - )} +
+ {(customer.address_line1 || customer.city) && ( +
+ {customer.address_line1 &&

{customer.address_line1}

} + {customer.address_line2 &&

{customer.address_line2}

} +

+ {customer.postal_code} {customer.city} + {customer.country !== 'SE' && `, ${customer.country}`} +

- {(customer.address_line1 || customer.city) && ( -
- {customer.address_line1 &&

{customer.address_line1}

} - {customer.address_line2 &&

{customer.address_line2}

} -

- {customer.postal_code} {customer.city} - {customer.country !== 'SE' && `, ${customer.country}`} + )} +

+ + + + {/* Invoice items */} + + + Fakturarader + + +
+ {/* Header — desktop */} +
+
Beskrivning
+
Antal
+
Enhet
+
à-pris
+
Summa
+
+ + {/* Items — desktop */} +
+ {invoice.items.map((item) => ( +
+
{item.description}
+
{item.quantity}
+
{item.unit}
+
+ {formatCurrency(item.unit_price, invoice.currency)} +
+
+ {formatCurrency(item.line_total, invoice.currency)} +
+
+ ))} +
+ + {/* Items — mobile cards */} +
+ {invoice.items.map((item) => ( +
+

{item.description}

+
+ {item.quantity} {item.unit} × {formatCurrency(item.unit_price, invoice.currency)} +
+

+ {formatCurrency(item.line_total, invoice.currency)}

- )} + ))}
- - - {/* Invoice items */} - - - Fakturarader - - -
- {/* Header — desktop */} -
-
Beskrivning
-
Antal
-
Enhet
-
à-pris
-
Summa
+ + + {/* Totals */} +
+
+ Delsumma + {formatCurrency(invoice.subtotal, invoice.currency)}
+ {(() => { + const vatByRate = new Map() + for (const item of invoice.items) { + const rate = item.vat_rate ?? 0 + const lineVat = Math.round(item.line_total * (rate / 100) * 100) / 100 + vatByRate.set(rate, (vatByRate.get(rate) || 0) + lineVat) + } + const entries = Array.from(vatByRate.entries()) + .filter(([, vat]) => vat > 0) + .sort(([a], [b]) => b - a) - {/* Items — desktop */} -
- {invoice.items.map((item) => ( -
-
{item.description}
-
{item.quantity}
-
{item.unit}
-
- {formatCurrency(item.unit_price, invoice.currency)} -
-
- {formatCurrency(item.line_total, invoice.currency)} -
-
- ))} -
- - {/* Items — mobile cards */} -
- {invoice.items.map((item) => ( -
-

{item.description}

-
- {item.quantity} {item.unit} × {formatCurrency(item.unit_price, invoice.currency)} -
-

- {formatCurrency(item.line_total, invoice.currency)} -

-
- ))} -
- - - - {/* Totals */} -
-
- Delsumma - {formatCurrency(invoice.subtotal, invoice.currency)} -
- {(() => { - const vatByRate = new Map() - for (const item of invoice.items) { - const rate = item.vat_rate ?? 0 - const lineVat = Math.round(item.line_total * (rate / 100) * 100) / 100 - vatByRate.set(rate, (vatByRate.get(rate) || 0) + lineVat) - } - const entries = Array.from(vatByRate.entries()) - .filter(([, vat]) => vat > 0) - .sort(([a], [b]) => b - a) - - if (entries.length === 0) { - return ( -
- Moms - {formatCurrency(0, invoice.currency)} -
- ) - } - - return entries.map(([rate, vat]) => ( -
- Moms {rate}% - {formatCurrency(vat, invoice.currency)} -
- )) - })()} - - {(() => { - const rounding = getDisplayTotal(invoice, { ore_rounding: oreRounding }) + if (entries.length === 0) { return ( - <> - {rounding.applies && ( -
- Öresavrundning - {formatCurrency(rounding.roundingDelta, 'SEK')} -
- )} -
- Totalt - {formatCurrency(rounding.displayed, invoice.currency)} -
- +
+ Moms + {formatCurrency(0, invoice.currency)} +
) - })()} - {invoice.currency !== 'SEK' && invoice.total_sek && ( -
- I SEK (kurs {invoice.exchange_rate}) - {formatCurrency(invoice.total_sek)} -
- )} -
-
- - + } - {/* Notes */} - {(invoice.notes || invoice.reverse_charge_text) && ( - - - Anteckningar - - - {invoice.reverse_charge_text && ( -
-

Omvänd skattskyldighet

-

{invoice.reverse_charge_text}

+ return entries.map(([rate, vat]) => ( +
+ Moms {rate}% + {formatCurrency(vat, invoice.currency)} +
+ )) + })()} + + {(() => { + const rounding = getDisplayTotal(invoice, { ore_rounding: oreRounding }) + return ( + <> + {rounding.applies && ( +
+ Öresavrundning + {formatCurrency(rounding.roundingDelta, 'SEK')} +
+ )} +
+ Totalt + {formatCurrency(rounding.displayed, invoice.currency)} +
+ + ) + })()} + {invoice.currency !== 'SEK' && invoice.total_sek && ( +
+ I SEK (kurs {invoice.exchange_rate}) + {formatCurrency(invoice.total_sek)}
)} - {invoice.notes &&

{invoice.notes}

} - - +
+
+ + + + {/* Notes */} + {(invoice.notes || invoice.reverse_charge_text) && ( + + + Anteckningar + + + {invoice.reverse_charge_text && ( +
+

Omvänd skattskyldighet

+

{invoice.reverse_charge_text}

+
+ )} + {invoice.notes &&

{invoice.notes}

} +
+
)} -
{/* Sidebar */} -
+
{/* Invoice details */} diff --git a/app/(dashboard)/invoices/new/page.tsx b/app/(dashboard)/invoices/new/page.tsx index 40b872cc..3fd14ea2 100644 --- a/app/(dashboard)/invoices/new/page.tsx +++ b/app/(dashboard)/invoices/new/page.tsx @@ -473,157 +473,117 @@ export default function NewInvoicePage() { )}
-
- {/* Main content */} -
- {/* Customer selection */} - - - Kund - Välj vilken kund fakturan ska skickas till - - - ( - - )} - /> - - {errors.customer_id && ( -

{errors.customer_id.message}

+
+ {/* Customer selection */} + + + Kund + Välj vilken kund fakturan ska skickas till + + + ( + )} + /> + + {errors.customer_id && ( +

{errors.customer_id.message}

+ )} -
-
+ + - {/* Invoice items */} - - - Fakturarader - Lägg till produkter eller tjänster - - -
- {fields.map((field, index) => { - const lineTotal = (watchItems[index]?.quantity || 0) * (watchItems[index]?.unit_price || 0) - const lineVat = Math.round(lineTotal * (watchItems[index]?.vat_rate ?? 25) / 100 * 100) / 100 - return ( -
- {/* Description + mobile delete button */} -
-
- - - {errors.items?.[index]?.description && ( -

- {errors.items[index].description?.message} -

- )} -
- + {/* Invoice items */} + + + Fakturarader + Lägg till produkter eller tjänster + + +
+ {fields.map((field, index) => { + const lineTotal = (watchItems[index]?.quantity || 0) * (watchItems[index]?.unit_price || 0) + const lineVat = Math.round(lineTotal * (watchItems[index]?.vat_rate ?? 25) / 100 * 100) / 100 + return ( +
+ {/* Description + mobile delete button */} +
+
+ + + {errors.items?.[index]?.description && ( +

+ {errors.items[index].description?.message} +

+ )}
+ +
- {/* Antal, Enhet, à-pris */} -
-
- - -
-
- - ( - - )} - /> -
-
- - -
-
- - {/* Moms */} + {/* Antal, Enhet, à-pris */} +
- + + +
+
+ ( - - {availableRates.map((opt) => ( - - {opt.label} + {units.map((unit) => ( + + {unit} ))} @@ -631,214 +591,248 @@ export default function NewInvoicePage() { )} />
- - {/* Desktop delete button */} -
- -
- - {/* Mobile summary row */} -
- Rad {index + 1} - {formatCurrency(lineTotal + lineVat, watchCurrency)} +
+ +
- ) - })} - -
- - + {/* Moms */} +
+ + ( + + )} + /> +
- {/* Notes */} - - - Anteckningar - Valfritt meddelande på fakturan - - -