Files
bookspec/examples/1099-money-from-a-payee-is-netted-only-as-a-refund.toml
T
swaitsandClaude Opus 5.5 b2d45905f8 feat(arap)!: documents read their figures from standing lines; control accounts
refuse hand entries (P4b S1, part A)

Plan P4b (D-45), slice S1, the engine half: rules.md B-38.

- ledger-core: every receivables or payables line carries its document
  (Ledger.line_docs: DocRef, Movement, DocTag), written by the document
  commands in the step that posts it, derived on replay from the existing
  document events (the role's account as it stood then; payments by the
  ordinal rule), copied onto a reversal's mirror. Ledger::stands_on;
  doc_state / invoice_state / bill_state (total, paid, outstanding,
  status Open/Partial/Paid/WrittenOff/Void, overdue days) as of any day;
  ar_aging / ap_aging read them, so reversed payments and voids are right.
  The control guard (add_line, add_line_fx, post_entry,
  register_recurring) refuses untagged lines on receivables/payables,
  exempting a reversal's mirror of a legacy line; replay never refuses.
  Bounds (kernel::running_bounds, bounds_kept) on payments and reversals;
  a document reversal never dated before its entry; deposit/payer accounts
  must be cash-like and hold no role. New API: receive_payment_with /
  pay_bill_with (PaymentDetails: instrument, reference), void_document /
  check_void_document, control_check, payments_recorded,
  standing_payments, replay_reverse_entry. Invoice.amount_paid/status
  remain only as derived caches for the MCP layer until part B.
  cashflow bill_splits reads payments from the tags.
- ledger-store: journal feature document_standing (D-39 gated) for a
  payment accepted only because an earlier one was reversed;
  receive_payment_with / pay_bill_with (instrument journaled as
  entry.instrument_set, reference in the description), void_document;
  replay uses replay_reverse_entry; schema 15 drops the projection's
  amount_paid/status. Fixture p4-arap-before + golden figures written by
  the version before S1, adjudicated in arap_standing.rs.
- ledger-gui: party card, cash "coming up" and company owed totals read
  doc_state as of today (late from the derived state).
- Lean Documents.lean (control_agrees and friends); Kani
  bounds_check_matches_definition,
bounds_kept_is_the_definition_on_books_in_bounds;
  rules B-38, B-18/B-23 amended; bookspec `older = true` for legacy hand
  lines; tests that hand-post to 1200/2000 post as replay or renumbered.

Review fixes (both reviews of part A):
- control_role_of guards only an account the role is assigned to or one
  holding its documents' lines (a chart's own 1200 Equipment or card at
  2000 is free until a document posts there); payment accounts check
  other roles by holds_role (type-checked). The MCP tests that failed on
  the guard (oracle_mcp, spec_projects) pass unchanged.
- standing_payments leaves out reversals (sorted, deduped); replayed
  tags match the movement's side; payments_recorded counts every payment
  event (tagged or not, Ledger.payment_events) and refuses overflow.
- The record caches settled (paid + discounted + written off) and owed
  (Invoice/Bill.owed, never serialized): a void never reads as paid.
- assign_role: receivables/payables refuse moving onto an account with
  untagged lines (RoleAccountHasHandLines) and, on their control account
  holding a balance, say DocumentRoleHoldsBalance (no hand entry named);
  B-23 reworded. The reversal-mirror exemption only takes a hand line
  off (even chain depth); putting it back is refused.
- DocStatus Overpaid and Unattached; aging leaves out credit balances;
  ControlCheck.attention lists out-of-bounds and unattached documents.
- stands_on counts a chain in order; indexes (doc_index, doc_accounts,
  order_pos, reversed_by) keep the derived figures linear.
- Viewer reads a post-dated document as of its own date; one state per
  row. bounds_kept's side-crossing documented as intended.
- Tests: ten new spec_documents scenarios, stronger proptests, store
  proptest arap_props (live = replay = snapshot at every date, closed
  months), fuzz ops void/payments with details, a GUI post-dated test;
  Lean Step.assign / Step.release with unattached_zero kept; model
  limits stated in docs/testing.md and B-38.
