Spaces:
Running
Running
| 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 }); | |
| } | |
| }; | |