Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kjozsa committed Jun 27, 2024
1 parent d0e3fb6 commit 048798c
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
import org.apache.fineract.organisation.holiday.domain.Holiday;
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
Expand All @@ -57,21 +61,25 @@
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
import org.apache.fineract.portfolio.collateralmanagement.exception.LoanCollateralAmountNotSufficientException;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanChargeRefundException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanRepaymentScheduleNotFoundException;
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;

Expand All @@ -84,6 +92,7 @@ public final class LoanTransactionValidator {
private final LoanRepository loanRepository;
private final ApplicationCurrencyRepository applicationCurrencyRepository;
private final LoanUtilService loanUtilService;
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;

private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
Expand All @@ -92,7 +101,7 @@ private void throwExceptionIfValidationWarningsExist(final List<ApiParameterErro
}
}

public void validateDisbursement(JsonCommand command, boolean isAccountTransfer, Long loanId) {
public void preValidateDisbursement(JsonCommand command, boolean isAccountTransfer, Long loanId) {
String json = command.json();
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
Expand Down Expand Up @@ -134,6 +143,62 @@ public void validateDisbursement(JsonCommand command, boolean isAccountTransfer,
});
}

public void validateDisbursement(Loan loan, JsonCommand command) {
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");

if (loan.isChargedOff() && DateUtils.isBefore(actualDisbursementDate, loan.getChargedOffOnDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
+ loan.getId()
+ " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
loan.getId());
}

LoanApplicationValidator.validateOrThrow("loan.disbursement", baseDataValidator -> {
if ((!(loan.isApproved() && loan.isNotDisbursed()) && !loan.getLoanProduct().isMultiDisburseLoan())
|| (loan.getLoanProduct().isMultiDisburseLoan() && !loan.isAllTranchesNotDisbursed())) {
final String defaultUserMessage = "Loan Disbursal is not allowed. Loan Account is not in approved and not disbursed state.";
final ApiParameterError error = ApiParameterError
.generalError("error.msg.loan.disbursal.account.is.not.approve.not.disbursed.state", defaultUserMessage);
baseDataValidator.getDataValidationErrors().add(error);
}

final BigDecimal disbursedAmount = loan.getDisbursedAmount();
final Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();

// Get relevant loan collateral modules
if ((loanCollateralManagements != null && !loanCollateralManagements.isEmpty()) && loan.getLoanType().isIndividualAccount()) {
BigDecimal totalCollateral = BigDecimal.ZERO;

for (LoanCollateralManagement loanCollateralManagement : loanCollateralManagements) {
BigDecimal quantity = loanCollateralManagement.getQuantity();
BigDecimal pctToBase = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getPctToBase();
BigDecimal basePrice = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getBasePrice();
totalCollateral = totalCollateral.add(quantity.multiply(basePrice).multiply(pctToBase).divide(BigDecimal.valueOf(100)));
}

// Validate the loan collateral value against the disbursedAmount
if (disbursedAmount.compareTo(totalCollateral) > 0) {
throw new LoanCollateralAmountNotSufficientException(disbursedAmount);
}
}

// validate ActualDisbursement Date Against Expected Disbursement Date
LoanProduct loanProduct = loan.loanProduct();
if (loanProduct.isSyncExpectedWithDisbursementDate()) {
if (!loan.getExpectedDisbursedOnLocalDate().equals(actualDisbursementDate)) {
throw new DateMismatchException(actualDisbursementDate, loan.getExpectedDisbursedOnLocalDate());
}
}


entityDatatableChecksWritePlatformService.runTheCheckForProduct(loan.getId(), EntityTables.LOAN.getName(),
StatusEnum.DISBURSE.getCode().longValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());


});
}


