//Get the data models const { Supplier, DeliveryBoy, profilePictureSupplier } = require("../models/supplier"); const { FriendRequest,RequestedBooking,RecurringRequestedBooking } = require("../models/supplier"); const { Tanker,Tankerbooking } = require("../models/tankers"); const { ProfilePicture, User } = require("../models/User"); const supplierController = require("../controllers/supplierController"); const customJwtAuth = require("../customAuthJwt"); const fastify = require("fastify")({ logger: true, //disableRequestLogging: true, genReqId(req) { // you get access to the req here if you need it - must be a synchronous function return uuidv4(); }, }); const moment = require('moment'); const fastifyEnv = require("fastify-env"); const boom = require("boom"); const emailValidator = require("email-validator"); const libphonenumberjs = require("libphonenumber-js"); fastify.register(customJwtAuth); const schema = { type: "object", required: ["PORT"], properties: { PORT: { type: "string", default: 3000, }, APIVERSION: { type: "string", default: "1.0.0", }, }, }; const options = { confKey: "config", // optional, default: 'config' schema: schema, // data: data // optional, default: process.env }; fastify.register(fastifyEnv, options).ready((err) => { if (err) console.error(err); console.log(fastify.config.PORT); // or fastify[options.confKey] // output: { PORT: 3000 } fastify.decorate("conf", { port: fastify.config.PORT, APIVERSION: fastify.config.APIVERSION, }); }); const apiversion = "1.0.0"; // fastify.register(require('fastify-cookie')) // fastify.register(require('fastify-session'), { // secret: 'my-secret-key', // cookie: { secure: true } // }); isSupplierFormUrlEncoded = (req) => { var isSupplierFormUrlEncoded = false; console.log("check is Supplier encoe url funtion"); // This iterates through the req headers object. // could not access req.headers.content-type due to the hyphen in the content-type key. // console.log(`${key}: ${value}`); for (const [key, value] of Object.entries(req.headers)) { if (`${key}` === "content-type") { if (`${value}` == "application/x-www-form-urlencoded") { // console.log( "data supplied is with content type," , `${value}`) // set isUserFormUrlEncoded value to true isSupplierFormUrlEncoded = true; // create user object with form variables . Password is used from the request object directly. s_data = { suppliername: req.body.suppliername, phone: req.body.phone, office_address: req.body.office_address, password: req.body.password, emails: [ { email: req.body.email, }, ], profile: { firstName: req.body.firstName, lastName: req.body.lastName, }, }; return { isSupplierFormUrlEncoded: isSupplierFormUrlEncoded, supplier: s_data, }; } else { return { isSupplierFormUrlEncoded: false, s_data: "" }; } } } }; fastify.register((fastify, opts, done) => { fastify.addContentTypeParser( "application/json", { parseAs: "buffer" }, function (_req, body, done) { try { done(null, body); } catch (error) { error.statusCode = 400; done(error, undefined); } } ); done(null); }); //Login Supplier Handler exports.loginSupplier = async (request, reply) => { loginObject = await supplierController.loginSupplier(request); console.log("loginObject...", loginObject); if (loginObject.same) { const phoneVerified = loginObject.supplier.phoneVerified; const oneTimePasswordSetFlag = loginObject.supplier.oneTimePasswordSetFlag; console.log( "oneTimePasswordSetFlag is ......", oneTimePasswordSetFlag, typeof oneTimePasswordSetFlag, typeof phoneVerified ); if (!phoneVerified) { reply.send({ simplydata: { error: false, phoneVerified: false, phone: loginObject.supplier.phone, oneTimePasswordSetFlag: oneTimePasswordSetFlag, message: "Please Verify your phone number", }, }); } else if (oneTimePasswordSetFlag) { reply.send({ simplydata: { error: false, phoneVerified: phoneVerified, phone: loginObject.supplier.phone, oneTimePasswordSetFlag: true, message: "Password must be reset", }, }); } else { const token = fastify.jwt.sign( { suppliername: loginObject.supplier.suppliername, supplierId: loginObject.supplier._id, roles: loginObject.supplier.profile.role, }, //expiresIn: expressed in seconds or a string describing a time span zeit/ms. Eg: 60, "2 days", "10h", "7d". //A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), //otherwise milliseconds unit is used by default ("120" is equal to "120ms"). { expiresIn: "30d" } ); console.log(token, "..token"); var arr = loginObject.supplier.profile.role; var arrayToString = JSON.stringify(Object.assign({}, arr)); // convert array to string var stringToJsonObject = JSON.parse(arrayToString); // convert string to json object var s_id = loginObject.supplier.supplierId; console.log(s_id, "supplierId"); var profilePicture = await profilePictureSupplier.findOne({ supplierId: s_id }); // request.session.set('supplierId', loginObject.supplier._id) if (!profilePicture) { reply.send({ simplydata: { error: false, apiversion: fastify.config.APIVERSION, access_token: token, email: loginObject.supplier.emails, phone: loginObject.supplier.phone, supplierId: loginObject.supplier.supplierId, suppliername: loginObject.supplier.suppliername, office_address: loginObject.supplier.profile.office_address, phoneVerified: loginObject.supplier.phoneVerified, oneTimePasswordSetFlag: loginObject.supplier.oneTimePasswordSetFlag, latitude: loginObject.supplier.latitude, longitude: loginObject.supplier.longitude, type: loginObject.supplier.profile.role, description : loginObject.supplier.description, typeasobj: stringToJsonObject, }, }); } if (profilePicture) { reply.send({ simplydata: { error: false, apiversion: fastify.config.APIVERSION, access_token: token, picture: profilePicture.picture, email: loginObject.supplier.emails, phone: loginObject.supplier.phone, supplierId: loginObject.supplier.supplierId, suppliername: loginObject.supplier.suppliername, office_address: loginObject.supplier.profile.office_address, phoneVerified: loginObject.supplier.phoneVerified, oneTimePasswordSetFlag: loginObject.supplier.oneTimePasswordSetFlag, latitude: loginObject.supplier.latitude, longitude: loginObject.supplier.longitude, description : loginObject.supplier.description, type: loginObject.supplier.profile.role, typeasobj: stringToJsonObject, }, }); } } } else { error = { simplydata: { error: true, code: 400, message: "Invalid SupplierId , Password supplied", }, }; reply.send(error); } }; //Login Delivery Handler exports.loginDeliveryBoy = async (request, reply) => { phone = request.body.phone; phoneVerificationCode = request.body.phoneVerificationCode; // check if user exists in the system. If user exists , display message that // username is not available console.log( "this is the phone and verification code", phone, phoneVerificationCode ); deliveryBoyExists = await DeliveryBoy.findOne({ phone: phone, phoneVerified: false, phoneVerificationCode: phoneVerificationCode, }); console.log(deliveryBoyExists); if (deliveryBoyExists) { // update the phoneVerified flag to true. const filter = { phone: phone, phoneVerificationCode: phoneVerificationCode, }; const update = { phoneVerified: true }; const doc = await DeliveryBoy.findOneAndUpdate(filter, update); updatedDeliveryBoy = await DeliveryBoy.findOne({ phone: phone }); } loginObject = await supplierController.loginDeliveryBoy(request); console.log("loginObject...", loginObject); if (loginObject.same) { const phoneVerified = loginObject.delivery.phoneVerified; const oneTimePasswordSetFlag = loginObject.delivery.oneTimePasswordSetFlag; console.log( "oneTimePasswordSetFlag is ......", oneTimePasswordSetFlag, typeof oneTimePasswordSetFlag, typeof phoneVerified ); if (!phoneVerified) { reply.send({ simplydata: { error: false, phoneVerified: false, phone: loginObject.delivery.phone, oneTimePasswordSetFlag: oneTimePasswordSetFlag, message: "Please Verify your phone number", }, }); } else if (oneTimePasswordSetFlag) { reply.send({ simplydata: { error: false, phoneVerified: phoneVerified, phone: loginObject.delivery.phone, oneTimePasswordSetFlag: true, message: "Password must be reset", }, }); } else { const token = fastify.jwt.sign( { deliveryBoyname: loginObject.delivery.name, deliveryBoyId: loginObject.delivery._id, }, //expiresIn: expressed in seconds or a string describing a time span zeit/ms. Eg: 60, "2 days", "10h", "7d". //A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), //otherwise milliseconds unit is used by default ("120" is equal to "120ms"). { expiresIn: "30d" } ); console.log(token, "..token"); var d_id = loginObject.delivery._id; console.log(d_id, "deliveryId"); var profilePicture = await ProfilePicture.findOne({ deliveryBoyId: d_id, }); // request.session.set('supplierId', loginObject.supplier._id) if (!profilePicture) { reply.send({ simplydata: { error: false, apiversion: fastify.config.APIVERSION, access_token: token, phone: loginObject.delivery.phone, deliveryBoyId: loginObject.delivery.deliveryBoyId, deliveryBoyname: loginObject.delivery.name, address: loginObject.delivery.address, phoneVerified: loginObject.delivery.phoneVerified, oneTimePasswordSetFlag: loginObject.delivery.oneTimePasswordSetFlag, supplierId: loginObject.delivery.supplierId, suppliername: loginObject.delivery.suppliername, longitude: loginObject.delivery.longitude, latitude: loginObject.delivery.latitude, }, }); } if (profilePicture) { reply.send({ simplydata: { error: false, apiversion: fastify.config.APIVERSION, access_token: token, picture: profilePicture.picture, phone: loginObject.delivery.phone, deliveryBoyId: loginObject.delivery.deliveryBoyId, deliveryBoyname: loginObject.delivery.name, address: loginObject.delivery.address, phoneVerified: loginObject.delivery.phoneVerified, oneTimePasswordSetFlag: loginObject.delivery.oneTimePasswordSetFlag, supplierId: loginObject.delivery.supplierId, suppliername: loginObject.delivery.suppliername, longitude: loginObject.delivery.longitude, latitude: loginObject.delivery.latitude, }, }); } } } else { error = { simplydata: { error: true, code: 400, message: "Invalid Details", }, }; reply.send(error); } }; exports.deliveryBoyVerifyPhone = async (req, reply) => { console.log("-------------------------------------------------"); try { phone = req.body.phone; phoneVerificationCode = req.body.phoneVerificationCode; // check if user exists in the system. If user exists , display message that // username is not available console.log( "this is the phone and verification code", phone, phoneVerificationCode ); deliveryBoyExists = await DeliveryBoy.findOne({ phone: phone, //phoneVerified: false, phoneVerificationCode: phoneVerificationCode, }); console.log(deliveryBoyExists); if (deliveryBoyExists) { // update the phoneVerified flag to true. const filter = { phone: phone, phoneVerificationCode: phoneVerificationCode, }; const update = { phoneVerified: true }; const doc = await DeliveryBoy.findOneAndUpdate(filter, update); updatedDeliveryBoy = await DeliveryBoy.findOne({ phone: phone }); if (updatedDeliveryBoy.phoneVerified) { loginObject = await supplierController.loginDeliveryBoy(req); console.log("loginObject...", loginObject); if (loginObject.same) { const phoneVerified = loginObject.delivery.phoneVerified; const oneTimePasswordSetFlag = loginObject.delivery.oneTimePasswordSetFlag; console.log( "oneTimePasswordSetFlag is ......", oneTimePasswordSetFlag, typeof oneTimePasswordSetFlag, typeof phoneVerified ); if (!phoneVerified) { reply.send({ simplydata: { error: false, phoneVerified: false, phone: loginObject.delivery.phone, oneTimePasswordSetFlag: oneTimePasswordSetFlag, message: "Please Verify your phone number", }, }); } else if (oneTimePasswordSetFlag) { reply.send({ simplydata: { error: false, phoneVerified: phoneVerified, phone: loginObject.delivery.phone, oneTimePasswordSetFlag: true, message: "Password must be reset", }, }); } else { const token = fastify.jwt.sign( { deliveryBoyname: loginObject.delivery.name, deliveryBoyId: loginObject.delivery._id, }, //expiresIn: expressed in seconds or a string describing a time span zeit/ms. Eg: 60, "2 days", "10h", "7d". //A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), //otherwise milliseconds unit is used by default ("120" is equal to "120ms"). { expiresIn: "30d" } ); console.log(token, "..token"); var d_id = loginObject.delivery._id; console.log(d_id, "deliveryId"); var profilePicture = await ProfilePicture.findOne({ deliveryBoyId: d_id, }); // request.session.set('supplierId', loginObject.supplier._id) if (!profilePicture) { reply.send({ simplydata: { error: false, apiversion: fastify.config.APIVERSION, access_token: token, phone: loginObject.delivery.phone, deliveryBoyId: loginObject.delivery.deliveryBoyId, deliveryBoyname: loginObject.delivery.name, address: loginObject.delivery.address, phoneVerified: loginObject.delivery.phoneVerified, oneTimePasswordSetFlag: loginObject.delivery.oneTimePasswordSetFlag, supplierId: loginObject.delivery.supplierId, suppliername: loginObject.delivery.suppliername, longitude: loginObject.delivery.longitude, latitude: loginObject.delivery.latitude, }, }); } if (profilePicture) { reply.send({ simplydata: { error: false, apiversion: fastify.config.APIVERSION, access_token: token, picture: profilePicture.picture, phone: loginObject.delivery.phone, deliveryBoyId: loginObject.delivery.deliveryBoyId, deliveryBoyname: loginObject.delivery.name, address: loginObject.delivery.address, phoneVerified: loginObject.delivery.phoneVerified, oneTimePasswordSetFlag: loginObject.delivery.oneTimePasswordSetFlag, supplierId: loginObject.delivery.supplierId, suppliername: loginObject.delivery.suppliername, longitude: loginObject.delivery.longitude, latitude: loginObject.delivery.latitude, }, }); } } } else { error = { simplydata: { error: true, code: 400, message: "Invalid Details", }, }; reply.send(error); } } }else { error = { armintatankdata: { error: true, code: 10005, message: "10005 - Verification code entered cannot be validated.", }, }; req.body.regError = error; reply.send(error); } } catch (err) { throw boom.boomify(err); } }; // Check if all the required fields are supplied by the user exports.fieldCheck = async (req, reply) => { try { s_Data = { suppliername: req.body.suppliername, emails: req.body.emails, password: req.body.password, services: { password: { bcrypt: req.body.password } }, phone: req.body.phone, profile: { firstName: req.body.firstName, lastName: req.body.lastName, contactNumber: req.body.phone, alternativeContactNumber: req.body.alternativeContactNumber, country: req.body.country, state: req.body.state, city: req.body.city, office_address: req.body.office_adress, zip: req.body.zip, }, }; var supplier = new Supplier(s_Data); console.log(supplier, "..supplier"); //password is not at the top level in the collection. password = req.body.password; // capture fields if data is sent via form instead of json encoding checkFormEncoding = isSupplierFormUrlEncoded(req); if (checkFormEncoding.isSupplierFormUrlEncoded) { suppliertobeInserted = checkFormEncoding.supplier; supplier.suppliername = suppliertobeInserted.suppliername; supplier.phone = suppliertobeInserted.phone; supplier.emails = suppliertobeInserted.emails; password = suppliertobeInserted.password; } console.log( "User to be inserted is ", supplier.suppliername, password, supplier.phone, supplier.profile ); // check if all rerquired fields are passed. if ( !( supplier.suppliername && password && supplier.phone && // user.profile.firstName && // user.profile.lastName && // user.profile.address1 && supplier.emails[0].email ) ) { console.log( supplier.suppliername, password, supplier.phone, // user.profile.firstName, // user.profile.lastName, supplier.emails[0].email ); // Required Fields are missing suppliedvalues = supplier.suppliername + " ," + password + " ," + supplier.phone + " ," + supplier.firstName + " ," + supplier.lastName + " ," + supplier.emails[0].email; error = { armintatankdata: { error: true, code: 10004, message: "10004 - suppliername, password, phone , firstname , lastname email city country state address1 and zip are required fields. Supplied values are " + suppliedvalues, }, }; req.body.regError = error; reply.send(error); } } catch (err) { throw boom.boomify(err); } }; exports.validatePhoneFormat = async (req, reply) => { try { var supplier = new Supplier(req.body); // check if user supplied phone is of the right format. // Handle if the user data is supplied via a url encoded form // capture fields if data is sent via form instead of json encoding checkFormEncoding = isSupplierFormUrlEncoded(req); console.log(checkFormEncoding); if (checkFormEncoding.isSupplierFormUrlEncoded) { suppliertobeInserted = checkFormEncoding.supplier; supplier.suppliername = suppliertobeInserted.suppliername; supplierser.firstName = suppliertobeInserted.firstName; supplier.lastName = suppliertobeInserted.lastName; supplier.phone = suppliertobeInserted.phone; supplier.emails = suppliertobeInserted.emails; } if (supplier) { const phoneNumber = libphonenumberjs.parsePhoneNumber(supplier.phone); if (phoneNumber) { // access returned collection if (!phoneNumber.isValid()) { error = { armintatankdata: { error: true, code: 10002, message: "10002 - Phone # " + supplier.phone + " is not a valid phone number", }, }; req.body.regError = error; reply.status(406).send(error); } } } } catch (err) { throw boom.boomify(err); } }; exports.verifySupplier = async (req, reply) => { try { var supplier = new Supplier(req.body); // Handle if the user data is supplied via a url encoded form // capture fields if data is sent via form instead of json encoding checkFormEncoding = isSupplierFormUrlEncoded(req); if (checkFormEncoding.isSupplierFormUrlEncoded) { suppliertobeInserted = checkFormEncoding.supplier; supplier.suppliername = suppliertobeInserted.suppliername; supplier.firstName = suppliertobeInserted.firstName; supplier.lastName = suppliertobeInserted.lastName; supplierr.phone = suppliertobeInserted.phone; supplier.emails = suppliertobeInserted.emails; } phone = supplier.phone; supplierpass = req.body.password; // check if user exists in the system. If user exists , display message that // phone number is not available supplierExists = await Supplier.findOne({ phone: phone }); if (supplierExists) { // return user exists message error = { armintatankdata: { error: true, code: 10001, message: "10001 - Phone " + supplierExists.phone + " is not available. please use a different phone number", }, }; req.body.regError = error; reply.send(error); } } catch (err) { throw boom.boomify(err); } }; exports.validateEmailFormat = async (req, reply) => { try { var supplier = new Supplier(req.body); // Handle if the user data is supplied via a url encoded form // capture fields if data is sent via form instead of json encoding checkFormEncoding = isSupplierFormUrlEncoded(req); if (checkFormEncoding.isSupplierFormUrlEncoded) { suppliertobeInserted = checkFormEncoding.supplier; supplier.suppliername = suppliertobeInserted.suppliername; supplier.firstName = suppliertobeInserted.firstName; supplier.lastName = suppliertobeInserted.lastName; supplierr.phone = suppliertobeInserted.phone; supplier.emails = suppliertobeInserted.emails; } supplieremail = await supplier.emails[0].email; // check if user supplied email is of the right format. if (supplier) { const isValidEmail = emailValidator.validate(supplieremail.trim()); if (!isValidEmail) { // Return email invalid format message error = { armintatankdata: { error: true, code: 10003, message: "10003 - Email " + supplier.emails[0].email + " is not a valid email", }, }; req.body.regError = error; reply.send(error); } } } catch (err) { throw boom.boomify(err); } }; exports.logoutsupplier = async (request, reply) => { // request.session.delete(); // // send response to clear token // reply.send({ message: 'Successfully logged out' }) const invalidatedTokens = {}; const accessToken = request.headers.authorization && request.body.access_token; invalidatedTokens[accessToken] = true; // // localStorage.removeItem(invalidatedTokens[accessToken]) reply.send({ message: "Logout successful" }); }; exports.verifyPhone = async (req, reply) => { console.log("-------------------------------------------------"); try { phone = req.body.phone; phoneVerificationCode = req.body.phoneVerificationCode; // check if user exists in the system. If user exists , display message that // username is not available console.log( "this is the phone and verification code", phone, phoneVerificationCode ); supplierExists = await Supplier.findOne({ phone: phone, phoneVerified: false, phoneVerificationCode: phoneVerificationCode, }); console.log(supplierExists); if (supplierExists) { // update the phoneVerified flag to true. const filter = { phone: phone, phoneVerificationCode: phoneVerificationCode, }; const update = { phoneVerified: true }; const doc = await Supplier.findOneAndUpdate(filter, update); updatedSupplier = await Supplier.findOne({ phone: phone }); if (updatedSupplier.phoneVerified) { reply.send('{"armintatankdata":{"error":false,"verified": true}}'); } else { error = { armintatankdata: { error: true, code: 10005, message: "10005 - Verification code entered cannot be validated.", }, }; req.body.regError = error; reply.send(error); } } else { error = { armintatankdata: { error: true, code: 10005, message: "10005 - Verification code entered cannot be validated.", }, }; req.body.regError = error; reply.send(error); } } catch (err) { throw boom.boomify(err); } }; // exports.deliveryBoyVerifyPhone = async (req, reply) => { // console.log("-------------------------------------------------"); // try { // phone = req.body.phone; // phoneVerificationCode = req.body.phoneVerificationCode; // // check if user exists in the system. If user exists , display message that // // username is not available // console.log( // "this is the phone and verification code", // phone, // phoneVerificationCode // ); // deliveryBoyExists = await DeliveryBoy.findOne({ // phone: phone, // phoneVerified: false, // phoneVerificationCode: phoneVerificationCode, // }); // console.log(deliveryBoyExists); // if (deliveryBoyExists) { // // update the phoneVerified flag to true. // const filter = { // phone: phone, // phoneVerificationCode: phoneVerificationCode, // }; // const update = { phoneVerified: true }; // const doc = await DeliveryBoy.findOneAndUpdate(filter, update); // updatedDeliveryBoy = await DeliveryBoy.findOne({ phone: phone }); // if (updatedDeliveryBoy.phoneVerified) { // reply.send('{"armintatankdata":{"error":false,"verified": true}}'); // } else { // error = { // armintatankdata: { // error: true, // code: 10005, // message: "10005 - Verification code entered cannot be validated.", // }, // }; // req.body.regError = error; // reply.send(error); // } // } // } catch (err) { // throw boom.boomify(err); // } // }; //delete selected deliveryboy exports.deleteDeliveryBoy = async (req, reply) => { try { var supplierId = req.params.supplierId; var phone = req.query.phone; const delivery = await DeliveryBoy.findOneAndDelete({ phone: phone, supplierId: supplierId, }); reply.send({ status_code: 200, data: delivery }); // return tank; } catch (err) { throw boom.boomify(err); } }; //update selected Delivery Boy Details exports.updateDeliveryBoy = async (req, reply) => { try { var supplierId = req.params.supplierId; var phone = req.query.phone; const delivery = req.body; const { ...updateData } = delivery; const update = await DeliveryBoy.findOneAndUpdate( { phone: phone, supplierId: supplierId }, updateData, { new: true } ); console.log(update); //return update; reply.send({ status_code: 200, data: update }); } catch (err) { throw boom.boomify(err); } }; // Get current supplier exports.getCurrentSupplier = async (req, reply) => { try { const supplierId = req.body.supplierId; const suppliers = await Supplier.findOne({ supplierId: supplierId }); return suppliers; } catch (err) { throw boom.boomify(err); } }; // Get all suppliers // exports.getSuppliers = async (req, reply) => { // const limit = parseInt(req.query.limit) || 100; // const page = parseInt(req.query.page) || 1; // const startindex = (page - 1) * limit; // try { // await Supplier.find() // .limit(limit) // .skip(startindex) // .exec() // .then((docs) => { // reply.send({ status_code: 200, data: docs, count: docs.length }); // }) // .catch((err) => { // console.log(err); // reply.send({ error: err }); // }); // } catch (err) { // throw boom.boomify(err); // } // }; exports.getSuppliers = async (req, reply) => { const customerId = req.params.customerId; const { type_of_water, capacity: requestedCapacityStr, quantity: requestedQuantityStr, date, time, price_from, price_to, radius_from, radius_to, rating_from, rating_to } = req.body; const parseCapacity = (value) => parseFloat((value || "0").toString().replace(/,/g, "")); const parsePrice = (value) => parseInt((value || "0").toString().replace(/,/g, "")); const requestedCapacity = parseCapacity(requestedCapacityStr); const requestedQuantity = parseInt(requestedQuantityStr || "0"); const totalRequiredCapacity = requestedCapacity * requestedQuantity; const priceFrom = parsePrice(price_from); const priceTo = parsePrice(price_to); try { const customerData = await User.findOne({ customerId }); const favorateSuppliers = customerData?.favorate_suppliers || []; const tankerBookings = await Tankerbooking.find({ date }); const bookedTankerSet = new Set( tankerBookings.map(booking => `${booking.supplierId}_${booking.tankerName}`) ); const tankerQuery = {}; if (type_of_water && type_of_water.trim() !== "") { tankerQuery.typeofwater = type_of_water; } let tankers = await Tanker.find(tankerQuery); const isValidPrice = (val) => { const num = parseInt(val); return !isNaN(num) && isFinite(num); }; if (isValidPrice(price_from) && isValidPrice(price_to)) { tankers = tankers.filter(tanker => { const tankerPrice = parsePrice(tanker.price); return tankerPrice >= priceFrom && tankerPrice <= priceTo; }); } tankers = tankers.filter(tanker => { const key = `${tanker.supplierId}_${tanker.tankerName}`; return !bookedTankerSet.has(key); }); const supplierTankerMap = {}; for (let tanker of tankers) { if (!supplierTankerMap[tanker.supplierId]) { supplierTankerMap[tanker.supplierId] = []; } supplierTankerMap[tanker.supplierId].push(tanker); } const qualifiedSuppliers = []; for (let [supplierId, supplierTankers] of Object.entries(supplierTankerMap)) { const totalAvailableCapacity = supplierTankers.reduce( (sum, t) => sum + parseCapacity(t.capacity), 0 ); if (requestedCapacity > 0 && requestedQuantity > 0) { if (totalAvailableCapacity < totalRequiredCapacity) { continue; } } qualifiedSuppliers.push({ supplierId, tankers: supplierTankers }); } const suppliers = []; for (let supplierObj of qualifiedSuppliers) { const supplierData = await Supplier.findOne({ supplierId: supplierObj.supplierId }); const friendRequest = await FriendRequest.findOne({ customerId: customerId, supplierId: supplierObj.supplierId }); const isConnected = friendRequest && friendRequest.status === "accepted"; const isFavorite = favorateSuppliers.includes(supplierObj.supplierId); // 🔥 Now check RequestedBooking collection const requestedBookingRecord = await RequestedBooking.findOne({ customerId: customerId, "requested_suppliers.supplierId": supplierObj.supplierId }); let requestedBooking = { status: false }; if (requestedBookingRecord) { requestedBooking = { status: true, time: requestedBookingRecord.time }; } suppliers.push({ supplier: supplierData, tankers: supplierObj.tankers, isConnected: isConnected, isFavorite: isFavorite, requestedBooking: requestedBooking }); } reply.send({ status_code: 200, suppliers }); } catch (err) { console.error(err); reply.send({ status_code: 500, message: "Something went wrong", error: err.message }); } }; // GET SUPPLIERS (simple): only needs customerId; no tanker checks exports.getSupplierswithoutbooking = async (req, reply) => { try { const { customerId } = req.params; // 1) Load customer to read favorites const customer = await User.findOne({ customerId }, { favorate_suppliers: 1, _id: 0 }); if (!customer) { return reply.code(404).send({ status_code: 404, message: "Customer not found" }); } const favoriteSet = new Set(customer.favorate_suppliers || []); // 2) Load all suppliers const suppliers = await Supplier.find({}); // add projection if you want to slim payload // 3) Find accepted connections for this customer across ALL suppliers in one go const supplierIds = suppliers.map(s => s.supplierId).filter(Boolean); const acceptedReqs = await FriendRequest.find( { customerId, supplierId: { $in: supplierIds }, status: "accepted" }, { supplierId: 1, _id: 0 } ); const connectedSet = new Set(acceptedReqs.map(r => r.supplierId)); // 4) Build response const result = suppliers.map(s => ({ supplier: s, isFavorite: favoriteSet.has(s.supplierId), isConnected: connectedSet.has(s.supplierId), })); return reply.send({ status_code: 200, suppliers: result }); } catch (err) { console.error(err); return reply.code(500).send({ status_code: 500, message: "Something went wrong", error: err.message, }); } }; exports.createRequestedBooking = async (req, reply) => { const { customerId, type_of_water, capacity, quantity, date, time, requested_suppliers } = req.body; const parseCapacity = (value) => parseFloat((value || "0").toString().replace(/,/g, "")); const requestedCapacity = parseCapacity(capacity); const requestedQuantity = parseInt(quantity || "0"); const totalRequiredCapacity = requestedCapacity * requestedQuantity; try { const requestedBooking = new RequestedBooking({ customerId, type_of_water, capacity, quantity, total_required_capacity: totalRequiredCapacity, date, time, requested_suppliers, // ✅ already contains supplierId, quoted_amount, custom_field status: "pending" }); await requestedBooking.save(); reply.send({ status_code: 200, message: "Requested booking created successfully", data: requestedBooking }); } catch (err) { console.error(err); reply.send({ status_code: 500, message: "Something went wrong while saving", error: err.message }); } } // Get single user by ID exports.getSingleSupplier = async (req, reply) => { try { const supplierId = req.params.supplierId; const supplier = await Supplier.findOne({ supplierId: supplierId }); return supplier; } catch (err) { throw boom.boomify(err); } }; // exports.getConnectedSuppliers = async (req, reply) => { // const limit = parseInt(req.query.limit) || 100; // const page = parseInt(req.query.page) || 1; // const startindex = (page - 1) * limit; // const customerId = req.params.customerId; // Assuming you have already authenticated the user and stored their ID in the request object // try { // const friendRequests = await FriendRequest.find({ // customerId, // status: "accepted", // }); // // console.log(friendRequests,customerId) // const supplierIdsToInclude = friendRequests.map( // (request) => request.supplierId // ); // await Supplier.find({ supplierId: { $in: supplierIdsToInclude } }) // .limit(limit) // .skip(startindex) // .exec() // .then((docs) => { // reply.send({ status_code: 200, data: docs, count: docs.length }); // }) // .catch((err) => { // console.log(err); // reply.send({ error: err }); // }); // } catch (err) { // throw boom.boomify(err); // } // }; exports.getConnectedSuppliers = async (req, reply) => { const limit = parseInt(req.query.limit) || 100; const page = parseInt(req.query.page) || 1; const startindex = (page - 1) * limit; const customerId = req.params.customerId; try { // Get user's favorite suppliers const user = await User.findOne({ customerId }, 'favorate_suppliers'); const favorateSuppliers = user?.favorate_suppliers || []; // Get accepted friend requests const friendRequests = await FriendRequest.find({ customerId, status: "accepted", }); const supplierIdsToInclude = friendRequests.map(req => req.supplierId); // Get suppliers const suppliers = await Supplier.find({ supplierId: { $in: supplierIdsToInclude } }) .limit(limit) .skip(startindex) .exec(); const supplierIds = suppliers.map(s => s.supplierId); // Get profile pictures const profilePictures = await profilePictureSupplier.find({ supplierId: { $in: supplierIds } }); // Construct final response const data = suppliers.map((supplier) => { const profilePicture = profilePictures.find( (pic) => pic.supplierId === supplier.supplierId ); const isFavorate = favorateSuppliers.includes(supplier.supplierId); return { ...supplier.toObject(), picture: profilePicture ? profilePicture.picture : null, favorate: isFavorate, }; }); reply.send({ status_code: 200, data, count: data.length }); } catch (err) { throw boom.boomify(err); } }; // exports.getPendingSuppliers = async (req, reply) => { // const limit = parseInt(req.query.limit) || 100; // const page = parseInt(req.query.page) || 1; // const startindex = (page - 1) * limit; // const customerId = req.params.customerId; // Assuming you have already authenticated the user and stored their ID in the request object // try { // const friendRequests = await FriendRequest.find({ // customerId, // status: ["pending"], // }); // // console.log(friendRequests,customerId) // const supplierIdsToInclude = friendRequests.map( // (request) => request.supplierId // ); // console.log(supplierIdsToInclude, "SUPLIERINCLUDE"); // const timestamps = friendRequests.map(request => request.timestamp); // console.log(timestamps, "timestamps"); // await Supplier.find({ supplierId: { $in: supplierIdsToInclude } }) // .limit(limit) // .skip(startindex) // .exec() // .then((docs) => { // const customerDataWithTimestamp = docs.map((doc, index) => ({ // ...doc._doc, // timestamp: timestamps[index], // })); // reply.send({ // status_code: 200, // data: customerDataWithTimestamp, // count: docs.length, // }); // //reply.send({ status_code: 200, data: docs, count: docs.length }); // }) // .catch((err) => { // console.log(err); // reply.send({ error: err }); // }); // } catch (err) { // throw boom.boomify(err); // } // }; exports.getPendingSuppliers = async (req, reply) => { const limit = parseInt(req.query.limit) || 100; const page = parseInt(req.query.page) || 1; const startindex = (page - 1) * limit; const customerId = req.params.customerId; // Assuming you have already authenticated the user and stored their ID in the request object try { const friendRequests = await FriendRequest.find({ customerId, status: ["pending"], }); const supplierIdsToInclude = friendRequests.map( (request) => request.supplierId ); const timestamps = friendRequests.map((request) => request.timestamp); const suppliers = await Supplier.find({ supplierId: { $in: supplierIdsToInclude }, }) .limit(limit) .skip(startindex) .exec(); const supplierIds = suppliers.map((supplier) => supplier.supplierId); const profilePictures = await profilePictureSupplier.find({ supplierId: { $in: supplierIds }, }).exec(); const data = suppliers.map((supplier, index) => { const profilePicture = profilePictures.find( (picture) => picture.supplierId === supplier.supplierId ); return { ...supplier.toObject(), timestamp: timestamps[index], picture: profilePicture ? profilePicture.picture : null, }; }); reply.send({ status_code: 200, data, count: data.length }); } catch (err) { throw boom.boomify(err); } }; // exports.getRejectSuppliers = async (req, reply) => { // const limit = parseInt(req.query.limit) || 100; // const page = parseInt(req.query.page) || 1; // const startindex = (page - 1) * limit; // const customerId = req.params.customerId; // Assuming you have already authenticated the user and stored their ID in the request object // try { // const friendRequests = await FriendRequest.find({ // customerId, // status: ["rejected"], // }); // // console.log(friendRequests,customerId) // const supplierIdsToInclude = friendRequests.map( // (request) => request.supplierId // ); // // console.log(supplierIdsToInclude) // await Supplier.find({ supplierId: { $in: supplierIdsToInclude } }) // .limit(limit) // .skip(startindex) // .exec() // .then((docs) => { // reply.send({ status_code: 200, data: docs, count: docs.length }); // }) // .catch((err) => { // console.log(err); // reply.send({ error: err }); // }); // } catch (err) { // throw boom.boomify(err); // } // }; exports.getRejectSuppliers = async (req, reply) => { const limit = parseInt(req.query.limit) || 100; const page = parseInt(req.query.page) || 1; const startindex = (page - 1) * limit; const customerId = req.params.customerId; // Assuming you have already authenticated the user and stored their ID in the request object try { const friendRequests = await FriendRequest.find({ customerId, status: ["rejected"], }); const supplierIdsToInclude = friendRequests.map( (request) => request.supplierId ); const suppliers = await Supplier.find({ supplierId: { $in: supplierIdsToInclude }, }) .limit(limit) .skip(startindex) .exec(); const supplierIds = suppliers.map((supplier) => supplier.supplierId); const profilePictures = await profilePictureSupplier.find({ supplierId: { $in: supplierIds }, }).exec(); const data = suppliers.map((supplier) => { const profilePicture = profilePictures.find( (picture) => picture.supplierId === supplier.supplierId ); return { ...supplier.toObject(), picture: profilePicture ? profilePicture.picture : null, }; }); reply.send({ status_code: 200, data, count: data.length }); } catch (err) { throw boom.boomify(err); } }; exports.getPendingCustomers = async (req, reply) => { const limit = parseInt(req.query.limit) || 100; const page = parseInt(req.query.page) || 1; const startindex = (page - 1) * limit; const supplierId = req.params.supplierId; try { const friendRequests = await FriendRequest.find({ supplierId, status: ["pending"], }); const supplierIdsToInclude = friendRequests.map( (request) => request.customerId ); // const timestamps = friendRequests.map(request => // moment(request.timestamp, "DD-MM-YYYYTHH:mm:ss.SSSZ").format("DD-MM-YYYY hh:mm:ss") // ); const timestamps = friendRequests.map(request => request.timestamp); console.log(timestamps, "timestamps"); await User.find({ customerId: { $in: supplierIdsToInclude } }) .limit(limit) .skip(startindex) .exec() .then((docs) => { const customerDataWithTimestamp = docs.map((doc, index) => ({ ...doc._doc, timestamp: timestamps[index], })); reply.send({ status_code: 200, data: customerDataWithTimestamp, count: docs.length, }); }) .catch((err) => { console.log(err); reply.send({ error: err }); }); } catch (err) { throw boom.boomify(err); } }; exports.getRejectCustomers = async (req, reply) => { const limit = parseInt(req.query.limit) || 100; const page = parseInt(req.query.page) || 1; const startindex = (page - 1) * limit; const supplierId = req.params.supplierId; // Assuming you have already authenticated the user and stored their ID in the request object try { const friendRequests = await FriendRequest.find({ supplierId, status: ["rejected"], }); console.log(friendRequests, supplierId, "su...."); const supplierIdsToInclude = friendRequests.map( (request) => request.customerId ); console.log(supplierIdsToInclude, "supplierIdsToInclude.."); await User.find({ customerId: { $in: supplierIdsToInclude } }) .limit(limit) .skip(startindex) .exec() .then((docs) => { reply.send({ status_code: 200, data: docs, count: docs.length }); }) .catch((err) => { console.log(err); reply.send({ error: err }); }); } catch (err) { throw boom.boomify(err); } }; exports.getconnectedCustomers = async (req, reply) => { const limit = parseInt(req.query.limit) || 100; const page = parseInt(req.query.page) || 1; const startindex = (page - 1) * limit; const supplierId = req.params.supplierId; // Assuming you have already authenticated the user and stored their ID in the request object try { const friendRequests = await FriendRequest.find({ supplierId, status: ["accepted"], }); console.log(friendRequests, supplierId, "su...."); const supplierIdsToInclude = friendRequests.map( (request) => request.customerId ); console.log(supplierIdsToInclude, "supplierIdsToInclude.."); await User.find({ customerId: { $in: supplierIdsToInclude } }) .limit(limit) .skip(startindex) .exec() .then((docs) => { reply.send({ status_code: 200, data: docs, count: docs.length }); }) .catch((err) => { console.log(err); reply.send({ error: err }); }); } catch (err) { throw boom.boomify(err); } }; // exports.uploadProfilePicture = async (req, reply) => { // try { // const supplierId = req.params.supplierId; // const picture = req.body.picture; // let profilePicture = await profilePictureSupplier.findOne({ supplierId }); // console.log(profilePicture,"profile===") // if (!profilePicture) { // profilePicture = new profilePictureSupplier({ // supplierId, // picture, // }); // } else { // profilePicture.picture = picture; // } // await profilePicture.save(); // reply.send({ message: 'Profile picture uploaded successfully' }); // } catch (error) { // reply.status(500).send({ error: error.message }); // } // }; // const multer = require('multer'); // const fs = require('fs'); // // Multer storage configuration // const storage = multer.diskStorage({ // destination: function (req, file, cb) { // // Specify the destination folder for storing uploaded files // cb(null, './uploads'); // }, // filename: function (req, file, cb) { // // Generate a unique filename for the uploaded file // cb(null, file.originalname); // }, // }); // // Multer upload configuration // const upload = multer({ storage: storage }).single('picture'); // // Handler for uploading profile picture // exports.uploadProfilePicture = async (req, res) => { // try { // upload(req, res, async (err) => { // if (err) { // return res.status(400).send({ error: 'Failed to upload profile picture' }); // } // const supplierId = req.params.supplierId; // const picture = req.file.filename; // let profilePicture = await ProfilePictureSupplier.findOne({ supplierId }); // if (!profilePicture) { // profilePicture = new ProfilePictureSupplier({ // supplierId, // picture, // }); // } else { // profilePicture.picture = picture; // } // await profilePicture.save(); // res.send({ message: 'Profile picture uploaded successfully' }); // }); // } catch (error) { // res.status(500).send({ error: error.message }); // } // }; // const { Storage } = require('@google-cloud/storage'); // const storage = new Storage({ // keyFilename :`/home/bhaskar/Downloads/arminta-tank-0f492875da39.json`, // projectId : `arminta-tank` // }); // const bucketName = 'armintaprofile_picture'; // exports.uploadProfilePicture = async (req, res) => { // try { // const supplierId = req.params.supplierId; // const file = req.body.picture; // // Check if the required properties are present // if (!file) { // return res.status(400).send({ error: 'No picture file provided' }); // } // // Upload the file to the GCS bucket // const bucket = storage.bucket(bucketName); // const fileName = `${supplierId}/${file.originalname}`; // const fileOptions = { // metadata: { // contentType: 'multipart/form-data', // }, // }; // await bucket.upload(file.path, { // destination: `armintaprofile_picture/${fileName}`, // ...fileOptions, // }); // const imageUrl = `https://storage.googleapis.com/${bucketName}/${fileName}`; // console.log(imageUrl); // let profilePicture = await ProfilePictureSupplier.findOne({ supplierId }); // console.log("profilePicture", profilePicture); // if (!profilePicture) { // profilePicture = new ProfilePictureSupplier({ // supplierId, // picture: imageUrl, // }); // } else { // profilePicture.picture = imageUrl; // } // await profilePicture.save(); // // Delete the temporary file after uploading to GCS // fs.unlinkSync(file.path); // res.send({ message: 'Profile picture uploaded successfully' }); // } catch (error) { // res.status(500).send({ error: error.message }); // } // }, // const { Storage } = require('@google-cloud/storage'); // const storage = new Storage({ // keyFilename: '/home/bhaskar/Downloads/arminta-tank-0f492875da39.json', // projectId: 'arminta-tank', // }); // const bucketName = 'armintaprofile_picture'; // exports.uploadProfilePicture = async (req, res) => { // try { // const supplierId = req.params.supplierId; // // Check if the required properties are present // if (!req.files || !req.files.picture) { // return res.status(400).send({ error: 'No picture file provided' }); // } // const file = req.files.picture; // console.log("file", file) // // Upload the file to the GCS bucket // const bucket = storage.bucket(bucketName); // const fileName = `${supplierId}/${file.name}`; // const fileOptions = { // metadata: { // contentType: file.mimetype, // }, // }; // await bucket.upload(file.tempFilePath, { // destination: fileName, // ...fileOptions, // }); // const imageUrl = `https://storage.googleapis.com/${bucketName}/${fileName}`; // console.log(imageUrl); // let profilePicture = await ProfilePictureSupplier.findOne({ supplierId }); // console.log('profilePicture', profilePicture); // if (!profilePicture) { // profilePicture = new ProfilePictureSupplier({ // supplierId, // picture: imageUrl, // }); // } else { // profilePicture.picture = imageUrl; // } // await profilePicture.save(); // // Delete the temporary file after uploading to GCS // fs.unlinkSync(file.tempFilePath); // res.send({ message: 'Profile picture uploaded successfully' }); // } catch (error) { // res.status(500).send({ error: error.message }); // } // }; const { Storage } = require('@google-cloud/storage'); const fs = require('fs'); const storage = new Storage({ keyFilename: '/home/bhaskar/Downloads/arminta-tank-3c665db761bc.json', projectId: 'arminta-tank', }); const bucketName = 'armintaprofile_picture'; // exports.uploadProfilePicture = async (req, res) => { // try { // const supplierId = req.body.supplierId; // // Check if the required properties are present // if (!req.files || !req.files.picture) { // return res.status(400).send({ error: 'No picture file provided' }); // } // const file = `/home/bhaskar/Desktop/download.jpeg`; // console.log("file", file) // console.log("supplierId", supplierId) // // Upload the file to the GCS bucket // const bucket = storage.bucket(bucketName); // const fileName = `${supplierId}/${file.name}`; // const fileOptions = { // metadata: { // contentType: file.mimetype, // }, // }; // await bucket.upload(file.tempFilePath, { // destination: fileName, // ...fileOptions, // }); // const imageUrl = `https://storage.googleapis.com/${bucketName}/${fileName}`; // let profilePicture = await profilePictureSupplier.findOne({ supplierId }); // if (!profilePicture) { // profilePicture = new profilePictureSupplier({ // supplierId, // picture: imageUrl, // }); // } else { // profilePicture.picture = imageUrl; // } // await profilePicture.save(); // // Delete the temporary file after uploading to GCS // fs.unlinkSync(file.tempFilePath); // res.send({ message: 'Profile picture uploaded successfully' }); // } catch (error) { // res.status(500).send({ error: error.message }); // } // }; // exports.uploadProfilePicture = async (req, res) => { // try { // upload(req, res, async (err) => { // if (err) { // return res.status(400).send({ error: 'Failed to upload profile picture' }); // } // const supplierId = req.params.supplierId; // const picture = req.file; // Assuming the file field in the request is named 'picture' // // Upload the file to the GCS bucket // const bucket = storage.bucket(bucketName); // const fileName = `${supplierId}/${file.originalname}`; // const fileOptions = { // metadata: { // contentType: file.mimetype, // }, // }; // await bucket.upload(filepath, { // destination: `armintaprofile_picture/${picture}`, // ...fileOptions, // }); // const imageUrl = `https://storage.googleapis.com/${bucketName}/${fileName}`; // console.log(imageUrl) // let profilePicture = await ProfilePictureSupplier.findOne({ supplierId }); // console.log("profilePicture", profilePicture) // if (!profilePicture) { // profilePicture = new ProfilePictureSupplier({ // supplierId, // picture: imageUrl, // }); // } else { // profilePicture.picture = imageUrl; // } // await profilePicture.save(); // // Delete the temporary file after uploading to GCS // fs.unlinkSync(file.path); // res.send({ message: 'Profile picture uploaded successfully' }); // }); // } catch (error) { // res.status(500).send({ error: error.message }); // } // }; // exports.uploadProfilePicture = async (req, res) => { // try { // upload(req, res, async (err) => { // if (err) { // return res.status(400).send({ error: 'Failed to upload profile picture' }); // } // const supplierId = req.params.supplierId; // const picture = req.file.filename; // Assuming the file field in the request is named 'picture' // // Upload the picture to the GCP bucket // const bucketName = 'armintaprofile_picture'; // const bucket = storage.bucket(bucketName); // const uploadPath = `armintaprofile_picture/${picture}`; // await bucket.upload(req.file.path, { // destination: uploadPath, // }); // console.log("bucket",bucket) // console.log(uploadPath, "uploadPath") // // Get the public URL of the uploaded picture // const file = bucket.file(uploadPath); // const [metadata] = await file.getMetadata(); // const pictureUrl = metadata.mediaLink; // let profilePicture = await profilePictureSupplier.findOne({ supplierId }); // if (!profilePicture) { // profilePicture = new profilePictureSupplier({ // supplierId, // picture: pictureUrl, // }); // } else { // profilePicture.picture = pictureUrl; // } // await profilePicture.save(); // res.send({ message: 'Profile picture uploaded successfully' }); // }); // } catch (error) { // res.status(500).send({ error: error.message }); // } // }; // Route for fetching profile picture data fastify.get('/api/users/profile-picture-supplier/:supplierId', async (req, res) => { try { const supplierId = req.params.supplierId; const profilePicture = await ProfilePictureSupplier.findOne({ supplierId }); if (!profilePicture) { return res.status(404).send({ error: 'Profile picture not found' }); } // Send the profile picture data as a response res.send({ picture: profilePicture.picture }); } catch (error) { res.status(500).send({ error: error.message }); } }); exports.getSuppliersForPlanSearch = async (req, reply) => { const { customerId } = req.params; const { type_of_water, capacity: requestedCapacityStr, quantity: requestedQuantityStr, // frequency, start_date, end_date are provided by UI but not used for filtering now } = req.body; // helpers inside function (per your preference) const parseCapacity = (v) => parseFloat((v || "0").toString().replace(/,/g, "")) || 0; const requestedCapacity = parseCapacity(requestedCapacityStr); const requestedQuantity = parseInt((requestedQuantityStr || "0").toString(), 10) || 0; const totalRequiredCapacity = requestedCapacity * requestedQuantity; try { // favorites const customer = await User.findOne({ customerId }, { favorate_suppliers: 1 }).lean(); const favoriteSet = new Set(customer?.favorate_suppliers || []); // Tanker filter: ONLY by type_of_water (NO booked tanker exclusion, NO price/radius/rating) const tankerQuery = {}; if (type_of_water && type_of_water.trim() !== "") { tankerQuery.typeofwater = type_of_water; } const tankers = await Tanker.find(tankerQuery).lean(); // Group tankers by supplier const supplierTankerMap = {}; for (const t of tankers) { if (!supplierTankerMap[t.supplierId]) supplierTankerMap[t.supplierId] = []; supplierTankerMap[t.supplierId].push(t); } // Capacity check per supplier (capacity * quantity) const qualified = []; for (const [supplierId, supplierTankers] of Object.entries(supplierTankerMap)) { const totalAvail = supplierTankers.reduce((sum, tt) => sum + parseCapacity(tt.capacity), 0); if (requestedCapacity > 0 && requestedQuantity > 0) { if (totalAvail < totalRequiredCapacity) continue; } qualified.push({ supplierId, tankers: supplierTankers }); } // Fetch suppliers + connection flags const supplierIds = qualified.map(q => q.supplierId); const [suppliersData, acceptedReqs] = await Promise.all([ Supplier.find({ supplierId: { $in: supplierIds } }).lean(), FriendRequest.find( { customerId, supplierId: { $in: supplierIds }, status: "accepted" }, { supplierId: 1, _id: 0 } ).lean() ]); const supplierById = new Map(suppliersData.map(s => [s.supplierId, s])); const connectedSet = new Set(acceptedReqs.map(r => r.supplierId)); // Optional: check if any (single-day) requested booking exists with that supplier const suppliers = []; for (const q of qualified) { const supplierData = supplierById.get(q.supplierId); const friendRequestAccepted = connectedSet.has(q.supplierId); const isFavorite = favoriteSet.has(q.supplierId); const requestedBookingRecord = await RequestedBooking.findOne({ customerId, "requested_suppliers.supplierId": q.supplierId }, { time: 1 }).lean(); const requestedBooking = requestedBookingRecord ? { status: true, time: requestedBookingRecord.time } : { status: false }; suppliers.push({ supplier: supplierData, tankers: q.tankers, isConnected: friendRequestAccepted, isFavorite, requestedBooking }); } return reply.send({ status_code: 200, suppliers }); } catch (err) { console.error(err); return reply.send({ status_code: 500, message: "Something went wrong", error: err.message }); } }; // controllers/validationHandler.js (add below the previous handler) exports.createRequestedPlanBooking = async (req, reply) => { const { customerId, type_of_water, capacity, quantity, start_date, end_date, time, frequency, // "daily" | "weekly_once" | "weekly_twice" | "weekly_thrice" | "weekly" weekly_count, // used only if frequency === "weekly" requested_suppliers } = req.body; // helpers inside function (as you prefer) const parseCapacity = (v) => parseFloat((v || "0").toString().replace(/,/g, "")) || 0; const parseIntSafe = (v) => parseInt((v || "0").toString().replace(/,/g, ""), 10) || 0; const toISODate = (d) => d.toISOString().slice(0, 10); const mkUTCDate = (yyyy_mm_dd) => { const [y, m, d] = (yyyy_mm_dd || "").split("-").map(Number); return new Date(Date.UTC(y, (m || 1) - 1, d || 1)); }; const normalizeWeeklyCount = (freq, wc) => { if (freq === "weekly_once") return 1; if (freq === "weekly_twice") return 2; if (freq === "weekly_thrice") return 3; if (freq === "weekly") return wc || 1; return 1; }; const computeWeeklyDOWs = ({ anchorDow, weeklyCount }) => { if (weeklyCount === 1) return [anchorDow]; if (weeklyCount === 2) return [anchorDow, (anchorDow + 3) % 7]; if (weeklyCount === 3) return [anchorDow, (anchorDow + 2) % 7, (anchorDow + 4) % 7]; return [anchorDow]; }; const generateDates = ({ frequency, start_date, end_date, weekly_count }) => { const start = mkUTCDate(start_date); const end = mkUTCDate(end_date); if (isNaN(start) || isNaN(end)) throw new Error("Invalid start_date or end_date"); if (end < start) throw new Error("end_date must be after or equal to start_date"); // ~3 months cap const maxMs = 92 * 24 * 60 * 60 * 1000; if ((end - start) > maxMs) throw new Error("Range exceeds 3 months"); const out = []; if (frequency === "daily") { for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) { out.push(toISODate(d)); } return out; } if (frequency.startsWith("weekly") || frequency === "weekly") { const wc = normalizeWeeklyCount(frequency, weekly_count); const dows = computeWeeklyDOWs({ anchorDow: start.getUTCDay(), weeklyCount: wc }); const set = new Set(dows); for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) { if (set.has(d.getUTCDay())) out.push(toISODate(d)); } return out; } throw new Error("Unsupported frequency"); }; try { if (!customerId || !type_of_water || !capacity || !quantity || !start_date || !end_date || !time || !frequency || !requested_suppliers) { return reply.code(400).send({ status_code: 400, message: "Missing required fields" }); } const cap = parseCapacity(capacity); const qty = parseIntSafe(quantity); const total_required_capacity = cap * qty; const dates = generateDates({ frequency, start_date, end_date, weekly_count }); if (dates.length === 0) { return reply.code(400).send({ status_code: 400, message: "No dates generated for given inputs" }); } const doc = new RecurringRequestedBooking({ customerId, type_of_water, capacity, quantity, total_required_capacity, frequency, weekly_count: normalizeWeeklyCount(frequency, weekly_count), start_date, end_date, time, dates, requested_suppliers, status: "pending" }); await doc.save(); return reply.send({ status_code: 200, message: "Plan requested booking created successfully", count: dates.length, dates, data: doc }); } catch (err) { console.error(err); return reply.code(500).send({ status_code: 500, message: "Something went wrong while saving", error: err.message }); } };