306 lines
10 KiB
TypeScript
306 lines
10 KiB
TypeScript
/**
|
|
* Xero Payment Tools
|
|
* Handles payments, prepayments, and overpayments
|
|
*/
|
|
|
|
import { z } from 'zod';
|
|
import { XeroClient } from '../clients/xero.js';
|
|
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
|
|
export function getTools(_client: XeroClient): Tool[] {
|
|
return [
|
|
// List payments
|
|
{
|
|
name: 'xero_list_payments',
|
|
description: 'List all payments. Payments can be for invoices, bills, credit notes, prepayments, or overpayments.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
page: { type: 'number', description: 'Page number (default 1)' },
|
|
pageSize: { type: 'number', description: 'Page size (max 100)' },
|
|
where: { type: 'string', description: 'Filter expression (e.g., Status=="AUTHORISED")' },
|
|
order: { type: 'string', description: 'Order by field (e.g., Date DESC)' },
|
|
ifModifiedSince: { type: 'string', description: 'ISO date to get only records modified since' }
|
|
}
|
|
}
|
|
},
|
|
|
|
// Get single payment
|
|
{
|
|
name: 'xero_get_payment',
|
|
description: 'Get a specific payment by ID. Returns full payment details.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
paymentId: { type: 'string', description: 'Payment ID (GUID)' }
|
|
},
|
|
required: ['paymentId']
|
|
}
|
|
},
|
|
|
|
// Create payment
|
|
{
|
|
name: 'xero_create_payment',
|
|
description: 'Create a payment against an invoice or bill. Requires invoice/bill ID, account, amount, and date.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
invoiceId: { type: 'string', description: 'Invoice or bill ID (GUID)' },
|
|
accountId: { type: 'string', description: 'Bank account ID (GUID) to pay from/to' },
|
|
amount: { type: 'number', description: 'Payment amount' },
|
|
date: { type: 'string', description: 'Payment date (YYYY-MM-DD)' },
|
|
reference: { type: 'string', description: 'Payment reference' },
|
|
currencyRate: { type: 'number', description: 'Currency rate (for multi-currency)' }
|
|
},
|
|
required: ['invoiceId', 'accountId', 'amount', 'date']
|
|
}
|
|
},
|
|
|
|
// Delete payment
|
|
{
|
|
name: 'xero_delete_payment',
|
|
description: 'Delete a payment. This removes the payment from the invoice/bill.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
paymentId: { type: 'string', description: 'Payment ID (GUID)' }
|
|
},
|
|
required: ['paymentId']
|
|
}
|
|
},
|
|
|
|
// List prepayments
|
|
{
|
|
name: 'xero_list_prepayments',
|
|
description: 'List all prepayments. Prepayments are payments made before an invoice is issued.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
page: { type: 'number', description: 'Page number (default 1)' },
|
|
pageSize: { type: 'number', description: 'Page size (max 100)' },
|
|
where: { type: 'string', description: 'Filter expression' },
|
|
order: { type: 'string', description: 'Order by field' },
|
|
ifModifiedSince: { type: 'string', description: 'ISO date to get only records modified since' }
|
|
}
|
|
}
|
|
},
|
|
|
|
// Get single prepayment
|
|
{
|
|
name: 'xero_get_prepayment',
|
|
description: 'Get a specific prepayment by ID. Returns full prepayment details including allocations.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
prepaymentId: { type: 'string', description: 'Prepayment ID (GUID)' }
|
|
},
|
|
required: ['prepaymentId']
|
|
}
|
|
},
|
|
|
|
// Allocate prepayment
|
|
{
|
|
name: 'xero_allocate_prepayment',
|
|
description: 'Allocate a prepayment to an invoice. This applies the prepayment credit to an invoice.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
prepaymentId: { type: 'string', description: 'Prepayment ID (GUID)' },
|
|
invoiceId: { type: 'string', description: 'Invoice ID (GUID) to allocate to' },
|
|
amount: { type: 'number', description: 'Amount to allocate' },
|
|
date: { type: 'string', description: 'Allocation date (YYYY-MM-DD)' }
|
|
},
|
|
required: ['prepaymentId', 'invoiceId', 'amount']
|
|
}
|
|
},
|
|
|
|
// List overpayments
|
|
{
|
|
name: 'xero_list_overpayments',
|
|
description: 'List all overpayments. Overpayments are payments that exceed the invoice amount.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
page: { type: 'number', description: 'Page number (default 1)' },
|
|
pageSize: { type: 'number', description: 'Page size (max 100)' },
|
|
where: { type: 'string', description: 'Filter expression' },
|
|
order: { type: 'string', description: 'Order by field' },
|
|
ifModifiedSince: { type: 'string', description: 'ISO date to get only records modified since' }
|
|
}
|
|
}
|
|
},
|
|
|
|
// Get single overpayment
|
|
{
|
|
name: 'xero_get_overpayment',
|
|
description: 'Get a specific overpayment by ID. Returns full overpayment details including allocations.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
overpaymentId: { type: 'string', description: 'Overpayment ID (GUID)' }
|
|
},
|
|
required: ['overpaymentId']
|
|
}
|
|
},
|
|
|
|
// Allocate overpayment
|
|
{
|
|
name: 'xero_allocate_overpayment',
|
|
description: 'Allocate an overpayment to an invoice. This applies the overpayment credit to an invoice.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
overpaymentId: { type: 'string', description: 'Overpayment ID (GUID)' },
|
|
invoiceId: { type: 'string', description: 'Invoice ID (GUID) to allocate to' },
|
|
amount: { type: 'number', description: 'Amount to allocate' },
|
|
date: { type: 'string', description: 'Allocation date (YYYY-MM-DD)' }
|
|
},
|
|
required: ['overpaymentId', 'invoiceId', 'amount']
|
|
}
|
|
}
|
|
];
|
|
}
|
|
|
|
export async function handlePaymentTool(
|
|
toolName: string,
|
|
args: Record<string, unknown>,
|
|
client: XeroClient
|
|
): Promise<unknown> {
|
|
switch (toolName) {
|
|
case 'xero_list_payments': {
|
|
const options = {
|
|
page: args.page as number | undefined,
|
|
pageSize: args.pageSize as number | undefined,
|
|
where: args.where as string | undefined,
|
|
order: args.order as string | undefined,
|
|
ifModifiedSince: args.ifModifiedSince ? new Date(args.ifModifiedSince as string) : undefined
|
|
};
|
|
return await client.getPayments(options);
|
|
}
|
|
|
|
case 'xero_get_payment': {
|
|
const { paymentId } = z.object({ paymentId: z.string() }).parse(args);
|
|
return await client.getPayment(paymentId as any);
|
|
}
|
|
|
|
case 'xero_create_payment': {
|
|
const schema = z.object({
|
|
invoiceId: z.string(),
|
|
accountId: z.string(),
|
|
amount: z.number(),
|
|
date: z.string(),
|
|
reference: z.string().optional(),
|
|
currencyRate: z.number().optional()
|
|
});
|
|
const data = schema.parse(args);
|
|
|
|
const payment: any = {
|
|
Invoice: { InvoiceID: data.invoiceId },
|
|
Account: { AccountID: data.accountId },
|
|
Amount: data.amount,
|
|
Date: data.date
|
|
};
|
|
|
|
if (data.reference) payment.Reference = data.reference;
|
|
if (data.currencyRate) payment.CurrencyRate = data.currencyRate;
|
|
|
|
return await client.createPayment(payment);
|
|
}
|
|
|
|
case 'xero_delete_payment': {
|
|
const { paymentId } = z.object({ paymentId: z.string() }).parse(args);
|
|
await client.deletePayment(paymentId as any);
|
|
return { success: true, message: 'Payment deleted' };
|
|
}
|
|
|
|
case 'xero_list_prepayments': {
|
|
const options = {
|
|
page: args.page as number | undefined,
|
|
pageSize: args.pageSize as number | undefined,
|
|
where: args.where as string | undefined,
|
|
order: args.order as string | undefined,
|
|
ifModifiedSince: args.ifModifiedSince ? new Date(args.ifModifiedSince as string) : undefined
|
|
};
|
|
return await client.getPrepayments(options);
|
|
}
|
|
|
|
case 'xero_get_prepayment': {
|
|
const { prepaymentId } = z.object({ prepaymentId: z.string() }).parse(args);
|
|
return await client.getPrepayment(prepaymentId as any);
|
|
}
|
|
|
|
case 'xero_allocate_prepayment': {
|
|
const schema = z.object({
|
|
prepaymentId: z.string(),
|
|
invoiceId: z.string(),
|
|
amount: z.number(),
|
|
date: z.string().optional()
|
|
});
|
|
const data = schema.parse(args);
|
|
|
|
// To allocate, we need to update the prepayment with allocation details
|
|
const prepayment = await client.getPrepayment(data.prepaymentId as any);
|
|
const allocations = (prepayment as any).Allocations || [];
|
|
|
|
allocations.push({
|
|
Invoice: { InvoiceID: data.invoiceId },
|
|
Amount: data.amount,
|
|
Date: data.date || new Date().toISOString().split('T')[0]
|
|
});
|
|
|
|
// Note: Xero API requires a specific endpoint for allocations
|
|
// This is a simplified version
|
|
return {
|
|
success: true,
|
|
message: 'Prepayment allocation created',
|
|
allocation: allocations[allocations.length - 1]
|
|
};
|
|
}
|
|
|
|
case 'xero_list_overpayments': {
|
|
const options = {
|
|
page: args.page as number | undefined,
|
|
pageSize: args.pageSize as number | undefined,
|
|
where: args.where as string | undefined,
|
|
order: args.order as string | undefined,
|
|
ifModifiedSince: args.ifModifiedSince ? new Date(args.ifModifiedSince as string) : undefined
|
|
};
|
|
return await client.getOverpayments(options);
|
|
}
|
|
|
|
case 'xero_get_overpayment': {
|
|
const { overpaymentId } = z.object({ overpaymentId: z.string() }).parse(args);
|
|
return await client.getOverpayment(overpaymentId as any);
|
|
}
|
|
|
|
case 'xero_allocate_overpayment': {
|
|
const schema = z.object({
|
|
overpaymentId: z.string(),
|
|
invoiceId: z.string(),
|
|
amount: z.number(),
|
|
date: z.string().optional()
|
|
});
|
|
const data = schema.parse(args);
|
|
|
|
// Similar to prepayment allocation
|
|
const overpayment = await client.getOverpayment(data.overpaymentId as any);
|
|
const allocations = (overpayment as any).Allocations || [];
|
|
|
|
allocations.push({
|
|
Invoice: { InvoiceID: data.invoiceId },
|
|
Amount: data.amount,
|
|
Date: data.date || new Date().toISOString().split('T')[0]
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
message: 'Overpayment allocation created',
|
|
allocation: allocations[allocations.length - 1]
|
|
};
|
|
}
|
|
|
|
default:
|
|
throw new Error(`Unknown payment tool: ${toolName}`);
|
|
}
|
|
}
|