private static @NotNull Set<String> getDisbursementParameters(boolean isAccountTransfer) {
Set<String> disbursementParameters;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
import org.apache.fineract.portfolio.collateralmanagement.exception.LoanCollateralAmountNotSufficientException;
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
import org.apache.fineract.portfolio.collectionsheet.command.SingleDisbursalCommand;
Expand Down Expand Up @@ -291,75 +290,36 @@ public CommandProcessingResult disburseLoan(Long loanId, JsonCommand command, Bo
@Override
public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand command, Boolean isAccountTransfer,
Boolean isWithoutAutoPayment) {
this.loanTransactionValidator.validateDisbursement(command, isAccountTransfer, loanId);
loanTransactionValidator.preValidateDisbursement(command, isAccountTransfer, loanId);

Loan loan = loanAssembler.assembleFrom(loanId);

Loan loan = this.loanAssembler.assembleFrom(loanId);
// Fail fast if client/group is not active or actual loan status disallows disbursal
checkClientOrGroupActive(loan);

final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");

if (loan.isChargedOff() && DateUtils.isBefore(actualDisbursementDate, loan.getChargedOffOnDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
+ loanId
+ " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
loanId);
}

if (loan.loanProduct().isDisallowExpectedDisbursements()) {
List<LoanDisbursementDetails> filteredList = loan.getDisbursementDetails().stream()
.filter(disbursementDetails -> disbursementDetails.actualDisbursementDate() == null).toList();
// Check whether a new LoanDisbursementDetails is required
if (filteredList.isEmpty()) {
// create artificial 'tranche/expected disbursal' as current disburse code expects it for
// multi-disbursal
// products
// multi-disbursal products
final LocalDate artificialExpectedDate = loan.getExpectedDisbursedOnLocalDate();
LoanDisbursementDetails disbursementDetail = new LoanDisbursementDetails(artificialExpectedDate, null,
loan.getDisbursedAmount(), null, false);
disbursementDetail.updateLoan(loan);
loan.getAllDisbursementDetails().add(disbursementDetail);
}
}
loan.validateAccountStatus(LoanEvent.LOAN_DISBURSED);

// Get disbursedAmount
final BigDecimal disbursedAmount = loan.getDisbursedAmount();
final Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();

// Get relevant loan collateral modules
if ((loanCollateralManagements != null && !loanCollateralManagements.isEmpty()) && loan.getLoanType().isIndividualAccount()) {
BigDecimal totalCollateral = BigDecimal.valueOf(0);

for (LoanCollateralManagement loanCollateralManagement : loanCollateralManagements) {
BigDecimal quantity = loanCollateralManagement.getQuantity();
BigDecimal pctToBase = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getPctToBase();
BigDecimal basePrice = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getBasePrice();
totalCollateral = totalCollateral.add(quantity.multiply(basePrice).multiply(pctToBase).divide(BigDecimal.valueOf(100)));
}

// Validate the loan collateral value against the disbursedAmount
if (disbursedAmount.compareTo(totalCollateral) > 0) {
throw new LoanCollateralAmountNotSufficientException(disbursedAmount);
}
}

// validate ActualDisbursement Date Against Expected Disbursement Date
LoanProduct loanProduct = loan.loanProduct();
if (loanProduct.isSyncExpectedWithDisbursementDate()) {
syncExpectedDateWithActualDisbursementDate(loan, actualDisbursementDate);
}

final LocalDate nextPossibleRepaymentDate = loan.getNextPossibleRepaymentDateForRescheduling();
final LocalDate rescheduledRepaymentDate = command.localDateValueOfParameterNamed("adjustRepaymentDate");

entityDatatableChecksWritePlatformService.runTheCheckForProduct(loanId, EntityTables.LOAN.getName(),
StatusEnum.DISBURSE.getCode().longValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
loanTransactionValidator.validateDisbursement(loan, command);

final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
LocalDate recalculateFrom = null;
if (!loan.isMultiDisburmentLoan()) {
loan.setActualDisbursementDate(actualDisbursementDate);
}

ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);

// validate actual disbursement date against meeting date
Expand Down Expand Up @@ -389,12 +349,11 @@ public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand
// Recalculate first repayment date based in actual disbursement date.
updateLoanCounters(loan, actualDisbursementDate);
Money amountBeforeAdjust = loan.getPrincipal();
boolean canDisburse = loan.canDisburse(actualDisbursementDate);
ChangedTransactionDetail changedTransactionDetail = null;
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
if (canDisburse) {

if (loan.canDisburse(actualDisbursementDate)) {
// Get netDisbursalAmount from disbursal screen field.
final BigDecimal netDisbursalAmount = command
.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName);
Expand Down Expand Up @@ -430,9 +389,9 @@ public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand
}

amountToDisburse = disburseAmount.minus(loanOutstanding);

disburseLoanToLoan(loan, command, loanOutstanding);
}

LoanTransaction disbursementTransaction = null;
if (isAccountTransfer) {
disburseLoanToSavings(loan, command, amountToDisburse, paymentDetail);
Expand All @@ -452,6 +411,9 @@ public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand
*/
recalculateSchedule = true;
}

final LocalDate nextPossibleRepaymentDate = loan.getNextPossibleRepaymentDateForRescheduling();
final LocalDate rescheduledRepaymentDate = command.localDateValueOfParameterNamed("adjustRepaymentDate");
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate,
rescheduledRepaymentDate);
boolean downPaymentEnabled = loan.repaymentScheduleDetail().isEnableDownPayment();
Expand Down Expand Up @@ -2000,7 +1962,6 @@ public CommandProcessingResult closeAsRescheduled(final Long loanId, final JsonC
}

private void disburseLoanToLoan(final Loan loan, final JsonCommand command, final BigDecimal amount) {

final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);

Expand All @@ -2015,7 +1976,6 @@ private void disburseLoanToLoan(final Loan loan, final JsonCommand command, fina
}

private void disburseLoanToSavings(final Loan loan, final JsonCommand command, final Money amount, final PaymentDetail paymentDetail) {

final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);

Expand All @@ -2035,7 +1995,6 @@ private void disburseLoanToSavings(final Loan loan, final JsonCommand command, f
LoanTransactionType.DISBURSEMENT.getValue(), null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null,
txnExternalId, loan, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck);
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);

}

@Transactional
Expand Down

0 comments on commit 048798c

Please sign in to comment.