From fe7702836e051af28ffb930db65d545187fb0bab Mon Sep 17 00:00:00 2001 From: ilham Date: Wed, 29 Jun 2022 18:07:07 +0700 Subject: [PATCH 1/3] fix: rollback --- src/transaction/transaction.controller.ts | 6 +- src/transaction/transaction.service.ts | 70 ++++++++++------------- 2 files changed, 33 insertions(+), 43 deletions(-) diff --git a/src/transaction/transaction.controller.ts b/src/transaction/transaction.controller.ts index 4f37830..8cac296 100644 --- a/src/transaction/transaction.controller.ts +++ b/src/transaction/transaction.controller.ts @@ -132,9 +132,9 @@ export class TransactionController { }; } - @Get('rollback-jurnal/:trxId') - async rollbackJurnal(@Request() req, @Param('trxId') trxId: string) { - const data = await this.transactionService.rollbackJurnal(trxId); + @Post('rollback-jurnal') + async rollbackJurnal(@Body() request, @Request() req) { + const data = await this.transactionService.rollbackJurnal(request.trxId); return { data, statusCode: HttpStatus.OK, diff --git a/src/transaction/transaction.service.ts b/src/transaction/transaction.service.ts index 6d87697..e33c081 100644 --- a/src/transaction/transaction.service.ts +++ b/src/transaction/transaction.service.ts @@ -3,7 +3,7 @@ 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 { Between, Connection, EntityNotFoundError, Repository } from 'typeorm'; +import { Between, Connection, EntityNotFoundError, In, Repository } from 'typeorm'; import { COA } from './entities/coa.entity'; import { TransactionJournal } from './entities/transaction-journal.entity'; import { CoaService } from './coa.service'; @@ -1317,35 +1317,25 @@ export class TransactionService { return res; } - async rollbackJurnal(trxId: string) { - const dataTransaction = await this.transactionRepository.findOne({ - where: { - id: trxId, - }, - relations: ['product_price'], - }); + async rollbackJurnal(trxId: string[]) { + // const dataTransaction = await this.transactionRepository.findOne({ + // where: { + // id: trxId, + // }, + // relations: ['product_price'], + // }); - let dataTransactionJurnal; - if (dataTransaction.type == typeTransaction.ORDER) { - dataTransactionJurnal = await this.transactionJournalRepository.find({ - where: { - transaction_head: trxId, - }, - relations: ['coa'], - skip: 4, - order: { - createdAt: 'ASC', - }, - }); - } else { - dataTransactionJurnal = await this.transactionJournalRepository.find({ - where: { - transaction_head: trxId, - }, - relations: ['coa'], - }); + if (trxId.length % 2 != 0) { + throw Error("Not Balance") } + const dataTransactionJurnal = await this.transactionJournalRepository.find({ + where: { + id: In(trxId), + }, + relations: ['coa'], + }); + let dataRollbackJurnal = []; dataTransactionJurnal.map((it) => { @@ -1362,20 +1352,20 @@ export class TransactionService { dataRollbackJurnal.push(data); }); - if (dataRollbackJurnal.length > 0) { - try { - await this.connection.transaction(async (manager) => { - await this.accountingTransaction({ - createTransaction: false, - transactionalEntityManager: manager, - transaction: dataTransaction, - amount: dataTransaction.amount, - journals: dataRollbackJurnal, - }); + const dataTransaction = new Transactions(); + + try { + await this.connection.transaction(async (manager) => { + await this.accountingTransaction({ + createTransaction: false, + transactionalEntityManager: manager, + transaction: dataTransaction, + amount: dataTransaction.amount, + journals: dataRollbackJurnal, }); - } catch (e) { - throw e; - } + }); + } catch (e) { + throw e; } } From 1fcc7ca8ad27ae06791f54e1d1622c855ae8ce77 Mon Sep 17 00:00:00 2001 From: Fadli Date: Wed, 29 Jun 2022 18:10:05 +0700 Subject: [PATCH 2/3] -add condition for ppob_callback can't hit 2 times if the transaction is already success or failed (only pending) --- src/transaction/ppob_callback.controller.ts | 18 +--- src/transaction/transaction.service.ts | 106 +++++++++++++------- 2 files changed, 73 insertions(+), 51 deletions(-) diff --git a/src/transaction/ppob_callback.controller.ts b/src/transaction/ppob_callback.controller.ts index a196727..5da6003 100644 --- a/src/transaction/ppob_callback.controller.ts +++ b/src/transaction/ppob_callback.controller.ts @@ -19,31 +19,17 @@ export class PpobCallbackController { if (response['statuscode'] == 2) { //TODO: UPDATE GAGAL - const updateTransaction = - await this.transactionService.callbackOrderFailed( + await this.transactionService.callbackOrderFailed( response['clientid'], response, ); - - return { - updateTransaction, - statusCode: HttpStatus.BAD_REQUEST, - message: 'failed to proccess', - }; } //TODO: UPDATE BERHASIL - const updateTransaction = - await this.transactionService.callbackOrderSuccess( + await this.transactionService.callbackOrderSuccess( response['clientid'], response, ); - - return { - updateTransaction, - statusCode: HttpStatus.OK, - message: 'success', - }; } @Public() diff --git a/src/transaction/transaction.service.ts b/src/transaction/transaction.service.ts index 2c3abb4..a28fbfb 100644 --- a/src/transaction/transaction.service.ts +++ b/src/transaction/transaction.service.ts @@ -1064,6 +1064,17 @@ export class TransactionService { relations: ['product_price'], }); + if (dataTransaction.status == statusTransaction.FAILED) { + return { + statusCode: HttpStatus.BAD_REQUEST, + message: 'failed to update, the transaction already failed', + }; + } else if (dataTransaction.status == statusTransaction.SUCCESS) { + return { + statusCode: HttpStatus.BAD_REQUEST, + message: 'failed to update, the transaction already success', + }; + } else { const dataMsg = callback.msg; const failedReason = dataMsg.split('.'); @@ -1074,29 +1085,29 @@ export class TransactionService { const userData = await this.userService.findExist(dataTransaction.user); const product_price = await this.productHistoryPriceService.findById( - dataTransaction.product_price.id, + dataTransaction.product_price.id, ); const product = await this.productService.findOneById( - product_price.product.id, + product_price.product.id, ); //GET COA const coaAccount = await this.coaService.findByUser( - userData.id, - coaType.WALLET, + userData.id, + coaType.WALLET, ); const coaInventory = await this.coaService.findByName( - `${coaType[coaType.INVENTORY]}-${product.supplier.code}`, + `${coaType[coaType.INVENTORY]}-${product.supplier.code}`, ); const coaCostOfSales = await this.coaService.findByName( - `${coaType[coaType.COST_OF_SALES]}-${product.supplier.code}`, + `${coaType[coaType.COST_OF_SALES]}-${product.supplier.code}`, ); const coaSales = await this.coaService.findByName( - `${coaType[coaType.SALES]}-SYSTEM`, + `${coaType[coaType.SALES]}-SYSTEM`, ); try { @@ -1135,16 +1146,23 @@ export class TransactionService { if (userData.partner) { const message = `Transaksi ${product.code} dengan tujuan ${dataTransaction.destination} telah gagal.`; this.callbackToPartner( - userData.id, - message, - dataTransaction.partner_trx_id, - dataTransaction.amount, - product.code, - dataTransaction.destination, - '-', - 'gagal', + userData.id, + message, + dataTransaction.partner_trx_id, + dataTransaction.amount, + product.code, + dataTransaction.destination, + '-', + 'gagal', ); } + + return { + statusCode: HttpStatus.BAD_REQUEST, + message: 'failed to proccess', + }; + + } } async callbackOrderSuccess(supplier_trx_id: string, callback: any) { @@ -1155,6 +1173,19 @@ export class TransactionService { relations: ['product_price'], }); + + if (dataTransaction.status == statusTransaction.FAILED) { + return { + statusCode: HttpStatus.BAD_REQUEST, + message: 'failed to update, the transaction already failed', + }; + } else if (dataTransaction.status == statusTransaction.SUCCESS) { + return { + statusCode: HttpStatus.BAD_REQUEST, + message: 'failed to update, the transaction already success', + }; + } else { + dataTransaction.status = statusTransaction.SUCCESS; dataTransaction.seri_number = callback['sn']; dataTransaction.callback_json = callback; @@ -1164,33 +1195,33 @@ export class TransactionService { let supervisorData = []; const product_price = await this.productHistoryPriceService.findById( - dataTransaction.product_price.id, + dataTransaction.product_price.id, ); const product = await this.productService.findOneById( - product_price.product.id, + product_price.product.id, ); let profit = product_price.mark_up_price; //GET COA const coaExpense = await this.coaService.findByName( - `${coaType[coaType.EXPENSE]}-SYSTEM`, + `${coaType[coaType.EXPENSE]}-SYSTEM`, ); if (userData.partner != null) { //GET SALES supervisorData = await this.calculateCommission( - supervisorData, - profit, - userData, + supervisorData, + profit, + userData, ); profit = supervisorData - .map((item) => { - return item.credit; - }) - .reduce((prev, curr) => { - return prev + curr; - }, 0); + .map((item) => { + return item.credit; + }) + .reduce((prev, curr) => { + return prev + curr; + }, 0); supervisorData = supervisorData.concat([ { @@ -1219,16 +1250,21 @@ export class TransactionService { if (userData.partner) { const message = `Transaksi ${product.code} dengan tujuan ${dataTransaction.destination} telah berhasil.`; this.callbackToPartner( - userData.id, - message, - dataTransaction.partner_trx_id, - dataTransaction.amount, - product.code, - dataTransaction.destination, - dataTransaction.seri_number, - 'berhasil', + userData.id, + message, + dataTransaction.partner_trx_id, + dataTransaction.amount, + product.code, + dataTransaction.destination, + dataTransaction.seri_number, + 'berhasil', ); } + return { + statusCode: HttpStatus.OK, + message: 'success', + }; + } } async callbackToPartner( From 7fe73c0dc42aa54a8cbef97560fd9d074671f79f Mon Sep 17 00:00:00 2001 From: Fadli Date: Wed, 29 Jun 2022 22:46:28 +0700 Subject: [PATCH 3/3] -add condition for ppob_callback can't hit 2 times if the transaction is already success or failed (only pending) --- src/transaction/ppob_callback.controller.ts | 53 +++---- src/transaction/transaction.service.ts | 158 ++++++++++++++++---- 2 files changed, 155 insertions(+), 56 deletions(-) diff --git a/src/transaction/ppob_callback.controller.ts b/src/transaction/ppob_callback.controller.ts index 5da6003..90c5eae 100644 --- a/src/transaction/ppob_callback.controller.ts +++ b/src/transaction/ppob_callback.controller.ts @@ -19,17 +19,18 @@ export class PpobCallbackController { if (response['statuscode'] == 2) { //TODO: UPDATE GAGAL - await this.transactionService.callbackOrderFailed( - response['clientid'], - response, - ); + await this.transactionService.checkCallbackOrderFailed( + response['clientid'], + response, + ); } //TODO: UPDATE BERHASIL - await this.transactionService.callbackOrderSuccess( - response['clientid'], - response, - ); + await this.transactionService.checkCallbackOrderSuccess( + response['clientid'], + response, + ); + } @Public() @@ -41,11 +42,11 @@ export class PpobCallbackController { if (response['status'] != 20) { //TODO: UPDATE GAGAL await this.transactionService.updateBill( - response['refid'], - null, - null, - false, - response['message'], + response['refid'], + null, + null, + false, + response['message'], ); return { @@ -57,21 +58,21 @@ export class PpobCallbackController { const splitMessage = response['message'].split('"'); //TODO: UPDATE BERHASIL await this.transactionService.updateBill( - response['refid'], - Number(splitMessage[21].replace(/^\D+/g, '')), - Number(splitMessage[17].replace(/^\D+/g, '')), - true, - response['message'], + response['refid'], + Number(splitMessage[21].replace(/^\D+/g, '')), + Number(splitMessage[17].replace(/^\D+/g, '')), + true, + response['message'], ); // } else { if (response['status'].toString() != '20') { //TODO: UPDATE GAGAL const updateTransaction = - await this.transactionService.callbackOrderFailed( - response['refid'], - response, - ); + await this.transactionService.callbackOrderFailed( + response['refid'], + response, + ); return { updateTransaction, @@ -82,10 +83,10 @@ export class PpobCallbackController { //TODO: UPDATE BERHASIL const updateTransaction = - await this.transactionService.callbackOrderSuccess( - response['refid'], - response, - ); + await this.transactionService.callbackOrderSuccess( + response['refid'], + response, + ); } this.logger.log({ diff --git a/src/transaction/transaction.service.ts b/src/transaction/transaction.service.ts index e33c081..4059304 100644 --- a/src/transaction/transaction.service.ts +++ b/src/transaction/transaction.service.ts @@ -1,35 +1,30 @@ -import { HttpException, HttpStatus, Injectable, Logger } 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 { Between, Connection, EntityNotFoundError, In, Repository } from 'typeorm'; -import { COA } from './entities/coa.entity'; -import { TransactionJournal } from './entities/transaction-journal.entity'; -import { CoaService } from './coa.service'; +import {HttpException, HttpStatus, Injectable, Logger} 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 {Between, Connection, EntityNotFoundError, In, Repository} from 'typeorm'; +import {COA} from './entities/coa.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 { CreateJournalDto } from './dto/create-journal.dto'; -import { UsersService } from 'src/users/users.service'; -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'; -import { UserDetail } from '../users/entities/user_detail.entity'; -import { doTransaction } from '../helper/irs-api'; -import { ProductHistoryPrice } from '../product/entities/product-history-price.entity'; +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 {CreateJournalDto} from './dto/create-journal.dto'; +import {UsersService} from 'src/users/users.service'; +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'; +import {UserDetail} from '../users/entities/user_detail.entity'; +import {doTransaction} from '../helper/irs-api'; +import {ProductHistoryPrice} from '../product/entities/product-history-price.entity'; import axios from 'axios'; -import { CheckBillHistory } from './entities/check-bill-history.entity'; -import { CallbackPartner } from './entities/callback-partner.entity'; +import {CheckBillHistory} from './entities/check-bill-history.entity'; +import {CallbackPartner} from './entities/callback-partner.entity'; @Injectable() export class TransactionService { @@ -1059,6 +1054,109 @@ export class TransactionService { return transactionData; } + async checkCallbackOrderFailed(supplier_trx_id: string, callback: any) { + + const transactionData = await this.findDataTransactionBySupplierTrxId( + supplier_trx_id + ); + + if (transactionData.status == statusTransaction.FAILED) { + throw new HttpException( + { + statusCode: HttpStatus.BAD_REQUEST, + error: 'failed to update, the transaction already failed', + }, + HttpStatus.BAD_REQUEST, + ); + } else if (transactionData.status == statusTransaction.SUCCESS) { + throw new HttpException( + { + statusCode: HttpStatus.BAD_REQUEST, + error: 'failed to update, the transaction already success', + }, + HttpStatus.BAD_REQUEST, + ); + } else { + + const updateTransaction = + await this.callbackOrderFailed( + supplier_trx_id, + callback, + ); + + return { + updateTransaction, + statusCode: HttpStatus.BAD_REQUEST, + message: 'failed to proccess', + }; + + } + + } + + async checkCallbackOrderSuccess(supplier_trx_id: string, callback: any) { + + const transactionData = await this.findDataTransactionBySupplierTrxId( + supplier_trx_id + ); + + if (transactionData.status == statusTransaction.FAILED) { + throw new HttpException( + { + statusCode: HttpStatus.BAD_REQUEST, + error: 'failed to update, the transaction already failed', + }, + HttpStatus.BAD_REQUEST, + ); + } else if (transactionData.status == statusTransaction.SUCCESS) { + throw new HttpException( + { + statusCode: HttpStatus.BAD_REQUEST, + error: 'failed to update, the transaction already success', + }, + HttpStatus.BAD_REQUEST, + ); + } else { + + const updateTransaction = + await this.callbackOrderSuccess( + supplier_trx_id, + callback, + ); + + return { + updateTransaction, + statusCode: HttpStatus.OK, + message: 'success', + }; + + } + + } + + async findDataTransactionBySupplierTrxId(supplier_trx_id: string) { + try { + return await this.transactionRepository.findOneOrFail({ + where: { + supplier_trx_id: supplier_trx_id, + }, + relations: ['product_price'], + }); + } catch (e) { + if (e instanceof EntityNotFoundError) { + throw new HttpException( + { + statusCode: HttpStatus.NOT_FOUND, + error: 'data not found', + }, + HttpStatus.NOT_FOUND, + ); + } else { + throw e; + } + } + } + async callbackOrderFailed(supplier_trx_id: string, callback: any) { const dataTransaction = await this.transactionRepository.findOne({ where: {