diff --git a/src/helper/enum-list.ts b/src/helper/enum-list.ts index 8c29165..aad950e 100644 --- a/src/helper/enum-list.ts +++ b/src/helper/enum-list.ts @@ -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 { diff --git a/src/transaction/coa.service.ts b/src/transaction/coa.service.ts index fac2a6e..3fb4a7e 100644 --- a/src/transaction/coa.service.ts +++ b/src/transaction/coa.service.ts @@ -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, diff --git a/src/transaction/dto/add-saldo-supplier.dto.ts b/src/transaction/dto/add-saldo-supplier.dto.ts index 8927ac7..518c5b5 100644 --- a/src/transaction/dto/add-saldo-supplier.dto.ts +++ b/src/transaction/dto/add-saldo-supplier.dto.ts @@ -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() diff --git a/src/transaction/dto/deposit_return.dto.ts b/src/transaction/dto/deposit_return.dto.ts new file mode 100644 index 0000000..7d1110b --- /dev/null +++ b/src/transaction/dto/deposit_return.dto.ts @@ -0,0 +1,7 @@ +import { DistributeTransactionDto } from './distribute-transaction.dto'; +import { IsNotEmpty } from 'class-validator'; + +export class DepositReturnDto extends DistributeTransactionDto { + @IsNotEmpty() + image_prove: string; +} diff --git a/src/transaction/dto/distribute-transaction.dto.ts b/src/transaction/dto/distribute-transaction.dto.ts index c1728ce..7d79186 100644 --- a/src/transaction/dto/distribute-transaction.dto.ts +++ b/src/transaction/dto/distribute-transaction.dto.ts @@ -6,7 +6,4 @@ export class DistributeTransactionDto { @IsNotEmpty() destination: string; - - @IsNotEmpty() - supplier: string; } diff --git a/src/transaction/entities/transactions.entity.ts b/src/transaction/entities/transactions.entity.ts index 0a3d34e..a1fed62 100644 --- a/src/transaction/entities/transactions.entity.ts +++ b/src/transaction/entities/transactions.entity.ts @@ -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; } diff --git a/src/transaction/transaction.controller.ts b/src/transaction/transaction.controller.ts index d93fad0..b3e472c 100644 --- a/src/transaction/transaction.controller.ts +++ b/src/transaction/transaction.controller.ts @@ -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( - orderTransactionDto, - req.user, - ); + 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', + }; + } } diff --git a/src/transaction/transaction.service.ts b/src/transaction/transaction.service.ts index eaa232b..5204d80 100644 --- a/src/transaction/transaction.service.ts +++ b/src/transaction/transaction.service.ts @@ -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,77 +130,80 @@ export class TransactionService { currentUser.username, ); - if (userData.roles.name != 'Admin') { - throw new HttpException( - { - statusCode: HttpStatus.NOT_ACCEPTABLE, - error: 'Roles Not Admin', - }, - HttpStatus.NOT_ACCEPTABLE, + const supplier = await this.supplierService.findByActive(); + + try { + if (userData.roles.name != 'Admin') { + throw new HttpException( + { + statusCode: HttpStatus.NOT_ACCEPTABLE, + error: 'Roles Not Admin', + }, + HttpStatus.NOT_ACCEPTABLE, + ); + } + + // GET COA + const coaAR = await this.coaService.findByTwoUser( + distributeTransactionDto.destination, + currentUser.userId, + coaType.ACCOUNT_RECEIVABLE, + ); + const coaWallet = await this.coaService.findByUser( + distributeTransactionDto.destination, + coaType.WALLET, + ); + const coaBudget = await this.coaService.findByName( + `${coaType[coaType.BUDGET]}-${supplier.code}`, ); - } - //GET Supplier - const supplier = await this.supplierService.findByCode( - distributeTransactionDto.supplier, - ); + const coaContraBudget = await this.coaService.findByName( + `${coaType[coaType.CONTRA_BUDGET]}-${supplier.code}`, + ); - // GET COA - const coaAR = await this.coaService.findByUser( - distributeTransactionDto.destination, - coaType.ACCOUNT_RECEIVABLE, - ); - const coaWallet = await this.coaService.findByUser( - distributeTransactionDto.destination, - coaType.WALLET, - ); - const coaBudget = await this.coaService.findByName( - `${coaType[coaType.BUDGET]}-${supplier.code}`, - ); + await this.connection.transaction(async (manager) => { + //INSERT TRANSACTION + const transactionData = new Transactions(); - const coaContraBudget = await this.coaService.findByName( - `${coaType[coaType.CONTRA_BUDGET]}-${supplier.code}`, - ); + 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; - await this.connection.transaction(async (manager) => { - //INSERT TRANSACTION - const transactionData = new Transactions(); + await manager.insert(Transactions, transactionData); - 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, - }, - { - coa_id: coaBudget.id, - credit: transactionData.amount, - }, - { - coa_id: coaContraBudget.id, - debit: transactionData.amount, - }, - ], + 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, + }, + { + coa_id: coaBudget.id, + credit: transactionData.amount, + }, + { + coa_id: coaContraBudget.id, + debit: transactionData.amount, + }, + ], + }); }); - }); - return true; + 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 Promise.all( + supervisorData.map(async (it) => { + const coaAccount = await this.coaService.findByUser( + it.id, + coaType.WALLET, + ); + const commissionValue = await this.commissionService.findOne( + it.roles.id, + ); + return { + coa_id: coaAccount.id, + credit: (totalPrice * commissionValue.commission) / 100, + }; + }), ); - - return supervisorData.map(async (it) => { - const coaAccount = await this.coaService.findByUser( - it.id, - coaType.WALLET, - ); - const commissionValue = await this.commissionService.findOne(it.role.id); - - return { - coa_id: coaAccount.id, - credit: totalPrice * commissionValue.commission, - }; - }); } async accountingTransaction(createJournalDto: CreateJournalDto) {