feat: CNB exchange rates, multi-currency KPI stats, invoice PDF VAT in CZK
- ČNB exchange rate service with date-specific rates and caching - Invoice/received invoice stats convert foreign currencies to CZK - Dashboard revenue converts all currencies to CZK - Invoice PDF: VAT recap table always in CZK with CNB rate footer - Inline styles replaced with utility classes (step 4 cleanup) - Spinner animation exempt from prefers-reduced-motion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -270,7 +270,7 @@ function SortableInvoiceRow({
|
||||
</select>
|
||||
</td>
|
||||
) : null}
|
||||
<td style={{ textAlign: "right", fontWeight: 600, whiteSpace: "nowrap" }}>
|
||||
<td className="text-right fw-600 whitespace-nowrap">
|
||||
{formatCurrency(lineTotal, currency)}
|
||||
</td>
|
||||
<td>
|
||||
@@ -354,12 +354,7 @@ function SortableInvoiceEditRow({
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
<td
|
||||
className="text-tertiary"
|
||||
style={{ textAlign: "center", fontWeight: 500 }}
|
||||
>
|
||||
{index + 1}
|
||||
</td>
|
||||
<td className="text-tertiary text-center fw-500">{index + 1}</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
@@ -1371,10 +1366,7 @@ export default function InvoiceDetail() {
|
||||
))}
|
||||
</select>
|
||||
{form.due_date && (
|
||||
<span
|
||||
className="text-tertiary"
|
||||
style={{ fontSize: "0.75rem", marginTop: "0.25rem" }}
|
||||
>
|
||||
<span className="text-tertiary text-xs mt-1">
|
||||
Splatnost:{" "}
|
||||
{new Date(form.due_date).toLocaleDateString("cs-CZ")}
|
||||
</span>
|
||||
@@ -1448,10 +1440,7 @@ export default function InvoiceDetail() {
|
||||
</FormField>
|
||||
<FormField label="DPH">
|
||||
<div className="flex-row-gap">
|
||||
<label
|
||||
className="admin-form-checkbox"
|
||||
style={{ whiteSpace: "nowrap" }}
|
||||
>
|
||||
<label className="admin-form-checkbox whitespace-nowrap">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!!form.apply_vat}
|
||||
@@ -1809,7 +1798,7 @@ export default function InvoiceDetail() {
|
||||
{invoice.paid_date && (
|
||||
<div className="admin-form-row mt-2">
|
||||
<FormField label="Datum úhrady">
|
||||
<div style={{ color: "var(--success)", fontWeight: 500 }}>
|
||||
<div className="fw-500" style={{ color: "var(--success)" }}>
|
||||
{formatDate(invoice.paid_date)}
|
||||
</div>
|
||||
</FormField>
|
||||
@@ -1954,16 +1943,13 @@ export default function InvoiceDetail() {
|
||||
: 0;
|
||||
return (
|
||||
<tr key={item.id || index}>
|
||||
<td
|
||||
className="text-tertiary"
|
||||
style={{ textAlign: "center", fontWeight: 500 }}
|
||||
>
|
||||
<td className="text-tertiary text-center fw-500">
|
||||
{index + 1}
|
||||
</td>
|
||||
<td className="fw-500">
|
||||
{item.description || "\u2014"}
|
||||
</td>
|
||||
<td style={{ textAlign: "center" }}>
|
||||
<td className="text-center">
|
||||
{item.quantity}{" "}
|
||||
{item.unit && (
|
||||
<span className="text-tertiary">
|
||||
@@ -1971,7 +1957,7 @@ export default function InvoiceDetail() {
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td style={{ textAlign: "center" }}>
|
||||
<td className="text-center">
|
||||
{item.unit || "\u2014"}
|
||||
</td>
|
||||
<td className="admin-mono text-right">
|
||||
@@ -1980,16 +1966,13 @@ export default function InvoiceDetail() {
|
||||
invoice.currency,
|
||||
)}
|
||||
</td>
|
||||
<td style={{ textAlign: "center" }}>
|
||||
<td className="text-center">
|
||||
{Number(invoice.apply_vat)
|
||||
? Number(item.vat_rate)
|
||||
: 0}
|
||||
%
|
||||
</td>
|
||||
<td
|
||||
className="admin-mono"
|
||||
style={{ textAlign: "right", fontWeight: 600 }}
|
||||
>
|
||||
<td className="admin-mono text-right fw-600">
|
||||
{formatCurrency(
|
||||
lineSubtotal + lineVat,
|
||||
invoice.currency,
|
||||
|
||||
Reference in New Issue
Block a user