samoulla-backend / controllers /providerController.js
Samoulla Sync Bot
Auto-deploy Samoulla Backend: b68e45770de26ed39feb4b1c0925e5345eb3a61d
634b9bb
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,
});
}
};