Skip to content

Commit

Permalink
[Implement][EF-172-178][Task,Order]Delivery staff view shipping order…
Browse files Browse the repository at this point in the history
… and progress, complete shipping task API
  • Loading branch information
MinhhTien authored and nghiavohuynhdai committed Mar 3, 2024
1 parent b2d0093 commit b75edcb
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 24 deletions.
5 changes: 5 additions & 0 deletions src/common/contracts/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,9 @@ export const Errors: Record<string, ErrorResponse> = {
message: 'Không tìm thấy nhân viên giao hàng',
httpStatus: HttpStatus.BAD_REQUEST
},
SHIPPING_TASK_INVALID: {
error: 'SHIPPING_TASK_INVALID',
message: 'Công việc được chọn không hợp lệ',
httpStatus: HttpStatus.BAD_REQUEST
},
}
22 changes: 20 additions & 2 deletions src/order/controllers/provider.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import { OrderHistoryDto } from '@order/schemas/order.schema'

@ApiTags('Order - Provider')
@ApiBearerAuth()
@Roles(UserRole.ADMIN, UserRole.STAFF)
@UseGuards(JwtAuthGuard.ACCESS_TOKEN, RolesGuard)
@UseGuards(JwtAuthGuard.ACCESS_TOKEN)
@Controller('provider')
export class OrderProviderController {
constructor(private readonly orderService: OrderService) {}
Expand All @@ -24,6 +23,8 @@ export class OrderProviderController {
@ApiOperation({
summary: 'Paginate list order'
})
@Roles(UserRole.ADMIN, UserRole.STAFF)
@UseGuards(RolesGuard)
@ApiOkResponse({ type: OrderPaginateResponseDto })
@ApiQuery({ type: PaginationQuery })
async getListOrder(@Pagination() paginationParams: PaginationParams) {
Expand All @@ -34,6 +35,8 @@ export class OrderProviderController {
@ApiOperation({
summary: 'View order detail'
})
@Roles(UserRole.ADMIN, UserRole.STAFF)
@UseGuards(RolesGuard)
@ApiOkResponse({ type: OrderResponseDto })
getOrderDetail(@Param('orderId') orderId: string) {
return this.orderService.getOrderDetails({ _id: orderId })
Expand All @@ -43,6 +46,8 @@ export class OrderProviderController {
@ApiOperation({
summary: 'Confirm order'
})
@Roles(UserRole.ADMIN, UserRole.STAFF)
@UseGuards(RolesGuard)
@ApiOkResponse({ type: SuccessDataResponse })
@ApiBadRequestResponse({ type: ErrorResponse })
async confirmOrder(@Req() req, @Param('orderId') orderId: string) {
Expand All @@ -55,6 +60,8 @@ export class OrderProviderController {
@ApiOperation({
summary: 'Cancel order (new field: cancel reason)'
})
@Roles(UserRole.ADMIN, UserRole.STAFF)
@UseGuards(RolesGuard)
@ApiOkResponse({ type: SuccessDataResponse })
@ApiBadRequestResponse({ type: ErrorResponse })
async cancelOrder(@Req() req, @Param('orderId') orderId: string, @Body() cancelOrderDto: CancelOrderDto) {
Expand All @@ -69,4 +76,15 @@ export class OrderProviderController {
const result = await this.orderService.cancelOrder(cancelOrderDto)
return result
}

@Get(':orderId/shipping')
@ApiOperation({
summary: '(Delivery Staff) View shipping order detail'
})
@Roles(UserRole.DELIVERY_STAFF)
@UseGuards(RolesGuard)
@ApiOkResponse({ type: OrderResponseDto })
getShippingOrderDetail(@Param('orderId') orderId: string) {
return this.orderService.getOrderDetails({ _id: orderId })
}
}
23 changes: 22 additions & 1 deletion src/order/services/order.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export class OrderService {
// transactionStatus: TransactionStatus.CAPTURED
},
{
$set: { orderStatus: OrderStatus.DELIVERING },
$set: { orderStatus: OrderStatus.DELIVERING, deliveryDate: new Date() },
$push: { orderHistory },
}, {
session
Expand Down Expand Up @@ -266,4 +266,25 @@ export class OrderService {
throw error
}
}

public async completeOrder(orderId: string, userId: string, role: UserRole, session?: ClientSession) {
// 1. Update order status and order history
const orderHistory = new OrderHistoryDto(OrderStatus.COMPLETED, TransactionStatus.CAPTURED, userId, role)
const order = await this.orderRepository.findOneAndUpdate(
{
_id: orderId,
orderStatus: OrderStatus.DELIVERING
// transactionStatus: TransactionStatus.CAPTURED
},
{
$set: { orderStatus: OrderStatus.COMPLETED, completeDate: new Date() },
$push: { orderHistory },
}, {
session
}
)
if (!order) throw new AppException(Errors.ORDER_STATUS_INVALID)

return order
}
}
44 changes: 38 additions & 6 deletions src/task/controllers/task.controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Body, Controller, Get, Post, Query, Req, UseGuards } from '@nestjs/common'
import { Body, Controller, Get, Param, Patch, Post, Query, Req, UseGuards } from '@nestjs/common'
import { ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger'
import * as _ from 'lodash'

import { ErrorResponse, IDDataResponse, PaginationQuery } from '@common/contracts/dto'
import { ErrorResponse, IDDataResponse, PaginationQuery, SuccessDataResponse } from '@common/contracts/dto'
import { JwtAuthGuard } from '@auth/guards/jwt-auth.guard'
import { RolesGuard } from '@auth/guards/roles.guard'
import { Roles } from '@auth/decorators/roles.decorator'
Expand All @@ -11,6 +11,7 @@ import { UserRole } from '@common/contracts/constant'
import { Pagination, PaginationParams } from '@common/decorators/pagination.decorator'
import { TaskService } from '@task/services/task.service'
import { CreateShippingTaskDto, FilterTaskDto, TaskPaginateResponseDto } from '@task/dto/task.dto'
import { Types } from 'mongoose'

@ApiTags('Task')
@ApiBearerAuth()
Expand All @@ -21,7 +22,7 @@ export class TaskController {

@Post('shipping')
@ApiOperation({
summary: 'Change order status to DELIVERING and Create new shipping task'
summary: 'Create new shipping task'
})
@Roles(UserRole.ADMIN, UserRole.STAFF)
@UseGuards(RolesGuard)
Expand All @@ -30,7 +31,33 @@ export class TaskController {
create(@Req() req, @Body() createShippingTaskDto: CreateShippingTaskDto) {
const { _id } = _.get(req, 'user')
createShippingTaskDto.reporterId = _id
return this.taskService.create(createShippingTaskDto)
return this.taskService.createShippingTask(createShippingTaskDto)
}

@Patch('shipping/:orderId/progress')
@ApiOperation({
summary: '(Delivery Staff) Change task status to IN_PROGRESS and order status to DELIVERING'
})
@Roles(UserRole.DELIVERY_STAFF)
@UseGuards(RolesGuard)
@ApiOkResponse({ type: SuccessDataResponse })
@ApiBadRequestResponse({ type: ErrorResponse })
progressShippingTask(@Req() req, @Param('orderId') orderId: string) {
const { _id: userId } = _.get(req, 'user')
return this.taskService.progressShippingTask(orderId, userId)
}

@Patch('shipping/:orderId/complete')
@ApiOperation({
summary: '(Delivery Staff) Change order status to COMPLETED and order status to COMPLETED'
})
@Roles(UserRole.DELIVERY_STAFF)
@UseGuards(RolesGuard)
@ApiOkResponse({ type: SuccessDataResponse })
@ApiBadRequestResponse({ type: ErrorResponse })
completeShippingTask(@Req() req, @Param('orderId') orderId: string) {
const { _id: userId } = _.get(req, 'user')
return this.taskService.completeShippingTask(orderId, userId)
}

@Get()
Expand All @@ -41,15 +68,20 @@ export class TaskController {
@UseGuards(RolesGuard)
@ApiOkResponse({ type: TaskPaginateResponseDto })
@ApiQuery({ type: PaginationQuery })
async getListStaff(@Req() req, @Pagination() paginationParams: PaginationParams, @Query() filterTaskDto: FilterTaskDto) {
async getListStaff(
@Req() req,
@Pagination() paginationParams: PaginationParams,
@Query() filterTaskDto: FilterTaskDto
) {
const { _id, role } = _.get(req, 'user')

const providerId = await this.staffService.getProviderId(_id)
filterTaskDto['reporter.providerId'] = providerId

if ([UserRole.CONSULTANT_STAFF, UserRole.DELIVERY_STAFF].includes(role)) {
filterTaskDto['assignee._id'] = _id
filterTaskDto['assignee._id'] = new Types.ObjectId(_id)
}

return this.taskService.paginate(filterTaskDto, paginationParams)
}
}
100 changes: 85 additions & 15 deletions src/task/services/task.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Injectable } from '@nestjs/common'
import { StaffRepository } from '@staff/repositories/staff.repository'
import { IDResponse } from '@common/contracts/dto'
import { IDResponse, SuccessResponse } from '@common/contracts/dto'
import * as _ from 'lodash'
import { InjectConnection } from '@nestjs/mongoose'
import { Connection, FilterQuery } from 'mongoose'
import { PaginationParams } from '@common/decorators/pagination.decorator'
import { Status, TaskType, UserRole } from '@common/contracts/constant'
import { Status, TaskStatus, TaskType, UserRole } from '@common/contracts/constant'
import { AppException } from '@common/exceptions/app.exception'
import { Errors } from '@common/contracts/error'
import { Task } from '@task/schemas/task.schema'
Expand All @@ -22,7 +22,7 @@ export class TaskService {
private readonly orderService: OrderService
) {}

public async create(createShippingTaskDto: CreateShippingTaskDto) {
public async createShippingTask(createShippingTaskDto: CreateShippingTaskDto) {
// Execute in transaction
const session = await this.connection.startSession()
session.startTransaction()
Expand All @@ -49,27 +49,18 @@ export class TaskService {
throw new AppException(Errors.DELIVERY_STAFF_NOT_FOUND)
}

// 2. Update order status to DELIVERING
await this.orderService.deliveryOrder(
createShippingTaskDto.orderId,
assignee._id.toString(),
UserRole.DELIVERY_STAFF,
session
)

// 3. Create shipping task
// 2. Create shipping task
const task = await this.taskRepository.create(
{
...createShippingTaskDto,
type: TaskType.SHIPPING,
reporter,
assignee,
assignee
},
{ session }
)

// 4. Send email/notification to customer
// 5. Send notification to staff
// 3. Send notification to staff

await session.commitTransaction()
return new IDResponse(task._id)
Expand All @@ -92,4 +83,83 @@ export class TaskService {
)
return result
}

public async progressShippingTask(orderId: string, userId: string) {
// Execute in transaction
const session = await this.connection.startSession()
session.startTransaction()
try {
// 1. Update shipping task to IN_PROGRESS
const task = await this.taskRepository.findOneAndUpdate(
{
orderId
},
{
status: TaskStatus.IN_PROGRESS
},
{ session }
)
if (
!task ||
task.status !== TaskStatus.PENDING ||
task.type !== TaskType.SHIPPING ||
task.assignee?._id.toString() !== userId
) {
throw new AppException(Errors.SHIPPING_TASK_INVALID)
}

// 2. Update order status to DELIVERING
await this.orderService.deliveryOrder(orderId, userId, UserRole.DELIVERY_STAFF, session)

// 3. Send email/notification to customer
// 4. Send notification to staff

await session.commitTransaction()
return new SuccessResponse(true)
} catch (error) {
await session.abortTransaction()
console.error(error)
throw error
}
}

public async completeShippingTask(orderId: string, userId: string) {
// Execute in transaction
const session = await this.connection.startSession()
session.startTransaction()
try {
// 1. Update shipping task to COMPLETED
const task = await this.taskRepository.findOneAndUpdate(
{
orderId
},
{
status: TaskStatus.COMPLETED,
completionDate: new Date()
},
{ session }
)
if (
!task ||
task.status !== TaskStatus.IN_PROGRESS ||
task.type !== TaskType.SHIPPING ||
task.assignee?._id.toString() !== userId
) {
throw new AppException(Errors.SHIPPING_TASK_INVALID)
}

// 2. Update order status to COMPLETED
await this.orderService.completeOrder(orderId, userId, UserRole.DELIVERY_STAFF, session)

// 3. Send email/notification to customer
// 4. Send notification to staff

await session.commitTransaction()
return new SuccessResponse(true)
} catch (error) {
await session.abortTransaction()
console.error(error)
throw error
}
}
}

0 comments on commit b75edcb

Please sign in to comment.