diff --git a/app.js b/app.js index 6adea88..4781cde 100644 --- a/app.js +++ b/app.js @@ -171,6 +171,8 @@ import CouponRoute from "./resources/Affiliate&Coupon/Coupon/CouponRoute.js"; // import ShortUrlRouter from "./resources/Businesses/Short_Urls/ShortUrlRoute.js"; //support Ticket import SupportRouter from "./resources/Supports/supportRoute.js"; +// Point of Sale +import PosorderRoute from "./resources/PosOrders/PosorderRoute.js"; app.use("/api/v1/", user); //Product @@ -238,4 +240,6 @@ app.use("/api/panel", PanelRoute); // app.use("/api/shorturl", ShortUrlRouter); //Support app.use("/api", SupportRouter); +// Point of Sale +app.use("/api/posOrder", PosorderRoute); export default app; diff --git a/resources/PosOrders/PosCheckoutController.js b/resources/PosOrders/PosCheckoutController.js new file mode 100644 index 0000000..8acb18e --- /dev/null +++ b/resources/PosOrders/PosCheckoutController.js @@ -0,0 +1,85 @@ +import { POSOrder } from "./PosorderModel.js"; +import { shippingAddress } from "../ShippingAddresses/ShippingAddressModel.js"; + +//generate unique order id +const generateOrderId = async () => { + const currentYear = new Date().getFullYear(); + // Find the latest order to get the last serial number + const latestOrder = await POSOrder.findOne({}, {}, { sort: { orderID: -1 } }); + let serialNumber = 1; + + if (latestOrder) { + const lastYear = parseInt(latestOrder.orderID.substring(0, 4), 10); + if (lastYear === currentYear) { + // If the last order was in the current year, increment the serial number + serialNumber = parseInt(latestOrder.orderID.substring(4), 10) + 1; + } + } + // Pad the serial number with zeros and concatenate with the current year + const paddedSerialNumber = serialNumber.toString().padStart(7, "0"); + const orderId = `${currentYear}${paddedSerialNumber}`; + return orderId; +}; + +export const createOrderCheckout = async (req, res) => { + try { + const { address, cart, user, SalesType, paymentMode } = + req.body; +// console.log(req.body) + // Perform validation + if (!address || !cart || cart.length === 0 || !SalesType || !user || !paymentMode) { + return res.status(400).json({ message: "Invalid order data" }); + } + + // Retrieve shipping address from database + const shippingInfo = await shippingAddress.findById(address); + if (!shippingInfo) { + return res.status(404).json({ message: "Shipping address not found" }); + } + + // Ensure that addressId is included in the shippingInfo object + const { _id: addressId, ...restOfShippingInfo } = shippingInfo.toObject(); + + // Calculate total amount based on the product_Subtotal of each product + const totalAmount = cart.reduce( + (acc, item) => acc + item.product_Subtotal, + 0 + ); + + // Construct order items array + const orderItems = cart.map((item) => ({ + product: item.product, + name: item.name, + price: item.price, + quantity: item.quantity, + product_Subtotal: item.total_amount, + gst_amount: item.gst_amount, + image: item.image, + variant_Name: item.variant_Name, + })); + + // Generate a unique order ID + const orderId = await generateOrderId(); + + // Create the order document + const order = await POSOrder.create({ + orderID: orderId, + total_amount: totalAmount, + orderItems, + shippingInfo: { + addressId: addressId, // Include the addressId + ...restOfShippingInfo, // Include other shipping information + }, + user, // Assuming you have authenticated users + SalesType, + paymentMode, + }); + + return res.status(201).json({ success: true, order }); + } catch (error) { + console.error("Error creating order:", error); + return res + .status(500) + .json({ success: false, message: "Internal server error" }); + } +}; diff --git a/resources/PosOrders/PosRazerPayCheckoutController.js b/resources/PosOrders/PosRazerPayCheckoutController.js new file mode 100644 index 0000000..111fbe4 --- /dev/null +++ b/resources/PosOrders/PosRazerPayCheckoutController.js @@ -0,0 +1,481 @@ +import bodyParser from "body-parser"; +import crypto from "crypto"; +import Razorpay from "razorpay"; +import { POSOrder } from "./PosorderModel.js"; +import { shippingAddress } from "../ShippingAddresses/ShippingAddressModel.js"; +import sendEmail from "../../Utils/sendEmail.js"; +const instance = new Razorpay({ + key_id: process.env.RAZERPAY_KEY_ID, + key_secret: process.env.RAZERPAY_SECRET_KEY, +}); + +const generateUniqueOrderId = async () => { + const currentYear = new Date().getFullYear(); + // Find the latest order to get the last serial number + const latestOrder = await POSOrder.findOne({}, {}, { sort: { orderID: -1 } }); + let serialNumber = 1; + + if (latestOrder) { + const lastYear = parseInt(latestOrder.orderID.substring(0, 4), 10); + if (lastYear === currentYear) { + // If the last order was in the current year, increment the serial number + serialNumber = parseInt(latestOrder.orderID.substring(4), 10) + 1; + } + } + // Pad the serial number with zeros and concatenate with the current year + const paddedSerialNumber = serialNumber.toString().padStart(7, "0"); + const orderId = `${currentYear}${paddedSerialNumber}`; + return orderId; +}; + +export const getRzpKey = async (req, res) => { + try { + const { name, email } = req.params; + console.log("name", name, "email", email); + if (!name || !email) { + throw new Error("Name and email are required parameters"); + } + res.status(200).json({ + success: true, + key: process.env.RAZERPAY_KEY_ID, + name, + email, + }); + } catch (error) { + console.error("Error in getRzpKey:", error); + res + .status(500) + .json({ + success: false, + message: error.message || "Internal server error", + }); + } +}; + +export const checkout = async (req, res) => { + try { + console.log(req.body); + const options = { + amount: Number(req.body.subtotal * 100), + currency: "INR", + }; + + console.log("options", options); + + // Wait for the order creation to complete + const order = await instance.orders.create(options); + + console.log("order", order); + + // Check if the order was created successfully + if (!order || !order.id) { + return res.status(400).json({ + success: false, + message: "Failed to create order", + }); + } + + // Extract required data from request parameters and body + const { email } = req.params; + const { address, cart, user, SalesType, paymentMode, subtotal } = req.body; + + // Check for required parameters + if (!email) { + return res.status(400).send({ message: "Please enter the email" }); + } + + if (cart.length < 1) { + return res.status(400).json({ message: "Cart is empty!" }); + } + + if (!address) { + return res + .status(404) + .json({ message: "Please select a shipping address!" }); + } + + if (!subtotal) { + return res + .status(404) + .json({ message: "Please provide the product subtotal!" }); + } + + // Fetch shipping information from the database + const shippingInfo = await shippingAddress.findById(address); + if (!shippingInfo) { + return res.status(404).json({ message: "Shipping address not found" }); + } + + console.log("shippinginfo", shippingInfo); + + // Extract addressId and other shipping information + const { _id: addressId, ...restOfShippingInfo } = shippingInfo.toObject(); + + // Prepare order items + const orderItems = cart.map((item) => ({ + product: item.product, + name: item.name, + price: item.price, + quantity: item.quantity, + product_Subtotal: item.total_amount, + gst_amount: item.gst_amount, + image: item.image, + variant_Name: item.variant_Name, + })); + + // Generate a unique order ID + const orderId = await generateUniqueOrderId(); + + // Create the order in the database + const orders = await POSOrder.create({ + orderID: orderId, + total_amount: subtotal, + orderItems, + shippingInfo: { + addressId: addressId, + ...restOfShippingInfo, + }, + user, + SalesType, + paymentMode, + razorpay_order_id: order.id, + }); + + res.status(200).json({ + success: true, + order, + }); + } catch (error) { + console.error("Error in checkout:", error); + res + .status(500) + .json({ + success: false, + message: error.message || "Internal server error", + }); + } +}; + +export const paymentVerification = async (req, res) => { + const { razorpay_order_id, razorpay_payment_id, razorpay_signature } = + req.body; + console.log(req.body); + const body = razorpay_order_id + "|" + razorpay_payment_id; + + const expectedSignature = crypto + .createHmac("sha256", process.env.RAZERPAY_SECRET_KEY) + .update(body.toString()) + .digest("hex"); + + const isAuthentic = expectedSignature === razorpay_signature; + + if (isAuthentic) { + // Database comes here + let findSameOrder = await POSOrder.findOne({ + razorpay_order_id: razorpay_order_id, + }).populate({ + path: "user", + select: "name email -_id", + }); + // console.log("findSameOrder", findSameOrder); + if (findSameOrder) { + (findSameOrder.razorpay_payment_id = razorpay_payment_id), // await Payment.create({ + (findSameOrder.isPaid = true), + (findSameOrder.paidAt = Date.now()), + (findSameOrder.razorpay_signature = razorpay_signature); + // await Payment.create({ + findSameOrder.payment_status = "success"; + + findSameOrder.orderStatus = "new"; + await findSameOrder.save(); + } + //send email to customer + // console.log("findSameOrder", findSameOrder); + await sendEmail({ + to: `${findSameOrder?.user?.email}`, // Change to your recipient + + from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender + + subject: `Your POSOrder #${findSameOrder?.orderID} Confirmation`, + html: `

