purchase-mcp-server
Version:
Purchase and budget management server handling requisitions, purchase orders, expenses, budgets, and vendor management with ERP access for data extraction
552 lines • 20.2 kB
JavaScript
import { getMongoClient } from "../utils/mongodb.js";
import { getTypesenseClient } from "../utils/typesense.js";
import { config } from "../utils/config.js";
import { logger } from "../utils/logger.js";
import { ObjectId } from "mongodb";
export class PurchaseToolHandler {
constructor() {
this.typesenseClient = getTypesenseClient();
}
async getPurchaseRequisitionDetails(arguments_) {
const { requisitionId } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
const requisition = await db.collection('purchase_requisitions')
.findOne({ _id: new ObjectId(requisitionId) });
if (!requisition) {
throw new Error(`Purchase requisition not found: ${requisitionId}`);
}
return [{
type: "text",
text: JSON.stringify(requisition, null, 2)
}];
}
catch (error) {
logger.error('Error getting purchase requisition details:', error);
throw error;
}
}
async getPurchaseOrderDetails(arguments_) {
const { orderId } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
const order = await db.collection('purchase_orders')
.findOne({ _id: new ObjectId(orderId) });
if (!order) {
throw new Error(`Purchase order not found: ${orderId}`);
}
return [{
type: "text",
text: JSON.stringify(order, null, 2)
}];
}
catch (error) {
logger.error('Error getting purchase order details:', error);
throw error;
}
}
async listRequisitionsByStatus(arguments_) {
const { status, limit = 10 } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
const requisitions = await db.collection('purchase_requisitions')
.find({ status })
.sort({ createdAt: -1 })
.limit(limit)
.toArray();
return [{
type: "text",
text: JSON.stringify(requisitions, null, 2)
}];
}
catch (error) {
logger.error('Error listing requisitions by status:', error);
throw error;
}
}
async listPurchaseOrdersByStatus(arguments_) {
const { status, limit = 10 } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
const orders = await db.collection('purchase_orders')
.find({ status })
.sort({ createdAt: -1 })
.limit(limit)
.toArray();
return [{
type: "text",
text: JSON.stringify(orders, null, 2)
}];
}
catch (error) {
logger.error('Error listing purchase orders by status:', error);
throw error;
}
}
async listRequisitionsByTypeAndStage(arguments_) {
const { type, stage, limit = 10 } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
const requisitions = await db.collection('purchase_requisitions')
.find({ type, stage })
.sort({ createdAt: -1 })
.limit(limit)
.toArray();
return [{
type: "text",
text: JSON.stringify(requisitions, null, 2)
}];
}
catch (error) {
logger.error('Error listing requisitions by type and stage:', error);
throw error;
}
}
async listRecentRequisitionsByOrderPriority(arguments_) {
const { priority, limit = 10 } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
const requisitions = await db.collection('purchase_requisitions')
.find({ priority })
.sort({ createdAt: -1 })
.limit(limit)
.toArray();
return [{
type: "text",
text: JSON.stringify(requisitions, null, 2)
}];
}
catch (error) {
logger.error('Error listing recent requisitions by priority:', error);
throw error;
}
}
async listTopExpensesByCategory(arguments_) {
const { category, year, limit = 10 } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
const expenses = await db.collection('expenses')
.aggregate([
{
$match: {
category,
year,
status: 'approved'
}
},
{
$group: {
_id: "$subcategory",
totalAmount: { $sum: "$amount" },
count: { $sum: 1 }
}
},
{
$sort: { totalAmount: -1 }
},
{
$limit: limit
}
]).toArray();
return [{
type: "text",
text: JSON.stringify(expenses, null, 2)
}];
}
catch (error) {
logger.error('Error listing top expenses by category:', error);
throw error;
}
}
async listCommittedCostExpenses(arguments_) {
const { vesselId, year } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
const expenses = await db.collection('expenses')
.find({
vesselId,
year,
status: 'committed'
})
.sort({ createdAt: -1 })
.toArray();
return [{
type: "text",
text: JSON.stringify(expenses, null, 2)
}];
}
catch (error) {
logger.error('Error listing committed cost expenses:', error);
throw error;
}
}
async getVesselPurchaseLogTable(arguments_) {
const { vesselId, startDate, endDate } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
const query = { vesselId };
if (startDate || endDate) {
query.createdAt = {};
if (startDate) {
query.createdAt.$gte = new Date(startDate);
}
if (endDate) {
query.createdAt.$lte = new Date(endDate);
}
}
const logs = await db.collection('purchase_logs')
.find(query)
.sort({ createdAt: -1 })
.toArray();
return [{
type: "text",
text: JSON.stringify(logs, null, 2)
}];
}
catch (error) {
logger.error('Error getting vessel purchase log table:', error);
throw error;
}
}
async listRecentUrgentRequisitions(arguments_) {
const { limit = 10 } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
const requisitions = await db.collection('purchase_requisitions')
.find({
priority: 'urgent',
status: { $in: ['pending', 'in_progress'] }
})
.sort({ createdAt: -1 })
.limit(limit)
.toArray();
return [{
type: "text",
text: JSON.stringify(requisitions, null, 2)
}];
}
catch (error) {
logger.error('Error listing recent urgent requisitions:', error);
throw error;
}
}
async getCompleteVesselBudgetData(arguments_) {
const { imo, year = new Date().getFullYear() } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
// Get vessel information
const vessel = await db.collection('vessels').findOne({ imo });
if (!vessel) {
throw new Error(`Vessel not found with IMO: ${imo}`);
}
// Get budget data
const budgetData = await db.collection('budgets')
.aggregate([
{
$match: {
vesselId: vessel._id,
year: year
}
},
{
$group: {
_id: {
category: "$category",
group: "$group"
},
totalBudget: { $sum: "$budgetAmount" },
totalActual: { $sum: "$actualAmount" },
totalCommitted: { $sum: "$committedAmount" }
}
},
{
$project: {
_id: 0,
category: "$_id.category",
group: "$_id.group",
totalBudget: 1,
totalActual: 1,
totalCommitted: 1,
variance: { $subtract: ["$totalBudget", { $add: ["$totalActual", "$totalCommitted"] }] }
}
},
{
$sort: { group: 1, category: 1 }
}
]).toArray();
// Get expense breakdown
const expenseBreakdown = await db.collection('expenses')
.aggregate([
{
$match: {
vesselId: vessel._id,
year: year,
status: 'approved'
}
},
{
$group: {
_id: {
category: "$category",
month: { $month: "$date" }
},
totalAmount: { $sum: "$amount" },
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
category: "$_id.category",
month: "$_id.month",
totalAmount: 1,
count: 1
}
},
{
$sort: { month: 1, category: 1 }
}
]).toArray();
// Get committed costs
const committedCosts = await db.collection('purchase_orders')
.aggregate([
{
$match: {
vesselId: vessel._id,
year: year,
status: { $in: ['committed', 'in_progress'] }
}
},
{
$group: {
_id: {
category: "$category",
month: { $month: "$createdAt" }
},
totalAmount: { $sum: "$amount" },
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
category: "$_id.category",
month: "$_id.month",
totalAmount: 1,
count: 1
}
},
{
$sort: { month: 1, category: 1 }
}
]).toArray();
// Calculate totals
const totalBudget = budgetData.reduce((sum, item) => sum + (item.totalBudget || 0), 0);
const totalActual = budgetData.reduce((sum, item) => sum + (item.totalActual || 0), 0);
const totalCommitted = budgetData.reduce((sum, item) => sum + (item.totalCommitted || 0), 0);
const result = {
vessel: {
imo: vessel.imo,
name: vessel.name
},
year,
budgetSummary: budgetData,
expenseBreakdown,
committedCosts,
totalBudget,
totalActual,
totalCommitted
};
return [{
type: "text",
text: JSON.stringify(result, null, 2)
}];
}
catch (error) {
logger.error('Error getting complete vessel budget data:', error);
throw error;
}
}
async getAllVesselPurchaseRequisitions(arguments_) {
const { imo, status, startDate, endDate } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
// Get vessel information
const vessel = await db.collection('vessels').findOne({ imo });
if (!vessel) {
throw new Error(`Vessel not found with IMO: ${imo}`);
}
// Build query
const query = { vesselId: vessel._id };
if (status) {
query.status = status;
}
if (startDate || endDate) {
query.createdAt = {};
if (startDate) {
query.createdAt.$gte = new Date(startDate);
}
if (endDate) {
query.createdAt.$lte = new Date(endDate);
}
}
// Get purchase requisitions
const requisitions = await db.collection('purchase_requisitions')
.find(query)
.sort({ createdAt: -1 })
.toArray();
// Get related purchase orders for each requisition
const requisitionsWithOrders = await Promise.all(requisitions.map(async (req) => {
const orders = await db.collection('purchase_orders')
.find({ requisitionId: req._id })
.toArray();
return {
...req,
purchaseOrders: orders
};
}));
return [{
type: "text",
text: JSON.stringify({
vessel: {
imo: vessel.imo,
name: vessel.name
},
requisitions: requisitionsWithOrders
}, null, 2)
}];
}
catch (error) {
logger.error('Error getting vessel purchase requisitions:', error);
throw error;
}
}
async getVesselExpenseData(arguments_) {
const { imo, year = new Date().getFullYear(), category, group } = arguments_;
try {
const mongoClient = await getMongoClient(config.mongoUri);
const db = mongoClient.db(config.dbName);
// Get vessel information
const vessel = await db.collection('vessels').findOne({ imo });
if (!vessel) {
throw new Error(`Vessel not found with IMO: ${imo}`);
}
// Build query
const query = {
vesselId: vessel._id,
year: year
};
if (category) {
query.category = category;
}
if (group) {
query.group = group;
}
// Get actual expenses
const actualExpenses = await db.collection('expenses')
.aggregate([
{
$match: {
...query,
status: 'approved'
}
},
{
$group: {
_id: {
category: "$category",
group: "$group",
month: { $month: "$date" }
},
totalAmount: { $sum: "$amount" },
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
category: "$_id.category",
group: "$_id.group",
month: "$_id.month",
totalAmount: 1,
count: 1
}
},
{
$sort: { month: 1, category: 1 }
}
]).toArray();
// Get committed costs
const committedCosts = await db.collection('purchase_orders')
.aggregate([
{
$match: {
...query,
status: { $in: ['committed', 'in_progress'] }
}
},
{
$group: {
_id: {
category: "$category",
group: "$group",
month: { $month: "$createdAt" }
},
totalAmount: { $sum: "$amount" },
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
category: "$_id.category",
group: "$_id.group",
month: "$_id.month",
totalAmount: 1,
count: 1
}
},
{
$sort: { month: 1, category: 1 }
}
]).toArray();
// Calculate totals
const totalActual = actualExpenses.reduce((sum, item) => sum + (item.totalAmount || 0), 0);
const totalCommitted = committedCosts.reduce((sum, item) => sum + (item.totalAmount || 0), 0);
return [{
type: "text",
text: JSON.stringify({
vessel: {
imo: vessel.imo,
name: vessel.name
},
year,
actualExpenses,
committedCosts,
totals: {
actual: totalActual,
committed: totalCommitted,
total: totalActual + totalCommitted
}
}, null, 2)
}];
}
catch (error) {
logger.error('Error getting vessel expense data:', error);
throw error;
}
}
}
//# sourceMappingURL=purchaseTools.js.map