From fc1baff582e4299a710a761763b5901583393386 Mon Sep 17 00:00:00 2001 From: Cesar Ho Date: Sat, 17 Jan 2026 12:16:34 +0800 Subject: [PATCH] Enhance list-invoices tool with advanced filtering options and update README. - Updated the `list-invoices` tool to support advanced filtering capabilities, including a required 'where' parameter for invoice types and additional filtering options for statuses, dates, and amounts. - Refactored the `getInvoices` function to accept a single parameter object for better clarity and usability. - Revised the README to reflect the new advanced filtering features for the `list-invoices` command. --- README.md | 2 +- src/handlers/list-xero-invoices.handler.ts | 38 ++++++--- src/tools/list/list-invoices.tool.ts | 98 +++++++++++++++++++--- 3 files changed, 114 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index af4d6d1d..40ca5c06 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ NOTE: The `XERO_CLIENT_BEARER_TOKEN` will take precedence over the `XERO_CLIENT_ - `list-accounts`: Retrieve a list of accounts - `list-contacts`: Retrieve a list of contacts from Xero - `list-credit-notes`: Retrieve a list of credit notes -- `list-invoices`: Retrieve a list of invoices +- `list-invoices`: Retrieve a list of invoices with advanced filtering - `list-items`: Retrieve a list of items - `list-organisation-details`: Retrieve details about an organisation - `list-profit-and-loss`: Retrieve a profit and loss report diff --git a/src/handlers/list-xero-invoices.handler.ts b/src/handlers/list-xero-invoices.handler.ts index 1eb3f26f..91b084b4 100644 --- a/src/handlers/list-xero-invoices.handler.ts +++ b/src/handlers/list-xero-invoices.handler.ts @@ -4,22 +4,40 @@ import { formatError } from "../helpers/format-error.js"; import { Invoice } from "xero-node"; import { getClientHeaders } from "../helpers/get-client-headers.js"; +export interface ListInvoicesParams { + page?: number; + contactIds?: string[]; + invoiceNumbers?: string[]; + invoiceIds?: string[]; + statuses?: string[]; + where?: string; + order?: string; +} + async function getInvoices( - invoiceNumbers: string[] | undefined, - contactIds: string[] | undefined, - page: number, + params: ListInvoicesParams, ): Promise { await xeroClient.authenticate(); + const { + page = 1, + contactIds, + invoiceNumbers, + invoiceIds, + statuses, + where, + order = "UpdatedDateUTC DESC", + } = params; + const invoices = await xeroClient.accountingApi.getInvoices( xeroClient.tenantId, undefined, // ifModifiedSince - undefined, // where - "UpdatedDateUTC DESC", // order - undefined, // iDs + where, // where + order, // order + invoiceIds, // iDs invoiceNumbers, // invoiceNumbers contactIds, // contactIDs - undefined, // statuses + statuses, // statuses page, false, // includeArchived false, // createdByMyApp @@ -36,12 +54,10 @@ async function getInvoices( * List all invoices from Xero */ export async function listXeroInvoices( - page: number = 1, - contactIds?: string[], - invoiceNumbers?: string[], + params: ListInvoicesParams, ): Promise> { try { - const invoices = await getInvoices(invoiceNumbers, contactIds, page); + const invoices = await getInvoices(params); return { result: invoices, diff --git a/src/tools/list/list-invoices.tool.ts b/src/tools/list/list-invoices.tool.ts index ac400fc9..df57c176 100644 --- a/src/tools/list/list-invoices.tool.ts +++ b/src/tools/list/list-invoices.tool.ts @@ -5,23 +5,97 @@ import { formatLineItem } from "../../helpers/format-line-item.js"; const ListInvoicesTool = CreateXeroTool( "list-invoices", - "List invoices in Xero. This includes Draft, Submitted, and Paid invoices. \ - Ask the user if they want to see invoices for a specific contact, \ - invoice number, or to see all invoices before running. \ - Ask the user if they want the next page of invoices after running this tool \ - if 10 invoices are returned. \ - If they want the next page, call this tool again with the next page number \ - and the contact or invoice number if one was provided in the previous call.", + `List invoices in Xero with advanced filtering capabilities. + + ⚠️ CRITICAL: To filter by invoice Type, you MUST use the 'where' parameter: + - Type=="ACCREC": Sales invoices (customer invoices, accounts receivable) + - Type=="ACCPAY": Bills (purchase invoices, supplier invoices, accounts payable) + There is NO separate 'type' or 'types' parameter - you MUST use where=Type=="ACCREC" or where=Type=="ACCPAY" + + This tool supports multiple filtering methods: + 1. Simple filters: contactIds, invoiceNumbers, invoiceIds, statuses + 2. Advanced 'where' parameter (REQUIRED for Type, dates, amounts, contact names, complex queries) + + AI AGENT DECISION GUIDE - Which parameter to use: + - "Type ACCREC" or "sales invoices" → where=Type=="ACCREC" + - "Type ACCPAY" or "bills" → where=Type=="ACCPAY" + - "Status authorised" alone → statuses=["AUTHORISED"] + - "Type ACCREC AND status authorised" → where=Type=="ACCREC" AND Status=="AUTHORISED" + - Date filtering → where=Date>=DateTime(2024,01,01) + - Amount filtering → where=AmountDue>1000 + - Contact name → where=Contact.Name=="ABC Ltd" + - Multiple statuses only → statuses=["AUTHORISED","PAID"] + - Specific invoice IDs → invoiceIds=["id1","id2"] + + WHEN TO USE THE 'WHERE' PARAMETER (available fields): + - Type: where=Type=="ACCREC" (sales) or Type=="ACCPAY" (bills) + - Status: where=Status=="AUTHORISED" (DRAFT, SUBMITTED, DELETED, AUTHORISED, PAID, VOIDED) + - Contact.Name: where=Contact.Name=="ABC Limited" + - Date: where=Date>=DateTime(2020,01,01) (supports >, >=, <, <=) + - DueDate: where=DueDate, >=, <, <=) + - AmountDue: where=AmountDue>=1000 (supports >, >=, <, <=) + - Reference, InvoiceNumber, Contact.ContactID, etc. + + COMMON QUERY EXAMPLES: + - "Show sales invoices" → where=Type=="ACCREC" + - "Show bills" → where=Type=="ACCPAY" + - "Authorised sales invoices" → where=Type=="ACCREC" AND Status=="AUTHORISED" + - "Overdue bills" → where=Type=="ACCPAY" AND DueDate=DateTime(2024,01,01) AND Date10000 AND Status=="AUTHORISED" + + For filtering by multiple IDs or statuses, prefer using the dedicated array parameters + (invoiceIds, statuses, contactIds) over the where filter for better performance. + + Ask the user if they want the next page after returning 10 invoices.`, { - page: z.number(), - contactIds: z.array(z.string()).optional(), + page: z.number().optional().describe("Page number for pagination (default: 1)"), + contactIds: z + .array(z.string()) + .optional() + .describe("Filter by contact IDs (comma-separated list for optimal performance)"), invoiceNumbers: z .array(z.string()) .optional() - .describe("If provided, invoice line items will also be returned"), + .describe("Filter by invoice numbers. When provided, line items will also be returned"), + invoiceIds: z + .array(z.string()) + .optional() + .describe("Filter by invoice IDs (comma-separated list for optimal performance)"), + statuses: z + .array(z.string()) + .optional() + .describe( + "Filter by invoice statuses (comma-separated list). Valid values: DRAFT, SUBMITTED, DELETED, AUTHORISED, PAID, VOIDED", + ), + where: z + .string() + .optional() + .describe( + "REQUIRED for filtering by invoice Type (ACCREC/ACCPAY), dates, amounts, contact names, or complex queries. " + + "Common patterns: Type==\"ACCREC\" (sales invoices), Type==\"ACCPAY\" (bills), " + + "Type==\"ACCREC\" AND Status==\"AUTHORISED\", Date>=DateTime(2024,01,01), AmountDue>1000, Contact.Name==\"ABC Ltd\". " + + "Range operators: >, >=, <, <=. Logical operators: AND, OR", + ), + order: z + .string() + .optional() + .describe( + "Order by field. Optimized fields: InvoiceId, UpdatedDateUTC, Date. Default: 'UpdatedDateUTC DESC'", + ), }, - async ({ page, contactIds, invoiceNumbers }) => { - const response = await listXeroInvoices(page, contactIds, invoiceNumbers); + async (params) => { + const { page, contactIds, invoiceNumbers, invoiceIds, statuses, where, order } = params; + const response = await listXeroInvoices({ + page, + contactIds, + invoiceNumbers, + invoiceIds, + statuses, + where, + order, + }); + if (response.error !== null) { return { content: [