Welcome to Smellika - Let the Shopping Begin!

+ Hi ${ + findSameOrder?.shippingInfo?.first_Name + }, + +

Great news! Your order #${ + findSameOrder?.orderID + } has been confirmed. Here are the details

+

Shipping Address : ${ + findSameOrder?.shippingInfo?.first_Name + } ${findSameOrder?.shippingInfo?.last_Name} , ${ + findSameOrder?.shippingInfo?.street + } ${findSameOrder?.shippingInfo?.city} ${ + findSameOrder?.shippingInfo?.state + } ${findSameOrder?.shippingInfo?.country}, PIN-${ + findSameOrder?.shippingInfo?.postalCode + }, Phone Number: ${findSameOrder?.shippingInfo?.phone_Number}

+

POSOrder Items :

+ + + + + + + + + + + + + + + + + + ${findSameOrder?.orderItems + ?.map( + (product, index) => ` + + + + + + + + + + + + + ` + ) + .join("")} + + + + + +
S No.Product NameImageQuantityPriceGST AmountSubTotal
${ + index + 1 + }${ + product.name + }${
+          product.name
+        }${ + product.quantity + }₹${ + product.price + }₹${ + product?.gst_amount + }₹${ + product.product_Subtotal + }
Total Amount :₹${ + findSameOrder?.total_amount + }
+ +
+ Best regards,
+ + Team Smellika`, + }); + // console.log("findSameOrder", findSameOrder); + + // // findSameOrder.razorpay_payment_id=razorpay_payment_id,// await Payment.create({ + // findOrder.paidAt = new Date(event.data.object.created * 1000); + // findOrder.isPaid = true; + + // razorpay_signature: { type: String }, + // razorpay_order_id, + // razorpay_payment_id, + // razorpay_signature, + // }); + + // res.redirect(`https://admin.smellika.com/#/pos`); + res.redirect(`http://localhost:3000/#/pos`); + } else { + res.status(400).json({ + success: false, + }); + } +}; +export const handlePayment = async (req, res) => { + try { + const { email } = req.user; + if (!email) + return res.status(400).send({ message: "Please enter the email" }); + const { address, cart, subtotal } = req.body; + if (cart.length < 1) + return res.status(400).json({ message: "cart is empty!" }); + switch (true) { + //validation + case !address: { + return res.status(404).json({ msg: "please provide shipping address" }); + } + case !subtotal: { + return res.status(404).json({ msg: "please provide product subtotal" }); + } + } + let addss = await shippingAddress.findById(address); + // console.log(addss?.postalCode); + let shipping = { + first_Name: addss.first_Name, + last_Name: addss.last_Name, + phone_Number: addss.phone_Number, + street: addss.street, + city: addss.city, + state: addss.state, + postalCode: addss?.postalCode, + country: addss.country, + addressId: address, + }; + const orderItems = await cart.map((item) => ({ + product: item.product._id, + name: item.product.name, + price: item.product.total_amount, + image: item.product.image, + quantity: item.quantity, + product_Subtotal: item.subtotal, + })); + + // console.log("line", lineItems[0]); + const Id = await generateUniqueOrderId(); + const order = await POSOrder.create({ + orderID: Id, + total_amount: subtotal, + orderItems, + shippingInfo: shipping, + user: req.user._id, + }); + console.log("fffffffff", order, "llllllllll"); + const lineItems = await cart.map((item) => ({ + price_data: { + currency: "inr", + product_data: { + name: item.product.name, + + images: [item.product.image[0]?.url], + }, + unit_amount: Number(item.product.total_amount) * 100, + }, + quantity: Number(item.quantity), + })); + if (order) { + const session = await stripe.checkout.sessions.create({ + payment_method_types: ["card"], + line_items: lineItems, + mode: "payment", + customer_email: `${email}`, + metadata: { + orderId: order._id.toString(), + + // Add any other key-value pairs as needed + }, + success_url: `${process.env.FRONTEND_URL}/cart`, + cancel_url: `${process.env.FRONTEND_URL}/error`, + }); + // res.json({ sessionId: session.id }); + + res.status(200).send({ message: "order created", url: session.url }); + } + } catch (err) { + console.log(err); + res.status(500).send({ message: "Something went wrong", err }); + } +}; + +export const webhook = async (req, res) => { + const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; + const signature = req.headers["stripe-signature"]; + let event; + if (webhookSecret) { + try { + event = stripe.webhooks.constructEvent( + req.body, + signature, + webhookSecret + ); + } catch (err) { + console.log(`❌ Error message: ${err.message}`); + res.status(400).send(`Webhook Error: ${err.message}`); + return; + } + } + + if (event.type === "checkout.session.completed") { + // console.log("dddddddddddd", event.data); + const findOrder = await POSOrder.findById( + event.data.object.metadata?.orderId + ); + findOrder.paypal_payer_id = event.data.object.id; + findOrder.paidAt = new Date(event.data.object.created * 1000); + findOrder.isPaid = true; + if (event.data.object?.payment_status === "paid") { + findOrder.payment_status = "success"; + } else { + findOrder.payment_status = "failed"; + } + findOrder.orderStatus = "new"; + await findOrder.save(); + await sendEmail({ + to: `${event.data.object.customer_email}`, // Change to your recipient + + from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender + + subject: `Your POSOrder #${findOrder?.orderID} Confirmation`, + html: `

