rename
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
export { UserId } from './user-id.decorator';
|
||||
export { AdminId } from './admin-id.decorator';
|
||||
export { ShopId, RestId } from './rest-id.decorator';
|
||||
export { ShopId, RestId } from './shop-id.decorator';
|
||||
export { Permissions, PERMISSIONS_KEY } from './permissions.decorator';
|
||||
export { RateLimit } from './rate-limit.decorator';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createParamDecorator, type ExecutionContext } from '@nestjs/common';
|
||||
import type { Request } from 'express';
|
||||
|
||||
export const RestSlug = createParamDecorator((data: unknown, ctx: ExecutionContext): string => {
|
||||
export const ShopSlug = createParamDecorator((data: unknown, ctx: ExecutionContext): string => {
|
||||
const request = ctx.switchToHttp().getRequest<Request & { slug?: string }>();
|
||||
return request.slug || '';
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import { AdminId } from 'src/common/decorators/admin-id.decorator';
|
||||
import { Admin } from '../entities/admin.entity';
|
||||
import { Permission } from 'src/common/enums/permission.enum';
|
||||
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
||||
import { CreateMyRestaurantAdminDto } from '../dto/create-my-restaurant-admin.dto';
|
||||
import { CreateMyShopAdminDto } from '../dto/create-shop-admin.dto';
|
||||
|
||||
@ApiBearerAuth()
|
||||
@ApiTags('admin')
|
||||
@@ -22,8 +22,8 @@ export class AdminController {
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@Permissions(Permission.MANAGE_ADMINS)
|
||||
@ApiOperation({ summary: 'admin' })
|
||||
async getAll(@ShopId() restId: string) {
|
||||
const admin = await this.adminService.findAllByRestaurantId(restId);
|
||||
async getAll(@ShopId() shopId: string) {
|
||||
const admin = await this.adminService.findAllByShopId(shopId);
|
||||
return admin;
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ export class AdminController {
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@Permissions(Permission.MANAGE_ADMINS)
|
||||
@ApiOperation({ summary: 'admin' })
|
||||
async getMe(@AdminId() adminId: string, @ShopId() restId: string) {
|
||||
const admin = await this.adminService.findById(adminId, restId);
|
||||
async getMe(@AdminId() adminId: string, @ShopId() shopId: string) {
|
||||
const admin = await this.adminService.findById(adminId, shopId);
|
||||
return admin;
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ export class AdminController {
|
||||
@HttpCode(HttpStatus.CREATED)
|
||||
@ApiOperation({ summary: 'Create a new admin' })
|
||||
@ApiBody({ type: CreateAdminDto })
|
||||
async create(@Body() dto: CreateAdminDto, @ShopId() restId: string) {
|
||||
const admin = await this.adminService.createAdminForMyRestaurant(restId, dto);
|
||||
async create(@Body() dto: CreateAdminDto, @ShopId() shopId: string) {
|
||||
const admin = await this.adminService.createAdminForShop(shopId, dto);
|
||||
return admin;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ export class AdminController {
|
||||
@ApiOperation({ summary: 'Update an admin' })
|
||||
@ApiParam({ name: 'adminId', description: 'Admin ID' })
|
||||
@ApiBody({ type: UpdateAdminDto })
|
||||
update(@Param('adminId') adminId: string, @Body() dto: UpdateAdminDto, @ShopId() restId: string): Promise<Admin> {
|
||||
return this.adminService.update(adminId, restId, dto);
|
||||
update(@Param('adminId') adminId: string, @Body() dto: UpdateAdminDto, @ShopId() shopId: string): Promise<Admin> {
|
||||
return this.adminService.update(adminId, shopId, dto);
|
||||
}
|
||||
|
||||
@Get('admin/admins/:adminId')
|
||||
@@ -62,8 +62,8 @@ export class AdminController {
|
||||
@Permissions(Permission.MANAGE_ADMINS)
|
||||
@ApiOperation({ summary: 'Get an admin by ID' })
|
||||
@ApiParam({ name: 'id', description: 'Admin ID' })
|
||||
getById(@Param('adminId') adminId: string, @ShopId() restId: string): Promise<Admin | null> {
|
||||
return this.adminService.findById(adminId, restId);
|
||||
getById(@Param('adminId') adminId: string, @ShopId() shopId: string): Promise<Admin | null> {
|
||||
return this.adminService.findById(adminId, shopId);
|
||||
}
|
||||
|
||||
@Delete('admin/admins/:adminId')
|
||||
@@ -71,40 +71,40 @@ export class AdminController {
|
||||
@Permissions(Permission.MANAGE_ADMINS)
|
||||
@ApiOperation({ summary: 'Delete an admin by ID' })
|
||||
@ApiParam({ name: 'id', description: 'Admin ID' })
|
||||
deleteById(@Param('adminId') adminId: string, @ShopId() restId: string): Promise<void> {
|
||||
return this.adminService.remove(adminId, restId);
|
||||
deleteById(@Param('adminId') adminId: string, @ShopId() shopId: string): Promise<void> {
|
||||
return this.adminService.remove(adminId, shopId);
|
||||
}
|
||||
|
||||
/** Super Admin Endpoints */
|
||||
@UseGuards(SuperAdminAuthGuard)
|
||||
@Get('super-admin/shops/:restaurantId/admins')
|
||||
@Get('super-admin/shops/:shopId/admins')
|
||||
@ApiOperation({ summary: 'Get admins for a specific shop (Super Admin only)' })
|
||||
@ApiParam({ name: 'restaurantId', description: 'Shop ID' })
|
||||
getAdminForRestaurant(@Param('restaurantId') restaurantId: string): Promise<Admin[]> {
|
||||
return this.adminService.getAdminsForRestaurantBySuperAdmin(restaurantId);
|
||||
@ApiParam({ name: 'shopId', description: 'Shop ID' })
|
||||
getAdminForRestaurant(@Param('shopId') shopId: string): Promise<Admin[]> {
|
||||
return this.adminService.getShopAdminsBySuperAdmin(shopId);
|
||||
}
|
||||
|
||||
@UseGuards(SuperAdminAuthGuard)
|
||||
@Post('super-admin/shops/:restaurantId/admins')
|
||||
@Post('super-admin/shops/:shopId/admins')
|
||||
@ApiOperation({ summary: 'Create admin for a specific shop (Super Admin only)' })
|
||||
@ApiParam({ name: 'restaurantId', description: 'Shop ID' })
|
||||
@ApiBody({ type: CreateMyRestaurantAdminDto })
|
||||
@ApiParam({ name: 'shopId', description: 'Shop ID' })
|
||||
@ApiBody({ type: CreateMyShopAdminDto })
|
||||
createAdminForRestaurant(
|
||||
@Param('restaurantId') restaurantId: string,
|
||||
@Body() dto: CreateMyRestaurantAdminDto,
|
||||
@Param('shopId') shopId: string,
|
||||
@Body() dto: CreateMyShopAdminDto,
|
||||
): Promise<Admin> {
|
||||
return this.adminService.createAdminForRestaurantBySuperAdmin(restaurantId, dto);
|
||||
return this.adminService.createAdminForShopBySuperAdmin(shopId, dto);
|
||||
}
|
||||
|
||||
@UseGuards(SuperAdminAuthGuard)
|
||||
@Delete('super-admin/shops/:restaurantId/admins/:adminId')
|
||||
@Delete('super-admin/shops/:shopId/admins/:adminId')
|
||||
@ApiOperation({ summary: 'Revoke admin role from a shop (Super Admin only)' })
|
||||
@ApiParam({ name: 'restaurantId', description: 'Shop ID' })
|
||||
@ApiParam({ name: 'shopId', description: 'Shop ID' })
|
||||
@ApiParam({ name: 'adminId', description: 'Admin ID' })
|
||||
revokeAdminFromRestaurant(
|
||||
@Param('restaurantId') restaurantId: string,
|
||||
@Param('shopId') shopId: string,
|
||||
@Param('adminId') adminId: string,
|
||||
): Promise<void> {
|
||||
return this.adminService.remove(adminId, restaurantId);
|
||||
return this.adminService.remove(adminId, shopId);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateMyRestaurantAdminDto {
|
||||
export class CreateMyShopAdminDto {
|
||||
@ApiProperty({ description: 'Mobile phone number' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@@ -6,7 +6,7 @@ import { Role } from '../../roles/entities/role.entity';
|
||||
import { Shop } from '../../shops/entities/shop.entity';
|
||||
import { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { CacheService } from '../../utils/cache.service';
|
||||
import { CreateMyRestaurantAdminDto } from '../dto/create-my-restaurant-admin.dto';
|
||||
import { CreateMyShopAdminDto } from '../dto/create-shop-admin.dto';
|
||||
import { UpdateAdminDto } from '../dto/update-admin.dto';
|
||||
import { AdminRole } from '../entities/adminRole.entity';
|
||||
import { normalizePhone } from '../../utils/phone.util';
|
||||
@@ -27,19 +27,19 @@ export class AdminService {
|
||||
return this.adminRepository.findOne({ phone: normalizedPhone });
|
||||
}
|
||||
|
||||
async findById(adminId: string, restId: string): Promise<Admin | null> {
|
||||
async findById(adminId: string, shopId: string): Promise<Admin | null> {
|
||||
const admin = await this.adminRepository.findOne(
|
||||
{ id: adminId, roles: { shop: { id: restId } } },
|
||||
{ id: adminId, roles: { shop: { id: shopId } } },
|
||||
{ populate: ['roles', 'roles.role'] },
|
||||
);
|
||||
return admin;
|
||||
}
|
||||
|
||||
async findAllByRestaurantId(restId: string): Promise<Admin[]> {
|
||||
return this.adminRepository.find({ roles: { shop: { id: restId } } }, { populate: ['roles', 'roles.role'] });
|
||||
async findAllByShopId(shopId: string): Promise<Admin[]> {
|
||||
return this.adminRepository.find({ roles: { shop: { id: shopId } } }, { populate: ['roles', 'roles.role'] });
|
||||
}
|
||||
|
||||
async createAdminForMyRestaurant(restId: string, dto: CreateMyRestaurantAdminDto) {
|
||||
async createAdminForShop(shopId: string, dto: CreateMyShopAdminDto) {
|
||||
const { phone, firstName, lastName, roleId } = dto;
|
||||
const normalizedPhone = normalizePhone(phone);
|
||||
let admin: Admin | null = null;
|
||||
@@ -57,7 +57,7 @@ export class AdminService {
|
||||
const role = await this.em.findOne(Role, { id: roleId, isSystem: false });
|
||||
if (!role) throw new NotFoundException('Role not found');
|
||||
|
||||
const shop = await this.em.findOne(Shop, { id: restId });
|
||||
const shop = await this.em.findOne(Shop, { id: shopId });
|
||||
if (!shop) throw new NotFoundException('Shop not found');
|
||||
|
||||
let adminRole = await this.adminRoleRepository.findOne({
|
||||
@@ -79,7 +79,7 @@ export class AdminService {
|
||||
return admin;
|
||||
}
|
||||
|
||||
async createAdminForRestaurantBySuperAdmin(restId: string, dto: CreateMyRestaurantAdminDto) {
|
||||
async createAdminForShopBySuperAdmin(shopId: string, dto: CreateMyShopAdminDto) {
|
||||
const { phone, firstName, lastName, roleId } = dto;
|
||||
const normalizedPhone = normalizePhone(phone);
|
||||
let admin: Admin | null = null;
|
||||
@@ -97,7 +97,7 @@ export class AdminService {
|
||||
const role = await this.em.findOne(Role, { id: roleId });
|
||||
if (!role) throw new NotFoundException('Role* not found' + roleId);
|
||||
|
||||
const shop = await this.em.findOne(Shop, { id: restId });
|
||||
const shop = await this.em.findOne(Shop, { id: shopId });
|
||||
if (!shop) throw new NotFoundException('Shop not found');
|
||||
|
||||
let adminRole = await this.adminRoleRepository.findOne({
|
||||
@@ -119,16 +119,16 @@ export class AdminService {
|
||||
return admin;
|
||||
}
|
||||
|
||||
async getAdminsForRestaurantBySuperAdmin(restId: string): Promise<Admin[]> {
|
||||
const shop = await this.em.findOne(Shop, { id: restId });
|
||||
async getShopAdminsBySuperAdmin(shopId: string): Promise<Admin[]> {
|
||||
const shop = await this.em.findOne(Shop, { id: shopId });
|
||||
if (!shop) throw new NotFoundException('Shop not found');
|
||||
|
||||
return this.adminRepository.find({ roles: { shop: shop } }, { populate: ['roles', 'roles.role'] });
|
||||
}
|
||||
|
||||
async update(adminId: string, restId: string, dto: UpdateAdminDto): Promise<Admin> {
|
||||
async update(adminId: string, shopId: string, dto: UpdateAdminDto): Promise<Admin> {
|
||||
const admin = await this.adminRepository.findOne(
|
||||
{ id: adminId, roles: { shop: { id: restId } } },
|
||||
{ id: adminId, roles: { shop: { id: shopId } } },
|
||||
{ populate: ['roles', 'roles.role', 'roles.shop'] },
|
||||
);
|
||||
|
||||
@@ -164,14 +164,14 @@ export class AdminService {
|
||||
// Find existing AdminRole for this admin and shop
|
||||
const existingAdminRole = await this.em.findOne(AdminRole, {
|
||||
admin: { id: adminId },
|
||||
shop: { id: restId },
|
||||
shop: { id: shopId },
|
||||
});
|
||||
|
||||
if (existingAdminRole) {
|
||||
// Update existing role
|
||||
existingAdminRole.role = role;
|
||||
} else {
|
||||
const shop = await this.em.findOne(Shop, { id: restId });
|
||||
const shop = await this.em.findOne(Shop, { id: shopId });
|
||||
if (!shop) throw new NotFoundException('Shop not found');
|
||||
// Create new AdminRole
|
||||
const newAdminRole = this.em.create(AdminRole, {
|
||||
@@ -187,8 +187,8 @@ export class AdminService {
|
||||
return admin;
|
||||
}
|
||||
|
||||
async remove(adminId: string, restId: string): Promise<void> {
|
||||
const adminRole = await this.adminRoleRepository.findOne({ admin: { id: adminId }, shop: { id: restId } });
|
||||
async remove(adminId: string, shopId: string): Promise<void> {
|
||||
const adminRole = await this.adminRoleRepository.findOne({ admin: { id: adminId }, shop: { id: shopId } });
|
||||
if (!adminRole) {
|
||||
throw new NotFoundException('Admin role not found');
|
||||
}
|
||||
|
||||
@@ -10,10 +10,10 @@ export class AdminRepository extends EntityRepository<Admin> {
|
||||
super(em, Admin);
|
||||
}
|
||||
|
||||
async findAdminsWithPermission(restaurantId: string, permission: string): Promise<Admin[]> {
|
||||
async findAdminsWithPermission(shopId: string, permission: string): Promise<Admin[]> {
|
||||
const admins = await this.em.find(
|
||||
Admin,
|
||||
{ roles: { shop: { id: restaurantId }, role: { permissions: { name: permission } } } },
|
||||
{ roles: { shop: { id: shopId }, role: { permissions: { name: permission } } } },
|
||||
{ populate: ['roles', 'roles.role', 'roles.role.permissions'] },
|
||||
);
|
||||
return admins;
|
||||
|
||||
@@ -8,7 +8,7 @@ export enum RefreshTokenType {
|
||||
}
|
||||
|
||||
@Entity({ tableName: 'refreshtokens' })
|
||||
@Index({ properties: ['ownerId', 'restId', 'type'] })
|
||||
@Index({ properties: ['ownerId', 'shopId', 'type'] })
|
||||
@Index({ properties: ['hashedToken'] })
|
||||
@Index({ properties: ['expiresAt'] })
|
||||
export class RefreshToken extends BaseEntity {
|
||||
@@ -19,7 +19,7 @@ export class RefreshToken extends BaseEntity {
|
||||
ownerId!: string;
|
||||
|
||||
@Property()
|
||||
restId!: string;
|
||||
shopId!: string;
|
||||
|
||||
@Enum(() => RefreshTokenType)
|
||||
type!: RefreshTokenType;
|
||||
|
||||
@@ -82,7 +82,7 @@ export class AdminAuthGuard implements CanActivate {
|
||||
if (!hasPermission) {
|
||||
this.logger.warn('Insufficient permissions', {
|
||||
adminId: payload.adminId,
|
||||
restId: payload.shopId,
|
||||
shopId: payload.shopId,
|
||||
required: requiredPermissions,
|
||||
has: adminPermission,
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ export class OptionalAuthGuard implements CanActivate {
|
||||
private readonly jwtService: JwtService,
|
||||
@Inject(ConfigService)
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest<UserOptionalAuthRequest>();
|
||||
@@ -73,7 +73,7 @@ export class OptionalAuthGuard implements CanActivate {
|
||||
this.logger.debug('Token verification failed in OptionalAuthGuard', {
|
||||
error: err instanceof Error ? err.message : 'Unknown error',
|
||||
});
|
||||
// Set restId from slug
|
||||
// Set shopId from slug
|
||||
|
||||
request['slug'] = slug;
|
||||
|
||||
|
||||
@@ -31,12 +31,12 @@ export class AuthService {
|
||||
this.OTP_EXPIRATION_TIME = this.configService.get<number>('OTP_EXPIRATION_TIME') ?? 240;
|
||||
}
|
||||
|
||||
private userOtpKey(restaurantSlug: string, phone: string) {
|
||||
return `otp:${restaurantSlug}:${phone}`;
|
||||
private userOtpKey(shopSlug: string, phone: string) {
|
||||
return `otp:${shopSlug}:${phone}`;
|
||||
}
|
||||
|
||||
private adminOtpKey(restaurantSlug: string, phone: string) {
|
||||
return `otp-admin:${restaurantSlug}:${phone}`;
|
||||
private adminOtpKey(shopSlug: string, phone: string) {
|
||||
return `otp-admin:${shopSlug}:${phone}`;
|
||||
}
|
||||
|
||||
async requestOtp(dto: RequestOtpDto, isAdmin: boolean) {
|
||||
@@ -62,15 +62,15 @@ export class AuthService {
|
||||
|
||||
const user = await this.userService.findOrCreateByPhone(normalizedPhone);
|
||||
|
||||
const rest = await this.shopRepository.findOne({ slug });
|
||||
const shop = await this.shopRepository.findOne({ slug });
|
||||
|
||||
if (!rest) {
|
||||
if (!shop) {
|
||||
throw new BadRequestException(RestMessage.NOT_FOUND);
|
||||
}
|
||||
|
||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(user.id, rest.id, false, slug);
|
||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(user.id, shop.id, false, slug);
|
||||
|
||||
const userResponse = UserLoginTransformer.transform(user, rest);
|
||||
const userResponse = UserLoginTransformer.transform(user, shop);
|
||||
|
||||
return { tokens, user: userResponse };
|
||||
}
|
||||
@@ -87,15 +87,15 @@ export class AuthService {
|
||||
|
||||
const admin = await this.adminService.findByPhoneAndShopSlug(normalizedPhone, slug);
|
||||
|
||||
const rest = await this.shopRepository.findOne({ slug });
|
||||
const shop = await this.shopRepository.findOne({ slug });
|
||||
|
||||
if (!rest) {
|
||||
if (!shop) {
|
||||
throw new BadRequestException(RestMessage.NOT_FOUND);
|
||||
}
|
||||
|
||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, rest.id, true, slug);
|
||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, shop.id, true, slug);
|
||||
|
||||
const adminResponse = await AdminLoginTransformer.transform(admin, rest);
|
||||
const adminResponse = await AdminLoginTransformer.transform(admin, shop);
|
||||
|
||||
return { tokens, admin: adminResponse };
|
||||
}
|
||||
@@ -111,15 +111,15 @@ export class AuthService {
|
||||
throw new BadRequestException(AuthMessage.ADMIN_NOT_FOUND);
|
||||
}
|
||||
|
||||
const rest = await this.shopRepository.findOne({ slug });
|
||||
const shop = await this.shopRepository.findOne({ slug });
|
||||
|
||||
if (!rest) {
|
||||
if (!shop) {
|
||||
throw new BadRequestException(RestMessage.NOT_FOUND);
|
||||
}
|
||||
|
||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, rest.id, true, slug);
|
||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, shop.id, true, slug);
|
||||
|
||||
const adminResponse = await AdminLoginTransformer.transform(admin, rest);
|
||||
const adminResponse = await AdminLoginTransformer.transform(admin, shop);
|
||||
|
||||
return { tokens, admin: adminResponse };
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export class TokensService {
|
||||
|
||||
async storeRefreshToken(
|
||||
ownerId: string,
|
||||
restId: string,
|
||||
shopId: string,
|
||||
refreshToken: string,
|
||||
type: RefreshTokenType,
|
||||
em?: EntityManager,
|
||||
@@ -70,7 +70,7 @@ export class TokensService {
|
||||
const token = entityManager.create(RefreshToken, {
|
||||
hashedToken,
|
||||
ownerId,
|
||||
restId,
|
||||
shopId,
|
||||
type,
|
||||
expiresAt,
|
||||
});
|
||||
@@ -106,11 +106,11 @@ export class TokensService {
|
||||
|
||||
// Store token data before removal
|
||||
const ownerId = token.ownerId;
|
||||
const restId = token.restId;
|
||||
const shopId = token.shopId;
|
||||
const isAdmin = token.type === RefreshTokenType.ADMIN;
|
||||
|
||||
// Verify shop still exists
|
||||
const shop = await em.findOne(Shop, { id: restId });
|
||||
const shop = await em.findOne(Shop, { id: shopId });
|
||||
if (!shop) {
|
||||
throw new UnauthorizedException(AuthMessage.INVALID_REFRESH_TOKEN);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ApplyCouponDto } from '../dto/apply-coupon.dto';
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth, ApiBody, ApiParam, ApiHeader } from '@nestjs/swagger';
|
||||
import { AuthGuard } from 'src/modules/auth/guards/auth.guard';
|
||||
import { UserId } from 'src/common/decorators/user-id.decorator';
|
||||
import { ShopId } from 'src/common/decorators/rest-id.decorator';
|
||||
import { ShopId } from 'src/common/decorators/shop-id.decorator';
|
||||
import { SetAllCartParmsDto } from '../dto/set-all-cart-params.dto';
|
||||
import { API_HEADER_SLUG } from 'src/common/constants/index';
|
||||
|
||||
|
||||
@@ -103,8 +103,8 @@ export class CartCalculationService {
|
||||
/**
|
||||
* Calculate tax
|
||||
*/
|
||||
async calculateTax(restaurantId: string, amountAfterDiscounts: number): Promise<number> {
|
||||
const shop = await this.em.findOne(Shop, { id: restaurantId });
|
||||
async calculateTax(shopId: string, amountAfterDiscounts: number): Promise<number> {
|
||||
const shop = await this.em.findOne(Shop, { id: shopId });
|
||||
const vat = shop?.vat ? Number(shop.vat) : 0;
|
||||
if (!vat || vat <= 0) return 0;
|
||||
return (Math.max(0, amountAfterDiscounts) * vat) / 100;
|
||||
|
||||
@@ -20,8 +20,8 @@ import { OptionalAuthGuard } from 'src/modules/auth/guards/optinalAuth.guard';
|
||||
import { API_HEADER_SLUG } from 'src/common/constants';
|
||||
import { Permission } from 'src/common/enums/permission.enum';
|
||||
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
||||
import { ShopId } from 'src/common/decorators/rest-id.decorator';
|
||||
import { RestSlug } from 'src/common/decorators/rest-slug.decorator';
|
||||
import { ShopId } from 'src/common/decorators/shop-id.decorator';
|
||||
import { ShopSlug } from 'src/common/decorators/rest-slug.decorator';
|
||||
|
||||
@ApiTags('contact')
|
||||
@Controller()
|
||||
@@ -32,8 +32,8 @@ export class ContactController {
|
||||
@Post('public/contact')
|
||||
@ApiOperation({ summary: 'Create a new contact (public endpoint)' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
async create(@Body() createContactDto: CreateContactDto, @RestSlug() slug: string, @ShopId() restId?: string) {
|
||||
return this.contactService.create(createContactDto, restId, slug);
|
||||
async create(@Body() createContactDto: CreateContactDto, @ShopSlug() slug: string, @ShopId() shopId?: string) {
|
||||
return this.contactService.create(createContactDto, shopId, slug);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ export class ContactController {
|
||||
@ApiQuery({ name: 'orderBy', required: false, type: String })
|
||||
@ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] })
|
||||
@ApiQuery({ name: 'status', required: false, enum: ['new', 'seen'] })
|
||||
async findAllPaginated(@ShopId() restId: string, @Query() dto: FindContactsDto) {
|
||||
return this.contactService.findAllPaginatedAdmin(dto, restId);
|
||||
async findAllPaginated(@ShopId() shopId: string, @Query() dto: FindContactsDto) {
|
||||
return this.contactService.findAllPaginatedAdmin(dto, shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -58,8 +58,8 @@ export class ContactController {
|
||||
@Get('admin/contact/:id')
|
||||
@ApiOperation({ summary: 'Get contact details by ID (admin only)' })
|
||||
@ApiParam({ name: 'id', description: 'Contact ID' })
|
||||
async findOne(@ShopId() restId: string, @Param('id') id: string) {
|
||||
return this.contactService.findOne(id, restId);
|
||||
async findOne(@ShopId() shopId: string, @Param('id') id: string) {
|
||||
return this.contactService.findOne(id, shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -68,8 +68,8 @@ export class ContactController {
|
||||
@Patch('admin/contact/:id/status')
|
||||
@ApiOperation({ summary: 'Update contact status (admin only)' })
|
||||
@ApiParam({ name: 'id', description: 'Contact ID' })
|
||||
async updateStatus(@ShopId() restId: string, @Param('id') id: string, @Body() dto: UpdateContactStatusDto) {
|
||||
return this.contactService.updateStatus(id, dto, restId);
|
||||
async updateStatus(@ShopId() shopId: string, @Param('id') id: string, @Body() dto: UpdateContactStatusDto) {
|
||||
return this.contactService.updateStatus(id, dto, shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -78,8 +78,8 @@ export class ContactController {
|
||||
@Delete('admin/contact/:id')
|
||||
@ApiOperation({ summary: 'Hard delete a contact (admin only)' })
|
||||
@ApiParam({ name: 'id', description: 'Contact ID' })
|
||||
async remove(@ShopId() restId: string, @Param('id') id: string) {
|
||||
await this.contactService.remove(id, restId);
|
||||
async remove(@ShopId() shopId: string, @Param('id') id: string) {
|
||||
await this.contactService.remove(id, shopId);
|
||||
}
|
||||
|
||||
/*** Super Admin ***/
|
||||
|
||||
@@ -17,18 +17,18 @@ export class ContactService {
|
||||
private readonly em: EntityManager,
|
||||
) { }
|
||||
|
||||
async create(dto: CreateContactDto, restId?: string, slug?: string): Promise<Contact> {
|
||||
async create(dto: CreateContactDto, shopId?: string, slug?: string): Promise<Contact> {
|
||||
|
||||
|
||||
let shop: Shop | null = null;
|
||||
|
||||
if ((restId || slug) && dto.scope === ContactScope.SHOP) {
|
||||
if ((shopId || slug) && dto.scope === ContactScope.SHOP) {
|
||||
shop = await this.em.findOne(Shop, {
|
||||
...(restId ? { id: restId } : {})
|
||||
...(shopId ? { id: shopId } : {})
|
||||
, ...(slug ? { slug: slug } : {})
|
||||
});
|
||||
if (!shop) {
|
||||
throw new NotFoundException(`Shop with ID ${restId} not found`);
|
||||
throw new NotFoundException(`Shop with ID ${shopId} not found`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export class ContactService {
|
||||
return contact;
|
||||
}
|
||||
|
||||
async findAllPaginatedAdmin(dto: FindContactsDto, restaurantId?: string): Promise<PaginatedResult<Contact>> {
|
||||
async findAllPaginatedAdmin(dto: FindContactsDto, shopId?: string): Promise<PaginatedResult<Contact>> {
|
||||
return this.contactRepository.findAllPaginated({
|
||||
page: dto.page,
|
||||
limit: dto.limit,
|
||||
@@ -50,7 +50,7 @@ export class ContactService {
|
||||
order: dto.order,
|
||||
status: dto.status,
|
||||
scope: ContactScope.SHOP,
|
||||
restaurantId
|
||||
shopId
|
||||
});
|
||||
}
|
||||
async findAllPaginatedSuper(dto: FindContactsDto): Promise<PaginatedResult<Contact>> {
|
||||
@@ -65,10 +65,10 @@ export class ContactService {
|
||||
});
|
||||
}
|
||||
|
||||
async findOne(id: string, restaurantId?: string): Promise<Contact> {
|
||||
async findOne(id: string, shopId?: string): Promise<Contact> {
|
||||
const where: any = { id };
|
||||
if (restaurantId) {
|
||||
where.shop = restaurantId;
|
||||
if (shopId) {
|
||||
where.shop = shopId;
|
||||
}
|
||||
|
||||
const contact = await this.contactRepository.findOne(where);
|
||||
@@ -78,10 +78,10 @@ export class ContactService {
|
||||
return contact;
|
||||
}
|
||||
|
||||
async updateStatus(id: string, dto: UpdateContactStatusDto, restaurantId?: string): Promise<Contact> {
|
||||
async updateStatus(id: string, dto: UpdateContactStatusDto, shopId?: string): Promise<Contact> {
|
||||
const where: any = { id };
|
||||
if (restaurantId) {
|
||||
where.shop = restaurantId;
|
||||
if (shopId) {
|
||||
where.shop = shopId;
|
||||
}
|
||||
|
||||
const contact = await this.contactRepository.findOne(where);
|
||||
@@ -93,10 +93,10 @@ export class ContactService {
|
||||
return contact;
|
||||
}
|
||||
|
||||
async remove(id: string, restaurantId?: string): Promise<void> {
|
||||
async remove(id: string, shopId?: string): Promise<void> {
|
||||
const where: any = { id };
|
||||
if (restaurantId) {
|
||||
where.shop = restaurantId;
|
||||
if (shopId) {
|
||||
where.shop = shopId;
|
||||
}
|
||||
|
||||
const contact = await this.contactRepository.findOne(where);
|
||||
|
||||
@@ -13,7 +13,7 @@ type FindContactsOpts = {
|
||||
order?: 'asc' | 'desc';
|
||||
status?: ContactStatusEnum;
|
||||
scope?: ContactScope;
|
||||
restaurantId?: string;
|
||||
shopId?: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@@ -27,7 +27,7 @@ export class ContactRepository extends EntityRepository<Contact> {
|
||||
* Supports: search (subject/content/phone), status, ordering.
|
||||
*/
|
||||
async findAllPaginated(opts: FindContactsOpts = {}): Promise<PaginatedResult<Contact>> {
|
||||
const { page = 1, limit = 10, search, scope, orderBy = 'createdAt', order = 'desc', status, restaurantId } = opts;
|
||||
const { page = 1, limit = 10, search, scope, orderBy = 'createdAt', order = 'desc', status, shopId } = opts;
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
@@ -39,8 +39,8 @@ export class ContactRepository extends EntityRepository<Contact> {
|
||||
if (scope) {
|
||||
where.scope = scope;
|
||||
}
|
||||
if (restaurantId) {
|
||||
where.shop = restaurantId;
|
||||
if (shopId) {
|
||||
where.shop = shopId;
|
||||
}
|
||||
|
||||
if (search) {
|
||||
|
||||
@@ -25,12 +25,12 @@ export class CouponRepository extends EntityRepository<Coupon> {
|
||||
* Find coupons with pagination and optional filters.
|
||||
* Supports: search (code/name), type, isActive, ordering.
|
||||
*/
|
||||
async findAllPaginated(restId: string, opts: FindCouponsOpts = {}): Promise<PaginatedResult<Coupon>> {
|
||||
async findAllPaginated(shopId: string, opts: FindCouponsOpts = {}): Promise<PaginatedResult<Coupon>> {
|
||||
const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc', type, isActive } = opts;
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const where: FilterQuery<Coupon> = { shop: { id: restId } };
|
||||
const where: FilterQuery<Coupon> = { shop: { id: shopId } };
|
||||
|
||||
if (typeof isActive === 'boolean') {
|
||||
where.isActive = isActive;
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { DeliveryService } from '../providers/delivery.service';
|
||||
import { CreateDeliveryDto } from '../dto/create-delivery.dto';
|
||||
import { UpdateDeliveryDto } from '../dto/update-delivery.dto';
|
||||
import { ShopId } from 'src/common/decorators/rest-id.decorator';
|
||||
import { ShopId } from 'src/common/decorators/shop-id.decorator';
|
||||
import { AuthGuard } from 'src/modules/auth/guards/auth.guard';
|
||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||
import { Delivery } from '../entities/delivery.entity';
|
||||
@@ -31,8 +31,8 @@ export class DeliveryController {
|
||||
@Get('public/delivery-methods/shop')
|
||||
@ApiOperation({ summary: 'Get shop delivery methods' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
findAllDeliveryMethods(@ShopId() restId: string) {
|
||||
return this.deliveryService.findAllForRestaurantId(restId);
|
||||
findAllDeliveryMethods(@ShopId() shopId: string) {
|
||||
return this.deliveryService.findAllForRestaurantId(shopId);
|
||||
}
|
||||
|
||||
/*** Admin ***/
|
||||
@@ -42,8 +42,8 @@ export class DeliveryController {
|
||||
@Post('admin/delivery-methods/shop')
|
||||
@ApiOperation({ summary: 'Create a delivery method for a shop' })
|
||||
@ApiBody({ type: CreateDeliveryDto })
|
||||
create(@Body() createDto: CreateDeliveryDto, @ShopId() restId: string) {
|
||||
return this.deliveryService.create(restId, createDto);
|
||||
create(@Body() createDto: CreateDeliveryDto, @ShopId() shopId: string) {
|
||||
return this.deliveryService.create(shopId, createDto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -51,8 +51,8 @@ export class DeliveryController {
|
||||
@Permissions(Permission.MANAGE_DELIVERY)
|
||||
@Get('admin/delivery-methods/shop')
|
||||
@ApiOperation({ summary: 'Get the shop delivery methods' })
|
||||
findRestaurantDeliveryMethods(@ShopId() restId: string) {
|
||||
return this.deliveryService.findAllForRestaurantId(restId);
|
||||
findRestaurantDeliveryMethods(@ShopId() shopId: string) {
|
||||
return this.deliveryService.findAllForRestaurantId(shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -61,8 +61,8 @@ export class DeliveryController {
|
||||
@Get('admin/delivery-methods/shop/:deliveryId')
|
||||
@ApiOperation({ summary: 'Get a shop delivery method by delivery ID' })
|
||||
@ApiParam({ name: 'deliveryId', description: 'Delivery method ID' })
|
||||
findOne(@Param('deliveryId') deliveryId: string, @ShopId() restId: string) {
|
||||
return this.deliveryService.findOrFail(restId, deliveryId);
|
||||
findOne(@Param('deliveryId') deliveryId: string, @ShopId() shopId: string) {
|
||||
return this.deliveryService.findOrFail(shopId, deliveryId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -72,8 +72,8 @@ export class DeliveryController {
|
||||
@ApiOperation({ summary: 'Update a shop delivery method' })
|
||||
@ApiParam({ name: 'deliveryId', description: 'Delivery method ID' })
|
||||
@ApiBody({ type: UpdateDeliveryDto })
|
||||
update(@Param('deliveryId') deliveryId: string, @Body() updateDto: UpdateDeliveryDto, @ShopId() restId: string) {
|
||||
return this.deliveryService.update(restId, deliveryId, updateDto);
|
||||
update(@Param('deliveryId') deliveryId: string, @Body() updateDto: UpdateDeliveryDto, @ShopId() shopId: string) {
|
||||
return this.deliveryService.update(shopId, deliveryId, updateDto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -82,7 +82,7 @@ export class DeliveryController {
|
||||
@Delete('admin/delivery-methods/shop/:deliveryId')
|
||||
@ApiOperation({ summary: 'Delete a shop delivery method' })
|
||||
@ApiParam({ name: 'deliveryId', description: 'Delivery method ID' })
|
||||
remove(@Param('deliveryId') deliveryId: string, @ShopId() restId: string) {
|
||||
return this.deliveryService.remove(restId, deliveryId);
|
||||
remove(@Param('deliveryId') deliveryId: string, @ShopId() shopId: string) {
|
||||
return this.deliveryService.remove(shopId, deliveryId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,15 @@ export class DeliveryService {
|
||||
private readonly em: EntityManager,
|
||||
) { }
|
||||
|
||||
async create(restId: string, createDto: CreateDeliveryDto): Promise<Delivery> {
|
||||
const shop = await this.shopRepository.findOne({ id: restId });
|
||||
async create(shopId: string, createDto: CreateDeliveryDto): Promise<Delivery> {
|
||||
const shop = await this.shopRepository.findOne({ id: shopId });
|
||||
if (!shop) {
|
||||
throw new NotFoundException(DeliveryMessage.SHOP_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Check if the delivery method with the same name already exists for this shop
|
||||
const existing = await this.deliveryRepository.findOne({
|
||||
shop: { id: restId },
|
||||
shop: { id: shopId },
|
||||
method: createDto.method,
|
||||
});
|
||||
|
||||
@@ -50,14 +50,14 @@ export class DeliveryService {
|
||||
return delivery;
|
||||
}
|
||||
|
||||
async findAllForRestaurantId(restId: string): Promise<Delivery[]> {
|
||||
return this.deliveryRepository.find({ shop: { id: restId } }, { orderBy: { order: 'asc' } });
|
||||
async findAllForRestaurantId(shopId: string): Promise<Delivery[]> {
|
||||
return this.deliveryRepository.find({ shop: { id: shopId } }, { orderBy: { order: 'asc' } });
|
||||
}
|
||||
|
||||
async findOrFail(restId: string, deliveryId: string): Promise<Delivery> {
|
||||
async findOrFail(shopId: string, deliveryId: string): Promise<Delivery> {
|
||||
const delivery = await this.deliveryRepository.findOne({
|
||||
id: deliveryId,
|
||||
shop: { id: restId },
|
||||
shop: { id: shopId },
|
||||
});
|
||||
|
||||
if (!delivery) {
|
||||
@@ -67,9 +67,9 @@ export class DeliveryService {
|
||||
return delivery;
|
||||
}
|
||||
|
||||
async update(restId: string, deliveryId: string, updateDto: UpdateDeliveryDto): Promise<Delivery> {
|
||||
async update(shopId: string, deliveryId: string, updateDto: UpdateDeliveryDto): Promise<Delivery> {
|
||||
const delivery = await this.deliveryRepository.findOne({
|
||||
shop: { id: restId },
|
||||
shop: { id: shopId },
|
||||
id: deliveryId,
|
||||
});
|
||||
|
||||
@@ -80,7 +80,7 @@ export class DeliveryService {
|
||||
// If method is being updated, check for conflicts
|
||||
if (updateDto.method !== undefined && updateDto.method !== delivery.method) {
|
||||
const existing = await this.deliveryRepository.findOne({
|
||||
shop: { id: restId },
|
||||
shop: { id: shopId },
|
||||
method: updateDto.method,
|
||||
id: { $ne: deliveryId },
|
||||
});
|
||||
@@ -96,9 +96,9 @@ export class DeliveryService {
|
||||
return delivery;
|
||||
}
|
||||
|
||||
async remove(restId: string, deliveryId: string): Promise<void> {
|
||||
async remove(shopId: string, deliveryId: string): Promise<void> {
|
||||
const delivery = await this.deliveryRepository.findOne({
|
||||
shop: { id: restId },
|
||||
shop: { id: shopId },
|
||||
id: deliveryId,
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { NotificationPreferenceService } from '../services/notification-preferen
|
||||
import { AuthGuard } from '../../auth/guards/auth.guard';
|
||||
import { UserId } from '../../../common/decorators/user-id.decorator';
|
||||
import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard';
|
||||
import { ShopId } from '../../../common/decorators/rest-id.decorator';
|
||||
import { ShopId } from '../../../common/decorators/shop-id.decorator';
|
||||
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
|
||||
import { CreatePreferenceDto } from '../dto/create-preference.dto';
|
||||
import { AdminId } from 'src/common/decorators/admin-id.decorator';
|
||||
@@ -38,14 +38,14 @@ export class NotificationsController {
|
||||
@ApiQuery({ name: 'status', required: false, enum: ['seen', 'unseen'], description: 'Filter by notification status' })
|
||||
async getUserNotifications(
|
||||
@UserId() userId: string,
|
||||
@ShopId() restaurantId: string,
|
||||
@ShopId() shopId: string,
|
||||
@Query('limit') limit?: number,
|
||||
@Query('cursor') cursor?: string,
|
||||
@Query('status') status?: 'seen' | 'unseen',
|
||||
) {
|
||||
return await this.notificationService.findByUserAndRestaurant(
|
||||
userId,
|
||||
restaurantId,
|
||||
shopId,
|
||||
limit ? parseInt(limit.toString(), 10) : 50,
|
||||
cursor,
|
||||
status,
|
||||
@@ -57,8 +57,8 @@ export class NotificationsController {
|
||||
@Get('public/notifications/unseen-count')
|
||||
@ApiOperation({ summary: 'Get unseen notifications count for user' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
async getUserUnseenCount(@UserId() userId: string, @ShopId() restaurantId: string) {
|
||||
const count = await this.notificationService.countUnseenByUserAndRestaurant(userId, restaurantId);
|
||||
async getUserUnseenCount(@UserId() userId: string, @ShopId() shopId: string) {
|
||||
const count = await this.notificationService.countUnseenByUserAndRestaurant(userId, shopId);
|
||||
return { count };
|
||||
}
|
||||
|
||||
@@ -67,8 +67,8 @@ export class NotificationsController {
|
||||
@Put('public/notifications/:id')
|
||||
@ApiOperation({ summary: 'Read a notification ' })
|
||||
@ApiParam({ name: 'id', description: 'Notification ID' })
|
||||
async readNotificationUser(@ShopId() restaurantId: string, @Param('id') id: string, @UserId() userId: string) {
|
||||
await this.notificationService.readNotificationAsUser(id, userId, restaurantId);
|
||||
async readNotificationUser(@ShopId() shopId: string, @Param('id') id: string, @UserId() userId: string) {
|
||||
await this.notificationService.readNotificationAsUser(id, userId, shopId);
|
||||
return { message: NotificationMessage.READ_SUCCESS };
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ export class NotificationsController {
|
||||
@ApiBearerAuth()
|
||||
@Put('public/notifications/read/all')
|
||||
@ApiOperation({ summary: 'Read all notification ' })
|
||||
async readAllNotificationUser(@ShopId() restaurantId: string, @UserId() userId: string) {
|
||||
await this.notificationService.readAllNotifsAsUser(userId, restaurantId);
|
||||
async readAllNotificationUser(@ShopId() shopId: string, @UserId() userId: string) {
|
||||
await this.notificationService.readAllNotifsAsUser(userId, shopId);
|
||||
return { message: NotificationMessage.READ_SUCCESS };
|
||||
}
|
||||
/* ***************** Admin Endpoints ***************** */
|
||||
@@ -96,21 +96,21 @@ export class NotificationsController {
|
||||
@ApiQuery({ name: 'status', required: false, enum: ['seen', 'unseen'], description: 'Filter by notification status' })
|
||||
async getAdminNotifications(
|
||||
@AdminId() adminId: string,
|
||||
@ShopId() restaurantId: string,
|
||||
@ShopId() shopId: string,
|
||||
@Query('limit') limit?: number,
|
||||
@Query('cursor') cursor?: string,
|
||||
@Query('status') status?: 'seen' | 'unseen',
|
||||
) {
|
||||
const parsedLimit = limit ? parseInt(limit.toString(), 10) : 50;
|
||||
return await this.notificationService.findByRestaurant(restaurantId, adminId, parsedLimit, cursor, status);
|
||||
return await this.notificationService.findByRestaurant(shopId, adminId, parsedLimit, cursor, status);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/notifications/unseen-count')
|
||||
@ApiOperation({ summary: 'Get unseen notifications count for admin' })
|
||||
async getAdminUnseenCount(@AdminId() adminId: string, @ShopId() restaurantId: string) {
|
||||
const count = await this.notificationService.countUnseenByRestaurant(adminId, restaurantId);
|
||||
async getAdminUnseenCount(@AdminId() adminId: string, @ShopId() shopId: string) {
|
||||
const count = await this.notificationService.countUnseenByRestaurant(adminId, shopId);
|
||||
return { count };
|
||||
}
|
||||
|
||||
@@ -119,8 +119,8 @@ export class NotificationsController {
|
||||
@Put('admin/notifications/:id')
|
||||
@ApiOperation({ summary: 'Read a notification ' })
|
||||
@ApiParam({ name: 'id', description: 'Notification ID' })
|
||||
async readNotificationAdmin(@ShopId() restaurantId: string, @AdminId() adminId: string, @Param('id') id: string) {
|
||||
await this.notificationService.readNotificationAdmin(id, adminId, restaurantId);
|
||||
async readNotificationAdmin(@ShopId() shopId: string, @AdminId() adminId: string, @Param('id') id: string) {
|
||||
await this.notificationService.readNotificationAdmin(id, adminId, shopId);
|
||||
return { message: NotificationMessage.READ_SUCCESS };
|
||||
}
|
||||
|
||||
@@ -128,8 +128,8 @@ export class NotificationsController {
|
||||
@ApiBearerAuth()
|
||||
@Put('admin/notifications/read/all')
|
||||
@ApiOperation({ summary: 'Read all notification ' })
|
||||
async readAllNotificationAdmin(@ShopId() restaurantId: string, @AdminId() adminId: string, @Param('id') id: string) {
|
||||
await this.notificationService.readAllNotifsAsAdmin(adminId, restaurantId);
|
||||
async readAllNotificationAdmin(@ShopId() shopId: string, @AdminId() adminId: string, @Param('id') id: string) {
|
||||
await this.notificationService.readAllNotifsAsAdmin(adminId, shopId);
|
||||
return { message: NotificationMessage.READ_SUCCESS };
|
||||
}
|
||||
|
||||
@@ -138,8 +138,8 @@ export class NotificationsController {
|
||||
@Permissions(Permission.MANAGE_SETTINGS)
|
||||
@Get('admin/notification-preferences')
|
||||
@ApiOperation({ summary: 'Get all notification preferences for a shop' })
|
||||
async getPreferences(@ShopId() restaurantId: string) {
|
||||
return this.preferenceService.findByRestaurant(restaurantId);
|
||||
async getPreferences(@ShopId() shopId: string) {
|
||||
return this.preferenceService.findByRestaurant(shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -149,11 +149,11 @@ export class NotificationsController {
|
||||
@ApiOperation({ summary: 'Update notification channels' })
|
||||
@ApiParam({ name: 'id', description: 'Notification preference ID' })
|
||||
async updatePreference(
|
||||
@ShopId() restaurantId: string,
|
||||
@ShopId() shopId: string,
|
||||
@Param('id') preferenceId: string,
|
||||
@Body() dto: UpdatePreferenceDto,
|
||||
) {
|
||||
return this.preferenceService.updatePreference(preferenceId, restaurantId, dto);
|
||||
return this.preferenceService.updatePreference(preferenceId, shopId, dto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -162,18 +162,18 @@ export class NotificationsController {
|
||||
@Post('admin/notification-preferences')
|
||||
@ApiOperation({ summary: 'Create notification preference' })
|
||||
async createPreference(
|
||||
@ShopId() restaurantId: string,
|
||||
@ShopId() shopId: string,
|
||||
@Body() dto: CreatePreferenceDto,
|
||||
) {
|
||||
return this.preferenceService.create(restaurantId, dto);
|
||||
return this.preferenceService.create(shopId, dto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/notifications/sms-usage')
|
||||
@ApiOperation({ summary: 'Get SMS usage for my shop' })
|
||||
async getRestaurantSmsUsage(@ShopId() restaurantId: string) {
|
||||
const smsCount = await this.notificationService.getSmsCountByRestaurantId(restaurantId);
|
||||
async getRestaurantSmsUsage(@ShopId() shopId: string) {
|
||||
const smsCount = await this.notificationService.getSmsCountByRestaurantId(shopId);
|
||||
return { smsCount };
|
||||
}
|
||||
|
||||
|
||||
@@ -4,23 +4,23 @@ import type { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
|
||||
|
||||
/**
|
||||
* Extract shop ID from authenticated WebSocket client
|
||||
* Use this decorator in WebSocket handlers to get the restId from the authenticated admin
|
||||
* Use this decorator in WebSocket handlers to get the shopId from the authenticated admin
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @SubscribeMessage('get:notifications')
|
||||
* handleGetNotifications(@WsRestId() restId: string) {
|
||||
* // restId is automatically extracted from authenticated admin
|
||||
* handleGetNotifications(@WsRestId() shopId: string) {
|
||||
* // shopId is automatically extracted from authenticated admin
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export const WsRestId = createParamDecorator((data: unknown, ctx: ExecutionContext): string => {
|
||||
const client = ctx.switchToWs().getClient<AuthenticatedSocket>();
|
||||
const restId = client.restId;
|
||||
const shopId = client.shopId;
|
||||
|
||||
if (!restId) {
|
||||
if (!shopId) {
|
||||
throw new Error('Shop ID not found. Ensure WsAdminAuthGuard is applied.');
|
||||
}
|
||||
|
||||
return restId;
|
||||
return shopId;
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ export class SendNotificationDto {
|
||||
@ApiProperty({ description: 'Shop ID (ULID)' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
restaurantId: string;
|
||||
shopId: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'User ID (ULID)' })
|
||||
@IsString()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export class SmsSentEvent {
|
||||
constructor(
|
||||
public readonly phoneNumber: string,
|
||||
public readonly restaurantId: string,
|
||||
) {}
|
||||
public readonly shopId: string,
|
||||
) { }
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IAdminTokenPayload } from '../../auth/interfaces/IToken-payload';
|
||||
|
||||
export interface AuthenticatedSocket extends Socket {
|
||||
adminId?: string;
|
||||
restId?: string;
|
||||
shopId?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -48,9 +48,9 @@ export class WsAdminAuthGuard implements CanActivate {
|
||||
|
||||
// Attach admin info to socket
|
||||
(client as AuthenticatedSocket).adminId = payload.adminId;
|
||||
(client as AuthenticatedSocket).restId = payload.shopId;
|
||||
(client as AuthenticatedSocket).shopId = payload.shopId;
|
||||
|
||||
this.logger.log(`Admin authenticated via WebSocket: ${payload.adminId}, restId: ${payload.shopId}`);
|
||||
this.logger.log(`Admin authenticated via WebSocket: ${payload.adminId}, shopId: ${payload.shopId}`);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { NotifTitleEnum, recipientType } from './notification.interface';
|
||||
export interface SmsNotificationQueueJob {
|
||||
recipient: recipientType;
|
||||
templateId: string;
|
||||
restaurantId: string;
|
||||
shopId: string;
|
||||
parameters?: Record<string, string>;
|
||||
}
|
||||
export interface PushNotificationQueueJob {
|
||||
@@ -18,7 +18,7 @@ export interface PushNotificationQueueJob {
|
||||
pushTokens?: string[]; // Multiple FCM tokens for bulk push notifications
|
||||
}
|
||||
export interface InAppNotificationQueueJob {
|
||||
recipient: { adminId: string; restaurantId: string };
|
||||
recipient: { adminId: string; shopId: string };
|
||||
subject: NotifTitleEnum;
|
||||
body: string;
|
||||
notificationId: string;
|
||||
|
||||
@@ -40,7 +40,7 @@ export interface NotifRequest {
|
||||
// timestamp: Date;
|
||||
// notifType: NotifTypeEnum;
|
||||
// channels: NotifChannelEnum[];
|
||||
restaurantId: string;
|
||||
shopId: string;
|
||||
recipients: recipientType[];
|
||||
message: NotifRequestMessage;
|
||||
metadata: {
|
||||
|
||||
@@ -19,15 +19,15 @@ export class SmsListeners {
|
||||
async handleSmsSent(event: SmsSentEvent) {
|
||||
try {
|
||||
this.logger.log(
|
||||
`SMS sent event received: phone ${event.phoneNumber} for shop: ${event.restaurantId}`,
|
||||
`SMS sent event received: phone ${event.phoneNumber} for shop: ${event.shopId}`,
|
||||
);
|
||||
|
||||
// Get the shop entity
|
||||
const shop = await this.shopRepository.findOne({ id: event.restaurantId });
|
||||
const shop = await this.shopRepository.findOne({ id: event.shopId });
|
||||
|
||||
if (!shop) {
|
||||
this.logger.warn(
|
||||
`Shop not found for SMS log: ${event.restaurantId}`,
|
||||
`Shop not found for SMS log: ${event.shopId}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export class SmsListeners {
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to create SMS log for event: ${event.restaurantId}`,
|
||||
`Failed to create SMS log for event: ${event.shopId}`,
|
||||
error instanceof Error ? error.stack : String(error),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
|
||||
@Inject(forwardRef(() => NotificationService))
|
||||
private readonly notificationService: NotificationService,
|
||||
private readonly moduleRef: ModuleRef,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
async handleConnection(client: Socket) {
|
||||
try {
|
||||
@@ -70,8 +70,8 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
|
||||
// }
|
||||
}
|
||||
|
||||
sendInAppNotification(repipient: { adminId: string; restaurantId: string }, payload: IInAppNotificationPayload) {
|
||||
const room = this.getRoom(repipient.adminId, repipient.restaurantId);
|
||||
sendInAppNotification(repipient: { adminId: string; shopId: string }, payload: IInAppNotificationPayload) {
|
||||
const room = this.getRoom(repipient.adminId, repipient.shopId);
|
||||
|
||||
this.logger.log(`Sending in app notification to admin: ${repipient.adminId}`);
|
||||
this.server.to(room).emit('notifications', payload, async () => {
|
||||
@@ -81,19 +81,19 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
|
||||
this.logger.log(`In app notification sent to admin: ${repipient.adminId}`);
|
||||
}
|
||||
|
||||
private getRoom(adminId: string, restaurantId: string) {
|
||||
return `shop:${restaurantId}-admin:${adminId}`;
|
||||
private getRoom(adminId: string, shopId: string) {
|
||||
return `shop:${shopId}-admin:${adminId}`;
|
||||
}
|
||||
|
||||
handleLeaveRoom(client: AuthenticatedSocket) {
|
||||
const room = this.getRoom(client.adminId!, client.restId!);
|
||||
const room = this.getRoom(client.adminId!, client.shopId!);
|
||||
void client.leave(room);
|
||||
this.logger.log(`Admin ${client.adminId} (Client ${client.id}) left room: ${room}`);
|
||||
void client.emit('left', { room, message: 'Successfully Admin left room' });
|
||||
}
|
||||
|
||||
handleJoinRoom(client: AuthenticatedSocket) {
|
||||
const room = this.getRoom(client.adminId!, client.restId!);
|
||||
const room = this.getRoom(client.adminId!, client.shopId!);
|
||||
void client.join(room);
|
||||
this.logger.log(`Admin ${client.adminId} (Client ${client.id}) joined room: ${room}`);
|
||||
void client.emit('joined', { room, message: 'Successfully Admin joined room' });
|
||||
|
||||
@@ -27,7 +27,7 @@ export class InAppProcessor extends WorkerHost {
|
||||
|
||||
try {
|
||||
this.notificationsGateway.sendInAppNotification(
|
||||
{ adminId: recipient.adminId, restaurantId: recipient.restaurantId },
|
||||
{ adminId: recipient.adminId, shopId: recipient.shopId },
|
||||
{ subject, body, notificationId },
|
||||
);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export class SmsProcessor extends WorkerHost {
|
||||
}
|
||||
|
||||
async process(job: Job<SmsNotificationQueueJob>) {
|
||||
const { recipient, templateId, parameters, restaurantId } = job.data;
|
||||
const { recipient, templateId, parameters, shopId } = job.data;
|
||||
|
||||
this.logger.log(`Processing SMS notification - Recipient: ${JSON.stringify(recipient)}, templateID: ${templateId}`);
|
||||
|
||||
@@ -58,7 +58,7 @@ export class SmsProcessor extends WorkerHost {
|
||||
|
||||
this.logger.log(`SMS notification sent successfully to ${phone}`);
|
||||
|
||||
this.eventEmitter.emit(SmsSentEvent.name, new SmsSentEvent(phone, restaurantId));
|
||||
this.eventEmitter.emit(SmsSentEvent.name, new SmsSentEvent(phone, shopId));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
||||
@@ -12,13 +12,13 @@ export class SmsLogRepository extends EntityRepository<SmsLog> {
|
||||
async getSmsCountByRestaurant(
|
||||
page: number = 1,
|
||||
limit: number = 10,
|
||||
): Promise<PaginatedResult<{ restaurantId: string; restaurantName: string; smsCount: number }>> {
|
||||
): Promise<PaginatedResult<{ shopId: string; shopName: string; smsCount: number }>> {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// Get total count of unique shops with SMS logs
|
||||
const totalResult = await this.em.execute(
|
||||
`
|
||||
SELECT COUNT(DISTINCT sl.restaurant_id) as total
|
||||
SELECT COUNT(DISTINCT sl.shop_id) as total
|
||||
FROM sms_logs sl
|
||||
WHERE sl.restaurant_id IS NOT NULL
|
||||
`,
|
||||
@@ -29,11 +29,11 @@ export class SmsLogRepository extends EntityRepository<SmsLog> {
|
||||
const results = await this.em.execute(
|
||||
`
|
||||
SELECT
|
||||
r.id as "restaurantId",
|
||||
r.name as "restaurantName",
|
||||
r.id as "shopId",
|
||||
r.name as "shopName",
|
||||
COUNT(sl.id)::int as "smsCount"
|
||||
FROM sms_logs sl
|
||||
INNER JOIN shops r ON sl.restaurant_id = r.id
|
||||
INNER JOIN shops r ON sl.shop_id = r.id
|
||||
GROUP BY r.id, r.name
|
||||
ORDER BY "smsCount" DESC, r.name ASC
|
||||
LIMIT ? OFFSET ?
|
||||
@@ -42,8 +42,8 @@ export class SmsLogRepository extends EntityRepository<SmsLog> {
|
||||
);
|
||||
|
||||
const data = results.map((row: any) => ({
|
||||
restaurantId: row.restaurantId,
|
||||
restaurantName: row.restaurantName || 'Unknown',
|
||||
shopId: row.shopId,
|
||||
shopName: row.shopName || 'Unknown',
|
||||
smsCount: parseInt(row.smsCount || '0', 10),
|
||||
}));
|
||||
|
||||
@@ -60,14 +60,14 @@ export class SmsLogRepository extends EntityRepository<SmsLog> {
|
||||
};
|
||||
}
|
||||
|
||||
async getSmsCountByRestaurantId(restaurantId: string): Promise<number> {
|
||||
async getSmsCountByRestaurantId(shopId: string): Promise<number> {
|
||||
const result = await this.em.execute(
|
||||
`
|
||||
SELECT COUNT(sl.id)::int as "smsCount"
|
||||
FROM sms_logs sl
|
||||
WHERE sl.restaurant_id = ?
|
||||
WHERE sl.shop_id = ?
|
||||
`,
|
||||
[restaurantId],
|
||||
[shopId],
|
||||
);
|
||||
|
||||
return parseInt(result[0]?.smsCount || '0', 10);
|
||||
|
||||
@@ -10,8 +10,8 @@ import { NotifTitleEnum } from '../interfaces/notification.interface';
|
||||
export class NotificationPreferenceService {
|
||||
constructor(private readonly em: EntityManager) { }
|
||||
|
||||
async create(restaurantId: string, dto: CreatePreferenceDto): Promise<NotificationPreference> {
|
||||
const shop = await this.em.findOne(Shop, { id: restaurantId });
|
||||
async create(shopId: string, dto: CreatePreferenceDto): Promise<NotificationPreference> {
|
||||
const shop = await this.em.findOne(Shop, { id: shopId });
|
||||
if (!shop) {
|
||||
throw new NotFoundException('Shop not found');
|
||||
}
|
||||
@@ -26,25 +26,25 @@ export class NotificationPreferenceService {
|
||||
return preference;
|
||||
}
|
||||
|
||||
async findByRestaurant(restaurantId: string): Promise<NotificationPreference[]> {
|
||||
async findByRestaurant(shopId: string): Promise<NotificationPreference[]> {
|
||||
return this.em.find(NotificationPreference, {
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
});
|
||||
}
|
||||
|
||||
async findByRestaurantAndType(restaurantId: string, title: NotifTitleEnum): Promise<NotificationPreference | null> {
|
||||
async findByRestaurantAndType(shopId: string, title: NotifTitleEnum): Promise<NotificationPreference | null> {
|
||||
return this.em.findOne(NotificationPreference, {
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
title,
|
||||
});
|
||||
}
|
||||
|
||||
// async updateEnabled(
|
||||
// restaurantId: string,
|
||||
// shopId: string,
|
||||
// notificationType: string,
|
||||
// enabled: boolean,
|
||||
// ): Promise<NotificationPreference> {
|
||||
// const preference = await this.findByRestaurantAndType(restaurantId, notificationType);
|
||||
// const preference = await this.findByRestaurantAndType(shopId, notificationType);
|
||||
// if (!preference) {
|
||||
// throw new NotFoundException('Notification preference not found');
|
||||
// }
|
||||
@@ -56,12 +56,12 @@ export class NotificationPreferenceService {
|
||||
|
||||
async updatePreference(
|
||||
preferenceId: string,
|
||||
restaurantId: string,
|
||||
shopId: string,
|
||||
dto: UpdatePreferenceDto,
|
||||
): Promise<NotificationPreference> {
|
||||
const preference = await this.em.findOne(NotificationPreference, {
|
||||
id: preferenceId,
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
});
|
||||
if (!preference) {
|
||||
throw new NotFoundException('Notification preference not found');
|
||||
@@ -72,9 +72,9 @@ export class NotificationPreferenceService {
|
||||
return preference;
|
||||
}
|
||||
|
||||
async delete(restaurantId: string, preferenceId: string): Promise<void> {
|
||||
async delete(shopId: string, preferenceId: string): Promise<void> {
|
||||
const preference = await this.em.findOne(NotificationPreference, {
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
id: preferenceId,
|
||||
});
|
||||
if (!preference) {
|
||||
|
||||
@@ -21,12 +21,12 @@ export class NotificationService {
|
||||
) { }
|
||||
|
||||
async sendNotification(params: NotifRequest): Promise<Notification[]> {
|
||||
const { recipients, message, metadata, restaurantId } = params;
|
||||
const { recipients, message, metadata, shopId } = params;
|
||||
|
||||
// create Database notifications
|
||||
const notifications = await this.createAdminBulkNotifications(
|
||||
recipients.map(recipient => ({
|
||||
restaurantId,
|
||||
shopId,
|
||||
title: message.title,
|
||||
content: message.content,
|
||||
adminId: 'adminId' in recipient ? recipient.adminId : null,
|
||||
@@ -35,10 +35,10 @@ export class NotificationService {
|
||||
);
|
||||
|
||||
// get admin prefrences
|
||||
const preference = await this.preferenceService.findByRestaurantAndType(restaurantId, message.title);
|
||||
const preference = await this.preferenceService.findByRestaurantAndType(shopId, message.title);
|
||||
|
||||
if (preference?.channels?.length === 0) {
|
||||
this.logger.warn(`Notification type is NONE for shop ${restaurantId}, title ${message.title}`);
|
||||
this.logger.warn(`Notification type is NONE for shop ${shopId}, title ${message.title}`);
|
||||
return notifications;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class NotificationService {
|
||||
if (preference?.channels?.includes(NotifChannelEnum.IN_APP)) {
|
||||
await this.queueService.addBulkInAppNotifications(
|
||||
notifications.map(notification => ({
|
||||
recipient: { adminId: notification.admin?.id || '', restaurantId },
|
||||
recipient: { adminId: notification.admin?.id || '', shopId },
|
||||
subject: message.title,
|
||||
body: message.content,
|
||||
notificationId: (notification as any).id,
|
||||
@@ -61,19 +61,19 @@ export class NotificationService {
|
||||
recipient,
|
||||
templateId: message.sms.templateId,
|
||||
parameters: message.sms.parameters,
|
||||
restaurantId,
|
||||
shopId,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.log(`Queued notification for shop ${restaurantId}, title ${message.title}`);
|
||||
this.logger.log(`Queued notification for shop ${shopId}, title ${message.title}`);
|
||||
|
||||
return notifications;
|
||||
}
|
||||
|
||||
async createAdminBulkNotifications(
|
||||
params: {
|
||||
restaurantId: string;
|
||||
shopId: string;
|
||||
title: NotifTitleEnum;
|
||||
content: string;
|
||||
adminId: string | null;
|
||||
@@ -82,7 +82,7 @@ export class NotificationService {
|
||||
): Promise<Notification[]> {
|
||||
const notifications = params.map(param => {
|
||||
return this.em.create(Notification, {
|
||||
shop: param.restaurantId,
|
||||
shop: param.shopId,
|
||||
admin: param.adminId,
|
||||
user: param.userId && param.userId.trim() !== '' ? param.userId : null,
|
||||
title: param.title,
|
||||
@@ -102,14 +102,14 @@ export class NotificationService {
|
||||
}
|
||||
|
||||
async findByRestaurant(
|
||||
restaurantId: string,
|
||||
shopId: string,
|
||||
adminId: string,
|
||||
limit = 50,
|
||||
cursor?: string,
|
||||
status?: 'seen' | 'unseen',
|
||||
): Promise<{ data: Notification[]; nextCursor: string | null }> {
|
||||
const where: FilterQuery<Notification> = {
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
admin: { id: adminId },
|
||||
};
|
||||
|
||||
@@ -143,14 +143,14 @@ export class NotificationService {
|
||||
|
||||
async findByUserAndRestaurant(
|
||||
userId: string,
|
||||
restaurantId: string,
|
||||
shopId: string,
|
||||
limit = 50,
|
||||
cursor?: string,
|
||||
status?: 'seen' | 'unseen',
|
||||
): Promise<{ data: Notification[]; nextCursor: string | null }> {
|
||||
const where: FilterQuery<Notification> = {
|
||||
user: { id: userId },
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
};
|
||||
|
||||
// Filter by status (seen/unseen)
|
||||
@@ -181,11 +181,11 @@ export class NotificationService {
|
||||
};
|
||||
}
|
||||
|
||||
async readNotificationAdmin(id: string, adminId: string, restaurantId: string): Promise<void> {
|
||||
async readNotificationAdmin(id: string, adminId: string, shopId: string): Promise<void> {
|
||||
const notification = await this.em.findOne(Notification, {
|
||||
id,
|
||||
admin: { id: adminId },
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
});
|
||||
if (!notification) {
|
||||
throw new NotFoundException('Notification not found');
|
||||
@@ -193,11 +193,11 @@ export class NotificationService {
|
||||
notification.seenAt = new Date();
|
||||
await this.em.persistAndFlush(notification);
|
||||
}
|
||||
async readNotificationAsUser(id: string, userId: string, restaurantId: string): Promise<void> {
|
||||
async readNotificationAsUser(id: string, userId: string, shopId: string): Promise<void> {
|
||||
const notification = await this.em.findOne(Notification, {
|
||||
id,
|
||||
user: { id: userId },
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
});
|
||||
if (!notification) {
|
||||
throw new NotFoundException('Notification not found');
|
||||
@@ -206,11 +206,11 @@ export class NotificationService {
|
||||
await this.em.persistAndFlush(notification);
|
||||
}
|
||||
|
||||
async findByRestaurantAndType(restaurantId: string, title: NotifTitleEnum, limit = 50): Promise<Notification[]> {
|
||||
async findByRestaurantAndType(shopId: string, title: NotifTitleEnum, limit = 50): Promise<Notification[]> {
|
||||
return this.em.find(
|
||||
Notification,
|
||||
{
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
title,
|
||||
},
|
||||
{
|
||||
@@ -221,10 +221,10 @@ export class NotificationService {
|
||||
);
|
||||
}
|
||||
|
||||
async findByAdminAndRestaurant(adminId: string, restaurantId: string, limit = 50): Promise<Notification[]> {
|
||||
async findByAdminAndRestaurant(adminId: string, shopId: string, limit = 50): Promise<Notification[]> {
|
||||
return this.em.find(
|
||||
Notification,
|
||||
{ admin: { id: adminId }, shop: { id: restaurantId } },
|
||||
{ admin: { id: adminId }, shop: { id: shopId } },
|
||||
{
|
||||
orderBy: { createdAt: 'DESC' },
|
||||
limit,
|
||||
@@ -232,37 +232,37 @@ export class NotificationService {
|
||||
);
|
||||
}
|
||||
|
||||
async countUnseenByUserAndRestaurant(userId: string, restaurantId: string): Promise<number> {
|
||||
async countUnseenByUserAndRestaurant(userId: string, shopId: string): Promise<number> {
|
||||
const where: FilterQuery<Notification> = {
|
||||
user: { id: userId },
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
seenAt: null,
|
||||
};
|
||||
return this.em.count(Notification, where);
|
||||
}
|
||||
|
||||
async countUnseenByRestaurant(adminId: string, restaurantId: string): Promise<number> {
|
||||
async countUnseenByRestaurant(adminId: string, shopId: string): Promise<number> {
|
||||
const where: FilterQuery<Notification> = {
|
||||
admin: { id: adminId },
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
seenAt: null,
|
||||
};
|
||||
return this.em.count(Notification, where);
|
||||
}
|
||||
|
||||
readAllNotifsAsUser(userId: string, restaurantId: string): Promise<number> {
|
||||
readAllNotifsAsUser(userId: string, shopId: string): Promise<number> {
|
||||
const where: FilterQuery<Notification> = {
|
||||
user: { id: userId },
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
seenAt: null,
|
||||
};
|
||||
return this.em.nativeUpdate(Notification, where, { seenAt: new Date() });
|
||||
}
|
||||
|
||||
readAllNotifsAsAdmin(adminId: string, restaurantId: string): Promise<number> {
|
||||
readAllNotifsAsAdmin(adminId: string, shopId: string): Promise<number> {
|
||||
const where: FilterQuery<Notification> = {
|
||||
admin: { id: adminId },
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
seenAt: null,
|
||||
};
|
||||
return this.em.nativeUpdate(Notification, where, { seenAt: new Date() });
|
||||
@@ -271,11 +271,11 @@ export class NotificationService {
|
||||
async getSmsCountByRestaurant(
|
||||
page: number = 1,
|
||||
limit: number = 10,
|
||||
): Promise<PaginatedResult<{ restaurantId: string; restaurantName: string; smsCount: number }>> {
|
||||
): Promise<PaginatedResult<{ shopId: string; shopName: string; smsCount: number }>> {
|
||||
return this.smsLogRepository.getSmsCountByRestaurant(page, limit);
|
||||
}
|
||||
|
||||
async getSmsCountByRestaurantId(restaurantId: string): Promise<number> {
|
||||
return this.smsLogRepository.getSmsCountByRestaurantId(restaurantId);
|
||||
async getSmsCountByRestaurantId(shopId: string): Promise<number> {
|
||||
return this.smsLogRepository.getSmsCountByRestaurantId(shopId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiHeader, ApiBody } fr
|
||||
import { OrdersService } from '../providers/orders.service';
|
||||
import { AuthGuard } from '../../auth/guards/auth.guard';
|
||||
import { UserId } from '../../../common/decorators/user-id.decorator';
|
||||
import { ShopId } from 'src/common/decorators/rest-id.decorator';
|
||||
import { ShopId } from 'src/common/decorators/shop-id.decorator';
|
||||
import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard';
|
||||
import { FindOrdersDto } from '../dto/find-orders.dto';
|
||||
import { OrderStatus } from '../interface/order.interface';
|
||||
@@ -22,16 +22,16 @@ export class OrdersController {
|
||||
@Post('public/checkout')
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiOperation({ summary: 'Checkout : create order and payment record' })
|
||||
checkout(@UserId() userId: string, @ShopId() restaurantId: string) {
|
||||
return this.ordersService.checkout(userId, restaurantId);
|
||||
checkout(@UserId() userId: string, @ShopId() shopId: string) {
|
||||
return this.ordersService.checkout(userId, shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Get('public/orders')
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
|
||||
findAll(@ShopId() restId: string, @Query() dto: FindOrdersDto, @UserId() userId: string) {
|
||||
return this.ordersService.findAllForUser(restId, dto, userId);
|
||||
findAll(@ShopId() shopId: string, @Query() dto: FindOrdersDto, @UserId() userId: string) {
|
||||
return this.ordersService.findAllForUser(shopId, dto, userId);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@@ -39,8 +39,8 @@ export class OrdersController {
|
||||
@ApiParam({ name: 'orderId', description: 'Order ID' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@Get('public/orders/:orderId')
|
||||
findOne(@Param('orderId') orderId: string, @ShopId() restId: string) {
|
||||
return this.ordersService.findOne(orderId, restId);
|
||||
findOne(@Param('orderId') orderId: string, @ShopId() shopId: string) {
|
||||
return this.ordersService.findOne(orderId, shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@@ -58,9 +58,9 @@ export class OrdersController {
|
||||
@Body() dto: UpdateOrderStatusDto,
|
||||
@Param('id') orderId: string,
|
||||
@Param('status') status: OrderStatus,
|
||||
@ShopId() restId: string,
|
||||
@ShopId() shopId: string,
|
||||
) {
|
||||
return this.ordersService.changeOrderStatus(orderId, restId, status, 'user', dto?.desc);
|
||||
return this.ordersService.changeOrderStatus(orderId, shopId, status, 'user', dto?.desc);
|
||||
}
|
||||
|
||||
/******************** Admin Routes **********************/
|
||||
@@ -68,8 +68,8 @@ export class OrdersController {
|
||||
@Permissions(Permission.MANAGE_ORDERS)
|
||||
@Get('admin/orders')
|
||||
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
|
||||
findAllAdmin(@ShopId() restId: string, @Query() dto: FindOrdersDto) {
|
||||
return this.ordersService.findAllForAdmin(restId, dto);
|
||||
findAllAdmin(@ShopId() shopId: string, @Query() dto: FindOrdersDto) {
|
||||
return this.ordersService.findAllForAdmin(shopId, dto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -77,8 +77,8 @@ export class OrdersController {
|
||||
@ApiOperation({ summary: 'Get an order By id for User' })
|
||||
@ApiParam({ name: 'orderId', description: 'Order ID' })
|
||||
@Get('admin/orders/:orderId')
|
||||
findOneAsAdmin(@Param('orderId') orderId: string, @ShopId() restId: string) {
|
||||
return this.ordersService.findOne(orderId, restId);
|
||||
findOneAsAdmin(@Param('orderId') orderId: string, @ShopId() shopId: string) {
|
||||
return this.ordersService.findOne(orderId, shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -96,17 +96,17 @@ export class OrdersController {
|
||||
@Param('orderId') orderId: string,
|
||||
@Body() dto: UpdateOrderStatusDto,
|
||||
@Param('status') status: OrderStatus,
|
||||
@ShopId() restId: string,
|
||||
@ShopId() shopId: string,
|
||||
) {
|
||||
return this.ordersService.changeOrderStatus(orderId, restId, status, 'admin', dto?.desc);
|
||||
return this.ordersService.changeOrderStatus(orderId, shopId, status, 'admin', dto?.desc);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@Permissions(Permission.VIEW_REPORTS)
|
||||
@ApiOperation({ summary: 'Get Stats for report page' })
|
||||
@Get('admin/orders/stats')
|
||||
findStats(@ShopId() restId: string) {
|
||||
return this.ordersService.getStats(restId);
|
||||
findStats(@ShopId() shopId: string) {
|
||||
return this.ordersService.getStats(shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -114,7 +114,7 @@ export class OrdersController {
|
||||
@ApiOperation({ summary: 'Get product sales pie chart data for last month' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@Get('admin/orders/product-sales-pie-chart')
|
||||
getFoodSalesPieChart(@ShopId() restId: string) {
|
||||
return this.ordersService.getFoodSalesPieChart(restId);
|
||||
getFoodSalesPieChart(@ShopId() shopId: string) {
|
||||
return this.ordersService.getFoodSalesPieChart(shopId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ export class OrdersCrone {
|
||||
}
|
||||
|
||||
const previousStatus = reloadedOrder.status;
|
||||
const restaurantId =
|
||||
const shopId =
|
||||
typeof reloadedOrder.shop === 'string'
|
||||
? reloadedOrder.shop
|
||||
: reloadedOrder.shop.id;
|
||||
@@ -152,7 +152,7 @@ export class OrdersCrone {
|
||||
reloadedOrder.id,
|
||||
reloadedOrder.user?.id || '',
|
||||
String(reloadedOrder.orderNumber) || '',
|
||||
restaurantId,
|
||||
shopId,
|
||||
previousStatus,
|
||||
OrderStatus.COMPLETED,
|
||||
'admin',
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { OrderStatus, StatusTransitionRef } from '../interface/order.interf
|
||||
export class OrderCreatedEvent {
|
||||
constructor(
|
||||
public readonly orderId: string,
|
||||
public readonly restaurantId: string,
|
||||
public readonly shopId: string,
|
||||
public readonly orderNumber: string,
|
||||
public readonly total: number,
|
||||
) {}
|
||||
@@ -14,7 +14,7 @@ export class OrderStatusChangedEvent {
|
||||
public readonly orderId: string,
|
||||
public readonly userId: string,
|
||||
public readonly orderNumber: string,
|
||||
public readonly restaurantId: string,
|
||||
public readonly shopId: string,
|
||||
public readonly previousStatus: OrderStatus,
|
||||
public readonly newStatus: OrderStatus,
|
||||
public readonly changedBy: StatusTransitionRef,
|
||||
|
||||
@@ -47,7 +47,7 @@ export class OrderListeners {
|
||||
async handleOrderCreated(event: OrderCreatedEvent) {
|
||||
try {
|
||||
this.logger.log(
|
||||
`Order created event received: ${event.orderId} for shop: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||
`Order created event received: ${event.orderId} for shop: ${event.shopId} and order number: ${event.orderNumber}`,
|
||||
);
|
||||
|
||||
const order = await this.OrderRepository.findOne(event.orderId);
|
||||
@@ -57,13 +57,13 @@ export class OrderListeners {
|
||||
|
||||
|
||||
// get admnin os restuaraant that have order permissuins
|
||||
const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_ORDERS);
|
||||
const admins = await this.adminService.findAdminsWithPermission(event.shopId, Permission.MANAGE_ORDERS);
|
||||
const recipients = admins.map(admin => ({
|
||||
adminId: admin.id,
|
||||
}));
|
||||
|
||||
await this.notificationService.sendNotification({
|
||||
restaurantId: event.restaurantId,
|
||||
shopId: event.shopId,
|
||||
message: {
|
||||
title: NotifTitleEnum.ORDER_CREATED,
|
||||
content: `سفارش شماره ${event.orderNumber} با مبلغ ${event.total} تومان با موفقیت ایجاد شد`,
|
||||
@@ -91,7 +91,7 @@ export class OrderListeners {
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to send notification for order created event: ${event.restaurantId}`,
|
||||
`Failed to send notification for order created event: ${event.shopId}`,
|
||||
error instanceof Error ? error.stack : String(error),
|
||||
);
|
||||
}
|
||||
@@ -101,7 +101,7 @@ export class OrderListeners {
|
||||
async handleOrderStatusChanged(event: OrderStatusChangedEvent) {
|
||||
try {
|
||||
this.logger.log(
|
||||
`Order status changed event received: ${event.orderId} for shop: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||
`Order status changed event received: ${event.orderId} for shop: ${event.shopId} and order number: ${event.orderNumber}`,
|
||||
);
|
||||
//TODO : REFACTOR to use queue or other way to handle this
|
||||
const recipients = [
|
||||
@@ -113,21 +113,21 @@ export class OrderListeners {
|
||||
|
||||
if (!event?.userId) {
|
||||
this.logger.log(
|
||||
`User not found for order: ${event.orderId} for shop: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||
`User not found for order: ${event.orderId} for shop: ${event.shopId} and order number: ${event.orderNumber}`,
|
||||
);
|
||||
}
|
||||
|
||||
// const shop = await this.RestaurantRepository.findOne(event.restaurantId);
|
||||
// const shop = await this.RestaurantRepository.findOne(event.shopId);
|
||||
// if (!shop) {
|
||||
// this.logger.log(
|
||||
// `Shop not found for order: ${event.orderId} for shop: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||
// `Shop not found for order: ${event.orderId} for shop: ${event.shopId} and order number: ${event.orderNumber}`,
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
// const score = shop.score;
|
||||
// if (!score) {
|
||||
// this.logger.log(
|
||||
// `Score not found for shop: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||
// `Score not found for shop: ${event.shopId} and order number: ${event.orderNumber}`,
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
@@ -136,18 +136,18 @@ export class OrderListeners {
|
||||
// const order = await this.OrderRepository.findOne(event.orderId);
|
||||
// if (!order) {
|
||||
// this.logger.log(
|
||||
// `Order not found for order: ${event.orderId} for shop: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||
// `Order not found for order: ${event.orderId} for shop: ${event.shopId} and order number: ${event.orderNumber}`,
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
// this.userService.createWalletTransaction(event.userId, event.restaurantId, {
|
||||
// this.userService.createWalletTransaction(event.userId, event.shopId, {
|
||||
// amount: order.subTotal,
|
||||
// type: WalletTransactionType.CREDIT,
|
||||
// reason: WalletTransactionReason.ORDER_COMPLETED_DEPOSIT,
|
||||
// });
|
||||
|
||||
await this.notificationService.sendNotification({
|
||||
restaurantId: event.restaurantId,
|
||||
shopId: event.shopId,
|
||||
message: {
|
||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||
content: `لطفابرای ثبت نظر سفارش ${event.orderNumber} به اپ مراجعه کنید`,
|
||||
@@ -174,7 +174,7 @@ export class OrderListeners {
|
||||
});
|
||||
} else {
|
||||
await this.notificationService.sendNotification({
|
||||
restaurantId: event.restaurantId,
|
||||
shopId: event.shopId,
|
||||
message: {
|
||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
||||
content: `وضعیت سفارش شماره ${event.orderNumber} به ${this.getStatusFarsi(event.newStatus)} تغییر کرد`,
|
||||
@@ -203,7 +203,7 @@ export class OrderListeners {
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to send notification for order status changed event: ${event.restaurantId}`,
|
||||
`Failed to send notification for order status changed event: ${event.shopId}`,
|
||||
error instanceof Error ? error.stack : String(error),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export class OrderRepository extends EntityRepository<Order> {
|
||||
* Find orders with pagination and optional filters.
|
||||
* Supports: statuses, paymentStatus, search (orderNumber), date range, ordering.
|
||||
*/
|
||||
async findAllPaginated(restId: string, opts: FindOrdersOpts = {}): Promise<PaginatedResult<Order>> {
|
||||
async findAllPaginated(shopId: string, opts: FindOrdersOpts = {}): Promise<PaginatedResult<Order>> {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 10,
|
||||
@@ -47,7 +47,7 @@ export class OrderRepository extends EntityRepository<Order> {
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const where: FilterQuery<Order> = { shop: { id: restId } };
|
||||
const where: FilterQuery<Order> = { shop: { id: shopId } };
|
||||
|
||||
// Filter by statuses
|
||||
if (statuses) {
|
||||
|
||||
@@ -35,8 +35,8 @@ export class PaymentsController {
|
||||
@ApiOperation({ summary: 'Get the shop payment methods' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiNotFoundResponse({ description: 'Shop payment methods not found' })
|
||||
getTheRestaurantPaymentMethods(@ShopId() restId: string) {
|
||||
return this.paymentMethodService.findByRestaurant(restId);
|
||||
getTheRestaurantPaymentMethods(@ShopId() shopId: string) {
|
||||
return this.paymentMethodService.findByRestaurant(shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@@ -44,8 +44,8 @@ export class PaymentsController {
|
||||
@Get('public/payments')
|
||||
@ApiOperation({ summary: 'Get all the shop payments for user' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
findAllByRestaurant(@UserId() userId: string, @ShopId() restId: string) {
|
||||
return this.paymentsService.findAllPaymentsByRestaurantId(restId, userId);
|
||||
findAllByRestaurant(@UserId() userId: string, @ShopId() shopId: string) {
|
||||
return this.paymentsService.findAllPaymentsByRestaurantId(shopId, userId);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@@ -73,8 +73,8 @@ export class PaymentsController {
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/payments/methods')
|
||||
@ApiOperation({ summary: 'Get shop all payment methods' })
|
||||
findByRestaurant(@ShopId() restId: string) {
|
||||
return this.paymentMethodService.findByRestaurant(restId);
|
||||
findByRestaurant(@ShopId() shopId: string) {
|
||||
return this.paymentMethodService.findByRestaurant(shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -83,8 +83,8 @@ export class PaymentsController {
|
||||
@Post('admin/payments/methods')
|
||||
@ApiOperation({ summary: 'Create a new shop payment method' })
|
||||
@ApiBody({ type: CreatePaymentMethodDto })
|
||||
createRestaurantPaymentMethod(@ShopId() restId: string, @Body() createPaymentMethodDto: CreatePaymentMethodDto) {
|
||||
return this.paymentMethodService.create(restId, createPaymentMethodDto);
|
||||
createRestaurantPaymentMethod(@ShopId() shopId: string, @Body() createPaymentMethodDto: CreatePaymentMethodDto) {
|
||||
return this.paymentMethodService.create(shopId, createPaymentMethodDto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -137,7 +137,7 @@ export class PaymentsController {
|
||||
@Get('admin/payments/chart')
|
||||
@ApiOperation({ summary: 'Get payment chart data with date and period filters' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
getPaymentChart(@Query() query: PaymentChartDto, @ShopId() restId: string) {
|
||||
return this.paymentsService.getChartData(query, restId);
|
||||
getPaymentChart(@Query() query: PaymentChartDto, @ShopId() shopId: string) {
|
||||
return this.paymentsService.getChartData(query, shopId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { PaymentMethodEnum } from "../interface/payment";
|
||||
export class paymentSucceedEvent {
|
||||
constructor(
|
||||
public readonly orderId: string,
|
||||
public readonly restaurantId: string,
|
||||
public readonly shopId: string,
|
||||
public readonly orderNumber: string,
|
||||
public readonly paymentMethod: PaymentMethodEnum,
|
||||
public readonly total: number
|
||||
@@ -16,7 +16,7 @@ export class onlinePaymentSucceedEvent {
|
||||
constructor(
|
||||
public readonly paymentId: string,
|
||||
public readonly orderId: string,
|
||||
public readonly restaurantId: string,
|
||||
public readonly shopId: string,
|
||||
public readonly orderNumber: string,
|
||||
public readonly total: number,
|
||||
) { }
|
||||
|
||||
@@ -36,16 +36,16 @@ export class PaymentListeners {
|
||||
async handlePaymentSucceed(event: paymentSucceedEvent) {
|
||||
try {
|
||||
this.logger.log(
|
||||
`Payment paid event received: ${event.orderId} for shop: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||
`Payment paid event received: ${event.orderId} for shop: ${event.shopId} and order number: ${event.orderNumber}`,
|
||||
);
|
||||
// get admnin os restuaraant that have order permissuins
|
||||
const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_ORDERS);
|
||||
const admins = await this.adminService.findAdminsWithPermission(event.shopId, Permission.MANAGE_ORDERS);
|
||||
// const order=await
|
||||
const recipients = admins.map(admin => ({
|
||||
adminId: admin.id,
|
||||
}));
|
||||
await this.notificationService.sendNotification({
|
||||
restaurantId: event.restaurantId,
|
||||
shopId: event.shopId,
|
||||
message: {
|
||||
title: NotifTitleEnum.PAYMENT_SUCCESS,
|
||||
content: `پرداخت سفارش شماره ${event.orderNumber} با روش ${this.getPaymentMethodFarsi(event.paymentMethod)} و مبلغ ${event.total} تومان با موفقیت انجام شد`,
|
||||
@@ -73,7 +73,7 @@ export class PaymentListeners {
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to send notification for order created event: ${event.restaurantId}`,
|
||||
`Failed to send notification for order created event: ${event.shopId}`,
|
||||
error instanceof Error ? error.stack : String(error),
|
||||
);
|
||||
}
|
||||
@@ -84,16 +84,16 @@ export class PaymentListeners {
|
||||
try {
|
||||
|
||||
this.logger.log(
|
||||
`Online payment succeed event received: ${event.paymentId} for shop: ${event.restaurantId} and order number: ${event.orderNumber}`,
|
||||
`Online payment succeed event received: ${event.paymentId} for shop: ${event.shopId} and order number: ${event.orderNumber}`,
|
||||
);
|
||||
const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_ORDERS);
|
||||
const admins = await this.adminService.findAdminsWithPermission(event.shopId, Permission.MANAGE_ORDERS);
|
||||
const recipients = admins.map(admin => ({
|
||||
adminId: admin.id,
|
||||
}));
|
||||
|
||||
// admin notifs
|
||||
await this.notificationService.sendNotification({
|
||||
restaurantId: event.restaurantId,
|
||||
shopId: event.shopId,
|
||||
message: {
|
||||
title: NotifTitleEnum.ORDER_CREATED,
|
||||
content: `سفارش شماره ${event.orderNumber} با مبلغ ${event.total} تومان با موفقیت ایجاد شد`,
|
||||
@@ -120,10 +120,10 @@ export class PaymentListeners {
|
||||
},
|
||||
});
|
||||
|
||||
const order = await this.orderService.findOne(event.orderId, event.restaurantId);
|
||||
const order = await this.orderService.findOne(event.orderId, event.shopId);
|
||||
if (!order) {
|
||||
this.logger.error(
|
||||
`Order not found: ${event.orderId} for shop: ${event.restaurantId}`,
|
||||
`Order not found: ${event.orderId} for shop: ${event.shopId}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -134,7 +134,7 @@ export class PaymentListeners {
|
||||
];
|
||||
// user notif
|
||||
await this.notificationService.sendNotification({
|
||||
restaurantId: event.restaurantId,
|
||||
shopId: event.shopId,
|
||||
message: {
|
||||
title: NotifTitleEnum.PAYMENT_SUCCESS,
|
||||
content: `پرداخت سفارش شماره ${order.orderNumber} با روش ${this.getPaymentMethodFarsi(order.paymentMethod.method)} و مبلغ ${order.total} تومان با موفقیت انجام شد`,
|
||||
@@ -162,7 +162,7 @@ export class PaymentListeners {
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to send notification for online payment succeed event: ${event.restaurantId}`,
|
||||
`Failed to send notification for online payment succeed event: ${event.shopId}`,
|
||||
error instanceof Error ? error.stack : String(error),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ export class PaymentMethodService {
|
||||
private readonly em: EntityManager,
|
||||
) { }
|
||||
|
||||
async create(restId: string, createPaymentMethodDto: CreatePaymentMethodDto): Promise<PaymentMethod> {
|
||||
async create(shopId: string, createPaymentMethodDto: CreatePaymentMethodDto): Promise<PaymentMethod> {
|
||||
// Check if shop exists
|
||||
const shop = await this.em.findOne(Shop, { id: restId });
|
||||
const shop = await this.em.findOne(Shop, { id: shopId });
|
||||
if (!shop) {
|
||||
throw new NotFoundException(RestMessage.NOT_FOUND);
|
||||
}
|
||||
@@ -42,8 +42,8 @@ export class PaymentMethodService {
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
async findByRestaurant(restaurantId: string): Promise<PaymentMethod[]> {
|
||||
return this.paymentMethodRepository.find({ shop: { id: restaurantId } }, { populate: ['shop'] });
|
||||
async findByRestaurant(shopId: string): Promise<PaymentMethod[]> {
|
||||
return this.paymentMethodRepository.find({ shop: { id: shopId } }, { populate: ['shop'] });
|
||||
}
|
||||
|
||||
async update(id: string, updatePaymentMethodDto: UpdatePaymentMethodDto): Promise<PaymentMethod> {
|
||||
|
||||
@@ -235,10 +235,10 @@ export class PaymentsService {
|
||||
return payment;
|
||||
}
|
||||
|
||||
findAllPaymentsByRestaurantId(restId: string, userId: string) {
|
||||
findAllPaymentsByRestaurantId(shopId: string, userId: string) {
|
||||
return this.em.find(
|
||||
Payment,
|
||||
{ order: { shop: { id: restId }, user: { id: userId } } },
|
||||
{ order: { shop: { id: shopId }, user: { id: userId } } },
|
||||
{ populate: ['order', 'order.paymentMethod'] },
|
||||
);
|
||||
}
|
||||
@@ -328,7 +328,7 @@ export class PaymentsService {
|
||||
return payment;
|
||||
}
|
||||
|
||||
async getChartData(dto: PaymentChartDto, restaurantId: string): Promise<Array<{ date: string; cash: number; online: number }>> {
|
||||
async getChartData(dto: PaymentChartDto, shopId: string): Promise<Array<{ date: string; cash: number; online: number }>> {
|
||||
const { startDate, endDate, type = ChartPeriodEnum.Daily } = dto;
|
||||
|
||||
const start = startDate ? new Date(startDate) : new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
||||
@@ -351,8 +351,8 @@ export class PaymentsService {
|
||||
const params: any[] = [startOfDay, endOfDay];
|
||||
let restaurantFilter = '';
|
||||
|
||||
if (restaurantId) {
|
||||
params.push(restaurantId);
|
||||
if (shopId) {
|
||||
params.push(shopId);
|
||||
restaurantFilter = `AND o.restaurant_id = ?`;
|
||||
}
|
||||
|
||||
@@ -374,7 +374,7 @@ export class PaymentsService {
|
||||
ORDER BY DATE_TRUNC('${pgPeriod}', CAST(p.paid_at AS timestamp)) ASC
|
||||
`;
|
||||
|
||||
this.logger.debug(`Chart query params: startOfDay=${startOfDay.toISOString()}, endOfDay=${endOfDay.toISOString()}, restaurantId=${restaurantId}`);
|
||||
this.logger.debug(`Chart query params: startOfDay=${startOfDay.toISOString()}, endOfDay=${endOfDay.toISOString()}, shopId=${shopId}`);
|
||||
const result = await this.em.execute(query, params);
|
||||
this.logger.debug(`Chart query returned ${result.length} rows`);
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ export class FoodController {
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'get my favorites' })
|
||||
getMyFavorites(@UserId() userId: string, @ShopId() restId: string) {
|
||||
return this.productService.getMyFavorites(userId, restId);
|
||||
getMyFavorites(@UserId() userId: string, @ShopId() shopId: string) {
|
||||
return this.productService.getMyFavorites(userId, shopId);
|
||||
}
|
||||
|
||||
/* ---------------------------------- Admin ---------------------------------- */
|
||||
@@ -68,8 +68,8 @@ export class FoodController {
|
||||
@Permissions(Permission.MANAGE_FOODS)
|
||||
@Post('admin/products')
|
||||
@ApiOperation({ summary: 'Create a new product' })
|
||||
create(@Body() createFoodDto: CreateProductDto, @ShopId() restId: string) {
|
||||
return this.productService.create(restId, createFoodDto);
|
||||
create(@Body() createFoodDto: CreateProductDto, @ShopId() shopId: string) {
|
||||
return this.productService.create(shopId, createFoodDto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -84,8 +84,8 @@ export class FoodController {
|
||||
@ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] })
|
||||
@ApiQuery({ name: 'categoryId', required: false, type: String })
|
||||
@ApiQuery({ name: 'isActive', required: false, type: Boolean })
|
||||
async findAll(@Query() dto: FindProductsDto, @ShopId() restId: string) {
|
||||
const result = await this.productService.findAll(restId, dto);
|
||||
async findAll(@Query() dto: FindProductsDto, @ShopId() shopId: string) {
|
||||
const result = await this.productService.findAll(shopId, dto);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ export class FoodController {
|
||||
@Get('admin/products/:id')
|
||||
@ApiOperation({ summary: 'Get a product by id' })
|
||||
@ApiParam({ name: 'id', required: true })
|
||||
findById(@Param('id') id: string, @ShopId() restId: string) {
|
||||
return this.productService.findAdminById(restId, id);
|
||||
findById(@Param('id') id: string, @ShopId() shopId: string) {
|
||||
return this.productService.findAdminById(shopId, id);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -106,15 +106,15 @@ export class FoodController {
|
||||
@ApiOperation({ summary: 'Update a product' })
|
||||
@ApiParam({ name: 'id', required: true })
|
||||
@ApiBody({ type: UpdateFoodDto })
|
||||
update(@Param('id') id: string, @Body() updateFoodDto: UpdateFoodDto, @ShopId() restId: string) {
|
||||
return this.productService.update(restId, id, updateFoodDto);
|
||||
update(@Param('id') id: string, @Body() updateFoodDto: UpdateFoodDto, @ShopId() shopId: string) {
|
||||
return this.productService.update(shopId, id, updateFoodDto);
|
||||
}
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Permissions(Permission.MANAGE_FOODS)
|
||||
@Delete('admin/products/:id')
|
||||
@ApiOperation({ summary: 'Delete (soft) a product' })
|
||||
remove(@Param('id') id: string, @ShopId() restId: string) {
|
||||
return this.productService.remove(restId, id);
|
||||
remove(@Param('id') id: string, @ShopId() shopId: string) {
|
||||
return this.productService.remove(shopId, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ export class ProductRepository extends EntityRepository<Product> {
|
||||
* Find products with pagination and optional filters.
|
||||
* Supports: search (title/content), categoryId, isActive, ordering.
|
||||
*/
|
||||
async findAllPaginated(restId: string, opts: FindFoodsOpts = {}): Promise<PaginatedResult<Product>> {
|
||||
async findAllPaginated(shopId: string, opts: FindFoodsOpts = {}): Promise<PaginatedResult<Product>> {
|
||||
const { page = 1, limit = 10, search, orderBy = 'order', order = 'asc', categoryId, isActive } = opts;
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const where: FilterQuery<Product> = { shop: { id: restId } };
|
||||
const where: FilterQuery<Product> = { shop: { id: shopId } };
|
||||
|
||||
if (typeof isActive === 'boolean') {
|
||||
where.isActive = isActive;
|
||||
@@ -48,7 +48,7 @@ export class ProductRepository extends EntityRepository<Product> {
|
||||
limit,
|
||||
offset,
|
||||
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
|
||||
populate: ['category','variants'],
|
||||
populate: ['category', 'variants'],
|
||||
});
|
||||
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import { AuthGuard } from 'src/modules/auth/guards/auth.guard';
|
||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||
import { UserId } from 'src/common/decorators/user-id.decorator';
|
||||
import { ShopId } from 'src/common/decorators/rest-id.decorator';
|
||||
import { ShopId } from 'src/common/decorators/shop-id.decorator';
|
||||
import { ReviewStatus } from '../enums/review-status.enum';
|
||||
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
||||
import { Permission } from 'src/common/enums/permission.enum';
|
||||
@@ -88,8 +88,8 @@ export class ReviewController {
|
||||
@Permissions(Permission.MANAGE_REVIEWS)
|
||||
@Get('admin/reviews')
|
||||
@ApiOperation({ summary: 'Get all reviews (admin - including unapproved)' })
|
||||
findAllAdmin(@Query() dto: FindReviewsDto, @ShopId() restId: string) {
|
||||
return this.reviewService.findAll({ ...dto, shopId: restId });
|
||||
findAllAdmin(@Query() dto: FindReviewsDto, @ShopId() shopId: string) {
|
||||
return this.reviewService.findAll({ ...dto, shopId: shopId });
|
||||
}
|
||||
|
||||
@Patch('admin/reviews/:id')
|
||||
@@ -106,8 +106,8 @@ export class ReviewController {
|
||||
@Permissions(Permission.MANAGE_REVIEWS)
|
||||
@Get('admin/reviews/:id')
|
||||
@ApiOperation({ summary: 'review detail' })
|
||||
reviewDetail(@Param('id') reviewId: string, @ShopId() restaurantId: string) {
|
||||
return this.reviewService.findById(reviewId, restaurantId);
|
||||
reviewDetail(@Param('id') reviewId: string, @ShopId() shopId: string) {
|
||||
return this.reviewService.findById(reviewId, shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -118,9 +118,9 @@ export class ReviewController {
|
||||
changeStatus(
|
||||
@Param('id') reviewId: string,
|
||||
@Body() changeStatusDto: ChangeStatusDto,
|
||||
@ShopId() restaurantId: string,
|
||||
@ShopId() shopId: string,
|
||||
) {
|
||||
return this.reviewService.changeStatus(reviewId, changeStatusDto.status, restaurantId);
|
||||
return this.reviewService.changeStatus(reviewId, changeStatusDto.status, shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
export class ReviewCreatedEvent {
|
||||
constructor(
|
||||
public readonly reviewId: string,
|
||||
public readonly restaurantId: string,
|
||||
public readonly shopId: string,
|
||||
public readonly userId: string,
|
||||
public readonly foodId: string,
|
||||
public readonly foodName: string,
|
||||
public readonly orderId: string,
|
||||
public readonly orderNumber: string | number,
|
||||
public readonly rating: number,
|
||||
) {}
|
||||
) { }
|
||||
}
|
||||
|
||||
|
||||
@@ -24,22 +24,22 @@ export class ReviewListeners {
|
||||
async handleReviewCreated(event: ReviewCreatedEvent) {
|
||||
try {
|
||||
this.logger.log(
|
||||
`Review created event received: ${event.reviewId} for shop: ${event.restaurantId}, product: ${event.foodName}, rating: ${event.rating}`,
|
||||
`Review created event received: ${event.reviewId} for shop: ${event.shopId}, product: ${event.foodName}, rating: ${event.rating}`,
|
||||
);
|
||||
|
||||
// Get admins of shop that have review management permissions
|
||||
const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_REVIEWS);
|
||||
const admins = await this.adminService.findAdminsWithPermission(event.shopId, Permission.MANAGE_REVIEWS);
|
||||
const recipients = admins.map(admin => ({
|
||||
adminId: admin.id,
|
||||
}));
|
||||
|
||||
if (recipients.length === 0) {
|
||||
this.logger.warn(`No admins found with MANAGE_REVIEWS permission for shop ${event.restaurantId}`);
|
||||
this.logger.warn(`No admins found with MANAGE_REVIEWS permission for shop ${event.shopId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.notificationService.sendNotification({
|
||||
restaurantId: event.restaurantId,
|
||||
shopId: event.shopId,
|
||||
message: {
|
||||
title: NotifTitleEnum.REVIEW_CREATED,
|
||||
content: `نظر جدید برای غذا "${event.foodName}" با امتیاز ${event.rating} از 5 برای سفارش شماره ${event.orderNumber} ثبت شد`,
|
||||
|
||||
@@ -40,7 +40,7 @@ export class ReviewService {
|
||||
throw new NotFoundException(UserMessage.USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Find order and verify it belongs to the user, populate shop to get restaurantId
|
||||
// Find order and verify it belongs to the user, populate shop to get shopId
|
||||
const order = await this.em.findOne(Order, { id: orderId, user: { id: userId } }, { populate: ['shop'] });
|
||||
if (!order) {
|
||||
throw new NotFoundException('Order not found or does not belong to the current user');
|
||||
@@ -117,12 +117,12 @@ export class ReviewService {
|
||||
if (!shop) {
|
||||
throw new NotFoundException('RestaurantMessage.NOT_FOUND');
|
||||
}
|
||||
return this.reviewRepository.findAllPaginated({ ...restDto, restId: shop.id });
|
||||
return this.reviewRepository.findAllPaginated({ ...restDto, shopId: shop.id });
|
||||
}
|
||||
|
||||
async findById(id: string, restaurantId: string): Promise<Review> {
|
||||
async findById(id: string, shopId: string): Promise<Review> {
|
||||
const review = await this.reviewRepository.findOne(
|
||||
{ id, order: { shop: { id: restaurantId } } },
|
||||
{ id, order: { shop: { id: shopId } } },
|
||||
{ populate: ['product', 'user', 'order'] },
|
||||
);
|
||||
if (!review) {
|
||||
@@ -166,8 +166,8 @@ export class ReviewService {
|
||||
return review;
|
||||
}
|
||||
|
||||
async changeStatus(reviewId: string, status: ReviewStatus, restaurantId: string): Promise<Review> {
|
||||
const review = await this.reviewRepository.findOne({ id: reviewId, order: { shop: { id: restaurantId } } });
|
||||
async changeStatus(reviewId: string, status: ReviewStatus, shopId: string): Promise<Review> {
|
||||
const review = await this.reviewRepository.findOne({ id: reviewId, order: { shop: { id: shopId } } });
|
||||
if (!review) {
|
||||
throw new NotFoundException(ReviewMessage.NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ type FindReviewsOpts = {
|
||||
userId?: string;
|
||||
status?: ReviewStatus;
|
||||
orderBy?: string;
|
||||
restId?: string;
|
||||
shopId?: string;
|
||||
order?: 'asc' | 'desc';
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ export class ReviewRepository extends EntityRepository<Review> {
|
||||
* Supports: foodId, userId, status, ordering.
|
||||
*/
|
||||
async findAllPaginated(opts: FindReviewsOpts = {}): Promise<PaginatedResult<Review>> {
|
||||
const { page = 1, limit = 10, foodId, restId, userId, status, orderBy = 'createdAt', order = 'desc' } = opts;
|
||||
const { page = 1, limit = 10, foodId, shopId, userId, status, orderBy = 'createdAt', order = 'desc' } = opts;
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
@@ -37,8 +37,8 @@ export class ReviewRepository extends EntityRepository<Review> {
|
||||
where.product = { id: foodId };
|
||||
}
|
||||
|
||||
if (restId) {
|
||||
where.product = { shop: { id: restId } };
|
||||
if (shopId) {
|
||||
where.product = { shop: { id: shopId } };
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
|
||||
@@ -26,8 +26,8 @@ export class RolesController {
|
||||
@Permissions(Permission.MANAGE_ROLES)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Get all through shop roles with pagination and filters' })
|
||||
findAll(@ShopId() restId: string) {
|
||||
return this.roleService.findAllGeneralAndRestaurantRoles(restId);
|
||||
findAll(@ShopId() shopId: string) {
|
||||
return this.roleService.findAllGeneralAndRestaurantRoles(shopId);
|
||||
}
|
||||
|
||||
@Get('admin/roles/permissions')
|
||||
@@ -35,9 +35,9 @@ export class RolesController {
|
||||
// @Permissions(Permission.MANAGE_ROLES)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Get all Admin permissions ' })
|
||||
async findAllPermissions(@AdminId() adminId: string, @ShopId() restId: string) {
|
||||
console.log(adminId, restId)
|
||||
const adminPermissionNames = await this.permissionService.getAdminFullPermissions(adminId, restId);
|
||||
async findAllPermissions(@AdminId() adminId: string, @ShopId() shopId: string) {
|
||||
console.log(adminId, shopId)
|
||||
const adminPermissionNames = await this.permissionService.getAdminFullPermissions(adminId, shopId);
|
||||
|
||||
return adminPermissionNames;
|
||||
}
|
||||
@@ -50,8 +50,8 @@ export class RolesController {
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Create a new role' })
|
||||
@ApiBody({ type: CreateRoleDto })
|
||||
create(@Body() dto: CreateRoleDto, @ShopId() restId: string) {
|
||||
return this.roleService.createRestaurantRole(dto, restId);
|
||||
create(@Body() dto: CreateRoleDto, @ShopId() shopId: string) {
|
||||
return this.roleService.createRestaurantRole(dto, shopId);
|
||||
}
|
||||
|
||||
@Get('admin/roles/:id')
|
||||
@@ -59,8 +59,8 @@ export class RolesController {
|
||||
@Permissions(Permission.MANAGE_ROLES)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Get a specific role by ID' })
|
||||
findOne(@Param('id') id: string, @ShopId() restId: string) {
|
||||
return this.roleService.findOne(restId, id);
|
||||
findOne(@Param('id') id: string, @ShopId() shopId: string) {
|
||||
return this.roleService.findOne(shopId, id);
|
||||
}
|
||||
|
||||
@Patch('admin/roles/:id')
|
||||
@@ -69,8 +69,8 @@ export class RolesController {
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Update a role' })
|
||||
@ApiBody({ type: UpdateRoleDto })
|
||||
update(@Param('id') id: string, @Body() dto: UpdateRoleDto, @ShopId() restId: string) {
|
||||
return this.roleService.update(restId, id, dto);
|
||||
update(@Param('id') id: string, @Body() dto: UpdateRoleDto, @ShopId() shopId: string) {
|
||||
return this.roleService.update(shopId, id, dto);
|
||||
}
|
||||
|
||||
@Delete('admin/roles/:id')
|
||||
@@ -78,8 +78,8 @@ export class RolesController {
|
||||
@Permissions(Permission.MANAGE_ROLES)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Delete a role' })
|
||||
remove(@Param('id') id: string, @ShopId() restId: string) {
|
||||
return this.roleService.remove(restId, id);
|
||||
remove(@Param('id') id: string, @ShopId() shopId: string) {
|
||||
return this.roleService.remove(shopId, id);
|
||||
}
|
||||
|
||||
/** Super Admin Endpoints */
|
||||
|
||||
@@ -24,8 +24,8 @@ export class PermissionsService {
|
||||
}
|
||||
|
||||
|
||||
async getAdminPermissions(adminId: string, restId: string): Promise<string[]> {
|
||||
const cacheKey = `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${restId}`;
|
||||
async getAdminPermissions(adminId: string, shopId: string): Promise<string[]> {
|
||||
const cacheKey = `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${shopId}`;
|
||||
|
||||
// Try to get from cache first
|
||||
const cachedPermissions = await this.cacheService.get<string>(cacheKey);
|
||||
@@ -44,7 +44,7 @@ export class PermissionsService {
|
||||
|
||||
// If not in cache, fetch from database
|
||||
const admin = await this.adminRepository.findOne(
|
||||
{ id: adminId, roles: { shop: { id: restId } } },
|
||||
{ id: adminId, roles: { shop: { id: shopId } } },
|
||||
{ populate: ['roles', 'roles.role', 'roles.role.permissions'] },
|
||||
);
|
||||
|
||||
@@ -72,14 +72,14 @@ export class PermissionsService {
|
||||
return permissions.flat().map(p => p.name);
|
||||
}
|
||||
|
||||
async getAdminFullPermissions(adminId: string, restId: string): Promise<Permission[]> {
|
||||
async getAdminFullPermissions(adminId: string, shopId: string): Promise<Permission[]> {
|
||||
const listOfPermissions = []
|
||||
const adminRoles = await this.em.findOne(AdminRole, {
|
||||
admin: {
|
||||
id: adminId,
|
||||
},
|
||||
shop: {
|
||||
id: restId,
|
||||
id: shopId,
|
||||
},
|
||||
}, { populate: ['role', 'role.permissions'] });
|
||||
if (adminRoles) {
|
||||
@@ -89,8 +89,8 @@ export class PermissionsService {
|
||||
}
|
||||
|
||||
|
||||
async invalidateAdminPermissionsCache(adminId: string, restId: string): Promise<void> {
|
||||
const cacheKey = `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${restId}`;
|
||||
async invalidateAdminPermissionsCache(adminId: string, shopId: string): Promise<void> {
|
||||
const cacheKey = `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${shopId}`;
|
||||
await this.cacheService.del(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,18 +19,18 @@ export class RolesService {
|
||||
private readonly em: EntityManager,
|
||||
) { }
|
||||
|
||||
async createRestaurantRole(dto: CreateRoleDto, restId: string) {
|
||||
async createRestaurantRole(dto: CreateRoleDto, shopId: string) {
|
||||
const { name, permissionIds } = dto;
|
||||
|
||||
// Check if role already exists
|
||||
const existing = await this.roleRepository.findOne({ name, shop: restId ? { id: restId } : null });
|
||||
const existing = await this.roleRepository.findOne({ name, shop: shopId ? { id: shopId } : null });
|
||||
if (existing) {
|
||||
throw new BadRequestException('Role with this name already exists for the shop');
|
||||
}
|
||||
|
||||
let shop: Shop | null = null;
|
||||
if (restId) {
|
||||
shop = await this.em.findOne(Shop, { id: restId });
|
||||
if (shopId) {
|
||||
shop = await this.em.findOne(Shop, { id: shopId });
|
||||
if (!shop) {
|
||||
throw new NotFoundException('Shop not found');
|
||||
}
|
||||
@@ -55,8 +55,8 @@ export class RolesService {
|
||||
return role;
|
||||
}
|
||||
|
||||
async findAllGeneralAndRestaurantRoles(restId: string) {
|
||||
const where: FilterQuery<Role> = { $or: [{ shop: restId }, { shop: null }], isSystem: false };
|
||||
async findAllGeneralAndRestaurantRoles(shopId: string) {
|
||||
const where: FilterQuery<Role> = { $or: [{ shop: shopId }, { shop: null }], isSystem: false };
|
||||
|
||||
const roles = await this.roleRepository.find(where, {
|
||||
orderBy: { createdAt: 'desc' },
|
||||
@@ -66,9 +66,9 @@ export class RolesService {
|
||||
return roles;
|
||||
}
|
||||
|
||||
async findOne(restId: string, id: string) {
|
||||
async findOne(shopId: string, id: string) {
|
||||
const role = await this.roleRepository.findOne(
|
||||
{ id, shop: { id: restId } },
|
||||
{ id, shop: { id: shopId } },
|
||||
{ populate: ['permissions', 'shop'] },
|
||||
);
|
||||
if (!role) {
|
||||
@@ -77,9 +77,9 @@ export class RolesService {
|
||||
return role;
|
||||
}
|
||||
|
||||
async update(restId: string, id: string, dto: UpdateRoleDto) {
|
||||
async update(shopId: string, id: string, dto: UpdateRoleDto) {
|
||||
const role = await this.roleRepository.findOne(
|
||||
{ id, shop: { id: restId } },
|
||||
{ id, shop: { id: shopId } },
|
||||
{ populate: ['permissions', 'shop'] },
|
||||
);
|
||||
if (!role) {
|
||||
@@ -116,9 +116,9 @@ export class RolesService {
|
||||
return roles;
|
||||
}
|
||||
|
||||
async remove(restId: string, id: string) {
|
||||
async remove(shopId: string, id: string) {
|
||||
const role = await this.roleRepository.findOne(
|
||||
{ id, shop: { id: restId } },
|
||||
{ id, shop: { id: shopId } },
|
||||
{ populate: ['permissions', 'shop'] },
|
||||
);
|
||||
if (!role) {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { UpdateScheduleDto } from '../dto/update-schedule.dto';
|
||||
import { FindSchedulesDto } from '../dto/find-schedules.dto';
|
||||
import { Schedule } from '../entities/schedule.entity';
|
||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||
import { ShopId } from 'src/common/decorators/rest-id.decorator';
|
||||
import { ShopId } from 'src/common/decorators/shop-id.decorator';
|
||||
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
||||
import { Permission } from 'src/common/enums/permission.enum';
|
||||
import { API_HEADER_SLUG } from 'src/common/constants';
|
||||
@@ -45,8 +45,8 @@ export class ScheduleController {
|
||||
@ApiOperation({ summary: 'Create a new schedule' })
|
||||
@ApiBody({ type: CreateScheduleDto })
|
||||
@ApiCreatedResponse({ description: 'Schedule created successfully', type: Schedule })
|
||||
create(@Body() createScheduleDto: CreateScheduleDto, @ShopId() restId: string) {
|
||||
return this.scheduleService.create(restId, createScheduleDto);
|
||||
create(@Body() createScheduleDto: CreateScheduleDto, @ShopId() shopId: string) {
|
||||
return this.scheduleService.create(shopId, createScheduleDto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -63,9 +63,9 @@ export class ScheduleController {
|
||||
@ApiOkResponse({ description: 'List of schedules', type: [Schedule] })
|
||||
findAll(
|
||||
@Query(new ValidationPipe({ transform: true, whitelist: true })) query: FindSchedulesDto,
|
||||
@ShopId() restId: string,
|
||||
@ShopId() shopId: string,
|
||||
) {
|
||||
return this.scheduleService.findAll(restId, query);
|
||||
return this.scheduleService.findAll(shopId, query);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -76,8 +76,8 @@ export class ScheduleController {
|
||||
@ApiParam({ name: 'id', description: 'Schedule ID' })
|
||||
@ApiOkResponse({ description: 'Schedule details', type: Schedule })
|
||||
@ApiNotFoundResponse({ description: 'Schedule not found' })
|
||||
findOne(@Param('id') id: string, @ShopId() restId: string) {
|
||||
return this.scheduleService.findOne(id, restId);
|
||||
findOne(@Param('id') id: string, @ShopId() shopId: string) {
|
||||
return this.scheduleService.findOne(id, shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -89,8 +89,8 @@ export class ScheduleController {
|
||||
@ApiBody({ type: UpdateScheduleDto })
|
||||
@ApiOkResponse({ description: 'Schedule updated successfully', type: Schedule })
|
||||
@ApiNotFoundResponse({ description: 'Schedule not found' })
|
||||
update(@Param('id') id: string, @Body() updateScheduleDto: UpdateScheduleDto, @ShopId() restId: string) {
|
||||
return this.scheduleService.update(id, restId, updateScheduleDto);
|
||||
update(@Param('id') id: string, @Body() updateScheduleDto: UpdateScheduleDto, @ShopId() shopId: string) {
|
||||
return this.scheduleService.update(id, shopId, updateScheduleDto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -101,7 +101,7 @@ export class ScheduleController {
|
||||
@ApiParam({ name: 'id', description: 'Schedule ID' })
|
||||
@ApiOkResponse({ description: 'Schedule deleted successfully' })
|
||||
@ApiNotFoundResponse({ description: 'Schedule not found' })
|
||||
remove(@Param('id') id: string, @ShopId() restId: string) {
|
||||
return this.scheduleService.remove(id, restId);
|
||||
remove(@Param('id') id: string, @ShopId() shopId: string) {
|
||||
return this.scheduleService.remove(id, shopId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ export class ShopController {
|
||||
@Permissions(Permission.UPDATE_RESTAURANT)
|
||||
@ApiOperation({ summary: 'Get shop by ID from request' })
|
||||
@Get('admin/shops/my-shop')
|
||||
async findOne(@ShopId() restId: string) {
|
||||
return await this.shopService.findOne(restId);
|
||||
async findOne(@ShopId() shopId: string) {
|
||||
return await this.shopService.findOne(shopId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -51,8 +51,8 @@ export class ShopController {
|
||||
@ApiOperation({ summary: 'Update a shop' })
|
||||
@ApiBody({ type: UpdateRestaurantDto })
|
||||
@Patch('admin/shops/my-shop')
|
||||
updateMyRestaurant(@ShopId() restId: string, @Body() updateRestaurantDto: UpdateRestaurantDto) {
|
||||
return this.shopService.update(restId, updateRestaurantDto);
|
||||
updateMyRestaurant(@ShopId() shopId: string, @Body() updateRestaurantDto: UpdateRestaurantDto) {
|
||||
return this.shopService.update(shopId, updateRestaurantDto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
|
||||
@@ -16,5 +16,5 @@ export class Schedule extends BaseEntity {
|
||||
isActive: boolean = true;
|
||||
|
||||
@Property()
|
||||
restId!: string;
|
||||
shopId!: string;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ export class ScheduleService {
|
||||
private readonly em: EntityManager,
|
||||
) { }
|
||||
|
||||
async create(restId: string, createScheduleDto: CreateScheduleDto): Promise<Schedule> {
|
||||
const rest = await this.restRepository.findOne({ id: restId });
|
||||
async create(shopId: string, createScheduleDto: CreateScheduleDto): Promise<Schedule> {
|
||||
const rest = await this.restRepository.findOne({ id: shopId });
|
||||
if (!rest) {
|
||||
throw new NotFoundException(`رستوران با شناسه ${restId} یافت نشد.`);
|
||||
throw new NotFoundException(`رستوران با شناسه ${shopId} یافت نشد.`);
|
||||
}
|
||||
|
||||
const data: RequiredEntityData<Schedule> = {
|
||||
@@ -30,7 +30,7 @@ export class ScheduleService {
|
||||
openTime: createScheduleDto.openTime,
|
||||
closeTime: createScheduleDto.closeTime,
|
||||
isActive: createScheduleDto.isActive ?? true,
|
||||
restId,
|
||||
shopId,
|
||||
};
|
||||
|
||||
const schedule = this.scheduleRepository.create(data);
|
||||
@@ -38,8 +38,8 @@ export class ScheduleService {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
async findAll(restId?: string, query?: FindSchedulesDto): Promise<Schedule[]> {
|
||||
const where: FilterQuery<Schedule> = restId ? { restId } : {};
|
||||
async findAll(shopId?: string, query?: FindSchedulesDto): Promise<Schedule[]> {
|
||||
const where: FilterQuery<Schedule> = shopId ? { shopId } : {};
|
||||
|
||||
if (query?.weekDay !== undefined) {
|
||||
where.weekDay = query.weekDay;
|
||||
@@ -57,7 +57,7 @@ export class ScheduleService {
|
||||
const weekDay = today.getDay(); // 0 = Sunday, 6 = Saturday
|
||||
|
||||
const where: FilterQuery<Schedule> = {
|
||||
restId: shop.id.toString(),
|
||||
shopId: shop.id.toString(),
|
||||
weekDay,
|
||||
isActive: true,
|
||||
};
|
||||
@@ -72,23 +72,23 @@ export class ScheduleService {
|
||||
}
|
||||
|
||||
const where: FilterQuery<Schedule> = {
|
||||
restId: shop.id.toString(),
|
||||
shopId: shop.id.toString(),
|
||||
isActive: true,
|
||||
};
|
||||
|
||||
return this.scheduleRepository.find(where);
|
||||
}
|
||||
|
||||
async findOne(id: string, restId: string): Promise<Schedule> {
|
||||
const schedule = await this.scheduleRepository.findOne({ id, restId });
|
||||
async findOne(id: string, shopId: string): Promise<Schedule> {
|
||||
const schedule = await this.scheduleRepository.findOne({ id, shopId });
|
||||
if (!schedule) {
|
||||
throw new NotFoundException(`برنامه با شناسه ${id} یافت نشد.`);
|
||||
}
|
||||
return schedule;
|
||||
}
|
||||
|
||||
async update(id: string, restId: string, updateScheduleDto: UpdateScheduleDto): Promise<Schedule> {
|
||||
const schedule = await this.scheduleRepository.findOne({ id, restId });
|
||||
async update(id: string, shopId: string, updateScheduleDto: UpdateScheduleDto): Promise<Schedule> {
|
||||
const schedule = await this.scheduleRepository.findOne({ id, shopId });
|
||||
if (!schedule) {
|
||||
throw new NotFoundException(`برنامه با شناسه ${id} یافت نشد.`);
|
||||
}
|
||||
@@ -112,8 +112,8 @@ export class ScheduleService {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
async remove(id: string, restId: string): Promise<void> {
|
||||
const schedule = await this.scheduleRepository.findOne({ id, restId });
|
||||
async remove(id: string, shopId: string): Promise<void> {
|
||||
const schedule = await this.scheduleRepository.findOne({ id, shopId });
|
||||
if (!schedule) {
|
||||
throw new NotFoundException(`برنامه با شناسه ${id} یافت نشد.`);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ShopRepository } from '../repositories/rest.repository';
|
||||
import { RestMessage } from 'src/common/enums/message.enum';
|
||||
import { FindRestaurantsDto } from '../dto/find-shops.dto';
|
||||
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||
import { Admin } from '../../admin/entities/admin.entity';
|
||||
import { Admin } from '../../admin/entities/admin.entity';
|
||||
import { AdminRole } from '../../admin/entities/adminRole.entity';
|
||||
import { Role } from '../../roles/entities/role.entity';
|
||||
import { normalizePhone } from 'src/modules/utils/phone.util';
|
||||
@@ -62,7 +62,7 @@ export class ShopService {
|
||||
subscriptionStartDate: dto.subscriptionStartDate,
|
||||
});
|
||||
|
||||
const role = await em.findOne(Role, { isSystem:true });
|
||||
const role = await em.findOne(Role, { isSystem: true });
|
||||
if (!role) {
|
||||
throw new BadRequestException(`System role not found`);
|
||||
}
|
||||
@@ -219,7 +219,7 @@ export class ShopService {
|
||||
await em.nativeDelete(Delivery, { shop: id });
|
||||
|
||||
// Delete schedules
|
||||
await em.nativeDelete(Schedule, { restId: id });
|
||||
await em.nativeDelete(Schedule, { shopId: id });
|
||||
|
||||
// Delete notification preferences
|
||||
await em.nativeDelete(NotificationPreference, { shop: id });
|
||||
|
||||
@@ -39,8 +39,8 @@ export class UsersController {
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiBody({ type: UpdateUserDto })
|
||||
@Patch('public/user/update')
|
||||
async updateUser(@UserId() userId: string, @ShopId() restId: string, @Body() dto: UpdateUserDto) {
|
||||
const user = await this.userService.updateUser(userId, restId, dto);
|
||||
async updateUser(@UserId() userId: string, @ShopId() shopId: string, @Body() dto: UpdateUserDto) {
|
||||
const user = await this.userService.updateUser(userId, shopId, dto);
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -60,8 +60,8 @@ export class UsersController {
|
||||
@ApiOperation({ summary: 'Get User Wallet' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@Get('public/user/wallet/balance')
|
||||
async getUserWalletBalance(@UserId() userId: string, @ShopId() restId: string) {
|
||||
const wallet = await this.walletService.getUserCurrentWalletBalance(userId, restId);
|
||||
async getUserWalletBalance(@UserId() userId: string, @ShopId() shopId: string) {
|
||||
const wallet = await this.walletService.getUserCurrentWalletBalance(userId, shopId);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ export class UsersController {
|
||||
@ApiOperation({ summary: 'Get User Wallet' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@Get('public/user/points/balance')
|
||||
async getUserWallet(@UserId() userId: string, @ShopId() restId: string) {
|
||||
const wallet = await this.walletService.getUserCurrentPoinrBalance(userId, restId);
|
||||
async getUserWallet(@UserId() userId: string, @ShopId() shopId: string) {
|
||||
const wallet = await this.walletService.getUserCurrentPoinrBalance(userId, shopId);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@@ -82,11 +82,11 @@ export class UsersController {
|
||||
@Get('public/user/wallet/transactions')
|
||||
async getUserWalletTransactions(
|
||||
@UserId() userId: string,
|
||||
@ShopId() restId: string,
|
||||
@ShopId() shopId: string,
|
||||
@Query(new ValidationPipe({ transform: true, whitelist: true }))
|
||||
query: FindWalletTransactionsDto,
|
||||
) {
|
||||
const transactions = await this.userService.getUserWalletTransactions(userId, restId, query);
|
||||
const transactions = await this.userService.getUserWalletTransactions(userId, shopId, query);
|
||||
return transactions;
|
||||
}
|
||||
|
||||
@@ -159,8 +159,8 @@ export class UsersController {
|
||||
@ApiOperation({ summary: 'Convert user score (points) to wallet balance' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@Post('public/user/convert-score-to-wallet')
|
||||
async convertScoreToWallet(@UserId() userId: string, @ShopId() restId: string) {
|
||||
await this.userService.convertScoreToWallet(userId, restId);
|
||||
async convertScoreToWallet(@UserId() userId: string, @ShopId() shopId: string) {
|
||||
await this.userService.convertScoreToWallet(userId, shopId);
|
||||
return {
|
||||
message: UserSuccessMessage.SCORE_CONVERTED_SUCCESS,
|
||||
};
|
||||
@@ -176,8 +176,8 @@ export class UsersController {
|
||||
async findAllRestaurantUsers(
|
||||
@Query(new ValidationPipe({ transform: true, whitelist: true }))
|
||||
query: FindUsersDto,
|
||||
@ShopId() restId: string,
|
||||
@ShopId() shopId: string,
|
||||
) {
|
||||
return this.userService.findAll(restId, query);
|
||||
return this.userService.findAll(shopId, query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ export class UserService {
|
||||
return this.userRepository.findOne({ id });
|
||||
}
|
||||
// todo : transaction code here
|
||||
async create(phone: string, restId: string): Promise<User> {
|
||||
async create(phone: string, shopId: string): Promise<User> {
|
||||
const normalizedPhone = normalizePhone(phone);
|
||||
const _raw = {
|
||||
phone: normalizedPhone,
|
||||
@@ -84,7 +84,7 @@ export class UserService {
|
||||
marriageDate: undefined,
|
||||
};
|
||||
// get registerscore from shop
|
||||
const shop = await this.restaurantRepository.findOne({ id: restId });
|
||||
const shop = await this.restaurantRepository.findOne({ id: shopId });
|
||||
if (!shop) {
|
||||
throw new NotFoundException(`Shop not found.`);
|
||||
}
|
||||
@@ -104,7 +104,7 @@ export class UserService {
|
||||
return user;
|
||||
}
|
||||
|
||||
async updateUser(userId: string, restId: string, dto: UpdateUserDto): Promise<User> {
|
||||
async updateUser(userId: string, shopId: string, dto: UpdateUserDto): Promise<User> {
|
||||
const user = await this.userRepository.findOne({ id: userId });
|
||||
if (!user) {
|
||||
throw new NotFoundException(`User with ID ${userId} not found.`);
|
||||
@@ -125,7 +125,7 @@ export class UserService {
|
||||
return user;
|
||||
}
|
||||
|
||||
async findAll(restId: string, dto: FindUsersDto): Promise<PaginatedResult<User>> {
|
||||
async findAll(shopId: string, dto: FindUsersDto): Promise<PaginatedResult<User>> {
|
||||
const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc' } = dto;
|
||||
|
||||
// 1. Calculate pagination
|
||||
@@ -135,7 +135,7 @@ export class UserService {
|
||||
const where: FilterQuery<User> = {
|
||||
orders: {
|
||||
shop: {
|
||||
id: restId,
|
||||
id: shopId,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -306,17 +306,17 @@ export class UserService {
|
||||
return address;
|
||||
}
|
||||
|
||||
async convertScoreToWallet(userId: string, restId: string) {
|
||||
async convertScoreToWallet(userId: string, shopId: string) {
|
||||
return this.em.transactional(async em => {
|
||||
const user = await em.findOne(User, { id: userId });
|
||||
if (!user) {
|
||||
throw new NotFoundException(`User with ID ${userId} not found.`);
|
||||
}
|
||||
const shop = await em.findOne(Shop, { id: restId });
|
||||
const shop = await em.findOne(Shop, { id: shopId });
|
||||
if (!shop) {
|
||||
throw new NotFoundException(`Shop with ID ${restId} not found.`);
|
||||
throw new NotFoundException(`Shop with ID ${shopId} not found.`);
|
||||
}
|
||||
let walletTransaction = await em.findOne(WalletTransaction, { user: { id: userId }, shop: { id: restId } },
|
||||
let walletTransaction = await em.findOne(WalletTransaction, { user: { id: userId }, shop: { id: shopId } },
|
||||
{ orderBy: { createdAt: 'DESC' } });
|
||||
|
||||
if (!walletTransaction) {
|
||||
@@ -370,18 +370,18 @@ export class UserService {
|
||||
});
|
||||
}
|
||||
|
||||
getUserWallet(userId: string, restId: string): Promise<WalletTransaction | null> {
|
||||
return this.walletTransactionRepository.findOne({ user: { id: userId }, shop: { id: restId } },
|
||||
getUserWallet(userId: string, shopId: string): Promise<WalletTransaction | null> {
|
||||
return this.walletTransactionRepository.findOne({ user: { id: userId }, shop: { id: shopId } },
|
||||
{ orderBy: { createdAt: 'DESC' } });
|
||||
}
|
||||
|
||||
getUserWalletTransactions(userId: string, restId: string, dto: FindWalletTransactionsDto) {
|
||||
return this.walletService.getUserWalletTransactions(userId, restId, dto);
|
||||
getUserWalletTransactions(userId: string, shopId: string, dto: FindWalletTransactionsDto) {
|
||||
return this.walletService.getUserWalletTransactions(userId, shopId, dto);
|
||||
}
|
||||
|
||||
async createWalletTransaction(userId: string, restId: string, dto: CreateWalletTransactionDto) {
|
||||
async createWalletTransaction(userId: string, shopId: string, dto: CreateWalletTransactionDto) {
|
||||
return this.em.transactional(async em => {
|
||||
return this.walletService.createTransaction(em, userId, restId, dto);
|
||||
return this.walletService.createTransaction(em, userId, shopId, dto);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ export class WalletService {
|
||||
constructor(
|
||||
private readonly walletTransactionRepository: WalletTransactionRepository,
|
||||
private readonly pointTransactionRepository: PointTransactionRepository,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
async getUserWalletTransactions(
|
||||
userId: string,
|
||||
restId: string,
|
||||
shopId: string,
|
||||
dto: FindWalletTransactionsDto,
|
||||
): Promise<PaginatedResult<WalletTransaction>> {
|
||||
const {
|
||||
@@ -40,17 +40,17 @@ export class WalletService {
|
||||
// Find the user's wallet for this shop
|
||||
const walletTransaction = await this.walletTransactionRepository.findOne({
|
||||
user: { id: userId },
|
||||
shop: { id: restId },
|
||||
shop: { id: shopId },
|
||||
});
|
||||
|
||||
if (!walletTransaction) {
|
||||
throw new NotFoundException(`User wallet not found for user ${userId} and shop ${restId}.`);
|
||||
throw new NotFoundException(`User wallet not found for user ${userId} and shop ${shopId}.`);
|
||||
}
|
||||
|
||||
// Build the 'where' filter query
|
||||
const where: FilterQuery<WalletTransaction> = {
|
||||
user: { id: userId },
|
||||
shop: { id: restId },
|
||||
shop: { id: shopId },
|
||||
};
|
||||
|
||||
// Add type filter
|
||||
@@ -107,11 +107,11 @@ export class WalletService {
|
||||
};
|
||||
}
|
||||
|
||||
getUserWallet(userId: string, restId: string): Promise<WalletTransaction | null> {
|
||||
getUserWallet(userId: string, shopId: string): Promise<WalletTransaction | null> {
|
||||
return this.walletTransactionRepository.findOne(
|
||||
{
|
||||
user: { id: userId },
|
||||
shop: { id: restId },
|
||||
shop: { id: shopId },
|
||||
},
|
||||
{ orderBy: { createdAt: 'DESC' } },
|
||||
);
|
||||
@@ -120,7 +120,7 @@ export class WalletService {
|
||||
async createTransaction(
|
||||
em: EntityManager,
|
||||
userId: string,
|
||||
restId: string,
|
||||
shopId: string,
|
||||
dto: CreateWalletTransactionDto,
|
||||
): Promise<WalletTransaction> {
|
||||
const { amount, type, reason } = dto;
|
||||
@@ -133,11 +133,11 @@ export class WalletService {
|
||||
// Find the user's wallet for this shop
|
||||
const walletTransaction = await em.findOne(WalletTransaction, {
|
||||
user: { id: userId },
|
||||
shop: { id: restId },
|
||||
shop: { id: shopId },
|
||||
});
|
||||
|
||||
if (!walletTransaction) {
|
||||
throw new NotFoundException(`User wallet not found for user ${userId} and shop ${restId}.`);
|
||||
throw new NotFoundException(`User wallet not found for user ${userId} and shop ${shopId}.`);
|
||||
}
|
||||
|
||||
// Calculate new balance
|
||||
|
||||
@@ -7,10 +7,10 @@ export class PointTransactionRepository extends EntityRepository<PointTransactio
|
||||
constructor(readonly em: EntityManager) {
|
||||
super(em, PointTransaction);
|
||||
}
|
||||
async getcurrentPointBalance(userId: string, restaurantId: string): Promise<number> {
|
||||
async getcurrentPointBalance(userId: string, shopId: string): Promise<number> {
|
||||
const pointTransaction = await this.em.findOne(PointTransaction, {
|
||||
user: { id: userId },
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
});
|
||||
return pointTransaction?.balance || 0;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ export class WalletTransactionRepository extends EntityRepository<WalletTransact
|
||||
constructor(readonly em: EntityManager) {
|
||||
super(em, WalletTransaction);
|
||||
}
|
||||
async getCurrentWalletBalance(userId: string, restaurantId: string): Promise<number> {
|
||||
async getCurrentWalletBalance(userId: string, shopId: string): Promise<number> {
|
||||
const walletTransaction = await this.em.findOne(
|
||||
WalletTransaction,
|
||||
{
|
||||
user: { id: userId },
|
||||
shop: { id: restaurantId },
|
||||
shop: { id: shopId },
|
||||
},
|
||||
{ orderBy: { createdAt: 'desc' } },
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ export class SchedulesSeeder {
|
||||
// Create 3 schedules per day
|
||||
for (const timeSlot of timeSlots) {
|
||||
const existing = await em.findOne(Schedule, {
|
||||
restId: shop.id,
|
||||
shopId: shop.id,
|
||||
weekDay,
|
||||
openTime: timeSlot.openTime,
|
||||
closeTime: timeSlot.closeTime,
|
||||
@@ -21,7 +21,7 @@ export class SchedulesSeeder {
|
||||
|
||||
if (!existing) {
|
||||
const schedule = em.create(Schedule, {
|
||||
restId: shop.id,
|
||||
shopId: shop.id,
|
||||
weekDay,
|
||||
openTime: timeSlot.openTime,
|
||||
closeTime: timeSlot.closeTime,
|
||||
|
||||
Reference in New Issue
Block a user