feat(viewer): Invoices & Bills, a document's page, overview tiles (P4b S2)
check / check (push) Canceled after 0s

One sidebar item, Invoices & Bills (the owner's choice, 2026-10-24),
with Money in (invoices) and Money out (bills) tabs: what is owed and
overdue, and the list tied to its receivables or payables account
(Agrees, or what to look at); filters Open, Overdue, Paid, Void, All;
rows by due date, an overdue one saying how many days late, each named
in full for assistive tech. A document's page: total, received or paid,
still owed, status; its lines with their projects; its timeline, a
payment later reversed marked so, each row opening its entry. The
overview adds Owed to you and You owe on bills once the books have
documents. Every figure is the document's derived state (rules.md B-38).
The sidebar's rows (and the rail's) give way to 22 points, not 24, so
13 links still fit the owner's 1479x519 window unscrolled; the
sidebar's tests compute heights from the link count; the 1440x920
golden is re-blessed for the new link.

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 15:33:04 -06:00
1 parent 6f0fd97db0
commit b7b7f17163
11 files changed
+927 -13

No files matched your search

+29 -8
View File
@@ -24,6 +24,7 @@ pub struct PageState {
pub parties: pages::parties::State,
pub projects: pages::projects::State,
pub documents: pages::documents::State,
pub invoices: pages::invoices::State,
pub journal: pages::journal::State,
}
@@ -1039,7 +1040,9 @@ const SIDEBAR_ROWS: Rows = Rows {
n: Section::ALL.len(),
row: 32.0,
gap: 6.0,
row_min: 24.0,
// 22, not 24, since Invoices & Bills made 13 links (2026-10-24): so
// they still fit the owner's 1479x519 window unscrolled.
row_min: 22.0,
head_gap: 18.0,
foldable: true,
};
@@ -1049,8 +1052,8 @@ const RAIL_ROWS: Rows = Rows {
row: 36.0,
gap: 6.0,
// As low as the sidebar's: at 28 the rail scrolled in the owner's
// window (review, 2026-10-22).
row_min: 24.0,
// window (review, 2026-10-22), and at 24 once there were 13 links.
row_min: 22.0,
head_gap: 18.0,
foldable: false,
};
@@ -1733,18 +1736,36 @@ mod tests {
#[test]
fn the_sidebar_gives_way_in_order() {
// The links' height at a row and gap, however many sections there
// are (12 until 2026-10-24, when Invoices & Bills made 13).
let links = |row: f32, gap: f32| SIDEBAR_ROWS.height(row, gap);
// Card folds first.
let f = fit(18.0 + 450.0 + 8.0 + 147.0, FOOTER, &SIDEBAR_ROWS, 0.5);
let f = fit(
18.0 + links(32.0, 6.0) + 8.0 + 147.0,
FOOTER,
&SIDEBAR_ROWS,
0.5,
);
assert!(f.folded && !f.tight && f.row == 32.0 && f.gap == 6.0);
// Then the spacing goes, before the rows.
let f = fit(8.0 + 410.0 + 8.0 + 112.0, FOOTER, &SIDEBAR_ROWS, 0.5);
let f = fit(
8.0 + links(32.0, 4.0) + 8.0 + 112.0,
FOOTER,
&SIDEBAR_ROWS,
0.5,
);
assert!(f.folded && f.tight && f.row == 32.0 && f.gap < 6.0 && f.gap >= GAP_MIN);
// Then the rows, down to their least.
let f = fit(8.0 + 330.0 + 8.0 + 112.0, FOOTER, &SIDEBAR_ROWS, 0.5);
assert!(f.tight && !f.scroll && f.gap == GAP_MIN && f.row < 32.0 && f.row >= 24.0);
let f = fit(
8.0 + links(28.0, GAP_MIN) + 8.0 + 112.0,
FOOTER,
&SIDEBAR_ROWS,
0.5,
);
assert!(f.tight && !f.scroll && f.gap == GAP_MIN && f.row < 32.0 && f.row >= 22.0);
// Then they scroll.
let f = fit(300.0, FOOTER, &SIDEBAR_ROWS, 0.5);
assert!(f.scroll && f.row == 24.0 && f.gap == GAP_MIN);
assert!(f.scroll && f.row == 22.0 && f.gap == GAP_MIN);
}
#[test]