Merge branch 'development' into 'master'

Development

See merge request empatnusabangsa/ppob/ppob-backend!66
This commit is contained in:
ilham dwi pratama 2021-12-25 17:44:22 +00:00
commit c663dbda92
85 changed files with 5033 additions and 346 deletions

15
.vscode/launch.json vendored Normal file
View 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}"
}
]
}

View File

@ -15,12 +15,19 @@ spec:
spec: spec:
containers: containers:
- name: ppob-backend - 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: ports:
- containerPort: 5000 - containerPort: 5000
envFrom: envFrom:
- secretRef: - secretRef:
name: ppob-backend-env name: ppob-backend-env
volumeMounts:
- name: storage
mountPath: /home/node/files
volumes:
- name: storage
persistentVolumeClaim:
claimName: ppob-backend-pvc
imagePullSecrets: imagePullSecrets:
- name: regcred - name: regcred

13
k8s/staging/pvc.yaml Normal file
View 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

View File

@ -10,7 +10,8 @@
"build": "nest build", "build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start", "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:debug": "nest start --debug --watch",
"start:prod": "node dist/main", "start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
@ -24,16 +25,30 @@
"@nestjs/common": "^8.0.0", "@nestjs/common": "^8.0.0",
"@nestjs/config": "^1.0.1", "@nestjs/config": "^1.0.1",
"@nestjs/core": "^8.0.0", "@nestjs/core": "^8.0.0",
"@nestjs/jwt": "^8.0.0",
"@nestjs/mapped-types": "*", "@nestjs/mapped-types": "*",
"@nestjs/passport": "^8.0.1",
"@nestjs/platform-express": "^8.0.0", "@nestjs/platform-express": "^8.0.0",
"@nestjs/platform-fastify": "^8.0.8", "@nestjs/platform-fastify": "^8.0.8",
"@nestjs/typeorm": "^8.0.2", "@nestjs/typeorm": "^8.0.2",
"axios": "^0.24.0",
"bluebird": "^3.7.2",
"class-transformer": "^0.4.0", "class-transformer": "^0.4.0",
"class-validator": "^0.13.1", "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", "joi": "^17.4.2",
"lodash": "^4.17.21",
"nestjs-pino": "^2.3.1", "nestjs-pino": "^2.3.1",
"passport": "^0.5.0",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"pg": "^8.7.1", "pg": "^8.7.1",
"pino-http": "^6.3.0", "pino-http": "^6.3.0",
"pino-pretty": "^7.3.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rxjs": "^7.2.0", "rxjs": "^7.2.0",
@ -46,7 +61,10 @@
"@nestjs/testing": "^8.0.0", "@nestjs/testing": "^8.0.0",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/jest": "^27.0.1", "@types/jest": "^27.0.1",
"@types/multer": "^1.4.7",
"@types/node": "^16.0.0", "@types/node": "^16.0.0",
"@types/passport-jwt": "^3.0.6",
"@types/passport-local": "^1.0.34",
"@types/supertest": "^2.0.11", "@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^4.28.2", "@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2", "@typescript-eslint/parser": "^4.28.2",

View File

@ -6,8 +6,11 @@ import { UsersModule } from './users/users.module';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
import { LoggerModule } from 'nestjs-pino'; import { LoggerModule } from 'nestjs-pino';
import { TransactionModule } from './transaction/transaction.module'; import { TransactionModule } from './transaction/transaction.module';
import configuration from './config/configuration'; import { ProductModule } from './product/product.module';
import { ConfigurableModule } from './configurable/configurable.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({ @Module({
imports: [ imports: [
@ -19,14 +22,18 @@ import { ConfigurableModule } from './configurable/configurable.module';
.valid('development', 'production', 'test', 'provision') .valid('development', 'production', 'test', 'provision')
.default('development'), .default('development'),
PORT: Joi.number().default(3000), PORT: Joi.number().default(3000),
DATABASE_CLIENT: Joi.valid('mysql', 'postgres'), SECRET: Joi.string().required(),
DATABASE_HOST: Joi.string(), DATABASE_CLIENT: Joi.valid('mysql', 'postgres').required(),
DATABASE_NAME: Joi.string(), DATABASE_HOST: Joi.string().required(),
DATABASE_USERNAME: Joi.string(), DATABASE_NAME: Joi.string().required(),
DATABASE_USERNAME: Joi.string().required(),
DATABASE_PASSWORD: Joi.string().empty('').default(''), DATABASE_PASSWORD: Joi.string().empty('').default(''),
DATABASE_PORT: Joi.number().default(5432), DATABASE_PORT: Joi.number().default(5432),
}), }),
}), }),
MulterModule.register({
dest: './files',
}),
TypeOrmModule.forRootAsync({ TypeOrmModule.forRootAsync({
imports: [ConfigModule], imports: [ConfigModule],
useFactory: (configService: ConfigService) => { useFactory: (configService: ConfigService) => {
@ -49,6 +56,8 @@ import { ConfigurableModule } from './configurable/configurable.module';
UsersModule, UsersModule,
TransactionModule, TransactionModule,
ConfigurableModule, ConfigurableModule,
ProductModule,
AuthModule,
], ],
}) })
export class AppModule {} export class AppModule {}

