/** * Lightspeed Inventory Tools */ import { z } from 'zod'; import type { LightspeedClient } from '../client.js'; import type { InventoryCount, InventoryTransfer, Supplier, PurchaseOrder } from '../types/index.js'; export function createInventoryTools(client: LightspeedClient) { return { lightspeed_list_inventory: { description: 'List inventory counts for all products at a shop', inputSchema: z.object({ shopId: z.string().describe('Shop ID'), limit: z.number().optional().describe('Max items to return (default 100)'), }), handler: async (args: { shopId: string; limit?: number }) => { try { const inventory = await client.getAll( `/Shop/${args.shopId}/ItemShop`, 'ItemShop', args.limit || 100 ); return { content: [ { type: 'text' as const, text: JSON.stringify({ success: true, count: inventory.length, inventory: inventory.map(i => ({ itemID: i.itemID, qoh: i.qoh, reorderPoint: i.reorderPoint, backorder: i.backorder, })), }, null, 2), }, ], }; } catch (error) { return { content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }], isError: true, }; } }, }, lightspeed_get_item_inventory: { description: 'Get inventory count for a specific product at a shop', inputSchema: z.object({ itemId: z.string().describe('Product item ID'), shopId: z.string().describe('Shop ID'), }), handler: async (args: { itemId: string; shopId: string }) => { try { const inventory = await client.get<{ ItemShop: InventoryCount }>( `/Item/${args.itemId}/ItemShop/${args.shopId}` ); return { content: [ { type: 'text' as const, text: JSON.stringify({ success: true, inventory: inventory.ItemShop }, null, 2), }, ], }; } catch (error) { return { content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }], isError: true, }; } }, }, lightspeed_update_inventory_count: { description: 'Update inventory quantity for a product at a shop', inputSchema: z.object({ itemId: z.string().describe('Product item ID'), shopId: z.string().describe('Shop ID'), quantity: z.number().describe('New quantity on hand'), reorderPoint: z.number().optional().describe('Reorder point threshold'), reorderLevel: z.number().optional().describe('Reorder quantity'), }), handler: async (args: any) => { try { const updateData: any = { qoh: args.quantity.toString(), }; if (args.reorderPoint !== undefined) { updateData.reorderPoint = args.reorderPoint.toString(); } if (args.reorderLevel !== undefined) { updateData.reorderLevel = args.reorderLevel.toString(); } const result = await client.put<{ ItemShop: InventoryCount }>( `/Item/${args.itemId}/ItemShop`, args.shopId, { ItemShop: updateData } ); return { content: [ { type: 'text' as const, text: JSON.stringify({ success: true, inventory: result.ItemShop }, null, 2), }, ], }; } catch (error) { return { content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }], isError: true, }; } }, }, lightspeed_transfer_stock: { description: 'Transfer inventory between shops', inputSchema: z.object({ fromShopId: z.string().describe('Source shop ID'), toShopId: z.string().describe('Destination shop ID'), items: z.array(z.object({ itemId: z.string().describe('Product item ID'), quantity: z.number().describe('Quantity to transfer'), })).describe('Items to transfer'), }), handler: async (args: any) => { try { const transferData = { fromShopID: args.fromShopId, toShopID: args.toShopId, TransferItems: { TransferItem: args.items.map((item: any) => ({ itemID: item.itemId, quantity: item.quantity.toString(), })), }, }; const result = await client.post<{ Transfer: InventoryTransfer }>( '/Transfer', { Transfer: transferData } ); return { content: [ { type: 'text' as const, text: JSON.stringify({ success: true, transfer: result.Transfer }, null, 2), }, ], }; } catch (error) { return { content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }], isError: true, }; } }, }, lightspeed_list_inventory_adjustments: { description: 'List inventory adjustments (stock changes)', inputSchema: z.object({ itemId: z.string().optional().describe('Filter by product item ID'), shopId: z.string().optional().describe('Filter by shop ID'), limit: z.number().optional().describe('Max adjustments to return (default 100)'), }), handler: async (args: any) => { try { const params: any = {}; if (args.itemId) params.itemID = args.itemId; if (args.shopId) params.shopID = args.shopId; // Note: Lightspeed tracks adjustments through SaleLine with special types const adjustments = await client.get<{ SaleLine: any[] }>('/SaleLine', params); const results = Array.isArray(adjustments.SaleLine) ? adjustments.SaleLine : adjustments.SaleLine ? [adjustments.SaleLine] : []; return { content: [ { type: 'text' as const, text: JSON.stringify({ success: true, count: results.length, adjustments: results.slice(0, args.limit || 100), }, null, 2), }, ], }; } catch (error) { return { content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }], isError: true, }; } }, }, lightspeed_list_suppliers: { description: 'List all suppliers/vendors', inputSchema: z.object({ limit: z.number().optional().describe('Max suppliers to return (default 100)'), archived: z.boolean().optional().describe('Include archived suppliers'), }), handler: async (args: { limit?: number; archived?: boolean }) => { try { const params: any = {}; if (args.archived !== undefined) { params.archived = args.archived; } const suppliers = await client.getAll('/Vendor', 'Vendor', args.limit || 100); return { content: [ { type: 'text' as const, text: JSON.stringify({ success: true, count: suppliers.length, suppliers: suppliers.map(s => ({ vendorID: s.vendorID, name: s.name, accountNumber: s.accountNumber, archived: s.archived, })), }, null, 2), }, ], }; } catch (error) { return { content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }], isError: true, }; } }, }, lightspeed_create_purchase_order: { description: 'Create a purchase order for restocking inventory', inputSchema: z.object({ vendorId: z.string().describe('Supplier/vendor ID'), shopId: z.string().describe('Shop ID'), items: z.array(z.object({ itemId: z.string().describe('Product item ID'), quantity: z.number().describe('Quantity to order'), unitCost: z.string().describe('Unit cost'), })).describe('Items to order'), }), handler: async (args: any) => { try { const poData = { vendorID: args.vendorId, shopID: args.shopId, PurchaseOrderLines: { PurchaseOrderLine: args.items.map((item: any) => ({ itemID: item.itemId, quantity: item.quantity.toString(), unitCost: item.unitCost, })), }, }; const result = await client.post<{ PurchaseOrder: PurchaseOrder }>( '/PurchaseOrder', { PurchaseOrder: poData } ); return { content: [ { type: 'text' as const, text: JSON.stringify({ success: true, purchaseOrder: result.PurchaseOrder }, null, 2), }, ], }; } catch (error) { return { content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }], isError: true, }; } }, }, lightspeed_list_purchase_orders: { description: 'List purchase orders', inputSchema: z.object({ vendorId: z.string().optional().describe('Filter by vendor ID'), status: z.string().optional().describe('Filter by status (e.g., open, complete)'), limit: z.number().optional().describe('Max POs to return (default 100)'), }), handler: async (args: any) => { try { const params: any = {}; if (args.vendorId) params.vendorID = args.vendorId; if (args.status) params.status = args.status; const pos = await client.getAll( '/PurchaseOrder', 'PurchaseOrder', args.limit || 100 ); return { content: [ { type: 'text' as const, text: JSON.stringify({ success: true, count: pos.length, purchaseOrders: pos.map(po => ({ purchaseOrderID: po.purchaseOrderID, vendorID: po.vendorID, orderNumber: po.orderNumber, status: po.status, createTime: po.createTime, })), }, null, 2), }, ], }; } catch (error) { return { content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }], isError: true, }; } }, }, }; }