UNPKG

jobnimbus-mcp-client

Version:

JobNimbus MCP Client - Connect Claude Desktop to remote JobNimbus MCP server

283 lines 11.7 kB
/** * Get Estimates Tool * Enhanced with status filtering, sent/approved date filtering, and sorting capabilities */ import { BaseTool } from '../baseTool.js'; export class GetEstimatesTool extends BaseTool { get definition() { return { name: 'get_estimates', description: 'Retrieve estimates from JobNimbus with pagination, date filtering, status filtering, sent/approved date filtering, and sorting', inputSchema: { type: 'object', properties: { from: { type: 'number', description: 'Starting index for pagination (default: 0)', }, size: { type: 'number', description: 'Number of records to retrieve (default: 50, max: 100)', }, date_from: { type: 'string', description: 'Start date filter for date_created (YYYY-MM-DD format)', }, date_to: { type: 'string', description: 'End date filter for date_created (YYYY-MM-DD format)', }, sent_from: { type: 'string', description: 'Filter estimates sent on or after this date (date_sent >= fecha, YYYY-MM-DD format)', }, sent_to: { type: 'string', description: 'Filter estimates sent on or before this date (date_sent <= fecha, YYYY-MM-DD format)', }, approved_from: { type: 'string', description: 'Filter estimates signed on or after this date (date_signed >= fecha, YYYY-MM-DD format)', }, approved_to: { type: 'string', description: 'Filter estimates signed on or before this date (date_signed <= fecha, YYYY-MM-DD format)', }, has_approval: { type: 'boolean', description: 'Filter only estimates with approval/signed status (date_signed > 0)', }, status: { type: 'string', description: 'Filter by estimate status (e.g., "pending", "approved", "rejected")', }, sort_by: { type: 'string', description: 'Field to sort by', enum: ['date_sent', 'date_approved', 'date_created', 'date_updated'], }, order: { type: 'string', description: 'Sort order (asc or desc)', enum: ['asc', 'desc'], }, }, }, }; } /** * Convert YYYY-MM-DD string to Unix timestamp */ dateStringToUnix(dateStr, isStartOfDay = true) { const date = new Date(dateStr + 'T00:00:00Z'); if (isStartOfDay) { return Math.floor(date.getTime() / 1000); } else { // End of day (23:59:59) return Math.floor(date.getTime() / 1000) + 86399; } } /** * Filter estimates by date_created */ filterByDateCreated(estimates, dateFrom, dateTo) { let filtered = estimates; if (dateFrom) { const fromTs = this.dateStringToUnix(dateFrom, true); filtered = filtered.filter(e => (e.date_created || 0) >= fromTs); } if (dateTo) { const toTs = this.dateStringToUnix(dateTo, false); filtered = filtered.filter(e => (e.date_created || 0) <= toTs); } return filtered; } /** * Filter estimates by sent date (date_sent) */ filterBySentDate(estimates, sentFrom, sentTo) { let filtered = estimates; if (sentFrom) { const sentFromTs = this.dateStringToUnix(sentFrom, true); filtered = filtered.filter(e => (e.date_sent || 0) >= sentFromTs); } if (sentTo) { const sentToTs = this.dateStringToUnix(sentTo, false); filtered = filtered.filter(e => { const dateSent = e.date_sent || 0; return dateSent > 0 && dateSent <= sentToTs; }); } return filtered; } /** * Filter estimates by signed date (date_signed) and has_approval */ filterByApprovedDate(estimates, approvedFrom, approvedTo, hasApproval) { let filtered = estimates; // Filter by has_approval first (using date_signed) if (hasApproval !== undefined) { if (hasApproval) { filtered = filtered.filter(e => (e.date_signed || 0) > 0); } else { filtered = filtered.filter(e => (e.date_signed || 0) === 0); } } // Filter by approved_from (using date_signed) if (approvedFrom) { const approvedFromTs = this.dateStringToUnix(approvedFrom, true); filtered = filtered.filter(e => (e.date_signed || 0) >= approvedFromTs); } // Filter by approved_to (using date_signed) if (approvedTo) { const approvedToTs = this.dateStringToUnix(approvedTo, false); filtered = filtered.filter(e => { const dateSigned = e.date_signed || 0; return dateSigned > 0 && dateSigned <= approvedToTs; }); } return filtered; } /** * Filter estimates by status */ filterByStatus(estimates, status) { if (!status) { return estimates; } const lowerStatus = status.toLowerCase(); return estimates.filter(e => { const estimateStatus = String(e.status_name || '').toLowerCase(); return estimateStatus.includes(lowerStatus); }); } /** * Sort estimates by specified field */ sortEstimates(estimates, sortBy, order = 'desc') { if (!sortBy || estimates.length === 0) { return estimates; } const validFields = ['date_sent', 'date_approved', 'date_created', 'date_updated']; if (!validFields.includes(sortBy)) { return estimates; } const reverse = order === 'desc'; return [...estimates].sort((a, b) => { const aVal = a[sortBy] || 0; const bVal = b[sortBy] || 0; return reverse ? bVal - aVal : aVal - bVal; }); } async execute(input, context) { const fromIndex = input.from || 0; const requestedSize = Math.min(input.size || 50, 100); const order = input.order || 'desc'; // Determine if we need to fetch all estimates for filtering/sorting const needsFullFetch = input.date_from || input.date_to || input.sent_from || input.sent_to || input.approved_from || input.approved_to || input.has_approval !== undefined || input.status || input.sort_by; if (needsFullFetch) { // Fetch all estimates with pagination const batchSize = 100; const maxIterations = 50; let allEstimates = []; let offset = 0; let iteration = 0; while (iteration < maxIterations) { const params = { size: batchSize, from: offset }; const response = await this.client.get(context.apiKey, 'estimates', params); const batch = response.data?.results || []; if (batch.length === 0) { break; } allEstimates = allEstimates.concat(batch); offset += batchSize; iteration++; if (batch.length < batchSize) { break; } } // Apply date_created filtering let filteredEstimates = this.filterByDateCreated(allEstimates, input.date_from, input.date_to); // Apply sent date filtering if (input.sent_from || input.sent_to) { filteredEstimates = this.filterBySentDate(filteredEstimates, input.sent_from, input.sent_to); } // Apply approved date filtering if (input.approved_from || input.approved_to || input.has_approval !== undefined) { filteredEstimates = this.filterByApprovedDate(filteredEstimates, input.approved_from, input.approved_to, input.has_approval); } // Apply status filtering if (input.status) { filteredEstimates = this.filterByStatus(filteredEstimates, input.status); } // Apply sorting if (input.sort_by) { filteredEstimates = this.sortEstimates(filteredEstimates, input.sort_by, order); } // Paginate const paginatedEstimates = filteredEstimates.slice(fromIndex, fromIndex + requestedSize); return { count: paginatedEstimates.length, total_filtered: filteredEstimates.length, total_fetched: allEstimates.length, iterations: iteration, from: fromIndex, size: requestedSize, has_more: fromIndex + paginatedEstimates.length < filteredEstimates.length, total_pages: Math.ceil(filteredEstimates.length / requestedSize), current_page: Math.floor(fromIndex / requestedSize) + 1, date_filter_applied: !!(input.date_from || input.date_to), date_from: input.date_from, date_to: input.date_to, sent_date_filter_applied: !!(input.sent_from || input.sent_to), sent_from: input.sent_from, sent_to: input.sent_to, approved_date_filter_applied: !!(input.approved_from || input.approved_to || input.has_approval !== undefined), approved_from: input.approved_from, approved_to: input.approved_to, has_approval: input.has_approval, status_filter_applied: !!input.status, status: input.status, sort_applied: !!input.sort_by, sort_by: input.sort_by, order: order, results: paginatedEstimates, }; } else { // Simple pagination without filtering const params = { from: fromIndex, size: requestedSize, }; const result = await this.client.get(context.apiKey, 'estimates', params); const estimates = result.data?.results || []; return { count: estimates.length, total_filtered: estimates.length, from: fromIndex, size: requestedSize, has_more: false, date_filter_applied: false, sent_date_filter_applied: false, approved_date_filter_applied: false, status_filter_applied: false, sort_applied: false, results: estimates, }; } } } //# sourceMappingURL=getEstimates.js.map