From cabdf5be5f22c1430c3d141d74a3253964a35642 Mon Sep 17 00:00:00 2001 From: Sabeera Date: Wed, 17 Sep 2025 20:57:40 +0530 Subject: [PATCH] modify the partila fullfiment api --- controllers/cart.controller.js | 5 +- controllers/driver.controller.js | 41 +-- controllers/order.controller.js | 417 ++++++++++++++++++++++++++----- routes/driver.routes.js | 1 + services/analytics.service.js | 1 + utils/cartCalculations.js | 6 +- 6 files changed, 385 insertions(+), 86 deletions(-) diff --git a/controllers/cart.controller.js b/controllers/cart.controller.js index 7f928fd..14af88b 100644 --- a/controllers/cart.controller.js +++ b/controllers/cart.controller.js @@ -978,6 +978,7 @@ module.exports.viewCart = async (req, res) => { // ✅ Use the most recent cart as primary cart const primaryCart = userCarts.rows[0]; + // console.log("primaryCartttttt",primaryCart) // ✅ Get all cart items from ALL active carts const cartItems = await getAllCartItemService({ @@ -1105,7 +1106,9 @@ module.exports.viewCart = async (req, res) => { const customerState = await getCustomerDefaultShippingState(user.id); // Get storeId from cart model (primary source) - const storeId = primaryCart.store_id; + const storeId = primaryCart.dataValues.store_id; + + console.log("storeId after",storeId) // Get store details for additional context let storeDetails = null; diff --git a/controllers/driver.controller.js b/controllers/driver.controller.js index 1480be4..7abbf24 100644 --- a/controllers/driver.controller.js +++ b/controllers/driver.controller.js @@ -4284,7 +4284,7 @@ module.exports.driverAcceptedOrderPickup = async (req, res) => { where: { driverId: parseInt(driverId), assignmentStatus: { - [Op.in]: ["accepted", "picked_up", "failed", "return"], + [Op.in]: ["accepted", "picked_up","failed"], }, isActive: true, }, @@ -6319,6 +6319,7 @@ module.exports.returnOrder = async (req, res) => { assignment.get({ plain: true }) ); + // Get orderId from the first assignment's order (from database, not req.params) const selectedOrder = plainAssignments[0]; // Always take the first available order const orderIdToUpdate = selectedOrder.order.ID; @@ -6450,27 +6451,27 @@ module.exports.returnOrder = async (req, res) => { emitter.emit("createActivityLog", { activityLogPayload, req, res }); // Create activity log for transaction creation (if transaction was created successfully) - if (createdTransaction) { - const transactionActivityLogPayload = { - action: "CREATE", - entityType: "transaction", - entityId: createdTransaction.id, - entityName: `Return Transaction - ${order.order_number}`, - userId: driverId, - userType: "driver", - description: `Return transaction created for order ${order.order_number}. Transaction ID: ${createdTransaction.id}`, - }; + // if (createdTransaction) { + // const transactionActivityLogPayload = { + // action: "CREATE", + // entityType: "transaction", + // entityId: createdTransaction.id, + // entityName: `Return Transaction - ${order.order_number}`, + // userId: driverId, + // userType: "driver", + // description: `Return transaction created for order ${order.order_number}. Transaction ID: ${createdTransaction.id}`, + // }; - // Emit transaction activity log event - emitter.emit("createActivityLog", { - activityLogPayload: transactionActivityLogPayload, - req, - res, - }); - } + // // Emit transaction activity log event + // emitter.emit("createActivityLog", { + // activityLogPayload: transactionActivityLogPayload, + // req, + // res, + // }); + // } // Update driver last active time - await updateDriverService(driverId, { lastActiveAt: new Date() }); + // await updateDriverService(driverId, { lastActiveAt: new Date() }); // Prepare return details for response const returnDetails = { @@ -6480,7 +6481,7 @@ module.exports.returnOrder = async (req, res) => { assignmentStatus: updatedAssignment.assignmentStatus, returnStatus: "return", initiatedAt: returnInfo.initiatedAt, - transactionId: createdTransaction?.id || null, // Include transaction ID if created + // transactionId: createdTransaction?.id || null, // Include transaction ID if created // Return information returnInfo: { diff --git a/controllers/order.controller.js b/controllers/order.controller.js index ecefdd2..33f9842 100644 --- a/controllers/order.controller.js +++ b/controllers/order.controller.js @@ -211,8 +211,6 @@ const findAvailableDrivers = async ( radiusKm = 2 ) => { try { - - // First, let's check total drivers in database const totalDrivers = await DRIVER_MODEL.count(); @@ -240,7 +238,6 @@ const findAvailableDrivers = async ( where: { currentLocation: { [Op.ne]: null } }, }); - // Get all available drivers const availableDrivers = await DRIVER_MODEL.findAll({ where: { @@ -340,7 +337,6 @@ const findAvailableDrivers = async ( */ const assignOrderToDriver = async (orderId, storeId) => { try { - // Get order details const order = await require("../models/orderModel").findByPk(orderId); if (!order) { @@ -1075,19 +1071,21 @@ module.exports.getStoreOrders = async (req, res) => { discountAmount: order.discount_amount, tipAmount: order.tipAmount || 0, orderStatus: order.order_status, - orderItems: order.orderItems?.map((item) => ({ - id: item.ID, - productName: item.product_name, - variantName: item.variant_name === "DUMMY" ? "" : item.variant_name, - sku: item.product_sku, - qty: item.quantity, - pickedQty: item.pickedQty, - isPicked: item.isPicked, - unitPrice: item.unit_price, - totalPrice: item.total_price, - taxAmount: item.tax_amount, - productImage: item.product_image, - })) || [], + orderItems: + order.orderItems?.map((item) => ({ + id: item.ID, + productName: item.product_name, + variantName: + item.variant_name === "DUMMY" ? "" : item.variant_name, + sku: item.product_sku, + qty: item.quantity, + pickedQty: item.pickedQty, + isPicked: item.isPicked, + unitPrice: item.unit_price, + totalPrice: item.total_price, + taxAmount: item.tax_amount, + productImage: item.product_image, + })) || [], driver: order.driverAssignments && order.driverAssignments.length > 0 ? { @@ -1149,7 +1147,6 @@ module.exports.getStoreOrders = async (req, res) => { email: order.billing_email, }, }, - })), pagination: getPagination(page, limit, orders.count), }, @@ -1340,19 +1337,21 @@ module.exports.getAllOrdersForSuperAdmin = async (req, res) => { storeName: order.store?.storeName || "Unknown Store", storeId: order.store?.ID, orderStatus: order.order_status, - orderItems: order.orderItems?.map((item) => ({ - id: item.ID, - productName: item.product_name, - variantName: item.variant_name === "DUMMY" ? "" : item.variant_name, - sku: item.product_sku, - qty: item.quantity, - pickedQty: item.pickedQty, - isPicked: item.isPicked, - unitPrice: item.unit_price, - totalPrice: item.total_price, - taxAmount: item.tax_amount, - productImage: item.product_image, - })) || [], + orderItems: + order.orderItems?.map((item) => ({ + id: item.ID, + productName: item.product_name, + variantName: + item.variant_name === "DUMMY" ? "" : item.variant_name, + sku: item.product_sku, + qty: item.quantity, + pickedQty: item.pickedQty, + isPicked: item.isPicked, + unitPrice: item.unit_price, + totalPrice: item.total_price, + taxAmount: item.tax_amount, + productImage: item.product_image, + })) || [], driver: order.driverAssignments && order.driverAssignments.length > 0 ? { @@ -1921,7 +1920,7 @@ module.exports.partialFulfillOrder = async (req, res) => { const { orderId } = req.params; const { fulfillmentItems } = req.body; const user = req.user; - + // Validate input if ( !fulfillmentItems || @@ -2026,6 +2025,7 @@ module.exports.partialFulfillOrder = async (req, res) => { const orderItem = order.orderItems.find( (item) => item.ID === orderItemId ); + if (!orderItem) { await transaction.rollback(); return res.status(400).json({ @@ -2035,11 +2035,19 @@ module.exports.partialFulfillOrder = async (req, res) => { } // Validate fulfillment quantity - if (fulfilledQty < 0 || fulfilledQty > orderItem.quantity) { + if (fulfilledQty < 0) { + await transaction.rollback(); + return res.status(400).json({ + status: false, + message: `Fulfillment quantity cannot be negative for item ${orderItem.product_name}`, + }); + } + + if (fulfilledQty > orderItem.quantity) { await transaction.rollback(); return res.status(400).json({ status: false, - message: `Invalid fulfillment quantity for item ${orderItem.product_name}`, + message: `Fulfillment quantity (${fulfilledQty}) cannot be greater than ordered quantity (${orderItem.quantity}) for item ${orderItem.product_name}`, }); } @@ -2102,6 +2110,63 @@ module.exports.partialFulfillOrder = async (req, res) => { shortTax: shortTax, }); + // Update product store stock for the fulfilled quantity + try { + // Get the actual variant ID (handle single products as variants) + let actualVariantId = orderItem.variant_id; + if (!actualVariantId) { + // For single products, find the default variant + const variants = await VARIANT_MODEL.findOne({ + where: { + productId: orderItem.product_id, + status: "active", + }, + attributes: ["ID"], + order: [["ID", "ASC"]], + transaction, + }); + actualVariantId = variants?.ID || null; + } + + if (actualVariantId) { + // Get current stock record + const stockRecord = await PRODUCT_STORE_STOCK_MODEL.findOne({ + where: { + storeId: order.store_id, + productId: orderItem.product_id, + variationId: actualVariantId, + }, + transaction, + }); + + if (stockRecord) { + // Add back the unfulfilled quantity to stock + const unfulfilledQty = orderItem.quantity - fulfilledQty; + const newStockAvailable = + stockRecord.stockAvailable + unfulfilledQty; + + await stockRecord.update( + { + stockAvailable: newStockAvailable, + }, + { transaction } + ); + + console.log( + `✅ Updated stock for product ${orderItem.product_id}, variant ${actualVariantId}: added back ${unfulfilledQty} units (new stock: ${newStockAvailable})` + ); + } else { + console.warn( + `⚠️ Stock record not found for product ${orderItem.product_id}, variant ${actualVariantId} in store ${order.store_id}` + ); + } + } + } catch (stockError) { + console.error("Error updating product store stock:", stockError); + // Don't fail the entire operation for stock update errors + // Log the error but continue with fulfillment + } + // Accumulate totals totalFulfilledAmount += fulfilledAmount; totalFulfilledTax += fulfilledTax; @@ -2362,7 +2427,6 @@ module.exports.generateShippingLabel = async (req, res) => { }); } - // Check if order belongs to the store (for store admin) const user = req.user; if (user.role === "store_admin" && order.store_id !== user.storeId) { @@ -2457,20 +2521,31 @@ module.exports.generateShippingLabel = async (req, res) => { labelNotes: "", }, // Order Items (map from order.orderItems) - orderItems: order.orderItems ? order.orderItems.map(item => ({ - name: item.product_name || "Product", - variant: item.variant_name && item.variant_name !== "DUMMY" ? item.variant_name : "", - quantity: item.quantity || 1, - totalPrice: parseFloat(item.total_price) || 0.00 - })) : [], + orderItems: order.orderItems + ? order.orderItems.map((item) => ({ + name: item.product_name || "Product", + variant: + item.variant_name && item.variant_name !== "DUMMY" + ? item.variant_name + : "", + quantity: item.quantity || 1, + totalPrice: parseFloat(item.total_price) || 0.0, + })) + : [], // Order Totals orderTotals: { - subTotal: parseFloat(order.subtotal_amount) || 0.00, - tax: parseFloat(order.tax_amount) || 0.00, - shipping: parseFloat(order.shipping_cost) || 0.00, - discount: parseFloat(order.discount_amount) || 0.00, - total: parseFloat(order.total_amount) || (parseFloat(order.subtotal_amount) + parseFloat(order.tax_amount) + parseFloat(order.shipping_cost) - parseFloat(order.discount_amount)) || 0.00 + subTotal: parseFloat(order.subtotal_amount) || 0.0, + tax: parseFloat(order.tax_amount) || 0.0, + shipping: parseFloat(order.shipping_cost) || 0.0, + discount: parseFloat(order.discount_amount) || 0.0, + total: + parseFloat(order.total_amount) || + parseFloat(order.subtotal_amount) + + parseFloat(order.tax_amount) + + parseFloat(order.shipping_cost) - + parseFloat(order.discount_amount) || + 0.0, }, }; @@ -3132,43 +3207,75 @@ const generateLabelHTML = async (labelData) => { ${labelData.packageDetails || "Standard Package"} - ${labelData.orderItems && labelData.orderItems.length > 0 ? ` + ${ + labelData.orderItems && labelData.orderItems.length > 0 + ? `
ORDER ITEMS:
- ${labelData.orderItems.map(item => ` + ${labelData.orderItems + .map( + (item) => `
- ${item.name}${item.variant ? ` - ${item.variant}` : ''} - ${item.quantity}x - $${item.totalPrice.toFixed(2)} + ${item.name}${ + item.variant ? ` - ${item.variant}` : "" + } + ${ + item.quantity + }x + $${item.totalPrice.toFixed( + 2 + )}
- `).join('')} + ` + ) + .join("")} - ${labelData.orderTotals ? ` + ${ + labelData.orderTotals + ? `
Sub Total: - $${labelData.orderTotals.subTotal ? labelData.orderTotals.subTotal.toFixed(2) : '0.00'} + $${ + labelData.orderTotals.subTotal + ? labelData.orderTotals.subTotal.toFixed(2) + : "0.00" + }
Tax: - $${parseFloat(labelData.orderTotals.tax || 0).toFixed(2)} + $${parseFloat(labelData.orderTotals.tax || 0).toFixed( + 2 + )}
Shipping: - $${parseFloat(labelData.orderTotals.shipping || 0).toFixed(2)} + $${parseFloat( + labelData.orderTotals.shipping || 0 + ).toFixed(2)}
Discount: - -$${parseFloat(labelData.orderTotals.discount || 0).toFixed(2)} + -$${parseFloat( + labelData.orderTotals.discount || 0 + ).toFixed(2)}
TOTAL: - $${labelData.orderTotals.total ? labelData.orderTotals.total.toFixed(2) : '0.00'} + $${ + labelData.orderTotals.total + ? labelData.orderTotals.total.toFixed(2) + : "0.00" + }
- ` : ''} + ` + : "" + }
- ` : ''} + ` + : "" + } @@ -4806,7 +4913,7 @@ module.exports.createOrderForSuperAdmin = async (req, res) => { delivery_method: deliveryMethod, delivery_instructions: deliveryInstructions, order_notes: notes, - created_by: user.ID, + created_by: user.id || user.ID, created_by_user: user.userName || user.email, is_super_admin_order: true, // Shipping address fields @@ -4889,7 +4996,7 @@ module.exports.createOrderForSuperAdmin = async (req, res) => { discount_amount: summaryItem.discountAmount || 0, tax_amount: summaryItem.taxAmount || 0, total_amount: summaryItem.itemTotal, - created_by: user.ID, + created_by: user.id || user.ID, created_by_user: user.userName || user.email, }, { transaction } @@ -4922,6 +5029,190 @@ module.exports.createOrderForSuperAdmin = async (req, res) => { }); } + // Create transaction record for the order (same as ecommerce) + try { + const transactionPayload = { + store_id: storeId, + type: "sell", + sub_type: null, + status: "final", + sub_status: null, + is_quotation: false, + payment_status: paymentMethod === "cod" ? "due" : "paid", + adjustment_type: null, + contact_id: customerId, + customer_group_id: null, + invoice_no: orderNumber, + ref_no: orderNumber, + source: "super_admin", + subscription_no: null, + subscription_repeat_on: null, + transaction_date: new Date().toISOString().split("T")[0], + total_before_tax: subtotalAmount, + tax_id: null, + tax_amount: taxAmount, + discount_type: null, + discount_amount: 0, + rp_redeemed: null, + rp_redeemed_amount: 0, + shipping_details: null, + shipping_address: `${shippingAddress?.addressLine1 || ""}, ${ + shippingAddress?.city || "" + }, ${shippingAddress?.state || ""} ${shippingAddress?.pinCode || ""}`, + delivery_date: new Date(), + shipping_status: "ordered", + delivered_to: null, + delivery_person: null, + price_details: null, + subtotal_amount: subtotalAmount, + shipping_charges: shippingAmount, + total_amount: totalAmount, + shipping_charges_contribution: + cartSummary.shipping_charges_contribution || null, + shipping_customer_contribution: + cartSummary.shipping_charges_contribution?.customerContribution || 0, + shipping_store_contribution: + cartSummary.shipping_charges_contribution?.storeContribution || 0, + shipping_admin_contribution: + cartSummary.shipping_charges_contribution?.adminContribution || 0, + payment_gateway_charges: cartSummary.paymentGatewayCharges || 0, + haze_platform_fee: cartSummary.hazePlatformFee || 0, + surcharges: cartSummary.surcharges || 0, + surcharges_details: cartSummary.surchargeDetails || null, + payment_id: null, // No payment record for super admin orders + store_profit: cartSummary.storeProfit || 0, + haze_profit: cartSummary.hazeProfit || 0, + driver_profit: cartSummary.driverProfit || 0, + additional_notes: notes, + staff_note: `Super admin created order - Order ID: ${order.ID}`, + is_export: false, + export_custom_fields_info: null, + round_off_amount: 0.0, + final_total: totalAmount, + order_id: order.ID, + created_by: user.id || user.ID, + rp_earned: 0, + tip: tipAmount, + tipDistribution: cartSummary.tipDistribution || null, + tip_store_distribution: + cartSummary.tipDistribution?.storeDistribution || 0, + tip_admin_distribution: + cartSummary.tipDistribution?.adminDistribution || 0, + tip_driver_distribution: + cartSummary.tipDistribution?.driverDistribution || 0, + order_addresses: { + shipping: { + firstName: shippingAddress?.firstName || customer.firstName || "", + lastName: shippingAddress?.lastName || customer.lastName || "", + company: shippingAddress?.company || "", + address1: shippingAddress?.addressLine1 || "", + address2: shippingAddress?.addressLine2 || "", + city: shippingAddress?.city || "", + state: shippingAddress?.state || "", + zip: shippingAddress?.pinCode || "", + country: shippingAddress?.country || "US", + phone: shippingAddress?.phoneNumber || customer.phoneNumber || "", + email: shippingAddress?.email || customer.email || "", + latitude: shippingAddress?.latitude || "0.000000", + longitude: shippingAddress?.longitude || "0.000000", + }, + billing: { + firstName: + billingAddress?.firstName || + shippingAddress?.firstName || + customer.firstName || + "", + lastName: + billingAddress?.lastName || + shippingAddress?.lastName || + customer.lastName || + "", + company: billingAddress?.company || shippingAddress?.company || "", + address1: + billingAddress?.addressLine1 || + shippingAddress?.addressLine1 || + "", + address2: + billingAddress?.addressLine2 || + shippingAddress?.addressLine2 || + "", + city: billingAddress?.city || shippingAddress?.city || "", + state: billingAddress?.state || shippingAddress?.state || "", + zip: billingAddress?.pinCode || shippingAddress?.pinCode || "", + country: + billingAddress?.country || shippingAddress?.country || "US", + phone: + billingAddress?.phoneNumber || + shippingAddress?.phoneNumber || + customer.phoneNumber || + "", + email: + billingAddress?.email || + shippingAddress?.email || + customer.email || + "", + latitude: + billingAddress?.latitude || + shippingAddress?.latitude || + "0.000000", + longitude: + billingAddress?.longitude || + shippingAddress?.longitude || + "0.000000", + }, + }, + is_created_from_api: true, + is_direct_sale: true, + is_suspend: false, + exchange_rate: 1.0, + priority: "normal", + created_at: new Date(), + updated_at: new Date(), + estimatedDeliveryTime: cartSummary.estimatedDeliveryTime, + estimatedDistance: cartSummary.estimatedDistance, + estimatedArrival: cartSummary.estimatedArrival, + }; + + const transactionRecord = await createTransactionService( + transactionPayload, + { + transaction: transaction, + } + ); + + if (!transactionRecord) { + await transaction.rollback(); + console.error( + "Failed to create transaction record for super admin order:", + transactionRecord + ); + // Don't throw error here as order is already created successfully + } else { + console.log( + "✅ Transaction record created successfully for super admin order:", + transactionRecord.id + ); + + // Update order with transaction ID + await ORDER_MODEL.update( + { + transaction_id: transactionRecord.id, + }, + { + where: { ID: order.ID }, + transaction: transaction, + } + ); + } + } catch (transactionError) { + console.error( + "Error creating transaction record for super admin order:", + transactionError + ); + // Don't rollback the entire transaction as order is already created successfully + // Just log the error and continue + } + // Commit transaction await transaction.commit(); @@ -4931,7 +5222,7 @@ module.exports.createOrderForSuperAdmin = async (req, res) => { entityType: "order", entityId: order.ID, entityName: `Order ${order.order_number}`, - userId: user.ID, + userId: user.id || user.ID, userType: user.role || "admin", description: `Super admin created order ${order.order_number} for customer ${customer.firstName} ${customer.lastName} (ID: ${customerId})`, }; diff --git a/routes/driver.routes.js b/routes/driver.routes.js index 341f1bd..a11fe74 100644 --- a/routes/driver.routes.js +++ b/routes/driver.routes.js @@ -398,6 +398,7 @@ router.get( driverController.driverAcceptedOrderPickup ); + // Get driver's accepted orders router.get( "/accepted-orders/:driverId", diff --git a/services/analytics.service.js b/services/analytics.service.js index b71cab0..619caae 100644 --- a/services/analytics.service.js +++ b/services/analytics.service.js @@ -202,3 +202,4 @@ module.exports = { getOrderMetricsService, getAllAnalyticsMetricsService }; + diff --git a/utils/cartCalculations.js b/utils/cartCalculations.js index 87acf0e..af080f0 100644 --- a/utils/cartCalculations.js +++ b/utils/cartCalculations.js @@ -730,6 +730,7 @@ const processCartItems = async ({ variationId: actualVariantId, }, }); + stockAvailable = stockRecord?.stockAvailable || 0; } } catch (error) { @@ -809,6 +810,7 @@ const calculateCartSummary = async ({ storeId = null, tipAmount=0, }) => { + // Validate cartItems if (!cartItems || !Array.isArray(cartItems) || cartItems.length === 0) { throw new Error("Cart items are required and cannot be empty"); @@ -816,12 +818,12 @@ const calculateCartSummary = async ({ // Get storeId from first cart item if not provided const calculationStoreId = storeId || cartItems[0]?.store_id; - + // Process cart items with calculations const processedCartItems = await processCartItems({ cartItems, customerState, - calculationStoreId, + storeId:calculationStoreId, }); // console.log("processedCartItems", processedCartItems); // Calculate basic cart totals -- GitLab