FINERACT-2317: Add support for modifying loan approved amounts with validation and history tracking
This commit is contained in:
+69
@@ -127,12 +127,14 @@ import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.GlimRepaymentTemplate;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanApprovedAmountHistoryData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanCollateralManagementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanApprovedAmountHistoryRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
|
||||
@@ -176,6 +178,8 @@ import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -303,6 +307,7 @@ public class LoansApiResource {
|
||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||
private final LoanSummaryProviderDelegate loanSummaryProviderDelegate;
|
||||
private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
|
||||
private final LoanApprovedAmountHistoryRepository loanApprovedAmountHistoryRepository;
|
||||
|
||||
/*
|
||||
* This template API is used for loan approval, ideally this should be invoked on loan that are pending for
|
||||
@@ -872,6 +877,55 @@ public class LoansApiResource {
|
||||
return createLoanDelinquencyAction(null, ExternalIdFactory.produce(loanExternalId), apiRequestBodyAsJson);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{loanId}/approved-amount")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Modifies the approved amount of the loan", description = "")
|
||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PutLoansApprovedAmountRequest.class)))
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PutLoansApprovedAmountResponse.class))) })
|
||||
public CommandProcessingResult modifyLoanApprovedAmount(
|
||||
@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId, @Context final UriInfo uriInfo,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
return modifyLoanApprovedAmount(loanId, ExternalId.empty(), apiRequestBodyAsJson);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("external-id/{loanExternalId}/approved-amount")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Modifies the approved amount of the loan", description = "")
|
||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PutLoansApprovedAmountRequest.class)))
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoansApiResourceSwagger.PutLoansApprovedAmountResponse.class))) })
|
||||
public CommandProcessingResult modifyLoanApprovedAmount(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId,
|
||||
@Context final UriInfo uriInfo, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
return modifyLoanApprovedAmount(null, ExternalIdFactory.produce(loanExternalId), apiRequestBodyAsJson);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{loanId}/approved-amount")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Collects and returns the approved amount modification history for a given loan", description = "")
|
||||
public List<LoanApprovedAmountHistoryData> getLoanApprovedAmountHistory(
|
||||
@PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId, @Context final UriInfo uriInfo) {
|
||||
return getLoanApprovedAmountHistory(loanId, ExternalId.empty());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("external-id/{loanExternalId}/approved-amount")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Collects and returns the approved amount modification history for a given loan", description = "")
|
||||
public List<LoanApprovedAmountHistoryData> getLoanApprovedAmountHistory(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
return getLoanApprovedAmountHistory(null, ExternalIdFactory.produce(loanExternalId));
|
||||
}
|
||||
|
||||
private String retrieveApprovalTemplate(final Long loanId, final String loanExternalIdStr, final String templateType,
|
||||
final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
@@ -1294,4 +1348,19 @@ public class LoansApiResource {
|
||||
return delinquencyActionSerializer.serialize(result);
|
||||
}
|
||||
|
||||
private CommandProcessingResult modifyLoanApprovedAmount(Long loanId, ExternalId loanExternalId, String apiRequestBodyAsJson) {
|
||||
Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||
CommandWrapper commandRequest = builder.updateLoanApprovedAmount(resolvedLoanId).build();
|
||||
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
}
|
||||
|
||||
private List<LoanApprovedAmountHistoryData> getLoanApprovedAmountHistory(Long loanId, ExternalId loanExternalId) {
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
|
||||
Pageable sortedByCreationDate = Pageable.unpaged(Sort.by("createdDate").ascending());
|
||||
return loanApprovedAmountHistoryRepository.findAllByLoanId(resolvedLoanId, sortedByCreationDate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+43
@@ -1771,4 +1771,47 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(description = "PostLoansLoanIdChanges")
|
||||
public PostLoansLoanIdChanges changes;
|
||||
}
|
||||
|
||||
@Schema(description = "PutLoansApprovedAmountRequest")
|
||||
public static final class PutLoansApprovedAmountRequest {
|
||||
|
||||
private PutLoansApprovedAmountRequest() {}
|
||||
|
||||
@Schema(example = "1000")
|
||||
public BigDecimal amount;
|
||||
@Schema(example = "en")
|
||||
public String locale;
|
||||
}
|
||||
|
||||
@Schema(description = "PutLoansApprovedAmountResponse")
|
||||
public static final class PutLoansApprovedAmountResponse {
|
||||
|
||||
private PutLoansApprovedAmountResponse() {}
|
||||
|
||||
static final class PutLoansApprovedAmountChanges {
|
||||
|
||||
private PutLoansApprovedAmountChanges() {}
|
||||
|
||||
@Schema(example = "1000")
|
||||
public BigDecimal oldApprovedAmount;
|
||||
@Schema(example = "1000")
|
||||
public BigDecimal newApprovedAmount;
|
||||
@Schema(example = "en_GB")
|
||||
public String locale;
|
||||
}
|
||||
|
||||
@Schema(example = "3")
|
||||
public Long resourceId;
|
||||
@Schema(example = "95174ff9-1a75-4d72-a413-6f9b1cb988b7")
|
||||
public String resourceExternalId;
|
||||
@Schema(example = "2")
|
||||
public Long officeId;
|
||||
@Schema(example = "6")
|
||||
public Long clientId;
|
||||
@Schema(example = "10")
|
||||
public Long groupId;
|
||||
|
||||
@Schema(description = "PutLoansApprovedAmountChanges")
|
||||
public PutLoansApprovedAmountChanges changes;
|
||||
}
|
||||
}
|
||||
|
||||
+23
-2
@@ -2174,12 +2174,33 @@ public final class LoanApplicationValidator {
|
||||
|
||||
public BigDecimal getOverAppliedMax(Loan loan) {
|
||||
LoanProduct loanProduct = loan.getLoanProduct();
|
||||
|
||||
// Check if overapplied calculation type and number are properly configured
|
||||
if (loanProduct.getOverAppliedCalculationType() == null || loanProduct.getOverAppliedNumber() == null) {
|
||||
// If overapplied calculation is not configured, return proposed principal (original behavior)
|
||||
return loan.getProposedPrincipal();
|
||||
}
|
||||
|
||||
// For loans with approved amount modifications, use proposed principal as base to allow
|
||||
// disbursement up to the originally requested amount regardless of the reduced approved amount
|
||||
boolean hasApprovedAmountModification = loan.getApprovedPrincipal() != null && loan.getProposedPrincipal() != null
|
||||
&& loan.getApprovedPrincipal().compareTo(loan.getProposedPrincipal()) != 0;
|
||||
|
||||
BigDecimal basePrincipal;
|
||||
if (hasApprovedAmountModification) {
|
||||
// Use proposed principal for loans with approved amount modifications
|
||||
basePrincipal = loan.getProposedPrincipal();
|
||||
} else {
|
||||
// Use approved principal for normal loans
|
||||
basePrincipal = loan.getApprovedPrincipal() != null ? loan.getApprovedPrincipal() : loan.getProposedPrincipal();
|
||||
}
|
||||
|
||||
if ("percentage".equals(loanProduct.getOverAppliedCalculationType())) {
|
||||
BigDecimal overAppliedNumber = BigDecimal.valueOf(loanProduct.getOverAppliedNumber());
|
||||
BigDecimal totalPercentage = BigDecimal.valueOf(1).add(overAppliedNumber.divide(BigDecimal.valueOf(100)));
|
||||
return loan.getProposedPrincipal().multiply(totalPercentage);
|
||||
return basePrincipal.multiply(totalPercentage);
|
||||
} else {
|
||||
return loan.getProposedPrincipal().add(BigDecimal.valueOf(loanProduct.getOverAppliedNumber()));
|
||||
return basePrincipal.add(BigDecimal.valueOf(loanProduct.getOverAppliedNumber()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.serialization;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.portfolio.common.service.Validator;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public final class LoanApprovedAmountValidatorImpl implements LoanApprovedAmountValidator {
|
||||
|
||||
private static final Set<LoanStatus> INVALID_LOAN_STATUSES_FOR_APPROVED_AMOUNT_MODIFICATION = Set.of(LoanStatus.INVALID,
|
||||
LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, LoanStatus.REJECTED);
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final LoanRepository loanRepository;
|
||||
private final LoanApplicationValidator loanApplicationValidator;
|
||||
|
||||
@Override
|
||||
public void validateLoanApprovedAmountModification(JsonCommand command) {
|
||||
String json = command.json();
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Set<String> supportedParameters = new HashSet<>(
|
||||
Arrays.asList(LoanApiConstants.amountParameterName, LoanApiConstants.localeParameterName));
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
|
||||
|
||||
final BigDecimal newApprovedAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.amountParameterName,
|
||||
element);
|
||||
|
||||
Validator.validateOrThrow("loan.approved.amount", baseDataValidator -> {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.amountParameterName).value(newApprovedAmount).notNull();
|
||||
});
|
||||
|
||||
Validator.validateOrThrowDomainViolation("loan.approved.amount", baseDataValidator -> {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.amountParameterName).value(newApprovedAmount).positiveAmount();
|
||||
|
||||
final Long loanId = command.getLoanId();
|
||||
Loan loan = this.loanRepository.findById(loanId).orElseThrow(() -> new LoanNotFoundException(loanId));
|
||||
|
||||
if (INVALID_LOAN_STATUSES_FOR_APPROVED_AMOUNT_MODIFICATION.contains(loan.getStatus())) {
|
||||
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("loan.status.not.valid.for.approved.amount.modification");
|
||||
}
|
||||
|
||||
BigDecimal maximumThresholdForApprovedAmount;
|
||||
if (loan.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) {
|
||||
maximumThresholdForApprovedAmount = loanApplicationValidator.getOverAppliedMax(loan);
|
||||
} else {
|
||||
maximumThresholdForApprovedAmount = loan.getProposedPrincipal();
|
||||
}
|
||||
|
||||
if (MathUtil.isGreaterThan(newApprovedAmount, maximumThresholdForApprovedAmount)) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.amountParameterName)
|
||||
.failWithCode("can't.be.greater.than.maximum.applied.loan.amount.calculation");
|
||||
}
|
||||
|
||||
BigDecimal totalPrincipalOnLoan = loan.getSummary().getTotalPrincipal();
|
||||
if (MathUtil.isLessThan(newApprovedAmount, totalPrincipalOnLoan)) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.amountParameterName)
|
||||
.failWithCode("less.than.disbursed.principal.and.capitalized.income");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -44,7 +44,7 @@ public final class LoanDisbursementValidator {
|
||||
} else {
|
||||
if ((totalDisbursed.compareTo(loan.getApprovedPrincipal()) > 0)
|
||||
|| (totalDisbursed.add(capitalizedIncome).compareTo(loan.getApprovedPrincipal()) > 0)) {
|
||||
final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved principal ";
|
||||
final String errorMsg = "Loan can't be disbursed, disburse amount is exceeding approved principal.";
|
||||
throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.principal", totalDisbursed,
|
||||
loan.getApprovedPrincipal());
|
||||
}
|
||||
|
||||
+1
-1
@@ -158,7 +158,7 @@ public final class LoanTransactionValidatorImpl implements LoanTransactionValida
|
||||
validateLoanClientIsActive(loan);
|
||||
validateLoanGroupIsActive(loan);
|
||||
|
||||
final BigDecimal disbursedAmount = loan.getDisbursedAmount();
|
||||
final BigDecimal disbursedAmount = loan.getSummary().getTotalPrincipalDisbursed();
|
||||
loanDisbursementValidator.compareDisbursedToApprovedOrProposedPrincipal(loan, principal, disbursedAmount);
|
||||
|
||||
if (loan.isChargedOff()) {
|
||||
|
||||
+1
@@ -161,6 +161,7 @@ public class LoanDisbursementService {
|
||||
} else {
|
||||
loan.getLoanRepaymentScheduleDetail()
|
||||
.setPrincipal(loan.getLoanRepaymentScheduleDetail().getPrincipal().minus(diff).getAmount());
|
||||
totalAmount = loan.getLoanRepaymentScheduleDetail().getPrincipal().getAmount();
|
||||
}
|
||||
loanDisbursementValidator.compareDisbursedToApprovedOrProposedPrincipal(loan, disburseAmount.getAmount(), totalAmount);
|
||||
}
|
||||
|
||||
+1
-1
@@ -432,7 +432,7 @@ public class LoanAccountConfiguration {
|
||||
LoanJournalEntryPoster journalEntryPoster, LoanAdjustmentService loanAdjustmentService,
|
||||
LoanAccountingBridgeMapper loanAccountingBridgeMapper, LoanMapper loanMapper,
|
||||
LoanTransactionProcessingService loanTransactionProcessingService, final LoanBalanceService loanBalanceService,
|
||||
LoanTransactionService loanTransactionService, BuyDownFeePlatformService buyDownFeePlatformService) {
|
||||
LoanTransactionService loanTransactionService) {
|
||||
return new LoanWritePlatformServiceJpaRepositoryImpl(context, loanTransactionValidator, loanUpdateCommandFromApiJsonDeserializer,
|
||||
loanRepositoryWrapper, loanAccountDomainService, noteRepository, loanTransactionRepository,
|
||||
loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService, calendarInstanceRepository,
|
||||
|
||||
@@ -209,4 +209,6 @@
|
||||
<include file="parts/0188_create_loan_buy_down_fee_balance.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0189_add_loan_buydown_fee_event.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0190_buy_down_fee_amortization.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0191_add_LoanApprovedAmountChangedBusinessEvent.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0192_create_loan_approved_amount_history.xml" relativeToChangelogFile="true" />
|
||||
</databaseChangeLog>
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet id="1" author="fineract">
|
||||
<insert tableName="m_external_event_configuration">
|
||||
<column name="type" value="LoanApprovedAmountChangedBusinessEvent"/>
|
||||
<column name="enabled" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||
<changeSet author="fineract" id="1" context="postgresql">
|
||||
<createTable tableName="m_loan_approved_amount_history">
|
||||
<column autoIncrement="true" name="id" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_m_loan_approved_amount_history"/>
|
||||
</column>
|
||||
<column name="loan_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="new_approved_amount" type="DECIMAL(19, 6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="old_approved_amount" type="DECIMAL(19, 6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="timestamp with time zone"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="timestamp with time zone"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
<changeSet author="fineract" id="2" context="mysql">
|
||||
<createTable tableName="m_loan_approved_amount_history">
|
||||
<column autoIncrement="true" name="id" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_m_loan_approved_amount_history"/>
|
||||
</column>
|
||||
<column name="loan_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="new_approved_amount" type="DECIMAL(19, 6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="old_approved_amount" type="DECIMAL(19, 6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="DATETIME"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="DATETIME"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
<changeSet author="fineract" id="3">
|
||||
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_loan_approved_amount_history"
|
||||
constraintName="FK_loan_approved_amount_history_created_by" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_appuser" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_loan_approved_amount_history"
|
||||
constraintName="FK_loan_approved_amount_history_last_modified_by" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_appuser" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="loan_id" baseTableName="m_loan_approved_amount_history"
|
||||
constraintName="FK_loan_approved_amount_history_loan_id" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_loan" validate="true"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+2
-2
@@ -112,7 +112,7 @@ public class ExternalEventConfigurationValidationServiceTest {
|
||||
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent", "LoanUndoContractTerminationBusinessEvent",
|
||||
"LoanBuyDownFeeTransactionCreatedBusinessEvent", "LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent",
|
||||
"LoanBuyDownFeeAmortizationTransactionCreatedBusinessEvent",
|
||||
"LoanBuyDownFeeAmortizationAdjustmentTransactionCreatedBusinessEvent");
|
||||
"LoanBuyDownFeeAmortizationAdjustmentTransactionCreatedBusinessEvent", "LoanApprovedAmountChangedBusinessEvent");
|
||||
|
||||
List<FineractPlatformTenant> tenants = Arrays
|
||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||
@@ -206,7 +206,7 @@ public class ExternalEventConfigurationValidationServiceTest {
|
||||
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent", "LoanUndoContractTerminationBusinessEvent",
|
||||
"LoanBuyDownFeeTransactionCreatedBusinessEvent", "LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent",
|
||||
"LoanBuyDownFeeAmortizationTransactionCreatedBusinessEvent",
|
||||
"LoanBuyDownFeeAmortizationAdjustmentTransactionCreatedBusinessEvent");
|
||||
"LoanBuyDownFeeAmortizationAdjustmentTransactionCreatedBusinessEvent", "LoanApprovedAmountChangedBusinessEvent");
|
||||
|
||||
List<FineractPlatformTenant> tenants = Arrays
|
||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||
|
||||
Reference in New Issue
Block a user