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