Shared parties with exact shares, member capital statements, TINs for any party, K-1 worksheet
Entries may name several parties, each with an exact amount, a percent, or a share derived from the members' capital lines or [[company.members]] shares (entry.parties_set); shares always sum exactly. The 1099 worksheet counts a vendor's share. A member's party page is their partner capital statement, from the same ledger_core::k1 source as the CPA package. books.set_tin stores any party's TIN (members' SSNs for K-1s) in the vault (party.tin_set, last four in the books). The CPA package carries a K-1 member worksheet (k1-members.csv, with a members' total that ties to Schedule L) and the 1099 worksheet with full TINs, saved outside the books only. files_jointly_with notes spouses' two K-1s land on one 1040. Adversarial review fixes: reversed contributions no longer inflate K-1 contributions and distributions; a K-1 row never takes another party's TIN; TIN detection covers dot/slash/dash separators; refusals redact TINs; vendor tagging can't silently replace shared parties; reversals follow re-apportioning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
1 parent
ef00ccc242
commit
e1b2509c22
32 files changed
+7232
-375
No files matched your search
@@ -473,6 +473,30 @@ pub fn describe(b: &Books, line: &JournalLine) -> Option<Event> {
|
||||
};
|
||||
ev(icon::IDENTIFICATION_CARD, text, pid.map(Route::Party))
|
||||
}
|
||||
"party.tin_set" => {
|
||||
let pid = p.get("id").and_then(|v| v.as_u64());
|
||||
let name = party_name(b, pid);
|
||||
let text = match p.get("facts").filter(|f| !f.is_null()) {
|
||||
Some(f) => {
|
||||
let kind = match f.get("tin_type").and_then(|v| v.as_str()) {
|
||||
Some("ssn") => "SSN",
|
||||
Some("ein") => "EIN",
|
||||
Some("itin") => "ITIN",
|
||||
_ => "TIN",
|
||||
};
|
||||
let l4 = f.get("last4").and_then(|v| v.as_str()).unwrap_or("????");
|
||||
if f.get("purged").is_some_and(|v| !v.is_null()) {
|
||||
format!("Purged {name}'s {kind} (••••{l4}) from the vault")
|
||||
} else {
|
||||
format!(
|
||||
"Taxpayer ID for {name} recorded: {kind} ending {l4}, kept in the vault"
|
||||
)
|
||||
}
|
||||
}
|
||||
None => format!("Taxpayer ID facts for {name} cleared"),
|
||||
};
|
||||
ev(icon::IDENTIFICATION_CARD, text, pid.map(Route::Party))
|
||||
}
|
||||
"party.w4_set" => {
|
||||
let pid = p.get("id").and_then(|v| v.as_u64());
|
||||
let name = party_name(b, pid);
|
||||
@@ -491,6 +515,59 @@ pub fn describe(b: &Books, line: &JournalLine) -> Option<Event> {
|
||||
};
|
||||
ev(icon::IDENTIFICATION_BADGE, text, pid.map(Route::Party))
|
||||
}
|
||||
"entry.parties_set" => {
|
||||
let e = entry_id.unwrap_or(0);
|
||||
let named: Vec<(u64, String)> = p
|
||||
.get("parties")
|
||||
.and_then(|v| v.as_array())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|x| {
|
||||
let id = x.get("party_id").and_then(|v| v.as_u64())?;
|
||||
let share = match x.get("share") {
|
||||
Some(Value::Object(o)) => o
|
||||
.get("amount")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|a| {
|
||||
rust_decimal::Decimal::from_str_exact(a)
|
||||
.map(fmt::dollars)
|
||||
.unwrap_or_else(|_| a.to_string())
|
||||
})
|
||||
.or_else(|| {
|
||||
o.get("percent")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|pc| format!("{pc}%"))
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
let name = party_name(b, Some(id));
|
||||
Some((
|
||||
id,
|
||||
match share {
|
||||
Some(sh) => format!("{name} ({sh})"),
|
||||
None => name,
|
||||
},
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
Some(Event {
|
||||
icon: icon::ADDRESS_BOOK,
|
||||
text: if named.is_empty() {
|
||||
format!("Cleared the parties of #{e}")
|
||||
} else {
|
||||
format!(
|
||||
"Tagged #{e} with {}",
|
||||
named
|
||||
.iter()
|
||||
.map(|(_, n)| n.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" and ")
|
||||
)
|
||||
},
|
||||
route: entry_id.map(Route::Entry),
|
||||
also: named.first().map(|(id, _)| Route::Party(*id)),
|
||||
})
|
||||
}
|
||||
"entry.party_set" => {
|
||||
let pid = p.get("party_id").and_then(|v| v.as_u64());
|
||||
let e = entry_id.unwrap_or(0);
|
||||
|
||||
Reference in new issue
Block a user