merge: P4b S7, bad debts (branch S7, 4b4fecb9)

The fourth of the paused branches: Ledger::write_off and the bad_debts
control role, invoice.written_off with its evidence filed in the same
store call, books.write_off_invoice, the write-off on the timeline and
in Needs attention, Lean writeOff_within_bounds, rules.md B-41 and the
bookspec examples. Twenty-eight files conflicted with the S4.4, S5 and
S6 merges; resolved as plan 8.0.2 says, keeping both sides' appends with
their seams restored, and: the scope table at 125; S7's law numbered 37;
B-41 placed before B-42; the handbook's step 4 and recovery procedures
folded into the one invoices-and-bills section; S7's wire tests in their
own module with the confirm S4.4's contract needs; S7's copy of the
payment steps dropped (S6 replaced them with Ledger::settle) and its
write-off journaled through S6's journal_auto_lines; in the old-books
fixture S7's hand-written write-off moved to a second invoice, since
S6's hand-written payment settles invoice 1 in full.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
swaitsandClaude Fable 5.1 committed 2026-10-28 21:47:33 -06:00
commit 335a3eaa7e
47 files changed
+2883 -74

No files matched your search

+30
View File
@@ -770,6 +770,28 @@ fn books_p2_served_cut_over_whole() {
)
.unwrap()["id"]
.clone();
// A receipt not yet on an entry (P2 reads the unlink; entry 7's bank
// fee is under the receipt policy's threshold, so taking its last
// receipt is allowed), and a legacy-shape invoice (no new field, so no
// feature): the evidence and the document P4b S7's write-off needs
// after the go-ahead.
a.call(
"books.unlink_receipt",
json!({"attachment_id": 7, "reason": "filed against the wrong entry"}),
)
.unwrap();
let customer = a
.call("books.add_customer", json!({"name": "Ridgeway Co"}))
.unwrap()["id"]
.clone();
let unpaid = a
.call(
"books.create_invoice",
json!({"party_id": customer, "date": "2026-01-10", "due_date": "2026-02-09",
"lines": [{"description": "Survey", "amount": "300.00"}], "confirmed": true}),
)
.unwrap()["id"]
.clone();
let waiting = journal(&dir);
let refused: Vec<(&str, Value)> = vec![
// An invoice carrying documents_v2's fields (terms: P4b S4.4) and
@@ -824,6 +846,14 @@ fn books_p2_served_cut_over_whole() {
"books.close_period",
json!({"period_id": q2, "confirmed": true}),
),
// A bad-debt write-off (documents_v2's new event, P4b S7): its
// evidence filed and linked in the same call.
(
"books.write_off_invoice",
json!({"invoice": unpaid, "date": "2026-02-20",
"reason": "collection exhausted",
"existing_attachment_id": 7, "confirmed": true}),
),
];
for (tool, args) in &refused {
let e = a.call(tool, args.clone()).unwrap_err();