The odd-cent regression's net loss and its splits, the website's example Apple charge and the GUI plan's example receipt now use made-up amounts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
6 files changed
+51
-51
No files matched your search
@@ -6,18 +6,18 @@ sources = ["26 U.S.C. §704(a)", "Treas. Reg. §1.704-1(b)(2)(iv)(b)"]
|
|||||||
tags = ["partnership", "close", "rounding", "book", "us"]
|
tags = ["partnership", "close", "rounding", "book", "us"]
|
||||||
|
|
||||||
# The regression this example comes from (wanderbooks P3 defect 17, audit
|
# The regression this example comes from (wanderbooks P3 defect 17, audit
|
||||||
# F52, anonymized): a net loss of 399.23 at 50/50, closed in January and
|
# F52, anonymized): a net loss of 287.53 at 50/50, closed in January and
|
||||||
# again in March, with February closed to retained earnings. Restarting the
|
# again in March, with February closed to retained earnings. Restarting the
|
||||||
# count at March gave A the tied cent twice (A −399.24, B −399.22).
|
# count at March gave A the tied cent twice (A −287.54, B −287.52).
|
||||||
#
|
#
|
||||||
# By hand, from the rule:
|
# By hand, from the rule:
|
||||||
# January: −399.23 × 0.5 = −199.615 each; rounded toward zero, −199.61
|
# January: −287.53 × 0.5 = −143.765 each; rounded toward zero, −143.76
|
||||||
# each, one cent short; the remainders tie (0.005), so the cent goes to
|
# each, one cent short; the remainders tie (0.005), so the cent goes to
|
||||||
# the member listed first: A −199.62, B −199.61.
|
# the member listed first: A −143.77, B −143.76.
|
||||||
# February: 10.00 to retained earnings; the members' count skips it.
|
# February: 10.00 to retained earnings; the members' count skips it.
|
||||||
# March: the count's running total is −798.46, −399.23 each exactly;
|
# March: the count's running total is −575.06, −287.53 each exactly;
|
||||||
# less January's −199.62 / −199.61, March gives A −199.61, B −199.62.
|
# less January's −143.77 / −143.76, March gives A −143.76, B −143.77.
|
||||||
# Each member ends at −399.23, their exact share.
|
# Each member ends at −287.53, their exact share.
|
||||||
|
|
||||||
[setup]
|
[setup]
|
||||||
currency = "USD"
|
currency = "USD"
|
||||||
@@ -65,8 +65,8 @@ share = "0.5"
|
|||||||
date = "2026-01-15"
|
date = "2026-01-15"
|
||||||
description = "January's fees"
|
description = "January's fees"
|
||||||
lines = [
|
lines = [
|
||||||
{ account = "6000", side = "debit", amount = "399.23" },
|
{ account = "6000", side = "debit", amount = "287.53" },
|
||||||
{ account = "1000", side = "credit", amount = "399.23" },
|
{ account = "1000", side = "credit", amount = "287.53" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[events]] # event 2: the closing entry
|
[[events]] # event 2: the closing entry
|
||||||
@@ -91,8 +91,8 @@ close = { start = "2026-02-01", to = "retained_earnings" }
|
|||||||
date = "2026-03-15"
|
date = "2026-03-15"
|
||||||
description = "March's fees"
|
description = "March's fees"
|
||||||
lines = [
|
lines = [
|
||||||
{ account = "6000", side = "debit", amount = "399.23" },
|
{ account = "6000", side = "debit", amount = "287.53" },
|
||||||
{ account = "1000", side = "credit", amount = "399.23" },
|
{ account = "1000", side = "credit", amount = "287.53" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[events]] # event 6
|
[[events]] # event 6
|
||||||
@@ -105,25 +105,25 @@ query = "entry"
|
|||||||
entry = 2
|
entry = 2
|
||||||
|
|
||||||
[expect.values]
|
[expect.values]
|
||||||
"6000.credit" = "399.23"
|
"6000.credit" = "287.53"
|
||||||
"3000.debit" = "199.62"
|
"3000.debit" = "143.77"
|
||||||
"3001.debit" = "199.61"
|
"3001.debit" = "143.76"
|
||||||
|
|
||||||
[[expect]]
|
[[expect]]
|
||||||
query = "entry"
|
query = "entry"
|
||||||
entry = 6
|
entry = 6
|
||||||
|
|
||||||
[expect.values]
|
[expect.values]
|
||||||
"6000.credit" = "399.23"
|
"6000.credit" = "287.53"
|
||||||
"3000.debit" = "199.61"
|
"3000.debit" = "143.76"
|
||||||
"3001.debit" = "199.62"
|
"3001.debit" = "143.77"
|
||||||
|
|
||||||
[[expect]]
|
[[expect]]
|
||||||
query = "balances"
|
query = "balances"
|
||||||
as_of = "2026-03-31"
|
as_of = "2026-03-31"
|
||||||
|
|
||||||
[expect.values]
|
[expect.values]
|
||||||
"3000" = "-399.23"
|
"3000" = "-287.53"
|
||||||
"3001" = "-399.23"
|
"3001" = "-287.53"
|
||||||
"3999" = "-10.00"
|
"3999" = "-10.00"
|
||||||
"1000" = "-808.46"
|
"1000" = "-585.06"
|
||||||
@@ -2225,10 +2225,10 @@ impl Ledger {
|
|||||||
/// 6. IF a journaled MembersCumulative names a start other than the
|
/// 6. IF a journaled MembersCumulative names a start other than the
|
||||||
/// one the books derive: SHOULD refuse (InvalidMembers)
|
/// one the books derive: SHOULD refuse (InvalidMembers)
|
||||||
/// BECAUSE replay would split differently from the journal.
|
/// BECAUSE replay would split differently from the journal.
|
||||||
/// 7. IF 50/50 members closed −399.23 in January (A −199.62, B
|
/// 7. IF 50/50 members closed −287.53 in January (A −143.77, B
|
||||||
/// −199.61), February closed to retained earnings, and March closes
|
/// −143.76), February closed to retained earnings, and March closes
|
||||||
/// −399.23 to the members: SHOULD give A −199.61, B −199.62, so
|
/// −287.53 to the members: SHOULD give A −143.76, B −143.77, so
|
||||||
/// each has −399.23 (the count skips February's close).
|
/// each has −287.53 (the count skips February's close).
|
||||||
/// ```
|
/// ```
|
||||||
pub fn close_period_to(
|
pub fn close_period_to(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -205,50 +205,50 @@ const THIRDS: [&str; 3] = ["0.3333", "0.3333", "0.3334"];
|
|||||||
// The regression (audit F52, 2026-10-16, anonymized)
|
// The regression (audit F52, 2026-10-16, anonymized)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Regression (P3 defect 17; audit F52's −399.23, anonymized): members A
|
/// Regression (P3 defect 17; audit F52's −287.53, anonymized): members A
|
||||||
/// and B at half each. January's net loss of 399.23 closes to them: exact
|
/// and B at half each. January's net loss of 287.53 closes to them: exact
|
||||||
/// −199.615 each, so A −199.62, B −199.61 (the tie to the member listed
|
/// −143.765 each, so A −143.77, B −143.76 (the tie to the member listed
|
||||||
/// first). February's 10.00 loss closes to retained earnings. March's
|
/// first). February's 10.00 loss closes to retained earnings. March's
|
||||||
/// 399.23 loss closes to them again. The run rule (2026-10-13 to P3)
|
/// 287.53 loss closes to them again. The run rule (2026-10-13 to P3)
|
||||||
/// started over at March, handing A the tied cent a second time: A
|
/// started over at March, handing A the tied cent a second time: A
|
||||||
/// −399.24, B −399.22 for an exact −399.23 each. Now February's close is
|
/// −287.54, B −287.52 for an exact −287.53 each. Now February's close is
|
||||||
/// skipped and March continues January's count: the running −798.46 splits
|
/// skipped and March continues January's count: the running −575.06 splits
|
||||||
/// −399.23 each, less January's −199.62 / −199.61, so March gives A
|
/// −287.53 each, less January's −143.77 / −143.76, so March gives A
|
||||||
/// −199.61 and B −199.62. The March entry: Cr 6000 399.23, Dr 3000 199.61,
|
/// −143.76 and B −143.77. The March entry: Cr 6000 287.53, Dr 3000 143.76,
|
||||||
/// Dr 3001 199.62.
|
/// Dr 3001 143.77.
|
||||||
#[test]
|
#[test]
|
||||||
fn the_audit_odd_cent_never_piles_up_across_closes() {
|
fn the_audit_odd_cent_never_piles_up_across_closes() {
|
||||||
type Rule = fn(Vec<MemberClose>) -> CloseTo;
|
type Rule = fn(Vec<MemberClose>) -> CloseTo;
|
||||||
let (run, cumulative): (Rule, Rule) = (CloseTo::MembersRun, CloseTo::Members);
|
let (run, cumulative): (Rule, Rule) = (CloseTo::MembersRun, CloseTo::Members);
|
||||||
for (rule, a, b) in [
|
for (rule, a, b) in [
|
||||||
(run, "-399.24", "-399.22"),
|
(run, "-287.54", "-287.52"),
|
||||||
(cumulative, "-399.23", "-399.23"),
|
(cumulative, "-287.53", "-287.53"),
|
||||||
] {
|
] {
|
||||||
let mut co = company(Currency::USD, 2);
|
let mut co = company(Currency::USD, 2);
|
||||||
let to = rule(co.members(&[0, 1], &HALF));
|
let to = rule(co.members(&[0, 1], &HALF));
|
||||||
let jan = co.month(1, "-399.23", &to);
|
let jan = co.month(1, "-287.53", &to);
|
||||||
let jan_entry = co.l.get_period(jan).unwrap().closing_entry.unwrap();
|
let jan_entry = co.l.get_period(jan).unwrap().closing_entry.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
co.lines(jan_entry),
|
co.lines(jan_entry),
|
||||||
[
|
[
|
||||||
line("6000", Side::Credit, "399.23"),
|
line("6000", Side::Credit, "287.53"),
|
||||||
line("3000", Side::Debit, "199.62"),
|
line("3000", Side::Debit, "143.77"),
|
||||||
line("3001", Side::Debit, "199.61"),
|
line("3001", Side::Debit, "143.76"),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
co.month(2, "-10.00", &CloseTo::RetainedEarnings { sweep: vec![] });
|
co.month(2, "-10.00", &CloseTo::RetainedEarnings { sweep: vec![] });
|
||||||
let mar = co.month(3, "-399.23", &to);
|
let mar = co.month(3, "-287.53", &to);
|
||||||
assert_eq!(co.capitals(), decs(&[a, b]));
|
assert_eq!(co.capitals(), decs(&[a, b]));
|
||||||
let retained = co.l.chart().find_by_code("3999").unwrap().id;
|
let retained = co.l.chart().find_by_code("3999").unwrap().id;
|
||||||
assert_eq!(co.l.account_balance(retained).unwrap(), dec("-10.00"));
|
assert_eq!(co.l.account_balance(retained).unwrap(), dec("-10.00"));
|
||||||
if a == "-399.23" {
|
if a == "-287.53" {
|
||||||
let mar_entry = co.l.get_period(mar).unwrap().closing_entry.unwrap();
|
let mar_entry = co.l.get_period(mar).unwrap().closing_entry.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
co.lines(mar_entry),
|
co.lines(mar_entry),
|
||||||
[
|
[
|
||||||
line("6000", Side::Credit, "399.23"),
|
line("6000", Side::Credit, "287.53"),
|
||||||
line("3000", Side::Debit, "199.61"),
|
line("3000", Side::Debit, "143.76"),
|
||||||
line("3001", Side::Debit, "199.62"),
|
line("3001", Side::Debit, "143.77"),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
let (since, split) = co.l.member_close_split(mar).unwrap();
|
let (since, split) = co.l.member_close_split(mar).unwrap();
|
||||||
@@ -257,8 +257,8 @@ fn the_audit_odd_cent_never_piles_up_across_closes() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
rows,
|
rows,
|
||||||
[
|
[
|
||||||
(dec("-199.61"), dec("-399.23"), dec("-399.23")),
|
(dec("-143.76"), dec("-287.53"), dec("-287.53")),
|
||||||
(dec("-199.62"), dec("-399.23"), dec("-399.23")),
|
(dec("-143.77"), dec("-287.53"), dec("-287.53")),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -602,10 +602,10 @@ Defect 17 (rules.md B-13; D-10, D-38), built without its mutation run
|
|||||||
worked by hand in the test's comment.
|
worked by hand in the test's comment.
|
||||||
|
|
||||||
**The named regression.** `the_audit_odd_cent_never_piles_up_across_closes`
|
**The named regression.** `the_audit_odd_cent_never_piles_up_across_closes`
|
||||||
(`ledger-core` `tests/odd_cent.rs`): audit F52's −399.23 at 50/50,
|
(`ledger-core` `tests/odd_cent.rs`): an audit finding's −287.53 at 50/50,
|
||||||
anonymized, with a close to retained earnings between two members closes.
|
anonymized, with a close to retained earnings between two members closes.
|
||||||
The run rule left A −399.24 / B −399.22; the rule since P3 gives −399.23
|
The run rule left A −287.54 / B −287.52; the rule since P3 gives −287.53
|
||||||
each (March: Dr 3000 199.61, Dr 3001 199.62). Its bookspec example is
|
each (March: Dr 3000 143.76, Dr 3001 143.77). Its bookspec example is
|
||||||
`bookspec/examples/members-close.odd-cent-across-closes.toml`; it and the
|
`bookspec/examples/members-close.odd-cent-across-closes.toml`; it and the
|
||||||
other `members-close.*` examples are checked with every example by
|
other `members-close.*` examples are checked with every example by
|
||||||
`ledger-core` `tests/bookspec.rs` (`every_bookspec_example_holds`).
|
`ledger-core` `tests/bookspec.rs` (`every_bookspec_example_holds`).
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ journal with its verification status.
|
|||||||
hash-chained journal and the blob store and builds its own in-memory
|
hash-chained journal and the blob store and builds its own in-memory
|
||||||
view (`ledger_store::Snapshot`). Changes happen through the agent (MCP).
|
view (`ledger_store::Snapshot`). Changes happen through the agent (MCP).
|
||||||
Where the GUI finds something to fix, it offers a ready-to-paste prompt
|
Where the GUI finds something to fix, it offers a ready-to-paste prompt
|
||||||
for the agent ("Attach the receipt for entry 12 — Anthropic, $5.82").
|
for the agent ("Attach the receipt for entry 12 — Anthropic, $7.40").
|
||||||
5. **Live.** It follows the journal: a change made by the agent appears
|
5. **Live.** It follows the journal: a change made by the agent appears
|
||||||
within a second, highlighted as new.
|
within a second, highlighted as new.
|
||||||
6. **Numbers are typography.** Tabular figures everywhere (Inter with
|
6. **Numbers are typography.** Tabular figures everywhere (Inter with
|
||||||
|
|||||||
+1
-1
@@ -193,7 +193,7 @@ footer { padding: 72px 0 48px; color: var(--faint); font-size: 14px; display: fl
|
|||||||
<h2>Just ask.</h2>
|
<h2>Just ask.</h2>
|
||||||
<p class="sub">Your bookkeeper is the AI you already use, working through an accounting engine that won't let it be wrong. Talk to it the way you'd talk to a good bookkeeper. It does the work, shows you the result, and waits for your OK before anything is posted.</p>
|
<p class="sub">Your bookkeeper is the AI you already use, working through an accounting engine that won't let it be wrong. Talk to it the way you'd talk to a good bookkeeper. It does the work, shows you the result, and waits for your OK before anything is posted.</p>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="card ask"><div class="say">Find the receipt for the $106.38 Apple charge in my email and record it.</div><p>Searches your inbox, files the receipt as a PDF, drafts the entry with the right accounts, and shows you the preview.</p></div>
|
<div class="card ask"><div class="say">Find the receipt for the $84.17 Apple charge in my email and record it.</div><p>Searches your inbox, files the receipt as a PDF, drafts the entry with the right accounts, and shows you the preview.</p></div>
|
||||||
<div class="card ask"><div class="say">Here's last month's bank export. Categorize everything.</div><p>Imports the transactions as drafts, proposes an account for each from how you've booked them before, and asks about the ones it isn't sure of.</p></div>
|
<div class="card ask"><div class="say">Here's last month's bank export. Categorize everything.</div><p>Imports the transactions as drafts, proposes an account for each from how you've booked them before, and asks about the ones it isn't sure of.</p></div>
|
||||||
<div class="card ask"><div class="say">The domain renewal went to the wrong account. Fix it.</div><p>Reverses the entry and records it correctly. Nothing is ever erased; the history shows exactly what changed and why.</p></div>
|
<div class="card ask"><div class="say">The domain renewal went to the wrong account. Fix it.</div><p>Reverses the entry and records it correctly. Nothing is ever erased; the history shows exactly what changed and why.</p></div>
|
||||||
<div class="card ask"><div class="say">Show me every Adobe charge this year.</div><p>Opens the search in the viewer beside you, every charge listed, each one a click from its receipt.</p></div>
|
<div class="card ask"><div class="say">Show me every Adobe charge this year.</div><p>Opens the search in the viewer beside you, every charge listed, each one a click from its receipt.</p></div>
|
||||||
|
|||||||
Reference in new issue
Block a user