Merge branch 'development' into 'devops-staging'

Development

See merge request empatnusabangsa/ppob/ppob-backend!37
This commit is contained in:
ilham dwi pratama 2021-12-20 03:23:54 +00:00
commit 988d0293aa
8 changed files with 441 additions and 115 deletions

View File

@ -2,12 +2,15 @@ export enum statusTransaction {
PENDING,
SUCCESS,
FAILED,
APPROVED,
REJECTED,
}
export enum typeTransaction {
DISTRIBUTION,
ORDER,
DEPOSIT_IRS,
DEPOSIT_SUPPLIER,
DEPOSIT_RETURN,
}
export enum productType {

View File

@ -76,6 +76,28 @@ export class CoaService {
}
}
async findByTwoUser(from: string, destination: string, typeOfCoa: coaType) {
try {
return await this.coaRepository.findOneOrFail({
user: from,
relatedUser: destination,
type: typeOfCoa,
});
} catch (e) {
if (e instanceof EntityNotFoundError) {
throw new HttpException(
{
statusCode: HttpStatus.NOT_FOUND,
error: 'COA not found',
},
HttpStatus.NOT_FOUND,
);
} else {
throw e;
}
}
}
async findByUserWithRelated(
id: string,
relatedId: string,

View File

@ -1,11 +1,4 @@
import { IsNotEmpty, IsUUID } from 'class-validator';
import {
balanceType,
coaType,
statusTransaction,
typeTransaction,
} from 'src/helper/enum-list';
import { EntityManager } from 'typeorm';
export class AddSaldoSupplier {
@IsNotEmpty()

View File

@ -0,0 +1,7 @@
import { DistributeTransactionDto } from './distribute-transaction.dto';
import { IsNotEmpty } from 'class-validator';
export class DepositReturnDto extends DistributeTransactionDto {
@IsNotEmpty()
image_prove: string;
}

View File

@ -6,7 +6,4 @@ export class DistributeTransactionDto {
@IsNotEmpty()
destination: string;
@IsNotEmpty()
supplier: string;
}

View File

@ -37,5 +37,10 @@ export class Transactions extends BaseModel {
@ManyToOne(() => ProductHistoryPrice, (product) => product.id)
product_price: ProductHistoryPrice;
@Column({
nullable: true,
})
image_prove: string;
mark_up_price: number;
}

View File

@ -9,12 +9,14 @@ import {
Request,
HttpStatus,
Query,
Put,
ParseUUIDPipe,
} from '@nestjs/common';
import { TransactionService } from './transaction.service';
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';
import { DepositReturnDto } from './dto/deposit_return.dto';
@Controller({
path: 'transaction',
@ -24,23 +26,27 @@ export class TransactionController {
constructor(private readonly transactionService: TransactionService) {}
@Post('distribute')
create(
@Body() createTransactionDto: DistributeTransactionDto,
@Request() req,
) {
return this.transactionService.distributeDeposit(
createTransactionDto,
req.user,
);
}
@Post('distribute-admin')
distributeAdmin(
async create(
@Body() createTransactionDto: DistributeTransactionDto,
@Request() req,
) {
return {
data: this.transactionService.distributeFromAdmin(
data: await this.transactionService.distributeDeposit(
createTransactionDto,
req.user,
),
statusCode: HttpStatus.CREATED,
message: 'success',
};
}
@Post('distribute-admin')
async distributeAdmin(
@Body() createTransactionDto: DistributeTransactionDto,
@Request() req,
) {
return {
data: await this.transactionService.distributeFromAdmin(
createTransactionDto,
req.user,
),
@ -65,14 +71,33 @@ export class TransactionController {
}
@Post('order')
orderTransaction(
async orderTransaction(
@Body() orderTransactionDto: OrderTransactionDto,
@Request() req,
) {
return this.transactionService.orderTransaction(
return {
data: await this.transactionService.orderTransaction(
orderTransactionDto,
req.user,
);
),
statusCode: HttpStatus.CREATED,
message: 'success',
};
}
@Post('deposit-return')
async depositReturn(
@Body() depositReturnDto: DepositReturnDto,
@Request() req,
) {
return {
data: await this.transactionService.createDepositReturn(
req.user,
depositReturnDto,
),
statusCode: HttpStatus.CREATED,
message: 'success',
};
}
@Get('history')
@ -88,4 +113,69 @@ export class TransactionController {
message: 'success',
};
}
@Get('deposit-return')
async findDepositReturn(@Query('page') page: number, @Request() req) {
const data = await this.transactionService.getAllDepositReturnFromUser(
req.user.userId,
page,
);
return {
...data,
statusCode: HttpStatus.OK,
message: 'success',
};
}
@Get('deposit-return/confirmation')
async findDepositReturnConfirmation(
@Query('page') page: number,
@Request() req,
) {
const data = await this.transactionService.getAllDepositReturnToUser(
req.user.userId,
page,
);
return {
...data,
statusCode: HttpStatus.OK,
message: 'success',
};
}
@Put('deposit-return/confirmation/:id')
async confirmDepositReturn(
@Param('id', ParseUUIDPipe) id: string,
@Request() req,
@Body() status: string,
) {
return {
data: await this.transactionService.confirmationDepositReturn(
id,
req.user,
status,
),
statusCode: HttpStatus.OK,
message: 'success',
};
}
@Put('deposit-return/confirmation-admin/:id')
async confirmDepositReturnAdmin(
@Param('id', ParseUUIDPipe) id: string,
@Request() req,
@Body() status: string,
) {
return {
data: await this.transactionService.confirmationAdminDepositReturn(
id,
req.user,
status,
),
statusCode: HttpStatus.OK,
message: 'success',
};
}
}

View File

@ -3,7 +3,12 @@ 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 {
Connection,
EntityManager,
EntityNotFoundError,
Repository,
} from 'typeorm';
import { COA } from './entities/coa.entity';
import { TransactionJournal } from './entities/transaction-journal.entity';
import { CoaService } from './coa.service';
@ -23,6 +28,7 @@ import { AddSaldoSupplier } from './dto/add-saldo-supplier.dto';
import { SupplierService } from '../users/supplier/supplier.service';
import { ProductHistoryPriceService } from '../product/history-price/history-price.service';
import { CommissionService } from '../configurable/commission.service';
import { DepositReturnDto } from './dto/deposit_return.dto';
interface JournalEntry {
coa_id: string;
@ -82,7 +88,7 @@ export class TransactionService {
transactionData.amount = addSaldoSupplier.amount;
transactionData.user = userData.id;
transactionData.status = statusTransaction.SUCCESS;
transactionData.type = typeTransaction.DEPOSIT_IRS;
transactionData.type = typeTransaction.DEPOSIT_SUPPLIER;
await manager.insert(Transactions, transactionData);
@ -124,6 +130,9 @@ export class TransactionService {
currentUser.username,
);
const supplier = await this.supplierService.findByActive();
try {
if (userData.roles.name != 'Admin') {
throw new HttpException(
{
@ -134,14 +143,10 @@ export class TransactionService {
);
}
//GET Supplier
const supplier = await this.supplierService.findByCode(
distributeTransactionDto.supplier,
);
// GET COA
const coaAR = await this.coaService.findByUser(
const coaAR = await this.coaService.findByTwoUser(
distributeTransactionDto.destination,
currentUser.userId,
coaType.ACCOUNT_RECEIVABLE,
);
const coaWallet = await this.coaService.findByUser(
@ -163,6 +168,7 @@ export class TransactionService {
transactionData.id = uuid.v4();
transactionData.amount = distributeTransactionDto.amount;
transactionData.user = userData.id;
transactionData.user_destination = distributeTransactionDto.destination;
transactionData.status = statusTransaction.SUCCESS;
transactionData.type = typeTransaction.DISTRIBUTION;
@ -195,6 +201,9 @@ export class TransactionService {
});
return true;
} catch (e) {
throw e;
}
}
async distributeDeposit(
@ -235,6 +244,7 @@ export class TransactionService {
transactionData.id = uuid.v4();
transactionData.amount = distributeTransactionDto.amount;
transactionData.user = userData.id;
transactionData.user_destination = distributeTransactionDto.destination;
transactionData.status = statusTransaction.SUCCESS;
transactionData.type = typeTransaction.DISTRIBUTION;
@ -290,7 +300,7 @@ export class TransactionService {
let supervisorData = [];
const profit = product_price.mark_up_price - product_price.price;
let profit = product_price.mark_up_price - product_price.price;
if (!userData.partner) {
//GET SALES
@ -299,6 +309,9 @@ export class TransactionService {
profit,
userData,
);
profit = supervisorData
.map((item) => item.credit)
.reduce((prev, curr) => prev + curr, 0);
}
//GET COA
@ -369,7 +382,7 @@ export class TransactionService {
},
{
coa_id: coaExpense.id,
credit: userData.partner ? 0 : profit,
debit: userData.partner ? 0 : profit,
},
].concat(supervisorData),
});
@ -381,6 +394,154 @@ export class TransactionService {
return true;
}
async createDepositReturn(currentUser, depositReturnDto: DepositReturnDto) {
const userData = await this.userService.findByUsername(
currentUser.username,
);
try {
const transactionData = new Transactions();
transactionData.id = uuid.v4();
transactionData.amount = depositReturnDto.amount;
transactionData.user = userData.id;
transactionData.user_destination = depositReturnDto.destination;
transactionData.status = statusTransaction.PENDING;
transactionData.type = typeTransaction.DEPOSIT_RETURN;
await this.connection.transaction(async (manager) => {
await manager.insert(Transactions, transactionData);
});
return transactionData;
} catch (e) {
throw e;
}
}
async confirmationDepositReturn(
id: string,
userData,
statusApproval: string,
) {
const transactionData = await this.findApprovalDepositReturn(id);
const coaSenderWallet = await this.coaService.findByUser(
transactionData.user_destination,
coaType.WALLET,
);
const coaAP = await this.coaService.findByUserWithRelated(
userData.userId,
transactionData.user_destination,
coaType.ACCOUNT_PAYABLE,
);
const coaReceiverWallet = await this.coaService.findByUser(
transactionData.user_destination,
coaType.WALLET,
);
const coaAR = await this.coaService.findByUserWithRelated(
userData.userId,
transactionData.user_destination,
coaType.ACCOUNT_RECEIVABLE,
);
try {
await this.connection.transaction(async (manager) => {
transactionData.status =
statusApproval === 'Accept'
? statusTransaction.APPROVED
: statusTransaction.REJECTED;
await manager.save(transactionData);
await this.accountingTransaction({
createTransaction: false,
transactionalEntityManager: manager,
transaction: transactionData,
amount: transactionData.amount,
journals: [
{
coa_id: coaSenderWallet.id,
credit: transactionData.amount,
},
{
coa_id: coaReceiverWallet.id,
debit: transactionData.amount,
},
{
coa_id: coaAR.id,
credit: transactionData.amount,
},
{
coa_id: coaAP.id,
debit: transactionData.amount,
},
],
});
});
return transactionData;
} catch (e) {
throw e;
}
return transactionData;
}
async confirmationAdminDepositReturn(
id: string,
userData,
statusApproval: string,
) {
const transactionData = await this.findApprovalDepositReturn(id);
const coaAR = await this.coaService.findByUserWithRelated(
userData.userId,
transactionData.user_destination,
coaType.ACCOUNT_RECEIVABLE,
);
const coaBank = await this.coaService.findByName(
`${coaType[coaType.BANK]}-SYSTEM`,
);
try {
await this.connection.transaction(async (manager) => {
transactionData.status =
statusApproval === 'Accept'
? statusTransaction.APPROVED
: statusTransaction.REJECTED;
await manager.save(transactionData);
await this.accountingTransaction({
createTransaction: false,
transactionalEntityManager: manager,
transaction: transactionData,
amount: transactionData.amount,
journals: [
{
coa_id: coaAR.id,
credit: transactionData.amount,
},
{
coa_id: coaBank.id,
debit: transactionData.amount,
},
],
});
});
return transactionData;
} catch (e) {
throw e;
}
return transactionData;
}
async transactionHistoryByUser(page: number, user: string) {
const baseQuery = this.transactionRepository
.createQueryBuilder('transaction')
@ -411,6 +572,58 @@ export class TransactionService {
};
}
async findApprovalDepositReturn(id: string) {
try {
return await this.transactionRepository.findOneOrFail({
where: {
id: id,
type: typeTransaction.DEPOSIT_RETURN,
status: statusTransaction.PENDING,
},
});
} catch (e) {
if (e instanceof EntityNotFoundError) {
throw new HttpException(
{
statusCode: HttpStatus.NOT_FOUND,
error: 'Return Deposit not found',
},
HttpStatus.NOT_FOUND,
);
} else {
throw e;
}
}
}
async getAllDepositReturnFromUser(user: string, page: number) {
return this.transactionRepository.findAndCount({
skip: page * 10,
take: 10,
where: {
user: user,
type: typeTransaction.DEPOSIT_RETURN,
},
order: {
createdAt: 'DESC',
},
});
}
async getAllDepositReturnToUser(user: string, page: number) {
return this.transactionRepository.findAndCount({
skip: page * 10,
take: 10,
where: {
user_destination: user,
type: typeTransaction.DEPOSIT_RETURN,
},
order: {
createdAt: 'DESC',
},
});
}
async calculateCommission(data, totalPrice, userData) {
const supervisorData = [];
@ -425,25 +638,21 @@ export class TransactionService {
),
);
//GET Admin
supervisorData.push(
await this.userService.findByUsername(
supervisorData[1].superior.username,
),
);
return supervisorData.map(async (it) => {
return Promise.all(
supervisorData.map(async (it) => {
const coaAccount = await this.coaService.findByUser(
it.id,
coaType.WALLET,
);
const commissionValue = await this.commissionService.findOne(it.role.id);
const commissionValue = await this.commissionService.findOne(
it.roles.id,
);
return {
coa_id: coaAccount.id,
credit: totalPrice * commissionValue.commission,
credit: (totalPrice * commissionValue.commission) / 100,
};
});
}),
);
}
async accountingTransaction(createJournalDto: CreateJournalDto) {