const Food = require('../models/Food') const CartItem = require('../models/CartItem') const {body, validationResult} = require('express-validator') const fs = require('fs') const sharp = require('sharp') const {_sr} = require('../plugins/serverResponses') const {getID} = require('../authentication') const {res404, res500, checkValidations} = require('../plugins/controllersHelperFunctions') const StoreInfo = require("../models/StoreInfo"); ///// variables const foodImageWidth = 500 const foodImageHeight = 500 const foodImageQuality = 100 const limit = 20 //////////////////////////////////////////////////////////////////////////////// create module.exports.create = [ [ body('name') .isLength({min: 2}).withMessage(_sr['fa'].min_char.min2) .bail() .custom((value, {req}) => { return Food.findOne({name: value, branchId: req?.body?.branchId}) .then(food => { if (food) return Promise.reject(_sr['fa'].duplicated.name) else return true }) }), body('price') .notEmpty().withMessage(_sr['fa'].required.price) .bail() .isNumeric().withMessage(_sr['fa'].format.number), body('index') .notEmpty().withMessage(_sr['fa'].required.index) .bail() .isNumeric().withMessage(_sr['fa'].format.number), body('estimated_time') .if((value, {req}) => value) .isNumeric().withMessage(_sr['fa'].format.number), body('static_discount') .notEmpty().withMessage(_sr['fa'].required.field) .bail() .isNumeric().withMessage(_sr['fa'].format.number), body('special') .isBoolean().withMessage(_sr['fa'].format.boolean), body('stock') .notEmpty().withMessage(_sr['fa'].required.quantity) .bail() .isNumeric().withMessage(_sr['fa'].format.number), body('category') .notEmpty().withMessage(_sr['fa'].required.category), body('menuType_id') .notEmpty().withMessage(_sr['fa'].required.menuType_id), body('havePoint') .optional().isBoolean().withMessage(_sr['fa'].format.boolean), body('club') .optional().isBoolean().withMessage(_sr['fa'].format.boolean), // body('point') // .custom((value, {req}) => { // if (req.body.club && !Boolean(Number(value))) return Promise.reject('لطفا مقدار امتیاز مورد نیاز برای خرید این محصول را وارد نمایید') // else return true // }), body('sale') .optional().isBoolean().withMessage(_sr['fa'].format.boolean), body('branchId') .notEmpty().withMessage(_sr['fa'].required.field) .bail() .custom((value, {req}) => { return StoreInfo.findOne({_id: value}) .then(store => { if (!store) return Promise.reject(_sr['fa'].not_found.item_id) else return true }) }) ], checkValidations(validationResult), async (req, res) => { if (req.files?.image && !_sr.supportedImageFormats.includes(req.files.image.mimetype.split('/')[1])) return res.status(422).json({validation: {image: {msg: _sr['fa'].format.image}}}) const {name, description, short_description, price, stock, static_discount, estimated_time, category, index, special, havePoint, club, point, sale , branchId, menuType_id} = req.body const data = {name, description, short_description, price, stock, static_discount, estimated_time, category, index, special, havePoint, club, point, sale , branchId, menuType_id} if (req?.files?.image) { const image = req?.files?.image const imageName = 'food_' + Date.now() + '.png' data.image = imageName try { const target = sharp(image.data) await target .resize(foodImageWidth, foodImageHeight, {fit: 'cover'}) .toFormat('png') .png({quality: foodImageQuality}) .toFile(`./static/uploads/images/food/${imageName}`) } catch (e) { return res500(res, e) } } const food = new Food(data) food.save((err, data) => { if (err) return res500(res, err) return res.json({message: _sr['fa'].response.success_save}) }) } ] //////////////////////////////////////////////////////////////////////////////// get all module.exports.getAll = [ async (req, res) => { try { const allFoods = await Food.find({branchId : req?.params?.branchId}).select('-ratings').sort({index: 1}); const getCarts = await CartItem.find({branchId: req?.params?.branchId, user_id: req.user._id}); const clone = JSON.parse(JSON.stringify(allFoods)) for (const item of clone) { item.quantity = Number(getCarts.find(item2 => item2?.product_id?.toString() === item?._id?.toString())?.quantity) || 0; } return res.json(clone); } catch (error) { return res500(res, _sr['fa'].response.unknownError); } } ] module.exports.getAllForWebSite = [ async (req, res) => { try { const allFoods = await Food.find().select('-ratings').sort({index: 1}); return res.json(allFoods); } catch (error) { return res500(res, _sr['fa'].response.unknownError); } } ] module.exports.getAllRestaurant = [ async (req, res) => { try { const allFoods = await Food.find().select('-ratings').sort({index: 1}); return res.json(allFoods); } catch (error) { return res500(res, _sr['fa'].response.unknownError); } } ] module.exports.getAllForPanel = [ async (req, res) => { try { const allFoods = await Food.paginate({branchId: req?.params?.branchId}, { page: req.query.page || 1, limit, select: '-ratings', sort: {index: 1} }); return res.json(allFoods); } catch (error) { return res500(res, _sr['fa'].response.unknownError); } } ] //////////////////////////////////////////////////////////////////////////////// get one module.exports.getOne = [ (req, res) => { Food.findById(req.params.id).populate('ratings.user_id', 'first_name last_name').exec((err, food) => { if (err || !food) return res404(res, `غذای مورد نظر پیدا نشد`) CartItem.findOne({user_id: req.user._id, product_id: req.params.id}).exec((error, cart) => { const clone = JSON.parse(JSON.stringify(food)) clone.ratings = clone.ratings.filter(item => item.confirmed === true) let quantity = 0; if (error) return res500(res, _sr['fa'].response.unknownError) if (cart) quantity = cart.quantity clone.quantity = quantity; return res.json(clone) }) }) } ] module.exports.getOneByAdmin = [ (req, res) => { Food.findById(req.params.id).populate('ratings.user_id', 'first_name last_name').exec((err, food) => { if (err || !food) return res404(res, `غذای مورد نظر پیدا نشد`) CartItem.findOne({user_id: req.user._id, product_id: req.params.id}).exec((error, cart) => { const clone = JSON.parse(JSON.stringify(food)) let quantity = 0; if (error) return res500(res, _sr['fa'].response.unknownError) if (cart) quantity = cart.quantity clone.quantity = quantity; return res.json(clone) }) }) } ] //////////////////////////////////////////////////////////////////////////////// update module.exports.update = [ [ body('name') .isLength({min: 2}).withMessage(_sr['fa'].min_char.min2) .bail() .custom((value, {req}) => { return Food.findOne({name: value, branchId: req?.body?.branchId}) .then(food => { if (food && food._id.toString() !== req.params.id) return Promise.reject(_sr['fa'].duplicated.name) else return true }) }), body('price') .notEmpty().withMessage(_sr['fa'].required.price) .bail() .isNumeric().withMessage(_sr['fa'].format.number), body('index') .notEmpty().withMessage(_sr['fa'].required.index) .bail() .isNumeric().withMessage(_sr['fa'].format.number), body('estimated_time') .if((value, {req}) => value) .isNumeric().withMessage(_sr['fa'].format.number), body('static_discount') .notEmpty().withMessage(_sr['fa'].required.field) .bail() .isNumeric().withMessage(_sr['fa'].format.number), body('stock') .notEmpty().withMessage(_sr['fa'].required.quantity) .bail() .isNumeric().withMessage(_sr['fa'].format.number), body('category') .notEmpty().withMessage(_sr['fa'].required.category), body('havePoint') .optional().isBoolean().withMessage(_sr['fa'].format.boolean), body('club') .optional().isBoolean().withMessage(_sr['fa'].format.boolean), // body('point') // .custom((value, {req}) => { // if (req.body.club && !Boolean(Number(value))) return Promise.reject('لطفا مقدار امتیاز مورد نیاز برای خرید این محصول را وارد نمایید') // else return true // }), body('sale') .optional().isBoolean().withMessage(_sr['fa'].format.boolean), body('branchId') .notEmpty().withMessage(_sr['fa'].required.field) .bail() .custom((value, {req}) => { return StoreInfo.findOne({_id: value}) .then(store => { if (!store) return Promise.reject(_sr['fa'].not_found.item_id) else return true }) }) ], checkValidations(validationResult), async (req, res) => { if (req.files?.image && !_sr.supportedImageFormats.includes(req.files.image.mimetype.split('/')[1])) return res.status(422).json({validation: {image: {msg: _sr['fa'].format.image}}}) const {name, description, short_description, price, stock, static_discount, estimated_time, category, index, special, havePoint, club, point, sale , branchId, menuType_id} = req.body const data = {name, description, short_description, price, stock, static_discount, estimated_time, category, index, special, havePoint, club, point, sale , branchId, menuType_id} if (req.files?.image) { const image = req.files.image const imageName = 'food_' + Date.now() + '.png' data.image = imageName try { const target = sharp(image.data) await target .resize(foodImageWidth, foodImageHeight, {fit: 'cover'}) .toFormat('png') .png({quality: foodImageQuality}) .toFile(`./static/uploads/images/food/${imageName}`) } catch (e) { return res500(res, e) } } Food.findByIdAndUpdate(req.params.id, data, (err, oldData) => { if (err || !oldData) return res404(res, err) if (req.files?.image && oldData.image) { fs.unlink(`./static/${oldData.image}`, err => { if (err) console.log(err) }) } return res.json({message: _sr['fa'].response.success_save}) }) } ] //////////////////////////////////////////////////////////////////////////////// delete module.exports.delete = [ async (req, res) => { try { const food = await Food.findByIdAndDelete(req.params.id) // if (food.image) { // fs.unlink(`./static/${data.image}`, err => { // if (err) console.log(err) // }) // } await CartItem.deleteMany({product_id : food._id}) return res.json({message: _sr['fa'].response.success_remove}) }catch (error) { return res500(res , _sr['fa'].response.unknownError) } } ] //////////////////////////////////////////////////////////////////////////////// search module.exports.search = [ async (req, res) => { try { let { club, sale, name, category, branchId } = req.body; let findQuery = { branchId }; findQuery['$and'] = []; findQuery['$or'] = []; /** search by name */ if (name && !_.isEmpty(name)) { findQuery['$and'].push({ name: { $regex: '.*' + name + '.*' }, }); } /** search by sale */ if (!_.isUndefined(sale) && _.isBoolean(sale)) { findQuery['$and'].push({ sale }) } /** search by club */ if (!_.isUndefined(club) && _.isBoolean(club)) { findQuery['$and'].push({ club }) } /** search by category */ if (category && !_.isEmpty(category)) { findQuery['$and'].push({ category }); } _.isEmpty(findQuery['$and']) && delete findQuery['$and']; _.isEmpty(findQuery['$or']) && delete findQuery['$or']; /** get news */ const orders = await Food.paginate(findQuery, { page: req?.query?.page || 1, limit, populate: [{path: 'user_id', select: 'first_name last_name'}], sort: { _id: -1 } }); /** show result */ return res.json(orders); } catch (error) { console.log(error.message) return res500(res, _sr['fa'].response.unknownError) } } ] /////////////// rate foods module.exports.rate_food = [ [ body('rate') .notEmpty().withMessage('فیلد رتبه نباید خالی باشد') .bail() .isNumeric().withMessage(_sr['fa'].format.number) .bail() .custom((value, {req}) => { if (Number(value) < 0) return Promise.reject('مقدار نباید کوچیکتر از 0 باشد') if (Number(value) > 5) return Promise.reject('مقدار نباید بزرگتر از 5 باشد') else return true }) ], checkValidations(validationResult), async (req, res) => { try { const {rate, comment, orderId} = req.body const data = {rate, comment, order_id: orderId} userID = await getID(req) data.user_id = userID const food = await Food.findById(req.params.id) if (!food) return res404(res, 'غذا پیدا نشد') if (!food?.canReview || !food.canReview.includes(req.user._id)) return res.status(403).json({message: 'شما قبلا نظر خود را ثبت کرده اید'}) const index = food.canReview.indexOf(req.user._id); if (index > -1) { food.canReview.splice(index, 1); } food.ratings.push(data) let rateSUM = 0 food.ratings.forEach(item => rateSUM += Number(item.rate)) food.rate = (rateSUM / food.ratings.length).toFixed(1) food.save(err => { if (err) console.log(err) return res.json({message: _sr['fa'].response.success_save}) }) } catch (e) { return res500(res, e) } } ] module.exports.confirm_comment_by_admin = [ async (req, res) => { try { const admin_id = await getID(req) Food.findOne({'ratings._id': req.params.id}, (err, food) => { if (err || !food) return res404(res, 'item id is incorrect') const comment = food.ratings.id(req.params.id) comment.confirmed = req.body.confirmed comment.confirmed_by = admin_id food.save(err => { if (err) return res500(res, err) else return res.json({message: _sr['fa'].response.success_save}) }) }) } catch (e) { return res500(res, e) } } ] module.exports.reply_comment_by_admin = [ [ body('replay') .notEmpty().withMessage(_sr['fa'].required.field) ], checkValidations(validationResult), async (req, res) => { try { const targetComment = await Food.findOne({'ratings._id': req.params.id}) if (!targetComment) return res404(res, `کامنت مورد نظر وجود ندارد`) let comment = targetComment.ratings.id(req.params.id) comment.replay = req.body.replay comment.replied_by = req.user._id await targetComment.save() return res.json({message: _sr['fa'].response.success_save}) } catch (e) { console.log(e.message) return res500(res, _sr['fa'].response.unknownError); } } ] module.exports.remove_comment_by_admin = [ (req, res) => { Food.findOne({'ratings._id': req.params.id}, (err, food) => { if (err || !food) return res404(res, 'item id is incorrect') const comment = food.ratings.id(req.params.id) comment.remove() food.save(err => { if (err) return res500(res, err) else return res.json({message: _sr['fa'].response.success_remove}) }) }) } ]