Welcome to Smellika - Let the Shopping Begin!

+ Hi ${findOrder?.shippingInfo?.first_Name}, + +

Great news! Your order #${findOrder?.orderID} has been confirmed. Here are the details

+
+ Best regards,
+ + Team Smellika`, + }); + + // Items: [List of Purchased Items] + // Total Amount: [Total Amount] + // Shipping Address: [Shipping Address] + + // We'll keep you updated on the shipping progress. Thanks for choosing Smellika! + + // Best regards + // Team Smellika + console.log( + "event.data.object", + event.data.object, + "---------------------" + ); + + console.log(`💰 Payment status: ${event.data.object?.payment_status}`); + + // Saving the payment details in the database + // const payment = await Payment.create({ + // customer_email: event.data.object.customer_email, + // amount: event.data.object.amount_total / 100, + // paymentId: event.data.object.id, + // paymentStatus: event.data.object.payment_status, + // createdAt: event.data.object.created, + // }); + } + // if (event.type === "checkout.session.completed") { + // console.log("dddddddddddd", event.data); + // console.log("event.data.object", event.data.object); + // console.log(`💰 Payment status: ${event.data.object?.payment_status}`); + // payment_intent.payment_failed; + + // // Saving the payment details in the database + // // const payment = await Payment.create({ + // // customer_email: event.data.object.customer_email, + // // amount: event.data.object.amount_total / 100, + // // paymentId: event.data.object.id, + // // paymentStatus: event.data.object.payment_status, + // // createdAt: event.data.object.created, + // // }); + // } + + // Return a 200 res to acknowledge receipt of the event + res.status(200).end(); + // res.send().end(); +}; diff --git a/resources/PosOrders/PosorderController.js b/resources/PosOrders/PosorderController.js new file mode 100644 index 0000000..6a02741 --- /dev/null +++ b/resources/PosOrders/PosorderController.js @@ -0,0 +1,552 @@ +import sendEmail from "../../Utils/sendEmail.js"; +import { POSOrder } from "./PosorderModel.js"; + +export const getAllOrder = async (req, res) => { + try { + const { status } = req.params; + const order = await POSOrder.find({ + IsStoreDelivery: "Cash", + orderStatus: status, + }) + .populate({ + path: "user", + select: "name -_id", + }) + .populate({ + path: "shippingInfo.addressId", + }) + .sort({ updatedAt: -1 }); + if (order) { + res.status(201).json({ + success: true, + order, + message: "All POSOrder Fetched", + }); + } + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went Wrong", + }); + } +}; +export const getOrders = async (req, res) => { + try { + const order = await POSOrder.find({ + // payment_status: "success", + }) + .populate({ + path: "user", + select: "name -_id", + }) + .populate({ + path: "shippingInfo.addressId", + }) + .sort({ updatedAt: -1 }); + if (order) { + res.status(201).json({ + success: true, + order, + message: "All POSOrder Fetched", + }); + } + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went Wrong", + }); + } +}; +export const getSingleOrder = async (req, res) => { + try { + if (!req.params.id) + return res.status(400).json({ message: "please Provide POSOrder Id" }); + + const order = await POSOrder.findById(req.params.id) + .populate({ + path: "user", + select: "name email -_id", + }) + .populate({ + path: "shippingInfo.addressId", + }) + .sort({ createdAt: -1 }); + if (order) { + res.status(201).json({ + success: true, + order, + message: " POSOrder Fetched", + }); + } + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went Wrong", + }); + } +}; + +//get self User POSOrder +export const getUserSelf = async (req, res) => { + if (!req?.user) return res.status(400).json({ message: "please login !" }); + try { + const order = await POSOrder.find({ + user: req.user?._id, + payment_status: "success", + }).sort({ createdAt: -1 }); + + if (order) { + return res.status(200).json({ + success: true, + order, + message: "self POSOrder fetched", + }); + } + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went Wrong", + }); + } +}; + +export const deleteOneOrder = async (req, res) => { + try { + if (!req?.user) return res.status(400).json({ message: "please login !" }); + if (!req.params.id) + return res.status(400).json({ message: "please Provide POSOrder Id" }); + const getOrder = await POSOrder.findById(req.params.id); + if (!getOrder) { + return res.status(404).json({ + success: false, + message: "No POSOrder Found!", + }); + } + const order = await POSOrder.findByIdAndDelete(req.params.id); + + await order.remove(); + res.status(200).json({ + success: true, + message: "POSOrder Deleted Successfully!!", + }); + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went Wrong", + }); + } +}; +export const updateOrderStatusById = async (req, res) => { + try { + let body = { orderStatus: req.body.status }; + + const currentDate = new Date(); + body["status_timeline." + req.body.status] = currentDate; + // if (req.body?.package_weight) body.package_weight = req.body.package_weight; + const order = await POSOrder.findById(req.params.id).populate({ + path: "user", + select: "name email -_id", + }); + // console.log("order", order); + // const parentData = { email: order?.parent?.email }; + if (req.body.status === "cancelled") { + body["order_Cancelled_Reason"] = req.body?.ReasonforCancellation; + body["iscancelled"] = true; + await POSOrder.findByIdAndUpdate(order._id, body); + await sendEmail({ + to: `${order?.user?.email}`, // Change to your recipient + from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender + subject: `POSOrder #${order?.orderID} Update: Cancellation and Refund Process`, + html: ` Hi ${ + order?.shippingInfo?.first_Name + }, +

