import { db } from '../db/db_index' import { addresses, complaints, couponUsage, orderItems, orders, orderStatus, payments, refunds, } from '../db/schema' import { and, desc, eq, inArray, lt, SQL } from 'drizzle-orm' import type { AdminOrderDetails, AdminOrderRow, AdminOrderStatusRecord, AdminOrderUpdateResult, AdminOrderItemPackagingResult, AdminOrderMessageResult, AdminOrderBasicResult, AdminGetSlotOrdersResult, AdminGetAllOrdersResultWithUserId, AdminRebalanceSlotsResult, AdminCancelOrderResult, AdminRefundRecord, RefundStatus, PaymentStatus, } from '@packages/shared' import type { InferSelectModel } from 'drizzle-orm' const isPaymentStatus = (value: string): value is PaymentStatus => value === 'pending' || value === 'success' || value === 'cod' || value === 'failed' const isRefundStatus = (value: string): value is RefundStatus => value === 'success' || value === 'pending' || value === 'failed' || value === 'none' || value === 'na' || value === 'processed' type OrderStatusRow = InferSelectModel const mapOrderStatusRecord = (record: OrderStatusRow): AdminOrderStatusRecord => ({ id: record.id, orderTime: record.orderTime, userId: record.userId, orderId: record.orderId, isPackaged: record.isPackaged, isDelivered: record.isDelivered, isCancelled: record.isCancelled, cancelReason: record.cancelReason ?? null, isCancelledByAdmin: record.isCancelledByAdmin ?? null, paymentStatus: isPaymentStatus(record.paymentStatus) ? record.paymentStatus : 'pending', cancellationUserNotes: record.cancellationUserNotes ?? null, cancellationAdminNotes: record.cancellationAdminNotes ?? null, cancellationReviewed: record.cancellationReviewed, cancellationReviewedAt: record.cancellationReviewedAt ?? null, refundCouponId: record.refundCouponId ?? null, }) export async function updateOrderNotes(orderId: number, adminNotes: string | null): Promise { const [result] = await db .update(orders) .set({ adminNotes }) .where(eq(orders.id, orderId)) .returning() return result || null } export async function updateOrderPackaged(orderId: string, isPackaged: boolean): Promise { const orderIdNumber = parseInt(orderId) await db .update(orderItems) .set({ is_packaged: isPackaged }) .where(eq(orderItems.orderId, orderIdNumber)) if (!isPackaged) { await db .update(orderStatus) .set({ isPackaged, isDelivered: false }) .where(eq(orderStatus.orderId, orderIdNumber)) } else { await db .update(orderStatus) .set({ isPackaged }) .where(eq(orderStatus.orderId, orderIdNumber)) } const order = await db.query.orders.findFirst({ where: eq(orders.id, orderIdNumber), }) return { success: true, userId: order?.userId ?? null } } export async function updateOrderDelivered(orderId: string, isDelivered: boolean): Promise { const orderIdNumber = parseInt(orderId) await db .update(orderStatus) .set({ isDelivered }) .where(eq(orderStatus.orderId, orderIdNumber)) const order = await db.query.orders.findFirst({ where: eq(orders.id, orderIdNumber), }) return { success: true, userId: order?.userId ?? null } } export async function getOrderDetails(orderId: number): Promise { // Single optimized query with all relations const orderData = await db.query.orders.findFirst({ where: eq(orders.id, orderId), with: { user: true, address: true, slot: true, orderItems: { with: { product: { with: { unit: true, }, }, }, }, payment: true, paymentInfo: true, orderStatus: true, refunds: true, }, }) if (!orderData) { return null } const couponUsageData = await db.query.couponUsage.findMany({ where: eq(couponUsage.orderId, orderData.id), with: { coupon: true, }, }) let couponData = null if (couponUsageData.length > 0) { let totalDiscountAmount = 0 const orderTotal = parseFloat(orderData.totalAmount.toString()) for (const usage of couponUsageData) { let discountAmount = 0 if (usage.coupon.discountPercent) { discountAmount = (orderTotal * parseFloat(usage.coupon.discountPercent.toString())) / 100 } else if (usage.coupon.flatDiscount) { discountAmount = parseFloat(usage.coupon.flatDiscount.toString()) } if ( usage.coupon.maxValue && discountAmount > parseFloat(usage.coupon.maxValue.toString()) ) { discountAmount = parseFloat(usage.coupon.maxValue.toString()) } totalDiscountAmount += discountAmount } couponData = { couponCode: couponUsageData.map((u) => u.coupon.couponCode).join(', '), couponDescription: `${couponUsageData.length} coupons applied`, discountAmount: totalDiscountAmount, } } const statusRecord = orderData.orderStatus?.[0] const orderStatusRecord = statusRecord ? mapOrderStatusRecord(statusRecord) : null let status: 'pending' | 'delivered' | 'cancelled' = 'pending' if (orderStatusRecord?.isCancelled) { status = 'cancelled' } else if (orderStatusRecord?.isDelivered) { status = 'delivered' } const refund = orderData.refunds?.[0] const refundStatus = refund?.refundStatus && isRefundStatus(refund.refundStatus) ? refund.refundStatus : null const refundRecord: AdminRefundRecord | null = refund ? { id: refund.id, orderId: refund.orderId, refundAmount: refund.refundAmount, refundStatus, merchantRefundId: refund.merchantRefundId, refundProcessedAt: refund.refundProcessedAt, createdAt: refund.createdAt, } : null return { id: orderData.id, readableId: orderData.id, userId: orderData.user.id, customerName: `${orderData.user.name}`, customerEmail: orderData.user.email, customerMobile: orderData.user.mobile, address: { name: orderData.address.name, line1: orderData.address.addressLine1, line2: orderData.address.addressLine2, city: orderData.address.city, state: orderData.address.state, pincode: orderData.address.pincode, phone: orderData.address.phone, }, slotInfo: orderData.slot ? { time: orderData.slot.deliveryTime.toISOString(), sequence: orderData.slot.deliverySequence, } : null, isCod: orderData.isCod, isOnlinePayment: orderData.isOnlinePayment, totalAmount: parseFloat(orderData.totalAmount?.toString() || '0') - parseFloat(orderData.deliveryCharge?.toString() || '0'), deliveryCharge: parseFloat(orderData.deliveryCharge?.toString() || '0'), adminNotes: orderData.adminNotes, userNotes: orderData.userNotes, createdAt: orderData.createdAt, status, isPackaged: orderStatusRecord?.isPackaged || false, isDelivered: orderStatusRecord?.isDelivered || false, items: orderData.orderItems.map((item) => ({ id: item.id, name: item.product.name, quantity: item.quantity, productSize: item.product.productQuantity, price: item.price, unit: item.product.unit?.shortNotation, amount: parseFloat(item.price.toString()) * parseFloat(item.quantity || '0'), isPackaged: item.is_packaged, isPackageVerified: item.is_package_verified, })), payment: orderData.payment ? { status: orderData.payment.status, gateway: orderData.payment.gateway, merchantOrderId: orderData.payment.merchantOrderId, } : null, paymentInfo: orderData.paymentInfo ? { status: orderData.paymentInfo.status, gateway: orderData.paymentInfo.gateway, merchantOrderId: orderData.paymentInfo.merchantOrderId, } : null, cancelReason: orderStatusRecord?.cancelReason || null, cancellationReviewed: orderStatusRecord?.cancellationReviewed || false, isRefundDone: refundStatus === 'processed' || false, refundStatus, refundAmount: refund?.refundAmount ? parseFloat(refund.refundAmount.toString()) : null, couponData, couponCode: couponData?.couponCode || null, couponDescription: couponData?.couponDescription || null, discountAmount: couponData?.discountAmount || null, orderStatus: orderStatusRecord, refundRecord, isFlashDelivery: orderData.isFlashDelivery, } } export async function updateOrderItemPackaging( orderItemId: number, isPackaged?: boolean, isPackageVerified?: boolean ): Promise { const orderItem = await db.query.orderItems.findFirst({ where: eq(orderItems.id, orderItemId), }) if (!orderItem) { return { success: false, updated: false } } const updateData: Partial<{ is_packaged: boolean is_package_verified: boolean }> = {} if (isPackaged !== undefined) { updateData.is_packaged = isPackaged } if (isPackageVerified !== undefined) { updateData.is_package_verified = isPackageVerified } await db .update(orderItems) .set(updateData) .where(eq(orderItems.id, orderItemId)) return { success: true, updated: true } } export async function removeDeliveryCharge(orderId: number): Promise { const order = await db.query.orders.findFirst({ where: eq(orders.id, orderId), }) if (!order) { return null } const currentDeliveryCharge = parseFloat(order.deliveryCharge?.toString() || '0') const currentTotalAmount = parseFloat(order.totalAmount?.toString() || '0') const newTotalAmount = currentTotalAmount - currentDeliveryCharge await db .update(orders) .set({ deliveryCharge: '0', totalAmount: newTotalAmount.toString(), }) .where(eq(orders.id, orderId)) return { success: true, message: 'Delivery charge removed' } } export async function getSlotOrders(slotId: string): Promise { const slotOrders = await db.query.orders.findMany({ where: eq(orders.slotId, parseInt(slotId)), with: { user: true, address: true, slot: true, orderItems: { with: { product: { with: { unit: true, }, }, }, }, orderStatus: true, }, }) const filteredOrders = slotOrders.filter((order) => { const statusRecord = order.orderStatus[0] return order.isCod || (statusRecord && statusRecord.paymentStatus === 'success') }) const formattedOrders = filteredOrders.map((order) => { const statusRecord = order.orderStatus[0] let status: 'pending' | 'delivered' | 'cancelled' = 'pending' if (statusRecord?.isCancelled) { status = 'cancelled' } else if (statusRecord?.isDelivered) { status = 'delivered' } const items = order.orderItems.map((item) => ({ id: item.id, name: item.product.name, quantity: parseFloat(item.quantity), price: parseFloat(item.price.toString()), amount: parseFloat(item.quantity) * parseFloat(item.price.toString()), unit: item.product.unit?.shortNotation || '', isPackaged: item.is_packaged, isPackageVerified: item.is_package_verified, })) const paymentMode: 'COD' | 'Online' = order.isCod ? 'COD' : 'Online' return { id: order.id, readableId: order.id, customerName: order.user.name || order.user.mobile+'', address: `${order.address.addressLine1}${ order.address.addressLine2 ? `, ${order.address.addressLine2}` : '' }, ${order.address.city}, ${order.address.state} - ${ order.address.pincode }, Phone: ${order.address.phone}`, addressId: order.addressId, latitude: order.address.adminLatitude ?? order.address.latitude, longitude: order.address.adminLongitude ?? order.address.longitude, totalAmount: parseFloat(order.totalAmount), items, deliveryTime: order.slot?.deliveryTime.toISOString() || null, status, isPackaged: order.orderItems.every((item) => item.is_packaged) || false, isDelivered: statusRecord?.isDelivered || false, isCod: order.isCod, paymentMode, paymentStatus: isPaymentStatus(statusRecord?.paymentStatus || 'pending') ? statusRecord?.paymentStatus || 'pending' : 'pending', slotId: order.slotId, adminNotes: order.adminNotes, userNotes: order.userNotes, } }) return { success: true, data: formattedOrders } } export async function updateAddressCoords( addressId: number, latitude: number, longitude: number ): Promise { const result = await db .update(addresses) .set({ adminLatitude: latitude, adminLongitude: longitude, }) .where(eq(addresses.id, addressId)) .returning() return { success: result.length > 0 } } type GetAllOrdersInput = { cursor?: number limit: number slotId?: number | null packagedFilter?: 'all' | 'packaged' | 'not_packaged' deliveredFilter?: 'all' | 'delivered' | 'not_delivered' cancellationFilter?: 'all' | 'cancelled' | 'not_cancelled' flashDeliveryFilter?: 'all' | 'flash' | 'regular' } export async function getAllOrders(input: GetAllOrdersInput): Promise { const { cursor, limit, slotId, packagedFilter, deliveredFilter, cancellationFilter, flashDeliveryFilter, } = input let whereCondition: SQL | undefined = eq(orders.id, orders.id) if (cursor) { whereCondition = and(whereCondition, lt(orders.id, cursor)) } if (slotId) { whereCondition = and(whereCondition, eq(orders.slotId, slotId)) } if (packagedFilter === 'packaged') { whereCondition = and(whereCondition, eq(orderStatus.isPackaged, true)) } else if (packagedFilter === 'not_packaged') { whereCondition = and(whereCondition, eq(orderStatus.isPackaged, false)) } if (deliveredFilter === 'delivered') { whereCondition = and(whereCondition, eq(orderStatus.isDelivered, true)) } else if (deliveredFilter === 'not_delivered') { whereCondition = and(whereCondition, eq(orderStatus.isDelivered, false)) } if (cancellationFilter === 'cancelled') { whereCondition = and(whereCondition, eq(orderStatus.isCancelled, true)) } else if (cancellationFilter === 'not_cancelled') { whereCondition = and(whereCondition, eq(orderStatus.isCancelled, false)) } if (flashDeliveryFilter === 'flash') { whereCondition = and(whereCondition, eq(orders.isFlashDelivery, true)) } else if (flashDeliveryFilter === 'regular') { whereCondition = and(whereCondition, eq(orders.isFlashDelivery, false)) } const allOrders = await db.query.orders.findMany({ where: whereCondition, orderBy: desc(orders.createdAt), limit: limit + 1, with: { user: true, address: true, slot: true, orderItems: { with: { product: { with: { unit: true, }, }, }, }, orderStatus: true, }, }) const hasMore = allOrders.length > limit const ordersToReturn = hasMore ? allOrders.slice(0, limit) : allOrders const filteredOrders = ordersToReturn.filter((order) => { const statusRecord = order.orderStatus[0] return order.isCod || (statusRecord && statusRecord.paymentStatus === 'success') }) const formattedOrders = filteredOrders.map((order) => { const statusRecord = order.orderStatus[0] let status: 'pending' | 'delivered' | 'cancelled' = 'pending' if (statusRecord?.isCancelled) { status = 'cancelled' } else if (statusRecord?.isDelivered) { status = 'delivered' } const items = order.orderItems .map((item) => ({ id: item.id, name: item.product.name, quantity: parseFloat(item.quantity), price: parseFloat(item.price.toString()), amount: parseFloat(item.quantity) * parseFloat(item.price.toString()), unit: item.product.unit?.shortNotation || '', productSize: item.product.productQuantity, isPackaged: item.is_packaged, isPackageVerified: item.is_package_verified, })) .sort((first, second) => first.id - second.id) return { id: order.id, orderId: order.id.toString(), readableId: order.id, customerName: order.user.name || order.user.mobile + '', customerMobile: order.user.mobile, address: `${order.address.addressLine1}${ order.address.addressLine2 ? `, ${order.address.addressLine2}` : '' }, ${order.address.city}, ${order.address.state} - ${ order.address.pincode }, Phone: ${order.address.phone}`, addressId: order.addressId, latitude: order.address.adminLatitude ?? order.address.latitude, longitude: order.address.adminLongitude ?? order.address.longitude, totalAmount: parseFloat(order.totalAmount), deliveryCharge: parseFloat(order.deliveryCharge || '0'), items, createdAt: order.createdAt, deliveryTime: order.slot?.deliveryTime.toISOString() || null, status, isPackaged: order.orderItems.every((item) => item.is_packaged) || false, isDelivered: statusRecord?.isDelivered || false, isCod: order.isCod, isFlashDelivery: order.isFlashDelivery, userNotes: order.userNotes, adminNotes: order.adminNotes, userNegativityScore: 0, userId: order.userId, } }) return { orders: formattedOrders, nextCursor: hasMore ? ordersToReturn[ordersToReturn.length - 1].id : undefined, } } export async function rebalanceSlots(slotIds: number[]): Promise { const ordersList = await db.query.orders.findMany({ where: inArray(orders.slotId, slotIds), with: { orderItems: { with: { product: true, }, }, couponUsages: { with: { coupon: true, }, }, }, }) const processedOrdersData = ordersList.map((order) => { let newTotal = order.orderItems.reduce((acc, item) => { const latestPrice = +item.product.price const amount = latestPrice * Number(item.quantity) return acc + amount }, 0) order.orderItems.forEach((item) => { item.price = item.product.price item.discountedPrice = item.product.price }) const coupon = order.couponUsages[0]?.coupon let discount = 0 if (coupon && !coupon.isInvalidated && (!coupon.validTill || new Date(coupon.validTill) > new Date())) { const proportion = Number(order.orderGroupProportion || 1) if (coupon.discountPercent) { const maxDiscount = Number(coupon.maxValue || Infinity) * proportion discount = Math.min((newTotal * parseFloat(coupon.discountPercent)) / 100, maxDiscount) } else { discount = Number(coupon.flatDiscount) * proportion } } newTotal -= discount const { couponUsages, orderItems: orderItemsRaw, ...rest } = order const updatedOrderItems = orderItemsRaw.map((item) => { const { product, ...rawOrderItem } = item return rawOrderItem }) return { order: rest, updatedOrderItems, newTotal } }) const updatedOrderIds: number[] = [] await db.transaction(async (tx) => { for (const { order, updatedOrderItems, newTotal } of processedOrdersData) { await tx.update(orders).set({ totalAmount: newTotal.toString() }).where(eq(orders.id, order.id)) updatedOrderIds.push(order.id) for (const item of updatedOrderItems) { await tx .update(orderItems) .set({ price: item.price, discountedPrice: item.discountedPrice, }) .where(eq(orderItems.id, item.id)) } } }) return { success: true, updatedOrders: updatedOrderIds, message: `Rebalanced ${updatedOrderIds.length} orders.`, } } export async function cancelOrder(orderId: number, reason: string): Promise { const order = await db.query.orders.findFirst({ where: eq(orders.id, orderId), with: { orderStatus: true, }, }) if (!order) { return { success: false, message: 'Order not found', error: 'order_not_found' } } const status = order.orderStatus[0] if (!status) { return { success: false, message: 'Order status not found', error: 'status_not_found' } } if (status.isCancelled) { return { success: false, message: 'Order is already cancelled', error: 'already_cancelled' } } if (status.isDelivered) { return { success: false, message: 'Cannot cancel delivered order', error: 'already_delivered' } } const result = await db.transaction(async (tx) => { await tx .update(orderStatus) .set({ isCancelled: true, isCancelledByAdmin: true, cancelReason: reason, cancellationAdminNotes: reason, cancellationReviewed: true, cancellationReviewedAt: new Date(), }) .where(eq(orderStatus.id, status.id)) const refundStatus = order.isCod ? 'na' : 'pending' await tx.insert(refunds).values({ orderId: order.id, refundStatus, }) return { orderId: order.id, userId: order.userId } }) return { success: true, message: 'Order cancelled successfully', orderId: result.orderId, userId: result.userId, } } export async function deleteOrderById(orderId: number): Promise { await db.transaction(async (tx) => { await tx.delete(orderItems).where(eq(orderItems.orderId, orderId)) await tx.delete(orderStatus).where(eq(orderStatus.orderId, orderId)) await tx.delete(payments).where(eq(payments.orderId, orderId)) await tx.delete(refunds).where(eq(refunds.orderId, orderId)) await tx.delete(couponUsage).where(eq(couponUsage.orderId, orderId)) await tx.delete(complaints).where(eq(complaints.orderId, orderId)) await tx.delete(orders).where(eq(orders.id, orderId)) }) }