feat: the invoice PDF, with stamps and each payment (P4b S3)
check / check (push) Canceled after 0s

ledger-import invoice_pdf (templates/invoice.typ): seller, bill-to,
number, dates, terms, lines, total, each settled line and the balance
due; money formatted in Rust (the template does no arithmetic), text as
data, deterministic bytes. Stamps as a rubber stamp would mark it, in
red: PAID (with the day it was paid in full: the invoice is then the
customer's receipt), PARTIALLY PAID, PAST DUE, VOID; DRAFT in gray for
S4's preview. ledger-package invoice_document builds it from the books
as of a day: the derived figures (B-38), each payment that stands on its
own line with its date, payment method and reference; re-created copies
say so (the issued copy is kept from S4 on and never changes).
books.document pdf hands it over as a download; the viewer's invoice
page saves or opens it. The owner asked for the paid stamp with the
payment's date and details (2026-10-24).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-24 18:20:55 -06:00
1 parent b7b7f17163
commit c1f9001cfe
12 files changed
+843 -4

No files matched your search

+12
View File
@@ -2727,6 +2727,18 @@ fn documents_list_and_timeline_over_the_wire() {
],
"{one}"
);
// Its PDF, by download (P4b S3): re-created from the books.
let with_pdf = m.ok(
"books.document",
json!({"ref": format!("invoice:{late}"), "as_of": "2026-03-15", "pdf": true}),
);
let bytes = m.wire.fetch(&with_pdf["pdf"]);
assert!(bytes.starts_with(b"%PDF"), "{with_pdf}");
assert_eq!(with_pdf["pdf"]["file_name"], format!("invoice-{late}.pdf"));
assert!(
m.tool("books.document", json!({"ref": "bill:1", "pdf": true}))
.refused()
);
assert!(
m.tool("books.document", json!({"ref": "invoice"}))
.refused()