We hope this message finds you well. We're writing to inform you that your order ${ + order?.orderID + } has been canceled. We understand that circumstances may change, and we're here to assist you throughout the process.

+ + +

Items :

+ + + + + + + + + + + + + + + + + + ${order?.orderItems + ?.map( + (product, index) => ` + + + + + + + + + + + + + ` + ) + .join("")} + + + + + +
S No.Product NameImageQuantityPriceGST AmountSubTotal
${ + index + 1 + }${ + product.name + }${
+          product.name
+        }${ + product.quantity + }₹${ + product.price + }₹${ + product?.gst_amount + }₹${ + product.product_Subtotal + }
Total Amount :₹${ + order?.total_amount + }
+

Cancellation Reason : ${ + req.body?.ReasonforCancellation + }

+

Refund Information: The amount from your canceled order will be processed for a refund. Please allow up to 7 working days for the amount to be transferred back to your original payment method.

+ +
If you have any concerns or further questions, please feel free to reply to this email. We appreciate your understanding and hope to serve you better in the future. +
+
+ Best regards,
+ + Team Smellika`, + }); + return res + .status(200) + .json({ status: "ok", message: "POSOrder status updated successfully!" }); + } else if (req.body.status === "processing") { + await POSOrder.findByIdAndUpdate(order._id, body); + + await sendEmail({ + to: `${order?.user?.email}`, // Change to your recipient + from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender + subject: `Your POSOrder #${order?.orderID} is in Processing!`, + html: `

