Parties, attachment links, statements, reconciliations (store), TIN vault crate
Parties: one directory for vendors, contractors, customers, employees, members, platforms, agencies, banks, landlords (ledger-core parties.rs). W-9/W-4 facts without the number (last four only; any nine-digit run in a party field is refused, Treas. Reg. 301.6109-4). Vendor and customer records link to parties on replay; core tags every bill, invoice, payment, and reversal with its counterparty. Journal events party.*, entry.party_set; payroll lines name the employee party. MCP: books.add_party, update_party, set_party_roles, set_w9, set_w4, parties, party, set_entry_party; draft_entry party; run_payroll employee; show party. Viewer: Parties directory and party pages, search, journal text, W-9 attention, demo data. One definition of a party's money (ledger_core::parties::activity) for MCP and viewer. 1099 amounts count payments from cash and financing accounts (a member paying for the company counts; a card doesn't; a prepayment counts when made): vendor_1099_report_with, B-19. Store: attachment links to any subject, statements, reconciliations (schema v11), with replay and projection. ledger-vault: encrypted TIN and W-9 records (ChaCha20-Poly1305, key file outside the books). Spec-blind tests for parties (core, store, MCP, viewer) and attachments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
1 parent
2a188bd039
commit
a299077991
54 files changed
+10961
-178
No files matched your search
@@ -147,7 +147,7 @@ fn mcp_end_to_end_over_stdio() {
|
||||
// Tool listing: the narrow tools; chart is read-only.
|
||||
let list = mcp.request("tools/list", json!({}));
|
||||
let tools = list["result"]["tools"].as_array().unwrap();
|
||||
assert_eq!(tools.len(), 74, "expected 74 tools, got {}", tools.len());
|
||||
assert_eq!(tools.len(), 82, "expected 82 tools, got {}", tools.len());
|
||||
let chart = tools.iter().find(|t| t["name"] == "books.chart").unwrap();
|
||||
assert_eq!(chart["annotations"]["readOnlyHint"], true);
|
||||
assert!(
|
||||
|
||||
@@ -51,11 +51,13 @@ const MONEY_KEYS: &[&str] = &[
|
||||
"adjustments",
|
||||
"after",
|
||||
"amount",
|
||||
"amount_1099",
|
||||
"assets",
|
||||
"balance",
|
||||
"balance_after",
|
||||
"balance_before",
|
||||
"before",
|
||||
"billed",
|
||||
"book_balance",
|
||||
"book_value",
|
||||
"cash_beginning",
|
||||
@@ -84,12 +86,18 @@ const MONEY_KEYS: &[&str] = &[
|
||||
"final_depreciation",
|
||||
"gain_loss",
|
||||
"gross",
|
||||
"gross_pay",
|
||||
"invoiced",
|
||||
"liabilities",
|
||||
"movement",
|
||||
"net_change_in_cash",
|
||||
"net_income",
|
||||
"net_pay",
|
||||
"owed",
|
||||
"paid",
|
||||
"proceeds",
|
||||
"received",
|
||||
"receivable",
|
||||
"remaining",
|
||||
"revenue",
|
||||
"salvage_value",
|
||||
@@ -588,6 +596,7 @@ fn happy_path(name: &str) -> Mcp {
|
||||
m.ok("books.list_recurring", json!({}));
|
||||
m.ok("books.list_schedules", json!({}));
|
||||
m.ok("books.list_vendors", json!({}));
|
||||
m.ok("books.parties", json!({}));
|
||||
m.ok("books.digest", json!({"since": "2026-01-01"}));
|
||||
|
||||
// Chart.
|
||||
@@ -645,6 +654,28 @@ fn happy_path(name: &str) -> Mcp {
|
||||
json!({"vendor": "Landlord", "form_1099": "misc", "tin_on_file": true}),
|
||||
);
|
||||
m.ok("books.list_vendors", json!({}));
|
||||
m.ok(
|
||||
"books.add_party",
|
||||
json!({"name": "Pat", "roles": ["employee"]}),
|
||||
);
|
||||
m.ok(
|
||||
"books.update_party",
|
||||
json!({"party": "Pat", "display_name": "Pat D.", "notes": "first hire"}),
|
||||
);
|
||||
m.ok(
|
||||
"books.set_w4",
|
||||
json!({"party": "Pat", "received_on": "2026-01-02", "state": "UT", "kept_at": "binder"}),
|
||||
);
|
||||
m.ok(
|
||||
"books.set_party_roles",
|
||||
json!({"party": "Landlord", "add": ["landlord"]}),
|
||||
);
|
||||
m.ok(
|
||||
"books.set_w9",
|
||||
json!({"party": "Landlord", "received_on": "2026-01-03", "entity_type": "LLC (partnership)",
|
||||
"tin_last4": "4321", "kept_at": "CPA portal"}),
|
||||
);
|
||||
m.ok("books.parties", json!({"role": "landlord", "year": 2026}));
|
||||
|
||||
// Owner contribution: draft, lines, receipt, preview, post.
|
||||
let e1 = m.draft(
|
||||
@@ -693,6 +724,10 @@ fn happy_path(name: &str) -> Mcp {
|
||||
"books.set_entry_vendor",
|
||||
json!({"entry_id": e2, "vendor": vendor.to_string()}),
|
||||
);
|
||||
m.ok(
|
||||
"books.set_entry_party",
|
||||
json!({"entry_id": e2, "party": "Landlord"}),
|
||||
);
|
||||
// Replace the receipt: attach new, unlink old, retire it.
|
||||
m.attach(Some(e2), b"rent receipt v2");
|
||||
m.ok(
|
||||
@@ -853,7 +888,7 @@ fn happy_path(name: &str) -> Mcp {
|
||||
m.ok(
|
||||
"books.run_payroll",
|
||||
json!({"date": "2026-02-15", "cash_account": "1000", "lines": [{
|
||||
"employee_name": "Pat", "gross_pay": "1000.00", "federal_withholding": "100.00",
|
||||
"employee": "Pat", "gross_pay": "1000.00", "federal_withholding": "100.00",
|
||||
"state_withholding": "50.00", "ss_employee": "62.00", "medicare_employee": "14.50",
|
||||
"ss_employer": "62.00", "medicare_employer": "14.50", "other_deductions": "0.00"}]}),
|
||||
);
|
||||
@@ -908,7 +943,11 @@ fn happy_path(name: &str) -> Mcp {
|
||||
m.ok("books.digest", json!({"since": "2026-01-01"}));
|
||||
m.ok("books.search_entries", json!({"text": "rent", "limit": 10}));
|
||||
m.ok("books.vendor_1099", json!({"year": 2026}));
|
||||
m.ok("books.party", json!({"party": "Landlord", "year": 2026}));
|
||||
m.ok("books.party", json!({"party": "Pat", "year": 2026}));
|
||||
m.ok("books.party", json!({"party": "Client"}));
|
||||
m.ok("books.verify", json!({}));
|
||||
m.ok("books.show", json!({"party": "Landlord"}));
|
||||
m.ok("books.show", json!({"entry_id": e1}));
|
||||
m.ok(
|
||||
"books.show",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user