farsi messages
This commit is contained in:
@@ -66,6 +66,10 @@ export const enum AuthMessage {
|
|||||||
TOKEN_MISSED_OR_EXPIRED = 'توکن مفقود یا منقضی شده است',
|
TOKEN_MISSED_OR_EXPIRED = 'توکن مفقود یا منقضی شده است',
|
||||||
OLD_PASSWORD_REQUIRED = 'رمز عبور قبلی الزامی است',
|
OLD_PASSWORD_REQUIRED = 'رمز عبور قبلی الزامی است',
|
||||||
PASSWORD_CHANGE_FAILED = 'تغییر رمز عبور با مشکل روبرد شد',
|
PASSWORD_CHANGE_FAILED = 'تغییر رمز عبور با مشکل روبرد شد',
|
||||||
|
SLUG_REQUIRED = 'اسلاگ الزامی است',
|
||||||
|
INVALID_TOKEN_PAYLOAD = 'محتویات توکن نامعتبر است',
|
||||||
|
INVALID_SLUG = 'اسلاگ نامعتبر است',
|
||||||
|
INVALID_OR_EXPIRED_TOKEN = 'توکن نامعتبر یا منقضی شده است',
|
||||||
}
|
}
|
||||||
export const enum FoodMessage {
|
export const enum FoodMessage {
|
||||||
NOT_FOUND = 'غذایی با این مشخصات یافت نشد',
|
NOT_FOUND = 'غذایی با این مشخصات یافت نشد',
|
||||||
@@ -579,6 +583,12 @@ export const enum NotificationMessage {
|
|||||||
NEW_EMAIL_MESSAGE = 'یک ایمیل جدید با موضوع [subject] دریافت شد',
|
NEW_EMAIL_MESSAGE = 'یک ایمیل جدید با موضوع [subject] دریافت شد',
|
||||||
CHANGE_PASSWORD_MESSAGE = 'رمز عبور شما با موفقیت تغییر کرد',
|
CHANGE_PASSWORD_MESSAGE = 'رمز عبور شما با موفقیت تغییر کرد',
|
||||||
CHANGE_PASSWORD = 'تغییر رمز عبور',
|
CHANGE_PASSWORD = 'تغییر رمز عبور',
|
||||||
|
NOT_FOUND = 'اعلان یافت نشد',
|
||||||
|
READ_SUCCESS = 'اعلان با موفقیت خوانده شد',
|
||||||
|
ORDER_CREATED = 'سفارش جدید ایجاد شد',
|
||||||
|
ORDER_STATUS_CHANGED = 'وضعیت سفارش تغییر کرد',
|
||||||
|
PAYMENT_SUCCESS = 'پرداخت با موفقیت انجام شد',
|
||||||
|
PAGER_CREATED = 'پیج جدید ایجاد شد',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum SettingMessageEnum {
|
export const enum SettingMessageEnum {
|
||||||
@@ -664,3 +674,101 @@ export const enum GroupMessage {
|
|||||||
NOT_UPDATED = 'بهروزرسانی گروه آیکون با خطا مواجه شد',
|
NOT_UPDATED = 'بهروزرسانی گروه آیکون با خطا مواجه شد',
|
||||||
NOT_DELETED = 'حذف گروه آیکون با خطا مواجه شد',
|
NOT_DELETED = 'حذف گروه آیکون با خطا مواجه شد',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum OrderMessage {
|
||||||
|
NOT_FOUND = 'سفارش یافت نشد',
|
||||||
|
PAYMENT_METHOD_MISSING = 'روش پرداخت سفارش مشخص نشده است',
|
||||||
|
INVALID_STATUS_TRANSITION = 'انتقال وضعیت نامعتبر است',
|
||||||
|
CART_EMPTY = 'سبد خرید خالی است. لطفا قبل از ایجاد سفارش، آیتمهایی به سبد اضافه کنید',
|
||||||
|
DELIVERY_METHOD_NOT_FOUND = 'روش ارسال یافت نشد',
|
||||||
|
PAYMENT_METHOD_REQUIRED = 'روش پرداخت الزامی است. لطفا قبل از ایجاد سفارش، روش پرداخت را تنظیم کنید',
|
||||||
|
USER_NOT_FOUND = 'کاربر یافت نشد',
|
||||||
|
RESTAURANT_NOT_FOUND = 'رستوران یافت نشد',
|
||||||
|
DELIVERY_NOT_FOUND = 'روش ارسال یافت نشد',
|
||||||
|
MIN_ORDER_AMOUNT_NOT_MET = 'مبلغ سفارش کمتر از حداقل مجاز برای این روش ارسال است',
|
||||||
|
TABLE_NUMBER_REQUIRED = 'شماره میز برای سفارش در محل الزامی است',
|
||||||
|
ADDRESS_REQUIRED = 'آدرس الزامی است. لطفا قبل از ایجاد سفارش، آدرس تحویل را تنظیم کنید',
|
||||||
|
CAR_ADDRESS_REQUIRED = 'آدرس خودرو الزامی است. لطفا قبل از ایجاد سفارش، آدرس خودرو را تنظیم کنید',
|
||||||
|
PAYMENT_METHOD_NOT_FOUND = 'روش پرداخت یافت نشد',
|
||||||
|
PAYMENT_METHOD_NOT_ENABLED = 'روش پرداخت برای این رستوران فعال نیست',
|
||||||
|
FOOD_NOT_FOUND = 'غذا یافت نشد',
|
||||||
|
FOOD_NOT_BELONGS_TO_RESTAURANT = 'غذا به این رستوران تعلق ندارد',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum CartMessage {
|
||||||
|
TABLE_NUMBER_REQUIRED = 'شماره میز برای سفارش در محل الزامی است',
|
||||||
|
CAR_ADDRESS_REQUIRED = 'آدرس خودرو برای تحویل به خودرو الزامی است',
|
||||||
|
ADDRESS_REQUIRED = 'آدرس برای تحویل پیک الزامی است',
|
||||||
|
NOT_FOUND = 'سبد خرید یافت نشد',
|
||||||
|
COUPON_NOT_FOUND = 'کوپن یافت نشد',
|
||||||
|
FOOD_NOT_FOUND = 'غذا یافت نشد',
|
||||||
|
FOOD_NOT_BELONGS_TO_RESTAURANT = 'غذا به این رستوران تعلق ندارد',
|
||||||
|
FOOD_NO_INVENTORY = 'غذا موجودی ندارد',
|
||||||
|
INSUFFICIENT_STOCK = 'موجودی کافی نیست',
|
||||||
|
USER_NOT_FOUND = 'کاربر یافت نشد',
|
||||||
|
ADDRESS_NOT_FOUND = 'آدرس یافت نشد',
|
||||||
|
RESTAURANT_NOT_FOUND = 'رستوران یافت نشد',
|
||||||
|
ADDRESS_NOT_BELONGS_TO_USER = 'آدرس به کاربر فعلی تعلق ندارد',
|
||||||
|
ADDRESS_NO_COORDINATES = 'آدرس مختصات جغرافیایی ندارد',
|
||||||
|
ADDRESS_OUTSIDE_SERVICE_AREA = 'آدرس خارج از محدوده سرویس رستوران است',
|
||||||
|
DELIVERY_METHOD_NOT_FOUND = 'روش ارسال یافت نشد',
|
||||||
|
DELIVERY_METHOD_NOT_ENABLED = 'روش ارسال برای این رستوران فعال نیست',
|
||||||
|
PAYMENT_METHOD_NOT_FOUND = 'روش پرداخت یافت نشد',
|
||||||
|
PAYMENT_METHOD_NOT_ENABLED = 'روش پرداخت برای این رستوران فعال نیست',
|
||||||
|
WALLET_NOT_FOUND = 'کیف پول کاربر یافت نشد',
|
||||||
|
WALLET_INSUFFICIENT = 'موجودی کیف پول کافی نیست',
|
||||||
|
COUPON_MAX_USES_REACHED = 'حداکثر استفاده از کوپن به پایان رسیده است',
|
||||||
|
ITEM_NOT_FOUND = 'آیتم در سبد خرید یافت نشد',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum PaymentMessage {
|
||||||
|
UNSUPPORTED_PAYMENT_METHOD = 'روش پرداخت پشتیبانی نمیشود',
|
||||||
|
ORDER_NOT_FOUND = 'سفارش یافت نشد',
|
||||||
|
AMOUNT_MUST_BE_GREATER_THAN_ZERO = 'مبلغ باید بیشتر از صفر باشد',
|
||||||
|
PAYMENT_METHOD_NOT_FOUND = 'روش پرداخت یافت نشد',
|
||||||
|
GATEWAY_REQUIRED = 'درگاه پرداخت برای پرداخت آنلاین الزامی است',
|
||||||
|
MERCHANT_ID_REQUIRED = 'شناسه مرچنت برای پرداخت آنلاین الزامی است',
|
||||||
|
RESTAURANT_DOMAIN_REQUIRED = 'دامنه رستوران برای پرداخت آنلاین الزامی است',
|
||||||
|
WALLET_NOT_FOUND = 'کیف پول کاربر یافت نشد',
|
||||||
|
INSUFFICIENT_WALLET_BALANCE = 'موجودی کیف پول کافی نیست',
|
||||||
|
PAYMENT_NOT_FOUND = 'پرداخت یافت نشد',
|
||||||
|
INVALID_PAYMENT_CONFIGURATION = 'پیکربندی پرداخت نامعتبر است',
|
||||||
|
PAYMENT_METHOD_NOT_CASH = 'روش پرداخت نقدی نیست',
|
||||||
|
PAYMENT_ALREADY_PAID = 'پرداخت قبلا انجام شده است',
|
||||||
|
EXISTING_PENDING_PAYMENT_MISMATCH = 'پرداخت در انتظار موجود با روش پرداخت سفارش مطابقت ندارد',
|
||||||
|
ZARINPAL_INVALID_RESPONSE = 'پاسخ نامعتبر از API زرینپال',
|
||||||
|
ZARINPAL_PAYMENT_REQUEST_ERROR = 'خطا در درخواست پرداخت زرینپال',
|
||||||
|
ZARINPAL_VERIFY_ERROR = 'خطا در تایید پرداخت زرینپال',
|
||||||
|
GATEWAY_ERROR = 'خطا در اتصال به درگاه پرداخت',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum DeliveryMessage {
|
||||||
|
RESTAURANT_NOT_FOUND = 'رستوران یافت نشد',
|
||||||
|
DELIVERY_METHOD_NOT_FOUND = 'روش ارسال یافت نشد',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum InventoryMessage {
|
||||||
|
AVAILABLE_STOCK_EXCEEDS_TOTAL = 'موجودی موجود نمیتواند از موجودی کل بیشتر باشد',
|
||||||
|
FOOD_NOT_FOUND = 'غذا یافت نشد',
|
||||||
|
FOOD_NOT_BELONGS_TO_RESTAURANT = 'غذا به این رستوران تعلق ندارد',
|
||||||
|
RESTAURANT_NOT_FOUND = 'رستوران یافت نشد',
|
||||||
|
FOODS_NOT_FOUND = 'غذاها یافت نشدند',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const enum PagerMessage {
|
||||||
|
CREATED_SUCCESS = 'درخواست پیج با موفقیت ایجاد شد',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum UserSuccessMessage {
|
||||||
|
ADDRESS_DELETED_SUCCESS = 'آدرس با موفقیت حذف شد',
|
||||||
|
SCORE_CONVERTED_SUCCESS = 'امتیاز با موفقیت به کیف پول تبدیل شد',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum ContactMessage {
|
||||||
|
NOT_FOUND = 'تماس یافت نشد',
|
||||||
|
CREATED = 'تماس با موفقیت ثبت شد',
|
||||||
|
UPDATED = 'تماس با موفقیت بهروزرسانی شد',
|
||||||
|
DELETED = 'تماس با موفقیت حذف شد',
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Food } from '../../foods/entities/food.entity';
|
|||||||
import { Cart, CartItem } from '../interfaces/cart.interface';
|
import { Cart, CartItem } from '../interfaces/cart.interface';
|
||||||
import { CartValidationService } from './cart-validation.service';
|
import { CartValidationService } from './cart-validation.service';
|
||||||
import { CartCalculationService } from './cart-calculation.service';
|
import { CartCalculationService } from './cart-calculation.service';
|
||||||
|
import { CartMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CartItemService {
|
export class CartItemService {
|
||||||
@@ -83,7 +84,7 @@ export class CartItemService {
|
|||||||
removeItemOrFail(cart: Cart, foodId: string): void {
|
removeItemOrFail(cart: Cart, foodId: string): void {
|
||||||
const itemIndex = this.getItemIndex(cart, foodId);
|
const itemIndex = this.getItemIndex(cart, foodId);
|
||||||
if (itemIndex < 0) {
|
if (itemIndex < 0) {
|
||||||
throw new NotFoundException(`Item with food ID ${foodId} not found in cart`);
|
throw new NotFoundException(CartMessage.ITEM_NOT_FOUND);
|
||||||
}
|
}
|
||||||
cart.items.splice(itemIndex, 1);
|
cart.items.splice(itemIndex, 1);
|
||||||
}
|
}
|
||||||
@@ -94,7 +95,7 @@ export class CartItemService {
|
|||||||
async decrementOrRemoveItem(cart: Cart, foodId: string): Promise<void> {
|
async decrementOrRemoveItem(cart: Cart, foodId: string): Promise<void> {
|
||||||
const itemIndex = this.getItemIndex(cart, foodId);
|
const itemIndex = this.getItemIndex(cart, foodId);
|
||||||
if (itemIndex < 0) {
|
if (itemIndex < 0) {
|
||||||
throw new NotFoundException(`Item with food ID ${foodId} not found in cart`);
|
throw new NotFoundException(CartMessage.ITEM_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingItem = cart.items[itemIndex];
|
const existingItem = cart.items[itemIndex];
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { OrderStatus } from '../../orders/interface/order.interface';
|
|||||||
import { Cart } from '../interfaces/cart.interface';
|
import { Cart } from '../interfaces/cart.interface';
|
||||||
import { GeographicUtils } from '../utils/geographic.utils';
|
import { GeographicUtils } from '../utils/geographic.utils';
|
||||||
import { MealType } from '../../foods/interface/food.interface';
|
import { MealType } from '../../foods/interface/food.interface';
|
||||||
|
import { CartMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CartValidationService {
|
export class CartValidationService {
|
||||||
@@ -24,15 +25,15 @@ export class CartValidationService {
|
|||||||
async validateAndGetFood(foodId: string, restaurantId: string, quantity: number): Promise<Food> {
|
async validateAndGetFood(foodId: string, restaurantId: string, quantity: number): Promise<Food> {
|
||||||
const food = await this.em.findOne(Food, { id: foodId }, { populate: ['restaurant', 'inventory'] });
|
const food = await this.em.findOne(Food, { id: foodId }, { populate: ['restaurant', 'inventory'] });
|
||||||
if (!food) {
|
if (!food) {
|
||||||
throw new NotFoundException(`Food with ID ${foodId} not found`);
|
throw new NotFoundException(CartMessage.FOOD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (food.restaurant.id !== restaurantId) {
|
if (food.restaurant.id !== restaurantId) {
|
||||||
throw new BadRequestException(`Food ${food.title || food.id} does not belong to restaurant ${restaurantId}`);
|
throw new BadRequestException(CartMessage.FOOD_NOT_BELONGS_TO_RESTAURANT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!food.inventory) {
|
if (!food.inventory) {
|
||||||
throw new BadRequestException(`The food ${food.title || food.id} does not have inventory`);
|
throw new BadRequestException(CartMessage.FOOD_NO_INVENTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.validateStock(food, quantity);
|
this.validateStock(food, quantity);
|
||||||
@@ -45,7 +46,7 @@ export class CartValidationService {
|
|||||||
validateStock(food: Food, quantity: number): void {
|
validateStock(food: Food, quantity: number): void {
|
||||||
const availableStock = food.inventory!.availableStock;
|
const availableStock = food.inventory!.availableStock;
|
||||||
if (availableStock < quantity) {
|
if (availableStock < quantity) {
|
||||||
throw new BadRequestException(`Insufficient stock for food ${food.title}. Available: ${availableStock}`);
|
throw new BadRequestException(CartMessage.INSUFFICIENT_STOCK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ export class CartValidationService {
|
|||||||
async getUserOrFail(userId: string): Promise<User> {
|
async getUserOrFail(userId: string): Promise<User> {
|
||||||
const user = await this.em.findOne(User, { id: userId });
|
const user = await this.em.findOne(User, { id: userId });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new NotFoundException(`User with ID ${userId} not found`);
|
throw new NotFoundException(CartMessage.USER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
@@ -66,7 +67,7 @@ export class CartValidationService {
|
|||||||
async getUserAddressOrFail(addressId: string): Promise<UserAddress> {
|
async getUserAddressOrFail(addressId: string): Promise<UserAddress> {
|
||||||
const address = await this.em.findOne(UserAddress, { id: addressId }, { populate: ['user'] });
|
const address = await this.em.findOne(UserAddress, { id: addressId }, { populate: ['user'] });
|
||||||
if (!address) {
|
if (!address) {
|
||||||
throw new NotFoundException(`Address with ID ${addressId} not found`);
|
throw new NotFoundException(CartMessage.ADDRESS_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
@@ -77,7 +78,7 @@ export class CartValidationService {
|
|||||||
async getFoodOrFail(foodId: string): Promise<Food> {
|
async getFoodOrFail(foodId: string): Promise<Food> {
|
||||||
const food = await this.em.findOne(Food, { id: foodId });
|
const food = await this.em.findOne(Food, { id: foodId });
|
||||||
if (!food) {
|
if (!food) {
|
||||||
throw new NotFoundException(`Food with ID ${foodId} not found`);
|
throw new NotFoundException(CartMessage.FOOD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return food;
|
return food;
|
||||||
}
|
}
|
||||||
@@ -88,7 +89,7 @@ export class CartValidationService {
|
|||||||
async getRestaurantOrFail(restaurantId: string): Promise<Restaurant> {
|
async getRestaurantOrFail(restaurantId: string): Promise<Restaurant> {
|
||||||
const restaurant = await this.em.findOne(Restaurant, { id: restaurantId });
|
const restaurant = await this.em.findOne(Restaurant, { id: restaurantId });
|
||||||
if (!restaurant) {
|
if (!restaurant) {
|
||||||
throw new NotFoundException('Restaurant not found');
|
throw new NotFoundException(CartMessage.RESTAURANT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return restaurant;
|
return restaurant;
|
||||||
}
|
}
|
||||||
@@ -98,7 +99,7 @@ export class CartValidationService {
|
|||||||
*/
|
*/
|
||||||
validateAddressOwnership(address: UserAddress, userId: string): void {
|
validateAddressOwnership(address: UserAddress, userId: string): void {
|
||||||
if (address.user.id !== userId) {
|
if (address.user.id !== userId) {
|
||||||
throw new BadRequestException('Address does not belong to the current user');
|
throw new BadRequestException(CartMessage.ADDRESS_NOT_BELONGS_TO_USER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +134,7 @@ export class CartValidationService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!GeographicUtils.isPointInPolygon(point, polygon)) {
|
if (!GeographicUtils.isPointInPolygon(point, polygon)) {
|
||||||
throw new BadRequestException('Address is outside the restaurant service area');
|
throw new BadRequestException(CartMessage.ADDRESS_OUTSIDE_SERVICE_AREA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,12 +169,10 @@ export class CartValidationService {
|
|||||||
{ populate: ['restaurant'] },
|
{ populate: ['restaurant'] },
|
||||||
);
|
);
|
||||||
if (!deliveryMethod) {
|
if (!deliveryMethod) {
|
||||||
throw new NotFoundException(
|
throw new NotFoundException(CartMessage.DELIVERY_METHOD_NOT_FOUND);
|
||||||
`Delivery method with ID ${deliveryMethodId} not found for restaurant ${restaurantId}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!deliveryMethod.enabled) {
|
if (!deliveryMethod.enabled) {
|
||||||
throw new BadRequestException('Delivery method is not enabled for this restaurant');
|
throw new BadRequestException(CartMessage.DELIVERY_METHOD_NOT_ENABLED);
|
||||||
}
|
}
|
||||||
return deliveryMethod;
|
return deliveryMethod;
|
||||||
}
|
}
|
||||||
@@ -207,10 +206,10 @@ export class CartValidationService {
|
|||||||
{ populate: ['restaurant'] },
|
{ populate: ['restaurant'] },
|
||||||
);
|
);
|
||||||
if (!paymentMethod) {
|
if (!paymentMethod) {
|
||||||
throw new NotFoundException(`Payment method with ID ${paymentMethodId} not found for restaurant ${restaurantId}`);
|
throw new NotFoundException(CartMessage.PAYMENT_METHOD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
if (!paymentMethod.enabled) {
|
if (!paymentMethod.enabled) {
|
||||||
throw new BadRequestException('Payment method is not enabled for this restaurant');
|
throw new BadRequestException(CartMessage.PAYMENT_METHOD_NOT_ENABLED);
|
||||||
}
|
}
|
||||||
return paymentMethod;
|
return paymentMethod;
|
||||||
}
|
}
|
||||||
@@ -225,11 +224,11 @@ export class CartValidationService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!wallet) {
|
if (!wallet) {
|
||||||
throw new BadRequestException('User wallet not found');
|
throw new BadRequestException(CartMessage.WALLET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wallet.wallet < amount) {
|
if (wallet.wallet < amount) {
|
||||||
throw new BadRequestException('User wallet is not enough');
|
throw new BadRequestException(CartMessage.WALLET_INSUFFICIENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +246,7 @@ export class CartValidationService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (ordersThatUsedTheCouponCount >= maxUsesPerUser) {
|
if (ordersThatUsedTheCouponCount >= maxUsesPerUser) {
|
||||||
throw new BadRequestException(`You have reached the maximum number of uses (${maxUsesPerUser}) for this coupon`);
|
throw new BadRequestException(CartMessage.COUPON_MAX_USES_REACHED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { CartValidationService } from './cart-validation.service';
|
|||||||
import { CartCalculationService } from './cart-calculation.service';
|
import { CartCalculationService } from './cart-calculation.service';
|
||||||
import { CartItemService } from './cart-item.service';
|
import { CartItemService } from './cart-item.service';
|
||||||
import { SetCarDeliveryDto } from '../dto/set-car-delivery.dto';
|
import { SetCarDeliveryDto } from '../dto/set-car-delivery.dto';
|
||||||
|
import { CartMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CartService {
|
export class CartService {
|
||||||
@@ -44,7 +45,7 @@ export class CartService {
|
|||||||
case DeliveryMethodEnum.DineIn:
|
case DeliveryMethodEnum.DineIn:
|
||||||
// DineIn requires table number and clears incompatible fields
|
// DineIn requires table number and clears incompatible fields
|
||||||
if (!tableNumber) {
|
if (!tableNumber) {
|
||||||
throw new BadRequestException('Table number is required for dine-in orders');
|
throw new BadRequestException(CartMessage.TABLE_NUMBER_REQUIRED);
|
||||||
}
|
}
|
||||||
this.clearFieldsIncompatibleWithDeliveryMethod(cart, DeliveryMethodEnum.DineIn);
|
this.clearFieldsIncompatibleWithDeliveryMethod(cart, DeliveryMethodEnum.DineIn);
|
||||||
cart.tableNumber = tableNumber;
|
cart.tableNumber = tableNumber;
|
||||||
@@ -58,7 +59,7 @@ export class CartService {
|
|||||||
case DeliveryMethodEnum.DeliveryCar:
|
case DeliveryMethodEnum.DeliveryCar:
|
||||||
// DeliveryCar requires carAddress and clears incompatible fields
|
// DeliveryCar requires carAddress and clears incompatible fields
|
||||||
if (!carAddress) {
|
if (!carAddress) {
|
||||||
throw new BadRequestException('Car address is required for car delivery');
|
throw new BadRequestException(CartMessage.CAR_ADDRESS_REQUIRED);
|
||||||
}
|
}
|
||||||
this.clearFieldsIncompatibleWithDeliveryMethod(cart, DeliveryMethodEnum.DeliveryCar);
|
this.clearFieldsIncompatibleWithDeliveryMethod(cart, DeliveryMethodEnum.DeliveryCar);
|
||||||
const user = await this.validationService.getUserOrFail(userId);
|
const user = await this.validationService.getUserOrFail(userId);
|
||||||
@@ -73,7 +74,7 @@ export class CartService {
|
|||||||
case DeliveryMethodEnum.DeliveryCourier:
|
case DeliveryMethodEnum.DeliveryCourier:
|
||||||
// DeliveryCourier requires addressId and clears incompatible fields
|
// DeliveryCourier requires addressId and clears incompatible fields
|
||||||
if (!addressId) {
|
if (!addressId) {
|
||||||
throw new BadRequestException('Address is required for courier delivery');
|
throw new BadRequestException(CartMessage.ADDRESS_REQUIRED);
|
||||||
}
|
}
|
||||||
this.clearFieldsIncompatibleWithDeliveryMethod(cart, DeliveryMethodEnum.DeliveryCourier);
|
this.clearFieldsIncompatibleWithDeliveryMethod(cart, DeliveryMethodEnum.DeliveryCourier);
|
||||||
const address = await this.validationService.getUserAddressOrFail(addressId);
|
const address = await this.validationService.getUserAddressOrFail(addressId);
|
||||||
@@ -167,7 +168,7 @@ export class CartService {
|
|||||||
async findOneOrFail(userId: string, restaurantId: string): Promise<Cart> {
|
async findOneOrFail(userId: string, restaurantId: string): Promise<Cart> {
|
||||||
const cart = await this.cartRepository.findByUserAndRestaurant(userId, restaurantId);
|
const cart = await this.cartRepository.findByUserAndRestaurant(userId, restaurantId);
|
||||||
if (!cart) {
|
if (!cart) {
|
||||||
throw new NotFoundException('Cart not found');
|
throw new NotFoundException(CartMessage.NOT_FOUND);
|
||||||
}
|
}
|
||||||
return cart;
|
return cart;
|
||||||
}
|
}
|
||||||
@@ -229,11 +230,11 @@ export class CartService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
throw new BadRequestException(message || 'Invalid coupon code');
|
throw new BadRequestException(message || CartMessage.COUPON_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!coupon) {
|
if (!coupon) {
|
||||||
throw new BadRequestException('Coupon not found');
|
throw new BadRequestException(CartMessage.COUPON_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check maxUsesPerUser limit by counting how many orders this user has with this coupon
|
// Check maxUsesPerUser limit by counting how many orders this user has with this coupon
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { CreateDeliveryDto } from '../dto/create-delivery.dto';
|
|||||||
import { UpdateDeliveryDto } from '../dto/update-delivery.dto';
|
import { UpdateDeliveryDto } from '../dto/update-delivery.dto';
|
||||||
import { RestRepository } from '../../restaurants/repositories/rest.repository';
|
import { RestRepository } from '../../restaurants/repositories/rest.repository';
|
||||||
import { DeliveryMethodEnum } from '../interface/delivery';
|
import { DeliveryMethodEnum } from '../interface/delivery';
|
||||||
|
import { DeliveryMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DeliveryService {
|
export class DeliveryService {
|
||||||
@@ -18,7 +19,7 @@ export class DeliveryService {
|
|||||||
async create(restId: string, createDto: CreateDeliveryDto): Promise<Delivery> {
|
async create(restId: string, createDto: CreateDeliveryDto): Promise<Delivery> {
|
||||||
const restaurant = await this.restRepository.findOne({ id: restId });
|
const restaurant = await this.restRepository.findOne({ id: restId });
|
||||||
if (!restaurant) {
|
if (!restaurant) {
|
||||||
throw new NotFoundException(`رستوران با شناسه ${restId} یافت نشد.`);
|
throw new NotFoundException(DeliveryMessage.RESTAURANT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the delivery method with the same name already exists for this restaurant
|
// Check if the delivery method with the same name already exists for this restaurant
|
||||||
@@ -60,7 +61,7 @@ export class DeliveryService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!delivery) {
|
if (!delivery) {
|
||||||
throw new NotFoundException(`روش ارسال با شناسه ${deliveryId} برای رستوران با شناسه ${restId} یافت نشد.`);
|
throw new NotFoundException(DeliveryMessage.DELIVERY_METHOD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
return delivery;
|
return delivery;
|
||||||
@@ -73,7 +74,7 @@ export class DeliveryService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!delivery) {
|
if (!delivery) {
|
||||||
throw new NotFoundException(`روش ارسال با شناسه ${deliveryId} برای رستوران با شناسه ${restId} یافت نشد.`);
|
throw new NotFoundException(DeliveryMessage.DELIVERY_METHOD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If method is being updated, check for conflicts
|
// If method is being updated, check for conflicts
|
||||||
@@ -102,7 +103,7 @@ export class DeliveryService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!delivery) {
|
if (!delivery) {
|
||||||
throw new NotFoundException(`روش ارسال با شناسه ${deliveryId} برای رستوران با شناسه ${restId} یافت نشد.`);
|
throw new NotFoundException(DeliveryMessage.DELIVERY_METHOD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.em.removeAndFlush(delivery);
|
await this.em.removeAndFlush(delivery);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { RestId } from '../../../common/decorators/rest-id.decorator';
|
|||||||
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
|
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
|
||||||
import { AdminId } from 'src/common/decorators/admin-id.decorator';
|
import { AdminId } from 'src/common/decorators/admin-id.decorator';
|
||||||
import { API_HEADER_SLUG } from 'src/common/constants/index';
|
import { API_HEADER_SLUG } from 'src/common/constants/index';
|
||||||
|
import { NotificationMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@ApiTags('notifications')
|
@ApiTags('notifications')
|
||||||
@Controller()
|
@Controller()
|
||||||
@@ -64,7 +65,7 @@ export class NotificationsController {
|
|||||||
@ApiParam({ name: 'id', description: 'Notification ID' })
|
@ApiParam({ name: 'id', description: 'Notification ID' })
|
||||||
async readNotificationUser(@RestId() restaurantId: string, @Param('id') id: string, @UserId() userId: string) {
|
async readNotificationUser(@RestId() restaurantId: string, @Param('id') id: string, @UserId() userId: string) {
|
||||||
await this.notificationService.readNotificationAsUser(id, userId, restaurantId);
|
await this.notificationService.readNotificationAsUser(id, userId, restaurantId);
|
||||||
return { message: 'Notification read successfully' };
|
return { message: NotificationMessage.READ_SUCCESS };
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@@ -73,7 +74,7 @@ export class NotificationsController {
|
|||||||
@ApiOperation({ summary: 'Read all notification ' })
|
@ApiOperation({ summary: 'Read all notification ' })
|
||||||
async readAllNotificationUser(@RestId() restaurantId: string, @UserId() userId: string) {
|
async readAllNotificationUser(@RestId() restaurantId: string, @UserId() userId: string) {
|
||||||
await this.notificationService.readAllNotifsAsUser(userId, restaurantId);
|
await this.notificationService.readAllNotifsAsUser(userId, restaurantId);
|
||||||
return { message: 'Notification read successfully' };
|
return { message: NotificationMessage.READ_SUCCESS };
|
||||||
}
|
}
|
||||||
/* ***************** Admin Endpoints ***************** */
|
/* ***************** Admin Endpoints ***************** */
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ export class NotificationsController {
|
|||||||
@ApiParam({ name: 'id', description: 'Notification ID' })
|
@ApiParam({ name: 'id', description: 'Notification ID' })
|
||||||
async readNotificationAdmin(@RestId() restaurantId: string, @AdminId() adminId: string, @Param('id') id: string) {
|
async readNotificationAdmin(@RestId() restaurantId: string, @AdminId() adminId: string, @Param('id') id: string) {
|
||||||
await this.notificationService.readNotificationAdmin(id, adminId, restaurantId);
|
await this.notificationService.readNotificationAdmin(id, adminId, restaurantId);
|
||||||
return { message: 'Notification read successfully' };
|
return { message: NotificationMessage.READ_SUCCESS };
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
@@ -125,7 +126,7 @@ export class NotificationsController {
|
|||||||
@ApiOperation({ summary: 'Read all notification ' })
|
@ApiOperation({ summary: 'Read all notification ' })
|
||||||
async readAllNotificationAdmin(@RestId() restaurantId: string, @AdminId() adminId: string, @Param('id') id: string) {
|
async readAllNotificationAdmin(@RestId() restaurantId: string, @AdminId() adminId: string, @Param('id') id: string) {
|
||||||
await this.notificationService.readAllNotifsAsAdmin(adminId, restaurantId);
|
await this.notificationService.readAllNotifsAsAdmin(adminId, restaurantId);
|
||||||
return { message: 'Notification read successfully' };
|
return { message: NotificationMessage.READ_SUCCESS };
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
|
|||||||
@@ -24,6 +24,21 @@ export class OrderListeners {
|
|||||||
this.orderStatusChangedSmsTemplateId = this.configService.get<string>('SMS_PATTERN_ORDER_STATUS_CHANGE') ?? '123';
|
this.orderStatusChangedSmsTemplateId = this.configService.get<string>('SMS_PATTERN_ORDER_STATUS_CHANGE') ?? '123';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getStatusFarsi(status: OrderStatus): string {
|
||||||
|
const statusMap: Record<OrderStatus, string> = {
|
||||||
|
[OrderStatus.PENDING_PAYMENT]: 'در انتظار پرداخت',
|
||||||
|
[OrderStatus.PAID]: 'پرداخت شده',
|
||||||
|
[OrderStatus.CONFIRMED]: 'تایید شده',
|
||||||
|
[OrderStatus.PREPARING]: 'در حال آمادهسازی',
|
||||||
|
[OrderStatus.DELIVERED_TO_RECEPTIONIST]: 'تحویل به پذیرش',
|
||||||
|
[OrderStatus.DELIVERED_TO_WAITER]: 'تحویل به گارسون',
|
||||||
|
[OrderStatus.SHIPPED]: 'ارسال شده',
|
||||||
|
[OrderStatus.COMPLETED]: 'تکمیل شده',
|
||||||
|
[OrderStatus.CANCELED]: 'لغو شده',
|
||||||
|
};
|
||||||
|
return statusMap[status] || status;
|
||||||
|
}
|
||||||
|
|
||||||
@OnEvent(OrderCreatedEvent.name)
|
@OnEvent(OrderCreatedEvent.name)
|
||||||
async handleOrderCreated(event: OrderCreatedEvent) {
|
async handleOrderCreated(event: OrderCreatedEvent) {
|
||||||
try {
|
try {
|
||||||
@@ -39,7 +54,7 @@ export class OrderListeners {
|
|||||||
restaurantId: event.restaurantId,
|
restaurantId: event.restaurantId,
|
||||||
message: {
|
message: {
|
||||||
title: NotifTitleEnum.ORDER_CREATED,
|
title: NotifTitleEnum.ORDER_CREATED,
|
||||||
content: `Order ${event.orderNumber} with ${event.total} has been created successfully`,
|
content: `سفارش شماره ${event.orderNumber} با مبلغ ${event.total} تومان با موفقیت ایجاد شد`,
|
||||||
sms: {
|
sms: {
|
||||||
templateId: this.orderCreatedSmsTemplateId,
|
templateId: this.orderCreatedSmsTemplateId,
|
||||||
parameters: {
|
parameters: {
|
||||||
@@ -48,8 +63,8 @@ export class OrderListeners {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
pushNotif: {
|
pushNotif: {
|
||||||
title: `Order ${event.orderNumber} with ${event.total} has been created successfully`,
|
title: `سفارش جدید`,
|
||||||
content: `Order ${event.orderNumber} with ${event.total} has been created successfully`,
|
content: `سفارش شماره ${event.orderNumber} با مبلغ ${event.total} تومان با موفقیت ایجاد شد`,
|
||||||
icon: `/`,
|
icon: `/`,
|
||||||
action: {
|
action: {
|
||||||
type: NotifTitleEnum.ORDER_CREATED,
|
type: NotifTitleEnum.ORDER_CREATED,
|
||||||
@@ -95,7 +110,7 @@ export class OrderListeners {
|
|||||||
restaurantId: event.restaurantId,
|
restaurantId: event.restaurantId,
|
||||||
message: {
|
message: {
|
||||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
content: `Order #${event.orderNumber} status is changed to ${event.newStatus} .`,
|
content: `وضعیت سفارش شماره ${event.orderNumber} به ${this.getStatusFarsi(event.newStatus)} تغییر کرد`,
|
||||||
sms: {
|
sms: {
|
||||||
templateId: this.orderStatusChangedSmsTemplateId,
|
templateId: this.orderStatusChangedSmsTemplateId,
|
||||||
parameters: {
|
parameters: {
|
||||||
@@ -104,8 +119,8 @@ export class OrderListeners {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
pushNotif: {
|
pushNotif: {
|
||||||
title: `Order #${event.orderNumber} status is changed to ${event.newStatus} .`,
|
title: `تغییر وضعیت سفارش`,
|
||||||
content: `Order #${event.orderNumber} status is changed to ${event.newStatus} .`,
|
content: `وضعیت سفارش شماره ${event.orderNumber} به ${this.getStatusFarsi(event.newStatus)} تغییر کرد`,
|
||||||
icon: `/`,
|
icon: `/`,
|
||||||
action: {
|
action: {
|
||||||
type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
@@ -126,140 +141,140 @@ export class OrderListeners {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private factory(orderStatus: OrderStatus, event: OrderStatusChangedEvent) {
|
// private factory(orderStatus: OrderStatus, event: OrderStatusChangedEvent) {
|
||||||
switch (orderStatus) {
|
// switch (orderStatus) {
|
||||||
case OrderStatus.CONFIRMED:
|
// case OrderStatus.CONFIRMED:
|
||||||
return {
|
// return {
|
||||||
message: {
|
// message: {
|
||||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
content: `Order ${event.orderNumber} confirmed .`,
|
// content: `Order ${event.orderNumber} confirmed .`,
|
||||||
sms: {
|
// sms: {
|
||||||
templateId: this.orderCreatedSmsTemplateId,
|
// templateId: this.orderCreatedSmsTemplateId,
|
||||||
parameters: {
|
// parameters: {
|
||||||
orderNumber: event.orderNumber,
|
// orderNumber: event.orderNumber,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
pushNotif: {
|
// pushNotif: {
|
||||||
title: `Order ${event.orderNumber} has been confirmed successfully`,
|
// title: `تایید سفارش`,
|
||||||
content: `Order ${event.orderNumber} has been confirmed successfully`,
|
// content: `سفارش شماره ${event.orderNumber} با موفقیت تایید شد`,
|
||||||
icon: `Order ${event.orderNumber} has been confirmed successfully`,
|
// icon: `Order ${event.orderNumber} has been confirmed successfully`,
|
||||||
action: {
|
// action: {
|
||||||
type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
url: ``,
|
// url: ``,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
case OrderStatus.PREPARING:
|
// case OrderStatus.PREPARING:
|
||||||
return {
|
// return {
|
||||||
message: {
|
// message: {
|
||||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
content: `Order ${event.orderNumber} is preparing.`,
|
// content: `سفارش شماره ${event.orderNumber} در حال آمادهسازی است`,
|
||||||
sms: {
|
// sms: {
|
||||||
templateId: this.orderCreatedSmsTemplateId,
|
// templateId: this.orderCreatedSmsTemplateId,
|
||||||
parameters: {
|
// parameters: {
|
||||||
orderNumber: event.orderNumber,
|
// orderNumber: event.orderNumber,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
pushNotif: {
|
// pushNotif: {
|
||||||
title: `Order ${event.orderNumber} is preparing`,
|
// title: `آمادهسازی سفارش`,
|
||||||
content: `Order ${event.orderNumber} is preparing`,
|
// content: `سفارش شماره ${event.orderNumber} در حال آمادهسازی است`,
|
||||||
icon: `Order ${event.orderNumber} is preparing`,
|
// icon: `Order ${event.orderNumber} is preparing`,
|
||||||
action: {
|
// action: {
|
||||||
type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
url: ``,
|
// url: ``,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
case OrderStatus.DELIVERED_TO_RECEPTIONIST:
|
// case OrderStatus.DELIVERED_TO_RECEPTIONIST:
|
||||||
return {
|
// return {
|
||||||
message: {
|
// message: {
|
||||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
content: `Order ${event.orderNumber} is ready.`,
|
// content: `سفارش شماره ${event.orderNumber} آماده است`,
|
||||||
sms: {
|
// sms: {
|
||||||
templateId: this.orderCreatedSmsTemplateId,
|
// templateId: this.orderCreatedSmsTemplateId,
|
||||||
parameters: {
|
// parameters: {
|
||||||
orderNumber: event.orderNumber,
|
// orderNumber: event.orderNumber,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
pushNotif: {
|
// pushNotif: {
|
||||||
title: `Order ${event.orderNumber} is ready`,
|
// title: `سفارش آماده است`,
|
||||||
content: `Order ${event.orderNumber} is ready`,
|
// content: `سفارش شماره ${event.orderNumber} آماده است`,
|
||||||
icon: `Order ${event.orderNumber} is ready`,
|
// icon: `Order ${event.orderNumber} is ready`,
|
||||||
action: {
|
// action: {
|
||||||
type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
url: ``,
|
// url: ``,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
case OrderStatus.SHIPPED:
|
// case OrderStatus.SHIPPED:
|
||||||
return {
|
// return {
|
||||||
message: {
|
// message: {
|
||||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
content: `Order ${event.orderNumber} is shipped.`,
|
// content: `سفارش شماره ${event.orderNumber} ارسال شد`,
|
||||||
sms: {
|
// sms: {
|
||||||
templateId: this.orderCreatedSmsTemplateId,
|
// templateId: this.orderCreatedSmsTemplateId,
|
||||||
parameters: {
|
// parameters: {
|
||||||
orderNumber: event.orderNumber,
|
// orderNumber: event.orderNumber,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
pushNotif: {
|
// pushNotif: {
|
||||||
title: `Order ${event.orderNumber} is shipped`,
|
// title: `ارسال سفارش`,
|
||||||
content: `Order ${event.orderNumber} is shipped`,
|
// content: `سفارش شماره ${event.orderNumber} ارسال شد`,
|
||||||
icon: `Order ${event.orderNumber} is shipped`,
|
// icon: `Order ${event.orderNumber} is shipped`,
|
||||||
action: {
|
// action: {
|
||||||
type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
url: ``,
|
// url: ``,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
case OrderStatus.CANCELED:
|
// case OrderStatus.CANCELED:
|
||||||
return {
|
// return {
|
||||||
message: {
|
// message: {
|
||||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
content: `Order ${event.orderNumber} is canceled.`,
|
// content: `سفارش شماره ${event.orderNumber} لغو شد`,
|
||||||
sms: {
|
// sms: {
|
||||||
templateId: this.orderCreatedSmsTemplateId,
|
// templateId: this.orderCreatedSmsTemplateId,
|
||||||
parameters: {
|
// parameters: {
|
||||||
orderNumber: event.orderNumber,
|
// orderNumber: event.orderNumber,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
pushNotif: {
|
// pushNotif: {
|
||||||
title: `Order ${event.orderNumber} is canceled`,
|
// title: `لغو سفارش`,
|
||||||
content: `Order ${event.orderNumber} is canceled`,
|
// content: `سفارش شماره ${event.orderNumber} لغو شد`,
|
||||||
icon: `Order ${event.orderNumber} is canceled`,
|
// icon: `Order ${event.orderNumber} is canceled`,
|
||||||
action: {
|
// action: {
|
||||||
type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
url: ``,
|
// url: ``,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
default:
|
// default:
|
||||||
return {
|
// return {
|
||||||
message: {
|
// message: {
|
||||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
content: `Order ${event.orderNumber} is ....`,
|
// content: `Order ${event.orderNumber} is ....`,
|
||||||
sms: {
|
// sms: {
|
||||||
templateId: this.orderCreatedSmsTemplateId,
|
// templateId: this.orderCreatedSmsTemplateId,
|
||||||
parameters: {
|
// parameters: {
|
||||||
orderNumber: event.orderNumber,
|
// orderNumber: event.orderNumber,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
pushNotif: {
|
// pushNotif: {
|
||||||
title: `Order ${event.orderNumber} is ...`,
|
// title: `Order ${event.orderNumber} is ...`,
|
||||||
content: `Order ${event.orderNumber} is ...`,
|
// content: `Order ${event.orderNumber} is ...`,
|
||||||
icon: `Order ${event.orderNumber} is ...`,
|
// icon: `Order ${event.orderNumber} is ...`,
|
||||||
action: {
|
// action: {
|
||||||
type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// type: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||||
url: ``,
|
// url: ``,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { BulkReserveFoodDto } from 'src/modules/inventory/dto/bulk-reserve-food.
|
|||||||
import { StatusTransitionRef } from '../interface/order.interface';
|
import { StatusTransitionRef } from '../interface/order.interface';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events';
|
import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events';
|
||||||
|
import { OrderMessage } from 'src/common/enums/message.enum';
|
||||||
type OrderItemData = { food: Food; quantity: number; unitPrice: number; discount: number };
|
type OrderItemData = { food: Food; quantity: number; unitPrice: number; discount: number };
|
||||||
|
|
||||||
type ValidatedCartForOrder = {
|
type ValidatedCartForOrder = {
|
||||||
@@ -224,7 +225,7 @@ export class OrdersService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (!order) {
|
if (!order) {
|
||||||
throw new NotFoundException('Order not found');
|
throw new NotFoundException(OrderMessage.NOT_FOUND);
|
||||||
}
|
}
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
@@ -291,11 +292,11 @@ export class OrdersService {
|
|||||||
private assertStatusTransitionAllowed(order: Order, to: OrderStatus, ref: StatusTransitionRef) {
|
private assertStatusTransitionAllowed(order: Order, to: OrderStatus, ref: StatusTransitionRef) {
|
||||||
const paymentMethod = order.paymentMethod?.method;
|
const paymentMethod = order.paymentMethod?.method;
|
||||||
if (!paymentMethod) {
|
if (!paymentMethod) {
|
||||||
throw new BadRequestException('Order payment method is missing');
|
throw new BadRequestException(OrderMessage.PAYMENT_METHOD_MISSING);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.canTransition(order.status, to, paymentMethod, ref)) {
|
if (!this.canTransition(order.status, to, paymentMethod, ref)) {
|
||||||
throw new BadRequestException(`Invalid status transition: ${order.status} -> ${to}`);
|
throw new BadRequestException(OrderMessage.INVALID_STATUS_TRANSITION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,70 +337,66 @@ export class OrdersService {
|
|||||||
{ id: orderId, restaurant: { id: restId } },
|
{ id: orderId, restaurant: { id: restId } },
|
||||||
{ populate: ['paymentMethod'] },
|
{ populate: ['paymentMethod'] },
|
||||||
);
|
);
|
||||||
if (!order) throw new NotFoundException('Order not found');
|
if (!order) throw new NotFoundException(OrderMessage.NOT_FOUND);
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
private assertCartHasItems(cart: Cart) {
|
private assertCartHasItems(cart: Cart) {
|
||||||
if (!cart.items || cart.items.length === 0) {
|
if (!cart.items || cart.items.length === 0) {
|
||||||
throw new BadRequestException('Cart is empty. Add items to cart before creating an order.');
|
throw new BadRequestException(OrderMessage.CART_EMPTY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private assertCartHasDeliveryMethod(cart: Cart) {
|
private assertCartHasDeliveryMethod(cart: Cart) {
|
||||||
if (!cart.deliveryMethodId) {
|
if (!cart.deliveryMethodId) {
|
||||||
throw new NotFoundException('Delivery method not found');
|
throw new NotFoundException(OrderMessage.DELIVERY_METHOD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private assertCartHasPaymentMethod(cart: Cart) {
|
private assertCartHasPaymentMethod(cart: Cart) {
|
||||||
if (!cart.paymentMethodId) {
|
if (!cart.paymentMethodId) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(OrderMessage.PAYMENT_METHOD_REQUIRED);
|
||||||
'Payment method is required. Please set a payment method before creating an order.',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getUserOrFail(userId: string): Promise<User> {
|
private async getUserOrFail(userId: string): Promise<User> {
|
||||||
const user = await this.em.findOne(User, { id: userId });
|
const user = await this.em.findOne(User, { id: userId });
|
||||||
if (!user) throw new NotFoundException('User not found');
|
if (!user) throw new NotFoundException(OrderMessage.USER_NOT_FOUND);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getRestaurantOrFail(restaurantId: string): Promise<Restaurant> {
|
private async getRestaurantOrFail(restaurantId: string): Promise<Restaurant> {
|
||||||
const restaurant = await this.em.findOne(Restaurant, { id: restaurantId });
|
const restaurant = await this.em.findOne(Restaurant, { id: restaurantId });
|
||||||
if (!restaurant) throw new NotFoundException('Restaurant not found');
|
if (!restaurant) throw new NotFoundException(OrderMessage.RESTAURANT_NOT_FOUND);
|
||||||
return restaurant;
|
return restaurant;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getDeliveryOrFail(deliveryId: string): Promise<Delivery> {
|
private async getDeliveryOrFail(deliveryId: string): Promise<Delivery> {
|
||||||
const delivery = await this.em.findOne(Delivery, { id: deliveryId });
|
const delivery = await this.em.findOne(Delivery, { id: deliveryId });
|
||||||
if (!delivery) throw new NotFoundException('Delivery not found');
|
if (!delivery) throw new NotFoundException(OrderMessage.DELIVERY_NOT_FOUND);
|
||||||
return delivery;
|
return delivery;
|
||||||
}
|
}
|
||||||
|
|
||||||
private assertMeetsMinOrderForDelivery(cart: Cart, delivery: Delivery) {
|
private assertMeetsMinOrderForDelivery(cart: Cart, delivery: Delivery) {
|
||||||
const minOrderPrice = Number(delivery.minOrderPrice) || 0;
|
const minOrderPrice = Number(delivery.minOrderPrice) || 0;
|
||||||
if (minOrderPrice > 0 && cart.total < minOrderPrice) {
|
if (minOrderPrice > 0 && cart.total < minOrderPrice) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(OrderMessage.MIN_ORDER_AMOUNT_NOT_MET);
|
||||||
`Minimum order amount for this delivery method is ${minOrderPrice}. Current total is ${cart.total}.`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private assertDeliveryMethodRequirements(cart: Cart, delivery: Delivery) {
|
private assertDeliveryMethodRequirements(cart: Cart, delivery: Delivery) {
|
||||||
if (delivery.method === DeliveryMethodEnum.DineIn) {
|
if (delivery.method === DeliveryMethodEnum.DineIn) {
|
||||||
if (!cart.tableNumber || cart.tableNumber.trim() === '') {
|
if (!cart.tableNumber || cart.tableNumber.trim() === '') {
|
||||||
throw new BadRequestException('Table number is required when delivery method is DineIn');
|
throw new BadRequestException(OrderMessage.TABLE_NUMBER_REQUIRED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delivery.method === DeliveryMethodEnum.DeliveryCourier && !cart.userAddress) {
|
if (delivery.method === DeliveryMethodEnum.DeliveryCourier && !cart.userAddress) {
|
||||||
throw new BadRequestException('Address is required. Please set a delivery address before creating an order.');
|
throw new BadRequestException(OrderMessage.ADDRESS_REQUIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delivery.method === DeliveryMethodEnum.DeliveryCar && !cart.carAddress) {
|
if (delivery.method === DeliveryMethodEnum.DeliveryCar && !cart.carAddress) {
|
||||||
throw new BadRequestException('Car address is required. Please set a car address before creating an order.');
|
throw new BadRequestException(OrderMessage.CAR_ADDRESS_REQUIRED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +419,7 @@ export class OrdersService {
|
|||||||
|
|
||||||
private assertPaymentMethodEnabled(paymentMethod: PaymentMethod) {
|
private assertPaymentMethodEnabled(paymentMethod: PaymentMethod) {
|
||||||
if (!paymentMethod.enabled) {
|
if (!paymentMethod.enabled) {
|
||||||
throw new BadRequestException('Payment method is not enabled for this restaurant');
|
throw new BadRequestException(OrderMessage.PAYMENT_METHOD_NOT_ENABLED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,10 +428,10 @@ export class OrdersService {
|
|||||||
|
|
||||||
for (const cartItem of cart.items) {
|
for (const cartItem of cart.items) {
|
||||||
const food = await this.em.findOne(Food, { id: cartItem.foodId }, { populate: ['restaurant', 'inventory'] });
|
const food = await this.em.findOne(Food, { id: cartItem.foodId }, { populate: ['restaurant', 'inventory'] });
|
||||||
if (!food) throw new NotFoundException(`Food with ID ${cartItem.foodId} not found`);
|
if (!food) throw new NotFoundException(OrderMessage.FOOD_NOT_FOUND);
|
||||||
|
|
||||||
if (food.restaurant.id !== restaurantId) {
|
if (food.restaurant.id !== restaurantId) {
|
||||||
throw new BadRequestException(`Food ${food.title || food.id} does not belong to restaurant ${restaurantId}`);
|
throw new BadRequestException(OrderMessage.FOOD_NOT_BELONGS_TO_RESTAURANT);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.assertFoodHasSufficientStock(food, cartItem.quantity);
|
this.assertFoodHasSufficientStock(food, cartItem.quantity);
|
||||||
@@ -453,9 +450,7 @@ export class OrdersService {
|
|||||||
private assertFoodHasSufficientStock(food: Food, quantity: number) {
|
private assertFoodHasSufficientStock(food: Food, quantity: number) {
|
||||||
const availableStock = food.inventory?.availableStock ?? 0;
|
const availableStock = food.inventory?.availableStock ?? 0;
|
||||||
if (availableStock < quantity) {
|
if (availableStock < quantity) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(OrderMessage.FOOD_NOT_FOUND);
|
||||||
`Insufficient stock for food ${food.title || food.id}. Available: ${availableStock}, Requested: ${quantity}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import type { FastifyReply, FastifyRequest } from 'fastify';
|
|||||||
import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard';
|
import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard';
|
||||||
import { AdminId } from 'src/common/decorators/admin-id.decorator';
|
import { AdminId } from 'src/common/decorators/admin-id.decorator';
|
||||||
import { UpdatePagerStatusDto } from '../dto/update-pager.dto';
|
import { UpdatePagerStatusDto } from '../dto/update-pager.dto';
|
||||||
|
import { PagerMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@ApiTags('pager')
|
@ApiTags('pager')
|
||||||
@Controller()
|
@Controller()
|
||||||
@@ -49,7 +50,7 @@ export class PagerController {
|
|||||||
});
|
});
|
||||||
return reply.code(200).send({
|
return reply.code(200).send({
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Pager request created successfully',
|
message: PagerMessage.CREATED_SUCCESS,
|
||||||
data: null,
|
data: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
IZarinpalVerifyResponse,
|
IZarinpalVerifyResponse,
|
||||||
} from '../interface/gateway';
|
} from '../interface/gateway';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { PaymentMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ZarinpalGateway implements IPaymentGateway {
|
export class ZarinpalGateway implements IPaymentGateway {
|
||||||
@@ -64,7 +65,7 @@ export class ZarinpalGateway implements IPaymentGateway {
|
|||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
this.logger.error('Invalid response from Zarinpal API (missing data)', JSON.stringify(res.data));
|
this.logger.error('Invalid response from Zarinpal API (missing data)', JSON.stringify(res.data));
|
||||||
throw new BadRequestException('Invalid response from Zarinpal API');
|
throw new BadRequestException(PaymentMessage.ZARINPAL_INVALID_RESPONSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Array.isArray(errors) && errors.length > 0) || code !== 100 || !authority) {
|
if ((Array.isArray(errors) && errors.length > 0) || code !== 100 || !authority) {
|
||||||
@@ -79,7 +80,7 @@ export class ZarinpalGateway implements IPaymentGateway {
|
|||||||
orderId,
|
orderId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
throw new BadRequestException(message ?? 'Zarinpal payment request error');
|
throw new BadRequestException(message ?? PaymentMessage.ZARINPAL_PAYMENT_REQUEST_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { transactionId: authority };
|
return { transactionId: authority };
|
||||||
@@ -98,12 +99,12 @@ export class ZarinpalGateway implements IPaymentGateway {
|
|||||||
orderId,
|
orderId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
throw new BadRequestException('Zarinpal payment request error');
|
throw new BadRequestException(PaymentMessage.ZARINPAL_PAYMENT_REQUEST_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||||
this.logger.error('Zarinpal payment request error', errorMessage);
|
this.logger.error('Zarinpal payment request error', errorMessage);
|
||||||
throw new BadRequestException(`Failed to request payment from gateway: ${errorMessage}`);
|
throw new BadRequestException(PaymentMessage.GATEWAY_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,14 +125,14 @@ export class ZarinpalGateway implements IPaymentGateway {
|
|||||||
|
|
||||||
// Check if response data exists
|
// Check if response data exists
|
||||||
if (!res.data || !res.data.data) {
|
if (!res.data || !res.data.data) {
|
||||||
throw new BadRequestException('Invalid response from Zarinpal API');
|
throw new BadRequestException(PaymentMessage.ZARINPAL_INVALID_RESPONSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { code, message, ref_id, card_pan } = res.data.data;
|
const { code, message, ref_id, card_pan } = res.data.data;
|
||||||
|
|
||||||
// Check if there are errors in the response
|
// Check if there are errors in the response
|
||||||
if (code !== 100 && code !== 0) {
|
if (code !== 100 && code !== 0) {
|
||||||
throw new BadRequestException(message ?? 'Zarinpal error');
|
throw new BadRequestException(message ?? PaymentMessage.ZARINPAL_VERIFY_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -146,7 +147,7 @@ export class ZarinpalGateway implements IPaymentGateway {
|
|||||||
throw new BadRequestException(error.response.data);
|
throw new BadRequestException(error.response.data);
|
||||||
}
|
}
|
||||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||||
throw new BadRequestException(`Failed to verify payment with gateway: ${errorMessage}`);
|
throw new BadRequestException(PaymentMessage.GATEWAY_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,15 @@ export class PaymentListeners {
|
|||||||
this.paymentSuccessSmsTemplateId = this.configService.get<string>('SMS_PATTERN_PAYMENT_SUCCESS') ?? '123';
|
this.paymentSuccessSmsTemplateId = this.configService.get<string>('SMS_PATTERN_PAYMENT_SUCCESS') ?? '123';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getPaymentMethodFarsi(method: string): string {
|
||||||
|
const methodMap: Record<string, string> = {
|
||||||
|
Cash: 'نقدی',
|
||||||
|
Wallet: 'کیف پول',
|
||||||
|
Online: 'آنلاین',
|
||||||
|
};
|
||||||
|
return methodMap[method] || method;
|
||||||
|
}
|
||||||
|
|
||||||
@OnEvent(paymentSucceedEvent.name)
|
@OnEvent(paymentSucceedEvent.name)
|
||||||
async handlePaymentSucceed(event: paymentSucceedEvent) {
|
async handlePaymentSucceed(event: paymentSucceedEvent) {
|
||||||
try {
|
try {
|
||||||
@@ -36,7 +45,7 @@ export class PaymentListeners {
|
|||||||
restaurantId: event.restaurantId,
|
restaurantId: event.restaurantId,
|
||||||
message: {
|
message: {
|
||||||
title: NotifTitleEnum.PAYMENT_SUCCESS,
|
title: NotifTitleEnum.PAYMENT_SUCCESS,
|
||||||
content: `payment order #${event.orderNumber} has been paid successfully with ${event.paymentMethod} method and total amount of ${event.total}`,
|
content: `پرداخت سفارش شماره ${event.orderNumber} با روش ${this.getPaymentMethodFarsi(event.paymentMethod)} و مبلغ ${event.total} تومان با موفقیت انجام شد`,
|
||||||
sms: {
|
sms: {
|
||||||
templateId: this.paymentSuccessSmsTemplateId,
|
templateId: this.paymentSuccessSmsTemplateId,
|
||||||
parameters: {
|
parameters: {
|
||||||
@@ -45,8 +54,8 @@ export class PaymentListeners {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
pushNotif: {
|
pushNotif: {
|
||||||
title: `Payment Order ${event.orderNumber} has been paid successfully with ${event.paymentMethod} method and total amount of ${event.total} `,
|
title: `پرداخت موفق`,
|
||||||
content: `Payment Order ${event.orderNumber} has been paid successfully with ${event.paymentMethod} method and total amount of ${event.total} `,
|
content: `پرداخت سفارش شماره ${event.orderNumber} با روش ${this.getPaymentMethodFarsi(event.paymentMethod)} و مبلغ ${event.total} تومان با موفقیت انجام شد`,
|
||||||
icon: ``,
|
icon: ``,
|
||||||
action: {
|
action: {
|
||||||
type: NotifTitleEnum.PAYMENT_SUCCESS,
|
type: NotifTitleEnum.PAYMENT_SUCCESS,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { CreatePaymentMethodDto } from '../dto/create-payment-method.dto';
|
|||||||
import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto';
|
import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto';
|
||||||
import { RequiredEntityData } from '@mikro-orm/core';
|
import { RequiredEntityData } from '@mikro-orm/core';
|
||||||
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
||||||
|
import { RestMessage, PaymentMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PaymentMethodService {
|
export class PaymentMethodService {
|
||||||
@@ -18,7 +19,7 @@ export class PaymentMethodService {
|
|||||||
// Check if restaurant exists
|
// Check if restaurant exists
|
||||||
const restaurant = await this.em.findOne(Restaurant, { id: restId });
|
const restaurant = await this.em.findOne(Restaurant, { id: restId });
|
||||||
if (!restaurant) {
|
if (!restaurant) {
|
||||||
throw new NotFoundException(`Restaurant with ID ${restId} not found`);
|
throw new NotFoundException(RestMessage.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const paymentMethod = this.paymentMethodRepository.create({
|
const paymentMethod = this.paymentMethodRepository.create({
|
||||||
@@ -36,7 +37,7 @@ export class PaymentMethodService {
|
|||||||
async findOne(id: string): Promise<PaymentMethod> {
|
async findOne(id: string): Promise<PaymentMethod> {
|
||||||
const paymentMethod = await this.paymentMethodRepository.findOne({ id }, { populate: ['restaurant'] });
|
const paymentMethod = await this.paymentMethodRepository.findOne({ id }, { populate: ['restaurant'] });
|
||||||
if (!paymentMethod) {
|
if (!paymentMethod) {
|
||||||
throw new NotFoundException(`PaymentMethod with ID ${id} not found`);
|
throw new NotFoundException(PaymentMessage.PAYMENT_METHOD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return paymentMethod;
|
return paymentMethod;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { UserWallet } from 'src/modules/users/entities/user-wallet.entity';
|
|||||||
import { OrderPaymentContext } from '../interface/payment';
|
import { OrderPaymentContext } from '../interface/payment';
|
||||||
import { OrderStatus } from 'src/modules/orders/interface/order.interface';
|
import { OrderStatus } from 'src/modules/orders/interface/order.interface';
|
||||||
import { ChartPeriodEnum, PaymentChartDto } from '../dto/payment-chart.dto';
|
import { ChartPeriodEnum, PaymentChartDto } from '../dto/payment-chart.dto';
|
||||||
|
import { PaymentMessage, OrderMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PaymentsService {
|
export class PaymentsService {
|
||||||
@@ -40,7 +41,7 @@ export class PaymentsService {
|
|||||||
return this.handleOnlinePayment(ctx);
|
return this.handleOnlinePayment(ctx);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new BadRequestException('Unsupported payment method');
|
throw new BadRequestException(PaymentMessage.UNSUPPORTED_PAYMENT_METHOD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,22 +53,22 @@ export class PaymentsService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!order) {
|
if (!order) {
|
||||||
throw new NotFoundException('Order not found');
|
throw new NotFoundException(OrderMessage.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (order.total <= 0) {
|
if (order.total <= 0) {
|
||||||
throw new BadRequestException('Amount must be greater than zero');
|
throw new BadRequestException(PaymentMessage.AMOUNT_MUST_BE_GREATER_THAN_ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pm = order.paymentMethod;
|
const pm = order.paymentMethod;
|
||||||
if (!pm) {
|
if (!pm) {
|
||||||
throw new NotFoundException('Payment method not found');
|
throw new NotFoundException(PaymentMessage.PAYMENT_METHOD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pm.method === PaymentMethodEnum.Online) {
|
if (pm.method === PaymentMethodEnum.Online) {
|
||||||
if (!pm.gateway) throw new BadRequestException('Gateway is required for online payment');
|
if (!pm.gateway) throw new BadRequestException(PaymentMessage.GATEWAY_REQUIRED);
|
||||||
if (!pm.merchantId) throw new BadRequestException('Merchant ID is required for online payment');
|
if (!pm.merchantId) throw new BadRequestException(PaymentMessage.MERCHANT_ID_REQUIRED);
|
||||||
if (!pm.restaurant?.domain) throw new BadRequestException('Restaurant domain is required for online payment');
|
if (!pm.restaurant?.domain) throw new BadRequestException(PaymentMessage.RESTAURANT_DOMAIN_REQUIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -91,7 +92,7 @@ export class PaymentsService {
|
|||||||
private async handleWalletPayment(ctx: OrderPaymentContext): Promise<void> {
|
private async handleWalletPayment(ctx: OrderPaymentContext): Promise<void> {
|
||||||
await this.em.transactional(async em => {
|
await this.em.transactional(async em => {
|
||||||
const order = await em.findOne(Order, { id: ctx.order.id }, { populate: ['user', 'restaurant'] });
|
const order = await em.findOne(Order, { id: ctx.order.id }, { populate: ['user', 'restaurant'] });
|
||||||
if (!order) throw new NotFoundException('Order not found');
|
if (!order) throw new NotFoundException(OrderMessage.NOT_FOUND);
|
||||||
|
|
||||||
if (order.status === OrderStatus.PAID) {
|
if (order.status === OrderStatus.PAID) {
|
||||||
return;
|
return;
|
||||||
@@ -169,7 +170,7 @@ export class PaymentsService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!payment) {
|
if (!payment) {
|
||||||
throw new NotFoundException('Payment not found');
|
throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payment.status === PaymentStatusEnum.Paid) {
|
if (payment.status === PaymentStatusEnum.Paid) {
|
||||||
@@ -178,7 +179,7 @@ export class PaymentsService {
|
|||||||
|
|
||||||
const pm = payment.order.paymentMethod;
|
const pm = payment.order.paymentMethod;
|
||||||
if (!pm?.merchantId || !payment.gateway) {
|
if (!pm?.merchantId || !payment.gateway) {
|
||||||
throw new BadRequestException('Invalid payment configuration');
|
throw new BadRequestException(PaymentMessage.INVALID_PAYMENT_CONFIGURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
const gateway = this.gatewayManager.get(payment.gateway);
|
const gateway = this.gatewayManager.get(payment.gateway);
|
||||||
@@ -215,13 +216,13 @@ export class PaymentsService {
|
|||||||
const payment = await this.em.transactional(async em => {
|
const payment = await this.em.transactional(async em => {
|
||||||
const payment = await em.findOne(Payment, id, { populate: ['order'] });
|
const payment = await em.findOne(Payment, id, { populate: ['order'] });
|
||||||
if (!payment) {
|
if (!payment) {
|
||||||
throw new NotFoundException('Payment not found');
|
throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
if (payment.method !== PaymentMethodEnum.Cash) {
|
if (payment.method !== PaymentMethodEnum.Cash) {
|
||||||
throw new BadRequestException('Payment method is not cash');
|
throw new BadRequestException(PaymentMessage.PAYMENT_METHOD_NOT_CASH);
|
||||||
}
|
}
|
||||||
if (payment.status === PaymentStatusEnum.Paid) {
|
if (payment.status === PaymentStatusEnum.Paid) {
|
||||||
throw new BadRequestException('Payment already paid');
|
throw new BadRequestException(PaymentMessage.PAYMENT_ALREADY_PAID);
|
||||||
}
|
}
|
||||||
payment.order.status = OrderStatus.PAID;
|
payment.order.status = OrderStatus.PAID;
|
||||||
payment.status = PaymentStatusEnum.Paid;
|
payment.status = PaymentStatusEnum.Paid;
|
||||||
@@ -269,7 +270,7 @@ export class PaymentsService {
|
|||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
if (existing.method !== params.method) {
|
if (existing.method !== params.method) {
|
||||||
throw new BadRequestException('Existing pending payment method does not match order payment method');
|
throw new BadRequestException(PaymentMessage.EXISTING_PENDING_PAYMENT_MISMATCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
return existing;
|
return existing;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { RestId } from 'src/common/decorators';
|
|||||||
import { FindUsersDto } from '../dto/find-user.dto';
|
import { FindUsersDto } from '../dto/find-user.dto';
|
||||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||||
import { API_HEADER_SLUG } from 'src/common/constants/index';
|
import { API_HEADER_SLUG } from 'src/common/constants/index';
|
||||||
|
import { UserSuccessMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@ApiTags('User')
|
@ApiTags('User')
|
||||||
@Controller()
|
@Controller()
|
||||||
@@ -107,7 +108,7 @@ export class UsersController {
|
|||||||
@Delete('public/user/addresses/:addressId')
|
@Delete('public/user/addresses/:addressId')
|
||||||
async deleteUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
|
async deleteUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
|
||||||
await this.userService.deleteUserAddress(userId, addressId);
|
await this.userService.deleteUserAddress(userId, addressId);
|
||||||
return { message: 'Address deleted successfully' };
|
return { message: UserSuccessMessage.ADDRESS_DELETED_SUCCESS };
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@@ -129,7 +130,7 @@ export class UsersController {
|
|||||||
async convertScoreToWallet(@UserId() userId: string, @RestId() restId: string) {
|
async convertScoreToWallet(@UserId() userId: string, @RestId() restId: string) {
|
||||||
await this.userService.convertScoreToWallet(userId, restId);
|
await this.userService.convertScoreToWallet(userId, restId);
|
||||||
return {
|
return {
|
||||||
message: 'Score converted to wallet successfully',
|
message: UserSuccessMessage.SCORE_CONVERTED_SUCCESS,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user