From 131b07318e9d8ecb28940f272f0bd11c9f6ff390 Mon Sep 17 00:00:00 2001 From: Nayan Date: Sat, 8 Nov 2014 19:01:00 +0530 Subject: [PATCH] Option to change approved amount during loan approval --- api-docs/apiLive.htm | 9 + .../LoanApplicationApprovalTest.java | 107 ++++ .../common/loans/LoanTransactionHelper.java | 28 ++ .../loanaccount/api/LoanApiConstants.java | 32 +- .../loanaccount/api/LoansApiResource.java | 39 +- .../loanaccount/data/LoanAccountData.java | 458 +++++++++--------- .../loanaccount/data/LoanApprovalData.java | 33 ++ .../portfolio/loanaccount/domain/Loan.java | 98 +++- .../domain/LoanAccountDomainService.java | 13 + .../domain/LoanAccountDomainServiceJpa.java | 21 + ...ApplicationTransitionApiJsonValidator.java | 43 +- ...WritePlatformServiceJpaRepositoryImpl.java | 28 +- .../service/LoanReadPlatformService.java | 3 + .../service/LoanReadPlatformServiceImpl.java | 16 +- ...4__adding_loan_proposed_amount_to_loan.sql | 2 + 15 files changed, 662 insertions(+), 268 deletions(-) create mode 100644 mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanApplicationApprovalTest.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApprovalData.java create mode 100644 mifosng-provider/src/main/resources/sql/migrations/core_db/V214__adding_loan_proposed_amount_to_loan.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index a08ca9155..19466e418 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -8023,6 +8023,15 @@ Request Body: approvedOnDate +
+ + + + + + + +
Optional Fields
approvedLoanAmount
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanApplicationApprovalTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanApplicationApprovalTest.java new file mode 100644 index 000000000..fbbd84c22 --- /dev/null +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanApplicationApprovalTest.java @@ -0,0 +1,107 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.integrationtests; + +import java.util.HashMap; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.builder.ResponseSpecBuilder; +import com.jayway.restassured.http.ContentType; +import com.jayway.restassured.specification.RequestSpecification; +import com.jayway.restassured.specification.ResponseSpecification; + +import org.mifosplatform.integrationtests.common.ClientHelper; +import org.mifosplatform.integrationtests.common.CommonConstants; +import org.mifosplatform.integrationtests.common.Utils; +import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder; +import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder; +import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker; +import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper; + +@SuppressWarnings("rawtypes") +public class LoanApplicationApprovalTest { + + private ResponseSpecification responseSpec; + private ResponseSpecification responseSpecForStatusCode403; + private RequestSpecification requestSpec; + private LoanTransactionHelper loanTransactionHelper; + + @Before + public void setup() { + Utils.initializeRESTAssured(); + this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + this.responseSpecForStatusCode403 = new ResponseSpecBuilder().expectStatusCode(403).build(); + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + } + + /* + * Positive test case: Approved amount non zero is less than proposed amount + * + */ + @Test + public void loanApplicationApprovedAmountLessThanProposedAmount() { + + final String proposedAmount = "8000"; + final String approvalAmount = "5000"; + final String approveDate = "20 September 2012"; + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012"); + final Integer loanProductID = this.loanTransactionHelper.getLoanProductId(new LoanProductTestBuilder().build(null)); + final Integer loanID = applyForLoanApplication(clientID, loanProductID, proposedAmount); + + HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID); + LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap); + + loanStatusHashMap = this.loanTransactionHelper.approveLoanWithApproveAmount(approveDate, approvalAmount, loanID); + LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap); + + } + + /* + * Negative test case: Approved amount non zero is greater than proposed amount + * + */ + @Test + public void loanApplicationApprovedAmountGreaterThanProposedAmount() { + + final String proposedAmount = "5000"; + final String approvalAmount = "9000"; + final String approveDate = "20 September 2011"; + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012"); + final Integer loanProductID = this.loanTransactionHelper.getLoanProductId(new LoanProductTestBuilder().build(null)); + final Integer loanID = applyForLoanApplication(clientID, loanProductID, proposedAmount); + + HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID); + LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap); + + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpecForStatusCode403); + + @SuppressWarnings("unchecked") + List error = (List) this.loanTransactionHelper.approveLoanWithApproveAmount(approveDate, approvalAmount, loanID, + CommonConstants.RESPONSE_ERROR); + + assertEquals("error.msg.loan.approval.amount.can't.be.greater.than.loan.amount.demanded", error + .get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); + + } + + private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID, final String proposedAmount) { + final String loanApplication = new LoanApplicationTestBuilder().withPrincipal(proposedAmount).withLoanTermFrequency("5") + .withLoanTermFrequencyAsMonths().withNumberOfRepayments("5").withRepaymentEveryAfter("1") + .withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("2").withExpectedDisbursementDate("04 April 2012") + .withSubmittedOnDate("02 April 2012").build(clientID.toString(), loanProductID.toString(), null); + return this.loanTransactionHelper.getLoanId(loanApplication); + } +} diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java index 21a8bb4ce..2cf84e814 100755 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java @@ -97,6 +97,19 @@ public class LoanTransactionHelper { public HashMap approveLoan(final String approvalDate, final Integer loanID) { return performLoanTransaction(createLoanOperationURL(APPROVE_LOAN_COMMAND, loanID), getApproveLoanAsJSON(approvalDate)); } + + public HashMap approveLoanWithApproveAmount(final String approvalDate, final String approvalAmount, final Integer loanID) { + return performLoanTransaction(createLoanOperationURL(APPROVE_LOAN_COMMAND, loanID), getApproveLoanAsJSON(approvalDate, approvalAmount)); + } + + public Object approveLoanWithApproveAmount(final String approvalDate, final String approvalAmount, final Integer loanID, + final String responseAttribute) { + + final String approvalURL = createLoanOperationURL(APPROVE_LOAN_COMMAND, loanID); + final String approvalJSONData = getApproveLoanAsJSON(approvalDate, approvalAmount); + + return performLoanTransaction(approvalURL, approvalJSONData, responseAttribute); + } public HashMap undoApproval(final Integer loanID) { final String undoBodyJson = "{'note':'UNDO APPROVAL'}"; @@ -206,6 +219,16 @@ public class LoanTransactionHelper { map.put("note", "Approval NOTE"); return new Gson().toJson(map); } + + private String getApproveLoanAsJSON(final String approvalDate, final String approvalAmount) { + final HashMap map = new HashMap<>(); + map.put("locale", "en"); + map.put("dateFormat", "dd MMMM yyyy"); + map.put("approvedLoanAmount", approvalAmount); + map.put("approvedOnDate", approvalDate); + map.put("note", "Approval NOTE"); + return new Gson().toJson(map); + } private String getRepaymentBodyAsJSON(final String transactionDate, final Float transactionAmount) { final HashMap map = new HashMap<>(); @@ -356,6 +379,11 @@ public class LoanTransactionHelper { return (HashMap) response.get("status"); } + private Object performLoanTransaction(final String postURLForLoanTransaction, final String jsonToBeSent, final String responseAttribute) { + return Utils.performServerPost(this.requestSpec, this.responseSpec, postURLForLoanTransaction, jsonToBeSent, responseAttribute); + } + + public HashMap getPrepayAmount(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final Integer loanID) { final String URL = "/mifosng-provider/api/v1/loans/" + loanID + "/transactions/template?command=prepayLoan&" + Utils.TENANT_IDENTIFIER; final HashMap response = Utils.performServerGet(requestSpec, responseSpec, URL, ""); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanApiConstants.java index dec97b8bc..a6b17f7de 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanApiConstants.java @@ -8,30 +8,20 @@ package org.mifosplatform.portfolio.loanaccount.api; public interface LoanApiConstants { public static final String emiAmountParameterName = "fixedEmiAmount"; - public static final String maxOutstandingBalanceParameterName = "maxOutstandingLoanBalance"; - public static final String disbursementDataParameterName = "disbursementData"; - public static final String disbursementDateParameterName = "expectedDisbursementDate"; - public static final String disbursementPrincipalParameterName = "principal"; - - public static final String DISBURSEMENT_DATE_START_WITH_ERROR = "first.disbursement.date.must.start.with.expected.disbursement.date"; - - public static final String PRINCIPAL_AMOUNT_SHOULD_BE_SAME = "sum.of.multi.disburse.amounts.must.equal.with.total.principal"; - - public static final String DISBURSEMENT_DATE_UNIQUE_ERROR = "disbursement.date.must.be.unique.for.tranches"; - public static final String disbursementIdParameterName = "id"; - public static final String principalDisbursedParameterName = "transactionAmount"; - public static final String ALREADY_DISBURSED = "can.not.change.disbursement.date"; - - public static final String LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE= "loancharge.with.calculation.type.interest.not.allowed"; - - public static final String LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE= "loancharge.with.calculation.type.principal.not.allowed"; + public static final String approvedLoanAmountParameterName = "approvedLoanAmount"; + public static final String approvedOnDateParameterName = "approvedOnDate"; + public static final String noteParameterName = "note"; + public static final String localeParameterName = "locale"; + public static final String dateFormatParameterName = "dateFormat"; + public static final String rejectedOnDateParameterName = "rejectedOnDate"; + public static final String withdrawnOnDateParameterName = "withdrawnOnDate"; // Interest recalculation related public static final String isInterestRecalculationEnabledParameterName = "isInterestRecalculationEnabled"; @@ -39,5 +29,13 @@ public interface LoanApiConstants { public static final String daysInMonthTypeParameterName = "daysInMonthType"; public static final String interestRecalculationCompoundingMethodParameterName = "interestRecalculationCompoundingMethod"; public static final String rescheduleStrategyMethodParameterName = "rescheduleStrategyMethod"; + + // Error codes + public static final String LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE= "loancharge.with.calculation.type.interest.not.allowed"; + public static final String LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE= "loancharge.with.calculation.type.principal.not.allowed"; + public static final String DISBURSEMENT_DATE_START_WITH_ERROR = "first.disbursement.date.must.start.with.expected.disbursement.date"; + public static final String PRINCIPAL_AMOUNT_SHOULD_BE_SAME = "sum.of.multi.disburse.amounts.must.equal.with.total.principal"; + public static final String DISBURSEMENT_DATE_UNIQUE_ERROR = "disbursement.date.must.be.unique.for.tranches"; + public static final String ALREADY_DISBURSED = "can.not.change.disbursement.date"; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java index 2254fd10b..570f12bf7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java @@ -65,6 +65,7 @@ import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; import org.mifosplatform.portfolio.loanaccount.data.DisbursementData; import org.mifosplatform.portfolio.loanaccount.data.LoanAccountData; +import org.mifosplatform.portfolio.loanaccount.data.LoanApprovalData; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; @@ -114,7 +115,8 @@ public class LoansApiResource { "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions", "syncDisbursementWithMeeting", "loanCounter", "loanProductCounter", "notes", "accountLinkingOptions", "linkedAccount")); - + + private final Set LOAN_APPROVAL_DATA_PARAMETERS = new HashSet<>(Arrays.asList("approvalDate", "approvalAmount")); private final String resourceNameForPermissions = "LOAN"; private final PlatformSecurityContext context; @@ -130,6 +132,7 @@ public class LoansApiResource { private final CodeValueReadPlatformService codeValueReadPlatformService; private final GroupReadPlatformService groupReadPlatformService; private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final DefaultToApiJsonSerializer loanApprovalDataToApiJsonSerializer; private final DefaultToApiJsonSerializer loanScheduleToApiJsonSerializer; private final ApiRequestParameterHelper apiRequestParameterHelper; private final FromJsonHelper fromJsonHelper; @@ -150,6 +153,7 @@ public class LoansApiResource { final GuarantorReadPlatformService guarantorReadPlatformService, final CodeValueReadPlatformService codeValueReadPlatformService, final GroupReadPlatformService groupReadPlatformService, final DefaultToApiJsonSerializer toApiJsonSerializer, + final DefaultToApiJsonSerializer loanApprovalDataToApiJsonSerializer, final DefaultToApiJsonSerializer loanScheduleToApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, final FromJsonHelper fromJsonHelper, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, @@ -170,6 +174,7 @@ public class LoansApiResource { this.codeValueReadPlatformService = codeValueReadPlatformService; this.groupReadPlatformService = groupReadPlatformService; this.toApiJsonSerializer = toApiJsonSerializer; + this.loanApprovalDataToApiJsonSerializer = loanApprovalDataToApiJsonSerializer; this.loanScheduleToApiJsonSerializer = loanScheduleToApiJsonSerializer; this.apiRequestParameterHelper = apiRequestParameterHelper; this.fromJsonHelper = fromJsonHelper; @@ -180,6 +185,38 @@ public class LoansApiResource { this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService; this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService; } + + /* + * This template API is used for loan approval, ideally this should be + * invoked on loan that are pending for approval. But system does not + * validate the status of the loan, it returns the template irrespective of + * loan status + * + */ + + @GET + @Path("{loanId}/template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveApprovalTemplate(@PathParam("loanId") final Long loanId, @QueryParam("templateType") final String templateType, + @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); + + 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, this.LOAN_APPROVAL_DATA_PARAMETERS); + + } + @GET @Path("template") diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java index 43c8ada8c..40ae42e19 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java @@ -76,6 +76,8 @@ public class LoanAccountData { private final CurrencyData currency; private final BigDecimal principal; private final BigDecimal approvedPrincipal; + private final BigDecimal proposedPrincipal; + private final Integer termFrequency; private final EnumOptionData termPeriodFrequencyType; private final Integer numberOfRepayments; @@ -194,6 +196,7 @@ public class LoanAccountData { final Long loanOfficerId = null; final String loanOfficerName = null; final CurrencyData currencyData = null; + final BigDecimal proposedPrincipal = null; final BigDecimal principal = null; final BigDecimal totalOverpaid = null; final BigDecimal inArrearsTolerance = null; @@ -265,19 +268,19 @@ public class LoanAccountData { return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, - currencyData, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency, termPeriodFrequencyType, - numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionProcessingStrategyId, - transactionProcessingStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, - interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, - graceOnInterestCharged, interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, - transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, - repaymentFrequencyTypeOptions, transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions, - amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, - chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, - syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount, - disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations, - product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType, - isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); + currencyData, proposedPrincipal, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency, + termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, + transactionProcessingStrategyId, transactionProcessingStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, + annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, + graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, + repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, + termFrequencyTypeOptions, repaymentFrequencyTypeOptions, transactionProcessingStrategyOptions, + interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, + chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, + calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, + linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, + memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, + daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); } /** @@ -303,6 +306,7 @@ public class LoanAccountData { final Long loanOfficerId = null; final String loanOfficerName = null; final CurrencyData currencyData = null; + final BigDecimal proposedPrincipal = null; final BigDecimal principal = null; final BigDecimal totalOverpaid = null; final BigDecimal inArrearsTolerance = null; @@ -375,18 +379,18 @@ public class LoanAccountData { return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, - currencyData, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency, termPeriodFrequencyType, - numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionProcessingStrategyId, - transactionProcessingStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, - interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, - graceOnInterestCharged, interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, - transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, - repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, - interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, - loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, - notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, - maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, - isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); + currencyData, proposedPrincipal, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency, + termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, + transactionProcessingStrategyId, transactionProcessingStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, + annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, + graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, + repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, + termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, + amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, + loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, + loanProductCounter, notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, + fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, + overdueCharges, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); } public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) { @@ -394,22 +398,22 @@ public class LoanAccountData { return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, clientAcc.clientId, clientAcc.clientName, clientAcc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, - acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, - acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, - acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType, - acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, - acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, - acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate, clientAcc.timeline, acc.summary, - acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges, acc.collateral, acc.guarantors, - acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, - acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, - acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, - acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, - acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, - acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, - acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, - acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, - acc.originalSchedule); + acc.proposedPrincipal, acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, + acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, + acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, + acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, + acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate, + acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate, clientAcc.timeline, + acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges, acc.collateral, + acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions, + acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, + acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, + null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, + acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, + acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, + acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, + acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, + acc.interestRecalculationData, acc.originalSchedule); } /** @@ -436,6 +440,7 @@ public class LoanAccountData { final Long loanOfficerId = null; final String loanOfficerName = null; final CurrencyData currencyData = null; + final BigDecimal proposedPrincipal = null; final BigDecimal principal = null; final BigDecimal totalOverpaid = null; final BigDecimal inArrearsTolerance = null; @@ -508,40 +513,40 @@ public class LoanAccountData { return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, - currencyData, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency, termPeriodFrequencyType, - numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionProcessingStrategyId, - transactionProcessingStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, - interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, - graceOnInterestCharged, interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, - transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, - repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, - interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, - loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, - notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingBalance, - emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, - daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); + currencyData, proposedPrincipal, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency, + termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, + transactionProcessingStrategyId, transactionProcessingStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, + annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, + graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, + repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, + termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, + amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, + loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, + loanProductCounter, notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, + maxOutstandingBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, + daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); } public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) { return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId, groupAcc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName, - acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal, - acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, - acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, - acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType, - acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate, - acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate, - groupAcc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges, - acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions, - acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, - acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, - acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, - acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, - acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, - acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, - acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, - acc.originalSchedule); + acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal, + acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, + acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, + acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, + acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, + acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, + acc.interestChargedFromDate, groupAcc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, + acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, + acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, + acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, + acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, + acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, + acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, + acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, + acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, + acc.interestRecalculationData, acc.originalSchedule); } public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product, @@ -618,6 +623,8 @@ public class LoanAccountData { final Collection disbursementData = null; final Collection emiAmountVariations = null; BigDecimal principal = null; + BigDecimal proposedPrincipal = null; + BigDecimal interestRatePerPeriod = null; Integer numberOfRepayments = null; if (product.useBorrowerCycle() && loanCycleNumber > 0) { @@ -628,6 +635,7 @@ public class LoanAccountData { Collection repaymentVariationsForBorrowerCycle = product .getNumberOfRepaymentVariationsForBorrowerCycle(); principal = fetchLoanCycleDefaultValue(principalVariationsForBorrowerCycle, loanCycleNumber); + proposedPrincipal = principal; interestRatePerPeriod = fetchLoanCycleDefaultValue(interestForVariationsForBorrowerCycle, loanCycleNumber); BigDecimal numberofRepaymentval = fetchLoanCycleDefaultValue(repaymentVariationsForBorrowerCycle, loanCycleNumber); if (numberofRepaymentval != null) { @@ -636,6 +644,7 @@ public class LoanAccountData { } if (principal == null) { principal = product.getPrincipal(); + proposedPrincipal = principal; } if (interestRatePerPeriod == null) { interestRatePerPeriod = product.getInterestRatePerPeriod(); @@ -653,22 +662,22 @@ public class LoanAccountData { return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), loanPurposeId, - loanPurposeName, loanOfficerId, loanOfficerName, product.getCurrency(), principal, principal, totalOverpaid, - product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, numberOfRepayments, product.getRepaymentEvery(), - product.getRepaymentFrequencyType(), product.getTransactionProcessingStrategyId(), transactionProcessingStrategyName, - product.getAmortizationType(), interestRatePerPeriod, product.getInterestRateFrequencyType(), - product.getAnnualInterestRate(), product.getInterestType(), product.getInterestCalculationPeriodType(), - expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(), product.getGraceOnInterestPayment(), - product.getGraceOnInterestCharged(), interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, - repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, - repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, - interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, - loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, - notes, accountLinkingOptions, linkedAccount, disbursementData, product.getMultiDisburseLoan(), fixedEmi, - product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears, - product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA, product.getDaysInMonthType(), - product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(), - originalSchedule); + loanPurposeName, loanOfficerId, loanOfficerName, product.getCurrency(), proposedPrincipal, principal, principal, + totalOverpaid, product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, numberOfRepayments, + product.getRepaymentEvery(), product.getRepaymentFrequencyType(), product.getTransactionProcessingStrategyId(), + transactionProcessingStrategyName, product.getAmortizationType(), interestRatePerPeriod, + product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), product.getInterestType(), + product.getInterestCalculationPeriodType(), expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(), + product.getGraceOnInterestPayment(), product.getGraceOnInterestCharged(), interestChargedFromDate, timeline, summary, + feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, + termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, + amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, + loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, + loanProductCounter, notes, accountLinkingOptions, linkedAccount, disbursementData, product.getMultiDisburseLoan(), + fixedEmi, product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, + inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA, + product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(), + product.toLoanInterestRecalculationData(), originalSchedule); } public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) { @@ -706,23 +715,23 @@ public class LoanAccountData { return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId, acc.group, acc.loanType, product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, - product.getCurrency(), product.getPrincipal(), product.getPrincipal(), acc.totalOverpaid, product.getInArrearsTolerance(), - termFrequency, termPeriodFrequencyType, product.getNumberOfRepayments(), product.getRepaymentEvery(), - product.getRepaymentFrequencyType(), product.getTransactionProcessingStrategyId(), - product.getTransactionProcessingStrategyName(), product.getAmortizationType(), product.getInterestRatePerPeriod(), - product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), product.getInterestType(), - product.getInterestCalculationPeriodType(), acc.expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(), - product.getGraceOnInterestPayment(), product.getGraceOnInterestCharged(), acc.interestChargedFromDate, acc.timeline, - acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, - calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, - interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, - fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, - acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes, acc.accountLinkingOptions, - acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), acc.fixedEmiAmount, - product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, acc.inArrears, - product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA, product.getDaysInMonthType(), - product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(), - acc.originalSchedule); + product.getCurrency(), product.getPrincipal(), product.getPrincipal(), product.getPrincipal(), acc.totalOverpaid, + product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, product.getNumberOfRepayments(), + product.getRepaymentEvery(), product.getRepaymentFrequencyType(), + product.getTransactionProcessingStrategyId(), product.getTransactionProcessingStrategyName(), product.getAmortizationType(), + product.getInterestRatePerPeriod(), product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), + product.getInterestType(), product.getInterestCalculationPeriodType(), acc.expectedFirstRepaymentOnDate, + product.getGraceOnPrincipalPayment(), product.getGraceOnInterestPayment(), product.getGraceOnInterestCharged(), acc.interestChargedFromDate, + acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, + guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, + repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, + interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, + calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes, + acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), + acc.fixedEmiAmount, product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, + acc.inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA, + product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(), + product.toLoanInterestRecalculationData(), acc.originalSchedule); } /* @@ -733,7 +742,7 @@ public class LoanAccountData { final String externalId, final Long clientId, final String clientName, final Long clientOfficeId, final GroupGeneralData group, final EnumOptionData loanType, final Long loanProductId, final String loanProductName, final String loanProductDescription, final Long fundId, final String fundName, final Long loanPurposeId, final String loanPurposeName, final Long loanOfficerId, - final String loanOfficerName, final CurrencyData currencyData, final BigDecimal principal, final BigDecimal approvedPrincipal, + final String loanOfficerName, final CurrencyData currencyData, final BigDecimal proposedPrincipal, final BigDecimal principal, final BigDecimal approvedPrincipal, final BigDecimal totalOverpaid, final BigDecimal inArrearsTolerance, final Integer termFrequency, final EnumOptionData termPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery, final EnumOptionData repaymentFrequencyType, final Long transactionStrategyId, final String transactionStrategyName, @@ -781,18 +790,18 @@ public class LoanAccountData { return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, - currencyData, principal, approvedPrincipal, totalOverpaid, inArrearsTolerance, termFrequency, termPeriodFrequencyType, - numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionStrategyId, transactionStrategyName, - amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, - interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, - graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged, - repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, - repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, - interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, - loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, - notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmont, outstandingLoanBalance, - emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, - daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); + currencyData, proposedPrincipal, principal, approvedPrincipal, totalOverpaid, inArrearsTolerance, termFrequency, + termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionStrategyId, + transactionStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, + interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, + graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary, + feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, + termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, + amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, + loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, + loanProductCounter, notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmont, + outstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, + daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); } /* @@ -816,21 +825,21 @@ public class LoanAccountData { return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName, - acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal, - acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, - acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, - acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType, - acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate, - acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate, - acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, - guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, - transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, - interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, - loanCollateralOptions, calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes, - accountLinkingOptions, linkedAccount, disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, - acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, - acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, - acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule); + acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal, + acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, + acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, + acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, + acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, + acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, + acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, + collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, + repaymentFrequencyTypeOptions, transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, + interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, + loanPurposeOptions, loanCollateralOptions, calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, + notes, accountLinkingOptions, linkedAccount, disbursementDetails, acc.multiDisburseLoan, + acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, + acc.inArrears, acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, + acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule); } public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection productOptions, @@ -847,22 +856,22 @@ public class LoanAccountData { public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) { return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId, group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName, - acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal, - acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, - acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, - acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType, - acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate, - acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate, - acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges, - acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions, - acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, - acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, - acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, - acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, - acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, - acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, - acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, - acc.originalSchedule); + acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal, + acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, + acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, + acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, + acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, + acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, + acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, + acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, + acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, + acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, + acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, + acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, + acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, + acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, + acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, + acc.interestRecalculationData, acc.originalSchedule); } public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map memberLoanCycle) { @@ -905,22 +914,22 @@ public class LoanAccountData { return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName, - acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal, - acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, - acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, - acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType, - acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate, - acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate, - acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges, - acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions, - acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, - acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, - acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, - acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, - acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, - acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, - acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, - acc.originalSchedule); + acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal, + acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, + acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, + acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, + acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, + acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, + acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, + acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, + acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, + acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, + acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, + acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, + acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, + acc.maxOutstandingLoanBalance, acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, + acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, + acc.interestRecalculationData, acc.originalSchedule); } public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData) { @@ -929,44 +938,44 @@ public class LoanAccountData { acc.interestRecalculationData, calendarData); return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName, - acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal, - acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, - acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, - acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType, - acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate, - acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate, - acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges, - acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions, - acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, - acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, - acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, - acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, - acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, - acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, - acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, interestRecalculationData, - acc.originalSchedule); + acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal, + acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, + acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, + acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, + acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, + acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, + acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, + acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, + acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, + acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, + acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, + acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, + acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, + acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, + acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, + interestRecalculationData, acc.originalSchedule); } public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) { return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName, - acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal, - acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, - acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, - acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType, - acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate, - acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate, - acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges, - acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions, - acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, - acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, - acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, - acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, - acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, - acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, - acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, - originalSchedule); + acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal, + acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, + acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, + acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, + acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, + acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, + acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, + acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, + acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, + acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, + acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, + acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, + acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, + acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, + acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, + acc.interestRecalculationData, originalSchedule); } private LoanAccountData( @@ -989,40 +998,40 @@ public class LoanAccountData { final Long loanOfficerId, final String loanOfficerName, // final CurrencyData currency, + BigDecimal proposedPrincipal, final BigDecimal principal, final BigDecimal approvedPrincipal, - final BigDecimal totalOverpaid, - final BigDecimal inArrearsTolerance, // - final Integer termFrequency, - final EnumOptionData termPeriodFrequencyType, // + final BigDecimal totalOverpaid, // + final BigDecimal inArrearsTolerance, + final Integer termFrequency, // + final EnumOptionData termPeriodFrequencyType, final Integer numberOfRepayments, - final Integer repaymentEvery, - final EnumOptionData repaymentFrequencyType, // - final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName, - final EnumOptionData amortizationType, final BigDecimal interestRatePerPeriod, final EnumOptionData interestRateFrequencyType, - final BigDecimal annualInterestRate, final EnumOptionData interestType, final EnumOptionData interestCalculationPeriodType, - final LocalDate expectedFirstRepaymentOnDate, final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, - final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline, - final LoanSummaryData summary, final BigDecimal feeChargesDueAtDisbursementCharged, final LoanScheduleData repaymentSchedule, - final Collection transactions, final Collection charges, - final Collection collateral, final Collection guarantors, final CalendarData meeting, - final Collection productOptions, final Collection termFrequencyTypeOptions, + final Integer repaymentEvery, // + final EnumOptionData repaymentFrequencyType, final Long transactionProcessingStrategyId, + final String transactionProcessingStrategyName, final EnumOptionData amortizationType, final BigDecimal interestRatePerPeriod, + final EnumOptionData interestRateFrequencyType, final BigDecimal annualInterestRate, final EnumOptionData interestType, + final EnumOptionData interestCalculationPeriodType, final LocalDate expectedFirstRepaymentOnDate, final Integer graceOnPrincipalPayment, + final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, + final LoanApplicationTimelineData timeline, final LoanSummaryData summary, final BigDecimal feeChargesDueAtDisbursementCharged, + final LoanScheduleData repaymentSchedule, final Collection transactions, + final Collection charges, final Collection collateral, final Collection guarantors, + final CalendarData meeting, final Collection productOptions, + final Collection termFrequencyTypeOptions, final Collection repaymentFrequencyTypeOptions, - final Collection transactionProcessingStrategyOptions, - final Collection interestRateFrequencyTypeOptions, final Collection amortizationTypeOptions, - final Collection interestTypeOptions, final Collection interestCalculationPeriodTypeOptions, - final Collection fundOptions, final Collection chargeOptions, final ChargeData chargeTemplate, - final Collection loanOfficerOptions, final Collection loanPurposeOptions, - final Collection loanCollateralOptions, final Collection calendarOptions, - final Boolean syncDisbursementWithMeeting, final Integer loanCounter, final Integer loanProductCounter, - final Collection notes, final Collection accountLinkingOptions, - final PortfolioAccountData linkedAccount, final Collection disbursementDetails, - final Boolean multiDisburseLoan, BigDecimal fixedEmiAmount, final BigDecimal maxOutstandingLoanBalance, - final Collection emiAmountVariations, final Map memberVariations, - final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing, - final Collection overdueCharges, final Boolean isNPA, final EnumOptionData daysInMonthType, - final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled, - final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule) { + final Collection transactionProcessingStrategyOptions, final Collection interestRateFrequencyTypeOptions, + final Collection amortizationTypeOptions, final Collection interestTypeOptions, + final Collection interestCalculationPeriodTypeOptions, final Collection fundOptions, final Collection chargeOptions, + final ChargeData chargeTemplate, final Collection loanOfficerOptions, + final Collection loanPurposeOptions, final Collection loanCollateralOptions, + final Collection calendarOptions, final Boolean syncDisbursementWithMeeting, final Integer loanCounter, + final Integer loanProductCounter, final Collection notes, + final Collection accountLinkingOptions, final PortfolioAccountData linkedAccount, + final Collection disbursementDetails, final Boolean multiDisburseLoan, BigDecimal fixedEmiAmount, + final BigDecimal maxOutstandingLoanBalance, final Collection emiAmountVariations, + final Map memberVariations, final LoanProductData product, final Boolean inArrears, + final Integer graceOnArrearsAgeing, final Collection overdueCharges, final Boolean isNPA, + final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, + final boolean isInterestRecalculationEnabled, final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule) { this.id = id; this.accountNo = accountNo; this.status = status; @@ -1042,6 +1051,7 @@ public class LoanAccountData { this.loanOfficerId = loanOfficerId; this.loanOfficerName = loanOfficerName; this.currency = currency; + this.proposedPrincipal = proposedPrincipal; this.principal = principal; this.approvedPrincipal = approvedPrincipal; this.totalOverpaid = totalOverpaid; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApprovalData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApprovalData.java new file mode 100644 index 000000000..497c537e1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApprovalData.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.loanaccount.data; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; + +/** + * Immutable data object representing a loan transaction. + */ +public class LoanApprovalData { + + private final LocalDate approvalDate; + private final BigDecimal approvalAmount; + + public LoanApprovalData(final BigDecimal approvalAmount, final LocalDate approvalDate) { + this.approvalDate = approvalDate; + this.approvalAmount = approvalAmount; + } + + public LocalDate getApprovalDate() { + return this.approvalDate; + } + + public BigDecimal getApprovalAmount() { + return this.approvalAmount; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index ef00c3a4f..23f3c475e 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -298,6 +298,9 @@ public class Loan extends AbstractPersistable { @Transient private LoanSummaryWrapper loanSummaryWrapper; + @Column(name = "principal_amount_proposed", scale = 6, precision = 19, nullable = false) + private BigDecimal proposedPrincipal; + @Column(name = "approved_principal", scale = 6, precision = 19, nullable = false) private BigDecimal approvedPrincipal; @@ -422,6 +425,16 @@ public class Loan extends AbstractPersistable { this.maxOutstandingLoanBalance = maxOutstandingLoanBalance; this.disbursementDetails = disbursementDetails; this.approvedPrincipal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount(); + + /* + * During loan origination stage and before loan is approved + * principal_amount, approved_principal and principal_amount_demanded + * will same amount and that amount is same as applicant loan demanded + * amount. + */ + + this.proposedPrincipal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount(); + } private LoanSummary updateSummaryWithTotalFeeChargesDueAtDisbursement(final BigDecimal feeChargesDueAtDisbursement) { @@ -1345,6 +1358,11 @@ public class Loan extends AbstractPersistable { this.approvedPrincipal = newValue; } + if (command.isChangeInBigDecimalParameterNamed(principalParamName, this.proposedPrincipal)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(principalParamName); + this.proposedPrincipal = newValue; + } + if (loanProduct.isMultiDisburseLoan()) { updateDisbursementDetails(command, actualChanges); if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount)) { @@ -1817,9 +1835,26 @@ public class Loan extends AbstractPersistable { final Map actualChanges = new LinkedHashMap<>(); - final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, LoanStatus.fromInt(this.loanStatus)); - if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { - this.loanStatus = statusEnum.getValue(); + /* + * statusEnum is holding the possible new status derived from + * loanLifecycleStateMachine.transition. + * + */ + + final LoanStatus newStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, LoanStatus.fromInt(this.loanStatus)); + + + /* + * FIXME: There is no need to check below condition, if + * loanLifecycleStateMachine.transition is doing it's responsibility + * properly. Better implementation approach is, if code passes invalid + * combination of states (fromState and toState), state machine should + * return invalidate state and below if condition should check for not + * equal to invalidateState, instead of check new value is same as present value. + */ + + if (!newStatusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { + this.loanStatus = newStatusEnum.getValue(); actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); // only do below if status has changed in the 'approval' case @@ -1830,6 +1865,36 @@ public class Loan extends AbstractPersistable { approvedOnDateChange = command.stringValueOfParameterNamed("eventDate"); } + BigDecimal approvedLoanAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.approvedLoanAmountParameterName); + + if (approvedLoanAmount != null) { + + // Approved amount has to be less than or equal to principal + // amount demanded + + if (approvedLoanAmount.compareTo(this.proposedPrincipal) < 1) { + + this.approvedPrincipal = approvedLoanAmount; + + /* + * All the calculations are done based on the principal + * amount, so it is necessary to set principal amount to approved + * amount + */ + + this.loanRepaymentScheduleDetail.setPrincipal(approvedLoanAmount); + + + actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, approvedLoanAmount); + actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, approvedLoanAmount); + + } else { + final String errorMessage = "Loan approved amount can't be greater than loan amount demanded."; + throw new InvalidLoanStateTransitionException("approval", "amount.can't.be.greater.than.loan.amount.demanded", + errorMessage, this.proposedPrincipal, approvedLoanAmount); + } + } + this.approvedOnDate = approvedOn.toDate(); this.approvedBy = currentUser; actualChanges.put("locale", command.locale()); @@ -1874,6 +1939,16 @@ public class Loan extends AbstractPersistable { this.approvedOnDate = null; this.approvedBy = null; + + if(this.approvedPrincipal.compareTo(this.proposedPrincipal) != 0){ + this.approvedPrincipal = this.proposedPrincipal; + this.loanRepaymentScheduleDetail.setPrincipal(this.proposedPrincipal); + + actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, this.proposedPrincipal); + actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, this.proposedPrincipal); + + } + actualChanges.put("approvedOnDate", ""); this.loanOfficerHistory.clear(); @@ -2154,7 +2229,7 @@ public class Loan extends AbstractPersistable { * Ability to regenerate the repayment schedule based on the loans current * details/state. */ - private void regenerateRepaymentSchedule(final ScheduleGeneratorDTO scheduleGeneratorDTO) { + public void regenerateRepaymentSchedule(final ScheduleGeneratorDTO scheduleGeneratorDTO) { final LoanScheduleModel loanSchedule = regenerateScheduleModel(scheduleGeneratorDTO); @@ -2318,7 +2393,7 @@ public class Loan extends AbstractPersistable { final boolean isScheduleRegenerateRequired = isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate); this.actualDisbursementDate = null; this.disbursedBy = null; - boolean isDisbueseAmtChanged = !this.approvedPrincipal.equals(this.loanRepaymentScheduleDetail.getPrincipal()); + boolean isDisbursedAmountChanged = !this.approvedPrincipal.equals(this.loanRepaymentScheduleDetail.getPrincipal()); this.loanRepaymentScheduleDetail.setPrincipal(this.approvedPrincipal); if (this.loanProduct.isMultiDisburseLoan()) { for (final LoanDisbursementDetails details : this.disbursementDetails) { @@ -2328,13 +2403,13 @@ public class Loan extends AbstractPersistable { } boolean isEmiAmountChanged = this.loanTermVariations.size() > 0; updateLoanToPreDisbursalState(); - if (isScheduleRegenerateRequired || isDisbueseAmtChanged || isEmiAmountChanged + if (isScheduleRegenerateRequired || isDisbursedAmountChanged || isEmiAmountChanged || this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { // clear off actual disbusrement date so schedule regeneration // uses expected date. regenerateRepaymentSchedule(scheduleGeneratorDTO); - if (isDisbueseAmtChanged) { + if (isDisbursedAmountChanged) { updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); } } @@ -3334,6 +3409,10 @@ public class Loan extends AbstractPersistable { return expectedDisbursementDate; } + /* + * Reason for derving + */ + public BigDecimal getDisburseAmountForTemplate() { BigDecimal principal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount(); Collection details = fetchUndisbursedDetail(); @@ -3654,6 +3733,11 @@ public class Loan extends AbstractPersistable { public Set getCollateral() { return this.collateral; } + + public BigDecimal getProposedPrincipal(){ + return this.proposedPrincipal; + } + public Map deriveAccountingBridgeData(final CurrencyData currencyData, final List existingTransactionIds, final List existingReversedTransactionIds, boolean isAccountTransfer) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainService.java index c98fbb83f..0dc4ca143 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainService.java @@ -10,6 +10,7 @@ import java.math.BigDecimal; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; +import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; public interface LoanAccountDomainService { @@ -39,4 +40,16 @@ public interface LoanAccountDomainService { * @param loan */ void recalculateAccruals(Loan loan); + + /** + * This method is used for building ScheduleGeneratorDTO from loan, usages + * are as, if have loan and some of the terms are modified which may impact + * the schedule, in that case need to recompute the schedule, so this method + * helps to build the ScheduleGeneratorDTO from modified loan. + * + * @param loan + */ + + public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan); + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index 8784bd049..7439fe687 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -500,5 +500,26 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { this.accountTransferRepository.save(transferTransaction); } } + + public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan) { + + final MonetaryCurrency currency = loan.getCurrency(); + ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency); + LocalDate calculatedRepaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate(); + final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); + final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan + .getExpectedDisbursedOnLocalDate().toDate(), HolidayStatusType.ACTIVE.getValue()); + final WorkingDays workingDays = this.workingDaysRepository.findOne(); + final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); + final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); + + HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday, + allowTransactionsOnNonWorkingDay); + + ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency, + calculatedRepaymentsStartingFromDate, holidayDetailDTO); + + return scheduleGeneratorDTO; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationTransitionApiJsonValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationTransitionApiJsonValidator.java index f0e1ea8c6..e293d57f5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationTransitionApiJsonValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationTransitionApiJsonValidator.java @@ -6,6 +6,7 @@ package org.mifosplatform.portfolio.loanaccount.serialization; import java.lang.reflect.Type; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -20,6 +21,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -45,7 +47,9 @@ public final class LoanApplicationTransitionApiJsonValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } - final Set disbursementParameters = new HashSet<>(Arrays.asList("approvedOnDate", "note", "locale", "dateFormat")); + final Set disbursementParameters = new HashSet<>(Arrays.asList(LoanApiConstants.approvedLoanAmountParameterName, + LoanApiConstants.approvedOnDateParameterName, LoanApiConstants.noteParameterName, LoanApiConstants.localeParameterName, + LoanApiConstants.dateFormatParameterName)); final Type typeOfMap = new TypeToken>() {}.getType(); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters); @@ -54,11 +58,18 @@ public final class LoanApplicationTransitionApiJsonValidator { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanapplication"); final JsonElement element = this.fromApiJsonHelper.parse(json); - final LocalDate approvedOnDate = this.fromApiJsonHelper.extractLocalDateNamed("approvedOnDate", element); - baseDataValidator.reset().parameter("approvedOnDate").value(approvedOnDate).notNull(); - final String note = this.fromApiJsonHelper.extractStringNamed("note", element); - baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000); + final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed( + LoanApiConstants.approvedLoanAmountParameterName, element); + baseDataValidator.reset().parameter(LoanApiConstants.approvedLoanAmountParameterName).value(principal).ignoreIfNull() + .positiveAmount(); + + final LocalDate approvedOnDate = this.fromApiJsonHelper + .extractLocalDateNamed(LoanApiConstants.approvedOnDateParameterName, element); + baseDataValidator.reset().parameter(LoanApiConstants.approvedOnDateParameterName).value(approvedOnDate).notNull(); + + final String note = this.fromApiJsonHelper.extractStringNamed(LoanApiConstants.noteParameterName, element); + baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000); throwExceptionIfValidationWarningsExist(dataValidationErrors); } @@ -67,7 +78,8 @@ public final class LoanApplicationTransitionApiJsonValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } - final Set disbursementParameters = new HashSet<>(Arrays.asList("rejectedOnDate", "note", "locale", "dateFormat")); + final Set disbursementParameters = new HashSet<>(Arrays.asList(LoanApiConstants.rejectedOnDateParameterName, + LoanApiConstants.noteParameterName, LoanApiConstants.localeParameterName, LoanApiConstants.dateFormatParameterName)); final Type typeOfMap = new TypeToken>() {}.getType(); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters); @@ -76,11 +88,11 @@ public final class LoanApplicationTransitionApiJsonValidator { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanapplication"); final JsonElement element = this.fromApiJsonHelper.parse(json); - final LocalDate rejectedOnDate = this.fromApiJsonHelper.extractLocalDateNamed("rejectedOnDate", element); - baseDataValidator.reset().parameter("rejectedOnDate").value(rejectedOnDate).notNull(); + final LocalDate rejectedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.rejectedOnDateParameterName, element); + baseDataValidator.reset().parameter(LoanApiConstants.rejectedOnDateParameterName).value(rejectedOnDate).notNull(); - final String note = this.fromApiJsonHelper.extractStringNamed("note", element); - baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000); + final String note = this.fromApiJsonHelper.extractStringNamed(LoanApiConstants.noteParameterName, element); + baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000); throwExceptionIfValidationWarningsExist(dataValidationErrors); } @@ -89,7 +101,8 @@ public final class LoanApplicationTransitionApiJsonValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } - final Set disbursementParameters = new HashSet<>(Arrays.asList("withdrawnOnDate", "note", "locale", "dateFormat")); + final Set disbursementParameters = new HashSet<>(Arrays.asList(LoanApiConstants.withdrawnOnDateParameterName, + LoanApiConstants.noteParameterName, LoanApiConstants.localeParameterName, LoanApiConstants.dateFormatParameterName)); final Type typeOfMap = new TypeToken>() {}.getType(); this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters); @@ -98,11 +111,11 @@ public final class LoanApplicationTransitionApiJsonValidator { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanapplication"); final JsonElement element = this.fromApiJsonHelper.parse(json); - final LocalDate withdrawnOnDate = this.fromApiJsonHelper.extractLocalDateNamed("withdrawnOnDate", element); - baseDataValidator.reset().parameter("withdrawnOnDate").value(withdrawnOnDate).notNull(); + final LocalDate withdrawnOnDate = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.withdrawnOnDateParameterName, element); + baseDataValidator.reset().parameter(LoanApiConstants.withdrawnOnDateParameterName).value(withdrawnOnDate).notNull(); - final String note = this.fromApiJsonHelper.extractStringNamed("note", element); - baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000); + final String note = this.fromApiJsonHelper.extractStringNamed(LoanApiConstants.noteParameterName, element); + baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000); throwExceptionIfValidationWarningsExist(dataValidationErrors); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index f77e17cd3..df582f4f6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -48,9 +48,12 @@ import org.mifosplatform.portfolio.fund.domain.Fund; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper; import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; +import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; +import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService; import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; @@ -120,7 +123,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa private final AccountAssociationsRepository accountAssociationsRepository; private final LoanReadPlatformService loanReadPlatformService; private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository; - + private final LoanAccountDomainService loanAccountDomainService; + @Autowired public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper, final LoanApplicationTransitionApiJsonValidator loanApplicationTransitionApiJsonValidator, @@ -135,7 +139,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final CalendarRepository calendarRepository, final CalendarInstanceRepository calendarInstanceRepository, final SavingsAccountAssembler savingsAccountAssembler, final AccountAssociationsRepository accountAssociationsRepository, final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository, - final LoanReadPlatformService loanReadPlatformService) { + final LoanReadPlatformService loanReadPlatformService, final LoanAccountDomainService loanAccountDomainService) { this.context = context; this.fromJsonHelper = fromJsonHelper; this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator; @@ -160,6 +164,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa this.accountAssociationsRepository = accountAssociationsRepository; this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository; this.loanReadPlatformService = loanReadPlatformService; + this.loanAccountDomainService = loanAccountDomainService; + } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { @@ -720,6 +726,14 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final Map changes = loan.loanApplicationApproval(currentUser, command, defaultLoanLifecycleStateMachine()); if (!changes.isEmpty()) { + + // If loan approved amount less than loan demanded amount, then need + // to recompute the schedule + if (changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName)) { + ScheduleGeneratorDTO scheduleGeneratorDTO = loanAccountDomainService.buildScheduleGeneratorDTO(loan); + loan.regenerateRepaymentSchedule(scheduleGeneratorDTO); + } + this.loanRepository.save(loan); final String noteText = command.stringValueOfParameterNamed("note"); @@ -754,6 +768,16 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final Map changes = loan.undoApproval(defaultLoanLifecycleStateMachine()); if (!changes.isEmpty()) { + + // If loan approved amount is not same as loan amount demanded, then + // during undo, restore the demand amount to principal amount. + + if (changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName) + || changes.containsKey(LoanApiConstants.disbursementPrincipalParameterName )) { + ScheduleGeneratorDTO scheduleGeneratorDTO = loanAccountDomainService.buildScheduleGeneratorDTO(loan); + loan.regenerateRepaymentSchedule(scheduleGeneratorDTO); + } + this.loanRepository.save(loan); final String noteText = command.stringValueOfParameterNamed("note"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java index b05c03e1c..92d0cefe7 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java @@ -14,6 +14,7 @@ import org.mifosplatform.portfolio.calendar.data.CalendarData; import org.mifosplatform.portfolio.group.service.SearchParameters; import org.mifosplatform.portfolio.loanaccount.data.DisbursementData; import org.mifosplatform.portfolio.loanaccount.data.LoanAccountData; +import org.mifosplatform.portfolio.loanaccount.data.LoanApprovalData; import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData; import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; @@ -44,6 +45,8 @@ public interface LoanReadPlatformService { LoanTransactionData retrieveNewClosureDetails(); LoanTransactionData retrieveDisbursalTemplate(Long loanId, boolean paymentDetailsRequired); + + LoanApprovalData retrieveApprovalTemplate(Long loanId); LoanAccountData retrieveTemplateWithCompleteGroupAndProductDetails(Long groupId, Long productId); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 8b31a87a0..b9ba20bd5 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -60,6 +60,7 @@ import org.mifosplatform.portfolio.group.service.SearchParameters; import org.mifosplatform.portfolio.loanaccount.data.DisbursementData; import org.mifosplatform.portfolio.loanaccount.data.LoanAccountData; import org.mifosplatform.portfolio.loanaccount.data.LoanApplicationTimelineData; +import org.mifosplatform.portfolio.loanaccount.data.LoanApprovalData; import org.mifosplatform.portfolio.loanaccount.data.LoanInterestRecalculationData; import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData; import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData; @@ -439,6 +440,16 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { } + @Override + public LoanApprovalData retrieveApprovalTemplate(final Long loanId) { + + final Loan loan = this.loanRepository.findOne(loanId); + if (loan == null) { throw new LoanNotFoundException(loanId); } + + return new LoanApprovalData(loan.getProposedPrincipal(),DateUtils.getLocalDateOfTenant()); + + } + @Override public LoanTransactionData retrieveDisbursalTemplate(final Long loanId, boolean paymentDetailsRequired) { final Loan loan = this.loanRepository.findOne(loanId); @@ -498,7 +509,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " l.expected_disbursedon_date as expectedDisbursementDate, l.disbursedon_date as actualDisbursementDate, dbu.username as disbursedByUsername, dbu.firstname as disbursedByFirstname, dbu.lastname as disbursedByLastname," + " l.closedon_date as closedOnDate, cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname, l.writtenoffon_date as writtenOffOnDate, " + " l.expected_firstrepaymenton_date as expectedFirstRepaymentOnDate, l.interest_calculated_from_date as interestChargedFromDate, l.expected_maturedon_date as expectedMaturityDate, " - + " l.principal_amount as principal,l.approved_principal as approvedPrincipal, l.arrearstolerance_amount as inArrearsTolerance, l.number_of_repayments as numberOfRepayments, l.repay_every as repaymentEvery," + + " l.principal_amount_proposed as proposedPrincipal, l.principal_amount as principal, l.approved_principal as approvedPrincipal, l.arrearstolerance_amount as inArrearsTolerance, l.number_of_repayments as numberOfRepayments, l.repay_every as repaymentEvery," + " l.grace_on_principal_periods as graceOnPrincipalPayment, l.grace_on_interest_periods as graceOnInterestPayment, l.grace_interest_free_periods as graceOnInterestCharged,l.grace_on_arrears_ageing as graceOnArrearsAgeing," + " l.nominal_interest_rate_per_period as interestRatePerPeriod, l.annual_nominal_interest_rate as annualInterestRate, " + " l.repayment_period_frequency_enum as repaymentFrequencyType, l.interest_period_frequency_enum as interestRateFrequencyType, " @@ -660,6 +671,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal principal = rs.getBigDecimal("principal"); final BigDecimal approvedPrincipal = rs.getBigDecimal("approvedPrincipal"); + final BigDecimal proposedPrincipal = rs.getBigDecimal("proposedPrincipal"); final BigDecimal totalOverpaid = rs.getBigDecimal("totalOverpaid"); final BigDecimal inArrearsTolerance = rs.getBigDecimal("inArrearsTolerance"); @@ -803,7 +815,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, - loanOfficerId, loanOfficerName, currencyData, principal, approvedPrincipal, totalOverpaid, inArrearsTolerance, + loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, approvedPrincipal, totalOverpaid, inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionStrategyId, transactionStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V214__adding_loan_proposed_amount_to_loan.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V214__adding_loan_proposed_amount_to_loan.sql new file mode 100644 index 000000000..09f24dcb8 --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V214__adding_loan_proposed_amount_to_loan.sql @@ -0,0 +1,2 @@ + ALTER TABLE `m_loan` ADD COLUMN `principal_amount_proposed` DECIMAL(19,6) NOT NULL AFTER `currency_multiplesof`; + UPDATE m_loan SET principal_amount_proposed = approved_principal; \ No newline at end of file