201 lines
6.4 KiB
TypeScript
201 lines
6.4 KiB
TypeScript
import {
|
|
getOrdersByIdsWithFullData,
|
|
getOrderByIdWithFullData,
|
|
composeSkuName,
|
|
composeUnitNotation,
|
|
} from '@/src/dbService'
|
|
import { sendTelegramMessage } from '@/src/lib/telegram-service'
|
|
import { queueDataPusher } from '@/src/lib/queue-data-pusher'
|
|
import { ensureWorkerInit } from './worker-init';
|
|
import { getAppUrl } from '@/src/lib/env-exporter'
|
|
|
|
interface OrderIdMessage {
|
|
orderIds: number[];
|
|
}
|
|
|
|
interface CancellationMessage {
|
|
orderId: number;
|
|
cancelledBy: 'user' | 'admin';
|
|
reason: string;
|
|
cancelledAt: string;
|
|
}
|
|
|
|
const formatDateTime = (dateStr: string | null | undefined): string => {
|
|
if (!dateStr) return 'N/A';
|
|
return new Date(dateStr).toLocaleString('en-IN', {
|
|
dateStyle: 'medium',
|
|
timeStyle: 'short',
|
|
timeZone: 'Asia/Kolkata',
|
|
});
|
|
};
|
|
|
|
const buildTelegramLinks = (orderId: number, userId?: number | null): string => {
|
|
const baseUrl = getAppUrl() || 'https://ui.freshyo.in'
|
|
const orderUrl = `${baseUrl}/manage-orders/order-details/${orderId}`
|
|
const orderLink = `↪ <a href="${orderUrl}">Order</a>`
|
|
if (!userId) {
|
|
return orderLink
|
|
}
|
|
const userUrl = `${baseUrl}/user-management/${userId}`
|
|
const userLink = `↪ <a href="${userUrl}">User</a>`
|
|
return `${orderLink} | ${userLink}`
|
|
}
|
|
|
|
const formatOrderMessageWithFullData = (ordersData: any[]): string => {
|
|
console.log('formatting the msg')
|
|
let message = '🛒 <b>New Order Placed</b>\n\n';
|
|
|
|
ordersData.forEach((order, index) => {
|
|
message += `<b>Order ${order.id}</b>\n`;
|
|
|
|
message += '📦 <b>Items:</b>\n';
|
|
order.orderItems?.forEach((item: any) => {
|
|
const sku = item.sku
|
|
const features = sku?.features || []
|
|
const name = composeSkuName(sku?.product?.name || 'Unknown', features)
|
|
const unit = composeUnitNotation(features)
|
|
message += ` • ${name} ${unit} x${item.quantity}\n`;
|
|
});
|
|
message += `\n💰 <b>Total:</b> ₹${order.totalAmount}\n`;
|
|
|
|
message += `🚚 <b>Delivery:</b> ${
|
|
order.isFlashDelivery ? 'Flash Delivery' : formatDateTime(order.slot?.deliveryTime)
|
|
}\n`;
|
|
|
|
message += `\n📍 <b>Address:</b>\n`;
|
|
message += ` ${order.address?.name || 'N/A'}\n`;
|
|
message += ` ${order.address?.addressLine1 || ''}\n`;
|
|
if (order.address?.addressLine2) {
|
|
message += ` ${order.address.addressLine2}\n`;
|
|
}
|
|
message += ` ${order.address?.city || ''}, ${order.address?.state || ''} - ${order.address?.pincode || ''}\n`;
|
|
if (order.address?.phone) {
|
|
message += ` 📞 ${order.address.phone}\n`;
|
|
}
|
|
|
|
message += `\n${buildTelegramLinks(order.id, order.userId)}\n`
|
|
|
|
if (index < ordersData.length - 1) {
|
|
message += '\n---\n\n';
|
|
}
|
|
});
|
|
|
|
return message;
|
|
};
|
|
|
|
const formatCancellationMessage = (orderData: any, cancellationData: CancellationMessage): string => {
|
|
const message = `❌ <b>Order Cancelled</b>
|
|
|
|
<b>Order #${orderData.id}</b>
|
|
|
|
👤 <b>Name:</b> ${orderData.address?.name || 'N/A'}
|
|
📞 <b>Phone:</b> ${orderData.address?.phone || 'N/A'}
|
|
|
|
📦 <b>Items:</b>
|
|
${orderData.orderItems?.map((item: any) => ` • ${item.sku?.product?.name || 'Unknown'} x${item.quantity}`).join('\n') || ' N/A'}
|
|
|
|
💰 <b>Total:</b> ₹${orderData.totalAmount}
|
|
💳 <b>Refund:</b> ${orderData.refundStatus === 'na' ? 'N/A (COD)' : orderData.refundStatus || 'Pending'}
|
|
|
|
❓ <b>Reason:</b> ${cancellationData.reason}
|
|
👤 <b>Cancelled by:</b> ${cancellationData.cancelledBy === 'admin' ? 'Admin' : 'User'}
|
|
⏰ <b>Time:</b> ${formatDateTime(cancellationData.cancelledAt)}
|
|
|
|
${buildTelegramLinks(orderData.id, orderData.userId)}
|
|
`;
|
|
|
|
return message;
|
|
};
|
|
|
|
export const handleOrderPlaced = async (orderIds: number[], rawMessage?: string): Promise<void> => {
|
|
try {
|
|
const ordersData = await getOrdersByIdsWithFullData(orderIds)
|
|
const telegramMessage = formatOrderMessageWithFullData(ordersData)
|
|
await sendTelegramMessage(telegramMessage)
|
|
} catch (error) {
|
|
console.error('Failed to process order message:', error)
|
|
const fallback = rawMessage ? `⚠️ Error parsing order: ${rawMessage}` : '⚠️ Error parsing order'
|
|
await sendTelegramMessage(fallback)
|
|
}
|
|
}
|
|
|
|
export const handleOrderCancelled = async (
|
|
cancellationData: CancellationMessage,
|
|
rawMessage?: string
|
|
): Promise<void> => {
|
|
try {
|
|
console.log('Order cancellation received, sending to Telegram...')
|
|
const orderData = await getOrderByIdWithFullData(cancellationData.orderId)
|
|
|
|
if (!orderData) {
|
|
console.error('Order not found for cancellation:', cancellationData.orderId)
|
|
await sendTelegramMessage(`⚠️ Order ${cancellationData.orderId} was cancelled but could not be found in database`)
|
|
return
|
|
}
|
|
|
|
const refundStatus = orderData.refunds?.[0]?.refundStatus || 'pending'
|
|
|
|
|
|
const telegramMessage = formatCancellationMessage({ ...orderData, refundStatus }, cancellationData)
|
|
await sendTelegramMessage(telegramMessage)
|
|
} catch (error) {
|
|
console.error('Failed to process cancellation message:', error)
|
|
const fallback = rawMessage ? `⚠️ Error processing cancellation: ${rawMessage}` : '⚠️ Error processing cancellation'
|
|
await sendTelegramMessage(fallback)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start the post order handler
|
|
* Subscribes to the orders:placed channel and sends to Telegram
|
|
*/
|
|
export const publishOrder = async (orderDetails: OrderIdMessage): Promise<boolean> => {
|
|
console.log('publishing order')
|
|
try {
|
|
await queueDataPusher.pushOrderPlacedQueue({
|
|
name: 'order-placed',
|
|
orderIds: orderDetails.orderIds,
|
|
})
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Failed to publish order:', error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
export const publishFormattedOrder = async (
|
|
createdOrders: any[],
|
|
ordersBySlot: Map<number | null, any[]>
|
|
): Promise<boolean> => {
|
|
try {
|
|
const orderIds = createdOrders.map(order => order.id);
|
|
return await publishOrder({ orderIds });
|
|
} catch (error) {
|
|
console.error('Failed to format and publish order:', error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
export const publishCancellation = async (
|
|
orderId: number,
|
|
cancelledBy: 'user' | 'admin',
|
|
reason: string
|
|
): Promise<boolean> => {
|
|
try {
|
|
const message: CancellationMessage = {
|
|
orderId,
|
|
cancelledBy,
|
|
reason,
|
|
cancelledAt: new Date().toISOString(),
|
|
};
|
|
await queueDataPusher.pushOrderCancelledQueue({
|
|
name: 'order-cancelled',
|
|
...message,
|
|
})
|
|
console.log('Cancellation published to queue:', orderId);
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Failed to publish cancellation:', error);
|
|
return false;
|
|
}
|
|
};
|