Next steps 5-10: resources, cash flow, QB interop, backfill, HTTP, parallel-run plan
This commit is contained in:
1 parent
0e4ec80cd8
commit
7025056125
1 file changed
+201
-5
@@ -113,6 +113,153 @@ pub struct ProfitAndLossOut {
|
|||||||
pub net_income: String,
|
pub net_income: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parameters for `books.cash_flow`.
|
||||||
|
#[derive(Debug, Deserialize, JsonSchema)]
|
||||||
|
pub struct CashFlowParams {
|
||||||
|
/// Period start, YYYY-MM-DD. Defaults to the beginning of time.
|
||||||
|
pub start: Option<String>,
|
||||||
|
/// Period end, YYYY-MM-DD. Defaults to today.
|
||||||
|
pub end: Option<String>,
|
||||||
|
/// Account codes treated as cash (e.g. `["1000", "1010"]`). Required:
|
||||||
|
/// the statement reconciles to the net change in these accounts.
|
||||||
|
pub cash_accounts: Vec<String>,
|
||||||
|
/// Account codes for liabilities treated as financing (debt).
|
||||||
|
/// Defaults to empty: liabilities are operating working capital.
|
||||||
|
#[serde(default)]
|
||||||
|
pub financing_accounts: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One cash flow line.
|
||||||
|
#[derive(Debug, Serialize, JsonSchema)]
|
||||||
|
pub struct CashFlowLineOut {
|
||||||
|
/// Human label.
|
||||||
|
pub label: String,
|
||||||
|
/// Signed amount, decimal string. Positive = inflow.
|
||||||
|
pub amount: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result of `books.cash_flow`.
|
||||||
|
#[derive(Debug, Serialize, JsonSchema)]
|
||||||
|
pub struct CashFlowOut {
|
||||||
|
/// Period start, YYYY-MM-DD.
|
||||||
|
pub start: String,
|
||||||
|
/// Period end, YYYY-MM-DD.
|
||||||
|
pub end: String,
|
||||||
|
/// Period net income, decimal string.
|
||||||
|
pub net_income: String,
|
||||||
|
/// Non-cash depreciation added back, decimal string.
|
||||||
|
pub depreciation: String,
|
||||||
|
/// Working-capital adjustments.
|
||||||
|
pub working_capital: Vec<CashFlowLineOut>,
|
||||||
|
/// Cash from operations, decimal string.
|
||||||
|
pub cash_from_operations: String,
|
||||||
|
/// Investing flows (purchases, disposal proceeds).
|
||||||
|
pub investing: Vec<CashFlowLineOut>,
|
||||||
|
/// Cash from investing, decimal string.
|
||||||
|
pub cash_from_investing: String,
|
||||||
|
/// Financing flows (equity, designated debt).
|
||||||
|
pub financing: Vec<CashFlowLineOut>,
|
||||||
|
/// Cash from financing, decimal string.
|
||||||
|
pub cash_from_financing: String,
|
||||||
|
/// Net change in cash, decimal string.
|
||||||
|
pub net_change_in_cash: String,
|
||||||
|
/// Cash balance at period start, decimal string.
|
||||||
|
pub cash_beginning: String,
|
||||||
|
/// Cash balance at period end, decimal string.
|
||||||
|
pub cash_ending: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters for `books.import_bank_csv`.
|
||||||
|
#[derive(Debug, Deserialize, JsonSchema)]
|
||||||
|
pub struct ImportBankCsvParams {
|
||||||
|
/// The CSV text (QBO bank upload layout: Date,Description,Amount or
|
||||||
|
/// Date,Description,Expense,Income).
|
||||||
|
pub csv_text: String,
|
||||||
|
/// Account code for the bank account (the offset side of each entry).
|
||||||
|
pub bank_account_code: String,
|
||||||
|
/// Account code for the default offset (e.g. an expense account).
|
||||||
|
/// Used when no rule matches.
|
||||||
|
pub default_offset_code: String,
|
||||||
|
/// Optional categorization rules: { "pattern": "account_code" }.
|
||||||
|
/// First case-insensitive substring match on the description wins.
|
||||||
|
#[serde(default)]
|
||||||
|
pub rules: std::collections::HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One imported bank transaction (as a draft entry).
|
||||||
|
#[derive(Debug, Serialize, JsonSchema)]
|
||||||
|
pub struct ImportedTxnOut {
|
||||||
|
/// Draft entry id.
|
||||||
|
pub entry_id: u64,
|
||||||
|
/// Transaction date, YYYY-MM-DD.
|
||||||
|
pub date: String,
|
||||||
|
/// Bank description.
|
||||||
|
pub description: String,
|
||||||
|
/// Signed amount, decimal string.
|
||||||
|
pub amount: String,
|
||||||
|
/// Account code used for the offset.
|
||||||
|
pub offset_account: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result of `books.import_bank_csv`.
|
||||||
|
#[derive(Debug, Serialize, JsonSchema)]
|
||||||
|
pub struct ImportBankCsvOut {
|
||||||
|
/// Drafts created (one per transaction).
|
||||||
|
pub imported: Vec<ImportedTxnOut>,
|
||||||
|
/// Number of transactions imported.
|
||||||
|
pub count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters for `books.import_chart_csv`.
|
||||||
|
#[derive(Debug, Deserialize, JsonSchema)]
|
||||||
|
pub struct ImportChartCsvParams {
|
||||||
|
/// The CSV text: code,name,type,parent_code with a header row.
|
||||||
|
pub csv_text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result of `books.import_chart_csv`.
|
||||||
|
#[derive(Debug, Serialize, JsonSchema)]
|
||||||
|
pub struct ImportChartCsvOut {
|
||||||
|
/// Account codes created, in order.
|
||||||
|
pub created: Vec<String>,
|
||||||
|
/// Number of accounts created.
|
||||||
|
pub count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters for `books.import_local_file`.
|
||||||
|
#[derive(Debug, Deserialize, JsonSchema)]
|
||||||
|
pub struct ImportLocalFileParams {
|
||||||
|
/// Absolute path to the file on the server's filesystem.
|
||||||
|
pub path: String,
|
||||||
|
/// "receipt" or "document".
|
||||||
|
pub category: String,
|
||||||
|
/// Entry id to attach to (receipts only). Omit to file unlinked.
|
||||||
|
#[serde(default)]
|
||||||
|
pub entry_id: Option<u64>,
|
||||||
|
/// Document category for documents: formation, legal, tax, accounting, other.
|
||||||
|
#[serde(default)]
|
||||||
|
pub document_category: Option<String>,
|
||||||
|
/// Override the file name (defaults to the path's file name).
|
||||||
|
#[serde(default)]
|
||||||
|
pub file_name: Option<String>,
|
||||||
|
/// Optional description.
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result of `books.import_local_file`.
|
||||||
|
#[derive(Debug, Serialize, JsonSchema)]
|
||||||
|
pub struct ImportLocalFileOut {
|
||||||
|
/// Attachment id.
|
||||||
|
pub attachment_id: u64,
|
||||||
|
/// SHA-256 hex of the content.
|
||||||
|
pub sha256: String,
|
||||||
|
/// True if the content was already stored (deduplicated).
|
||||||
|
pub deduplicated: bool,
|
||||||
|
/// File name used.
|
||||||
|
pub file_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// Parameters for `books.balance_sheet`.
|
/// Parameters for `books.balance_sheet`.
|
||||||
#[derive(Debug, Deserialize, JsonSchema)]
|
#[derive(Debug, Deserialize, JsonSchema)]
|
||||||
pub struct BalanceSheetParams {
|
pub struct BalanceSheetParams {
|
||||||
@@ -244,6 +391,10 @@ pub struct PostEntryOut {
|
|||||||
/// True when a receipt is required but none is attached. Posting with
|
/// True when a receipt is required but none is attached. Posting with
|
||||||
/// confirmed=true fails in this state.
|
/// confirmed=true fails in this state.
|
||||||
pub receipt_missing: bool,
|
pub receipt_missing: bool,
|
||||||
|
/// Posted entry ids that look like they split this entry to dodge the
|
||||||
|
/// receipt threshold (same date, description, and debit accounts).
|
||||||
|
/// Empty when no split is detected.
|
||||||
|
pub split_detected: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parameters for `books.reverse_entry`.
|
/// Parameters for `books.reverse_entry`.
|
||||||
@@ -544,16 +695,21 @@ pub struct DigestOut {
|
|||||||
/// Parameters for `books.export`.
|
/// Parameters for `books.export`.
|
||||||
#[derive(Debug, Deserialize, JsonSchema)]
|
#[derive(Debug, Deserialize, JsonSchema)]
|
||||||
pub struct ExportParams {
|
pub struct ExportParams {
|
||||||
/// "csv" or "json".
|
/// "csv", "json", "xlsx", or "iif". XLSX is base64-encoded; IIF is only
|
||||||
|
/// valid with report "journal".
|
||||||
pub format: String,
|
pub format: String,
|
||||||
/// "journal", "trial_balance", "profit_and_loss", or "balance_sheet".
|
/// "journal", "trial_balance", "profit_and_loss", "balance_sheet", or
|
||||||
|
/// "cash_flow".
|
||||||
pub report: String,
|
pub report: String,
|
||||||
/// Period start for profit_and_loss, YYYY-MM-DD.
|
/// Period start for profit_and_loss and cash_flow, YYYY-MM-DD.
|
||||||
pub start: Option<String>,
|
pub start: Option<String>,
|
||||||
/// Period end for profit_and_loss, YYYY-MM-DD.
|
/// Period end for profit_and_loss and cash_flow, YYYY-MM-DD.
|
||||||
pub end: Option<String>,
|
pub end: Option<String>,
|
||||||
/// As-of date for balance_sheet, YYYY-MM-DD.
|
/// As-of date for balance_sheet, YYYY-MM-DD.
|
||||||
pub as_of: Option<String>,
|
pub as_of: Option<String>,
|
||||||
|
/// Cash account codes for cash_flow (required for that report).
|
||||||
|
#[serde(default)]
|
||||||
|
pub cash_accounts: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result of `books.export`.
|
/// Result of `books.export`.
|
||||||
@@ -563,8 +719,12 @@ pub struct ExportOut {
|
|||||||
pub format: String,
|
pub format: String,
|
||||||
/// The requested report.
|
/// The requested report.
|
||||||
pub report: String,
|
pub report: String,
|
||||||
/// The exported content.
|
/// The exported content. Base64-encoded when `content_encoding` is
|
||||||
|
/// "base64" (XLSX); otherwise raw text.
|
||||||
pub content: String,
|
pub content: String,
|
||||||
|
/// "base64" for XLSX, otherwise absent.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub content_encoding: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -763,3 +923,39 @@ pub struct DisposeAssetOut {
|
|||||||
/// True when posting requires a receipt (bill of sale) first.
|
/// True when posting requires a receipt (bill of sale) first.
|
||||||
pub receipt_required: bool,
|
pub receipt_required: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Structured machine-readable tool error for receipt policy rejections.
|
||||||
|
///
|
||||||
|
/// Tool errors surface as strings in MCP; this serializes to JSON so
|
||||||
|
/// clients can match on `code` instead of parsing prose. The human-readable
|
||||||
|
/// `message` is still included for logs and chat.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct ReceiptRequiredError {
|
||||||
|
/// Stable machine-readable code: always `"receipt_required"`.
|
||||||
|
pub code: String,
|
||||||
|
/// Human-readable explanation with the remedy.
|
||||||
|
pub message: String,
|
||||||
|
/// The entry (or asset) that needs the receipt. Set for entries.
|
||||||
|
pub entry_id: Option<u64>,
|
||||||
|
/// The asset being disposed. Set for disposals.
|
||||||
|
pub asset_id: Option<u64>,
|
||||||
|
/// Total debits moved, decimal string.
|
||||||
|
pub debits: String,
|
||||||
|
/// The policy that triggered, e.g. "strictly greater than $25.00".
|
||||||
|
pub policy: String,
|
||||||
|
/// Tool to call to satisfy the requirement.
|
||||||
|
pub remedy_tool: String,
|
||||||
|
/// Concrete next step for the agent.
|
||||||
|
pub remedy_hint: String,
|
||||||
|
/// When the requirement was triggered by anti-splitting detection: the
|
||||||
|
/// ids of the posted entries this entry appears to split.
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub split_group: Vec<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReceiptRequiredError {
|
||||||
|
/// Serialize to the string form returned as the MCP tool error.
|
||||||
|
pub fn to_tool_error(&self) -> String {
|
||||||
|
serde_json::to_string(self).unwrap_or_else(|_| self.message.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user