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
84 lines
3.7 KiB
Rust
84 lines
3.7 KiB
Rust
//! 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)
|
|
);
|
|
}
|