merge: P4b S6, paying on terms (branch S6, 6c4480cd)
The third of the paused branches: Ledger::settle with discounts and the processor's fee (kernel::settle, Kani settle_is_exact, Lean settle_posts_a_document_step), books.receive_payment and books.pay_bill with applications, books.bills_due, the agings' detail and tie-out, the aging exports and the CPA package's aging CSVs, the handlers in tools/settlements.rs. Twelve files conflicted with the S4.4 and S5 merges, resolved as plan 8.0.2 says: both routers; both test appends (the shared match arm in arap_standing.rs spliced into both tests; the shared `.unwrap()` in store.rs); the scope table at 124; one invoices-and-bills handbook (S5's send step 1 with S6's steps 2, 3, 5 and procedures) registered once; docs/mcp-interface.md with S4.4's create docs and S6's payment docs; S5's law renumbered 35 and S6's 36 (S1's is 34); the parity rows and sections of both. S6's wire tests create their bills as S4.4's contract now requires (confirmed, the vendor's number, a line account, the vendor's invoice uploaded) and live in their own module beside S4.4's tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
commit
be58059830
45 files changed
+3949
-520
No files matched your search
@@ -55,6 +55,7 @@ struct Item {
|
||||
party: String,
|
||||
date: NaiveDate,
|
||||
due: NaiveDate,
|
||||
terms: Option<ledger_core::Terms>,
|
||||
state: Result<Option<DocState>, ledger_core::LedgerError>,
|
||||
}
|
||||
|
||||
@@ -74,6 +75,7 @@ fn items(b: &Books, bills: bool) -> Vec<Item> {
|
||||
.unwrap_or_default(),
|
||||
date: d.date,
|
||||
due: d.due_date,
|
||||
terms: d.terms.clone(),
|
||||
state: b.doc_today(DocRef::Bill(d.id)),
|
||||
})
|
||||
.collect()
|
||||
@@ -89,6 +91,7 @@ fn items(b: &Books, bills: bool) -> Vec<Item> {
|
||||
.unwrap_or_default(),
|
||||
date: d.date,
|
||||
due: d.due_date,
|
||||
terms: d.terms.clone(),
|
||||
state: b.doc_today(DocRef::Invoice(d.id)),
|
||||
})
|
||||
.collect()
|
||||
@@ -282,26 +285,23 @@ pub fn show_list(ui: &mut Ui, b: &Books, bills: bool, shell: &mut Shell<'_>) {
|
||||
return;
|
||||
}
|
||||
let amt = widgets::AMOUNT_MIN + 16.0;
|
||||
let fixed = 110.0 + 104.0 * 2.0 + 120.0 + amt * 2.0;
|
||||
let fixed = 110.0 + 104.0 * 2.0 + 120.0 + amt * 2.0 + if bills { 104.0 } else { 0.0 };
|
||||
let w = ui.available_width().max(fixed + 160.0);
|
||||
widgets::min_width(ui, "invoice_list", w, |ui| {
|
||||
let head = |t: &str, cw: f32, right: bool| (t.to_string(), cw, right, p.muted);
|
||||
row(
|
||||
ui,
|
||||
w,
|
||||
&[
|
||||
head("Document", 110.0, false),
|
||||
head(if bills { "Vendor" } else { "Customer" }, 0.0, false),
|
||||
head("Date", 104.0, false),
|
||||
head("Due", 104.0, false),
|
||||
head("Status", 120.0, false),
|
||||
head("Total", amt, true),
|
||||
head("Still owed", amt, true),
|
||||
],
|
||||
None,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
let mut header = vec![
|
||||
head("Document", 110.0, false),
|
||||
head(if bills { "Vendor" } else { "Customer" }, 0.0, false),
|
||||
head("Date", 104.0, false),
|
||||
head("Due", 104.0, false),
|
||||
head("Status", 120.0, false),
|
||||
head("Total", amt, true),
|
||||
head("Still owed", amt, true),
|
||||
];
|
||||
if bills {
|
||||
header.push(head("Discount by", 104.0, false));
|
||||
}
|
||||
row(ui, w, &header, None, true, false);
|
||||
widgets::rule(ui);
|
||||
for it in rows {
|
||||
let (status, total, owed, late) = match &it.state {
|
||||
@@ -314,28 +314,49 @@ pub fn show_list(ui: &mut Ui, b: &Books, bills: bool, shell: &mut Shell<'_>) {
|
||||
Ok(None) => ("Unknown".into(), String::new(), String::new(), false),
|
||||
Err(_) => ("Can't compute".into(), String::new(), String::new(), false),
|
||||
};
|
||||
let discount_by = it.terms.as_ref().and_then(|t| t.discount_by(it.date));
|
||||
let discount_open = bills && discount_by.is_some_and(|d| d >= b.today);
|
||||
let discount_soon = discount_open
|
||||
&& discount_by.is_some_and(|d| d <= b.today + chrono::Days::new(3));
|
||||
let due_soon = bills
|
||||
&& matches!(&it.state, Ok(Some(s))
|
||||
if !s.outstanding.is_zero() && s.status != DocStatus::Void)
|
||||
&& it.due <= b.today + chrono::Days::new(7);
|
||||
let name = format!("{}, {}, {}, owes {}", it.name, it.party, status, owed);
|
||||
let resp = row(
|
||||
ui,
|
||||
w,
|
||||
&[
|
||||
(it.name.clone(), 110.0, false, p.text),
|
||||
(it.party.clone(), 0.0, false, p.text),
|
||||
(fmt::date(it.date), 104.0, false, p.text),
|
||||
(
|
||||
fmt::date(it.due),
|
||||
104.0,
|
||||
false,
|
||||
if late { p.warning } else { p.text },
|
||||
),
|
||||
(status, 120.0, false, if late { p.warning } else { p.muted }),
|
||||
(total, amt, true, p.text),
|
||||
(owed, amt, true, p.text),
|
||||
],
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
let mut cells = vec![
|
||||
(it.name.clone(), 110.0, false, p.text),
|
||||
(it.party.clone(), 0.0, false, p.text),
|
||||
(fmt::date(it.date), 104.0, false, p.text),
|
||||
(
|
||||
fmt::date(it.due),
|
||||
104.0,
|
||||
false,
|
||||
if late || due_soon { p.warning } else { p.text },
|
||||
),
|
||||
(
|
||||
status.clone(),
|
||||
120.0,
|
||||
false,
|
||||
if late { p.warning } else { p.muted },
|
||||
),
|
||||
(total, amt, true, p.text),
|
||||
(owed, amt, true, p.text),
|
||||
];
|
||||
if bills {
|
||||
cells.push((
|
||||
discount_by.map(fmt::date).unwrap_or_default(),
|
||||
104.0,
|
||||
false,
|
||||
if discount_soon {
|
||||
p.warning
|
||||
} else if discount_open {
|
||||
p.text
|
||||
} else {
|
||||
p.muted
|
||||
},
|
||||
));
|
||||
}
|
||||
let resp = row(ui, w, &cells, None, false, true);
|
||||
// Named in full, for assistive tech (and the tests).
|
||||
resp.widget_info(|| {
|
||||
egui::WidgetInfo::labeled(egui::WidgetType::Button, true, &name)
|
||||
|
||||
Reference in new issue
Block a user