View 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();
});
});

View 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
View 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 {}

View 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
View 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);
};
}

View 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
View 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,
};
}
}

View File

@ -0,0 +1,5 @@
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}

View 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;
}
}

View File

@ -0,0 +1,4 @@
import { SetMetadata } from '@nestjs/common';
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

View File

@ -1,6 +1,7 @@
export default () => { export default () => {
return { return {
port: parseInt(process.env.PORT, 10) || 3000, port: parseInt(process.env.PORT, 10) || 3000,
secret: process.env.SECRET,
database: { database: {
client: process.env.DATABASE_CLIENT, client: process.env.DATABASE_CLIENT,
host: process.env.DATABASE_HOST, host: process.env.DATABASE_HOST,
@ -9,5 +10,7 @@ export default () => {
password: process.env.DATABASE_PASSWORD || '', password: process.env.DATABASE_PASSWORD || '',
name: process.env.DATABASE_NAME, name: process.env.DATABASE_NAME,
}, },
upload_dir: __dirname + '/../uploads',
upload_url_path: '/files/',
}; };
}; };

View 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);
}
}

View File

@ -1,6 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { ConfigurableController } from './configurable.controller'; import { ConfigurableController } from './configurable.controller';
import { ConfigurableService } from './configurable.service'; import { RoleService } from './roles.service';
describe('ConfigurableController', () => { describe('ConfigurableController', () => {
let controller: ConfigurableController; let controller: ConfigurableController;
@ -8,7 +8,7 @@ describe('ConfigurableController', () => {
beforeEach(async () => { beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
controllers: [ConfigurableController], controllers: [ConfigurableController],
providers: [ConfigurableService], providers: [RoleService],
}).compile(); }).compile();
controller = module.get<ConfigurableController>(ConfigurableController); controller = module.get<ConfigurableController>(ConfigurableController);

View File

@ -1,26 +1,40 @@
import { import {
Body,
Controller, Controller,
Get, Get,
Post,
Body,
Put,
Param,
Delete,
ParseUUIDPipe,
HttpStatus, HttpStatus,
Param,
ParseUUIDPipe,
Post,
Put,
Query,
Res,
UploadedFile,
UseInterceptors,
} from '@nestjs/common'; } 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({ @Controller({
path: 'config', path: 'config',
version: '1', version: '1',
}) })
export class ConfigurableController { export class ConfigurableController {
constructor(private readonly usersService: ConfigurableService) {} constructor(
private readonly roleService: RoleService,
private readonly commissionService: CommissionService,
) {}
@Get() @Get('/roles')
async findAll() { async findAll(
const [data, count] = await this.usersService.findAll(); @Query('page') page: number,
@Query('pageSize') pageSize: number,
) {
const [data, count] = await this.roleService.findAllRoles(page, pageSize);
return { return {
data, data,
@ -30,13 +44,84 @@ export class ConfigurableController {
}; };
} }
@Get(':id') @Get('/commission')
async findOne(@Param('id', ParseUUIDPipe) id: string) { async findCommission(
@Query('page') page: number,
@Query('pageSize') pageSize: number,
) {
const [data, count] = await this.commissionService.findAllCommission(
page,
pageSize,
);
return { return {
data: await this.usersService.findOne(id), data,
count,
statusCode: HttpStatus.OK, statusCode: HttpStatus.OK,
message: 'success', 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',
};
}
} }

View File

@ -2,11 +2,14 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { Roles } from './entities/roles.entity'; import { Roles } from './entities/roles.entity';
import { ConfigurableController } from './configurable.controller'; 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({ @Module({
imports: [TypeOrmModule.forFeature([Roles])], imports: [TypeOrmModule.forFeature([Roles, CommissionSetting])],
controllers: [ConfigurableController], controllers: [ConfigurableController],
providers: [ConfigurableService], providers: [RoleService, CommissionService],
exports: [RoleService, CommissionService],
}) })
export class ConfigurableModule {} export class ConfigurableModule {}

View File

@ -1,15 +1,15 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { ConfigurableService } from './configurable.service'; import { RoleService } from './roles.service';
describe('ConfigurableService', () => { describe('RoleService', () => {
let service: ConfigurableService; let service: RoleService;
beforeEach(async () => { beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
providers: [ConfigurableService], providers: [RoleService],
}).compile(); }).compile();
service = module.get<ConfigurableService>(ConfigurableService); service = module.get<RoleService>(RoleService);
}); });
it('should be defined', () => { it('should be defined', () => {

View File

@ -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;
}
}
}
}

View 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;
}

View 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
View 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
View 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,
}

View 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}`);
};

View 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
View 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
View 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.', '')}`;
};

View 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;
// }
// }

View File

@ -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[];
}

View File