Exciting news! Your order #${ + order?.orderID + } has entered the processing phase. Our team is diligently preparing your items for dispatch. Rest assured, we're working hard to ensure everything is perfect for you.

+ Hi ${ + order?.shippingInfo?.first_Name + }, +

POSOrder Status : Processing

+

POSOrder Items :

+ + + + + + + + + + + + + + + + + + ${order?.orderItems + ?.map( + (product, index) => ` + + + + + + + + + + + + + ` + ) + .join("")} + + + + + +
S No.Product NameImageQuantityPriceGST AmountSubTotal
${ + index + 1 + }${ + product.name + }${
+          product.name
+        }${ + product.quantity + }₹${ + product.price + }₹${ + product?.gst_amount + }₹${ + product.product_Subtotal + }
Total Amount :₹${ + order?.total_amount + }
+
We'll send you another email with the tracking details as soon as your order is dispatched. If you have any questions or need assistance, feel free to reply to this email.
+
Thank you for choosing Smellika!
+
+ Best regards,
+ + Team Smellika`, + }); + return res + .status(200) + .json({ status: "ok", message: "POSOrder status updated successfully!" }); + } + // else if (body.status === "dispatched") { + // const noBalanceRemaining = + // order?.sales_items?.filter((e) => Number(e?.balance_quantity) > 0) + // ?.length === 0 + // ? true + // : false; + // if (!noBalanceRemaining) + // return res + // .status(400) + // .json({ message: "Few items still have balance quantity!" }); + // await OrderDispatchedEmail(parentData.email, order.order_id, body); + // await Invoice.updateMany( + // { order: order._id, status: { $in: ["processing"] } }, + // { status: body.status, "status_timeline.dispatched": currentDate } + // ); + // } else if (body.status === "delivered") { + // await OrderDeliveredEmail(parentData.email, order.order_id); + // await Invoice.updateMany( + // { order: order._id, status: { $in: ["processing", "dispatched"] } }, + // { status: body.status, "status_timeline.delivered": currentDate } + // ); + // } + else if (req.body.status === "dispatched") { + body["courier_name"] = req.body.courierName; + body["courier_tracking_id"] = req.body.TrackingID; + await POSOrder.findByIdAndUpdate(order._id, body); + await sendEmail({ + to: `${order?.user?.email}`, // Change to your recipient + from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender + subject: `Your POSOrder #${order?.orderID} is On Its Way!`, + html: ` Hi ${ + order?.shippingInfo?.first_Name + }, +

