qbo-mcp-ts
Version:
TypeScript QuickBooks Online MCP Server with enhanced features and dual transport support
273 lines • 11.8 kB
JavaScript
;
/**
* Natural language date parser for accounting queries
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DateParser = void 0;
const date_fns_1 = require("date-fns");
class DateParser {
/**
* Parse natural language date string to ISO date
*/
static parse(input) {
if (!input) {
return (0, date_fns_1.format)(new Date(), 'yyyy-MM-dd');
}
const normalized = input.toLowerCase().trim();
const today = new Date();
// Check for relative dates
if (normalized === 'today') {
return (0, date_fns_1.format)(today, 'yyyy-MM-dd');
}
if (normalized === 'yesterday') {
return (0, date_fns_1.format)((0, date_fns_1.addDays)(today, -1), 'yyyy-MM-dd');
}
if (normalized === 'tomorrow') {
return (0, date_fns_1.format)((0, date_fns_1.addDays)(today, 1), 'yyyy-MM-dd');
}
// Month-based patterns
if (normalized === 'this month' || normalized === 'current month') {
return (0, date_fns_1.format)((0, date_fns_1.startOfMonth)(today), 'yyyy-MM-dd');
}
if (normalized === 'last month' || normalized === 'previous month') {
return (0, date_fns_1.format)((0, date_fns_1.startOfMonth)((0, date_fns_1.subMonths)(today, 1)), 'yyyy-MM-dd');
}
if (normalized === 'next month') {
return (0, date_fns_1.format)((0, date_fns_1.startOfMonth)((0, date_fns_1.addDays)(today, 30)), 'yyyy-MM-dd');
}
// Quarter-based patterns
const quarterMatch = normalized.match(/q([1-4])\s*(\d{4})?/);
if (quarterMatch) {
const quarter = parseInt(quarterMatch[1]);
const year = quarterMatch[2] ? parseInt(quarterMatch[2]) : today.getFullYear();
const date = new Date(year, (quarter - 1) * 3, 1);
return (0, date_fns_1.format)((0, date_fns_1.startOfQuarter)(date), 'yyyy-MM-dd');
}
if (normalized === 'this quarter' || normalized === 'current quarter') {
return (0, date_fns_1.format)((0, date_fns_1.startOfQuarter)(today), 'yyyy-MM-dd');
}
if (normalized === 'last quarter' || normalized === 'previous quarter') {
return (0, date_fns_1.format)((0, date_fns_1.startOfQuarter)((0, date_fns_1.subQuarters)(today, 1)), 'yyyy-MM-dd');
}
// Year-based patterns
if (normalized === 'this year' || normalized === 'current year') {
return (0, date_fns_1.format)((0, date_fns_1.startOfYear)(today), 'yyyy-MM-dd');
}
if (normalized === 'last year' || normalized === 'previous year') {
return (0, date_fns_1.format)((0, date_fns_1.startOfYear)((0, date_fns_1.subYears)(today, 1)), 'yyyy-MM-dd');
}
if (normalized === 'year to date' || normalized === 'ytd') {
return (0, date_fns_1.format)((0, date_fns_1.startOfYear)(today), 'yyyy-MM-dd');
}
// Fiscal year patterns (assuming fiscal year = calendar year for now)
if (normalized.includes('fiscal year') || normalized.includes('fy')) {
const fyMatch = normalized.match(/(?:fiscal year|fy)\s*(\d{4})/);
if (fyMatch) {
const year = parseInt(fyMatch[1]);
return (0, date_fns_1.format)(new Date(year, 0, 1), 'yyyy-MM-dd');
}
return (0, date_fns_1.format)((0, date_fns_1.startOfYear)(today), 'yyyy-MM-dd');
}
// Month name patterns
const monthNames = [
'january',
'february',
'march',
'april',
'may',
'june',
'july',
'august',
'september',
'october',
'november',
'december',
];
for (let i = 0; i < monthNames.length; i++) {
if (normalized.includes(monthNames[i])) {
const yearMatch = normalized.match(/(\d{4})/);
const year = yearMatch ? parseInt(yearMatch[1]) : today.getFullYear();
return (0, date_fns_1.format)(new Date(year, i, 1), 'yyyy-MM-dd');
}
}
// Try standard date formats
const formats = [
'yyyy-MM-dd',
'MM/dd/yyyy',
'MM-dd-yyyy',
'dd/MM/yyyy',
'dd-MM-yyyy',
'MMM dd, yyyy',
'MMMM dd, yyyy',
];
for (const fmt of formats) {
try {
const parsed = (0, date_fns_1.parse)(input, fmt, new Date());
if ((0, date_fns_1.isValid)(parsed)) {
return (0, date_fns_1.format)(parsed, 'yyyy-MM-dd');
}
}
catch {
// Continue to next format
}
}
// If nothing matches, return the original input
// QuickBooks might understand it better
return input;
}
/**
* Parse date range from natural language
*/
static parseRange(input) {
const normalized = input.toLowerCase().trim();
const today = new Date();
// Common ranges
if (normalized === 'this month' || normalized === 'current month') {
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfMonth)(today), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)((0, date_fns_1.endOfMonth)(today), 'yyyy-MM-dd'),
};
}
if (normalized === 'last month' || normalized === 'previous month') {
const lastMonth = (0, date_fns_1.subMonths)(today, 1);
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfMonth)(lastMonth), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)((0, date_fns_1.endOfMonth)(lastMonth), 'yyyy-MM-dd'),
};
}
if (normalized === 'this quarter' || normalized === 'current quarter') {
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfQuarter)(today), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)((0, date_fns_1.endOfQuarter)(today), 'yyyy-MM-dd'),
};
}
if (normalized === 'last quarter' || normalized === 'previous quarter') {
const lastQuarter = (0, date_fns_1.subQuarters)(today, 1);
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfQuarter)(lastQuarter), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)((0, date_fns_1.endOfQuarter)(lastQuarter), 'yyyy-MM-dd'),
};
}
if (normalized === 'this year' || normalized === 'current year') {
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfYear)(today), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)((0, date_fns_1.endOfYear)(today), 'yyyy-MM-dd'),
};
}
if (normalized === 'last year' || normalized === 'previous year') {
const lastYear = (0, date_fns_1.subYears)(today, 1);
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfYear)(lastYear), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)((0, date_fns_1.endOfYear)(lastYear), 'yyyy-MM-dd'),
};
}
if (normalized === 'year to date' || normalized === 'ytd') {
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfYear)(today), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)(today, 'yyyy-MM-dd'),
};
}
if (normalized === 'month to date' || normalized === 'mtd') {
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfMonth)(today), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)(today, 'yyyy-MM-dd'),
};
}
if (normalized === 'quarter to date' || normalized === 'qtd') {
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfQuarter)(today), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)(today, 'yyyy-MM-dd'),
};
}
// Last N days/months/years
const lastNMatch = normalized.match(/last (\d+) (days?|months?|years?)/);
if (lastNMatch) {
const n = parseInt(lastNMatch[1]);
const unit = lastNMatch[2];
let start;
if (unit.startsWith('day')) {
start = (0, date_fns_1.addDays)(today, -n);
}
else if (unit.startsWith('month')) {
start = (0, date_fns_1.subMonths)(today, n);
}
else {
start = (0, date_fns_1.subYears)(today, n);
}
return {
start: (0, date_fns_1.format)(start, 'yyyy-MM-dd'),
end: (0, date_fns_1.format)(today, 'yyyy-MM-dd'),
};
}
// Quarter patterns (e.g., "Q1 2024")
const quarterMatch = normalized.match(/q([1-4])\s*(\d{4})?/);
if (quarterMatch) {
const quarter = parseInt(quarterMatch[1]);
const year = quarterMatch[2] ? parseInt(quarterMatch[2]) : today.getFullYear();
const date = new Date(year, (quarter - 1) * 3, 1);
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfQuarter)(date), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)((0, date_fns_1.endOfQuarter)(date), 'yyyy-MM-dd'),
};
}
// Default to current month
return {
start: (0, date_fns_1.format)((0, date_fns_1.startOfMonth)(today), 'yyyy-MM-dd'),
end: (0, date_fns_1.format)((0, date_fns_1.endOfMonth)(today), 'yyyy-MM-dd'),
};
}
/**
* Get fiscal year dates (customize based on company settings)
*/
static getFiscalYear(year, fiscalYearStart = 1) {
const targetYear = year || new Date().getFullYear();
const start = new Date(targetYear, fiscalYearStart - 1, 1);
const end = new Date(targetYear + 1, fiscalYearStart - 1, 0);
return {
start: (0, date_fns_1.format)(start, 'yyyy-MM-dd'),
end: (0, date_fns_1.format)(end, 'yyyy-MM-dd'),
};
}
/**
* Format date for display
*/
static formatDisplay(date, formatStr = 'MMM dd, yyyy') {
const d = typeof date === 'string' ? new Date(date) : date;
return (0, date_fns_1.isValid)(d) ? (0, date_fns_1.format)(d, formatStr) : String(date);
}
/**
* Calculate days between dates
*/
static daysBetween(start, end) {
const startDate = typeof start === 'string' ? new Date(start) : start;
const endDate = typeof end === 'string' ? new Date(end) : end;
const diffTime = Math.abs(endDate.getTime() - startDate.getTime());
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
}
/**
* Check if date is overdue
*/
static isOverdue(date) {
const d = typeof date === 'string' ? new Date(date) : date;
return d < new Date();
}
/**
* Add business days to a date (excluding weekends)
*/
static addBusinessDays(date, days) {
let current = typeof date === 'string' ? new Date(date) : new Date(date);
let remaining = Math.abs(days);
const direction = days > 0 ? 1 : -1;
while (remaining > 0) {
current = (0, date_fns_1.addDays)(current, direction);
const dayOfWeek = current.getDay();
// Skip weekends
if (dayOfWeek !== 0 && dayOfWeek !== 6) {
remaining--;
}
}
return (0, date_fns_1.format)(current, 'yyyy-MM-dd');
}
}
exports.DateParser = DateParser;
//# sourceMappingURL=date-parser.js.map