refactor(mcp): the party_id, document_id and cash_account aliases are removed (P5)

Agent reason: one name for one thing. S4 and S6 kept the old spellings "until
P5" so an agent that learned them did not break; P5 removes them, so the
schema an agent reads has one parameter for each fact and a call cannot name
the same thing twice with different answers.

books.create_invoice takes customer and books.create_bill vendor (a record's
id, its party's id or its name; required), no party_id.
books.receive_payment and books.pay_bill take invoice or bill and
deposit_to or paid_from, no document_id or cash_account; a payment names its
document and its account or is refused saying which to pass. A number that is
one record's id and another's name is still refused, never guessed (INVARIANT
6).

The over-the-wire tests that used the old spellings now use the new
(document_id becomes invoice or bill, cash_account becomes deposit_to or
paid_from, party_id becomes customer or vendor given as the id's text); the
one test that named the alias, party_id_alias_still_works, is now
a_customer_by_id_and_by_name_is_one_record and checks the same two spellings
of a customer, by id and by name, name one record.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
swaitsandClaude Fable 5.1 committed 2026-10-29 15:48:27 -06:00
1 parent 70a3f795f4
commit a319a64c3a
13 files changed
+81 -135

No files matched your search

+15 -15
View File
@@ -496,18 +496,18 @@ fn terms_fall_back_in_order() {
assert_eq!(bill["terms"], "Net 10", "{bill}");
}
/// `party_id` is the old spelling of `customer`/`vendor` and still names
/// the same record (until P5); a number that is one record's id and
/// A customer given by id and by name is the same record (the old
/// `party_id` spelling is gone, P5); a number that is one record's id and
/// another's name is refused, never guessed.
#[test]
fn party_id_alias_still_works() {
fn a_customer_by_id_and_by_name_is_one_record() {
let books = Books::new("documents-alias");
let mut m = Mcp::spawn(&books);
let harbor = m.customer("Harbor Coffee");
let line = json!([{"description": "Work", "amount": "10.00"}]);
let by_alias = m.call(
let by_id = m.call(
"books.create_invoice",
json!({"party_id": harbor, "date": "2026-02-01", "terms": "Net 30",
json!({"customer": (harbor).to_string(), "date": "2026-02-01", "terms": "Net 30",
"lines": line, "confirmed": true}),
);
let by_name = m.call(
@@ -520,8 +520,8 @@ fn party_id_alias_still_works() {
.iter()
.map(|e| e["payload"]["customer_id"].as_u64().unwrap())
.collect();
assert_eq!(parties, [harbor, harbor], "one customer, two spellings");
assert_eq!(by_alias["total"], by_name["total"]);
assert_eq!(parties, [harbor, harbor], "one customer, an id and a name");
assert_eq!(by_id["total"], by_name["total"]);
// A customer named "2" that is not customer 2: the number is
// ambiguous, and is refused rather than guessed.
@@ -730,7 +730,7 @@ fn mark_sent_records_who_and_how() {
.unwrap();
let invoice = common::answer_of(&wire.call(
"books.create_invoice",
json!({"party_id": customer, "date": "2026-03-01", "due_date": "2026-03-31",
json!({"customer": (customer).to_string(), "date": "2026-03-01", "due_date": "2026-03-31",
"lines": [{"description": "Design", "amount": "1200.00"}], "confirmed": true}),
))
.unwrap()["id"]
@@ -876,7 +876,7 @@ mod paying_on_terms {
ok(
&mut self.wire,
"books.create_invoice",
json!({"party_id": self.customer, "date": date, "due_date": due,
json!({"customer": (self.customer).to_string(), "date": date, "due_date": due,
"lines": [{"description": "Consulting", "amount": amount}],
"confirmed": true}),
)["id"]
@@ -897,7 +897,7 @@ mod paying_on_terms {
ok(
&mut self.wire,
"books.create_bill",
json!({"party_id": self.vendor, "date": date, "due_date": due,
json!({"vendor": (self.vendor).to_string(), "date": date, "due_date": due,
"vendor_ref": format!("TH-{}", self.bills),
"lines": [{"description": "Paper", "amount": amount, "account": "6999"}],
"upload_id": file, "confirmed": true}),
@@ -1258,7 +1258,7 @@ mod bad_debts {
wire,
"books.create_invoice",
json!({
"party_id": customer,
"customer": (customer).to_string(),
"date": "2026-01-05",
"due_date": "2026-02-04",
"lines": [{ "description": "Design", "amount": "100.00" }],
@@ -1286,10 +1286,10 @@ mod bad_debts {
&mut wire,
"books.receive_payment",
json!({"confirmed": true,
"document_id": inv,
"invoice": inv,
"date": "2026-01-20",
"amount": "40.00",
"cash_account": "1000",
"deposit_to": "1000",
}),
);
// The write-off's evidence, uploaded as an agent uploads it (D-46).
@@ -1361,10 +1361,10 @@ mod bad_debts {
&mut wire,
"books.receive_payment",
json!({"confirmed": true,
"document_id": inv,
"invoice": inv,
"date": "2026-03-02",
"amount": "60.00",
"cash_account": "1000",
"deposit_to": "1000",
}),
);
let doc = ok(
+2 -2
View File
@@ -767,7 +767,7 @@ fn books_p2_served_cut_over_whole() {
let invoice = a
.call(
"books.create_invoice",
json!({"party_id": 1, "date": "2026-01-05", "due_date": "2026-02-04",
json!({"customer": (1).to_string(), "date": "2026-01-05", "due_date": "2026-02-04",
"lines": [{"description": "Design", "amount": "400.00"}], "confirmed": true}),
)
.unwrap()["id"]
@@ -789,7 +789,7 @@ fn books_p2_served_cut_over_whole() {
let unpaid = a
.call(
"books.create_invoice",
json!({"party_id": customer, "date": "2026-01-10", "due_date": "2026-02-09",
json!({"customer": (customer).to_string(), "date": "2026-01-10", "due_date": "2026-02-09",
"lines": [{"description": "Survey", "amount": "300.00"}], "confirmed": true}),
)
.unwrap()["id"]
+3 -3
View File
@@ -1017,13 +1017,13 @@ fn inputs_that_once_panicked_are_refused_or_answered() {
),
(
"books.create_invoice",
json!({"party_id": client, "date": "2026-05-01", "due_date": "2026-06-01",
json!({"customer": (client).to_string(), "date": "2026-05-01", "due_date": "2026-06-01",
"lines": [{"description": "a", "amount": MAX},
{"description": "b", "amount": "1"}], "confirmed": true}),
),
(
"books.create_bill",
json!({"party_id": acme, "date": "2026-05-01", "due_date": "2026-06-01",
json!({"vendor": (acme).to_string(), "date": "2026-05-01", "due_date": "2026-06-01",
"vendor_ref": "H-1",
"lines": [{"description": "a", "amount": MAX, "account": "6000"},
{"description": "b", "amount": "1", "account": "6000"}],
@@ -1031,7 +1031,7 @@ fn inputs_that_once_panicked_are_refused_or_answered() {
),
(
"books.create_invoice",
json!({"party_id": client, "date": "2026-05-01", "due_date": "+262142-12-31",
json!({"customer": (client).to_string(), "date": "2026-05-01", "due_date": "+262142-12-31",
"lines": [{"description": "a", "amount": "1"}], "confirmed": true}),
),
(
+1 -1
View File
@@ -274,7 +274,7 @@ fn invoiced_and_billed_project_figures_are_the_same_in_the_viewer_and_from_the_s
let bill = server.tool("books.create_bill", bill_args);
server.tool(
"books.receive_payment",
json!({"confirmed": true, "document_id": inv["id"], "date": "2026-02-20", "amount": "1830.00", "cash_account": "1000"}),
json!({"confirmed": true, "invoice": inv["id"], "date": "2026-02-20", "amount": "1830.00", "deposit_to": "1000"}),
);
let b = data::load_once(&Source {
data_dir: data.clone(),
+2 -2
View File
@@ -424,7 +424,7 @@ fn a_file_links_to_every_kind_of_subject() {
let rent_invoice = m.upload(b"rent invoice", "rent.pdf", "application/pdf", "receipt");
let bill = m.ok(
"books.create_bill",
json!({"party_id": vendor, "date": "2026-02-01", "due_date": "2026-02-28",
json!({"vendor": (vendor).to_string(), "date": "2026-02-01", "due_date": "2026-02-28",
"vendor_ref": "RENT-2",
"lines": [{"description": "Rent", "amount": "300.00", "account": "6000"}],
"upload_id": rent_invoice, "confirmed": true}),
@@ -432,7 +432,7 @@ fn a_file_links_to_every_kind_of_subject() {
.clone();
let invoice = m.ok(
"books.create_invoice",
json!({"party_id": customer, "date": "2026-02-01", "due_date": "2026-03-01",
json!({"customer": (customer).to_string(), "date": "2026-02-01", "due_date": "2026-03-01",
"lines": [{"description": "Work", "amount": "500.00"}],
"confirmed": true}),
)["id"]
+17 -17
View File
@@ -911,7 +911,7 @@ fn happy_path(name: &str) -> Mcp {
// A/R and A/P.
let inv = m.ok(
"books.create_invoice",
json!({"party_id": customer, "date": "2026-02-01", "due_date": "2026-03-01",
json!({"customer": (customer).to_string(), "date": "2026-02-01", "due_date": "2026-03-01",
"lines": [{"description": "Consulting", "amount": "500.00"}],
"confirmed": true}),
)["id"]
@@ -919,7 +919,7 @@ fn happy_path(name: &str) -> Mcp {
.unwrap();
m.ok(
"books.receive_payment",
json!({"confirmed": true, "document_id": inv, "date": "2026-02-10", "amount": "200.00", "cash_account": "1000"}),
json!({"confirmed": true, "invoice": inv, "date": "2026-02-10", "amount": "200.00", "deposit_to": "1000"}),
);
let vendor_invoice = m.upload(
b"vendor invoice",
@@ -929,7 +929,7 @@ fn happy_path(name: &str) -> Mcp {
);
let bill = m.ok(
"books.create_bill",
json!({"party_id": vendor, "date": "2026-02-01", "due_date": "2026-02-28",
json!({"vendor": (vendor).to_string(), "date": "2026-02-01", "due_date": "2026-02-28",
"vendor_ref": "V-1001",
"lines": [{"description": "Office rent", "amount": "300.00", "account": "6000"}],
"upload_id": vendor_invoice, "confirmed": true}),
@@ -938,7 +938,7 @@ fn happy_path(name: &str) -> Mcp {
.unwrap();
m.ok(
"books.pay_bill",
json!({"confirmed": true, "document_id": bill, "date": "2026-02-05", "amount": "300.00", "cash_account": "1000"}),
json!({"confirmed": true, "bill": bill, "date": "2026-02-05", "amount": "300.00", "paid_from": "1000"}),
);
m.ok("books.ar_aging", json!({"as_of": "2026-06-30"}));
m.ok("books.ap_aging", json!({"as_of": "2026-06-30"}));
@@ -946,7 +946,7 @@ fn happy_path(name: &str) -> Mcp {
// P4b S1: a document voided, and the control accounts' tie-out.
let dup = m.ok(
"books.create_invoice",
json!({"party_id": customer, "date": "2026-02-02", "due_date": "2026-03-02",
json!({"customer": (customer).to_string(), "date": "2026-02-02", "due_date": "2026-03-02",
"lines": [{"description": "Consulting, entered twice", "amount": "500.00"}],
"confirmed": true}),
)["id"]
@@ -972,7 +972,7 @@ fn happy_path(name: &str) -> Mcp {
// P4b S7: a bad debt, its evidence uploaded and filed with it.
let bad = m.ok(
"books.create_invoice",
json!({"party_id": customer, "date": "2026-02-03", "due_date": "2026-03-03",
json!({"customer": (customer).to_string(), "date": "2026-02-03", "due_date": "2026-03-03",
"lines": [{"description": "Consulting, never paid", "amount": "400.00"}],
"confirmed": true}),
)["id"]
@@ -1685,15 +1685,15 @@ fn run_op(m: &mut Mcp, k: &mut Known, op: &Op) {
vendor_ref,
} => (
"books.create_bill",
json!({"party_id": party.resolve(&k.vendors), "date": "2026-03-01",
json!({"vendor": (party.resolve(&k.vendors)).to_string(), "date": "2026-03-01",
"due_date": "2026-03-31", "vendor_ref": vendor_ref,
"lines": [{"description": "thing", "amount": amount, "account": "6000"}],
"existing_attachment_id": k.vendor_invoice, "confirmed": true}),
),
Op::PayBill { doc, amount } => (
"books.pay_bill",
json!({"confirmed": true, "document_id": doc.resolve(&k.bills), "date": "2026-03-15", "amount": amount,
"cash_account": "1000"}),
json!({"confirmed": true, "bill": doc.resolve(&k.bills), "date": "2026-03-15", "amount": amount,
"paid_from": "1000"}),
),
Op::Payout { deposit } => (
"books.record_payout",
@@ -1834,7 +1834,7 @@ fn inv3_other_tools_refuse_ungrammatical_amounts() {
);
m.refuse(
"books.create_bill",
json!({"party_id": vendor, "date": "2026-03-01", "due_date": "2026-03-31",
json!({"vendor": (vendor).to_string(), "date": "2026-03-01", "due_date": "2026-03-31",
"lines": [{"description": "x", "amount": bad}]}),
);
m.refuse(
@@ -2591,7 +2591,7 @@ fn assigned_roles_steer_invoices() {
.unwrap();
m.ok(
"books.create_invoice",
json!({"party_id": customer, "date": "2026-02-01", "due_date": "2026-03-01",
json!({"customer": (customer).to_string(), "date": "2026-02-01", "due_date": "2026-03-01",
"lines": [{"description": "Consulting", "amount": "500.00"}],
"confirmed": true}),
);
@@ -2654,7 +2654,7 @@ fn documents_void_and_tie_out_over_the_wire() {
);
let inv = m.ok(
"books.create_invoice",
json!({"party_id": customer, "date": "2026-02-01", "due_date": "2026-03-01",
json!({"customer": (customer).to_string(), "date": "2026-02-01", "due_date": "2026-03-01",
"lines": [{"description": "Consulting", "amount": "500.00"}],
"confirmed": true}),
)["id"]
@@ -2662,8 +2662,8 @@ fn documents_void_and_tie_out_over_the_wire() {
.unwrap();
let paid = m.ok(
"books.receive_payment",
json!({"confirmed": true, "document_id": inv, "date": "2026-02-10", "amount": "200.00",
"cash_account": "1000", "instrument": "Harbor checking", "reference": "tr_8812"}),
json!({"confirmed": true, "invoice": inv, "date": "2026-02-10", "amount": "200.00",
"deposit_to": "1000", "instrument": "Harbor checking", "reference": "tr_8812"}),
);
assert_eq!(paid["remaining"], "300.00", "{paid}");
assert_eq!(paid["status"], "partial", "{paid}");
@@ -2685,7 +2685,7 @@ fn documents_void_and_tie_out_over_the_wire() {
let dup = m.ok(
"books.create_invoice",
json!({"party_id": customer, "date": "2026-02-02", "due_date": "2026-03-02",
json!({"customer": (customer).to_string(), "date": "2026-02-02", "due_date": "2026-03-02",
"lines": [{"description": "Consulting, entered twice", "amount": "500.00"}],
"confirmed": true}),
)["id"]
@@ -2753,7 +2753,7 @@ fn documents_list_and_timeline_over_the_wire() {
let invoice = |m: &mut Mcp, date: &str, due: &str, amount: &str| {
m.ok(
"books.create_invoice",
json!({"party_id": customer, "date": date, "due_date": due,
json!({"customer": (customer).to_string(), "date": date, "due_date": due,
"lines": [{"description": "Consulting", "amount": amount}],
"confirmed": true}),
)["id"]
@@ -2764,7 +2764,7 @@ fn documents_list_and_timeline_over_the_wire() {
let later = invoice(&mut m, "2026-02-05", "2026-04-30", "80.00");
let pay = m.ok(
"books.receive_payment",
json!({"confirmed": true, "document_id": late, "date": "2026-02-10", "amount": "200.00", "cash_account": "1000"}),
json!({"confirmed": true, "invoice": late, "date": "2026-02-10", "amount": "200.00", "deposit_to": "1000"}),
)["entry_id"]
.as_u64()
.unwrap();
+6 -6
View File
@@ -835,15 +835,15 @@ fn party_totals_match_a_hand_computed_year() {
);
let bill = m.ok(
"books.create_bill",
json!({"party_id": v, "date": "2026-07-01", "due_date": "2026-07-31",
json!({"vendor": (v).to_string(), "date": "2026-07-01", "due_date": "2026-07-31",
"vendor_ref": "D-1041",
"lines": [{"description": "Logo", "amount": "800.00", "account": "6000"}],
"upload_id": logo_invoice, "confirmed": true}),
);
let pay = m.ok(
"books.pay_bill",
json!({"confirmed": true, "document_id": bill["id"], "date": "2026-07-15", "amount": "300.00",
"cash_account": "1000"}),
json!({"confirmed": true, "bill": bill["id"], "date": "2026-07-15", "amount": "300.00",
"paid_from": "1000"}),
)["entry_id"]
.as_u64()
.unwrap();
@@ -917,14 +917,14 @@ fn party_totals_for_a_customer_a_member_and_a_prepaid() {
.unwrap();
let inv = m.ok(
"books.create_invoice",
json!({"party_id": c, "date": "2026-03-01", "due_date": "2026-03-31",
json!({"customer": (c).to_string(), "date": "2026-03-01", "due_date": "2026-03-31",
"lines": [{"description": "Work", "amount": "1000.00"}],
"confirmed": true}),
);
let pay = m.ok(
"books.receive_payment",
json!({"confirmed": true, "document_id": inv["id"], "date": "2026-03-20", "amount": "600.00",
"cash_account": "1000"}),
json!({"confirmed": true, "invoice": inv["id"], "date": "2026-03-20", "amount": "600.00",
"deposit_to": "1000"}),
)["entry_id"]
.as_u64()
.unwrap();
+10 -10
View File
@@ -1231,7 +1231,7 @@ fn invoice_and_bill_lines_count_toward_their_projects() {
);
let inv = m.ok(
"books.create_invoice",
json!({"party_id": cedar, "date": "2026-03-02", "due_date": "2026-04-01", "lines": [
json!({"customer": (cedar).to_string(), "date": "2026-03-02", "due_date": "2026-04-01", "lines": [
{"description": "Design", "amount": "3000.00", "class": "Zeta site"},
{"description": "Hosting", "amount": "200.00", "class": harbor.to_string()},
{"description": "Rush fee", "amount": "100.00"},
@@ -1264,7 +1264,7 @@ fn invoice_and_bill_lines_count_toward_their_projects() {
);
let bill = m.ok(
"books.create_bill",
json!({"party_id": dana_vendor, "date": "2026-03-09", "due_date": "2026-04-08",
json!({"vendor": (dana_vendor).to_string(), "date": "2026-03-09", "due_date": "2026-04-08",
"vendor_ref": "DANA-77",
"lines": [
{"description": "Illustration", "amount": "1200.00", "account": "6000", "class": "Zeta site"},
@@ -1284,11 +1284,11 @@ fn invoice_and_bill_lines_count_toward_their_projects() {
// Payments are balance-sheet only: no project.
let got = m.ok(
"books.receive_payment",
json!({"confirmed": true, "document_id": inv["id"], "date": "2026-03-20", "amount": "3800.00", "cash_account": "1000"}),
json!({"confirmed": true, "invoice": inv["id"], "date": "2026-03-20", "amount": "3800.00", "deposit_to": "1000"}),
);
let paid = m.ok(
"books.pay_bill",
json!({"confirmed": true, "document_id": bill["id"], "date": "2026-03-30", "amount": "1250.00", "cash_account": "1000"}),
json!({"confirmed": true, "bill": bill["id"], "date": "2026-03-30", "amount": "1250.00", "paid_from": "1000"}),
);
for e in [&got["entry_id"], &paid["entry_id"]] {
let e = e.as_u64().unwrap();
@@ -1356,7 +1356,7 @@ fn a_document_naming_an_unknown_project_is_refused_whole() {
for (tool, party) in [("books.create_invoice", &cedar), ("books.create_bill", &v)] {
let why = m.refuse(
tool,
json!({"party_id": party, "date": "2026-03-02", "due_date": "2026-04-01", "lines": [
json!({"customer": (party).to_string(), "date": "2026-03-02", "due_date": "2026-04-01", "lines": [
{"description": "a", "amount": "10.00", "class": "Zeta site"},
{"description": "b", "amount": "10.00", "class": "Nowhere"},
]}),
@@ -1379,7 +1379,7 @@ fn a_document_for_a_finished_project_says_so() {
let cedar = m.ok("books.add_customer", json!({"name": "Cedar"}))["id"].clone();
let out = m.ok(
"books.create_invoice",
json!({"party_id": cedar, "date": "2026-10-05", "due_date": "2026-11-04", "lines": [
json!({"customer": (cedar).to_string(), "date": "2026-10-05", "due_date": "2026-11-04", "lines": [
{"description": "Final", "amount": "10.00", "class": "Harbor"},
{"description": "Again", "amount": "10.00", "class": "harbor"},
{"description": "Old", "amount": "10.00", "class": "Dock"},
@@ -1424,7 +1424,7 @@ fn an_invoice_for_an_on_spec_project_asks_about_an_agreement() {
let v = m.ok("books.add_vendor", json!({"name": "Dana"}))["id"].clone();
let out = m.ok(
"books.create_invoice",
json!({"party_id": cedar, "date": "2026-10-05", "due_date": "2026-11-04", "lines": [
json!({"customer": (cedar).to_string(), "date": "2026-10-05", "due_date": "2026-11-04", "lines": [
{"description": "Build", "amount": "10.00", "class": "Kestrel"},
{"description": "More", "amount": "5.00", "class": "kestrel"},
{"description": "Site", "amount": "7.00", "class": "Zeta"},
@@ -1450,7 +1450,7 @@ fn an_invoice_for_an_on_spec_project_asks_about_an_agreement() {
);
let bill = m.ok(
"books.create_bill",
json!({"party_id": v, "date": "2026-10-05", "due_date": "2026-11-04",
json!({"vendor": (v).to_string(), "date": "2026-10-05", "due_date": "2026-11-04",
"vendor_ref": "V-512",
"lines": [
{"description": "Icons", "amount": "10.00", "account": "6000", "class": "Kestrel"},
@@ -1475,7 +1475,7 @@ fn books_without_the_feature_refuse_a_tagged_document() {
for (tool, party) in [("books.create_invoice", &cedar), ("books.create_bill", &v)] {
let why = m.refuse(
tool,
json!({"party_id": party, "date": "2026-03-02", "due_date": "2026-04-01", "lines": [
json!({"customer": (party).to_string(), "date": "2026-03-02", "due_date": "2026-04-01", "lines": [
{"description": "a", "amount": "10.00", "class": "Zeta site"},
]}),
);
@@ -1487,7 +1487,7 @@ fn books_without_the_feature_refuse_a_tagged_document() {
assert_eq!(m.journal(), head, "nothing journaled");
m.ok(
"books.create_invoice",
json!({"party_id": cedar, "date": "2026-03-02", "due_date": "2026-04-01", "lines": [
json!({"customer": (cedar).to_string(), "date": "2026-03-02", "due_date": "2026-04-01", "lines": [
{"description": "a", "amount": "10.00"},
], "confirmed": true}),
);
+2 -2
View File
@@ -278,7 +278,7 @@ fn require_tax_category_refuses_defect_8() {
);
let bill = m.call(
"books.create_bill",
json!({"party_id": vendor["id"], "date": "2026-02-01", "due_date": "2026-03-01",
json!({"vendor": (vendor["id"]).to_string(), "date": "2026-02-01", "due_date": "2026-03-01",
"vendor_ref": "CH-1",
"lines": [{"description": "hosting", "amount": "42.00", "account": "6150"}],
"upload_id": first_invoice, "confirmed": true}),
@@ -303,7 +303,7 @@ fn require_tax_category_refuses_defect_8() {
);
let again = m.call(
"books.create_bill",
json!({"party_id": vendor["id"], "date": "2026-02-02", "due_date": "2026-03-02",
json!({"vendor": (vendor["id"]).to_string(), "date": "2026-02-02", "due_date": "2026-03-02",
"vendor_ref": "CH-2",
"lines": [{"description": "hosting", "amount": "1.00", "account": "6150"}],
"upload_id": second_invoice, "confirmed": true}),
+2 -22
View File
@@ -455,17 +455,7 @@ impl BooksServer {
) -> Result<Json<InvoiceOut>, String> {
let p = params.0;
let mut store = self.lock()?;
let customer = match (&p.customer, p.party_id) {
(Some(c), None) => c.trim().to_string(),
(None, Some(id)) => id.to_string(),
(Some(c), Some(id)) if c.trim() == id.to_string() => c.trim().to_string(),
(Some(_), Some(_)) => {
return Err("give the customer once: customer or party_id, not both".into());
}
(None, None) => {
return Err("give customer: a customer id, a party id or a name".into());
}
};
let customer = p.customer.trim().to_string();
let customer_id = resolve_customer(&store, &customer)?;
let date = parse_date(&p.date)?;
let lines = document_lines(&store, p.lines)?;
@@ -606,17 +596,7 @@ impl BooksServer {
) -> Result<Json<BillOut>, String> {
let p = params.0;
let mut store = self.lock()?;
let vendor = match (&p.vendor, p.party_id) {
(Some(v), None) => v.trim().to_string(),
(None, Some(id)) => id.to_string(),
(Some(v), Some(id)) if v.trim() == id.to_string() => v.trim().to_string(),
(Some(_), Some(_)) => {
return Err("give the vendor once: vendor or party_id, not both".into());
}
(None, None) => {
return Err("give vendor: a vendor id, a party id or a name".into());
}
};
let vendor = p.vendor.trim().to_string();
let vendor_id = resolve_vendor_arg(&store, &vendor)?;
let date = parse_date(&p.date)?;
let received_on = p.received_on.as_deref().map(parse_date).transpose()?;
+9 -29
View File
@@ -39,35 +39,17 @@ fn doc_party(ledger: &Ledger, doc: ledger_core::DocRef) -> Result<u64, String> {
}
}
/// The one id a payment tool's single form names: `invoice`/`bill`, or
/// the older `document_id`. Both at once is refused unless they agree.
fn single_document(primary: Option<u64>, alias: Option<u64>, what: &str) -> Result<u64, String> {
match (primary, alias) {
(Some(a), Some(b)) if a != b => Err(format!(
"pass {what} or document_id, not both: {} and {b} differ",
a
)),
(Some(id), _) | (None, Some(id)) => Ok(id),
(None, None) => Err(format!(
"pass {what} (or an applications list): see books.documents"
)),
}
/// The one id a payment tool's single form names: `invoice` or `bill`.
fn single_document(primary: Option<u64>, what: &str) -> Result<u64, String> {
primary.ok_or_else(|| format!("pass {what} (or an applications list): see books.documents"))
}
/// The one account a payment tool's single form names: `deposit_to`/
/// `paid_from`, or the older `cash_account`.
fn single_account(
primary: &Option<String>,
alias: &Option<String>,
what: &str,
) -> Result<String, String> {
match (primary, alias) {
(Some(a), Some(b)) if a != b => Err(format!(
"pass {what} or cash_account, not both: {a:?} and {b:?} differ"
)),
(Some(a), _) | (None, Some(a)) => Ok(a.clone()),
(None, None) => Err(format!("pass {what} (or cash_account)")),
}
/// The one account a payment tool's single form names: `deposit_to` or
/// `paid_from`.
fn single_account(primary: &Option<String>, what: &str) -> Result<String, String> {
primary
.clone()
.ok_or_else(|| format!("pass {what}: the account the cash moves through"))
}
/// Every document a payment tool's parameters name, as the core takes
@@ -128,7 +110,6 @@ fn applications(
}
let id = single_document(
if invoices { p.invoice } else { p.bill },
p.document_id,
if invoices { "invoice" } else { "bill" },
)?;
let amount = p
@@ -178,7 +159,6 @@ fn settlement(
} else {
&p.paid_from
},
&p.cash_account,
if invoices { "deposit_to" } else { "paid_from" },
)?,
)?;
+2 -16
View File
@@ -3265,10 +3265,7 @@ pub struct CreateInvoiceParams {
pub preview: Option<String>,
/// The customer: a customer id, its party's id, or its name. A name
/// that two customers answer to is refused.
pub customer: Option<String>,
/// The old spelling of `customer` (accepted until P5): a customer id.
#[serde(default)]
pub party_id: Option<u64>,
pub customer: String,
/// The invoice date, YYYY-MM-DD.
pub date: String,
/// Its payment terms ("Net 30", "Net 15 EOM", "2/10 net 30", "Due on
@@ -3300,10 +3297,7 @@ pub struct CreateBillParams {
pub preview: Option<String>,
/// The vendor: a vendor id, its party's id, or its name. A name that
/// two vendors answer to is refused.
pub vendor: Option<String>,
/// The old spelling of `vendor` (accepted until P5): a vendor id.
#[serde(default)]
pub party_id: Option<u64>,
pub vendor: String,
/// The bill's date, YYYY-MM-DD.
pub date: String,
/// The day the bill was received, when it differs from its date.
@@ -3383,10 +3377,6 @@ pub struct PaymentParams {
/// preview: nothing posts.
#[serde(default)]
pub confirmed: Option<bool>,
/// Invoice id (receive_payment) or bill id (pay_bill); the older name
/// of `invoice` / `bill`.
#[serde(default)]
pub document_id: Option<u64>,
/// The invoice, by id (books.receive_payment).
#[serde(default)]
pub invoice: Option<u64>,
@@ -3399,10 +3389,6 @@ pub struct PaymentParams {
/// applications' amounts are the cash; a discount is beside it.
#[serde(default)]
pub amount: Option<String>,
/// Cash account id or code; the older name of `deposit_to` /
/// `paid_from`.
#[serde(default)]
pub cash_account: Option<String>,
/// The account the cash reaches (receive_payment): a bank or card that
/// holds no role.
#[serde(default)]