UNPKG

amazon-seller-mcp

Version:

Model Context Protocol (MCP) client for Amazon Selling Partner API

319 lines 11.8 kB
/** * Order notifications handler * * This file implements the order status change notification handler */ import { getLogger, info, error, warn } from '../utils/logger.js'; /** * Order status monitor class */ export class OrderStatusMonitor { ordersClient; notificationManager; config; monitoringInterval = null; orderStatusCache = new Map(); isMonitoring = false; constructor(ordersClient, notificationManager, config = {}) { this.ordersClient = ordersClient; this.notificationManager = notificationManager; this.config = { enablePeriodicMonitoring: config.enablePeriodicMonitoring ?? false, monitoringInterval: config.monitoringInterval ?? 5 * 60 * 1000, // 5 minutes maxOrdersPerCheck: config.maxOrdersPerCheck ?? 100, monitoringWindowHours: config.monitoringWindowHours ?? 24, }; } /** * Starts monitoring order status changes */ startMonitoring() { if (this.isMonitoring) { getLogger().info('Order status monitoring is already running'); return; } if (!this.config.enablePeriodicMonitoring) { getLogger().info('Periodic order monitoring is disabled'); return; } getLogger().info(`Starting order status monitoring (interval: ${this.config.monitoringInterval}ms)`); this.isMonitoring = true; // Initial check this.checkOrderStatusChanges().catch((error) => { getLogger().error('Error in initial order status check:', { error: error.message, }); }); // Set up periodic monitoring this.monitoringInterval = setInterval(() => { this.checkOrderStatusChanges().catch((error) => { getLogger().error('Error in periodic order status check:', { error: error.message, }); }); }, this.config.monitoringInterval); } /** * Stops monitoring order status changes */ stopMonitoring() { if (!this.isMonitoring) { getLogger().info('Order status monitoring is not running'); return; } getLogger().info('Stopping order status monitoring'); this.isMonitoring = false; if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = null; } } /** * Checks for order status changes */ async checkOrderStatusChanges() { try { // Calculate the time window for monitoring const now = new Date(); const windowStart = new Date(now.getTime() - this.config.monitoringWindowHours * 60 * 60 * 1000); // Get recent orders const ordersResult = await this.ordersClient.getOrders({ lastUpdatedAfter: windowStart.toISOString(), maxResultsPerPage: this.config.maxOrdersPerCheck, orderStatuses: [ 'PENDING', 'UNSHIPPED', 'PARTIALLY_SHIPPED', 'SHIPPED', 'CANCELED', 'UNFULFILLABLE', ], }); // Check each order for status changes for (const order of ordersResult.orders) { await this.checkSingleOrderStatusChange(order); } info(`Checked ${ordersResult.orders.length} orders for status changes`); } catch (err) { error('Error checking order status changes:', { error: err }); } } /** * Checks a single order for status changes */ async checkSingleOrderStatusChange(order) { const orderId = order.amazonOrderId; const currentStatus = order.orderStatus; const previousStatus = this.orderStatusCache.get(orderId); // Update cache with current status this.orderStatusCache.set(orderId, currentStatus); // If we have a previous status and it's different, send notification if (previousStatus && previousStatus !== currentStatus) { info(`Order ${orderId} status changed from ${previousStatus} to ${currentStatus}`); this.notificationManager.sendOrderStatusChangeNotification({ orderId, previousStatus, newStatus: currentStatus, marketplaceId: order.marketplaceId, orderDetails: { purchaseDate: order.purchaseDate, orderTotal: order.orderTotal, fulfillmentChannel: order.fulfillmentChannel, numberOfItems: (order.numberOfItemsShipped || 0) + (order.numberOfItemsUnshipped || 0), }, }); } } /** * Manually checks a specific order for status changes */ async checkOrderStatus(orderId) { try { const order = await this.ordersClient.getOrder({ amazonOrderId: orderId }); await this.checkSingleOrderStatusChange(order); } catch (err) { error(`Error checking status for order ${orderId}:`, { error: err }); throw err; } } /** * Gets the current monitoring status */ isMonitoringActive() { return this.isMonitoring; } /** * Gets the monitoring configuration */ getConfig() { return { ...this.config }; } /** * Updates the monitoring configuration */ updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; // Restart monitoring if it's currently active and interval changed if (this.isMonitoring && newConfig.monitoringInterval) { this.stopMonitoring(); this.startMonitoring(); } } /** * Clears the order status cache */ clearCache() { this.orderStatusCache.clear(); info('Order status cache cleared'); } /** * Gets the current cache size */ getCacheSize() { return this.orderStatusCache.size; } } /** * Enhanced order status change notification handler */ export class OrderStatusChangeHandler { ordersClient; notificationManager; statusMonitor; constructor(ordersClient, notificationManager, monitoringConfig = {}) { this.ordersClient = ordersClient; this.notificationManager = notificationManager; this.statusMonitor = new OrderStatusMonitor(ordersClient, notificationManager, monitoringConfig); } /** * Sets up order status change notifications */ setup() { // Override updateOrderStatus method to add notification support this.setupUpdateOrderStatusNotifications(); // Start monitoring if enabled this.statusMonitor.startMonitoring(); info('Order status change notifications set up'); } /** * Sets up notifications for updateOrderStatus method calls */ setupUpdateOrderStatusNotifications() { // Store original updateOrderStatus method const originalUpdateOrderStatus = this.ordersClient.updateOrderStatus.bind(this.ordersClient); // Override updateOrderStatus method to add notification support this.ordersClient.updateOrderStatus = async (params, emitNotification = true) => { const { amazonOrderId, action } = params; // Get current order status if notification is enabled let previousStatus = ''; let orderDetails; let marketplaceId = ''; if (emitNotification) { try { const currentOrder = await this.ordersClient.getOrder({ amazonOrderId }); previousStatus = currentOrder.orderStatus; marketplaceId = currentOrder.marketplaceId; orderDetails = { purchaseDate: currentOrder.purchaseDate, orderTotal: currentOrder.orderTotal, fulfillmentChannel: currentOrder.fulfillmentChannel, numberOfItems: (currentOrder.numberOfItemsShipped || 0) + (currentOrder.numberOfItemsUnshipped || 0), }; } catch (err) { // If we can't get the current order, just continue with the update warn(`Could not get current order for ID ${amazonOrderId}: ${err.message}`); } } // Call original method const result = await originalUpdateOrderStatus(params); // Send notification if successful and notification is enabled if (emitNotification && result.success) { // Determine new status based on action const newStatus = this.mapActionToStatus(action, previousStatus); // Only send notification if we have both previous and new status and they're different if (previousStatus && newStatus && previousStatus !== newStatus) { this.notificationManager.sendOrderStatusChangeNotification({ orderId: amazonOrderId, previousStatus, newStatus, marketplaceId: marketplaceId, orderDetails, }); // Update the monitor's cache this.statusMonitor['orderStatusCache'].set(amazonOrderId, newStatus); } } return result; }; } /** * Maps an action to the expected new status */ mapActionToStatus(action, currentStatus) { switch (action) { case 'CONFIRM': // Confirmation typically moves from PENDING to UNSHIPPED return currentStatus === 'PENDING' ? 'UNSHIPPED' : currentStatus; case 'SHIP': // Shipping moves to SHIPPED or PARTIALLY_SHIPPED return currentStatus === 'UNSHIPPED' ? 'SHIPPED' : currentStatus === 'PARTIALLY_SHIPPED' ? 'SHIPPED' : 'SHIPPED'; case 'CANCEL': // Cancellation moves to CANCELED return 'CANCELED'; default: warn(`Unknown action: ${action}`); return currentStatus; } } /** * Gets the status monitor */ getStatusMonitor() { return this.statusMonitor; } /** * Manually checks an order for status changes */ async checkOrderStatus(orderId) { return this.statusMonitor.checkOrderStatus(orderId); } /** * Starts monitoring */ startMonitoring() { this.statusMonitor.startMonitoring(); } /** * Stops monitoring */ stopMonitoring() { this.statusMonitor.stopMonitoring(); } /** * Cleanup method */ cleanup() { this.statusMonitor.stopMonitoring(); this.statusMonitor.clearCache(); } } /** * Sets up order status change notifications (backward compatibility) * * @param ordersClient Orders client * @param notificationManager Notification manager * @param monitoringConfig Optional monitoring configuration */ export function setupOrderStatusChangeNotifications(ordersClient, notificationManager, monitoringConfig = {}) { const handler = new OrderStatusChangeHandler(ordersClient, notificationManager, monitoringConfig); handler.setup(); return handler; } //# sourceMappingURL=order-notifications.js.map