FINERACT-1761: New Charge Adjustment transaction
This commit is contained in:
@@ -205,6 +205,17 @@
|
||||
"items": "org.apache.fineract.avro.loan.v1.LoanChargePaidByDataV1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"default": null,
|
||||
"name": "transactionRelations",
|
||||
"type": [
|
||||
"null",
|
||||
{
|
||||
"type": "array",
|
||||
"items": "org.apache.fineract.avro.loan.v1.LoanTransactionRelationDataV1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "LoanTransactionRelationDataV1",
|
||||
"namespace": "org.apache.fineract.avro.loan.v1",
|
||||
"type": "record",
|
||||
"fields": [
|
||||
{
|
||||
"name": "fromLoanTransaction",
|
||||
"type": "int"
|
||||
},
|
||||
{
|
||||
"default": null,
|
||||
"name": "toLoanTransaction",
|
||||
"type": [
|
||||
"null",
|
||||
"int"
|
||||
]
|
||||
},
|
||||
{
|
||||
"default": null,
|
||||
"name": "toLoanCharge",
|
||||
"type": [
|
||||
"null",
|
||||
"int"
|
||||
]
|
||||
},
|
||||
{
|
||||
"default": null,
|
||||
"name": "amount",
|
||||
"type": [
|
||||
"null",
|
||||
"bigdecimal"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "relationType",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"default": null,
|
||||
"name": "paymentType",
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+6
-6
@@ -120,16 +120,16 @@ final class GLAccountsApiResourceSwagger {
|
||||
public Boolean manualEntriesAllowed;
|
||||
|
||||
@Schema(example = "1")
|
||||
public String type;
|
||||
public Integer type;
|
||||
|
||||
@Schema(example = "10")
|
||||
public String tagId;
|
||||
public Long tagId;
|
||||
|
||||
@Schema(example = "1")
|
||||
public Long parentId;
|
||||
|
||||
@Schema(example = "1")
|
||||
public String usage;
|
||||
public Integer usage;
|
||||
|
||||
@Schema(example = "Desc")
|
||||
public String description;
|
||||
@@ -144,7 +144,7 @@ final class GLAccountsApiResourceSwagger {
|
||||
}
|
||||
|
||||
@Schema(example = "22")
|
||||
public int resourceId;
|
||||
public Long resourceId;
|
||||
}
|
||||
|
||||
@Schema(description = "PutGLAccountsRequest")
|
||||
@@ -175,7 +175,7 @@ final class GLAccountsApiResourceSwagger {
|
||||
}
|
||||
|
||||
@Schema(example = "1")
|
||||
public int resourceId;
|
||||
public Long resourceId;
|
||||
public PutGLAccountsResponsechangesSwagger changes;
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ final class GLAccountsApiResourceSwagger {
|
||||
}
|
||||
|
||||
@Schema(example = "1")
|
||||
public int resourceId;
|
||||
public Long resourceId;
|
||||
|
||||
private DeleteGLAccountsRequestchangesSwagger changes;
|
||||
}
|
||||
|
||||
+2
@@ -24,6 +24,7 @@ import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@@ -58,4 +59,5 @@ public class LoanTransactionDTO {
|
||||
private boolean isLoanToLoanTransfer;
|
||||
|
||||
private final String chargeRefundChargeType;
|
||||
private final LoanChargeData loanChargeData;
|
||||
}
|
||||
|
||||
+14
-1
@@ -66,6 +66,7 @@ import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatfor
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientTransaction;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
@@ -125,6 +126,7 @@ public class AccountingProcessorHelper {
|
||||
final boolean reversed = (Boolean) map.get("reversed");
|
||||
final Long paymentTypeId = (Long) map.get("paymentTypeId");
|
||||
final String chargeRefundChargeType = (String) map.get("chargeRefundChargeType");
|
||||
final LoanChargeData loanChargeData = (LoanChargeData) map.get("loanChargeData");
|
||||
|
||||
final List<ChargePaymentDTO> feePaymentDetails = new ArrayList<>();
|
||||
final List<ChargePaymentDTO> penaltyPaymentDetails = new ArrayList<>();
|
||||
@@ -152,7 +154,7 @@ public class AccountingProcessorHelper {
|
||||
}
|
||||
final LoanTransactionDTO transaction = new LoanTransactionDTO(transactionOfficeId, paymentTypeId, transactionId,
|
||||
transactionDate, transactionType, amount, principal, interest, fees, penalties, overPayments, reversed,
|
||||
penaltyPaymentDetails, feePaymentDetails, isAccountTransfer, chargeRefundChargeType);
|
||||
penaltyPaymentDetails, feePaymentDetails, isAccountTransfer, chargeRefundChargeType, loanChargeData);
|
||||
Boolean isLoanToLoanTransfer = (Boolean) accountingBridgeData.get("isLoanToLoanTransfer");
|
||||
transaction.setLoanToLoanTransfer(isLoanToLoanTransfer != null && isLoanToLoanTransfer);
|
||||
newLoanTransactions.add(transaction);
|
||||
@@ -622,6 +624,17 @@ public class AccountingProcessorHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public void createDebitJournalEntryOrReversalForLoanCharges(final Office office, final String currencyCode,
|
||||
final int accountMappingTypeId, final Long loanProductId, final Long chargeId, final Long loanId, final String transactionId,
|
||||
final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) {
|
||||
final GLAccount account = getLinkedGLAccountForLoanCharges(loanProductId, accountMappingTypeId, chargeId);
|
||||
if (isReversal) {
|
||||
createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount);
|
||||
} else {
|
||||
createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount);
|
||||
}
|
||||
}
|
||||
|
||||
public void createCreditJournalEntryOrReversalForLoanCharges(final Office office, final String currencyCode,
|
||||
final int accountMappingTypeId, final Long loanProductId, final Long loanId, final String transactionId,
|
||||
final LocalDate transactionDate, final BigDecimal totalAmount, final Boolean isReversal,
|
||||
|
||||
+106
-2
@@ -61,9 +61,10 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
|
||||
/***
|
||||
* Handle repayments, loan refunds, repayments at disbursement and reversal of Repayments and Repayments at
|
||||
* disbursement
|
||||
* disbursement (except charge adjustment)
|
||||
***/
|
||||
else if (loanTransactionDTO.getTransactionType().isRepaymentType()
|
||||
else if ((loanTransactionDTO.getTransactionType().isRepaymentType()
|
||||
&& !loanTransactionDTO.getTransactionType().isChargeAdjustment())
|
||||
|| loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement()
|
||||
|| loanTransactionDTO.getTransactionType().isChargePayment()) {
|
||||
createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, false,
|
||||
@@ -99,6 +100,109 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
else if (loanTransactionDTO.getTransactionType().isChargeback()) {
|
||||
createJournalEntriesForChargeback(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
// Logic for Charge Adjustment
|
||||
else if (loanTransactionDTO.getTransactionType().isChargeAdjustment()) {
|
||||
createJournalEntriesForChargeAdjustment(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createJournalEntriesForChargeAdjustment(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
|
||||
// loan properties
|
||||
final Long loanProductId = loanDTO.getLoanProductId();
|
||||
final Long loanId = loanDTO.getLoanId();
|
||||
final String currencyCode = loanDTO.getCurrencyCode();
|
||||
|
||||
// transaction properties
|
||||
final String transactionId = loanTransactionDTO.getTransactionId();
|
||||
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
|
||||
final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
|
||||
final BigDecimal interestAmount = loanTransactionDTO.getInterest();
|
||||
final BigDecimal feesAmount = loanTransactionDTO.getFees();
|
||||
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
|
||||
final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment();
|
||||
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
||||
final boolean isReversal = loanTransactionDTO.isReversed();
|
||||
|
||||
BigDecimal totalDebitAmount = new BigDecimal(0);
|
||||
|
||||
Map<GLAccount, BigDecimal> accountMap = new HashMap<>();
|
||||
|
||||
// handle principal payment (and reversals)
|
||||
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(principalAmount);
|
||||
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
|
||||
AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId);
|
||||
accountMap.put(account, principalAmount);
|
||||
}
|
||||
|
||||
// handle interest payment (and reversals)
|
||||
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(interestAmount);
|
||||
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
|
||||
AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), paymentTypeId);
|
||||
if (accountMap.containsKey(account)) {
|
||||
BigDecimal amount = accountMap.get(account).add(interestAmount);
|
||||
accountMap.put(account, amount);
|
||||
} else {
|
||||
accountMap.put(account, interestAmount);
|
||||
}
|
||||
}
|
||||
|
||||
// handle fees payment (and reversals)
|
||||
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(feesAmount);
|
||||
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
|
||||
AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), paymentTypeId);
|
||||
if (accountMap.containsKey(account)) {
|
||||
BigDecimal amount = accountMap.get(account).add(feesAmount);
|
||||
accountMap.put(account, amount);
|
||||
} else {
|
||||
accountMap.put(account, feesAmount);
|
||||
}
|
||||
}
|
||||
|
||||
// handle penalties payment (and reversals)
|
||||
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
|
||||
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
|
||||
AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), paymentTypeId);
|
||||
if (accountMap.containsKey(account)) {
|
||||
BigDecimal amount = accountMap.get(account).add(penaltiesAmount);
|
||||
accountMap.put(account, amount);
|
||||
} else {
|
||||
accountMap.put(account, penaltiesAmount);
|
||||
}
|
||||
}
|
||||
|
||||
// handle overpayment
|
||||
if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
|
||||
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
|
||||
paymentTypeId);
|
||||
if (accountMap.containsKey(account)) {
|
||||
BigDecimal amount = accountMap.get(account).add(overPaymentAmount);
|
||||
accountMap.put(account, amount);
|
||||
} else {
|
||||
accountMap.put(account, overPaymentAmount);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<GLAccount, BigDecimal> entry : accountMap.entrySet()) {
|
||||
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate,
|
||||
entry.getValue(), isReversal, entry.getKey());
|
||||
}
|
||||
|
||||
if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
Long chargeId = loanTransactionDTO.getLoanChargeData().getChargeId();
|
||||
Integer accountMappingTypeId;
|
||||
if (loanTransactionDTO.getLoanChargeData().isPenalty()) {
|
||||
accountMappingTypeId = AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue();
|
||||
} else {
|
||||
accountMappingTypeId = AccrualAccountsForLoan.INCOME_FROM_FEES.getValue();
|
||||
}
|
||||
this.helper.createDebitJournalEntryOrReversalForLoanCharges(office, currencyCode, accountMappingTypeId, loanProductId, chargeId,
|
||||
loanId, transactionId, transactionDate, totalDebitAmount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+110
-2
@@ -21,11 +21,15 @@ package org.apache.fineract.accounting.journalentry.service;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.closure.domain.GLClosure;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForLoan;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.LoanDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
|
||||
@@ -58,9 +62,10 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
||||
}
|
||||
/***
|
||||
* Logic for repayments, repayments at disbursement and reversal of Repayments and Repayments at
|
||||
* disbursement
|
||||
* disbursement (except charge adjustment)
|
||||
***/
|
||||
else if (loanTransactionDTO.getTransactionType().isRepaymentType()
|
||||
else if ((loanTransactionDTO.getTransactionType().isRepaymentType()
|
||||
&& !loanTransactionDTO.getTransactionType().isChargeAdjustment())
|
||||
|| loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement()
|
||||
|| loanTransactionDTO.getTransactionType().isChargePayment()) {
|
||||
createJournalEntriesForRepayments(loanDTO, loanTransactionDTO, office);
|
||||
@@ -106,6 +111,109 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
||||
else if (loanTransactionDTO.getTransactionType().isChargeback()) {
|
||||
createJournalEntriesForChargeback(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
// Logic for Charge Adjustment
|
||||
else if (loanTransactionDTO.getTransactionType().isChargeAdjustment()) {
|
||||
createJournalEntriesForChargeAdjustment(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createJournalEntriesForChargeAdjustment(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
|
||||
// loan properties
|
||||
final Long loanProductId = loanDTO.getLoanProductId();
|
||||
final Long loanId = loanDTO.getLoanId();
|
||||
final String currencyCode = loanDTO.getCurrencyCode();
|
||||
|
||||
// transaction properties
|
||||
final String transactionId = loanTransactionDTO.getTransactionId();
|
||||
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
|
||||
final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
|
||||
final BigDecimal interestAmount = loanTransactionDTO.getInterest();
|
||||
final BigDecimal feesAmount = loanTransactionDTO.getFees();
|
||||
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
|
||||
final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment();
|
||||
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
||||
final boolean isReversal = loanTransactionDTO.isReversed();
|
||||
|
||||
BigDecimal totalDebitAmount = new BigDecimal(0);
|
||||
|
||||
Map<GLAccount, BigDecimal> accountMap = new HashMap<>();
|
||||
|
||||
// handle principal payment (and reversals)
|
||||
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(principalAmount);
|
||||
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
|
||||
AccountingConstants.CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId);
|
||||
accountMap.put(account, principalAmount);
|
||||
}
|
||||
|
||||
// handle interest payment (and reversals)
|
||||
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(interestAmount);
|
||||
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
|
||||
AccountingConstants.CashAccountsForLoan.INTEREST_ON_LOANS.getValue(), paymentTypeId);
|
||||
if (accountMap.containsKey(account)) {
|
||||
BigDecimal amount = accountMap.get(account).add(interestAmount);
|
||||
accountMap.put(account, amount);
|
||||
} else {
|
||||
accountMap.put(account, interestAmount);
|
||||
}
|
||||
}
|
||||
|
||||
// handle fees payment (and reversals)
|
||||
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(feesAmount);
|
||||
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
|
||||
AccountingConstants.CashAccountsForLoan.INCOME_FROM_FEES.getValue(), paymentTypeId);
|
||||
if (accountMap.containsKey(account)) {
|
||||
BigDecimal amount = accountMap.get(account).add(feesAmount);
|
||||
accountMap.put(account, amount);
|
||||
} else {
|
||||
accountMap.put(account, feesAmount);
|
||||
}
|
||||
}
|
||||
|
||||
// handle penalties payment (and reversals)
|
||||
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
|
||||
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
|
||||
CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), paymentTypeId);
|
||||
if (accountMap.containsKey(account)) {
|
||||
BigDecimal amount = accountMap.get(account).add(penaltiesAmount);
|
||||
accountMap.put(account, amount);
|
||||
} else {
|
||||
accountMap.put(account, penaltiesAmount);
|
||||
}
|
||||
}
|
||||
|
||||
// handle overpayment
|
||||
if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
|
||||
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
|
||||
AccountingConstants.CashAccountsForLoan.OVERPAYMENT.getValue(), paymentTypeId);
|
||||
if (accountMap.containsKey(account)) {
|
||||
BigDecimal amount = accountMap.get(account).add(overPaymentAmount);
|
||||
accountMap.put(account, amount);
|
||||
} else {
|
||||
accountMap.put(account, overPaymentAmount);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<GLAccount, BigDecimal> entry : accountMap.entrySet()) {
|
||||
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate,
|
||||
entry.getValue(), isReversal, entry.getKey());
|
||||
}
|
||||
|
||||
if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
Long chargeId = loanTransactionDTO.getLoanChargeData().getChargeId();
|
||||
Integer accountMappingTypeId;
|
||||
if (loanTransactionDTO.getLoanChargeData().isPenalty()) {
|
||||
accountMappingTypeId = AccountingConstants.CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue();
|
||||
} else {
|
||||
accountMappingTypeId = AccountingConstants.CashAccountsForLoan.INCOME_FROM_FEES.getValue();
|
||||
}
|
||||
this.helper.createDebitJournalEntryOrReversalForLoanCharges(office, currencyCode, accountMappingTypeId, loanProductId, chargeId,
|
||||
loanId, transactionId, transactionDate, totalDebitAmount, isReversal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -22,8 +22,8 @@ import java.util.Collection;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@@ -31,14 +31,14 @@ import org.springframework.stereotype.Component;
|
||||
public class ApplyChargeToOverdueLoansBusinessStep implements LoanCOBBusinessStep {
|
||||
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
private final LoanWritePlatformService loanWritePlatformService;
|
||||
private final LoanChargeWritePlatformService loanChargeWritePlatformService;
|
||||
|
||||
@Override
|
||||
public Loan execute(Loan loan) {
|
||||
final Collection<OverdueLoanScheduleData> overdueLoanScheduleDataList = loanReadPlatformService
|
||||
.retrieveAllOverdueInstallmentsForLoan(loan);
|
||||
|
||||
loanWritePlatformService.applyOverdueChargesForLoan(loan.getId(), overdueLoanScheduleDataList);
|
||||
loanChargeWritePlatformService.applyOverdueChargesForLoan(loan.getId(), overdueLoanScheduleDataList);
|
||||
return loan;
|
||||
}
|
||||
|
||||
|
||||
+9
@@ -787,6 +787,15 @@ public class CommandWrapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder adjustmentForLoanCharge(final Long loanId, final Long loanChargeId) {
|
||||
this.actionName = "ADJUSTMENT";
|
||||
this.entityName = "LOANCHARGE";
|
||||
this.entityId = loanChargeId;
|
||||
this.loanId = loanId;
|
||||
this.href = "/loans/" + loanId + "/charges/" + loanChargeId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deleteLoanCharge(final Long loanId, final Long loanChargeId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "LOANCHARGE";
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.event.business.domain.loan.transaction;
|
||||
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
|
||||
public class LoanChargeAdjustmentPostBusinessEvent extends LoanTransactionBusinessEvent {
|
||||
|
||||
private static final String TYPE = "LoanChargeAdjustmentPostBusinessEvent";
|
||||
|
||||
public LoanChargeAdjustmentPostBusinessEvent(LoanTransaction value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.event.business.domain.loan.transaction;
|
||||
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBusinessEvent;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
|
||||
public class LoanChargeAdjustmentPreBusinessEvent extends LoanBusinessEvent {
|
||||
|
||||
private static final String TYPE = "LoanChargeAdjustmentPreBusinessEvent";
|
||||
|
||||
public LoanChargeAdjustmentPreBusinessEvent(Loan value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -74,7 +74,10 @@ public class CurrencyData implements Serializable {
|
||||
|
||||
private String generateDisplayLabel() {
|
||||
|
||||
final StringBuilder builder = new StringBuilder(this.name).append(' ');
|
||||
final StringBuilder builder = new StringBuilder(20);
|
||||
if (this.name != null) {
|
||||
builder.append(this.name).append(' ');
|
||||
}
|
||||
|
||||
if (this.displaySymbol != null && !"".equalsIgnoreCase(this.displaySymbol.trim())) {
|
||||
builder.append('(').append(this.displaySymbol).append(')');
|
||||
|
||||
+18
-18
@@ -52,7 +52,7 @@ final class ChargesApiResourceSwagger {
|
||||
private GetChargesTimeTypeResponse() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "chargeTimeType.disbursement")
|
||||
public String code;
|
||||
@Schema(example = "Disbursement")
|
||||
@@ -64,7 +64,7 @@ final class ChargesApiResourceSwagger {
|
||||
private GetChargesAppliesToResponse() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "chargeAppliesTo.loan")
|
||||
public String code;
|
||||
@Schema(example = "Loan")
|
||||
@@ -76,7 +76,7 @@ final class ChargesApiResourceSwagger {
|
||||
private GetChargesCalculationTypeResponse() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "chargeCalculationType.flat")
|
||||
public String code;
|
||||
@Schema(example = "Flat")
|
||||
@@ -88,7 +88,7 @@ final class ChargesApiResourceSwagger {
|
||||
private GetChargesPaymentModeResponse() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "chargepaymentmode.accounttransfer")
|
||||
public String code;
|
||||
@Schema(example = "Account Transfer")
|
||||
@@ -100,12 +100,12 @@ final class ChargesApiResourceSwagger {
|
||||
@Schema(example = "Loan Service fee")
|
||||
public String name;
|
||||
@Schema(example = "true")
|
||||
public String active;
|
||||
public boolean active;
|
||||
@Schema(example = "false")
|
||||
public String penalty;
|
||||
public boolean penalty;
|
||||
public GetChargesCurrencyResponse currency;
|
||||
@Schema(example = "230.56")
|
||||
public Float amount;
|
||||
public Double amount;
|
||||
public GetChargesTimeTypeResponse chargeTimeType;
|
||||
public GetChargesAppliesToResponse chargeAppliesTo;
|
||||
public GetChargesCalculationTypeResponse chargeCalculationType;
|
||||
@@ -126,7 +126,7 @@ final class ChargesApiResourceSwagger {
|
||||
@Schema(example = "en")
|
||||
public String locale;
|
||||
@Schema(example = "230.56")
|
||||
public Float amount;
|
||||
public Double amount;
|
||||
@Schema(example = "1")
|
||||
public Integer chargeTimeType;
|
||||
@Schema(example = "1")
|
||||
@@ -134,11 +134,11 @@ final class ChargesApiResourceSwagger {
|
||||
@Schema(example = "1")
|
||||
public Integer chargePaymentMode;
|
||||
@Schema(example = "true")
|
||||
public String active;
|
||||
public boolean active;
|
||||
@Schema(example = "dd MMMM")
|
||||
public String monthDayFormat;
|
||||
@Schema(example = "false")
|
||||
public String penalty;
|
||||
public boolean penalty;
|
||||
}
|
||||
|
||||
@Schema(description = "PostChargesResponse")
|
||||
@@ -147,7 +147,7 @@ final class ChargesApiResourceSwagger {
|
||||
private PostChargesResponse() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer resourceId;
|
||||
public Long resourceId;
|
||||
}
|
||||
|
||||
@Schema(description = "PutChargesChargeIdRequest")
|
||||
@@ -165,7 +165,7 @@ final class ChargesApiResourceSwagger {
|
||||
private PutChargesChargeIdResponse() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer resourceId;
|
||||
public Long resourceId;
|
||||
public PutChargesChargeIdRequest changes;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ final class ChargesApiResourceSwagger {
|
||||
private DeleteChargesChargeIdResponse() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer resourceId;
|
||||
public Long resourceId;
|
||||
}
|
||||
|
||||
@Schema(description = "GetChargesTemplateResponse")
|
||||
@@ -188,7 +188,7 @@ final class ChargesApiResourceSwagger {
|
||||
private GetChargesTemplateLoanChargeCalculationTypeOptions() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "chargeCalculationType.flat")
|
||||
public String code;
|
||||
@Schema(example = "Flat")
|
||||
@@ -200,7 +200,7 @@ final class ChargesApiResourceSwagger {
|
||||
private GetChargesTemplateLoanChargeTimeTypeOptions() {}
|
||||
|
||||
@Schema(example = "2")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "chargeTimeType.specifiedDueDate")
|
||||
public String code;
|
||||
@Schema(example = "Specified due date")
|
||||
@@ -212,7 +212,7 @@ final class ChargesApiResourceSwagger {
|
||||
private GetChargesTemplateFeeFrequencyOptions() {}
|
||||
|
||||
@Schema(example = "0")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "loanTermFrequency.periodFrequencyType.days")
|
||||
public String code;
|
||||
@Schema(example = "Days")
|
||||
@@ -220,9 +220,9 @@ final class ChargesApiResourceSwagger {
|
||||
}
|
||||
|
||||
@Schema(example = "false")
|
||||
public String active;
|
||||
public boolean active;
|
||||
@Schema(example = "false")
|
||||
public String penalty;
|
||||
public boolean penalty;
|
||||
public Set<GetChargesResponse.GetChargesCurrencyResponse> currencyOptions;
|
||||
public Set<GetChargesResponse.GetChargesCalculationTypeResponse> chargeCalculationTypeOptions;
|
||||
public Set<GetChargesResponse.GetChargesAppliesToResponse> chargeAppliesToOptions;
|
||||
|
||||
+6
-3
@@ -657,8 +657,11 @@ public class Charge extends AbstractPersistableCustom {
|
||||
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(this.chargeTimeType);
|
||||
final EnumOptionData chargeAppliesTo = ChargeEnumerations.chargeAppliesTo(this.chargeAppliesTo);
|
||||
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(this.chargeCalculation);
|
||||
final EnumOptionData chargePaymentmode = ChargeEnumerations.chargePaymentMode(this.chargePaymentMode);
|
||||
final EnumOptionData feeFrequencyType = ChargeEnumerations.chargePaymentMode(this.feeFrequency);
|
||||
final EnumOptionData chargePaymentMode = ChargeEnumerations.chargePaymentMode(this.chargePaymentMode);
|
||||
EnumOptionData feeFrequencyType = null;
|
||||
if (this.feeFrequency != null) {
|
||||
feeFrequencyType = ChargeEnumerations.feeFrequencyType(this.feeFrequency);
|
||||
}
|
||||
GLAccountData accountData = null;
|
||||
if (account != null) {
|
||||
accountData = new GLAccountData().setId(account.getId()).setName(account.getName()).setGlCode(account.getGlCode());
|
||||
@@ -675,7 +678,7 @@ public class Charge extends AbstractPersistableCustom {
|
||||
|
||||
final CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, 0, null, null);
|
||||
return ChargeData.instance(getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType,
|
||||
chargePaymentmode, getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active, this.enableFreeWithdrawal,
|
||||
chargePaymentMode, getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active, this.enableFreeWithdrawal,
|
||||
this.freeWithdrawalFrequency, this.restartFrequency, this.restartFrequencyEnum, this.enablePaymentType, paymentTypeData,
|
||||
this.minCap, this.maxCap, feeFrequencyType, accountData, taxGroupData);
|
||||
}
|
||||
|
||||
+23
@@ -23,6 +23,7 @@ import org.apache.fineract.portfolio.charge.domain.ChargeAppliesTo;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
public final class ChargeEnumerations {
|
||||
|
||||
@@ -191,4 +192,26 @@ public final class ChargeEnumerations {
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData feeFrequencyType(final int id) {
|
||||
return feeFrequencyType(PeriodFrequencyType.fromInt(id));
|
||||
}
|
||||
|
||||
public static EnumOptionData feeFrequencyType(final PeriodFrequencyType frequencyType) {
|
||||
EnumOptionData optionData;
|
||||
switch (frequencyType) {
|
||||
case DAYS -> optionData = new EnumOptionData(PeriodFrequencyType.DAYS.getValue().longValue(),
|
||||
PeriodFrequencyType.DAYS.getCode(), "Daily");
|
||||
case WEEKS -> optionData = new EnumOptionData(PeriodFrequencyType.WEEKS.getValue().longValue(),
|
||||
PeriodFrequencyType.WEEKS.getCode(), "Weekly");
|
||||
case MONTHS -> optionData = new EnumOptionData(PeriodFrequencyType.MONTHS.getValue().longValue(),
|
||||
PeriodFrequencyType.MONTHS.getCode(), "Monthly");
|
||||
case YEARS -> optionData = new EnumOptionData(PeriodFrequencyType.YEARS.getValue().longValue(),
|
||||
PeriodFrequencyType.YEARS.getCode(), "Yearly");
|
||||
case WHOLE_TERM -> optionData = new EnumOptionData(PeriodFrequencyType.WHOLE_TERM.getValue().longValue(),
|
||||
PeriodFrequencyType.WHOLE_TERM.getCode(), "Whole term");
|
||||
default -> throw new UnsupportedOperationException(frequencyType + " is not supported");
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-10
@@ -43,7 +43,6 @@ import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
@@ -52,6 +51,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.service.CommandParameterUtil;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
@@ -77,6 +77,9 @@ public class LoanChargesApiResource {
|
||||
"amount", "amountPaid", "chargeOptions", "installmentChargeData"));
|
||||
|
||||
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
|
||||
public static final String COMMAND_PAY = "pay";
|
||||
public static final String COMMAND_WAIVE = "waive";
|
||||
public static final String COMMAND_ADJUSTMENT = "adjustment";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
@@ -98,10 +101,6 @@ public class LoanChargesApiResource {
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
}
|
||||
|
||||
private boolean is(final String commandParam, final String commandValue) {
|
||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@@ -176,8 +175,8 @@ public class LoanChargesApiResource {
|
||||
@QueryParam("command") @Parameter(description = "command") final String commandParam,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "pay")) {
|
||||
CommandProcessingResult result;
|
||||
if (CommandParameterUtil.is(commandParam, COMMAND_PAY)) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().payLoanCharge(loanId, null).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
@@ -224,13 +223,16 @@ public class LoanChargesApiResource {
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "waive")) {
|
||||
CommandProcessingResult result;
|
||||
if (CommandParameterUtil.is(commandParam, COMMAND_WAIVE)) {
|
||||
final CommandWrapper commandRequest = builder.waiveLoanCharge(loanId, loanChargeId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "pay")) {
|
||||
} else if (CommandParameterUtil.is(commandParam, COMMAND_PAY)) {
|
||||
final CommandWrapper commandRequest = builder.payLoanCharge(loanId, loanChargeId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (CommandParameterUtil.is(commandParam, COMMAND_ADJUSTMENT)) {
|
||||
final CommandWrapper commandRequest = builder.adjustmentForLoanCharge(loanId, loanChargeId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||
}
|
||||
|
||||
+41
-12
@@ -39,7 +39,7 @@ final class LoanChargesApiResourceSwagger {
|
||||
private GetLoanChargeTimeType() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "chargeTimeType.disbursement")
|
||||
public String code;
|
||||
@Schema(example = "Disbursement")
|
||||
@@ -51,7 +51,7 @@ final class LoanChargesApiResourceSwagger {
|
||||
private GetLoanChargeCalculationType() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "chargeCalculationType.flat")
|
||||
public String code;
|
||||
@Schema(example = "Flat")
|
||||
@@ -77,9 +77,9 @@ final class LoanChargesApiResourceSwagger {
|
||||
}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "1")
|
||||
public Integer chargeId;
|
||||
public Long chargeId;
|
||||
@Schema(example = "Loan Processing fee")
|
||||
public String name;
|
||||
public GetLoanChargeTimeType chargeTimeType;
|
||||
@@ -121,7 +121,7 @@ final class LoanChargesApiResourceSwagger {
|
||||
private GetLoanChargeTemplateChargeTimeType() {}
|
||||
|
||||
@Schema(example = "2")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "chargeTimeType.specifiedDueDate")
|
||||
public String code;
|
||||
@Schema(example = "Specified due date")
|
||||
@@ -133,7 +133,7 @@ final class LoanChargesApiResourceSwagger {
|
||||
private GetLoanChargeTemplateChargeAppliesTo() {}
|
||||
|
||||
@Schema(example = "1 ")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "chargeAppliesTo.loan")
|
||||
public String code;
|
||||
@Schema(example = "Loan")
|
||||
@@ -141,7 +141,7 @@ final class LoanChargesApiResourceSwagger {
|
||||
}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "Collection fee")
|
||||
public String name;
|
||||
@Schema(example = "true")
|
||||
@@ -173,7 +173,7 @@ final class LoanChargesApiResourceSwagger {
|
||||
private PostLoansLoanIdChargesRequest() {}
|
||||
|
||||
@Schema(example = "2")
|
||||
public Integer chargeId;
|
||||
public Long chargeId;
|
||||
@Schema(example = "en")
|
||||
public String locale;
|
||||
@Schema(example = "100.00")
|
||||
@@ -198,7 +198,7 @@ final class LoanChargesApiResourceSwagger {
|
||||
@Schema(example = "1")
|
||||
public Long loanId;
|
||||
@Schema(example = "31")
|
||||
public Integer resourceId;
|
||||
public Long resourceId;
|
||||
}
|
||||
|
||||
@Schema(description = " PutLoansLoanIdChargesChargeIdRequest")
|
||||
@@ -228,7 +228,7 @@ final class LoanChargesApiResourceSwagger {
|
||||
@Schema(example = "1")
|
||||
public Long loanId;
|
||||
@Schema(example = "6")
|
||||
public Integer resourceId;
|
||||
public Long resourceId;
|
||||
public PutLoansLoanIdChargesChargeIdRequest changes;
|
||||
}
|
||||
|
||||
@@ -243,6 +243,16 @@ final class LoanChargesApiResourceSwagger {
|
||||
public String locale;
|
||||
@Schema(example = "19 September 2013")
|
||||
public String transactionDate;
|
||||
@Schema(example = "1")
|
||||
public Long chargeId;
|
||||
@Schema(example = "19 September 2013")
|
||||
public String dueDate;
|
||||
@Schema(example = "1")
|
||||
public Long installmentNumber;
|
||||
@Schema(example = "100.00")
|
||||
public Double amount;
|
||||
@Schema(example = "786444UUUYYH7")
|
||||
public String externalId;
|
||||
}
|
||||
|
||||
@Schema(description = "PostLoansLoanIdChargesChargeIdResponse")
|
||||
@@ -259,7 +269,26 @@ final class LoanChargesApiResourceSwagger {
|
||||
@Schema(example = "1")
|
||||
public Long savingsId;
|
||||
@Schema(example = "12")
|
||||
public Integer resourceId;
|
||||
public Long resourceId;
|
||||
public PostLoansLoanIdChargesChargeIdChanges changes;
|
||||
|
||||
static final class PostLoansLoanIdChargesChargeIdChanges {
|
||||
|
||||
@Schema(example = "en")
|
||||
public String locale;
|
||||
@Schema(example = "19 September 2013")
|
||||
public LocalDate transactionDate;
|
||||
@Schema(example = "1")
|
||||
public Long chargeId;
|
||||
@Schema(example = "19 September 2013")
|
||||
public LocalDate dueDate;
|
||||
@Schema(example = "1")
|
||||
public Long installmentNumber;
|
||||
@Schema(example = "100.00")
|
||||
public Double amount;
|
||||
@Schema(example = "786444UUUYYH7")
|
||||
public String externalId;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "DeleteLoansLoanIdChargesChargeIdResponse")
|
||||
@@ -274,6 +303,6 @@ final class LoanChargesApiResourceSwagger {
|
||||
@Schema(example = "1")
|
||||
public Long loanId;
|
||||
@Schema(example = "2")
|
||||
public Integer resourceId;
|
||||
public Long resourceId;
|
||||
}
|
||||
}
|
||||
|
||||
+20
-16
@@ -39,7 +39,7 @@ final class LoanTransactionsApiResourceSwagger {
|
||||
private GetLoansTransactionType() {}
|
||||
|
||||
@Schema(example = "2")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "loanTransactionType.repayment")
|
||||
public String code;
|
||||
@Schema(example = "Repayment")
|
||||
@@ -88,7 +88,7 @@ final class LoanTransactionsApiResourceSwagger {
|
||||
private GetLoansType() {}
|
||||
|
||||
@Schema(example = "2")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "loanTransactionType.repayment")
|
||||
public String code;
|
||||
@Schema(example = "Repayment")
|
||||
@@ -137,17 +137,19 @@ final class LoanTransactionsApiResourceSwagger {
|
||||
public Long fromLoanTransaction;
|
||||
@Schema(example = "10")
|
||||
public Long toLoanTransaction;
|
||||
@Schema(example = "10")
|
||||
public Long toLoanCharge;
|
||||
@Schema(example = "CHARGEBACK")
|
||||
private String relationType;
|
||||
public String relationType;
|
||||
@Schema(example = "100.00")
|
||||
private Double amount;
|
||||
public Double amount;
|
||||
@Schema(example = "Repayment Adjustment Chargeback")
|
||||
private String paymentType;
|
||||
public String paymentType;
|
||||
|
||||
}
|
||||
|
||||
@Schema(example = "3")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
public GetLoansType type;
|
||||
@Schema(example = "[2012, 5, 14]")
|
||||
public LocalDate date;
|
||||
@@ -162,6 +164,8 @@ final class LoanTransactionsApiResourceSwagger {
|
||||
public Double interestPortion;
|
||||
@Schema(example = "20120514")
|
||||
public String reversalExternalId;
|
||||
@Schema(example = "20120514")
|
||||
public String externalId;
|
||||
@Schema(example = "[2012, 5, 18]")
|
||||
public LocalDate reversedOnDate;
|
||||
@Schema(example = "1000.00")
|
||||
@@ -201,7 +205,7 @@ final class LoanTransactionsApiResourceSwagger {
|
||||
@Schema(example = "3e7791ce-aa10-11ec-b909-0242ac120002")
|
||||
public String externalId;
|
||||
@Schema(example = "3")
|
||||
public Integer paymentTypeId;
|
||||
public Long paymentTypeId;
|
||||
@Schema(example = "acc123")
|
||||
public String accountNumber;
|
||||
@Schema(example = "che123")
|
||||
@@ -223,11 +227,11 @@ final class LoanTransactionsApiResourceSwagger {
|
||||
private PostLoansLoanIdTransactionsResponse() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer officeId;
|
||||
public Long officeId;
|
||||
@Schema(example = "1")
|
||||
public Integer clientId;
|
||||
public Long clientId;
|
||||
@Schema(example = "22")
|
||||
public Integer resourceId;
|
||||
public Long resourceId;
|
||||
}
|
||||
|
||||
@Schema(description = "PostLoansLoanIdTransactionsTransactionIdRequest")
|
||||
@@ -248,7 +252,7 @@ final class LoanTransactionsApiResourceSwagger {
|
||||
@Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
|
||||
public String reversalExternalId;
|
||||
@Schema(example = "1")
|
||||
public Integer paymentTypeId;
|
||||
public Long paymentTypeId;
|
||||
@Schema(example = "4ff9b1cb988b7")
|
||||
public String externalId;
|
||||
}
|
||||
@@ -259,7 +263,7 @@ final class LoanTransactionsApiResourceSwagger {
|
||||
private PostLoansLoanIdTransactionsTransactionIdResponse() {}
|
||||
|
||||
@Schema(example = "16")
|
||||
public Integer resourceId;
|
||||
public Long resourceId;
|
||||
}
|
||||
|
||||
@Schema(description = "PutChargeTransactionChangesResponse")
|
||||
@@ -276,9 +280,9 @@ final class LoanTransactionsApiResourceSwagger {
|
||||
}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer resourceId;
|
||||
public Long resourceId;
|
||||
@Schema(example = "48")
|
||||
public Integer loanId;
|
||||
public Long loanId;
|
||||
public PutChargeTransactionChangesResponse.Changes changes;
|
||||
|
||||
}
|
||||
@@ -289,8 +293,8 @@ final class LoanTransactionsApiResourceSwagger {
|
||||
private PutChargeTransactionChangesRequest() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
public Long id;
|
||||
@Schema(example = "2")
|
||||
public Integer loanId;
|
||||
public Long loanId;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -267,7 +267,7 @@ final class LoansApiResourceSwagger {
|
||||
public Double totalPaidLate;
|
||||
@Schema(example = "0.00")
|
||||
public Double totalOutstanding;
|
||||
public Set<GetLoansLoanIdRepaymentPeriod> periods;
|
||||
public List<GetLoansLoanIdRepaymentPeriod> periods;
|
||||
}
|
||||
|
||||
static final class GetLoansLoanIdRepaymentPeriod {
|
||||
@@ -635,6 +635,8 @@ final class LoansApiResourceSwagger {
|
||||
public boolean refundForActiveLoans;
|
||||
@Schema(example = "false")
|
||||
public boolean creditBalanceRefund;
|
||||
@Schema(example = "false")
|
||||
public boolean chargeAdjustment;
|
||||
}
|
||||
|
||||
static final class GetLoansLoanIdPaymentDetailData {
|
||||
@@ -964,13 +966,13 @@ final class LoansApiResourceSwagger {
|
||||
public GetLoansLoanIdSummary summary;
|
||||
public GetLoansLoanIdRepaymentSchedule repaymentSchedule;
|
||||
@Schema(description = "Set of GetLoansLoanIdTransactions")
|
||||
public Set<GetLoansLoanIdTransactions> transactions;
|
||||
public List<GetLoansLoanIdTransactions> transactions;
|
||||
@Schema(description = "Set of GetLoansLoanIdDisbursementDetails")
|
||||
public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
|
||||
@Schema(description = "Delinquent data")
|
||||
public GetLoansLoanIdCollectionData delinquent;
|
||||
@Schema(description = "Set of charges")
|
||||
public Set<GetLoansLoanIdLoanChargeData> charges;
|
||||
public List<GetLoansLoanIdLoanChargeData> charges;
|
||||
public GetDelinquencyRangesResponse delinquencyRange;
|
||||
@Schema(example = "false")
|
||||
public Boolean fraud;
|
||||
|
||||
+4
@@ -21,6 +21,8 @@ package org.apache.fineract.portfolio.loanaccount.data;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
@@ -32,6 +34,8 @@ import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
* Immutable data object for loan charge data.
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class LoanChargeData {
|
||||
|
||||
private final Long id;
|
||||
|
||||
+2
-41
@@ -20,9 +20,11 @@ package org.apache.fineract.portfolio.loanaccount.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class LoanInstallmentChargeData {
|
||||
|
||||
private final Integer installmentNumber;
|
||||
@@ -32,47 +34,6 @@ public class LoanInstallmentChargeData {
|
||||
private final BigDecimal amountWaived;
|
||||
private final boolean paid;
|
||||
private final boolean waived;
|
||||
|
||||
private BigDecimal amountAccrued;
|
||||
private BigDecimal amountUnrecognized;
|
||||
|
||||
public LoanInstallmentChargeData(final Integer installmentNumber, final LocalDate dueDate, final BigDecimal amount,
|
||||
final BigDecimal amountOutstanding, BigDecimal amountWaived, final boolean paid, final boolean waived) {
|
||||
this.installmentNumber = installmentNumber;
|
||||
this.dueDate = dueDate;
|
||||
this.amount = amount;
|
||||
this.amountOutstanding = amountOutstanding;
|
||||
this.paid = paid;
|
||||
this.waived = waived;
|
||||
this.amountWaived = amountWaived;
|
||||
}
|
||||
|
||||
public LoanInstallmentChargeData(final Integer installmentNumber, final LocalDate dueDate, final BigDecimal amount,
|
||||
final BigDecimal amountOutstanding, final BigDecimal amountWaived, final boolean paid, final boolean waived,
|
||||
final BigDecimal amountAccrued) {
|
||||
this.installmentNumber = installmentNumber;
|
||||
this.dueDate = dueDate;
|
||||
this.amount = amount;
|
||||
this.amountOutstanding = amountOutstanding;
|
||||
this.paid = paid;
|
||||
this.waived = waived;
|
||||
this.amountWaived = amountWaived;
|
||||
this.amountAccrued = amountAccrued;
|
||||
}
|
||||
|
||||
public LoanInstallmentChargeData(final LoanInstallmentChargeData installmentChargeData, final BigDecimal amountUnrecognized) {
|
||||
this.installmentNumber = installmentChargeData.installmentNumber;
|
||||
this.dueDate = installmentChargeData.dueDate;
|
||||
this.amount = installmentChargeData.amount;
|
||||
this.amountOutstanding = installmentChargeData.amountOutstanding;
|
||||
this.paid = installmentChargeData.paid;
|
||||
this.waived = installmentChargeData.waived;
|
||||
this.amountWaived = installmentChargeData.amountWaived;
|
||||
this.amountAccrued = installmentChargeData.amountAccrued;
|
||||
this.amountUnrecognized = amountUnrecognized;
|
||||
}
|
||||
|
||||
public boolean isPaymentPending() {
|
||||
return !(this.paid || this.waived);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -51,7 +51,7 @@ public class LoanTransactionEnumData {
|
||||
private final boolean refund;
|
||||
private final boolean refundForActiveLoans;
|
||||
private final boolean creditBalanceRefund;
|
||||
|
||||
private final boolean chargeAdjustment;
|
||||
private final boolean chargeback;
|
||||
|
||||
public LoanTransactionEnumData(final Long id, final String code, final String value) {
|
||||
@@ -80,6 +80,7 @@ public class LoanTransactionEnumData {
|
||||
this.refundForActiveLoans = Long.valueOf(18).equals(this.id);
|
||||
this.creditBalanceRefund = Long.valueOf(20).equals(this.id);
|
||||
this.chargeback = Long.valueOf(25).equals(this.id);
|
||||
this.chargeAdjustment = Long.valueOf(26).equals(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,7 +97,8 @@ public class LoanTransactionEnumData {
|
||||
}
|
||||
|
||||
public boolean isRepaymentType() {
|
||||
if (isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit() || isChargeRefund()) {
|
||||
if (isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit() || isChargeRefund()
|
||||
|| isChargeAdjustment()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
+1
@@ -32,6 +32,7 @@ public class LoanTransactionRelationData implements Serializable {
|
||||
|
||||
private Long fromLoanTransaction;
|
||||
private Long toLoanTransaction;
|
||||
private Long toLoanCharge;
|
||||
private LoanTransactionRelationTypeEnum relationType;
|
||||
private BigDecimal amount;
|
||||
private String paymentType;
|
||||
|
||||
+19
-17
@@ -18,23 +18,20 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanStatusChangedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
// TODO: introduce tests for the state machine
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachine {
|
||||
|
||||
private final List<LoanStatus> allowedLoanStatuses;
|
||||
private static final List<LoanStatus> ALLOWED_LOAN_STATUSES = List.of(LoanStatus.values());
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
|
||||
public DefaultLoanLifecycleStateMachine(LoanStatus[] allowedLoanStatuses, BusinessEventNotifierService businessEventNotifierService) {
|
||||
this(Arrays.asList(allowedLoanStatuses), businessEventNotifierService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanStatus dryTransition(final LoanEvent loanEvent, final Loan loan) {
|
||||
LoanStatus newStatus = getNextStatus(loanEvent, loan);
|
||||
@@ -164,6 +161,11 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
newState = activeTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_CHARGE_ADJUSTMENT:
|
||||
if (from.hasStateOf(LoanStatus.CLOSED_OBLIGATIONS_MET)) {
|
||||
newState = overpaidTransition();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -171,47 +173,47 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
}
|
||||
|
||||
private LoanStatus transferOnHold() {
|
||||
return stateOf(LoanStatus.TRANSFER_ON_HOLD, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.TRANSFER_ON_HOLD, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus transferInProgress() {
|
||||
return stateOf(LoanStatus.TRANSFER_IN_PROGRESS, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.TRANSFER_IN_PROGRESS, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus overpaidTransition() {
|
||||
return stateOf(LoanStatus.OVERPAID, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.OVERPAID, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus closedRescheduleOutstandingAmountTransition() {
|
||||
return stateOf(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus closedWrittenOffTransition() {
|
||||
return stateOf(LoanStatus.CLOSED_WRITTEN_OFF, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.CLOSED_WRITTEN_OFF, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus closeObligationsMetTransition() {
|
||||
return stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus activeTransition() {
|
||||
return stateOf(LoanStatus.ACTIVE, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.ACTIVE, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus withdrawnByClientTransition() {
|
||||
return stateOf(LoanStatus.WITHDRAWN_BY_CLIENT, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.WITHDRAWN_BY_CLIENT, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus approvedTransition() {
|
||||
return stateOf(LoanStatus.APPROVED, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.APPROVED, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus rejectedTransition() {
|
||||
return stateOf(LoanStatus.REJECTED, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.REJECTED, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus submittedTransition() {
|
||||
return stateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, this.allowedLoanStatuses);
|
||||
return stateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, ALLOWED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
private LoanStatus stateOf(final LoanStatus state, final List<LoanStatus> allowedLoanStatuses) {
|
||||
|
||||
+11
-6
@@ -3379,7 +3379,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
private boolean doPostLoanTransactionChecks(final LocalDate transactionDate,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
boolean statusChanged = false;
|
||||
if (isOverPaid()) {
|
||||
boolean isOverpaid = getTotalOverpaid() != null && getTotalOverpaid().compareTo(BigDecimal.ZERO) > 0;
|
||||
if (isOverpaid) {
|
||||
// FIXME - kw - update account balance to negative amount.
|
||||
handleLoanOverpayment(loanLifecycleStateMachine);
|
||||
statusChanged = true;
|
||||
@@ -3620,7 +3621,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
if (transactionForAdjustment.isNotRepaymentType() && transactionForAdjustment.isNotWaiver()
|
||||
&& transactionForAdjustment.isNotCreditBalanceRefund()) {
|
||||
final String errorMessage = "Only transactions of type repayment, waiver or credit balance refund can be adjusted.";
|
||||
final String errorMessage = "Only (non-reversed) transactions of type repayment, waiver or credit balance refund can be adjusted.";
|
||||
throw new InvalidLoanTransactionTypeException("transaction",
|
||||
"adjustment.is.only.allowed.to.repayment.or.waiver.or.creditbalancerefund.transactions", errorMessage);
|
||||
}
|
||||
@@ -4935,8 +4936,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
|
||||
switch (event) {
|
||||
case LOAN_CREATED:
|
||||
break;
|
||||
case LOAN_APPROVED:
|
||||
if (!isSubmittedAndPendingApproval()) {
|
||||
final String defaultUserMessage = "Loan Account Approval is not allowed. Loan Account is not in submitted and pending approval state.";
|
||||
@@ -5016,8 +5015,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
break;
|
||||
case REPAID_IN_FULL:
|
||||
break;
|
||||
case LOAN_CHARGE_PAYMENT:
|
||||
if (!isOpen()) {
|
||||
final String defaultUserMessage = "Charge payment is not allowed. Loan Account is not Active.";
|
||||
@@ -5082,6 +5079,14 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
break;
|
||||
case LOAN_CHARGE_ADJUSTMENT:
|
||||
if (!(getStatus().isActive() || getStatus().isClosedObligationsMet() || getStatus().isOverpaid())) {
|
||||
final String defaultUserMessage = "Loan Charge Adjustment is not allowed. Loan Account must be either Active, Fully repaid or Overpaid.";
|
||||
final ApiParameterError error = ApiParameterError
|
||||
.generalError("error.msg.loan.charge.adjustment.account.is.not.in.valid.state", defaultUserMessage);
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+5
-1
@@ -30,7 +30,7 @@ public interface LoanAccountDomainService {
|
||||
|
||||
LoanTransaction makeRepayment(LoanTransactionType repaymentTransactionType, Loan loan, CommandProcessingResultBuilder builderResult,
|
||||
LocalDate transactionDate, BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId,
|
||||
boolean isRecoveryRepayment, String chargeRefundChargeType, boolean isAccountTransfer, HolidayDetailDTO holidatDetailDto,
|
||||
boolean isRecoveryRepayment, String chargeRefundChargeType, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto,
|
||||
Boolean isHolidayValidationDone);
|
||||
|
||||
LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
|
||||
@@ -76,6 +76,10 @@ public interface LoanAccountDomainService {
|
||||
boolean isRecoveryRepayment, String chargeRefundChargeType, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto,
|
||||
Boolean isHolidayValidationDone, boolean isLoanToLoanTransfer);
|
||||
|
||||
void saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction);
|
||||
|
||||
void saveAndFlushLoanWithDataIntegrityViolationChecks(Loan loan);
|
||||
|
||||
void saveLoanWithDataIntegrityViolationChecks(Loan loan);
|
||||
|
||||
Map<String, Object> foreCloseLoan(Loan loan, LocalDate foreClourseDate, String noteText);
|
||||
|
||||
+31
-43
@@ -123,6 +123,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
private final PostDatedChecksRepository postDatedChecksRepository;
|
||||
private final LoanCollateralManagementRepository loanCollateralManagementRepository;
|
||||
private final DelinquencyWritePlatformService delinquencyWritePlatformService;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -193,7 +194,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
holidayDetailDto);
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction,
|
||||
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, isRecoveryRepayment,
|
||||
defaultLoanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds, isRecoveryRepayment,
|
||||
scheduleGeneratorDTO, isHolidayValidationDone);
|
||||
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newRepaymentTransaction);
|
||||
@@ -310,38 +311,23 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
return repaymentEvent;
|
||||
}
|
||||
|
||||
private void saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction) {
|
||||
@Override
|
||||
public void saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction) {
|
||||
try {
|
||||
this.loanTransactionRepository.saveAndFlush(newRepaymentTransaction);
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException e) {
|
||||
final Throwable realCause = e.getCause();
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
if (realCause.getMessage().toLowerCase().contains("external_id_unique")) {
|
||||
baseDataValidator.reset().parameter("externalId").value(newRepaymentTransaction.getExternalId())
|
||||
.failWithCode("value.must.be.unique");
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors, e);
|
||||
}
|
||||
raiseValidationExceptionForUniqueConstraintViolation(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||
@Override
|
||||
public void saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) {
|
||||
try {
|
||||
this.loanRepositoryWrapper.saveAndFlush(loan);
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException e) {
|
||||
final Throwable realCause = e.getCause();
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
if (realCause.getMessage().toLowerCase().contains("external_id_unique")) {
|
||||
baseDataValidator.reset().parameter("externalId").failWithCode("value.must.be.unique");
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors, e);
|
||||
}
|
||||
raiseValidationExceptionForUniqueConstraintViolation(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,16 +336,22 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
try {
|
||||
this.loanRepositoryWrapper.save(loan);
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException e) {
|
||||
final Throwable realCause = e.getCause();
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
if (realCause.getMessage().toLowerCase().contains("external_id_unique")) {
|
||||
baseDataValidator.reset().parameter("externalId").failWithCode("value.must.be.unique");
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors, e);
|
||||
}
|
||||
raiseValidationExceptionForUniqueConstraintViolation(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void raiseValidationExceptionForUniqueConstraintViolation(Exception e) {
|
||||
final Throwable realCause = e.getCause();
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
if (realCause.getMessage().toLowerCase().contains("external_id_unique") || realCause.getMessage()
|
||||
.contains("duplicate key value violates unique constraint \"m_loan_transaction_external_id_key\"")) {
|
||||
baseDataValidator.reset().parameter("externalId").failWithCode("value.must.be.unique");
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,7 +385,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||
allowTransactionsOnNonWorkingDay);
|
||||
|
||||
loan.makeChargePayment(chargeId, defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds,
|
||||
loan.makeChargePayment(chargeId, defaultLoanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds,
|
||||
holidayDetailDTO, newPaymentTransaction, installmentNumber);
|
||||
}
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newPaymentTransaction);
|
||||
@@ -427,10 +419,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
return new DefaultLoanLifecycleStateMachine(LoanStatus.values(), businessEventNotifierService);
|
||||
}
|
||||
|
||||
private void checkClientOrGroupActive(final Loan loan) {
|
||||
final Client client = loan.client();
|
||||
if (client != null) {
|
||||
@@ -466,7 +454,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||
|
||||
loan.makeRefund(newRefundTransaction, defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds,
|
||||
loan.makeRefund(newRefundTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds,
|
||||
allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay);
|
||||
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newRefundTransaction);
|
||||
@@ -659,7 +647,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
final LoanTransaction newCreditBalanceRefundTransaction = LoanTransaction.creditBalanceRefund(loan, loan.getOffice(), refundAmount,
|
||||
transactionDate, externalId);
|
||||
|
||||
loan.creditBalanceRefund(newCreditBalanceRefundTransaction, defaultLoanLifecycleStateMachine(), existingTransactionIds,
|
||||
loan.creditBalanceRefund(newCreditBalanceRefundTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
|
||||
this.loanTransactionRepository.saveAndFlush(newCreditBalanceRefundTransaction);
|
||||
@@ -699,7 +687,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||
|
||||
loan.makeRefundForActiveLoan(newRefundTransaction, defaultLoanLifecycleStateMachine(), existingTransactionIds,
|
||||
loan.makeRefundForActiveLoan(newRefundTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
||||
existingReversedTransactionIds, allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay);
|
||||
|
||||
this.loanTransactionRepository.saveAndFlush(newRefundTransaction);
|
||||
@@ -782,7 +770,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
List<Long> transactionIds = new ArrayList<>();
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.handleForeClosureTransactions(payment,
|
||||
defaultLoanLifecycleStateMachine(), scheduleGeneratorDTO);
|
||||
defaultLoanLifecycleStateMachine, scheduleGeneratorDTO);
|
||||
|
||||
/***
|
||||
* TODO Vishwas Batch save is giving me a HibernateOptimisticLockingFailureException, looping and saving for the
|
||||
|
||||
+22
@@ -30,6 +30,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
@@ -41,6 +42,7 @@ import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
@@ -52,7 +54,9 @@ import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.apache.fineract.portfolio.charge.exception.LoanChargeWithoutMandatoryFieldException;
|
||||
import org.apache.fineract.portfolio.loanaccount.command.LoanChargeCommand;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_loan_charge", uniqueConstraints = { @UniqueConstraint(columnNames = { "external_id" }, name = "external_id") })
|
||||
@@ -1101,4 +1105,22 @@ public class LoanCharge extends AbstractPersistableCustom {
|
||||
return ChargeTimeType.fromInt(this.chargeTime);
|
||||
}
|
||||
|
||||
public LoanChargeData toData() {
|
||||
EnumOptionData chargeTimeTypeData = new EnumOptionData((long) getChargeTimeType().ordinal(), getChargeTimeType().getCode(),
|
||||
String.valueOf(getChargeTimeType().getValue()));
|
||||
EnumOptionData chargeCalculationTypeData = new EnumOptionData((long) getChargeCalculation().ordinal(),
|
||||
getChargeCalculation().getCode(), String.valueOf(getChargeCalculation().getValue()));
|
||||
EnumOptionData chargePaymentModeData = new EnumOptionData((long) getChargePaymentMode().ordinal(), getChargePaymentMode().getCode(),
|
||||
String.valueOf(getChargePaymentMode().getValue()));
|
||||
Set<LoanInstallmentChargeData> loanInstallmentChargeDataSet = installmentCharges().stream().map(LoanInstallmentCharge::toData)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return LoanChargeData.builder().id(getId()).chargeId(getCharge().getId()).name(getCharge().getName())
|
||||
.currency(getCharge().toData().getCurrency()).amount(amount).amountPaid(amountPaid).amountWaived(amountWaived)
|
||||
.amountWrittenOff(amountWrittenOff).amountOutstanding(amountOutstanding).chargeTimeType(chargeTimeTypeData)
|
||||
.submittedOnDate(submittedOnDate).dueDate(dueDate).chargeCalculationType(chargeCalculationTypeData).percentage(percentage)
|
||||
.amountPercentageAppliedTo(amountPercentageAppliedTo).amountOrPercentage(amountOrPercentage).penalty(penaltyCharge)
|
||||
.chargePaymentMode(chargePaymentModeData).paid(paid).waived(waived).loanId(loan.getId()).minCap(minCap).maxCap(maxCap)
|
||||
.installmentChargeData(loanInstallmentChargeDataSet).externalId(externalId).build();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -50,5 +50,6 @@ public enum LoanEvent {
|
||||
LOAN_WITHDRAW_TRANSFER, //
|
||||
LOAN_CREDIT_BALANCE_REFUND, //
|
||||
LOAN_CHARGEBACK, //
|
||||
LOAN_CHARGE_ADDED;
|
||||
LOAN_CHARGE_ADDED, //
|
||||
LOAN_CHARGE_ADJUSTMENT;
|
||||
}
|
||||
|
||||
+6
@@ -27,6 +27,7 @@ import javax.persistence.Table;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_loan_installment_charge")
|
||||
@@ -322,4 +323,9 @@ public class LoanInstallmentCharge extends AbstractPersistableCustom implements
|
||||
public void setInstallment(LoanRepaymentScheduleInstallment installment) {
|
||||
this.installment = installment;
|
||||
}
|
||||
|
||||
public LoanInstallmentChargeData toData() {
|
||||
return LoanInstallmentChargeData.builder().installmentNumber(installment.getInstallmentNumber()).dueDate(installment.getDueDate())
|
||||
.amount(amount).amountOutstanding(amountOutstanding).amountWaived(amountWaived).paid(paid).waived(waived).build();
|
||||
}
|
||||
}
|
||||
|
||||
+32
-6
@@ -172,6 +172,11 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
chargeRefundChargeType);
|
||||
}
|
||||
|
||||
public static LoanTransaction chargeAdjustment(final Loan loan, final BigDecimal amount, final LocalDate transactionDate,
|
||||
final String externalId) {
|
||||
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.CHARGE_ADJUSTMENT, amount, transactionDate, externalId);
|
||||
}
|
||||
|
||||
public void setLoanTransactionToRepaymentScheduleMappings(final Integer installmentId, final BigDecimal chargePerInstallment) {
|
||||
for (LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping : this.loanTransactionToRepaymentScheduleMappings) {
|
||||
final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loanTransactionToRepaymentScheduleMapping
|
||||
@@ -296,15 +301,12 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
public static boolean transactionAmountsMatch(final MonetaryCurrency currency, final LoanTransaction loanTransaction,
|
||||
final LoanTransaction newLoanTransaction) {
|
||||
if (loanTransaction.getAmount(currency).isEqualTo(newLoanTransaction.getAmount(currency))
|
||||
return loanTransaction.getAmount(currency).isEqualTo(newLoanTransaction.getAmount(currency))
|
||||
&& loanTransaction.getPrincipalPortion(currency).isEqualTo(newLoanTransaction.getPrincipalPortion(currency))
|
||||
&& loanTransaction.getInterestPortion(currency).isEqualTo(newLoanTransaction.getInterestPortion(currency))
|
||||
&& loanTransaction.getFeeChargesPortion(currency).isEqualTo(newLoanTransaction.getFeeChargesPortion(currency))
|
||||
&& loanTransaction.getPenaltyChargesPortion(currency).isEqualTo(newLoanTransaction.getPenaltyChargesPortion(currency))
|
||||
&& loanTransaction.getOverPaymentPortion(currency).isEqualTo(newLoanTransaction.getOverPaymentPortion(currency))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
&& loanTransaction.getOverPaymentPortion(currency).isEqualTo(newLoanTransaction.getOverPaymentPortion(currency));
|
||||
}
|
||||
|
||||
private LoanTransaction(final Loan loan, final Office office, final Integer typeOf, final LocalDate dateOf, final BigDecimal amount,
|
||||
@@ -526,7 +528,8 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
}
|
||||
|
||||
public boolean isRepaymentType() {
|
||||
return isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit() || isChargeRefund();
|
||||
return isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit() || isChargeRefund()
|
||||
|| isChargeAdjustment();
|
||||
}
|
||||
|
||||
public boolean isRepayment() {
|
||||
@@ -549,6 +552,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return LoanTransactionType.CHARGE_REFUND.equals(getTypeOf()) && isNotReversed();
|
||||
}
|
||||
|
||||
public boolean isChargeAdjustment() {
|
||||
return LoanTransactionType.CHARGE_ADJUSTMENT.equals(getTypeOf()) && isNotReversed();
|
||||
}
|
||||
|
||||
public boolean isNotRepaymentType() {
|
||||
return !isRepaymentType();
|
||||
}
|
||||
@@ -717,6 +724,13 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
thisTransactionData.put("loanChargesPaid", loanChargesPaidData);
|
||||
}
|
||||
|
||||
LoanTransactionRelation loanTransactionRelation = loanTransactionRelations.stream()
|
||||
.filter(e -> LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT.equals(e.getRelationType())).findAny().orElse(null);
|
||||
if (loanTransactionRelation != null) {
|
||||
LoanCharge loanCharge = loanTransactionRelation.getToCharge();
|
||||
thisTransactionData.put("loanChargeData", loanCharge.toData());
|
||||
}
|
||||
|
||||
return thisTransactionData;
|
||||
}
|
||||
|
||||
@@ -874,10 +888,22 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return (loanTransactionRelations != null && loanTransactionRelations.size() > 0);
|
||||
}
|
||||
|
||||
public boolean hasChargebackLoanTransactionRelations() {
|
||||
return (loanTransactionRelations != null
|
||||
&& loanTransactionRelations.stream().anyMatch(e -> LoanTransactionRelationTypeEnum.CHARGEBACK.equals(e.getRelationType())));
|
||||
}
|
||||
|
||||
public Set<LoanTransactionRelation> getLoanTransactionRelations() {
|
||||
return loanTransactionRelations;
|
||||
}
|
||||
|
||||
public void copyLoanTransactionRelations(Set<LoanTransactionRelation> sourceLoanTransactionRelations) {
|
||||
for (LoanTransactionRelation existingLoanTransactionRelation : sourceLoanTransactionRelations) {
|
||||
loanTransactionRelations.add(new LoanTransactionRelation(this, existingLoanTransactionRelation.getToTransaction(),
|
||||
existingLoanTransactionRelation.getToCharge(), existingLoanTransactionRelation.getRelationType()));
|
||||
}
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
+17
-4
@@ -23,6 +23,7 @@ import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Version;
|
||||
import javax.validation.constraints.NotNull;
|
||||
@@ -36,12 +37,18 @@ import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDa
|
||||
@Table(name = "m_loan_transaction_relation")
|
||||
public class LoanTransactionRelation extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "from_loan_transaction_id", nullable = false)
|
||||
private LoanTransaction fromTransaction;
|
||||
|
||||
@JoinColumn(name = "to_loan_transaction_id", nullable = false)
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "to_loan_transaction_id")
|
||||
private LoanTransaction toTransaction;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "to_loan_charge_id")
|
||||
private LoanCharge toCharge;
|
||||
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(name = "relation_type_enum", nullable = false)
|
||||
private LoanTransactionRelationTypeEnum relationType;
|
||||
@@ -51,15 +58,21 @@ public class LoanTransactionRelation extends AbstractAuditableWithUTCDateTimeCus
|
||||
|
||||
protected LoanTransactionRelation() {}
|
||||
|
||||
protected LoanTransactionRelation(@NotNull LoanTransaction fromTransaction, @NotNull LoanTransaction toTransaction,
|
||||
protected LoanTransactionRelation(@NotNull LoanTransaction fromTransaction, LoanTransaction toTransaction, LoanCharge toCharge,
|
||||
LoanTransactionRelationTypeEnum relationType) {
|
||||
this.fromTransaction = fromTransaction;
|
||||
this.toTransaction = toTransaction;
|
||||
this.toCharge = toCharge;
|
||||
this.relationType = relationType;
|
||||
}
|
||||
|
||||
public static LoanTransactionRelation instance(@NotNull LoanTransaction fromTransaction, @NotNull LoanTransaction toTransaction,
|
||||
public static LoanTransactionRelation linkToTransaction(@NotNull LoanTransaction fromTransaction,
|
||||
@NotNull LoanTransaction toTransaction, LoanTransactionRelationTypeEnum relation) {
|
||||
return new LoanTransactionRelation(fromTransaction, toTransaction, null, relation);
|
||||
}
|
||||
|
||||
public static LoanTransactionRelation linkToCharge(@NotNull LoanTransaction fromTransaction, @NotNull LoanCharge loanCharge,
|
||||
LoanTransactionRelationTypeEnum relation) {
|
||||
return new LoanTransactionRelation(fromTransaction, toTransaction, relation);
|
||||
return new LoanTransactionRelation(fromTransaction, null, loanCharge, relation);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -25,7 +25,6 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
public interface LoanTransactionRelationRepository
|
||||
extends JpaRepository<LoanTransactionRelation, Long>, JpaSpecificationExecutor<LoanTransactionRelation> {
|
||||
|
||||
List<LoanTransactionRelation> findByFromTransactionAndRelationType(LoanTransaction loanTransaction,
|
||||
LoanTransactionRelationTypeEnum relationType);
|
||||
List<LoanTransactionRelation> findByFromTransaction(LoanTransaction fromTransaction);
|
||||
|
||||
}
|
||||
|
||||
+3
-1
@@ -21,7 +21,8 @@ package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
public enum LoanTransactionRelationTypeEnum {
|
||||
|
||||
INVALID(0, "loanTransactionType.invalid"), //
|
||||
CHARGEBACK(1, "loanTransactionRelationType.chargeback");
|
||||
CHARGEBACK(1, "loanTransactionRelationType.chargeback"), //
|
||||
CHARGE_ADJUSTMENT(2, "loanTransactionRelationType.chargeAdjustment");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -47,6 +48,7 @@ public enum LoanTransactionRelationTypeEnum {
|
||||
|
||||
return switch (transactionType) {
|
||||
case 1 -> LoanTransactionRelationTypeEnum.CHARGEBACK;
|
||||
case 2 -> LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT;
|
||||
default -> LoanTransactionRelationTypeEnum.INVALID;
|
||||
};
|
||||
}
|
||||
|
||||
+31
-25
@@ -57,7 +57,8 @@ public enum LoanTransactionType {
|
||||
PAYOUT_REFUND(22, "loanTransactionType.payoutRefund"), //
|
||||
GOODWILL_CREDIT(23, "loanTransactionType.goodwillCredit"), //
|
||||
CHARGE_REFUND(24, "loanTransactionType.chargeRefund"), //
|
||||
CHARGEBACK(25, "loanTransactionType.chargeback");
|
||||
CHARGEBACK(25, "loanTransactionType.chargeback"), //
|
||||
CHARGE_ADJUSTMENT(26, "loanTransactionType.chargeAdjustment");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -67,14 +68,6 @@ public enum LoanTransactionType {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public static LoanTransactionType fromInt(final Integer transactionType) {
|
||||
|
||||
if (transactionType == null) {
|
||||
@@ -106,36 +99,45 @@ public enum LoanTransactionType {
|
||||
case 23 -> LoanTransactionType.GOODWILL_CREDIT;
|
||||
case 24 -> LoanTransactionType.CHARGE_REFUND;
|
||||
case 25 -> LoanTransactionType.CHARGEBACK;
|
||||
case 26 -> LoanTransactionType.CHARGE_ADJUSTMENT;
|
||||
default -> LoanTransactionType.INVALID;
|
||||
};
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public boolean isDisbursement() {
|
||||
return this.value.equals(LoanTransactionType.DISBURSEMENT.getValue());
|
||||
return this.equals(LoanTransactionType.DISBURSEMENT);
|
||||
}
|
||||
|
||||
public boolean isRepaymentAtDisbursement() {
|
||||
return this.value.equals(LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue());
|
||||
return this.equals(LoanTransactionType.REPAYMENT_AT_DISBURSEMENT);
|
||||
}
|
||||
|
||||
public boolean isRepayment() {
|
||||
return this.value.equals(LoanTransactionType.REPAYMENT.getValue());
|
||||
return this.equals(LoanTransactionType.REPAYMENT);
|
||||
}
|
||||
|
||||
public boolean isMerchantIssuedRefund() {
|
||||
return this.value.equals(LoanTransactionType.MERCHANT_ISSUED_REFUND.getValue());
|
||||
return this.equals(LoanTransactionType.MERCHANT_ISSUED_REFUND);
|
||||
}
|
||||
|
||||
public boolean isPayoutRefund() {
|
||||
return this.value.equals(LoanTransactionType.PAYOUT_REFUND.getValue());
|
||||
return this.equals(LoanTransactionType.PAYOUT_REFUND);
|
||||
}
|
||||
|
||||
public boolean isGoodwillCredit() {
|
||||
return this.value.equals(LoanTransactionType.GOODWILL_CREDIT.getValue());
|
||||
return this.equals(LoanTransactionType.GOODWILL_CREDIT);
|
||||
}
|
||||
|
||||
public boolean isChargeRefund() {
|
||||
return this.value.equals(LoanTransactionType.CHARGE_REFUND.getValue());
|
||||
return this.equals(LoanTransactionType.CHARGE_REFUND);
|
||||
}
|
||||
|
||||
public boolean isRepaymentType() {
|
||||
@@ -143,39 +145,43 @@ public enum LoanTransactionType {
|
||||
}
|
||||
|
||||
public boolean isRecoveryRepayment() {
|
||||
return this.value.equals(LoanTransactionType.RECOVERY_REPAYMENT.getValue());
|
||||
return this.equals(LoanTransactionType.RECOVERY_REPAYMENT);
|
||||
}
|
||||
|
||||
public boolean isWaiveInterest() {
|
||||
return this.value.equals(LoanTransactionType.WAIVE_INTEREST.getValue());
|
||||
return this.equals(LoanTransactionType.WAIVE_INTEREST);
|
||||
}
|
||||
|
||||
public boolean isWaiveCharges() {
|
||||
return this.value.equals(LoanTransactionType.WAIVE_CHARGES.getValue());
|
||||
return this.equals(LoanTransactionType.WAIVE_CHARGES);
|
||||
}
|
||||
|
||||
public boolean isAccrual() {
|
||||
return this.value.equals(LoanTransactionType.ACCRUAL.getValue());
|
||||
return this.equals(LoanTransactionType.ACCRUAL);
|
||||
}
|
||||
|
||||
public boolean isWriteOff() {
|
||||
return this.value.equals(LoanTransactionType.WRITEOFF.getValue());
|
||||
return this.equals(LoanTransactionType.WRITEOFF);
|
||||
}
|
||||
|
||||
public boolean isChargePayment() {
|
||||
return this.value.equals(LoanTransactionType.CHARGE_PAYMENT.getValue());
|
||||
return this.equals(LoanTransactionType.CHARGE_PAYMENT);
|
||||
}
|
||||
|
||||
public boolean isRefundForActiveLoan() {
|
||||
return this.value.equals(LoanTransactionType.REFUND_FOR_ACTIVE_LOAN.getValue());
|
||||
return this.equals(LoanTransactionType.REFUND_FOR_ACTIVE_LOAN);
|
||||
}
|
||||
|
||||
public boolean isIncomePosting() {
|
||||
return this.value.equals(LoanTransactionType.INCOME_POSTING.getValue());
|
||||
return this.equals(LoanTransactionType.INCOME_POSTING);
|
||||
}
|
||||
|
||||
public boolean isChargeback() {
|
||||
return this.value.equals(LoanTransactionType.CHARGEBACK.getValue());
|
||||
return this.equals(LoanTransactionType.CHARGEBACK);
|
||||
}
|
||||
|
||||
public boolean isChargeAdjustment() {
|
||||
return this.equals(LoanTransactionType.CHARGE_ADJUSTMENT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -185,6 +185,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
} else {
|
||||
loanTransaction.reverse(loanTransaction.getExternalId());
|
||||
loanTransaction.updateExternalId(null);
|
||||
newLoanTransaction.copyLoanTransactionRelations(loanTransaction.getLoanTransactionRelations());
|
||||
changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(), newLoanTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class LoanChargeAdjustmentException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public LoanChargeAdjustmentException(final String errorCode, final String defaultUserMessage, final Object... defaultUserMessageArgs) {
|
||||
super("error.msg." + errorCode, defaultUserMessage, defaultUserMessageArgs);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -24,7 +24,7 @@ import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.DataIntegrityErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -35,7 +35,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "LOANCHARGE", action = "CREATE")
|
||||
public class AddLoanChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
private final LoanChargeWritePlatformService writePlatformService;
|
||||
private final DataIntegrityErrorHandler dataIntegrityErrorHandler;
|
||||
|
||||
@Transactional
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "LOANCHARGE", action = "DELETE")
|
||||
public class DeleteLoanChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
private final LoanChargeWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@CommandType(entity = "LOANCHARGE", action = "ADJUSTMENT")
|
||||
public class LoanChargeAdjustmentCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanChargeWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return writePlatformService.adjustmentForLoanCharge(command.getLoanId(), command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -24,7 +24,7 @@ import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.DataIntegrityErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -35,7 +35,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "LOAN", action = "CHARGEREFUND")
|
||||
public class LoanChargeRefundCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
private final LoanChargeWritePlatformService writePlatformService;
|
||||
private final DataIntegrityErrorHandler dataIntegrityErrorHandler;
|
||||
|
||||
@Transactional
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "LOANCHARGE", action = "PAY")
|
||||
public class PayLoanChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
private final LoanChargeWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "WAIVECHARGE", action = "UNDO")
|
||||
public class UndoLoanChargeWaiveCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
final LoanWritePlatformService loanWritePlatformService;
|
||||
final LoanChargeWritePlatformService loanWritePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "LOANCHARGE", action = "UPDATE")
|
||||
public class UpdateLoanChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
private final LoanChargeWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "LOANCHARGE", action = "WAIVE")
|
||||
public class WaiveLoanChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
private final LoanChargeWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -20,8 +20,8 @@ package org.apache.fineract.portfolio.loanaccount.jobs.applychargetooverdueloani
|
||||
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||
@@ -44,7 +44,7 @@ public class ApplyChargeToOverdueLoanInstallmentConfig {
|
||||
@Autowired
|
||||
private LoanReadPlatformService loanReadPlatformService;
|
||||
@Autowired
|
||||
private LoanWritePlatformService loanWritePlatformService;
|
||||
private LoanChargeWritePlatformService loanChargeWritePlatformService;
|
||||
|
||||
@Bean
|
||||
protected Step applyChargeToOverdueLoanInstallmentStep() {
|
||||
@@ -61,6 +61,6 @@ public class ApplyChargeToOverdueLoanInstallmentConfig {
|
||||
@Bean
|
||||
public ApplyChargeToOverdueLoanInstallmentTasklet applyChargeToOverdueLoanInstallmentTasklet() {
|
||||
return new ApplyChargeToOverdueLoanInstallmentTasklet(configurationDomainService, loanReadPlatformService,
|
||||
loanWritePlatformService);
|
||||
loanChargeWritePlatformService);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -31,8 +31,8 @@ import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainR
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeWritePlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
@@ -44,7 +44,7 @@ public class ApplyChargeToOverdueLoanInstallmentTasklet implements Tasklet {
|
||||
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
private final LoanWritePlatformService loanWritePlatformService;
|
||||
private final LoanChargeWritePlatformService loanChargeWritePlatformService;
|
||||
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
@@ -68,7 +68,7 @@ public class ApplyChargeToOverdueLoanInstallmentTasklet implements Tasklet {
|
||||
List<Throwable> exceptions = new ArrayList<>();
|
||||
for (final Long loanId : overdueScheduleData.keySet()) {
|
||||
try {
|
||||
loanWritePlatformService.applyOverdueChargesForLoan(loanId, overdueScheduleData.get(loanId));
|
||||
loanChargeWritePlatformService.applyOverdueChargesForLoan(loanId, overdueScheduleData.get(loanId));
|
||||
|
||||
} catch (final PlatformApiDataValidationException e) {
|
||||
final List<ApiParameterError> errors = e.getErrors();
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ public interface LoanTransactionRelationMapper {
|
||||
|
||||
@Mapping(target = "fromLoanTransaction", source = "source.fromTransaction.id")
|
||||
@Mapping(target = "toLoanTransaction", source = "source.toTransaction.id")
|
||||
@Mapping(target = "toLoanCharge", source = "source.toCharge.id")
|
||||
@Mapping(target = "amount", source = "source.toTransaction.amount")
|
||||
@Mapping(target = "paymentType", source = "source.toTransaction.paymentDetail.paymentType.name")
|
||||
LoanTransactionRelationData map(LoanTransactionRelation source);
|
||||
|
||||
+6
-60
@@ -47,13 +47,10 @@ import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollatera
|
||||
import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollateralQuantity;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollaterals;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
@@ -107,7 +104,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
|
||||
private final ClientCollateralManagementRepositoryWrapper clientCollateralManagementRepositoryWrapper;
|
||||
|
||||
private final LoanChargeApiJsonValidator loanChargeApiJsonValidator;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
|
||||
public void validateForCreate(final String json, final boolean isMeetingMandatoryForJLGLoans, final LoanProduct loanProduct) {
|
||||
@@ -391,15 +388,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
.extractStringNamed(LoanApiConstants.transactionProcessingStrategyCodeParameterName, element);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.transactionProcessingStrategyCodeParameterName)
|
||||
.value(transactionProcessingStrategy).notNull();
|
||||
LoanRepaymentScheduleTransactionProcessor processor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(transactionProcessingStrategy);
|
||||
|
||||
if (processor == null) {
|
||||
Object[] values = loanRepaymentScheduleTransactionProcessorFactory.getStrategies().stream()
|
||||
.map(TransactionProcessingStrategyData::getCode).toList().toArray(new String[0]);
|
||||
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.transactionProcessingStrategyCodeParameterName).isOneOfTheseValues(values);
|
||||
}
|
||||
// Validating whether the processor is existing
|
||||
loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(transactionProcessingStrategy);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.linkAccountIdParameterName, element)) {
|
||||
final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.linkAccountIdParameterName, element);
|
||||
@@ -640,16 +630,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
.extractStringNamed(LoanApiConstants.transactionProcessingStrategyCodeParameterName, element);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.transactionProcessingStrategyCodeParameterName)
|
||||
.value(transactionProcessingStrategy).notNull();
|
||||
LoanRepaymentScheduleTransactionProcessor processor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(transactionProcessingStrategy);
|
||||
|
||||
if (processor == null) {
|
||||
Object[] values = loanRepaymentScheduleTransactionProcessorFactory.getStrategies().stream()
|
||||
.map(TransactionProcessingStrategyData::getCode).toList().toArray(new String[0]);
|
||||
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.transactionProcessingStrategyCodeParameterName)
|
||||
.isOneOfTheseValues(values);
|
||||
}
|
||||
// Validating whether the processor is existing
|
||||
loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(transactionProcessingStrategy);
|
||||
}
|
||||
|
||||
BigDecimal principal = null;
|
||||
@@ -1282,46 +1264,10 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
|
||||
}
|
||||
|
||||
public void validateLoanCharges(final Set<LoanCharge> charges, final List<ApiParameterError> dataValidationErrors) {
|
||||
if (charges == null) {
|
||||
return;
|
||||
}
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
for (LoanCharge loanCharge : charges) {
|
||||
String errorcode = null;
|
||||
switch (loanCharge.getChargeCalculation()) {
|
||||
case PERCENT_OF_AMOUNT:
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
errorcode = "installment." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE;
|
||||
|
||||
}
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
errorcode = "installment." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE;
|
||||
} else if (loanCharge.isSpecifiedDueDate()) {
|
||||
errorcode = "specific." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE;
|
||||
}
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
if (loanCharge.isSpecifiedDueDate()) {
|
||||
errorcode = "specific." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (errorcode != null) {
|
||||
baseDataValidator.reset().parameter("charges").failWithCode(errorcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void validateLoanForInterestRecalculation(final Loan loan) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
|
||||
validateLoanCharges(loan.getActiveCharges(), dataValidationErrors);
|
||||
loanChargeApiJsonValidator.validateLoanCharges(loan.getActiveCharges(), dataValidationErrors);
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.serialization;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public final class LoanChargeApiJsonValidator {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
public void validateAddLoanCharge(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Set<String> disbursementParameters = new HashSet<>(
|
||||
Arrays.asList("chargeId", "amount", "dueDate", "locale", "dateFormat", "externalId"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
|
||||
}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanCharge");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
final Long chargeId = this.fromApiJsonHelper.extractLongNamed("chargeId", element);
|
||||
baseDataValidator.reset().parameter("chargeId").value(chargeId).notNull().integerGreaterThanZero();
|
||||
|
||||
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element);
|
||||
baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount();
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("dueDate", element)) {
|
||||
final LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed("dueDate", element);
|
||||
baseDataValidator.reset().parameter("dueDate").value(dueDate).notBlank();
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateUpdateOfLoanCharge(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Set<String> disbursementParameters = new HashSet<>(Arrays.asList("amount", "dueDate", "locale", "dateFormat"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
|
||||
}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanCharge");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element);
|
||||
baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount();
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("dueDate", element)) {
|
||||
this.fromApiJsonHelper.extractLocalDateNamed("dueDate", element);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateChargePaymentTransaction(final String json, final boolean isChargeIdIncluded) {
|
||||
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
Set<String> transactionParameters = null;
|
||||
if (isChargeIdIncluded) {
|
||||
transactionParameters = new HashSet<>(
|
||||
Arrays.asList("transactionDate", "locale", "dateFormat", "chargeId", "dueDate", "installmentNumber"));
|
||||
} else {
|
||||
transactionParameters = new HashSet<>(Arrays.asList("transactionDate", "locale", "dateFormat", "dueDate", "installmentNumber"));
|
||||
}
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
|
||||
}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource("loan.charge.payment.transaction");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed("transactionDate", element);
|
||||
if (isChargeIdIncluded) {
|
||||
final Long chargeId = this.fromApiJsonHelper.extractLongNamed("chargeId", element);
|
||||
baseDataValidator.reset().parameter("chargeId").value(chargeId).notNull().integerGreaterThanZero();
|
||||
}
|
||||
baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
|
||||
final Integer installmentNumber = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("installmentNumber", element);
|
||||
baseDataValidator.reset().parameter("installmentNumber").value(installmentNumber).ignoreIfNull().integerGreaterThanZero();
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateLoanChargeRefundTransaction(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final String loanChargeIdParam = "loanChargeId";
|
||||
final String installmentNumberParam = "installmentNumber";
|
||||
final String dueDateParam = "dueDate";
|
||||
Set<String> transactionParameters = new HashSet<>(
|
||||
Arrays.asList(loanChargeIdParam, dueDateParam, "locale", "dateFormat", installmentNumberParam, //
|
||||
// remainder below relate to payment part of refund and not validated here
|
||||
"transactionAmount", "externalId", "note", "locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber",
|
||||
"routingCode", "receiptNumber", "bankNumber"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
|
||||
}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource("loan.charge.refund.transaction");
|
||||
|
||||
JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final Long chargeId = this.fromApiJsonHelper.extractLongNamed(loanChargeIdParam, element);
|
||||
baseDataValidator.reset().parameter(loanChargeIdParam).value(chargeId).notNull().integerGreaterThanZero();
|
||||
|
||||
final Integer installmentNumber = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(installmentNumberParam, element);
|
||||
baseDataValidator.reset().parameter(installmentNumberParam).value(installmentNumber).ignoreIfNull().integerGreaterThanZero();
|
||||
|
||||
final BigDecimal transactionAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("transactionAmount", element);
|
||||
baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).ignoreIfNull().positiveAmount();
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateLoanAdjustmentRequest(final Long loanId, final Long loanChargeId, final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
Set<String> transactionParameters = new HashSet<>(List.of("amount", "externalId", "locale"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
|
||||
}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource("loan.charge.adjustment.request");
|
||||
|
||||
JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element);
|
||||
baseDataValidator.reset().parameter("amount").value(amount).ignoreIfNull().positiveAmount();
|
||||
baseDataValidator.reset().parameter("loanId").value(loanId).notNull().positiveAmount();
|
||||
baseDataValidator.reset().parameter("loanChargeId").value(loanChargeId).notNull().positiveAmount();
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateInstallmentChargeTransaction(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) {
|
||||
return;
|
||||
}
|
||||
Set<String> transactionParameters = new HashSet<>(Arrays.asList("dueDate", "locale", "dateFormat", "installmentNumber"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
|
||||
}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource("loan.charge.waive.transaction");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final Integer installmentNumber = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("installmentNumber", element);
|
||||
baseDataValidator.reset().parameter("installmentNumber").value(installmentNumber).ignoreIfNull().integerGreaterThanZero();
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateLoanCharges(final Set<LoanCharge> charges, final List<ApiParameterError> dataValidationErrors) {
|
||||
if (charges == null) {
|
||||
return;
|
||||
}
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
for (LoanCharge loanCharge : charges) {
|
||||
String errorcode = null;
|
||||
switch (loanCharge.getChargeCalculation()) {
|
||||
case PERCENT_OF_AMOUNT:
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
errorcode = "installment." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE;
|
||||
|
||||
}
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
errorcode = "installment." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE;
|
||||
} else if (loanCharge.isSpecifiedDueDate()) {
|
||||
errorcode = "specific." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE;
|
||||
}
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
if (loanCharge.isSpecifiedDueDate()) {
|
||||
errorcode = "specific." + LoanApiConstants.LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (errorcode != null) {
|
||||
baseDataValidator.reset().parameter("charges").failWithCode(errorcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-143
@@ -338,60 +338,6 @@ public final class LoanEventApiJsonValidator {
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateAddLoanCharge(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Set<String> disbursementParameters = new HashSet<>(
|
||||
Arrays.asList("chargeId", "amount", "dueDate", "locale", "dateFormat", "externalId"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanCharge");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
final Long chargeId = this.fromApiJsonHelper.extractLongNamed("chargeId", element);
|
||||
baseDataValidator.reset().parameter("chargeId").value(chargeId).notNull().integerGreaterThanZero();
|
||||
|
||||
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element);
|
||||
baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount();
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("dueDate", element)) {
|
||||
final LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed("dueDate", element);
|
||||
baseDataValidator.reset().parameter("dueDate").value(dueDate).notBlank();
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateUpdateOfLoanCharge(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Set<String> disbursementParameters = new HashSet<>(Arrays.asList("amount", "dueDate", "locale", "dateFormat"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanCharge");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element);
|
||||
baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount();
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("dueDate", element)) {
|
||||
this.fromApiJsonHelper.extractLocalDateNamed("dueDate", element);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateUpdateOfLoanOfficer(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
@@ -450,74 +396,6 @@ public final class LoanEventApiJsonValidator {
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateChargePaymentTransaction(final String json, final boolean isChargeIdIncluded) {
|
||||
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
Set<String> transactionParameters = null;
|
||||
if (isChargeIdIncluded) {
|
||||
transactionParameters = new HashSet<>(
|
||||
Arrays.asList("transactionDate", "locale", "dateFormat", "chargeId", "dueDate", "installmentNumber"));
|
||||
} else {
|
||||
transactionParameters = new HashSet<>(Arrays.asList("transactionDate", "locale", "dateFormat", "dueDate", "installmentNumber"));
|
||||
}
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource("loan.charge.payment.transaction");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed("transactionDate", element);
|
||||
if (isChargeIdIncluded) {
|
||||
final Long chargeId = this.fromApiJsonHelper.extractLongNamed("chargeId", element);
|
||||
baseDataValidator.reset().parameter("chargeId").value(chargeId).notNull().integerGreaterThanZero();
|
||||
}
|
||||
baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
|
||||
final Integer installmentNumber = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("installmentNumber", element);
|
||||
baseDataValidator.reset().parameter("installmentNumber").value(installmentNumber).ignoreIfNull().integerGreaterThanZero();
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateLoanChargeRefundTransaction(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final String loanChargeIdParam = "loanChargeId";
|
||||
final String installmentNumberParam = "installmentNumber";
|
||||
final String dueDateParam = "dueDate";
|
||||
Set<String> transactionParameters = new HashSet<>(
|
||||
Arrays.asList(loanChargeIdParam, dueDateParam, "locale", "dateFormat", installmentNumberParam, //
|
||||
// remainder below relate to payment part of refund and not validated here
|
||||
"transactionAmount", "externalId", "note", "locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber",
|
||||
"routingCode", "receiptNumber", "bankNumber"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource("loan.charge.refund.transaction");
|
||||
|
||||
JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final Long chargeId = this.fromApiJsonHelper.extractLongNamed(loanChargeIdParam, element);
|
||||
baseDataValidator.reset().parameter(loanChargeIdParam).value(chargeId).notNull().integerGreaterThanZero();
|
||||
|
||||
final Integer installmentNumber = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(installmentNumberParam, element);
|
||||
baseDataValidator.reset().parameter(installmentNumberParam).value(installmentNumber).ignoreIfNull().integerGreaterThanZero();
|
||||
|
||||
final BigDecimal transactionAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("transactionAmount", element);
|
||||
baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).ignoreIfNull().positiveAmount();
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateMarkAsFraudLoan(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
return;
|
||||
@@ -539,27 +417,6 @@ public final class LoanEventApiJsonValidator {
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateInstallmentChargeTransaction(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) {
|
||||
return;
|
||||
}
|
||||
Set<String> transactionParameters = new HashSet<>(Arrays.asList("dueDate", "locale", "dateFormat", "installmentNumber"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource("loan.charge.waive.transaction");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final Integer installmentNumber = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("installmentNumber", element);
|
||||
baseDataValidator.reset().parameter("installmentNumber").value(installmentNumber).ignoreIfNull().integerGreaterThanZero();
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateUpdateDisbursementDateAndAmount(final String json, LoanDisbursementDetails loanDisbursementDetails) {
|
||||
|
||||
if (StringUtils.isBlank(json)) {
|
||||
|
||||
+6
-10
@@ -98,7 +98,6 @@ import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
@@ -192,10 +191,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
private final GLIMAccountInfoRepository glimRepository;
|
||||
private final LoanRepository loanRepository;
|
||||
private final GSIMReadPlatformService gsimReadPlatformService;
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
return new DefaultLoanLifecycleStateMachine(LoanStatus.values(), businessEventNotifierService);
|
||||
}
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -1355,7 +1351,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
}
|
||||
|
||||
final Map<String, Object> changes = loan.loanApplicationApproval(currentUser, command, disbursementDataArray,
|
||||
defaultLoanLifecycleStateMachine());
|
||||
defaultLoanLifecycleStateMachine);
|
||||
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(loanId, EntityTables.LOAN.getName(),
|
||||
StatusEnum.APPROVE.getCode().longValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
|
||||
@@ -1462,7 +1458,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
final Loan loan = retrieveLoanBy(loanId);
|
||||
checkClientOrGroupActive(loan);
|
||||
|
||||
final Map<String, Object> changes = loan.undoApproval(defaultLoanLifecycleStateMachine());
|
||||
final Map<String, Object> changes = loan.undoApproval(defaultLoanLifecycleStateMachine);
|
||||
if (!changes.isEmpty()) {
|
||||
|
||||
// If loan approved amount is not same as loan amount demanded, then
|
||||
@@ -1544,7 +1540,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(loanId, EntityTables.LOAN.getName(),
|
||||
StatusEnum.REJECTED.getCode().longValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
|
||||
|
||||
final Map<String, Object> changes = loan.loanApplicationRejection(currentUser, command, defaultLoanLifecycleStateMachine());
|
||||
final Map<String, Object> changes = loan.loanApplicationRejection(currentUser, command, defaultLoanLifecycleStateMachine);
|
||||
if (!changes.isEmpty()) {
|
||||
this.loanRepositoryWrapper.saveAndFlush(loan);
|
||||
|
||||
@@ -1581,7 +1577,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
StatusEnum.WITHDRAWN.getCode().longValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
|
||||
|
||||
final Map<String, Object> changes = loan.loanApplicationWithdrawnByApplicant(currentUser, command,
|
||||
defaultLoanLifecycleStateMachine());
|
||||
defaultLoanLifecycleStateMachine);
|
||||
|
||||
// Release attached collaterals
|
||||
if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) {
|
||||
@@ -1619,7 +1615,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
|
||||
private Loan retrieveLoanBy(final Long loanId) {
|
||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||
loan.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
loan.setHelpers(defaultLoanLifecycleStateMachine, this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
return loan;
|
||||
}
|
||||
|
||||
|
||||
+6
-13
@@ -34,7 +34,6 @@ import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDoma
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayRepository;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayStatusType;
|
||||
@@ -60,7 +59,6 @@ import org.apache.fineract.portfolio.group.exception.ClientNotInGroupException;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
|
||||
@@ -68,7 +66,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollaterals;
|
||||
@@ -109,18 +106,18 @@ public class LoanAssembler {
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final RateAssembler rateAssembler;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
|
||||
public Loan assembleFrom(final Long accountId) {
|
||||
final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId, true);
|
||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper,
|
||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine, this.loanSummaryWrapper,
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
|
||||
return loanAccount;
|
||||
}
|
||||
|
||||
public void setHelpers(final Loan loanAccount) {
|
||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper,
|
||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine, this.loanSummaryWrapper,
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
}
|
||||
|
||||
@@ -296,7 +293,7 @@ public class LoanAssembler {
|
||||
if (loanApplication == null) {
|
||||
throw new IllegalStateException("No loan application exists for either a client or group (or both).");
|
||||
}
|
||||
loanApplication.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper,
|
||||
loanApplication.setHelpers(defaultLoanLifecycleStateMachine, this.loanSummaryWrapper,
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
|
||||
if (loanProduct.isMultiDisburseLoan()) {
|
||||
@@ -314,16 +311,12 @@ public class LoanAssembler {
|
||||
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||
final LoanScheduleModel loanScheduleModel = this.loanScheduleAssembler.assembleLoanScheduleFrom(loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays, element, disbursementDetails);
|
||||
loanApplication.loanApplicationSubmittal(loanScheduleModel, loanApplicationTerms, defaultLoanLifecycleStateMachine(),
|
||||
submittedOnDate, externalId, allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay);
|
||||
loanApplication.loanApplicationSubmittal(loanScheduleModel, loanApplicationTerms, defaultLoanLifecycleStateMachine, submittedOnDate,
|
||||
externalId, allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay);
|
||||
|
||||
return loanApplication;
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
return new DefaultLoanLifecycleStateMachine(LoanStatus.values(), businessEventNotifierService);
|
||||
}
|
||||
|
||||
public CodeValue findCodeValueByIdIfProvided(final Long codeValueId) {
|
||||
CodeValue codeValue = null;
|
||||
if (codeValueId != null) {
|
||||
|
||||
+1
-1
@@ -43,6 +43,6 @@ public interface LoanChargeReadPlatformService {
|
||||
|
||||
Collection<LoanChargeData> retrieveLoanChargesForAccural(Long loanId);
|
||||
|
||||
Collection<LoanChargePaidByData> retriveLoanChargesPaidBy(Long chargeId, LoanTransactionType transactionType,
|
||||
Collection<LoanChargePaidByData> retrieveLoanChargesPaidBy(Long chargeId, LoanTransactionType transactionType,
|
||||
Integer installmentNumber);
|
||||
}
|
||||
|
||||
+17
-11
@@ -65,7 +65,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
+ "lc.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, "
|
||||
+ "lc.charge_time_enum as chargeTime, " + "lc.is_penalty as penalty, "
|
||||
+ "lc.due_for_collection_as_of_date as dueAsOfDate, " + "lc.charge_calculation_enum as chargeCalculation, "
|
||||
+ "lc.charge_payment_mode_enum as chargePaymentMode, " + "lc.is_paid_derived as paid, " + "lc.waived as waied, "
|
||||
+ "lc.charge_payment_mode_enum as chargePaymentMode, " + "lc.is_paid_derived as paid, " + "lc.waived as waived, "
|
||||
+ "lc.min_cap as minCap, lc.max_cap as maxCap, " + "lc.charge_amount_or_percentage as amountOrPercentage, "
|
||||
+ "lc.loan_id as loanId, c.currency_code as currencyCode, oc.name as currencyName, "
|
||||
+ "date(coalesce(dd.disbursedon_date,dd.expected_disburse_date)) as disbursementDate, "
|
||||
@@ -112,7 +112,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
final int chargePaymentMode = rs.getInt("chargePaymentMode");
|
||||
final EnumOptionData paymentMode = ChargeEnumerations.chargePaymentMode(chargePaymentMode);
|
||||
final boolean paid = rs.getBoolean("paid");
|
||||
final boolean waived = rs.getBoolean("waied");
|
||||
final boolean waived = rs.getBoolean("waived");
|
||||
final BigDecimal minCap = rs.getBigDecimal("minCap");
|
||||
final BigDecimal maxCap = rs.getBigDecimal("maxCap");
|
||||
final BigDecimal amountOrPercentage = rs.getBigDecimal("amountOrPercentage");
|
||||
@@ -227,7 +227,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
public String schema() {
|
||||
return " lsi.installment as installmentNumber, lsi.duedate as dueAsOfDate, "
|
||||
+ "lic.amount_outstanding_derived as amountOutstanding," + "lic.amount as amount, " + "lic.is_paid_derived as paid, "
|
||||
+ "lic.amount_waived_derived as amountWaived, " + "lic.waived as waied " + "from m_loan_installment_charge lic "
|
||||
+ "lic.amount_waived_derived as amountWaived, " + "lic.waived as waived " + "from m_loan_installment_charge lic "
|
||||
+ "join m_loan_repayment_schedule lsi on lsi.id = lic.loan_schedule_id ";
|
||||
}
|
||||
|
||||
@@ -239,9 +239,10 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
final BigDecimal amount = rs.getBigDecimal("amount");
|
||||
final BigDecimal amountWaived = rs.getBigDecimal("amountWaived");
|
||||
final boolean paid = rs.getBoolean("paid");
|
||||
final boolean waived = rs.getBoolean("waied");
|
||||
final boolean waived = rs.getBoolean("waived");
|
||||
|
||||
return new LoanInstallmentChargeData(installmentNumber, dueAsOfDate, amount, amountOutstanding, amountWaived, paid, waived);
|
||||
return LoanInstallmentChargeData.builder().installmentNumber(installmentNumber).dueDate(dueAsOfDate).amount(amount)
|
||||
.amountOutstanding(amountOutstanding).amountWaived(amountWaived).paid(paid).waived(waived).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,7 +420,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
sb.append("lic.is_paid_derived as paid, ");
|
||||
sb.append("lic.amount_waived_derived as amountWaived, ");
|
||||
sb.append(" sum(cp.amount) as amountAccrued, ");
|
||||
sb.append("lic.waived as waied ");
|
||||
sb.append("lic.waived as waived ");
|
||||
sb.append("from m_loan_installment_charge lic ");
|
||||
sb.append("join m_loan_repayment_schedule lsi on lsi.id = lic.loan_schedule_id ");
|
||||
sb.append("left join (");
|
||||
@@ -443,11 +444,12 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
final BigDecimal amount = rs.getBigDecimal("amount");
|
||||
final BigDecimal amountWaived = rs.getBigDecimal("amountWaived");
|
||||
final boolean paid = rs.getBoolean("paid");
|
||||
final boolean waived = rs.getBoolean("waied");
|
||||
final boolean waived = rs.getBoolean("waived");
|
||||
final BigDecimal amountAccrued = rs.getBigDecimal("amountAccrued");
|
||||
|
||||
return new LoanInstallmentChargeData(installmentNumber, dueAsOfDate, amount, amountOutstanding, amountWaived, paid, waived,
|
||||
amountAccrued);
|
||||
return LoanInstallmentChargeData.builder().installmentNumber(installmentNumber).dueDate(dueAsOfDate).amount(amount)
|
||||
.amountOutstanding(amountOutstanding).amountWaived(amountWaived).paid(paid).waived(waived).amountAccrued(amountAccrued)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,12 +485,16 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
final Integer installmentNumber = rs.getInt("installmentNumber");
|
||||
final BigDecimal amountUnrecognized = rs.getBigDecimal("amountUnrecognized");
|
||||
LoanInstallmentChargeData installmentChargeData = this.installmentChargeDatas.get(installmentNumber);
|
||||
return new LoanInstallmentChargeData(installmentChargeData, amountUnrecognized);
|
||||
return LoanInstallmentChargeData.builder().installmentNumber(installmentChargeData.getInstallmentNumber())
|
||||
.dueDate(installmentChargeData.getDueDate()).amount(installmentChargeData.getAmount())
|
||||
.amountOutstanding(installmentChargeData.getAmountOutstanding()).amountWaived(installmentChargeData.getAmountWaived())
|
||||
.paid(installmentChargeData.isPaid()).waived(installmentChargeData.isWaived())
|
||||
.amountAccrued(installmentChargeData.getAmountAccrued()).amountUnrecognized(amountUnrecognized).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanChargePaidByData> retriveLoanChargesPaidBy(Long chargeId, final LoanTransactionType transactionType,
|
||||
public Collection<LoanChargePaidByData> retrieveLoanChargesPaidBy(Long chargeId, final LoanTransactionType transactionType,
|
||||
final Integer installmentNumber) {
|
||||
|
||||
LoanChargesPaidByMapper rm = new LoanChargesPaidByMapper();
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
|
||||
public interface LoanChargeWritePlatformService {
|
||||
|
||||
CommandProcessingResult addLoanCharge(Long loanId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult loanChargeRefund(Long loanId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult undoWaiveLoanCharge(JsonCommand command);
|
||||
|
||||
CommandProcessingResult updateLoanCharge(Long loanId, Long loanChargeId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult waiveLoanCharge(Long loanId, Long loanChargeId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult deleteLoanCharge(Long loanId, Long loanChargeId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult payLoanCharge(Long loanId, Long loanChargeId, JsonCommand command, boolean isChargeIdIncludedInJson);
|
||||
|
||||
CommandProcessingResult adjustmentForLoanCharge(Long loanId, Long loanChargeId, JsonCommand command);
|
||||
|
||||
void applyOverdueChargesForLoan(Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDataList);
|
||||
}
|
||||
+1245
File diff suppressed because it is too large
Load Diff
+5
-6
@@ -110,7 +110,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
@@ -264,11 +263,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final String sql = "select " + rm.loanPaymentsSchema() + " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 3) "
|
||||
+ " and (tr.is_reversed=false or tr.manually_adjusted_or_reversed = true) order by tr.transaction_date ASC,id ";
|
||||
Collection<LoanTransactionData> loanTransactionData = this.jdbcTemplate.query(sql, rm, loanId); // NOSONAR
|
||||
// TODO: would worth to rework in the future. It is not nice to fetch relations one by one... might worth to
|
||||
// give a try to get rid of native queries
|
||||
for (LoanTransactionData loanTransaction : loanTransactionData) {
|
||||
if (loanTransaction.getType().isRepaymentType()) {
|
||||
loanTransaction
|
||||
.setLoanTransactionRelations(this.retrieveLoanTransactionRelationsByLoanTransactionId(loanTransaction.getId()));
|
||||
}
|
||||
loanTransaction
|
||||
.setLoanTransactionRelations(this.retrieveLoanTransactionRelationsByLoanTransactionId(loanTransaction.getId()));
|
||||
}
|
||||
return loanTransactionData;
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
@@ -2399,7 +2398,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
public List<LoanTransactionRelationData> retrieveLoanTransactionRelationsByLoanTransactionId(Long loanTransactionId) {
|
||||
final LoanTransaction loanTransaction = this.loanTransactionRepository.getReferenceById(loanTransactionId);
|
||||
List<LoanTransactionRelation> loanTransactionRelations = this.loanTransactionRelationRepository
|
||||
.findByFromTransactionAndRelationType(loanTransaction, LoanTransactionRelationTypeEnum.CHARGEBACK);
|
||||
.findByFromTransaction(loanTransaction);
|
||||
return loanTransactionRelationMapper.map(loanTransactionRelations);
|
||||
}
|
||||
|
||||
|
||||
+7
-17
@@ -32,7 +32,7 @@ import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulk
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
public interface LoanWritePlatformService {
|
||||
|
||||
@@ -46,6 +46,10 @@ public interface LoanWritePlatformService {
|
||||
CommandProcessingResult makeLoanRepayment(LoanTransactionType repaymentTransactionType, Long loanId, JsonCommand command,
|
||||
boolean isRecoveryRepayment);
|
||||
|
||||
@Transactional
|
||||
CommandProcessingResult makeLoanRepaymentWithChargeRefundChargeType(LoanTransactionType repaymentTransactionType, Long loanId,
|
||||
JsonCommand command, boolean isRecoveryRepayment, String chargeRefundChargeType);
|
||||
|
||||
Map<String, Object> makeLoanBulkRepayment(CollectionSheetBulkRepaymentCommand bulkRepaymentCommand);
|
||||
|
||||
CommandProcessingResult adjustLoanTransaction(Long loanId, Long transactionId, JsonCommand command);
|
||||
@@ -60,18 +64,6 @@ public interface LoanWritePlatformService {
|
||||
|
||||
CommandProcessingResult closeAsRescheduled(Long loanId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult addLoanCharge(Long loanId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult updateLoanCharge(Long loanId, Long loanChargeId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult deleteLoanCharge(Long loanId, Long loanChargeId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult waiveLoanCharge(Long loanId, Long loanChargeId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult undoWaiveLoanCharge(JsonCommand command);
|
||||
|
||||
CommandProcessingResult loanChargeRefund(Long loanId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult loanReassignment(Long loanId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult bulkLoanReassignment(JsonCommand command);
|
||||
@@ -89,8 +81,6 @@ public interface LoanWritePlatformService {
|
||||
|
||||
LoanTransaction acceptLoanTransfer(Loan loan, LocalDate transferDate, Office acceptedInOffice, Staff loanOfficer);
|
||||
|
||||
CommandProcessingResult payLoanCharge(Long loanId, Long loanChargeId, JsonCommand command, boolean isChargeIdIncludedInJson);
|
||||
|
||||
CommandProcessingResult undoWriteOff(Long loanId);
|
||||
|
||||
CommandProcessingResult updateDisbursementDateAndAmountForTranche(Long loanId, Long disbursementId, JsonCommand command);
|
||||
@@ -103,8 +93,6 @@ public interface LoanWritePlatformService {
|
||||
|
||||
CommandProcessingResult addAndDeleteLoanDisburseDetails(Long loanId, JsonCommand command);
|
||||
|
||||
void applyOverdueChargesForLoan(Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDatas);
|
||||
|
||||
void recalculateInterest(long loanId);
|
||||
|
||||
CommandProcessingResult undoLastLoanDisbursal(Long loanId, JsonCommand command);
|
||||
@@ -117,6 +105,8 @@ public interface LoanWritePlatformService {
|
||||
|
||||
CommandProcessingResult makeGLIMLoanRepayment(Long loanId, JsonCommand command);
|
||||
|
||||
void updateOriginalSchedule(Loan loan);
|
||||
|
||||
CommandProcessingResult creditBalanceRefund(Long loanId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult markLoanAsFraud(Long loanId, JsonCommand command);
|
||||
|
||||
+27
-1000
File diff suppressed because it is too large
Load Diff
+6
-15
@@ -46,9 +46,7 @@ import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
@@ -315,17 +313,9 @@ public final class LoanProductDataValidator {
|
||||
|
||||
final String transactionProcessingStrategyCode = this.fromApiJsonHelper.extractStringNamed(TRANSACTION_PROCESSING_STRATEGY_CODE,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(TRANSACTION_PROCESSING_STRATEGY_CODE).value(transactionProcessingStrategyCode).notNull();
|
||||
|
||||
LoanRepaymentScheduleTransactionProcessor processor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(transactionProcessingStrategyCode);
|
||||
|
||||
if (processor == null) {
|
||||
Object[] values = loanRepaymentScheduleTransactionProcessorFactory.getStrategies().stream()
|
||||
.map(TransactionProcessingStrategyData::getCode).toList().toArray(new String[0]);
|
||||
|
||||
baseDataValidator.reset().parameter(TRANSACTION_PROCESSING_STRATEGY_CODE).isOneOfTheseValues(values);
|
||||
}
|
||||
baseDataValidator.reset().parameter(TRANSACTION_PROCESSING_STRATEGY_CODE).value(transactionProcessingStrategyCode).notBlank();
|
||||
// Validating whether the processor is existing
|
||||
loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(transactionProcessingStrategyCode);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, element)) {
|
||||
final Long delinquencyBucketId = this.fromApiJsonHelper.extractLongNamed(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME,
|
||||
@@ -1093,8 +1083,9 @@ public final class LoanProductDataValidator {
|
||||
if (this.fromApiJsonHelper.parameterExists(TRANSACTION_PROCESSING_STRATEGY_CODE, element)) {
|
||||
final String transactionProcessingStrategyCode = this.fromApiJsonHelper.extractStringNamed(TRANSACTION_PROCESSING_STRATEGY_CODE,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(TRANSACTION_PROCESSING_STRATEGY_CODE).value(transactionProcessingStrategyCode).notNull()
|
||||
.integerGreaterThanZero();
|
||||
baseDataValidator.reset().parameter(TRANSACTION_PROCESSING_STRATEGY_CODE).value(transactionProcessingStrategyCode).notBlank();
|
||||
// Validating whether the processor is existing
|
||||
loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(transactionProcessingStrategyCode);
|
||||
}
|
||||
|
||||
// grace validation
|
||||
|
||||
+2
@@ -289,6 +289,8 @@ public final class LoanEnumerations {
|
||||
LoanTransactionType.CHARGE_REFUND.getCode(), "Charge Refund");
|
||||
case CHARGEBACK -> new LoanTransactionEnumData(LoanTransactionType.CHARGEBACK.getValue().longValue(),
|
||||
LoanTransactionType.CHARGEBACK.getCode(), "Chargeback");
|
||||
case CHARGE_ADJUSTMENT -> new LoanTransactionEnumData(LoanTransactionType.CHARGE_ADJUSTMENT.getValue().longValue(),
|
||||
LoanTransactionType.CHARGE_ADJUSTMENT.getCode(), "Charge Adjustment");
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -87,4 +87,6 @@
|
||||
<include file="parts/0065_add_bypass_loan_write_transaction_permission.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0066_delinquency_classification_chargeback.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0067_add_configurations_for_repayment_due_business_steps.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0068_loan_charge_adjustment.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0069_add_unique_constraint_for_reversal_external_id_of_loan_transactions.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<insert tableName="m_external_event_configuration">
|
||||
<column name="type" value="LoanChargeAdjustmentPostBusinessEvent"/>
|
||||
<column name="enabled" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_external_event_configuration">
|
||||
<column name="type" value="LoanChargeAdjustmentPreBusinessEvent"/>
|
||||
<column name="enabled" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
<changeSet id="2" author="fineract">
|
||||
<addColumn tableName="m_loan_transaction_relation">
|
||||
<column name="to_loan_charge_id" type="BIGINT">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<dropNotNullConstraint columnDataType="BIGINT"
|
||||
columnName="to_loan_transaction_id"
|
||||
tableName="m_loan_transaction_relation"/>
|
||||
<addForeignKeyConstraint baseColumnNames="to_loan_charge_id" baseTableName="m_loan_transaction_relation"
|
||||
constraintName="FK_m_loan_charge_to_relation" deferrable="false"
|
||||
initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
|
||||
referencedColumnNames="id" referencedTableName="m_loan_charge" validate="true"/>
|
||||
</changeSet>
|
||||
<changeSet id="3" author="fineract">
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="transaction_loan"/>
|
||||
<column name="code" value="ADJUSTMENT_LOANCHARGE"/>
|
||||
<column name="entity_name" value="LOANCHARGE"/>
|
||||
<column name="action_name" value="ADJUSTMENT"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<addUniqueConstraint columnNames="reversal_external_id," constraintName="UNIQUE_REVERSAL_EXTERNAL_ID_LOAN_TRANSACTION"
|
||||
tableName="m_loan_transaction"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+4
-2
@@ -88,7 +88,8 @@ public class ExternalEventConfigurationValidationServiceTest {
|
||||
"RecurringDepositAccountCreateBusinessEvent", "SavingsActivateBusinessEvent", "SavingsApproveBusinessEvent",
|
||||
"SavingsCloseBusinessEvent", "SavingsCreateBusinessEvent", "SavingsDepositBusinessEvent",
|
||||
"SavingsPostInterestBusinessEvent", "SavingsRejectBusinessEvent", "SavingsWithdrawalBusinessEvent",
|
||||
"ShareAccountApproveBusinessEvent", "ShareAccountCreateBusinessEvent", "ShareProductDividentsCreateBusinessEvent");
|
||||
"ShareAccountApproveBusinessEvent", "ShareAccountCreateBusinessEvent", "ShareProductDividentsCreateBusinessEvent",
|
||||
"LoanChargeAdjustmentPostBusinessEvent", "LoanChargeAdjustmentPreBusinessEvent");
|
||||
|
||||
List<FineractPlatformTenant> tenants = Arrays
|
||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||
@@ -160,7 +161,8 @@ public class ExternalEventConfigurationValidationServiceTest {
|
||||
"RecurringDepositAccountCreateBusinessEvent", "SavingsActivateBusinessEvent", "SavingsApproveBusinessEvent",
|
||||
"SavingsCloseBusinessEvent", "SavingsCreateBusinessEvent", "SavingsDepositBusinessEvent",
|
||||
"SavingsPostInterestBusinessEvent", "SavingsRejectBusinessEvent", "SavingsWithdrawalBusinessEvent",
|
||||
"ShareAccountApproveBusinessEvent", "ShareAccountCreateBusinessEvent", "ShareProductDividentsCreateBusinessEvent");
|
||||
"ShareAccountApproveBusinessEvent", "ShareAccountCreateBusinessEvent", "ShareProductDividentsCreateBusinessEvent",
|
||||
"LoanChargeAdjustmentPostBusinessEvent", "LoanChargeAdjustmentPreBusinessEvent");
|
||||
List<FineractPlatformTenant> tenants = Arrays
|
||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ class DefaultLoanLifecycleStateMachineTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
underTest = new DefaultLoanLifecycleStateMachine(LoanStatus.values(), businessEventNotifierService);
|
||||
underTest = new DefaultLoanLifecycleStateMachine(businessEventNotifierService);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+672
-16
@@ -20,6 +20,8 @@ package org.apache.fineract.integrationtests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
@@ -45,6 +47,28 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||
import org.apache.fineract.client.models.BusinessDateRequest;
|
||||
import org.apache.fineract.client.models.GetLoanTransactionRelation;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdSummary;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
|
||||
import org.apache.fineract.client.models.PostChargesRequest;
|
||||
import org.apache.fineract.client.models.PostChargesResponse;
|
||||
import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.models.PostGLAccountsRequest;
|
||||
import org.apache.fineract.client.models.PostGLAccountsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
||||
import org.apache.fineract.client.util.CallFailedRuntimeException;
|
||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
|
||||
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
|
||||
@@ -64,6 +88,9 @@ import org.apache.fineract.integrationtests.common.savings.AccountTransferHelper
|
||||
import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
|
||||
import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
|
||||
import org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -77,10 +104,9 @@ import org.slf4j.LoggerFactory;
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public class ClientLoanIntegrationTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClientLoanIntegrationTest.class);
|
||||
|
||||
public static final String MINIMUM_OPENING_BALANCE = "1000.0";
|
||||
public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClientLoanIntegrationTest.class);
|
||||
private static final String NONE = "1";
|
||||
private static final String CASH_BASED = "2";
|
||||
private static final String ACCRUAL_PERIODIC = "3";
|
||||
@@ -97,6 +123,9 @@ public class ClientLoanIntegrationTest {
|
||||
private AccountTransferHelper accountTransferHelper;
|
||||
|
||||
private DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
|
||||
private BusinessDateHelper businessDateHelper;
|
||||
private ChargesHelper chargesHelper;
|
||||
private ClientHelper clientHelper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
@@ -111,6 +140,9 @@ public class ClientLoanIntegrationTest {
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
this.periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(this.requestSpec, this.responseSpec);
|
||||
this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, this.responseSpec);
|
||||
this.businessDateHelper = new BusinessDateHelper();
|
||||
this.chargesHelper = new ChargesHelper();
|
||||
this.clientHelper = new ClientHelper(this.requestSpec, this.responseSpec);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1112,39 +1144,37 @@ public class ClientLoanIntegrationTest {
|
||||
private void validateCharge(Integer amountPercentage, final List<HashMap> loanCharges, final String amount, final String outstanding,
|
||||
String amountPaid, String amountWaived) {
|
||||
HashMap chargeDetail = getloanCharge(amountPercentage, loanCharges);
|
||||
Assertions.assertTrue(Float.valueOf(amount).compareTo(Float.valueOf(String.valueOf(chargeDetail.get("amountOrPercentage")))) == 0);
|
||||
Assertions.assertTrue(
|
||||
Float.valueOf(outstanding).compareTo(Float.valueOf(String.valueOf(chargeDetail.get("amountOutstanding")))) == 0);
|
||||
Assertions.assertTrue(Float.valueOf(amountPaid).compareTo(Float.valueOf(String.valueOf(chargeDetail.get("amountPaid")))) == 0);
|
||||
Assertions.assertTrue(Float.valueOf(amountWaived).compareTo(Float.valueOf(String.valueOf(chargeDetail.get("amountWaived")))) == 0);
|
||||
assertTrue(Float.valueOf(amount).compareTo(Float.valueOf(String.valueOf(chargeDetail.get("amountOrPercentage")))) == 0);
|
||||
assertTrue(Float.valueOf(outstanding).compareTo(Float.valueOf(String.valueOf(chargeDetail.get("amountOutstanding")))) == 0);
|
||||
assertTrue(Float.valueOf(amountPaid).compareTo(Float.valueOf(String.valueOf(chargeDetail.get("amountPaid")))) == 0);
|
||||
assertTrue(Float.valueOf(amountWaived).compareTo(Float.valueOf(String.valueOf(chargeDetail.get("amountWaived")))) == 0);
|
||||
}
|
||||
|
||||
private void validateChargeExcludePrecission(Integer amountPercentage, final List<HashMap> loanCharges, final String amount,
|
||||
final String outstanding, String amountPaid, String amountWaived) {
|
||||
DecimalFormat twoDForm = new DecimalFormat("#");
|
||||
HashMap chargeDetail = getloanCharge(amountPercentage, loanCharges);
|
||||
Assertions.assertTrue(Float.valueOf(twoDForm.format(Float.valueOf(amount)))
|
||||
assertTrue(Float.valueOf(twoDForm.format(Float.valueOf(amount)))
|
||||
.compareTo(Float.parseFloat(twoDForm.format(Float.valueOf(String.valueOf(chargeDetail.get("amountOrPercentage")))))) == 0);
|
||||
Assertions.assertTrue(Float.valueOf(twoDForm.format(Float.valueOf(outstanding)))
|
||||
assertTrue(Float.valueOf(twoDForm.format(Float.valueOf(outstanding)))
|
||||
.compareTo(Float.valueOf(twoDForm.format(Float.parseFloat(String.valueOf(chargeDetail.get("amountOutstanding")))))) == 0);
|
||||
Assertions.assertTrue(Float.valueOf(twoDForm.format(Float.parseFloat(amountPaid)))
|
||||
assertTrue(Float.valueOf(twoDForm.format(Float.parseFloat(amountPaid)))
|
||||
.compareTo(Float.valueOf(twoDForm.format(Float.parseFloat(String.valueOf(chargeDetail.get("amountPaid")))))) == 0);
|
||||
Assertions.assertTrue(Float.valueOf(twoDForm.format(Float.parseFloat(amountWaived)))
|
||||
assertTrue(Float.valueOf(twoDForm.format(Float.parseFloat(amountWaived)))
|
||||
.compareTo(Float.valueOf(twoDForm.format(Float.parseFloat(String.valueOf(chargeDetail.get("amountWaived")))))) == 0);
|
||||
}
|
||||
|
||||
private void validateNumberForEqual(String val, String val2) {
|
||||
Assertions.assertTrue(Float.valueOf(val).compareTo(Float.valueOf(val2)) == 0);
|
||||
assertTrue(Float.valueOf(val).compareTo(Float.valueOf(val2)) == 0);
|
||||
}
|
||||
|
||||
private void validateNumberForEqualWithMsg(String msg, String val, String val2) {
|
||||
Assertions.assertTrue(Float.valueOf(val).compareTo(Float.valueOf(val2)) == 0, msg + "expected " + val + " but was " + val2);
|
||||
assertTrue(Float.valueOf(val).compareTo(Float.valueOf(val2)) == 0, msg + "expected " + val + " but was " + val2);
|
||||
}
|
||||
|
||||
private void validateNumberForEqualExcludePrecission(String val, String val2) {
|
||||
DecimalFormat twoDForm = new DecimalFormat("#");
|
||||
Assertions.assertTrue(
|
||||
Float.valueOf(twoDForm.format(Float.valueOf(val))).compareTo(Float.valueOf(twoDForm.format(Float.valueOf(val2)))) == 0,
|
||||
assertTrue(Float.valueOf(twoDForm.format(Float.valueOf(val))).compareTo(Float.valueOf(twoDForm.format(Float.valueOf(val2)))) == 0,
|
||||
String.format("%s is not equal to %s", val, val2));
|
||||
}
|
||||
|
||||
@@ -5286,7 +5316,7 @@ public class ClientLoanIntegrationTest {
|
||||
|
||||
HashMap modifications = this.savingsAccountHelper.updateSavingsAccount(clientID, savingsProductID, savingsId,
|
||||
ACCOUNT_TYPE_INDIVIDUAL);
|
||||
Assertions.assertTrue(modifications.containsKey("submittedOnDate"));
|
||||
assertTrue(modifications.containsKey("submittedOnDate"));
|
||||
|
||||
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
|
||||
@@ -5991,6 +6021,632 @@ public class ClientLoanIntegrationTest {
|
||||
((Map) ((List) response.get("errors")).get(0)).get("defaultUserMessage"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chargeAdjustmentChargeWrongParams() {
|
||||
CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class,
|
||||
() -> loanTransactionHelper.chargeAdjustment(0L, 0L, new PostLoansLoanIdChargesChargeIdRequest().amount(0.0)));
|
||||
assertEquals(400, exception.getResponse().code());
|
||||
assertTrue(exception.getMessage().contains("validation.msg.loan.charge.adjustment.request.amount.not.greater.than.zero"));
|
||||
assertTrue(exception.getMessage().contains("validation.msg.loan.charge.adjustment.request.loanId.not.greater.than.zero"));
|
||||
assertTrue(exception.getMessage().contains("validation.msg.loan.charge.adjustment.request.loanChargeId.not.greater.than.zero"));
|
||||
exception = assertThrows(CallFailedRuntimeException.class,
|
||||
() -> loanTransactionHelper.chargeAdjustment(1L, 0L, new PostLoansLoanIdChargesChargeIdRequest().amount(0.0)));
|
||||
assertEquals(400, exception.getResponse().code());
|
||||
assertTrue(exception.getMessage().contains("validation.msg.loan.charge.adjustment.request.amount.not.greater.than.zero"));
|
||||
assertTrue(exception.getMessage().contains("validation.msg.loan.charge.adjustment.request.loanChargeId.not.greater.than.zero"));
|
||||
exception = assertThrows(CallFailedRuntimeException.class,
|
||||
() -> loanTransactionHelper.chargeAdjustment(1L, 1L, new PostLoansLoanIdChargesChargeIdRequest().amount(0.0)));
|
||||
assertEquals(400, exception.getResponse().code());
|
||||
assertTrue(exception.getMessage().contains("validation.msg.loan.charge.adjustment.request.amount.not.greater.than.zero"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chargeAdjustmentChargeDoesNotExist() {
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
final Integer loanProductID = createLoanProductWithPeriodicAccrualAccountingNoInterest(assetAccount, incomeAccount, expenseAccount,
|
||||
overpaymentAccount);
|
||||
|
||||
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2011");
|
||||
|
||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID);
|
||||
|
||||
CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class,
|
||||
() -> loanTransactionHelper.chargeAdjustment((long) loanID, 1L, new PostLoansLoanIdChargesChargeIdRequest().amount(1.0)));
|
||||
assertEquals(404, exception.getResponse().code());
|
||||
assertTrue(exception.getMessage().contains("error.msg.loanCharge.id.invalid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chargeAdjustmentChargeDoesNotExistForLoan() {
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
final Integer loanProductID = createLoanProductWithPeriodicAccrualAccountingNoInterest(assetAccount, incomeAccount, expenseAccount,
|
||||
overpaymentAccount);
|
||||
|
||||
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2011");
|
||||
|
||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID);
|
||||
|
||||
HashMap<String, Object> loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
|
||||
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = this.loanTransactionHelper.approveLoan("02 September 2022", loanID);
|
||||
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("03 September 2022", loanID, "1000");
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
|
||||
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", true));
|
||||
LocalDate targetDate = LocalDate.of(2022, 9, 7);
|
||||
final String penaltyCharge1AddedDate = dateFormatter.format(targetDate);
|
||||
Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID,
|
||||
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10"));
|
||||
|
||||
final Integer loanID2 = applyForLoanApplication(clientID, loanProductID);
|
||||
|
||||
CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class, () -> loanTransactionHelper
|
||||
.chargeAdjustment((long) loanID2, (long) penalty1LoanChargeId, new PostLoansLoanIdChargesChargeIdRequest().amount(1.0)));
|
||||
assertEquals(404, exception.getResponse().code());
|
||||
assertTrue(exception.getMessage().contains("error.msg.loanCharge.id.invalid.for.given.loan"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chargeAdjustmentForUnpaidCharge() {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName()).date("2022.11.01")
|
||||
.dateFormat("yyyy.MM.dd").locale("en"));
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", true));
|
||||
final Integer loanProductID = createLoanProductWithPeriodicAccrualAccountingNoInterest(assetAccount, incomeAccount, expenseAccount,
|
||||
overpaymentAccount);
|
||||
|
||||
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, "01 January 2011");
|
||||
|
||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID);
|
||||
|
||||
HashMap<String, Object> loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
|
||||
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = this.loanTransactionHelper.approveLoan("02 September 2022", loanID);
|
||||
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("03 September 2022", loanID, "1000");
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
|
||||
ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
||||
assertEquals(2, loanSchedule.size());
|
||||
assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue"));
|
||||
assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
||||
assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
||||
assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
||||
LocalDate targetDate = LocalDate.of(2022, 9, 7);
|
||||
final String penaltyCharge1AddedDate = dateFormatter.format(targetDate);
|
||||
Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID,
|
||||
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10"));
|
||||
|
||||
this.loanTransactionHelper.noAccrualTransactionForRepayment(loanID);
|
||||
|
||||
loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
||||
assertEquals(2, loanSchedule.size());
|
||||
assertEquals(10.0f, loanSchedule.get(1).get("penaltyChargesDue"));
|
||||
assertEquals(10.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
||||
assertEquals(1010.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
||||
assertEquals(1010.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
||||
assertEquals(0, loanSchedule.get(1).get("totalWaivedForPeriod"));
|
||||
|
||||
HashMap loanSummary = this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID, "summary");
|
||||
assertEquals(10.0f, loanSummary.get("penaltyChargesCharged"));
|
||||
assertEquals(10.0f, loanSummary.get("penaltyChargesOutstanding"));
|
||||
assertEquals(0.0f, loanSummary.get("penaltyChargesWaived"));
|
||||
assertEquals(1010.0f, loanSummary.get("totalOutstanding"));
|
||||
assertEquals(0.0f, loanSummary.get("totalWaived"));
|
||||
|
||||
String externalId = UUID.randomUUID().toString();
|
||||
PostLoansLoanIdChargesChargeIdResponse chargeAdjustmentResponse = this.loanTransactionHelper.chargeAdjustment((long) loanID,
|
||||
(long) penalty1LoanChargeId, new PostLoansLoanIdChargesChargeIdRequest().amount(10.0).externalId(externalId));
|
||||
|
||||
loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
||||
assertEquals(2, loanSchedule.size());
|
||||
assertEquals(10.0f, loanSchedule.get(1).get("penaltyChargesDue"));
|
||||
assertEquals(10.0f, loanSchedule.get(1).get("penaltyChargesPaid"));
|
||||
assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
||||
assertEquals(1010.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
||||
assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
||||
assertEquals(10.0f, loanSchedule.get(1).get("totalPaidForPeriod"));
|
||||
|
||||
loanSummary = this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID, "summary");
|
||||
assertEquals(10.0f, loanSummary.get("penaltyChargesCharged"));
|
||||
assertEquals(0.0f, loanSummary.get("penaltyChargesOutstanding"));
|
||||
assertEquals(10.0f, loanSummary.get("penaltyChargesPaid"));
|
||||
assertEquals(1000.0f, loanSummary.get("totalOutstanding"));
|
||||
|
||||
GetLoansLoanIdTransactionsTransactionIdResponse chargeAdjustmentTransaction = this.loanTransactionHelper
|
||||
.getLoanTransactionDetails((long) loanID, chargeAdjustmentResponse.getResourceId());
|
||||
assertEquals(10.0, chargeAdjustmentTransaction.getAmount());
|
||||
assertEquals(10.0, chargeAdjustmentTransaction.getPenaltyChargesPortion());
|
||||
assertEquals("loanTransactionType.chargeAdjustment", chargeAdjustmentTransaction.getType().getCode());
|
||||
assertEquals(externalId, chargeAdjustmentTransaction.getExternalId());
|
||||
GetLoanTransactionRelation transactionRelation = chargeAdjustmentTransaction.getTransactionRelations().iterator().next();
|
||||
assertEquals(chargeAdjustmentResponse.getResourceId(), transactionRelation.getFromLoanTransaction());
|
||||
assertEquals((long) penalty1LoanChargeId, transactionRelation.getToLoanCharge());
|
||||
assertEquals("CHARGE_ADJUSTMENT", transactionRelation.getRelationType());
|
||||
|
||||
PostLoansLoanIdTransactionsResponse repaymentResult = loanTransactionHelper.makeLoanRepayment((long) loanID,
|
||||
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("06 September 2022").locale("en")
|
||||
.transactionAmount(5.0));
|
||||
|
||||
loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
||||
assertEquals(2, loanSchedule.size());
|
||||
assertEquals(10.0f, loanSchedule.get(1).get("penaltyChargesDue"));
|
||||
assertEquals(10.0f, loanSchedule.get(1).get("penaltyChargesPaid"));
|
||||
assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
||||
assertEquals(1000.0f, loanSchedule.get(1).get("principalDue"));
|
||||
assertEquals(5.0f, loanSchedule.get(1).get("principalPaid"));
|
||||
assertEquals(995.0f, loanSchedule.get(1).get("principalOutstanding"));
|
||||
assertEquals(1010.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
||||
assertEquals(995.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
||||
assertEquals(15.0f, loanSchedule.get(1).get("totalPaidForPeriod"));
|
||||
|
||||
loanSummary = this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID, "summary");
|
||||
assertEquals(10.0f, loanSummary.get("penaltyChargesCharged"));
|
||||
assertEquals(0.0f, loanSummary.get("penaltyChargesOutstanding"));
|
||||
assertEquals(10.0f, loanSummary.get("penaltyChargesPaid"));
|
||||
assertEquals(1000.0f, loanSummary.get("principalDisbursed"));
|
||||
assertEquals(995.0f, loanSummary.get("principalOutstanding"));
|
||||
assertEquals(5.0f, loanSummary.get("principalPaid"));
|
||||
assertEquals(995.0f, loanSummary.get("totalOutstanding"));
|
||||
|
||||
Long chargeAdjustmentTxnId = repaymentResult.getResourceId() + 1;
|
||||
chargeAdjustmentTransaction = this.loanTransactionHelper.getLoanTransactionDetails((long) loanID, chargeAdjustmentTxnId);
|
||||
assertEquals(10.0, chargeAdjustmentTransaction.getAmount());
|
||||
assertEquals(5.0, chargeAdjustmentTransaction.getPenaltyChargesPortion());
|
||||
assertEquals(5.0, chargeAdjustmentTransaction.getPrincipalPortion());
|
||||
assertEquals("loanTransactionType.chargeAdjustment", chargeAdjustmentTransaction.getType().getCode());
|
||||
assertEquals(externalId, chargeAdjustmentTransaction.getExternalId());
|
||||
transactionRelation = chargeAdjustmentTransaction.getTransactionRelations().iterator().next();
|
||||
assertEquals(chargeAdjustmentTxnId, transactionRelation.getFromLoanTransaction());
|
||||
assertEquals((long) penalty1LoanChargeId, transactionRelation.getToLoanCharge());
|
||||
assertEquals("CHARGE_ADJUSTMENT", transactionRelation.getRelationType());
|
||||
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
this.loanTransactionHelper.reverseLoanTransaction((long) loanID, chargeAdjustmentTxnId,
|
||||
new PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat("dd MMMM yyyy").transactionDate("08 September 2022")
|
||||
.transactionAmount(0.0).locale("en").reversalExternalId(uuid));
|
||||
|
||||
// Should fail due to external id collusion
|
||||
assertThrows(CallFailedRuntimeException.class,
|
||||
() -> this.loanTransactionHelper.reverseLoanTransaction((long) loanID, repaymentResult.getResourceId(),
|
||||
new PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat("dd MMMM yyyy")
|
||||
.transactionDate("08 September 2022").transactionAmount(0.0).locale("en").reversalExternalId(uuid)));
|
||||
|
||||
loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
||||
assertEquals(2, loanSchedule.size());
|
||||
assertEquals(10.0f, loanSchedule.get(1).get("penaltyChargesDue"));
|
||||
assertEquals(5.0f, loanSchedule.get(1).get("penaltyChargesPaid"));
|
||||
assertEquals(5.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
|
||||
assertEquals(1000.0f, loanSchedule.get(1).get("principalDue"));
|
||||
assertEquals(0, loanSchedule.get(1).get("principalPaid"));
|
||||
assertEquals(1000.0f, loanSchedule.get(1).get("principalOutstanding"));
|
||||
assertEquals(1010.0f, loanSchedule.get(1).get("totalDueForPeriod"));
|
||||
assertEquals(1005.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
|
||||
assertEquals(5.0f, loanSchedule.get(1).get("totalPaidForPeriod"));
|
||||
|
||||
loanSummary = this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanID, "summary");
|
||||
assertEquals(10.0f, loanSummary.get("penaltyChargesCharged"));
|
||||
assertEquals(5.0f, loanSummary.get("penaltyChargesOutstanding"));
|
||||
assertEquals(5.0f, loanSummary.get("penaltyChargesPaid"));
|
||||
assertEquals(1000.0f, loanSummary.get("principalDisbursed"));
|
||||
assertEquals(1000.0f, loanSummary.get("principalOutstanding"));
|
||||
assertEquals(0.0f, loanSummary.get("principalPaid"));
|
||||
assertEquals(1005.0f, loanSummary.get("totalOutstanding"));
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chargeAdjustmentAccountingValidation() {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName()).date("2022.11.01")
|
||||
.dateFormat("yyyy.MM.dd").locale("en"));
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account assetFeeAndPenaltyAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||
final PostGLAccountsResponse uniqueIncomeAccountForFee = this.accountHelper
|
||||
.createGLAccount(new PostGLAccountsRequest().type(GLAccountType.INCOME.getValue())
|
||||
.glCode(Utils.randomStringGenerator("UNIQUE_FEE_INCOME" + Calendar.getInstance().getTimeInMillis(), 5))
|
||||
.manualEntriesAllowed(true)
|
||||
.name(Utils.randomStringGenerator("UNIQUE_FEE_INCOME" + Calendar.getInstance().getTimeInMillis(), 5)).usage(1));
|
||||
final PostGLAccountsResponse uniqueIncomeAccountForPenalty = this.accountHelper
|
||||
.createGLAccount(new PostGLAccountsRequest().type(GLAccountType.INCOME.getValue())
|
||||
.glCode(Utils.randomStringGenerator("UNIQUE_PENALTY_INCOME" + Calendar.getInstance().getTimeInMillis(), 5))
|
||||
.manualEntriesAllowed(true)
|
||||
.name(Utils.randomStringGenerator("UNIQUE_PENALTY_INCOME" + Calendar.getInstance().getTimeInMillis(), 5)).usage(1));
|
||||
|
||||
PostChargesResponse penaltyCharge = chargesHelper.createCharges(new PostChargesRequest().penalty(true).amount(10.0)
|
||||
.chargeCalculationType(ChargeCalculationType.FLAT.getValue()).chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
||||
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
||||
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5)).chargeAppliesTo(1).locale("en")
|
||||
.active(true));
|
||||
|
||||
PostChargesResponse feeCharge = chargesHelper.createCharges(new PostChargesRequest().penalty(false).amount(9.0)
|
||||
.chargeCalculationType(ChargeCalculationType.FLAT.getValue()).chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
||||
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
||||
.name(Utils.randomStringGenerator("FEE_" + Calendar.getInstance().getTimeInMillis(), 5)).chargeAppliesTo(1).locale("en")
|
||||
.active(true));
|
||||
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth()
|
||||
.withRepaymentAfterEvery("1").withNumberOfRepayments("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
|
||||
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat()
|
||||
.withAccountingRulePeriodicAccrual(new Account[] { assetAccount, incomeAccount, expenseAccount, overpaymentAccount })
|
||||
.withDaysInMonth("30").withDaysInYear("365").withMoratorium("0", "0")
|
||||
.withFeeToIncomeAccountMapping(feeCharge.getResourceId(), uniqueIncomeAccountForFee.getResourceId())
|
||||
.withPenaltyToIncomeAccountMapping(penaltyCharge.getResourceId(), uniqueIncomeAccountForPenalty.getResourceId())
|
||||
.withFeeAndPenaltyAssetAccount(assetFeeAndPenaltyAccount).build(null);
|
||||
final Integer loanProductID = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
|
||||
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||
|
||||
final Integer loanID = applyForLoanApplication(client.getClientId().intValue(), loanProductID);
|
||||
|
||||
HashMap<String, Object> loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
|
||||
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = this.loanTransactionHelper.approveLoan("02 September 2022", loanID);
|
||||
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("03 September 2022", loanID, "1000");
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
List<GetLoansLoanIdRepaymentPeriod> loanSchedulePeriods = loanDetails.getRepaymentSchedule().getPeriods();
|
||||
assertEquals(2, loanSchedulePeriods.size());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getPenaltyChargesDue());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getPenaltyChargesOutstanding());
|
||||
assertEquals(1000.0, loanSchedulePeriods.get(1).getTotalDueForPeriod());
|
||||
assertEquals(1000.0, loanSchedulePeriods.get(1).getTotalOutstandingForPeriod());
|
||||
|
||||
LocalDate targetDate = LocalDate.of(2022, 9, 7);
|
||||
final String penaltyCharge1AddedDate = dateFormatter.format(targetDate);
|
||||
Integer penaltyLoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID, LoanTransactionHelper
|
||||
.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penaltyCharge.getResourceId()), penaltyCharge1AddedDate, "10"));
|
||||
|
||||
final String penalty1LoanChargeDate = dateFormatter.format(targetDate);
|
||||
this.periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(penalty1LoanChargeDate);
|
||||
|
||||
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
|
||||
assertEquals(10.0, transactions.get(1).getAmount());
|
||||
assertTrue(transactions.get(1).getType().getAccrual());
|
||||
assertEquals(10.0, transactions.get(1).getPenaltyChargesPortion());
|
||||
Long accrualTransactionId = transactions.get(1).getId();
|
||||
|
||||
List<HashMap> journalEntries = this.journalEntryHelper.getJournalEntriesByTransactionId("L" + accrualTransactionId);
|
||||
assertEquals(10.0f, (float) journalEntries.get(0).get("amount"));
|
||||
assertEquals(uniqueIncomeAccountForPenalty.getResourceId().intValue(), (int) journalEntries.get(0).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(0).get("entryType")).get("value"));
|
||||
assertEquals(10.0f, (float) journalEntries.get(1).get("amount"));
|
||||
assertEquals(assetFeeAndPenaltyAccount.getAccountID(), (int) journalEntries.get(1).get("glAccountId"));
|
||||
assertEquals("DEBIT", ((HashMap) journalEntries.get(1).get("entryType")).get("value"));
|
||||
|
||||
loanSchedulePeriods = loanDetails.getRepaymentSchedule().getPeriods();
|
||||
assertEquals(2, loanSchedulePeriods.size());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesDue());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesOutstanding());
|
||||
assertEquals(1010.0, loanSchedulePeriods.get(1).getTotalDueForPeriod());
|
||||
assertEquals(1010.0, loanSchedulePeriods.get(1).getTotalOutstandingForPeriod());
|
||||
|
||||
GetLoansLoanIdSummary loanSummary = loanDetails.getSummary();
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesCharged());
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesOutstanding());
|
||||
assertEquals(1010.0, loanSummary.getTotalOutstanding());
|
||||
|
||||
String externalId = UUID.randomUUID().toString();
|
||||
PostLoansLoanIdChargesChargeIdResponse chargeAdjustmentResponse = this.loanTransactionHelper.chargeAdjustment((long) loanID,
|
||||
(long) penaltyLoanChargeId, new PostLoansLoanIdChargesChargeIdRequest().amount(10.0).externalId(externalId));
|
||||
|
||||
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
|
||||
loanSchedulePeriods = loanDetails.getRepaymentSchedule().getPeriods();
|
||||
assertEquals(2, loanSchedulePeriods.size());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesDue());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesPaid());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getPenaltyChargesOutstanding());
|
||||
assertEquals(1010.0, loanSchedulePeriods.get(1).getTotalDueForPeriod());
|
||||
assertEquals(1000.0, loanSchedulePeriods.get(1).getTotalOutstandingForPeriod());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getTotalPaidForPeriod());
|
||||
|
||||
loanSummary = loanDetails.getSummary();
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesCharged());
|
||||
assertEquals(0.0, loanSummary.getPenaltyChargesOutstanding());
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesPaid());
|
||||
assertEquals(1000.0, loanSummary.getTotalOutstanding());
|
||||
|
||||
transactions = loanDetails.getTransactions();
|
||||
assertEquals(10.0, transactions.get(2).getAmount());
|
||||
assertTrue(transactions.get(2).getType().getChargeAdjustment());
|
||||
assertEquals(10.0, transactions.get(2).getPenaltyChargesPortion());
|
||||
Long chargeAdjustmentTransactionId = transactions.get(2).getId();
|
||||
|
||||
journalEntries = this.journalEntryHelper.getJournalEntriesByTransactionId("L" + chargeAdjustmentTransactionId);
|
||||
assertEquals(10.0f, (float) journalEntries.get(0).get("amount"));
|
||||
assertEquals(uniqueIncomeAccountForPenalty.getResourceId().intValue(), (int) journalEntries.get(0).get("glAccountId"));
|
||||
assertEquals("DEBIT", ((HashMap) journalEntries.get(0).get("entryType")).get("value"));
|
||||
assertEquals(10.0f, (float) journalEntries.get(1).get("amount"));
|
||||
assertEquals(assetFeeAndPenaltyAccount.getAccountID(), (int) journalEntries.get(1).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(1).get("entryType")).get("value"));
|
||||
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
this.loanTransactionHelper.reverseLoanTransaction((long) loanID, chargeAdjustmentTransactionId,
|
||||
new PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat("dd MMMM yyyy").transactionDate("08 September 2022")
|
||||
.transactionAmount(0.0).locale("en").reversalExternalId(uuid));
|
||||
|
||||
journalEntries = this.journalEntryHelper.getJournalEntriesByTransactionId("L" + chargeAdjustmentTransactionId);
|
||||
assertEquals(10.0f, (float) journalEntries.get(0).get("amount"));
|
||||
assertEquals(uniqueIncomeAccountForPenalty.getResourceId().intValue(), (int) journalEntries.get(0).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(0).get("entryType")).get("value"));
|
||||
assertEquals(10.0f, (float) journalEntries.get(1).get("amount"));
|
||||
assertEquals(assetFeeAndPenaltyAccount.getAccountID(), (int) journalEntries.get(1).get("glAccountId"));
|
||||
assertEquals("DEBIT", ((HashMap) journalEntries.get(1).get("entryType")).get("value"));
|
||||
assertEquals(10.0f, (float) journalEntries.get(2).get("amount"));
|
||||
assertEquals(uniqueIncomeAccountForPenalty.getResourceId().intValue(), (int) journalEntries.get(2).get("glAccountId"));
|
||||
assertEquals("DEBIT", ((HashMap) journalEntries.get(2).get("entryType")).get("value"));
|
||||
assertEquals(10.0f, (float) journalEntries.get(3).get("amount"));
|
||||
assertEquals(assetFeeAndPenaltyAccount.getAccountID(), (int) journalEntries.get(3).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(3).get("entryType")).get("value"));
|
||||
|
||||
targetDate = LocalDate.of(2022, 9, 10);
|
||||
final String feeCharge1AddedDate = dateFormatter.format(targetDate);
|
||||
Integer feeLoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID, LoanTransactionHelper
|
||||
.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge.getResourceId()), feeCharge1AddedDate, "3"));
|
||||
|
||||
final String feeLoanChargeDate = dateFormatter.format(targetDate);
|
||||
this.periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(feeLoanChargeDate);
|
||||
|
||||
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
transactions = loanDetails.getTransactions();
|
||||
assertEquals(3.0, transactions.get(2).getAmount());
|
||||
assertTrue(transactions.get(2).getType().getAccrual());
|
||||
assertEquals(3.0, transactions.get(2).getFeeChargesPortion());
|
||||
accrualTransactionId = transactions.get(2).getId();
|
||||
|
||||
journalEntries = this.journalEntryHelper.getJournalEntriesByTransactionId("L" + accrualTransactionId);
|
||||
assertEquals(3.0f, (float) journalEntries.get(0).get("amount"));
|
||||
assertEquals(uniqueIncomeAccountForFee.getResourceId().intValue(), (int) journalEntries.get(0).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(0).get("entryType")).get("value"));
|
||||
assertEquals(3.0f, (float) journalEntries.get(1).get("amount"));
|
||||
assertEquals(assetFeeAndPenaltyAccount.getAccountID(), (int) journalEntries.get(1).get("glAccountId"));
|
||||
assertEquals("DEBIT", ((HashMap) journalEntries.get(1).get("entryType")).get("value"));
|
||||
|
||||
loanSchedulePeriods = loanDetails.getRepaymentSchedule().getPeriods();
|
||||
assertEquals(2, loanSchedulePeriods.size());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesDue());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesOutstanding());
|
||||
assertEquals(3.0, loanSchedulePeriods.get(1).getFeeChargesDue());
|
||||
assertEquals(3.0, loanSchedulePeriods.get(1).getFeeChargesOutstanding());
|
||||
assertEquals(1013.0, loanSchedulePeriods.get(1).getTotalDueForPeriod());
|
||||
assertEquals(1013.0, loanSchedulePeriods.get(1).getTotalOutstandingForPeriod());
|
||||
|
||||
loanSummary = loanDetails.getSummary();
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesCharged());
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesOutstanding());
|
||||
assertEquals(3.0, loanSummary.getFeeChargesCharged());
|
||||
assertEquals(3.0, loanSummary.getFeeChargesOutstanding());
|
||||
assertEquals(1013.0, loanSummary.getTotalOutstanding());
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment((long) loanID, new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy")
|
||||
.transactionDate("11 September 2022").locale("en").transactionAmount(5.0));
|
||||
|
||||
externalId = UUID.randomUUID().toString();
|
||||
chargeAdjustmentResponse = this.loanTransactionHelper.chargeAdjustment((long) loanID, (long) feeLoanChargeId,
|
||||
new PostLoansLoanIdChargesChargeIdRequest().amount(2.0).externalId(externalId));
|
||||
|
||||
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
|
||||
loanSchedulePeriods = loanDetails.getRepaymentSchedule().getPeriods();
|
||||
assertEquals(2, loanSchedulePeriods.size());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesDue());
|
||||
assertEquals(7.0, loanSchedulePeriods.get(1).getPenaltyChargesPaid());
|
||||
assertEquals(3.0, loanSchedulePeriods.get(1).getPenaltyChargesOutstanding());
|
||||
assertEquals(3.0, loanSchedulePeriods.get(1).getFeeChargesDue());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getFeeChargesPaid());
|
||||
assertEquals(3.0, loanSchedulePeriods.get(1).getFeeChargesOutstanding());
|
||||
assertEquals(1000.0, loanSchedulePeriods.get(1).getPrincipalDue());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getPrincipalPaid());
|
||||
assertEquals(1000.0, loanSchedulePeriods.get(1).getPrincipalOutstanding());
|
||||
assertEquals(1013.0, loanSchedulePeriods.get(1).getTotalDueForPeriod());
|
||||
assertEquals(1006.0, loanSchedulePeriods.get(1).getTotalOutstandingForPeriod());
|
||||
assertEquals(7.0, loanSchedulePeriods.get(1).getTotalPaidForPeriod());
|
||||
|
||||
loanSummary = loanDetails.getSummary();
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesCharged());
|
||||
assertEquals(3.0, loanSummary.getPenaltyChargesOutstanding());
|
||||
assertEquals(7.0, loanSummary.getPenaltyChargesPaid());
|
||||
assertEquals(3.0, loanSummary.getFeeChargesCharged());
|
||||
assertEquals(3.0, loanSummary.getFeeChargesOutstanding());
|
||||
assertEquals(0.0, loanSummary.getFeeChargesPaid());
|
||||
assertEquals(1000.0, loanSummary.getPrincipalOutstanding());
|
||||
assertEquals(0.0, loanSummary.getPrincipalPaid());
|
||||
assertEquals(7.0, loanSummary.getTotalRepayment());
|
||||
assertEquals(1006.0, loanSummary.getTotalOutstanding());
|
||||
|
||||
transactions = loanDetails.getTransactions();
|
||||
assertEquals(2.0, transactions.get(5).getAmount());
|
||||
assertTrue(transactions.get(4).getType().getChargeAdjustment());
|
||||
assertEquals(2.0, transactions.get(5).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, transactions.get(5).getFeeChargesPortion());
|
||||
assertEquals(0.0, transactions.get(5).getPrincipalPortion());
|
||||
chargeAdjustmentTransactionId = transactions.get(5).getId();
|
||||
|
||||
journalEntries = this.journalEntryHelper.getJournalEntriesByTransactionId("L" + chargeAdjustmentTransactionId);
|
||||
assertEquals(2.0f, (float) journalEntries.get(0).get("amount"));
|
||||
assertEquals(uniqueIncomeAccountForFee.getResourceId().intValue(), (int) journalEntries.get(0).get("glAccountId"));
|
||||
assertEquals("DEBIT", ((HashMap) journalEntries.get(0).get("entryType")).get("value"));
|
||||
assertEquals(2.0f, (float) journalEntries.get(1).get("amount"));
|
||||
assertEquals(assetFeeAndPenaltyAccount.getAccountID(), (int) journalEntries.get(1).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(1).get("entryType")).get("value"));
|
||||
|
||||
externalId = UUID.randomUUID().toString();
|
||||
chargeAdjustmentResponse = this.loanTransactionHelper.chargeAdjustment((long) loanID, (long) penaltyLoanChargeId,
|
||||
new PostLoansLoanIdChargesChargeIdRequest().amount(7.0).externalId(externalId));
|
||||
|
||||
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
|
||||
loanSchedulePeriods = loanDetails.getRepaymentSchedule().getPeriods();
|
||||
assertEquals(2, loanSchedulePeriods.size());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesDue());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesPaid());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getPenaltyChargesOutstanding());
|
||||
assertEquals(3.0, loanSchedulePeriods.get(1).getFeeChargesDue());
|
||||
assertEquals(3.0, loanSchedulePeriods.get(1).getFeeChargesPaid());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getFeeChargesOutstanding());
|
||||
assertEquals(1000.0, loanSchedulePeriods.get(1).getPrincipalDue());
|
||||
assertEquals(1.0, loanSchedulePeriods.get(1).getPrincipalPaid());
|
||||
assertEquals(999.0, loanSchedulePeriods.get(1).getPrincipalOutstanding());
|
||||
assertEquals(1013.0, loanSchedulePeriods.get(1).getTotalDueForPeriod());
|
||||
assertEquals(999.0, loanSchedulePeriods.get(1).getTotalOutstandingForPeriod());
|
||||
assertEquals(14.0, loanSchedulePeriods.get(1).getTotalPaidForPeriod());
|
||||
|
||||
loanSummary = loanDetails.getSummary();
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesCharged());
|
||||
assertEquals(0.0, loanSummary.getPenaltyChargesOutstanding());
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesPaid());
|
||||
assertEquals(3.0, loanSummary.getFeeChargesCharged());
|
||||
assertEquals(0.0, loanSummary.getFeeChargesOutstanding());
|
||||
assertEquals(3.0, loanSummary.getFeeChargesPaid());
|
||||
assertEquals(999.0, loanSummary.getPrincipalOutstanding());
|
||||
assertEquals(1.0, loanSummary.getPrincipalPaid());
|
||||
assertEquals(14.0, loanSummary.getTotalRepayment());
|
||||
assertEquals(999.0, loanSummary.getTotalOutstanding());
|
||||
|
||||
transactions = loanDetails.getTransactions();
|
||||
assertEquals(7.0, transactions.get(6).getAmount());
|
||||
assertTrue(transactions.get(6).getType().getChargeAdjustment());
|
||||
assertEquals(3.0, transactions.get(6).getPenaltyChargesPortion());
|
||||
assertEquals(3.0, transactions.get(6).getFeeChargesPortion());
|
||||
assertEquals(1.0, transactions.get(6).getPrincipalPortion());
|
||||
chargeAdjustmentTransactionId = transactions.get(6).getId();
|
||||
|
||||
journalEntries = this.journalEntryHelper.getJournalEntriesByTransactionId("L" + chargeAdjustmentTransactionId);
|
||||
assertEquals(7.0f, (float) journalEntries.get(0).get("amount"));
|
||||
assertEquals(uniqueIncomeAccountForPenalty.getResourceId().intValue(), (int) journalEntries.get(0).get("glAccountId"));
|
||||
assertEquals("DEBIT", ((HashMap) journalEntries.get(0).get("entryType")).get("value"));
|
||||
if (assetAccount.getAccountID() == (int) journalEntries.get(1).get("glAccountId")) {
|
||||
assertEquals(1.0f, (float) journalEntries.get(1).get("amount"));
|
||||
assertEquals(assetAccount.getAccountID(), (int) journalEntries.get(1).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(1).get("entryType")).get("value"));
|
||||
|
||||
assertEquals(6.0f, (float) journalEntries.get(2).get("amount"));
|
||||
assertEquals(assetFeeAndPenaltyAccount.getAccountID(), (int) journalEntries.get(2).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(2).get("entryType")).get("value"));
|
||||
} else {
|
||||
assertEquals(1.0f, (float) journalEntries.get(2).get("amount"));
|
||||
assertEquals(assetAccount.getAccountID(), (int) journalEntries.get(2).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(2).get("entryType")).get("value"));
|
||||
|
||||
assertEquals(6.0f, (float) journalEntries.get(1).get("amount"));
|
||||
assertEquals(assetFeeAndPenaltyAccount.getAccountID(), (int) journalEntries.get(1).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(1).get("entryType")).get("value"));
|
||||
}
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment((long) loanID, new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy")
|
||||
.transactionDate("13 September 2022").locale("en").transactionAmount(998.0));
|
||||
|
||||
externalId = UUID.randomUUID().toString();
|
||||
chargeAdjustmentResponse = this.loanTransactionHelper.chargeAdjustment((long) loanID, (long) feeLoanChargeId,
|
||||
new PostLoansLoanIdChargesChargeIdRequest().amount(1.0).externalId(externalId));
|
||||
|
||||
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
|
||||
loanSchedulePeriods = loanDetails.getRepaymentSchedule().getPeriods();
|
||||
assertEquals(2, loanSchedulePeriods.size());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesDue());
|
||||
assertEquals(10.0, loanSchedulePeriods.get(1).getPenaltyChargesPaid());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getPenaltyChargesOutstanding());
|
||||
assertEquals(3.0, loanSchedulePeriods.get(1).getFeeChargesDue());
|
||||
assertEquals(3.0, loanSchedulePeriods.get(1).getFeeChargesPaid());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getFeeChargesOutstanding());
|
||||
assertEquals(1000.0, loanSchedulePeriods.get(1).getPrincipalDue());
|
||||
assertEquals(1000.0, loanSchedulePeriods.get(1).getPrincipalPaid());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getPrincipalOutstanding());
|
||||
assertEquals(1013.0, loanSchedulePeriods.get(1).getTotalDueForPeriod());
|
||||
assertEquals(0.0, loanSchedulePeriods.get(1).getTotalOutstandingForPeriod());
|
||||
assertEquals(1013.0, loanSchedulePeriods.get(1).getTotalPaidForPeriod());
|
||||
|
||||
loanSummary = loanDetails.getSummary();
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesCharged());
|
||||
assertEquals(0.0, loanSummary.getPenaltyChargesOutstanding());
|
||||
assertEquals(10.0, loanSummary.getPenaltyChargesPaid());
|
||||
assertEquals(3.0, loanSummary.getFeeChargesCharged());
|
||||
assertEquals(0.0, loanSummary.getFeeChargesOutstanding());
|
||||
assertEquals(3.0, loanSummary.getFeeChargesPaid());
|
||||
assertEquals(0.0, loanSummary.getPrincipalOutstanding());
|
||||
assertEquals(1000.0, loanSummary.getPrincipalPaid());
|
||||
assertEquals(1013.0, loanSummary.getTotalRepayment());
|
||||
assertEquals(0.0, loanSummary.getTotalOutstanding());
|
||||
|
||||
transactions = loanDetails.getTransactions();
|
||||
assertEquals(1.0, transactions.get(8).getAmount());
|
||||
assertTrue(transactions.get(8).getType().getChargeAdjustment());
|
||||
assertEquals(0.0, transactions.get(8).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, transactions.get(8).getFeeChargesPortion());
|
||||
assertEquals(1.0, transactions.get(8).getPrincipalPortion());
|
||||
chargeAdjustmentTransactionId = transactions.get(8).getId();
|
||||
|
||||
journalEntries = this.journalEntryHelper.getJournalEntriesByTransactionId("L" + chargeAdjustmentTransactionId);
|
||||
assertEquals(1.0f, (float) journalEntries.get(0).get("amount"));
|
||||
assertEquals(uniqueIncomeAccountForFee.getResourceId().intValue(), (int) journalEntries.get(0).get("glAccountId"));
|
||||
assertEquals("DEBIT", ((HashMap) journalEntries.get(0).get("entryType")).get("value"));
|
||||
assertEquals(1.0f, (float) journalEntries.get(1).get("amount"));
|
||||
assertEquals(assetAccount.getAccountID(), (int) journalEntries.get(1).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(1).get("entryType")).get("value"));
|
||||
|
||||
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
|
||||
|
||||
externalId = UUID.randomUUID().toString();
|
||||
chargeAdjustmentResponse = this.loanTransactionHelper.chargeAdjustment((long) loanID, (long) penaltyLoanChargeId,
|
||||
new PostLoansLoanIdChargesChargeIdRequest().amount(1.0).externalId(externalId));
|
||||
|
||||
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanID);
|
||||
|
||||
transactions = loanDetails.getTransactions();
|
||||
assertEquals(1.0, transactions.get(9).getAmount());
|
||||
assertTrue(transactions.get(9).getType().getChargeAdjustment());
|
||||
assertEquals(0.0, transactions.get(9).getPenaltyChargesPortion());
|
||||
assertEquals(0.0, transactions.get(9).getFeeChargesPortion());
|
||||
assertEquals(0.0, transactions.get(9).getPrincipalPortion());
|
||||
assertEquals(1.0, transactions.get(9).getOverpaymentPortion());
|
||||
chargeAdjustmentTransactionId = transactions.get(9).getId();
|
||||
|
||||
journalEntries = this.journalEntryHelper.getJournalEntriesByTransactionId("L" + chargeAdjustmentTransactionId);
|
||||
assertEquals(1.0f, (float) journalEntries.get(0).get("amount"));
|
||||
assertEquals(uniqueIncomeAccountForPenalty.getResourceId().intValue(), (int) journalEntries.get(0).get("glAccountId"));
|
||||
assertEquals("DEBIT", ((HashMap) journalEntries.get(0).get("entryType")).get("value"));
|
||||
assertEquals(1.0f, (float) journalEntries.get(1).get("amount"));
|
||||
assertEquals(overpaymentAccount.getAccountID(), (int) journalEntries.get(1).get("glAccountId"));
|
||||
assertEquals("CREDIT", ((HashMap) journalEntries.get(1).get("entryType")).get("value"));
|
||||
|
||||
assertTrue(loanDetails.getStatus().getOverpaid());
|
||||
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undoWaivedChargeWaiveTransactionDoesNotExist() {
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
|
||||
+5
-5
@@ -118,14 +118,14 @@ public class DelinquencyAndChargebackIntegrationTest {
|
||||
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate,
|
||||
transactionAmount, loanId);
|
||||
assertNotNull(loanIdTransactionsResponse);
|
||||
final Integer transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
loanTransactionHelper.reviewLoanTransactionRelations(loanId, transactionId, 0);
|
||||
final Long transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
loanTransactionHelper.reviewLoanTransactionRelations(loanId, transactionId.intValue(), 0);
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
validateLoanAccount(getLoansLoanIdResponse, "0.00", "1100.00", 0, doubleZERO);
|
||||
|
||||
final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, amountVal, 0,
|
||||
responseSpec);
|
||||
loanTransactionHelper.reviewLoanTransactionRelations(loanId, transactionId, 1);
|
||||
final Long chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(), amountVal,
|
||||
0, responseSpec);
|
||||
loanTransactionHelper.reviewLoanTransactionRelations(loanId, transactionId.intValue(), 1);
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
|
||||
+1
-1
@@ -597,7 +597,7 @@ public class DelinquencyBucketsIntegrationTest {
|
||||
|
||||
// Reverse the Previous Loan Repayment
|
||||
PostLoansLoanIdTransactionsResponse loansLoanIdReverseTransactions = loanTransactionHelper.reverseLoanTransaction(loanId,
|
||||
loansLoanIdTransactions.getResourceId(), operationDate, responseSpec);
|
||||
loansLoanIdTransactions.getResourceId().intValue(), operationDate, responseSpec);
|
||||
assertNotNull(loansLoanIdReverseTransactions);
|
||||
log.info("Loan repayment reverse transaction id {}", loansLoanIdReverseTransactions.getResourceId());
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ public class LoanChargeSpecificDueDateTest {
|
||||
String payloadJSON = ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, feeAmount, false);
|
||||
final PostChargesResponse postChargesResponse = ChargesHelper.createLoanCharge(requestSpec, responseSpec, payloadJSON);
|
||||
assertNotNull(postChargesResponse);
|
||||
final Integer loanChargeId = postChargesResponse.getResourceId();
|
||||
final Long loanChargeId = postChargesResponse.getResourceId();
|
||||
|
||||
payloadJSON = LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(loanChargeId.toString(), operationDate, feeAmount);
|
||||
PostLoansLoanIdChargesResponse postLoansLoanIdChargesResponse = loanTransactionHelper.addChargeForLoan(loanId, payloadJSON,
|
||||
|
||||
+48
-46
@@ -29,7 +29,7 @@ import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
|
||||
import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
|
||||
@@ -87,19 +87,19 @@ public class LoanTransactionChargebackTest {
|
||||
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
|
||||
loanId);
|
||||
assertNotNull(loanIdTransactionsResponse);
|
||||
final Integer transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
final Long transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.closed.obligations.met");
|
||||
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 0, Double.valueOf("0.00"));
|
||||
|
||||
final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "1000.00", 0,
|
||||
responseSpec);
|
||||
final Long chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(), "1000.00",
|
||||
0, responseSpec);
|
||||
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("1000.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId.intValue(), 0, Double.valueOf("1000.00"));
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
@@ -109,10 +109,11 @@ public class LoanTransactionChargebackTest {
|
||||
|
||||
// Try to reverse a Loan Transaction charge back
|
||||
PostLoansLoanIdTransactionsResponse reverseTransactionResponse = loanTransactionHelper.reverseLoanTransaction(loanId,
|
||||
chargebackTransactionId, operationDate, responseSpecError);
|
||||
chargebackTransactionId.intValue(), operationDate, responseSpecError);
|
||||
|
||||
// Try to reverse a Loan Transaction repayment with linked transactions
|
||||
reverseTransactionResponse = loanTransactionHelper.reverseLoanTransaction(loanId, transactionId, operationDate, responseSpecError);
|
||||
reverseTransactionResponse = loanTransactionHelper.reverseLoanTransaction(loanId, transactionId.intValue(), operationDate,
|
||||
responseSpecError);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -135,13 +136,13 @@ public class LoanTransactionChargebackTest {
|
||||
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
|
||||
loanId);
|
||||
assertNotNull(loanIdTransactionsResponse);
|
||||
final Integer transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("666.67"));
|
||||
final Long transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 0, Double.valueOf("666.67"));
|
||||
|
||||
final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, amount.toString(),
|
||||
0, responseSpec);
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("666.67"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("1000.00"));
|
||||
final Long chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(),
|
||||
amount.toString(), 0, responseSpec);
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 1, Double.valueOf("666.67"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId.intValue(), 0, Double.valueOf("1000.00"));
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
@@ -170,7 +171,7 @@ public class LoanTransactionChargebackTest {
|
||||
GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
|
||||
Set<GetLoansLoanIdTransactions> loanTransactions = getLoansLoanIdResponse.getTransactions();
|
||||
List<GetLoansLoanIdTransactions> loanTransactions = getLoansLoanIdResponse.getTransactions();
|
||||
assertNotNull(loanTransactions);
|
||||
log.info("Loan Id {} with {} transactions", loanId, loanTransactions.size());
|
||||
assertEquals(2, loanTransactions.size());
|
||||
@@ -203,19 +204,19 @@ public class LoanTransactionChargebackTest {
|
||||
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
|
||||
loanId);
|
||||
assertNotNull(loanIdTransactionsResponse);
|
||||
final Integer transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
final Long transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.closed.obligations.met");
|
||||
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 0, Double.valueOf("0.00"));
|
||||
|
||||
Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "500.00", 0,
|
||||
Long chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(), "500.00", 0,
|
||||
responseSpec);
|
||||
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("500.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId.intValue(), 0, Double.valueOf("500.00"));
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
@@ -237,10 +238,11 @@ public class LoanTransactionChargebackTest {
|
||||
}
|
||||
}
|
||||
|
||||
chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "300.00", 0, responseSpec);
|
||||
chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(), "300.00", 0,
|
||||
responseSpec);
|
||||
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 2, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("800.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 2, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId.intValue(), 0, Double.valueOf("800.00"));
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
@@ -281,19 +283,19 @@ public class LoanTransactionChargebackTest {
|
||||
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
|
||||
loanId);
|
||||
assertNotNull(loanIdTransactionsResponse);
|
||||
final Integer transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
final Long transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.overpaid");
|
||||
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 0, Double.valueOf("0.00"));
|
||||
|
||||
final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "200.00", 0,
|
||||
final Long chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(), "200.00", 0,
|
||||
responseSpec);
|
||||
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("100.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId.intValue(), 0, Double.valueOf("100.00"));
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
@@ -320,19 +322,19 @@ public class LoanTransactionChargebackTest {
|
||||
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
|
||||
loanId);
|
||||
assertNotNull(loanIdTransactionsResponse);
|
||||
final Integer transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
final Long transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.overpaid");
|
||||
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 0, Double.valueOf("0.00"));
|
||||
|
||||
final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "100.00", 0,
|
||||
final Long chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(), "100.00", 0,
|
||||
responseSpec);
|
||||
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId.intValue(), 0, Double.valueOf("0.00"));
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
@@ -355,21 +357,21 @@ public class LoanTransactionChargebackTest {
|
||||
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
|
||||
loanId);
|
||||
assertNotNull(loanIdTransactionsResponse);
|
||||
final Integer transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
final Long transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.overpaid");
|
||||
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 0, Double.valueOf("0.00"));
|
||||
|
||||
GetDelinquencyRangesResponse delinquencyRange = getLoansLoanIdResponse.getDelinquencyRange();
|
||||
assertNull(delinquencyRange);
|
||||
log.info("Loan Delinquency Range is null {}", (delinquencyRange == null));
|
||||
final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "50.00", 0,
|
||||
final Long chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(), "50.00", 0,
|
||||
responseSpec);
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, chargebackTransactionId.intValue(), 0, Double.valueOf("0.00"));
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
@@ -396,16 +398,16 @@ public class LoanTransactionChargebackTest {
|
||||
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
|
||||
loanId);
|
||||
assertNotNull(loanIdTransactionsResponse);
|
||||
final Integer transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
final Long transactionId = loanIdTransactionsResponse.getResourceId();
|
||||
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
assertNotNull(getLoansLoanIdResponse);
|
||||
loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.closed.obligations.met");
|
||||
|
||||
// First round, empty array
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 0, Double.valueOf("0.00"));
|
||||
|
||||
loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "200.00", 0, responseSpec);
|
||||
loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(), "200.00", 0, responseSpec);
|
||||
|
||||
Double expectedAmount = Double.valueOf("200.00");
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
@@ -416,9 +418,9 @@ public class LoanTransactionChargebackTest {
|
||||
DelinquencyBucketsHelper.evaluateLoanCollectionData(getLoansLoanIdResponse, 0, expectedAmount);
|
||||
|
||||
// Second round, array size equal to 1
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 1, Double.valueOf("0.00"));
|
||||
|
||||
loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "300.00", 1, responseSpec);
|
||||
loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(), "300.00", 1, responseSpec);
|
||||
|
||||
expectedAmount = Double.valueOf("500.00");
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
@@ -428,9 +430,9 @@ public class LoanTransactionChargebackTest {
|
||||
DelinquencyBucketsHelper.evaluateLoanCollectionData(getLoansLoanIdResponse, 0, expectedAmount);
|
||||
|
||||
// Third round, array size equal to 2
|
||||
reviewLoanTransactionRelations(loanId, transactionId, 2, Double.valueOf("0.00"));
|
||||
reviewLoanTransactionRelations(loanId, transactionId.intValue(), 2, Double.valueOf("0.00"));
|
||||
|
||||
loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "500.00", 0, responseSpec);
|
||||
loanTransactionHelper.applyChargebackTransaction(loanId, transactionId.intValue(), "500.00", 0, responseSpec);
|
||||
|
||||
expectedAmount = Double.valueOf("1000.00");
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
|
||||
+20
-2
@@ -23,13 +23,17 @@ import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.client.models.BusinessDateRequest;
|
||||
import org.apache.fineract.client.models.BusinessDateResponse;
|
||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||
import org.apache.fineract.integrationtests.client.IntegrationTest;
|
||||
|
||||
@Slf4j
|
||||
public final class BusinessDateHelper {
|
||||
public final class BusinessDateHelper extends IntegrationTest {
|
||||
|
||||
private BusinessDateHelper() {}
|
||||
public BusinessDateHelper() {}
|
||||
|
||||
public static HashMap updateBusinessDate(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final BusinessDateType type, final LocalDate date) {
|
||||
@@ -39,6 +43,20 @@ public final class BusinessDateHelper {
|
||||
return Utils.performServerPost(requestSpec, responseSpec, BUSINESS_DATE_API, buildBusinessDateRequest(type, date), "changes");
|
||||
}
|
||||
|
||||
public BusinessDateResponse updateBusinessDate(final BusinessDateRequest request) {
|
||||
log.info("------------------UPDATE BUSINESS DATE----------------------");
|
||||
log.info("------------------Type: {}, date: {}----------------------", request.getType(), request.getDate());
|
||||
return ok(fineract().businessDateManagement.updateBusinessDate(request));
|
||||
}
|
||||
|
||||
public BusinessDateResponse getBusinessDate(final String type) {
|
||||
return ok(fineract().businessDateManagement.getBusinessDate(type));
|
||||
}
|
||||
|
||||
public List<BusinessDateResponse> getBusinessDates() {
|
||||
return ok(fineract().businessDateManagement.getBusinessDates());
|
||||
}
|
||||
|
||||
private static String buildBusinessDateRequest(BusinessDateType type, LocalDate date) {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
map.put("type", type.name());
|
||||
|
||||
+7
-1
@@ -41,15 +41,17 @@ import org.apache.fineract.client.models.GetClientsClientIdResponse;
|
||||
import org.apache.fineract.client.models.PostClientClientIdAddressesRequest;
|
||||
import org.apache.fineract.client.models.PostClientClientIdAddressesResponse;
|
||||
import org.apache.fineract.client.models.PostClientsRequest;
|
||||
import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.util.JSON;
|
||||
import org.apache.fineract.infrastructure.bulkimport.data.GlobalEntityType;
|
||||
import org.apache.fineract.integrationtests.client.IntegrationTest;
|
||||
import org.apache.fineract.integrationtests.common.system.CodeHelper;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ClientHelper {
|
||||
public class ClientHelper extends IntegrationTest {
|
||||
|
||||
private static final String CREATE_CLIENT_URL = "/fineract-provider/api/v1/clients?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String CLIENT_URL = "/fineract-provider/api/v1/clients";
|
||||
@@ -77,6 +79,10 @@ public class ClientHelper {
|
||||
return Utils.performServerPost(requestSpec, responseSpec, CREATE_CLIENT_URL, requestBody, "clientId");
|
||||
}
|
||||
|
||||
public PostClientsResponse createClient(final PostClientsRequest request) {
|
||||
return ok(fineract().clients.create6(request));
|
||||
}
|
||||
|
||||
public static Integer createClient(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
return createClient(requestSpec, responseSpec, "04 March 2011");
|
||||
}
|
||||
|
||||
+10
@@ -425,6 +425,16 @@ public class ExternalEventConfigurationHelper {
|
||||
shareProductDividentsCreateBusinessEvent.put("enabled", false);
|
||||
defaults.add(shareProductDividentsCreateBusinessEvent);
|
||||
|
||||
Map<String, Object> loanChargeAdjustmentPostBusinessEvent = new HashMap<>();
|
||||
loanChargeAdjustmentPostBusinessEvent.put("type", "LoanChargeAdjustmentPostBusinessEvent");
|
||||
loanChargeAdjustmentPostBusinessEvent.put("enabled", false);
|
||||
defaults.add(loanChargeAdjustmentPostBusinessEvent);
|
||||
|
||||
Map<String, Object> loanChargeAdjustmentPreBusinessEvent = new HashMap<>();
|
||||
loanChargeAdjustmentPreBusinessEvent.put("type", "LoanChargeAdjustmentPreBusinessEvent");
|
||||
loanChargeAdjustmentPreBusinessEvent.put("enabled", false);
|
||||
defaults.add(loanChargeAdjustmentPreBusinessEvent);
|
||||
|
||||
return defaults;
|
||||
|
||||
}
|
||||
|
||||
+8
-1
@@ -22,10 +22,13 @@ import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import org.apache.fineract.client.models.PostGLAccountsRequest;
|
||||
import org.apache.fineract.client.models.PostGLAccountsResponse;
|
||||
import org.apache.fineract.integrationtests.client.IntegrationTest;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class AccountHelper {
|
||||
public class AccountHelper extends IntegrationTest {
|
||||
|
||||
private static final String CREATE_GL_ACCOUNT_URL = "/fineract-provider/api/v1/glaccounts?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String GL_ACCOUNT_ID_RESPONSE = "resourceId";
|
||||
@@ -79,4 +82,8 @@ public class AccountHelper {
|
||||
return accountRunningBalance;
|
||||
}
|
||||
|
||||
public PostGLAccountsResponse createGLAccount(final PostGLAccountsRequest request) {
|
||||
return ok(fineract().glAccounts.createGLAccount1(request));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-2
@@ -23,8 +23,10 @@ import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import org.apache.fineract.client.models.PostChargesRequest;
|
||||
import org.apache.fineract.client.models.PostChargesResponse;
|
||||
import org.apache.fineract.client.util.JSON;
|
||||
import org.apache.fineract.integrationtests.client.IntegrationTest;
|
||||
import org.apache.fineract.integrationtests.common.CommonConstants;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
@@ -32,9 +34,9 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public final class ChargesHelper {
|
||||
public final class ChargesHelper extends IntegrationTest {
|
||||
|
||||
private ChargesHelper() {
|
||||
public ChargesHelper() {
|
||||
|
||||
}
|
||||
|
||||
@@ -522,4 +524,8 @@ public final class ChargesHelper {
|
||||
"status");
|
||||
|
||||
}
|
||||
|
||||
public PostChargesResponse createCharges(PostChargesRequest request) {
|
||||
return ok(fineract().charges.createCharge(request));
|
||||
}
|
||||
}
|
||||
|
||||
+46
-4
@@ -91,6 +91,10 @@ public class LoanProductTestBuilder {
|
||||
private String maxPrincipal = "10000000.00";
|
||||
private Account[] accountList = null;
|
||||
|
||||
private List<Map<String, Long>> feeToIncomeAccountMappings = null;
|
||||
private List<Map<String, Long>> penaltyToIncomeAccountMappings = null;
|
||||
private Account feeAndPenaltyAssetAccount;
|
||||
|
||||
private Boolean multiDisburseLoan = false;
|
||||
private final String outstandingLoanBalance = "35000";
|
||||
private String maxTrancheCount = "3";
|
||||
@@ -196,7 +200,7 @@ public class LoanProductTestBuilder {
|
||||
}
|
||||
|
||||
if (this.accountingRule.equals(ACCRUAL_UPFRONT) || this.accountingRule.equals(ACCRUAL_PERIODIC)) {
|
||||
map.putAll(getAccountMappingForAccrualBased());
|
||||
map.putAll(getAccountMappingForAccrualBased(this.feeAndPenaltyAssetAccount));
|
||||
} else if (this.accountingRule.equals(CASH_BASED)) {
|
||||
map.putAll(getAccountMappingForCashBased());
|
||||
}
|
||||
@@ -250,6 +254,13 @@ public class LoanProductTestBuilder {
|
||||
map.put("delinquencyBucketId", delinquencyBucketId);
|
||||
}
|
||||
|
||||
if (this.feeToIncomeAccountMappings != null) {
|
||||
map.put("feeToIncomeAccountMappings", this.feeToIncomeAccountMappings);
|
||||
}
|
||||
if (this.penaltyToIncomeAccountMappings != null) {
|
||||
map.put("penaltyToIncomeAccountMappings", this.penaltyToIncomeAccountMappings);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -436,7 +447,7 @@ public class LoanProductTestBuilder {
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, String> getAccountMappingForAccrualBased() {
|
||||
private Map<String, String> getAccountMappingForAccrualBased(Account feeAndPenaltyAssetAccount) {
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
for (int i = 0; i < this.accountList.length; i++) {
|
||||
if (this.accountList[i].getAccountType().equals(Account.AccountType.ASSET)) {
|
||||
@@ -444,9 +455,14 @@ public class LoanProductTestBuilder {
|
||||
map.put("fundSourceAccountId", ID);
|
||||
map.put("loanPortfolioAccountId", ID);
|
||||
map.put("transfersInSuspenseAccountId", ID);
|
||||
if (feeAndPenaltyAssetAccount != null) {
|
||||
map.put("receivableFeeAccountId", feeAndPenaltyAssetAccount.getAccountID().toString());
|
||||
map.put("receivablePenaltyAccountId", feeAndPenaltyAssetAccount.getAccountID().toString());
|
||||
} else {
|
||||
map.put("receivableFeeAccountId", ID);
|
||||
map.put("receivablePenaltyAccountId", ID);
|
||||
}
|
||||
map.put("receivableInterestAccountId", ID);
|
||||
map.put("receivableFeeAccountId", ID);
|
||||
map.put("receivablePenaltyAccountId", ID);
|
||||
|
||||
}
|
||||
if (this.accountList[i].getAccountType().equals(Account.AccountType.INCOME)) {
|
||||
@@ -587,4 +603,30 @@ public class LoanProductTestBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withFeeToIncomeAccountMapping(final Long chargeId, final Long accountId) {
|
||||
if (this.feeToIncomeAccountMappings == null) {
|
||||
this.feeToIncomeAccountMappings = new ArrayList<>();
|
||||
}
|
||||
Map<String, Long> newMap = new HashMap<>();
|
||||
newMap.put("chargeId", chargeId);
|
||||
newMap.put("incomeAccountId", accountId);
|
||||
this.feeToIncomeAccountMappings.add(newMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withPenaltyToIncomeAccountMapping(final Long chargeId, final Long accountId) {
|
||||
if (this.penaltyToIncomeAccountMappings == null) {
|
||||
this.penaltyToIncomeAccountMappings = new ArrayList<>();
|
||||
}
|
||||
Map<String, Long> newMap = new HashMap<>();
|
||||
newMap.put("chargeId", chargeId);
|
||||
newMap.put("incomeAccountId", accountId);
|
||||
this.penaltyToIncomeAccountMappings.add(newMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withFeeAndPenaltyAssetAccount(final Account account) {
|
||||
this.feeAndPenaltyAssetAccount = account;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
+35
-9
@@ -51,12 +51,17 @@ import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdSummary;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
|
||||
import org.apache.fineract.client.models.GetPaymentTypesResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdResponse;
|
||||
import org.apache.fineract.client.models.PutLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.util.JSON;
|
||||
import org.apache.fineract.integrationtests.client.IntegrationTest;
|
||||
import org.apache.fineract.integrationtests.common.CommonConstants;
|
||||
import org.apache.fineract.integrationtests.common.PaymentTypeHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
@@ -65,11 +70,9 @@ import org.apache.poi.ss.usermodel.Workbook;
|
||||
|
||||
@Slf4j
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public class LoanTransactionHelper {
|
||||
|
||||
private final RequestSpecification requestSpec;
|
||||
private final ResponseSpecification responseSpec;
|
||||
public class LoanTransactionHelper extends IntegrationTest {
|
||||
|
||||
public static final String DATE_TIME_FORMAT = "dd MMMM yyyy HH:mm";
|
||||
private static final String CREATE_LOAN_PRODUCT_URL = "/fineract-provider/api/v1/loanproducts?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String APPLY_LOAN_URL = "/fineract-provider/api/v1/loans?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String LOAN_ACCOUNT_URL = "/fineract-provider/api/v1/loans";
|
||||
@@ -90,10 +93,9 @@ public class LoanTransactionHelper {
|
||||
private static final String RECOVER_FROM_GUARANTORS_COMMAND = "recoverGuarantees";
|
||||
private static final String MAKE_REFUND_BY_CASH_COMMAND = "refundByCash";
|
||||
private static final String FORECLOSURE_COMMAND = "foreclosure";
|
||||
|
||||
public static final String DATE_TIME_FORMAT = "dd MMMM yyyy HH:mm";
|
||||
|
||||
private static final Gson GSON = new JSON().getGson();
|
||||
private final RequestSpecification requestSpec;
|
||||
private final ResponseSpecification responseSpec;
|
||||
|
||||
public LoanTransactionHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
this.requestSpec = requestSpec;
|
||||
@@ -221,7 +223,9 @@ public class LoanTransactionHelper {
|
||||
final ResponseSpecification responseSpec, final Integer loanID) {
|
||||
final String URL = "/fineract-provider/api/v1/loans/" + loanID + "/delinquencytags?" + Utils.TENANT_IDENTIFIER;
|
||||
final String response = Utils.performServerGet(requestSpec, responseSpec, URL);
|
||||
Type delinquencyTagsListType = new TypeToken<ArrayList<GetDelinquencyTagHistoryResponse>>() {}.getType();
|
||||
Type delinquencyTagsListType = new TypeToken<ArrayList<GetDelinquencyTagHistoryResponse>>() {
|
||||
|
||||
}.getType();
|
||||
return GSON.fromJson(response, delinquencyTagsListType);
|
||||
}
|
||||
|
||||
@@ -461,11 +465,20 @@ public class LoanTransactionHelper {
|
||||
return postLoanTransaction(createLoanTransactionURL(MAKE_REPAYMENT_COMMAND, loanID), getRepaymentBodyAsJSON(date, amountToBePaid));
|
||||
}
|
||||
|
||||
public PostLoansLoanIdTransactionsResponse makeLoanRepayment(final Long loanId, final PostLoansLoanIdTransactionsRequest request) {
|
||||
return ok(fineract().loanTransactions.executeLoanTransaction(loanId, request, "repayment"));
|
||||
}
|
||||
|
||||
public PostLoansLoanIdTransactionsResponse reverseLoanTransaction(final Integer loanId, final Integer transactionId, String date,
|
||||
ResponseSpecification responseSpec) {
|
||||
return postLoanTransaction(createLoanTransactionURL(UNDO, loanId, transactionId), getUndoJsonBody(date), responseSpec);
|
||||
}
|
||||
|
||||
public PostLoansLoanIdTransactionsTransactionIdResponse reverseLoanTransaction(final Long loanId, final Long transactionId,
|
||||
final PostLoansLoanIdTransactionsTransactionIdRequest request) {
|
||||
return ok(fineract().loanTransactions.adjustLoanTransaction(loanId, transactionId, request, "undo"));
|
||||
}
|
||||
|
||||
public HashMap makeRepaymentWithPDC(final String date, final Float amountToBePaid, final Integer loanID, final Integer paymentType) {
|
||||
return (HashMap) performLoanTransaction(createLoanTransactionURL(MAKE_REPAYMENT_COMMAND, loanID),
|
||||
getRepaymentWithPDCBodyAsJSON(date, amountToBePaid, paymentType), "");
|
||||
@@ -540,6 +553,11 @@ public class LoanTransactionHelper {
|
||||
return Utils.performServerPut(requestSpec, responseSpec, TRANSAC_URL, body, "");
|
||||
}
|
||||
|
||||
public PostLoansLoanIdChargesChargeIdResponse chargeAdjustment(final Long loanId, final Long chargeId,
|
||||
final PostLoansLoanIdChargesChargeIdRequest request) {
|
||||
return ok(fineract().loanCharges.executeLoanCharge1(loanId, chargeId, request, "adjustment"));
|
||||
}
|
||||
|
||||
public Integer undoWaiveChargesForLoanReturnResourceId(final Integer loanId, final Integer transactionId, final String body) {
|
||||
log.info("--------------------------------- UNDO WAIVE CHARGES FOR LOAN --------------------------------");
|
||||
final String TRANSAC_URL = "/fineract-provider/api/v1/loans/" + loanId + "/transactions/" + transactionId + "?"
|
||||
@@ -575,6 +593,14 @@ public class LoanTransactionHelper {
|
||||
return Utils.performServerGet(requestSpec, responseSpec, GET_LOAN_CHARGES_URL, param);
|
||||
}
|
||||
|
||||
public GetLoansLoanIdTransactionsTransactionIdResponse getLoanTransactionDetails(final Long loanId, final Long transactionId) {
|
||||
return ok(fineract().loanTransactions.retrieveTransaction(loanId, transactionId, null));
|
||||
}
|
||||
|
||||
public GetLoansLoanIdResponse getLoanDetails(final Long loanId) {
|
||||
return ok(fineract().loans.retrieveLoan(loanId, false, "all", null, null));
|
||||
}
|
||||
|
||||
public GetLoansLoanIdTransactionsTransactionIdResponse getLoanTransaction(final Integer loanId, final Integer txnId) {
|
||||
final String GET_LOAN_CHARGES_URL = "/fineract-provider/api/v1/loans/" + loanId + "/transactions/" + txnId + "?"
|
||||
+ Utils.TENANT_IDENTIFIER;
|
||||
@@ -1203,7 +1229,7 @@ public class LoanTransactionHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public Integer applyChargebackTransaction(final Integer loanId, final Integer transactionId, final String amount,
|
||||
public Long applyChargebackTransaction(final Integer loanId, final Integer transactionId, final String amount,
|
||||
final Integer paymentTypeIdx, ResponseSpecification responseSpec) {
|
||||
List<GetPaymentTypesResponse> paymentTypeList = PaymentTypeHelper.getSystemPaymentType(this.requestSpec, this.responseSpec);
|
||||
assertTrue(!paymentTypeList.isEmpty());
|
||||
|
||||
Reference in New Issue
Block a user