P3: odd cents split the same running way everywhere (defect 17, D-38)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
Members' shares of earnings are split cumulatively by largest remainder in the closes, the members' equity statement, the K-1 worksheet and the viewer, so a member's capital on a given day never moves by a cent when its month is later closed. Old closes replay as they were; the first P3 close carries any true-up. Feature members_cumulative. Kani and Lean proofs of the bounded drift; bookspec examples. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
44 files changed
+6636
-195
No files matched your search
@@ -0,0 +1,83 @@
|
||||
//! x_member_closes: members closes under each rounding rule main wrote
|
||||
//! (P3 review R-2), through MAIN's Store API (main 00226a4c, 2026-10-20).
|
||||
//! Two members, A and B, 50/50; A has a distributions account. Eleven
|
||||
//! months, each closed in order:
|
||||
//! Jan-Mar: untagged (the last member absorbs): +0.01, -0.03, +0.05;
|
||||
//! A's 1.00 distribution in February is swept into A's capital.
|
||||
//! Apr-Jun: "largest_remainder" (each close on its own): +0.07, -0.01, +0.03.
|
||||
//! Jul-Nov: "cumulative" (the run rule), broken by a close to retained
|
||||
//! earnings in September: +0.01, +0.01, (RE +0.01), +0.03, -0.05.
|
||||
//! Hand-computed: A's earnings 0.07, B's 0.05 (of 0.12 closed to them;
|
||||
//! exact 0.06 each); retained earnings 0.01.
|
||||
use chrono::NaiveDate;
|
||||
use ledger_core::*;
|
||||
use ledger_store::Store;
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
fn d(m: u32, day: u32) -> NaiveDate {
|
||||
NaiveDate::from_ymd_opt(2026, m, day).unwrap()
|
||||
}
|
||||
fn usd(s: &str) -> Money {
|
||||
Money::new(s.parse::<Decimal>().unwrap(), Currency::USD)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let dir = std::path::PathBuf::from(std::env::args().nth(1).unwrap()).join("x_member_closes");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let mut s = Store::create(&dir, Currency::USD).unwrap();
|
||||
let cash = s.add_account("1000", "Cash", AccountType::Asset, None, false).unwrap();
|
||||
let cap_a = s.add_account("3000", "Capital A", AccountType::Equity, None, false).unwrap();
|
||||
let cap_b = s.add_account("3001", "Capital B", AccountType::Equity, None, false).unwrap();
|
||||
let dist_a = s.add_account("3100", "Distributions A", AccountType::Equity, None, false).unwrap();
|
||||
let rev = s.add_account("4000", "Sales", AccountType::Revenue, None, false).unwrap();
|
||||
let exp = s.add_account("6000", "Fees", AccountType::Expense, None, false).unwrap();
|
||||
let post = |s: &mut Store, date, dr, cr, amount: &str| {
|
||||
let e = s.create_entry(date, "x").unwrap();
|
||||
s.add_line(e, dr, Side::Debit, usd(amount)).unwrap();
|
||||
s.add_line(e, cr, Side::Credit, usd(amount)).unwrap();
|
||||
s.post_entry(e).unwrap();
|
||||
};
|
||||
post(&mut s, d(1, 2), cash, cap_a, "100.00");
|
||||
post(&mut s, d(1, 2), cash, cap_b, "100.00");
|
||||
post(&mut s, d(2, 20), dist_a, cash, "1.00");
|
||||
let members = vec![
|
||||
MemberClose { capital: cap_a, share: "0.5".parse().unwrap(), distributions: Some(dist_a) },
|
||||
MemberClose { capital: cap_b, share: "0.5".parse().unwrap(), distributions: None },
|
||||
];
|
||||
let months: [(u32, &str, &str); 11] = [
|
||||
(1, "0.01", "last"),
|
||||
(2, "-0.03", "last"),
|
||||
(3, "0.05", "last"),
|
||||
(4, "0.07", "per"),
|
||||
(5, "-0.01", "per"),
|
||||
(6, "0.03", "per"),
|
||||
(7, "0.01", "run"),
|
||||
(8, "0.01", "run"),
|
||||
(9, "0.01", "re"),
|
||||
(10, "0.03", "run"),
|
||||
(11, "-0.05", "run"),
|
||||
];
|
||||
for (m, net, rule) in months {
|
||||
match net.strip_prefix('-') {
|
||||
Some(loss) => post(&mut s, d(m, 10), exp, cash, loss),
|
||||
None => post(&mut s, d(m, 10), cash, rev, net),
|
||||
}
|
||||
let end = NaiveDate::from_ymd_opt(2026, m + 1, 1).unwrap().pred_opt().unwrap();
|
||||
let p = s.create_period(&format!("2026-{m:02}"), d(m, 1), end).unwrap();
|
||||
let to = match rule {
|
||||
"last" => CloseTo::MembersLastAbsorbs(members.clone()),
|
||||
"per" => CloseTo::MembersPerClose(members.clone()),
|
||||
"run" => CloseTo::Members(members.clone()),
|
||||
_ => CloseTo::RetainedEarnings { sweep: vec![] },
|
||||
};
|
||||
s.close_period_to(p, &to).unwrap();
|
||||
}
|
||||
let bal = |id| s.ledger().account_balance(id).unwrap();
|
||||
println!(
|
||||
"WROTE x_member_closes: A {} B {} dist {}",
|
||||
bal(cap_a),
|
||||
bal(cap_b),
|
||||
bal(dist_a)
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user