refactor(arap): rustfmt the unit's files, and the write-off's SOURCE line (P4b S7)
No behaviour: the files as rustfmt leaves them after the unit's edits (the commits before this one were made before the formatter ran), and the Ledger::write_off spec block cites A-5 alone for the direct write-off method (A-6 is the hand-entry refusal, not this command).
This commit is contained in:
1 parent
f31dd2640b
commit
22cdfcf34b
9 files changed
+75
-50
No files matched your search
@@ -87,7 +87,12 @@ fn write_off_then_recovery() {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
// The write-off's evidence, uploaded as an agent uploads it (D-46).
|
// The write-off's evidence, uploaded as an agent uploads it (D-46).
|
||||||
let upload = wire.upload(LETTER, "collection-letter.pdf", "application/pdf", "document");
|
let upload = wire.upload(
|
||||||
|
LETTER,
|
||||||
|
"collection-letter.pdf",
|
||||||
|
"application/pdf",
|
||||||
|
"document",
|
||||||
|
);
|
||||||
let out = ok(
|
let out = ok(
|
||||||
&mut wire,
|
&mut wire,
|
||||||
"books.write_off_invoice",
|
"books.write_off_invoice",
|
||||||
@@ -110,7 +115,10 @@ fn write_off_then_recovery() {
|
|||||||
// invoice: get_entry shows the one, the document's timeline the other.
|
// invoice: get_entry shows the one, the document's timeline the other.
|
||||||
let got = ok(&mut wire, "books.get_entry", json!({ "entry_id": entry }));
|
let got = ok(&mut wire, "books.get_entry", json!({ "entry_id": entry }));
|
||||||
assert_eq!(got["receipts"].as_array().map(Vec::len), Some(1), "{got}");
|
assert_eq!(got["receipts"].as_array().map(Vec::len), Some(1), "{got}");
|
||||||
assert_eq!(got["receipts"][0]["file_name"], json!("collection-letter.pdf"));
|
assert_eq!(
|
||||||
|
got["receipts"][0]["file_name"],
|
||||||
|
json!("collection-letter.pdf")
|
||||||
|
);
|
||||||
let doc = ok(
|
let doc = ok(
|
||||||
&mut wire,
|
&mut wire,
|
||||||
"books.document",
|
"books.document",
|
||||||
@@ -225,6 +233,12 @@ fn write_off_needs_evidence() {
|
|||||||
json!({ "attachment_id": attachment }),
|
json!({ "attachment_id": attachment }),
|
||||||
);
|
);
|
||||||
let links = receipt["links"].as_array().cloned().unwrap_or_default();
|
let links = receipt["links"].as_array().cloned().unwrap_or_default();
|
||||||
assert!(links.iter().any(|l| l["kind"] == json!("invoice")), "{receipt}");
|
assert!(
|
||||||
assert!(links.iter().any(|l| l["kind"] == json!("entry")), "{receipt}");
|
links.iter().any(|l| l["kind"] == json!("invoice")),
|
||||||
|
"{receipt}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
links.iter().any(|l| l["kind"] == json!("entry")),
|
||||||
|
"{receipt}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -6329,9 +6329,9 @@ impl Ledger {
|
|||||||
/// §1.166-2(a) (worthlessness on all the pertinent evidence),
|
/// §1.166-2(a) (worthlessness on all the pertinent evidence),
|
||||||
/// §1.166-3(a) (a partial write-off, to the extent charged
|
/// §1.166-3(a) (a partial write-off, to the extent charged
|
||||||
/// off); docs/spec/rules.md B-41; plan/served.md P4b S7,
|
/// off); docs/spec/rules.md B-41; plan/served.md P4b S7,
|
||||||
/// A-5 (direct write-off), A-6; ASC 326-20 noted (an allowance
|
/// A-5 (the direct write-off method); ASC 326-20 noted (an
|
||||||
/// is the GAAP preference; a small company's CPA accepts the
|
/// allowance is the GAAP preference; a small company's CPA
|
||||||
/// direct method, stated).
|
/// accepts the direct method, stated).
|
||||||
/// INVARIANTS:
|
/// INVARIANTS:
|
||||||
/// 1. MUST post one balanced entry: Dr the bad_debts role (6990 Bad
|
/// 1. MUST post one balanced entry: Dr the bad_debts role (6990 Bad
|
||||||
/// Debt Expense), Cr receivables, for the amount, dated the
|
/// Debt Expense), Cr receivables, for the amount, dated the
|
||||||
|
|||||||
@@ -1171,13 +1171,14 @@ impl<'a> Books<'a> {
|
|||||||
.unwrap_or_else(|| panic!("{what}: event {} posted no invoice", w.invoice));
|
.unwrap_or_else(|| panic!("{what}: event {} posted no invoice", w.invoice));
|
||||||
let amount = match &w.amount {
|
let amount = match &w.amount {
|
||||||
Some(a) => exact(a, what),
|
Some(a) => exact(a, what),
|
||||||
None => self
|
None => {
|
||||||
.store
|
self.store
|
||||||
.ledger()
|
.ledger()
|
||||||
.invoice_state(invoice, day())
|
.invoice_state(invoice, day())
|
||||||
.expect("the invoice's figures")
|
.expect("the invoice's figures")
|
||||||
.expect("dated by its own day")
|
.expect("dated by its own day")
|
||||||
.outstanding,
|
.outstanding
|
||||||
|
}
|
||||||
};
|
};
|
||||||
self.store
|
self.store
|
||||||
.write_off_invoice(
|
.write_off_invoice(
|
||||||
|
|||||||
@@ -252,8 +252,7 @@ fn a_write_off_shows_on_the_timeline() {
|
|||||||
let mut h = open(&dir, Route::Invoice(inv));
|
let mut h = open(&dir, Route::Invoice(inv));
|
||||||
shot(&mut h, "invoice-written-off");
|
shot(&mut h, "invoice-written-off");
|
||||||
assert!(
|
assert!(
|
||||||
h.query_by_label_contains("Written off, entry #")
|
h.query_by_label_contains("Written off, entry #").is_some(),
|
||||||
.is_some(),
|
|
||||||
"the timeline shows the write-off"
|
"the timeline shows the write-off"
|
||||||
);
|
);
|
||||||
// The status chip reads its label ("Written off"), not "late".
|
// The status chip reads its label ("Written off"), not "late".
|
||||||
|
|||||||
@@ -892,10 +892,12 @@ fn a_write_off_is_flagged_for_the_cpa() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
let want = format!("Write-off awaiting the CPA's note: Invoice {inv}");
|
let want = format!("Write-off awaiting the CPA's note: Invoice {inv}");
|
||||||
let said = server
|
let said = server.iter().find(|a| a.title == want).unwrap_or_else(|| {
|
||||||
.iter()
|
panic!(
|
||||||
.find(|a| a.title == want)
|
"the server's list has no {want:?}: {:?}",
|
||||||
.unwrap_or_else(|| panic!("the server's list has no {want:?}: {:?}", server.iter().map(|a| &a.title).collect::<Vec<_>>()));
|
server.iter().map(|a| &a.title).collect::<Vec<_>>()
|
||||||
|
)
|
||||||
|
});
|
||||||
assert_eq!(said.severity, ledger_package::Severity::Todo);
|
assert_eq!(said.severity, ledger_package::Severity::Todo);
|
||||||
assert!(said.prompt.contains("wanderbooks"), "{}", said.prompt);
|
assert!(said.prompt.contains("wanderbooks"), "{}", said.prompt);
|
||||||
assert!(said.detail.contains("100.00"), "{}", said.detail);
|
assert!(said.detail.contains("100.00"), "{}", said.detail);
|
||||||
@@ -910,14 +912,15 @@ fn a_write_off_is_flagged_for_the_cpa() {
|
|||||||
.attention
|
.attention
|
||||||
.iter()
|
.iter()
|
||||||
.find(|a| a.title == want)
|
.find(|a| a.title == want)
|
||||||
.unwrap_or_else(|| panic!("the viewer shows no {want:?}: {:?}", books.attention.iter().map(|a| &a.title).collect::<Vec<_>>()));
|
.unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"the viewer shows no {want:?}: {:?}",
|
||||||
|
books.attention.iter().map(|a| &a.title).collect::<Vec<_>>()
|
||||||
|
)
|
||||||
|
});
|
||||||
assert_eq!(shown.target, Some(ledger_gui::nav::Route::Documents));
|
assert_eq!(shown.target, Some(ledger_gui::nav::Route::Documents));
|
||||||
// The viewer reads the same figures from the journal it replayed.
|
// The viewer reads the same figures from the journal it replayed.
|
||||||
let st = books
|
let st = books.ledger.invoice_state(inv, today).unwrap().unwrap();
|
||||||
.ledger
|
|
||||||
.invoice_state(inv, today)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(st.status, ledger_core::DocStatus::WrittenOff);
|
assert_eq!(st.status, ledger_core::DocStatus::WrittenOff);
|
||||||
assert!(books.ledger.get_entry(entry).is_some());
|
assert!(books.ledger.get_entry(entry).is_some());
|
||||||
}
|
}
|
||||||
@@ -128,10 +128,6 @@ impl BooksServer {
|
|||||||
entry_id
|
entry_id
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(Json(write_off_out(
|
Ok(Json(write_off_out(store.ledger(), p.invoice, entry_id)?))
|
||||||
store.ledger(),
|
|
||||||
p.invoice,
|
|
||||||
entry_id,
|
|
||||||
)?))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,6 @@ pub use snapshot::{DiskCheck, Snapshot};
|
|||||||
pub use store::{
|
pub use store::{
|
||||||
Attachment, BillPreview, DepreciationRun, DisposalPreview, EntryRow, EventRow, InvoicePreview,
|
Attachment, BillPreview, DepreciationRun, DisposalPreview, EntryRow, EventRow, InvoicePreview,
|
||||||
JOURNAL_FILE, LEGACY_DB_FILE, LOCK_FILE, MAX_RECURRING_RUN, PayrollLineInput, Reconciliation,
|
JOURNAL_FILE, LEGACY_DB_FILE, LOCK_FILE, MAX_RECURRING_RUN, PayrollLineInput, Reconciliation,
|
||||||
Statement, Store, Subject, VerifyReport, WriteOffEvidence, database_in_use, db_file, db_file_for,
|
Statement, Store, Subject, VerifyReport, WriteOffEvidence, database_in_use, db_file,
|
||||||
is_database_file,
|
db_file_for, is_database_file,
|
||||||
};
|
};
|
||||||
@@ -3747,13 +3747,8 @@ impl Store {
|
|||||||
let description = format!("Write off invoice {invoice}: {}", reason.trim());
|
let description = format!("Write off invoice {invoice}: {}", reason.trim());
|
||||||
let (entry_id, mut journaled) =
|
let (entry_id, mut journaled) =
|
||||||
self.journal_auto_entry(date, &description, bad, ar, amount)?;
|
self.journal_auto_entry(date, &description, bad, ar, amount)?;
|
||||||
let written_off = crate::meta::invoice_written_off(
|
let written_off =
|
||||||
invoice,
|
crate::meta::invoice_written_off(invoice, entry_id, date, amount, reason.trim());
|
||||||
entry_id,
|
|
||||||
date,
|
|
||||||
amount,
|
|
||||||
reason.trim(),
|
|
||||||
);
|
|
||||||
journaled.push(self.append_journal("invoice.written_off", written_off)?);
|
journaled.push(self.append_journal("invoice.written_off", written_off)?);
|
||||||
let live = self.ledger.write_off(invoice, date, amount, reason)?;
|
let live = self.ledger.write_off(invoice, date, amount, reason)?;
|
||||||
debug_assert_eq!(live, entry_id, "id allocator diverged from journal");
|
debug_assert_eq!(live, entry_id, "id allocator diverged from journal");
|
||||||
|
|||||||
@@ -1100,10 +1100,7 @@ fn write_off_waits_for_documents_v2() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
s.verify().unwrap();
|
s.verify().unwrap();
|
||||||
let live = s.ledger().clone();
|
let live = s.ledger().clone();
|
||||||
let st = live
|
let st = live.invoice_state(1, d(2026, 12, 31)).unwrap().unwrap();
|
||||||
.invoice_state(1, d(2026, 12, 31))
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(st.status, ledger_core::DocStatus::WrittenOff);
|
assert_eq!(st.status, ledger_core::DocStatus::WrittenOff);
|
||||||
assert_eq!(st.written_off, cents(10_000));
|
assert_eq!(st.written_off, cents(10_000));
|
||||||
// The evidence is on the write-off's entry and on its invoice.
|
// The evidence is on the write-off's entry and on its invoice.
|
||||||
@@ -1135,7 +1132,12 @@ fn write_off_replays_and_projects() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let c = s.add_customer("Fernhill Labs").unwrap();
|
let c = s.add_customer("Fernhill Labs").unwrap();
|
||||||
let inv = s
|
let inv = s
|
||||||
.create_invoice(c, d(2026, 4, 1), d(2026, 5, 1), vec![line("Design", 10_000)])
|
.create_invoice(
|
||||||
|
c,
|
||||||
|
d(2026, 4, 1),
|
||||||
|
d(2026, 5, 1),
|
||||||
|
vec![line("Design", 10_000)],
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// Half paid, the rest written off: the invoice reads Written off with
|
// Half paid, the rest written off: the invoice reads Written off with
|
||||||
// 50 paid and 50 written off (B-38 SCENARIO 7).
|
// 50 paid and 50 written off (B-38 SCENARIO 7).
|
||||||
@@ -1175,10 +1177,16 @@ fn write_off_replays_and_projects() {
|
|||||||
assert!(got.iter().any(|(t, v)| t == "receipt.attached"
|
assert!(got.iter().any(|(t, v)| t == "receipt.attached"
|
||||||
&& v["attachment_id"] == json!(attachment)
|
&& v["attachment_id"] == json!(attachment)
|
||||||
&& v["entry_id"] == json!(entry)));
|
&& v["entry_id"] == json!(entry)));
|
||||||
assert!(got.iter().any(|(t, v)| t == "attachment.linked"
|
assert!(
|
||||||
&& v["attachment_id"] == json!(attachment)));
|
got.iter()
|
||||||
|
.any(|(t, v)| t == "attachment.linked" && v["attachment_id"] == json!(attachment))
|
||||||
|
);
|
||||||
// The figures, live.
|
// The figures, live.
|
||||||
let st = s.ledger().invoice_state(inv, d(2026, 5, 1)).unwrap().unwrap();
|
let st = s
|
||||||
|
.ledger()
|
||||||
|
.invoice_state(inv, d(2026, 5, 1))
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(st.status, ledger_core::DocStatus::WrittenOff);
|
assert_eq!(st.status, ledger_core::DocStatus::WrittenOff);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(st.paid, st.written_off, st.outstanding),
|
(st.paid, st.written_off, st.outstanding),
|
||||||
@@ -1206,7 +1214,11 @@ fn write_off_replays_and_projects() {
|
|||||||
"{:?}",
|
"{:?}",
|
||||||
live.differs_from(s.ledger())
|
live.differs_from(s.ledger())
|
||||||
);
|
);
|
||||||
let replayed = s.ledger().invoice_state(inv, d(2026, 5, 1)).unwrap().unwrap();
|
let replayed = s
|
||||||
|
.ledger()
|
||||||
|
.invoice_state(inv, d(2026, 5, 1))
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(replayed, st);
|
assert_eq!(replayed, st);
|
||||||
s.verify().unwrap();
|
s.verify().unwrap();
|
||||||
}
|
}
|
||||||
@@ -1220,7 +1232,12 @@ fn write_off_without_evidence_is_refused() {
|
|||||||
let mut s = Store::create(&dir.0, Currency::USD).unwrap();
|
let mut s = Store::create(&dir.0, Currency::USD).unwrap();
|
||||||
let c = s.add_customer("Fernhill Labs").unwrap();
|
let c = s.add_customer("Fernhill Labs").unwrap();
|
||||||
let inv = s
|
let inv = s
|
||||||
.create_invoice(c, d(2026, 4, 1), d(2026, 5, 1), vec![line("Design", 10_000)])
|
.create_invoice(
|
||||||
|
c,
|
||||||
|
d(2026, 4, 1),
|
||||||
|
d(2026, 5, 1),
|
||||||
|
vec![line("Design", 10_000)],
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let head = s.head();
|
let head = s.head();
|
||||||
let len = journal_len(&dir.0);
|
let len = journal_len(&dir.0);
|
||||||
|
|||||||
Reference in new issue
Block a user