Skip to content

Commit

Permalink
Merge branch 'develop' into FINERACT-2090/restructure_loan_approve
Browse files Browse the repository at this point in the history
  • Loading branch information
kjozsa committed Jun 7, 2024
2 parents 246b718 + 92e291a commit d928403
Show file tree
Hide file tree
Showing 12 changed files with 408 additions and 142 deletions.
3 changes: 3 additions & 0 deletions fineract-doc/src/docs/en/introduction.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Fineract provides a reliable, robust, and affordable solution for entrepreneurs,

Fineract 1.x is a mature platform with open APIs.

```
image:https://static.scarf.sh/a.png?x-pxid=c8fb6966-9836-4ad6-882f-0461000fcbc7[]
```



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.apache.fineract.portfolio.loanaccount.command;

import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
Expand All @@ -30,6 +31,9 @@
@Data
public class LoanChargeCommand implements Serializable {

@Serial
private static final long serialVersionUID = 1L;

@SuppressWarnings("unused")
private Long id;
private Long chargeId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1392,9 +1392,7 @@ private void applyPeriodicAccruals(final Collection<LoanTransaction> accruals) {
Money fee = Money.zero(getCurrency());
Money penality = Money.zero(getCurrency());
for (LoanTransaction loanTransaction : accruals) {
LocalDate transactionDateForRange = isBasedOnSubmittedOnDate
? loanTransaction.getLoanChargesPaid().stream().findFirst().get().getLoanCharge().getDueDate()
: loanTransaction.getTransactionDate();
LocalDate transactionDateForRange = getDateForRangeCalculation(loanTransaction, isBasedOnSubmittedOnDate);
boolean isInPeriod = LoanRepaymentScheduleProcessingWrapper.isInPeriod(transactionDateForRange, installment, installments);
if (isInPeriod) {
interest = interest.plus(loanTransaction.getInterestPortion(getCurrency()));
Expand Down Expand Up @@ -1423,6 +1421,12 @@ private void applyPeriodicAccruals(final Collection<LoanTransaction> accruals) {
}
}

private LocalDate getDateForRangeCalculation(LoanTransaction loanTransaction, boolean isChargeAccrualBasedOnSubmittedOnDate) {
return isChargeAccrualBasedOnSubmittedOnDate && !loanTransaction.getLoanChargesPaid().isEmpty()
? loanTransaction.getLoanChargesPaid().stream().findFirst().get().getLoanCharge().getEffectiveDueDate()
: loanTransaction.getTransactionDate();
}

private void updateAccrualsForNonPeriodicAccruals(final Collection<LoanTransaction> accruals) {
final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged());
ExternalId externalId = ExternalId.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
import org.apache.fineract.portfolio.loanproduct.serialization.LoanProductDataValidator;
import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

Expand Down Expand Up @@ -151,6 +152,7 @@ public final class LoanApplicationValidator {
private final LoanCollateralAssembler collateralAssembler;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final HolidayRepository holidayRepository;
private final SavingsAccountRepositoryWrapper savingsAccountRepository;

public void validateForCreate(final Loan loan) {
final LocalDate expectedFirstRepaymentOnDate = loan.getExpectedFirstRepaymentOnDate();
Expand Down Expand Up @@ -207,7 +209,6 @@ private void validateForCreate(final JsonElement element) {
final Long groupId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.groupIdParameterName, element);
final Client client = clientId != null ? this.clientRepository.findOneWithNotFoundDetection(clientId) : null;
final Group group = groupId != null ? this.groupRepository.findOneWithNotFoundDetection(groupId) : null;

validateClientOrGroup(client, group, productId);

validateOrThrow("loan", baseDataValidator -> {
Expand Down Expand Up @@ -257,7 +258,6 @@ private void validateForCreate(final JsonElement element) {
}

}

}

boolean isEqualAmortization = false;
Expand Down Expand Up @@ -483,11 +483,7 @@ private void validateForCreate(final JsonElement element) {

validateTransactionProcessingStrategy(transactionProcessingStrategy, loanProduct, baseDataValidator);

if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.linkAccountIdParameterName, element)) {
final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.linkAccountIdParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.linkAccountIdParameterName).value(linkAccountId).ignoreIfNull()
.longGreaterThanZero();
}
validateLinkedSavingsAccount(element, baseDataValidator);

if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.createStandingInstructionAtDisbursementParameterName, element)) {
final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper
Expand All @@ -503,7 +499,7 @@ private void validateForCreate(final JsonElement element) {
// charges
loanChargeApiJsonValidator.validateLoanCharges(element, loanProduct, baseDataValidator);

/*
/**
* TODO: Add collaterals for other loan accounts if needed. For now it's only applicable for individual
* accounts. (loanType.isJLG() || loanType.isGLIM())
*/
Expand Down Expand Up @@ -700,7 +696,7 @@ private void validateForCreate(final JsonElement element) {
}

checkForProductMixRestrictions(element);
validateSubmittedOnDate(element, loanProduct);
validateSubmittedOnDate(element, null, loanProduct);
validateDisbursementDetails(loanProduct, element);
validateCollateral(element);
// validate if disbursement date is a holiday or a non-working day
Expand Down Expand Up @@ -1053,7 +1049,7 @@ public void validateForModify(final JsonCommand command, final Loan loan) {
"Principal fixing cannot be done with equal installment amortization");
}

LocalDate expectedDisbursementDate = null;
LocalDate expectedDisbursementDate = loan.getExpectedDisbursementDate();
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.expectedDisbursementDateParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;

Expand Down Expand Up @@ -1135,12 +1131,7 @@ public void validateForModify(final JsonCommand command, final Loan loan) {
.notExceedingLengthOf(500);
}

if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.submittedOnNoteParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.linkAccountIdParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.linkAccountIdParameterName).value(linkAccountId).ignoreIfNull()
.longGreaterThanZero();
}
validateLinkedSavingsAccount(element, baseDataValidator);

