Merge branch 'development' into 'master'
Development See merge request empatnusabangsa/ppob/ppob-backend!9
This commit is contained in:
commit
7ed72efb53
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -15,7 +15,7 @@ spec:
|
|||
spec:
|
||||
containers:
|
||||
- name: ppob-backend
|
||||
image: registry-harbor.app.bangun-kreatif.com/empatnusabangsa/ppob-backend:2
|
||||
image: registry-harbor.app.bangun-kreatif.com/empatnusabangsa/ppob-backend:<VERSION>
|
||||
ports:
|
||||
- containerPort: 5000
|
||||
envFrom:
|
||||
|
|
11
package.json
11
package.json
|
@ -24,14 +24,23 @@
|
|||
"@nestjs/common": "^8.0.0",
|
||||
"@nestjs/config": "^1.0.1",
|
||||
"@nestjs/core": "^8.0.0",
|
||||
"@nestjs/jwt": "^8.0.0",
|
||||
"@nestjs/mapped-types": "*",
|
||||
"@nestjs/passport": "^8.0.1",
|
||||
"@nestjs/platform-express": "^8.0.0",
|
||||
"@nestjs/platform-fastify": "^8.0.8",
|
||||
"@nestjs/typeorm": "^8.0.2",
|
||||
"axios": "^0.24.0",
|
||||
"class-transformer": "^0.4.0",
|
||||
"class-validator": "^0.13.1",
|
||||
"crypto": "^1.0.1",
|
||||
"decimal.js": "^10.3.1",
|
||||
"joi": "^17.4.2",
|
||||
"lodash": "^4.17.21",
|
||||
"nestjs-pino": "^2.3.1",
|
||||
"passport": "^0.5.0",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"pg": "^8.7.1",
|
||||
"pino-http": "^6.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
|
@ -47,6 +56,8 @@
|
|||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/passport-jwt": "^3.0.6",
|
||||
"@types/passport-local": "^1.0.34",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.2",
|
||||
"@typescript-eslint/parser": "^4.28.2",
|
||||
|
|
|
@ -6,8 +6,10 @@ import { UsersModule } from './users/users.module';
|
|||
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
|
||||
import { LoggerModule } from 'nestjs-pino';
|
||||
import { TransactionModule } from './transaction/transaction.module';
|
||||
import configuration from './config/configuration';
|
||||
import { ProductModule } from './product/product.module';
|
||||
import { ConfigurableModule } from './configurable/configurable.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import configuration from './config/configuration';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -19,10 +21,11 @@ import { ConfigurableModule } from './configurable/configurable.module';
|
|||
.valid('development', 'production', 'test', 'provision')
|
||||
.default('development'),
|
||||
PORT: Joi.number().default(3000),
|
||||
DATABASE_CLIENT: Joi.valid('mysql', 'postgres'),
|
||||
DATABASE_HOST: Joi.string(),
|
||||
DATABASE_NAME: Joi.string(),
|
||||
DATABASE_USERNAME: Joi.string(),
|
||||
SECRET: Joi.string().required(),
|
||||
DATABASE_CLIENT: Joi.valid('mysql', 'postgres').required(),
|
||||
DATABASE_HOST: Joi.string().required(),
|
||||
DATABASE_NAME: Joi.string().required(),
|
||||
DATABASE_USERNAME: Joi.string().required(),
|
||||
DATABASE_PASSWORD: Joi.string().empty('').default(''),
|
||||
DATABASE_PORT: Joi.number().default(5432),
|
||||
}),
|
||||
|
@ -49,6 +52,8 @@ import { ConfigurableModule } from './configurable/configurable.module';
|
|||
UsersModule,
|
||||
TransactionModule,
|
||||
ConfigurableModule,
|
||||
ProductModule,
|
||||
AuthModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
18
src/auth/auth.controller.spec.ts
Normal file
18
src/auth/auth.controller.spec.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AuthController } from './auth.controller';
|
||||
|
||||
describe('AuthController', () => {
|
||||
let controller: AuthController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AuthController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<AuthController>(AuthController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
26
src/auth/auth.controller.ts
Normal file
26
src/auth/auth.controller.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { Controller, Post, UseGuards, Request, Get } from '@nestjs/common';
|
||||
import { LocalAuthGuard } from './local-auth.guard';
|
||||
import { AuthService } from './auth.service';
|
||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
||||
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);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('profile')
|
||||
getProfile(@Request() req) {
|
||||
return req.user;
|
||||
}
|
||||
}
|
41
src/auth/auth.module.ts
Normal file
41
src/auth/auth.module.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { UsersModule } from '../users/users.module';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { LocalStrategy } from './local.strategy';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { JwtStrategy } from './jwt.strategy';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
UsersModule,
|
||||
PassportModule,
|
||||
ConfigModule,
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService) => {
|
||||
return {
|
||||
secret: configService.get<string>('secret'),
|
||||
signOptions: { expiresIn: '1d' },
|
||||
};
|
||||
},
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
AuthService,
|
||||
LocalStrategy,
|
||||
JwtStrategy,
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: JwtAuthGuard,
|
||||
},
|
||||
],
|
||||
controllers: [AuthController],
|
||||
exports: [AuthService],
|
||||
})
|
||||
export class AuthModule {}
|
18
src/auth/auth.service.spec.ts
Normal file
18
src/auth/auth.service.spec.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
describe('AuthService', () => {
|
||||
let service: AuthService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [AuthService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<AuthService>(AuthService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
37
src/auth/auth.service.ts
Normal file
37
src/auth/auth.service.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
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,
|
||||
};
|
||||
|
||||
return {
|
||||
access_token: this.jwtService.sign(payload),
|
||||
};
|
||||
}
|
||||
}
|
24
src/auth/jwt-auth.guard.ts
Normal file
24
src/auth/jwt-auth.guard.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { IS_PUBLIC_KEY } from './public.decorator';
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthGuard extends AuthGuard('jwt') {
|
||||
constructor(private reflector: Reflector) {
|
||||
super();
|
||||
}
|
||||
|
||||
canActivate(context: ExecutionContext) {
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
]);
|
||||
|
||||
if (isPublic) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.canActivate(context);
|
||||
}
|
||||
}
|
19
src/auth/jwt.strategy.ts
Normal file
19
src/auth/jwt.strategy.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {ConfigService} from "@nestjs/config";
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(configService: ConfigService) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: configService.get<string>('secret'),
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: any) {
|
||||
return { userId: payload.sub, username: payload.username };
|
||||
}
|
||||
}
|
5
src/auth/local-auth.guard.ts
Normal file
5
src/auth/local-auth.guard.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class LocalAuthGuard extends AuthGuard('local') {}
|
21
src/auth/local.strategy.ts
Normal file
21
src/auth/local.strategy.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { Strategy } from 'passport-local';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
@Injectable()
|
||||
export class LocalStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(private authService: AuthService) {
|
||||
super();
|
||||
}
|
||||
|
||||
async validate(username: string, password: string): Promise<any> {
|
||||
const user = await this.authService.validateUser(username, password);
|
||||
|
||||
if (!user) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
4
src/auth/public.decorator.ts
Normal file
4
src/auth/public.decorator.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { SetMetadata } from '@nestjs/common';
|
||||
|
||||
export const IS_PUBLIC_KEY = 'isPublic';
|
||||
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
|
|
@ -1,6 +1,7 @@
|
|||
export default () => {
|
||||
return {
|
||||
port: parseInt(process.env.PORT, 10) || 3000,
|
||||
secret: process.env.SECRET,
|
||||
database: {
|
||||
client: process.env.DATABASE_CLIENT,
|
||||
host: process.env.DATABASE_HOST,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigurableController } from './configurable.controller';
|
||||
import { ConfigurableService } from './configurable.service';
|
||||
import { RoleService } from './roles.service';
|
||||
|
||||
describe('ConfigurableController', () => {
|
||||
let controller: ConfigurableController;
|
||||
|
@ -8,7 +8,7 @@ describe('ConfigurableController', () => {
|
|||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [ConfigurableController],
|
||||
providers: [ConfigurableService],
|
||||
providers: [RoleService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<ConfigurableController>(ConfigurableController);
|
||||
|
|
|
@ -7,20 +7,20 @@ import {
|
|||
Param,
|
||||
Delete,
|
||||
ParseUUIDPipe,
|
||||
HttpStatus,
|
||||
HttpStatus, Query,
|
||||
} from '@nestjs/common';
|
||||
import { ConfigurableService } from './configurable.service';
|
||||
import { RoleService } from './roles.service';
|
||||
|
||||
@Controller({
|
||||
path: 'config',
|
||||
version: '1',
|
||||
})
|
||||
export class ConfigurableController {
|
||||
constructor(private readonly usersService: ConfigurableService) {}
|
||||
constructor(private readonly usersService: RoleService) {}
|
||||
|
||||
@Get()
|
||||
async findAll() {
|
||||
const [data, count] = await this.usersService.findAll();
|
||||
async findAll(@Query('page') page: number) {
|
||||
const [data, count] = await this.usersService.findAllRoles(page);
|
||||
|
||||
return {
|
||||
data,
|
||||
|
|
|
@ -2,11 +2,12 @@ import { Module } from '@nestjs/common';
|
|||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Roles } from './entities/roles.entity';
|
||||
import { ConfigurableController } from './configurable.controller';
|
||||
import { ConfigurableService } from './configurable.service';
|
||||
import { RoleService } from './roles.service';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Roles])],
|
||||
controllers: [ConfigurableController],
|
||||
providers: [ConfigurableService],
|
||||
providers: [RoleService],
|
||||
exports: [RoleService]
|
||||
})
|
||||
export class ConfigurableModule {}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigurableService } from './configurable.service';
|
||||
import { RoleService } from './roles.service';
|
||||
|
||||
describe('ConfigurableService', () => {
|
||||
let service: ConfigurableService;
|
||||
describe('RoleService', () => {
|
||||
let service: RoleService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [ConfigurableService],
|
||||
providers: [RoleService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ConfigurableService>(ConfigurableService);
|
||||
service = module.get<RoleService>(RoleService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
|
|
|
@ -4,14 +4,20 @@ import { Roles } from './entities/roles.entity';
|
|||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class ConfigurableService {
|
||||
export class RoleService {
|
||||
constructor(
|
||||
@InjectRepository(Roles)
|
||||
private rolesRepository: Repository<Roles>,
|
||||
) {}
|
||||
|
||||
findAll() {
|
||||
return this.rolesRepository.findAndCount();
|
||||
findAllRoles(page) {
|
||||
return this.rolesRepository.findAndCount({
|
||||
skip: page * 10,
|
||||
take: 10,
|
||||
order: {
|
||||
version: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
33
src/helper/enum-list.ts
Normal file
33
src/helper/enum-list.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
export enum statusTransaction {
|
||||
PENDING,
|
||||
SUCCESS,
|
||||
FAILED,
|
||||
}
|
||||
|
||||
export enum typeTransaction {
|
||||
DISTRIBUTION,
|
||||
ORDER,
|
||||
DEPOSIT_IRS,
|
||||
}
|
||||
|
||||
export enum productType {
|
||||
NORMAL,
|
||||
PROMO,
|
||||
}
|
||||
|
||||
export enum coaType {
|
||||
WALLET,
|
||||
INCOME,
|
||||
INVENTORY,
|
||||
COST_OF_SALES,
|
||||
SALES,
|
||||
BANK,
|
||||
EXPENSE,
|
||||
ACCOUNT_RECEIVABLE,
|
||||
ACCOUNT_PAYABLE
|
||||
}
|
||||
|
||||
export enum balanceType {
|
||||
DEBIT,
|
||||
CREDIT,
|
||||
}
|
13
src/helper/hash_password.ts
Normal file
13
src/helper/hash_password.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import * as crypto from 'crypto';
|
||||
|
||||
export function hashPassword(password, salt): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
crypto.pbkdf2(password, salt, 50, 100, 'sha512', (err, values) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(values.toString('hex'));
|
||||
});
|
||||
});
|
||||
}
|
25
src/helper/irs-service.ts
Normal file
25
src/helper/irs-service.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import * as axios from 'axios';
|
||||
|
||||
export const createTransaction = async (kode, tujuan) => {
|
||||
const codeTransaksi = generateRequestId();
|
||||
|
||||
return axios.default
|
||||
.get(
|
||||
`http://h2h.elangpangarep.com/api/h2h?id=PT0005&pin=04JFGC&user=D10BD0&pass=6251F3&kodeproduk=${kode}&tujuan=${tujuan}&counter=1&idtrx=${codeTransaksi}`,
|
||||
)
|
||||
.then((response) => {
|
||||
return response.data;
|
||||
});
|
||||
};
|
||||
|
||||
export const generateRequestId = () => {
|
||||
return `${new Date()
|
||||
.toLocaleString('en-us', {
|
||||
year: '2-digit',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
})
|
||||
.replace(/(\d+)\/(\d+)\/(\d+)/, '$3$1$2')}${Math.random()
|
||||
.toPrecision(3)
|
||||
.replace('0.', '')}`;
|
||||
};
|
22
src/helper/jwt.strategy.ts
Normal file
22
src/helper/jwt.strategy.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
// import { PassportStrategy } from '@nestjs/passport';
|
||||
// import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
// import { Injectable } from '@nestjs/common';
|
||||
// import { AuthService } from '../auth/auth.service';
|
||||
|
||||
// @Injectable()
|
||||
// export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
// constructor(private readonly authService: AuthService) {
|
||||
// super({
|
||||
// jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
// secretOrKey: process.env.SECRETKEY,
|
||||
// });
|
||||
// }
|
||||
|
||||
// async validate(payload: JwtPayload): Promise<UserDto> {
|
||||
// const user = await this.authService.validateUser(payload);
|
||||
// if (!user) {
|
||||
// throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
|
||||
// }
|
||||
// return user;
|
||||
// }
|
||||
// }
|
|
@ -1,44 +0,0 @@
|
|||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn, ManyToOne, ManyToMany, JoinTable,
|
||||
} from 'typeorm';
|
||||
import { Product } from '../../product/entities/product.entity';
|
||||
import { User } from '../../users/entities/user.entity';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
|
||||
enum type {
|
||||
SYSTEM_BANk,
|
||||
INCOME,
|
||||
}
|
||||
|
||||
enum balanceType {
|
||||
DEBIT,
|
||||
CREDIT,
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class Roles extends BaseModel{
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column('text')
|
||||
type: type;
|
||||
|
||||
@Column('text')
|
||||
balanceType: balanceType;
|
||||
|
||||
@Column()
|
||||
amount: number;
|
||||
|
||||
@ManyToMany(() => User)
|
||||
@JoinTable()
|
||||
user: User[];
|
||||
}
|
|
@ -4,7 +4,7 @@ import {
|
|||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { AppModule } from './app.module';
|
||||
import {ValidationPipe, VersioningType} from '@nestjs/common';
|
||||
import { ValidationPipe, VersioningType} from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Logger } from 'nestjs-pino';
|
||||
|
||||
|
|
|
@ -10,6 +10,12 @@ export class CreateProductDto {
|
|||
@IsNotEmpty()
|
||||
status: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
price: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
markUpPrice: number;
|
||||
|
||||
@IsUUID()
|
||||
subCategoriesId: string;
|
||||
}
|
17
src/product/dto/product/update-price-product.dto.ts
Normal file
17
src/product/dto/product/update-price-product.dto.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { IsNotEmpty } from 'class-validator';
|
||||
import { productType } from '../../../helper/enum-list';
|
||||
|
||||
export class UpdatePriceProductDto {
|
||||
@IsNotEmpty()
|
||||
price: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
markUpPrice: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
type: productType;
|
||||
|
||||
startDate: Date;
|
||||
|
||||
endDate: Date;
|
||||
}
|
6
src/product/dto/product/update-product.dto.ts
Normal file
6
src/product/dto/product/update-product.dto.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { OmitType, PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateProductDto } from './create-product.dto';
|
||||
|
||||
export class UpdateProductDto extends PartialType(
|
||||
OmitType(CreateProductDto, ['price'] as const),
|
||||
) {}
|
|
@ -2,6 +2,9 @@ import { IsNotEmpty, IsUUID } from 'class-validator';
|
|||
import { CreateCategoriesProductDto } from '../categories/create-categories-product.dto';
|
||||
|
||||
export class CreateSubCategoriesProductDto extends CreateCategoriesProductDto {
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
|
||||
@IsUUID()
|
||||
categoryId: string;
|
||||
}
|
||||
|
|
|
@ -2,20 +2,11 @@ import {
|
|||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn,
|
||||
OneToMany,
|
||||
ManyToOne,
|
||||
} from 'typeorm';
|
||||
import { Product } from './product.entity';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
|
||||
enum Type {
|
||||
NORMAL,
|
||||
PROMO,
|
||||
}
|
||||
import { productType } from '../../helper/enum-list';
|
||||
|
||||
@Entity()
|
||||
export class ProductHistoryPrice extends BaseModel {
|
||||
|
@ -23,14 +14,23 @@ export class ProductHistoryPrice extends BaseModel {
|
|||
id: string;
|
||||
|
||||
@ManyToOne(() => Product, (product) => product.id)
|
||||
productId: string;
|
||||
product: Product;
|
||||
|
||||
@Column()
|
||||
price: number;
|
||||
|
||||
@Column()
|
||||
markUpPrice: number;
|
||||
|
||||
@Column({ type: 'date' })
|
||||
startDate: string;
|
||||
startDate: Date;
|
||||
|
||||
@Column({ type: 'date' })
|
||||
endDate: string;
|
||||
@Column({
|
||||
type: 'date',
|
||||
nullable:true
|
||||
})
|
||||
endDate: Date;
|
||||
|
||||
@Column('text')
|
||||
type: Type;
|
||||
type: productType;
|
||||
}
|
||||
|
|
|
@ -2,17 +2,15 @@ import {
|
|||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
} from 'typeorm';
|
||||
import { ProductCategories } from './product-category.entity';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
import { Product } from './product.entity';
|
||||
|
||||
@Entity()
|
||||
export class ProductSubCategories extends BaseModel{
|
||||
export class ProductSubCategories extends BaseModel {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
|
@ -21,4 +19,7 @@ export class ProductSubCategories extends BaseModel{
|
|||
|
||||
@ManyToOne(() => ProductCategories, (categories) => categories.subCategories)
|
||||
category: ProductCategories;
|
||||
|
||||
@OneToMany(() => Product, (product) => product.subCategories)
|
||||
product: Product;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { ProductSubCategories } from './product-sub-category.entity';
|
|||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
|
||||
@Entity()
|
||||
export class Product extends BaseModel{
|
||||
export class Product extends BaseModel {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
|
@ -26,9 +26,21 @@ export class Product extends BaseModel{
|
|||
@Column()
|
||||
status: string;
|
||||
|
||||
@Column()
|
||||
price: number;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
basePrice: number;
|
||||
|
||||
@ManyToOne(
|
||||
() => ProductSubCategories,
|
||||
(subCategories) => subCategories.category,
|
||||
() => {
|
||||
return ProductSubCategories;
|
||||
},
|
||||
(subCategories) => {
|
||||
return subCategories.product;
|
||||
},
|
||||
)
|
||||
subCategories: ProductSubCategories;
|
||||
}
|
||||
|
|
|
@ -4,19 +4,26 @@ import { ProductSubCategories } from './entities/product-sub-category.entity';
|
|||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { CreateSubCategoriesProductDto } from './dto/sub-categories/create-sub-categories-product.dto';
|
||||
import { UpdateSubCategoriesProductDto } from './dto/sub-categories/update-sub-categories-product.dto';
|
||||
import { ProductCategoriesService } from './product-categories.service';
|
||||
|
||||
@Injectable()
|
||||
export class ProductSubCategoriesService {
|
||||
constructor(
|
||||
@InjectRepository(ProductSubCategories)
|
||||
private productSubCategoriesRepository: Repository<ProductSubCategories>,
|
||||
private productCategoriesService: ProductCategoriesService,
|
||||
) {}
|
||||
|
||||
async create(CreateCategoriesProductDto: CreateSubCategoriesProductDto) {
|
||||
const result = await this.productSubCategoriesRepository.insert(
|
||||
CreateCategoriesProductDto,
|
||||
async create(createSubCategoriesProductDto: CreateSubCategoriesProductDto) {
|
||||
const categories = await this.productCategoriesService.findOne(
|
||||
createSubCategoriesProductDto.categoryId,
|
||||
);
|
||||
|
||||
const result = await this.productSubCategoriesRepository.insert({
|
||||
name: createSubCategoriesProductDto.name,
|
||||
category: categories,
|
||||
});
|
||||
|
||||
return this.productSubCategoriesRepository.findOneOrFail(
|
||||
result.identifiers[0].id,
|
||||
);
|
||||
|
@ -70,11 +77,15 @@ export class ProductSubCategoriesService {
|
|||
}
|
||||
}
|
||||
|
||||
const result = await this.productSubCategoriesRepository.update(
|
||||
id,
|
||||
updateCategoriesProductDto,
|
||||
const categories = await this.productCategoriesService.findOne(
|
||||
updateCategoriesProductDto.categoryId,
|
||||
);
|
||||
|
||||
const result = await this.productSubCategoriesRepository.update(id, {
|
||||
name: updateCategoriesProductDto.name,
|
||||
category: categories,
|
||||
});
|
||||
|
||||
return this.productSubCategoriesRepository.findOneOrFail(id);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,18 @@ import {
|
|||
Param,
|
||||
Delete,
|
||||
ParseUUIDPipe,
|
||||
HttpStatus, Query,
|
||||
HttpStatus,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ProductService } from './product.service';
|
||||
import { ProductCategoriesService } from './product-categories.service';
|
||||
import { CreateCategoriesProductDto } from './dto/categories/create-categories-product.dto';
|
||||
import { UpdateCategoriesProductDto } from '../product/dto/categories/update-categories-product.dto';
|
||||
import { UpdateSubCategoriesProductDto } from '../product/dto/sub-categories/update-sub-categories-product.dto';
|
||||
import { ProductSubCategoriesService } from './product-sub-categories.service';
|
||||
import { CreateSubCategoriesProductDto } from './dto/sub-categories/create-sub-categories-product.dto';
|
||||
import { CreateProductDto } from './dto/product/create-product.dto';
|
||||
import { UpdateProductDto } from './dto/product/update-product.dto';
|
||||
|
||||
@Controller({
|
||||
path: 'product',
|
||||
|
@ -21,8 +28,18 @@ export class ProductController {
|
|||
constructor(
|
||||
private readonly productService: ProductService,
|
||||
private readonly productCategoriesService: ProductCategoriesService,
|
||||
private readonly productSubCategoriesService: ProductSubCategoriesService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
async create(@Body() createProductDto: CreateProductDto) {
|
||||
return {
|
||||
data: await this.productService.create(createProductDto),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('categories')
|
||||
async createCategories(
|
||||
@Body() createCategoriesProductDto: CreateCategoriesProductDto,
|
||||
|
@ -36,6 +53,19 @@ export class ProductController {
|
|||
};
|
||||
}
|
||||
|
||||
@Post('sub-categories')
|
||||
async createSubCategories(
|
||||
@Body() createSubCategoriesProductDto: CreateSubCategoriesProductDto,
|
||||
) {
|
||||
return {
|
||||
data: await this.productSubCategoriesService.create(
|
||||
createSubCategoriesProductDto,
|
||||
),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get()
|
||||
async findAll(@Query('page') page: number) {
|
||||
const [data, count] = await this.productService.findAll(page);
|
||||
|
@ -48,6 +78,45 @@ export class ProductController {
|
|||
};
|
||||
}
|
||||
|
||||
@Get('by-categories')
|
||||
async findByCategories(
|
||||
@Query('page') page: number,
|
||||
@Query('categories') categories: string,
|
||||
) {
|
||||
const [data, count] = await this.productService.findAll(page);
|
||||
|
||||
return {
|
||||
data,
|
||||
count,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('categories')
|
||||
async findAllCategories(@Query('page') page: number) {
|
||||
const [data, count] = await this.productCategoriesService.findAll(page);
|
||||
|
||||
return {
|
||||
data,
|
||||
count,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('sub-categories')
|
||||
async findAllSubCategories(@Query('page') page: number) {
|
||||
const [data, count] = await this.productSubCategoriesService.findAll(page);
|
||||
|
||||
return {
|
||||
data,
|
||||
count,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id', ParseUUIDPipe) id: string) {
|
||||
return {
|
||||
|
@ -57,4 +126,90 @@ export class ProductController {
|
|||
};
|
||||
}
|
||||
|
||||
@Get('categories/:id')
|
||||
async findOneCategories(@Param('id', ParseUUIDPipe) id: string) {
|
||||
return {
|
||||
data: await this.productCategoriesService.findOne(id),
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('sub-categories/:id')
|
||||
async findOneSubCategories(@Param('id', ParseUUIDPipe) id: string) {
|
||||
return {
|
||||
data: await this.productSubCategoriesService.findOne(id),
|
||||
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',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,28 @@ import { Module } from '@nestjs/common';
|
|||
import { ProductService } from './product.service';
|
||||
import { ProductController } from './product.controller';
|
||||
import { ProductCategoriesService } from './product-categories.service';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Product } from './entities/product.entity';
|
||||
import { ProductCategories } from './entities/product-category.entity';
|
||||
import { ProductHistoryPrice } from './entities/product-history-price.entity';
|
||||
import { ProductSubCategories } from './entities/product-sub-category.entity';
|
||||
import { ProductSubCategoriesService } from './product-sub-categories.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
Product,
|
||||
ProductCategories,
|
||||
ProductHistoryPrice,
|
||||
ProductSubCategories,
|
||||
]),
|
||||
],
|
||||
controllers: [ProductController],
|
||||
providers: [ProductService, ProductCategoriesService],
|
||||
providers: [
|
||||
ProductService,
|
||||
ProductCategoriesService,
|
||||
ProductSubCategoriesService,
|
||||
],
|
||||
exports: [ProductService],
|
||||
})
|
||||
export class ProductModule {}
|
||||
|
|
|
@ -1,28 +1,65 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { Product } from './entities/product.entity';
|
||||
import { ProductCategories } from './entities/product-category.entity';
|
||||
import { ProductSubCategories } from './entities/product-sub-category.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { CreateProductDto } from '../product/dto/create-product.dto';
|
||||
import { CreateCategoriesProductDto } from './dto/categories/create-categories-product.dto';
|
||||
import { CreateSubCategoriesProductDto } from './dto/sub-categories/create-sub-categories-product.dto';
|
||||
import { CreateProductDto } from './dto/product/create-product.dto';
|
||||
import { ProductSubCategoriesService } from './product-sub-categories.service';
|
||||
import { UpdateProductDto } from './dto/product/update-product.dto';
|
||||
import { ProductHistoryPrice } from './entities/product-history-price.entity';
|
||||
import { productType } from '../helper/enum-list';
|
||||
import { UpdatePriceProductDto } from './dto/product/update-price-product.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ProductService {
|
||||
constructor(
|
||||
@InjectRepository(Product)
|
||||
private productRepository: Repository<Product>,
|
||||
@InjectRepository(ProductHistoryPrice)
|
||||
private productHistoryPrice: Repository<ProductHistoryPrice>,
|
||||
private productSubCategoriesService: ProductSubCategoriesService,
|
||||
) {}
|
||||
|
||||
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,
|
||||
subCategories: subCategories,
|
||||
price: createProductDto.price,
|
||||
});
|
||||
|
||||
await this.productHistoryPrice.insert({
|
||||
product: result.identifiers[0],
|
||||
type: productType.NORMAL,
|
||||
price: createProductDto.price,
|
||||
markUpPrice: createProductDto.markUpPrice,
|
||||
startDate: new Date(),
|
||||
endDate: null,
|
||||
});
|
||||
|
||||
return this.productRepository.findOneOrFail(result.identifiers[0].id);
|
||||
}
|
||||
|
||||
findAll(page) {
|
||||
return this.productRepository.findAndCount({
|
||||
skip: page * 10,
|
||||
relations: ['subCategories'],
|
||||
take: 10,
|
||||
order: {
|
||||
version: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
findAllByCategories(page, categories) {
|
||||
return this.productRepository.findAndCount({
|
||||
join: {
|
||||
alias: 'subCategories',
|
||||
innerJoin: { subCategories: 'roles.users' },
|
||||
},
|
||||
skip: page * 10,
|
||||
take: 10,
|
||||
order: {
|
||||
|
@ -31,9 +68,9 @@ export class ProductService {
|
|||
});
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
async findOne(code: string) {
|
||||
try {
|
||||
return await this.productRepository.findOneOrFail(id);
|
||||
return await this.productRepository.findOneOrFail({ code: code });
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
|
@ -48,4 +85,73 @@ export class ProductService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: 'Data 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,
|
||||
subCategories: 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,
|
||||
markUpPrice: 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: 'Data not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
await this.productRepository.delete(id);
|
||||
}
|
||||
}
|
||||
|
|
89
src/transaction/coa.service.ts
Normal file
89
src/transaction/coa.service.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
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(() => UsersService))
|
||||
private userService: UsersService,
|
||||
) {}
|
||||
|
||||
async create(inputCoaDto: InputCoaDto) {
|
||||
const user = inputCoaDto.user
|
||||
let coaData = new COA();
|
||||
coaData.user = user.id;
|
||||
coaData.name = coaType[inputCoaDto.type] + '-' + user.username;
|
||||
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: 'Data 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
src/transaction/dto/add-saldo-supplier.dto.ts
Normal file
12
src/transaction/dto/add-saldo-supplier.dto.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
import { balanceType, coaType, statusTransaction, typeTransaction } from 'src/helper/enum-list';
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
|
||||
export class AddSaldoSupplier {
|
||||
@IsNotEmpty()
|
||||
supplier?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
amount?: number;
|
||||
}
|
36
src/transaction/dto/create-journal.dto.ts
Normal file
36
src/transaction/dto/create-journal.dto.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
import { balanceType, coaType, statusTransaction, typeTransaction } from 'src/helper/enum-list';
|
||||
import { EntityManager } from 'typeorm';
|
||||
import { Transactions } from '../entities/transactions.entity';
|
||||
|
||||
interface JournalEntry {
|
||||
coa_id: string;
|
||||
debit?: number;
|
||||
credit?: number;
|
||||
}
|
||||
|
||||
export class CreateJournalDto {
|
||||
@IsNotEmpty()
|
||||
transactionalEntityManager: EntityManager;
|
||||
|
||||
@IsNotEmpty()
|
||||
createTransaction?: boolean;
|
||||
|
||||
@IsNotEmpty()
|
||||
userId?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
transaction?: Transactions;
|
||||
|
||||
@IsNotEmpty()
|
||||
type?: typeTransaction;
|
||||
|
||||
@IsNotEmpty()
|
||||
amount?: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
transactionStatus?: statusTransaction;
|
||||
|
||||
@IsNotEmpty()
|
||||
journals: JournalEntry[]
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export class CreateTransactionDto {}
|
9
src/transaction/dto/distribute-transaction.dto.ts
Normal file
9
src/transaction/dto/distribute-transaction.dto.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
|
||||
export class DistributeTransactionDto {
|
||||
@IsNotEmpty()
|
||||
amount: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
destination: string;
|
||||
}
|
21
src/transaction/dto/input-coa.dto.ts
Normal file
21
src/transaction/dto/input-coa.dto.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
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';
|
||||
|
||||
export class InputCoaDto {
|
||||
@IsUUID()
|
||||
user: User;
|
||||
|
||||
@IsNotEmpty()
|
||||
type: coaType;
|
||||
|
||||
@IsNotEmpty()
|
||||
balanceType: balanceType;
|
||||
|
||||
@IsUUID()
|
||||
relatedUserId: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
coaEntityManager: EntityManager;
|
||||
}
|
6
src/transaction/dto/order-transaction.dto.ts
Normal file
6
src/transaction/dto/order-transaction.dto.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
|
||||
export class OrderTransactionDto {
|
||||
@IsNotEmpty()
|
||||
productCode: string;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateTransactionDto } from './create-transaction.dto';
|
||||
import { DistributeTransactionDto } from './distribute-transaction.dto';
|
||||
|
||||
export class UpdateTransactionDto extends PartialType(CreateTransactionDto) {}
|
||||
export class UpdateTransactionDto extends PartialType(DistributeTransactionDto) {}
|
||||
|
|
29
src/transaction/entities/coa.entity.ts
Normal file
29
src/transaction/entities/coa.entity.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
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()
|
||||
user: string;
|
||||
|
||||
@Column({
|
||||
nullable:true
|
||||
})
|
||||
relatedUser: string;
|
||||
}
|
42
src/transaction/entities/transaction-journal.entity.ts
Normal file
42
src/transaction/entities/transaction-journal.entity.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
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 { TransactionType } from './transaction-type.entity';
|
||||
import { balanceType } from '../../helper/enum-list';
|
||||
|
||||
@Entity()
|
||||
export class TransactionJournal extends BaseModel {
|
||||
@Column('text')
|
||||
type: balanceType;
|
||||
|
||||
@Column()
|
||||
amount: number;
|
||||
|
||||
@OneToOne(
|
||||
() => {
|
||||
return Transactions;
|
||||
},
|
||||
(trans) => {
|
||||
return trans.id;
|
||||
},
|
||||
)
|
||||
transaction: Transactions;
|
||||
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return COA;
|
||||
},
|
||||
(coa) => {
|
||||
return coa.id;
|
||||
},
|
||||
)
|
||||
coa: COA;
|
||||
}
|
11
src/transaction/entities/transaction-type.entity.ts
Normal file
11
src/transaction/entities/transaction-type.entity.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import {
|
||||
Entity,
|
||||
Column,
|
||||
} from 'typeorm';
|
||||
import { BaseModel } from '../../config/basemodel.entity';
|
||||
|
||||
@Entity()
|
||||
export class TransactionType extends BaseModel {
|
||||
@Column()
|
||||
name: string;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export class Transaction {}
|
34
src/transaction/entities/transactions.entity.ts
Normal file
34
src/transaction/entities/transactions.entity.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
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';
|
||||
|
||||
@Entity()
|
||||
export class Transactions extends BaseModel {
|
||||
@Column()
|
||||
amount: number;
|
||||
|
||||
@Column()
|
||||
status: statusTransaction;
|
||||
|
||||
@Column()
|
||||
type: typeTransaction;
|
||||
|
||||
@Column()
|
||||
user: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
userDestination: string;
|
||||
}
|
|
@ -11,7 +11,7 @@ import {
|
|||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { TransactionService } from './transaction.service';
|
||||
import { CreateTransactionDto } from './dto/create-transaction.dto';
|
||||
import { DistributeTransactionDto } from './dto/distribute-transaction.dto';
|
||||
import { FastifyRequest } from 'fastify';
|
||||
|
||||
@Controller({
|
||||
|
|
|
@ -6,10 +6,14 @@ import {
|
|||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
Request,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { TransactionService } from './transaction.service';
|
||||
import { CreateTransactionDto } from './dto/create-transaction.dto';
|
||||
import { DistributeTransactionDto } from './dto/distribute-transaction.dto';
|
||||
import { OrderTransactionDto } from './dto/order-transaction.dto';
|
||||
import { UpdateTransactionDto } from './dto/update-transaction.dto';
|
||||
import { AddSaldoSupplier } from './dto/add-saldo-supplier.dto';
|
||||
|
||||
@Controller({
|
||||
path: 'transaction',
|
||||
|
@ -18,31 +22,55 @@ import { UpdateTransactionDto } from './dto/update-transaction.dto';
|
|||
export class TransactionController {
|
||||
constructor(private readonly transactionService: TransactionService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() createTransactionDto: CreateTransactionDto) {
|
||||
return this.transactionService.create(createTransactionDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.transactionService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.transactionService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(
|
||||
@Param('id') id: string,
|
||||
@Body() updateTransactionDto: UpdateTransactionDto,
|
||||
@Post('distribute')
|
||||
create(
|
||||
@Body() createTransactionDto: DistributeTransactionDto,
|
||||
@Request() req,
|
||||
) {
|
||||
return this.transactionService.update(+id, updateTransactionDto);
|
||||
return this.transactionService.distributeDeposit(
|
||||
createTransactionDto,
|
||||
req.user,
|
||||
);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.transactionService.remove(+id);
|
||||
@Post('distribute-admin')
|
||||
distributeAdmin(
|
||||
@Body() createTransactionDto: DistributeTransactionDto,
|
||||
@Request() req,
|
||||
) {
|
||||
return {
|
||||
data: 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.addIRSWallet(
|
||||
addSaldoSupplier,
|
||||
req.user,
|
||||
),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('order')
|
||||
orderTransaction(
|
||||
@Body() orderTransactionDto: OrderTransactionDto,
|
||||
@Request() req,
|
||||
) {
|
||||
return this.transactionService.orderTransaction(
|
||||
orderTransactionDto,
|
||||
req.user,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,29 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { TransactionService } from './transaction.service';
|
||||
import { TransactionController } from './transaction.controller';
|
||||
import { PpobCallbackController } from './ppob_callback.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { COA } from './entities/coa.entity';
|
||||
import { TransactionType } from './entities/transaction-type.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';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
TransactionType,
|
||||
COA,
|
||||
TransactionJournal,
|
||||
Transactions,
|
||||
]),
|
||||
ProductModule,
|
||||
forwardRef(() => UsersModule),
|
||||
],
|
||||
controllers: [TransactionController, PpobCallbackController],
|
||||
providers: [TransactionService],
|
||||
providers: [TransactionService, CoaService],
|
||||
exports:[CoaService]
|
||||
})
|
||||
export class TransactionModule {}
|
||||
|
|
|
@ -1,26 +1,461 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateTransactionDto } from './dto/create-transaction.dto';
|
||||
import { UpdateTransactionDto } from './dto/update-transaction.dto';
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { DistributeTransactionDto } from './dto/distribute-transaction.dto';
|
||||
import { OrderTransactionDto } from './dto/order-transaction.dto';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Transactions } from './entities/transactions.entity';
|
||||
import { Connection, EntityManager, Repository } from 'typeorm';
|
||||
import { COA } from './entities/coa.entity';
|
||||
import { TransactionType } from './entities/transaction-type.entity';
|
||||
import { TransactionJournal } from './entities/transaction-journal.entity';
|
||||
import { CoaService } from './coa.service';
|
||||
import * as uuid from 'uuid';
|
||||
import { uniq } from 'lodash';
|
||||
import { Decimal } from 'decimal.js';
|
||||
import {
|
||||
balanceType,
|
||||
coaType,
|
||||
statusTransaction,
|
||||
typeTransaction,
|
||||
} from '../helper/enum-list';
|
||||
import { ProductService } from '../product/product.service';
|
||||
import * as irsService from '../helper/irs-service';
|
||||
import { CreateJournalDto } from './dto/create-journal.dto';
|
||||
import { UsersService } from 'src/users/users.service';
|
||||
import { AddSaldoSupplier } from './dto/add-saldo-supplier.dto';
|
||||
|
||||
interface JournalEntry {
|
||||
coa_id: string;
|
||||
debit?: string;
|
||||
credit?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TransactionService {
|
||||
create(createTransactionDto: CreateTransactionDto) {
|
||||
return 'This action adds a new transaction';
|
||||
constructor(
|
||||
@InjectRepository(Transactions)
|
||||
private transactionRepository: Repository<Transactions>,
|
||||
@InjectRepository(TransactionType)
|
||||
private transactionTypeRepository: Repository<TransactionType>,
|
||||
@InjectRepository(TransactionJournal)
|
||||
private transactionJournalRepository: Repository<TransactionJournal>,
|
||||
@InjectRepository(COA)
|
||||
private coaRepository: Repository<COA>,
|
||||
private coaService: CoaService,
|
||||
private productService: ProductService,
|
||||
private userService: UsersService,
|
||||
private connection: Connection,
|
||||
) {}
|
||||
|
||||
async addIRSWallet(addSaldoSupplier: AddSaldoSupplier, currentUser: any) {
|
||||
// GET COA
|
||||
const coaBank = await this.coaService.findByName(
|
||||
`${coaType[coaType.BANK]}-SYSTEM`,
|
||||
);
|
||||
|
||||
const coaInventory = await this.coaService.findByName(
|
||||
`${coaType[coaType.INVENTORY]}-${addSaldoSupplier.supplier}`,
|
||||
);
|
||||
|
||||
//GET USER
|
||||
const userData = await this.userService.findByUsername(
|
||||
currentUser.username,
|
||||
);
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
//INSERT TRANSACTION
|
||||
const transactionData = new Transactions();
|
||||
|
||||
transactionData.id = uuid.v4();
|
||||
transactionData.amount = addSaldoSupplier.amount;
|
||||
transactionData.user = userData.id;
|
||||
transactionData.status = statusTransaction.SUCCESS;
|
||||
transactionData.type = typeTransaction.DEPOSIT_IRS;
|
||||
|
||||
await manager.insert(Transactions, transactionData);
|
||||
|
||||
await this.accountingTransaction({
|
||||
createTransaction: false,
|
||||
transactionalEntityManager: manager,
|
||||
transaction: transactionData,
|
||||
amount: transactionData.amount,
|
||||
journals: [
|
||||
{
|
||||
coa_id: coaBank.id,
|
||||
debit: transactionData.amount,
|
||||
},
|
||||
{
|
||||
coa_id: coaInventory.id,
|
||||
credit: transactionData.amount,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all transaction`;
|
||||
async distributeFromAdmin(
|
||||
distributeTransactionDto: DistributeTransactionDto,
|
||||
currentUser: any,
|
||||
) {
|
||||
// GET COA
|
||||
const coaAR = await this.coaService.findByUser(
|
||||
distributeTransactionDto.destination,
|
||||
coaType.ACCOUNT_RECEIVABLE,
|
||||
);
|
||||
const coaWallet = await this.coaService.findByUser(
|
||||
distributeTransactionDto.destination,
|
||||
coaType.WALLET,
|
||||
);
|
||||
|
||||
//GET USER
|
||||
const userData = await this.userService.findByUsername(
|
||||
currentUser.username,
|
||||
);
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
//INSERT TRANSACTION
|
||||
const transactionData = new Transactions();
|
||||
|
||||
transactionData.id = uuid.v4();
|
||||
transactionData.amount = distributeTransactionDto.amount;
|
||||
transactionData.user = userData.id;
|
||||
transactionData.status = statusTransaction.SUCCESS;
|
||||
transactionData.type = typeTransaction.DISTRIBUTION;
|
||||
|
||||
await manager.insert(Transactions, transactionData);
|
||||
|
||||
await this.accountingTransaction({
|
||||
createTransaction: false,
|
||||
transactionalEntityManager: manager,
|
||||
transaction: transactionData,
|
||||
amount: transactionData.amount,
|
||||
journals: [
|
||||
{
|
||||
coa_id: coaAR.id,
|
||||
debit: transactionData.amount,
|
||||
},
|
||||
{
|
||||
coa_id: coaWallet.id,
|
||||
credit: transactionData.amount,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
return `This action returns a #${id} transaction`;
|
||||
async distributeDeposit(
|
||||
distributeTransactionDto: DistributeTransactionDto,
|
||||
currentUser: any,
|
||||
) {
|
||||
//GET USER
|
||||
const userData = await this.userService.findByUsername(
|
||||
currentUser.username,
|
||||
);
|
||||
|
||||
// GET COA
|
||||
const coaSenderWallet = await this.coaService.findByUser(
|
||||
userData.id,
|
||||
coaType.WALLET,
|
||||
);
|
||||
|
||||
const coaAP = await this.coaService.findByUserWithRelated(
|
||||
distributeTransactionDto.destination,
|
||||
userData.id,
|
||||
coaType.ACCOUNT_PAYABLE,
|
||||
);
|
||||
|
||||
const coaReceiverWallet = await this.coaService.findByUser(
|
||||
distributeTransactionDto.destination,
|
||||
coaType.WALLET,
|
||||
);
|
||||
|
||||
const coaAR = await this.coaService.findByUserWithRelated(
|
||||
distributeTransactionDto.destination,
|
||||
userData.id,
|
||||
coaType.ACCOUNT_RECEIVABLE,
|
||||
);
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
const transactionData = new Transactions();
|
||||
|
||||
transactionData.id = uuid.v4();
|
||||
transactionData.amount = distributeTransactionDto.amount;
|
||||
transactionData.user = userData.id;
|
||||
transactionData.status = statusTransaction.SUCCESS;
|
||||
transactionData.type = typeTransaction.DISTRIBUTION;
|
||||
|
||||
await manager.insert(Transactions, transactionData);
|
||||
|
||||
await this.accountingTransaction({
|
||||
createTransaction: false,
|
||||
transactionalEntityManager: manager,
|
||||
transaction: transactionData,
|
||||
amount: transactionData.amount,
|
||||
journals: [
|
||||
{
|
||||
coa_id: coaSenderWallet.id,
|
||||
debit: transactionData.amount,
|
||||
},
|
||||
{
|
||||
coa_id: coaReceiverWallet.id,
|
||||
credit: transactionData.amount,
|
||||
},
|
||||
{
|
||||
coa_id: coaAR.id,
|
||||
debit: transactionData.amount,
|
||||
},
|
||||
{
|
||||
coa_id: coaAP.id,
|
||||
credit: transactionData.amount,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
update(id: number, updateTransactionDto: UpdateTransactionDto) {
|
||||
return `This action updates a #${id} transaction`;
|
||||
async orderTransaction(
|
||||
orderTransactionDto: OrderTransactionDto,
|
||||
currentUser: any,
|
||||
) {
|
||||
//GET PRODUCT
|
||||
const product = await this.productService.findOne(
|
||||
orderTransactionDto.productCode,
|
||||
);
|
||||
|
||||
//GET USER
|
||||
const userData = await this.userService.findByUsername(
|
||||
currentUser.username,
|
||||
);
|
||||
let supervisorData = [];
|
||||
|
||||
if (userData.superior != null) {
|
||||
supervisorData.push(
|
||||
await this.userService.findByUsername(currentUser.username),
|
||||
);
|
||||
|
||||
if (supervisorData[0].superior != null) {
|
||||
supervisorData.push(
|
||||
await this.userService.findByUsername(currentUser.username),
|
||||
);
|
||||
|
||||
if (supervisorData[0].superior != null) {
|
||||
supervisorData.push(
|
||||
await this.userService.findByUsername(currentUser.username),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} transaction`;
|
||||
//GET COA
|
||||
const coaAccount = await this.coaService.findByUser(
|
||||
userData.id,
|
||||
coaType.WALLET,
|
||||
);
|
||||
|
||||
const coaInventory = await this.coaService.findByName(
|
||||
`${coaType[coaType.INVENTORY]}-IRS`,
|
||||
);
|
||||
|
||||
const coaCostOfSales = await this.coaService.findByName(
|
||||
`${coaType[coaType.COST_OF_SALES]}-SYSTEM`,
|
||||
);
|
||||
|
||||
const coaSales = await this.coaService.findByName(
|
||||
`${coaType[coaType.SALES]}-SYSTEM`,
|
||||
);
|
||||
|
||||
const coaExpense = await this.coaService.findByName(
|
||||
`${coaType[coaType.EXPENSE]}-SYSTEM`,
|
||||
);
|
||||
|
||||
supervisorData = supervisorData.map(async (it) => {
|
||||
const coaAccount = await this.coaService.findByUser(
|
||||
it.id,
|
||||
coaType.WALLET,
|
||||
);
|
||||
|
||||
return {
|
||||
coa_id: coaAccount.id,
|
||||
credit: 0,
|
||||
};
|
||||
});
|
||||
|
||||
if (coaAccount.amount <= product.price) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
error: `Transaction Failed because saldo not enough`,
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.connection.transaction(async (manager) => {
|
||||
const transactionData = new Transactions();
|
||||
|
||||
transactionData.id = uuid.v4();
|
||||
transactionData.amount = product.price;
|
||||
transactionData.user = userData.id;
|
||||
transactionData.status = statusTransaction.SUCCESS;
|
||||
transactionData.type = typeTransaction.DISTRIBUTION;
|
||||
await manager.insert(Transactions, transactionData);
|
||||
|
||||
await this.accountingTransaction({
|
||||
createTransaction: false,
|
||||
transactionalEntityManager: manager,
|
||||
transaction: transactionData,
|
||||
amount: transactionData.amount,
|
||||
journals: [
|
||||
{
|
||||
coa_id: coaInventory.id,
|
||||
credit: product.basePrice,
|
||||
},
|
||||
{
|
||||
coa_id: coaCostOfSales.id,
|
||||
debit: product.basePrice,
|
||||
},
|
||||
{
|
||||
coa_id: coaAccount.id,
|
||||
debit: product.price,
|
||||
},
|
||||
{
|
||||
coa_id: coaSales.id,
|
||||
credit: product.price,
|
||||
},
|
||||
{
|
||||
coa_id: coaExpense.id,
|
||||
credit: 0,
|
||||
},
|
||||
].concat(supervisorData),
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async accountingTransaction(createJournalDto: CreateJournalDto) {
|
||||
const creditSum = createJournalDto.journals
|
||||
.map((it) => {
|
||||
return it.credit;
|
||||
})
|
||||
.filter((it) => {
|
||||
return it;
|
||||
})
|
||||
.reduce((a, b) => {
|
||||
return a.plus(b);
|
||||
}, new Decimal(0));
|
||||
const debitSum = createJournalDto.journals
|
||||
.map((it) => {
|
||||
return it.debit;
|
||||
})
|
||||
.filter((it) => {
|
||||
return it;
|
||||
})
|
||||
.reduce((a, b) => {
|
||||
return a.plus(b);
|
||||
}, new Decimal(0));
|
||||
const coaIds = uniq(
|
||||
createJournalDto.journals.map((it) => {
|
||||
return it.coa_id;
|
||||
}),
|
||||
);
|
||||
|
||||
if (!creditSum.equals(debitSum)) {
|
||||
throw new Error(`credit and debit doesn't match`);
|
||||
}
|
||||
|
||||
const coas = await this.coaRepository.findByIds(coaIds);
|
||||
|
||||
const transaction = createJournalDto.transaction;
|
||||
|
||||
await Promise.all(
|
||||
createJournalDto.journals.map((journal) => {
|
||||
const coa = coas.find((it) => {
|
||||
return it.id === journal.coa_id;
|
||||
});
|
||||
|
||||
if (!coa) {
|
||||
throw new Error(`coa ${journal.coa_id} not found`);
|
||||
}
|
||||
|
||||
const journalEntry = new TransactionJournal();
|
||||
|
||||
journalEntry.coa = coa;
|
||||
journalEntry.type = journal.debit
|
||||
? balanceType.DEBIT
|
||||
: balanceType.CREDIT;
|
||||
journalEntry.amount = journal.debit ? journal.debit : journal.credit;
|
||||
journalEntry.transaction = transaction;
|
||||
|
||||
return this.transactionJournalRepository.save(journalEntry);
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
coaIds.map((coaId) => {
|
||||
const journalPerCoa = createJournalDto.journals.filter((journal) => {
|
||||
return journal.coa_id == coaId;
|
||||
});
|
||||
|
||||
const creditSum = journalPerCoa
|
||||
.map((it) => {
|
||||
return it.credit;
|
||||
})
|
||||
.filter((it) => {
|
||||
return it;
|
||||
})
|
||||
.reduce((a, b) => {
|
||||
return a.plus(b);
|
||||
}, new Decimal(0));
|
||||
const debitSum = journalPerCoa
|
||||
.map((it) => {
|
||||
return it.debit;
|
||||
})
|
||||
.filter((it) => {
|
||||
return it;
|
||||
})
|
||||
.reduce((a, b) => {
|
||||
return a.plus(b);
|
||||
}, new Decimal(0));
|
||||
|
||||
const coa = coas.find((it) => {
|
||||
(it) => {
|
||||
return it.id.toLowerCase() === coaId.toLowerCase();
|
||||
};
|
||||
});
|
||||
|
||||
let balance = new Decimal(coa.amount);
|
||||
|
||||
if (coa.balanceType === balanceType.DEBIT) {
|
||||
balance = balance.plus(debitSum.minus(creditSum));
|
||||
} else if (coa.balanceType === balanceType.CREDIT) {
|
||||
balance = balance.plus(creditSum.minus(debitSum));
|
||||
}
|
||||
|
||||
const diff = balance.minus(new Decimal(coa.amount));
|
||||
|
||||
return createJournalDto.transactionalEntityManager
|
||||
.createQueryBuilder()
|
||||
.update(COA)
|
||||
.set({
|
||||
amount: () => {
|
||||
return `amount + ${diff.toString()}`;
|
||||
},
|
||||
})
|
||||
.where('id = :id', { id: coa.id })
|
||||
.execute();
|
||||
}),
|
||||
);
|
||||
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
import { IsNotEmpty } from 'class-validator';
|
||||
import { IsNotEmpty, IsOptional, IsUUID, ValidateIf } from 'class-validator';
|
||||
|
||||
export class CreateUserDto {
|
||||
@IsNotEmpty()
|
||||
firstName: string;
|
||||
username: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
lastName: string;
|
||||
password: string;
|
||||
|
||||
@IsUUID()
|
||||
roleId: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
superior: boolean;
|
||||
|
||||
// @ValidateIf((o) => {
|
||||
// return !!o.superior;
|
||||
// })
|
||||
// @IsUUID()
|
||||
// superior: string;
|
||||
}
|
||||
|
|
|
@ -1,45 +1,42 @@
|
|||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
VersionColumn,
|
||||
CreateDateColumn,
|
||||
} from 'typeorm';
|
||||
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 User {
|
||||
export class User extends BaseModel {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
firstName: string;
|
||||
username: string;
|
||||
|
||||
@Column()
|
||||
lastName: string;
|
||||
password: string;
|
||||
|
||||
@Column()
|
||||
salt: string;
|
||||
|
||||
@Column({ default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@CreateDateColumn({
|
||||
type: 'timestamp with time zone',
|
||||
nullable: false,
|
||||
})
|
||||
createdAt: Date;
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
(user) => {
|
||||
return user.id;
|
||||
},
|
||||
)
|
||||
superior: User;
|
||||
|
||||
@UpdateDateColumn({
|
||||
type: 'timestamp with time zone',
|
||||
nullable: false,
|
||||
})
|
||||
updatedAt: Date;
|
||||
|
||||
@DeleteDateColumn({
|
||||
type: 'timestamp with time zone',
|
||||
nullable: true,
|
||||
})
|
||||
deletedAt: Date;
|
||||
|
||||
@VersionColumn()
|
||||
version: number;
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return Roles;
|
||||
},
|
||||
(roles) => {
|
||||
return roles.id;
|
||||
},
|
||||
)
|
||||
roles: Roles;
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@ import {
|
|||
Delete,
|
||||
ParseUUIDPipe,
|
||||
HttpStatus,
|
||||
Query,
|
||||
Request,
|
||||
} from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { Public } from '../auth/public.decorator';
|
||||
|
||||
@Controller({
|
||||
path: 'users',
|
||||
|
@ -21,17 +24,18 @@ export class UsersController {
|
|||
constructor(private readonly usersService: UsersService) {}
|
||||
|
||||
@Post()
|
||||
async create(@Body() createUserDto: CreateUserDto) {
|
||||
async create(@Request() req, @Body() createUserDto: CreateUserDto) {
|
||||
return {
|
||||
data: await this.usersService.create(createUserDto),
|
||||
data: await this.usersService.create(createUserDto, req.user),
|
||||
statusCode: HttpStatus.CREATED,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get()
|
||||
async findAll() {
|
||||
const [data, count] = await this.usersService.findAll();
|
||||
async findAll(@Query('page') page: number) {
|
||||
const [data, count] = await this.usersService.findAll(page);
|
||||
|
||||
return {
|
||||
data,
|
||||
|
@ -41,6 +45,34 @@ export class UsersController {
|
|||
};
|
||||
}
|
||||
|
||||
@Get('find-by-supperior')
|
||||
async findBySuperrior(@Request() req, @Query('page') page: number) {
|
||||
const [data, count] = await this.usersService.findBySuperrior(
|
||||
req.user.userId,
|
||||
page,
|
||||
);
|
||||
return {
|
||||
data,
|
||||
count,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('find-by-roles/:id')
|
||||
async findByRoles(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Query('page') page: number,
|
||||
) {
|
||||
const [data, count] = await this.usersService.findByRoles(id, page);
|
||||
return {
|
||||
data,
|
||||
count,
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id', ParseUUIDPipe) id: string) {
|
||||
return {
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { UsersService } from './users.service';
|
||||
import { UsersController } from './users.controller';
|
||||
import { User } from './entities/user.entity';
|
||||
import { TransactionModule } from 'src/transaction/transaction.module';
|
||||
import { ConfigurableModule } from 'src/configurable/configurable.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([User])],
|
||||
imports: [TypeOrmModule.forFeature([User]), forwardRef(() => TransactionModule), ConfigurableModule],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
exports: [UsersService],
|
||||
})
|
||||
export class UsersModule {}
|
||||
|
|
|
@ -1,30 +1,178 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import {
|
||||
forwardRef,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Inject,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { Connection, EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { User } from './entities/user.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
|
||||
import { hashPassword } from '../helper/hash_password';
|
||||
import { CoaService } from 'src/transaction/coa.service';
|
||||
import { balanceType, coaType } from 'src/helper/enum-list';
|
||||
import { RoleService } from 'src/configurable/roles.service';
|
||||
import { InputCoaDto } from 'src/transaction/dto/input-coa.dto';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(
|
||||
@InjectRepository(User)
|
||||
private usersRepository: Repository<User>,
|
||||
@Inject(
|
||||
forwardRef(() => {
|
||||
return CoaService;
|
||||
}),
|
||||
)
|
||||
private coaService: CoaService,
|
||||
private roleService: RoleService,
|
||||
private connection: Connection,
|
||||
) {}
|
||||
|
||||
async create(createUserDto: CreateUserDto) {
|
||||
const result = await this.usersRepository.insert(createUserDto);
|
||||
async create(createUserDto: CreateUserDto, currentUser: any) {
|
||||
const roles = await this.roleService.findOne(createUserDto.roleId);
|
||||
const superior = await this.findByUsername(currentUser.username);
|
||||
const salt = randomStringGenerator();
|
||||
|
||||
return this.usersRepository.findOneOrFail(result.identifiers[0].id);
|
||||
const userData = new User();
|
||||
|
||||
userData.id = uuid.v4();
|
||||
userData.username = createUserDto.username;
|
||||
userData.password = await hashPassword(createUserDto.password, salt);
|
||||
userData.salt = salt;
|
||||
userData.superior = superior;
|
||||
userData.roles = roles;
|
||||
|
||||
await this.connection.transaction(async (manager) => {
|
||||
const result = await manager.insert(User, userData);
|
||||
|
||||
const dataCoaWallet = new InputCoaDto();
|
||||
|
||||
dataCoaWallet.user = userData;
|
||||
dataCoaWallet.balanceType = balanceType.CREDIT;
|
||||
dataCoaWallet.type = coaType.WALLET;
|
||||
dataCoaWallet.coaEntityManager = manager;
|
||||
|
||||
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;
|
||||
|
||||
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(dataCoaAP);
|
||||
await this.coaService.create(dataCoaAR);
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return this.usersRepository.findAndCount();
|
||||
await this.coaService.create(dataCoaWallet);
|
||||
});
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
findAll(page: number) {
|
||||
return this.usersRepository.findAndCount({
|
||||
skip: page * 10,
|
||||
take: 10,
|
||||
order: {
|
||||
version: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
findByRoles(relationId: string, page: number) {
|
||||
return this.usersRepository.findAndCount({
|
||||
skip: page * 10,
|
||||
take: 10,
|
||||
where: {
|
||||
roles: relationId,
|
||||
},
|
||||
order: {
|
||||
updatedAt: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
findBySuperrior(superriorId: string, page: number) {
|
||||
return this.usersRepository.findAndCount({
|
||||
skip: page * 10,
|
||||
take: 10,
|
||||
where: {
|
||||
superior: superriorId,
|
||||
},
|
||||
order: {
|
||||
updatedAt: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async findExist(id: string) {
|
||||
try {
|
||||
return await this.usersRepository.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findByUsername(username: string) {
|
||||
try {
|
||||
return await this.usersRepository.findOneOrFail({
|
||||
username: username,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Data not found',
|
||||
},
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
const coa = await this.coaService.findByUser(id, coaType.WALLET);
|
||||
|
||||
try {
|
||||
return await this.usersRepository.findOneOrFail(id);
|
||||
const userData = await this.usersRepository.findOneOrFail({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
relations: ['roles', 'superior'],
|
||||
});
|
||||
|
||||
return {
|
||||
...userData,
|
||||
wallet: coa.amount,
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new HttpException(
|
||||
|
@ -57,7 +205,7 @@ export class UsersService {
|
|||
}
|
||||
}
|
||||
|
||||
const result = await this.usersRepository.update(id, updateUserDto);
|
||||
// const result = await this.usersRepository.update(id, updateUserDto);
|
||||
|
||||
return this.usersRepository.findOneOrFail(id);
|
||||
}
|
||||
|
@ -81,4 +229,13 @@ export class UsersService {
|
|||
|
||||
await this.usersRepository.delete(id);
|
||||
}
|
||||
|
||||
async findOneByUsername(username: string) {
|
||||
return this.usersRepository.findOneOrFail({
|
||||
where: {
|
||||
username,
|
||||
},
|
||||
relations: ['roles'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
194
yarn.lock
194
yarn.lock
|
@ -675,11 +675,24 @@
|
|||
tslib "2.3.1"
|
||||
uuid "8.3.2"
|
||||
|
||||
"@nestjs/jwt@^8.0.0":
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@nestjs/jwt/-/jwt-8.0.0.tgz#6c811c17634252dd1dcd5dabf409dbd692b812da"
|
||||
integrity sha512-fz2LQgYY2zmuD8S+8UE215anwKyXlnB/1FwJQLVR47clNfMeFMK8WCxmn6xdPhF5JKuV1crO6FVabb1qWzDxqQ==
|
||||
dependencies:
|
||||
"@types/jsonwebtoken" "8.5.4"
|
||||
jsonwebtoken "8.5.1"
|
||||
|
||||
"@nestjs/mapped-types@*":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.0.tgz"
|
||||
integrity sha512-26AW5jHadLXtvHs+M+Agd9KZ92dDlBrmD0rORlBlvn2KvsWs4JRaKl2mUsrW7YsdZeAu3Hc4ukqyYyDdyCmMWQ==
|
||||
|
||||
"@nestjs/passport@^8.0.1":
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@nestjs/passport/-/passport-8.0.1.tgz#f1ed39a19489f794d1fe3fef592b4523bc48da68"
|
||||
integrity sha512-vn/ZJLXQKvSf9D0BvEoNFJLfzl9AVqfGtDyQMfWDLbaNpoEB2FyeaHGxdiX6H71oLSrQV78c/yuhfantzwdjdg==
|
||||
|
||||
"@nestjs/platform-express@^8.0.0":
|
||||
version "8.2.3"
|
||||
resolved "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-8.2.3.tgz"
|
||||
|
@ -904,7 +917,7 @@
|
|||
"@types/qs" "*"
|
||||
"@types/range-parser" "*"
|
||||
|
||||
"@types/express@^4.17.13":
|
||||
"@types/express@*", "@types/express@^4.17.13":
|
||||
version "4.17.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
|
||||
integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
|
||||
|
@ -958,6 +971,20 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
||||
|
||||
"@types/jsonwebtoken@*":
|
||||
version "8.5.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.6.tgz#1913e5a61e70a192c5a444623da4901a7b1a9d42"
|
||||
integrity sha512-+P3O/xC7nzVizIi5VbF34YtqSonFsdnbXBnWUCYRiKOi1f9gA4sEFvXkrGr/QVV23IbMYvcoerI7nnhDUiWXRQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/jsonwebtoken@8.5.4":
|
||||
version "8.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz#50ccaf0aa6f5d7b9956e70fe323b76e582991913"
|
||||
integrity sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/mime@^1":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
||||
|
@ -973,6 +1000,39 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||
|
||||
"@types/passport-jwt@^3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport-jwt/-/passport-jwt-3.0.6.tgz#41cc8b5803d5f5f06eb33e19c453b42716def4f1"
|
||||
integrity sha512-cmAAMIRTaEwpqxlrZyiEY9kdibk94gP5KTF8AT1Ra4rWNZYHNMreqhKUEeC5WJtuN5SJZjPQmV+XO2P5PlnvNQ==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
"@types/jsonwebtoken" "*"
|
||||
"@types/passport-strategy" "*"
|
||||
|
||||
"@types/passport-local@^1.0.34":
|
||||
version "1.0.34"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport-local/-/passport-local-1.0.34.tgz#84d3b35b2fd4d36295039ded17fe5f3eaa62f4f6"
|
||||
integrity sha512-PSc07UdYx+jhadySxxIYWuv6sAnY5e+gesn/5lkPKfBeGuIYn9OPR+AAEDq73VRUh6NBTpvE/iPE62rzZUslog==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
"@types/passport" "*"
|
||||
"@types/passport-strategy" "*"
|
||||
|
||||
"@types/passport-strategy@*":
|
||||
version "0.2.35"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport-strategy/-/passport-strategy-0.2.35.tgz#e52f5212279ea73f02d9b06af67efe9cefce2d0c"
|
||||
integrity sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
"@types/passport" "*"
|
||||
|
||||
"@types/passport@*":
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport/-/passport-1.0.7.tgz#85892f14932168158c86aecafd06b12f5439467a"
|
||||
integrity sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
|
||||
"@types/prettier@^2.1.5":
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281"
|
||||
|
@ -1475,7 +1535,7 @@ avvio@^7.1.2:
|
|||
fastq "^1.6.1"
|
||||
queue-microtask "^1.1.2"
|
||||
|
||||
axios@0.24.0:
|
||||
axios@0.24.0, axios@^0.24.0:
|
||||
version "0.24.0"
|
||||
resolved "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz"
|
||||
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
|
||||
|
@ -1628,6 +1688,11 @@ bser@2.1.1:
|
|||
dependencies:
|
||||
node-int64 "^0.4.0"
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
|
||||
|
@ -1996,6 +2061,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
|||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
crypto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037"
|
||||
integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==
|
||||
|
||||
cssom@^0.4.4:
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
|
||||
|
@ -2036,7 +2106,7 @@ debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
|
|||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
decimal.js@^10.2.1:
|
||||
decimal.js@^10.2.1, decimal.js@^10.3.1:
|
||||
version "10.3.1"
|
||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
|
||||
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
|
||||
|
@ -2147,6 +2217,13 @@ duplexify@^4.1.2:
|
|||
readable-stream "^3.1.1"
|
||||
stream-shift "^1.0.0"
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
|
||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
|
||||
|
@ -3742,6 +3819,39 @@ jsonfile@^6.0.1:
|
|||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
jsonwebtoken@8.5.1, jsonwebtoken@^8.2.0:
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
|
||||
dependencies:
|
||||
jws "^3.2.2"
|
||||
lodash.includes "^4.3.0"
|
||||
lodash.isboolean "^3.0.3"
|
||||
lodash.isinteger "^4.0.4"
|
||||
lodash.isnumber "^3.0.3"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
lodash.isstring "^4.0.1"
|
||||
lodash.once "^4.0.0"
|
||||
ms "^2.1.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
jwa@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
|
||||
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
|
||||
dependencies:
|
||||
buffer-equal-constant-time "1.0.1"
|
||||
ecdsa-sig-formatter "1.0.11"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
jws@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
||||
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
||||
dependencies:
|
||||
jwa "^1.4.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
kleur@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||
|
@ -3820,6 +3930,36 @@ lodash.has@4.5.2:
|
|||
resolved "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz"
|
||||
integrity sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=
|
||||
|
||||
lodash.includes@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
|
||||
|
||||
lodash.isboolean@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
|
||||
|
||||
lodash.isinteger@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
||||
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
|
||||
|
||||
lodash.isnumber@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
||||
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||
|
||||
lodash.isstring@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
|
||||
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
|
||||
|
||||
lodash.memoize@4.x:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
|
@ -3830,6 +3970,11 @@ lodash.merge@^4.6.2:
|
|||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash.once@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||
|
||||
lodash.set@4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz"
|
||||
|
@ -4006,6 +4151,11 @@ ms@2.1.2:
|
|||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
multer@1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz"
|
||||
|
@ -4266,6 +4416,34 @@ parseurl@~1.3.3:
|
|||
resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"
|
||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
||||
|
||||
passport-jwt@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-jwt/-/passport-jwt-4.0.0.tgz#7f0be7ba942e28b9f5d22c2ebbb8ce96ef7cf065"
|
||||
integrity sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==
|
||||
dependencies:
|
||||
jsonwebtoken "^8.2.0"
|
||||
passport-strategy "^1.0.0"
|
||||
|
||||
passport-local@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee"
|
||||
integrity sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
|
||||
passport-strategy@1.x.x, passport-strategy@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
|
||||
integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=
|
||||
|
||||
passport@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/passport/-/passport-0.5.0.tgz#7914aaa55844f9dce8c3aa28f7d6b73647ee0169"
|
||||
integrity sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
pause "0.0.1"
|
||||
|
||||
path-exists@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||
|
@ -4306,6 +4484,11 @@ path-type@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||
|
||||
pause@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d"
|
||||
integrity sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=
|
||||
|
||||
pg-connection-string@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz"
|
||||
|
@ -4832,6 +5015,11 @@ semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
|
|||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
semver@^6.0.0, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
|
|
Loading…
Reference in New Issue
Block a user