986 lines
20 KiB
TypeScript
986 lines
20 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
import { PriceEngineService } from '../services/price-engine.service';
|
|
import { PromotionService } from '../services/promotion.service';
|
|
import { CouponService } from '../services/coupon.service';
|
|
import {
|
|
CreatePromotionInput,
|
|
UpdatePromotionInput,
|
|
CreateCouponInput,
|
|
UpdateCouponInput,
|
|
CalculatePricesInput,
|
|
RedeemCouponInput,
|
|
ListPromotionsQuery,
|
|
ListCouponsQuery,
|
|
PromotionProductInput,
|
|
GenerateBulkCouponsInput,
|
|
} from '../validation/pricing.schema';
|
|
import { AppDataSource } from '../../../config/database';
|
|
import { Promotion } from '../entities/promotion.entity';
|
|
import { Coupon } from '../entities/coupon.entity';
|
|
|
|
const priceEngineService = new PriceEngineService(AppDataSource);
|
|
const promotionService = new PromotionService(
|
|
AppDataSource,
|
|
AppDataSource.getRepository(Promotion)
|
|
);
|
|
const couponService = new CouponService(
|
|
AppDataSource,
|
|
AppDataSource.getRepository(Coupon)
|
|
);
|
|
|
|
// ==================== PRICE ENGINE ====================
|
|
|
|
/**
|
|
* Calculate prices for cart/order items
|
|
* POST /api/pricing/calculate
|
|
*/
|
|
export const calculatePrices = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const input = req.body as CalculatePricesInput;
|
|
|
|
const context = {
|
|
tenantId,
|
|
branchId: input.branchId,
|
|
customerId: input.customerId,
|
|
customerLevelId: input.customerLevelId,
|
|
isNewCustomer: input.isNewCustomer || false,
|
|
isLoyaltyMember: input.isLoyaltyMember || false,
|
|
isFirstOrder: input.isFirstOrder || false,
|
|
channel: input.channel,
|
|
};
|
|
|
|
const result = await priceEngineService.calculatePrices(
|
|
context,
|
|
input.lines,
|
|
input.couponCode
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Validate a coupon code
|
|
* POST /api/pricing/validate-coupon
|
|
*/
|
|
export const validateCoupon = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const branchId = req.headers['x-branch-id'] as string;
|
|
const { code, orderAmount } = req.body;
|
|
|
|
const context = {
|
|
tenantId,
|
|
branchId,
|
|
channel: 'pos' as const,
|
|
isNewCustomer: false,
|
|
isLoyaltyMember: false,
|
|
isFirstOrder: false,
|
|
};
|
|
|
|
const result = await priceEngineService.validateCoupon(
|
|
context,
|
|
code,
|
|
orderAmount
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get active promotions for a branch
|
|
* GET /api/pricing/active-promotions
|
|
*/
|
|
export const getActivePromotions = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const branchId = req.headers['x-branch-id'] as string;
|
|
const channel = (req.query.channel as 'pos' | 'ecommerce' | 'mobile') || 'pos';
|
|
|
|
const promotions = await priceEngineService.getActivePromotions(
|
|
tenantId,
|
|
branchId,
|
|
channel
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: promotions,
|
|
meta: {
|
|
count: promotions.length,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
// ==================== PROMOTIONS ====================
|
|
|
|
/**
|
|
* List promotions with filters
|
|
* GET /api/pricing/promotions
|
|
*/
|
|
export const listPromotions = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const query = req.query as unknown as ListPromotionsQuery;
|
|
|
|
const result = await promotionService.listPromotions(tenantId, query);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
meta: result.meta,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get promotion by ID
|
|
* GET /api/pricing/promotions/:id
|
|
*/
|
|
export const getPromotion = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await promotionService.getById(tenantId, id, {
|
|
relations: ['products'],
|
|
});
|
|
|
|
if (!result.success) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create a new promotion
|
|
* POST /api/pricing/promotions
|
|
*/
|
|
export const createPromotion = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const input = req.body as CreatePromotionInput;
|
|
|
|
const result = await promotionService.createPromotion(tenantId, input, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update a promotion
|
|
* PUT /api/pricing/promotions/:id
|
|
*/
|
|
export const updatePromotion = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
const input = req.body as UpdatePromotionInput;
|
|
|
|
const result = await promotionService.updatePromotion(tenantId, id, input, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Activate a promotion
|
|
* POST /api/pricing/promotions/:id/activate
|
|
*/
|
|
export const activatePromotion = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await promotionService.activatePromotion(tenantId, id, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Pause a promotion
|
|
* POST /api/pricing/promotions/:id/pause
|
|
*/
|
|
export const pausePromotion = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await promotionService.pausePromotion(tenantId, id, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* End a promotion
|
|
* POST /api/pricing/promotions/:id/end
|
|
*/
|
|
export const endPromotion = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await promotionService.endPromotion(tenantId, id, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Cancel a promotion
|
|
* POST /api/pricing/promotions/:id/cancel
|
|
*/
|
|
export const cancelPromotion = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await promotionService.cancelPromotion(tenantId, id, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Add products to a promotion
|
|
* POST /api/pricing/promotions/:id/products
|
|
*/
|
|
export const addPromotionProducts = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
const { products } = req.body as { products: PromotionProductInput[] };
|
|
|
|
const result = await promotionService.addProducts(tenantId, id, products, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Remove product from a promotion
|
|
* DELETE /api/pricing/promotions/:id/products/:productId
|
|
*/
|
|
export const removePromotionProduct = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id, productId } = req.params;
|
|
|
|
const result = await promotionService.removeProduct(tenantId, id, productId, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Product removed from promotion',
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Delete a promotion
|
|
* DELETE /api/pricing/promotions/:id
|
|
*/
|
|
export const deletePromotion = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await promotionService.softDelete(tenantId, id, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Promotion deleted',
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
// ==================== COUPONS ====================
|
|
|
|
/**
|
|
* List coupons with filters
|
|
* GET /api/pricing/coupons
|
|
*/
|
|
export const listCoupons = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const query = req.query as unknown as ListCouponsQuery;
|
|
|
|
const result = await couponService.listCoupons(tenantId, query);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
meta: result.meta,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get coupon by ID
|
|
* GET /api/pricing/coupons/:id
|
|
*/
|
|
export const getCoupon = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await couponService.getById(tenantId, id, {
|
|
relations: ['redemptions'],
|
|
});
|
|
|
|
if (!result.success) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get coupon by code
|
|
* GET /api/pricing/coupons/code/:code
|
|
*/
|
|
export const getCouponByCode = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const { code } = req.params;
|
|
|
|
const result = await couponService.getByCode(tenantId, code);
|
|
|
|
if (!result.success) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create a new coupon
|
|
* POST /api/pricing/coupons
|
|
*/
|
|
export const createCoupon = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const input = req.body as CreateCouponInput;
|
|
|
|
const result = await couponService.createCoupon(tenantId, input, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Generate bulk coupons
|
|
* POST /api/pricing/coupons/bulk
|
|
*/
|
|
export const generateBulkCoupons = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { quantity, prefix, baseInput } = req.body as GenerateBulkCouponsInput;
|
|
|
|
const result = await couponService.generateBulkCoupons(
|
|
tenantId,
|
|
baseInput,
|
|
quantity,
|
|
prefix,
|
|
userId
|
|
);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: result.data,
|
|
meta: {
|
|
count: result.data?.length || 0,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update a coupon
|
|
* PUT /api/pricing/coupons/:id
|
|
*/
|
|
export const updateCoupon = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
const input = req.body as UpdateCouponInput;
|
|
|
|
const result = await couponService.updateCoupon(tenantId, id, input, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Activate a coupon
|
|
* POST /api/pricing/coupons/:id/activate
|
|
*/
|
|
export const activateCoupon = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await couponService.activateCoupon(tenantId, id, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Deactivate a coupon
|
|
* POST /api/pricing/coupons/:id/deactivate
|
|
*/
|
|
export const deactivateCoupon = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await couponService.deactivateCoupon(tenantId, id, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Redeem a coupon
|
|
* POST /api/pricing/coupons/:id/redeem
|
|
*/
|
|
export const redeemCoupon = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
const input = req.body as RedeemCouponInput;
|
|
|
|
// Get coupon code from ID
|
|
const couponResult = await couponService.getById(tenantId, id);
|
|
if (!couponResult.success || !couponResult.data) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: 'Coupon not found',
|
|
});
|
|
return;
|
|
}
|
|
|
|
const result = await couponService.redeemCoupon(
|
|
tenantId,
|
|
couponResult.data.code,
|
|
input,
|
|
userId
|
|
);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Redeem coupon by code
|
|
* POST /api/pricing/coupons/redeem
|
|
*/
|
|
export const redeemCouponByCode = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { code, ...input } = req.body as RedeemCouponInput & { code: string };
|
|
|
|
const result = await couponService.redeemCoupon(tenantId, code, input, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Reverse a coupon redemption
|
|
* POST /api/pricing/coupons/redemptions/:redemptionId/reverse
|
|
*/
|
|
export const reverseRedemption = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { redemptionId } = req.params;
|
|
const { reason } = req.body as { reason: string };
|
|
|
|
const result = await couponService.reverseRedemption(
|
|
tenantId,
|
|
redemptionId,
|
|
reason,
|
|
userId
|
|
);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get coupon redemption history
|
|
* GET /api/pricing/coupons/:id/redemptions
|
|
*/
|
|
export const getCouponRedemptions = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const { id } = req.params;
|
|
const { page = 1, limit = 20 } = req.query;
|
|
|
|
const result = await couponService.getRedemptionHistory(
|
|
tenantId,
|
|
id,
|
|
Number(page),
|
|
Number(limit)
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
meta: result.meta,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Delete a coupon
|
|
* DELETE /api/pricing/coupons/:id
|
|
*/
|
|
export const deleteCoupon = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.user?.id as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await couponService.softDelete(tenantId, id, userId);
|
|
|
|
if (!result.success) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Coupon deleted',
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get coupon statistics
|
|
* GET /api/pricing/coupons/:id/stats
|
|
*/
|
|
export const getCouponStats = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> => {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const { id } = req.params;
|
|
|
|
const result = await couponService.getCouponStats(tenantId, id);
|
|
|
|
if (!result.success) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: result.error,
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.data,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|