diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/loan/LoanImportHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/loan/LoanImportHandler.java index 054846e20..401cd13ab 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/loan/LoanImportHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/loan/LoanImportHandler.java @@ -42,7 +42,9 @@ import org.apache.fineract.infrastructure.bulkimport.importhandler.helper.DateSe import org.apache.fineract.infrastructure.bulkimport.importhandler.helper.EnumOptionDataValueSerializer; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.portfolio.loanaccount.data.DisbursementData; import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData; import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData; @@ -140,7 +142,7 @@ public class LoanImportHandler implements ImportHandler { private LoanAccountData readLoan(final Workbook workbook, final Row row, final List statuses, final String locale, final String dateFormat) { - String externalId = ImportHandlerUtils.readAsString(LoanConstants.EXTERNAL_ID_COL, row); + ExternalId externalId = ExternalIdFactory.produce(ImportHandlerUtils.readAsString(LoanConstants.EXTERNAL_ID_COL, row)); String status = ImportHandlerUtils.readAsString(LoanConstants.STATUS_COL, row); String productName = ImportHandlerUtils.readAsString(LoanConstants.PRODUCT_COL, row); Long productId = ImportHandlerUtils.getIdByName(workbook.getSheet(TemplatePopulateImportConstants.PRODUCT_SHEET_NAME), productName); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java index 8e8bc4335..a7e4879fc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java @@ -47,7 +47,7 @@ public class LoanDelinquencyRangeChangeBusinessEventSerializer extends AbstractB LoanAccountData data = service.retrieveOne(event.get().getId()); Long id = data.getId(); String accountNumber = data.getAccountNo(); - String externalId = data.getExternalId(); + String externalId = data.getExternalId().getValue(); DelinquencyRangeDataV1 delinquencyRange = mapper.map(data.getDelinquencyRange()); return new LoanAccountDelinquencyRangeDataV1(id, accountNumber, externalId, delinquencyRange); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java index f283ba604..6e04102fe 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java @@ -48,7 +48,7 @@ public class LoanRepaymentBusinessEventSerializer extends AbstractBusinessEventS Loan loan = repaymentInstallment.getLoan(); Long id = loan.getId(); String accountNo = loan.getAccountNumber(); - String externalId = loan.getExternalId(); + String externalId = loan.getExternalId().getValue(); String dueDate = dataTimeMapper.mapLocalDate(repaymentInstallment.getDueDate()); MonetaryCurrency loanCurrency = loan.getCurrency(); CurrencyDataV1 currency = CurrencyDataV1.newBuilder().setCode(loanCurrency.getCode()) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java index a9a1688df..45898c9c6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java @@ -163,7 +163,8 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat final CollectionData collectionData = loanDelinquencyDomainService.getOverdueCollectionData(loan); changes = lookUpDelinquencyRange(loan, delinquencyBucket, collectionData.getDelinquentDays()); } - return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(loan.getId()).with(changes).build(); + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(loan.getId()) + .withEntityExternalId(loan.getExternalId()).with(changes).build(); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java index ccd158429..c56f76cfb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java @@ -560,7 +560,7 @@ public class LoanChargesApiResource { Long resolvedLoanId = loanId; if (resolvedLoanId == null) { loanExternalId.throwExceptionIfEmpty(); - resolvedLoanId = this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId.getValue()); + resolvedLoanId = this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId); if (resolvedLoanId == null) { throw new LoanNotFoundException(loanExternalId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java index 691d6c799..c5afc8c7a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java @@ -587,7 +587,7 @@ public class LoanTransactionsApiResource { Long resolvedLoanId = loanId; if (resolvedLoanId == null) { loanExternalId.throwExceptionIfEmpty(); - resolvedLoanId = this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId.getValue()); + resolvedLoanId = this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId); if (resolvedLoanId == null) { throw new LoanNotFoundException(loanExternalId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java index ff47b912b..b7b4f5071 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java @@ -73,11 +73,13 @@ import org.apache.fineract.infrastructure.core.api.JsonQuery; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.UploadRequest; +import org.apache.fineract.infrastructure.core.domain.ExternalId; 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.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.service.CommandParameterUtil; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant; @@ -127,6 +129,7 @@ import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData; import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData; import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType; +import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException; import org.apache.fineract.portfolio.loanaccount.exception.LoanTemplateTypeRequiredException; import org.apache.fineract.portfolio.loanaccount.exception.NotSupportedLoanTemplateTypeException; import org.apache.fineract.portfolio.loanaccount.guarantor.data.GuarantorData; @@ -282,24 +285,12 @@ public class LoansApiResource { @Path("{loanId}/template") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveApprovalTemplate(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId, + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.GetLoansApprovalTemplateResponse.class))) }) + public String retrieveApprovalTemplate(@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId, @QueryParam("templateType") @Parameter(description = "templateType") final String templateType, @Context final UriInfo uriInfo) { - - this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); - - LoanApprovalData loanApprovalTemplate = null; - - if (templateType == null) { - final String errorMsg = "Loan template type must be provided"; - throw new LoanTemplateTypeRequiredException(errorMsg); - } else if (templateType.equals("approval")) { - loanApprovalTemplate = this.loanReadPlatformService.retrieveApprovalTemplate(loanId); - } - - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.loanApprovalDataToApiJsonSerializer.serialize(settings, loanApprovalTemplate, LOAN_APPROVAL_DATA_PARAMETERS); - + return retrieveApprovalTemplate(loanId, null, templateType, uriInfo); } @GET @@ -376,7 +367,7 @@ public class LoansApiResource { calendarOptions = this.loanReadPlatformService.retrieveCalendars(groupId); newLoanAccount = newLoanAccount == null ? loanAccountGroupData : LoanAccountData.populateGroupDefaults(newLoanAccount, loanAccountGroupData); - accountLinkingOptions = getaccountLinkingOptions(newLoanAccount, clientId, groupId); + accountLinkingOptions = getAccountLinkingOptions(newLoanAccount, clientId, groupId); } else if (templateType.equals("jlgbulk")) { // get group details along with members in that group @@ -393,7 +384,7 @@ public class LoansApiResource { for (ClientData clientData : members) { Integer loanCounter = this.loanReadPlatformService.retriveLoanCounter(clientData.getId(), productId); memberLoanCycle.put(clientData.getId(), loanCounter); - accountLinkingOptions.addAll(getaccountLinkingOptions(newLoanAccount, clientData.getId(), groupId)); + accountLinkingOptions.addAll(getAccountLinkingOptions(newLoanAccount, clientData.getId(), groupId)); } } @@ -408,7 +399,7 @@ public class LoansApiResource { allowedLoanOfficers = this.loanReadPlatformService.retrieveAllowedLoanOfficers(officeId, staffInSelectedOfficeOnly); if (clientId != null) { - accountLinkingOptions = getaccountLinkingOptions(newLoanAccount, clientId, groupId); + accountLinkingOptions = getAccountLinkingOptions(newLoanAccount, clientId, groupId); } // add product options, allowed loan officers and calendar options @@ -424,7 +415,7 @@ public class LoansApiResource { return this.toApiJsonSerializer.serialize(settings, newLoanAccount, LOAN_DATA_PARAMETERS); } - private Collection getaccountLinkingOptions(final LoanAccountData newLoanAccount, final Long clientId, + private Collection getAccountLinkingOptions(final LoanAccountData newLoanAccount, final Long clientId, final Long groupId) { final CurrencyData currencyData = newLoanAccount.getCurrency(); String currencyCode = null; @@ -450,289 +441,14 @@ public class LoansApiResource { + "loans/1?fields=id,principal,annualInterestRate&associations=repaymentSchedule,transactions") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.GetLoansLoanIdResponse.class))) }) - public String retrieveLoan(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId, + public String retrieveLoan(@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId, @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly, @DefaultValue("all") @QueryParam("associations") @Parameter(in = ParameterIn.QUERY, name = "associations", description = "Loan object relations to be included in the response", required = false, examples = { @ExampleObject(value = "all"), @ExampleObject(value = "repaymentSchedule,transactions") }) final String associations, @QueryParam("exclude") @Parameter(in = ParameterIn.QUERY, name = "exclude", description = "Optional Loan object relation list to be filtered in the response", required = false, example = "guarantors,futureSchedule") final String exclude, @QueryParam("fields") @Parameter(in = ParameterIn.QUERY, name = "fields", description = "Optional Loan attribute list to be in the response", required = false, example = "id,principal,annualInterestRate") final String fields, @Context final UriInfo uriInfo) { - this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); - - LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveOne(loanId); - if (loanBasicDetails.isInterestRecalculationEnabled()) { - Collection interestRecalculationCalendarDatas = this.calendarReadPlatformService.retrieveCalendarsByEntity( - loanBasicDetails.getInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue(), - null); - CalendarData calendarData = null; - if (!CollectionUtils.isEmpty(interestRecalculationCalendarDatas)) { - calendarData = interestRecalculationCalendarDatas.iterator().next(); - } - - Collection interestRecalculationCompoundingCalendarDatas = this.calendarReadPlatformService - .retrieveCalendarsByEntity(loanBasicDetails.getInterestRecalculationDetailId(), - CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue(), null); - CalendarData compoundingCalendarData = null; - if (!CollectionUtils.isEmpty(interestRecalculationCompoundingCalendarDatas)) { - compoundingCalendarData = interestRecalculationCompoundingCalendarDatas.iterator().next(); - } - loanBasicDetails = LoanAccountData.withInterestRecalculationCalendarData(loanBasicDetails, calendarData, - compoundingCalendarData); - } - if (loanBasicDetails.getRepaymentFrequencyType() != null - && loanBasicDetails.getRepaymentFrequencyType().getId().intValue() == PeriodFrequencyType.MONTHS.getValue()) { - Collection loanCalendarDatas = this.calendarReadPlatformService.retrieveCalendarsByEntity(loanId, - CalendarEntityType.LOANS.getValue(), null); - CalendarData calendarData = null; - if (!CollectionUtils.isEmpty(loanCalendarDatas)) { - calendarData = loanCalendarDatas.iterator().next(); - } - if (calendarData != null) { - loanBasicDetails = LoanAccountData.withLoanCalendarData(loanBasicDetails, calendarData); - } - } - Collection interestRatesPeriods = this.loanReadPlatformService - .retrieveLoanInterestRatePeriodData(loanBasicDetails); - Collection loanRepayments = null; - LoanScheduleData repaymentSchedule = null; - Collection charges = null; - Collection guarantors = null; - CalendarData meeting = null; - Collection notes = null; - PortfolioAccountData linkedAccount = null; - Collection disbursementData = null; - Collection emiAmountVariations = null; - Collection loanCollateralManagements; - Collection loanCollateralManagementData = new ArrayList<>(); - CollectionData collectionData = CollectionData.template(); - - final Set mandatoryResponseParameters = new HashSet<>(); - final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); - final Collection currentLoanRepayments = this.loanReadPlatformService.retrieveLoanTransactions(loanId); - if (!associationParameters.isEmpty()) { - if (associationParameters.contains(DataTableApiConstant.allAssociateParamName)) { - associationParameters.addAll(Arrays.asList(DataTableApiConstant.repaymentScheduleAssociateParamName, - DataTableApiConstant.futureScheduleAssociateParamName, DataTableApiConstant.originalScheduleAssociateParamName, - DataTableApiConstant.transactionsAssociateParamName, DataTableApiConstant.chargesAssociateParamName, - DataTableApiConstant.guarantorsAssociateParamName, DataTableApiConstant.collateralAssociateParamName, - DataTableApiConstant.notesAssociateParamName, DataTableApiConstant.linkedAccountAssociateParamName, - DataTableApiConstant.multiDisburseDetailsAssociateParamName, DataTableApiConstant.collectionAssociateParamName)); - } - - ApiParameterHelper.excludeAssociationsForResponseIfProvided(exclude, associationParameters); - - if (associationParameters.contains(DataTableApiConstant.guarantorsAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.guarantorsAssociateParamName); - guarantors = this.guarantorReadPlatformService.retrieveGuarantorsForLoan(loanId); - if (CollectionUtils.isEmpty(guarantors)) { - guarantors = null; - } - } - - if (associationParameters.contains(DataTableApiConstant.transactionsAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.transactionsAssociateParamName); - if (!CollectionUtils.isEmpty(currentLoanRepayments)) { - loanRepayments = currentLoanRepayments; - } - } - - if (associationParameters.contains(DataTableApiConstant.multiDisburseDetailsAssociateParamName) - || associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.multiDisburseDetailsAssociateParamName); - disbursementData = this.loanReadPlatformService.retrieveLoanDisbursementDetails(loanId); - } - - if (associationParameters.contains(DataTableApiConstant.emiAmountVariationsAssociateParamName) - || associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.emiAmountVariationsAssociateParamName); - emiAmountVariations = this.loanReadPlatformService.retrieveLoanTermVariations(loanId, - LoanTermVariationType.EMI_AMOUNT.getValue()); - } - - if (associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.repaymentScheduleAssociateParamName); - final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = loanBasicDetails.getTimeline() - .repaymentScheduleRelatedData(loanBasicDetails.getCurrency(), loanBasicDetails.getPrincipal(), - loanBasicDetails.getApprovedPrincipal(), loanBasicDetails.getInArrearsTolerance(), - loanBasicDetails.getFeeChargesAtDisbursementCharged()); - repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, repaymentScheduleRelatedData, - disbursementData, loanBasicDetails.isInterestRecalculationEnabled(), - loanBasicDetails.getSummary() != null ? loanBasicDetails.getSummary().getFeeChargesPaid() : BigDecimal.ZERO); - - if (associationParameters.contains(DataTableApiConstant.futureScheduleAssociateParamName) - && loanBasicDetails.isInterestRecalculationEnabled()) { - mandatoryResponseParameters.add(DataTableApiConstant.futureScheduleAssociateParamName); - this.calculationPlatformService.updateFutureSchedule(repaymentSchedule, loanId); - } - - if (associationParameters.contains(DataTableApiConstant.originalScheduleAssociateParamName) - && loanBasicDetails.isInterestRecalculationEnabled() - && LoanStatus.fromInt(loanBasicDetails.getStatus().getId().intValue()).isActive()) { - mandatoryResponseParameters.add(DataTableApiConstant.originalScheduleAssociateParamName); - LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService.retrieveRepaymentArchiveSchedule(loanId, - repaymentScheduleRelatedData, disbursementData); - loanBasicDetails = LoanAccountData.withOriginalSchedule(loanBasicDetails, loanScheduleData); - } - } - - if (associationParameters.contains(DataTableApiConstant.chargesAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.chargesAssociateParamName); - charges = this.loanChargeReadPlatformService.retrieveLoanCharges(loanId); - if (CollectionUtils.isEmpty(charges)) { - charges = null; - } - } - - if (associationParameters.contains(DataTableApiConstant.collateralAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.collateralAssociateParamName); - loanCollateralManagements = this.loanCollateralManagementReadPlatformService.getLoanCollateralResponseDataList(loanId); - for (LoanCollateralResponseData loanCollateralManagement : loanCollateralManagements) { - loanCollateralManagementData.add(loanCollateralManagement.toCommand()); - } - } - - if (associationParameters.contains(DataTableApiConstant.meetingAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.meetingAssociateParamName); - meeting = this.calendarReadPlatformService.retrieveLoanCalendar(loanId); - } - - if (associationParameters.contains(DataTableApiConstant.notesAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.notesAssociateParamName); - notes = this.noteReadPlatformService.retrieveNotesByResource(loanId, NoteType.LOAN.getValue()); - if (CollectionUtils.isEmpty(notes)) { - notes = null; - } - } - - if (associationParameters.contains(DataTableApiConstant.linkedAccountAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.linkedAccountAssociateParamName); - linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId); - } - - if (associationParameters.contains(DataTableApiConstant.collectionAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.collectionAssociateParamName); - if (loanBasicDetails.isActive()) { - collectionData = this.delinquencyReadPlatformService.calculateLoanCollectionData(loanId); - } - } - } - - Collection productOptions = null; - LoanProductData product; - Collection loanTermFrequencyTypeOptions = null; - Collection repaymentFrequencyTypeOptions = null; - Collection repaymentFrequencyNthDayTypeOptions = null; - Collection repaymentFrequencyDayOfWeekTypeOptions = null; - Collection repaymentStrategyOptions = null; - Collection interestRateFrequencyTypeOptions = null; - Collection amortizationTypeOptions = null; - Collection interestTypeOptions = null; - Collection interestCalculationPeriodTypeOptions = null; - Collection fundOptions = null; - Collection allowedLoanOfficers = null; - Collection chargeOptions = null; - ChargeData chargeTemplate = null; - Collection loanPurposeOptions = null; - Collection loanCollateralOptions = null; - Collection calendarOptions = null; - Collection accountLinkingOptions = null; - PaidInAdvanceData paidInAdvanceTemplate; - Collection clientActiveLoanOptions = null; - - final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); - if (template) { - productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup(); - product = this.loanProductReadPlatformService.retrieveLoanProduct(loanBasicDetails.getLoanProductId()); - loanBasicDetails.setProduct(product); - loanTermFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions(); - repaymentFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions(); - repaymentFrequencyNthDayTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyOptionsForNthDayOfMonth(); - repaymentFrequencyDayOfWeekTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyOptionsForDaysOfWeek(); - interestRateFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions(); - - amortizationTypeOptions = this.dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions(); - if (product.isLinkedToFloatingInterestRates()) { - interestTypeOptions = Collections.singletonList(interestType(InterestMethod.DECLINING_BALANCE)); - } else { - interestTypeOptions = this.dropdownReadPlatformService.retrieveLoanInterestTypeOptions(); - } - interestCalculationPeriodTypeOptions = this.dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions(); - - fundOptions = this.fundReadPlatformService.retrieveAllFunds(); - repaymentStrategyOptions = this.dropdownReadPlatformService.retrieveTransactionProcessingStrategies(); - if (product.getMultiDisburseLoan()) { - chargeOptions = this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(loanId, - new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT }); - } else { - chargeOptions = this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(loanId, - new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT, ChargeTimeType.TRANCHE_DISBURSEMENT }); - } - chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate(); - - Long officeId = loanBasicDetails.getClientOfficeId(); - - if (officeId == null && loanBasicDetails.getGroup() != null) { - officeId = loanBasicDetails.getGroup().getOfficeId(); - } - allowedLoanOfficers = this.loanReadPlatformService.retrieveAllowedLoanOfficers(officeId, staffInSelectedOfficeOnly); - - loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose"); - loanCollateralOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanCollateral"); - final CurrencyData currencyData = loanBasicDetails.getCurrency(); - String currencyCode = null; - if (currencyData != null) { - currencyCode = currencyData.getCode(); - } - final long[] accountStatus = { SavingsAccountStatusType.ACTIVE.getValue() }; - PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(PortfolioAccountType.SAVINGS.getValue(), - loanBasicDetails.getClientId(), currencyCode, accountStatus, DepositAccountType.SAVINGS_DEPOSIT.getValue()); - accountLinkingOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(portfolioAccountDTO); - - if (!associationParameters.contains(DataTableApiConstant.linkedAccountAssociateParamName)) { - mandatoryResponseParameters.add(DataTableApiConstant.linkedAccountAssociateParamName); - linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId); - } - if (loanBasicDetails.getGroup() != null && loanBasicDetails.getGroup().getId() != null) { - calendarOptions = this.loanReadPlatformService.retrieveCalendars(loanBasicDetails.getGroup().getId()); - } - - if (loanBasicDetails.getProduct().isCanUseForTopup() && loanBasicDetails.getClientId() != null) { - clientActiveLoanOptions = this.accountDetailsReadPlatformService - .retrieveClientActiveLoanAccountSummary(loanBasicDetails.getClientId()); - } - - } - - Collection overdueCharges = this.chargeReadPlatformService - .retrieveLoanProductCharges(loanBasicDetails.getLoanProductId(), ChargeTimeType.OVERDUE_INSTALLMENT); - - paidInAdvanceTemplate = this.loanReadPlatformService.retrieveTotalPaidInAdvance(loanId); - - // Get rates from Loan - boolean isRatesEnabled = this.configurationDomainService.isSubRatesEnabled(); - List rates = null; - if (isRatesEnabled) { - rates = this.rateReadService.retrieveLoanRates(loanId); - } - - // updating summary with transaction amounts summary - if (loanBasicDetails.getSummary() != null) { - loanBasicDetails - .setSummary(LoanSummaryData.withTransactionAmountsSummary(loanBasicDetails.getSummary(), currentLoanRepayments)); - } - - final LoanAccountData loanAccount = LoanAccountData.associationsAndTemplate(loanBasicDetails, repaymentSchedule, loanRepayments, - charges, loanCollateralManagementData, guarantors, meeting, productOptions, loanTermFrequencyTypeOptions, - repaymentFrequencyTypeOptions, repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDayOfWeekTypeOptions, - repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, - interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions, - loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations, - overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions, rates, isRatesEnabled, - collectionData); - - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(), - mandatoryResponseParameters); - return this.toApiJsonSerializer.serialize(settings, loanAccount, LOAN_DATA_PARAMETERS); + return retrieveLoan(loanId, null, staffInSelectedOfficeOnly, exclude, uriInfo); } @GET @@ -807,20 +523,10 @@ public class LoansApiResource { @RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PutLoansLoanIdRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PutLoansLoanIdResponse.class))) }) - public String modifyLoanApplication(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId, + public String modifyLoanApplication(@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId, @QueryParam("command") @Parameter(description = "command") final String commandParam, @Parameter(hidden = true) final String apiRequestBodyAsJson) { - - final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); - CommandWrapper commandRequest = null; - if (CommandParameterUtil.is(commandParam, LoanApiConstants.MARK_AS_FRAUD_COMMAND)) { - commandRequest = builder.markAsFraud(loanId).build(); - } else { - commandRequest = builder.updateLoanApplication(loanId).build(); - } - - final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - return this.toApiJsonSerializer.serialize(result); + return modifyLoanApplication(loanId, null, commandParam, apiRequestBodyAsJson); } @DELETE @@ -830,13 +536,8 @@ public class LoansApiResource { @Operation(summary = "Delete a Loan Application", description = "Note: Only loans in \"Submitted and awaiting approval\" status can be deleted.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.DeleteLoansLoanIdResponse.class))) }) - public String deleteLoanApplication(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId) { - - final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteLoanApplication(loanId).build(); - - final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - - return this.toApiJsonSerializer.serialize(result); + public String deleteLoanApplication(@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId) { + return deleteLoanApplication(loanId, null); } @POST @@ -858,44 +559,10 @@ public class LoansApiResource { @RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PostLoansLoanIdRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PostLoansLoanIdResponse.class))) }) - public String stateTransitions(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId, + public String stateTransitions(@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId, @QueryParam("command") @Parameter(description = "command") final String commandParam, @Parameter(hidden = true) final String apiRequestBodyAsJson) { - - final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); - CommandWrapper commandRequest = null; - if (CommandParameterUtil.is(commandParam, "reject")) { - commandRequest = builder.rejectLoanApplication(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "withdrawnByApplicant")) { - commandRequest = builder.withdrawLoanApplication(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "approve")) { - commandRequest = builder.approveLoanApplication(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "disburse")) { - commandRequest = builder.disburseLoanApplication(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "disburseToSavings")) { - commandRequest = builder.disburseLoanToSavingsApplication(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "undoapproval")) { - commandRequest = builder.undoLoanApplicationApproval(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "undodisbursal")) { - commandRequest = builder.undoLoanApplicationDisbursal(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "undolastdisbursal")) { - commandRequest = builder.undoLastDisbursalLoanApplication(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "assignloanofficer")) { - commandRequest = builder.assignLoanOfficer(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "unassignloanofficer")) { - commandRequest = builder.unassignLoanOfficer(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "recoverGuarantees")) { - commandRequest = new CommandWrapperBuilder().recoverFromGuarantor(loanId).build(); - } else if (CommandParameterUtil.is(commandParam, "assigndelinquency")) { - commandRequest = builder.assignDelinquency(loanId).build(); - } - - if (commandRequest == null) { - throw new UnrecognizedQueryParamException("command", commandParam); - } - CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - - return this.toApiJsonSerializer.serialize(result); + return stateTransitions(loanId, null, commandParam, apiRequestBodyAsJson); } @GET @@ -1001,12 +668,495 @@ public class LoansApiResource { @Operation(summary = "Retrieve the Loan Delinquency Tag history using the Loan Id", description = "") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyTagHistoryResponse.class)))) }) - public String getDelinquencyTagHistory(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId, + public String getDelinquencyTagHistory(@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId, @Context final UriInfo uriInfo) { + return getDelinquencyTagHistory(loanId, null, uriInfo); + } + + // External id related APIs + @GET + @Path("external-id/{loanExternalId}/template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.GetLoansApprovalTemplateResponse.class))) }) + public String retrieveApprovalTemplate( + @PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId, + @QueryParam("templateType") @Parameter(description = "templateType") final String templateType, + @Context final UriInfo uriInfo) { + return retrieveApprovalTemplate(null, loanExternalId, templateType, uriInfo); + } + + @GET + @Path("external-id/{loanExternalId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Retrieve a Loan", description = "Note: template=true parameter doesn't apply to this resource." + + "Example Requests:\n" + "\n" + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854\n" + "\n" + "\n" + + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854?fields=id,principal,annualInterestRate\n" + "\n" + "\n" + + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854?associations=all\n" + "\n" + + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854?associations=all&exclude=guarantors\n" + "\n" + "\n" + + "loans/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854?fields=id,principal,annualInterestRate&associations=repaymentSchedule,transactions") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.GetLoansLoanIdResponse.class))) }) + public String retrieveLoan( + @PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId, + @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly, + @DefaultValue("all") @QueryParam("associations") @Parameter(in = ParameterIn.QUERY, name = "associations", description = "Loan object relations to be included in the response", required = false, examples = { + @ExampleObject(value = "all"), @ExampleObject(value = "repaymentSchedule,transactions") }) final String associations, + @QueryParam("exclude") @Parameter(in = ParameterIn.QUERY, name = "exclude", description = "Optional Loan object relation list to be filtered in the response", required = false, example = "guarantors,futureSchedule") final String exclude, + @QueryParam("fields") @Parameter(in = ParameterIn.QUERY, name = "fields", description = "Optional Loan attribute list to be in the response", required = false, example = "id,principal,annualInterestRate") final String fields, + @Context final UriInfo uriInfo) { + return retrieveLoan(null, loanExternalId, staffInSelectedOfficeOnly, exclude, uriInfo); + } + + @PUT + @Path("external-id/{loanExternalId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Modify a loan application", description = "Loan application can only be modified when in 'Submitted and pending approval' state. Once the application is approved, the details cannot be changed using this method.") + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PutLoansLoanIdRequest.class))) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PutLoansLoanIdResponse.class))) }) + public String modifyLoanApplication( + @PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId, + @QueryParam("command") @Parameter(description = "command") final String commandParam, + @Parameter(hidden = true) final String apiRequestBodyAsJson) { + return modifyLoanApplication(null, loanExternalId, commandParam, apiRequestBodyAsJson); + } + + @DELETE + @Path("external-id/{loanExternalId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Delete a Loan Application", description = "Note: Only loans in \"Submitted and awaiting approval\" status can be deleted.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.DeleteLoansLoanIdResponse.class))) }) + public String deleteLoanApplication( + @PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId) { + return deleteLoanApplication(null, loanExternalId); + } + + @POST + @Path("external-id/{loanExternalId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Approve Loan Application | Recover Loan Guarantee | Undo Loan Application Approval | Assign a Loan Officer | Unassign a Loan Officer | Reject Loan Application | Applicant Withdraws from Loan Application | Disburse Loan Disburse Loan To Savings Account | Undo Loan Disbursal", description = "Approve Loan Application:\n" + + "Mandatory Fields: approvedOnDate\n" + "Optional Fields: approvedLoanAmount and expectedDisbursementDate\n" + + "Approves the loan application\n\n" + "Recover Loan Guarantee:\n" + "Recovers the loan guarantee\n\n" + + "Undo Loan Application Approval:\n" + "Undoes the Loan Application Approval\n\n" + "Assign a Loan Officer:\n" + + "Allows you to assign Loan Officer for existing Loan.\n\n" + "Unassign a Loan Officer:\n" + + "Allows you to unassign the Loan Officer.\n\n" + "Reject Loan Application:\n" + "Mandatory Fields: rejectedOnDate\n" + + "Allows you to reject the loan application\n\n" + "Applicant Withdraws from Loan Application:\n" + + "Mandatory Fields: withdrawnOnDate\n" + "Allows the applicant to withdraw the loan application\n\n" + "Disburse Loan:\n" + + "Mandatory Fields: actualDisbursementDate\n" + "Optional Fields: transactionAmount and fixedEmiAmount\n" + + "Disburses the Loan\n\n" + "Disburse Loan To Savings Account:\n" + "Mandatory Fields: actualDisbursementDate\n" + + "Optional Fields: transactionAmount and fixedEmiAmount\n" + "Disburses the loan to Saving Account\n\n" + + "Undo Loan Disbursal:\n" + "Undoes the Loan Disbursal\n" + "Showing request and response for Assign a Loan Officer") + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PostLoansLoanIdRequest.class))) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PostLoansLoanIdResponse.class))) }) + public String stateTransitions( + @PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId, + @QueryParam("command") @Parameter(description = "command") final String commandParam, + @Parameter(hidden = true) final String apiRequestBodyAsJson) { + return stateTransitions(null, loanExternalId, commandParam, apiRequestBodyAsJson); + } + + @GET + @Path("external-id/{loanExternalId}/delinquencytags") + @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON }) + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Retrieve the Loan Delinquency Tag history using the Loan Id", description = "") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyTagHistoryResponse.class)))) }) + public String getDelinquencyTagHistory( + @PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId, + @Context final UriInfo uriInfo) { + return getDelinquencyTagHistory(null, loanExternalId, uriInfo); + } + + private String retrieveApprovalTemplate(final Long loanId, final String loanExternalIdStr, final String templateType, + final UriInfo uriInfo) { + this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); + LoanApprovalData loanApprovalTemplate = null; + ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr); + Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId); + if (templateType == null) { + final String errorMsg = "Loan template type must be provided"; + throw new LoanTemplateTypeRequiredException(errorMsg); + } else if (templateType.equals("approval")) { + loanApprovalTemplate = this.loanReadPlatformService.retrieveApprovalTemplate(resolvedLoanId); + } + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.loanApprovalDataToApiJsonSerializer.serialize(settings, loanApprovalTemplate, LOAN_APPROVAL_DATA_PARAMETERS); + } + + private String retrieveLoan(final Long loanId, final String loanExternalIdStr, boolean staffInSelectedOfficeOnly, final String exclude, + final UriInfo uriInfo) { + this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); + ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr); + Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId); + LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveOne(resolvedLoanId); + if (loanBasicDetails.isInterestRecalculationEnabled()) { + Collection interestRecalculationCalendarDatas = this.calendarReadPlatformService.retrieveCalendarsByEntity( + loanBasicDetails.getInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue(), + null); + CalendarData calendarData = null; + if (!CollectionUtils.isEmpty(interestRecalculationCalendarDatas)) { + calendarData = interestRecalculationCalendarDatas.iterator().next(); + } + + Collection interestRecalculationCompoundingCalendarDatas = this.calendarReadPlatformService + .retrieveCalendarsByEntity(loanBasicDetails.getInterestRecalculationDetailId(), + CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue(), null); + CalendarData compoundingCalendarData = null; + if (!CollectionUtils.isEmpty(interestRecalculationCompoundingCalendarDatas)) { + compoundingCalendarData = interestRecalculationCompoundingCalendarDatas.iterator().next(); + } + loanBasicDetails = LoanAccountData.withInterestRecalculationCalendarData(loanBasicDetails, calendarData, + compoundingCalendarData); + } + if (loanBasicDetails.getRepaymentFrequencyType() != null + && loanBasicDetails.getRepaymentFrequencyType().getId().intValue() == PeriodFrequencyType.MONTHS.getValue()) { + Collection loanCalendarDatas = this.calendarReadPlatformService.retrieveCalendarsByEntity(resolvedLoanId, + CalendarEntityType.LOANS.getValue(), null); + CalendarData calendarData = null; + if (!CollectionUtils.isEmpty(loanCalendarDatas)) { + calendarData = loanCalendarDatas.iterator().next(); + } + if (calendarData != null) { + loanBasicDetails = LoanAccountData.withLoanCalendarData(loanBasicDetails, calendarData); + } + } + Collection interestRatesPeriods = this.loanReadPlatformService + .retrieveLoanInterestRatePeriodData(loanBasicDetails); + Collection loanRepayments = null; + LoanScheduleData repaymentSchedule = null; + Collection charges = null; + Collection guarantors = null; + CalendarData meeting = null; + Collection notes = null; + PortfolioAccountData linkedAccount = null; + Collection disbursementData = null; + Collection emiAmountVariations = null; + Collection loanCollateralManagements; + Collection loanCollateralManagementData = new ArrayList<>(); + CollectionData collectionData = CollectionData.template(); + + final Set mandatoryResponseParameters = new HashSet<>(); + final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); + final Collection currentLoanRepayments = this.loanReadPlatformService.retrieveLoanTransactions(resolvedLoanId); + if (!associationParameters.isEmpty()) { + if (associationParameters.contains(DataTableApiConstant.allAssociateParamName)) { + associationParameters.addAll(Arrays.asList(DataTableApiConstant.repaymentScheduleAssociateParamName, + DataTableApiConstant.futureScheduleAssociateParamName, DataTableApiConstant.originalScheduleAssociateParamName, + DataTableApiConstant.transactionsAssociateParamName, DataTableApiConstant.chargesAssociateParamName, + DataTableApiConstant.guarantorsAssociateParamName, DataTableApiConstant.collateralAssociateParamName, + DataTableApiConstant.notesAssociateParamName, DataTableApiConstant.linkedAccountAssociateParamName, + DataTableApiConstant.multiDisburseDetailsAssociateParamName, DataTableApiConstant.collectionAssociateParamName)); + } + + ApiParameterHelper.excludeAssociationsForResponseIfProvided(exclude, associationParameters); + + if (associationParameters.contains(DataTableApiConstant.guarantorsAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.guarantorsAssociateParamName); + guarantors = this.guarantorReadPlatformService.retrieveGuarantorsForLoan(resolvedLoanId); + if (CollectionUtils.isEmpty(guarantors)) { + guarantors = null; + } + } + + if (associationParameters.contains(DataTableApiConstant.transactionsAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.transactionsAssociateParamName); + if (!CollectionUtils.isEmpty(currentLoanRepayments)) { + loanRepayments = currentLoanRepayments; + } + } + + if (associationParameters.contains(DataTableApiConstant.multiDisburseDetailsAssociateParamName) + || associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.multiDisburseDetailsAssociateParamName); + disbursementData = this.loanReadPlatformService.retrieveLoanDisbursementDetails(resolvedLoanId); + } + + if (associationParameters.contains(DataTableApiConstant.emiAmountVariationsAssociateParamName) + || associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.emiAmountVariationsAssociateParamName); + emiAmountVariations = this.loanReadPlatformService.retrieveLoanTermVariations(resolvedLoanId, + LoanTermVariationType.EMI_AMOUNT.getValue()); + } + + if (associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.repaymentScheduleAssociateParamName); + final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = loanBasicDetails.getTimeline() + .repaymentScheduleRelatedData(loanBasicDetails.getCurrency(), loanBasicDetails.getPrincipal(), + loanBasicDetails.getApprovedPrincipal(), loanBasicDetails.getInArrearsTolerance(), + loanBasicDetails.getFeeChargesAtDisbursementCharged()); + repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(resolvedLoanId, repaymentScheduleRelatedData, + disbursementData, loanBasicDetails.isInterestRecalculationEnabled(), + loanBasicDetails.getSummary() != null ? loanBasicDetails.getSummary().getFeeChargesPaid() : BigDecimal.ZERO); + + if (associationParameters.contains(DataTableApiConstant.futureScheduleAssociateParamName) + && loanBasicDetails.isInterestRecalculationEnabled()) { + mandatoryResponseParameters.add(DataTableApiConstant.futureScheduleAssociateParamName); + this.calculationPlatformService.updateFutureSchedule(repaymentSchedule, resolvedLoanId); + } + + if (associationParameters.contains(DataTableApiConstant.originalScheduleAssociateParamName) + && loanBasicDetails.isInterestRecalculationEnabled() + && LoanStatus.fromInt(loanBasicDetails.getStatus().getId().intValue()).isActive()) { + mandatoryResponseParameters.add(DataTableApiConstant.originalScheduleAssociateParamName); + LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService + .retrieveRepaymentArchiveSchedule(resolvedLoanId, repaymentScheduleRelatedData, disbursementData); + loanBasicDetails = LoanAccountData.withOriginalSchedule(loanBasicDetails, loanScheduleData); + } + } + + if (associationParameters.contains(DataTableApiConstant.chargesAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.chargesAssociateParamName); + charges = this.loanChargeReadPlatformService.retrieveLoanCharges(resolvedLoanId); + if (CollectionUtils.isEmpty(charges)) { + charges = null; + } + } + + if (associationParameters.contains(DataTableApiConstant.collateralAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.collateralAssociateParamName); + loanCollateralManagements = this.loanCollateralManagementReadPlatformService + .getLoanCollateralResponseDataList(resolvedLoanId); + for (LoanCollateralResponseData loanCollateralManagement : loanCollateralManagements) { + loanCollateralManagementData.add(loanCollateralManagement.toCommand()); + } + } + + if (associationParameters.contains(DataTableApiConstant.meetingAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.meetingAssociateParamName); + meeting = this.calendarReadPlatformService.retrieveLoanCalendar(resolvedLoanId); + } + + if (associationParameters.contains(DataTableApiConstant.notesAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.notesAssociateParamName); + notes = this.noteReadPlatformService.retrieveNotesByResource(resolvedLoanId, NoteType.LOAN.getValue()); + if (CollectionUtils.isEmpty(notes)) { + notes = null; + } + } + + if (associationParameters.contains(DataTableApiConstant.linkedAccountAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.linkedAccountAssociateParamName); + linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(resolvedLoanId); + } + + if (associationParameters.contains(DataTableApiConstant.collectionAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.collectionAssociateParamName); + if (loanBasicDetails.isActive()) { + collectionData = this.delinquencyReadPlatformService.calculateLoanCollectionData(resolvedLoanId); + } + } + } + + Collection productOptions = null; + LoanProductData product; + Collection loanTermFrequencyTypeOptions = null; + Collection repaymentFrequencyTypeOptions = null; + Collection repaymentFrequencyNthDayTypeOptions = null; + Collection repaymentFrequencyDayOfWeekTypeOptions = null; + Collection repaymentStrategyOptions = null; + Collection interestRateFrequencyTypeOptions = null; + Collection amortizationTypeOptions = null; + Collection interestTypeOptions = null; + Collection interestCalculationPeriodTypeOptions = null; + Collection fundOptions = null; + Collection allowedLoanOfficers = null; + Collection chargeOptions = null; + ChargeData chargeTemplate = null; + Collection loanPurposeOptions = null; + Collection loanCollateralOptions = null; + Collection calendarOptions = null; + Collection accountLinkingOptions = null; + PaidInAdvanceData paidInAdvanceTemplate; + Collection clientActiveLoanOptions = null; + + final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); + if (template) { + productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup(); + product = this.loanProductReadPlatformService.retrieveLoanProduct(loanBasicDetails.getLoanProductId()); + loanBasicDetails.setProduct(product); + loanTermFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions(); + repaymentFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions(); + repaymentFrequencyNthDayTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyOptionsForNthDayOfMonth(); + repaymentFrequencyDayOfWeekTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyOptionsForDaysOfWeek(); + interestRateFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions(); + + amortizationTypeOptions = this.dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions(); + if (product.isLinkedToFloatingInterestRates()) { + interestTypeOptions = Collections.singletonList(interestType(InterestMethod.DECLINING_BALANCE)); + } else { + interestTypeOptions = this.dropdownReadPlatformService.retrieveLoanInterestTypeOptions(); + } + interestCalculationPeriodTypeOptions = this.dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions(); + + fundOptions = this.fundReadPlatformService.retrieveAllFunds(); + repaymentStrategyOptions = this.dropdownReadPlatformService.retrieveTransactionProcessingStrategies(); + if (product.getMultiDisburseLoan()) { + chargeOptions = this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(resolvedLoanId, + new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT }); + } else { + chargeOptions = this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(resolvedLoanId, + new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT, ChargeTimeType.TRANCHE_DISBURSEMENT }); + } + chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate(); + + Long officeId = loanBasicDetails.getClientOfficeId(); + + if (officeId == null && loanBasicDetails.getGroup() != null) { + officeId = loanBasicDetails.getGroup().getOfficeId(); + } + allowedLoanOfficers = this.loanReadPlatformService.retrieveAllowedLoanOfficers(officeId, staffInSelectedOfficeOnly); + + loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose"); + loanCollateralOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanCollateral"); + final CurrencyData currencyData = loanBasicDetails.getCurrency(); + String currencyCode = null; + if (currencyData != null) { + currencyCode = currencyData.getCode(); + } + final long[] accountStatus = { SavingsAccountStatusType.ACTIVE.getValue() }; + PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(PortfolioAccountType.SAVINGS.getValue(), + loanBasicDetails.getClientId(), currencyCode, accountStatus, DepositAccountType.SAVINGS_DEPOSIT.getValue()); + accountLinkingOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(portfolioAccountDTO); + + if (!associationParameters.contains(DataTableApiConstant.linkedAccountAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.linkedAccountAssociateParamName); + linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(resolvedLoanId); + } + if (loanBasicDetails.getGroup() != null && loanBasicDetails.getGroup().getId() != null) { + calendarOptions = this.loanReadPlatformService.retrieveCalendars(loanBasicDetails.getGroup().getId()); + } + + if (loanBasicDetails.getProduct().isCanUseForTopup() && loanBasicDetails.getClientId() != null) { + clientActiveLoanOptions = this.accountDetailsReadPlatformService + .retrieveClientActiveLoanAccountSummary(loanBasicDetails.getClientId()); + } + + } + + Collection overdueCharges = this.chargeReadPlatformService + .retrieveLoanProductCharges(loanBasicDetails.getLoanProductId(), ChargeTimeType.OVERDUE_INSTALLMENT); + + paidInAdvanceTemplate = this.loanReadPlatformService.retrieveTotalPaidInAdvance(resolvedLoanId); + + // Get rates from Loan + boolean isRatesEnabled = this.configurationDomainService.isSubRatesEnabled(); + List rates = null; + if (isRatesEnabled) { + rates = this.rateReadService.retrieveLoanRates(resolvedLoanId); + } + + // updating summary with transaction amounts summary + if (loanBasicDetails.getSummary() != null) { + loanBasicDetails + .setSummary(LoanSummaryData.withTransactionAmountsSummary(loanBasicDetails.getSummary(), currentLoanRepayments)); + } + + final LoanAccountData loanAccount = LoanAccountData.associationsAndTemplate(loanBasicDetails, repaymentSchedule, loanRepayments, + charges, loanCollateralManagementData, guarantors, meeting, productOptions, loanTermFrequencyTypeOptions, + repaymentFrequencyTypeOptions, repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDayOfWeekTypeOptions, + repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, + interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions, + loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations, + overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions, rates, isRatesEnabled, + collectionData); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(), + mandatoryResponseParameters); + return this.toApiJsonSerializer.serialize(settings, loanAccount, LOAN_DATA_PARAMETERS); + } + + private String modifyLoanApplication(final Long loanId, final String loanExternalIdStr, final String commandParam, + final String apiRequestBodyAsJson) { + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); + CommandWrapper commandRequest; + ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr); + Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId); + if (CommandParameterUtil.is(commandParam, LoanApiConstants.MARK_AS_FRAUD_COMMAND)) { + commandRequest = builder.markAsFraud(resolvedLoanId).build(); + } else { + commandRequest = builder.updateLoanApplication(resolvedLoanId).build(); + } + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); + } + + private String deleteLoanApplication(final Long loanId, final String loanExternalIdStr) { + ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr); + Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId); + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteLoanApplication(resolvedLoanId).build(); + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); + } + + private String stateTransitions(final Long loanId, final String loanExternalIdStr, final String commandParam, + final String apiRequestBodyAsJson) { + ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr); + Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId); + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); + CommandWrapper commandRequest = null; + if (CommandParameterUtil.is(commandParam, "reject")) { + commandRequest = builder.rejectLoanApplication(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "withdrawnByApplicant")) { + commandRequest = builder.withdrawLoanApplication(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "approve")) { + commandRequest = builder.approveLoanApplication(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "disburse")) { + commandRequest = builder.disburseLoanApplication(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "disburseToSavings")) { + commandRequest = builder.disburseLoanToSavingsApplication(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "undoapproval")) { + commandRequest = builder.undoLoanApplicationApproval(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "undodisbursal")) { + commandRequest = builder.undoLoanApplicationDisbursal(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "undolastdisbursal")) { + commandRequest = builder.undoLastDisbursalLoanApplication(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "assignloanofficer")) { + commandRequest = builder.assignLoanOfficer(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "unassignloanofficer")) { + commandRequest = builder.unassignLoanOfficer(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "recoverGuarantees")) { + commandRequest = new CommandWrapperBuilder().recoverFromGuarantor(resolvedLoanId).build(); + } else if (CommandParameterUtil.is(commandParam, "assigndelinquency")) { + commandRequest = builder.assignDelinquency(resolvedLoanId).build(); + } + + if (commandRequest == null) { + throw new UnrecognizedQueryParamException("command", commandParam); + } + CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + private String getDelinquencyTagHistory(final Long loanId, final String loanExternalIdStr, final UriInfo uriInfo) { context.authenticatedUser().validateHasReadPermission("DELINQUENCY_TAGS"); + ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr); + Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId); final Collection loanDelinquencyTagHistoryData = this.delinquencyReadPlatformService - .retrieveDelinquencyRangeHistory(loanId); + .retrieveDelinquencyRangeHistory(resolvedLoanId); return this.jsonSerializerTagHistory.serialize(loanDelinquencyTagHistoryData); } + private Long getResolvedLoanId(final Long loanId, final ExternalId loanExternalId) { + Long resolvedLoanId = loanId; + if (resolvedLoanId == null) { + loanExternalId.throwExceptionIfEmpty(); + resolvedLoanId = this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId); + if (resolvedLoanId == null) { + throw new LoanNotFoundException(loanExternalId); + } + } + return resolvedLoanId; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java index 0e25608b9..da2b2801a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java @@ -32,6 +32,19 @@ final class LoansApiResourceSwagger { private LoansApiResourceSwagger() {} + @Schema(description = "GetLoansApprovalTemplateResponse") + public static final class GetLoansApprovalTemplateResponse { + + private GetLoansApprovalTemplateResponse() {} + + @Schema(example = "[2012, 4, 3]") + public LocalDate approvalDate; + @Schema(example = "200.000000") + public Double approvalAmount; + @Schema(example = "200.000000") + public Double netDisbursalAmount; + } + @Schema(description = "GetLoansTemplateResponse") public static final class GetLoansTemplateResponse { @@ -935,6 +948,8 @@ final class LoansApiResourceSwagger { @Schema(example = "1") public Long id; + @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7") + public String externalId; @Schema(example = "000000001") public Long accountNo; public GetLoansLoanIdStatus status; @@ -1302,6 +1317,8 @@ final class LoansApiResourceSwagger { public Integer loanId; @Schema(example = "1") public Integer resourceId; + @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7") + public String resourceExternalId; public PutLoansLoanIdChanges changes; } @@ -1318,6 +1335,8 @@ final class LoansApiResourceSwagger { public Integer loanId; @Schema(example = "1") public Integer resourceId; + @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7") + public String resourceExternalId; } @Schema(description = "PostLoansLoanIdRequest") @@ -1339,6 +1358,9 @@ final class LoansApiResourceSwagger { public Integer toLoanOfficerId; @Schema(example = "02 September 2014") public String assignmentDate; + + @Schema(example = "02 September 2014") + public String unassignedDate; @Schema(example = "en") public String locale; @Schema(example = "dd MMMM yyyy") @@ -1361,6 +1383,10 @@ final class LoansApiResourceSwagger { public String approvedLoanAmount; @Schema(example = "28 June 2022") public String expectedDisbursementDate; + @Schema(example = "28 June 2022") + public String rejectedOnDate; + @Schema(example = "28 June 2022") + public String withdrawnOnDate; @Schema(description = "List of PostLoansLoanIdDisbursementData") public List disbursementData; } @@ -1422,6 +1448,8 @@ final class LoansApiResourceSwagger { public Integer loanId; @Schema(example = "3") public Integer resourceId; + @Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7") + public String resourceExternalId; @Schema(description = "PostLoansLoanIdChanges") public PostLoansLoanIdChanges changes; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java index 4ddca73c0..c2cf973e3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java @@ -33,6 +33,7 @@ import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.staff.data.StaffData; @@ -65,7 +66,7 @@ public class LoanAccountData { // identity private Long id; private String accountNo; - private String externalId; + private ExternalId externalId = ExternalId.empty(); // status private LoanStatusEnumData status; @@ -260,7 +261,7 @@ public class LoanAccountData { EnumOptionData amortizationEnumOption, EnumOptionData interestMethodEnum, EnumOptionData interestCalculationPeriodTypeEnum, BigDecimal inArrearsTolerance, String transactionProcessingStrategyCode, Integer graceOnPrincipalPayment, Integer graceOnInterestPayment, Integer graceOnInterestCharged, LocalDate interestChargedFromDate, - LocalDate repaymentsStartingFromDate, Integer rowIndex, String externalId, Long groupId, Collection charges, + LocalDate repaymentsStartingFromDate, Integer rowIndex, ExternalId externalId, Long groupId, Collection charges, String linkAccountId, String locale, String dateFormat, List loanCollateralManagementData) { return new LoanAccountData().setLoanType(loanTypeEnumOption).setClientId(clientId).setProductId(productId) @@ -285,7 +286,7 @@ public class LoanAccountData { EnumOptionData interestMethodEnum, EnumOptionData interestCalculationPeriodEnum, BigDecimal arrearsTolerance, String transactionProcessingStrategyCode, Integer graceOnPrincipalPayment, Integer graceOnInterestPayment, Integer graceOnInterestCharged, LocalDate interestChargedFromDate, LocalDate repaymentsStartingFromDate, Integer rowIndex, - String externalId, String linkAccountId, String locale, String dateFormat) { + ExternalId externalId, String linkAccountId, String locale, String dateFormat) { return new LoanAccountData().setLoanType(loanTypeEnumOption).setGroupId(groupIdforGroupLoan).setProductId(productId) .setLoanOfficerId(loanOfficerId).setSubmittedOnDate(submittedOnDate).setFundId(fundId).setPrincipal(principal) @@ -626,22 +627,23 @@ public class LoanAccountData { * Used to send back loan account data with the basic details coming from query. */ public static LoanAccountData basicLoanDetails(final Long id, final String accountNo, final LoanStatusEnumData status, - final String externalId, final Long clientId, final String clientAccountNo, final String clientName, final Long clientOfficeId, - final String clientExternalId, final GroupGeneralData group, final EnumOptionData loanType, final Long loanProductId, - final String loanProductName, final String loanProductDescription, final boolean isLoanProductLinkedToFloatingRate, - final Long fundId, final String fundName, final Long loanPurposeId, final String loanPurposeName, final Long loanOfficerId, - final String loanOfficerName, final CurrencyData currencyData, final BigDecimal proposedPrincipal, final BigDecimal principal, - final BigDecimal approvedPrincipal, final BigDecimal netDisbursalAmount, final BigDecimal totalOverpaid, - final BigDecimal inArrearsTolerance, final Integer termFrequency, final EnumOptionData termPeriodFrequencyType, - final Integer numberOfRepayments, final Integer repaymentEvery, final EnumOptionData repaymentFrequencyType, - EnumOptionData repaymentFrequencyNthDayType, EnumOptionData repaymentFrequencyDayOfWeekType, final String transactionStrategy, - final String transactionStrategyName, final EnumOptionData amortizationType, final BigDecimal interestRatePerPeriod, - final EnumOptionData interestRateFrequencyType, final BigDecimal annualInterestRate, final EnumOptionData interestType, - final boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, - final EnumOptionData interestCalculationPeriodType, Boolean allowPartialPeriodInterestCalcualtion, - final LocalDate expectedFirstRepaymentOnDate, final Integer graceOnPrincipalPayment, - final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, - final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline, final LoanSummaryData loanSummary, + final ExternalId externalId, final Long clientId, final String clientAccountNo, final String clientName, + final Long clientOfficeId, final String clientExternalId, final GroupGeneralData group, final EnumOptionData loanType, + final Long loanProductId, final String loanProductName, final String loanProductDescription, + final boolean isLoanProductLinkedToFloatingRate, final Long fundId, final String fundName, final Long loanPurposeId, + final String loanPurposeName, final Long loanOfficerId, final String loanOfficerName, final CurrencyData currencyData, + final BigDecimal proposedPrincipal, final BigDecimal principal, final BigDecimal approvedPrincipal, + final BigDecimal netDisbursalAmount, final BigDecimal totalOverpaid, final BigDecimal inArrearsTolerance, + final Integer termFrequency, final EnumOptionData termPeriodFrequencyType, final Integer numberOfRepayments, + final Integer repaymentEvery, final EnumOptionData repaymentFrequencyType, EnumOptionData repaymentFrequencyNthDayType, + EnumOptionData repaymentFrequencyDayOfWeekType, final String transactionStrategy, final String transactionStrategyName, + final EnumOptionData amortizationType, final BigDecimal interestRatePerPeriod, final EnumOptionData interestRateFrequencyType, + final BigDecimal annualInterestRate, final EnumOptionData interestType, final boolean isFloatingInterestRate, + final BigDecimal interestRateDifferential, final EnumOptionData interestCalculationPeriodType, + Boolean allowPartialPeriodInterestCalcualtion, final LocalDate expectedFirstRepaymentOnDate, + final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, + final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, + final LoanApplicationTimelineData timeline, final LoanSummaryData loanSummary, final BigDecimal feeChargesDueAtDisbursementCharged, final Boolean syncDisbursementWithMeeting, final Integer loanCounter, final Integer loanProductCounter, final Boolean multiDisburseLoan, Boolean canDefineInstallmentAmount, final BigDecimal fixedEmiAmont, final BigDecimal outstandingLoanBalance, final Boolean inArrears, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java index d4d876ad1..b18e12d09 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java @@ -38,7 +38,7 @@ public class LoanTransactionData { private final Long id; private final Long loanId; - private final String externalLoanId; + private final ExternalId externalLoanId; private final Long officeId; private final String officeName; @@ -115,7 +115,7 @@ public class LoanTransactionData { this.type = null; this.id = null; this.loanId = null; - this.externalLoanId = null; + this.externalLoanId = ExternalId.empty(); this.officeId = null; this.officeName = null; this.currency = null; @@ -163,7 +163,7 @@ public class LoanTransactionData { this.locale = locale; this.id = null; this.loanId = null; - this.externalLoanId = null; + this.externalLoanId = ExternalId.empty(); this.officeId = null; this.officeName = null; this.type = null; @@ -216,7 +216,7 @@ public class LoanTransactionData { final BigDecimal netDisbursalAmount, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, final ExternalId externalId, final AccountTransferData transfer, BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance, - final BigDecimal unrecognizedIncomePortion, final boolean manuallyReversed, Long loanId, String externalLoanId) { + final BigDecimal unrecognizedIncomePortion, final boolean manuallyReversed, Long loanId, ExternalId externalLoanId) { this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, netDisbursalAmount, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, null, externalId, transfer, fixedEmiAmount, outstandingLoanBalance, manuallyReversed, loanId, externalLoanId); @@ -228,7 +228,7 @@ public class LoanTransactionData { final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, BigDecimal unrecognizedIncomePortion, final Collection paymentTypeOptions, final ExternalId externalId, final AccountTransferData transfer, final BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance, - boolean manuallyReversed, Long loanId, String externalLoanId) { + boolean manuallyReversed, Long loanId, ExternalId externalLoanId) { this(id, externalLoanId, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, netDisbursalAmount, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, paymentTypeOptions, externalId, transfer, fixedEmiAmount, outstandingLoanBalance, null, manuallyReversed, @@ -241,14 +241,14 @@ public class LoanTransactionData { final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, final BigDecimal unrecognizedIncomePortion, final ExternalId externalId, final AccountTransferData transfer, BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance, LocalDate submittedOnDate, final boolean manuallyReversed, - final ExternalId reversalExternalId, final LocalDate reversedOnDate, Long loanId, String externalLoanId) { + final ExternalId reversalExternalId, final LocalDate reversedOnDate, Long loanId, ExternalId externalLoanId) { this(id, externalLoanId, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, netDisbursalAmount, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, null, externalId, transfer, fixedEmiAmount, outstandingLoanBalance, submittedOnDate, manuallyReversed, reversalExternalId, reversedOnDate, loanId); } - public LoanTransactionData(final Long id, final String externalLoanId, final Long officeId, final String officeName, + public LoanTransactionData(final Long id, final ExternalId externalLoanId, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal netDisbursalAmount, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, @@ -288,7 +288,8 @@ public class LoanTransactionData { public LoanTransactionData(Long id, LoanTransactionEnumData transactionType, LocalDate date, BigDecimal totalAmount, BigDecimal netDisbursalAmount, BigDecimal principalPortion, BigDecimal interestPortion, BigDecimal feeChargesPortion, BigDecimal penaltyChargesPortion, BigDecimal overpaymentPortion, BigDecimal unrecognizedIncomePortion, - BigDecimal outstandingLoanBalance, final boolean manuallyReversed, ExternalId externalId, Long loanId, String externalLoanId) { + BigDecimal outstandingLoanBalance, final boolean manuallyReversed, ExternalId externalId, Long loanId, + ExternalId externalLoanId) { this(id, externalLoanId, null, null, transactionType, null, null, date, totalAmount, netDisbursalAmount, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, null, externalId, null, null, outstandingLoanBalance, null, manuallyReversed, ExternalId.empty(), null, loanId); @@ -300,7 +301,7 @@ public class LoanTransactionData { final LocalDate possibleNextRepaymentDate) { final Long id = null; final Long loanId = null; - final String externalLoanId = null; + final ExternalId externalLoanId = ExternalId.empty(); final Long officeId = null; final String officeName = null; final PaymentDetailData paymentDetailData = null; @@ -331,7 +332,7 @@ public class LoanTransactionData { BigDecimal unrecognizedIncomePortion, Collection paymentOptions, final AccountTransferData transfer, final ExternalId externalId, final BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance, final LocalDate submittedOnDate, final boolean manuallyReversed, final LocalDate possibleNextRepaymentDate, Long loanId, - String externalLoanId) { + ExternalId externalLoanId) { this.id = id; this.loanId = loanId; this.externalLoanId = externalLoanId; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index e6b3f10ad..8f3937e3b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -69,6 +69,7 @@ import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper; import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.security.service.RandomPasswordGenerator; import org.apache.fineract.organisation.holiday.domain.Holiday; import org.apache.fineract.organisation.holiday.service.HolidayUtil; @@ -194,7 +195,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { private String accountNumber; @Column(name = "external_id") - private String externalId; + private ExternalId externalId; @ManyToOne @JoinColumn(name = "client_id", nullable = true) @@ -1440,10 +1441,14 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { this.createStandingInstructionAtDisbursement = valueAsInput; } - if (command.isChangeInStringParameterNamed(EXTERNAL_ID, this.externalId)) { + if (command.isChangeInStringParameterNamed(EXTERNAL_ID, this.externalId.getValue())) { final String newValue = command.stringValueOfParameterNamed(EXTERNAL_ID); - actualChanges.put(EXTERNAL_ID, newValue); - this.externalId = StringUtils.defaultIfEmpty(newValue, null); + ExternalId externalId = ExternalIdFactory.produce(newValue); + if (externalId.isEmpty() && TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) { + externalId = ExternalId.generate(); + } + actualChanges.put(EXTERNAL_ID, externalId); + this.externalId = externalId; } // add clientId, groupId and loanType changes to actual changes @@ -2055,7 +2060,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { } public void loanApplicationSubmittal(final LoanScheduleModel loanSchedule, final LoanApplicationTerms loanApplicationTerms, - final LoanLifecycleStateMachine lifecycleStateMachine, final LocalDate submittedOn, final String externalId, + final LoanLifecycleStateMachine lifecycleStateMachine, final LocalDate submittedOn, final ExternalId externalId, final boolean allowTransactionsOnHoliday, final List holidays, final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) { @@ -3793,7 +3798,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE); final String txnExternalId = command.stringValueOfParameterNamedAllowingNull(EXTERNAL_ID); - ExternalId externalId = StringUtils.isBlank(txnExternalId) ? ExternalId.empty() : new ExternalId(txnExternalId); + ExternalId externalId = ExternalIdFactory.produce(txnExternalId); if (externalId.isEmpty() && TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) { externalId = ExternalId.generate(); @@ -3887,7 +3892,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final LocalDate closureDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE); final String txnExternalId = command.stringValueOfParameterNamedAllowingNull(EXTERNAL_ID); - ExternalId externalId = StringUtils.isBlank(txnExternalId) ? ExternalId.empty() : new ExternalId(txnExternalId); + ExternalId externalId = ExternalIdFactory.produce(txnExternalId); if (externalId.isEmpty() && TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) { externalId = ExternalId.generate(); } @@ -6033,7 +6038,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return this.accountNumber; } - public String getExternalId() { + public ExternalId getExternalId() { return this.externalId; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java index 04b473f1c..d41939658 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java @@ -83,7 +83,8 @@ public interface LoanAccountDomainService { void saveLoanWithDataIntegrityViolationChecks(Loan loan); - Map foreCloseLoan(Loan loan, LocalDate foreClourseDate, String noteText, ExternalId externalId); + LoanTransaction foreCloseLoan(Loan loan, LocalDate foreClourseDate, String noteText, ExternalId externalId, + Map changes); /** * Disables all standing instructions linked to a closed loan diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index 0846bbcaa..6db221125 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -22,7 +22,6 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -708,11 +707,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } @Override - public Map foreCloseLoan(final Loan loan, final LocalDate foreClosureDate, final String noteText, - final ExternalId externalId) { + public LoanTransaction foreCloseLoan(final Loan loan, final LocalDate foreClosureDate, final String noteText, + final ExternalId externalId, Map changes) { businessEventNotifierService.notifyPreBusinessEvent(new LoanForeClosurePreBusinessEvent(loan)); MonetaryCurrency currency = loan.getCurrency(); - final Map changes = new LinkedHashMap<>(); List newTransactions = new ArrayList<>(); final List existingTransactionIds = new ArrayList<>(); @@ -808,8 +806,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanForeClosurePostBusinessEvent(payment)); - return changes; - + return payment; } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java index 6e3a1e3ed..9714de9aa 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java @@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain; import java.time.LocalDate; import java.util.Collection; import java.util.List; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; @@ -167,13 +168,13 @@ public interface LoanRepository extends JpaRepository, JpaSpecificat @Query(FIND_BY_ACCOUNT_NUMBER) Loan findLoanAccountByAccountNumber(@Param("accountNumber") String accountNumber); - boolean existsByExternalId(@Param("externalId") String externalId); + boolean existsByExternalId(@Param("externalId") ExternalId externalId); @Query(FIND_ALL_NON_CLOSED) List findAllNonClosedLoanIds(); @Query(FIND_ID_BY_EXTERNAL_ID) - Long findIdByExternalId(@Param("externalId") String externalId); + Long findIdByExternalId(@Param("externalId") ExternalId externalId); @Query(FIND_ALL_NON_CLOSED_ONE_DAY_BEHIND) List findAllNonClosedLoanIdsOneDayBehind(@Param("last_closed_business_date") LocalDate businessDate); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java index a33762dac..f204786f2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.query.Param; @@ -238,7 +239,7 @@ public class LoanRepositoryWrapper { return this.repository.findNonClosedLoanByAccountNumber(accountNumber); } - public boolean existLoanByExternalId(final String externalId) { + public boolean existLoanByExternalId(final ExternalId externalId) { return this.repository.existsByExternalId(externalId); } @@ -252,7 +253,7 @@ public class LoanRepositoryWrapper { return loan; } - public Long findIdByExternalId(String externalId) { + public Long findIdByExternalId(ExternalId externalId) { return this.repository.findIdByExternalId(externalId); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index 8bb8f7396..a2cb19f3f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -48,10 +48,12 @@ import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException; 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.ExternalIdFactory; import org.apache.fineract.infrastructure.dataqueries.data.EntityTables; import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum; import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService; @@ -217,8 +219,9 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa this.fromApiJsonDeserializer.validateForCreate(command.json(), isMeetingMandatoryForJLGLoans, loanProduct); // Validate If the externalId is already registered - final String externalId = this.fromJsonHelper.extractStringNamed("externalId", command.parsedJson()); - if (StringUtils.isNotBlank(externalId)) { + final String externalIdStr = this.fromJsonHelper.extractStringNamed("externalId", command.parsedJson()); + ExternalId externalId = ExternalIdFactory.produce(externalIdStr); + if (!externalId.isEmpty()) { final boolean existByExternalId = this.loanRepositoryWrapper.existLoanByExternalId(externalId); if (existByExternalId) { throw new GeneralPlatformDomainRuleException("error.msg.loan.with.externalId.already.used", @@ -518,6 +521,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(newLoanApplication.getId()) // + .withEntityExternalId(newLoanApplication.getExternalId()) // .withOfficeId(newLoanApplication.getOfficeId()) // .withClientId(newLoanApplication.getClientId()) // .withGroupId(newLoanApplication.getGroupId()) // @@ -1165,6 +1169,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa return new CommandProcessingResultBuilder() // .withEntityId(loanId) // + .withEntityExternalId(existingLoanApplication.getExternalId()) // .withOfficeId(existingLoanApplication.getOfficeId()) // .withClientId(existingLoanApplication.getClientId()) // .withGroupId(existingLoanApplication.getGroupId()) // @@ -1239,6 +1244,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa return new CommandProcessingResultBuilder() // .withEntityId(loanId) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -1408,6 +1414,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loan.getId()) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -1486,6 +1493,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loan.getId()) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -1554,6 +1562,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loan.getId()) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -1605,6 +1614,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loan.getId()) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java index 1e6b3c6a2..e73883e0e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssembler.java @@ -33,7 +33,9 @@ import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrappe import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.holiday.domain.Holiday; import org.apache.fineract.organisation.holiday.domain.HolidayRepository; import org.apache.fineract.organisation.holiday.domain.HolidayStatusType; @@ -107,6 +109,7 @@ public class LoanAssembler { private final LoanUtilService loanUtilService; private final RateAssembler rateAssembler; private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine; + private final ExternalIdFactory externalIdFactory; public Loan assembleFrom(final Long accountId) { final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId, true); @@ -287,7 +290,8 @@ public class LoanAssembler { loanApplication.updateTransactionProcessingStrategy(transactionProcessingStrategyCode, transactionProcessingStrategyName); } - final String externalId = this.fromApiJsonHelper.extractStringNamed("externalId", element); + final String externalIdStr = this.fromApiJsonHelper.extractStringNamed("externalId", element); + ExternalId externalId = externalIdFactory.create(externalIdStr); final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed("submittedOnDate", element); if (loanApplication == null) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java index 745d76901..18b38e0a6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java @@ -631,7 +631,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo .withCommandId(command.commandId()) // .withEntityId(loanChargeId) // .withEntityExternalId(loanCharge.getExternalId()) // - .withSubEntityId(loanTransaction.getId()).withSubEntityExternalId(loanTransaction.getExternalId()) + .withSubEntityId(loanTransaction.getId()) // + .withSubEntityExternalId(loanTransaction.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java index 73c7bfd4f..4b4d9ce5e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java @@ -158,5 +158,5 @@ public interface LoanReadPlatformService { Long retrieveLoanTransactionIdByExternalId(ExternalId externalId); - Long retrieveLoanIdByExternalId(String externalId); + Long retrieveLoanIdByExternalId(ExternalId externalId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 530274e5c..db9333d41 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -696,7 +696,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Long id = rs.getLong("id"); final String accountNo = rs.getString("accountNo"); - final String externalId = rs.getString("externalId"); + final String externalIdStr = rs.getString("externalId"); + final ExternalId externalId = ExternalIdFactory.produce(externalIdStr); final Long clientId = JdbcSupport.getLong(rs, "clientId"); final String clientAccountNo = rs.getString("clientAccountNo"); @@ -1345,7 +1346,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Long id = rs.getLong("id"); final Long loanId = rs.getLong("loanId"); - final String externalLoanId = rs.getString("externalLoanId"); + final String externalLoanIdStr = rs.getString("externalLoanId"); + final ExternalId externalLoanId = ExternalIdFactory.produce(externalLoanIdStr); final Long officeId = rs.getLong("officeId"); final String officeName = rs.getString("officeName"); final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType"); @@ -2033,7 +2035,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Long id = rs.getLong("id"); final Long loanId = rs.getLong("loanId"); - final String externalLoanId = rs.getString("externalLoanId"); + final String externalLoanIdStr = rs.getString("externalLoanId"); + final ExternalId externalLoanId = ExternalIdFactory.produce(externalLoanIdStr); final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType"); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(transactionTypeInt); @@ -2186,7 +2189,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { private LoanTransactionData retrieveRefundTemplate(Long loanId, LoanTransactionType loanTransactionType, Collection paymentOptions, MonetaryCurrency currency, BigDecimal transactionAmount, BigDecimal netDisbursal, - String externalLoanId) { + ExternalId externalLoanId) { final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency); @@ -2365,7 +2368,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal netDisbursalAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "netDisbursalAmount"); final Long id = null; final Long loanId = null; - final String externalLoanId = null; final Long officeId = null; final String officeName = null; boolean manuallyReversed = false; @@ -2374,7 +2376,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal fixedEmiAmount = null; return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currencyData, date, totalDue, netDisbursalAmount, principalPortion, interestDue, feeDue, penaltyDue, overPaymentPortion, ExternalId.empty(), transfer, - fixedEmiAmount, outstandingLoanBalance, unrecognizedIncomePortion, manuallyReversed, loanId, externalLoanId); + fixedEmiAmount, outstandingLoanBalance, unrecognizedIncomePortion, manuallyReversed, loanId, ExternalId.empty()); } } @@ -2419,7 +2421,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { } @Override - public Long retrieveLoanIdByExternalId(String externalId) { + public Long retrieveLoanIdByExternalId(ExternalId externalId) { return loanRepositoryWrapper.findIdByExternalId(externalId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index a6887602a..264470d93 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.portfolio.loanaccount.service; +import com.google.common.collect.Lists; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -506,11 +507,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf businessEventNotifierService.notifyPostBusinessEvent(new LoanDisbursalBusinessEvent(loan)); Long entityId = loan.getId(); - ExternalId externalId = ExternalId.empty(); + ExternalId externalId = loan.getExternalId(); // During a disbursement, the entityId should be the disbursement transaction id if (!isAccountTransfer) { - LoanTransaction disbursalTransaction = loan.getLoanTransactions().get(loan.getLoanTransactions().size() - 1); + // If accounting is not periodic accrual, the last transaction might be the accrual not the disbursement + LoanTransaction disbursalTransaction = Lists.reverse(loan.getLoanTransactions()).stream() + .filter(e -> LoanTransactionType.DISBURSEMENT.equals(e.getTypeOf())).findFirst().orElseThrow(); entityId = disbursalTransaction.getId(); externalId = disbursalTransaction.getExternalId(); businessEventNotifierService.notifyPostBusinessEvent(new LoanDisbursalTransactionBusinessEvent(disbursalTransaction)); @@ -859,6 +862,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loan.getId()) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -1556,6 +1560,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf result = new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loanId) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -1616,6 +1621,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loanId) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -1785,6 +1791,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loanId) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -1862,6 +1869,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loanId) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -2353,7 +2361,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf public CommandProcessingResult recoverFromGuarantor(final Long loanId) { final Loan loan = this.loanAssembler.assembleFrom(loanId); this.guarantorDomainService.transferFundsFromGuarantor(loan); - return new CommandProcessingResultBuilder().withLoanId(loanId).build(); + return new CommandProcessingResultBuilder().withLoanId(loanId).withEntityId(loanId).withEntityExternalId(loan.getExternalId()) + .build(); } @SuppressWarnings("unused") @@ -2476,6 +2485,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loan.getId()) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -2585,6 +2595,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loan.getId()) // + .withEntityExternalId(loan.getExternalId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) // @@ -2603,7 +2614,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final ExternalId externalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName); this.loanEventApiJsonValidator.validateLoanForeclosure(command.json()); final Map changes = new LinkedHashMap<>(); - changes.put("transactionDate", transactionDate); + // Got changed to match with the rest of the APIs + changes.put("dateFormat", command.dateFormat()); + changes.put("transactionDate", command.stringValueOfParameterNamed(LoanApiConstants.transactionDateParamName)); changes.put("externalId", externalId); String noteText = this.fromApiJsonHelper.extractStringNamed(LoanApiConstants.noteParamName, element); @@ -2619,12 +2632,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest); - final Map modifications = this.loanAccountDomainService.foreCloseLoan(loan, transactionDate, noteText, externalId); - changes.putAll(modifications); + LoanTransaction foreclosureTransaction = this.loanAccountDomainService.foreCloseLoan(loan, transactionDate, noteText, externalId, + changes); final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder(); return commandProcessingResultBuilder // .withLoanId(loanId) // + .withEntityId(foreclosureTransaction.getId()) // + .withEntityExternalId(foreclosureTransaction.getExternalId()) // .with(changes) // .build(); } diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java index c1ea9114e..6415adf81 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java @@ -37,6 +37,7 @@ import org.apache.fineract.avro.loan.v1.RepaymentDueDataV1; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.event.business.domain.loan.repayment.LoanRepaymentDueBusinessEvent; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroDateTimeMapper; @@ -87,7 +88,7 @@ public class LoanRepaymentBusinessEventSerializerTest { when(loanForProcessing.getId()).thenReturn(1L); when(loanForProcessing.getAccountNumber()).thenReturn("0001"); - when(loanForProcessing.getExternalId()).thenReturn("externalId"); + when(loanForProcessing.getExternalId()).thenReturn(ExternalIdFactory.produce("externalId")); when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary); when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.valueOf(0.0)); when(loanForProcessing.getCurrency()).thenReturn(loanCurrency); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java index a0724f369..25ba263d8 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java @@ -31,18 +31,29 @@ import io.restassured.specification.ResponseSpecification; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.UUID; +import org.apache.fineract.client.models.BusinessDateRequest; import org.apache.fineract.client.models.DeleteLoansLoanIdChargesChargeIdResponse; +import org.apache.fineract.client.models.DeleteLoansLoanIdResponse; +import org.apache.fineract.client.models.GetDelinquencyRangesResponse; +import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse; +import org.apache.fineract.client.models.GetLoansApprovalTemplateResponse; import org.apache.fineract.client.models.GetLoansLoanIdChargesChargeIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdChargesTemplateResponse; +import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse; import org.apache.fineract.client.models.PostClientsResponse; +import org.apache.fineract.client.models.PostDelinquencyBucketResponse; +import org.apache.fineract.client.models.PostDelinquencyRangeResponse; import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse; import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest; import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse; +import org.apache.fineract.client.models.PostLoansLoanIdRequest; +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; @@ -50,8 +61,12 @@ import org.apache.fineract.client.models.PutChargeTransactionChangesRequest; import org.apache.fineract.client.models.PutChargeTransactionChangesResponse; import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdRequest; import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdResponse; +import org.apache.fineract.client.models.PutLoansLoanIdRequest; +import org.apache.fineract.client.models.PutLoansLoanIdResponse; import org.apache.fineract.client.util.CallFailedRuntimeException; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.integrationtests.client.IntegrationTest; +import org.apache.fineract.integrationtests.common.BusinessDateHelper; import org.apache.fineract.integrationtests.common.ClientHelper; import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper; import org.apache.fineract.integrationtests.common.Utils; @@ -61,6 +76,9 @@ import org.apache.fineract.integrationtests.common.charges.ChargesHelper; import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; +import org.apache.fineract.integrationtests.common.organisation.StaffHelper; +import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper; +import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper; import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -827,13 +845,241 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest { assertTrue(exception.getMessage().contains("error.msg.loanCharge.external.id.invalid")); } + @Test + public void loan() { + GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, 50, true); + GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE); + new BusinessDateHelper().updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName()) + .date("2022.10.10").dateFormat("yyyy.MM.dd").locale("en")); + try { + ArrayList rangeIds = new ArrayList<>(); + // First Range + String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3); + PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, + responseSpec, jsonRange); + rangeIds.add(delinquencyRangeResponse.getResourceId()); + jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60); + + GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec, + delinquencyRangeResponse.getResourceId()); + + // Second Range + delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange); + rangeIds.add(delinquencyRangeResponse.getResourceId()); + + String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds); + PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, + responseSpec, jsonBucket); + + final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth() + .withRepaymentAfterEvery("1").withNumberOfRepayments("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0") + .withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment() + .withInterestTypeAsDecliningBalance().withAccountingRuleAsNone() + .withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30").withDaysInYear("365") + .withMoratorium("0", "0").withDelinquencyBucket(delinquencyBucketResponse.getResourceId()) + .withInArrearsTolerance("1001").withMultiDisburse().withDisallowExpectedDisbursements(true).build(null); + final Integer loanProductID = this.loanTransactionHelper.getLoanProductId(loanProductJSON); + + final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()); + + String loanExternalIdStr = UUID.randomUUID().toString(); + final HashMap loan = applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr); + Integer loanId = (Integer) loan.get("resourceId"); + String resourceExternalId = (String) loan.get("resourceExternalId"); + assertEquals(loanExternalIdStr, resourceExternalId); + + LocalDate actualDate = LocalDate.of(2022, 10, 10); + + GetLoansApprovalTemplateResponse loanApprovalResult = this.loanTransactionHelper.getLoanApprovalTemplate(loanExternalIdStr); + assertEquals(actualDate, loanApprovalResult.getApprovalDate()); + assertEquals(1000.0, loanApprovalResult.getApprovalAmount()); + assertEquals(1000.0, loanApprovalResult.getNetDisbursalAmount()); + + GetLoansLoanIdResponse loanDetailsResult = this.loanTransactionHelper.getLoanDetails(loanExternalIdStr); + assertEquals(loanExternalIdStr, loanDetailsResult.getExternalId()); + + this.loanTransactionHelper.approveLoan("02 September 2022", loanId); + String txnExternalIdStr = UUID.randomUUID().toString(); + final HashMap disbursedLoanResult = this.loanTransactionHelper.disburseLoan("03 September 2022", loanId, "1000", + txnExternalIdStr); + + // Check whether the provided external id was retrieved + assertEquals(txnExternalIdStr, disbursedLoanResult.get("resourceExternalId")); + + String txnExternalIdStr2 = UUID.randomUUID().toString(); + final HashMap disbursedLoanResult2 = this.loanTransactionHelper.disburseLoan("04 September 2022", loanId, "1000", + txnExternalIdStr2); + + // Check whether the provided external id was retrieved + assertEquals(txnExternalIdStr2, disbursedLoanResult2.get("resourceExternalId")); + + PutLoansLoanIdResponse markLoanAsFraudResult = this.loanTransactionHelper.modifyLoanApplication(loanExternalIdStr, + "markAsFraud", new PutLoansLoanIdRequest().fraud(true)); + assertEquals(loanExternalIdStr, markLoanAsFraudResult.getResourceExternalId()); + + List delinquencyTagHistoryResponseResult = this.loanTransactionHelper + .getLoanDelinquencyTags(loanExternalIdStr); + assertEquals(1, delinquencyTagHistoryResponseResult.size()); + assertEquals((long) loanId, delinquencyTagHistoryResponseResult.get(0).getLoanId()); + + String loanExternalIdStr2 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr2); + + PutLoansLoanIdResponse modifyLoanApplicationResult = this.loanTransactionHelper.modifyLoanApplication(loanExternalIdStr2, + "modify", + new PutLoansLoanIdRequest().submittedOnDate("31 August 2022").dateFormat("dd MMMM yyyy").locale("en") + .loanType("individual").productId(loanProductID).clientId(client.getClientId()).interestType(0) + .interestCalculationPeriodType(1).interestRatePerPeriod(0).isEqualAmortization(false).loanTermFrequency(30) + .loanTermFrequencyType(0).maxOutstandingLoanBalance(10000L).numberOfRepayments(1).principal(10000L) + .repaymentEvery(30).repaymentFrequencyType(0).transactionProcessingStrategyCode("mifos-standard-strategy") + .expectedDisbursementDate("2 September 2022").amortizationType(1)); + + assertEquals(loanExternalIdStr2, modifyLoanApplicationResult.getResourceExternalId()); + DeleteLoansLoanIdResponse deleteLoanApplicationResult = this.loanTransactionHelper.deleteLoanApplication(loanExternalIdStr2); + assertEquals(loanExternalIdStr2, deleteLoanApplicationResult.getResourceExternalId()); + + String loanExternalIdStr3 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr3); + PostLoansLoanIdResponse result = this.loanTransactionHelper.rejectLoan(loanExternalIdStr3, + new PostLoansLoanIdRequest().rejectedOnDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + assertEquals(loanExternalIdStr3, result.getResourceExternalId()); + + String loanExternalIdStr4 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr4); + result = this.loanTransactionHelper.withdrawnByApplicantLoan(loanExternalIdStr4, + new PostLoansLoanIdRequest().withdrawnOnDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + assertEquals(loanExternalIdStr4, result.getResourceExternalId()); + + String loanExternalIdStr5 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr5); + this.loanTransactionHelper.approveLoan(loanExternalIdStr5, new PostLoansLoanIdRequest().approvedOnDate("2 September 2022") + .approvedLoanAmount("1000").expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + result = this.loanTransactionHelper.disburseLoan(loanExternalIdStr5, new PostLoansLoanIdRequest() + .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy")); + // It's commented out for now, till it got fixed to return the loan externalId as well + // assertEquals(loanExternalIdStr5, result.getResourceExternalId()); + + String loanExternalIdStr6 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr6); + this.loanTransactionHelper.approveLoan(loanExternalIdStr6, new PostLoansLoanIdRequest().approvedOnDate("2 September 2022") + .approvedLoanAmount("1000").expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + result = this.loanTransactionHelper.undoApprovalLoan(loanExternalIdStr6, new PostLoansLoanIdRequest()); + assertEquals(loanExternalIdStr6, result.getResourceExternalId()); + + final Integer savingsId = SavingsAccountHelper.openSavingsAccount(this.requestSpec, this.responseSpec, + client.getClientId().intValue(), "10000.0"); + + String loanExternalIdStr7 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr7, savingsId.toString()); + this.loanTransactionHelper.approveLoan(loanExternalIdStr7, new PostLoansLoanIdRequest().approvedOnDate("2 September 2022") + .approvedLoanAmount("1000").expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + result = this.loanTransactionHelper.disburseToSavingsLoan(loanExternalIdStr7, new PostLoansLoanIdRequest() + .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy")); + assertEquals(loanExternalIdStr7, result.getResourceExternalId()); + + String loanExternalIdStr8 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr8); + this.loanTransactionHelper.approveLoan(loanExternalIdStr8, new PostLoansLoanIdRequest().approvedOnDate("2 September 2022") + .approvedLoanAmount("1000").expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + this.loanTransactionHelper.disburseLoan(loanExternalIdStr8, new PostLoansLoanIdRequest() + .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy")); + result = this.loanTransactionHelper.undoDisbursalLoan(loanExternalIdStr8, new PostLoansLoanIdRequest()); + assertEquals(loanExternalIdStr8, result.getResourceExternalId()); + + String loanExternalIdStr9 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr9); + this.loanTransactionHelper.approveLoan(loanExternalIdStr9, new PostLoansLoanIdRequest().approvedOnDate("2 September 2022") + .approvedLoanAmount("1000").expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + this.loanTransactionHelper.disburseLoan(loanExternalIdStr9, new PostLoansLoanIdRequest() + .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy")); + this.loanTransactionHelper.disburseLoan(loanExternalIdStr9, new PostLoansLoanIdRequest() + .actualDisbursementDate("3 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy")); + result = this.loanTransactionHelper.undoLastDisbursalLoan(loanExternalIdStr9, new PostLoansLoanIdRequest()); + assertEquals(loanExternalIdStr9, result.getResourceExternalId()); + + Integer loanOfficerId = StaffHelper.createStaff(this.requestSpec, this.responseSpec); + String loanExternalIdStr10 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr10); + result = this.loanTransactionHelper.assignLoanOfficerLoan(loanExternalIdStr10, new PostLoansLoanIdRequest() + .assignmentDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy").toLoanOfficerId(loanOfficerId)); + assertEquals(loanExternalIdStr10, result.getResourceExternalId()); + result = this.loanTransactionHelper.unassignLoanOfficerLoan(loanExternalIdStr10, + new PostLoansLoanIdRequest().unassignedDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + assertEquals(loanExternalIdStr10, result.getResourceExternalId()); + + String loanExternalIdStr11 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr11); + result = this.loanTransactionHelper.recoverGuaranteesLoan(loanExternalIdStr11, new PostLoansLoanIdRequest()); + assertEquals(loanExternalIdStr11, result.getResourceExternalId()); + + String loanExternalIdStr12 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr12); + result = this.loanTransactionHelper.assignDelinquencyLoan(loanExternalIdStr12, new PostLoansLoanIdRequest()); + assertEquals(loanExternalIdStr12, result.getResourceExternalId()); + + String loanExternalIdStr13 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr13); + result = this.loanTransactionHelper.approveLoan(loanExternalIdStr13, + new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount("1000") + .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + assertEquals(loanExternalIdStr13, result.getResourceExternalId()); + + PostLoansLoanIdTransactionsResponse closeRescheduleResult = this.loanTransactionHelper.closeRescheduledLoan(loanExternalIdStr13, + new PostLoansLoanIdTransactionsRequest().transactionDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + assertEquals(loanExternalIdStr13, closeRescheduleResult.getResourceExternalId()); + + String loanExternalIdStr14 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr14); + String transactionExternalId = UUID.randomUUID().toString(); + result = this.loanTransactionHelper.approveLoan(loanExternalIdStr14, + new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount("1000") + .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + assertEquals(loanExternalIdStr14, result.getResourceExternalId()); + this.loanTransactionHelper.disburseLoan(loanExternalIdStr14, new PostLoansLoanIdRequest() + .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy")); + PostLoansLoanIdTransactionsResponse closeResult = this.loanTransactionHelper.closeLoan(loanExternalIdStr14, + new PostLoansLoanIdTransactionsRequest().transactionDate("3 September 2022").locale("en").dateFormat("dd MMMM yyyy") + .externalId(transactionExternalId)); + assertEquals(transactionExternalId, closeResult.getResourceExternalId()); + + String loanExternalIdStr15 = UUID.randomUUID().toString(); + String transactionExternalId2 = UUID.randomUUID().toString(); + applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr15); + result = this.loanTransactionHelper.approveLoan(loanExternalIdStr15, + new PostLoansLoanIdRequest().approvedOnDate("2 September 2022").approvedLoanAmount("1000") + .expectedDisbursementDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy")); + this.loanTransactionHelper.disburseLoan(loanExternalIdStr15, new PostLoansLoanIdRequest() + .actualDisbursementDate("2 September 2022").transactionAmount("1000").locale("en").dateFormat("dd MMMM yyyy")); + assertEquals(loanExternalIdStr15, result.getResourceExternalId()); + PostLoansLoanIdTransactionsResponse forecloseResult = this.loanTransactionHelper.forecloseLoan(loanExternalIdStr15, + new PostLoansLoanIdTransactionsRequest().transactionDate("2 September 2022").locale("en").dateFormat("dd MMMM yyyy") + .externalId(transactionExternalId2)); + assertEquals(transactionExternalId2, forecloseResult.getResourceExternalId()); + + } finally { + GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE); + GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, 50, false); + } + } + + private HashMap applyForLoanApplication(final Integer clientID, final Integer loanProductID, final String externalId, + final String linkAccountId) { + final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1") + .withLoanTermFrequencyAsMonths().withNumberOfRepayments("1").withRepaymentEveryAfter("1") + .withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance() + .withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod() + .withExpectedDisbursementDate("03 September 2022").withSubmittedOnDate("01 September 2022").withLoanType("individual") + .withExternalId(externalId).build(clientID.toString(), loanProductID.toString(), linkAccountId); + return (HashMap) this.loanTransactionHelper.createLoanAccount(loanApplicationJSON, ""); + } + private HashMap applyForLoanApplication(final Integer clientID, final Integer loanProductID, final String externalId) { final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1") .withLoanTermFrequencyAsMonths().withNumberOfRepayments("1").withRepaymentEveryAfter("1") .withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance() .withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod() .withExpectedDisbursementDate("03 September 2022").withSubmittedOnDate("01 September 2022").withLoanType("individual") - .withExternalId(externalId).build(clientID.toString(), loanProductID.toString(), null); + .withInArrearsTolerance("1001").withExternalId(externalId).build(clientID.toString(), loanProductID.toString(), null); return (HashMap) this.loanTransactionHelper.createLoanAccount(loanApplicationJSON, ""); } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java index ab21bb1d5..aaedefa19 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java @@ -79,6 +79,8 @@ public class LoanApplicationTestBuilder { private List> approvalFormData = null; private String fixedPrincipalPercentagePerInstallment; private String interestChargedFromDate; + private String linkAccountId; + private String inArrearsTolerance; public String build(final String clientID, final String groupID, final String loanProductId, final String savingsID) { final HashMap map = new HashMap<>(); @@ -169,6 +171,14 @@ public class LoanApplicationTestBuilder { map.put("linkAccountId", savingsID); } + if (this.linkAccountId != null) { + map.put("linkAccountId", this.linkAccountId); + } + + if (this.inArrearsTolerance != null) { + map.put("inArrearsTolerance", this.inArrearsTolerance); + } + if (graceOnPrincipalPayment != null) { map.put("graceOnPrincipalPayment", graceOnPrincipalPayment); } @@ -210,6 +220,11 @@ public class LoanApplicationTestBuilder { return this; } + public LoanApplicationTestBuilder withLinkedAccount(String linkAccountId) { + this.linkAccountId = linkAccountId; + return this; + } + public LoanApplicationTestBuilder withLoanTermFrequencyAsMonths() { this.loanTermFrequencyType = MONTHS; return this; @@ -398,4 +413,9 @@ public class LoanApplicationTestBuilder { this.interestChargedFromDate = interestChargedFromDate; return this; } + + public LoanApplicationTestBuilder withInArrearsTolerance(String amount) { + this.inArrearsTolerance = amount; + return this; + } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java index a55b808fc..adb4700d1 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java @@ -136,6 +136,7 @@ public class LoanProductTestBuilder { private String fixedPrincipalPercentagePerInstallment; private String installmentAmountInMultiplesOf; private boolean canDefineInstallmentAmount; + private Integer delinquencyBucketId; public String build(final String chargeId) { final HashMap map = build(chargeId, null); @@ -254,6 +255,10 @@ public class LoanProductTestBuilder { map.put("delinquencyBucketId", delinquencyBucketId); } + if (this.delinquencyBucketId != null) { + map.put("delinquencyBucketId", this.delinquencyBucketId); + } + if (this.feeToIncomeAccountMappings != null) { map.put("feeToIncomeAccountMappings", this.feeToIncomeAccountMappings); } @@ -269,6 +274,11 @@ public class LoanProductTestBuilder { return this; } + public LoanProductTestBuilder withDelinquencyBucket(Integer delinquencyBucketId) { + this.delinquencyBucketId = delinquencyBucketId; + return this; + } + public LoanProductTestBuilder withMinPrincipal(final String minPrincipal) { this.minPrincipal = minPrincipal; return this; diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java index f2f079129..148cf2f7a 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java @@ -43,8 +43,10 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.client.models.DeleteLoansLoanIdChargesChargeIdResponse; +import org.apache.fineract.client.models.DeleteLoansLoanIdResponse; import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse; import org.apache.fineract.client.models.GetLoanProductsProductIdResponse; +import org.apache.fineract.client.models.GetLoansApprovalTemplateResponse; import org.apache.fineract.client.models.GetLoansLoanIdChargesChargeIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdChargesTemplateResponse; import org.apache.fineract.client.models.GetLoansLoanIdCollectionData; @@ -59,6 +61,7 @@ import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse; import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest; import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse; +import org.apache.fineract.client.models.PostLoansLoanIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdResponse; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; @@ -67,6 +70,7 @@ import org.apache.fineract.client.models.PutChargeTransactionChangesRequest; import org.apache.fineract.client.models.PutChargeTransactionChangesResponse; import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdRequest; import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdResponse; +import org.apache.fineract.client.models.PutLoansLoanIdRequest; import org.apache.fineract.client.models.PutLoansLoanIdResponse; import org.apache.fineract.client.util.JSON; import org.apache.fineract.integrationtests.client.IntegrationTest; @@ -178,6 +182,11 @@ public class LoanTransactionHelper extends IntegrationTest { return GSON.fromJson(response, PutLoansLoanIdResponse.class); } + public PutLoansLoanIdResponse modifyLoanApplication(final String loanExternalId, final String command, + final PutLoansLoanIdRequest request) { + return ok(fineract().loans.modifyLoanApplication1(loanExternalId, request, command)); + } + public ArrayList getLoanRepaymentSchedule(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final Integer loanID) { final String URL = "/fineract-provider/api/v1/loans/" + loanID + "?associations=repaymentSchedule&" + Utils.TENANT_IDENTIFIER; @@ -920,6 +929,10 @@ public class LoanTransactionHelper extends IntegrationTest { return ok(fineract().loans.retrieveLoan(loanId, false, "all", null, null)); } + public GetLoansLoanIdResponse getLoanDetails(final String loanExternalId) { + return ok(fineract().loans.retrieveLoan1(loanExternalId, 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; @@ -1626,4 +1639,75 @@ public class LoanTransactionHelper extends IntegrationTest { fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, command, dateFormat, transactionDate, locale)); } + public GetLoansApprovalTemplateResponse getLoanApprovalTemplate(String loanExternalIdStr) { + return ok(fineract().loans.retrieveApprovalTemplate1(loanExternalIdStr, "approval")); + } + + public DeleteLoansLoanIdResponse deleteLoanApplication(String loanExternalId) { + return ok(fineract().loans.deleteLoanApplication1(loanExternalId)); + } + + public List getLoanDelinquencyTags(String loanExternalId) { + return ok(fineract().loans.getDelinquencyTagHistory1(loanExternalId)); + } + + public PostLoansLoanIdResponse approveLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "approve")); + } + + public PostLoansLoanIdResponse rejectLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "reject")); + } + + public PostLoansLoanIdResponse withdrawnByApplicantLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "withdrawnByApplicant")); + } + + public PostLoansLoanIdResponse disburseLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "disburse")); + } + + public PostLoansLoanIdResponse disburseToSavingsLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "disburseToSavings")); + } + + public PostLoansLoanIdResponse undoApprovalLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "undoapproval")); + } + + public PostLoansLoanIdResponse undoDisbursalLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "undodisbursal")); + } + + public PostLoansLoanIdResponse undoLastDisbursalLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "undolastdisbursal")); + } + + public PostLoansLoanIdResponse assignLoanOfficerLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "assignloanofficer")); + } + + public PostLoansLoanIdResponse unassignLoanOfficerLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "unassignloanofficer")); + } + + public PostLoansLoanIdResponse recoverGuaranteesLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "recoverGuarantees")); + } + + public PostLoansLoanIdResponse assignDelinquencyLoan(String loanExternalId, PostLoansLoanIdRequest request) { + return ok(fineract().loans.stateTransitions1(loanExternalId, request, "assigndelinquency")); + } + + public PostLoansLoanIdTransactionsResponse closeRescheduledLoan(String loanExternalId, PostLoansLoanIdTransactionsRequest request) { + return ok(fineract().loanTransactions.executeLoanTransaction1(loanExternalId, request, "close-rescheduled")); + } + + public PostLoansLoanIdTransactionsResponse closeLoan(String loanExternalId, PostLoansLoanIdTransactionsRequest request) { + return ok(fineract().loanTransactions.executeLoanTransaction1(loanExternalId, request, "close")); + } + + public PostLoansLoanIdTransactionsResponse forecloseLoan(String loanExternalId, PostLoansLoanIdTransactionsRequest request) { + return ok(fineract().loanTransactions.executeLoanTransaction1(loanExternalId, request, "foreclosure")); + } }