Review fixes (CPA and engineering lenses): only an assigned role's
account or one holding document lines is a control account (a chart's
own 1200 asset stays free); a role can't move onto untagged lines or off
document lines (Lean Step.assign); a void's cache never reads as paid;
no Decimal sum panics; per-document indexes keep figures linear; a
replayed tag matches its side; the feature gate counts payment events;
a payment's reversal is never listed as standing; an overpaid document
has its own status; the viewer keeps post-dated documents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
2026-10-24 09:17:42 -06:00

110 lines
3.0 KiB
TOML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
id = "1099-money-from-a-payee-is-netted-only-as-a-refund"
title = "Money received from a payee lowers its 1099 only when it refunds what was paid"
rule = """
A 1099 reports the payments the payer made to the payee in the calendar
year. Money the payee gives back of a payment (a refund crediting the
expense, or a deposit returned) reduces what was paid. Money received from
the payee for anything else (its payment of the payer's invoice, a sale to
it, an asset disposed of to it, a loan from it) is not a refund of a
payment and is not netted against the payments; it is listed beside them.
"""
status = "CONVENTION"
sources = [
"26 U.S.C. §6041(a) (payments made in the course of the trade or business: docs/spec/sources/irs-1099-thresholds.md)",
"wanderbooks P3 review R6 (2026-10-20): a sale to a payee doesn't lower its 1099",
]
tags = ["1099", "netting", "refund"]
[setup]
currency = "USD"
fiscal_year_start = "01-01"
basis = "cash"
rounding = "half-away-from-zero"
cash_accounts = ["1000"]
[[setup.accounts]]
code = "1000"
name = "Bank"
type = "asset"
[[setup.accounts]]
code = "1200"
name = "Accounts Receivable"
type = "asset"
[[setup.accounts]]
code = "1400"
name = "Deposits"
type = "asset"
[[setup.accounts]]
code = "2600"
name = "Loan payable"
type = "liability"
[[setup.accounts]]
code = "6000"
name = "Contract Labor"
type = "expense"
[[setup.payees]]
name = "Dana Studio"
form = "1099-NEC"
[[events]]
date = "2026-02-01"
description = "Design work"
payee = "Dana Studio"
lines = [
{ account = "6000", side = "debit", amount = "3000.00" },
{ account = "1000", side = "credit", amount = "3000.00" },
]
[[events]]
date = "2026-03-01"
description = "Dana Studio pays the company's invoice"
older = true
payee = "Dana Studio"
lines = [
{ account = "1000", side = "debit", amount = "450.00" },
{ account = "1200", side = "credit", amount = "450.00" },
]
[[events]]
date = "2026-04-01"
description = "Dana Studio lends the company money"
payee = "Dana Studio"
lines = [
{ account = "1000", side = "debit", amount = "1000.00" },
{ account = "2600", side = "credit", amount = "1000.00" },
]
[[events]]
date = "2026-05-01"
description = "Deposit for the next project"
payee = "Dana Studio"
lines = [
{ account = "1400", side = "debit", amount = "500.00" },
{ account = "1000", side = "credit", amount = "500.00" },
]
[[events]]
date = "2026-06-01"
description = "Deposit returned: the project was cancelled"
payee = "Dana Studio"
lines = [
{ account = "1000", side = "debit", amount = "500.00" },
{ account = "1400", side = "credit", amount = "500.00" },
]
# By hand: paid 3,000.00 + 500.00 (the deposit, paid when made) − 500.00
# (its return, a refund) = 3,000.00: reportable. The invoice payment
# (450.00) and the loan (1,000.00) are listed, not netted (netting them
# would leave 1,550.00, under the threshold).
[[expect]]
query = "vendor_1099"
payee = "Dana Studio"
from = "2026-01-01"
to = "2026-12-31"
values = { status = "reportable", threshold = "2000.00", paid = "3000.00", excluded_card = "0.00", undetermined = "0.00", not_reportable = "0.00" }