P3 review: the same figures agree across the pieces
A distribution declared to a payable and then paid is financing when paid, the declaration disclosed (ASC 230-10-45-15(a)); a disposal's depreciation lines count as depreciation in the cash flow and the tax-basis K-1; an as-of asset list says the status of that day. Seam tests tie the members' statement, K-1, party totals, cash flow, 1099s, depreciation and the CPA package together on three dates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
13 files changed
+2258
-24
No files matched your search
@@ -3124,3 +3124,108 @@ proptest! {
|
||||
prop_assert!(cf.noncash_disclosures.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
/// Rule 2 (P3 seam review): a distribution declared to a liability that
|
||||
/// holds only distributions (Dr capital / Cr 2400 Distributions Payable)
|
||||
/// and paid later is a distribution paid, financing (ASC
|
||||
/// 230-10-45-15(a)), under the payable's kind; the declaration is an
|
||||
/// exchange inside financing, disclosed (50-3, 50-4). Before the fix the
|
||||
/// payment was "Cash paid to suppliers and employees" and the declaration
|
||||
/// an "Other noncash items" line beside an increase in the payable.
|
||||
#[test]
|
||||
fn a_distribution_declared_then_paid_is_a_distribution_paid() {
|
||||
let mut b = funded();
|
||||
b.acct("2400", "Distributions payable", AccountType::Liability);
|
||||
let declared = b.post(
|
||||
d(2026, 1, 31),
|
||||
"Distribution declared",
|
||||
&[
|
||||
("3000", DR, "600.00"),
|
||||
("3001", DR, "400.00"),
|
||||
("2400", CR, "1000.00"),
|
||||
],
|
||||
);
|
||||
let paid = b.post(
|
||||
d(2026, 2, 10),
|
||||
"Distribution paid",
|
||||
&[("2400", DR, "1000.00"), ("1000", CR, "1000.00")],
|
||||
);
|
||||
let cf = feb(&b, &[], &[]);
|
||||
assert!(cf.operating.is_empty(), "{:?}", cf.operating);
|
||||
assert_eq!(
|
||||
kinds(&cf, CashFlowSection::Financing),
|
||||
[(
|
||||
s("Distributions to members — 2400 Distributions payable"),
|
||||
x("-1000.00")
|
||||
)]
|
||||
);
|
||||
assert_eq!(em(&cf.financing), [(paid, x("-1000.00"))]);
|
||||
assert!(recon(&cf).is_empty(), "{:?}", recon(&cf));
|
||||
// The declaration's month: no cash, the exchange disclosed, and no
|
||||
// operating item.
|
||||
let jan = b.cf(d(2026, 1, 1), d(2026, 1, 31), &["1000", "1010"], &[], &[]);
|
||||
foots(&jan);
|
||||
assert_eq!(disclosed(&jan), [(declared, x("1000.00"))]);
|
||||
assert!(
|
||||
recon(&jan).is_empty(),
|
||||
"{:?}",
|
||||
jan.operating_reconciliation.lines
|
||||
);
|
||||
// A liability credited for anything else as well is no distributions
|
||||
// payable: an expense accrued to it keeps it operating.
|
||||
b.post(
|
||||
d(2026, 2, 20),
|
||||
"Accrued supplies",
|
||||
&[("6000", DR, "50.00"), ("2400", CR, "50.00")],
|
||||
);
|
||||
let cf = feb(&b, &[], &[]);
|
||||
assert_eq!(
|
||||
cf.operating.iter().map(|l| l.entry).collect::<Vec<_>>(),
|
||||
[paid]
|
||||
);
|
||||
}
|
||||
|
||||
/// Rule 8 (P3 seam review): a disposal's lines on the asset's
|
||||
/// depreciation expense account (the days a month's run posted past the
|
||||
/// disposal, credited back) are depreciation on the reconciliation, not
|
||||
/// gain or loss, so its depreciation line is the P&L's depreciation
|
||||
/// expense.
|
||||
#[test]
|
||||
fn a_disposals_depreciation_credit_back_is_depreciation() {
|
||||
let mut b = funded();
|
||||
b.post(
|
||||
d(2026, 1, 10),
|
||||
"Buy equipment",
|
||||
&[("1500", DR, "1000.00"), ("1000", CR, "1000.00")],
|
||||
);
|
||||
b.post(
|
||||
d(2026, 2, 28),
|
||||
"Depreciation",
|
||||
&[("6100", DR, "30.00"), ("1510", CR, "30.00")],
|
||||
);
|
||||
// Sold Feb 14: 15 of the month's 30.00 taken back, a 10.00 loss on
|
||||
// the 985.00 book value.
|
||||
b.post(
|
||||
d(2026, 2, 14),
|
||||
"Sell equipment",
|
||||
&[
|
||||
("1000", DR, "975.00"),
|
||||
("1510", DR, "30.00"),
|
||||
("6900", DR, "10.00"),
|
||||
("1500", CR, "1000.00"),
|
||||
("6100", CR, "15.00"),
|
||||
],
|
||||
);
|
||||
let cf = feb(&b, &[], &[]);
|
||||
assert_eq!(
|
||||
recon(&cf),
|
||||
[
|
||||
(s("Depreciation"), x("15.00")),
|
||||
(
|
||||
s("(Gain) loss and other noncash changes in investing assets"),
|
||||
x("10.00")
|
||||
),
|
||||
]
|
||||
);
|
||||
assert_eq!(cf.cash_from_investing, x("975.00"));
|
||||
}
|
||||
@@ -9,3 +9,4 @@ cc 9f5e63191433240d6df895c196fdd89b56ce1a2a3d8d5225740c2f10dc720756 # shrinks to
|
||||
cc 5397d840688ed53dc033aceaf98b8db5df3a7a2616c4afe7f3f5fc2e0c8775f2 # shrinks to gens = [Gen { day: 140, mode: 2, picks: [0, 0, 2], x: 1, y: 1, classes: [0, 0, 0] }], (a, _m, e) = (0, 0, 140), designate = false
|
||||
cc abd84709578e7889c275a0019ae0e71366d77526ae93072777be614d7ffb0dd5 # shrinks to gens = [Gen { day: 108, mode: 1, picks: [1, 5, 3], x: 1, y: 1, classes: [0, 0, 0] }], (a, _m, e) = (0, 0, 108), k = 2
|
||||
cc 7481a0fb806d08439357ce4be66224389a7f76a7d91bf93ac320bb2b3c4d4891 # shrinks to gens = [Gen { day: 3, mode: 1, picks: [4, 5, 1], x: 1, y: 1, classes: [0, 0, 0] }], drafts = [Gen { day: 0, mode: 0, picks: [0, 0, 0], x: 1, y: 1, classes: [0, 0, 0] }], (a, _m, e) = (0, 0, 3)
|
||||
cc b594a44c6ad193ee922103d535d0a8e7b41ff808b37166c296491f27cd73d5a4 # shrinks to gens = [Gen { day: 0, mode: 2, picks: [3, 0, 2], x: 1, y: 1, classes: [0, 0, 0] }, Gen { day: 135, mode: 2, picks: [0, 3, 2], x: 1, y: 1, classes: [0, 0, 0] }], (a, _m, e) = (0, 0, 135), designate = false
|
||||
@@ -2517,12 +2517,33 @@ proptest! {
|
||||
let credits = |qs: &[i64]| qs.iter().map(|q| (-q).max(0)).sum::<i64>();
|
||||
let debits = |qs: &[i64]| qs.iter().map(|q| (*q).max(0)).sum::<i64>();
|
||||
let mut fin = 0i64;
|
||||
// Rule 2: a liability left operating that only entries debiting
|
||||
// nothing but equity credit, through the end, holds distributions
|
||||
// declared: financing, after the loan.
|
||||
let declared = |code: &str| {
|
||||
let mut by_declaration = false;
|
||||
for g in gens.iter().filter(|g| g.date() <= day(e)) {
|
||||
let legs = g.legs(1);
|
||||
if !legs.iter().any(|&(i, s, _)| s == CR && RCHART[i].0 == code) {
|
||||
continue;
|
||||
}
|
||||
if legs.iter().filter(|l| l.1 == DR).all(|&(i, _, _)| RCHART[i].2 == AccountType::Equity) {
|
||||
by_declaration = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
by_declaration
|
||||
};
|
||||
let (payables_declared, loan_declared) = (declared("2000"), !designate && declared("2500"));
|
||||
for g in gens.iter().filter(|g| g.date() >= day(a) && g.date() <= day(e)) {
|
||||
// Kinds: cash; operating balance sheet, revenue and expense;
|
||||
// capital A, capital B, the loan (financing when designated).
|
||||
// capital A, capital B, the loan (financing when designated),
|
||||
// then the liabilities holding distributions declared.
|
||||
let mut cash = 0i64;
|
||||
let mut op = [0i64; 2];
|
||||
let mut fi = [0i64; 3];
|
||||
let mut declared_fi = [0i64; 2];
|
||||
for (i, side, cents) in g.legs(1) {
|
||||
let m = if side == DR { cents } else { -cents };
|
||||
match RCHART[i].0 {
|
||||
@@ -2530,10 +2551,13 @@ proptest! {
|
||||
"3000" => fi[0] += m,
|
||||
"3001" => fi[1] += m,
|
||||
"2500" if designate => fi[2] += m,
|
||||
"2000" if payables_declared => declared_fi[0] += m,
|
||||
"2500" if loan_declared => declared_fi[1] += m,
|
||||
"4000" | "4100" | "6000" | "6100" => op[1] += m,
|
||||
_ => op[0] += m,
|
||||
}
|
||||
}
|
||||
let fi_all = fi.iter().sum::<i64>() + declared_fi.iter().sum::<i64>();
|
||||
// Rule 3: operating, then financing (no investing here).
|
||||
let take = |left: i64, m: i64| {
|
||||
if left > 0 && m < 0 {
|
||||
@@ -2545,10 +2569,12 @@ proptest! {
|
||||
}
|
||||
};
|
||||
let to_op = take(cash, op[0] + op[1]);
|
||||
let to_fin = take(cash - to_op, fi.iter().sum());
|
||||
let to_fin = take(cash - to_op, fi_all);
|
||||
fin += to_fin;
|
||||
if to_op > 0 { settle(&mut op, to_op, false) } else { settle(&mut op, -to_op, true) }
|
||||
if to_fin > 0 { settle(&mut fi, to_fin, false) } else { settle(&mut fi, -to_fin, true) }
|
||||
let mut kinds: Vec<i64> = fi.iter().chain(&declared_fi).copied().collect();
|
||||
if to_fin > 0 { settle(&mut kinds, to_fin, false) } else { settle(&mut kinds, -to_fin, true) }
|
||||
fi.copy_from_slice(&kinds[..3]);
|
||||
// Rule 11: the loan pays, then receives; then capital.
|
||||
if designate {
|
||||
let paid = credits(&fi[2..]).min(debits(&op));
|
||||
|
||||
Reference in new issue
Block a user