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:
12 files changed
+843
-4
No files matched your search
@@ -8826,7 +8826,7 @@ impl BooksServer {
|
||||
name = "books.document",
|
||||
title = "An invoice or bill",
|
||||
description = described(
|
||||
"One invoice or bill (ref \"invoice:7\" or \"bill:12\", from books.documents): party, dates, lines with their projects, total, what was paid, discounted and written off, what it still owes and its status, all as of a day; and its timeline: every ledger line tagged with it in posting order (its own entry, each payment, each reversal), each saying whether its entry still stands. A reversed payment shows twice: the payment (no longer standing) and its reversal (standing), which net to nothing.",
|
||||
"One invoice or bill (ref \"invoice:7\" or \"bill:12\", from books.documents) as of a day: party, dates, lines with projects, total, paid, discounted, written off, still owed, status; and its timeline, every ledger line tagged with it in posting order, each saying whether its entry still stands (a reversed payment shows as the payment, not standing, and its reversal, standing: they net to nothing). pdf: true also hands over an invoice's PDF to download, re-created from the books.",
|
||||
ledger_core::calendar::PeriodKind::AsOf
|
||||
),
|
||||
annotations(read_only_hint = true, open_world_hint = false)
|
||||
@@ -8866,6 +8866,33 @@ impl BooksServer {
|
||||
.doc_state(doc, as_of)
|
||||
.map_err(|e| e.to_string())?
|
||||
.ok_or_else(|| format!("{} is dated after {as_of}", doc_ref_text(doc)))?;
|
||||
let pdf = match (p.pdf, doc) {
|
||||
(false, _) => None,
|
||||
(true, ledger_core::DocRef::Bill(_)) => {
|
||||
return Err(
|
||||
"a bill is the vendor's document: its PDF is the file filed with it (books.list_receipts)"
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
(true, ledger_core::DocRef::Invoice(id)) => {
|
||||
let settings = self.settings();
|
||||
let printed = ledger_package::invoice_document(
|
||||
ledger,
|
||||
&settings.company,
|
||||
id,
|
||||
as_of,
|
||||
today(),
|
||||
)?;
|
||||
let bytes =
|
||||
ledger_import::invoice_pdf::invoice_pdf(&printed).map_err(|e| e.to_string())?;
|
||||
Some(self.hand_over(
|
||||
&book_name(&store),
|
||||
&format!("invoice-{id}.pdf"),
|
||||
"application/pdf",
|
||||
bytes,
|
||||
)?)
|
||||
}
|
||||
};
|
||||
let timeline = ledger
|
||||
.doc_lines(doc)
|
||||
.into_iter()
|
||||
@@ -8904,6 +8931,7 @@ impl BooksServer {
|
||||
status: st.status.key().to_string(),
|
||||
overdue_days: st.overdue_days,
|
||||
timeline,
|
||||
pdf,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -3195,6 +3195,10 @@ pub struct DocumentParams {
|
||||
#[serde(default)]
|
||||
#[schemars(description = AS_OF_DOC)]
|
||||
pub as_of: Option<String>,
|
||||
/// For an invoice: also hand over its PDF (as of the day, re-created
|
||||
/// from the books) to download.
|
||||
#[serde(default)]
|
||||
pub pdf: bool,
|
||||
}
|
||||
|
||||
/// One line of a document.
|
||||
@@ -3259,6 +3263,9 @@ pub struct DocumentOut {
|
||||
pub overdue_days: i64,
|
||||
/// Every ledger line tagged with it, in posting order.
|
||||
pub timeline: Vec<DocumentEventOut>,
|
||||
/// The invoice's PDF, handed over (with `pdf`): run its download.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub pdf: Option<HandedFile>,
|
||||
}
|
||||
|
||||
/// Parameters for `books.void_document`.
|
||||
|
||||
Reference in new issue
Block a user