// charges
loanChargeApiJsonValidator.validateLoanCharges(element, loanProduct, baseDataValidator);
Expand Down Expand Up @@ -1360,7 +1351,7 @@ public void validateForModify(final JsonCommand command, final Loan loan) {
}

validateDisbursementDetails(loanProduct, element);
validateSubmittedOnDate(element, loanProduct);
validateSubmittedOnDate(element, loan.getSubmittedOnDate(), loanProduct);

validateClientOrGroup(client, group, productId);

Expand Down Expand Up @@ -1504,21 +1495,29 @@ private void validateLoanTermAndRepaidEveryValues(final Integer loanTermFrequenc
}
}

public void validatelinkedSavingsAccount(final SavingsAccount savingsAccount, final Loan loanApplication) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
if (savingsAccount.isNotActive()) {
dataValidationErrors.add(ApiParameterError.parameterError("validation.msg.loan.linked.savings.account.is.not.active",
"Linked Savings account with id:" + savingsAccount.getId() + " is not in active state", "linkAccountId",
savingsAccount.getId()));
} else if (!loanApplication.getClientId().equals(savingsAccount.clientId())) {
dataValidationErrors
.add(ApiParameterError.parameterError("validation.msg.loan.linked.savings.account.not.belongs.to.same.client",
"Linked Savings account with id:" + savingsAccount.getId() + " is not belongs to the same client",
"linkAccountId", savingsAccount.getId()));
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
public void validateLinkedSavingsAccount(final JsonElement element, DataValidatorBuilder baseDataValidator) {
final boolean linkedAccountIdWasProvided = this.fromApiJsonHelper.parameterExists(LoanApiConstants.linkAccountIdParameterName,
element);
if (linkedAccountIdWasProvided) {
final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.linkAccountIdParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.linkAccountIdParameterName).value(linkAccountId).ignoreIfNull()
.longGreaterThanZero();

final Long linkedAccountId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.linkAccountIdParameterName, element);
final SavingsAccount savingsAccount = savingsAccountRepository.findOneWithNotFoundDetection(linkedAccountId);
final Long clientId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.clientIdParameterName, element);
if (savingsAccount.isNotActive()) {
final ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.linked.savings.account.is.not.active",
"Linked Savings account with id:" + savingsAccount.getId() + " is not in active state", "linkAccountId",
savingsAccount.getId());
baseDataValidator.getDataValidationErrors().add(error);
} else if (!clientId.equals(savingsAccount.clientId())) {
final ApiParameterError error = ApiParameterError.parameterError(
"validation.msg.loan.linked.savings.account.not.belongs.to.same.client",
"Linked Savings account with id:" + savingsAccount.getId() + " is not belongs to the same client", "linkAccountId",
savingsAccount.getId());
baseDataValidator.getDataValidationErrors().add(error);
}
}
}

Expand Down Expand Up @@ -1623,9 +1622,7 @@ public void validateLoanMultiDisbursementDate(final JsonElement element, final D
.integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());

}

}

}

public void validateLoanForCollaterals(final Loan loan, final BigDecimal total) {
Expand Down Expand Up @@ -1770,11 +1767,12 @@ private void checkForProductMixRestrictions(final List<Long> activeLoansLoanProd
}
}

private void validateSubmittedOnDate(final JsonElement element, LoanProduct loanProduct) {
private void validateSubmittedOnDate(final JsonElement element, LocalDate originalSubmittedOnDate, LoanProduct loanProduct) {
final LocalDate startDate = loanProduct.getStartDate();
final LocalDate closeDate = loanProduct.getCloseDate();
final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.submittedOnDateParameterName,
element);
final LocalDate submittedOnDate = this.fromApiJsonHelper.parameterExists(LoanApiConstants.submittedOnDateParameterName, element)
? this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.submittedOnDateParameterName, element)
: originalSubmittedOnDate;
final Long clientId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.clientIdParameterName, element);
final Long groupId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.groupIdParameterName, element);
final LocalDate expectedDisbursementDate = this.fromApiJsonHelper
Expand Down
Loading

0 comments on commit d928403

Please sign in to comment.