AroFlo: Get PurchaseOrders
aroflo_get_purchaseordersRetrieve and filter purchase order data from AroFlo using customizable queries, joins, and output modes to manage procurement workflows.
Instructions
Query the AroFlo PurchaseOrders zone (GET). Use pipe-delimited WHERE clauses like "and|field|=|value", ORDER clauses like "field|asc", and JOIN areas like "lineitems". where/order/join accept either a single string or an array. mode: data|verbose|debug|raw (default: data). Set compact=true and optionally select=["field","nested.field"] to reduce payload size. See resource "aroflo://docs/api/" (example: "aroflo://docs/api/quotes") for valid fields/values.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| where | No | ||
| order | No | ||
| join | No | ||
| page | No | ||
| pageSize | No | ||
| autoPaginate | No | ||
| maxPages | No | ||
| maxResults | No | ||
| maxItemsTotal | No | ||
| validateWhere | No | ||
| mode | No | ||
| verbose | No | ||
| debug | No | ||
| raw | No | ||
| compact | No | ||
| select | No | ||
| maxItems | No | ||
| extra | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/mcp/tools/reports.ts:2243-2524 (handler)Registration and handler implementation for 'aroflo_report_open_projects_with_task_hours' (a similar reporting tool). Although 'aroflo_get_purchaseorders' was not found in the codebase, the structure for registering such tools in 'src/mcp/app.ts' and 'src/mcp/tools/*.ts' has been confirmed.
server.registerTool( 'aroflo_report_open_projects_with_task_hours', { title: 'AroFlo: Report Open Projects With Task Hours', description: 'Report open projects (status=open, closeddate empty) and their tasks with total labour hours. ' + 'Internally fetches Projects then Tasks (join project + tasktotals) and returns a compact grouped output.', inputSchema: { sinceCreatedUtc: z.string().min(1).optional(), orgId: z.string().min(1).optional(), managerUserId: z.string().min(1).optional(), sinceDateRequested: z.string().min(1).optional(), sinceTaskCreatedUtc: z.string().min(1).optional(), includeTasksWithoutProject: z.boolean().default(false), hoursOnly: z.boolean().default(false), includeTaskStatus: z.boolean().default(true), pageSize: z.number().int().positive().max(500).default(200), maxProjects: z.number().int().positive().max(5000).default(2000), maxTasksPerClient: z.number().int().positive().max(5000).default(2000), mode: z.enum(['data', 'verbose', 'debug', 'raw']).optional(), verbose: z.boolean().optional(), debug: z.boolean().optional() }, // MCP SDK expects output schemas to be object schemas (or raw object shapes). // `z.any()` causes output validation to crash under the current SDK. outputSchema: z.object({}).passthrough(), annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true } }, async (args) => { const mode = resolveOutputMode(args); try { const whereClauses: string[] = []; if (args.orgId) { whereClauses.push(`and|orgid|=|${args.orgId}`); } if (args.sinceCreatedUtc) { whereClauses.push(`and|createdutc|>|${args.sinceCreatedUtc}`); } const where = normalizeWhereParam(whereClauses); const pageSize = args.pageSize; const maxProjects = args.maxProjects; let projectsResponse = await client.get('Projects', { where, page: 1, pageSize }); let pagesFetchedProjects = 1; while (true) { const projects = getProjectsArray(projectsResponse.data); if (projects.length >= maxProjects) { projectsResponse = { ...projectsResponse, data: truncateZoneArrays(projectsResponse.data, maxProjects).truncated }; break; } if (projects.length < pageSize) { break; } const nextPage = pagesFetchedProjects + 1; const next = await client.get('Projects', { where, page: nextPage, pageSize }); const nextProjects = getProjectsArray(next.data); if (nextProjects.length === 0) { break; } projectsResponse = { ...projectsResponse, data: mergeZoneResponseData(projectsResponse.data, next.data).merged }; pagesFetchedProjects += 1; if (nextProjects.length < pageSize) { break; } } const allProjects = getProjectsArray(projectsResponse.data); const openProjects = pickOpenProjects(allProjects, args.managerUserId); const openProjectIds = new Set<string>(); const clientIds = new Set<string>(); for (const p of openProjects) { if (!isRecord(p)) continue; const pid = typeof p.projectid === 'string' ? p.projectid : ''; if (pid) openProjectIds.add(pid); const c = p.client; const cid = isRecord(c) && typeof c.orgid === 'string' ? c.orgid : ''; if (cid) clientIds.add(cid); } const tasksByProject: Record<string, any[]> = {}; for (const pid of openProjectIds) { tasksByProject[pid] = []; } const unassignedByClient: Record<string, any[]> = {}; const perClientMeta: Record<string, unknown> = {}; for (const clientId of clientIds) { const taskWhere: string[] = [`and|clientid|=|${clientId}`]; if (args.sinceDateRequested) { taskWhere.push(`and|daterequested|>|${args.sinceDateRequested}`); } if (args.sinceTaskCreatedUtc) { taskWhere.push(`and|createdutc|>|${args.sinceTaskCreatedUtc}`); } const normalizedTaskWhere = normalizeWhereParam(taskWhere); let tasksResponse = await client.get('Tasks', { where: normalizedTaskWhere, join: ['project', 'tasktotals'], order: ['daterequested|desc'], page: 1, pageSize }); let pagesFetchedTasks = 1; while (true) { const tasks = getTasksArray(tasksResponse.data); if (tasks.length >= args.maxTasksPerClient) { tasksResponse = { ...tasksResponse, data: truncateZoneArrays(tasksResponse.data, args.maxTasksPerClient).truncated }; break; } if (tasks.length < pageSize) { break; } const nextPage = pagesFetchedTasks + 1; const next = await client.get('Tasks', { where: normalizedTaskWhere, join: ['project', 'tasktotals'], order: ['daterequested|desc'], page: nextPage, pageSize }); const nextTasks = getTasksArray(next.data); if (nextTasks.length === 0) { break; } tasksResponse = { ...tasksResponse, data: mergeZoneResponseData(tasksResponse.data, next.data).merged }; pagesFetchedTasks += 1; if (nextTasks.length < pageSize) { break; } } const tasks = getTasksArray(tasksResponse.data).filter(isRecord); let matched = 0; let unassigned = 0; for (const t of tasks) { const proj = t.project; const joinedProjectId = isRecord(proj) && typeof proj.projectid === 'string' ? proj.projectid : undefined; const rawProjectId = typeof t.projectid === 'string' ? t.projectid : undefined; const projectId = joinedProjectId ?? rawProjectId; const totals = t.tasktotals; const hours = isRecord(totals) ? toNumber(totals.totalhrs) : 0; if (args.hoursOnly && hours <= 0) { continue; } const dateRequested = (t.daterequested as any) ?? (t.requestdate as any) ?? (t.requestdatetime as any) ?? undefined; const mapped: Record<string, unknown> = { taskid: t.taskid, refcode: t.refcode, taskname: t.taskname, daterequested: dateRequested, hours }; if (args.includeTaskStatus) { mapped.status = t.status; } if (projectId && openProjectIds.has(projectId)) { tasksByProject[projectId]!.push(mapped); matched += 1; continue; } if (args.includeTasksWithoutProject) { if (!unassignedByClient[clientId]) unassignedByClient[clientId] = []; unassignedByClient[clientId]!.push(mapped); unassigned += 1; } } perClientMeta[clientId] = { pagesFetched: pagesFetchedTasks, totalTasksFetched: tasks.length, matched, unassigned }; } const projects = openProjects .filter(isRecord) .map((p) => { const pid = p.projectid as string; const tasks = (tasksByProject[pid] ?? []).filter(isRecord).sort((a, b) => { const ad = String((a as any).daterequested ?? ''); const bd = String((b as any).daterequested ?? ''); if (ad !== bd) return ad.localeCompare(bd); return String((a as any).refcode ?? '').localeCompare( String((b as any).refcode ?? '') ); }); const totalHours = tasks.reduce((sum, t) => sum + toNumber((t as any).hours), 0); const projectname = typeof p.projectname === 'string' ? p.projectname : undefined; const projectnumber = p.projectnumber; const clientOrg = isRecord(p.client) ? p.client : undefined; return { projectid: pid, projectnumber, projectname, refno: p.refno, client: clientOrg ? { orgid: clientOrg.orgid, orgname: clientOrg.orgname } : undefined, totalHours, tasks }; }) .sort((a, b) => (toInt(a.projectnumber) ?? 0) - (toInt(b.projectnumber) ?? 0)); const projectCount = projects.length; const taskCount = projects.reduce((sum, p) => sum + (p.tasks as any[]).length, 0); const totalHours = projects.reduce((sum, p) => sum + toNumber(p.totalHours), 0); const out: Record<string, unknown> = { projects, summary: { projectCount, taskCount, totalHours } }; if (args.includeTasksWithoutProject) { out.unassignedTasksByClient = unassignedByClient; } if (mode === 'verbose' || mode === 'debug' || mode === 'raw') { out.meta = { projectsFetched: allProjects.length, openProjects: openProjects.length, pagesFetchedProjects, clientsQueried: Array.from(clientIds), perClient: perClientMeta }; } if (mode === 'debug' || mode === 'raw') { out.debug = args.debug ? { whereProjects: where } : undefined; } if (mode === 'raw') { out.raw = { openProjects }; } return successToolResult(out); } catch (error) { return errorToolResult(error, { mode, debug: { tool: 'aroflo_report_open_projects_with_task_hours' } }); } } ); }