@ -1,17 +1,14 @@
import { NestFactory } from '@nestjs/core'; import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import {ValidationPipe, VersioningType} from '@nestjs/common'; import { ValidationPipe, VersioningType } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { Logger } from 'nestjs-pino'; import { Logger } from 'nestjs-pino';
import { NestExpressApplication } from '@nestjs/platform-express';
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>( const app = await NestFactory.create<NestExpressApplication>(
AppModule, AppModule,
new FastifyAdapter(), // new FastifyAdapter(),
{ bufferLogs: true }, { bufferLogs: true },
); );
@ -31,13 +28,14 @@ async function bootstrap() {
const configService = app.get<ConfigService>(ConfigService); const configService = app.get<ConfigService>(ConfigService);
const port = configService.get<number>('port'); const port = configService.get<number>('port');
await app.listen(port, '0.0.0.0', (error, address) => { await app.listen(port, '0.0.0.0', () => {
if (error) { logger.log('Service Started');
logger.error(error); // if (error) {
process.exit(1); // logger.error(error);
} else { // process.exit(1);
logger.log(`Server listening on ${address}`); // } else {
} // logger.log(`Server listening on ${address}`);
// }
}); });
} }

View File

@ -3,4 +3,7 @@ import { IsNotEmpty, IsUUID } from 'class-validator';
export class CreateCategoriesProductDto { export class CreateCategoriesProductDto {
@IsNotEmpty() @IsNotEmpty()
name: string; name: string;
@IsNotEmpty()
code: string;
} }

View File

@ -10,6 +10,15 @@ export class CreateProductDto {
@IsNotEmpty() @IsNotEmpty()
status: string; status: string;
@IsNotEmpty()
price: number;
@IsNotEmpty()
markUpPrice: number;
@IsUUID() @IsUUID()
subCategoriesId: string; subCategoriesId: string;
@IsUUID()
supplierId: string;
} }

View 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;
}

View 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),
) {}

View File

@ -0,0 +1,9 @@
import { IsNotEmpty, IsUUID } from 'class-validator';
export class UploadProductDto {
@IsNotEmpty()
fileName: string;
@IsNotEmpty()
supplierCode: string;
}

View File

@ -2,6 +2,12 @@ import { IsNotEmpty, IsUUID } from 'class-validator';
import { CreateCategoriesProductDto } from '../categories/create-categories-product.dto'; import { CreateCategoriesProductDto } from '../categories/create-categories-product.dto';
export class CreateSubCategoriesProductDto extends CreateCategoriesProductDto { export class CreateSubCategoriesProductDto extends CreateCategoriesProductDto {
@IsNotEmpty()
name: string;
@IsNotEmpty()
code: string;
@IsUUID() @IsUUID()
categoryId: string; categoryId: string;
} }

View File

@ -19,9 +19,14 @@ export class ProductCategories extends BaseModel {
@Column() @Column()
name: string; name: string;
@Column({
nullable: true,
})
code: string;
@OneToMany( @OneToMany(
() => ProductSubCategories, () => ProductSubCategories,
(subCategories) => subCategories.category, (subCategories) => subCategories.category,
) )
subCategories: ProductSubCategories; sub_categories: ProductSubCategories;
} }

View File

@ -1,21 +1,9 @@
import { import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
Entity,
Column,
PrimaryGeneratedColumn,
UpdateDateColumn,
DeleteDateColumn,
VersionColumn,
CreateDateColumn,
OneToMany,
ManyToOne,
} from 'typeorm';
import { Product } from './product.entity'; import { Product } from './product.entity';
import { BaseModel } from '../../config/basemodel.entity'; import { BaseModel } from '../../config/basemodel.entity';
import { productType } from '../../helper/enum-list';
enum Type { import { User } from '../../users/entities/user.entity';
NORMAL, import { Partner } from '../../users/entities/partner.entity';
PROMO,
}
@Entity() @Entity()
export class ProductHistoryPrice extends BaseModel { export class ProductHistoryPrice extends BaseModel {
@ -23,14 +11,26 @@ export class ProductHistoryPrice extends BaseModel {
id: string; id: string;
@ManyToOne(() => Product, (product) => product.id) @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' }) @Column({ type: 'date' })
startDate: string; startDate: Date;
@Column({ type: 'date' }) @Column({
endDate: string; type: 'date',
nullable: true,
})
endDate: Date;
@Column('text') @Column('text')
type: Type; type: productType;
} }

View File

@ -2,23 +2,29 @@ import {
Entity, Entity,
Column, Column,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
UpdateDateColumn,
DeleteDateColumn,
VersionColumn,
CreateDateColumn,
ManyToOne, ManyToOne,
OneToMany,
} from 'typeorm'; } from 'typeorm';
import { ProductCategories } from './product-category.entity'; import { ProductCategories } from './product-category.entity';
import { BaseModel } from '../../config/basemodel.entity'; import { BaseModel } from '../../config/basemodel.entity';
import { Product } from './product.entity';
@Entity() @Entity()
export class ProductSubCategories extends BaseModel{ export class ProductSubCategories extends BaseModel {
@PrimaryGeneratedColumn('uuid') @PrimaryGeneratedColumn('uuid')
id: string; id: string;
@Column() @Column()
name: string; name: string;
@ManyToOne(() => ProductCategories, (categories) => categories.subCategories) @Column({
nullable: true,
})
code: string;
@ManyToOne(() => ProductCategories, (categories) => categories.sub_categories)
category: ProductCategories; category: ProductCategories;
@OneToMany(() => Product, (product) => product.sub_categories)
product: Product;
} }

View File