Exciting news! Your order #${ + order?.orderID + } has been dispatched and is en route to you. 🚚 Here are the details:

+ +

Courier Name : ${ + req.body.courierName + }

+

Courier Tracking ID : ${ + req.body.TrackingID + }

+ + +

Items :

+ + + + + + + + + + + + + + + + + + ${order?.orderItems + ?.map( + (product, index) => ` + + + + + + + + + + + + + ` + ) + .join("")} + + + + + +
S No.Product NameImageQuantityPriceGST AmountSubTotal
${ + index + 1 + }${ + product.name + }${
+          product.name
+        }${ + product.quantity + }₹${ + product.price + }₹${ + product?.gst_amount + }₹${ + product.product_Subtotal + }
Total Amount :₹${ + order?.total_amount + }
+

POSOrder Status : Dispatched

+

If you have any questions or need assistance, feel free to reply to this email. +

+
Thanks for choosing Smellika! We hope you enjoy your purchase. +
+
+ Best regards,
+ + Team Smellika`, + }); + return res + .status(200) + .json({ status: "ok", message: "POSOrder status updated successfully!" }); + } else if (req.body.status === "delivered") { + body["isDelivered"] = true; + body["DeliveredDate"] = req.body.DDate; + await POSOrder.findByIdAndUpdate(order._id, body); + await sendEmail({ + to: `${order?.user?.email}`, // Change to your recipient + from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender + subject: `Your POSOrder #${order?.orderID} Has Been Delivered!`, + html: ` Hi ${ + order?.shippingInfo?.first_Name + }, +

