P3: dated 1099 decisions, payment methods and the card exception (defects 23, 12, 18, D-40)
check / check (push) Canceled after 0s

A vendor's 1099 decision is its own dated fact (files NEC or MISC, not
reportable with its reason, or undetermined), applied by each payment's
date. Payment methods are recorded; card and network payments are left
out and listed; a member's check through financing counts; a bank payment
with no method counts (D-40); $2,000 for payments after 2025. One
worksheet for the tool, the viewer and the CPA package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-21 12:12:26 -06:00
1 parent c2ceabbae9
commit 1304b9c0ad
77 files changed
+11455 -970

No files matched your search

+164 -48
View File
@@ -542,8 +542,10 @@ every tool over every book in ledger-store's `tests/fixtures/old-books`.
and card accounts due for reconciliation (`{ code, name,
reconciled_through?, due_through, next_step }`): the cash accounts
(`[books] cash_accounts`, else asset accounts named for cash: cash,
bank, checking, savings, money market), the card accounts (liabilities
named "card", before their first statement), and every other liability
bank, checking, savings, money market), the card accounts (the
business-card payment methods' liabilities, else liabilities named
"card", not "gift", before their first statement:
`ledger_core::parties::card_accounts`), and every other liability
with a recorded statement, that have posted activity through the end of
last month but no reconciliation (not voided) through it. The viewer's
Needs attention uses the same rule (`ledger_store::reconcilable`).
@@ -778,23 +780,95 @@ on from it.
## Tools — 1099 tracking
Payments are counted as cash: credits minus debits to asset accounts on
entries tagged with the vendor. Card payments move no asset (the card
processor's 1099-K covers them), so they drop out; reversals of tagged
entries carry the tag and net out. Thresholds: $600 for payments through
2025, $2,000 from 2026 (indexed for inflation after 2026); always
overridable, always for the CPA to confirm. The books keep whether a W-9
is on file and the TIN's last four; full TINs live only in the TIN vault
("Tools — the TIN vault").
What counts toward a vendor's 1099 is what left the books' cash accounts
(`books.cash_accounts`) and financing accounts (`books.financing_accounts`)
on entries tagged with the vendor, classified by how each payment was made
(rules.md B-19; plan P3, defects 12, 18 and 23; D-40):
- out of a cash account with no payment method recorded: counts
(presumed check, ACH or transfer; tag a debit-card payment with the
card);
- by card or payment network (a method of that kind, or a company card's
account): excluded, the processor's Form 1099-K, whoever's card it was
(Treas. Reg. §1.6041-1(a)(1)(iv));
- through a financing account (a member paid), or the account of a
member's or a platform's method, with no method recorded:
undetermined, until it is (their card: excluded; their own cash or
check: counts, the company being the payor, §1.6041-1(e)(1)).
Money moved between the company's own sources (the card bill, a member
reimbursed, a loan paid down) pays no one; money received from a vendor
for anything but a refund of what was paid (a sale, its payment of an
invoice, a disposal, a loan) isn't netted and is listed.
Books with no cash accounts named are refused. Thresholds: $600 for
payments through 2025, $2,000 from 2026 (Pub. L. 119-21 §70433; indexed
for inflation after 2026); always overridable, always for the CPA to
confirm. The books keep whether a W-9 is on file and the TIN's last four;
full TINs live only in the TIN vault ("Tools — the TIN vault").
Each vendor has a 1099 decision: `nec`, `misc`, `not_reportable` with a
reason kind (`corporation`, `tax_exempt`, `united_states`, `state`,
`foreign_government`, `foreign_person`, `merchandise`, `employee`, `other`)
and the facts in words, or `undetermined` (also what a vendor nobody
decided on reads as). Each is recorded with who decided and when, and the
date it takes effect, or none (from the beginning: the vendor's status
all along, and every decision written before decisions were kept). Each
payment takes the decision in force on its date (its reversal chain's
first entry's).
- `books.list_vendors {}` → `{ vendors: [{ id, name, form_1099?,
tin_on_file, party_id }] }`.
- `books.set_vendor_1099 { vendor, form_1099: "nec" | "misc" | "none",
tin_on_file? }` → the vendor. `vendor` is an id or a name.
decision_1099, decisions_1099, tin_on_file, party_id }] }`.
`decision_1099` is the decision in force today, `{ value, reason_kind?,
reason?, authority?, effective?, decided_by?, decided_on?, citation?,
note? }` (`authority`: the rule that exempts the reason kind; no
`decided_by`: nobody decided); `decisions_1099` every recorded
decision, in date order; `form_1099` "1099-NEC" or "1099-MISC" when
today's decision files one.
- `books.set_vendor_1099 { vendor, form_1099?, reason_kind?, reason?,
effective?, tin_on_file?, decided_by?, decided_on?, citation?, note? }`
→ the vendor, with `changed` ("set", "removed", "unchanged") and
`warnings`. `vendor` is an id or a name. With `form_1099` (`nec`,
`misc`, `not_reportable`, `undetermined`), `decided_by` is required
(`decided_on` defaults to today); `not_reportable` takes `reason_kind`
and `reason` (`other` a `citation` too), no other value takes them.
"none" is refused, saying what to say instead; paid by card is never a
reason. `effective` (YYYY-MM-DD) only when the payee's status changed:
an LLC's corporate election from its Form 8832 date; a payee that
incorporates is a new vendor. A dated decision with nothing decided
before it is refused; one the same as the decision before its date
takes out any decision recorded for that date ("removed"); recorded
again exactly so, nothing is journaled ("unchanged"). Without
`form_1099`, `tin_on_file` alone marks the W-9, keeping the decision and
who made it; `tin_on_file` omitted keeps the flag. `warnings` names each
year (and its entries) whose payments the change reaches in a closed
period or after that year's 1099s were due (January 31 of the next
year): tell the human, a filed 1099 may need correcting. Journaled as
`vendor.1099_set` (a W-9 flag alone as `vendor.w9_flag_set`), after a
`journal.format_set` declaring the `vendor_1099_decisions` feature once.
- `books.add_instrument { kind, label, last4?, owner_party?, account }` →
`{ id, kind, card_or_network, label, last4?, owner_party?, account_id,
account, warnings }`. `kind`: `bank` (the company's checks, ACH, wires,
bill-pay), `business_card` (a company card the payee charged),
`member_card` (a member's personal card the payee charged),
`member_cash_or_check` (a member's own cash, check, ACH, Zelle, or a
friends-and-family transfer), `platform` (paid to the payee as a seller
through PayPal or Venmo goods and services; never ACH, bill-pay, Zelle or
friends-and-family). `account` (id or code) is what it pays through: the
bank account, the card's liability, Due to Members for a member's own
card or cash; an asset or liability, not a header, not payables or
receivables. `label` is unique (ignoring case); `last4` exactly four
digits. Journaled as `instrument.added` (feature `payment_instruments`).
- `books.instruments {}` → `{ instruments: [...] }`, by id. Read-only.
- `books.set_entry_instrument { entry_id, instrument? }` → the entry (as
`books.get_entry`), with `changed` and `warnings` (as above). An id or
label; omit to clear. Metadata only: drafts and posted entries alike;
the entry and its reversals are tagged together. Journaled as
`entry.instrument_set`.
- `books.set_entry_vendor { entry_id, vendor? }` → the entry (as
`books.get_entry`, which now shows `vendor`). Omitting `vendor` clears
the tag. Metadata only: posted entries may be tagged. `books.draft_entry`
takes `vendor` too; bills and their payments are tagged automatically.
`books.get_entry`, which shows `vendor`, and `instrument` and
`instrument_id`). Omitting `vendor` clears the tag. Metadata only:
posted entries may be tagged. `books.draft_entry` takes `vendor` and
`instrument` too; bills and their payments are tagged automatically.
`books.set_entry_party` tags the vendor too when the party has a vendor
record, so tagging by party counts for 1099s the same way. On an entry
naming several parties the parties are `books.set_entry_party`'s:
@@ -802,27 +876,60 @@ is on file and the TIN's last four; full TINs live only in the TIN vault
is; tagging a vendor whose party isn't named, or clearing the tag while
its party is named, is refused (drop or add the party with `parties`
instead). An entry and its reversal always carry the same tags.
- `books.vendor_1099 { year, threshold?, include_tins?, csv? }` →
`{ year, threshold, vendors: [{ vendor_id, name, form_1099?, tin_on_file,
paid, reportable, party_id?, tin_last4?, tin?, tin_type?, tin_note? }],
missing_w9, notes, warning?, csv? }`. For a year whose threshold
- `books.vendor_1099 { year, threshold?, include_tins?, csv?,
cash_accounts?, financing_accounts?, through? }` →
`{ year, threshold, vendors: [{ vendor_id, name, form_1099?,
decision_1099, decisions_in_year, tin_on_file, paid, excluded_card,
undetermined, not_reportable, status, decision_needed, reportable,
party_id?, tin_last4?, tin?, tin_type?, tin_note? }], missing_w9,
excluded_card_payments, undetermined_payments, other_receipts,
untagged_entries, unresolved, notes, warning?, csv? }`. `paid` is what
counts toward the 1099; `excluded_card` what a card or network paid;
`undetermined` what waits on its payment method; `not_reportable` what
was paid while the vendor was decided not reportable. `status`:
`reportable` (file one: what counts reaches the threshold however the
unknowns settle), `under_threshold`, `not_reportable`, or
`undetermined` (a decision or a method is needed first;
`decision_needed` says which); `reportable` is `status = reportable`.
Each payment list is `[{ entry_id, date, vendor_id, vendor, amount,
instrument? }]`. `missing_w9` names the vendors a 1099 is or may be
("(may be reportable)") due for without a W-9. The notes are quiet:
untagged entries are one count (those that paid money out for
something) with the tool to tag them. For a year whose threshold
wanderbooks doesn't know (after 2026 until the indexed amount is added)
it refuses with what to do; pass `threshold` explicitly once the figure
is confirmed. On an entry naming several parties, `paid` counts only
the vendor's resolved share of the payment (rules.md B-19); an entry
whose shares don't resolve counts in full and `notes` names it.
`tin_last4` comes from the party's W-9 facts. With
`include_tins: true` (default false) each line carries `tin`, the full
TIN formatted as the IRS writes it (123-45-6789; 12-3456789 for an
EIN), and `tin_type`, read from the vault, or `tin_note` saying why not
(not in the vault, the vault locked, a record whose last four or hash
disagree with the books); `warning` then says "This worksheet holds
full TINs, for the CPA; keep it out of the books.". This is the only
tool output that ever holds a full TIN. `csv: true` (default false)
also hands the worksheet over as a CSV file, `csv` (a `HandedFile`,
"Tools — the TIN vault"), named `1099-worksheet-<year>.csv`, or
`1099-worksheet-<year>-with-tins.csv` with `include_tins` (TINs only
then).
it refuses with what to do; pass `threshold` explicitly once the
figure is confirmed. Payments are counted out of `cash_accounts` and
`financing_accounts` (account codes; each defaults to the `[books]`
setting, the CPA package's rule: `ledger_package::Settings::with_accounts`;
a code the chart lacks is refused), from January 1 of `year` through
`through` when given (`YYYY-MM-DD`, a day of `year`: the worksheet as of
a day, rules.md B-33), else the whole year. On an entry naming several
parties, the buckets count only the vendor's resolved share of the
payment, split across them to the cent (rules.md B-19); an entry whose
shares don't resolve counts in full and `notes` names it. `tin_last4`
is the party's TIN facts' last four, else its W-9 facts'
(`Party::tin_last4`). With `include_tins: true` (default false) each
line carries `tin`, the full TIN formatted as the IRS writes it (123-45-6789;
12-3456789 for an EIN), and `tin_type`, read from the vault, or
`tin_note` saying why not (not in the vault, the vault locked, a record
whose last four or hash disagree with the books); `warning` then says
"This worksheet holds full TINs, for the CPA; keep it out of the
books.". This is the only tool output that ever holds a full TIN.
`csv: true` (default false) also hands the worksheet over as a CSV file,
`csv` (a `HandedFile`, "Tools — the TIN vault"), named
`1099-worksheet-<year>.csv`, or `1099-worksheet-<year>-with-tins.csv`
with `include_tins` (TINs only then): `vendor_id, name, decision_1099,
form_1099, reason_kind, reason, decided_by, decided_on, effective,
decisions_in_year, paid, excluded_card, undetermined, not_reportable,
status, reportable, w9_on_file, tin_last4` (and `tin, tin_type,
tin_note`). The CPA package carries the same CSV: one list of cash
accounts and one of financing accounts per package, the request's, else
the settings', for every file in it (R18), the 1099 worksheet through
the package's `as_of`; so `books.vendor_1099` given the package's
`cash_accounts`, `financing_accounts` and `through` = `as_of` (and its
`include_tins`) hands over the package's `1099-worksheet-<year>.csv`
byte for byte (the tool names one with TINs `-with-tins`; checked by `ledger-mcp` `oracle_mcp.rs`
`cpa_package_holds_the_year_and_its_evidence`).
## Tools — parties
@@ -875,8 +982,8 @@ texts are at most 500 characters, no control characters.
w9_content_base64?, w9_sha256?, w9_file_name? }` or `{ party, clear:
true }` → the party. Journaled as `party.w9_set`. A party with a
vendor record also gets the record's `tin_on_file` set to match
(journaled as `vendor.1099_set`), so `books.vendor_1099`'s
`missing_w9` follows.
(journaled as `vendor.w9_flag_set`, its 1099 decision and who made it
kept), so `books.vendor_1099`'s `missing_w9` follows.
`tin` and `tin_type` put the full TIN in the TIN vault, and the signed
form (`w9_upload_id`, or `w9_content_base64` with `w9_sha256`, as in
"Tools — receipts & documents") goes there too (below); with them
@@ -992,8 +1099,11 @@ rules.md B-35):
posted entries dated in the year count.
- *Money accounts* (B-35 item 5, P3 defect 24): the `[books]
cash_accounts` (every asset account but receivables when none are set),
the cards (liability accounts whose names say "card", not "gift card":
the rule the reconciliation reminders read), and the `[books]
the cards (the business-card payment methods' liability accounts,
`books.instruments`; while the books record none, the liability
accounts whose names say "card", not "gift card"; never a member's
card or a platform's account: the one rule the reconciliation reminders
and the viewer read), and the `[books]
financing_accounts` (what the members advanced, loans). Any other
liability (an accrued expense, deferred revenue, gift cards sold,
distributions payable) is a promise, counted when it is settled, never
@@ -1022,9 +1132,11 @@ rules.md B-35):
- `invoiced` / `billed`: its invoices' and bills' totals dated in the year.
`owed` / `receivable`: its open bills' and invoices' balances now (every
year).
- `amount_1099` (vendor parties only): what `books.vendor_1099` counts as
paid to its vendor record in the year (asset accounts only, so card
payments drop out); `form_1099` is the record's mark.
- `amount_1099` (vendor parties only): what `books.vendor_1099` counts
toward its vendor record's 1099 in the year (its `paid`: card and
network payments excluded, member payments with no method recorded
undetermined; absent when the worksheet can't be made); `form_1099` and
`decision_1099` are the record's decision in force today.
- `gross_pay` / `net_pay`: payroll lines naming it, in the year.
## Tools — the TIN vault
@@ -1400,7 +1512,8 @@ posting.
matches accounts by name (tell a CPA who imported an earlier IIF), says
when sub-accounts' full names change with it, when the new name moves
the account into or out of the cash accounts (`books.cash_accounts`
unset: picked by name) or the card accounts (named "card"), and lists
unset: picked by name) or the card accounts (named "card", while no
business-card payment method is recorded), and lists
conventions whose text names the old name.
- `books.set_tax_category { account, category?, deductible_percent? }`:
map a revenue or expense account to a category (null clears),
@@ -1711,10 +1824,13 @@ entries.
member role, as the viewer's capital statement finds it; with none, the
member's TIN is a gap.
**1099 worksheet** (`1099-worksheet-<year>.csv`, `as_of`'s calendar
year): the payments from January 1 through `as_of` (the year to date;
the whole year for a December 31 package; DECISION D-36), and the
README says which. With TINs it adds `party_id, tin_last4, tin,
tin_type, tin_note`. A zip with the full set, each
year) is `books.vendor_1099`'s CSV (above) for the package's cash and
financing accounts: the payments from January 1 through `as_of` (the
year to date; the whole year for a December 31 package; DECISION D-36),
and the README says which. `books.vendor_1099` given the same
`cash_accounts`, `financing_accounts` and `through` hands over the
identical file. With TINs it adds `tin, tin_type, tin_note`. A zip with
the full set, each
statement as PDF and XLSX: `profit-and-loss`, `balance-sheet`,
`cash-flow` (the direct method, as `books.export` prints it: operating
by class, investing and financing by kind with each member named, and