order
This commit is contained in:
@@ -18,12 +18,13 @@ export enum PermissionEnum {
|
|||||||
VIEW_MY_ASSIGNED_INVOICED_REQUESTS = 'view_my_assigned_invoiced_requests',
|
VIEW_MY_ASSIGNED_INVOICED_REQUESTS = 'view_my_assigned_invoiced_requests',
|
||||||
|
|
||||||
VIEW_ORDERS = 'view_orders',
|
VIEW_ORDERS = 'view_orders',
|
||||||
VIEW_MY_ORDERS = 'view_my_orders',
|
VIEW_ASSIGNED_ORDERS = 'view_assigned_orders',
|
||||||
CREATE_ORDER = 'create_order',
|
CREATE_ORDER = 'create_order',
|
||||||
UPDATE_ORDER = 'update_order',
|
UPDATE_ORDER = 'update_order',
|
||||||
ASSIGN_DESIGNER = 'assign_designer',
|
ASSIGN_DESIGNER = 'assign_designer',
|
||||||
DELETE_ORDER = 'delete_order',
|
DELETE_ORDER = 'delete_order',
|
||||||
|
|
||||||
|
|
||||||
// User & Admin Management
|
// User & Admin Management
|
||||||
MANAGE_ADMINS = 'manage_admins',
|
MANAGE_ADMINS = 'manage_admins',
|
||||||
// Role Management
|
// Role Management
|
||||||
@@ -52,7 +53,7 @@ export const PermissionTitles: Record<PermissionEnum, string> = {
|
|||||||
[PermissionEnum.CREATE_ORDER]: 'ایجاد سفارش جدید',
|
[PermissionEnum.CREATE_ORDER]: 'ایجاد سفارش جدید',
|
||||||
[PermissionEnum.UPDATE_ORDER]: 'ویرایش سفارش ',
|
[PermissionEnum.UPDATE_ORDER]: 'ویرایش سفارش ',
|
||||||
[PermissionEnum.VIEW_ORDERS]: "مشاهده همه سفارش ها",
|
[PermissionEnum.VIEW_ORDERS]: "مشاهده همه سفارش ها",
|
||||||
[PermissionEnum.VIEW_MY_ORDERS]: "مشاهده سفارش های من",
|
[PermissionEnum.VIEW_ASSIGNED_ORDERS]: "مشاهده سفارش های من",
|
||||||
[PermissionEnum.ASSIGN_DESIGNER]: "اضافه کردن طراح به سفارش",
|
[PermissionEnum.ASSIGN_DESIGNER]: "اضافه کردن طراح به سفارش",
|
||||||
|
|
||||||
[PermissionEnum.MANAGE_ADMINS]: 'مدیریت مدیران',
|
[PermissionEnum.MANAGE_ADMINS]: 'مدیریت مدیران',
|
||||||
@@ -71,5 +72,5 @@ export const PermissionTitles: Record<PermissionEnum, string> = {
|
|||||||
[PermissionEnum.CREATE_INVOICE]: "",
|
[PermissionEnum.CREATE_INVOICE]: "",
|
||||||
[PermissionEnum.UPDATE_INVOICE]: "",
|
[PermissionEnum.UPDATE_INVOICE]: "",
|
||||||
[PermissionEnum.DELETE_INVOICE]: "",
|
[PermissionEnum.DELETE_INVOICE]: "",
|
||||||
[PermissionEnum.DELETE_ORDER]: ""
|
[PermissionEnum.DELETE_ORDER]: "",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -163,19 +163,19 @@ export class AdminService {
|
|||||||
return admins
|
return admins
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasPermission(adminId: string, permissionName: PermissionEnum): Promise<boolean> {
|
async hasPermissions(adminId: string, permissionNames: PermissionEnum[]): Promise<PermissionEnum[]> {
|
||||||
const has = await this.adminRepository.count({
|
if (permissionNames.length === 0) return []
|
||||||
id: adminId,
|
|
||||||
role: {
|
|
||||||
permissions: {
|
|
||||||
$some: {
|
|
||||||
name: permissionName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return !!has
|
const admin = await this.adminRepository.findOne(
|
||||||
|
{ id: adminId },
|
||||||
|
{ populate: ['role', 'role.permissions'] },
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!admin?.role?.permissions) return []
|
||||||
|
|
||||||
|
const adminPermissionNames = new Set(
|
||||||
|
admin.role.permissions.getItems().map((p) => p.name),
|
||||||
|
)
|
||||||
|
return permissionNames.filter((name) => adminPermissionNames.has(name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
|||||||
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
||||||
import { PermissionEnum } from 'src/common/enums/permission.enum';
|
import { PermissionEnum } from 'src/common/enums/permission.enum';
|
||||||
import { AssignDesignerDto } from '../dto/assign-designer.dto';
|
import { AssignDesignerDto } from '../dto/assign-designer.dto';
|
||||||
|
import { UpdateStatusDto } from '../dto/update-status.dto';
|
||||||
|
|
||||||
@ApiTags('orders')
|
@ApiTags('orders')
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@@ -45,6 +46,13 @@ export class OrderController {
|
|||||||
return this.orderService.createOrderAsAdmin(adminId, body);
|
return this.orderService.createOrderAsAdmin(adminId, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Permission handled inside service
|
||||||
|
@Get('admin/orders')
|
||||||
|
@UseGuards(AdminAuthGuard)
|
||||||
|
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
|
||||||
|
findAllAsAdmin(@Query() dto: FindOrdersDto, @AdminId() adminId: string) {
|
||||||
|
return this.orderService.findOrdersAsUser(adminId, dto);
|
||||||
|
}
|
||||||
|
|
||||||
@Get('admin/orders/:id')
|
@Get('admin/orders/:id')
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
@@ -79,6 +87,13 @@ export class OrderController {
|
|||||||
return this.orderService.assignDesigner(orderId, body.designerId);
|
return this.orderService.assignDesigner(orderId, body.designerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Permission handled inside service
|
||||||
|
@Post('admin/orders/:orderId/status')
|
||||||
|
@UseGuards(AdminAuthGuard)
|
||||||
|
@ApiOperation({ summary: 'Assign Order Designer ' })
|
||||||
|
updateStatus(@Param('orderId') orderId: string,@AdminId()adminId:string, @Body() dto: UpdateStatusDto) {
|
||||||
|
return this.orderService.updateStatus(orderId,adminId, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import {
|
||||||
|
IsString
|
||||||
|
} from 'class-validator';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { OrderStatusEnum } from '../interface/order.interface';
|
||||||
|
|
||||||
|
export class UpdateStatusAsDesignerDto {
|
||||||
|
@IsString()
|
||||||
|
@ApiProperty()
|
||||||
|
status: OrderStatusEnum.DESIGN_INITIATED | OrderStatusEnum.DESIGN_FINISHED | OrderStatusEnum.DESIGN_REVISION;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import {
|
||||||
|
IsString
|
||||||
|
} from 'class-validator';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { OrderStatusEnum } from '../interface/order.interface';
|
||||||
|
|
||||||
|
export class UpdateStatusDto {
|
||||||
|
@IsString()
|
||||||
|
@ApiProperty({enum:OrderStatusEnum})
|
||||||
|
status: OrderStatusEnum
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable, BadRequestException, Logger, NotFoundException } from '@nestjs/common';
|
import { Injectable, BadRequestException, Logger, NotFoundException, ForbiddenException } from '@nestjs/common';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { OrderRepository } from '../repositories/order.repository';
|
import { OrderRepository } from '../repositories/order.repository';
|
||||||
import { FindOrdersDto } from '../dto/find-orders.dto';
|
import { FindOrdersDto } from '../dto/find-orders.dto';
|
||||||
@@ -26,6 +26,8 @@ import { OrderType } from '../entities/order-type.entity';
|
|||||||
import { Product } from 'src/modules/product/entities/product.entity';
|
import { Product } from 'src/modules/product/entities/product.entity';
|
||||||
import { InvoiceItem } from 'src/modules/invoice/entities/invoice-item.entity';
|
import { InvoiceItem } from 'src/modules/invoice/entities/invoice-item.entity';
|
||||||
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||||
|
import { UpdateStatusAsDesignerDto } from '../dto/update-status-as-designer.dto';
|
||||||
|
import { UpdateStatusDto } from '../dto/update-status.dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrderService {
|
export class OrderService {
|
||||||
@@ -112,8 +114,26 @@ export class OrderService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOrdersAsAdmin(dto: FindOrdersDto): Promise<PaginatedResult<Order>> {
|
async findOrdersAsAdmin(adminId: string, dto: FindOrdersDto): Promise<PaginatedResult<Order>> {
|
||||||
return this.orderRepository.findAllPaginated(dto)
|
const permissions = await this.adminService.hasPermissions(adminId,
|
||||||
|
[PermissionEnum.VIEW_ORDERS, PermissionEnum.VIEW_ASSIGNED_ORDERS])
|
||||||
|
|
||||||
|
if (permissions.includes(PermissionEnum.VIEW_ORDERS)) {
|
||||||
|
|
||||||
|
return this.orderRepository.findAllPaginated(dto)
|
||||||
|
|
||||||
|
} else if (permissions.includes(PermissionEnum.VIEW_ASSIGNED_ORDERS)) {
|
||||||
|
|
||||||
|
return this.orderRepository.findAllPaginated({
|
||||||
|
designerId: adminId,
|
||||||
|
...dto
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
throw new ForbiddenException("You don't have permission to view orders")
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrderAsUser(userId: string, orderId: string): Promise<Order> {
|
async getOrderAsUser(userId: string, orderId: string): Promise<Order> {
|
||||||
@@ -124,13 +144,6 @@ export class OrderService {
|
|||||||
return order
|
return order
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOrderOrFail(id: string): Promise<Order> {
|
|
||||||
const order = await this.em.findOne(Order, { id }, { populate: ['user', 'type', 'product', 'creator'] });
|
|
||||||
if (!order) {
|
|
||||||
throw new NotFoundException(`Order with ID ${id} not found.`);
|
|
||||||
}
|
|
||||||
return order;
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateOrderAsAdmin(id: string, dto: UpdateOrderAsAdminDto): Promise<Order> {
|
async updateOrderAsAdmin(id: string, dto: UpdateOrderAsAdminDto): Promise<Order> {
|
||||||
const order = await this.findOrderOrFail(id);
|
const order = await this.findOrderOrFail(id);
|
||||||
@@ -177,6 +190,38 @@ export class OrderService {
|
|||||||
await this.em.flush();
|
await this.em.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateStatusAsDesigner(orderId: string, dto: UpdateStatusAsDesignerDto): Promise<Order> {
|
||||||
|
const order = await this.findOrderOrFail(orderId);
|
||||||
|
order.status = dto.status;
|
||||||
|
await this.em.flush();
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateStatus(orderId: string, adminId: string, dto: UpdateStatusDto): Promise<Order> {
|
||||||
|
const permissions = await this.adminService.hasPermissions(adminId,
|
||||||
|
[PermissionEnum.VIEW_ORDERS, PermissionEnum.VIEW_ASSIGNED_ORDERS])
|
||||||
|
|
||||||
|
if (permissions.includes(PermissionEnum.VIEW_ORDERS)) {
|
||||||
|
const order = await this.findOrderOrFail(orderId);
|
||||||
|
order.status = dto.status;
|
||||||
|
await this.em.flush();
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.includes(PermissionEnum.VIEW_ASSIGNED_ORDERS)) {
|
||||||
|
const order = await this.findOrderOrFailAssignedOrder(orderId, adminId);
|
||||||
|
order.status = dto.status;
|
||||||
|
await this.em.flush();
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
const order = await this.findOrderOrFail(orderId);
|
||||||
|
|
||||||
|
order.status = dto.status;
|
||||||
|
await this.em.flush();
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
async assignDesigner(orderId: string, designerId: string) {
|
async assignDesigner(orderId: string, designerId: string) {
|
||||||
const order = await this.findOrderOrFail(orderId)
|
const order = await this.findOrderOrFail(orderId)
|
||||||
const designer = await this.adminService.findOrFail(designerId)
|
const designer = await this.adminService.findOrFail(designerId)
|
||||||
@@ -185,4 +230,24 @@ export class OrderService {
|
|||||||
await this.em.flush()
|
await this.em.flush()
|
||||||
return order
|
return order
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------Hrlper Methods -----------
|
||||||
|
async findOrderOrFail(id: string): Promise<Order> {
|
||||||
|
const order = await this.em.findOne(Order, { id }, { populate: ['user', 'type', 'product', 'creator'] });
|
||||||
|
if (!order) {
|
||||||
|
throw new NotFoundException(`Order with ID ${id} not found.`);
|
||||||
|
}
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOrderOrFailAssignedOrder(id: string, adminId: string): Promise<Order> {
|
||||||
|
const order = await this.em.findOne(Order, { id }, { populate: ['user', 'type', 'product', 'creator'] });
|
||||||
|
if (!order) {
|
||||||
|
throw new NotFoundException(`Order with ID ${id} not found.`);
|
||||||
|
}
|
||||||
|
if (order?.designer?.id !== adminId) {
|
||||||
|
throw new ForbiddenException("You don't have permission to view this order")
|
||||||
|
}
|
||||||
|
return order;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { FindOrdersDto } from '../dto/find-orders.dto';
|
|||||||
|
|
||||||
class FindOrdersOpts extends FindOrdersDto {
|
class FindOrdersOpts extends FindOrdersDto {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
adminId?: string;
|
||||||
designerId?: string
|
designerId?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,7 +30,8 @@ export class OrderRepository extends EntityRepository<Order> {
|
|||||||
userId,
|
userId,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
designerId
|
designerId,
|
||||||
|
adminId
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
const offset = (page - 1) * limit;
|
const offset = (page - 1) * limit;
|
||||||
|
|||||||
Reference in New Issue
Block a user