samoulla-backend / controllers /promoController.js
Samoulla Sync Bot
Auto-deploy Samoulla Backend: 8574a71f0fc617aeb1ce9b5e35dac24c5319a12a
59c49c1
const Promo = require('../models/promoCodeModel');
const Order = require('../models/orderModel');
exports.createPromo = async (req, res) => {
try {
const data = req.body;
if (data.code) data.code = String(data.code).trim().toUpperCase();
if (data.categoryId === '') data.categoryId = null;
const promo = await Promo.create(data);
res.status(201).json({
status: 'success',
data: { promo },
});
} catch (err) {
res.status(500).json({
status: 'error',
message: err.message,
});
}
};
exports.getPromos = async (req, res) => {
try {
const promos = await Promo.find().sort({ createdAt: -1 }).limit(200);
res.status(200).json({
status: 'success',
results: promos.length,
data: { promos },
});
} catch (err) {
res.status(500).json({
status: 'error',
message: err.message,
});
}
};
exports.getPromo = async (req, res) => {
try {
const promo = await Promo.findById(req.params.id);
if (!promo)
return res
.status(404)
.json({ status: 'fail', message: 'Promo not found' });
res.status(200).json({
status: 'success',
data: { promo },
});
} catch (err) {
res.status(500).json({
status: 'error',
message: err.message,
});
}
};
exports.updatePromo = async (req, res) => {
try {
const data = req.body;
if (data.code) data.code = String(data.code).trim().toUpperCase();
if (data.categoryId === '') data.categoryId = null;
const promo = await Promo.findByIdAndUpdate(req.params.id, data, {
new: true,
runValidators: true,
});
if (!promo)
return res
.status(404)
.json({ status: 'fail', message: 'Promo not found' });
res.status(200).json({
status: 'success',
data: { promo },
});
} catch (err) {
res.status(500).json({
status: 'error',
message: err.message,
});
}
};
exports.deletePromo = async (req, res) => {
try {
const promo = await Promo.findByIdAndDelete(req.params.id);
if (!promo)
return res
.status(404)
.json({ status: 'fail', message: 'Promo not found' });
res.status(204).json({
status: 'success',
data: null,
});
} catch (err) {
res.status(500).json({
status: 'error',
message: err.message,
});
}
};
const computeDiscountAmount = (type, value, base) => {
const numericBase = Number(base) || 0;
const numericValue = Number(value) || 0;
if (type === 'shipping') return 0; // Shipping discount doesn't affect subtotal
if (type === 'percentage' || type === 'welcome')
return Math.max(0, (numericBase * numericValue) / 100);
return Math.max(0, numericValue);
};
exports.validatePromo = async (req, res) => {
try {
const { promoCode, subtotal, items } = req.body || {};
const base = Number(subtotal) || 0;
const cartItems = Array.isArray(items) ? items : [];
if (!promoCode)
return res.status(200).json({
status: 'success',
data: { code: null, discountedTotal: base },
});
const upper = String(promoCode).trim().toUpperCase();
const now = new Date();
const promo = await Promo.findOne({ code: upper });
if (!promo) {
return res.status(200).json({
status: 'fail',
message: 'كود الخصم غير موجود',
});
}
if (!promo.active) {
return res.status(200).json({
status: 'fail',
message: 'كود الخصم هذا غير مفعّل حالياً',
});
}
if (promo.startsAt && promo.startsAt > now) {
return res.status(200).json({
status: 'fail',
message: 'كود الخصم لم يبدأ مفعوله بعد',
});
}
if (promo.expiresAt && promo.expiresAt < now) {
return res.status(200).json({
status: 'fail',
message: 'انتهت صلاحية كود الخصم هذا',
});
}
if (promo.minOrderValue > base) {
return res.status(200).json({
status: 'fail',
message: `يجب أن تكون قيمة الطلب ${promo.minOrderValue} جنيه على الأقل لاستخدام هذا الكود`,
});
}
// Check usage limits if needed
if (promo.usageLimit !== null && promo.usedCount >= promo.usageLimit) {
return res.status(200).json({
status: 'fail',
message: 'تم استهلاك الحد الأقصى لاستخدام كود الخصم هذا',
});
}
// Check per-user limit
if (req.user && promo.perUserLimit !== null) {
const userUsageCount = await Order.countDocuments({
user: req.user._id,
promoCode: upper,
orderStatus: { $ne: 'cancelled' }, // Count everything except cancelled
});
if (userUsageCount >= promo.perUserLimit) {
return res.status(200).json({
status: 'fail',
message: `لقد استنفدت الحد الأقصى لاستخدام هذا الكود (${promo.perUserLimit} مرات)`,
});
}
}
// Determine eligible base for discount calculation
let eligibleBase = base;
if (promo.canBeUsedWithSaleItems === false && cartItems.length > 0) {
eligibleBase = cartItems.reduce((sum, item) => {
// If product is NOT on sale, it is eligible
if (!item.isOnSale) {
return sum + Number(item.price) * Number(item.quantity);
}
return sum;
}, 0);
// If no eligible items and it's not a shipping discount, return error
if (eligibleBase <= 0 && promo.type !== 'shipping') {
return res.status(200).json({
status: 'fail',
message: 'كود الخصم هذا لا يمكن استخدامه مع المنتجات المخفضة',
});
}
}
const discount = computeDiscountAmount(
promo.type,
promo.value,
eligibleBase,
);
const discounted = Math.max(0, base - discount);
return res.status(200).json({
status: 'success',
data: {
code: promo.code,
discountType: promo.type,
discountValue: promo.value,
discountedTotal: discounted,
isFreeShipping: promo.type === 'shipping',
expiresAt: promo.expiresAt,
},
});
} catch (err) {
res.status(500).json({ status: 'error', message: err.message });
}
};