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, }); } };