adamsaghy commented on code in PR #4046:
URL: https://github.com/apache/fineract/pull/4046#discussion_r1744134762


##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java:
##########
@@ -295,6 +303,139 @@ public CommandProcessingResult disburseLoan(Long loanId, 
JsonCommand command, Bo
         return disburseLoan(loanId, command, isAccountTransfer, false);
     }
 
+    @Transactional
+    @Override
+    public CommandProcessingResult disburseLoanToLinkedAccount(Long loanId, 
JsonCommand command, Boolean isAccountTransfer) {
+        Boolean isWithoutAutoPayment = false;
+        boolean isPaymentHubIntegrationEnabled = 
configurationDomainService.isPaymentHubIntegrationEnabled();
+        if (!isPaymentHubIntegrationEnabled) {
+            throw new 
GlobalConfigurationNotEnabledException("enable-payment-hub-integration");
+        }
+        loanTransactionValidator.validateDisbursement(command, 
isAccountTransfer, loanId);
+
+        Loan loan = loanAssembler.assembleFrom(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
+                final LocalDate artificialExpectedDate = 
loan.getExpectedDisbursedOnLocalDate();
+                LoanDisbursementDetails disbursementDetail = new 
LoanDisbursementDetails(artificialExpectedDate, null,
+                        loan.getDisbursedAmount(), null, false);
+                disbursementDetail.updateLoan(loan);
+                loan.getAllDisbursementDetails().add(disbursementDetail);
+            }
+        }
+
+        final LocalDate nextPossibleRepaymentDate = 
loan.getNextPossibleRepaymentDateForRescheduling();
+        final LocalDate rescheduledRepaymentDate = 
command.localDateValueOfParameterNamed("adjustRepaymentDate");
+        final LocalDate actualDisbursementDate = 
command.localDateValueOfParameterNamed("actualDisbursementDate");
+        if (!loan.isMultiDisburmentLoan()) {
+            loan.setActualDisbursementDate(actualDisbursementDate);
+        }
+
+        // validate actual disbursement date against meeting date
+        ScheduleGeneratorDTO scheduleGeneratorDTO = 
this.loanUtilService.buildScheduleGeneratorDTO(loan, null);
+
+        final AppUser currentUser = getAppUserIfPresent();
+        final Map<String, Object> changes = new LinkedHashMap<>();
+
+        final PaymentDetail paymentDetail = 
this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, 
changes);
+        if (paymentDetail != null && paymentDetail.getPaymentType() != null && 
paymentDetail.getPaymentType().getIsCashPayment()) {
+            BigDecimal transactionAmount = 
command.bigDecimalValueOfParameterNamed("transactionAmount");
+            
this.cashierTransactionDataValidator.validateOnLoanDisbursal(currentUser, 
loan.getCurrencyCode(), transactionAmount);
+        }
+        final boolean isPaymentTypeApplicableForDisbursementCharge = 
configurationDomainService
+                .isPaymentTypeApplicableForDisbursementCharge();
+
+        Money amountBeforeAdjust = loan.getPrincipal();
+        final Locale locale = command.extractLocale();
+        final DateTimeFormatter fmt = 
DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
+
+        if (loan.canDisburse()) {
+            // Get netDisbursalAmount from disbursal screen field.
+            final BigDecimal netDisbursalAmount = command
+                    
.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName);
+            if (netDisbursalAmount != null) {
+                loan.setNetDisbursalAmount(netDisbursalAmount);
+            }
+            Money disburseAmount = loan.adjustDisburseAmount(command, 
actualDisbursementDate);
+            boolean recalculateSchedule = 
amountBeforeAdjust.isNotEqualTo(loan.getPrincipal());
+            final ExternalId txnExternalId = 
externalIdFactory.createFromCommand(command, 
LoanApiConstants.externalIdParameterName);
+
+            if (loan.isTopup() && loan.getClientId() != null) {
+                final Long loanIdToClose = 
loan.getTopupLoanDetails().getLoanIdToClose();
+                final Loan loanToClose = 
this.loanRepositoryWrapper.findNonClosedLoanThatBelongsToClient(loanIdToClose, 
loan.getClientId());
+                if (loanToClose == null) {
+                    throw new 
GeneralPlatformDomainRuleException("error.msg.loan.to.be.closed.with.topup.is.not.active",
+                            "Loan to be closed with this topup is not 
active.");
+                }
+                final LocalDate lastUserTransactionOnLoanToClose = 
loanToClose.getLastUserTransactionDate();
+                if (DateUtils.isBefore(loan.getDisbursementDate(), 
lastUserTransactionOnLoanToClose)) {
+                    throw new GeneralPlatformDomainRuleException(
+                            
"error.msg.loan.disbursal.date.should.be.after.last.transaction.date.of.loan.to.be.closed",
+                            "Disbursal date of this loan application " + 
loan.getDisbursementDate()
+                                    + " should be after last transaction date 
of loan to be closed " + lastUserTransactionOnLoanToClose);
+                }
+
+                BigDecimal loanOutstanding = this.loanReadPlatformService
+                        
.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, 
actualDisbursementDate).getAmount();
+                final BigDecimal firstDisbursalAmount = 
loan.getFirstDisbursalAmount();
+                if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
+                    throw new 
GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
+                            "Topup loan amount should be greater than 
outstanding amount of loan to be closed.");
+                }
+            }
+            if (loan.getRepaymentScheduleInstallments().isEmpty()) {
+                /*
+                 * If no schedule, generate one (applicable to non-tranche 
multi-disbursal loans)
+                 */
+                recalculateSchedule = true;
+            }
+
+            regenerateScheduleOnDisbursement(command, loan, 
recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate,
+                    rescheduledRepaymentDate);
+            boolean downPaymentEnabled = 
loan.repaymentScheduleDetail().isEnableDownPayment();
+            if 
(loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || 
downPaymentEnabled) {
+                createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
+            }
+        }
+
+        final PortfolioAccountData portfolioAccountData = 
this.accountAssociationsReadPlatformService
+                .retriveLoanLinkedAssociation(loan.getId());
+        if (portfolioAccountData == null) {
+            final String errorMessage = "Disburse Loan with id:" + 
loan.getId() + " requires linked savings account for payment";
+            throw new 
LinkedAccountRequiredException("loan.disburse.to.savings", errorMessage, 
loan.getId());
+        }
+
+        InteropIdentifier identifier1 = 
interopService.getIdentifierByAccountId(portfolioAccountData.getId());
+        String payerIdentifierType = 
InteropIdentifierType.ACCOUNT_ID.toString();

