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