66ac184a6f
Phase 0 of the e-invoicing migration plan (see the E-Rechnung planning session) — moves invoicePdf.tsx, correctionInvoicePdf.tsx, and taxBreakdown.ts into a new shared package, consumed as a git dependency by both this repo and the payload backend, instead of hand-duplicating the correction-invoice logic between them (see that package's own README for the three real drifts the duplication had already caused). Consumed as raw TS/TSX source via next.config.ts's transpilePackages, not a pre-built package. Needs `git` in the Docker deps stage and a project .npmrc (allow-git=root) to let npm ci fetch a git-URL dependency at all — npm 12+ disables that by default. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
37 lines
1.8 KiB
TypeScript
37 lines
1.8 KiB
TypeScript
import { formatPrice } from "../lib/format";
|
|
import type { TaxBreakdownGroup } from "@einfach-produktiv/invoicing";
|
|
|
|
// The actual amount of VAT included in a total — not just a disclosure
|
|
// that VAT is included (see cartTotals.ts's effectiveTaxRate() for the
|
|
// "which %" shown next to each line item elsewhere). One line per rate
|
|
// when a cart/order spans more than one; a single line otherwise.
|
|
//
|
|
// Same row shape as the Gesamtsumme total line right above this
|
|
// (`flex w-full` + a `flex-1` spacer): a label on the left, flush with
|
|
// "Gesamtsumme", and the rate/amount pushed flush right so they land
|
|
// directly under the total's own € amount — not tucked in right next to
|
|
// the label. The rate itself gets a fixed-width right-aligned column
|
|
// (`w-8`, `tabular-nums`) so a single-digit rate ("7%") still lines up
|
|
// under a two-digit one ("19%") across rows instead of shifting the
|
|
// amount that follows it. Only the first row carries the "enthält
|
|
// MwSt.:" label; further rates repeat just the rate/amount pair.
|
|
export function VatBreakdown({ groups }: { groups: TaxBreakdownGroup[] }) {
|
|
if (groups.length === 0) return null;
|
|
return (
|
|
<div className="flex flex-col gap-0.5 w-full">
|
|
{groups.map((g, i) => (
|
|
<div key={g.rate} className="flex items-baseline w-full">
|
|
<span className="text-label text-text-muted">
|
|
{groups.length === 1 ? `enthält ${g.rate}% MwSt.` : i === 0 ? "enthält MwSt.:" : ""}
|
|
</span>
|
|
<span className="flex-1" />
|
|
{groups.length > 1 && (
|
|
<span className="w-8 shrink-0 text-right text-label text-text-muted tabular-nums">{g.rate}%</span>
|
|
)}
|
|
<span className="ml-1.5 text-label text-text-muted tabular-nums">{formatPrice(g.tax)}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|