P3 review: asset bases and the tax-basis K-1

Schema 13 is as shipped and the asset columns are schema 14 (shipped
migrations pinned by hash); the tax-basis K-1 leaves net income open (only
the return has it) and takes a contributed prepaid plan at its book amount
as its section 723 basis; a disposal of contributed property names its
704(c) gain or loss; aliases compare by meaning; fixture amounts made up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-21 17:43:44 -06:00
1 parent 2ae93fb2d6
commit 6288ead747
24 files changed
+860 -127

No files matched your search

+92 -13
View File
@@ -26,7 +26,7 @@
use crate::text::{csv_escape, money};
use ledger_core::Ledger;
use ledger_core::tax_capital::{Open, TaxCapital, TaxLine};
use ledger_core::tax_capital::{Note, Open, TaxCapital, TaxLine};
use rust_decimal::Decimal;
/// The file's name in the package.
@@ -73,10 +73,24 @@ pub fn open_text(ledger: &Ledger, o: &Open) -> String {
Open::Disposal {
asset: id,
proceeds,
} => format!(
"the tax gain or loss on {}'s disposal (proceeds {}), from its adjusted tax basis",
asset(*id),
money(*proceeds)
built_in_gain_704c,
} => match built_in_gain_704c {
Some(g) if !g.is_zero() => format!(
"the tax gain or loss on {}'s disposal (proceeds {}), from its adjusted tax basis; it is contributed property with a built-in {} of {} at contribution, which the gain or loss must take account of, allocated to the contributing member(s) (IRC §704(c)(1)(A); Treas. Reg. §1.704-3)",
asset(*id),
money(*proceeds),
if *g < Decimal::ZERO { "loss" } else { "gain" },
money(g.abs())
),
_ => format!(
"the tax gain or loss on {}'s disposal (proceeds {}), from its adjusted tax basis",
asset(*id),
money(*proceeds)
),
},
Open::TaxNetIncome { book } => format!(
"the member's share of net income (loss) as figured for tax purposes, from the return ({} on the books; every book-tax difference, Schedule M-1, is the CPA's)",
money(*book)
),
Open::OtherChange { book } => format!(
"the tax treatment of a {} change in book capital the books can't explain",
@@ -85,6 +99,17 @@ pub fn open_text(ledger: &Ledger, o: &Open) -> String {
}
}
/// What a note says: a figure taken on a stated rule, for the CPA to
/// confirm.
pub fn note_text(n: &Note) -> String {
match n {
Note::PrepaidAtBook { entry, book } => format!(
"{} of it is a prepaid contract handed over in entry {entry}, taken at its book amount as its basis (IRC §723 carryover; the CPA to confirm)",
money(*book)
),
}
}
/// The worksheet (INVARIANTS 1-3). `names` are the members' names, in the
/// rows' order.
pub fn tax_basis_csv(ledger: &Ledger, names: &[String], tc: &TaxCapital) -> String {
@@ -101,13 +126,18 @@ pub fn tax_basis_csv(ledger: &Ledger, names: &[String], tc: &TaxCapital) -> Stri
};
for (row, name) in tc.rows.iter().zip(names) {
let line = |book: Decimal, tax: &TaxLine, note: &str| -> [String; 5] {
let note: String = std::iter::once(note.to_string())
.filter(|n| !n.is_empty())
.chain(tax.notes.iter().map(note_text))
.collect::<Vec<_>>()
.join("; ");
match tax.tax() {
Some(t) => [
money(book),
money(t),
money(book - t),
"tax basis".into(),
note.into(),
note,
],
None => {
let needs: Vec<String> =
@@ -118,9 +148,14 @@ pub fn tax_basis_csv(ledger: &Ledger, names: &[String], tc: &TaxCapital) -> Stri
String::new(),
"open: the CPA's".into(),
format!(
"known so far {}; needs {}",
"known so far {}; needs {}{}",
money(tax.known),
needs.join("; ")
needs.join("; "),
if note.is_empty() {
String::new()
} else {
format!("; {note}")
}
),
]
}
@@ -130,6 +165,7 @@ pub fn tax_basis_csv(ledger: &Ledger, names: &[String], tc: &TaxCapital) -> Stri
let cash = TaxLine {
known: row.contributions_cash,
open: Vec::new(),
notes: Vec::new(),
};
push(
name,
@@ -153,11 +189,7 @@ pub fn tax_basis_csv(ledger: &Ledger, names: &[String], tc: &TaxCapital) -> Stri
push(
name,
"Current year net income (loss)",
line(
b.net_income,
&row.net_income,
"the book figure: the books know of no difference from it (any other, Schedule M-1, is the CPA's)",
),
line(b.net_income, &row.net_income, ""),
);
push(
name,
@@ -231,3 +263,50 @@ pub fn tax_basis_csv(ledger: &Ledger, names: &[String], tc: &TaxCapital) -> Stri
}
w
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
fn d(s: &str) -> Decimal {
Decimal::from_str(s).unwrap()
}
/// A disposal of contributed property names its built-in gain or loss
/// at contribution and §704(c)(1)(A); a purchase's doesn't. A prepaid
/// taken at book says so, for the CPA to confirm.
#[test]
fn disposal_and_prepaid_texts_say_what_the_cpa_needs() {
let l = Ledger::new(ledger_core::Currency::USD);
let sold = |g: Option<&str>| {
open_text(
&l,
&Open::Disposal {
asset: 7,
proceeds: d("1100.00"),
built_in_gain_704c: g.map(d),
},
)
};
let gain = sold(Some("300.00"));
assert!(gain.contains("built-in gain of 300.00"), "{gain}");
assert!(gain.contains("IRC §704(c)(1)(A)"), "{gain}");
let loss = sold(Some("-300.00"));
assert!(loss.contains("built-in loss of 300.00"), "{loss}");
for none in [sold(None), sold(Some("0"))] {
assert!(!none.contains("§704(c)"), "{none}");
assert!(
none.contains("asset 7's disposal (proceeds 1100.00)"),
"{none}"
);
}
let n = note_text(&Note::PrepaidAtBook {
entry: 3,
book: d("20.00"),
});
assert!(n.contains("20.00") && n.contains("entry 3") && n.contains("the CPA to confirm"));
let i = open_text(&l, &Open::TaxNetIncome { book: d("-5.00") });
assert!(i.contains("from the return (-5.00 on the books"), "{i}");
}
}
+1 -1
View File
@@ -641,7 +641,7 @@ pub fn build_package<B: PackageBooks + ?Sized>(
}
);
k.push_str(if k1_tax_included {
"k1-tax-basis-capital.csv: each member's capital account on the tax basis, as Item L reports it (Form 1065 instructions: the tax-basis method), line by line beside the book figures: cash contributed, property at its adjusted basis (IRC §723), the share of income as figured for tax, distributions at tax basis, and the reconciliation from book to tax capital (the §704(c) built-in gain or loss of contributed property). A line the books can't know (tax depreciation, a basis the books don't hold) is left blank, marked open, and says what the books know so far and what is needed.\n"
"k1-tax-basis-capital.csv: each member's capital account on the tax basis, as Item L reports it (Form 1065 instructions: the tax-basis method), line by line beside the book figures: cash contributed, property at its adjusted basis (IRC §723; a prepaid contract handed over at its book amount, the CPA to confirm), distributions at tax basis, and the reconciliation from book to tax capital (the §704(c) built-in gain or loss of contributed property). A line the books can't know is left blank, marked open, and says what the books know so far and what is needed: the share of income as figured for tax (always the return's), so the ending too; tax depreciation; the tax gain on a disposal (with a contributed property's built-in gain or loss); a basis the books don't hold.\n"
} else {
"k1-tax-basis-capital-NOT-INCLUDED.txt: why the tax-basis capital worksheet isn't here.\n"
});
+3 -2
View File
@@ -2241,6 +2241,7 @@ fn the_k1_worksheet_reports_tax_basis_capital() {
"member,item,book,tax_basis,book_less_tax,status,note"
);
assert_eq!(lines.len(), 1 + 2 * 12, "{csv}");
let income_note = "the member's share of net income (loss) as figured for tax purposes, from the return (-385.00 on the books; every book-tax difference, Schedule M-1, is the CPA's)";
let depreciation_note = "tax depreciation of the fixed assets in service (MACRS, IRC §168), in place of the book depreciation posted (1370.00 in the span, 1370.00 of it on contributed property, whose tax depreciation each property's §704(c) method allocates, Treas. Reg. §1.704-3)";
for member in ["Tobin Example", "Mo Example"] {
for want in [
@@ -2250,14 +2251,14 @@ fn the_k1_worksheet_reports_tax_basis_capital() {
"{member},Capital contributed: property,1370.00,1384.20,-14.20,tax basis,at the property's adjusted basis (IRC §723); book: its fair market value"
),
format!(
"{member},Current year net income (loss),-385.00,,,open: the CPA's,\"known so far -385.00; needs {depreciation_note}\""
"{member},Current year net income (loss),-385.00,,,open: the CPA's,\"known so far -385.00; needs {income_note}; {depreciation_note}\""
),
format!("{member},Other increase (decrease),0.00,0.00,0.00,tax basis,"),
format!(
"{member},Withdrawals and distributions,300.00,300.00,0.00,tax basis,\"cash, plus the adjusted tax basis of property distributed\""
),
format!(
"{member},Ending capital,1685.00,,,open: the CPA's,\"known so far 1699.20; needs {depreciation_note}\""
"{member},Ending capital,1685.00,,,open: the CPA's,\"known so far 1699.20; needs {income_note}; {depreciation_note}\""
),
format!(
"{member},\"Reconciliation: beginning capital, book less tax\",,,0.00,reconciliation,"