From f85a516808f7914da76b205a6c244f9f35b36b71 Mon Sep 17 00:00:00 2001 From: Raj-varu Date: Tue, 12 Mar 2024 13:53:24 +0530 Subject: [PATCH] Affiliate API and Coupon API --- app.js | 16 +- .../Affiliate/AffiliateController.js | 291 +++++++++++++ .../Affiliate/AffiliateModel.js | 122 ++++++ .../Affiliate/AffiliateRoute.js | 69 ++++ .../Coupon/CouponController.js | 383 ++++++++++++++++++ .../Affiliate&Coupon/Coupon/CouponRoute.js | 76 ++++ 6 files changed, 954 insertions(+), 3 deletions(-) create mode 100644 resources/Affiliate&Coupon/Affiliate/AffiliateController.js create mode 100644 resources/Affiliate&Coupon/Affiliate/AffiliateModel.js create mode 100644 resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js create mode 100644 resources/Affiliate&Coupon/Coupon/CouponController.js create mode 100644 resources/Affiliate&Coupon/Coupon/CouponRoute.js diff --git a/app.js b/app.js index c67ffcc..e10bf7d 100644 --- a/app.js +++ b/app.js @@ -157,8 +157,12 @@ import SpecialtiesRouter from "./resources/Specialties/SpecialtiesRoute.js"; import ShippingAddressRoute from "./resources/ShippingAddresses/ShippingAddressRoute.js"; import stripeRoute from "./resources/StripePayment/stripeRoute.js"; -import SeoRoute from "./resources/SEO&Analytics/SEORouter.js" +import SeoRoute from "./resources/SEO&Analytics/SEORouter.js"; +//Affiliate Routes +import AffiliateRoute from "./resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js"; +//Coupon Routes +import CouponRoute from "./resources/Affiliate&Coupon/Coupon/CouponRoute.js"; //short urls // import ShortUrlRouter from "./resources/Businesses/Short_Urls/ShortUrlRoute.js"; @@ -204,9 +208,15 @@ app.use("/api/tax", TaxRouter); //config app.use("/api/config", ConfigRouter); -app.use("/api/stripe",stripeRoute); +app.use("/api/stripe", stripeRoute); -app.use("/api/seo",SeoRoute); +app.use("/api/seo", SeoRoute); + +//Affiliates +app.use("/api/v1/affiliate", AffiliateRoute); + +//Coupons +app.use("/api/v1/coupon", CouponRoute); //config specialty // app.use("/api/config/specialty", SpecialtiesRouter); //specialties diff --git a/resources/Affiliate&Coupon/Affiliate/AffiliateController.js b/resources/Affiliate&Coupon/Affiliate/AffiliateController.js new file mode 100644 index 0000000..790f917 --- /dev/null +++ b/resources/Affiliate&Coupon/Affiliate/AffiliateController.js @@ -0,0 +1,291 @@ +import { AffiliateModel } from "./AffiliateModel.js"; + +// -----------------------------AFFILIATE & COUPONS ARE HARDLY BINDED DATA-------------------------------------------------------- +//Create Affiliate +export const createAffiliate = async (req, res) => { + try { + const result = req.body; + const affiliate = new AffiliateModel(result); + const savedData = await affiliate.save(); + if (savedData) { + return res + .status(201) + .json({ success: true, message: "Affiliate Added" }); + } + } catch (error) { + res.status(500).json({ + success: false, + message: error.message + .split(":") + .splice(1) + .join(":") + .trim() + .split(":") + .splice(1) + .join(":") + .trim(), + }); + } +}; + +//EDIT +export const editAffiliate = async (req, res) => { + const updateFields = {}; + + const { + name, + mobile, + email, + country, + state, + city, + address, + pincode, + nameAsBank, + accountNo, + ifsc, + bankName, + branchName, + } = req.body; + + // Add only the fields that are present in the request body to the updateFields object + if (name) updateFields.name = name; + if (mobile) updateFields.mobile = mobile; + if (email) updateFields.email = email; + if (country) updateFields.country = country; + if (state) { + updateFields.state = state; + } else { + updateFields.state = ""; + } + if (city) { + updateFields.city = city; + } else { + updateFields.city = ""; + } + if (address) updateFields.address = address; + if (pincode) updateFields.pincode = pincode; + if (nameAsBank) updateFields.nameAsBank = nameAsBank; + if (accountNo) updateFields.accountNo = accountNo; + if (ifsc) updateFields.ifsc = ifsc; + if (bankName) updateFields.bankName = bankName; + if (branchName) updateFields.branchName = branchName; + try { + const saveData = await AffiliateModel.findByIdAndUpdate( + { _id: req.params.id }, + { $set: updateFields }, + { new: true } + ); + res.json({ + success: true, + message: "Affiliate Updated Succesfully", + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in Updation", + }); + } +}; +//DELETE +export const deleteAffiliate = async (req, res) => {}; +//PAY AFFILIATE TODO +export const payAffiliate = async (req, res) => { + // console.log(req.body); + const { noOfCoupons, amountToPay, amount, transecId, date, time } = req.body; + if ( + !req.params.id || + !noOfCoupons || + !amountToPay || + !amount || + !transecId || + !date || + !time + ) { + return res.status(400).json({ + success: false, + message: "Error in Payment", + }); + } + try { + const affiliate = await AffiliateModel.findById(req.params.id); + //Checking if it's valid data from the client + if ( + amountToPay != affiliate.total_earning - affiliate.paid_amount || + noOfCoupons != affiliate.coupon_claimed - affiliate.no_of_paid_coupon + ) { + return res.status(400).json({ + success: false, + message: "Data invalid", + }); + } + + // Construct the update operation + const updateOperation = { + $push: { + affiliate_pay_history: { + amount: amountToPay, + transecId: transecId, + date: date, + time: time, + }, + }, + $inc: { + paid_amount: amountToPay, + no_of_paid_coupon: noOfCoupons, + }, + }; + + // Execute the update operation + const updatedAffiliate = await AffiliateModel.findByIdAndUpdate( + req.params.id, + updateOperation, + { new: true } + ); + + return res.json({ + success: true, + message: "Payment Done Successfully", + updatedAffiliate: { updatedAffiliate }, + }); + } catch (error) { + console.error(error); + return res.status(400).json({ + success: false, + message: "Error in Payment", + }); + } +}; + +//GET ONE AFFLILIATE +export const getOneAffiliate = async (req, res) => { + if (req.params?.id) { + try { + const saveData = await AffiliateModel.findById(req.params.id); + const resObj = { + name: saveData.name, + mobile: saveData.mobile, + email: saveData.email, + country: saveData.country, + state: saveData.state, + city: saveData.city, + address: saveData.address, + pincode: saveData.pincode, + nameAsBank: saveData.nameAsBank, + accountNo: saveData.accountNo, + ifsc: saveData.ifsc, + bankName: saveData.bankName, + branchName: saveData.branchName, + }; + res.status(200).json({ + success: true, + message: resObj, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in getting Affiliates", + }); + } + } +}; +//LIST ALL AFFILIATE +export const listAllAffiliate = async (req, res) => { + try { + const affiliate = await AffiliateModel.find( + {}, + { + name: 1, + _id: 1, + coupon_claimed: 1, + coupon_code: 1, + total_earning: 1, + paid_amount: 1, + is_affiliate_active: 1, + } + ).sort({ createdAt: -1 }); + res.status(200).json({ + success: true, + message: affiliate, + }); + } catch (error) { + res.status(500).json({ + success: false, + messgae: error.message ? error.message : "Something went wrong!", + }); + } +}; + +//Activate & Deactivate Affiliates +export const suspendAffiliate = async (req, res) => { + const { id, is_affiliate_active } = req.body; + try { + const saveData = await AffiliateModel.findByIdAndUpdate(id, { + is_affiliate_active: is_affiliate_active, + }); + res.status(200).json({ + success: true, + message: "Success", + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Affiliate Doesn't Exists", + }); + } +}; + +//Get Affiliate data for payment +export const getOneAffiliateForPay = async (req, res) => { + if (req.params?.id) { + try { + const saveData = await AffiliateModel.findById(req.params.id); + const resObj = { + name: saveData.name, + coupon_claimed: saveData.coupon_claimed, + total_earning: saveData.total_earning, + paid_amount: saveData.paid_amount, + no_of_paid_coupon: saveData.no_of_paid_coupon, + affiliate_discount_amount: saveData.affiliate_discount_amount, + coupon_code: saveData.coupon_code, + nameAsBank: saveData.nameAsBank, + accountNo: saveData.accountNo, + ifsc: saveData.ifsc, + bankName: saveData.bankName, + branchName: saveData.branchName, + }; + res.status(200).json({ + success: true, + message: resObj, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in getting Affiliates", + }); + } + } +}; +//Get Affiliate data for History +export const affiliatePayHistory = async (req, res) => { + if (req.params?.id) { + try { + const saveData = await AffiliateModel.findById(req.params.id).sort({ + updatedAt: -1, + }); + const resObj = { + affiliate_pay_history: saveData.affiliate_pay_history, + name: saveData.name, + }; + res.status(200).json({ + success: true, + message: resObj, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in getting History", + }); + } + } +}; diff --git a/resources/Affiliate&Coupon/Affiliate/AffiliateModel.js b/resources/Affiliate&Coupon/Affiliate/AffiliateModel.js new file mode 100644 index 0000000..928243e --- /dev/null +++ b/resources/Affiliate&Coupon/Affiliate/AffiliateModel.js @@ -0,0 +1,122 @@ +import mongoose from "mongoose"; +const { Schema, model } = mongoose; + +const couponUsedSchema = new Schema({ + userId: { type: String, required: true }, + orderId: { type: String, required: true }, + couponCode: { type: String, required: true }, + date: { type: String, required: true }, +}); +const affilitePaySchema = new Schema({ + amount: { type: Number, required: true }, + transecId: { type: String, required: true }, + date: { type: String, required: true }, + time: { type: String, required: true }, +}); +const affiliateSchema = new Schema( + { + name: { + type: String, + maxLength: [25, "name cannot exceed 25 characters"], + required: [true, "Please Enter Name"], + }, + mobile: { + type: Number, + maxLength: [10, "Mobile cannot exceed 10 characters"], + minlength: [10, "Invalid Mobile Number"], + required: [true, "Please Enter Mobile Number"], + unique: true, + }, + email: { + type: String, + required: [true, "Please Enter Email"], + unique: true, + }, + country: { + type: String, + required: [true, "Please Enter Country"], + }, + state: { + type: String, + }, + city: { + type: String, + }, + address: { + type: String, + required: [true, "Please Enter Address"], + }, + pincode: { + type: Number, + required: [true, "Please Enter Pincode"], + }, + nameAsBank: { + type: String, + required: [true, "Please Enter Name as Bank"], + }, + accountNo: { + type: Number, + required: [true, "Please Enter Account Number"], + unique: true, + }, + ifsc: { + type: String, + required: [true, "Please Enter IFSC code"], + }, + bankName: { + type: String, + required: [true, "Please Enter Bank Name"], + }, + branchName: { + type: String, + required: [true, "Please Enter Branch Name"], + }, + coupon_code: { + type: String, + unique: [true, "Coupon Alerady Exists"], + sparse: true, + }, + discount_amount: { + type: Number, + }, + affiliate_discount_amount: { + type: Number, + }, + valid_till: { + type: String, + }, + createdAt: { + type: String, + }, + + coupon_claimed: { + type: Number, + default: 0, + }, + total_earning: { + type: Number, + default: 0, + }, + paid_amount: { + type: Number, + default: 0, + }, + no_of_paid_coupon: { + type: Number, + default: 0, + }, + is_affiliate_active: { + type: Boolean, + default: true, + }, + is_coupon_active: { + type: Boolean, + default: false, + }, + coupon_used_history: [couponUsedSchema], + affiliate_pay_history: [affilitePaySchema], + }, + { timestamps: true } +); + +export const AffiliateModel = model("Affiliate", affiliateSchema); diff --git a/resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js b/resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js new file mode 100644 index 0000000..ccf9b66 --- /dev/null +++ b/resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js @@ -0,0 +1,69 @@ +import express from "express"; +import { + affiliatePayHistory, + createAffiliate, + editAffiliate, + getOneAffiliate, + getOneAffiliateForPay, + listAllAffiliate, + payAffiliate, + suspendAffiliate, +} from "./AffiliateController.js"; + +import { + isAuthenticatedUser, + authorizeRoles, +} from "../../../middlewares/auth.js"; + +const router = express.Router(); + +router.post( + "/create", + isAuthenticatedUser, + authorizeRoles("admin"), + createAffiliate +); +router.get( + "/getall", + isAuthenticatedUser, + authorizeRoles("admin"), + listAllAffiliate +); +router.get( + "/getone/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + getOneAffiliate +); +router.patch( + "/edit/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + editAffiliate +); +router.patch( + "/suspend", + isAuthenticatedUser, + authorizeRoles("admin"), + suspendAffiliate +); +router.post( + "/pay/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + payAffiliate +); +router.get( + "/getpay/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + getOneAffiliateForPay +); +router.get( + "/history/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + affiliatePayHistory +); + +export default router; diff --git a/resources/Affiliate&Coupon/Coupon/CouponController.js b/resources/Affiliate&Coupon/Coupon/CouponController.js new file mode 100644 index 0000000..b328f01 --- /dev/null +++ b/resources/Affiliate&Coupon/Coupon/CouponController.js @@ -0,0 +1,383 @@ +import { AffiliateModel } from "../Affiliate/AffiliateModel.js"; //Note AffiliteModel is binded with coupons + +//GET ALL Coupons +export const listAllCoupon = async (req, res) => { + try { + const coupon = await AffiliateModel.find( + {}, + { + name: 1, + _id: 1, + coupon_code: 1, + discount_amount: 1, + affiliate_discount_amount: 1, + is_coupon_active: 1, + } + ).sort({ createdAt: -1 }); + const filteredCoupons = coupon.filter( + (data) => !(data.coupon_code == null) + ); + // console.log(filteredCoupons); + // console.log(coupon); + + res.status(200).json({ + success: true, + message: filteredCoupons, + }); + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went wrong!", + }); + } +}; +//CREATE Coupon (AKA Need to update Affiliate ) +export const createCoupon = async (req, res) => { + //creation of date + const date = new Date(); + const daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + const monthsOfYear = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ]; + const dayOfWeek = daysOfWeek[date.getUTCDay()]; + const dateOfMonth = date.getUTCDate(); + const month = monthsOfYear[date.getUTCMonth()]; + const year = date.getUTCFullYear(); + const formattedDate = `${dayOfWeek} ${dateOfMonth}-${month}-${year}`; + const { + coupon_code, + discount_amount, + valid_till, + is_coupon_active, + affiliate_discount_amount, + } = req.body; + try { + const { id } = req.body; + const update = { + coupon_code, + discount_amount, + valid_till, + affiliate_discount_amount, + createdAt: formattedDate, + is_coupon_active, + }; + const options = { new: true }; + const saveData = await AffiliateModel.findByIdAndUpdate( + id, + update, + options + ); + + if (saveData) { + res.json("done"); + } else { + res.status(404).json("Affiliate not found"); + } + } catch (error) { + res.status(400).json({ + success: true, + message: "Coupon Already Exists", + }); + console.log(error); + } +}; + +//GET AFFILIATE FOR COUPON LIST +export const listAffiliateCoupon = async (req, res) => { + try { + let resArr = []; + const coupon = await AffiliateModel.find( + {}, + { + name: 1, + _id: 1, + is_coupon_active: 1, + mobile: 1, + } + ); + for (let i = 0; i < coupon.length; i++) { + if (coupon[i].is_coupon_active == false) { + resArr.push(coupon[i]); + } + } + // console.log(resArr); + res.status(200).json({ + success: true, + message: resArr, + }); + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went wrong!", + }); + } +}; +//EDIT COUPON +export const editCoupon = async (req, res) => { + const { + coupon_code, + discount_amount, + valid_till, + affiliate_discount_amount, + } = req.body; + + const updateFields = {}; + + // Add only the fields that are present in the request body to the updateFields object + if (coupon_code) updateFields.coupon_code = coupon_code; + if (discount_amount) updateFields.discount_amount = discount_amount; + if (valid_till) updateFields.valid_till = valid_till; + if (affiliate_discount_amount) + updateFields.affiliate_discount_amount = affiliate_discount_amount; + + try { + const saveData = await AffiliateModel.findByIdAndUpdate( + { _id: req.params.id }, + { $set: updateFields }, + { new: true } + ); + res.json({ + success: true, + message: "Coupon Edited Succesfully", + }); + } catch (error) { + res.status(400).json({ + success: false, + message: error.message + ? error.message + .split(":") + .splice(1) + .join(":") + .trim() + .split(":") + .splice(1) + .join(":") + .trim() + : "Error in Editing Coupon", + }); + } +}; +//SUSPEND COUPON +export const suspendCoupon = async (req, res) => { + const { id, is_coupon_active } = req.body; + try { + const saveData = await AffiliateModel.findByIdAndUpdate(id, { + is_coupon_active: is_coupon_active, + }); + res.status(200).json({ + success: true, + message: "Success", + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Coupon Doesn't Exists", + }); + } +}; + +//GET ONE COUPON +export const getOneCoupon = async (req, res) => { + if (req.params?.id) { + try { + const saveData = await AffiliateModel.findById(req.params.id); + + const resObj = { + name: saveData.name, + mobile: saveData.mobile, + coupon_code: saveData.coupon_code, + discount_amount: saveData.discount_amount, + valid_till: saveData.valid_till, + affiliate_discount_amount: saveData.affiliate_discount_amount, + }; + res.status(200).json({ + success: true, + message: resObj, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in getting Coupons", + }); + } + } +}; + +//Validate Coupon----------------------- +export const validateCoupon = async (req, res) => { + const { coupon } = req.params; + + if (!coupon) { + return res.status(400).json({ + success: false, + message: "Coupon code is required", + }); + } + + try { + // Find the coupon data in the database + const couponData = await AffiliateModel.findOne({ coupon_code: coupon }); + + if (!couponData) { + return res.status(404).json({ + success: false, + message: "Coupon not found", + }); + } + + const { valid_till, discount_amount, is_coupon_active } = couponData; + //Check whether Coupon is Active or not + if (!is_coupon_active) { + return res.status(404).json({ + success: false, + message: "Coupon Code Expired", + }); + } + // Check if the coupon is expired + const currentDate = new Date(); + const expirationDate = new Date(valid_till); + + if (currentDate > expirationDate) { + return res.status(400).json({ + success: false, + message: "Coupon has expired", + }); + } + + // If coupon is valid, return success response + res.status(200).json({ + success: true, + message: "Coupon is valid", + discount_amount: discount_amount, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Internal server error", + }); + } +}; + +//PAY & HISTORY--------------------- +export const usedCoupon = async (req, res) => { + // Retrieve orderId and coupon_code from request body or query parameters + const { orderId, coupon_code, userId } = req.body; + if (!orderId || !coupon_code || !userId) { + return res.status(400).json({ + success: false, + message: "Error in getting OrderId or Coupon", + }); + } + + // Validating Coupon + try { + const couponData = await AffiliateModel.findOne({ + coupon_code: coupon_code, + }); + + // Check if the coupon exists + if (!couponData) { + return res.status(404).json({ + success: false, + message: "Coupon not found", + }); + } + + const { + valid_till, + is_coupon_active, + total_earning, + is_affiliate_active, + coupon_claimed, + affiliate_discount_amount, + _id, + } = couponData; + // console.log(couponData); + if (!is_coupon_active || !is_affiliate_active) { + return res.status(404).json({ + success: false, + message: "Coupon Code Expired", + }); + } + const currentDate = new Date(); + const expirationDate = new Date(valid_till); + + if (currentDate > expirationDate) { + return res.status(400).json({ + success: false, + message: "Coupon has expired", + }); + } + + AffiliateModel.findByIdAndUpdate( + _id, + { + $inc: { total_earning: affiliate_discount_amount, coupon_claimed: 1 }, + $push: { + coupon_used_history: { + orderId: orderId, + userId: userId, + date: currentDate, + couponCode: coupon_code, + }, + }, + }, + { new: true } + ) + .then(() => { + res.status(200).json({ + success: true, + message: "Coupon add success", + }); + }) + .catch((error) => { + console.error(error); + res.status(500).json({ + success: false, + message: "Internal server error", + }); + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Internal server error", + }); + } +}; +//Get Coupon data for History +export const couponPayHistory = async (req, res) => { + if (req.params?.id) { + try { + const saveData = await AffiliateModel.findById(req.params.id).sort({ + updatedAt: -1, + }); + // console.log(saveData.coupon_used_history); + const resObj = { + coupon_used_history: saveData.coupon_used_history, + coupon_code: saveData.coupon_code, + }; + res.status(200).json({ + success: true, + message: resObj, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in getting History", + }); + } + } +}; diff --git a/resources/Affiliate&Coupon/Coupon/CouponRoute.js b/resources/Affiliate&Coupon/Coupon/CouponRoute.js new file mode 100644 index 0000000..bead939 --- /dev/null +++ b/resources/Affiliate&Coupon/Coupon/CouponRoute.js @@ -0,0 +1,76 @@ +import express from "express"; +import { + couponPayHistory, + createCoupon, + editCoupon, + getOneCoupon, + listAffiliateCoupon, + listAllCoupon, + suspendCoupon, + usedCoupon, + validateCoupon, +} from "./CouponController.js"; + +import { + isAuthenticatedUser, + authorizeRoles, +} from "../../../middlewares/auth.js"; + +const router = express.Router(); +router.get( + "/getall", + isAuthenticatedUser, + authorizeRoles("admin"), + listAllCoupon +); +router.patch( + "/create", + isAuthenticatedUser, + authorizeRoles("admin"), + createCoupon +); +router.get( + "/getaffiliate", + isAuthenticatedUser, + authorizeRoles("admin"), + listAffiliateCoupon +); +router.patch( + "/edit/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + editCoupon +); +router.get( + "/getone/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + getOneCoupon +); +router.get("/validcoupon/:coupon", validateCoupon); +router.patch( + "/suspend", + isAuthenticatedUser, + authorizeRoles("admin"), + suspendCoupon +); +router.patch( + "/paycoupon", + // isAuthenticatedUser, + usedCoupon +); +/* url:http://localhost:5000/api/v1/coupon/paycoupon + json structure to paycoupon , Need Header to be auth +{ + "userId":"random1", + "orderId":"12s213", + "coupon_code":"3000MONY" +}*/ +router.get( + "/history/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + couponPayHistory +); + +export default router;