Skip to content

Commit

Permalink
add courier module
Browse files Browse the repository at this point in the history
  • Loading branch information
bahram1249 committed May 8, 2024
1 parent 83f8b47 commit 1ce0d87
Show file tree
Hide file tree
Showing 17 changed files with 641 additions and 0 deletions.
84 changes: 84 additions & 0 deletions apps/e-commerce/src/admin/courier/courier.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Param,
Post,
Query,
UseGuards,
UseInterceptors,
} from '@nestjs/common';
import {
ApiBearerAuth,
ApiOperation,
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import { JwtGuard } from '@rahino/auth/guard';
import { PermissionGuard } from '@rahino/permission-checker/guard';
import { JsonResponseTransformInterceptor } from '@rahino/response/interceptor';
import { CheckPermission } from '@rahino/permission-checker/decorator';
import { GetUser } from '@rahino/auth/decorator';
import { User } from '@rahino/database/models/core/user.entity';
import { CourierService } from './courier.service';
import { CourierDto, GetCourierDto } from './dto';

@ApiTags('Admin-Couriers')
@ApiBearerAuth()
@UseGuards(JwtGuard, PermissionGuard)
@UseInterceptors(JsonResponseTransformInterceptor)
@Controller({
version: ['1'],
path: '/api/ecommerce/admin/couriers',
})
export class CourierController {
constructor(private readonly service: CourierService) {}

// public url
@ApiOperation({ description: 'show all couriers' })
@Get('/')
@ApiQuery({
name: 'filter',
type: GetCourierDto,
style: 'deepObject',
explode: true,
})
@CheckPermission({ permissionSymbol: 'ecommerce.admin.couriers.getall' })
@HttpCode(HttpStatus.OK)
async findAll(@Query() filter: GetCourierDto, @GetUser() user: User) {
return await this.service.findAll(user, filter);
}

@UseGuards(JwtGuard, PermissionGuard)
@ApiBearerAuth()
@ApiOperation({ description: 'show courier by given id' })
@CheckPermission({ permissionSymbol: 'ecommerce.admin.couriers.getone' })
@Get('/:id')
@HttpCode(HttpStatus.OK)
async findById(@Param('id') entityId: number, @GetUser() user: User) {
return await this.service.findById(entityId, user);
}

@UseGuards(JwtGuard, PermissionGuard)
@ApiBearerAuth()
@ApiOperation({ description: 'create courier by admin' })
@CheckPermission({ permissionSymbol: 'ecommerce.admin.couriers.create' })
@Post('/')
@HttpCode(HttpStatus.CREATED)
async create(@GetUser() user: User, @Body() dto: CourierDto) {
return await this.service.create(user, dto);
}

@UseGuards(JwtGuard, PermissionGuard)
@ApiBearerAuth()
@ApiOperation({ description: 'delete courier by admin' })
@Delete('/:id')
@CheckPermission({ permissionSymbol: 'ecommerce.admin.couriers.delete' })
@HttpCode(HttpStatus.OK)
async deleteById(@Param('id') entityId: number) {
return await this.service.deleteById(entityId);
}
}
19 changes: 19 additions & 0 deletions apps/e-commerce/src/admin/courier/courier.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Module } from '@nestjs/common';
import { SequelizeModule } from '@nestjs/sequelize';
import { Permission } from '@rahino/database/models/core/permission.entity';
import { User } from '@rahino/database/models/core/user.entity';
import { ECCourier } from '@rahino/database/models/ecommerce-eav/ec-courier.entity';
import { CourierService } from './courier.service';
import { CourierProfile } from './mapper';
import { UserRoleModule } from '@rahino/core/admin/user-role/user-role.module';
import { Role } from '@rahino/database/models/core/role.entity';

