Merge branch 'development' into 'master'
Development See merge request empatnusabangsa/ppob/ppob-backend!66
This commit is contained in:
commit
a2ed204efa
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -15,12 +15,19 @@ spec:
|
|||
spec:
|
||||
containers:
|
||||
- name: ppob-backend
|
||||
image: registry-harbor.app.bangun-kreatif.com/empatnusabangsa/ppob-backend:2
|
||||
image: registry-harbor.app.bangun-kreatif.com/empatnusabangsa/ppob-backend:<VERSION>
|
||||
ports:
|
||||
- containerPort: 5000
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: ppob-backend-env
|
||||
volumeMounts:
|
||||
- name: storage
|
||||
mountPath: /home/node/files
|
||||
volumes:
|
||||
- name: storage
|
||||
persistentVolumeClaim:
|
||||
claimName: ppob-backend-pvc
|
||||
imagePullSecrets:
|
||||
- name: regcred
|
||||
|
||||
|
|
13
k8s/staging/pvc.yaml
Normal file
13
k8s/staging/pvc.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
kind: PersistentVolumeClaim
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: ppob-backend-pvc
|
||||
namespace : empatnusabangsa-staging
|
||||
annotations:
|
||||
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
20
package.json
20
package.json
|
@ -10,7 +10,8 @@
|
|||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:formatted": "nest start | pino-pretty",
|
||||
"start:dev": "nest start --watch | pino-pretty",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
|
@ -24,16 +25,30 @@
|
|||
"@nestjs/common": "^8.0.0",
|
||||
"@nestjs/config": "^1.0.1",
|
||||
"@nestjs/core": "^8.0.0",
|
||||
"@nestjs/jwt": "^8.0.0",
|
||||
"@nestjs/mapped-types": "*",
|
||||
"@nestjs/passport": "^8.0.1",
|
||||
"@nestjs/platform-express": "^8.0.0",
|
||||
"@nestjs/platform-fastify": "^8.0.8",
|
||||
"@nestjs/typeorm": "^8.0.2",
|
||||
"axios": "^0.24.0",
|
||||
"bluebird": "^3.7.2",
|
||||
"class-transformer": "^0.4.0",
|
||||
"class-validator": "^0.13.1",
|
||||
"crypto": "^1.0.1",
|
||||
"csv-parser": "^3.0.0",
|
||||
"decimal.js": "^10.3.1",
|
||||
"fs": "^0.0.1-security",
|
||||
"fs-extra": "^10.0.0",
|
||||
"joi": "^17.4.2",
|
||||
"lodash": "^4.17.21",
|
||||
"nestjs-pino": "^2.3.1",
|
||||
"passport": "^0.5.0",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"pg": "^8.7.1",
|
||||
"pino-http": "^6.3.0",
|
||||
"pino-pretty": "^7.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.2.0",
|
||||
|
@ -46,7 +61,10 @@
|
|||
"@nestjs/testing": "^8.0.0",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/passport-jwt": "^3.0.6",
|
||||
"@types/passport-local": "^1.0.34",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.2",
|
||||
"@typescript-eslint/parser": "^4.28.2",
|
||||
|
|
|
@ -6,8 +6,11 @@ import { UsersModule } from './users/users.module';
|
|||
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
|
||||
import { LoggerModule } from 'nestjs-pino';
|
||||
import { TransactionModule } from './transaction/transaction.module';
|
||||
import configuration from './config/configuration';
|
||||
import { ProductModule } from './product/product.module';
|
||||
import { ConfigurableModule } from './configurable/configurable.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import configuration from './config/configuration';
|
||||
import { MulterModule } from '@nestjs/platform-express';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -19,14 +22,18 @@ import { ConfigurableModule } from './configurable/configurable.module';
|
|||
.valid('development', 'production', 'test', 'provision')
|
||||
.default('development'),
|
||||
PORT: Joi.number().default(3000),
|
||||
DATABASE_CLIENT: Joi.valid('mysql', 'postgres'),
|
||||
DATABASE_HOST: Joi.string(),
|
||||
DATABASE_NAME: Joi.string(),
|
||||
DATABASE_USERNAME: Joi.string(),
|
||||
SECRET: Joi.string().required(),
|
||||
DATABASE_CLIENT: Joi.valid('mysql', 'postgres').required(),
|
||||
DATABASE_HOST: Joi.string().required(),
|
||||
DATABASE_NAME: Joi.string().required(),
|
||||
DATABASE_USERNAME: Joi.string().required(),
|
||||
DATABASE_PASSWORD: Joi.string().empty('').default(''),
|
||||
DATABASE_PORT: Joi.number().default(5432),
|
||||
}),
|
||||
}),
|
||||
MulterModule.register({
|
||||
dest: './files',
|
||||
}),
|
||||
TypeOrmModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => {
|
||||
|
@ -49,6 +56,8 @@ import { ConfigurableModule } from './configurable/configurable.module';
|
|||
UsersModule,
|
||||
TransactionModule,
|
||||
ConfigurableModule,
|
||||
ProductModule,
|
||||
AuthModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
18
src/auth/auth.controller.spec.ts
Normal file
18
src/auth/auth.controller.spec.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AuthController } from './auth.controller';
|
||||
|
||||
describe('AuthController', () => {
|
||||
let controller: AuthController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AuthController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<AuthController>(AuthController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
24
src/auth/auth.controller.ts
Normal file
24
src/auth/auth.controller.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { Controller, Get, Post, Request, UseGuards } from '@nestjs/common';
|
||||
import { LocalAuthGuard } from './local-auth.guard';
|
||||
import { AuthService } from './auth.service';
|
||||
import { Public } from './public.decorator';
|
||||
|
||||
@Controller({
|
||||
path: 'auth',
|
||||
version: '1',
|
||||
})
|
||||
export class AuthController {
|
||||
constructor(private authService: AuthService) {}
|
||||
|
||||
@Public()
|
||||
@UseGuards(LocalAuthGuard)
|
||||
@Post('login')
|
||||
async login(@Request() req) {
|
||||
return this.authService.login(req.user);
|
||||
}
|
||||
|
||||
@Get('profile')
|
||||
getProfile(@Request() req) {
|
||||
return this.authService.getProfile(req.user.userId);
|
||||
}
|
||||
}
|
41
src/auth/auth.module.ts
Normal file
41
src/auth/auth.module.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { UsersModule } from '../users/users.module';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { LocalStrategy } from './local.strategy';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { JwtStrategy } from './jwt.strategy';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
UsersModule,
|
||||
PassportModule,
|
||||
ConfigModule,
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService) => {
|
||||
return {
|
||||
secret: configService.get<string>('secret'),
|
||||
signOptions: { expiresIn: '1d' },
|
||||
};
|
||||
},
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
AuthService,
|
||||
LocalStrategy,
|
||||
JwtStrategy,
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: JwtAuthGuard,
|
||||
},
|
||||
],
|
||||
controllers: [AuthController],
|
||||
exports: [AuthService],
|
||||
})
|
||||
export class AuthModule {}
|
18
src/auth/auth.service.spec.ts
Normal file
18
src/auth/auth.service.spec.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
describe('AuthService', () => {
|
||||
let service: AuthService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [AuthService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<AuthService>(AuthService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
42
src/auth/auth.service.ts
Normal file
42
src/auth/auth.service.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { hashPassword } from '../helper/hash_password';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { User } from '../users/entities/user.entity';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private usersService: UsersService,
|
||||
private jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
async validateUser(username: string, pass: string): Promise<any> {
|
||||
const user = await this.usersService.findOneByUsername(username);
|
||||
|
||||
if (user && user.password === (await hashPassword(pass, user.salt))) {
|
||||
const { password, ...result } = user;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async login(user: User) {
|
||||
const payload = {
|
||||
username: user.username,
|
||||
sub: user.id,
|
||||
role: user.roles.name,
|
||||
partner: user.partner?.id,
|
||||
};
|
||||
|
||||
return {
|
||||
access_token: this.jwtService.sign(payload),
|
||||
};
|
||||
}
|
||||
|
||||
getProfile = async (userId: string) => {
|
||||
return this.usersService.findOne(userId);
|
||||
};
|
||||
}
|
24
src/auth/jwt-auth.guard.ts
Normal file
24
src/auth/jwt-auth.guard.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { IS_PUBLIC_KEY } from './public.decorator';
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthGuard extends AuthGuard('jwt') {
|
||||
constructor(private reflector: Reflector) {
|
||||
super();
|
||||
}
|
||||
|
||||
canActivate(context: ExecutionContext) {
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
]);
|
||||
|
||||
if (isPublic) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.canActivate(context);
|
||||
}
|
||||
}
|
22
src/auth/jwt.strategy.ts
Normal file
22
src/auth/jwt.strategy.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(configService: ConfigService) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: configService.get<string>('secret'),
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: any) {
|
||||
return {
|
||||
userId: payload.sub,
|
||||
username: payload.username,
|
||||
};
|
||||
}
|
||||
}
|
5
src/auth/local-auth.guard.ts
Normal file
5
src/auth/local-auth.guard.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class LocalAuthGuard extends AuthGuard('local') {}
|
21
src/auth/local.strategy.ts
Normal file
21
src/auth/local.strategy.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { Strategy } from 'passport-local';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
@Injectable()
|
||||
export class LocalStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(private authService: AuthService) {
|
||||
super();
|
||||
}
|
||||
|
||||
async validate(username: string, password: string): Promise<any> {
|
||||
const user = await this.authService.validateUser(username, password);
|
||||
|
||||
if (!user) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
4
src/auth/public.decorator.ts
Normal file
4
src/auth/public.decorator.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { SetMetadata } from '@nestjs/common';
|
||||
|
||||
export const IS_PUBLIC_KEY = 'isPublic';
|
||||
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
|
|
@ -1,6 +1,7 @@
|
|||
export default () => {
|
||||
return {
|
||||
port: parseInt(process.env.PORT, 10) || 3000,
|
||||
secret: process.env.SECRET,
|
||||
database: {
|
||||
client: process.env.DATABASE_CLIENT,
|
||||
host: process.env.DATABASE_HOST,
|
||||
|
@ -9,5 +10,7 @@ export default () => {
|
|||
password: process.env.DATABASE_PASSWORD || '',
|
||||
name: process.env.DATABASE_NAME,
|
||||
},
|
||||
upload_dir: __dirname + '/../uploads',
|
||||
upload_url_path: '/files/',
|
||||
};
|
||||
};
|
||||
|
|
68
src/configurable/commission.service.ts
Normal file
68
src/configurable/commission.service.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { CommissionSetting } from './entities/commission_setting.entity';
|
||||
|
||||
@Injectable()
|
||||
export class CommissionService {
|
||||
constructor(
|
||||
@InjectRepository(CommissionSetting)
|
||||
private commissionRepository: Repository<CommissionSetting>,
|
||||
) {}
|
||||
|
||||
findAllCommission(page, pageSize?) {
|
||||
return this.commissionRepository.findAndCount({
|
||||
skip: page * (pageSize || 10),
|
||||
take: pageSize || 10,
|
||||
order: {
|
||||
version: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async findOne(role: string) {
|
||||
try {
|
||||
return await this.commissionRepository.findOneOrFail({
|
||||
where: {
|
||||
role: role,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Commission not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateCommission(id: string, request) {
|
||||
try {
|
||||
await this.commissionRepository.findOneOrFail(id);
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Commission not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.commissionRepository.update(id, {
|
||||
commission: request.value,
|
||||
});
|
||||
|
||||
return this.commissionRepository.findOneOrFail(id);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigurableController } from './configurable.controller';
|
||||
import { ConfigurableService } from './configurable.service';
|
||||
import { RoleService } from './roles.service';
|
||||
|
||||
describe('ConfigurableController', () => {
|
||||
let controller: ConfigurableController;
|
||||
|
@ -8,7 +8,7 @@ describe('ConfigurableController', () => {
|
|||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [ConfigurableController],
|
||||
providers: [ConfigurableService],
|
||||
providers: [RoleService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<ConfigurableController>(ConfigurableController);
|
||||
|
|
|
@ -1,26 +1,40 @@
|
|||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Put,
|
||||
Param,
|
||||
Delete,
|
||||
ParseUUIDPipe,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseUUIDPipe,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
Res,
|
||||
UploadedFile,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { ConfigurableService } from './configurable.service';
|
||||
import { RoleService } from './roles.service';
|
||||
import { CommissionService } from './commission.service';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { diskStorage } from 'multer';
|
||||
import { editFileName } from '../helper/file-handler';
|
||||
import { Public } from 'src/auth/public.decorator';
|
||||
|
||||
@Controller({
|
||||
path: 'config',
|
||||
version: '1',
|
||||
})
|
||||
export class ConfigurableController {
|
||||
constructor(private readonly usersService: ConfigurableService) {}
|
||||
constructor(
|
||||
private readonly roleService: RoleService,
|
||||
private readonly commissionService: CommissionService,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
async findAll() {
|
||||
const [data, count] = await this.usersService.findAll();
|
||||
@Get('/roles')
|
||||
async findAll(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
) {
|
||||
const [data, count] = await this.roleService.findAllRoles(page, pageSize);
|
||||
|
||||
return {
|
||||
data,
|
||||
|
@ -30,13 +44,84 @@ export class ConfigurableController {
|
|||
};
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id', ParseUUIDPipe) id: string) {
|
||||
@Get('/commission')
|
||||
async findCommission(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
) {
|
||||
const [data, count] = await this.commissionService.findAllCommission(
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
data: await this.usersService.findOne(id),
|
||||
data,
|
||||
count,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('/roles/for-membership')
|
||||
async findAllForMembership(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
) {
|
||||
const [data, count] = await this.roleService.findAllRolesForCreateMember(
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
data,
|
||||
count,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get('/image/:imgpath')
|
||||
seeUploadedFile(@Param('imgpath') image, @Res() res) {
|
||||
return res.sendFile(image, { root: './files' });
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id', ParseUUIDPipe) id: string) {
|
||||
return {
|
||||
data: await this.roleService.findOne(id),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Post('/upload-files')
|
||||
@UseInterceptors(
|
||||
FileInterceptor('file', {
|
||||
storage: diskStorage({
|
||||
destination: './files',
|
||||
filename: editFileName,
|
||||
}),
|
||||
}),
|
||||
)
|
||||
async uploadedFile(@UploadedFile() file: Express.Multer.File) {
|
||||
const response = {
|
||||
originalname: file,
|
||||
filename: file.filename,
|
||||
};
|
||||
return response;
|
||||
}
|
||||
|
||||
@Put('/commission/:id')
|
||||
async updateCommission(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Body() request,
|
||||
) {
|
||||
return {
|
||||
data: await this.commissionService.updateCommission(id, request),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,14 @@ import { Module } from '@nestjs/common';
|
|||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Roles } from './entities/roles.entity';
|
||||
import { ConfigurableController } from './configurable.controller';
|
||||
import { ConfigurableService } from './configurable.service';
|
||||
import { RoleService } from './roles.service';
|
||||
import { CommissionService } from './commission.service';
|
||||
import { CommissionSetting } from './entities/commission_setting.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Roles])],
|
||||
imports: [TypeOrmModule.forFeature([Roles, CommissionSetting])],
|
||||
controllers: [ConfigurableController],
|
||||
providers: [ConfigurableService],
|
||||
providers: [RoleService, CommissionService],
|
||||
exports: [RoleService, CommissionService],
|
||||
})
|
||||
export class ConfigurableModule {}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigurableService } from './configurable.service';
|
||||
import { RoleService } from './roles.service';
|
||||
|
||||
describe('ConfigurableService', () => {
|
||||
let service: ConfigurableService;
|
||||
describe('RoleService', () => {
|
||||
let service: RoleService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [ConfigurableService],
|
||||
providers: [RoleService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ConfigurableService>(ConfigurableService);
|
||||
service = module.get<RoleService>(RoleService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { Roles } from './entities/roles.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class ConfigurableService {
|
||||
constructor(
|
||||
@InjectRepository(Roles)
|
||||
private rolesRepository: Repository<Roles>,
|
||||
) {}
|
||||
|
||||
findAll() {
|
||||
return this.rolesRepository.findAndCount();
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
try {
|
||||
return await this.rolesRepository.findOneOrFail(id);
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
src/configurable/entities/commission_setting.entity.ts
Normal file
16
src/configurable/entities/commission_setting.entity.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { Entity, Column, OneToOne, JoinColumn } from 'typeorm';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
import { Roles } from './roles.entity';
|
||||
|
||||
@Entity()
|
||||
export class CommissionSetting extends BaseModel {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column()
|
||||
commission: number;
|
||||
|
||||
@OneToOne(() => Roles)
|
||||
@JoinColumn()
|
||||
role: Roles;
|
||||
}
|
58
src/configurable/roles.service.ts
Normal file
58
src/configurable/roles.service.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { EntityNotFoundError, In, Not, Repository } from 'typeorm';
|
||||
import { Roles } from './entities/roles.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class RoleService {
|
||||
constructor(
|
||||
@InjectRepository(Roles)
|
||||
private rolesRepository: Repository<Roles>,
|
||||
) {}
|
||||
|
||||
findAllRoles(page, pageSize?) {
|
||||
return this.rolesRepository.findAndCount({
|
||||
skip: page * (pageSize || 10),
|
||||
take: pageSize || 10,
|
||||
order: {
|
||||
version: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
findAllRolesForCreateMember(page, pageSize?) {
|
||||
return this.rolesRepository.findAndCount({
|
||||
skip: page * (pageSize || 10),
|
||||
take: pageSize || 10,
|
||||
where: {
|
||||
id: Not(
|
||||
In([
|
||||
'3196cdf4-ae5f-4677-9bcd-98be35c72321',
|
||||
'21dceea2-416e-4b55-b74c-12605e1f8d1b',
|
||||
]),
|
||||
),
|
||||
},
|
||||
order: {
|
||||
version: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
try {
|
||||
return await this.rolesRepository.findOneOrFail(id);
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data Role not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
src/helper/csv-parser.ts
Normal file
25
src/helper/csv-parser.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { createReadStream } from 'fs';
|
||||
import * as csvParser from 'csv-parser';
|
||||
import * as path from 'path';
|
||||
|
||||
export async function parsingFile(dataFile) {
|
||||
const parsingData: any = await new Promise((resolve, reject) => {
|
||||
const results = [];
|
||||
|
||||
const file = createReadStream(
|
||||
path.join(process.cwd(), `./files/${dataFile}`),
|
||||
);
|
||||
|
||||
file
|
||||
.pipe(
|
||||
csvParser({
|
||||
headers: false,
|
||||
}),
|
||||
)
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', () => {
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
return parsingData;
|
||||
}
|
46
src/helper/enum-list.ts
Normal file
46
src/helper/enum-list.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
export enum statusTransaction {
|
||||
PENDING,
|
||||
SUCCESS,
|
||||
FAILED,
|
||||
APPROVED,
|
||||
REJECTED,
|
||||
}
|
||||
|
||||
export enum typeTransaction {
|
||||
DISTRIBUTION,
|
||||
ORDER,
|
||||
DEPOSIT_SUPPLIER,
|
||||
DEPOSIT_RETURN,
|
||||
WITHDRAW,
|
||||
}
|
||||
|
||||
export enum productType {
|
||||
NORMAL,
|
||||
PROMO,
|
||||
}
|
||||
|
||||
export enum coaType {
|
||||
WALLET,
|
||||
INCOME,
|
||||
INVENTORY,
|
||||
COST_OF_SALES,
|
||||
SALES,
|
||||
BANK,
|
||||
EXPENSE,
|
||||
ACCOUNT_RECEIVABLE,
|
||||
ACCOUNT_PAYABLE,
|
||||
BUDGET,
|
||||
CONTRA_BUDGET,
|
||||
PROFIT,
|
||||
WITHDRAW,
|
||||
}
|
||||
|
||||
export enum balanceType {
|
||||
DEBIT,
|
||||
CREDIT,
|
||||
}
|
||||
|
||||
export enum accountType {
|
||||
PARTNER,
|
||||
CUSTOMER,
|
||||
}
|
12
src/helper/file-handler.ts
Normal file
12
src/helper/file-handler.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import * as path from 'path';
|
||||
|
||||
export const editFileName = (req, file, callback) => {
|
||||
const name = file.originalname.split('.')[0];
|
||||
const fileExtName = path.extname(file.originalname);
|
||||
// const fileExtName = 'asdasd';
|
||||
const randomName = Array(4)
|
||||
.fill(null)
|
||||
.map(() => Math.round(Math.random() * 16).toString(16))
|
||||
.join('');
|
||||
callback(null, `${name}-${randomName}${fileExtName}`);
|
||||
};
|
13
src/helper/hash_password.ts
Normal file
13
src/helper/hash_password.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import * as crypto from 'crypto';
|
||||
|
||||
export function hashPassword(password, salt): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
crypto.pbkdf2(password, salt, 50, 100, 'sha512', (err, values) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(values.toString('hex'));
|
||||
});
|
||||
});
|
||||
}
|
19
src/helper/irs-api.ts
Normal file
19
src/helper/irs-api.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import axios from 'axios';
|
||||
|
||||
const irs_url = 'http://h2h.elangpangarep.com/api/h2h';
|
||||
const irs_id = 'PT0005';
|
||||
const irs_pin = '04JFGC';
|
||||
const irs_user = 'D10BD0';
|
||||
const irs_pass = '6251F3';
|
||||
|
||||
export const doTransaction = async (productCode, destination, idtrx) => {
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`${irs_url}?id=${irs_id}&pin=${irs_pin}&user=${irs_user}&pass=${irs_pass}&kodeproduk=${productCode}&tujuan=${destination}&counter=1&idtrx=${idtrx}`,
|
||||
);
|
||||
|
||||
return res.data;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
25
src/helper/irs-service.ts
Normal file
25
src/helper/irs-service.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import * as axios from 'axios';
|
||||
|
||||
export const createTransaction = async (kode, tujuan) => {
|
||||
const codeTransaksi = generateRequestId();
|
||||
|
||||
return axios.default
|
||||
.get(
|
||||
`http://h2h.elangpangarep.com/api/h2h?id=PT0005&pin=04JFGC&user=D10BD0&pass=6251F3&kodeproduk=${kode}&tujuan=${tujuan}&counter=1&idtrx=${codeTransaksi}`,
|
||||
)
|
||||
.then((response) => {
|
||||
return response.data;
|
||||
});
|
||||
};
|
||||
|
||||
export const generateRequestId = () => {
|
||||
return `${new Date()
|
||||
.toLocaleString('en-us', {
|
||||
year: '2-digit',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
})
|
||||
.replace(/(\d+)\/(\d+)\/(\d+)/, '$3$1$2')}${Math.random()
|
||||
.toPrecision(3)
|
||||
.replace('0.', '')}`;
|
||||
};
|
22
src/helper/jwt.strategy.ts
Normal file
22
src/helper/jwt.strategy.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
// import { PassportStrategy } from '@nestjs/passport';
|
||||
// import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
// import { Injectable } from '@nestjs/common';
|
||||
// import { AuthService } from '../auth/auth.service';
|
||||
|
||||
// @Injectable()
|
||||
// export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
// constructor(private readonly authService: AuthService) {
|
||||
// super({
|
||||
// jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
// secretOrKey: process.env.SECRETKEY,
|
||||
// });
|
||||
// }
|
||||
|
||||
// async validate(payload: JwtPayload): Promise<UserDto> {
|
||||
// const user = await this.authService.validateUser(payload);
|
||||
// if (!user) {
|
||||
// throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
|
||||
// }
|
||||
// return user;
|
||||
// }
|
||||
// }
|
|
@ -1,44 +0,0 @@
|
|||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn, ManyToOne, ManyToMany, JoinTable,
|
||||
} from 'typeorm';
|
||||
import { Product } from '../../product/entities/product.entity';
|
||||
import { User } from '../../users/entities/user.entity';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
|
||||
enum type {
|
||||
SYSTEM_BANk,
|
||||
INCOME,
|
||||
}
|
||||
|
||||
enum balanceType {
|
||||
DEBIT,
|
||||
CREDIT,
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class Roles extends BaseModel{
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column('text')
|
||||
type: type;
|
||||
|
||||
@Column('text')
|
||||
balanceType: balanceType;
|
||||
|
||||
@Column()
|
||||
amount: number;
|
||||
|
||||
@ManyToMany(() => User)
|
||||
@JoinTable()
|
||||
user: User[];
|
||||
}
|
24
src/main.ts
24
src/main.ts
|
@ -1,17 +1,14 @@
|
|||
import { NestFactory } from '@nestjs/core';
|
||||
import {
|
||||
FastifyAdapter,
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { AppModule } from './app.module';
|
||||
import { ValidationPipe, VersioningType } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Logger } from 'nestjs-pino';
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create<NestFastifyApplication>(
|
||||
const app = await NestFactory.create<NestExpressApplication>(
|
||||
AppModule,
|
||||
new FastifyAdapter(),
|
||||
// new FastifyAdapter(),
|
||||
{ bufferLogs: true },
|
||||
);
|
||||
|
||||
|
@ -31,13 +28,14 @@ async function bootstrap() {
|
|||
const configService = app.get<ConfigService>(ConfigService);
|
||||
const port = configService.get<number>('port');
|
||||
|
||||
await app.listen(port, '0.0.0.0', (error, address) => {
|
||||
if (error) {
|
||||
logger.error(error);
|
||||
process.exit(1);
|
||||
} else {
|
||||
logger.log(`Server listening on ${address}`);
|
||||
}
|
||||
await app.listen(port, '0.0.0.0', () => {
|
||||
logger.log('Service Started');
|
||||
// if (error) {
|
||||
// logger.error(error);
|
||||
// process.exit(1);
|
||||
// } else {
|
||||
// logger.log(`Server listening on ${address}`);
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -3,4 +3,7 @@ import { IsNotEmpty, IsUUID } from 'class-validator';
|
|||
export class CreateCategoriesProductDto {
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
code: string;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,15 @@ export class CreateProductDto {
|
|||
@IsNotEmpty()
|
||||
status: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
price: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
markUpPrice: number;
|
||||
|
||||
@IsUUID()
|
||||
subCategoriesId: string;
|
||||
|
||||
@IsUUID()
|
||||
supplierId: string;
|
||||
}
|
17
src/product/dto/product/update-price-product.dto.ts
Normal file
17
src/product/dto/product/update-price-product.dto.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { IsNotEmpty } from 'class-validator';
|
||||
import { productType } from '../../../helper/enum-list';
|
||||
|
||||
export class UpdatePriceProductDto {
|
||||
@IsNotEmpty()
|
||||
price: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
markUpPrice: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
type: productType;
|
||||
|
||||
startDate: Date;
|
||||
|
||||
endDate: Date;
|
||||
}
|
6
src/product/dto/product/update-product.dto.ts
Normal file
6
src/product/dto/product/update-product.dto.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { OmitType, PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateProductDto } from './create-product.dto';
|
||||
|
||||
export class UpdateProductDto extends PartialType(
|
||||
OmitType(CreateProductDto, ['price'] as const),
|
||||
) {}
|
9
src/product/dto/product/upload-product.dto.ts
Normal file
9
src/product/dto/product/upload-product.dto.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
|
||||
export class UploadProductDto {
|
||||
@IsNotEmpty()
|
||||
fileName: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
supplierCode: string;
|
||||
}
|
|
@ -2,6 +2,12 @@ import { IsNotEmpty, IsUUID } from 'class-validator';
|
|||
import { CreateCategoriesProductDto } from '../categories/create-categories-product.dto';
|
||||
|
||||
export class CreateSubCategoriesProductDto extends CreateCategoriesProductDto {
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
code: string;
|
||||
|
||||
@IsUUID()
|
||||
categoryId: string;
|
||||
}
|
||||
|
|
|
@ -19,9 +19,14 @@ export class ProductCategories extends BaseModel {
|
|||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
code: string;
|
||||
|
||||
@OneToMany(
|
||||
() => ProductSubCategories,
|
||||
(subCategories) => subCategories.category,
|
||||
)
|
||||
subCategories: ProductSubCategories;
|
||||
sub_categories: ProductSubCategories;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,9 @@
|
|||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn,
|
||||
OneToMany,
|
||||
ManyToOne,
|
||||
} from 'typeorm';
|
||||
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
|
||||
import { Product } from './product.entity';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
|
||||
enum Type {
|
||||
NORMAL,
|
||||
PROMO,
|
||||
}
|
||||
import { productType } from '../../helper/enum-list';
|
||||
import { User } from '../../users/entities/user.entity';
|
||||
import { Partner } from '../../users/entities/partner.entity';
|
||||
|
||||
@Entity()
|
||||
export class ProductHistoryPrice extends BaseModel {
|
||||
|
@ -23,14 +11,26 @@ export class ProductHistoryPrice extends BaseModel {
|
|||
id: string;
|
||||
|
||||
@ManyToOne(() => Product, (product) => product.id)
|
||||
productId: string;
|
||||
product: Product;
|
||||
|
||||
@ManyToOne(() => Partner, (partner) => partner.id)
|
||||
partner: Partner;
|
||||
|
||||
@Column()
|
||||
price: number;
|
||||
|
||||
@Column()
|
||||
mark_up_price: number;
|
||||
|
||||
@Column({ type: 'date' })
|
||||
startDate: string;
|
||||
startDate: Date;
|
||||
|
||||
@Column({ type: 'date' })
|
||||
endDate: string;
|
||||
@Column({
|
||||
type: 'date',
|
||||
nullable: true,
|
||||
})
|
||||
endDate: Date;
|
||||
|
||||
@Column('text')
|
||||
type: Type;
|
||||
type: productType;
|
||||
}
|
||||
|
|
|
@ -2,14 +2,12 @@ import {
|
|||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
} from 'typeorm';
|
||||
import { ProductCategories } from './product-category.entity';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
import { Product } from './product.entity';
|
||||
|
||||
@Entity()
|
||||
export class ProductSubCategories extends BaseModel {
|
||||
|
@ -19,6 +17,14 @@ export class ProductSubCategories extends BaseModel{
|
|||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToOne(() => ProductCategories, (categories) => categories.subCategories)
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
code: string;
|
||||
|
||||
@ManyToOne(() => ProductCategories, (categories) => categories.sub_categories)
|
||||
category: ProductCategories;
|
||||
|
||||
@OneToMany(() => Product, (product) => product.sub_categories)
|
||||
product: Product;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import {
|
|||
} from 'typeorm';
|
||||
import { ProductSubCategories } from './product-sub-category.entity';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
import { Supplier } from '../../users/entities/supplier.entity';
|
||||
import { ProductHistoryPrice } from './product-history-price.entity';
|
||||
|
||||
@Entity()
|
||||
export class Product extends BaseModel {
|
||||
|
@ -26,9 +28,45 @@ export class Product extends BaseModel{
|
|||
@Column()
|
||||
status: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
price: number;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
basePrice: number;
|
||||
|
||||
@ManyToOne(
|
||||
() => ProductSubCategories,
|
||||
(subCategories) => subCategories.category,
|
||||
() => {
|
||||
return ProductSubCategories;
|
||||
},
|
||||
(subCategories) => {
|
||||
return subCategories.product;
|
||||
},
|
||||
)
|
||||
subCategories: ProductSubCategories;
|
||||
sub_categories: ProductSubCategories;
|
||||
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return Supplier;
|
||||
},
|
||||
(partner) => {
|
||||
return partner.id;
|
||||
},
|
||||
)
|
||||
supplier: Supplier;
|
||||
|
||||
@OneToMany(
|
||||
() => {
|
||||
return ProductHistoryPrice;
|
||||
},
|
||||
(php) => {
|
||||
return php.product;
|
||||
},
|
||||
)
|
||||
priceHistory: ProductHistoryPrice;
|
||||
|
||||
currentPrice: ProductHistoryPrice;
|
||||
}
|
||||
|
|
18
src/product/history-price/history-price.service.spec.ts
Normal file
18
src/product/history-price/history-price.service.spec.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { HistoryPriceService } from './history-price.service';
|
||||
|
||||
describe('HistoryPriceService', () => {
|
||||
let service: HistoryPriceService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [HistoryPriceService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<HistoryPriceService>(HistoryPriceService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
106
src/product/history-price/history-price.service.ts
Normal file
106
src/product/history-price/history-price.service.ts
Normal file
|
@ -0,0 +1,106 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { EntityNotFoundError, IsNull, Repository } from 'typeorm';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { ProductHistoryPrice } from '../entities/product-history-price.entity';
|
||||
|
||||
@Injectable()
|
||||
export class ProductHistoryPriceService {
|
||||
constructor(
|
||||
@InjectRepository(ProductHistoryPrice)
|
||||
private productHistoryPriceService: Repository<ProductHistoryPrice>,
|
||||
) {}
|
||||
|
||||
async create(dataProduct: ProductHistoryPrice) {
|
||||
const result = await this.productHistoryPriceService.save(dataProduct);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async findOne(product: string, partner: string) {
|
||||
try {
|
||||
return await this.productHistoryPriceService.findOneOrFail({
|
||||
where: {
|
||||
product: product,
|
||||
endDate: IsNull(),
|
||||
partner: partner ? partner : IsNull(),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Price not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findById(id: string) {
|
||||
try {
|
||||
return await this.productHistoryPriceService.findOneOrFail(id);
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Price not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findOneByProductId(
|
||||
page: number,
|
||||
productId: string,
|
||||
supplierId: string,
|
||||
pageSize?: number,
|
||||
) {
|
||||
try {
|
||||
const query = this.productHistoryPriceService
|
||||
.createQueryBuilder('product_history_price')
|
||||
.leftJoin('product_history_price.product', 'product')
|
||||
.where({ product: productId })
|
||||
.andWhere('product_history_price.endDate IS NULL');
|
||||
|
||||
if (supplierId !== 'null' && supplierId) {
|
||||
query.andWhere('product.supplier = :supplierId', {
|
||||
supplierId: supplierId,
|
||||
});
|
||||
}
|
||||
|
||||
const data = await query
|
||||
.orderBy('product_history_price.createdAt', 'DESC')
|
||||
.skip(page * (pageSize || 10))
|
||||
.take(pageSize || 10)
|
||||
.getMany();
|
||||
|
||||
const totalData = await query.getCount();
|
||||
|
||||
return {
|
||||
data,
|
||||
count: totalData,
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Product History Price not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,20 @@ export class ProductCategoriesService {
|
|||
) {}
|
||||
|
||||
async create(CreateCategoriesProductDto: CreateCategoriesProductDto) {
|
||||
const check = await this.productCategoriesRepository.findOne({
|
||||
code: CreateCategoriesProductDto.code,
|
||||
});
|
||||
|
||||
if (check) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_ACCEPTABLE,
|
||||
error: 'Category Already Exist',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const result = await this.productCategoriesRepository.insert(
|
||||
CreateCategoriesProductDto,
|
||||
);
|
||||
|
@ -22,10 +36,10 @@ export class ProductCategoriesService {
|
|||
);
|
||||
}
|
||||
|
||||
findAll(page) {
|
||||
findAll(page, pageSize?) {
|
||||
return this.productCategoriesRepository.findAndCount({
|
||||
skip: page * 10,
|
||||
take: 10,
|
||||
skip: page * (pageSize || 10),
|
||||
take: pageSize || 10,
|
||||
order: {
|
||||
version: 'DESC',
|
||||
},
|
||||
|
@ -40,7 +54,29 @@ export class ProductCategoriesService {
|
|||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
error: 'Product Categories not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findByCode(code: string) {
|
||||
try {
|
||||
return await this.productCategoriesRepository.findOneOrFail({
|
||||
where: {
|
||||
code: code,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Product Categories not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
|
@ -61,7 +97,7 @@ export class ProductCategoriesService {
|
|||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
error: 'Product Categories not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
|
@ -86,7 +122,7 @@ export class ProductCategoriesService {
|
|||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
error: 'Product Categories not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
|
|
|
@ -1,31 +1,86 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { EntityNotFoundError, In, Repository } from 'typeorm';
|
||||
import { ProductSubCategories } from './entities/product-sub-category.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { CreateSubCategoriesProductDto } from './dto/sub-categories/create-sub-categories-product.dto';
|
||||
import { UpdateSubCategoriesProductDto } from './dto/sub-categories/update-sub-categories-product.dto';
|
||||
import { ProductCategoriesService } from './product-categories.service';
|
||||
|
||||
@Injectable()
|
||||
export class ProductSubCategoriesService {
|
||||
constructor(
|
||||
@InjectRepository(ProductSubCategories)
|
||||
private productSubCategoriesRepository: Repository<ProductSubCategories>,
|
||||
private productCategoriesService: ProductCategoriesService,
|
||||
) {}
|
||||
|
||||
async create(CreateCategoriesProductDto: CreateSubCategoriesProductDto) {
|
||||
const result = await this.productSubCategoriesRepository.insert(
|
||||
CreateCategoriesProductDto,
|
||||
async create(createSubCategoriesProductDto: CreateSubCategoriesProductDto) {
|
||||
const check = await this.productSubCategoriesRepository.findOne({
|
||||
code: createSubCategoriesProductDto.code,
|
||||
});
|
||||
|
||||
if (check) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_ACCEPTABLE,
|
||||
error: 'Sub Category Already Exist',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const categories = await this.productCategoriesService.findOne(
|
||||
createSubCategoriesProductDto.categoryId,
|
||||
);
|
||||
|
||||
const result = await this.productSubCategoriesRepository.insert({
|
||||
name: createSubCategoriesProductDto.name,
|
||||
code: createSubCategoriesProductDto.code,
|
||||
category: categories,
|
||||
});
|
||||
|
||||
return this.productSubCategoriesRepository.findOneOrFail(
|
||||
result.identifiers[0].id,
|
||||
);
|
||||
}
|
||||
|
||||
findAll(page) {
|
||||
async findAll(page, category: string, pageSize?) {
|
||||
let filterCategories;
|
||||
if (category) {
|
||||
filterCategories = category.split(',').map((data) => data.trim());
|
||||
}
|
||||
|
||||
const baseQuery = this.productSubCategoriesRepository
|
||||
.createQueryBuilder('product_sub_categories')
|
||||
.leftJoinAndSelect('product_sub_categories.category', 'category');
|
||||
|
||||
if (category && filterCategories.length > 0) {
|
||||
baseQuery.where({
|
||||
category: In(filterCategories),
|
||||
});
|
||||
}
|
||||
|
||||
const data = await baseQuery
|
||||
.skip(page * (pageSize || 10))
|
||||
.take(pageSize || 10)
|
||||
.getMany();
|
||||
|
||||
const totalData = await baseQuery.getCount();
|
||||
|
||||
return {
|
||||
data,
|
||||
count: totalData,
|
||||
};
|
||||
}
|
||||
|
||||
findAllByCategories(page, category) {
|
||||
return this.productSubCategoriesRepository.findAndCount({
|
||||
skip: page * 10,
|
||||
take: 10,
|
||||
where: {
|
||||
category: category,
|
||||
},
|
||||
relations: ['category'],
|
||||
order: {
|
||||
version: 'DESC',
|
||||
},
|
||||
|
@ -40,7 +95,7 @@ export class ProductSubCategoriesService {
|
|||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
error: 'Product Sub Categories not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
|
@ -50,6 +105,14 @@ export class ProductSubCategoriesService {
|
|||
}
|
||||
}
|
||||
|
||||
async findOneForCSVParser(code: string) {
|
||||
return await this.productSubCategoriesRepository.findOne({
|
||||
where: {
|
||||
code: code,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
updateCategoriesProductDto: UpdateSubCategoriesProductDto,
|
||||
|
@ -61,7 +124,7 @@ export class ProductSubCategoriesService {
|
|||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
error: 'Product Sub Categories not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
|
@ -70,11 +133,15 @@ export class ProductSubCategoriesService {
|
|||
}
|
||||
}
|
||||
|
||||
const result = await this.productSubCategoriesRepository.update(
|
||||
id,
|
||||
updateCategoriesProductDto,
|
||||
const categories = await this.productCategoriesService.findOne(
|
||||
updateCategoriesProductDto.categoryId,
|
||||
);
|
||||
|
||||
const result = await this.productSubCategoriesRepository.update(id, {
|
||||
name: updateCategoriesProductDto.name,
|
||||
category: categories,
|
||||
});
|
||||
|
||||
return this.productSubCategoriesRepository.findOneOrFail(id);
|
||||
}
|
||||
|
||||
|
@ -86,7 +153,7 @@ export class ProductSubCategoriesService {
|
|||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
error: 'Product Sub Categories not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
|
|
|
@ -1,17 +1,27 @@
|
|||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Put,
|
||||
Param,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseUUIDPipe,
|
||||
HttpStatus, Query,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
Request,
|
||||
} from '@nestjs/common';
|
||||
import { ProductService } from './product.service';
|
||||
import { ProductCategoriesService } from './product-categories.service';
|
||||
import { CreateCategoriesProductDto } from './dto/categories/create-categories-product.dto';
|
||||
import { UpdateCategoriesProductDto } from '../product/dto/categories/update-categories-product.dto';
|
||||
import { UpdateSubCategoriesProductDto } from '../product/dto/sub-categories/update-sub-categories-product.dto';
|
||||
import { ProductSubCategoriesService } from './product-sub-categories.service';
|
||||
import { CreateSubCategoriesProductDto } from './dto/sub-categories/create-sub-categories-product.dto';
|
||||
import { CreateProductDto } from './dto/product/create-product.dto';
|
||||
import { UpdateProductDto } from './dto/product/update-product.dto';
|
||||
import { ProductHistoryPriceService } from './history-price/history-price.service';
|
||||
import { UploadProductDto } from './dto/product/upload-product.dto';
|
||||
|
||||
@Controller({
|
||||
path: 'product',
|
||||
|
@ -21,8 +31,19 @@ export class ProductController {
|
|||
constructor(
|
||||
private readonly productService: ProductService,
|
||||
private readonly productCategoriesService: ProductCategoriesService,
|
||||
private readonly productSubCategoriesService: ProductSubCategoriesService,
|
||||
private readonly productHistoryPriceService: ProductHistoryPriceService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
async create(@Body() createProductDto: CreateProductDto) {
|
||||
return {
|
||||
data: await this.productService.create(createProductDto),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('categories')
|
||||
async createCategories(
|
||||
@Body() createCategoriesProductDto: CreateCategoriesProductDto,
|
||||
|
@ -36,9 +57,116 @@ export class ProductController {
|
|||
};
|
||||
}
|
||||
|
||||
@Get()
|
||||
async findAll(@Query('page') page: number) {
|
||||
const [data, count] = await this.productService.findAll(page);
|
||||
@Post('sub-categories')
|
||||
async createSubCategories(
|
||||
@Body() createSubCategoriesProductDto: CreateSubCategoriesProductDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.productSubCategoriesService.create(
|
||||
createSubCategoriesProductDto,
|
||||
),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('upload-product')
|
||||
async createProductBaseOnCSV(@Body() uploadProductDto: UploadProductDto) {
|
||||
await this.productService.processUploadCSV(
|
||||
uploadProductDto.fileName,
|
||||
uploadProductDto.supplierCode,
|
||||
);
|
||||
|
||||
return {
|
||||
data: 'Done',
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('all')
|
||||
async findAll(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Query('sub-category') subcategory: string,
|
||||
@Query('supplier') supplier: string,
|
||||
) {
|
||||
const data = await this.productService.findAll(
|
||||
page,
|
||||
supplier == 'null' ? null : supplier,
|
||||
subcategory == 'null' ? null : subcategory,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('test')
|
||||
async test(@Request() req) {
|
||||
const data = await this.productService.processUploadCSV('','');
|
||||
|
||||
return {
|
||||
data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('by-categories-all')
|
||||
async findByCategoriesAll(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Query('sub-category') subcategory: string,
|
||||
@Query('supplier') supplier: string,
|
||||
) {
|
||||
const data = await this.productService.findAllBySubCategories(
|
||||
page,
|
||||
subcategory,
|
||||
supplier,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('by-categories')
|
||||
async findByCategories(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Query('sub-category') subcategory: string,
|
||||
@Request() req,
|
||||
) {
|
||||
const data = await this.productService.findAllForPartner(
|
||||
page,
|
||||
pageSize,
|
||||
subcategory,
|
||||
req.user.username,
|
||||
);
|
||||
|
||||
return {
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('categories')
|
||||
async findAllCategories(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
) {
|
||||
const [data, count] = await this.productCategoriesService.findAll(
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
data,
|
||||
|
@ -48,13 +176,121 @@ export class ProductController {
|
|||
};
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id', ParseUUIDPipe) id: string) {
|
||||
@Get('sub-categories')
|
||||
async findAllSubCategories(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Query('category') category: string,
|
||||
) {
|
||||
const data = await this.productSubCategoriesService.findAll(
|
||||
page,
|
||||
category == 'null' ? null : category,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
data: await this.productService.findOne(id),
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id', ParseUUIDPipe) id: string) {
|
||||
return {
|
||||
data: await this.productService.findOneById(id),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('price-history/:id')
|
||||
async findPriceHistoryByProductId(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Query('supplier') supplier: string,
|
||||
) {
|
||||
const data = await this.productHistoryPriceService.findOneByProductId(
|
||||
page,
|
||||
id,
|
||||
supplier,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
async update(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Body() updateProductDto: UpdateProductDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.productService.update(id, updateProductDto),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put('categories/:id')
|
||||
async updateCategories(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Body() updateCategoriesDto: UpdateCategoriesProductDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.productCategoriesService.update(id, updateCategoriesDto),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put('sub-categories/:id')
|
||||
async updateSubCategories(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Body() updateSubCategoriesDto: UpdateSubCategoriesProductDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.productSubCategoriesService.update(
|
||||
id,
|
||||
updateSubCategoriesDto,
|
||||
),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
async remove(@Param('id', ParseUUIDPipe) id: string) {
|
||||
await this.productService.remove(id);
|
||||
|
||||
return {
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Delete('categories/:id')
|
||||
async removeCategories(@Param('id', ParseUUIDPipe) id: string) {
|
||||
await this.productCategoriesService.remove(id);
|
||||
|
||||
return {
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Delete('sub-categories/:id')
|
||||
async removeSubCategories(@Param('id', ParseUUIDPipe) id: string) {
|
||||
await this.productSubCategoriesService.remove(id);
|
||||
|
||||
return {
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,33 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { ProductService } from './product.service';
|
||||
import { ProductController } from './product.controller';
|
||||
import { ProductCategoriesService } from './product-categories.service';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Product } from './entities/product.entity';
|
||||
import { ProductCategories } from './entities/product-category.entity';
|
||||
import { ProductHistoryPrice } from './entities/product-history-price.entity';
|
||||
import { ProductSubCategories } from './entities/product-sub-category.entity';
|
||||
import { ProductSubCategoriesService } from './product-sub-categories.service';
|
||||
import { UsersModule } from '../users/users.module';
|
||||
import { ProductHistoryPriceService } from './history-price/history-price.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
Product,
|
||||
ProductCategories,
|
||||
ProductHistoryPrice,
|
||||
ProductSubCategories,
|
||||
]),
|
||||
forwardRef(() => UsersModule),
|
||||
],
|
||||
controllers: [ProductController],
|
||||
providers: [ProductService, ProductCategoriesService],
|
||||
providers: [
|
||||
ProductService,
|
||||
ProductCategoriesService,
|
||||
ProductSubCategoriesService,
|
||||
ProductHistoryPriceService,
|
||||
],
|
||||
exports: [ProductService, ProductHistoryPriceService],
|
||||
})
|
||||
export class ProductModule {}
|
||||
|
|
|
@ -1,45 +1,363 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { Product } from './entities/product.entity';
|
||||
import { ProductCategories } from './entities/product-category.entity';
|
||||
import { ProductSubCategories } from './entities/product-sub-category.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { CreateProductDto } from '../product/dto/create-product.dto';
|
||||
import { CreateCategoriesProductDto } from './dto/categories/create-categories-product.dto';
|
||||
import { CreateSubCategoriesProductDto } from './dto/sub-categories/create-sub-categories-product.dto';
|
||||
import { CreateProductDto } from './dto/product/create-product.dto';
|
||||
import { ProductSubCategoriesService } from './product-sub-categories.service';
|
||||
import { UpdateProductDto } from './dto/product/update-product.dto';
|
||||
import { ProductHistoryPrice } from './entities/product-history-price.entity';
|
||||
import { productType } from '../helper/enum-list';
|
||||
import { UpdatePriceProductDto } from './dto/product/update-price-product.dto';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { SupplierService } from '../users/supplier/supplier.service';
|
||||
import { parsingFile } from '../helper/csv-parser';
|
||||
import { PartnerService } from '../users/partner/partner.service';
|
||||
import { mapSeries } from 'bluebird';
|
||||
import { isNull } from 'util';
|
||||
|
||||
@Injectable()
|
||||
export class ProductService {
|
||||
constructor(
|
||||
@InjectRepository(Product)
|
||||
private productRepository: Repository<Product>,
|
||||
@InjectRepository(ProductHistoryPrice)
|
||||
private productHistoryPrice: Repository<ProductHistoryPrice>,
|
||||
private productSubCategoriesService: ProductSubCategoriesService,
|
||||
private usersService: UsersService,
|
||||
private supplierService: SupplierService,
|
||||
private partnerService: PartnerService,
|
||||
) {}
|
||||
|
||||
async create(createProductDto: CreateProductDto) {
|
||||
const result = await this.productRepository.insert(createProductDto);
|
||||
const subCategories = await this.productSubCategoriesService.findOne(
|
||||
createProductDto.subCategoriesId,
|
||||
);
|
||||
|
||||
const result = await this.productRepository.insert({
|
||||
name: createProductDto.name,
|
||||
code: createProductDto.code,
|
||||
status: createProductDto.status,
|
||||
sub_categories: subCategories,
|
||||
price: createProductDto.price,
|
||||
});
|
||||
|
||||
await this.productHistoryPrice.insert({
|
||||
product: result.identifiers[0],
|
||||
type: productType.NORMAL,
|
||||
price: createProductDto.price,
|
||||
mark_up_price: createProductDto.markUpPrice,
|
||||
startDate: new Date(),
|
||||
endDate: null,
|
||||
});
|
||||
|
||||
return this.productRepository.findOneOrFail(result.identifiers[0].id);
|
||||
}
|
||||
|
||||
findAll(page) {
|
||||
return this.productRepository.findAndCount({
|
||||
skip: page * 10,
|
||||
take: 10,
|
||||
order: {
|
||||
version: 'DESC',
|
||||
async processUploadCSV(uploadFile: string, supplierCode: string) {
|
||||
const supplierData = await this.supplierService.findByCode(supplierCode);
|
||||
|
||||
const data = await parsingFile(uploadFile);
|
||||
data.shift();
|
||||
await mapSeries(data, async (it) => {
|
||||
let dataHistoryPrice;
|
||||
let partnerData;
|
||||
|
||||
const subCategories =
|
||||
await this.productSubCategoriesService.findOneForCSVParser(it[2]);
|
||||
|
||||
if (!subCategories) {
|
||||
return;
|
||||
}
|
||||
|
||||
const productData = await this.productRepository.findOne({
|
||||
code: it[0],
|
||||
supplier: supplierData,
|
||||
});
|
||||
if (productData) {
|
||||
//TODO : Handle Update Product
|
||||
productData.name = it[1];
|
||||
productData.status = it[5] == 'active' ? 'ACTIVE' : 'NOT ACTIVE';
|
||||
await this.productRepository.save(productData);
|
||||
|
||||
//TODO : Handle History Price
|
||||
if (it[6] != '-' && it[6] != '') {
|
||||
partnerData = await this.partnerService.findOne(it[6]);
|
||||
dataHistoryPrice = await this.productHistoryPrice.findOne({
|
||||
where: {
|
||||
product: productData.id,
|
||||
partner: partnerData.id,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
dataHistoryPrice = await this.productHistoryPrice.findOne({
|
||||
product: productData,
|
||||
});
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
if (!dataHistoryPrice) {
|
||||
return;
|
||||
}
|
||||
|
||||
dataHistoryPrice.endDate = new Date();
|
||||
await this.productHistoryPrice.save(dataHistoryPrice);
|
||||
|
||||
await this.productHistoryPrice.insert({
|
||||
product: productData,
|
||||
mark_up_price: it[4],
|
||||
price: it[3],
|
||||
type: productType.NORMAL,
|
||||
startDate: new Date(),
|
||||
partner: it[6] != '-' ? partnerData : null,
|
||||
});
|
||||
} else {
|
||||
let partnerData;
|
||||
if (it[6] != '-' && it[6] != '') {
|
||||
partnerData = await this.partnerService.findOne(it[6]);
|
||||
}
|
||||
const savedProduct = await this.productRepository.insert({
|
||||
name: it[1],
|
||||
code: it[0],
|
||||
status: it[5] == 'active' ? 'ACTIVE' : 'NOT ACTIVE',
|
||||
sub_categories: subCategories,
|
||||
supplier: supplierData,
|
||||
});
|
||||
|
||||
return await this.productHistoryPrice.insert({
|
||||
product: savedProduct.identifiers[0],
|
||||
mark_up_price: it[4],
|
||||
price: it[3],
|
||||
type: productType.NORMAL,
|
||||
startDate: new Date(),
|
||||
endDate: null,
|
||||
partner: partnerData,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async findAll(
|
||||
page: number,
|
||||
supplier: string,
|
||||
subCategories: string,
|
||||
pageSize?: number,
|
||||
) {
|
||||
let filterSupplier, filterSubCategories;
|
||||
|
||||
if (supplier) {
|
||||
filterSupplier = supplier.split(',').map((data) => {
|
||||
return data.trim();
|
||||
});
|
||||
}
|
||||
|
||||
if (subCategories) {
|
||||
filterSubCategories = subCategories.split(',').map((data) => {
|
||||
return data.trim();
|
||||
});
|
||||
}
|
||||
|
||||
const baseQuery = this.productRepository
|
||||
.createQueryBuilder('product')
|
||||
.leftJoin('product.sub_categories', 'sub_categories')
|
||||
.leftJoin('sub_categories.category', 'category')
|
||||
.leftJoin('product.supplier', 'supplier')
|
||||
.where('supplier.status = :status', { status: true })
|
||||
.innerJoinAndMapOne(
|
||||
'product.currentPrice',
|
||||
'product.priceHistory',
|
||||
'current_price',
|
||||
'current_price.partner_id is null and current_price.end_date is NULL',
|
||||
)
|
||||
.select(['product.id'])
|
||||
.addSelect([
|
||||
'product.name',
|
||||
'product.code',
|
||||
'sub_categories.name',
|
||||
'supplier.name',
|
||||
'category.name',
|
||||
'product.status',
|
||||
])
|
||||
.addSelect('current_price.price')
|
||||
.addSelect(
|
||||
'(current_price.price + current_price.mark_up_price) as mark_up_price',
|
||||
);
|
||||
|
||||
if (subCategories && filterSubCategories.length > 0) {
|
||||
baseQuery.where('product.sub_categories_id IN (:...subCategoryId)', {
|
||||
subCategoryId: filterSubCategories,
|
||||
});
|
||||
}
|
||||
|
||||
if (supplier && filterSupplier.length > 0) {
|
||||
baseQuery.where('supplier.id IN (:...supplierId)', {
|
||||
supplierId: filterSupplier,
|
||||
});
|
||||
}
|
||||
|
||||
const data = await baseQuery
|
||||
.offset(page * (pageSize || 10))
|
||||
.limit(pageSize || 10)
|
||||
.getRawMany();
|
||||
|
||||
const totalData = await baseQuery.getCount();
|
||||
|
||||
return {
|
||||
data,
|
||||
count: totalData,
|
||||
};
|
||||
}
|
||||
|
||||
async findAllByCategories(page, subCategories, supplier) {
|
||||
const baseQuery = this.productRepository
|
||||
.createQueryBuilder('product')
|
||||
.leftJoin('product.sub_categories', 'sub_categories')
|
||||
.where(
|
||||
'sub_categories.category_id = :id and product.supplier_id = :supplier_id',
|
||||
{
|
||||
id: subCategories,
|
||||
supplier_id: supplier,
|
||||
},
|
||||
)
|
||||
.leftJoinAndMapOne(
|
||||
'product.currentPrice',
|
||||
'product.priceHistory',
|
||||
'current_price',
|
||||
'current_price.partner_id is null',
|
||||
);
|
||||
|
||||
const data = await baseQuery
|
||||
.skip(page * 10)
|
||||
.take(10)
|
||||
.getMany();
|
||||
|
||||
const totalData = await baseQuery.getCount();
|
||||
|
||||
return {
|
||||
data,
|
||||
count: totalData,
|
||||
};
|
||||
}
|
||||
|
||||
async findAllBySubCategories(page, subCategories, supplier, pageSize?) {
|
||||
if (supplier != 'null' && !supplier) {
|
||||
supplier = (await this.supplierService.findByActive()).id;
|
||||
}
|
||||
|
||||
const baseQuery = this.productRepository
|
||||
.createQueryBuilder('product')
|
||||
.leftJoin('product.sub_categories', 'sub_categories')
|
||||
.where(
|
||||
`product.supplier_id = :supplier_id and product.status = 'ACTIVE'`,
|
||||
{
|
||||
supplier_id: supplier,
|
||||
},
|
||||
)
|
||||
.leftJoinAndMapOne(
|
||||
'product.currentPrice',
|
||||
'product.priceHistory',
|
||||
'current_price',
|
||||
'current_price.partner_id is NULL and current_price.end_date is NULL',
|
||||
)
|
||||
.select(['product.id'])
|
||||
.addSelect(['product.name', 'product.code', 'sub_categories.name'])
|
||||
.addSelect(
|
||||
'(current_price.price + current_price.mark_up_price) as price',
|
||||
);
|
||||
|
||||
if (subCategories != 'null' && subCategories) {
|
||||
baseQuery.where('product.sub_categories_id = :id', {
|
||||
id: subCategories,
|
||||
});
|
||||
}
|
||||
|
||||
const data = await baseQuery
|
||||
.offset(page * 10)
|
||||
.limit(10)
|
||||
.getRawMany();
|
||||
|
||||
const totalData = await baseQuery.getCount();
|
||||
|
||||
return {
|
||||
data,
|
||||
count: totalData,
|
||||
};
|
||||
}
|
||||
|
||||
async findAllForPartner(
|
||||
page: number,
|
||||
pageSize: number,
|
||||
subCategories: string,
|
||||
username: string,
|
||||
) {
|
||||
const user = await this.usersService.findOneByUsername(username);
|
||||
const supplier = await this.supplierService.findByActive();
|
||||
|
||||
let filterSupplier, filterSubCategories;
|
||||
|
||||
if (subCategories) {
|
||||
filterSubCategories = subCategories.split(',').map((data) => data.trim());
|
||||
} else {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Sub Categories not inlcude',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const baseQuery = this.productRepository
|
||||
.createQueryBuilder('product')
|
||||
.leftJoin('product.sub_categories', 'sub_categories')
|
||||
.where(
|
||||
`product.sub_categories_id IN (:...subCategoryId) and product.supplier_id = :supplier_id and product.status = 'ACTIVE'`,
|
||||
{
|
||||
subCategoryId: filterSubCategories,
|
||||
supplier_id: supplier.id,
|
||||
},
|
||||
)
|
||||
.leftJoinAndMapOne(
|
||||
'product.currentPrice',
|
||||
'product.priceHistory',
|
||||
'current_price',
|
||||
'current_price.partner_id = :id_partner and current_price.end_date is NULL',
|
||||
{
|
||||
id_partner: user.partner.id,
|
||||
},
|
||||
)
|
||||
.select(['product.id'])
|
||||
.addSelect(['product.name', 'product.code', 'sub_categories.name'])
|
||||
.addSelect(
|
||||
'(current_price.price + current_price.mark_up_price) as price',
|
||||
);
|
||||
|
||||
const data = await baseQuery
|
||||
.offset(page * 10)
|
||||
.limit(10)
|
||||
.getRawMany();
|
||||
|
||||
const totalData = await baseQuery.getCount();
|
||||
|
||||
return {
|
||||
data,
|
||||
count: totalData,
|
||||
};
|
||||
}
|
||||
|
||||
async findOne(code: string) {
|
||||
try {
|
||||
return await this.productRepository.findOneOrFail(id);
|
||||
return await this.productRepository.findOneOrFail({
|
||||
relations: ['supplier'],
|
||||
where: {
|
||||
code: code,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
error: 'Product not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
|
@ -48,4 +366,96 @@ export class ProductService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findOneById(id: string) {
|
||||
try {
|
||||
return await this.productRepository.findOneOrFail({
|
||||
relations: ['supplier'],
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Product not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: string, updateProductDto: UpdateProductDto) {
|
||||
try {
|
||||
await this.productRepository.findOneOrFail(id);
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Product not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
const subCategories = await this.productSubCategoriesService.findOne(
|
||||
updateProductDto.subCategoriesId,
|
||||
);
|
||||
|
||||
const result = await this.productRepository.update(id, {
|
||||
name: updateProductDto.name,
|
||||
code: updateProductDto.code,
|
||||
status: updateProductDto.status,
|
||||
sub_categories: subCategories,
|
||||
});
|
||||
|
||||
return this.productRepository.findOneOrFail(id);
|
||||
}
|
||||
|
||||
async updatePrice(
|
||||
code: string,
|
||||
updatePriceProductDto: UpdatePriceProductDto,
|
||||
) {
|
||||
const product = await this.findOne(code);
|
||||
|
||||
await this.productHistoryPrice.insert({
|
||||
product: product,
|
||||
type: updatePriceProductDto.type,
|
||||
price: updatePriceProductDto.price,
|
||||
mark_up_price: updatePriceProductDto.markUpPrice,
|
||||
startDate: updatePriceProductDto.startDate,
|
||||
endDate: updatePriceProductDto.endDate,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
try {
|
||||
await this.productRepository.findOneOrFail(id);
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Product not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
await this.productRepository.delete(id);
|
||||
}
|
||||
}
|
||||
|
|
144
src/transaction/coa.service.ts
Normal file
144
src/transaction/coa.service.ts
Normal file
|
@ -0,0 +1,144 @@
|
|||
import {
|
||||
forwardRef,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Inject,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { COA } from './entities/coa.entity';
|
||||
import { balanceType, coaType } from '../helper/enum-list';
|
||||
import { InputCoaDto } from './dto/input-coa.dto';
|
||||
import { UsersService } from 'src/users/users.service';
|
||||
|
||||
export class CoaService {
|
||||
constructor(
|
||||
@InjectRepository(COA)
|
||||
private coaRepository: Repository<COA>,
|
||||
@Inject(
|
||||
forwardRef(() => {
|
||||
return UsersService;
|
||||
}),
|
||||
)
|
||||
private userService: UsersService,
|
||||
) {}
|
||||
|
||||
async create(inputCoaDto: InputCoaDto) {
|
||||
const coaData = new COA();
|
||||
let name = '';
|
||||
if (inputCoaDto.user) {
|
||||
coaData.user = inputCoaDto.user.id;
|
||||
name = inputCoaDto.user.username;
|
||||
}
|
||||
|
||||
if (inputCoaDto.supplier) {
|
||||
coaData.supplier = inputCoaDto.supplier.id;
|
||||
name = inputCoaDto.supplier.code;
|
||||
}
|
||||
|
||||
coaData.name = `${coaType[inputCoaDto.type]}-${name}`;
|
||||
coaData.balanceType = inputCoaDto.balanceType;
|
||||
coaData.type = inputCoaDto.type;
|
||||
coaData.amount = 0;
|
||||
|
||||
const result = await inputCoaDto.coaEntityManager.insert(COA, coaData);
|
||||
|
||||
if (
|
||||
inputCoaDto.type == coaType.ACCOUNT_RECEIVABLE ||
|
||||
inputCoaDto.type == coaType.ACCOUNT_PAYABLE
|
||||
) {
|
||||
coaData.relatedUser = inputCoaDto.relatedUserId;
|
||||
await inputCoaDto.coaEntityManager.save(coaData);
|
||||
}
|
||||
|
||||
return coaData;
|
||||
}
|
||||
|
||||
async findByUser(id: string, typeOfCoa: coaType) {
|
||||
try {
|
||||
return await this.coaRepository.findOneOrFail({
|
||||
user: id,
|
||||
type: typeOfCoa,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'COA not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findByTwoUser(from: string, destination: string, typeOfCoa: coaType) {
|
||||
try {
|
||||
return await this.coaRepository.findOneOrFail({
|
||||
user: from,
|
||||
relatedUser: destination,
|
||||
type: typeOfCoa,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'COA not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findByUserWithRelated(
|
||||
id: string,
|
||||
relatedId: string,
|
||||
typeOfCoa: coaType,
|
||||
) {
|
||||
try {
|
||||
return await this.coaRepository.findOneOrFail({
|
||||
user: id,
|
||||
type: typeOfCoa,
|
||||
relatedUser: relatedId,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Coa Data not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findByName(name: string) {
|
||||
try {
|
||||
return await this.coaRepository.findOneOrFail({ name: name });
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'COA Data not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
src/transaction/dto/add-saldo-supplier.dto.ts
Normal file
9
src/transaction/dto/add-saldo-supplier.dto.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
|
||||
export class AddSaldoSupplier {
|
||||
@IsNotEmpty()
|
||||
supplier: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
amount: number;
|
||||
}
|
36
src/transaction/dto/create-journal.dto.ts
Normal file
36
src/transaction/dto/create-journal.dto.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
import { balanceType, coaType, statusTransaction, typeTransaction } from 'src/helper/enum-list';
|
||||
import { EntityManager } from 'typeorm';
|
||||
import { Transactions } from '../entities/transactions.entity';
|
||||
|
||||
interface JournalEntry {
|
||||
coa_id: string;
|
||||
debit?: number;
|
||||
credit?: number;
|
||||
}
|
||||
|
||||
export class CreateJournalDto {
|
||||
@IsNotEmpty()
|
||||
transactionalEntityManager: EntityManager;
|
||||
|
||||
@IsNotEmpty()
|
||||
createTransaction?: boolean;
|
||||
|
||||
@IsNotEmpty()
|
||||
userId?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
transaction?: Transactions;
|
||||
|
||||
@IsNotEmpty()
|
||||
type?: typeTransaction;
|
||||
|
||||
@IsNotEmpty()
|
||||
amount?: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
transactionStatus?: statusTransaction;
|
||||
|
||||
@IsNotEmpty()
|
||||
journals: JournalEntry[]
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export class CreateTransactionDto {}
|
7
src/transaction/dto/deposit_return.dto.ts
Normal file
7
src/transaction/dto/deposit_return.dto.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { DistributeTransactionDto } from './distribute-transaction.dto';
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
export class DepositReturnDto extends DistributeTransactionDto {
|
||||
@IsNotEmpty()
|
||||
image_prove: string;
|
||||
}
|
9
src/transaction/dto/distribute-transaction.dto.ts
Normal file
9
src/transaction/dto/distribute-transaction.dto.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
|
||||
export class DistributeTransactionDto {
|
||||
@IsNotEmpty()
|
||||
amount: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
destination: string;
|
||||
}
|
25
src/transaction/dto/input-coa.dto.ts
Normal file
25
src/transaction/dto/input-coa.dto.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
import { balanceType, coaType } from 'src/helper/enum-list';
|
||||
import { User } from 'src/users/entities/user.entity';
|
||||
import { EntityManager } from 'typeorm';
|
||||
import { Supplier } from '../../users/entities/supplier.entity';
|
||||
|
||||
export class InputCoaDto {
|
||||
@IsUUID()
|
||||
user: User;
|
||||
|
||||
@IsNotEmpty()
|
||||
type: coaType;
|
||||
|
||||
@IsNotEmpty()
|
||||
balanceType: balanceType;
|
||||
|
||||
@IsUUID()
|
||||
relatedUserId: string;
|
||||
|
||||
@IsUUID()
|
||||
supplier: Supplier;
|
||||
|
||||
@IsNotEmpty()
|
||||
coaEntityManager: EntityManager;
|
||||
}
|
9
src/transaction/dto/order-transaction.dto.ts
Normal file
9
src/transaction/dto/order-transaction.dto.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
|
||||
export class OrderTransactionDto {
|
||||
@IsNotEmpty()
|
||||
productCode: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
phoneNumber: string;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateTransactionDto } from './create-transaction.dto';
|
||||
import { DistributeTransactionDto } from './distribute-transaction.dto';
|
||||
|
||||
export class UpdateTransactionDto extends PartialType(CreateTransactionDto) {}
|
||||
export class UpdateTransactionDto extends PartialType(DistributeTransactionDto) {}
|
||||
|
|
36
src/transaction/entities/coa.entity.ts
Normal file
36
src/transaction/entities/coa.entity.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { Entity, Column } from 'typeorm';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
import { coaType, balanceType } from '../../helper/enum-list';
|
||||
|
||||
@Entity()
|
||||
export class COA extends BaseModel {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column('text')
|
||||
type: coaType;
|
||||
|
||||
@Column('text')
|
||||
balanceType: balanceType;
|
||||
|
||||
@Column()
|
||||
amount: number;
|
||||
|
||||
@Column({
|
||||
type: 'uuid',
|
||||
nullable: true,
|
||||
})
|
||||
user: string;
|
||||
|
||||
@Column({
|
||||
type: 'uuid',
|
||||
nullable: true,
|
||||
})
|
||||
relatedUser: string;
|
||||
|
||||
@Column({
|
||||
type: 'uuid',
|
||||
nullable: true,
|
||||
})
|
||||
supplier: string;
|
||||
}
|
41
src/transaction/entities/transaction-journal.entity.ts
Normal file
41
src/transaction/entities/transaction-journal.entity.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import {
|
||||
Entity,
|
||||
Column,
|
||||
ManyToOne,
|
||||
ManyToMany,
|
||||
JoinTable,
|
||||
OneToOne,
|
||||
} from 'typeorm';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
import { COA } from './coa.entity';
|
||||
import { Transactions } from './transactions.entity';
|
||||
import { balanceType } from '../../helper/enum-list';
|
||||
|
||||
@Entity()
|
||||
export class TransactionJournal extends BaseModel {
|
||||
@Column('text')
|
||||
type: balanceType;
|
||||
|
||||
@Column()
|
||||
amount: number;
|
||||
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return Transactions;
|
||||
},
|
||||
(transaction) => {
|
||||
return transaction.id;
|
||||
},
|
||||
)
|
||||
transaction_head: Transactions;
|
||||
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return COA;
|
||||
},
|
||||
(coa) => {
|
||||
return coa.id;
|
||||
},
|
||||
)
|
||||
coa: COA;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export class Transaction {}
|
66
src/transaction/entities/transactions.entity.ts
Normal file
66
src/transaction/entities/transactions.entity.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
ManyToMany,
|
||||
JoinTable,
|
||||
} from 'typeorm';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
import { statusTransaction, typeTransaction } from '../../helper/enum-list';
|
||||
import { Partner } from '../../users/entities/partner.entity';
|
||||
import { ProductHistoryPrice } from '../../product/entities/product-history-price.entity';
|
||||
import { User } from '../../users/entities/user.entity';
|
||||
import { UserDetail } from '../../users/entities/user_detail.entity';
|
||||
import { TransactionJournal } from './transaction-journal.entity';
|
||||
|
||||
@Entity()
|
||||
export class Transactions extends BaseModel {
|
||||
@Column()
|
||||
amount: number;
|
||||
|
||||
@Column()
|
||||
status: statusTransaction;
|
||||
|
||||
@Column()
|
||||
type: typeTransaction;
|
||||
|
||||
@Column({
|
||||
type: 'uuid',
|
||||
nullable: true,
|
||||
})
|
||||
user: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
user_destination: string;
|
||||
|
||||
@ManyToOne(() => ProductHistoryPrice, (product) => product.id)
|
||||
product_price: ProductHistoryPrice;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
image_prove: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
supplier_trx_id: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
phone_number: string;
|
||||
|
||||
mark_up_price: number;
|
||||
|
||||
userData: UserDetail;
|
||||
|
||||
transactionJournal: TransactionJournal;
|
||||
}
|
|
@ -11,7 +11,7 @@ import {
|
|||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { TransactionService } from './transaction.service';
|
||||
import { CreateTransactionDto } from './dto/create-transaction.dto';
|
||||
import { DistributeTransactionDto } from './dto/distribute-transaction.dto';
|
||||
import { FastifyRequest } from 'fastify';
|
||||
|
||||
@Controller({
|
||||
|
@ -24,7 +24,20 @@ export class PpobCallbackController {
|
|||
constructor(private readonly transactionService: TransactionService) {}
|
||||
|
||||
@Get()
|
||||
get(@Req() request: FastifyRequest) {
|
||||
async get(@Req() request: FastifyRequest) {
|
||||
const response = request.query;
|
||||
|
||||
if (response['statuscode'] == 2) {
|
||||
//TODO: UPDATE GAGAL
|
||||
const updateTransaction =
|
||||
await this.transactionService.callbackOrderFailed(response['clientid']);
|
||||
} else {
|
||||
//TODO: UPDATE BERHASIL
|
||||
const updateTransaction =
|
||||
await this.transactionService.callbackOrderSuccess(
|
||||
response['clientid'],
|
||||
);
|
||||
}
|
||||
this.logger.log({
|
||||
requestQuery: request.query,
|
||||
});
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
HttpStatus,
|
||||
Param,
|
||||
Delete,
|
||||
ParseUUIDPipe,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
Request,
|
||||
} from '@nestjs/common';
|
||||
import { TransactionService } from './transaction.service';
|
||||
import { CreateTransactionDto } from './dto/create-transaction.dto';
|
||||
import { UpdateTransactionDto } from './dto/update-transaction.dto';
|
||||
import { DistributeTransactionDto } from './dto/distribute-transaction.dto';
|
||||
import { OrderTransactionDto } from './dto/order-transaction.dto';
|
||||
import { AddSaldoSupplier } from './dto/add-saldo-supplier.dto';
|
||||
import { DepositReturnDto } from './dto/deposit_return.dto';
|
||||
|
||||
@Controller({
|
||||
path: 'transaction',
|
||||
|
@ -18,31 +23,227 @@ import { UpdateTransactionDto } from './dto/update-transaction.dto';
|
|||
export class TransactionController {
|
||||
constructor(private readonly transactionService: TransactionService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() createTransactionDto: CreateTransactionDto) {
|
||||
return this.transactionService.create(createTransactionDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.transactionService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.transactionService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(
|
||||
@Param('id') id: string,
|
||||
@Body() updateTransactionDto: UpdateTransactionDto,
|
||||
@Post('distribute')
|
||||
async create(
|
||||
@Body() createTransactionDto: DistributeTransactionDto,
|
||||
@Request() req,
|
||||
) {
|
||||
return this.transactionService.update(+id, updateTransactionDto);
|
||||
return {
|
||||
data: await this.transactionService.distributeDeposit(
|
||||
createTransactionDto,
|
||||
req.user,
|
||||
),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.transactionService.remove(+id);
|
||||
@Post('distribute-admin')
|
||||
async distributeAdmin(
|
||||
@Body() createTransactionDto: DistributeTransactionDto,
|
||||
@Request() req,
|
||||
) {
|
||||
return {
|
||||
data: await this.transactionService.distributeFromAdmin(
|
||||
createTransactionDto,
|
||||
req.user,
|
||||
),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('add-saldo-supplier')
|
||||
async addSaldoSupplier(
|
||||
@Body() addSaldoSupplier: AddSaldoSupplier,
|
||||
@Request() req,
|
||||
) {
|
||||
return {
|
||||
data: await this.transactionService.addSupplierSaldo(
|
||||
addSaldoSupplier,
|
||||
req.user,
|
||||
),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('order')
|
||||
async orderTransaction(
|
||||
@Body() orderTransactionDto: OrderTransactionDto,
|
||||
@Request() req,
|
||||
) {
|
||||
return {
|
||||
data: await this.transactionService.orderTransaction(
|
||||
orderTransactionDto,
|
||||
req.user,
|
||||
),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('order-prod')
|
||||
async orderTransactionProd(
|
||||
@Body() orderTransactionDto: OrderTransactionDto,
|
||||
@Request() req,
|
||||
) {
|
||||
return {
|
||||
data: await this.transactionService.orderTransactionProd(
|
||||
orderTransactionDto,
|
||||
req.user,
|
||||
),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('deposit-return')
|
||||
async depositReturn(
|
||||
@Body() depositReturnDto: DepositReturnDto,
|
||||
@Request() req,
|
||||
) {
|
||||
return {
|
||||
data: await this.transactionService.createDepositReturn(
|
||||
req.user,
|
||||
depositReturnDto,
|
||||
),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('history')
|
||||
async getHistoryTransactionUser(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Request() req,
|
||||
) {
|
||||
const data = await this.transactionService.transactionHistoryByUser(
|
||||
page,
|
||||
req.user.userId,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('history-deposit')
|
||||
async getHistoryDepositUser(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Query('user-destination') userDestination: string,
|
||||
@Request() req,
|
||||
) {
|
||||
const data = await this.transactionService.topUpHistoryByUser(
|
||||
page,
|
||||
req.user.userId,
|
||||
userDestination,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('deposit-return')
|
||||
async findDepositReturn(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Request() req,
|
||||
) {
|
||||
const [data, count] =
|
||||
await this.transactionService.getAllDepositReturnFromUser(
|
||||
req.user.userId,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
data,
|
||||
count,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('total-order')
|
||||
async findTotalOrder(@Request() req) {
|
||||
const data = await this.transactionService.getTotalSell();
|
||||
|
||||
return {
|
||||
data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('deposit-return/confirmation')
|
||||
async findDepositReturnConfirmation(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Request() req,
|
||||
) {
|
||||
const data = await this.transactionService.getAllDepositReturnToUser(
|
||||
req.user.userId,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put('deposit-return/confirmation/:id/:status')
|
||||
async confirmDepositReturn(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Param('status') status: string,
|
||||
@Request() req,
|
||||
) {
|
||||
return {
|
||||
data: await this.transactionService.confirmationDepositReturn(
|
||||
id,
|
||||
req.user,
|
||||
status,
|
||||
),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put('deposit-return/confirmation-admin/:id')
|
||||
async confirmDepositReturnAdmin(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Request() req,
|
||||
@Body() status: string,
|
||||
) {
|
||||
return {
|
||||
data: await this.transactionService.confirmationAdminDepositReturn(
|
||||
id,
|
||||
req.user,
|
||||
status,
|
||||
),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put('withdraw/:id')
|
||||
async withdrawProfit(@Param('id', ParseUUIDPipe) id: string, @Request() req) {
|
||||
return {
|
||||
data: await this.transactionService.withdrawBenefit(id),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { TransactionService } from './transaction.service';
|
||||
import { TransactionController } from './transaction.controller';
|
||||
import { PpobCallbackController } from './ppob_callback.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { COA } from './entities/coa.entity';
|
||||
import { TransactionJournal } from './entities/transaction-journal.entity';
|
||||
import { Transactions } from './entities/transactions.entity';
|
||||
import { CoaService } from './coa.service';
|
||||
import { ProductModule } from '../product/product.module';
|
||||
import { UsersModule } from 'src/users/users.module';
|
||||
import { ConfigurableModule } from '../configurable/configurable.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([COA, TransactionJournal, Transactions]),
|
||||
ProductModule,
|
||||
ConfigurableModule,
|
||||
forwardRef(() => UsersModule),
|
||||
],
|
||||
controllers: [TransactionController, PpobCallbackController],
|
||||
providers: [TransactionService],
|
||||
providers: [TransactionService, CoaService],
|
||||
exports: [CoaService],
|
||||
})
|
||||
export class TransactionModule {}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
24
src/users/dto/create-partner.dto.ts
Normal file
24
src/users/dto/create-partner.dto.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
export class CreatePartnerDto {
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
address: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
owner: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
code: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
npwp: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
password_account: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
phone_number: string;
|
||||
}
|
9
src/users/dto/create-supplier.dto.ts
Normal file
9
src/users/dto/create-supplier.dto.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
export class CreateSupplierDto {
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
code: string;
|
||||
}
|
|
@ -1,9 +1,30 @@
|
|||
import { IsNotEmpty } from 'class-validator';
|
||||
import { IsNotEmpty, IsOptional, IsUUID, ValidateIf } from 'class-validator';
|
||||
import { Partner } from '../entities/partner.entity';
|
||||
import { Column } from 'typeorm';
|
||||
|
||||
export class CreateUserDto {
|
||||
@IsNotEmpty()
|
||||
firstName: string;
|
||||
username: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
lastName: string;
|
||||
name: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
phone_number: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
password: string;
|
||||
|
||||
@IsUUID()
|
||||
roleId: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
superior: boolean;
|
||||
|
||||
partner: Partner;
|
||||
// @ValidateIf((o) => {
|
||||
// return !!o.superior;
|
||||
// })
|
||||
// @IsUUID()
|
||||
// superior: string;
|
||||
}
|
||||
|
|
5
src/users/dto/update-partner.dto.ts
Normal file
5
src/users/dto/update-partner.dto.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreatePartnerDto } from './create-partner.dto';
|
||||
|
||||
export class UpdatePartnerDto extends PartialType(CreatePartnerDto) {
|
||||
}
|
4
src/users/dto/update-supplier.dto.ts
Normal file
4
src/users/dto/update-supplier.dto.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateSupplierDto } from './create-supplier.dto';
|
||||
|
||||
export class UpdateSupplierDto extends PartialType(CreateSupplierDto) {}
|
32
src/users/entities/partner.entity.ts
Normal file
32
src/users/entities/partner.entity.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { Roles } from 'src/configurable/entities/roles.entity';
|
||||
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
import { hashPassword } from '../../helper/hash_password';
|
||||
|
||||
@Entity()
|
||||
export class Partner extends BaseModel {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
code: string;
|
||||
|
||||
@Column()
|
||||
npwp: string;
|
||||
|
||||
@Column()
|
||||
address: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
phone_number: string;
|
||||
|
||||
@Column({ default: true })
|
||||
status: boolean;
|
||||
}
|
24
src/users/entities/supplier.entity.ts
Normal file
24
src/users/entities/supplier.entity.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { Roles } from 'src/configurable/entities/roles.entity';
|
||||
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
import { hashPassword } from '../../helper/hash_password';
|
||||
import { COA } from '../../transaction/entities/coa.entity';
|
||||
|
||||
@Entity()
|
||||
export class Supplier extends BaseModel {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column()
|
||||
code: string;
|
||||
|
||||
@Column()
|
||||
status: boolean;
|
||||
|
||||
coa: COA;
|
||||
|
||||
coa_undistribute: COA;
|
||||
}
|
|
@ -1,45 +1,65 @@
|
|||
import { Roles } from 'src/configurable/entities/roles.entity';
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
OneToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
import { Partner } from './partner.entity';
|
||||
import { UserDetail } from './user_detail.entity';
|
||||
import { COA } from '../../transaction/entities/coa.entity';
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
export class User extends BaseModel {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
firstName: string;
|
||||
username: string;
|
||||
|
||||
@Column()
|
||||
lastName: string;
|
||||
password: string;
|
||||
|
||||
@Column()
|
||||
salt: string;
|
||||
|
||||
@Column({ default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@CreateDateColumn({
|
||||
type: 'timestamp with time zone',
|
||||
nullable: false,
|
||||
})
|
||||
createdAt: Date;
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
(user) => {
|
||||
return user.id;
|
||||
},
|
||||
)
|
||||
superior: User;
|
||||
|
||||
@UpdateDateColumn({
|
||||
type: 'timestamp with time zone',
|
||||
nullable: false,
|
||||
})
|
||||
updatedAt: Date;
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return Roles;
|
||||
},
|
||||
(roles) => {
|
||||
return roles.id;
|
||||
},
|
||||
)
|
||||
roles: Roles;
|
||||
|
||||
@DeleteDateColumn({
|
||||
type: 'timestamp with time zone',
|
||||
nullable: true,
|
||||
})
|
||||
deletedAt: Date;
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return Partner;
|
||||
},
|
||||
(partner) => {
|
||||
return partner.id;
|
||||
},
|
||||
)
|
||||
partner: Partner;
|
||||
|
||||
@VersionColumn()
|
||||
version: number;
|
||||
@OneToOne(() => UserDetail, (userDetail) => userDetail.user)
|
||||
userDetail: UserDetail;
|
||||
|
||||
coa: COA;
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
firstName: string;
|
||||
}
|
24
src/users/entities/user_detail.entity.ts
Normal file
24
src/users/entities/user_detail.entity.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import {
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity()
|
||||
export class UserDetail {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column()
|
||||
phone_number: string;
|
||||
|
||||
@OneToOne(() => User, (user) => user.userDetail)
|
||||
@JoinColumn()
|
||||
user: User;
|
||||
}
|
18
src/users/partner/partner.service.spec.ts
Normal file
18
src/users/partner/partner.service.spec.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PartnerService } from './partner.service';
|
||||
|
||||
describe('PartnerService', () => {
|
||||
let service: PartnerService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [PartnerService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<PartnerService>(PartnerService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
159
src/users/partner/partner.service.ts
Normal file
159
src/users/partner/partner.service.ts
Normal file
|
@ -0,0 +1,159 @@
|
|||
import {
|
||||
forwardRef,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Inject,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Connection, EntityNotFoundError, Not, Repository } from 'typeorm';
|
||||
import { CoaService } from '../../transaction/coa.service';
|
||||
import { CreatePartnerDto } from '../dto/create-partner.dto';
|
||||
import { Partner } from '../entities/partner.entity';
|
||||
import * as uuid from 'uuid';
|
||||
import { UsersService } from '../users.service';
|
||||
import { CreateUserDto } from '../dto/create-user.dto';
|
||||
import { UpdatePartnerDto } from '../dto/update-partner.dto';
|
||||
import { UpdateUserDto } from '../dto/update-user.dto';
|
||||
import { when } from 'joi';
|
||||
|
||||
@Injectable()
|
||||
export class PartnerService {
|
||||
constructor(
|
||||
@InjectRepository(Partner)
|
||||
private partnerRepository: Repository<Partner>,
|
||||
@Inject(
|
||||
forwardRef(() => {
|
||||
return CoaService;
|
||||
}),
|
||||
)
|
||||
private coaService: CoaService,
|
||||
private userService: UsersService,
|
||||
private connection: Connection,
|
||||
) {}
|
||||
|
||||
async create(createPartnerDto: CreatePartnerDto, currentUser: any) {
|
||||
const check = await this.partnerRepository.findOne({
|
||||
npwp: createPartnerDto.npwp,
|
||||
});
|
||||
|
||||
if (check) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_ACCEPTABLE,
|
||||
error: 'NPWP Already Exist',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const partnerData = new Partner();
|
||||
|
||||
partnerData.id = uuid.v4();
|
||||
partnerData.name = createPartnerDto.name;
|
||||
partnerData.npwp = createPartnerDto.npwp;
|
||||
partnerData.address = createPartnerDto.address;
|
||||
partnerData.code = createPartnerDto.code;
|
||||
partnerData.status = true;
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
const result = await manager.insert(Partner, partnerData);
|
||||
});
|
||||
|
||||
const dataUser = new CreateUserDto();
|
||||
|
||||
dataUser.username = `admin_${partnerData.name}`;
|
||||
dataUser.name = partnerData.name;
|
||||
dataUser.phone_number = partnerData.phone_number;
|
||||
dataUser.roleId = '21dceea2-416e-4b55-b74c-12605e1f8d1b';
|
||||
dataUser.superior = false;
|
||||
dataUser.partner = partnerData;
|
||||
dataUser.password = createPartnerDto.password_account;
|
||||
dataUser.phone_number = createPartnerDto.phone_number;
|
||||
|
||||
await this.userService.create(dataUser, currentUser);
|
||||
|
||||
return partnerData;
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
updatePartnerDto: UpdatePartnerDto,
|
||||
currentUser: any,
|
||||
) {
|
||||
const check = await this.partnerRepository.findOne({
|
||||
npwp: updatePartnerDto.npwp,
|
||||
id: Not(id),
|
||||
});
|
||||
|
||||
if (check) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_ACCEPTABLE,
|
||||
error: 'NPWP Already Exist',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const partnerData = new Partner();
|
||||
|
||||
partnerData.id = id;
|
||||
partnerData.name = updatePartnerDto.name;
|
||||
partnerData.address = updatePartnerDto.address;
|
||||
|
||||
if (updatePartnerDto.npwp) {
|
||||
partnerData.npwp = updatePartnerDto.npwp;
|
||||
}
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
await manager.update(Partner, { id: id }, partnerData);
|
||||
});
|
||||
|
||||
const dataUser = new UpdateUserDto();
|
||||
const userData = await this.userService.findOneByPartner(id);
|
||||
|
||||
dataUser.username = `admin_${partnerData.name}`;
|
||||
dataUser.partner = partnerData;
|
||||
|
||||
await this.userService.update(userData.id, dataUser, currentUser);
|
||||
|
||||
return partnerData;
|
||||
}
|
||||
|
||||
setStatus = async (id: string, type: string) => {
|
||||
const partnerData = await this.partnerRepository.findOne({
|
||||
id: id,
|
||||
});
|
||||
|
||||
if (type === 'active') {
|
||||
partnerData.status = true;
|
||||
} else {
|
||||
partnerData.status = false;
|
||||
}
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
await manager.update(Partner, { id: id }, partnerData);
|
||||
});
|
||||
|
||||
return partnerData;
|
||||
};
|
||||
|
||||
async findOne(code: string) {
|
||||
return await this.partnerRepository.findOne({
|
||||
where: {
|
||||
code: code,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
findAllPartner(page, pageSize?) {
|
||||
return this.partnerRepository.findAndCount({
|
||||
skip: page * (pageSize || 10),
|
||||
take: pageSize || 10,
|
||||
order: {
|
||||
version: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
18
src/users/supplier/supplier.service.spec.ts
Normal file
18
src/users/supplier/supplier.service.spec.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { SupplierService } from './supplier.service';
|
||||
|
||||
describe('PartnerService', () => {
|
||||
let service: SupplierService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [SupplierService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<SupplierService>(SupplierService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
216
src/users/supplier/supplier.service.ts
Normal file
216
src/users/supplier/supplier.service.ts
Normal file
|
@ -0,0 +1,216 @@
|
|||
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Connection, EntityNotFoundError, Not, Repository } from 'typeorm';
|
||||
import { Supplier } from '../entities/supplier.entity';
|
||||
import { InputCoaDto } from '../../transaction/dto/input-coa.dto';
|
||||
import { balanceType, coaType } from '../../helper/enum-list';
|
||||
import { CreateSupplierDto } from '../dto/create-supplier.dto';
|
||||
import { CoaService } from '../../transaction/coa.service';
|
||||
import * as uuid from 'uuid';
|
||||
import { UpdateSupplierDto } from '../dto/update-supplier.dto';
|
||||
import { COA } from '../../transaction/entities/coa.entity';
|
||||
|
||||
@Injectable()
|
||||
export class SupplierService {
|
||||
constructor(
|
||||
@InjectRepository(Supplier)
|
||||
private supplierRepository: Repository<Supplier>,
|
||||
@Inject(
|
||||
forwardRef(() => {
|
||||
return CoaService;
|
||||
}),
|
||||
)
|
||||
private coaService: CoaService,
|
||||
private connection: Connection,
|
||||
) {}
|
||||
|
||||
async create(createSupplierDto: CreateSupplierDto) {
|
||||
const check = await this.supplierRepository.findOne({
|
||||
code: createSupplierDto.code,
|
||||
});
|
||||
|
||||
if (check) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_ACCEPTABLE,
|
||||
error: 'Supplier Already Exist',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const supplierData = new Supplier();
|
||||
supplierData.id = uuid.v4();
|
||||
supplierData.name = createSupplierDto.name;
|
||||
supplierData.code = createSupplierDto.code;
|
||||
supplierData.status = false;
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
const result = await manager.insert(Supplier, supplierData);
|
||||
|
||||
const dataCoaInventory = new InputCoaDto();
|
||||
dataCoaInventory.supplier = supplierData;
|
||||
dataCoaInventory.balanceType = balanceType.DEBIT;
|
||||
dataCoaInventory.type = coaType.INVENTORY;
|
||||
dataCoaInventory.coaEntityManager = manager;
|
||||
await this.coaService.create(dataCoaInventory);
|
||||
|
||||
const dataCoaCostOfSales = new InputCoaDto();
|
||||
dataCoaCostOfSales.supplier = supplierData;
|
||||
dataCoaCostOfSales.balanceType = balanceType.DEBIT;
|
||||
dataCoaCostOfSales.type = coaType.COST_OF_SALES;
|
||||
dataCoaCostOfSales.coaEntityManager = manager;
|
||||
await this.coaService.create(dataCoaCostOfSales);
|
||||
|
||||
const dataCoaBudget = new InputCoaDto();
|
||||
dataCoaBudget.supplier = supplierData;
|
||||
dataCoaBudget.balanceType = balanceType.DEBIT;
|
||||
dataCoaBudget.type = coaType.BUDGET;
|
||||
dataCoaBudget.coaEntityManager = manager;
|
||||
await this.coaService.create(dataCoaBudget);
|
||||
|
||||
const dataCoaContraBudget = new InputCoaDto();
|
||||
dataCoaContraBudget.supplier = supplierData;
|
||||
dataCoaContraBudget.balanceType = balanceType.CREDIT;
|
||||
dataCoaContraBudget.type = coaType.CONTRA_BUDGET;
|
||||
dataCoaContraBudget.coaEntityManager = manager;
|
||||
await this.coaService.create(dataCoaContraBudget);
|
||||
});
|
||||
|
||||
return supplierData;
|
||||
}
|
||||
|
||||
async update(id: string, updateSupplierDto: UpdateSupplierDto) {
|
||||
const check = await this.supplierRepository.findOne({
|
||||
code: updateSupplierDto.code,
|
||||
id: Not(id),
|
||||
});
|
||||
|
||||
if (check) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_ACCEPTABLE,
|
||||
error: 'Supplier Already Exist',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const supplierData = new Supplier();
|
||||
|
||||
supplierData.name = updateSupplierDto.name;
|
||||
supplierData.status = true;
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
await manager.update(Supplier, { id: id }, supplierData);
|
||||
});
|
||||
|
||||
return supplierData;
|
||||
}
|
||||
|
||||
setStatus = async (id: string, type: string) => {
|
||||
const supplierData = new Supplier();
|
||||
|
||||
if (type === 'active') {
|
||||
supplierData.status = true;
|
||||
} else {
|
||||
supplierData.status = false;
|
||||
}
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
await manager.update(Supplier, { id: id }, supplierData);
|
||||
});
|
||||
|
||||
return supplierData;
|
||||
};
|
||||
|
||||
async findAllSupplier(page, pageSize?) {
|
||||
const baseQuery = this.supplierRepository
|
||||
.createQueryBuilder('supplier')
|
||||
.leftJoinAndMapOne(
|
||||
'supplier.coa',
|
||||
COA,
|
||||
'coa',
|
||||
`coa.supplier = supplier.id and coa.type = '2'`,
|
||||
)
|
||||
.leftJoinAndMapOne(
|
||||
'supplier.coa_undistribute',
|
||||
COA,
|
||||
'coa_undistribute',
|
||||
`coa_undistribute.supplier = supplier.id and coa_undistribute.type = '9'`,
|
||||
)
|
||||
.select(['supplier', 'coa.amount', 'coa_undistribute.amount']);
|
||||
|
||||
const data = await baseQuery
|
||||
.skip(page * (pageSize || 10))
|
||||
.take(pageSize || 10)
|
||||
.getMany();
|
||||
|
||||
const totalData = await baseQuery.getCount();
|
||||
|
||||
return {
|
||||
data,
|
||||
count: totalData,
|
||||
};
|
||||
}
|
||||
|
||||
async findByCode(code: string) {
|
||||
try {
|
||||
return await this.supplierRepository.findOneOrFail({
|
||||
code: code,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Supplier not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findByActive() {
|
||||
try {
|
||||
return await this.supplierRepository.findOneOrFail({
|
||||
status: true,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Supplier Data not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findByActiveAll() {
|
||||
try {
|
||||
return await this.supplierRepository.find({
|
||||
status: true,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Supplier Data not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,37 +1,185 @@
|
|||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Put,
|
||||
Param,
|
||||
Controller,
|
||||
Delete,
|
||||
ParseUUIDPipe,
|
||||
Get,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseUUIDPipe,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
Request,
|
||||
} from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { Public } from '../auth/public.decorator';
|
||||
import { CreateSupplierDto } from './dto/create-supplier.dto';
|
||||
import { SupplierService } from './supplier/supplier.service';
|
||||
import { PartnerService } from './partner/partner.service';
|
||||
import { CreatePartnerDto } from './dto/create-partner.dto';
|
||||
import { UpdatePartnerDto } from './dto/update-partner.dto';
|
||||
import { UpdateSupplierDto } from './dto/update-supplier.dto';
|
||||
|
||||
@Controller({
|
||||
path: 'users',
|
||||
version: '1',
|
||||
})
|
||||
export class UsersController {
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
constructor(
|
||||
private readonly usersService: UsersService,
|
||||
private readonly supplierService: SupplierService,
|
||||
private readonly partnerService: PartnerService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
async create(@Body() createUserDto: CreateUserDto) {
|
||||
async create(@Request() req, @Body() createUserDto: CreateUserDto) {
|
||||
return {
|
||||
data: await this.usersService.create(createUserDto),
|
||||
data: await this.usersService.create(createUserDto, req.user),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('supplier')
|
||||
async createSupplier(@Body() createPartnerDto: CreateSupplierDto) {
|
||||
return {
|
||||
data: await this.supplierService.create(createPartnerDto),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('supplier/:id/:type')
|
||||
async updateSupplier(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Param('type') type: string,
|
||||
) {
|
||||
return {
|
||||
data: await this.supplierService.setStatus(id, type),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put('supplier/:id')
|
||||
async setStatusSupplier(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Body() updatePartnerDto: UpdateSupplierDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.supplierService.update(id, updatePartnerDto),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('partner')
|
||||
async createPartner(
|
||||
@Request() req,
|
||||
@Body() createPartnerDto: CreatePartnerDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.partnerService.create(createPartnerDto, req.user),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('partner/:id/:type')
|
||||
async setStatusPartner(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Param('type') type: string,
|
||||
) {
|
||||
return {
|
||||
data: await this.partnerService.setStatus(id, type),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put('partner/:id')
|
||||
async updatePartner(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Request() req,
|
||||
@Body() updatePartnerDto: UpdatePartnerDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.partnerService.update(id, updatePartnerDto, req.user),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get()
|
||||
async findAll() {
|
||||
const [data, count] = await this.usersService.findAll();
|
||||
async findAll(@Request() req, @Query('page') page: number) {
|
||||
const data = await this.usersService.findAll(page, req.user.userId);
|
||||
|
||||
return {
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get('supplier')
|
||||
async findAllSupplier(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Request() req,
|
||||
) {
|
||||
const data = await this.supplierService.findAllSupplier(page, pageSize);
|
||||
|
||||
return {
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('partner')
|
||||
async findAllPartner(
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
@Request() req,
|
||||
) {
|
||||
const [data, count] = await this.partnerService.findAllPartner(
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
data,
|
||||
count,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('find-by-supperior')
|
||||
async findBySuperrior(@Request() req, @Query('page') page: number) {
|
||||
const data = await this.usersService.findBySuperrior(req.user.userId, page);
|
||||
|
||||
return {
|
||||
...data,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('find-by-roles/:id')
|
||||
async findByRoles(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Query('page') page: number,
|
||||
@Query('pageSize') pageSize: number,
|
||||
) {
|
||||
const [data, count] = await this.usersService.findByRoles(
|
||||
id,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
data,
|
||||
|
@ -50,13 +198,39 @@ export class UsersController {
|
|||
};
|
||||
}
|
||||
|
||||
@Get(':id/:type')
|
||||
async setStatusMembership(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Param('type') type: string,
|
||||
) {
|
||||
return {
|
||||
data: await this.usersService.setStatus(id, type),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
async update(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Request() req,
|
||||
@Body() updateUserDto: UpdateUserDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.usersService.update(id, updateUserDto),
|
||||
data: await this.usersService.update(id, updateUserDto, req.user),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Put('change-password/:id')
|
||||
async updatePassword(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Request() req,
|
||||
@Body() updateUserDto: UpdateUserDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.usersService.updatePassword(id, updateUserDto, req.user),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { UsersService } from './users.service';
|
||||
import { UsersController } from './users.controller';
|
||||
import { User } from './entities/user.entity';
|
||||
import { TransactionModule } from 'src/transaction/transaction.module';
|
||||
import { ConfigurableModule } from 'src/configurable/configurable.module';
|
||||
import { SupplierService } from './supplier/supplier.service';
|
||||
import { Supplier } from './entities/supplier.entity';
|
||||
import { Partner } from './entities/partner.entity';
|
||||
import { PartnerService } from './partner/partner.service';
|
||||
import { UserDetail } from './entities/user_detail.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([User])],
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([User, Supplier, Partner, UserDetail]),
|
||||
forwardRef(() => TransactionModule),
|
||||
ConfigurableModule,
|
||||
],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
providers: [UsersService, SupplierService, PartnerService],
|
||||
exports: [UsersService, SupplierService, PartnerService],
|
||||
})
|
||||
export class UsersModule {}
|
||||
|
|
|
@ -1,28 +1,224 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import {
|
||||
forwardRef,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Inject,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { Connection, EntityNotFoundError, Not, Repository } from 'typeorm';
|
||||
import { User } from './entities/user.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
|
||||
import { hashPassword } from '../helper/hash_password';
|
||||
import { CoaService } from 'src/transaction/coa.service';
|
||||
import { balanceType, coaType } from 'src/helper/enum-list';
|
||||
import { RoleService } from 'src/configurable/roles.service';
|
||||
import { InputCoaDto } from 'src/transaction/dto/input-coa.dto';
|
||||
import * as uuid from 'uuid';
|
||||
import { UserDetail } from './entities/user_detail.entity';
|
||||
import { COA } from '../transaction/entities/coa.entity';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(
|
||||
@InjectRepository(User)
|
||||
private usersRepository: Repository<User>,
|
||||
@InjectRepository(UserDetail)
|
||||
private userDetailRepository: Repository<UserDetail>,
|
||||
@Inject(
|
||||
forwardRef(() => {
|
||||
return CoaService;
|
||||
}),
|
||||
)
|
||||
private coaService: CoaService,
|
||||
private roleService: RoleService,
|
||||
private connection: Connection,
|
||||
) {}
|
||||
|
||||
async create(createUserDto: CreateUserDto) {
|
||||
const result = await this.usersRepository.insert(createUserDto);
|
||||
async create(createUserDto: CreateUserDto, currentUser: any) {
|
||||
const roles = await this.roleService.findOne(createUserDto.roleId);
|
||||
const superior = await this.findByUsername(currentUser.username);
|
||||
|
||||
return this.usersRepository.findOneOrFail(result.identifiers[0].id);
|
||||
const check = await this.usersRepository.findOne({
|
||||
username: createUserDto.username,
|
||||
});
|
||||
|
||||
if (check) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_ACCEPTABLE,
|
||||
error: 'Username Already Exist',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return this.usersRepository.findAndCount();
|
||||
const salt = randomStringGenerator();
|
||||
|
||||
const userData = new User();
|
||||
|
||||
userData.id = uuid.v4();
|
||||
userData.username = createUserDto.username;
|
||||
userData.password = await hashPassword(createUserDto.password, salt);
|
||||
userData.salt = salt;
|
||||
if (createUserDto.superior) {
|
||||
userData.superior = superior;
|
||||
} else {
|
||||
userData.superior = null;
|
||||
userData.partner = createUserDto.partner;
|
||||
}
|
||||
userData.roles = roles;
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
const result = await manager.insert(User, userData);
|
||||
|
||||
const userDetailData = new UserDetail();
|
||||
userDetailData.name = createUserDto.name;
|
||||
userDetailData.phone_number = createUserDto.phone_number;
|
||||
userDetailData.user = userData;
|
||||
const user_detail = await manager.insert(UserDetail, userDetailData);
|
||||
|
||||
const dataCoaWallet = new InputCoaDto();
|
||||
dataCoaWallet.user = userData;
|
||||
dataCoaWallet.balanceType = balanceType.CREDIT;
|
||||
dataCoaWallet.type = coaType.WALLET;
|
||||
dataCoaWallet.coaEntityManager = manager;
|
||||
await this.coaService.create(dataCoaWallet);
|
||||
|
||||
const dataCoaAR = new InputCoaDto();
|
||||
dataCoaAR.user = userData;
|
||||
dataCoaAR.balanceType = balanceType.DEBIT;
|
||||
dataCoaAR.relatedUserId = superior.id;
|
||||
dataCoaAR.type = coaType.ACCOUNT_RECEIVABLE;
|
||||
dataCoaAR.coaEntityManager = manager;
|
||||
await this.coaService.create(dataCoaAR);
|
||||
|
||||
if (roles.name == 'Supervisor' || roles.name == 'Sales') {
|
||||
const dataCOAProfit = new InputCoaDto();
|
||||
dataCOAProfit.user = userData;
|
||||
dataCOAProfit.balanceType = balanceType.CREDIT;
|
||||
dataCOAProfit.type = coaType.PROFIT;
|
||||
dataCOAProfit.coaEntityManager = manager;
|
||||
await this.coaService.create(dataCOAProfit);
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
if (createUserDto.superior) {
|
||||
const dataCoaAP = new InputCoaDto();
|
||||
dataCoaAP.user = userData;
|
||||
dataCoaAP.balanceType = balanceType.CREDIT;
|
||||
dataCoaAP.relatedUserId = superior.id;
|
||||
dataCoaAP.type = coaType.ACCOUNT_PAYABLE;
|
||||
dataCoaAP.coaEntityManager = manager;
|
||||
await this.coaService.create(dataCoaAP);
|
||||
}
|
||||
});
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
async findAll(page: number, id: string) {
|
||||
const baseQuery = this.usersRepository
|
||||
.createQueryBuilder('user')
|
||||
.where('user.id != :id', {
|
||||
id: id,
|
||||
})
|
||||
.leftJoinAndSelect('user.roles', 'roles', `roles.id = user.roles_id`)
|
||||
.leftJoinAndMapOne(
|
||||
'user.user_detail',
|
||||
UserDetail,
|
||||
'user_detail',
|
||||
`user_detail.user = user.id`,
|
||||
)
|
||||
.leftJoinAndMapOne(
|
||||
'user.coa',
|
||||
COA,
|
||||
'coa',
|
||||
`coa.user = user.id and coa.type = '0'`,
|
||||
)
|
||||
.select([
|
||||
'user.id',
|
||||
'user.username',
|
||||
'user.isActive',
|
||||
'user.createdAt',
|
||||
'roles.id',
|
||||
'roles.name',
|
||||
'user_detail',
|
||||
'coa.amount',
|
||||
]);
|
||||
|
||||
const data = await baseQuery
|
||||
.orderBy('user.createdAt', 'DESC')
|
||||
.skip(page * 10)
|
||||
.take(10)
|
||||
.getMany();
|
||||
|
||||
const totalData = await baseQuery.getCount();
|
||||
|
||||
return {
|
||||
data,
|
||||
count: totalData,
|
||||
};
|
||||
}
|
||||
|
||||
findByRoles(relationId: string, page: number, pageSize?: number) {
|
||||
return this.usersRepository.findAndCount({
|
||||
skip: page * (pageSize || 10),
|
||||
take: pageSize || 10,
|
||||
where: {
|
||||
roles: relationId,
|
||||
},
|
||||
order: {
|
||||
updatedAt: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async findBySuperrior(superriorId: string, page: number) {
|
||||
const baseQuery = this.usersRepository
|
||||
.createQueryBuilder('user')
|
||||
.where('user.id != :id and user.superior_id = :superior', {
|
||||
id: superriorId,
|
||||
superior: superriorId,
|
||||
})
|
||||
.leftJoinAndSelect('user.roles', 'roles', `roles.id = user.roles_id`)
|
||||
.leftJoinAndMapOne(
|
||||
'user.user_detail',
|
||||
UserDetail,
|
||||
'user_detail',
|
||||
`user_detail.user = user.id`,
|
||||
)
|
||||
.leftJoinAndMapOne(
|
||||
'user.coa',
|
||||
COA,
|
||||
'coa',
|
||||
`coa.user = user.id and coa.type = '0'`,
|
||||
)
|
||||
.select([
|
||||
'user.id',
|
||||
'user.username',
|
||||
'user.isActive',
|
||||
'roles.id',
|
||||
'roles.name',
|
||||
'user_detail',
|
||||
'coa.amount',
|
||||
]);
|
||||
|
||||
const data = await baseQuery
|
||||
.skip(page * 10)
|
||||
.take(10)
|
||||
.getMany();
|
||||
|
||||
const totalData = await baseQuery.getCount();
|
||||
|
||||
return {
|
||||
data,
|
||||
count: totalData,
|
||||
};
|
||||
}
|
||||
|
||||
async findExist(id: string) {
|
||||
try {
|
||||
return await this.usersRepository.findOneOrFail(id);
|
||||
} catch (e) {
|
||||
|
@ -30,7 +226,7 @@ export class UsersService {
|
|||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
error: 'User not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
|
@ -40,15 +236,92 @@ export class UsersService {
|
|||
}
|
||||
}
|
||||
|
||||
async update(id: string, updateUserDto: UpdateUserDto) {
|
||||
async findByUsername(username: string) {
|
||||
try {
|
||||
await this.usersRepository.findOneOrFail(id);
|
||||
return await this.usersRepository.findOneOrFail({
|
||||
where: {
|
||||
username: username,
|
||||
},
|
||||
relations: ['superior', 'roles', 'partner'],
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
error: 'User not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
const coa = await this.coaService.findByUser(id, coaType.WALLET);
|
||||
|
||||
try {
|
||||
const userData = await this.usersRepository
|
||||
.createQueryBuilder('users')
|
||||
.leftJoinAndSelect('users.roles', 'roles')
|
||||
.leftJoinAndSelect('users.superior', 'superior')
|
||||
.leftJoinAndSelect('users.userDetail', 'userDetail')
|
||||
.where('users.id = :id', {
|
||||
id: id,
|
||||
})
|
||||
.select([
|
||||
'users.id',
|
||||
'users.username',
|
||||
'users.isActive',
|
||||
'users.createdAt',
|
||||
'roles.id',
|
||||
'roles.name',
|
||||
'superior.id',
|
||||
'superior.username',
|
||||
'userDetail.id',
|
||||
'userDetail.name',
|
||||
'userDetail.phone_number',
|
||||
])
|
||||
.getOne();
|
||||
|
||||
return {
|
||||
...userData,
|
||||
wallet: coa.amount,
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'User not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: string, updateUserDto: UpdateUserDto, currentUser: any) {
|
||||
let userData;
|
||||
let userDetailData;
|
||||
|
||||
try {
|
||||
userData = await this.usersRepository.findOneOrFail(id);
|
||||
userDetailData = await this.userDetailRepository.findOneOrFail({
|
||||
where: {
|
||||
user: userData,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'User not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
|
@ -57,11 +330,78 @@ export class UsersService {
|
|||
}
|
||||
}
|
||||
|
||||
const result = await this.usersRepository.update(id, updateUserDto);
|
||||
const check = await this.usersRepository.findOne({
|
||||
username: updateUserDto.username,
|
||||
id: Not(id),
|
||||
});
|
||||
|
||||
return this.usersRepository.findOneOrFail(id);
|
||||
if (check) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_ACCEPTABLE,
|
||||
error: 'Username Already Exist',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
userData.username = updateUserDto.username;
|
||||
userData.partner = updateUserDto.partner;
|
||||
userDetailData.name = updateUserDto.name;
|
||||
userDetailData.phone_number = updateUserDto.phone_number;
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
await manager.save(userData);
|
||||
await manager.save(userDetailData);
|
||||
});
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
async updatePassword(
|
||||
id: string,
|
||||
updateUserDto: UpdateUserDto,
|
||||
currentUser: any,
|
||||
) {
|
||||
try {
|
||||
const dataUser = await this.usersRepository.findOneOrFail(id);
|
||||
dataUser.password = await hashPassword(
|
||||
updateUserDto.password,
|
||||
dataUser.salt,
|
||||
);
|
||||
const result = await this.usersRepository.save(dataUser);
|
||||
return dataUser;
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'User not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setStatus = async (id: string, type: string) => {
|
||||
const userData = new User();
|
||||
|
||||
if (type === 'active') {
|
||||
userData.isActive = true;
|
||||
} else {
|
||||
userData.isActive = false;
|
||||
}
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
await manager.update(User, { id: id }, userData);
|
||||
});
|
||||
|
||||
return userData;
|
||||
};
|
||||
|
||||
async remove(id: string) {
|
||||
try {
|
||||
await this.usersRepository.findOneOrFail(id);
|
||||
|
@ -70,7 +410,7 @@ export class UsersService {
|
|||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
error: 'User not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
|
@ -81,4 +421,37 @@ export class UsersService {
|
|||
|
||||
await this.usersRepository.delete(id);
|
||||
}
|
||||
|
||||
async findOneByUsername(username: string) {
|
||||
return this.usersRepository.findOneOrFail({
|
||||
where: {
|
||||
username,
|
||||
isActive: true,
|
||||
},
|
||||
relations: ['roles', 'partner'],
|
||||
});
|
||||
}
|
||||
|
||||
async findOneByPartner(partnerId: string) {
|
||||
try {
|
||||
return this.usersRepository.findOneOrFail({
|
||||
relations: ['roles'],
|
||||
where: {
|
||||
partner: partnerId,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'User not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
309
yarn.lock
309
yarn.lock
|
@ -675,11 +675,24 @@
|
|||
tslib "2.3.1"
|
||||
uuid "8.3.2"
|
||||
|
||||
"@nestjs/jwt@^8.0.0":
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@nestjs/jwt/-/jwt-8.0.0.tgz#6c811c17634252dd1dcd5dabf409dbd692b812da"
|
||||
integrity sha512-fz2LQgYY2zmuD8S+8UE215anwKyXlnB/1FwJQLVR47clNfMeFMK8WCxmn6xdPhF5JKuV1crO6FVabb1qWzDxqQ==
|
||||
dependencies:
|
||||
"@types/jsonwebtoken" "8.5.4"
|
||||
jsonwebtoken "8.5.1"
|
||||
|
||||
"@nestjs/mapped-types@*":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.0.tgz"
|
||||
integrity sha512-26AW5jHadLXtvHs+M+Agd9KZ92dDlBrmD0rORlBlvn2KvsWs4JRaKl2mUsrW7YsdZeAu3Hc4ukqyYyDdyCmMWQ==
|
||||
|
||||
"@nestjs/passport@^8.0.1":
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@nestjs/passport/-/passport-8.0.1.tgz#f1ed39a19489f794d1fe3fef592b4523bc48da68"
|
||||
integrity sha512-vn/ZJLXQKvSf9D0BvEoNFJLfzl9AVqfGtDyQMfWDLbaNpoEB2FyeaHGxdiX6H71oLSrQV78c/yuhfantzwdjdg==
|
||||
|
||||
"@nestjs/platform-express@^8.0.0":
|
||||
version "8.2.3"
|
||||
resolved "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-8.2.3.tgz"
|
||||
|
@ -904,7 +917,7 @@
|
|||
"@types/qs" "*"
|
||||
"@types/range-parser" "*"
|
||||
|
||||
"@types/express@^4.17.13":
|
||||
"@types/express@*", "@types/express@^4.17.13":
|
||||
version "4.17.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
|
||||
integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
|
||||
|
@ -958,11 +971,32 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
||||
|
||||
"@types/jsonwebtoken@*":
|
||||
version "8.5.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.6.tgz#1913e5a61e70a192c5a444623da4901a7b1a9d42"
|
||||
integrity sha512-+P3O/xC7nzVizIi5VbF34YtqSonFsdnbXBnWUCYRiKOi1f9gA4sEFvXkrGr/QVV23IbMYvcoerI7nnhDUiWXRQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/jsonwebtoken@8.5.4":
|
||||
version "8.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz#50ccaf0aa6f5d7b9956e70fe323b76e582991913"
|
||||
integrity sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/mime@^1":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
||||
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
|
||||
|
||||
"@types/multer@^1.4.7":
|
||||
version "1.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.7.tgz#89cf03547c28c7bbcc726f029e2a76a7232cc79e"
|
||||
integrity sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
|
||||
"@types/node@*", "@types/node@^16.0.0":
|
||||
version "16.11.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.10.tgz#2e3ad0a680d96367103d3e670d41c2fed3da61ae"
|
||||
|
@ -973,6 +1007,39 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||
|
||||
"@types/passport-jwt@^3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport-jwt/-/passport-jwt-3.0.6.tgz#41cc8b5803d5f5f06eb33e19c453b42716def4f1"
|
||||
integrity sha512-cmAAMIRTaEwpqxlrZyiEY9kdibk94gP5KTF8AT1Ra4rWNZYHNMreqhKUEeC5WJtuN5SJZjPQmV+XO2P5PlnvNQ==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
"@types/jsonwebtoken" "*"
|
||||
"@types/passport-strategy" "*"
|
||||
|
||||
"@types/passport-local@^1.0.34":
|
||||
version "1.0.34"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport-local/-/passport-local-1.0.34.tgz#84d3b35b2fd4d36295039ded17fe5f3eaa62f4f6"
|
||||
integrity sha512-PSc07UdYx+jhadySxxIYWuv6sAnY5e+gesn/5lkPKfBeGuIYn9OPR+AAEDq73VRUh6NBTpvE/iPE62rzZUslog==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
"@types/passport" "*"
|
||||
"@types/passport-strategy" "*"
|
||||
|
||||
"@types/passport-strategy@*":
|
||||
version "0.2.35"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport-strategy/-/passport-strategy-0.2.35.tgz#e52f5212279ea73f02d9b06af67efe9cefce2d0c"
|
||||
integrity sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
"@types/passport" "*"
|
||||
|
||||
"@types/passport@*":
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport/-/passport-1.0.7.tgz#85892f14932168158c86aecafd06b12f5439467a"
|
||||
integrity sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
|
||||
"@types/prettier@^2.1.5":
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281"
|
||||
|
@ -1435,6 +1502,16 @@ argparse@^2.0.1:
|
|||
resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
|
||||
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
||||
|
||||
args@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/args/-/args-5.0.1.tgz#4bf298df90a4799a09521362c579278cc2fdd761"
|
||||
integrity sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==
|
||||
dependencies:
|
||||
camelcase "5.0.0"
|
||||
chalk "2.4.2"
|
||||
leven "2.1.0"
|
||||
mri "1.1.4"
|
||||
|
||||
array-flatten@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz"
|
||||
|
@ -1475,7 +1552,7 @@ avvio@^7.1.2:
|
|||
fastq "^1.6.1"
|
||||
queue-microtask "^1.1.2"
|
||||
|
||||
axios@0.24.0:
|
||||
axios@0.24.0, axios@^0.24.0:
|
||||
version "0.24.0"
|
||||
resolved "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz"
|
||||
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
|
||||
|
@ -1567,6 +1644,11 @@ bl@^4.1.0:
|
|||
inherits "^2.0.4"
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
bluebird@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
||||
|
||||
body-parser@1.19.0:
|
||||
version "1.19.0"
|
||||
resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz"
|
||||
|
@ -1628,6 +1710,11 @@ bser@2.1.1:
|
|||
dependencies:
|
||||
node-int64 "^0.4.0"
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
|
||||
|
@ -1680,6 +1767,11 @@ callsites@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
camelcase@5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
|
||||
integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
|
||||
|
||||
camelcase@^5.3.1:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
|
@ -1695,6 +1787,15 @@ caniuse-lite@^1.0.30001280:
|
|||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz#8573685bdae4d733ef18f78d44ba0ca5fe9e896b"
|
||||
integrity sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg==
|
||||
|
||||
chalk@2.4.2, chalk@^2.0.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
|
||||
|
@ -1714,15 +1815,6 @@ chalk@^1.1.1:
|
|||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^2.0.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
|
||||
|
@ -1872,6 +1964,11 @@ color-name@~1.1.4:
|
|||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
colorette@^2.0.7:
|
||||
version "2.0.16"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da"
|
||||
integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==
|
||||
|
||||
colors@^1.1.2:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
||||
|
@ -1996,6 +2093,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
|||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
crypto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037"
|
||||
integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==
|
||||
|
||||
cssom@^0.4.4:
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
|
||||
|
@ -2013,6 +2115,13 @@ cssstyle@^2.3.0:
|
|||
dependencies:
|
||||
cssom "~0.3.6"
|
||||
|
||||
csv-parser@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/csv-parser/-/csv-parser-3.0.0.tgz#b88a6256d79e090a97a1b56451f9327b01d710e7"
|
||||
integrity sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
data-urls@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b"
|
||||
|
@ -2022,6 +2131,11 @@ data-urls@^2.0.0:
|
|||
whatwg-mimetype "^2.3.0"
|
||||
whatwg-url "^8.0.0"
|
||||
|
||||
dateformat@^4.6.3:
|
||||
version "4.6.3"
|
||||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5"
|
||||
integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==
|
||||
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
|
||||
|
@ -2036,7 +2150,7 @@ debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
|
|||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
decimal.js@^10.2.1:
|
||||
decimal.js@^10.2.1, decimal.js@^10.3.1:
|
||||
version "10.3.1"
|
||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
|
||||
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
|
||||
|
@ -2147,6 +2261,13 @@ duplexify@^4.1.2:
|
|||
readable-stream "^3.1.1"
|
||||
stream-shift "^1.0.0"
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
|
||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
|
||||
|
@ -2729,7 +2850,7 @@ fresh@0.5.2:
|
|||
resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
|
||||
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
||||
|
||||
fs-extra@10.0.0:
|
||||
fs-extra@10.0.0, fs-extra@^10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1"
|
||||
integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==
|
||||
|
@ -2758,6 +2879,11 @@ fs.realpath@^1.0.0:
|
|||
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
fs@^0.0.1-security:
|
||||
version "0.0.1-security"
|
||||
resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4"
|
||||
integrity sha1-invTcYa23d84E/I4WLV+yq9eQdQ=
|
||||
|
||||
fsevents@^2.3.2, fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
|
@ -3631,6 +3757,11 @@ joi@^17.4.2:
|
|||
"@sideway/formula" "^3.0.0"
|
||||
"@sideway/pinpoint" "^2.0.0"
|
||||
|
||||
joycon@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03"
|
||||
integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
|
@ -3742,11 +3873,49 @@ jsonfile@^6.0.1:
|
|||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
jsonwebtoken@8.5.1, jsonwebtoken@^8.2.0:
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
|
||||
dependencies:
|
||||
jws "^3.2.2"
|
||||
lodash.includes "^4.3.0"
|
||||
lodash.isboolean "^3.0.3"
|
||||
lodash.isinteger "^4.0.4"
|
||||
lodash.isnumber "^3.0.3"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
lodash.isstring "^4.0.1"
|
||||
lodash.once "^4.0.0"
|
||||
ms "^2.1.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
jwa@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
|
||||
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
|
||||
dependencies:
|
||||
buffer-equal-constant-time "1.0.1"
|
||||
ecdsa-sig-formatter "1.0.11"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
jws@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
||||
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
||||
dependencies:
|
||||
jwa "^1.4.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
kleur@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
||||
|
||||
leven@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
|
||||
integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA=
|
||||
|
||||
leven@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
|
||||
|
@ -3820,6 +3989,36 @@ lodash.has@4.5.2:
|
|||
resolved "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz"
|
||||
integrity sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=
|
||||
|
||||
lodash.includes@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
|
||||
|
||||
lodash.isboolean@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
|
||||
|
||||
lodash.isinteger@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
||||
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
|
||||
|
||||
lodash.isnumber@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
||||
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||
|
||||
lodash.isstring@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
|
||||
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
|
||||
|
||||
lodash.memoize@4.x:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
|
@ -3830,6 +4029,11 @@ lodash.merge@^4.6.2:
|
|||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash.once@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||
|
||||
lodash.set@4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz"
|
||||
|
@ -3991,6 +4195,11 @@ mkdirp@^1.0.4:
|
|||
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
mri@1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a"
|
||||
integrity sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
||||
|
@ -4006,6 +4215,11 @@ ms@2.1.2:
|
|||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
multer@1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz"
|
||||
|
@ -4266,6 +4480,34 @@ parseurl@~1.3.3:
|
|||
resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"
|
||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
||||
|
||||
passport-jwt@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-jwt/-/passport-jwt-4.0.0.tgz#7f0be7ba942e28b9f5d22c2ebbb8ce96ef7cf065"
|
||||
integrity sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==
|
||||
dependencies:
|
||||
jsonwebtoken "^8.2.0"
|
||||
passport-strategy "^1.0.0"
|
||||
|
||||
passport-local@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee"
|
||||
integrity sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
|
||||
passport-strategy@1.x.x, passport-strategy@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
|
||||
integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=
|
||||
|
||||
passport@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/passport/-/passport-0.5.0.tgz#7914aaa55844f9dce8c3aa28f7d6b73647ee0169"
|
||||
integrity sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
pause "0.0.1"
|
||||
|
||||
path-exists@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||
|
@ -4306,6 +4548,11 @@ path-type@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||
|
||||
pause@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d"
|
||||
integrity sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=
|
||||
|
||||
pg-connection-string@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz"
|
||||
|
@ -4367,7 +4614,7 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
|
|||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
|
||||
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
|
||||
|
||||
pino-abstract-transport@v0.5.0:
|
||||
pino-abstract-transport@^0.5.0, pino-abstract-transport@v0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0"
|
||||
integrity sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==
|
||||
|
@ -4385,6 +4632,24 @@ pino-http@^6.3.0:
|
|||
pino "^7.0.5"
|
||||
pino-std-serializers "^5.0.0"
|
||||
|
||||
pino-pretty@^7.3.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-7.3.0.tgz#277fdc2306a2f6d727a1127e7747d1c078efdd4b"
|
||||
integrity sha512-HAhShJ2z2QzxXhYAn6XfwYpF13o1PQbjzSNA9q+30FAvhjOmeACit9lprhV/mCOw/8YFWSyyNk0YCq2EDYGYpw==
|
||||
dependencies:
|
||||
args "^5.0.1"
|
||||
colorette "^2.0.7"
|
||||
dateformat "^4.6.3"
|
||||
fast-safe-stringify "^2.0.7"
|
||||
joycon "^3.1.1"
|
||||
pino-abstract-transport "^0.5.0"
|
||||
pump "^3.0.0"
|
||||
readable-stream "^3.6.0"
|
||||
rfdc "^1.3.0"
|
||||
secure-json-parse "^2.4.0"
|
||||
sonic-boom "^2.2.0"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
pino-std-serializers@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz"
|
||||
|
@ -4720,7 +4985,7 @@ reusify@^1.0.4:
|
|||
resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz"
|
||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||
|
||||
rfdc@^1.1.4, rfdc@^1.2.0:
|
||||
rfdc@^1.1.4, rfdc@^1.2.0, rfdc@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz"
|
||||
integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
|
||||
|
@ -4815,7 +5080,7 @@ schema-utils@^3.1.0, schema-utils@^3.1.1:
|
|||
ajv "^6.12.5"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
secure-json-parse@^2.0.0:
|
||||
secure-json-parse@^2.0.0, secure-json-parse@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz"
|
||||
integrity sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==
|
||||
|
@ -4832,6 +5097,11 @@ semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
|
|||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
semver@^6.0.0, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
|
@ -4953,6 +5223,13 @@ sonic-boom@^1.0.2:
|
|||
atomic-sleep "^1.0.0"
|
||||
flatstr "^1.0.12"
|
||||
|
||||
sonic-boom@^2.2.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.4.1.tgz#6e1c3762c677425c6ffbd7bd106c4f8258b45b39"
|
||||
integrity sha512-WgtVLfGl347/zS1oTuLaOAvVD5zijgjphAJHgbbnBJGgexnr+C1ULSj0j7ftoGxpuxR4PaV635NkwFemG8m/5w==
|
||||
dependencies:
|
||||
atomic-sleep "^1.0.0"
|
||||
|
||||
sonic-boom@^2.2.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.3.1.tgz#e6572184fb3adf145dbfeccff48141bbd1009e4c"
|
||||
|
|
Loading…
Reference in New Issue
Block a user