Review Comment:
   Please dont use the `toString` of an enum. There is no explicit `toString` 
for this enum so it is basically give you the value of `name()`. Please use 
that if that's the information you are looking for. Anyone who override the 
`toString` method of this enum in the future will effectively break you logic.



##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java:
##########
@@ -295,6 +303,139 @@ public CommandProcessingResult disburseLoan(Long loanId, 
JsonCommand command, Bo
         return disburseLoan(loanId, command, isAccountTransfer, false);
     }
 
+    @Transactional
+    @Override
+    public CommandProcessingResult disburseLoanToLinkedAccount(Long loanId, 
JsonCommand command, Boolean isAccountTransfer) {
+        Boolean isWithoutAutoPayment = false;
+        boolean isPaymentHubIntegrationEnabled = 
configurationDomainService.isPaymentHubIntegrationEnabled();
+        if (!isPaymentHubIntegrationEnabled) {
+            throw new 
GlobalConfigurationNotEnabledException("enable-payment-hub-integration");
+        }
+        loanTransactionValidator.validateDisbursement(command, 
isAccountTransfer, loanId);
+
+        Loan loan = loanAssembler.assembleFrom(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
+                final LocalDate artificialExpectedDate = 
loan.getExpectedDisbursedOnLocalDate();
+                LoanDisbursementDetails disbursementDetail = new 
LoanDisbursementDetails(artificialExpectedDate, null,
+                        loan.getDisbursedAmount(), null, false);
+                disbursementDetail.updateLoan(loan);
+                loan.getAllDisbursementDetails().add(disbursementDetail);
+            }
+        }
+
+        final LocalDate nextPossibleRepaymentDate = 
loan.getNextPossibleRepaymentDateForRescheduling();
+        final LocalDate rescheduledRepaymentDate = 
command.localDateValueOfParameterNamed("adjustRepaymentDate");
+        final LocalDate actualDisbursementDate = 
command.localDateValueOfParameterNamed("actualDisbursementDate");
+        if (!loan.isMultiDisburmentLoan()) {
+            loan.setActualDisbursementDate(actualDisbursementDate);
+        }
+
+        // validate actual disbursement date against meeting date
+        ScheduleGeneratorDTO scheduleGeneratorDTO = 
this.loanUtilService.buildScheduleGeneratorDTO(loan, null);
+
+        final AppUser currentUser = getAppUserIfPresent();
+        final Map<String, Object> changes = new LinkedHashMap<>();
+
+        final PaymentDetail paymentDetail = 
this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, 
changes);
+        if (paymentDetail != null && paymentDetail.getPaymentType() != null && 
paymentDetail.getPaymentType().getIsCashPayment()) {
+            BigDecimal transactionAmount = 
command.bigDecimalValueOfParameterNamed("transactionAmount");
+            
this.cashierTransactionDataValidator.validateOnLoanDisbursal(currentUser, 
loan.getCurrencyCode(), transactionAmount);
+        }
+        final boolean isPaymentTypeApplicableForDisbursementCharge = 
configurationDomainService
+                .isPaymentTypeApplicableForDisbursementCharge();
+
+        Money amountBeforeAdjust = loan.getPrincipal();
+        final Locale locale = command.extractLocale();
+        final DateTimeFormatter fmt = 
DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
+
+        if (loan.canDisburse()) {
+            // Get netDisbursalAmount from disbursal screen field.
+            final BigDecimal netDisbursalAmount = command
+                    
.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName);
+            if (netDisbursalAmount != null) {
+                loan.setNetDisbursalAmount(netDisbursalAmount);
+            }
+            Money disburseAmount = loan.adjustDisburseAmount(command, 
actualDisbursementDate);
+            boolean recalculateSchedule = 
amountBeforeAdjust.isNotEqualTo(loan.getPrincipal());
+            final ExternalId txnExternalId = 
externalIdFactory.createFromCommand(command, 
LoanApiConstants.externalIdParameterName);
+
+            if (loan.isTopup() && loan.getClientId() != null) {
+                final Long loanIdToClose = 
loan.getTopupLoanDetails().getLoanIdToClose();
+                final Loan loanToClose = 
this.loanRepositoryWrapper.findNonClosedLoanThatBelongsToClient(loanIdToClose, 
loan.getClientId());
+                if (loanToClose == null) {
+                    throw new 
GeneralPlatformDomainRuleException("error.msg.loan.to.be.closed.with.topup.is.not.active",
+                            "Loan to be closed with this topup is not 
active.");
+                }
+                final LocalDate lastUserTransactionOnLoanToClose = 
loanToClose.getLastUserTransactionDate();
+                if (DateUtils.isBefore(loan.getDisbursementDate(), 
lastUserTransactionOnLoanToClose)) {
+                    throw new GeneralPlatformDomainRuleException(
+                            
"error.msg.loan.disbursal.date.should.be.after.last.transaction.date.of.loan.to.be.closed",
+                            "Disbursal date of this loan application " + 
loan.getDisbursementDate()
+                                    + " should be after last transaction date 
of loan to be closed " + lastUserTransactionOnLoanToClose);
+                }
+
+                BigDecimal loanOutstanding = this.loanReadPlatformService
+                        
.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, 
actualDisbursementDate).getAmount();
+                final BigDecimal firstDisbursalAmount = 
loan.getFirstDisbursalAmount();
+                if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
+                    throw new 
GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
+                            "Topup loan amount should be greater than 
outstanding amount of loan to be closed.");
+                }
+            }
+            if (loan.getRepaymentScheduleInstallments().isEmpty()) {
+                /*
+                 * If no schedule, generate one (applicable to non-tranche 
multi-disbursal loans)
+                 */
+                recalculateSchedule = true;
+            }
+
+            regenerateScheduleOnDisbursement(command, loan, 
recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate,
+                    rescheduledRepaymentDate);
+            boolean downPaymentEnabled = 
loan.repaymentScheduleDetail().isEnableDownPayment();
+            if 
(loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || 
downPaymentEnabled) {
+                createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
+            }
+        }
+
+        final PortfolioAccountData portfolioAccountData = 
this.accountAssociationsReadPlatformService
+                .retriveLoanLinkedAssociation(loan.getId());
+        if (portfolioAccountData == null) {
+            final String errorMessage = "Disburse Loan with id:" + 
loan.getId() + " requires linked savings account for payment";
+            throw new 
LinkedAccountRequiredException("loan.disburse.to.savings", errorMessage, 
loan.getId());
+        }
+
+        InteropIdentifier identifier1 = 
interopService.getIdentifierByAccountId(portfolioAccountData.getId());
+        String payerIdentifierType = 
InteropIdentifierType.ACCOUNT_ID.toString();
+        String payerIdentifierValue = loan.getAccountNumber();
+        String payeeIdentifierType = identifier1.getType().toString();

Review Comment:
   Please dont use the `toString` of an enum. There is no explicit `toString` 
for this enum so it is basically give you the value of `name()`. Please use 
that if that's the information you are looking for. Anyone who override the 
`toString` method of this enum in the future will effectively break you logic.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to