gui: Save PDF on every report; short and narrow windows hold up
Reports get a Save PDF button beside Copy for a spreadsheet. It typesets the report as it's set up on screen (range, months/quarters/years, comparison) through ledger-import's Typst statement template, the same one the CPA package uses, in the background, writes it to the Downloads folder (never the books: the viewer stays read-only), and opens it. - ledger-import: Statement.percent_from marks percentage columns (comparison %, % of income), typeset as 12.5% with no dollar sign; wide statements turn the page, and past seven columns set smaller so a year of months fits; statement() is public for other callers. - Compared columns that read the same (Jan 1 - Oct 12 vs a year earlier) now say which year, on screen and on paper. - Short windows: the sidebar's and rail's links scroll above the status box instead of under it. The search palette fits the window. - A test typesets every table-backed report from the demo books, plain, by month, and compared (PDF_DIR keeps the files). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
1 parent
ea996f6e7e
commit
12308fe3e9
9 files changed
+444
-101
No files matched your search
@@ -67,7 +67,16 @@ pub fn template_data(st: &Statement, meta: &ReportMeta) -> serde_json::Value {
|
||||
"label": r.label,
|
||||
"rule": r.rule_above,
|
||||
"currency": r.currency,
|
||||
"amounts": r.amounts.iter().map(|a| a.map(|a| {
|
||||
"amounts": r.amounts.iter().enumerate().map(|(i, a)| a.map(|a| {
|
||||
if st.percent_from.is_some_and(|f| i >= f) {
|
||||
let a = a.round_dp(1);
|
||||
let negative = a.is_sign_negative() && !a.is_zero();
|
||||
return serde_json::json!({
|
||||
"digits": format!("{}%", a.abs().normalize()),
|
||||
"negative": negative,
|
||||
"percent": true,
|
||||
});
|
||||
}
|
||||
let p = amount_parts(a);
|
||||
serde_json::json!({ "digits": p.digits, "negative": p.negative })
|
||||
})).collect::<Vec<_>>(),
|
||||
|
||||
@@ -102,6 +102,9 @@ pub struct Statement {
|
||||
pub columns: Vec<String>,
|
||||
/// The rows, top to bottom.
|
||||
pub rows: Vec<Row>,
|
||||
/// Columns from this index on hold percentages ("12.5%"), not money:
|
||||
/// a comparison's "%" column, a P&L's "% of income".
|
||||
pub percent_from: Option<usize>,
|
||||
}
|
||||
|
||||
fn row(kind: RowKind, label: impl Into<String>, amounts: Vec<Option<Decimal>>) -> Row {
|
||||
@@ -131,7 +134,7 @@ fn spacer() -> Row {
|
||||
}
|
||||
|
||||
/// Finish a statement: currency signs on sums, rules above them.
|
||||
fn statement(title: &str, period: String, columns: &[&str], mut rows: Vec<Row>) -> Statement {
|
||||
pub fn statement(title: &str, period: String, columns: &[&str], mut rows: Vec<Row>) -> Statement {
|
||||
let mut above: Option<RowKind> = None;
|
||||
for r in &mut rows {
|
||||
r.currency = matches!(r.kind, RowKind::Subtotal | RowKind::Total);
|
||||
@@ -149,6 +152,7 @@ fn statement(title: &str, period: String, columns: &[&str], mut rows: Vec<Row>)
|
||||
period,
|
||||
columns: columns.iter().map(|c| c.to_string()).collect(),
|
||||
rows,
|
||||
percent_from: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user