@Module({
imports: [
SequelizeModule.forFeature([User, Permission, Role, ECCourier]),
UserRoleModule,
],
controllers: [],
providers: [CourierService, CourierProfile],
})
export class CourierModule {}
207 changes: 207 additions & 0 deletions apps/e-commerce/src/admin/courier/courier.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { User } from '@rahino/database/models/core/user.entity';
import { CourierDto, GetCourierDto } from './dto';
import { QueryOptionsBuilder } from '@rahino/query-filter/sequelize-query-builder';
import { Sequelize } from 'sequelize';
import { Op } from 'sequelize';
import { InjectModel } from '@nestjs/sequelize';
import { ECCourier } from '@rahino/database/models/ecommerce-eav/ec-courier.entity';
import { I18nContext, I18nService } from 'nestjs-i18n';
import { I18nTranslations } from 'apps/main/src/generated/i18n.generated';
import { InjectMapper } from 'automapper-nestjs';
import { Mapper } from 'automapper-core';
import { UserCourierDto } from './dto/user-courier-dto';
import _ from 'lodash';
import { UserRoleService } from '@rahino/core/admin/user-role/user-role.service';
import { Role } from '@rahino/database/models/core/role.entity';

@Injectable()
export class CourierService {
private readonly courierRoleStaticId = 3;
constructor(
@InjectModel(ECCourier)
private readonly repository: typeof ECCourier,
@InjectModel(User)
private readonly userRepository: typeof User,
@InjectModel(Role)
private readonly roleRepository: typeof Role,
private readonly i18n: I18nService<I18nTranslations>,
private readonly userRoleService: UserRoleService,
@InjectMapper()
private readonly mapper: Mapper,
) {}

async findAll(user: User, filter: GetCourierDto) {
let queryBuilder = new QueryOptionsBuilder().filter(
Sequelize.where(
Sequelize.fn('isnull', Sequelize.col('ECCourier.isDeleted'), 0),
{
[Op.eq]: 0,
},
),
);
const count = await this.repository.count(queryBuilder.build());
queryBuilder = queryBuilder
.attributes(['id', 'userId', 'createdAt', 'updatedAt'])
.include([
{
model: User,
as: 'user',
attributes: [
'id',
'firstname',
'lastname',
'username',
'phoneNumber',
],
},
])
.offset(filter.offset, filter.ignorePaging)
.limit(filter.limit, filter.ignorePaging)
.order({ orderBy: filter.orderBy, sortOrder: filter.sortOrder });
return {
result: await this.repository.findAll(queryBuilder.build()),
total: count,
};
}

async findById(entityId: number, user: User) {
let queryBuilder = new QueryOptionsBuilder()
.attributes(['id', 'userId', 'createdAt', 'updatedAt'])
.include([
{
model: User,
as: 'user',
attributes: [
'id',
'firstname',
'lastname',
'username',
'phoneNumber',
],
},
])
.filter(
Sequelize.where(
Sequelize.fn('isnull', Sequelize.col('ECCourier.isDeleted'), 0),
{
[Op.eq]: 0,
},
),
)
.filter({ id: entityId });
const item = await this.repository.findOne(queryBuilder.build());
if (!item) {
throw new NotFoundException(
this.i18n.t('core.not_found_id', {
lang: I18nContext.current().lang,
}),
);
}
return {
result: item,
};
}

async create(user: User, dto: CourierDto) {
let findUser = await this.userRepository.findOne(
new QueryOptionsBuilder()
.filter({ phoneNumber: dto.phoneNumber })
.build(),
);
if (findUser) {
const queryBuilder = new QueryOptionsBuilder()
.filter(
Sequelize.where(
Sequelize.fn('isnull', Sequelize.col('ECCourier.isDeleted'), 0),
{
[Op.eq]: 0,
},
),
)
.filter({ userId: findUser.id });
const item = await this.repository.findOne(queryBuilder.build());
if (item) {
throw new BadRequestException(
this.i18n.t('ecommerce.user_exists_before', {
lang: I18nContext.current().lang,
}),
);
}
} else {
const mappedItem = this.mapper.map(dto, UserCourierDto, User);
const insertedItem = _.omit(mappedItem.toJSON(), ['id']);
insertedItem.username = dto.phoneNumber;
findUser = await this.userRepository.create(insertedItem);
}
const role = await this.roleRepository.findOne(
new QueryOptionsBuilder()
.filter({ static_id: this.courierRoleStaticId })
.build(),
);
if (!role) {
throw new BadRequestException(
this.i18n.t('core.not_found_role', {
lang: I18nContext.current().lang,
}),
);
}
await this.userRoleService.insertRoleToUser(role, findUser);
const item = await this.repository.create({ userId: findUser.id });

return {
result: item,
};
}

async deleteById(entityId: number) {
let queryBuilder = new QueryOptionsBuilder()
.attributes(['id', 'userId', 'createdAt', 'updatedAt'])
.include([{ model: User, as: 'user' }])
.filter(
Sequelize.where(
Sequelize.fn('isnull', Sequelize.col('ECCourier.isDeleted'), 0),
{
[Op.eq]: 0,
},
),
)
.filter({ id: entityId });
let item = await this.repository.findOne(queryBuilder.build());
if (!item) {
throw new NotFoundException(
this.i18n.t('core.not_found_id', {
lang: I18nContext.current().lang,
}),
);
}

const user = await this.userRepository.findOne(
new QueryOptionsBuilder().filter({ id: item.userId }).build(),
);

const role = await this.roleRepository.findOne(
new QueryOptionsBuilder()
.filter({ static_id: this.courierRoleStaticId })
.build(),
);
if (!role) {
throw new BadRequestException(
this.i18n.t('core.not_found_role', {
lang: I18nContext.current().lang,
}),
);
}
await this.userRoleService.removeRoleFromUser(role, user);

item = await item.save();

return {
result: item,
};
}
}
4 changes: 4 additions & 0 deletions apps/e-commerce/src/admin/courier/dto/courier.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { IntersectionType } from '@nestjs/swagger';
import { UserCourierDto } from './user-courier-dto';

