purchase-mcp-server
Version:
Purchase and budget management server handling requisitions, purchase orders, expenses, budgets, and vendor management with ERP access for data extraction
169 lines • 7.54 kB
JavaScript
import { logger } from "../utils/logger.js";
import { Client as TypesenseClient } from "typesense";
import { processTypesenseResults } from "../utils/helper_functions.js";
const typesenseClient = new TypesenseClient({
nodes: [
{
host: "j51ouydaces0i2m7p-1.a1.typesense.net",
port: 443,
protocol: "https"
}
],
apiKey: "wgTfJoajCRXWWNlLILAdZh1bU66RA4wv"
});
export async function smartExpenseSearchHandler(arguments_) {
const collection = "expense";
const session_id = arguments_.session_id || "testing";
const query_text = (arguments_.query || "").trim() || "*";
const filters = arguments_.filters || {};
const sort_by = arguments_.sort_by || "relevance";
const sort_order = arguments_.sort_order || "desc";
const max_results = arguments_.max_results || 10;
try {
// Check if vendor filter is provided
if (filters.vendor) {
try {
// Query to find the actual vendor name
const vendorQuery = {
q: filters.vendor,
query_by: "vendor",
per_page: 1,
sort_by: "_text_match:desc" // Sort by best text match
};
logger.debug(`[Vendor Query] ${JSON.stringify(vendorQuery)}`);
const vendorResults = await typesenseClient.collections(collection).documents().search(vendorQuery);
if (vendorResults && vendorResults.hits && vendorResults.hits.length > 0) {
const document = vendorResults.hits[0].document;
const actualVendorName = document.vendor;
logger.info(`Found actual vendor name: ${actualVendorName} for query: ${filters.vendor}`);
// Replace the vendor filter with the actual vendor name
filters.vendor = actualVendorName;
}
else {
logger.info(`No vendor found for query: ${filters.vendor}`);
// Return early with a message that no matching vendor was found
return [{
type: "text",
text: `No matching vendor found for '${filters.vendor}'. Please try a different vendor name.`,
title: "No Vendor Found",
format: "json"
}];
}
// Keep the vendor filter for the main query
}
catch (error) {
logger.error('Error querying vendor:', error);
// Return early with an error message
return [{
type: "text",
text: `Error searching for vendor '${filters.vendor}': ${error.message}`,
title: "Vendor Search Error",
format: "json"
}];
}
}
// Compose filter_by string from filters
const filterParts = [];
if (filters) {
for (const [key, value] of Object.entries(filters)) {
if (value === null || value === undefined) {
continue;
}
if (key.endsWith("_range")) {
// Handle range filters - dates and amounts
const fieldBase = key.replace("_range", "");
if (fieldBase.toLowerCase().includes("date")) {
// Handle date ranges
const startDate = value.start_date;
const endDate = value.end_date;
if (startDate) {
const startTimestamp = Math.floor(new Date(startDate).getTime() / 1000);
filterParts.push(`${fieldBase}:>=${startTimestamp}`);
}
if (endDate) {
const endTimestamp = Math.floor(new Date(endDate).getTime() / 1000);
filterParts.push(`${fieldBase}:<=${endTimestamp}`);
}
}
else if (fieldBase.toLowerCase().includes("amount")) {
// Handle amount ranges
const minAmount = value.min_amount;
const maxAmount = value.max_amount;
if (minAmount !== undefined) {
filterParts.push(`${fieldBase}:>=${minAmount}`);
}
if (maxAmount !== undefined) {
filterParts.push(`${fieldBase}:<=${maxAmount}`);
}
}
else {
throw new Error(`Unsupported range filter field: ${fieldBase}`);
}
}
else if (typeof value === "boolean") {
filterParts.push(`${key}:=${value.toString().toLowerCase()}`);
}
else if (typeof value === "string") {
filterParts.push(`${key}:=${JSON.stringify(value).replace(/^"|"$/g, "")}`);
}
else {
filterParts.push(`${key}:=${value}`);
}
}
}
const filterBy = filterParts.length > 0 ? filterParts.join(" && ") : undefined;
// Set up query fields based on schema
const queryBy = "vesselName,accountDescription";
const excludeFields = "embedding,_id,docId,fleetId,vesselId,fleetManagerId,technicalSuperintendentId";
const query = {
q: query_text,
query_by: queryBy,
exclude_fields: excludeFields,
per_page: max_results
};
if (filterBy) {
query.filter_by = filterBy;
}
if (sort_by !== "relevance") {
query.sort_by = `${sort_by}:${sort_order}`;
}
logger.debug(`[Typesense Query] ${JSON.stringify(query)}`);
const results = await typesenseClient.collections(collection).documents().search(query);
logger.info(`[Typesense Results] ${JSON.stringify(results)}`);
if (!results || !results.hits || results.hits.length === 0) {
return [{
type: "text",
text: `No expense records found for query '${query_text}'.`,
title: "No Results Found",
format: "json"
}];
}
// Format results using the utility function
const title = `Smart Expense Search Results for '${query_text}'`;
const linkHeader = `Smart expense search result for query: '${query_text}'`;
return await processTypesenseResults(results, "smart_expense_search", title, session_id, linkHeader);
}
catch (error) {
logger.error('Error performing smart expense search:', error);
return [{
type: "text",
text: `Error performing search: ${error.message}`,
title: "Error",
format: "json"
}];
}
}
// test the function
const test = async () => {
const arguments_ = {
session_id: "testing",
query: "BW KIZOKU",
filters: {
vendor: "Marine Mechanics"
}
};
const results = await smartExpenseSearchHandler(arguments_);
console.log(results);
};
test();
//# sourceMappingURL=testing.js.map