FINERACT-1760: Enhanced external id support for loan

This commit is contained in:
Adam Saghy
2022-12-15 14:25:47 +01:00
parent 8457b31c0f
commit 2532c58cbc
26 changed files with 1010 additions and 428 deletions
@@ -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<String> 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);
@@ -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);
}
@@ -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())
@@ -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
@@ -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);
}
@@ -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);
}
@@ -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<PortfolioAccountData> getaccountLinkingOptions(final LoanAccountData newLoanAccount, final Long clientId,
private Collection<PortfolioAccountData> 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<CalendarData> 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<CalendarData> 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<CalendarData> 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<InterestRatePeriodData> interestRatesPeriods = this.loanReadPlatformService
.retrieveLoanInterestRatePeriodData(loanBasicDetails);
Collection<LoanTransactionData> loanRepayments = null;
LoanScheduleData repaymentSchedule = null;
Collection<LoanChargeData> charges = null;
Collection<GuarantorData> guarantors = null;
CalendarData meeting = null;
Collection<NoteData> notes = null;
PortfolioAccountData linkedAccount = null;
Collection<DisbursementData> disbursementData = null;
Collection<LoanTermVariationsData> emiAmountVariations = null;
Collection<LoanCollateralResponseData> loanCollateralManagements;
Collection<LoanCollateralManagementData> loanCollateralManagementData = new ArrayList<>();
CollectionData collectionData = CollectionData.template();
final Set<String> mandatoryResponseParameters = new HashSet<>();
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
final Collection<LoanTransactionData> 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<LoanProductData> productOptions = null;
LoanProductData product;
Collection<EnumOptionData> loanTermFrequencyTypeOptions = null;
Collection<EnumOptionData> repaymentFrequencyTypeOptions = null;
Collection<EnumOptionData> repaymentFrequencyNthDayTypeOptions = null;
Collection<EnumOptionData> repaymentFrequencyDayOfWeekTypeOptions = null;
Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = null;
Collection<EnumOptionData> interestRateFrequencyTypeOptions = null;
Collection<EnumOptionData> amortizationTypeOptions = null;
Collection<EnumOptionData> interestTypeOptions = null;
Collection<EnumOptionData> interestCalculationPeriodTypeOptions = null;
Collection<FundData> fundOptions = null;
Collection<StaffData> allowedLoanOfficers = null;
Collection<ChargeData> chargeOptions = null;
ChargeData chargeTemplate = null;
Collection<CodeValueData> loanPurposeOptions = null;
Collection<CodeValueData> loanCollateralOptions = null;
Collection<CalendarData> calendarOptions = null;
Collection<PortfolioAccountData> accountLinkingOptions = null;
PaidInAdvanceData paidInAdvanceTemplate;
Collection<LoanAccountSummaryData> 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<ChargeData> overdueCharges = this.chargeReadPlatformService
.retrieveLoanProductCharges(loanBasicDetails.getLoanProductId(), ChargeTimeType.OVERDUE_INSTALLMENT);
paidInAdvanceTemplate = this.loanReadPlatformService.retrieveTotalPaidInAdvance(loanId);
// Get rates from Loan
boolean isRatesEnabled = this.configurationDomainService.isSubRatesEnabled();
List<RateData> 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<CalendarData> 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<CalendarData> 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<CalendarData> 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<InterestRatePeriodData> interestRatesPeriods = this.loanReadPlatformService
.retrieveLoanInterestRatePeriodData(loanBasicDetails);
Collection<LoanTransactionData> loanRepayments = null;
LoanScheduleData repaymentSchedule = null;
Collection<LoanChargeData> charges = null;
Collection<GuarantorData> guarantors = null;
CalendarData meeting = null;
Collection<NoteData> notes = null;
PortfolioAccountData linkedAccount = null;
Collection<DisbursementData> disbursementData = null;
Collection<LoanTermVariationsData> emiAmountVariations = null;
Collection<LoanCollateralResponseData> loanCollateralManagements;
Collection<LoanCollateralManagementData> loanCollateralManagementData = new ArrayList<>();
CollectionData collectionData = CollectionData.template();
final Set<String> mandatoryResponseParameters = new HashSet<>();
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
final Collection<LoanTransactionData> 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<LoanProductData> productOptions = null;
LoanProductData product;
Collection<EnumOptionData> loanTermFrequencyTypeOptions = null;
Collection<EnumOptionData> repaymentFrequencyTypeOptions = null;
Collection<EnumOptionData> repaymentFrequencyNthDayTypeOptions = null;
Collection<EnumOptionData> repaymentFrequencyDayOfWeekTypeOptions = null;
Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = null;
Collection<EnumOptionData> interestRateFrequencyTypeOptions = null;
Collection<EnumOptionData> amortizationTypeOptions = null;
Collection<EnumOptionData> interestTypeOptions = null;
Collection<EnumOptionData> interestCalculationPeriodTypeOptions = null;
Collection<FundData> fundOptions = null;
Collection<StaffData> allowedLoanOfficers = null;
Collection<ChargeData> chargeOptions = null;
ChargeData chargeTemplate = null;
Collection<CodeValueData> loanPurposeOptions = null;
Collection<CodeValueData> loanCollateralOptions = null;
Collection<CalendarData> calendarOptions = null;
Collection<PortfolioAccountData> accountLinkingOptions = null;
PaidInAdvanceData paidInAdvanceTemplate;
Collection<LoanAccountSummaryData> 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<ChargeData> overdueCharges = this.chargeReadPlatformService
.retrieveLoanProductCharges(loanBasicDetails.getLoanProductId(), ChargeTimeType.OVERDUE_INSTALLMENT);
paidInAdvanceTemplate = this.loanReadPlatformService.retrieveTotalPaidInAdvance(resolvedLoanId);
// Get rates from Loan
boolean isRatesEnabled = this.configurationDomainService.isSubRatesEnabled();
List<RateData> 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> 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;
}
}
@@ -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<PostLoansLoanIdDisbursementData> 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;
}
@@ -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<LoanChargeData> charges,
LocalDate repaymentsStartingFromDate, Integer rowIndex, ExternalId externalId, Long groupId, Collection<LoanChargeData> charges,
String linkAccountId, String locale, String dateFormat, List<LoanCollateralManagementData> 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,
@@ -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<PaymentTypeData> 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<PaymentTypeData> 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;
@@ -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<Holiday> 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;
}
@@ -83,7 +83,8 @@ public interface LoanAccountDomainService {
void saveLoanWithDataIntegrityViolationChecks(Loan loan);
Map<String, Object> foreCloseLoan(Loan loan, LocalDate foreClourseDate, String noteText, ExternalId externalId);
LoanTransaction foreCloseLoan(Loan loan, LocalDate foreClourseDate, String noteText, ExternalId externalId,
Map<String, Object> changes);
/**
* Disables all standing instructions linked to a closed loan
@@ -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<String, Object> 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<String, Object> changes) {
businessEventNotifierService.notifyPreBusinessEvent(new LoanForeClosurePreBusinessEvent(loan));
MonetaryCurrency currency = loan.getCurrency();
final Map<String, Object> changes = new LinkedHashMap<>();
List<LoanTransaction> newTransactions = new ArrayList<>();
final List<Long> 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
@@ -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<Loan, Long>, 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<Long> 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<Long> findAllNonClosedLoanIdsOneDayBehind(@Param("last_closed_business_date") LocalDate businessDate);
@@ -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);
}
}
@@ -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()) //
@@ -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) {
@@ -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()) //
@@ -158,5 +158,5 @@ public interface LoanReadPlatformService {
Long retrieveLoanTransactionIdByExternalId(ExternalId externalId);
Long retrieveLoanIdByExternalId(String externalId);
Long retrieveLoanIdByExternalId(ExternalId externalId);
}
@@ -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<PaymentTypeData> 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);
}
@@ -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<String, Object> 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<String, Object> 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();
}
@@ -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);
@@ -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<Integer> 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<GetDelinquencyTagHistoryResponse> 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, "");
}
}
@@ -79,6 +79,8 @@ public class LoanApplicationTestBuilder {
private List<Map<String, Object>> 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<String, Object> 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;
}
}
@@ -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<String, Object> 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;
@@ -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<GetDelinquencyTagHistoryResponse> 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"));
}
}