purchase-mcp-server
Version:
Purchase and budget management server handling requisitions, purchase orders, expenses, budgets, and vendor management with ERP access for data extraction
287 lines • 13 kB
JavaScript
import { getTypesenseClient } from "syia-mcp-utils";
import { logger } from "../../index.js";
export class ExportToolHandler {
constructor() {
this.typesenseClient = getTypesenseClient();
}
/**
* Convert Unix timestamps to ISO strings for specific date fields
*/
convertUnixDates(doc) {
const dateFields = [
'purchaseRequisitionDate',
'purchaseOrderIssuedDate',
'orderReadinessDate',
'date',
'poDate',
'expenseDate'
];
const convertedDoc = { ...doc };
dateFields.forEach(field => {
if (convertedDoc[field] && typeof convertedDoc[field] === 'number') {
// Convert Unix timestamp (seconds) to ISO string
convertedDoc[field] = new Date(convertedDoc[field] * 1000).toISOString();
}
});
return convertedDoc;
}
async getCompleteVesselPurchaseRequisitionData(arguments_) {
const collection = "purchase";
const imo = arguments_.imo;
const start_date = arguments_.start_date;
const end_date = arguments_.end_date;
if (!imo) {
throw new Error("IMO number is required");
}
try {
// Compose filter_by string from inputs
const filterParts = [`imo:${imo}`];
if (start_date) {
const startTs = Math.floor(new Date(start_date).getTime() / 1000);
filterParts.push(`purchaseRequisitionDate:>=${startTs}`);
}
if (end_date) {
const endTs = Math.floor(new Date(end_date).getTime() / 1000);
filterParts.push(`purchaseRequisitionDate:<=${endTs}`);
}
const filterBy = filterParts.join(" && ");
// Build query for export
const query = {
filter_by: filterBy
};
logger.debug(`[Typesense Query] ${JSON.stringify(query)}`);
// Use export method to get all documents
const exportResult = await this.typesenseClient.collections(collection).documents().export(query);
// Debug the export result
logger.debug(`Export result type: ${typeof exportResult}`);
logger.debug(`Export result length: ${exportResult?.length || 'undefined'}`);
logger.debug(`First 200 chars of export result: ${exportResult?.substring(0, 200)}`);
// Parse the export result (it's typically JSONL format)
const lines = exportResult.split('\n').filter((line) => line.trim());
logger.debug(`Number of lines after split: ${lines.length}`);
logger.debug(`First line: ${lines[0]}`);
const documents = lines.map((line, index) => {
try {
const doc = JSON.parse(line);
// Remove embedding field and convert dates
delete doc.embedding;
const convertedDoc = this.convertUnixDates(doc);
if (index === 0) {
logger.debug(`First converted document keys: ${Object.keys(convertedDoc).join(', ')}`);
}
return convertedDoc;
}
catch (error) {
logger.error(`Error parsing line ${index}: ${line}`);
return {};
}
});
if (documents.length === 0) {
return [{
type: "text",
text: `No purchase requisitions found for vessel with IMO ${imo}.`,
title: "No Purchase Requisitions Found",
format: "json"
}];
}
const vesselName = documents[0]?.vesselName || null;
const title = `Exported purchase requisitions for IMO ${imo}`;
const artifactTitle = `Purchase requisition export artifact for IMO ${imo}`;
const linkHeader = `Purchase requisition export for IMO ${imo}`;
// Process results for export tool
return [{
type: "text",
text: JSON.stringify({
summary: {
imo: imo,
vesselName: vesselName,
recordCount: documents.length,
exportType: "purchase_requisition_data"
},
data: documents
}, null, 2),
title: title,
format: "json"
}];
}
catch (error) {
logger.error(`Error executing purchase requisition export: ${error}`);
throw new Error(`Error exporting purchase requisitions: ${error.message}`);
}
}
async getCompleteVesselExpenseData(arguments_) {
const collection = "expense";
const imo = arguments_.imo;
const start_date = arguments_.start_date;
const end_date = arguments_.end_date;
const excludeFieldsStr = "_id,docId,fleetId,vesselId,fleetManagerId,technicalSuperintendentId";
if (!imo) {
throw new Error("IMO number is required");
}
try {
// Build filter_by string from inputs
const filterParts = [`imo:${imo}`];
if (start_date) {
const startTs = Math.floor(new Date(start_date).getTime() / 1000);
filterParts.push(`expenseDate:>=${startTs}`);
}
if (end_date) {
const endTs = Math.floor(new Date(end_date).getTime() / 1000);
filterParts.push(`expenseDate:<=${endTs}`);
}
const filterBy = filterParts.join(" && ");
// Build query for export
const query = {
filter_by: filterBy,
exclude_fields: excludeFieldsStr
};
logger.debug(`[Typesense Query] ${JSON.stringify(query)}`);
// Use export method to get all documents
const exportResult = await this.typesenseClient.collections(collection).documents().export(query);
// Debug the export result
logger.debug(`Export result type: ${typeof exportResult}`);
logger.debug(`Export result length: ${exportResult?.length || 'undefined'}`);
logger.debug(`First 200 chars of export result: ${exportResult?.substring(0, 200)}`);
// Parse the export result (it's typically JSONL format)
const lines = exportResult.split('\n').filter((line) => line.trim());
logger.debug(`Number of lines after split: ${lines.length}`);
logger.debug(`First line: ${lines[0]}`);
const documents = lines.map((line, index) => {
try {
const doc = JSON.parse(line);
// Remove embedding field and convert dates
delete doc.embedding;
const convertedDoc = this.convertUnixDates(doc);
if (index === 0) {
logger.debug(`First converted document keys: ${Object.keys(convertedDoc).join(', ')}`);
}
return convertedDoc;
}
catch (error) {
logger.error(`Error parsing line ${index}: ${line}`);
return {};
}
});
if (documents.length === 0) {
return [{
type: "text",
text: `No expense records found for vessel with IMO ${imo}.`,
title: "No Expense Records Found",
format: "json"
}];
}
const vesselName = documents[0]?.vesselName || null;
const title = `Exported expense records for IMO ${imo}`;
// Process results for export tool
return [{
type: "text",
text: JSON.stringify({
summary: {
imo: imo,
vesselName: vesselName,
recordCount: documents.length,
exportType: "complete_expense_data"
},
data: documents
}, null, 2),
title: title,
format: "json"
}];
}
catch (error) {
logger.error(`Error executing expense export: ${error}`);
throw new Error(`Error exporting expense records: ${error.message}`);
}
}
async getCompleteVesselBudgetData(arguments_) {
const collection = "budget";
const imo = arguments_.imo;
const start_date = arguments_.start_date;
const end_date = arguments_.end_date;
const excludeFieldsStr = "_id,docId,fleetId,vesselId,fleetManagerId,technicalSuperintendentId";
if (!imo) {
throw new Error("IMO number is required");
}
try {
// Build filter_by string from inputs
const filterParts = [`imo:${imo}`];
if (start_date) {
const startTs = Math.floor(new Date(start_date).getTime() / 1000);
filterParts.push(`date:>=${startTs}`);
}
if (end_date) {
const endTs = Math.floor(new Date(end_date).getTime() / 1000);
filterParts.push(`date:<=${endTs}`);
}
const filterBy = filterParts.join(" && ");
// Build query for export
const query = {
filter_by: filterBy,
exclude_fields: excludeFieldsStr
};
logger.debug(`[Typesense Query] ${JSON.stringify(query)}`);
// Use export method to get all documents
const exportResult = await this.typesenseClient.collections(collection).documents().export(query);
// Debug the export result
logger.debug(`Export result type: ${typeof exportResult}`);
logger.debug(`Export result length: ${exportResult?.length || 'undefined'}`);
logger.debug(`First 200 chars of export result: ${exportResult?.substring(0, 200)}`);
// Parse the export result (it's typically JSONL format)
const lines = exportResult.split('\n').filter((line) => line.trim());
logger.debug(`Number of lines after split: ${lines.length}`);
logger.debug(`First line: ${lines[0]}`);
const documents = lines.map((line, index) => {
try {
const doc = JSON.parse(line);
// Remove embedding field
delete doc.embedding;
if (index === 0) {
logger.debug(`Original document keys before conversion: ${Object.keys(doc).join(', ')}`);
}
// Convert Unix timestamps to ISO strings
const convertedDoc = this.convertUnixDates(doc);
if (index === 0) {
logger.debug(`Final document keys: ${Object.keys(convertedDoc).join(', ')}`);
}
return convertedDoc;
}
catch (error) {
logger.error(`Error parsing line ${index}: ${line}`);
return {};
}
});
if (documents.length === 0) {
return [{
type: "text",
text: `No budget records found for vessel with IMO ${imo}.`,
title: "No Budget Records Found",
format: "json"
}];
}
const vesselName = documents[0]?.vesselName || null;
const title = `Exported budget records for IMO ${imo}`;
const artifactTitle = `Budget export artifact for IMO ${imo}`;
const linkHeader = `Budget export for IMO ${imo}`;
// Process results for export tool
return [{
type: "text",
text: JSON.stringify({
summary: {
imo: imo,
vesselName: vesselName,
recordCount: documents.length,
exportType: "complete_budget_data"
},
data: documents
}, null, 2),
title: title,
format: "json"
}];
}
catch (error) {
logger.error(`Error executing budget export: ${error}`);
throw new Error(`Error exporting budget records: ${error.message}`);
}
}
}
//# sourceMappingURL=exportTools.js.map