Regulators and registries that don't tax the company (a division of corporations, a licensing board), distinct from tax_agency. Core Role, MCP descriptions, viewer label/noun/icon, docs, and tests. 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
2275948a64
commit
9927e56431
8 files changed
+27
-8
No files matched your search
@@ -51,6 +51,9 @@ pub enum Role {
|
||||
PayoutPlatform,
|
||||
/// Taxes the company (the IRS, a state).
|
||||
TaxAgency,
|
||||
/// Regulates or registers the company without taxing it (a secretary
|
||||
/// of state, a division of corporations, a licensing board).
|
||||
GovernmentAgency,
|
||||
/// Holds the company's money or credit (a bank, a card issuer).
|
||||
FinancialInstitution,
|
||||
/// Rents property to the company (gets a 1099-MISC for rent).
|
||||
@@ -59,7 +62,7 @@ pub enum Role {
|
||||
|
||||
impl Role {
|
||||
/// Every role, in display order.
|
||||
pub const ALL: [Role; 9] = [
|
||||
pub const ALL: [Role; 10] = [
|
||||
Role::Vendor,
|
||||
Role::Contractor,
|
||||
Role::Customer,
|
||||
@@ -67,6 +70,7 @@ impl Role {
|
||||
Role::Member,
|
||||
Role::PayoutPlatform,
|
||||
Role::TaxAgency,
|
||||
Role::GovernmentAgency,
|
||||
Role::FinancialInstitution,
|
||||
Role::Landlord,
|
||||
];
|
||||
@@ -81,6 +85,7 @@ impl Role {
|
||||
Role::Member => "member",
|
||||
Role::PayoutPlatform => "payout_platform",
|
||||
Role::TaxAgency => "tax_agency",
|
||||
Role::GovernmentAgency => "government_agency",
|
||||
Role::FinancialInstitution => "financial_institution",
|
||||
Role::Landlord => "landlord",
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ fn gets_1099_is_contractor_and_landlord_only() {
|
||||
#[test]
|
||||
fn role_keys_round_trip() {
|
||||
let all: BTreeSet<Role> = Role::ALL.into_iter().collect();
|
||||
assert_eq!(all.len(), 9, "nine distinct roles");
|
||||
assert_eq!(all.len(), 10, "ten distinct roles");
|
||||
for r in [
|
||||
Role::Vendor,
|
||||
Role::Contractor,
|
||||
@@ -335,6 +335,7 @@ fn role_keys_round_trip() {
|
||||
Role::Member,
|
||||
Role::PayoutPlatform,
|
||||
Role::TaxAgency,
|
||||
Role::GovernmentAgency,
|
||||
Role::FinancialInstitution,
|
||||
Role::Landlord,
|
||||
] {
|
||||
@@ -343,6 +344,7 @@ fn role_keys_round_trip() {
|
||||
}
|
||||
assert_eq!(Role::PayoutPlatform.key(), "payout_platform");
|
||||
assert_eq!(Role::TaxAgency.key(), "tax_agency");
|
||||
assert_eq!(Role::GovernmentAgency.key(), "government_agency");
|
||||
assert_eq!(Role::FinancialInstitution.key(), "financial_institution");
|
||||
assert_eq!(Role::Contractor.key(), "contractor");
|
||||
assert_eq!(Role::parse("boss"), None);
|
||||
|
||||
@@ -94,6 +94,7 @@ pub fn role_label(r: Role) -> &'static str {
|
||||
Role::Member => "Member",
|
||||
Role::PayoutPlatform => "Payout platform",
|
||||
Role::TaxAgency => "Tax agency",
|
||||
Role::GovernmentAgency => "Government agency",
|
||||
Role::FinancialInstitution => "Bank or card issuer",
|
||||
Role::Landlord => "Landlord",
|
||||
}
|
||||
@@ -109,6 +110,7 @@ pub fn role_noun(r: Role) -> &'static str {
|
||||
Role::Member => "a member",
|
||||
Role::PayoutPlatform => "a payout platform",
|
||||
Role::TaxAgency => "a tax agency",
|
||||
Role::GovernmentAgency => "a government agency",
|
||||
Role::FinancialInstitution => "a bank or card issuer",
|
||||
Role::Landlord => "a landlord",
|
||||
}
|
||||
@@ -134,6 +136,8 @@ pub fn party_icon(p: &Party) -> &'static str {
|
||||
let has = |x| r.contains(&x);
|
||||
if has(Role::TaxAgency) {
|
||||
icon::BANK
|
||||
} else if has(Role::GovernmentAgency) {
|
||||
icon::SEAL_CHECK
|
||||
} else if has(Role::FinancialInstitution) {
|
||||
icon::BUILDING_OFFICE
|
||||
} else if has(Role::PayoutPlatform) {
|
||||
|
||||
@@ -5669,7 +5669,7 @@ impl BooksServer {
|
||||
|
||||
#[tool(
|
||||
name = "books.add_party",
|
||||
description = "Add a party to the directory: anyone the company deals with (a contractor, landlord, payout platform, tax agency, bank or card issuer, member, employee). Name it as on its documents (unique among parties not archived); give its roles: contractor, employee, member, payout_platform, tax_agency, financial_institution, landlord. Vendor and customer aren't given by hand: books.add_vendor and books.add_customer add those records and link them to the party of the same name. Never put a full TIN or SSN in any party field: the engine refuses nine-digit numbers. Journaled as party.added; returns the party."
|
||||
description = "Add a party to the directory: anyone the company deals with (a contractor, landlord, payout platform, tax agency, government agency, bank or card issuer, member, employee). Name it as on its documents (unique among parties not archived); give its roles: contractor, employee, member, payout_platform, tax_agency, government_agency, financial_institution, landlord. Vendor and customer aren't given by hand: books.add_vendor and books.add_customer add those records and link them to the party of the same name. Never put a full TIN or SSN in any party field: the engine refuses nine-digit numbers. Journaled as party.added; returns the party."
|
||||
)]
|
||||
async fn add_party(
|
||||
&self,
|
||||
@@ -5740,7 +5740,7 @@ impl BooksServer {
|
||||
|
||||
#[tool(
|
||||
name = "books.set_party_roles",
|
||||
description = "Give a party roles (add) or take them away (remove): contractor, employee, member, payout_platform, tax_agency, financial_institution, landlord. Vendor and customer follow their records and can't be set by hand. Adds apply before removes; a role it already has (or lacks) is left alone. Removing a party's last 1099 role (contractor, landlord) needs its W-9 facts cleared first (books.set_w9 clear), and removing employee its W-4 facts. All or nothing: a refusal changes nothing. Each change is journaled as party.role_set. Returns the party."
|
||||
description = "Give a party roles (add) or take them away (remove): contractor, employee, member, payout_platform, tax_agency, government_agency, financial_institution, landlord. Vendor and customer follow their records and can't be set by hand. Adds apply before removes; a role it already has (or lacks) is left alone. Removing a party's last 1099 role (contractor, landlord) needs its W-9 facts cleared first (books.set_w9 clear), and removing employee its W-4 facts. All or nothing: a refusal changes nothing. Each change is journaled as party.role_set. Returns the party."
|
||||
)]
|
||||
async fn set_party_roles(
|
||||
&self,
|
||||
|
||||
@@ -2601,7 +2601,7 @@ pub struct AddPartyParams {
|
||||
/// surrounding space) among parties not archived; never a full TIN.
|
||||
pub name: String,
|
||||
/// Roles: contractor, employee, member, payout_platform, tax_agency,
|
||||
/// financial_institution, landlord. Not vendor or customer: those come
|
||||
/// government_agency, financial_institution, landlord. Not vendor or customer: those come
|
||||
/// with their records (`books.add_vendor`, `books.add_customer`, which
|
||||
/// link to the party of the same name). May be empty.
|
||||
#[serde(default)]
|
||||
|
||||
@@ -1079,6 +1079,7 @@ fn parties_lists_by_id_and_filters_by_role() {
|
||||
let a = m.add_party("Acme Design", &["contractor"]);
|
||||
let i = m.add_party("IRS", &["tax_agency"]);
|
||||
let b = m.add_party("Bluevine", &["financial_institution"]);
|
||||
let u = m.add_party("Utah Division of Corporations", &["government_agency"]);
|
||||
let all = m.read_only("books.parties", json!({}));
|
||||
assert!(all["year"].is_i64(), "defaults to this year");
|
||||
let ids: Vec<u64> = all["parties"]
|
||||
@@ -1087,7 +1088,7 @@ fn parties_lists_by_id_and_filters_by_role() {
|
||||
.iter()
|
||||
.map(|p| p["party_id"].as_u64().unwrap())
|
||||
.collect();
|
||||
assert_eq!(ids, [a, i, b]);
|
||||
assert_eq!(ids, [a, i, b, u]);
|
||||
let row = &all["parties"][0];
|
||||
assert_eq!(row["name"], "Acme Design");
|
||||
assert_eq!(row["label"], "Acme Design");
|
||||
@@ -1098,6 +1099,10 @@ fn parties_lists_by_id_and_filters_by_role() {
|
||||
let agencies = m.read_only("books.parties", json!({"role": "tax_agency"}));
|
||||
assert_eq!(agencies["parties"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(agencies["parties"][0]["party_id"], json!(i));
|
||||
// A regulator is its own role, not a tax agency.
|
||||
let regulators = m.read_only("books.parties", json!({"role": "government_agency"}));
|
||||
assert_eq!(regulators["parties"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(regulators["parties"][0]["party_id"], json!(u));
|
||||
m.refuse("books.parties", json!({"role": "wizard"}));
|
||||
}
|
||||
|
||||
|
||||
@@ -354,7 +354,9 @@ goes by id). A number that is one party's id and another party's name is
|
||||
refused, never guessed.
|
||||
|
||||
**Roles:** `vendor`, `contractor`, `customer`, `employee`, `member`,
|
||||
`payout_platform`, `tax_agency`, `financial_institution`, `landlord`.
|
||||
`payout_platform`, `tax_agency`, `government_agency` (regulators and
|
||||
registries that don't tax: a division of corporations, a licensing board),
|
||||
`financial_institution`, `landlord`.
|
||||
Vendor and customer follow the records and are never set by hand.
|
||||
|
||||
**Rules the engine enforces** (each refusal changes nothing, the journal
|
||||
|
||||
@@ -25,7 +25,8 @@ Party {
|
||||
name: String, // legal or trade name, as on documents
|
||||
display_name: Option<String>,
|
||||
roles: BTreeSet<Role>, // set by hand: contractor, employee, member, payout_platform,
|
||||
// tax_agency, financial_institution, landlord (vendor and
|
||||
// tax_agency, government_agency, financial_institution,
|
||||
// landlord (vendor and
|
||||
// customer follow the records; see all_roles)
|
||||
website: Option<String>,
|
||||
address: Option<String>, // business address; no personal PII beyond it
|
||||
|
||||
Reference in new issue
Block a user