P3: party totals by what moved the money, once (defects 14, 11, 24)
Each party's part of an entry is signed as its own account would show it, with a direction and its cash and in-kind amounts; a reversal is its original negated. Totals on a cash or all basis: for a member, all equals the members' equity statement and cash the K-1's cash. Money is cash, cards and the financing accounts, and a liability credit never pays its own creditor (creditor_of), so accruals and declared distributions count once. tin_last4 from the TIN or W-9 facts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
52 files changed
+7535
-560
No files matched your search
+103
-49
@@ -2026,7 +2026,7 @@ fn party_roles(p: &ledger_core::Party) -> Vec<String> {
|
||||
}
|
||||
|
||||
/// A party's facts for a tool's output.
|
||||
fn party_out(p: &ledger_core::Party) -> PartyOut {
|
||||
fn party_out(ledger: &Ledger, p: &ledger_core::Party) -> PartyOut {
|
||||
PartyOut {
|
||||
party_id: p.id,
|
||||
name: p.name.clone(),
|
||||
@@ -2059,6 +2059,11 @@ fn party_out(p: &ledger_core::Party) -> PartyOut {
|
||||
in_vault: t.vault_record.is_some(),
|
||||
purged: t.purged.clone(),
|
||||
}),
|
||||
creditor_of: p
|
||||
.creditor_of
|
||||
.iter()
|
||||
.filter_map(|a| ledger.chart().get(*a).map(|a| a.code.clone()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2122,34 +2127,45 @@ fn checked_sum(
|
||||
struct PartyActivity {
|
||||
/// The party's entries, drafts included.
|
||||
entries: std::collections::BTreeSet<u64>,
|
||||
/// Paid to it in the year.
|
||||
/// Paid to it in the year, on the basis asked.
|
||||
paid: Decimal,
|
||||
/// Received from it in the year.
|
||||
/// Received from it in the year, on the basis asked.
|
||||
received: Decimal,
|
||||
}
|
||||
|
||||
/// The party's entries and the year's money paid and received
|
||||
/// A party-totals `basis` from a tool: "cash" (the default) or "all"
|
||||
/// (rules.md B-35 item 4); any other word is refused.
|
||||
fn parse_basis(basis: Option<&str>) -> Result<ledger_core::parties::Basis, String> {
|
||||
match basis {
|
||||
None => Ok(ledger_core::parties::Basis::default()),
|
||||
Some(b) => ledger_core::parties::Basis::parse(b)
|
||||
.ok_or_else(|| format!("basis is \"cash\" or \"all\", not {b:?}")),
|
||||
}
|
||||
}
|
||||
|
||||
/// The party's entries and the year's paid and received on `basis`
|
||||
/// (`PartyTotalsOut`): ledger_core::parties::activity_with over the
|
||||
/// calendar year, with the `[books] cash_accounts` and the members (so an
|
||||
/// entry naming several parties counts this one's share).
|
||||
/// calendar year, with the money accounts of [`share_context`] and the
|
||||
/// members (so an entry naming several parties counts this one's part).
|
||||
fn party_activity(
|
||||
store: &Store,
|
||||
party: &ledger_core::Party,
|
||||
settings: &Settings,
|
||||
year: i32,
|
||||
basis: ledger_core::parties::Basis,
|
||||
) -> Result<PartyActivity, String> {
|
||||
let ledger = store.ledger();
|
||||
let cash = cash_ids(ledger, &settings.roles.cash);
|
||||
let span = (
|
||||
NaiveDate::from_ymd_opt(year, 1, 1).ok_or("year out of range")?,
|
||||
NaiveDate::from_ymd_opt(year, 12, 31).ok_or("year out of range")?,
|
||||
);
|
||||
let members = settings.company.party_members(ledger);
|
||||
let a = ledger_core::parties::activity_with(ledger, party.id, span, &cash, &members);
|
||||
let (money, members) = share_context(ledger, settings);
|
||||
let a = ledger_core::parties::activity_with(ledger, party.id, span, &money, &members);
|
||||
let (paid, received) = a.totals(basis);
|
||||
Ok(PartyActivity {
|
||||
entries: a.entries,
|
||||
paid: a.paid,
|
||||
received: a.received,
|
||||
paid,
|
||||
received,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2175,7 +2191,8 @@ fn cash_ids(ledger: &Ledger, codes: &[String]) -> Vec<AccountId> {
|
||||
}
|
||||
|
||||
/// What party shares resolve with ([`ledger_core::shares::resolve`]): the
|
||||
/// money accounts over the `[books] cash_accounts`, and the members.
|
||||
/// money accounts over the `[books] cash_accounts` and
|
||||
/// `financing_accounts` (with the cards), and the members.
|
||||
fn share_context(
|
||||
ledger: &Ledger,
|
||||
settings: &Settings,
|
||||
@@ -2184,7 +2201,11 @@ fn share_context(
|
||||
Vec<ledger_core::PartyMember>,
|
||||
) {
|
||||
(
|
||||
ledger_core::parties::money_accounts(ledger, &cash_ids(ledger, &settings.roles.cash)),
|
||||
ledger_core::parties::money_accounts(
|
||||
ledger,
|
||||
&cash_ids(ledger, &settings.roles.cash),
|
||||
&cash_ids(ledger, &settings.roles.financing),
|
||||
),
|
||||
settings.company.party_members(ledger),
|
||||
)
|
||||
}
|
||||
@@ -2252,7 +2273,7 @@ fn check_shares_resolve(
|
||||
return Ok(());
|
||||
}
|
||||
let (money, members) = share_context(ledger, settings);
|
||||
ledger_core::shares::resolve(entry, shares, &money, &members).map(|_| ())
|
||||
ledger_core::shares::resolve(ledger, entry_id, shares, &money, &members).map(|_| ())
|
||||
}
|
||||
|
||||
/// Fix a posted entry's derived split among several parties: journal the
|
||||
@@ -2289,17 +2310,19 @@ fn fix_derived_shares(store: &mut Store, settings: &Settings, entry_id: u64) ->
|
||||
return Ok(());
|
||||
}
|
||||
let (money, members) = share_context(ledger, settings);
|
||||
let Ok(parts) = ledger_core::shares::resolve(entry, shares, &money, &members) else {
|
||||
let Ok(resolved) = ledger_core::shares::resolve(ledger, entry_id, shares, &money, &members)
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
if parts.iter().any(|(_, v)| v.is_zero()) {
|
||||
if resolved.parts.iter().any(|p| p.amount.is_zero()) {
|
||||
return Ok(());
|
||||
}
|
||||
let fixed: Vec<ledger_core::PartyShare> = parts
|
||||
.into_iter()
|
||||
.map(|(party, v)| ledger_core::PartyShare {
|
||||
party,
|
||||
share: ledger_core::Share::Amount(v.abs()),
|
||||
let fixed: Vec<ledger_core::PartyShare> = resolved
|
||||
.parts
|
||||
.iter()
|
||||
.map(|p| ledger_core::PartyShare {
|
||||
party: p.party,
|
||||
share: ledger_core::Share::Amount(p.amount.abs()),
|
||||
})
|
||||
.collect();
|
||||
store
|
||||
@@ -2317,10 +2340,11 @@ fn entry_parties_out(
|
||||
let ledger = store.ledger();
|
||||
let shares = ledger.entry_parties(entry.id());
|
||||
let (accounts, members) = share_context(ledger, settings);
|
||||
let (resolved, error) = match ledger_core::shares::resolve(entry, shares, &accounts, &members) {
|
||||
Ok(r) => (Some(r), None),
|
||||
Err(e) => (None, Some(e)),
|
||||
};
|
||||
let (resolved, error) =
|
||||
match ledger_core::shares::resolve(ledger, entry.id(), shares, &accounts, &members) {
|
||||
Ok(r) => (Some(r), None),
|
||||
Err(e) => (None, Some(e)),
|
||||
};
|
||||
let out = shares
|
||||
.iter()
|
||||
.enumerate()
|
||||
@@ -2341,7 +2365,12 @@ fn entry_parties_out(
|
||||
share: kind.to_string(),
|
||||
amount,
|
||||
percent,
|
||||
resolved: resolved.as_ref().map(|r| money(r[i].1)),
|
||||
resolved: resolved.as_ref().map(|r| money(r.parts[i].amount)),
|
||||
direction: resolved
|
||||
.as_ref()
|
||||
.map(|r| r.target.direction.key().to_string()),
|
||||
cash: resolved.as_ref().map(|r| money(r.parts[i].cash)),
|
||||
in_kind: resolved.as_ref().map(|r| money(r.parts[i].in_kind)),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@@ -6906,13 +6935,13 @@ impl BooksServer {
|
||||
.add_party(&p.name, &roles)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let party = store.ledger().get_party(id).expect("just added");
|
||||
Ok(Json(party_out(party)))
|
||||
Ok(Json(party_out(store.ledger(), party)))
|
||||
}
|
||||
|
||||
#[tool(
|
||||
name = "books.update_party",
|
||||
title = "Update a party",
|
||||
description = "Change a party's facts: name, display_name, website, address, notes, archived. Give only the fields to change; an empty string clears display_name, website, address, or notes. Every field is checked before any is written, so a bad one (a blank or taken name, a full TIN, over 500 characters) changes nothing; each is journaled as its own party.updated. Archive a party you no longer deal with (its history stays; it then goes by id). Returns the party.",
|
||||
description = "Change a party's facts: name, display_name, website, address, notes, archived, creditor_of. Give only the fields to change; \"\" clears display_name, website, address or notes. creditor_of replaces the list of liabilities the company owes the party itself (its card, its loan, what the members advanced): a credit to one never counts as paying it. Every field is checked before any is written, so a bad one (a blank or taken name, a full TIN, over 500 characters, not a liability) changes nothing; each is journaled as party.updated or party.creditor_set. Archive a party you no longer deal with (its history stays; it then goes by id). Returns the party.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = false,
|
||||
@@ -6946,24 +6975,46 @@ impl BooksServer {
|
||||
if let Some(a) = p.archived {
|
||||
fields.push(F::Archived(a));
|
||||
}
|
||||
if fields.is_empty() {
|
||||
// creditor_of replaces the list: each account added or dropped is
|
||||
// one change.
|
||||
let mut creditor: Vec<(AccountId, bool)> = Vec::new();
|
||||
if let Some(codes) = &p.creditor_of {
|
||||
let want: std::collections::BTreeSet<AccountId> = codes
|
||||
.iter()
|
||||
.map(|c| resolve_account(&store, c.trim()))
|
||||
.collect::<Result<_, _>>()?;
|
||||
let has = &store.ledger().get_party(id).expect("resolved").creditor_of;
|
||||
creditor.extend(want.difference(has).map(|a| (*a, true)));
|
||||
creditor.extend(has.difference(&want).map(|a| (*a, false)));
|
||||
}
|
||||
if fields.is_empty() && p.creditor_of.is_none() {
|
||||
return Err(
|
||||
"give at least one field to change: name, display_name, website, address, notes, archived"
|
||||
"give at least one field to change: name, display_name, website, address, notes, archived, creditor_of"
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
// Every field, in order, before any is written (INVARIANT 2).
|
||||
// Every change, in order, before any is written (INVARIANT 2).
|
||||
let mut rehearsal = store.ledger().clone();
|
||||
for f in &fields {
|
||||
rehearsal
|
||||
.update_party(id, f.clone())
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
for (a, on) in &creditor {
|
||||
rehearsal
|
||||
.set_party_creditor(id, *a, *on)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
for f in fields {
|
||||
store.update_party(id, f).map_err(|e| e.to_string())?;
|
||||
}
|
||||
for (a, on) in creditor {
|
||||
store
|
||||
.set_party_creditor(id, a, on)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
let party = store.ledger().get_party(id).expect("resolved");
|
||||
Ok(Json(party_out(party)))
|
||||
Ok(Json(party_out(store.ledger(), party)))
|
||||
}
|
||||
|
||||
#[tool(
|
||||
@@ -7030,7 +7081,7 @@ impl BooksServer {
|
||||
store.set_party_role(id, r, on).map_err(|e| e.to_string())?;
|
||||
}
|
||||
let party = store.ledger().get_party(id).expect("resolved");
|
||||
Ok(Json(party_out(party)))
|
||||
Ok(Json(party_out(store.ledger(), party)))
|
||||
}
|
||||
|
||||
/// ```text
|
||||
@@ -7272,7 +7323,7 @@ impl BooksServer {
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
let party = store.ledger().get_party(id).expect("resolved");
|
||||
Ok(Json(party_out(party)))
|
||||
Ok(Json(party_out(store.ledger(), party)))
|
||||
}
|
||||
|
||||
/// ```text
|
||||
@@ -7359,7 +7410,7 @@ impl BooksServer {
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
let party = store.ledger().get_party(id).expect("resolved");
|
||||
Ok(Json(party_out(party)))
|
||||
Ok(Json(party_out(store.ledger(), party)))
|
||||
}
|
||||
|
||||
/// ```text
|
||||
@@ -7494,11 +7545,7 @@ impl BooksServer {
|
||||
name: party.name.clone(),
|
||||
tin_in_vault: held_tin,
|
||||
w9_file_in_vault: held_w9,
|
||||
tin_last4: party
|
||||
.w9
|
||||
.as_ref()
|
||||
.map(|f| f.tin_last4.clone())
|
||||
.or_else(|| party.tin.as_ref().map(|t| t.last4.clone())),
|
||||
tin_last4: party.tin_last4().map(str::to_string),
|
||||
journaled: false,
|
||||
note: format!(
|
||||
"Confirming deletes {}{}; backups made before keep their copies. Make sure the CPA has what they need first.",
|
||||
@@ -7578,14 +7625,14 @@ impl BooksServer {
|
||||
let id = resolve_party(&store, &p.party)?;
|
||||
store.set_party_w4(id, facts).map_err(|e| e.to_string())?;
|
||||
let party = store.ledger().get_party(id).expect("resolved");
|
||||
Ok(Json(party_out(party)))
|
||||
Ok(Json(party_out(store.ledger(), party)))
|
||||
}
|
||||
|
||||
#[tool(
|
||||
name = "books.parties",
|
||||
title = "List parties",
|
||||
description = described(
|
||||
"The party directory, by id: each party's name, label, roles (vendor and customer included), vendor and customer record ids, whether W-9 facts (with the TIN's last four) and W-4 facts are on file, archived, and what was paid to and received from it in the year (defined as in books.party). Filter by role; archived parties only with include_archived.",
|
||||
"The party directory, by id: each party's name, label, roles (vendor and customer included), vendor and customer record ids, whether W-9 and W-4 facts are on file, the TIN's last four when the books know it (TIN or W-9 facts), archived, and what was paid to and received from it in the year on the basis asked: cash by default (money), or all (adds property contributed or distributed in kind); defined as in books.party, the basis echoed. Filter by role; archived parties only with include_archived.",
|
||||
ledger_core::calendar::PeriodKind::CalendarYear
|
||||
),
|
||||
annotations(read_only_hint = true, open_world_hint = false)
|
||||
@@ -7601,6 +7648,7 @@ impl BooksServer {
|
||||
.map(|r| parse_roles(&[r.to_string()]).map(|v| v[0]))
|
||||
.transpose()?;
|
||||
let year = p.year.unwrap_or_else(|| self.periods().calendar_year());
|
||||
let basis = parse_basis(p.basis.as_deref())?;
|
||||
let settings = self.settings();
|
||||
let store = self.lock()?;
|
||||
let mut parties = Vec::new();
|
||||
@@ -7611,7 +7659,7 @@ impl BooksServer {
|
||||
if role.is_some_and(|r| !party.all_roles().contains(&r)) {
|
||||
continue;
|
||||
}
|
||||
let act = party_activity(&store, party, &settings, year)?;
|
||||
let act = party_activity(&store, party, &settings, year, basis)?;
|
||||
parties.push(PartySummaryOut {
|
||||
party_id: party.id,
|
||||
name: party.name.clone(),
|
||||
@@ -7620,21 +7668,25 @@ impl BooksServer {
|
||||
vendor_id: party.vendor,
|
||||
customer_id: party.customer,
|
||||
w9_on_file: party.w9.is_some(),
|
||||
tin_last4: party.w9.as_ref().map(|w| w.tin_last4.clone()),
|
||||
tin_last4: party.tin_last4().map(str::to_string),
|
||||
w4_on_file: party.w4.is_some(),
|
||||
archived: party.archived,
|
||||
paid: money(act.paid),
|
||||
received: money(act.received),
|
||||
});
|
||||
}
|
||||
Ok(Json(ListPartiesOut { year, parties }))
|
||||
Ok(Json(ListPartiesOut {
|
||||
year,
|
||||
basis: basis.key().to_string(),
|
||||
parties,
|
||||
}))
|
||||
}
|
||||
|
||||
#[tool(
|
||||
name = "books.party",
|
||||
title = "Get a party",
|
||||
description = described(
|
||||
"One party, by id or name: its facts and roles, vendor and customer record ids, the year's totals (paid to it, received from it, invoiced, billed, the 1099 amount for a vendor, payroll gross and net), what it is owed and owes now (open bills and invoices), every entry it is on (tagged with it or its vendor record, its invoices and their payments, and their reversals), its invoices and bills, and the payroll lines naming it. Totals come from the journal: the output's schema says exactly how.",
|
||||
"One party, by id or name: its facts and roles, vendor and customer record ids, the year's totals (paid to it and received from it on the basis asked, cash by default or all with property in kind, the basis echoed; invoiced, billed, the 1099 amount for a vendor, payroll gross and net), what it is owed and owes now (open bills and invoices), every entry it is on (tagged with it or its vendor record, its invoices and their payments, and their reversals), its invoices and bills, and the payroll lines naming it. Totals come from the journal: the output's schema says exactly how.",
|
||||
ledger_core::calendar::PeriodKind::CalendarYear
|
||||
),
|
||||
annotations(read_only_hint = true, open_world_hint = false)
|
||||
@@ -7642,12 +7694,13 @@ impl BooksServer {
|
||||
async fn party(&self, params: Parameters<PartyParams>) -> Result<Json<PartyDetailOut>, String> {
|
||||
let p = params.0;
|
||||
let year = p.year.unwrap_or_else(|| self.periods().calendar_year());
|
||||
let basis = parse_basis(p.basis.as_deref())?;
|
||||
let settings = self.settings();
|
||||
let store = self.lock()?;
|
||||
let ledger = store.ledger();
|
||||
let id = resolve_party(&store, &p.party)?;
|
||||
let party = ledger.get_party(id).expect("resolved");
|
||||
let act = party_activity(&store, party, &settings, year)?;
|
||||
let act = party_activity(&store, party, &settings, year, basis)?;
|
||||
let in_year = |d: NaiveDate| chrono::Datelike::year(&d) == year;
|
||||
let mut entries: Vec<PartyEntryOut> = act
|
||||
.entries
|
||||
@@ -7704,6 +7757,7 @@ impl BooksServer {
|
||||
}
|
||||
}
|
||||
let totals = PartyTotalsOut {
|
||||
basis: basis.key().to_string(),
|
||||
paid: money(act.paid),
|
||||
received: money(act.received),
|
||||
invoiced: money(checked_sum(
|
||||
@@ -7734,7 +7788,7 @@ impl BooksServer {
|
||||
net_pay: money(net),
|
||||
};
|
||||
Ok(Json(PartyDetailOut {
|
||||
party: party_out(party),
|
||||
party: party_out(ledger, party),
|
||||
year,
|
||||
totals,
|
||||
entries,
|
||||
@@ -7769,7 +7823,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.set_entry_party",
|
||||
title = "Tag an entry's parties",
|
||||
description = "Tag an entry (draft or posted) with the party it was with (id or name), or pass party null (or omit it) to clear the tag. A party with a vendor record also tags the entry's vendor, so a payment to a contractor or landlord counts in books.vendor_1099; a party without one, or clearing, clears the vendor tag. For an entry with several counterparties pass parties instead: [{party, amount?|percent?}], each share an exact amount (cents), a percent (summing to 100; leftover cents to the first party), or neither (derived: a sole party gets all; members named together get what their capital or distributions lines say, else their [[company.members]] share; once the entry is posted, a derived split among several parties is recorded as the exact amounts it resolves to, so a later change to [[company.members]] never re-splits posted history). Name both members on a joint member transaction (a reimbursement on 2100, a joint contribution) and let the shares derive; give amounts when a split is uneven. Refused: a party twice, mixed kinds of share, two parties with vendor records, and, once the entry has balanced lines, shares that don't add up to what it moved. Metadata only: amounts never change. An entry and its reversal carry the same parties: tagging either tags both, so each party's part nets to zero. Journaled as entry.party_set (one party) or entry.parties_set (parties). Returns the entry (as books.get_entry), each party's share resolved.",
|
||||
description = "Tag an entry (draft or posted) with the party it was with (id or name), or pass party null (or omit it) to clear the tag. A party with a vendor record also tags the entry's vendor, so a payment to a contractor or landlord counts in books.vendor_1099; a party without one, or clearing, clears the vendor tag. For an entry with several counterparties pass parties instead: [{party, amount?|percent?}], each share an exact amount (cents), a percent (summing to 100; leftover cents to the first party), or neither (derived: a sole party gets all; members named together get what their capital or distributions lines say, else their [[company.members]] share; once the entry is posted, a derived split among several parties is recorded as the exact amounts it resolves to, so a later change to [[company.members]] never re-splits posted history). Name both members on a joint member transaction (a reimbursement on 2100, a joint contribution) and let the shares derive; give amounts when a split is uneven. Refused: a party twice, mixed kinds of share, two parties with vendor records, and, once the entry has balanced lines, shares that don't add up to what it moved. Metadata only: amounts never change. An entry and its reversal carry the same parties: tagging either tags both, so each party's part nets to zero. Journaled as entry.party_set (one party) or entry.parties_set (parties). Returns the entry (as books.get_entry), each party's share resolved, with its direction and what of it was money or property in kind.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = false,
|
||||
|
||||
+103
-27
@@ -1256,11 +1256,33 @@ pub struct EntryPartyOut {
|
||||
pub amount: Option<String>,
|
||||
/// The percent given, for a percent share, decimal string.
|
||||
pub percent: Option<String>,
|
||||
/// Its exact part of what the entry moved (positive: paid to it;
|
||||
/// negative: received from it; for an entry moving no money, its part
|
||||
/// of the total), decimal string. Null when the shares don't resolve
|
||||
/// (see `parties_error`).
|
||||
/// Its exact part of what the entry moved, signed as a line on the
|
||||
/// party's own account would be (debit-positive): positive, the
|
||||
/// company paid it or gave it value; negative, received from it. With
|
||||
/// direction "neither", its part of the entry's movement of its own
|
||||
/// accounts (a bill is negative, owed to it; an invoice positive), else
|
||||
/// of the entry's total. A reversal's is exactly its original's
|
||||
/// negated. A member's entry is what moved their capital (a property
|
||||
/// contribution is negative, received, in kind). Decimal string. Null
|
||||
/// when the shares don't resolve (see `parties_error`).
|
||||
pub resolved: Option<String>,
|
||||
/// "paid", "received" or "neither": neither is a promise not yet
|
||||
/// settled (a bill, an invoice, a fee charged on its own card, a
|
||||
/// distribution declared and not yet paid) or an entry that moved
|
||||
/// nothing with anyone (a close), counted in no party total. Null
|
||||
/// exactly when `resolved` is.
|
||||
pub direction: Option<String>,
|
||||
/// What of `resolved` moved as money (cash, a card, the members'
|
||||
/// financing, a loan; on a member's contribution, what the K-1
|
||||
/// worksheet counts as cash; of a distribution, the money it paid):
|
||||
/// what the party's totals count on basis "cash". "0.00" for direction
|
||||
/// neither. Decimal string, null when `resolved` is.
|
||||
pub cash: Option<String>,
|
||||
/// What of `resolved` moved as property (contributed or distributed in
|
||||
/// kind): basis "all" adds it. resolved − cash − in_kind is a promise
|
||||
/// not yet paid, counted on neither basis. Decimal string, null when
|
||||
/// `resolved` is.
|
||||
pub in_kind: Option<String>,
|
||||
}
|
||||
|
||||
/// Result of `books.draft_entry`.
|
||||
@@ -3000,6 +3022,14 @@ pub struct UpdatePartyParams {
|
||||
/// resolves by name, and its name is free for another party). False:
|
||||
/// dealt with again (refused while another party has its name).
|
||||
pub archived: Option<bool>,
|
||||
/// The liability accounts (codes or ids) the company owes this party
|
||||
/// itself on: the card it issued, the loan it made, what the members
|
||||
/// advanced (name each member). Replaces its list; [] clears it. A
|
||||
/// credit to one never counts as paying the party (a fee on its card,
|
||||
/// interest on its loan, a distribution still owed: a promise, counted
|
||||
/// when paid). Liabilities only, not placeholders, payables or payroll
|
||||
/// withholdings.
|
||||
pub creditor_of: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// Parameters for `books.set_party_roles`. Give at least one role to add
|
||||
@@ -3243,6 +3273,10 @@ pub struct ListPartiesParams {
|
||||
/// so year to date).
|
||||
#[schemars(description = YEAR_DOC)]
|
||||
pub year: Option<i32>,
|
||||
/// What `paid` and `received` count: "cash" (the default: money) or
|
||||
/// "all" (money and property contributed or distributed in kind). Not
|
||||
/// the books' accounting method. See `books.party` totals.
|
||||
pub basis: Option<String>,
|
||||
}
|
||||
|
||||
/// Parameters for `books.party`.
|
||||
@@ -3254,6 +3288,10 @@ pub struct PartyParams {
|
||||
/// date).
|
||||
#[schemars(description = YEAR_DOC)]
|
||||
pub year: Option<i32>,
|
||||
/// What `paid` and `received` count: "cash" (the default: money) or
|
||||
/// "all" (money and property contributed or distributed in kind). Not
|
||||
/// the books' accounting method. See the totals' schema.
|
||||
pub basis: Option<String>,
|
||||
}
|
||||
|
||||
/// Parameters for `books.set_entry_party`.
|
||||
@@ -3345,6 +3383,10 @@ pub struct PartyOut {
|
||||
pub w4: Option<W4Out>,
|
||||
/// Taxpayer-ID facts (any role), if on file: type and last four.
|
||||
pub tin: Option<TinOut>,
|
||||
/// The liability accounts the company owes this party itself on (its
|
||||
/// card, its loan, what the members advanced), by code: a credit to
|
||||
/// one never counts as paying it.
|
||||
pub creditor_of: Vec<String>,
|
||||
}
|
||||
|
||||
/// One party in `books.parties`.
|
||||
@@ -3364,16 +3406,19 @@ pub struct PartySummaryOut {
|
||||
pub customer_id: Option<u64>,
|
||||
/// W-9 facts are on file.
|
||||
pub w9_on_file: bool,
|
||||
/// The W-9's TIN last four digits, when on file.
|
||||
/// The TIN's last four digits when the books know it: from its TIN
|
||||
/// facts (`books.set_tin`; a member's SSN for the K-1) or its W-9
|
||||
/// facts, which never disagree. Null when neither is on file. Whether
|
||||
/// the vault still holds the full number: `books.party` `tin.in_vault`.
|
||||
pub tin_last4: Option<String>,
|
||||
/// W-4 facts are on file.
|
||||
pub w4_on_file: bool,
|
||||
/// No longer dealt with.
|
||||
pub archived: bool,
|
||||
/// Paid to the party in the year, decimal string (as `books.party`
|
||||
/// totals).
|
||||
/// Paid to the party in the year on `basis`, decimal string (as
|
||||
/// `books.party` totals).
|
||||
pub paid: String,
|
||||
/// Received from the party in the year, decimal string (as
|
||||
/// Received from the party in the year on `basis`, decimal string (as
|
||||
/// `books.party` totals).
|
||||
pub received: String,
|
||||
}
|
||||
@@ -3383,6 +3428,8 @@ pub struct PartySummaryOut {
|
||||
pub struct ListPartiesOut {
|
||||
/// The year `paid` and `received` cover.
|
||||
pub year: i32,
|
||||
/// What `paid` and `received` count: "cash" or "all".
|
||||
pub basis: String,
|
||||
/// Parties, by id.
|
||||
pub parties: Vec<PartySummaryOut>,
|
||||
}
|
||||
@@ -3394,30 +3441,59 @@ pub struct ListPartiesOut {
|
||||
/// the payments received on them, and reversals of any of these. Only
|
||||
/// posted entries dated in the calendar year count.
|
||||
///
|
||||
/// Each entry's money is `ledger_core::parties::entry_flow`: what left or
|
||||
/// reached the `[books] cash_accounts` (every asset account but
|
||||
/// receivables when none are set), plus what the company paid through a
|
||||
/// liability (a card, a member paying for it, a loan; not payables or
|
||||
/// payroll withholdings), with cash that pays a liability down counting
|
||||
/// once, as paid to that liability's creditor (a reimbursement to the
|
||||
/// members, a card payment to the issuer), a liability paid down by
|
||||
/// another counting the same way (a member paying the card from their own
|
||||
/// funds, a loan paying it off), and cash raised by a liability counting
|
||||
/// as received from the lender. Positive is paid to the party, negative
|
||||
/// received from it; a reversal counts against its root original's side
|
||||
/// (reversing the reversal counts on that side again). A transfer between the company's own cash accounts nets to zero.
|
||||
/// A bill is not paid until its payment is.
|
||||
/// An entry naming several parties counts each one's share of that money
|
||||
/// (its amount, its percent, or its derived share: see
|
||||
/// `books.set_entry_party` `parties`); an entry whose shares don't resolve
|
||||
/// counts for none of them.
|
||||
/// `paid` and `received` add up each entry's part for the party, as
|
||||
/// `books.get_entry` shows it (`resolved`, `direction`, `cash`,
|
||||
/// `in_kind`): on `basis` "cash" (the default) what moved as money, on
|
||||
/// "all" that plus property contributed or distributed in kind. A
|
||||
/// reversal counts on its original's side and nets it to zero. (Not the
|
||||
/// books' accounting method.)
|
||||
///
|
||||
/// Money is what left or reached the `[books] cash_accounts` (every asset
|
||||
/// account but receivables when none are set), the cards (liabilities
|
||||
/// whose names say "card", not "gift card"), and the `[books]
|
||||
/// financing_accounts` (what the members
|
||||
/// advanced, loans): a card charge or a member paying for the company is
|
||||
/// paid to the payee then; cash that pays one of these down is paid to
|
||||
/// its creditor (a reimbursement to the members, a card payment to the
|
||||
/// issuer); cash it raises is received from the lender. Any other
|
||||
/// liability is a promise (a bill, an accrual, deferred revenue): counted
|
||||
/// when it is settled, never also when it is booked. A credit to what the
|
||||
/// company owes a party itself (`books.party` `creditor_of`: a fee on its
|
||||
/// own card, interest on its own loan, a distribution still owed to a
|
||||
/// member) never counts as paying it.
|
||||
///
|
||||
/// An entry naming only members that moves equity counts what moved their
|
||||
/// capital: a contribution received, the property in it in kind (asset
|
||||
/// accounts that are neither cash nor receivables) and the rest cash, as
|
||||
/// on the K-1 worksheet; a distribution paid, the money it paid as cash
|
||||
/// and the property it gave in kind, the rest (declared to distributions
|
||||
/// payable, or credited to what the members advanced once they are named
|
||||
/// its creditors) still owed, counted when it is paid. So for a member,
|
||||
/// basis "all" over those entries and those payments is the statement of
|
||||
/// members' equity's contributions and distributions, and basis "cash"
|
||||
/// its cash contributions as the K-1 worksheet has them, but for a
|
||||
/// distribution declared and not yet paid, which is in the statement only. Known limits (record them apart): an entry
|
||||
/// that both repays a member and distributes to them counts the
|
||||
/// distribution only; a member's money that goes through the members'
|
||||
/// financing account both ways under their own name (lent, then converted
|
||||
/// to capital) counts both times, as does a distribution declared through
|
||||
/// it before the members are named its creditors; an expense a member paid
|
||||
/// booked straight to capital and tagged with the vendor isn't paid to the
|
||||
/// vendor; capital moved between members counts for neither.
|
||||
///
|
||||
/// An entry naming several parties counts each one's share (its amount,
|
||||
/// its percent, or its derived share: see `books.set_entry_party`
|
||||
/// `parties`); an entry whose shares don't resolve counts for none of
|
||||
/// them. A transfer between the company's own cash accounts nets to zero.
|
||||
/// (`ledger_core::parties::activity_with`, with the `[[company.members]]`,
|
||||
/// computes it; the viewer shows the same numbers.)
|
||||
#[derive(Debug, Clone, Serialize, JsonSchema)]
|
||||
pub struct PartyTotalsOut {
|
||||
/// Paid to the party, decimal string.
|
||||
/// What `paid` and `received` count: "cash" or "all".
|
||||
pub basis: String,
|
||||
/// Paid to the party on `basis`, decimal string.
|
||||
pub paid: String,
|
||||
/// Received from the party, decimal string.
|
||||
/// Received from the party on `basis`, decimal string.
|
||||
pub received: String,
|
||||
/// Its customer record's invoices dated in the year, total, decimal
|
||||
/// string.
|
||||
|
||||
Reference in new issue
Block a user