FINERACT-1847: Removing Edit function from Goodwill Credit or Payout Refund or Merchant Issued Refund transactions on API level

This commit is contained in:
Jose Alberto Hernandez
2023-01-19 14:06:23 +01:00
committed by Adam Saghy
parent 66e4ac6194
commit 6d3cac8c02
6 changed files with 197 additions and 15 deletions
@@ -651,6 +651,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
return getTypeOf().isChargeback() && isNotReversed();
}
public boolean isEditable() {
return !(isChargeback() || isGoodwillCredit() || isPayoutRefund() || isMerchantIssuedRefund());
}
public boolean isPenaltyPayment() {
boolean isPenalty = false;
if (isChargePayment()) {
@@ -37,7 +37,6 @@ public class LoanRepaymentAdjustmentCommandHandler implements NewCommandSourceHa
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.adjustLoanTransaction(command.getLoanId(), command.entityId(), command);
}
}
@@ -163,6 +163,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepositor
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException;
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidPaidInAdvanceAmountException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanForeclosureException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanMultiDisbursementException;
@@ -1066,16 +1067,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
transactionId);
}
if (transactionToAdjust.isChargeback()) {
throw new PlatformServiceUnavailableException("error.msg.loan.transaction.update.not.allowed", "Loan transaction:"
+ transactionId + " update not allowed as loan transaction is charge back and is linked to other transaction",
transactionId);
}
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
final boolean isAdjustCommand = (transactionAmount.compareTo(BigDecimal.ZERO) > 0);
if (isAdjustCommand && !transactionToAdjust.isEditable()) {
final String errorMessage = "Loan transaction: " + transactionId + " update not allowed as loan transaction is a "
+ transactionToAdjust.getTypeOf().getCode();
throw new InvalidLoanTransactionTypeException("transaction", "error.msg.loan.transaction.update.not.allowed", errorMessage);
}
// We dont need auto generation for reversal external id... if it is not provided, it remains null (empty)
final String reversalExternalId = command.stringValueOfParameterNamedAllowingNull(LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME);
final ExternalId reversalTxnExternalId = ExternalIdFactory.produce(reversalExternalId);
@@ -27,6 +27,7 @@ import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.util.ArrayList;
import java.util.HashMap;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.CommonConstants;
import org.apache.fineract.integrationtests.common.Utils;
@@ -50,6 +51,7 @@ public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.class);
private ResponseSpecification responseSpec;
private ResponseSpecification responseSpec403;
private RequestSpecification requestSpec;
private LoanTransactionHelper loanTransactionHelper;
private LoanTransactionHelper loanTransactionHelperValidationError;
@@ -73,6 +75,7 @@ public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest {
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
this.responseSpec403 = new ResponseSpecBuilder().expectStatusCode(403).build();
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
this.loanTransactionHelperValidationError = new LoanTransactionHelper(this.requestSpec, new ResponseSpecBuilder().build());
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
@@ -438,4 +441,142 @@ public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest {
}
@Test
public void undoGoodWillCreditTransactionTest() {
// Given
disburseLoanOfAccountingRule(CASH_BASED);
HashMap loanSummaryMap = this.loanTransactionHelper.getLoanSummary(this.requestSpec, this.responseSpec, disbursedLoanID);
// pay off all of principal, interest (no fees or penalties)
final Float principalOutstanding = (Float) loanSummaryMap.get("principalOutstanding");
final Float interestOutstanding = (Float) loanSummaryMap.get("interestOutstanding");
final Float totalOutstanding = (Float) loanSummaryMap.get("totalOutstanding");
final Float overpaidAmount = 159.00f;
final Float transactionAmount = totalOutstanding + overpaidAmount;
final String transactionDate = "09 January 2022";
PostLoansLoanIdTransactionsResponse loanTransactionResponse = loanTransactionHelper.makeLoanRepayment(GOODWILL_CREDIT,
transactionDate, transactionAmount, this.disbursedLoanID);
Assertions.assertNotNull(loanTransactionResponse);
Assertions.assertNotNull(loanTransactionResponse.getResourceId());
// Then
loanTransactionHelper.reverseLoanTransaction(this.disbursedLoanID, loanTransactionResponse.getResourceId(), transactionDate,
responseSpec);
}
@Test
public void undoPayoutRefundTransactionTest() {
// Given
disburseLoanOfAccountingRule(CASH_BASED);
HashMap loanSummaryMap = this.loanTransactionHelper.getLoanSummary(this.requestSpec, this.responseSpec, disbursedLoanID);
// pay off all of principal, interest (no fees or penalties)
final Float principalOutstanding = (Float) loanSummaryMap.get("principalOutstanding");
final Float interestOutstanding = (Float) loanSummaryMap.get("interestOutstanding");
final Float totalOutstanding = (Float) loanSummaryMap.get("totalOutstanding");
final Float overpaidAmount = 159.00f;
final Float transactionAmount = totalOutstanding + overpaidAmount;
final String transactionDate = "09 January 2022";
PostLoansLoanIdTransactionsResponse loanTransactionResponse = loanTransactionHelper.makeLoanRepayment(PAYOUT_REFUND,
transactionDate, transactionAmount, this.disbursedLoanID);
Assertions.assertNotNull(loanTransactionResponse);
Assertions.assertNotNull(loanTransactionResponse.getResourceId());
// Then
loanTransactionHelper.reverseLoanTransaction(this.disbursedLoanID, loanTransactionResponse.getResourceId(), transactionDate,
responseSpec);
}
@Test
public void undoMerchantIssuedRefundTransactionTest() {
// Given
disburseLoanOfAccountingRule(CASH_BASED);
HashMap loanSummaryMap = this.loanTransactionHelper.getLoanSummary(this.requestSpec, this.responseSpec, disbursedLoanID);
// pay off all of principal, interest (no fees or penalties)
final Float principalOutstanding = (Float) loanSummaryMap.get("principalOutstanding");
final Float interestOutstanding = (Float) loanSummaryMap.get("interestOutstanding");
final Float totalOutstanding = (Float) loanSummaryMap.get("totalOutstanding");
final Float overpaidAmount = 159.00f;
final Float transactionAmount = totalOutstanding + overpaidAmount;
final String transactionDate = "09 January 2022";
PostLoansLoanIdTransactionsResponse loanTransactionResponse = loanTransactionHelper.makeLoanRepayment(MERCHANT_ISSUED_REFUND,
transactionDate, transactionAmount, this.disbursedLoanID);
Assertions.assertNotNull(loanTransactionResponse);
Assertions.assertNotNull(loanTransactionResponse.getResourceId());
// Then
loanTransactionHelper.reverseLoanTransaction(this.disbursedLoanID, loanTransactionResponse.getResourceId(), transactionDate,
responseSpec);
}
@Test
public void adjustGoodWillCreditTransactionTest() {
// Given
disburseLoanOfAccountingRule(CASH_BASED);
HashMap loanSummaryMap = this.loanTransactionHelper.getLoanSummary(this.requestSpec, this.responseSpec, disbursedLoanID);
// pay off all of principal, interest (no fees or penalties)
final Float principalOutstanding = (Float) loanSummaryMap.get("principalOutstanding");
final Float interestOutstanding = (Float) loanSummaryMap.get("interestOutstanding");
final Float totalOutstanding = (Float) loanSummaryMap.get("totalOutstanding");
final Float overpaidAmount = 159.00f;
final Float transactionAmount = totalOutstanding + overpaidAmount;
final String transactionDate = "09 January 2022";
PostLoansLoanIdTransactionsResponse loanTransactionResponse = loanTransactionHelper.makeLoanRepayment(GOODWILL_CREDIT,
transactionDate, transactionAmount, this.disbursedLoanID);
Assertions.assertNotNull(loanTransactionResponse);
Assertions.assertNotNull(loanTransactionResponse.getResourceId());
// Then
loanTransactionHelper.adjustLoanTransaction(this.disbursedLoanID, loanTransactionResponse.getResourceId(), transactionDate,
responseSpec403);
}
@Test
public void adjustPayoutRefundTransactionTest() {
// Given
disburseLoanOfAccountingRule(CASH_BASED);
HashMap loanSummaryMap = this.loanTransactionHelper.getLoanSummary(this.requestSpec, this.responseSpec, disbursedLoanID);
// pay off all of principal, interest (no fees or penalties)
final Float principalOutstanding = (Float) loanSummaryMap.get("principalOutstanding");
final Float interestOutstanding = (Float) loanSummaryMap.get("interestOutstanding");
final Float totalOutstanding = (Float) loanSummaryMap.get("totalOutstanding");
final Float overpaidAmount = 159.00f;
final Float transactionAmount = totalOutstanding + overpaidAmount;
final String transactionDate = "09 January 2022";
PostLoansLoanIdTransactionsResponse loanTransactionResponse = loanTransactionHelper.makeLoanRepayment(PAYOUT_REFUND,
transactionDate, transactionAmount, this.disbursedLoanID);
Assertions.assertNotNull(loanTransactionResponse);
Assertions.assertNotNull(loanTransactionResponse.getResourceId());
// Then
loanTransactionHelper.adjustLoanTransaction(this.disbursedLoanID, loanTransactionResponse.getResourceId(), transactionDate,
responseSpec403);
}
@Test
public void adjustMerchantIssuedRefundTransactionTest() {
// Given
disburseLoanOfAccountingRule(CASH_BASED);
HashMap loanSummaryMap = this.loanTransactionHelper.getLoanSummary(this.requestSpec, this.responseSpec, disbursedLoanID);
// pay off all of principal, interest (no fees or penalties)
final Float principalOutstanding = (Float) loanSummaryMap.get("principalOutstanding");
final Float interestOutstanding = (Float) loanSummaryMap.get("interestOutstanding");
final Float totalOutstanding = (Float) loanSummaryMap.get("totalOutstanding");
final Float overpaidAmount = 159.00f;
final Float transactionAmount = totalOutstanding + overpaidAmount;
final String transactionDate = "09 January 2022";
PostLoansLoanIdTransactionsResponse loanTransactionResponse = loanTransactionHelper.makeLoanRepayment(MERCHANT_ISSUED_REFUND,
transactionDate, transactionAmount, this.disbursedLoanID);
Assertions.assertNotNull(loanTransactionResponse);
Assertions.assertNotNull(loanTransactionResponse.getResourceId());
// Then
loanTransactionHelper.adjustLoanTransaction(this.disbursedLoanID, loanTransactionResponse.getResourceId(), transactionDate,
responseSpec403);
}
}
@@ -57,6 +57,7 @@ public class LoanTransactionChargebackTest {
private ResponseSpecification responseSpec;
private ResponseSpecification responseSpecErr400;
private ResponseSpecification responseSpecErr403;
private ResponseSpecification responseSpecErr503;
private RequestSpecification requestSpec;
private LoanTransactionHelper loanTransactionHelper;
@@ -71,6 +72,7 @@ public class LoanTransactionChargebackTest {
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
this.responseSpecErr400 = new ResponseSpecBuilder().expectStatusCode(400).build();
this.responseSpecErr403 = new ResponseSpecBuilder().expectStatusCode(403).build();
this.responseSpecErr503 = new ResponseSpecBuilder().expectStatusCode(503).build();
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
@@ -114,12 +116,30 @@ public class LoanTransactionChargebackTest {
// Try to reverse a Loan Transaction charge back
PostLoansLoanIdTransactionsResponse reverseTransactionResponse = loanTransactionHelper.reverseLoanTransaction(loanId,
chargebackTransactionId, operationDate, responseSpecErr503);
chargebackTransactionId, operationDate, responseSpecErr403);
// Try to reverse a Loan Transaction repayment with linked transactions
reverseTransactionResponse = loanTransactionHelper.reverseLoanTransaction(loanId, transactionId, operationDate, responseSpecErr503);
}
@Test
public void applyAndAdjustLoanTransactionChargeback() {
// Client and Loan account creation
final Integer loanId = createAccounts(15, 1);
Float amount = Float.valueOf(amountVal);
PostLoansLoanIdTransactionsResponse loanTransactionResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
loanId);
assertNotNull(loanTransactionResponse);
final Long transactionId = loanTransactionResponse.getResourceId();
final Long chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "1000.00", 0,
responseSpec);
// Then
loanTransactionHelper.adjustLoanTransaction(loanId, chargebackTransactionId, operationDate, responseSpecErr403);
}
@Test
public void applyLoanTransactionChargebackWithAmountZero() {
// Client and Loan account creation
@@ -141,7 +161,6 @@ public class LoanTransactionChargebackTest {
loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.closed.obligations.met");
loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "0.00", 0, responseSpecErr400);
}
@Test
@@ -504,7 +504,14 @@ public class LoanTransactionHelper extends IntegrationTest {
}
public HashMap reverseRepayment(final Integer loanId, final Integer transactionId, String date) {
return (HashMap) performLoanTransaction(createLoanTransactionURL(UNDO, loanId, transactionId), getUndoJsonBody(date), "");
return (HashMap) performLoanTransaction(createLoanTransactionURL(UNDO, loanId, transactionId),
getAdjustTransactionJsonBody(date, "0"), "");
}
public PostLoansLoanIdTransactionsResponse makeLoanRepayment(final String repaymentTypeCommand, final String date,
final Float amountToBePaid, final Integer loanID) {
log.info("Repayment with amount {} in {} for Loan {}", amountToBePaid, date, loanID);
return postLoanTransaction(createLoanTransactionURL(repaymentTypeCommand, loanID), getRepaymentBodyAsJSON(date, amountToBePaid));
}
public PostLoansLoanIdTransactionsResponse makeLoanRepayment(final String date, final Float amountToBePaid, final Integer loanID) {
@@ -701,9 +708,16 @@ public class LoanTransactionHelper extends IntegrationTest {
return ok(fineract().loanTransactions.adjustLoanTransaction1(loanId, transactionExternalId, request, "adjust"));
}
public PostLoansLoanIdTransactionsResponse adjustLoanTransaction(final Integer loanId, final Long transactionId, String date,
ResponseSpecification responseSpec) {
return postLoanTransaction(createLoanTransactionURL(null, loanId, transactionId.intValue()),
getAdjustTransactionJsonBody(date, "10"), responseSpec);
}
public PostLoansLoanIdTransactionsResponse reverseLoanTransaction(final Integer loanId, final Long transactionId, String date,
ResponseSpecification responseSpec) {
return postLoanTransaction(createLoanTransactionURL(UNDO, loanId, transactionId.intValue()), getUndoJsonBody(date), responseSpec);
return postLoanTransaction(createLoanTransactionURL(UNDO, loanId, transactionId.intValue()),
getAdjustTransactionJsonBody(date, "0"), responseSpec);
}
public PostLoansLoanIdTransactionsResponse reverseLoanTransaction(final Long loanId, final Long transactionId,
@@ -1074,10 +1088,10 @@ public class LoanTransactionHelper extends IntegrationTest {
return new Gson().toJson(map);
}
private String getUndoJsonBody(String date) {
private String getAdjustTransactionJsonBody(String date, String amount) {
final HashMap<String, String> map = new HashMap<>();
map.put("transactionDate", date);
map.put("transactionAmount", "0");
map.put("transactionAmount", amount);
map.put("dateFormat", "dd MMMM yyyy");
map.put("locale", "en");
return new Gson().toJson(map);
@@ -1257,8 +1271,11 @@ public class LoanTransactionHelper extends IntegrationTest {
}
private String createLoanTransactionURL(final String command, final Integer loanID, final Integer transactionId) {
return "/fineract-provider/api/v1/loans/" + loanID + "/transactions/" + transactionId + "?command=" + command + "&"
+ Utils.TENANT_IDENTIFIER;
String url = "/fineract-provider/api/v1/loans/" + loanID + "/transactions/" + transactionId + "?";
if (command != null) {
url = url + "command=" + command + "&";
}
return url + Utils.TENANT_IDENTIFIER;
}
private String createGlimAccountURL(final String command, final Integer glimID) {