export class CourierDto extends IntersectionType(UserCourierDto) {}
7 changes: 7 additions & 0 deletions apps/e-commerce/src/admin/courier/dto/get-courier.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IntersectionType } from '@nestjs/swagger';
import { IgnorePagingFilter, ListFilter } from '@rahino/query-filter';

export class GetCourierDto extends IntersectionType(
ListFilter,
IgnorePagingFilter,
) {}
2 changes: 2 additions & 0 deletions apps/e-commerce/src/admin/courier/dto/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './courier.dto';
export * from './get-courier.dto';
56 changes: 56 additions & 0 deletions apps/e-commerce/src/admin/courier/dto/user-courier-dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ApiProperty } from '@nestjs/swagger';
import { I18nTranslations } from 'apps/main/src/generated/i18n.generated';
import { AutoMap } from 'automapper-classes';
import {
IsNotEmpty,
IsString,
Matches,
MaxLength,
MinLength,
} from 'class-validator';
import { i18nValidationMessage } from 'nestjs-i18n';

export class UserCourierDto {
@MinLength(3, {
message: i18nValidationMessage<I18nTranslations>('validation.MIN'),
})
@MaxLength(256, {
message: i18nValidationMessage<I18nTranslations>('validation.MAX'),
})
@IsNotEmpty({
message: i18nValidationMessage<I18nTranslations>('validation.NOT_EMPTY'),
})
@AutoMap()
firstname: string;

@MinLength(3, {
message: i18nValidationMessage<I18nTranslations>('validation.MIN'),
})
@MaxLength(256, {
message: i18nValidationMessage<I18nTranslations>('validation.MAX'),
})
@IsNotEmpty({
message: i18nValidationMessage<I18nTranslations>('validation.NOT_EMPTY'),
})
@AutoMap()
lastname: string;

@MinLength(3, {
message: i18nValidationMessage<I18nTranslations>('validation.MIN'),
})
@MaxLength(256, {
message: i18nValidationMessage<I18nTranslations>('validation.MAX'),
})
@IsNotEmpty({
message: i18nValidationMessage<I18nTranslations>('validation.NOT_EMPTY'),
})
@AutoMap()
@Matches(new RegExp('^([0-9]){4}([0-9]){7,8}$'))
@ApiProperty({
required: true,
type: IsString,
default: 'string',
description: 'phoneNumber',
})
phoneNumber: string;
}
Loading

0 comments on commit 1ce0d87

Please sign in to comment.