* feat(invoices): Betald-stämpel i faktura-PDF + betalningsbekräftelse till kund
Closes#1693. A paid faktura re-renders with a BETALD banner (paid date
and amount) and "Betalt: X" followed by "Att betala: 0"; partially_paid
gets the Betalt / Att betala (remaining) rows without a banner. Credit
notes and proformas are unchanged. Labels in sv and en.
The paid copy is its own document, a betalningsbekräftelse, never the
archived original: GET /api/invoices/[id]/pdf?variant=paid refuses
anything but status paid (409 INVOICE_PAYMENT_CONFIRMATION_NOT_PAID),
names the file Betalningsbekraftelse-<nr>.pdf and never reads or
replaces the delivery archive. invoice-pdf-source gains the
'payment_confirmation' re-render reason so the UI caveats it like any
re-render. POST /api/invoices/[id]/send-payment-confirmation emails the
paid PDF with a dedicated subject/body through the existing email
service and recipient routing, without touching status, sent_at,
journal entries or invoice_deliveries (no kind column there; logged via
the route logger instead).
Detail page: the two actions sit inside the Betald card (download paid
copy, send confirmation with an up-front confirm dialog), not in the
header row. No migrations.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(invoices): one-line hint for the betalningsbekräftelse actions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Reviewing an invoice or a verifikat bilaga meant saving a file and opening it
from the Downloads folder (user request, christian@odinaero.se 2026-07-25).
- GET /api/invoices/[id]/pdf accepts ?disposition=inline and serves the PDF for
in-browser review; anything else keeps the download behaviour every existing
caller relies on. The filename still travels in the header, so the browser
viewer's own save action produces the same name as the download button, and
nosniff pins the content type.
- The invoice detail page gets a "Förhandsgranska" action next to "Ladda ner
PDF". It resolves the document through the same resolveInvoicePdfSource path
as the download, so preview cannot become the shortcut that presents a
re-render as the invoice the customer received: the archived delivery wins,
a re-render is shown with its caveat, and an unreadable delivery history
still asks instead of guessing. The archive dialog now remembers whether the
user asked to view or to save, and its fallback does that.
- DocumentViewButton (supplier-invoice underlag, staged agent previews) points
at the existing /api/documents/:id/inline proxy, so bilagor render in the
browser. Navigation now happens straight from the click, so the signed-URL
fetch and its popup-blocker workaround are gone.
- The three re-render caveat strings and the two archive-dialog descriptions
lose their "you downloaded" wording so they stay true for both actions;
five new keys in sv + en.
Tests: route cases for the default, inline and unknown disposition values;
invoiceRerenderUrl cases for both modes and id encoding. npm test 11364
passed, lint 0 errors. Button row screenshotted against the design system
(pill outline, Eye icon) via a temporary sandbox route.
Closes#1190
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>