Spaces:
Running
Running
| const Provider = require('../models/providerModel'); | |
| const User = require('../models/userModel'); | |
| const Product = require('../models/productModel'); | |
| const Order = require('../models/orderModel'); | |
| const { deleteImage, getPublicIdFromUrl } = require('../config/cloudinary'); | |
| // Get all providers | |
| exports.getAllProviders = async (req, res) => { | |
| try { | |
| const providers = await Provider.find().populate('user', 'email'); | |
| // Calculate stats for each provider | |
| const providersWithStats = await Promise.all( | |
| providers.map(async (provider) => { | |
| const providerData = provider.toObject(); | |
| // Add email from user | |
| if (provider.user) { | |
| providerData.email = provider.user.email; | |
| } | |
| // Count total products for this provider | |
| const totalProducts = await Product.countDocuments({ | |
| provider: provider._id, | |
| }); | |
| // Get all products for this provider | |
| const products = await Product.find({ provider: provider._id }).select( | |
| '_id', | |
| ); | |
| const productIds = products.map((p) => p._id); | |
| // Calculate total sales and order count from COMPLETED orders only | |
| const orders = await Order.find({ | |
| 'items.product': { $in: productIds }, | |
| orderStatus: 'completed', // Only count delivered orders | |
| }); | |
| let totalSales = 0; | |
| const orderSet = new Set(); | |
| orders.forEach((order) => { | |
| orderSet.add(order._id.toString()); | |
| // Sum up sales for this provider's products in each order | |
| order.items.forEach((item) => { | |
| if (productIds.some((id) => id.equals(item.product))) { | |
| totalSales += item.quantity * item.unitPrice; | |
| } | |
| }); | |
| }); | |
| providerData.totalProducts = totalProducts; | |
| providerData.totalSales = totalSales; | |
| providerData.orderCount = orderSet.size; | |
| return providerData; | |
| }), | |
| ); | |
| res.status(200).json({ | |
| status: 'success', | |
| results: providersWithStats.length, | |
| data: { | |
| providers: providersWithStats, | |
| }, | |
| }); | |
| } catch (err) { | |
| res.status(500).json({ | |
| status: 'fail', | |
| message: err.message, | |
| }); | |
| } | |
| }; | |
| // Get single provider | |
| exports.getProvider = async (req, res) => { | |
| try { | |
| const provider = await Provider.findById(req.params.id).populate( | |
| 'user', | |
| 'email', | |
| ); | |
| if (!provider) { | |
| return res.status(404).json({ | |
| status: 'fail', | |
| message: 'No provider found with that ID', | |
| }); | |
| } | |
| // Include email from user in response | |
| const providerData = provider.toObject(); | |
| if (provider.user) { | |
| providerData.email = provider.user.email; | |
| } | |
| res.status(200).json({ | |
| status: 'success', | |
| data: { provider: providerData }, | |
| }); | |
| } catch (err) { | |
| res.status(400).json({ | |
| status: 'fail', | |
| message: err.message, | |
| }); | |
| } | |
| }; | |
| // Create a new provider | |
| exports.createProvider = async (req, res) => { | |
| try { | |
| const { email, password, name, storeName, phoneNumber, address } = req.body; | |
| // 1. Check if email or phone already exists in User collection | |
| const existingUser = await User.findOne({ | |
| $or: [{ email }, { phone: phoneNumber }], | |
| }); | |
| if (existingUser) { | |
| const field = | |
| existingUser.email === email ? 'البريد الإلكتروني' : 'رقم الهاتف'; | |
| return res.status(400).json({ | |
| status: 'fail', | |
| message: `${field} مسجل بالفعل لمستخدم آخر`, | |
| }); | |
| } | |
| // 2. Get next provider ID | |
| const lastProvider = await Provider.findOne().sort({ id: -1 }); | |
| const nextId = lastProvider ? lastProvider.id + 1 : 1; | |
| const providerData = { | |
| name, | |
| storeName, | |
| phoneNumber, | |
| address, | |
| id: nextId, | |
| }; | |
| if (req.file) { | |
| providerData.logo = req.file.path; | |
| } | |
| // 3. Create Provider document first | |
| let newProvider; | |
| try { | |
| newProvider = await Provider.create(providerData); | |
| } catch (err) { | |
| if (err.code === 11000) { | |
| const field = Object.keys(err.keyPattern)[0]; | |
| const message = | |
| field === 'storeName' | |
| ? 'اسم المتجر مسجل بالفعل' | |
| : 'رقم الهاتف مسجل بالفعل في قائمة الموردين'; | |
| return res.status(400).json({ status: 'fail', message }); | |
| } | |
| throw err; | |
| } | |
| // 4. Create User account for vendor | |
| try { | |
| const vendorUser = await User.create({ | |
| name, | |
| email, | |
| phone: phoneNumber, | |
| password, | |
| role: 'vendor', | |
| provider: newProvider._id, | |
| }); | |
| // 5. Update provider with user reference | |
| newProvider.user = vendorUser._id; | |
| await newProvider.save(); | |
| res.status(201).json({ | |
| status: 'success', | |
| data: { provider: newProvider }, | |
| }); | |
| } catch (err) { | |
| // Rollback: Delete the created provider if user creation fails | |
| await Provider.findByIdAndDelete(newProvider._id); | |
| throw err; | |
| } | |
| } catch (err) { | |
| res.status(400).json({ | |
| status: 'fail', | |
| message: err.message, | |
| }); | |
| } | |
| }; | |
| // Update a provider | |
| exports.updateProvider = async (req, res) => { | |
| try { | |
| const providerId = req.params.id; | |
| const oldProvider = await Provider.findById(providerId); | |
| if (!oldProvider) { | |
| return res.status(404).json({ | |
| status: 'fail', | |
| message: 'No provider found with that ID', | |
| }); | |
| } | |
| const updateData = { ...req.body }; | |
| // Handle logo update or deletion | |
| if (req.file) { | |
| // New logo uploaded | |
| updateData.logo = req.file.path; | |
| // Delete old logo from Cloudinary if it exists | |
| if (oldProvider.logo) { | |
| const publicId = getPublicIdFromUrl(oldProvider.logo); | |
| if (publicId) { | |
| await deleteImage(publicId).catch((err) => | |
| console.error('Cloudinary delete error:', err), | |
| ); | |
| } | |
| } | |
| } else if (req.body.logo === '') { | |
| // Logo explicitly removed | |
| updateData.logo = ''; | |
| // Delete old logo from Cloudinary if it exists | |
| if (oldProvider.logo) { | |
| const publicId = getPublicIdFromUrl(oldProvider.logo); | |
| if (publicId) { | |
| await deleteImage(publicId).catch((err) => | |
| console.error('Cloudinary delete error:', err), | |
| ); | |
| } | |
| } | |
| } else { | |
| // Keep existing logo - remove it from updateData so it doesn't try to update with a string/URL | |
| delete updateData.logo; | |
| } | |
| const updatedProvider = await Provider.findByIdAndUpdate( | |
| providerId, | |
| updateData, | |
| { new: true, runValidators: true }, | |
| ); | |
| if (!updatedProvider) { | |
| return res.status(404).json({ | |
| status: 'fail', | |
| message: 'No provider found with that ID', | |
| }); | |
| } | |
| // Also update the associated User's email, phone, and password if they changed | |
| if ( | |
| updatedProvider.user && | |
| (req.body.email || req.body.phoneNumber || req.body.password) | |
| ) { | |
| const user = await User.findById(updatedProvider.user); | |
| if (user) { | |
| if (req.body.email) user.email = req.body.email; | |
| if (req.body.phoneNumber) user.phone = req.body.phoneNumber; | |
| if (req.body.password) user.password = req.body.password; | |
| await user.save(); | |
| } | |
| } | |
| res.status(200).json({ | |
| status: 'success', | |
| data: { provider: updatedProvider }, | |
| }); | |
| } catch (err) { | |
| if (err.code === 11000) { | |
| const field = Object.keys(err.keyPattern)[0]; | |
| const message = | |
| field === 'storeName' | |
| ? 'اسم المتجر مسجل بالفعل' | |
| : 'رقم الهاتف أو البريد الإلكتروني مسجل بالفعل'; | |
| return res.status(400).json({ status: 'fail', message }); | |
| } | |
| res.status(400).json({ | |
| status: 'fail', | |
| message: err.message, | |
| }); | |
| } | |
| }; | |
| // Delete a provider | |
| exports.deleteProvider = async (req, res) => { | |
| try { | |
| const provider = await Provider.findById(req.params.id); | |
| if (!provider) { | |
| return res.status(404).json({ | |
| status: 'fail', | |
| message: 'No provider found with that ID', | |
| }); | |
| } | |
| // Delete associated user | |
| if (provider.user) { | |
| await User.findByIdAndDelete(provider.user); | |
| } | |
| // Delete logo from Cloudinary if it exists | |
| if (provider.logo) { | |
| const publicId = getPublicIdFromUrl(provider.logo); | |
| if (publicId) await deleteImage(publicId); | |
| } | |
| await Provider.findByIdAndDelete(req.params.id); | |
| res.status(204).json({ | |
| status: 'success', | |
| data: null, | |
| }); | |
| } catch (err) { | |
| res.status(400).json({ | |
| status: 'fail', | |
| message: err.message, | |
| }); | |
| } | |
| }; | |