Great news! Your order #${ + order?.orderID + } has been successfully delivered to your doorstep. We hope everything is just as you expected!

+

Items :

+ + + + + + + + + + + + + + + + + + ${order?.orderItems + ?.map( + (product, index) => ` + + + + + + + + + + + + + ` + ) + .join("")} + + + + + +
S No.Product NameImageQuantityPriceGST AmountSubTotal
${ + index + 1 + }${ + product.name + }${
+                product.name
+              }${ + product.quantity + }₹${ + product.price + }₹${ + product?.gst_amount + }₹${ + product.product_Subtotal + }
Total Amount :₹${ + order?.total_amount + }
+

Delivery Date: ${ + req.body.DDate + }

+

Your satisfaction is our priority, and we'd love to hear about your experience. Please take a moment to share your thoughts by leaving a review. Your feedback is invaluable to us! +

+
If you have any questions or concerns about your order, feel free to reply to this email. +
+
Thank you for choosing Smellika! We hope to serve you again soon. + +
+
+ Best regards,
+ + Team Smellika`, + }); + + return res + .status(200) + .json({ status: "ok", message: "POSOrder status updated successfully!" }); + } else { + // await POSOrder.findByIdAndUpdate(order._id, body); + // console.log(order); + res + .status(200) + .json({ status: "ok", message: "POSOrder status updated successfully!" }); + } + } catch (error) { + console.log(error); + res + .status(500) + .json({ message: error?.message || "Something went wrong!" }); + } +}; diff --git a/resources/PosOrders/PosorderModel.js b/resources/PosOrders/PosorderModel.js new file mode 100644 index 0000000..5134084 --- /dev/null +++ b/resources/PosOrders/PosorderModel.js @@ -0,0 +1,165 @@ +import mongoose from "mongoose"; + +const POSorderSchema = new mongoose.Schema( + { + orderID: { + type: String, + required: true, + unique: true, + }, + user: { + type: mongoose.Schema.ObjectId, + ref: "User", + required: true, + }, + shippingInfo: { + first_Name: { + type: String, + required: true, + }, + last_Name: { + type: String, + required: true, + }, + phone_Number: { + type: Number, + required: true, + }, + street: { + type: String, + required: true, + }, + city: { + type: String, + required: true, + trim: true, + }, + state: { + type: String, + required: true, + }, + postalCode: { + type: String, + required: true, + trim: true, + // Add a regular expression to enforce a specific postal code format + // For example, assuming a 5-digit format for the United States + match: /^\d{6}$/, + }, + country: { + type: String, + required: true, + }, + addressId: { + type: mongoose.Schema.ObjectId, + ref: "ShippingAddress", + required: true, + }, + }, + orderItems: [ + { + name: { + type: String, + default: "", + }, + price: { + type: Number, + default: "", + }, + variant_Name: { type: String, default: "" }, + quantity: { + type: Number, + default: "", + default: 1, + }, + image: [{}], + + product_Subtotal: { + type: Number, + default: "", + }, + gst_amount: { + type: Number, + default: "", + }, + gst_rate: { + type: Number, + default: "", + }, + tax_Name: { + type: String, + default: "", + }, + product: { + type: mongoose.Schema.ObjectId, + ref: "Product", + }, + }, + ], + + shipping_charge: { type: Number, default: 0 }, + tax_amount: { type: Number, default: 0 }, + total_amount: { type: Number, default: 0 }, + weight: { type: Number, default: 0 }, + + SalesType: { + type: String, + enum: ["inStoreDelivery", "shipToCustomer"], + }, + paymentMode: { + type: String, + enum: ["QRCode", "Cash","SendPaymentLink"], + }, + payment_status: { + type: String, + enum: ["pending", "success", "failed"], + }, + isPaid: { + type: Boolean, + default: false, + }, + paidAt: { + type: Date, + }, + + orderStatus: { + type: String, + enum: [ + "new", + "processing", + "dispatched", + "delivered", + "cancelled", + "returned", + ], + default: "new", + }, + razorpay_payment_id: { type: String }, + razorpay_order_id: { type: String }, + razorpay_signature: { type: String }, + isDelivered: { type: Boolean, required: true, default: false }, + DeliveredDate: { type: String, default: "" }, + + // deliveredAt: { type: Date }, + status_timeline: { + new: { type: Date }, + processing: { type: Date }, + dispatched: { type: Date }, + delivered: { type: Date }, + cancelled: { type: Date }, + returned: { type: Date }, + }, + iscancelled: { + type: Boolean, + default: false, + }, + order_Cancelled_Reason: { + type: String, + }, + courier_name: { type: String }, + courier_tracking_id: { type: String }, + }, + { timestamps: true } +); + +export const POSOrder = mongoose.model("POSOrder", POSorderSchema); diff --git a/resources/PosOrders/PosorderRoute.js b/resources/PosOrders/PosorderRoute.js new file mode 100644 index 0000000..65dace4 --- /dev/null +++ b/resources/PosOrders/PosorderRoute.js @@ -0,0 +1,51 @@ +import bodyParser from "body-parser"; +import { + deleteOneOrder, + getAllOrder, + getOrders, + getSingleOrder, + getUserSelf, + updateOrderStatusById, +} from "./PosorderController.js"; +import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js"; +import express from "express"; +import { + createOrderCheckout, +} from "./PosCheckoutController.js"; +import { checkout, getRzpKey, paymentVerification } from "./PosRazerPayCheckoutController.js"; + +const app = express(); + +// Configure bodyParser to parse the raw request body as a buffer +app.use(bodyParser.raw({ type: "application/json" })); + + +const router = express.Router(); +//checkout Routes-------------------------// +router.route("/pos-checkout/").post(isAuthenticatedUser, createOrderCheckout); +// --------------------------------------------------- + +// -------------------------------------------------- +//get user self +router.route("/user/self").get(isAuthenticatedUser, getUserSelf); + +//admin route +router + .route("/pos-getAll/:status") + .get(isAuthenticatedUser, authorizeRoles("admin"), getAllOrder); +router + .route("/pos-getAll/") + .get(isAuthenticatedUser, authorizeRoles("admin"), getOrders); +router.route("/pos-getOne/:id").get(isAuthenticatedUser, getSingleOrder); +router + .route("/pos-change/status/:id") + .patch(isAuthenticatedUser, authorizeRoles("admin"), updateOrderStatusById); + +router + .route("/pos-delete/:id") + .delete(isAuthenticatedUser, authorizeRoles("admin"), deleteOneOrder); +//RAZERPAY checkout +router.route("/getRzpKey/:name/:email").get(isAuthenticatedUser, getRzpKey); +router.route("/Rzpcheckout/").post(isAuthenticatedUser, checkout); +router.route("/paymentverification").post(paymentVerification); +export default router;