diff --git a/README.md b/README.md index 0d36835..f1b760c 100644 --- a/README.md +++ b/README.md @@ -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, `".debit"` and `".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): `".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): `".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 diff --git a/examples/p3-party-accrual-counts-once.toml b/examples/p3-party-accrual-counts-once.toml new file mode 100644 index 0000000..8d8d099 --- /dev/null +++ b/examples/p3-party-accrual-counts-once.toml @@ -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" } diff --git a/examples/p3-party-distribution-still-owed.toml b/examples/p3-party-distribution-still-owed.toml new file mode 100644 index 0000000..68b868b --- /dev/null +++ b/examples/p3-party-distribution-still-owed.toml @@ -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" } diff --git a/examples/p3-party-gift-card-is-a-promise.toml b/examples/p3-party-gift-card-is-a-promise.toml new file mode 100644 index 0000000..852eaab --- /dev/null +++ b/examples/p3-party-gift-card-is-a-promise.toml @@ -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" } diff --git a/examples/p3-party-members-equity-and-k1.toml b/examples/p3-party-members-equity-and-k1.toml new file mode 100644 index 0000000..f9b20ee --- /dev/null +++ b/examples/p3-party-members-equity-and-k1.toml @@ -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" } diff --git a/examples/p3-party-own-liability-never-pays.toml b/examples/p3-party-own-liability-never-pays.toml new file mode 100644 index 0000000..15f5b1b --- /dev/null +++ b/examples/p3-party-own-liability-never-pays.toml @@ -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" } diff --git a/examples/p3-party-reversed-card-payment.toml b/examples/p3-party-reversed-card-payment.toml new file mode 100644 index 0000000..4c39c96 --- /dev/null +++ b/examples/p3-party-reversed-card-payment.toml @@ -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" } diff --git a/examples/p3-party-signs-and-reversals.toml b/examples/p3-party-signs-and-reversals.toml new file mode 100644 index 0000000..85fc5c3 --- /dev/null +++ b/examples/p3-party-signs-and-reversals.toml @@ -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" } + diff --git a/examples/p3-party-tin-last4.toml b/examples/p3-party-tin-last4.toml new file mode 100644 index 0000000..6bffe0d --- /dev/null +++ b/examples/p3-party-tin-last4.toml @@ -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" }