feat(audit): audit runs and attestations are journaled (P8)
A claim that someone checked the books is evidence the CPA handoff rests on, so it is a journal fact and replays: audit.run (a programme run through a day at the books' figure head, with the hashes that make it reproducible and the two filed workpapers), receipt.attested, entry.reviewed and period.attested. Behind the journal feature audits, schema 20; no figure moves. The figure head is the last line that bears on a figure: filing a workpaper, a run or an attestation never makes an audit stale, an entry, document, close, settings or role change does, so a period is attested only on an audit that still describes the books. A receipt reading that differs from the entry is recorded as disagreeing and never counts; whether a claim counts is derived against the bar when asked, never stored. The store also reads what a ledger cannot: the chain, the database against a replay, stored and extracted files, the access log, duplicate files, and the backups' standing handed in by the server. Fixture old-books/p8_before was written by the version before this one and opens to its figures. period.attested was the tests' placeholder for an unknown event type; they use ledger.not_yet_written now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
1 parent
843bcc421f
commit
6b7dccd1ee
19 files changed
+2479
-18
No files matched your search
@@ -0,0 +1,85 @@
|
||||
//! Writes `p8_before`: books the version before P8 (main b97234ac) wrote
|
||||
//! through its Store API: a receipted opening and purchase, a 40.00
|
||||
//! purchase with no receipt, a small one under the threshold, a reversal,
|
||||
//! an invoice with a part payment, an open draft, and a closed January.
|
||||
//! No audit run, attestation or attestation setting exists in that
|
||||
//! version; this version opens the same journal and must report the same
|
||||
//! figures, and an audit of it finds exactly the missing receipt and the
|
||||
//! open draft.
|
||||
use chrono::NaiveDate;
|
||||
use ledger_core::{AccountType, Currency, DocumentLine, Money, Side};
|
||||
use ledger_store::Store;
|
||||
use rust_decimal::Decimal;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn d(y: i32, m: u32, day: u32) -> NaiveDate {
|
||||
NaiveDate::from_ymd_opt(y, m, day).unwrap()
|
||||
}
|
||||
fn cents(c: i64) -> Decimal {
|
||||
Decimal::new(c, 2)
|
||||
}
|
||||
fn usd(c: i64) -> Money {
|
||||
Money::new(cents(c), Currency::USD)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let root = PathBuf::from(std::env::args().nth(1).unwrap());
|
||||
let dir = root.join("p8_before");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let mut s = Store::create(&dir, Currency::USD).unwrap();
|
||||
let bank = s.add_account("1000", "Bank", AccountType::Asset, None, false).unwrap();
|
||||
let capital = s.add_account("3000", "Capital", AccountType::Equity, None, false).unwrap();
|
||||
let sales = s.add_account("4000", "Sales", AccountType::Revenue, None, false).unwrap();
|
||||
let rent = s.add_account("6000", "Rent", AccountType::Expense, None, false).unwrap();
|
||||
let software = s.add_account("6100", "Software", AccountType::Expense, None, false).unwrap();
|
||||
let post = |s: &mut Store, date: NaiveDate, what: &str, dr: u64, cr: u64, c: i64| {
|
||||
let e = s.create_entry(date, what).unwrap();
|
||||
s.add_line(e, dr, Side::Debit, usd(c)).unwrap();
|
||||
s.add_line(e, cr, Side::Credit, usd(c)).unwrap();
|
||||
e
|
||||
};
|
||||
let seed = post(&mut s, d(2026, 1, 1), "Opening capital", bank, capital, 100_000);
|
||||
s.post_entry(seed).unwrap();
|
||||
s.attach_receipt(Some(seed), "opening.txt", "text/plain", b"Opening deposit 1000.00 2026-01-01", None)
|
||||
.unwrap();
|
||||
let rent_jan = post(&mut s, d(2026, 1, 5), "January rent", rent, bank, 50_000);
|
||||
s.post_entry(rent_jan).unwrap();
|
||||
s.attach_receipt(Some(rent_jan), "rent.txt", "text/plain", b"Rent January 500.00 2026-01-05", None)
|
||||
.unwrap();
|
||||
// 40.00 is over the 25.00 threshold: no receipt, so an audit finds it.
|
||||
let paper = post(&mut s, d(2026, 1, 7), "Printer paper", software, bank, 4_000);
|
||||
s.post_entry(paper).unwrap();
|
||||
// Under the threshold: needs none.
|
||||
let stamps = post(&mut s, d(2026, 1, 8), "Stamps", software, bank, 1_200);
|
||||
s.post_entry(stamps).unwrap();
|
||||
let wrong = post(&mut s, d(2026, 1, 12), "Rent paid twice", rent, bank, 50_000);
|
||||
s.post_entry(wrong).unwrap();
|
||||
s.attach_receipt(Some(wrong), "rent2.txt", "text/plain", b"Rent again 500.00 2026-01-12", None)
|
||||
.unwrap();
|
||||
s.reverse_entry(wrong, d(2026, 1, 14), "Entered twice").unwrap();
|
||||
let customer = s.add_customer("Fernhill Labs").unwrap();
|
||||
let inv = s
|
||||
.create_invoice(
|
||||
customer,
|
||||
d(2026, 1, 10),
|
||||
d(2026, 2, 9),
|
||||
vec![DocumentLine {
|
||||
description: "Design".into(),
|
||||
amount: cents(120_000),
|
||||
class_id: None,
|
||||
account: None,
|
||||
quantity: None,
|
||||
unit_price: None,
|
||||
}],
|
||||
)
|
||||
.unwrap();
|
||||
s.receive_payment(inv, d(2026, 1, 20), cents(40_000), bank).unwrap();
|
||||
// An open draft in February; January closes around it.
|
||||
let _open = post(&mut s, d(2026, 2, 12), "Coworking day pass", rent, bank, 3_500);
|
||||
let jan = s.create_period("2026-01", d(2026, 1, 1), d(2026, 1, 31)).unwrap();
|
||||
s.close_period(jan).unwrap();
|
||||
let _ = sales;
|
||||
s.verify().unwrap();
|
||||
println!("WROTE {:?}", dir);
|
||||
}
|
||||
Reference in new issue
Block a user