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:
1 parent
06347159f0
commit
52555262a8
9 files changed
+1415
-20
No files matched your search
@@ -79,9 +79,10 @@ listed first; rules.md B-13). An example that closes to the members
|
||||
declares `largest-remainder`, the rule a members close splits by.
|
||||
|
||||
Required everywhere: the top-level keys, `[setup]` with its four keys and
|
||||
its accounts, the events, and at least one `[[expect]]` with values: a
|
||||
figure query's are exact decimal strings (`"2340.00"`, `"-12.50"`; never a
|
||||
float), a format query's are text (below). Amounts on lines are positive;
|
||||
its accounts, the events (`events = []`, before `[setup]`, when nothing is
|
||||
posted), and at least one `[[expect]]` with values: a figure query's are
|
||||
exact decimal strings (`"2340.00"`, `"-12.50"`; never a float), a format
|
||||
query's are text (below). Amounts on lines are positive;
|
||||
the side gives the direction.
|
||||
|
||||
### Optional keys, for the rules that need them
|
||||
@@ -99,26 +100,55 @@ the side gives the direction.
|
||||
- `today` (in `[setup]`): the day the books are asked on, `YYYY-MM-DD`,
|
||||
in the company's time zone: what an omitted date means (B-30). A query
|
||||
that omits a date needs it.
|
||||
- `cash_accounts`, `financing_accounts`, `investing_accounts`: arrays of
|
||||
account codes, the statement of cash flows' account roles (ASC
|
||||
230-10-45-10 to 45-15). The cash and financing accounts are also what a
|
||||
payee is paid from, for `vendor_1099_paid` (a payment card is neither:
|
||||
its charges go on Form 1099-K; rules.md B-19), and the cash accounts
|
||||
are an IIF file's bank accounts (B-34).
|
||||
- `cash_accounts`, `financing_accounts`, `investing_accounts` (in
|
||||
`[setup]`): arrays of account codes, the statement of cash flows'
|
||||
account roles (ASC 230-10-45-10 to 45-15). The cash and financing
|
||||
accounts are also what a payee is paid from, for `vendor_1099_paid` (a
|
||||
payment card is neither: its charges go on Form 1099-K; rules.md B-19);
|
||||
with the cards, what party money is, for
|
||||
`party_part` and `party_totals` (B-35 item 5: no `cash_accounts`, every
|
||||
asset account but receivables); the cash accounts are what the K-1
|
||||
worksheet reads as money (`k1`) and an IIF file's bank accounts (B-34).
|
||||
- `[[setup.fixed_assets]]` `{ cost_account, accumulated_account,
|
||||
expense_account }`: accounts that hold property and equipment (their
|
||||
cost and accumulated depreciation are investing; ASC 230-10-45-12(c),
|
||||
45-13(c)).
|
||||
- `[[setup.members]]` `{ name, capital_account, share? }`: the members of
|
||||
a multi-member LLC. `share` is the member's share of income, an exact
|
||||
decimal fraction (`"0.5"`, `"0.3333"`); every member has one or none
|
||||
does, and the shares sum to 1. A close to the members and
|
||||
`members_equity` need them.
|
||||
- `[[setup.parties]]` `{ name, roles, creditor_of?, tin_last4?,
|
||||
w9_tin_last4?, tin_purged? }`: the company's directory (B-26). `roles`
|
||||
are role keys (`member`, `vendor`, `customer`, `contractor`,
|
||||
`landlord`, `employee`, `financial_institution`, `payout_platform`,
|
||||
`tax_agency`, `government_agency`); `vendor` or `customer` (one at
|
||||
most) gives the party its record. `creditor_of`: the liabilities, by
|
||||
code, the company owes the party itself (a card's issuer, a lender,
|
||||
each member for what the members advanced: B-35 item 6). `tin_last4`:
|
||||
its TIN facts' last four (an SSN's); `w9_tin_last4`: its W-9's (a 1099
|
||||
role's); `tin_purged = true`: the vault's copy purged, the facts kept.
|
||||
Never a full TIN. Each name once.
|
||||
- `[[setup.members]]` `{ name, capital_account, share?,
|
||||
distributions_account? }`: the members of a multi-member LLC. `share`
|
||||
is the member's share of income, an exact decimal fraction (`"0.5"`,
|
||||
`"0.3333"`); every member has one or none does, and the shares sum to
|
||||
1. A close to the members, `members_equity` and `k1` need them.
|
||||
`distributions_account`: the member's distributions account (B-13). A
|
||||
member whose `name` is a party's in `setup.parties` with the `member`
|
||||
role is that party, as the company's settings name them: an entry's
|
||||
parties' shares resolve with those members (B-35), and so does the
|
||||
1099 worksheet.
|
||||
- `[[setup.payees]]` `{ name, form }`: a payee marked for an information
|
||||
return, `form` `"1099-NEC"` or `"1099-MISC"`; each name once.
|
||||
return from the beginning, `form` `"1099-NEC"` or `"1099-MISC"`; each
|
||||
name once. A payee in `setup.parties` with the `vendor` role is that
|
||||
party's vendor record; any other is a vendor record of its own.
|
||||
- `payee` on an event: the payee (by name, one of `setup.payees`) the
|
||||
entry paid. Not on a reversal, which takes its original's, nor on a
|
||||
close.
|
||||
- `parties = [{ party, amount? }, { party, percent? }, ...]` on an event:
|
||||
the parties (by name, in `setup.parties`) the entry is with, each with
|
||||
its share: an `amount` (an exact decimal string), a `percent`, or
|
||||
neither (derived from the entry: all of it for one party, the members'
|
||||
capital lines or income shares for several: B-35). An entry naming a
|
||||
party with a vendor record is tagged with that payee too; with
|
||||
`payee` as well, the payee's party is one of the parties.
|
||||
Not on a reversal, which takes its original's.
|
||||
- `close = { start, to, rounding? }` as an event: closes the period from
|
||||
`start` through the event's `date` (rules.md B-11, B-13), `to`
|
||||
`"members"` (split by share, in the order listed) or
|
||||
@@ -150,8 +180,9 @@ A query over a period takes `from` and `to`; a query as of a day takes
|
||||
`as_of`; the other is refused. A date left out is resolved by the one
|
||||
rule (rules.md B-30) on `setup.today`: an omitted `to` is today, an
|
||||
omitted `from` the first day of the fiscal year that holds `to`, an
|
||||
omitted `as_of` today (`vendor_1099_paid` and the format queries give
|
||||
theirs). A query no crate answers is refused, never skipped.
|
||||
omitted `as_of` today (`vendor_1099_paid`, `party_totals` and the
|
||||
format queries give theirs). A query no crate
|
||||
answers is refused, never skipped.
|
||||
|
||||
| query | dates | values | checked by |
|
||||
|---|---|---|---|
|
||||
@@ -161,7 +192,11 @@ theirs). A query no crate answers is refused, never skipped.
|
||||
| `entry` | `entry = N`, no dates | entry N's lines, `"<code>.debit"` and `"<code>.credit"` each the sum of that account's lines on that side, every line of the entry named | `ledger-core` `tests/bookspec.rs` |
|
||||
| `balances` | `as_of` | each account named by code, its balance on its normal side (a member's capital after a loss is negative); the example names the accounts it shows | `ledger-core` `tests/bookspec.rs` |
|
||||
| `members_equity` | `from`, `to` | the statement of changes in members' equity (rules.md B-13): `"<member>.beginning"`, `.contributions`, `.net_income`, `.distributions`, `.other`, `.ending`; the example names the figures it shows | `ledger-core` `tests/bookspec.rs` |
|
||||
| `vendor_1099_paid` | `from` (January 1 of `to`'s year), `to` | each payee's payments by name, out of the cash and financing accounts (`cash_accounts` required), every payee listed | `ledger-core` `tests/bookspec.rs` |
|
||||
| `k1` | `from`, `to` | the K-1 worksheet (B-13; Schedule K-1 Item L): `"<member>.beginning"`, `.contributions_cash`, `.contributions_property`, `.net_income`, `.distributions`, `.other`, `.ending`; the example names the figures it shows (`cash_accounts` required) | `ledger-core` `tests/bookspec.rs` |
|
||||
| `vendor_1099_paid` | `from` (January 1 of `to`'s year), `to` | each payee's payments that count toward its 1099, by name: out of the cash and financing accounts (`cash_accounts` required), a card's or payment network's left out (B-19), every payee listed | `ledger-core` `tests/bookspec.rs` |
|
||||
| `party_part` | `entry = N`, `party`, no dates | entry N's part for the party (B-35): `resolved`, signed as a line on the party's own account would be (paid or given positive, received negative), the entry's `direction` (`paid`, `received` or `neither`), and what of it moved as money (`cash`) and as property (`in_kind`), every one given | `ledger-core` `tests/bookspec.rs` |
|
||||
| `party_totals` | `party`, `basis` (`cash` or `all`), `from`, `to` | what was `paid` to the party and `received` from it over the period (B-35 item 4: basis `cash` money, `all` money and property in kind; each entry on its first original's side), both given | `ledger-core` `tests/bookspec.rs` |
|
||||
| `party_tin_last4` | `party`, no dates | `tin_last4`: the party's TIN facts' last four, else its W-9's, `""` for none (B-26) | `ledger-core` `tests/bookspec.rs` |
|
||||
| `period` | `from`, `to`, either or both left out | the dates a period report asked with those dates covers, by the one rule (B-30): `from`, `to` (`YYYY-MM-DD`); or `refused` | `ledger-core` `tests/bookspec.rs` |
|
||||
| `day` | `as_of`, or left out | the day a report as of a day is made as of: `as_of` | `ledger-core` `tests/bookspec.rs` |
|
||||
| `calendar_year` | none | the year a calendar-year count (1099s, party totals) asked without one counts: `year` | `ledger-core` `tests/bookspec.rs` |
|
||||
@@ -172,8 +207,9 @@ theirs). A query no crate answers is refused, never skipped.
|
||||
|
||||
A figure query is a table (`[expect.values]`, or `values = { ... }`); a
|
||||
format query, and `uncategorized_accounts`, an array (`values = [ ...
|
||||
]`). `period`, `day` and `calendar_year` give dates and a year, and
|
||||
`account_names` names, not figures.
|
||||
]`). `period`, `day` and `calendar_year` give dates and a year,
|
||||
`account_names` names, `party_part`'s `direction` and
|
||||
`party_tin_last4` words, not figures.
|
||||
|
||||
A command is tried on a copy of the books, as the books' own command
|
||||
does it, and changes nothing checked after it. It shows no values: it is
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
# P3 defect 24 (found in design; wanderbooks plan/served.md P3 "Settled in
|
||||
# design"): party totals treated every liability but payables and payroll
|
||||
# as money, so an accrual through another liability counted when it was
|
||||
# booked and again when it was settled. Regression test: ledger-core
|
||||
# tests/spec_party_totals.rs
|
||||
# defect24_an_accrual_through_another_liability_counts_once. The shape:
|
||||
# bookspec/README.md.
|
||||
|
||||
id = "p3-party-accrual-counts-once"
|
||||
title = "An accrual through a liability that isn't money counts once, when it is settled"
|
||||
rule = """
|
||||
Money is cash, the company's cards, the members' financing and loans.
|
||||
Every other liability (accrued expenses, deferred revenue) is a promise:
|
||||
an entry through it moves nothing with the party until it is settled in
|
||||
money.
|
||||
"""
|
||||
status = "CHOICE"
|
||||
sources = [
|
||||
"Helvering v. Price, 309 U.S. 409, 412-413 (1940): a promise to pay is not payment (wanderbooks docs/spec/sources/helvering-v-price-1940.md)",
|
||||
"DECISION (owner, 2026-10-20): money is cash, cards, the members' financing and loans (wanderbooks rules.md B-35 item 5)",
|
||||
]
|
||||
tags = ["parties", "accruals", "deferred-revenue", "money"]
|
||||
|
||||
[setup]
|
||||
currency = "USD"
|
||||
fiscal_year_start = "01-01"
|
||||
basis = "accrual"
|
||||
rounding = "half-even"
|
||||
cash_accounts = ["1000"]
|
||||
financing_accounts = ["2100"]
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1000"
|
||||
name = "Checking"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2100"
|
||||
name = "Due to Members"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2200"
|
||||
name = "Accrued Expenses"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2400"
|
||||
name = "Deferred Revenue"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "4000"
|
||||
name = "Revenue"
|
||||
type = "revenue"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "6050"
|
||||
name = "Domain & Hosting"
|
||||
type = "expense"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Northwind Filings"
|
||||
roles = ["vendor"]
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Riley Chen"
|
||||
roles = ["customer"]
|
||||
|
||||
[[events]]
|
||||
date = "2026-03-31"
|
||||
description = "Hosting accrued"
|
||||
parties = [{ party = "Northwind Filings" }]
|
||||
lines = [
|
||||
{ account = "6050", side = "debit", amount = "300.00" },
|
||||
{ account = "2200", side = "credit", amount = "300.00" },
|
||||
]
|
||||
|
||||
[[events]]
|
||||
date = "2026-04-15"
|
||||
description = "Hosting paid"
|
||||
parties = [{ party = "Northwind Filings" }]
|
||||
lines = [
|
||||
{ account = "2200", side = "debit", amount = "300.00" },
|
||||
{ account = "1000", side = "credit", amount = "300.00" },
|
||||
]
|
||||
|
||||
[[events]]
|
||||
date = "2026-01-02"
|
||||
description = "A year paid ahead"
|
||||
parties = [{ party = "Riley Chen" }]
|
||||
lines = [
|
||||
{ account = "1000", side = "debit", amount = "1200.00" },
|
||||
{ account = "2400", side = "credit", amount = "1200.00" },
|
||||
]
|
||||
|
||||
[[events]]
|
||||
date = "2026-01-31"
|
||||
description = "January earned"
|
||||
parties = [{ party = "Riley Chen" }]
|
||||
lines = [
|
||||
{ account = "2400", side = "debit", amount = "100.00" },
|
||||
{ account = "4000", side = "credit", amount = "100.00" },
|
||||
]
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 1
|
||||
party = "Northwind Filings"
|
||||
values = { resolved = "300.00", direction = "neither", cash = "0.00", in_kind = "0.00" }
|
||||
|
||||
# Before P3: 600.00.
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Northwind Filings"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "300.00", received = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 4
|
||||
party = "Riley Chen"
|
||||
values = { resolved = "100.00", direction = "neither", cash = "0.00", in_kind = "0.00" }
|
||||
|
||||
# Before P3: 1,300.00.
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Riley Chen"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "0.00", received = "1200.00" }
|
||||
@@ -0,0 +1,200 @@
|
||||
# P3 defect 24 in a member's entry (found in review, 2026-10-21): a
|
||||
# distribution declared to a liability that isn't money counted as paid
|
||||
# when declared and again when paid. A distribution's cash is the money it
|
||||
# paid, its in kind the property it gave; the rest is still owed. Regression
|
||||
# tests: ledger-core tests/spec_party_totals.rs
|
||||
# defect24_a_distribution_declared_to_a_payable_counts_once_when_paid and
|
||||
# a_distribution_of_money_and_depreciated_property_splits_by_what_moved.
|
||||
# The shape: bookspec/README.md.
|
||||
|
||||
id = "p3-party-distribution-still-owed"
|
||||
title = "A distribution counts the money it paid and the property it gave; what is still owed counts when paid"
|
||||
rule = """
|
||||
An entry naming only members that moves their capital counts what moved
|
||||
it. Of a distribution, the cash is the money it paid (through the cash
|
||||
accounts, a card, the members' financing or a loan), the in kind the
|
||||
property it gave up (at what the books carried it), and the rest, credited
|
||||
to a liability that isn't money for the entry (distributions payable, or
|
||||
what the members are owed when they are named its creditors), is a
|
||||
promise: it counts nothing until it is paid, then once, as paid. The
|
||||
statement of members' equity counts a distribution when it is declared,
|
||||
so party totals catch up with it when it is paid.
|
||||
"""
|
||||
status = "AUTHORITY"
|
||||
sources = [
|
||||
"Helvering v. Price, 309 U.S. 409, 412-413 (1940): a promise to pay is not payment (wanderbooks docs/spec/sources/helvering-v-price-1940.md)",
|
||||
"Treas. Reg. §1.704-1(b)(2)(iv)(b)(4)-(5): a partner's capital account falls by the money and the property distributed (docs/spec/sources/irs-capital-accounts.md)",
|
||||
"DECISION (owner, 2026-10-20): a member's entry counts what moved their capital; money is cash, cards, the members' financing and loans (rules.md B-35)",
|
||||
]
|
||||
tags = ["parties", "members", "distributions", "promises", "in-kind", "money"]
|
||||
|
||||
[setup]
|
||||
currency = "USD"
|
||||
fiscal_year_start = "01-01"
|
||||
basis = "accrual"
|
||||
rounding = "half-even"
|
||||
cash_accounts = ["1000"]
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1000"
|
||||
name = "Checking"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1500"
|
||||
name = "Equipment"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1510"
|
||||
name = "Accumulated Depreciation — Equipment"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2150"
|
||||
name = "Distributions Payable"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "3000"
|
||||
name = "Capital — Tobin Harlow"
|
||||
type = "equity"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "3001"
|
||||
name = "Capital — Morgan Ellis"
|
||||
type = "equity"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "3100"
|
||||
name = "Distributions — Tobin Harlow"
|
||||
type = "equity"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Tobin Harlow"
|
||||
roles = ["member"]
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Morgan Ellis"
|
||||
roles = ["member"]
|
||||
|
||||
[[setup.members]]
|
||||
name = "Tobin Harlow"
|
||||
share = "0.5"
|
||||
capital_account = "3000"
|
||||
distributions_account = "3100"
|
||||
|
||||
[[setup.members]]
|
||||
name = "Morgan Ellis"
|
||||
share = "0.5"
|
||||
capital_account = "3001"
|
||||
|
||||
# 1. A distribution declared to Tobin: still owed, counts nothing.
|
||||
[[events]]
|
||||
date = "2026-06-30"
|
||||
description = "Distribution declared"
|
||||
parties = [{ party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "3100", side = "debit", amount = "200.00" },
|
||||
{ account = "2150", side = "credit", amount = "200.00" },
|
||||
]
|
||||
|
||||
# 2. It is paid: paid, money, once.
|
||||
[[events]]
|
||||
date = "2026-07-15"
|
||||
description = "Distribution paid"
|
||||
parties = [{ party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "2150", side = "debit", amount = "200.00" },
|
||||
{ account = "1000", side = "credit", amount = "200.00" },
|
||||
]
|
||||
|
||||
# 3. Declared and partly paid at once: the money now, the rest when paid.
|
||||
[[events]]
|
||||
date = "2026-08-31"
|
||||
description = "Distribution, part paid"
|
||||
parties = [{ party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "3100", side = "debit", amount = "300.00" },
|
||||
{ account = "1000", side = "credit", amount = "100.00" },
|
||||
{ account = "2150", side = "credit", amount = "200.00" },
|
||||
]
|
||||
|
||||
# 4. Equipment carried at 1,000.00 less 300.00 depreciated, with 100.00 in
|
||||
# cash, distributed to Morgan: 100.00 cash, 700.00 in kind.
|
||||
[[events]]
|
||||
date = "2026-09-01"
|
||||
description = "Equipment and cash distributed"
|
||||
parties = [{ party = "Morgan Ellis" }]
|
||||
lines = [
|
||||
{ account = "3001", side = "debit", amount = "800.00" },
|
||||
{ account = "1510", side = "debit", amount = "300.00" },
|
||||
{ account = "1500", side = "credit", amount = "1000.00" },
|
||||
{ account = "1000", side = "credit", amount = "100.00" },
|
||||
]
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 1
|
||||
party = "Tobin Harlow"
|
||||
values = { resolved = "200.00", direction = "neither", cash = "0.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 3
|
||||
party = "Tobin Harlow"
|
||||
values = { resolved = "300.00", direction = "paid", cash = "100.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 4
|
||||
party = "Morgan Ellis"
|
||||
values = { resolved = "800.00", direction = "paid", cash = "100.00", in_kind = "700.00" }
|
||||
|
||||
# Before the review: 700.00 (event 1's 200.00 and the 200.00 still owed
|
||||
# on event 3 counted as paid, and event 2 paid the first again).
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Tobin Harlow"
|
||||
basis = "cash"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "300.00", received = "0.00" }
|
||||
|
||||
# Through June: declared, not yet paid. The statement counts it already.
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Tobin Harlow"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-06-30"
|
||||
values = { paid = "0.00", received = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "members_equity"
|
||||
from = "2026-01-01"
|
||||
to = "2026-06-30"
|
||||
values = { "Tobin Harlow.contributions" = "0.00", "Tobin Harlow.distributions" = "200.00" }
|
||||
|
||||
# Before the review: 0.00 cash, 800.00 in kind.
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Morgan Ellis"
|
||||
basis = "cash"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "100.00", received = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Morgan Ellis"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "800.00", received = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "members_equity"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { "Morgan Ellis.contributions" = "0.00", "Morgan Ellis.distributions" = "800.00" }
|
||||
@@ -0,0 +1,92 @@
|
||||
# P3 defect 24 and the card rule (found in review, 2026-10-21): the
|
||||
# company's cards are the liabilities whose names say "card", so a gift
|
||||
# card the company sold (deferred revenue) read as money: received when
|
||||
# sold and again when redeemed. Regression
|
||||
# test: ledger-core tests/spec_party_totals.rs
|
||||
# defect24_a_gift_card_sold_is_received_once.
|
||||
# The shape: bookspec/README.md.
|
||||
|
||||
id = "p3-party-gift-card-is-a-promise"
|
||||
title = "A gift card the company sold is a promise, not a card it pays with"
|
||||
rule = """
|
||||
The company's cards are liability accounts whose names say "card", but not
|
||||
"gift card": gift cards the company sold are revenue it owes its customers
|
||||
in service (deferred revenue), a promise. Selling one is money received
|
||||
from the buyer; redeeming it moves no money and counts nothing.
|
||||
"""
|
||||
status = "CONVENTION"
|
||||
sources = [
|
||||
"DECISION (owner, 2026-10-20): money is cash, cards, the members' financing and loans; every other liability is a promise, counted when settled (wanderbooks rules.md B-35 item 5)",
|
||||
"Helvering v. Price, 309 U.S. 409, 412-413 (1940): a promise to pay is not payment (docs/spec/sources/helvering-v-price-1940.md)",
|
||||
]
|
||||
tags = ["parties", "cards", "deferred-revenue", "money"]
|
||||
|
||||
[setup]
|
||||
currency = "USD"
|
||||
fiscal_year_start = "01-01"
|
||||
basis = "accrual"
|
||||
rounding = "half-even"
|
||||
cash_accounts = ["1000"]
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1000"
|
||||
name = "Checking"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2050"
|
||||
name = "Business Card"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2410"
|
||||
name = "Gift Card Liability"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "4000"
|
||||
name = "Revenue"
|
||||
type = "revenue"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Riley Chen"
|
||||
roles = ["customer"]
|
||||
|
||||
[[events]]
|
||||
date = "2026-03-01"
|
||||
description = "Gift card sold"
|
||||
parties = [{ party = "Riley Chen" }]
|
||||
lines = [
|
||||
{ account = "1000", side = "debit", amount = "100.00" },
|
||||
{ account = "2410", side = "credit", amount = "100.00" },
|
||||
]
|
||||
|
||||
[[events]]
|
||||
date = "2026-03-05"
|
||||
description = "Gift card redeemed"
|
||||
parties = [{ party = "Riley Chen" }]
|
||||
lines = [
|
||||
{ account = "2410", side = "debit", amount = "100.00" },
|
||||
{ account = "4000", side = "credit", amount = "100.00" },
|
||||
]
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 1
|
||||
party = "Riley Chen"
|
||||
values = { resolved = "-100.00", direction = "received", cash = "-100.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 2
|
||||
party = "Riley Chen"
|
||||
values = { resolved = "100.00", direction = "neither", cash = "0.00", in_kind = "0.00" }
|
||||
|
||||
# Before the review: 200.00.
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Riley Chen"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "0.00", received = "100.00" }
|
||||
@@ -0,0 +1,219 @@
|
||||
# P3 defect 14, settled in design (wanderbooks plan/served.md P3): party
|
||||
# totals count a member's entry by what moved their capital, so basis all
|
||||
# is the statement of members' equity and basis cash means what it means
|
||||
# on the K-1 worksheet. Regression tests: ledger-core
|
||||
# tests/spec_party_totals.rs a_members_mixed_entry_splits_as_the_k1_does,
|
||||
# basis_all_is_the_members_equity_statement_and_cash_the_k1s, and the
|
||||
# property member_totals_are_the_members_equity_statement. The shape:
|
||||
# bookspec/README.md.
|
||||
|
||||
id = "p3-party-members-equity-and-k1"
|
||||
title = "A member's totals are their capital account: basis all the statement of members' equity, basis cash the K-1's cash"
|
||||
rule = """
|
||||
An entry naming only members that moves their equity counts what moved
|
||||
their capital: a contribution received, a distribution paid. The
|
||||
property in it (asset accounts that are neither cash nor receivables) is
|
||||
in kind, shared among the members in proportion to their parts, to the
|
||||
cent, leftover cents by largest remainder, ties to the member listed
|
||||
first; the rest is cash, as the K-1 worksheet counts it: money, or what a
|
||||
member paid for the company (an expense booked straight to capital, a
|
||||
debt to them converted to capital).
|
||||
"""
|
||||
status = "CHOICE"
|
||||
sources = [
|
||||
"Treas. Reg. §1.704-1(b)(2)(iv)(b)(1)-(2), (c)(1): money and property contributed; a partnership liability a partner pays is money contributed (wanderbooks docs/spec/sources/irs-capital-accounts.md)",
|
||||
"Partner's Instructions for Schedule K-1 (Form 1065), Item L: the capital contributed during the year (docs/spec/sources/irs-capital-accounts.md)",
|
||||
"DECISION (owner, 2026-10-15): what a member paid for the company is cash on the K-1 (wanderbooks ledger-core k1 INVARIANT 3); DECISION (owner, 2026-10-20): a member's entry counts what moved their capital (rules.md B-35)",
|
||||
]
|
||||
tags = ["parties", "members", "k1", "members-equity", "in-kind", "rounding"]
|
||||
|
||||
[setup]
|
||||
currency = "USD"
|
||||
fiscal_year_start = "01-01"
|
||||
basis = "accrual"
|
||||
rounding = "largest-remainder"
|
||||
cash_accounts = ["1000"]
|
||||
financing_accounts = ["2100"]
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1000"
|
||||
name = "Checking"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1500"
|
||||
name = "Equipment"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2100"
|
||||
name = "Due to Members"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "3000"
|
||||
name = "Capital — Tobin Harlow"
|
||||
type = "equity"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "3001"
|
||||
name = "Capital — Morgan Ellis"
|
||||
type = "equity"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "6050"
|
||||
name = "Domain & Hosting"
|
||||
type = "expense"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Tobin Harlow"
|
||||
roles = ["member"]
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Morgan Ellis"
|
||||
roles = ["member"]
|
||||
|
||||
[[setup.members]]
|
||||
name = "Tobin Harlow"
|
||||
share = "0.5"
|
||||
capital_account = "3000"
|
||||
|
||||
[[setup.members]]
|
||||
name = "Morgan Ellis"
|
||||
share = "0.5"
|
||||
capital_account = "3001"
|
||||
|
||||
# 1. A joint contribution of cash and property (the K-1 worksheet's
|
||||
# SCENARIO 3): 500.00 each, of it 300.00 cash and 200.00 in kind.
|
||||
[[events]]
|
||||
date = "2026-02-01"
|
||||
description = "Joint contribution, cash and equipment"
|
||||
parties = [{ party = "Morgan Ellis" }, { party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "1000", side = "debit", amount = "600.00" },
|
||||
{ account = "1500", side = "debit", amount = "400.00" },
|
||||
{ account = "3000", side = "credit", amount = "500.00" },
|
||||
{ account = "3001", side = "credit", amount = "500.00" },
|
||||
]
|
||||
|
||||
# 2. One cent of property shared by two equal parts: the cent goes to the
|
||||
# member listed first in the company's members (Tobin), whatever the
|
||||
# order the entry names them in.
|
||||
[[events]]
|
||||
date = "2026-02-02"
|
||||
description = "A cent of property"
|
||||
parties = [{ party = "Morgan Ellis" }, { party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "1000", side = "debit", amount = "0.99" },
|
||||
{ account = "1500", side = "debit", amount = "0.01" },
|
||||
{ account = "3000", side = "credit", amount = "0.50" },
|
||||
{ account = "3001", side = "credit", amount = "0.50" },
|
||||
]
|
||||
|
||||
# 3. An expense Morgan paid, booked straight to capital: cash.
|
||||
[[events]]
|
||||
date = "2026-02-03"
|
||||
description = "Hosting Morgan paid"
|
||||
parties = [{ party = "Morgan Ellis" }]
|
||||
lines = [
|
||||
{ account = "6050", side = "debit", amount = "50.00" },
|
||||
{ account = "3001", side = "credit", amount = "50.00" },
|
||||
]
|
||||
|
||||
# 4. What the company owed Tobin, converted to capital: cash.
|
||||
[[events]]
|
||||
date = "2026-02-04"
|
||||
description = "Advance converted"
|
||||
parties = [{ party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "2100", side = "debit", amount = "70.00" },
|
||||
{ account = "3000", side = "credit", amount = "70.00" },
|
||||
]
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 1
|
||||
party = "Tobin Harlow"
|
||||
values = { resolved = "-500.00", direction = "received", cash = "-300.00", in_kind = "-200.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 2
|
||||
party = "Tobin Harlow"
|
||||
values = { resolved = "-0.50", direction = "received", cash = "-0.49", in_kind = "-0.01" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 2
|
||||
party = "Morgan Ellis"
|
||||
values = { resolved = "-0.50", direction = "received", cash = "-0.50", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 3
|
||||
party = "Morgan Ellis"
|
||||
values = { resolved = "-50.00", direction = "received", cash = "-50.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 4
|
||||
party = "Tobin Harlow"
|
||||
values = { resolved = "-70.00", direction = "received", cash = "-70.00", in_kind = "0.00" }
|
||||
|
||||
# Tobin: cash 300.00 + 0.49 + 70.00; in kind 200.00 + 0.01.
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Tobin Harlow"
|
||||
basis = "cash"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "0.00", received = "370.49" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Tobin Harlow"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "0.00", received = "570.50" }
|
||||
|
||||
# Morgan: cash 300.00 + 0.50 + 50.00; in kind 200.00.
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Morgan Ellis"
|
||||
basis = "cash"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "0.00", received = "350.50" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Morgan Ellis"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "0.00", received = "550.50" }
|
||||
|
||||
[[expect]]
|
||||
query = "members_equity"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { "Tobin Harlow.contributions" = "570.50", "Tobin Harlow.distributions" = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "members_equity"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { "Morgan Ellis.contributions" = "550.50", "Morgan Ellis.distributions" = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "k1"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { "Tobin Harlow.contributions_cash" = "370.49", "Tobin Harlow.contributions_property" = "200.01" }
|
||||
|
||||
[[expect]]
|
||||
query = "k1"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { "Morgan Ellis.contributions_cash" = "350.50", "Morgan Ellis.contributions_property" = "200.00" }
|
||||
@@ -0,0 +1,240 @@
|
||||
# P3 defect 24 and wanderbooks rules.md B-35 item 6: a credit to a
|
||||
# liability the company owes a party itself never counts as paying that
|
||||
# party. Regression test: ledger-core tests/spec_party_totals.rs
|
||||
# defect24_a_credit_to_a_partys_own_liability_never_pays_it. The shape:
|
||||
# bookspec/README.md.
|
||||
|
||||
id = "p3-party-own-liability-never-pays"
|
||||
title = "A credit to what the company owes a party itself never pays it"
|
||||
rule = """
|
||||
A party may be named the creditor of the liabilities the company owes it
|
||||
itself: a card's issuer, a loan's lender, each member for what the
|
||||
members advanced. For an entry naming it, a credit to one is a promise,
|
||||
not a payment: a fee on the issuer's own card, equipment bought on the
|
||||
seller's own note, a distribution credited to what the members are owed.
|
||||
It counts when it is paid. Property a member contributes subject to a
|
||||
loan counts at its value net of the loan, in kind.
|
||||
"""
|
||||
status = "AUTHORITY"
|
||||
sources = [
|
||||
"Helvering v. Price, 309 U.S. 409, 412-413 (1940): a promise to pay, even a secured one, is not payment (wanderbooks docs/spec/sources/helvering-v-price-1940.md)",
|
||||
"Treas. Reg. §1.704-1(b)(2)(iv)(b)(2): property contributed, net of liabilities the partnership takes it subject to (docs/spec/sources/irs-capital-accounts.md)",
|
||||
"DECISION (owner, 2026-10-20): a liability credit never counts as paying that liability's own creditor (rules.md B-35)",
|
||||
]
|
||||
tags = ["parties", "creditors", "cards", "notes", "distributions", "members"]
|
||||
|
||||
[setup]
|
||||
currency = "USD"
|
||||
fiscal_year_start = "01-01"
|
||||
basis = "accrual"
|
||||
rounding = "half-even"
|
||||
cash_accounts = ["1000"]
|
||||
financing_accounts = ["2100", "2500"]
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1000"
|
||||
name = "Checking"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1500"
|
||||
name = "Equipment"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2050"
|
||||
name = "Business Card"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2100"
|
||||
name = "Due to Members"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2500"
|
||||
name = "Note Payable"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "3000"
|
||||
name = "Capital — Tobin Harlow"
|
||||
type = "equity"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "3001"
|
||||
name = "Capital — Morgan Ellis"
|
||||
type = "equity"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "6400"
|
||||
name = "Bank Fees"
|
||||
type = "expense"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Harbor Bank"
|
||||
roles = ["financial_institution"]
|
||||
creditor_of = ["2050"]
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Lakeside Supply"
|
||||
roles = []
|
||||
creditor_of = ["2500"]
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Tobin Harlow"
|
||||
roles = ["member"]
|
||||
creditor_of = ["2100"]
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Morgan Ellis"
|
||||
roles = ["member"]
|
||||
creditor_of = ["2100"]
|
||||
|
||||
[[setup.members]]
|
||||
name = "Tobin Harlow"
|
||||
share = "0.5"
|
||||
capital_account = "3000"
|
||||
|
||||
[[setup.members]]
|
||||
name = "Morgan Ellis"
|
||||
share = "0.5"
|
||||
capital_account = "3001"
|
||||
|
||||
# 1. The issuer's fee on its own card: neither.
|
||||
[[events]]
|
||||
date = "2026-05-01"
|
||||
description = "Card fee"
|
||||
parties = [{ party = "Harbor Bank" }]
|
||||
lines = [
|
||||
{ account = "6400", side = "debit", amount = "25.00" },
|
||||
{ account = "2050", side = "credit", amount = "25.00" },
|
||||
]
|
||||
|
||||
# 2. The card paid off: paid to the issuer.
|
||||
[[events]]
|
||||
date = "2026-05-20"
|
||||
description = "Card payment"
|
||||
parties = [{ party = "Harbor Bank" }]
|
||||
lines = [
|
||||
{ account = "2050", side = "debit", amount = "125.00" },
|
||||
{ account = "1000", side = "credit", amount = "125.00" },
|
||||
]
|
||||
|
||||
# 3. A distribution to Tobin credited to what the members are owed:
|
||||
# still owed, counts as nothing.
|
||||
[[events]]
|
||||
date = "2026-06-30"
|
||||
description = "Distribution declared"
|
||||
parties = [{ party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "3000", side = "debit", amount = "200.00" },
|
||||
{ account = "2100", side = "credit", amount = "200.00" },
|
||||
]
|
||||
|
||||
# 4. It is paid: paid, money.
|
||||
[[events]]
|
||||
date = "2026-07-15"
|
||||
description = "Distribution paid"
|
||||
parties = [{ party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "2100", side = "debit", amount = "200.00" },
|
||||
{ account = "1000", side = "credit", amount = "200.00" },
|
||||
]
|
||||
|
||||
# 5. Equipment on the seller's own note: neither.
|
||||
[[events]]
|
||||
date = "2026-08-01"
|
||||
description = "Equipment on a note"
|
||||
parties = [{ party = "Lakeside Supply" }]
|
||||
lines = [
|
||||
{ account = "1500", side = "debit", amount = "900.00" },
|
||||
{ account = "2500", side = "credit", amount = "900.00" },
|
||||
]
|
||||
|
||||
# 6. A payment on the note: paid.
|
||||
[[events]]
|
||||
date = "2026-09-01"
|
||||
description = "Note payment"
|
||||
parties = [{ party = "Lakeside Supply" }]
|
||||
lines = [
|
||||
{ account = "2500", side = "debit", amount = "300.00" },
|
||||
{ account = "1000", side = "credit", amount = "300.00" },
|
||||
]
|
||||
|
||||
# 7. Equipment worth 1,000.00 Morgan contributes subject to 200.00 owed
|
||||
# on the note: 800.00 received, in kind (before P3: "paid 200.00").
|
||||
[[events]]
|
||||
date = "2026-09-15"
|
||||
description = "Equipment contributed with its loan"
|
||||
parties = [{ party = "Morgan Ellis" }]
|
||||
lines = [
|
||||
{ account = "1500", side = "debit", amount = "1000.00" },
|
||||
{ account = "2500", side = "credit", amount = "200.00" },
|
||||
{ account = "3001", side = "credit", amount = "800.00" },
|
||||
]
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 1
|
||||
party = "Harbor Bank"
|
||||
values = { resolved = "-25.00", direction = "neither", cash = "0.00", in_kind = "0.00" }
|
||||
|
||||
# Before naming the issuer the card's creditor: 150.00.
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Harbor Bank"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "125.00", received = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 3
|
||||
party = "Tobin Harlow"
|
||||
values = { resolved = "200.00", direction = "neither", cash = "0.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Tobin Harlow"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "200.00", received = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 5
|
||||
party = "Lakeside Supply"
|
||||
values = { resolved = "-900.00", direction = "neither", cash = "0.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Lakeside Supply"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "300.00", received = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 7
|
||||
party = "Morgan Ellis"
|
||||
values = { resolved = "-800.00", direction = "received", cash = "0.00", in_kind = "-800.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Morgan Ellis"
|
||||
basis = "cash"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "0.00", received = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Morgan Ellis"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "0.00", received = "800.00" }
|
||||
@@ -0,0 +1,99 @@
|
||||
# P3 defect 14, the second bug found in design (wanderbooks P3 brief
|
||||
# §1.4): a member pays the card from their own funds; reversing it read
|
||||
# as a second payment. Regression test: ledger-core
|
||||
# tests/spec_party_totals.rs
|
||||
# defect14_a_reversed_member_card_payment_nets_to_zero. The shape:
|
||||
# bookspec/README.md (`after = N` asks a query after event N).
|
||||
|
||||
id = "p3-party-reversed-card-payment"
|
||||
title = "A reversed payment of one liability by another nets to zero"
|
||||
rule = """
|
||||
A reversal's part for each party is its original's negated, never read
|
||||
from the reversal's own lines. A member paying the company's card from
|
||||
their own funds (Dr card / Cr due to members), tagged with the card's
|
||||
issuer, is paid to the issuer; its reversal takes that back, so the pair
|
||||
nets to zero; reversing the reversal pays it again.
|
||||
"""
|
||||
status = "AUTHORITY"
|
||||
sources = [
|
||||
"Pacioli, Summa (1494), Geijsbeek tr.: a mistake is corrected by the opposite entry (wanderbooks docs/spec/sources/pacioli-1494.md, rules.md B-9)",
|
||||
]
|
||||
tags = ["parties", "reversals", "liabilities"]
|
||||
|
||||
[setup]
|
||||
currency = "USD"
|
||||
fiscal_year_start = "01-01"
|
||||
basis = "accrual"
|
||||
rounding = "half-even"
|
||||
cash_accounts = ["1000"]
|
||||
financing_accounts = ["2100"]
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1000"
|
||||
name = "Checking"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2050"
|
||||
name = "Business Card"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2100"
|
||||
name = "Due to Members"
|
||||
type = "liability"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Harbor Bank"
|
||||
roles = ["financial_institution"]
|
||||
|
||||
[[events]]
|
||||
date = "2026-06-01"
|
||||
description = "A member pays the card from their own funds"
|
||||
parties = [{ party = "Harbor Bank" }]
|
||||
lines = [
|
||||
{ account = "2050", side = "debit", amount = "100.00" },
|
||||
{ account = "2100", side = "credit", amount = "100.00" },
|
||||
]
|
||||
|
||||
[[events]]
|
||||
date = "2026-06-02"
|
||||
description = "Reversed"
|
||||
reverses = 1
|
||||
|
||||
[[events]]
|
||||
date = "2026-06-03"
|
||||
description = "The reversal reversed"
|
||||
reverses = 2
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 1
|
||||
party = "Harbor Bank"
|
||||
values = { resolved = "100.00", direction = "paid", cash = "100.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 2
|
||||
party = "Harbor Bank"
|
||||
values = { resolved = "-100.00", direction = "received", cash = "-100.00", in_kind = "0.00" }
|
||||
|
||||
# After the reversal: nothing paid (before P3: 200.00, the reversal read
|
||||
# as a second payment).
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
after = 2
|
||||
party = "Harbor Bank"
|
||||
basis = "cash"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "0.00", received = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
after = 3
|
||||
party = "Harbor Bank"
|
||||
basis = "cash"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "100.00", received = "0.00" }
|
||||
@@ -0,0 +1,289 @@
|
||||
# P3 defect 14 (wanderbooks plan/served.md P3; audit findings F36, F37):
|
||||
# a party's part of an entry is signed as a line on the party's own
|
||||
# account, with its direction and what of it was money or property; a
|
||||
# reversal is the exact opposite of what it reverses; party totals take a
|
||||
# basis. Regression test: ledger-core tests/spec_party_totals.rs
|
||||
# defect14_shares_take_the_partys_side_and_reversals_flip. The shape:
|
||||
# bookspec/README.md.
|
||||
|
||||
id = "p3-party-signs-and-reversals"
|
||||
title = "A party's part of an entry is signed as its own account would show it, and a reversal is the exact opposite"
|
||||
rule = """
|
||||
A party's part of an entry is debit-positive, as a line on the party's own
|
||||
account would be: positive when the company paid it or gave it value,
|
||||
negative when the company received from it. Each part has a direction
|
||||
(paid, received, neither) and a cash and an in-kind amount. An entry
|
||||
naming only members that moves equity counts what moved their capital,
|
||||
the property in it in kind. A reversal's part is exactly its original's
|
||||
negated. A bill or invoice moves nothing with the party yet (neither).
|
||||
Party totals on basis cash count the cash parts, on basis all the cash
|
||||
and in-kind parts, each on its root original's side.
|
||||
"""
|
||||
status = "CONVENTION"
|
||||
sources = [
|
||||
"Pacioli, Summa (1494), Geijsbeek tr., ch. 36: debit is what owes, credit what is owed (wanderbooks docs/spec/sources/pacioli-1494.md)",
|
||||
"Treas. Reg. §1.704-1(b)(2)(iv)(b)(1)-(2), (4)-(5): a partner's capital account rises by money and property contributed, falls by money and property distributed (docs/spec/sources/irs-capital-accounts.md)",
|
||||
"Helvering v. Price, 309 U.S. 409, 412-413 (1940): a promise to pay is not payment (docs/spec/sources/helvering-v-price-1940.md)",
|
||||
"DECISION (owner, 2026-10-20): a member's entry counts what moved their capital (wanderbooks rules.md B-35)",
|
||||
]
|
||||
tags = ["parties", "signs", "reversals", "members", "in-kind", "basis"]
|
||||
|
||||
[setup]
|
||||
currency = "USD"
|
||||
fiscal_year_start = "01-01"
|
||||
basis = "accrual"
|
||||
rounding = "half-even"
|
||||
cash_accounts = ["1000"]
|
||||
financing_accounts = ["2100"]
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1000"
|
||||
name = "Checking"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1200"
|
||||
name = "Accounts Receivable"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1500"
|
||||
name = "Equipment"
|
||||
type = "asset"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2000"
|
||||
name = "Accounts Payable"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "2100"
|
||||
name = "Due to Members"
|
||||
type = "liability"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "3000"
|
||||
name = "Capital — Tobin Harlow"
|
||||
type = "equity"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "3001"
|
||||
name = "Capital — Morgan Ellis"
|
||||
type = "equity"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "3999"
|
||||
name = "Retained Earnings"
|
||||
type = "equity"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "4000"
|
||||
name = "Revenue"
|
||||
type = "revenue"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "6000"
|
||||
name = "Organizational Costs"
|
||||
type = "expense"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Tobin Harlow"
|
||||
roles = ["member"]
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Morgan Ellis"
|
||||
roles = ["member"]
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Northwind Filings"
|
||||
roles = ["vendor"]
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Riley Chen"
|
||||
roles = ["customer"]
|
||||
|
||||
[[setup.members]]
|
||||
name = "Tobin Harlow"
|
||||
share = "0.5"
|
||||
capital_account = "3000"
|
||||
|
||||
[[setup.members]]
|
||||
name = "Morgan Ellis"
|
||||
share = "0.5"
|
||||
capital_account = "3001"
|
||||
|
||||
# 1. Tobin pays the filing fee for the company (through the members'
|
||||
# financing account), tagged with the vendor: paid to the vendor, money.
|
||||
[[events]]
|
||||
date = "2026-10-04"
|
||||
description = "Filing fee, paid by Tobin"
|
||||
parties = [{ party = "Northwind Filings" }]
|
||||
lines = [
|
||||
{ account = "6000", side = "debit", amount = "64.00" },
|
||||
{ account = "2100", side = "credit", amount = "64.00" },
|
||||
]
|
||||
|
||||
# 2. Tobin's cash contribution: received, money.
|
||||
[[events]]
|
||||
date = "2026-10-05"
|
||||
description = "Tobin's contribution"
|
||||
parties = [{ party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "1000", side = "debit", amount = "400.00" },
|
||||
{ account = "3000", side = "credit", amount = "400.00" },
|
||||
]
|
||||
|
||||
# 3. Reimbursing Tobin for the fee: paid, money (cash paying down what
|
||||
# the company owed him).
|
||||
[[events]]
|
||||
date = "2026-10-08"
|
||||
description = "Reimbursement"
|
||||
parties = [{ party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "2100", side = "debit", amount = "64.00" },
|
||||
{ account = "1000", side = "credit", amount = "64.00" },
|
||||
]
|
||||
|
||||
# 4. A laptop Tobin contributes: received, in kind.
|
||||
[[events]]
|
||||
date = "2026-10-12"
|
||||
description = "Laptop contributed"
|
||||
parties = [{ party = "Tobin Harlow" }]
|
||||
lines = [
|
||||
{ account = "1500", side = "debit", amount = "1480.00" },
|
||||
{ account = "3000", side = "credit", amount = "1480.00" },
|
||||
]
|
||||
|
||||
# 5. Its reversal: the exact opposite, paid, in kind.
|
||||
[[events]]
|
||||
date = "2026-10-13"
|
||||
description = "Laptop contribution reversed"
|
||||
reverses = 4
|
||||
|
||||
# 6. The laptop again, half each, by amount.
|
||||
[[events]]
|
||||
date = "2026-10-14"
|
||||
description = "Laptop contributed jointly"
|
||||
parties = [{ party = "Tobin Harlow", amount = "740.00" }, { party = "Morgan Ellis", amount = "740.00" }]
|
||||
lines = [
|
||||
{ account = "1500", side = "debit", amount = "1480.00" },
|
||||
{ account = "3000", side = "credit", amount = "740.00" },
|
||||
{ account = "3001", side = "credit", amount = "740.00" },
|
||||
]
|
||||
|
||||
# 7. A bill from the vendor (credit to payables, its own account): neither.
|
||||
[[events]]
|
||||
date = "2026-10-20"
|
||||
description = "Bill"
|
||||
parties = [{ party = "Northwind Filings" }]
|
||||
lines = [
|
||||
{ account = "6000", side = "debit", amount = "80.00" },
|
||||
{ account = "2000", side = "credit", amount = "80.00" },
|
||||
]
|
||||
|
||||
# 8. The bill reversed: the exact opposite, still neither.
|
||||
[[events]]
|
||||
date = "2026-10-21"
|
||||
description = "Bill reversed"
|
||||
reverses = 7
|
||||
|
||||
# 9. An invoice to the customer (a debit to receivables): neither.
|
||||
[[events]]
|
||||
date = "2026-10-20"
|
||||
description = "Invoice"
|
||||
parties = [{ party = "Riley Chen" }]
|
||||
lines = [
|
||||
{ account = "1200", side = "debit", amount = "120.00" },
|
||||
{ account = "4000", side = "credit", amount = "120.00" },
|
||||
]
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 1
|
||||
party = "Northwind Filings"
|
||||
values = { resolved = "64.00", direction = "paid", cash = "64.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 2
|
||||
party = "Tobin Harlow"
|
||||
values = { resolved = "-400.00", direction = "received", cash = "-400.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 3
|
||||
party = "Tobin Harlow"
|
||||
values = { resolved = "64.00", direction = "paid", cash = "64.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 4
|
||||
party = "Tobin Harlow"
|
||||
values = { resolved = "-1480.00", direction = "received", cash = "0.00", in_kind = "-1480.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 5
|
||||
party = "Tobin Harlow"
|
||||
values = { resolved = "1480.00", direction = "paid", cash = "0.00", in_kind = "1480.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 6
|
||||
party = "Morgan Ellis"
|
||||
values = { resolved = "-740.00", direction = "received", cash = "0.00", in_kind = "-740.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 7
|
||||
party = "Northwind Filings"
|
||||
values = { resolved = "-80.00", direction = "neither", cash = "0.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 8
|
||||
party = "Northwind Filings"
|
||||
values = { resolved = "80.00", direction = "neither", cash = "0.00", in_kind = "0.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_part"
|
||||
entry = 9
|
||||
party = "Riley Chen"
|
||||
values = { resolved = "120.00", direction = "neither", cash = "0.00", in_kind = "0.00" }
|
||||
|
||||
# Tobin: cash in 400.00 (event 2), paid back 64.00 (event 3); in kind
|
||||
# 1,480.00 − 1,480.00 (events 4 and 5 net) + 740.00 (event 6).
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Tobin Harlow"
|
||||
basis = "cash"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "64.00", received = "400.00" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Tobin Harlow"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "64.00", received = "1140.00" }
|
||||
|
||||
# The vendor: the fee a member paid for the company; the bill and its
|
||||
# reversal count nothing.
|
||||
[[expect]]
|
||||
query = "party_totals"
|
||||
party = "Northwind Filings"
|
||||
basis = "all"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { paid = "64.00", received = "0.00" }
|
||||
|
||||
# Basis all over Tobin's equity entries is the statement of members'
|
||||
# equity: contributions 400.00 + 740.00 (events 4 and 5 cancel).
|
||||
[[expect]]
|
||||
query = "members_equity"
|
||||
from = "2026-01-01"
|
||||
to = "2026-12-31"
|
||||
values = { "Tobin Harlow.contributions" = "1140.00", "Tobin Harlow.distributions" = "0.00" }
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# P3 defect 11 (audit finding F28): the directory showed a member's TIN as
|
||||
# missing because it read W-9 facts only. Regression tests: ledger-core
|
||||
# tests/spec_party_totals.rs
|
||||
# defect11_a_partys_tin_last4_is_its_tin_facts_else_its_w9s; ledger-mcp
|
||||
# tests/spec_vault.rs defect11_the_directory_shows_a_members_tin_last4.
|
||||
# The shape: bookspec/README.md.
|
||||
|
||||
id = "p3-party-tin-last4"
|
||||
title = "A party's TIN last four is its TIN facts', else its W-9's"
|
||||
rule = """
|
||||
The books keep a taxpayer identification number truncated to its last
|
||||
four digits. A party's last four is its TIN facts' (a member's SSN for
|
||||
the K-1), else its W-9 facts'; the two never disagree. A TIN purged from
|
||||
the vault keeps its last four in the books. A party with neither has
|
||||
none.
|
||||
"""
|
||||
status = "AUTHORITY"
|
||||
sources = [
|
||||
"Treas. Reg. §301.6109-4(a): a truncated TIN shows the last four digits (wanderbooks docs/spec/sources/irs-truncated-tin.md)",
|
||||
"DECISION (owner, 2026-10-15): a party's TIN facts and W-9 facts never disagree (wanderbooks ledger-core parties INVARIANT 2a)",
|
||||
]
|
||||
tags = ["parties", "tin", "w9", "k1"]
|
||||
# Nothing is posted: the facts are the directory's.
|
||||
events = []
|
||||
|
||||
[setup]
|
||||
currency = "USD"
|
||||
fiscal_year_start = "01-01"
|
||||
basis = "accrual"
|
||||
rounding = "half-even"
|
||||
|
||||
[[setup.accounts]]
|
||||
code = "1000"
|
||||
name = "Checking"
|
||||
type = "asset"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Tobin Harlow"
|
||||
roles = ["member"]
|
||||
tin_last4 = "4321"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Riley Chen"
|
||||
roles = ["contractor"]
|
||||
w9_tin_last4 = "8765"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Morgan Ellis"
|
||||
roles = ["member", "contractor"]
|
||||
tin_last4 = "1111"
|
||||
w9_tin_last4 = "1111"
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Lakeside Supply"
|
||||
roles = []
|
||||
|
||||
[[setup.parties]]
|
||||
name = "Avery Quinn"
|
||||
roles = ["member"]
|
||||
tin_last4 = "2468"
|
||||
tin_purged = true
|
||||
|
||||
[[expect]]
|
||||
query = "party_tin_last4"
|
||||
party = "Tobin Harlow"
|
||||
values = { tin_last4 = "4321" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_tin_last4"
|
||||
party = "Riley Chen"
|
||||
values = { tin_last4 = "8765" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_tin_last4"
|
||||
party = "Morgan Ellis"
|
||||
values = { tin_last4 = "1111" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_tin_last4"
|
||||
party = "Lakeside Supply"
|
||||
values = { tin_last4 = "" }
|
||||
|
||||
[[expect]]
|
||||
query = "party_tin_last4"
|
||||
party = "Avery Quinn"
|
||||
values = { tin_last4 = "2468" }
|
||||
Reference in new issue
Block a user