| Current Path : /var/node/inatote/Inatote-Backend/orders/ |
| Current File : /var/node/inatote/Inatote-Backend/orders/orders-endpoint.js |
import {
UniqueConstraintError,
InvalidPropertyError,
RequiredParameterError
} from '../helpers/errors.js'
import makeHttpError from '../helpers/http-error.js'
import makeOrder from './order.js'
import validateHeaders from '../helpers/validate-headers.js'
import Joi from 'joi';
import FCM_Send from '../helpers/fcm_helper.js';
import makeUserList from '../users/user-list.js';
import makeDb from '../db/index.js';
import sendSms from '../helpers/twilio-helper.js';
import { createPayment } from "../services/square.js";
import sendEmail from '../helpers/twilio-send_grid.js';
import makeOrderMessage from "../orderStatus.js"
import { createHmac } from "crypto";
import config from "../config.js"
import fs from "fs";
import path from "path";
const __dirname = path.resolve();
import logger from "../services/logService.js";
const database = makeDb()
export default function makeordersEndpointHandler({ orderList }) {
return {
postOrder: postorder,
getOrder: getorder,
deleteOrder: deleteorder,
updateOrder: updateorder,
calculateCharges: calculateCharges,
confirmRider: confirmRider,
purchasedItem: purchasedItem,
acceptedOrder: acceptedOrder,
calculateProfit : calculateProfit,
payByBerkley: payByBerkley
}
function infoLogs(req , res) {
let msg = `\n*********${req.method}*${req.originalurl}********************************************************************************
\n ${JSON.stringify(req.body)} \n ${JSON.stringify(res)}
\n **********************************************************************************************************************************`;
console.log("msg" , msg);
logger.infologger.info(msg)
}
async function payByBerkley(req , res){
let msg = `\n*********${req.method}*${req.originalurl}********************************************************************************`
try{
var userList = makeUserList({ database });
let raw_data = req.body;
console.log("SING" , JSON.stringify(raw_data));
let hmac = createHmac('sha256' ,config.app.staggingSigningKey ).update(JSON.stringify(raw_data)).digest().toString('base64');
console.log("HMAC" , hmac);
fs.writeFileSync(path.join(__dirname,"logs","berkley.txt"), JSON.stringify(req.headers),"UTF8");
msg += `\nBody : ${JSON.stringify(raw_data)} \n headers : ${JSON.stringify(req.headers)} \n HMAC : ${hmac}`
logger.infologger.info(msg);
if(hmac == req.headers["X-BPS-Signature"] ){
throw 'X-BPS-Signature is not validated';
}
else{
if(raw_data && raw_data.account && raw_data.account.cardholder_id){
let user = await userList.findByCardHolderId(raw_data.account.cardholder_id );
if(!user){
throw 'User does not exists on this cardholder Id'
}
let order = await orderList.findOrderByRider(user._id , 'items collected');
console.log("ORDER", order);
if(order && order.subTotal != raw_data.billing.amount ){
throw 'Amount is not match to order amount'
}
else{
res.status(200).json({
"action": "approve",
"approval_code": "DEMO10"
})
}
}
}
}
catch(err){
console.log("ERR" , err);
logger.infologger.info(JSON.stringify(`ERROR: ${err}`));
res.status(400).json({
"action": "reject",
"approval_code": "DEMO10"
})
}
}
async function calculateProfit(req , res){
const id = req.query.id;
const role = req.query.role;
var earnings = 0;
console.log("ROLE" , role , id);
try{
if (role == "rider"){
let orders = await orderList.findOrderByRider(id ,"completed" );
//console.log("orders" , orders);
orders.forEach(f=>{
earnings += f.riderCharges;
})
}else if (role == "vendor"){
}else if(role == "admin"){
}
res.status(200).json({
earnings : earnings
})
}
catch(err){
console.log("err" , err);
res.status(400).json(err);
}
}
// todo
async function updateorder(req, res) {
const { id } = req.params || {}
//console.log(id)
let orderInfo = req.body
//console.log(orderInfo)
const result = await orderList.update(id, orderInfo)
res.status(200).json({
statusCode: 200,
msg: "Order has been updated"
})
}
async function acceptedOrder(req, res) {
try {
const { id } = req.params || {};
const {status} = req.query;
//console.log("id" , id);
const result = await orderList.findOrderByRider(id , status);
res.status(200).json({
status: true,
data: result
})
} catch (err) {
res.status(400).json({
status: false,
msg: err
})
}
}
// todo
async function postorder(req, res) {
let orderInfo = req.body
if (!orderInfo) {
return makeHttpError({
statusCode: 400,
errorMessage: 'Bad request. No POST body.'
})
}
if (typeof req.body === 'string') {
try {
orderInfo = JSON.parse(orderInfo)
} catch {
return makeHttpError({
statusCode: 400,
errorMessage: 'Bad request. POST body must be valid JSON.'
})
}
}
try {
var userList = makeUserList({ database });
let user = await userList.findById({ userId: orderInfo.userId });
if (!user) {
return res.status(400).json({
errorMessage: "User is not valid",
statusCode: 400
})
}
if (user && !user.customer_id) {
return res.status(400).json({
errorMessage: "Kindly add your payment details first",
statusCode: 400
})
}
if (!user.cardId) {
return res.status(400).json({
errorMessage: "Kindly add your card first",
statusCode: 400
})
}
orderInfo.orderNo = 1;
let orderNo = await orderList.findLastOrder();
if (orderNo && orderNo.length > 0) {
orderInfo.orderNo = parseInt(orderNo[0].orderNo) + 1
}
let heavyOrderFee = orderInfo.heavyOrderFee ? orderInfo.heavyOrderFee : 0;
let smallOrderFee = orderInfo.smallOrderFee ? orderInfo.smallOrderFee : 0;
let tip = orderInfo.tip ? orderInfo.tip : 0;
let itemsTax = orderInfo.itemsTax ? orderInfo.itemsTax : 0;
let serviceTax = orderInfo.serviceTax ? orderInfo.serviceTax : 0;
let totalPrice = parseFloat(orderInfo.deliveryService) + parseFloat(orderInfo.serviceFee) + parseFloat(heavyOrderFee) + parseFloat(smallOrderFee) + parseFloat(orderInfo.subTotal)
+ parseFloat(tip) + parseFloat(itemsTax) +parseFloat(serviceTax) ;
// if(orderInfo.totalPrice > totalPrice ){
// console.log("Totsal" ,orderInfo.totalPrice ,totalPrice );
// let priceDiff = orderInfo.totalPrice - totalPrice;
// if(priceDiff > 0.05){
// throw new Error ('total amount doesnot match to the amount that comes from request')
// }
// }
// else{
// let priceDiff = totalPrice - orderInfo.totalPrice ;
// if(priceDiff > 0.05){
// throw new Error ('total amount doesnot match to the amount that comes from request')
// }
// }
orderInfo.totalPrice = totalPrice;
let paymentData = {
customerId: user.customer_id,
cardId: orderInfo.nonce && orderInfo.nonce != null ? orderInfo.nonce : user.cardId,
amount: orderInfo.totalPrice
}
let payment = await createPayment(paymentData);
console.log("PAYMENT" , payment);
// return res.status(200).json({
// headers: {
// 'Content-Type': 'application/json'
// },
// statusCode: 203,
// data: payment,
// // paymentData
// })
if (!payment.id || payment.status != "COMPLETED" ) {
return res.status(400).json({
errorMessage: payment && payment.msg ? payment.msg : "order cannot proceed , beacause payment details are not valid",
statusCode: 400
})
}
orderInfo.paymentId = payment.id
const order = makeOrder(orderInfo);
const result = await orderList.add(order);
let orderDetail = await orderList.findById({ orderId: result.created.orderId });
if (orderDetail.user && orderDetail.user.mobileNumber ) {
sendSms({ toNumber: orderDetail.user.mobileNumber, message: makeOrderMessage(orderDetail.user.firstName ,"pending" ) })
}
if (orderDetail.user && orderDetail.user.emailAddress ) {
// sendEmail({ senderMail: "muhamadhasan043@gmail.com", templateId: "d-93621b1a8abe4addb921f713d3f1da93" });
let templateData = await parseTemplate(orderDetail)
sendEmail({ senderMail: orderDetail.user.emailAddress, templateId: "d-9c7e1c79349b4e51b54d65bcf9f62e4b" , templateData});
}
if (orderDetail.user && orderDetail.user.pushId) {
FCM_Send({ notificationTitle: "Order status", notificationBody: makeOrderMessage(orderDetail.user.firstName , 'pending') , token: user.pushId, payload: { payloadType: "orderChangeStatus", payloadData: orderDetail }, type: "client" });
}
return res.status(200).json({
headers: {
'Content-Type': 'application/json'
},
statusCode: 201,
data: orderDetail
})
} catch (e) {
console.log("e", e);
return res.status(400).json({
errorMessage: e.message,
statusCode:
e instanceof UniqueConstraintError
? 409
: e instanceof InvalidPropertyError ||
e instanceof RequiredParameterError
? 400
: 500
})
}
}
async function parseTemplate(data){
try {
let obj = {
images: data.productDetailsList.image,
subject: 'Order Confirmation',
"firstName": data.user.firstName,
"lastName": data.user.lastName,
"email": data.user.emailAddress,
"orderNo": data.orderNo,
vendor: data && data.vendor? {
name: data.vendor.vendorName,
address: data.vendor.vendorName,
phone: data.vendor.contacts.phone
}:{},
products:await parseOrderProducts(data.products , data.productDetailsList),
images: await parseOrderImages(data.products , data.productDetailsList),
total: data.totalPrice,
subTotal: data.subTotal,
serviceFee : parseFloat(data.serviceFee) + parseFloat(data.deliveryService) + parseFloat(data.riderCharges)
}
return obj
} catch (error) {
console.log("ERR" , error);
}
}
async function parseOrderProducts(productList , productDetails){
let details = [];
productList.map(f=>{
details = [...details ,
{
name: productDetails.filter(s=> s._id==f._id).productName,
sku: f.sku,
color: f.color,
size: f.size,
unitPrice : 150,
quantity: f.quantity,
total: 300
}
]
})
console.log("DETAILS" , details);
return details
}
async function parseOrderImages(productList , productDetails){
let images = [];
productList.forEach(f=>{
let img = productDetails.find(s=> f._id == s._id).imageURL;
console.log("IMG" , img);
images = [...images ,
config.app.imageURL + `${img[0].replace(" " , '%20')}`
]
})
console.log("IMAGES" , images);
return images
}
// todo
async function getorder(req, res) {
const { id } = req.params || {}
const { max, before, after, status, userId, cordinates, lat, long , riderId } = req.query || {}
let result;
// let promiseToken;
try{
let statusArr = req.query.status ? req.query.status.replace(/\[|\]/g, '').split(','): null;
console.log("statusArr" , statusArr);
//console.log("statusArr" ,statusArr , lat , long);
// if (statusArr.includes("pending") && req.query.lat == undefined && req.query.long == undefined) {
// return res.status(400).json({
// headers: {
// 'Content-Type': 'application/json'
// },
// status: false,
// msg: "Rider coordinates are required"
// })
// }
if (cordinates) {
result = await orderList.singleOrder(id);
let resp = {}
if (!result.riderLat && !result.riderLong) {
resp = {
_id : result._id,
riderLat: null,
riderLong: null,
orderStatus: result.orderStatus
}
} else {
resp = {
_id : result._id,
riderLat: result.riderLat,
riderLong: result.riderLong,
orderStatus: result.orderStatus
}
}
return res.status(200).json({
headers: {
'Content-Type': 'application/json'
},
statusCode: 200,
data: resp
})
}
else {
result = userId ? await orderList.findOrderByUser(userId) :
id ? await orderList.findById({ orderId: id })
: await orderList.getItems({ max, before, after, status : statusArr, cord: { lat, long } , riderId })
if (lat && long) {
var mainArr = [];
id ? result = [result] : null
console.log("RES agaya", result);
for (let i = 0; i < result.length; i++) {
if(result[i].vendor && result[i].vendor.location ){
let vendorCord = {
lat: result[i].vendor.location.coordinates[0],
long: result[i].vendor.location.coordinates[1]
}
try{
var rres = await orderList.calculateRiderCharges(vendorCord, { lat, long }, result[i].products.length, result[i]);
}
catch(err){
throw err
}
//console.log("RES" , rres);
if(rres){
mainArr = [...mainArr, rres];
}
}else{
//console.log("vendor not found" ,result[i].vendor[0] );
}
}
//console.log("mainArr", mainArr);
}
return res.status(200).json({
headers: {
'Content-Type': 'application/json'
},
statusCode: 200,
data: lat && long ? mainArr : result
})
}
}
catch(error){
console.log("ERR" , error.message);
return res.status(400).json({
headers: {
'Content-Type': 'application/json'
},
statusCode: 400,
error: error.message
})
}
}
// todo
async function deleteorder(req, res) {
const { id } = req.params || {}
const result = await orderList.remove({ orderId: id })
return res.status(200).json({
headers: {
'Content-Type': 'application/json'
},
statusCode: 200,
data: result.n == 1 ? "Order has been deleted" : "Something went wrong"
})
}
async function calculateCharges(req, res) {
try{
let { distance, itemCount, total, voucherCode } = req.body;
let response = {};
let configrations = await orderList.configrations();
let deliveryCharges = configrations.deliveryCharges
let deliveryRanges = Object.keys(deliveryCharges).reverse();
if (distance && distance > 0 && itemCount && itemCount > 0 && total && total > 0) {
if (distance >= (deliveryRanges[0] * 1000)) {
response["deliveryService"] = deliveryCharges[deliveryRanges[0]]
} else {
for (let index = 0; index < deliveryRanges.length; index++) {
//console.log("index", distance, [index] * 1000, distance <= (deliveryRanges[index] * 1000));
if (distance <= (deliveryRanges[index] * 1000)) {
//console.log("-----------------true");
response["deliveryService"] = deliveryCharges[deliveryRanges[index]]
}
}
}
response["serviceFee"] = configrations.serviceFee + ( parseInt(configrations.productMarkup) * itemCount);
// response["serviceFee"] = configrations.serviceFee ;
if (itemCount > 10) {
response["heavyOrderFee"] = configrations.heavyOrderFee;
} else if (total < 30) {
response["smallOrderFee"] = configrations.smallOrderFee;
}
let dkm = distance / 1000;
console.log("DKM" , dkm);
let fee = dkm * configrations.forDriver["km"];
fee += configrations.forDriver["base"];
fee += configrations.forDriver["item"] * itemCount;
response["riderCharges"] = fee;
if(configrations.tax > 0){
let allServicesAmount = parseFloat(response["serviceFee"])
+ parseFloat(response.heavyOrderFee ? response.heavyOrderFee : 0) + parseFloat(response.smallOrderFee ? response.smallOrderFee : 0) + parseFloat(response["deliveryService"])
console.log("allServicesAmount" ,allServicesAmount);
response["itemsTax"] = (configrations.tax / 100 ) * total;
response["serviceTax"] = (configrations.tax / 100 ) * allServicesAmount;
response["riderChargesTax"] = (configrations.tax / 100 ) * response["riderCharges"];
}
if (voucherCode && voucherCode.length > 0) {
let voucherData = await orderList.validateVoucher(voucherCode);
if (!voucherData) {
response["voucherErr"] = "Voucher code is not valid"
} else {
let check = true;
if (total < voucherData.amount) {
response["voucherErr"] = `Total amount must be greater than ${voucherData.amount} to avail this Offer`
check = false
}
if (voucherData.history.includes(req.user.userId)) {
response["voucherErr"] = `You already used this voucher code`;
check = false;
}
//console.log("c---------", new Date(voucherData.expiry), new Date(), new Date("2021-05-19"), new Date(voucherData.expiry) < new Date());
if (new Date(voucherData.expiry) < new Date()) {
response["voucherErr"] = `This voucher code has been expired`;
check = false
}
if (check) {
response["voucherDiscount"] = voucherData.price;
response["voucherId"] = voucherData._id;
// voucherData.history = [...voucherData.history , req.user.userId]
// await orderList.updateVoucher(voucherData)
}
}
//console.log("datta", voucherData);
}
res.status(200).json(response)
}
}
catch(err){
console.log("ERR" , err);
}
}
async function confirmRider(req, res) {
const data = req.body;
const schema = Joi.object({
orderId: Joi.string().alphanum().min(24).required(),
orderStatus: Joi.string().valid("rider assigned", "reached at store", "items collected" ,"items purchased", "delivery started" ,"reached at location", "completed").required(),
riderId: Joi.string(),
riderCharges : Joi.number()
})
//console.log("order", data);
var userList = makeUserList({ database });
try {
if (data.orderStatus == "rider assigned" && !data.riderId ) {
throw "RiderId is required for rider assigned status"
}
let value = await await schema.validateAsync(data);
let updateOrder = {
orderStatus: data.orderStatus
}
if (data.orderStatus == "rider assigned") {
const rider = await userList.findById({ userId: data.riderId });
if(!rider){
return res.status(400).json({
status: false,
msg: "Rider Id is not valid"
})
}
let orderDetails = await orderList.singleOrder(data.orderId);
//console.log("orderDetails", orderDetails);
if (orderDetails.riderId) {
if(orderDetails.riderId == data.riderId){
return res.status(400).json({
status: false,
msg: "You have already accepted this order"
})
}
else{
return res.status(400).json({
status: false,
msg: "This order has been asisgned to another rider"
})
}
} else {
// let heavyOrderFee = orderDetails.heavyOrderFee ? orderDetails.heavyOrderFee : 0;
// let smallOrderFee = orderDetails.smallOrderFee ? orderDetails.smallOrderFee : 0;
// let totalPrice = parseFloat(orderDetails.deliveryService) + parseFloat(orderDetails.serviceFee) + parseFloat(heavyOrderFee) + parseFloat(smallOrderFee) + parseFloat(orderDetails.subTotal) ;
//console.log("61c6e93cbb12371a54251093" , totalPrice);
// , totalPrice : totalPrice , riderCharges : parseFloat(data.riderCharges)
updateOrder = { ...updateOrder, riderId: data.riderId }
}
}
let orderHistory = {
action: data.orderStatus,
date: new Date()
}
let order = await orderList.singleOrder(data.orderId);
//console.log("order age" , order);
if(order.orderStatus == data.orderStatus){
return res.status(400).json({
status: false,
msg: "This order is already at this status"
})
}
// let updateOrder = await orderList.update(data.orderId, { orderStatus: data.orderStatus, riderId: data.riderId }, orderHistory);
// //console.log("updateOrder", updateOrder);
let updateOrders = await orderList.update(data.orderId, updateOrder, orderHistory);
//console.log("updateOrder", updateOrder);
// //console.log(order);
const database = makeDb();
const user = await userList.findById({ userId: order.userId })
// //console.log(user);
// Sending push notification
if (user.pushId) {
// FCM_Send({ notificationTitle: "Order status", notificationBody: data.orderStatus, token: user.pushId, payload: { payloadType: "orderChangeStatus", payloadData: data }, type: "rider" });
FCM_Send({ notificationTitle: "Order status", notificationBody: makeOrderMessage(user.firstName , 'rider assigned') , token: user.pushId, payload: { payloadType: "orderChangeStatus", payloadData: {orderId :order._id , orderStatus : updateOrder.orderStatus }}, type: "client" });
}
// Sending sms
if (user.mobileNumber ) {
sendSms({ toNumber: user.mobileNumber, message: makeOrderMessage(user.firstName , data.orderStatus) })
}
if (user.emailAddress && data.orderStatus == "completed") {
let orderDetail = await orderList.findById({ orderId: data.orderId });
let templateData = await parseTemplate(orderDetail)
sendEmail({ senderMail: orderDetail.user.emailAddress, templateId: "d-9c7e1c79349b4e51b54d65bcf9f62e4b" , templateData});
}
return res.status(200).json({
status: true,
msg: "status has been updated"
})
}
catch (err) {
//console.log("err", err);
res.status(400).json({
status: false,
msg: typeof err == "string" ? err : err.message ? err.message : err["details"][0]["message"]
})
}
}
async function purchasedItem(req, res) {
//console.log("files", req.file);
const data = req.body;
const schema = Joi.object({
orderId: Joi.string().required(),
productId: Joi.string().required(),
purchasedColor: Joi.string(),
purchasedSize: Joi.string(),
description: Joi.string(),
itemImage: Joi.string(),
status : Joi.string().valid('purchase' , 'refund'),
reason : Joi.string(),
quantity : Joi.number(),
sku : Joi.string().required()
})
try {
let value = await schema.validateAsync(data);
let orderData = await orderList.singleOrder(data.orderId);
let products = orderData.products;
var db = await database;
let productprice = 0;
if(["reached at store" , "items collected"].includes(orderData.orderStatus)){
if(data.status == "refund"){
// if(orderData.heavyOrderFee && orderData.heavyOrderFee > 0 ){
// if(orderData.products)
// }
var refundProducts = await db
.collection('refund')
.insertOne({
orderId : orderData._id,
productId : await db.makeId(data.productId),
reason : data.description,
});
var refundId = await db.makeId(refundProducts.insertedId)
}
var updated_products = []
products.forEach((element) => {
if (element.id == data.productId && element.sku == data.sku) {
//console.log("MACTH" , element.id);
productprice = element.price;
let obj = Object.assign(element, {
purchasedColor: data.purchasedColor,
purchasedSize: data.purchasedSize,
description: data.description,
itemImage: data.itemImage ,
status: data.status,
// refundId : refundProducts._id
})
data.status == "refund" ? obj = {...obj , refundId : refundId } : null;
//console.log("OBJ" , obj);
updated_products = [...updated_products, obj]
} else {
updated_products = [...updated_products, element]
}
});
orderData.products = updated_products;
if(data.status == "refund"){
let configrations = await db
.collection('configurations')
.findOne();
let orderQuantity = orderData.products.reduce((acc , cv)=> {return acc + cv.quantity} , 0 ) .quantity;
orderData.subTotal = orderData.subTotal - (parseFloat(productprice) * parseFloat(data.quantity));
orderData.riderCharges = parseFloat(orderData.riderCharges) - (0.5 * parseFloat(data.quantity) );
orderData.serviceFee -= (parseFloat(configrations.productMarkup) * parseFloat(data.quantity) );
if(orderQuantity == 10){
orderData.heavyOrderFee = 0;
}
orderData.totalPrice = parseFloat(orderInfo.deliveryService) + parseFloat(orderInfo.serviceFee) + parseFloat(heavyOrderFee) + parseFloat(smallOrderFee) + parseFloat(orderInfo.subTotal)
+ parseFloat(tip) + parseFloat(itemsTax) +parseFloat(serviceTax) ;
}
//console.log("productPrize" , productprice);
//console.log("orderData" ,orderData);
let updateOrder = await orderList.update(orderData._id, orderData, {});
res.status(200).json({
status: true,
data: updateOrder
});
}
else{
res.status(400).json({
status: false,
msg:"order status must be 'reached at store' or 'items collected'"
})
}
}
catch (err) {
//console.log("err", err);
res.status(400).json({
status: false,
msg: typeof err == "string" ? err : err.message ? err.message : err["details"][0]["message"]
})
}
}
}