Files
ember-market-frontend/backend/controllers/promotion.controller.js
2025-03-10 17:39:37 +00:00

271 lines
7.3 KiB
JavaScript

import Promotion from "../models/Promotion.model.js";
import logger from "../utils/logger.js";
/**
* Get all promotions for a store
*/
export const getPromotions = async (req, res) => {
try {
const { query } = req;
const filter = { storeId: req.user.storeId };
// Apply filters if provided
if (query.isActive) {
filter.isActive = query.isActive === 'true';
}
if (query.search) {
filter.code = { $regex: query.search, $options: 'i' };
}
// Handle date filters
if (query.active === 'true') {
const now = new Date();
filter.startDate = { $lte: now };
filter.$or = [
{ endDate: null },
{ endDate: { $gte: now } }
];
filter.isActive = true;
}
const promotions = await Promotion.find(filter).sort({ createdAt: -1 });
return res.status(200).json(promotions);
} catch (error) {
logger.error("Error fetching promotions", { error });
return res.status(500).json({ message: "Failed to fetch promotions", error: error.message });
}
};
/**
* Get a single promotion by ID
*/
export const getPromotionById = async (req, res) => {
try {
const { id } = req.params;
const promotion = await Promotion.findOne({
_id: id,
storeId: req.user.storeId
});
if (!promotion) {
return res.status(404).json({ message: "Promotion not found" });
}
return res.status(200).json(promotion);
} catch (error) {
logger.error("Error fetching promotion", { error });
return res.status(500).json({ message: "Failed to fetch promotion", error: error.message });
}
};
/**
* Create a new promotion
*/
export const createPromotion = async (req, res) => {
try {
const {
code,
discountType,
discountValue,
minOrderAmount = 0,
maxUsage = null,
isActive = true,
startDate = new Date(),
endDate = null,
description = ""
} = req.body;
// Validate required fields
if (!code || !discountType || discountValue === undefined) {
return res.status(400).json({ message: "Code, discount type, and discount value are required" });
}
// Check if code already exists for this store
const existingPromo = await Promotion.findOne({
storeId: req.user.storeId,
code: code.toUpperCase()
});
if (existingPromo) {
return res.status(400).json({ message: "A promotion with this code already exists" });
}
// Create new promotion
const newPromotion = new Promotion({
storeId: req.user.storeId,
code: code.toUpperCase(),
discountType,
discountValue,
minOrderAmount,
maxUsage,
isActive,
startDate,
endDate,
description
});
await newPromotion.save();
return res.status(201).json(newPromotion);
} catch (error) {
logger.error("Error creating promotion", { error });
if (error.name === 'ValidationError') {
return res.status(400).json({
message: "Invalid promotion data",
details: Object.values(error.errors).map(err => err.message)
});
}
return res.status(500).json({ message: "Failed to create promotion", error: error.message });
}
};
/**
* Update an existing promotion
*/
export const updatePromotion = async (req, res) => {
try {
const { id } = req.params;
const updates = req.body;
// Ensure the promotion exists and belongs to the vendor's store
const promotion = await Promotion.findOne({
_id: id,
storeId: req.user.storeId
});
if (!promotion) {
return res.status(404).json({ message: "Promotion not found" });
}
// Remove storeId from updates if present (can't change store)
if (updates.storeId) {
delete updates.storeId;
}
// Ensure code is uppercase if present
if (updates.code) {
updates.code = updates.code.toUpperCase();
// Check if updated code conflicts with existing promo
const codeExists = await Promotion.findOne({
storeId: req.user.storeId,
code: updates.code,
_id: { $ne: id } // Exclude current promotion
});
if (codeExists) {
return res.status(400).json({ message: "A promotion with this code already exists" });
}
}
// Update the promotion
const updatedPromotion = await Promotion.findByIdAndUpdate(
id,
{ ...updates, updatedAt: new Date() },
{ new: true, runValidators: true }
);
return res.status(200).json(updatedPromotion);
} catch (error) {
logger.error("Error updating promotion", { error });
if (error.name === 'ValidationError') {
return res.status(400).json({
message: "Invalid promotion data",
details: Object.values(error.errors).map(err => err.message)
});
}
return res.status(500).json({ message: "Failed to update promotion", error: error.message });
}
};
/**
* Delete a promotion
*/
export const deletePromotion = async (req, res) => {
try {
const { id } = req.params;
// Ensure the promotion exists and belongs to the vendor's store
const promotion = await Promotion.findOne({
_id: id,
storeId: req.user.storeId
});
if (!promotion) {
return res.status(404).json({ message: "Promotion not found" });
}
await Promotion.findByIdAndDelete(id);
return res.status(200).json({ message: "Promotion deleted successfully" });
} catch (error) {
logger.error("Error deleting promotion", { error });
return res.status(500).json({ message: "Failed to delete promotion", error: error.message });
}
};
/**
* Validate a promotion code for a store
*/
export const validatePromotion = async (req, res) => {
try {
const { code, orderTotal } = req.body;
const { storeId } = req.params;
if (!code || !storeId) {
return res.status(400).json({ message: "Promotion code and store ID are required" });
}
// Find the promotion
const promotion = await Promotion.findOne({
storeId,
code: code.toUpperCase(),
isActive: true,
});
if (!promotion) {
return res.status(404).json({ message: "Promotion not found or inactive" });
}
// Check if the promotion is valid
if (!promotion.isValid()) {
return res.status(400).json({ message: "Promotion is no longer valid" });
}
// Check minimum order amount
if (orderTotal && orderTotal < promotion.minOrderAmount) {
return res.status(400).json({
message: `Order total must be at least £${promotion.minOrderAmount} to use this promotion`,
minOrderAmount: promotion.minOrderAmount
});
}
// Calculate discount if order total provided
let discountAmount = null;
if (orderTotal) {
discountAmount = promotion.calculateDiscount(orderTotal);
}
return res.status(200).json({
promotion: {
_id: promotion._id,
code: promotion.code,
discountType: promotion.discountType,
discountValue: promotion.discountValue,
minOrderAmount: promotion.minOrderAmount,
},
discountAmount,
message: "Promotion is valid"
});
} catch (error) {
logger.error("Error validating promotion", { error });
return res.status(500).json({ message: "Failed to validate promotion", error: error.message });
}
};