@ -11,9 +11,11 @@ import {
} from 'typeorm'; } from 'typeorm';
import { ProductSubCategories } from './product-sub-category.entity'; import { ProductSubCategories } from './product-sub-category.entity';
import { BaseModel } from '../../config/basemodel.entity'; import { BaseModel } from '../../config/basemodel.entity';
import { Supplier } from '../../users/entities/supplier.entity';
import { ProductHistoryPrice } from './product-history-price.entity';
@Entity() @Entity()
export class Product extends BaseModel{ export class Product extends BaseModel {
@PrimaryGeneratedColumn('uuid') @PrimaryGeneratedColumn('uuid')
id: string; id: string;
@ -26,9 +28,45 @@ export class Product extends BaseModel{
@Column() @Column()
status: string; status: string;
@Column({
nullable: true,
})
price: number;
@Column({
nullable: true,
})
basePrice: number;
@ManyToOne( @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;
} }

View 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();
});
});

View 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;
}
}
}
}

View File

@ -13,6 +13,20 @@ export class ProductCategoriesService {
) {} ) {}
async create(CreateCategoriesProductDto: CreateCategoriesProductDto) { 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( const result = await this.productCategoriesRepository.insert(
CreateCategoriesProductDto, CreateCategoriesProductDto,
); );
@ -22,10 +36,10 @@ export class ProductCategoriesService {
); );
} }
findAll(page) { findAll(page, pageSize?) {
return this.productCategoriesRepository.findAndCount({ return this.productCategoriesRepository.findAndCount({
skip: page * 10, skip: page * (pageSize || 10),
take: 10, take: pageSize || 10,
order: { order: {
version: 'DESC', version: 'DESC',
}, },
@ -40,7 +54,29 @@ export class ProductCategoriesService {
throw new HttpException( throw new HttpException(
{ {
statusCode: HttpStatus.NOT_FOUND, 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, HttpStatus.NOT_FOUND,
); );
@ -61,7 +97,7 @@ export class ProductCategoriesService {
throw new HttpException( throw new HttpException(
{ {
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
error: 'Data not found', error: 'Product Categories not found',
}, },
HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND,
); );
@ -86,7 +122,7 @@ export class ProductCategoriesService {
throw new HttpException( throw new HttpException(
{ {
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
error: 'Data not found', error: 'Product Categories not found',
}, },
HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND,
); );

View File

@ -1,31 +1,86 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; 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 { ProductSubCategories } from './entities/product-sub-category.entity';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { CreateSubCategoriesProductDto } from './dto/sub-categories/create-sub-categories-product.dto'; import { CreateSubCategoriesProductDto } from './dto/sub-categories/create-sub-categories-product.dto';
import { UpdateSubCategoriesProductDto } from './dto/sub-categories/update-sub-categories-product.dto'; import { UpdateSubCategoriesProductDto } from './dto/sub-categories/update-sub-categories-product.dto';
import { ProductCategoriesService } from './product-categories.service';
@Injectable() @Injectable()
export class ProductSubCategoriesService { export class ProductSubCategoriesService {
constructor( constructor(
@InjectRepository(ProductSubCategories) @InjectRepository(ProductSubCategories)
private productSubCategoriesRepository: Repository<ProductSubCategories>, private productSubCategoriesRepository: Repository<ProductSubCategories>,
private productCategoriesService: ProductCategoriesService,
) {} ) {}
async create(CreateCategoriesProductDto: CreateSubCategoriesProductDto) { async create(createSubCategoriesProductDto: CreateSubCategoriesProductDto) {
const result = await this.productSubCategoriesRepository.insert( const check = await this.productSubCategoriesRepository.findOne({
CreateCategoriesProductDto, 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( return this.productSubCategoriesRepository.findOneOrFail(
result.identifiers[0].id, 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({ return this.productSubCategoriesRepository.findAndCount({
skip: page * 10, skip: page * 10,
take: 10, take: 10,
where: {
category: category,
},
relations: ['category'],
order: { order: {
version: 'DESC', version: 'DESC',
}, },
@ -40,7 +95,7 @@ export class ProductSubCategoriesService {
throw new HttpException( throw new HttpException(
{ {
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
error: 'Data not found', error: 'Product Sub Categories not found',
}, },
HttpStatus.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( async update(
id: string, id: string,
updateCategoriesProductDto: UpdateSubCategoriesProductDto, updateCategoriesProductDto: UpdateSubCategoriesProductDto,
@ -61,7 +124,7 @@ export class ProductSubCategoriesService {
throw new HttpException( throw new HttpException(
{ {
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
error: 'Data not found', error: 'Product Sub Categories not found',
}, },
HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND,
); );
@ -70,11 +133,15 @@ export class ProductSubCategoriesService {
} }
} }
const result = await this.productSubCategoriesRepository.update( const categories = await this.productCategoriesService.findOne(
id, updateCategoriesProductDto.categoryId,
updateCategoriesProductDto,
); );
const result = await this.productSubCategoriesRepository.update(id, {
name: updateCategoriesProductDto.name,
category: categories,
});
return this.productSubCategoriesRepository.findOneOrFail(id); return this.productSubCategoriesRepository.findOneOrFail(id);
} }
@ -86,7 +153,7 @@ export class ProductSubCategoriesService {
throw new HttpException( throw new HttpException(
{ {
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
error: 'Data not found', error: 'Product Sub Categories not found',
}, },
HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND,
); );

View File

@ -1,17 +1,27 @@
import { import {
Controller,
Get,
Post,
Body, Body,
Put, Controller,
Param,
Delete, Delete,
Get,
HttpStatus,
Param,
ParseUUIDPipe, ParseUUIDPipe,
HttpStatus, Query, Post,
Put,
Query,
Request,
} from '@nestjs/common'; } from '@nestjs/common';
import { ProductService } from './product.service'; import { ProductService } from './product.service';
import { ProductCategoriesService } from './product-categories.service'; import { ProductCategoriesService } from './product-categories.service';
import { CreateCategoriesProductDto } from './dto/categories/create-categories-product.dto'; 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({ @Controller({
path: 'product', path: 'product',
@ -21,8 +31,19 @@ export class ProductController {
constructor( constructor(
private readonly productService: ProductService, private readonly productService: ProductService,
private readonly productCategoriesService: ProductCategoriesService, 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') @Post('categories')
async createCategories( async createCategories(
@Body() createCategoriesProductDto: CreateCategoriesProductDto, @Body() createCategoriesProductDto: CreateCategoriesProductDto,
@ -36,9 +57,116 @@ export class ProductController {
}; };
} }
@Get() @Post('sub-categories')
async findAll(@Query('page') page: number) { async createSubCategories(
const [data, count] = await this.productService.findAll(page); @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 { return {
data, data,
@ -48,13 +176,121 @@ export class ProductController {
}; };
} }
@Get(':id') @Get('sub-categories')
async findOne(@Param('id', ParseUUIDPipe) id: string) { 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 { return {
data: await this.productService.findOne(id), ...data,
statusCode: HttpStatus.OK, statusCode: HttpStatus.OK,
message: 'success', 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',
};
}
} }

View File

@ -1,10 +1,33 @@
import { Module } from '@nestjs/common'; import { forwardRef, Module } from '@nestjs/common';
import { ProductService } from './product.service'; import { ProductService } from './product.service';
import { ProductController } from './product.controller'; import { ProductController } from './product.controller';
import { ProductCategoriesService } from './product-categories.service'; 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({ @Module({
imports: [
TypeOrmModule.forFeature([
Product,
ProductCategories,
ProductHistoryPrice,
ProductSubCategories,
]),
forwardRef(() => UsersModule),
],
controllers: [ProductController], controllers: [ProductController],
providers: [ProductService, ProductCategoriesService], providers: [
ProductService,
ProductCategoriesService,
ProductSubCategoriesService,
ProductHistoryPriceService,
],
exports: [ProductService, ProductHistoryPriceService],
}) })
export class ProductModule {} export class ProductModule {}

View File

@ -1,45 +1,363 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { HttpException, HttpStatus } from '@nestjs/common';
import { EntityNotFoundError, Repository } from 'typeorm'; import { EntityNotFoundError, Repository } from 'typeorm';
import { Product } from './entities/product.entity'; 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 { InjectRepository } from '@nestjs/typeorm';
import { CreateProductDto } from '../product/dto/create-product.dto'; import { CreateProductDto } from './dto/product/create-product.dto';
import { CreateCategoriesProductDto } from './dto/categories/create-categories-product.dto'; import { ProductSubCategoriesService } from './product-sub-categories.service';
import { CreateSubCategoriesProductDto } from './dto/sub-categories/create-sub-categories-product.dto'; 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 { export class ProductService {
constructor( constructor(
@InjectRepository(Product) @InjectRepository(Product)
private productRepository: Repository<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) { 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); return this.productRepository.findOneOrFail(result.identifiers[0].id);
} }
findAll(page) { async processUploadCSV(uploadFile: string, supplierCode: string) {
return this.productRepository.findAndCount({ const supplierData = await this.supplierService.findByCode(supplierCode);
skip: page * 10,
take: 10, const data = await parsingFile(uploadFile);
order: { data.shift();
version: 'DESC', 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,
});
}
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 findOne(id: string) { 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 { try {
return await this.productRepository.findOneOrFail(id); return await this.productRepository.findOneOrFail({
relations: ['supplier'],
where: {
code: code,
},
});
} catch (e) { } catch (e) {
if (e instanceof EntityNotFoundError) { if (e instanceof EntityNotFoundError) {
throw new HttpException( throw new HttpException(
{ {
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
error: 'Data not found', error: 'Product not found',
}, },
HttpStatus.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);
}
} }

View 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;
}
}
}
}

View File

@ -0,0 +1,9 @@
import { IsNotEmpty, IsUUID } from 'class-validator';
export class AddSaldoSupplier {
@IsNotEmpty()
supplier: string;
@IsNotEmpty()
amount: number;
}

View 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[]
}

View File

@ -1 +0,0 @@
export class CreateTransactionDto {}

View 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;
}

View File

@ -0,0 +1,9 @@
import { IsNotEmpty, IsUUID } from 'class-validator';
export class DistributeTransactionDto {
@IsNotEmpty()
amount: number;
@IsNotEmpty()
destination: string;
}

View 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;
}

View File

@ -0,0 +1,9 @@
import { IsNotEmpty, IsUUID } from 'class-validator';
export class OrderTransactionDto {
@IsNotEmpty()
productCode: string;
@IsNotEmpty()
phoneNumber: string;
}

View File

@ -1,4 +1,4 @@
import { PartialType } from '@nestjs/mapped-types'; 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) {}

View 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;
}

View 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;
}

View File

@ -1 +0,0 @@
export class Transaction {}

View 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;
}

View File

@ -11,7 +11,7 @@ import {
Req, Req,
} from '@nestjs/common'; } from '@nestjs/common';
import { TransactionService } from './transaction.service'; import { TransactionService } from './transaction.service';
import { CreateTransactionDto } from './dto/create-transaction.dto'; import { DistributeTransactionDto } from './dto/distribute-transaction.dto';
import { FastifyRequest } from 'fastify'; import { FastifyRequest } from 'fastify';
@Controller({ @Controller({
@ -24,7 +24,20 @@ export class PpobCallbackController {
constructor(private readonly transactionService: TransactionService) {} constructor(private readonly transactionService: TransactionService) {}
@Get() @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({ this.logger.log({
requestQuery: request.query, requestQuery: request.query,
}); });

View File

@ -1,15 +1,20 @@
import { import {
Body,
Controller, Controller,
Get, Get,
Post, HttpStatus,
Body,
Patch,
Param, Param,
Delete, ParseUUIDPipe,
Post,
Put,
Query,
Request,
} from '@nestjs/common'; } from '@nestjs/common';
import { TransactionService } from './transaction.service'; import { TransactionService } from './transaction.service';
import { CreateTransactionDto } from './dto/create-transaction.dto'; import { DistributeTransactionDto } from './dto/distribute-transaction.dto';
import { UpdateTransactionDto } from './dto/update-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({ @Controller({
path: 'transaction', path: 'transaction',
@ -18,31 +23,227 @@ import { UpdateTransactionDto } from './dto/update-transaction.dto';
export class TransactionController { export class TransactionController {
constructor(private readonly transactionService: TransactionService) {} constructor(private readonly transactionService: TransactionService) {}
@Post() @Post('distribute')
create(@Body() createTransactionDto: CreateTransactionDto) { async create(
return this.transactionService.create(createTransactionDto); @Body() createTransactionDto: DistributeTransactionDto,
} @Request() req,
@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,
) { ) {
return this.transactionService.update(+id, updateTransactionDto); return {
data: await this.transactionService.distributeDeposit(
createTransactionDto,
req.user,
),
statusCode: HttpStatus.CREATED,
message: 'success',
};
} }
@Delete(':id') @Post('distribute-admin')
remove(@Param('id') id: string) { async distributeAdmin(
return this.transactionService.remove(+id); @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',
};
} }
} }

View File

@ -1,10 +1,25 @@
import { Module } from '@nestjs/common'; import { forwardRef, Module } from '@nestjs/common';
import { TransactionService } from './transaction.service'; import { TransactionService } from './transaction.service';
import { TransactionController } from './transaction.controller'; import { TransactionController } from './transaction.controller';
import { PpobCallbackController } from './ppob_callback.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({ @Module({
imports: [
TypeOrmModule.forFeature([COA, TransactionJournal, Transactions]),
ProductModule,
ConfigurableModule,
forwardRef(() => UsersModule),
],
controllers: [TransactionController, PpobCallbackController], controllers: [TransactionController, PpobCallbackController],
providers: [TransactionService], providers: [TransactionService, CoaService],
exports: [CoaService],
}) })
export class TransactionModule {} export class TransactionModule {}

File diff suppressed because it is too large Load Diff

View 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;
}

View File

@ -0,0 +1,9 @@
import { IsNotEmpty } from 'class-validator';
export class CreateSupplierDto {
@IsNotEmpty()
name: string;
@IsNotEmpty()
code: string;
}

View File

@ -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 { export class CreateUserDto {
@IsNotEmpty() @IsNotEmpty()
firstName: string; username: string;
@IsNotEmpty() @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;
} }

View File

@ -0,0 +1,5 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreatePartnerDto } from './create-partner.dto';
export class UpdatePartnerDto extends PartialType(CreatePartnerDto) {
}

View File

@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateSupplierDto } from './create-supplier.dto';
export class UpdateSupplierDto extends PartialType(CreateSupplierDto) {}

View 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;
}

View 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;
}

View File

@ -1,45 +1,65 @@
import { Roles } from 'src/configurable/entities/roles.entity';
import { import {
Entity,
Column, Column,
Entity,
ManyToOne,
OneToOne,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
UpdateDateColumn,
DeleteDateColumn,
VersionColumn,
CreateDateColumn,
} from 'typeorm'; } 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() @Entity()
export class User { export class User extends BaseModel {
@PrimaryGeneratedColumn('uuid') @PrimaryGeneratedColumn('uuid')
id: string; id: string;
@Column() @Column()
firstName: string; username: string;
@Column() @Column()
lastName: string; password: string;
@Column()
salt: string;
@Column({ default: true }) @Column({ default: true })
isActive: boolean; isActive: boolean;
@CreateDateColumn({ @ManyToOne(
type: 'timestamp with time zone', () => {
nullable: false, return User;
}) },
createdAt: Date; (user) => {
return user.id;
},
)
superior: User;
@UpdateDateColumn({ @ManyToOne(
type: 'timestamp with time zone', () => {
nullable: false, return Roles;
}) },
updatedAt: Date; (roles) => {
return roles.id;
},
)
roles: Roles;
@DeleteDateColumn({ @ManyToOne(
type: 'timestamp with time zone', () => {
nullable: true, return Partner;
}) },
deletedAt: Date; (partner) => {
return partner.id;
},
)
partner: Partner;
@VersionColumn() @OneToOne(() => UserDetail, (userDetail) => userDetail.user)
version: number; userDetail: UserDetail;
coa: COA;
} }

View File

@ -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;
}

View 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;
}

View 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();
});
});

View 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',
},
});
}
}

View 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();
});
});

View 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;
}
}
}
}

View File

@ -1,37 +1,185 @@
import { import {
Controller,
Get,
Post,
Body, Body,
Put, Controller,
Param,
Delete, Delete,
ParseUUIDPipe, Get,
HttpStatus, HttpStatus,
Param,
ParseUUIDPipe,
Post,
Put,
Query,
Request,
} from '@nestjs/common'; } from '@nestjs/common';
import { UsersService } from './users.service'; import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto'; import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-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({ @Controller({
path: 'users', path: 'users',
version: '1', version: '1',
}) })
export class UsersController { export class UsersController {
constructor(private readonly usersService: UsersService) {} constructor(
private readonly usersService: UsersService,
private readonly supplierService: SupplierService,
private readonly partnerService: PartnerService,
) {}
@Post() @Post()
async create(@Body() createUserDto: CreateUserDto) { async create(@Request() req, @Body() createUserDto: CreateUserDto) {
return { 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, statusCode: HttpStatus.CREATED,
message: 'success', message: 'success',
}; };
} }
@Get() @Get()
async findAll() { async findAll(@Request() req, @Query('page') page: number) {
const [data, count] = await this.usersService.findAll(); 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 { return {
data, 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') @Put(':id')
async update( async update(
@Param('id', ParseUUIDPipe) id: string, @Param('id', ParseUUIDPipe) id: string,
@Request() req,
@Body() updateUserDto: UpdateUserDto, @Body() updateUserDto: UpdateUserDto,
) { ) {
return { 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, statusCode: HttpStatus.OK,
message: 'success', message: 'success',
}; };

View File

@ -1,12 +1,24 @@
import { Module } from '@nestjs/common'; import { forwardRef, Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersService } from './users.service'; import { UsersService } from './users.service';
import { UsersController } from './users.controller'; import { UsersController } from './users.controller';
import { User } from './entities/user.entity'; 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({ @Module({
imports: [TypeOrmModule.forFeature([User])], imports: [
TypeOrmModule.forFeature([User, Supplier, Partner, UserDetail]),
forwardRef(() => TransactionModule),
ConfigurableModule,
],
controllers: [UsersController], controllers: [UsersController],
providers: [UsersService], providers: [UsersService, SupplierService, PartnerService],
exports: [UsersService, SupplierService, PartnerService],
}) })
export class UsersModule {} export class UsersModule {}

View File

@ -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 { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-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 { User } from './entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm'; 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() @Injectable()
export class UsersService { export class UsersService {
constructor( constructor(
@InjectRepository(User) @InjectRepository(User)
private usersRepository: Repository<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) { async create(createUserDto: CreateUserDto, currentUser: any) {
const result = await this.usersRepository.insert(createUserDto); 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,
);
}
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);
}
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;
} }
findAll() { async findAll(page: number, id: string) {
return this.usersRepository.findAndCount(); 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,
};
} }
async findOne(id: string) { 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 { try {
return await this.usersRepository.findOneOrFail(id); return await this.usersRepository.findOneOrFail(id);
} catch (e) { } catch (e) {
@ -30,7 +226,7 @@ export class UsersService {
throw new HttpException( throw new HttpException(
{ {
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
error: 'Data not found', error: 'User not found',
}, },
HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND,
); );
@ -40,15 +236,92 @@ export class UsersService {
} }
} }
async update(id: string, updateUserDto: UpdateUserDto) { async findByUsername(username: string) {
try { try {
await this.usersRepository.findOneOrFail(id); return await this.usersRepository.findOneOrFail({
where: {
username: username,
},
relations: ['superior', 'roles', 'partner'],
});
} catch (e) { } catch (e) {
if (e instanceof EntityNotFoundError) { if (e instanceof EntityNotFoundError) {
throw new HttpException( throw new HttpException(
{ {
statusCode: HttpStatus.NOT_FOUND, 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, 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) { async remove(id: string) {
try { try {
await this.usersRepository.findOneOrFail(id); await this.usersRepository.findOneOrFail(id);
@ -70,7 +410,7 @@ export class UsersService {
throw new HttpException( throw new HttpException(
{ {
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
error: 'Data not found', error: 'User not found',
}, },
HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND,
); );
@ -81,4 +421,37 @@ export class UsersService {
await this.usersRepository.delete(id); 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
View File

@ -675,11 +675,24 @@
tslib "2.3.1" tslib "2.3.1"
uuid "8.3.2" 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@*": "@nestjs/mapped-types@*":
version "1.0.0" version "1.0.0"
resolved "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.0.tgz" resolved "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.0.tgz"
integrity sha512-26AW5jHadLXtvHs+M+Agd9KZ92dDlBrmD0rORlBlvn2KvsWs4JRaKl2mUsrW7YsdZeAu3Hc4ukqyYyDdyCmMWQ== 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": "@nestjs/platform-express@^8.0.0":
version "8.2.3" version "8.2.3"
resolved "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-8.2.3.tgz" resolved "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-8.2.3.tgz"
@ -904,7 +917,7 @@
"@types/qs" "*" "@types/qs" "*"
"@types/range-parser" "*" "@types/range-parser" "*"
"@types/express@^4.17.13": "@types/express@*", "@types/express@^4.17.13":
version "4.17.13" version "4.17.13"
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
@ -958,11 +971,32 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= 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": "@types/mime@^1":
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== 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": "@types/node@*", "@types/node@^16.0.0":
version "16.11.10" version "16.11.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.10.tgz#2e3ad0a680d96367103d3e670d41c2fed3da61ae" 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" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== 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": "@types/prettier@^2.1.5":
version "2.4.2" version "2.4.2"
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281" 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" resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 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: array-flatten@1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" 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" fastq "^1.6.1"
queue-microtask "^1.1.2" queue-microtask "^1.1.2"
axios@0.24.0: axios@0.24.0, axios@^0.24.0:
version "0.24.0" version "0.24.0"
resolved "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz" resolved "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz"
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
@ -1567,6 +1644,11 @@ bl@^4.1.0:
inherits "^2.0.4" inherits "^2.0.4"
readable-stream "^3.4.0" 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: body-parser@1.19.0:
version "1.19.0" version "1.19.0"
resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz" resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz"
@ -1628,6 +1710,11 @@ bser@2.1.1:
dependencies: dependencies:
node-int64 "^0.4.0" 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: buffer-from@^1.0.0:
version "1.1.2" version "1.1.2"
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" 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" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 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: camelcase@^5.3.1:
version "5.3.1" version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" 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" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz#8573685bdae4d733ef18f78d44ba0ca5fe9e896b"
integrity sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg== 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: chalk@3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" 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" strip-ansi "^3.0.0"
supports-color "^2.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: chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
version "4.1.2" version "4.1.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" 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" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 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: colors@^1.1.2:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" 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" shebang-command "^2.0.0"
which "^2.0.1" 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: cssom@^0.4.4:
version "0.4.4" version "0.4.4"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
@ -2013,6 +2115,13 @@ cssstyle@^2.3.0:
dependencies: dependencies:
cssom "~0.3.6" 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: data-urls@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" 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-mimetype "^2.3.0"
whatwg-url "^8.0.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: debug@2.6.9:
version "2.6.9" version "2.6.9"
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" 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: dependencies:
ms "2.1.2" ms "2.1.2"
decimal.js@^10.2.1: decimal.js@^10.2.1, decimal.js@^10.3.1:
version "10.3.1" version "10.3.1"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
@ -2147,6 +2261,13 @@ duplexify@^4.1.2:
readable-stream "^3.1.1" readable-stream "^3.1.1"
stream-shift "^1.0.0" 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: ee-first@1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" 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" resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
fs-extra@10.0.0: fs-extra@10.0.0, fs-extra@^10.0.0:
version "10.0.0" version "10.0.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1"
integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== 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" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 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: fsevents@^2.3.2, fsevents@~2.3.2:
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 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/formula" "^3.0.0"
"@sideway/pinpoint" "^2.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: js-tokens@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@ -3742,11 +3873,49 @@ jsonfile@^6.0.1:
optionalDependencies: optionalDependencies:
graceful-fs "^4.1.6" 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: kleur@^3.0.3:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== 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: leven@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" 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" resolved "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz"
integrity sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI= 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: lodash.memoize@4.x:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" 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" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 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: lodash.set@4.3.2:
version "4.3.2" version "4.3.2"
resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz" 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" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== 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: ms@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" 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" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 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: multer@1.4.3:
version "1.4.3" version "1.4.3"
resolved "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz" 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" resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 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: path-exists@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 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" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 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: pg-connection-string@^2.5.0:
version "2.5.0" version "2.5.0"
resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz" 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" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== 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" version "0.5.0"
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0" resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0"
integrity sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ== integrity sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==
@ -4385,6 +4632,24 @@ pino-http@^6.3.0:
pino "^7.0.5" pino "^7.0.5"
pino-std-serializers "^5.0.0" 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: pino-std-serializers@^3.1.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz" 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" resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 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" version "1.3.0"
resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz"
integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== 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 "^6.12.5"
ajv-keywords "^3.5.2" 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" version "2.4.0"
resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz" resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz"
integrity sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg== 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: dependencies:
lru-cache "^6.0.0" 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: semver@^6.0.0, semver@^6.3.0:
version "6.3.0" version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" 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" atomic-sleep "^1.0.0"
flatstr "^1.0.12" 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: sonic-boom@^2.2.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.3.1.tgz#e6572184fb3adf145dbfeccff48141bbd1009e4c" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.3.1.tgz#e6572184fb3adf145dbfeccff48141bbd1009e4c"