MIFOSX-1824 API changes and test case for loan approval in case of multi-disburse loans.

This commit is contained in:
Chandrika
2015-02-24 17:36:09 +05:30
parent 7ba9a81e53
commit f6f04d7ae3
19 changed files with 448 additions and 123 deletions
+7 -1
View File
@@ -8545,6 +8545,9 @@ Request Body:
<tr class=alt>
<td>approvedLoanAmount</td>
</tr>
<tr class=alt>
<td>expectedDisbursementDate</td>
</tr>
</table>
</div>
<div class="method-example">
@@ -8559,7 +8562,10 @@ Request Body:
"locale": "en",
"dateFormat": "dd MMMM yyyy",
"approvedOnDate": "20 September 2011",
"note": "Loan approval note"
"expectedDisbursementDate" : "20 September 2011",
"note": "Loan approval note",
"disbursementData" : [{ id=226, principal="5", expectedDisbursementDate="20 September 2011"},
{ id=227, principal="91", expectedDisbursementDate="21 September 2011"}]
}
</code>
<code class="method-response">
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.integrationtests;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -32,6 +33,7 @@ public class LoanApplicationApprovalTest {
private ResponseSpecification responseSpec;
private ResponseSpecification responseSpecForStatusCode403;
private ResponseSpecification responseSpecForStatusCode400;
private RequestSpecification requestSpec;
private LoanTransactionHelper loanTransactionHelper;
@@ -42,6 +44,7 @@ public class LoanApplicationApprovalTest {
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
this.responseSpecForStatusCode403 = new ResponseSpecBuilder().expectStatusCode(403).build();
this.responseSpecForStatusCode400 = new ResponseSpecBuilder().expectStatusCode(400).build();
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
}
@@ -63,7 +66,9 @@ public class LoanApplicationApprovalTest {
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
loanStatusHashMap = this.loanTransactionHelper.approveLoanWithApproveAmount(approveDate, approvalAmount, loanID);
final String expectedDisbursementDate = null;
List<HashMap> approveTranches = null;
loanStatusHashMap = this.loanTransactionHelper.approveLoanWithApproveAmount(approveDate,expectedDisbursementDate, approvalAmount, loanID, approveTranches);
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
}
@@ -97,6 +102,161 @@ public class LoanApplicationApprovalTest {
}
private HashMap createTrancheDetail(final String date, final String amount) {
HashMap<String, Object> detail = new HashMap<>();
detail.put("expectedDisbursementDate", date);
detail.put("principal", amount);
return detail;
}
@Test
public void loanApplicationApprovalAndValidationForMultiDisburseLoans() {
List<HashMap> createTranches = new ArrayList<>();
createTranches.add(createTrancheDetail("1 March 2014", "1000"));
createTranches.add(createTrancheDetail("23 March 2014", "4000"));
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2014");
System.out.println("---------------------------------CLIENT CREATED WITH ID---------------------------------------------------"+clientID);
final Integer loanProductID = this.loanTransactionHelper.getLoanProductId(new LoanProductTestBuilder()
.withInterestTypeAsDecliningBalance().
withTranches(true).build(null));
System.out.println("----------------------------------LOAN PRODUCT CREATED WITH ID-------------------------------------------"+loanProductID);
this.trancheLoansApprovedAmountLesserThanProposedAmount(clientID, loanProductID, createTranches);
this.trancheLoansApprovalValidation(clientID, loanProductID, createTranches);
}
private void trancheLoansApprovedAmountLesserThanProposedAmount(
Integer clientID, Integer loanProductID,
List<HashMap> createTranches) {
final String proposedAmount = "5000";
final String approvalAmount = "2000";
final String approveDate = "1 March 2014";
final String expectedDisbursementDate = "1 March 2014";
List<HashMap> approveTranches = new ArrayList<>();
approveTranches.add(createTrancheDetail("1 March 2014", "1000"));
approveTranches.add(createTrancheDetail("23 March 2014", "1000"));
final Integer loanID = applyForLoanApplicationWithTranches(clientID,
loanProductID, proposedAmount, createTranches);
System.out
.println("-----------------------------------LOAN CREATED WITH LOANID-------------------------------------------------"
+ loanID);
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(
this.requestSpec, this.responseSpec, loanID);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
System.out
.println("-----------------------------------APPROVE LOAN-----------------------------------------------------------");
loanStatusHashMap = this.loanTransactionHelper
.approveLoanWithApproveAmount(approveDate,
expectedDisbursementDate, approvalAmount, loanID,
approveTranches);
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
System.out
.println("-----------------------------------MULTI DISBURSAL LOAN APPROVED SUCCESSFULLY---------------------------------------");
}
private void trancheLoansApprovalValidation(Integer clientID,
Integer loanProductID, List<HashMap> createTranches) {
final String proposedAmount = "5000";
final String approvalAmount1 = "10000";
final String approvalAmount2 = "3000";
final String approvalAmount3 = "400";
final String approvalAmount4 = "200";
final String approvalAmount5 = "400";
final String approveDate = "1 March 2014";
final String expectedDisbursementDate = "1 March 2014";
List<HashMap> approveTranche1 = new ArrayList<>();
approveTranche1.add(createTrancheDetail("1 March 2014", "5000"));
approveTranche1.add(createTrancheDetail("23 March 2014", "5000"));
List<HashMap> approveTranche2 = new ArrayList<>();
approveTranche2.add(createTrancheDetail("1 March 2014", "1000"));
approveTranche2.add(createTrancheDetail("23 March 2014", "1000"));
approveTranche2.add(createTrancheDetail("23 March 2014", "1000"));
List<HashMap> approveTranche3 = new ArrayList<>();
approveTranche3.add(createTrancheDetail("1 March 2014", "100"));
approveTranche3.add(createTrancheDetail("23 March 2014", "100"));
approveTranche3.add(createTrancheDetail("24 March 2014", "100"));
approveTranche3.add(createTrancheDetail("25 March 2014", "100"));
List<HashMap> approveTranche4 = new ArrayList<>();
approveTranche4.add(createTrancheDetail("1 March 2014", "100"));
approveTranche4.add(createTrancheDetail("23 March 2014", "100"));
approveTranche4.add(createTrancheDetail("24 March 2014", "100"));
List<HashMap> approveTranche5 = new ArrayList<>();
final Integer loanID = applyForLoanApplicationWithTranches(clientID,
loanProductID, proposedAmount, createTranches);
System.out
.println("-----------------------------------LOAN CREATED WITH LOANID-------------------------------------------------"
+ loanID);
HashMap<String, Object> loanStatusHashMap = LoanStatusChecker
.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
System.out
.println("-----------------------------------APPROVE LOAN-----------------------------------------------------------");
this.loanTransactionHelper = new LoanTransactionHelper(
this.requestSpec, this.responseSpecForStatusCode400);
/* Tranches with same expected disbursement date */
List<HashMap<String, Object>> error = this.loanTransactionHelper
.approveLoanForTranches(approveDate, expectedDisbursementDate,
approvalAmount2, loanID, approveTranche2,
CommonConstants.RESPONSE_ERROR);
assertEquals(
"validation.msg.loan.expectedDisbursementDate.disbursement.date.must.be.unique.for.tranches",
error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
/* Sum of tranches is greater than approved amount */
error = this.loanTransactionHelper.approveLoanForTranches(approveDate,
expectedDisbursementDate, approvalAmount4, loanID,
approveTranche4, CommonConstants.RESPONSE_ERROR);
assertEquals(
"validation.msg.loan.principal.sum.of.multi.disburse.amounts.must.be.equal.to.or.lesser.than.approved.principal",
error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
this.loanTransactionHelper = new LoanTransactionHelper(
this.requestSpec, this.responseSpecForStatusCode403);
/* Sum of tranches exceeds the proposed amount */
error = this.loanTransactionHelper.approveLoanForTranches(approveDate,
expectedDisbursementDate, approvalAmount1, loanID,
approveTranche1, 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));
/* No. of tranches exceeds the max tranche count at product level */
error = this.loanTransactionHelper.approveLoanForTranches(approveDate,
expectedDisbursementDate, approvalAmount3, loanID,
approveTranche3, CommonConstants.RESPONSE_ERROR);
assertEquals("error.msg.disbursementData.exceeding.max.tranche.count",
error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
/* If tranches are not specified for a multi-disburse loan */
error = this.loanTransactionHelper.approveLoanForTranches(approveDate,
expectedDisbursementDate, approvalAmount5, loanID,
approveTranche5, CommonConstants.RESPONSE_ERROR);
assertEquals("error.msg.disbursementData.required",
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")
@@ -104,4 +264,30 @@ public class LoanApplicationApprovalTest {
.withSubmittedOnDate("02 April 2012").build(clientID.toString(), loanProductID.toString(), null);
return this.loanTransactionHelper.getLoanId(loanApplication);
}
private Integer applyForLoanApplicationWithTranches(final Integer clientID,
final Integer loanProductID, String principal,
List<HashMap> tranches) {
System.out
.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
final String loanApplicationJSON = new LoanApplicationTestBuilder()
//
.withPrincipal(principal)
//
.withLoanTermFrequency("5")
//
.withLoanTermFrequencyAsMonths()
//
.withNumberOfRepayments("5").withRepaymentEveryAfter("1")
.withRepaymentFrequencyTypeAsMonths() //
.withInterestRatePerPeriod("2") //
.withExpectedDisbursementDate("1 March 2014") //
.withTranches(tranches) //
.withInterestTypeAsDecliningBalance() //
.withSubmittedOnDate("1 March 2014") //
.build(clientID.toString(), loanProductID.toString(), null);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
}
@@ -77,7 +77,7 @@ public class LoanProductTestBuilder {
private Boolean multiDisburseLoan = false;
private final String outstandingLoanBalance = "35000";
private final String maxTrancheCount = "35000";
private final String maxTrancheCount = "3";
private Boolean isInterestRecalculationEnabled = false;
private String daysInYearType = "1";
@@ -46,7 +46,7 @@ public class LoanStatusChecker {
assertTrue(getStatus(loanStatusHashMap, "overpaid"));
}
public static HashMap getStatusOfLoan(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
public static HashMap<String, Object> getStatusOfLoan(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final Integer loanID) {
final String url = "/mifosng-provider/api/v1/loans/" + loanID + "?" + Utils.TENANT_IDENTIFIER;
return Utils.performServerGet(requestSpec, responseSpec, url, "status");
@@ -10,6 +10,7 @@ import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.integrationtests.common.Utils;
@@ -107,15 +108,22 @@ public class LoanTransactionHelper {
return performLoanTransaction(createLoanOperationURL(APPROVE_LOAN_COMMAND, loanID), getApproveLoanAsJSON(approvalDate));
}
public HashMap approveLoanWithApproveAmount(final String approvalDate, final String approvalAmount, final Integer loanID) {
public HashMap approveLoanWithApproveAmount(final String approvalDate, final String expectedDisbursementDate, final String approvalAmount, final Integer loanID,
List<HashMap> tranches) {
return performLoanTransaction(createLoanOperationURL(APPROVE_LOAN_COMMAND, loanID),
getApproveLoanAsJSON(approvalDate, approvalAmount));
getApproveLoanAsJSON(approvalDate, expectedDisbursementDate, approvalAmount, tranches));
}
public List<HashMap<String, Object>> approveLoanForTranches(final String approvalDate, final String expectedDisbursementDate, final String approvalAmount, final Integer loanID,
List<HashMap> tranches, final String responseAttribute) {
return (List<HashMap<String, Object>>) performLoanTransaction(createLoanOperationURL(APPROVE_LOAN_COMMAND, loanID),
getApproveLoanAsJSON(approvalDate, expectedDisbursementDate, approvalAmount, tranches), responseAttribute);
}
public Object approveLoan(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);
final String approvalJSONData = getApproveLoanAsJSON(approvalDate,null, approvalAmount, null);
return performLoanTransaction(approvalURL, approvalJSONData, responseAttribute);
}
@@ -225,17 +233,24 @@ public class LoanTransactionHelper {
}
private String getApproveLoanAsJSON(final String approvalDate) {
return getApproveLoanAsJSON(approvalDate, null);
return getApproveLoanAsJSON(approvalDate,null, null, null);
}
private String getApproveLoanAsJSON(final String approvalDate, final String approvalAmount) {
final HashMap<String, String> map = new HashMap<>();
private String getApproveLoanAsJSON(final String approvalDate, final String expectedDisbursementDate, final String approvalAmount,
List<HashMap> tranches) {
final HashMap<String, Object> map = new HashMap<>();
map.put("locale", "en");
map.put("dateFormat", "dd MMMM yyyy");
if (approvalAmount != null) {
map.put("approvedLoanAmount", approvalAmount);
}
map.put("approvedOnDate", approvalDate);
if(expectedDisbursementDate != null){
map.put("expectedDisbursementDate", expectedDisbursementDate);
}
if(tranches != null && tranches.size() > 0){
map.put("disbursementData",tranches);
}
map.put("note", "Approval NOTE");
return new Gson().toJson(map);
}
@@ -392,7 +407,7 @@ public class LoanTransactionHelper {
private Object performLoanTransaction(final String postURLForLoanTransaction, final String jsonToBeSent, final String responseAttribute) {
return Utils.performServerPost(this.requestSpec, this.responseSpec, postURLForLoanTransaction, jsonToBeSent, responseAttribute);
}
public Object adjustLoanTransaction(final Integer loanId, final Integer transactionId, final String date,
final String transactionAmount, final String responseAttribute) {
return adjustLoanTransaction(loanId, transactionId, getAdjustTransactionJSON(date, transactionAmount), responseAttribute);
@@ -37,5 +37,6 @@ public interface LoanApiConstants {
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";
public static final String APPROVED_AMOUNT_IS_LESS_THAN_SUM_OF_TRANCHES = "sum.of.multi.disburse.amounts.must.be.equal.to.or.lesser.than.approved.principal";
}
@@ -19,16 +19,13 @@ public class DisbursementData implements Comparable<DisbursementData> {
private final LocalDate expectedDisbursementDate;
private final LocalDate actualDisbursementDate;
private final BigDecimal principal;
@SuppressWarnings("unused")
private final BigDecimal approvedPrincipal;
public DisbursementData(Long id, final LocalDate expectedDisbursementDate, final LocalDate actualDisbursementDate,
final BigDecimal principalDisbursed, final BigDecimal approvedPrincipal) {
final BigDecimal principalDisbursed) {
this.id = id;
this.expectedDisbursementDate = expectedDisbursementDate;
this.actualDisbursementDate = actualDisbursementDate;
this.principal = principalDisbursed;
this.approvedPrincipal = approvedPrincipal;
}
public LocalDate disbursementDate() {
@@ -42,7 +39,7 @@ public class DisbursementData implements Comparable<DisbursementData> {
public BigDecimal amount() {
return this.principal;
}
public boolean isDisbursed() {
return this.actualDisbursementDate != null;
}
@@ -133,6 +133,6 @@ public class LoanApplicationTimelineData {
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData(final CurrencyData currency, final BigDecimal principal,
final BigDecimal approvedPrincipal, final BigDecimal inArrearsTolerance, final BigDecimal totalFeeChargesAtDisbursement) {
return new RepaymentScheduleRelatedLoanData(this.expectedDisbursementDate, this.actualDisbursementDate, currency, principal,
approvedPrincipal, inArrearsTolerance, totalFeeChargesAtDisbursement);
inArrearsTolerance, totalFeeChargesAtDisbursement);
}
}
@@ -19,18 +19,16 @@ public class RepaymentScheduleRelatedLoanData {
private final LocalDate actualDisbursementDate;
private final CurrencyData currency;
private final BigDecimal principal;
private final BigDecimal approvedPrincipal;
private final BigDecimal inArrearsTolerance;
private final BigDecimal totalFeeChargesAtDisbursement;
public RepaymentScheduleRelatedLoanData(final LocalDate expectedDisbursementDate, final LocalDate actualDisbursementDate,
final CurrencyData currency, final BigDecimal principal, final BigDecimal approvedPrincipal,
final CurrencyData currency, final BigDecimal principal,
final BigDecimal inArrearsTolerance, final BigDecimal totalFeeChargesAtDisbursement) {
this.expectedDisbursementDate = expectedDisbursementDate;
this.actualDisbursementDate = actualDisbursementDate;
this.currency = currency;
this.principal = principal;
this.approvedPrincipal = approvedPrincipal;
this.inArrearsTolerance = inArrearsTolerance;
this.totalFeeChargesAtDisbursement = totalFeeChargesAtDisbursement;
}
@@ -64,7 +62,6 @@ public class RepaymentScheduleRelatedLoanData {
}
public DisbursementData disbursementData() {
return new DisbursementData(null, this.expectedDisbursementDate, this.actualDisbursementDate, this.principal,
this.approvedPrincipal);
return new DisbursementData(null, this.expectedDisbursementDate, this.actualDisbursementDate, this.principal);
}
}
@@ -1588,7 +1588,8 @@ public class Loan extends AbstractPersistable<Long> {
&& StringUtils.isNotBlank((jsonObject.get(LoanApiConstants.disbursementIdParameterName).getAsString()))) {
id = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementIdParameterName).getAsLong();
}
LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, null, principal);
Date actualDisbursementDate = null;
LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate, principal);
if (id == null) {
disbursementDetails.updateLoan(this);
this.disbursementDetails.add(disbursementDetails);
@@ -1611,7 +1612,6 @@ public class Loan extends AbstractPersistable<Long> {
}
}
}
}
public LoanDisbursementDetails fetchLoanDisbursementsById(Long id) {
@@ -1865,6 +1865,7 @@ public class Loan extends AbstractPersistable<Long> {
}
public Map<String, Object> loanApplicationApproval(final AppUser currentUser, final JsonCommand command,
final JsonArray disbursementDataArray,
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
validateAccountStatus(LoanEvent.LOAN_APPROVED);
@@ -1899,11 +1900,14 @@ public class Loan extends AbstractPersistable<Long> {
approvedOn = command.localDateValueOfParameterNamed("eventDate");
approvedOnDateChange = command.stringValueOfParameterNamed("eventDate");
}
LocalDate expecteddisbursementDate = command.localDateValueOfParameterNamed("expectedDisbursementDate");
BigDecimal approvedLoanAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.approvedLoanAmountParameterName);
if (approvedLoanAmount != null) {
// Approved amount has to be less than or equal to principal
// amount demanded
@@ -1921,14 +1925,36 @@ public class Loan extends AbstractPersistable<Long> {
actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, approvedLoanAmount);
actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, approvedLoanAmount);
} else if (approvedLoanAmount.compareTo(this.proposedPrincipal) == 1) {
} else if(approvedLoanAmount.compareTo(this.proposedPrincipal) == 1) {
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);
}
}
/* Update disbursement details */
if (disbursementDataArray != null) {
updateDisbursementDetails(command, actualChanges);
}
}
if (loanProduct.isMultiDisburseLoan()) {
if (this.disbursementDetails.isEmpty()) {
final String errorMessage = "For this loan product, disbursement details must be provided";
throw new MultiDisbursementDataRequiredException(
LoanApiConstants.disbursementDataParameterName,
errorMessage);
}
if (this.disbursementDetails.size() > loanProduct
.maxTrancheCount()) {
final String errorMessage = "Number of tranche shouldn't be greter than "
+ loanProduct.maxTrancheCount();
throw new ExceedingTrancheCountException(
LoanApiConstants.disbursementDataParameterName,
errorMessage, loanProduct.maxTrancheCount(),
disbursementDetails.size());
}
}
this.approvedOnDate = approvedOn.toDate();
this.approvedBy = currentUser;
actualChanges.put("locale", command.locale());
@@ -1942,6 +1968,18 @@ public class Loan extends AbstractPersistable<Long> {
throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage,
getApprovedOnDate(), submittalDate);
}
if(expecteddisbursementDate != null){
this.expectedDisbursementDate = expecteddisbursementDate.toDate();
actualChanges.put("expectedDisbursementDate", expectedDisbursementDate);
if(expecteddisbursementDate.isBefore(approvedOn)) {
final String errorMessage = "The expected disbursement date should be either on or after the approval date: "
+ approvedOn.toString();
throw new InvalidLoanStateTransitionException("expecteddisbursal", "should.be.on.or.after.approval.date", errorMessage,
getApprovedOnDate(), expecteddisbursementDate);
}
}
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_APPROVED, approvedOn);
@@ -2442,7 +2480,6 @@ public class Loan extends AbstractPersistable<Long> {
if (this.loanProduct.isMultiDisburseLoan()) {
for (final LoanDisbursementDetails details : this.disbursementDetails) {
details.updateActualDisbursementDate(null);
details.resetPrincipal();
}
}
boolean isEmiAmountChanged = this.loanTermVariations.size() > 0;
@@ -4074,6 +4111,9 @@ public class Loan extends AbstractPersistable<Long> {
return AccountType.fromInt(this.loanType).isGroupAccount();
}
public boolean isJLGLoan(){
return AccountType.fromInt(this.loanType).isJLGAccount();
}
public void updateInterestRateFrequencyType() {
this.loanRepaymentScheduleDetail.updatenterestPeriodFrequencyType(this.loanProduct.getInterestPeriodFrequencyType());
}
@@ -4828,7 +4868,7 @@ public class Loan extends AbstractPersistable<Long> {
}
disbursementData.add(new DisbursementData(loanDisbursementDetails.getId(), expectedDisbursementDate, actualDisbursementDate,
loanDisbursementDetails.principal(), loanDisbursementDetails.approvedPrincipal()));
loanDisbursementDetails.principal()));
}
return disbursementData;
@@ -39,9 +39,6 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
@Column(name = "principal", scale = 6, precision = 19, nullable = false)
private BigDecimal principal;
@Column(name = "approved_principal", scale = 6, precision = 19, nullable = false)
private BigDecimal approvedPrincipal;
protected LoanDisbursementDetails() {
}
@@ -50,8 +47,7 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
this.expectedDisbursementDate = expectedDisbursementDate;
this.actualDisbursementDate = actualDisbursementDate;
this.principal = principal;
this.approvedPrincipal = principal;
}
}
public void updateLoan(final Loan loan) {
this.loan = loan;
@@ -61,14 +57,13 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
public boolean equals(final Object obj) {
final LoanDisbursementDetails loanDisbursementDetails = (LoanDisbursementDetails) obj;
if (loanDisbursementDetails.principal.equals(this.principal)
&& loanDisbursementDetails.expectedDisbursementDate.equals(this.expectedDisbursementDate)
&& loanDisbursementDetails.approvedPrincipal.equals(this.approvedPrincipal)) { return true; }
&& loanDisbursementDetails.expectedDisbursementDate.equals(this.expectedDisbursementDate))
{ return true; }
return false;
}
public void copy(final LoanDisbursementDetails disbursementDetails) {
this.principal = disbursementDetails.principal;
this.approvedPrincipal = disbursementDetails.approvedPrincipal;
this.expectedDisbursementDate = disbursementDetails.expectedDisbursementDate;
this.actualDisbursementDate = disbursementDetails.actualDisbursementDate;
}
@@ -93,10 +88,6 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
return this.principal;
}
public BigDecimal approvedPrincipal() {
return this.approvedPrincipal;
}
public void updatePrincipal(BigDecimal principal) {
this.principal = principal;
}
@@ -115,17 +106,13 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
if (this.actualDisbursementDate != null) {
actualDisburseDate = new LocalDate(this.actualDisbursementDate);
}
return new DisbursementData(getId(), expectedDisburseDate, actualDisburseDate, this.principal, this.approvedPrincipal);
return new DisbursementData(getId(), expectedDisburseDate, actualDisburseDate, this.principal);
}
public void updateActualDisbursementDate(Date actualDisbursementDate) {
this.actualDisbursementDate = actualDisbursementDate;
}
public BigDecimal resetPrincipal() {
return this.principal = this.approvedPrincipal;
}
public void updateExpectedDisbursementDate(Date expectedDisbursementDate) {
this.expectedDisbursementDate = expectedDisbursementDate;
}
@@ -462,11 +462,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
instalmentNumber++;
}
if (principalDisbursed.isNotEqualTo(expectedPrincipalDisburse) && diffAmt == null) {
final String errorMsg = "One of the Disbursement date is not falling on Loan Schedule";
throw new MultiDisbursementDisbursementDateException(errorMsg);
}
// 7. determine fees and penalties
for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) {
if (loanScheduleModelPeriod.isRepaymentPeriod()) {
@@ -285,7 +285,7 @@ public class LoanScheduleAssembler {
&& StringUtils.isNotBlank((jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString()))) {
principal = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName).getAsBigDecimal();
}
disbursementDatas.add(new DisbursementData(null, expectedDisbursementDate, null, principal, null));
disbursementDatas.add(new DisbursementData(null, expectedDisbursementDate, null, principal));
i++;
} while (i < disbursementDataArray.size());
}
@@ -303,7 +303,7 @@ public class LoanScheduleAssembler {
}
}
private void validateDisbursementDateWithMeetingDates(final LocalDate expectedDisbursementDate, final Calendar calendar) {
public void validateDisbursementDateWithMeetingDates(final LocalDate expectedDisbursementDate, final Calendar calendar) {
// disbursement date should fall on a meeting date
if (!calendar.isValidRecurringDate(expectedDisbursementDate)) {
final String errorMessage = "Expected disbursement date '" + expectedDisbursementDate + "' do not fall on a meeting date";
@@ -727,7 +727,6 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName).value(maxOutstandingBalance)
.ignoreIfNull().positiveAmount();
}
validateLoanMultiDisbursementdate(element, baseDataValidator, expectedDisbursementDate, principal);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
@@ -832,64 +831,115 @@ public final class LoanApplicationCommandFromApiJsonHelper {
"Validation errors exist.", dataValidationErrors); }
}
private void validateLoanMultiDisbursementdate(final JsonElement element, final DataValidatorBuilder baseDataValidator,
LocalDate expectedDisbursement, BigDecimal totalPrincipal) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.disbursementDataParameterName, element) && expectedDisbursement != null
&& totalPrincipal != null) {
BigDecimal tatalDisbursement = BigDecimal.ZERO;
boolean isFirstinstallmentOnExpectedDisbursementDate = false;
final JsonArray variationArray = this.fromApiJsonHelper.extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName,
element);
List<LocalDate> expectedDisbursementDates = new ArrayList<>();
if (variationArray != null && variationArray.size() > 0) {
int i = 0;
do {
final JsonObject jsonObject = variationArray.get(i).getAsJsonObject();
public void validateLoanMultiDisbursementdate(final JsonElement element,
final DataValidatorBuilder baseDataValidator,
LocalDate expectedDisbursement, BigDecimal totalPrincipal) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final Locale locale = this.fromApiJsonHelper
.extractLocaleParameter(topLevelJsonElement);
final String dateFormat = this.fromApiJsonHelper
.extractDateFormatParameter(topLevelJsonElement);
if (this.fromApiJsonHelper.parameterExists(
LoanApiConstants.disbursementDataParameterName, element)
&& expectedDisbursement != null && totalPrincipal != null) {
BigDecimal tatalDisbursement = BigDecimal.ZERO;
boolean isFirstinstallmentOnExpectedDisbursementDate = false;
final JsonArray variationArray = this.fromApiJsonHelper
.extractJsonArrayNamed(
LoanApiConstants.disbursementDataParameterName,
element);
List<LocalDate> expectedDisbursementDates = new ArrayList<>();
if (variationArray != null && variationArray.size() > 0) {
int i = 0;
do {
final JsonObject jsonObject = variationArray.get(i)
.getAsJsonObject();
LocalDate expectedDisbursementDate = this.fromApiJsonHelper
.extractLocalDateNamed(
LoanApiConstants.disbursementDateParameterName,
jsonObject, dateFormat, locale);
if (expectedDisbursementDates
.contains(expectedDisbursementDate)) {
baseDataValidator
.reset()
.parameter(
LoanApiConstants.disbursementDateParameterName)
.failWithCode(
LoanApiConstants.DISBURSEMENT_DATE_UNIQUE_ERROR);
}
expectedDisbursementDates.add(expectedDisbursementDate);
BigDecimal principal = this.fromApiJsonHelper
.extractBigDecimalNamed(
LoanApiConstants.disbursementPrincipalParameterName,
jsonObject, locale);
baseDataValidator
.reset()
.parameter(
LoanApiConstants.disbursementDataParameterName)
.parameterAtIndexArray(
LoanApiConstants.disbursementPrincipalParameterName,
i).value(principal).notBlank();
if (principal != null) {
tatalDisbursement = tatalDisbursement.add(principal);
}
BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalNamed(
LoanApiConstants.disbursementPrincipalParameterName, jsonObject, locale);
;
baseDataValidator
.reset()
.parameter(
LoanApiConstants.disbursementDataParameterName)
.parameterAtIndexArray(
LoanApiConstants.disbursementDateParameterName,
i).value(expectedDisbursementDate)
.notNull();
LocalDate expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed(
LoanApiConstants.disbursementDateParameterName, jsonObject, dateFormat, locale);
if (expectedDisbursementDates.contains(expectedDisbursementDate)) {
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDateParameterName)
.failWithCode(LoanApiConstants.DISBURSEMENT_DATE_UNIQUE_ERROR);
}
expectedDisbursementDates.add(expectedDisbursementDate);
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDataParameterName)
.parameterAtIndexArray(LoanApiConstants.disbursementPrincipalParameterName, i).value(principal).notBlank();
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDataParameterName)
.parameterAtIndexArray(LoanApiConstants.disbursementDateParameterName, i).value(expectedDisbursementDate)
.notNull();
if (principal != null) {
tatalDisbursement = tatalDisbursement.add(principal);
}
if (expectedDisbursement.equals(expectedDisbursementDate)) {
isFirstinstallmentOnExpectedDisbursementDate = true;
}
i++;
} while (i < variationArray.size());
if (!isFirstinstallmentOnExpectedDisbursementDate) {
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDateParameterName)
.failWithCode(LoanApiConstants.DISBURSEMENT_DATE_START_WITH_ERROR);
}
if (tatalDisbursement.compareTo(totalPrincipal) != 0) {
baseDataValidator.reset().parameter(LoanApiConstants.disbursementPrincipalParameterName)
.failWithCode(LoanApiConstants.PRINCIPAL_AMOUNT_SHOULD_BE_SAME);
}
final String interestTypeParameterName = "interestType";
final Integer interestType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestTypeParameterName, element);
baseDataValidator.reset().parameter(interestTypeParameterName).value(interestType).ignoreIfNull()
.integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());
}
if (expectedDisbursement.equals(expectedDisbursementDate)) {
isFirstinstallmentOnExpectedDisbursementDate = true;
}
i++;
} while (i < variationArray.size());
if (!isFirstinstallmentOnExpectedDisbursementDate) {
baseDataValidator
.reset()
.parameter(
LoanApiConstants.disbursementDateParameterName)
.failWithCode(
LoanApiConstants.DISBURSEMENT_DATE_START_WITH_ERROR);
}
/*if (tatalDisbursement.compareTo(totalPrincipal) != 0 && !(this.fromApiJsonHelper.parameterExists(
LoanApiConstants.approvedLoanAmountParameterName, element))) {
baseDataValidator
.reset()
.parameter(
LoanApiConstants.disbursementPrincipalParameterName)
.failWithCode(
LoanApiConstants.PRINCIPAL_AMOUNT_SHOULD_BE_SAME);
}*/
if (tatalDisbursement.compareTo(totalPrincipal) == 1) {
baseDataValidator
.reset()
.parameter(
LoanApiConstants.disbursementPrincipalParameterName)
.failWithCode(
LoanApiConstants.APPROVED_AMOUNT_IS_LESS_THAN_SUM_OF_TRANCHES);
}
final String interestTypeParameterName = "interestType";
final Integer interestType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(
interestTypeParameterName, element);
baseDataValidator
.reset()
.parameter(interestTypeParameterName)
.value(interestType)
.ignoreIfNull()
.integerSameAsNumber(
InterestMethod.DECLINING_BALANCE.getValue());
}
}
}
}
}
public void validateRecalcuationFrequency(final LocalDate recalculationFrequencyDate, final LocalDate expectedDisbursementDate,
final List<ApiParameterError> dataValidationErrors) {
@@ -49,7 +49,7 @@ public final class LoanApplicationTransitionApiJsonValidator {
final Set<String> disbursementParameters = new HashSet<>(Arrays.asList(LoanApiConstants.approvedLoanAmountParameterName,
LoanApiConstants.approvedOnDateParameterName, LoanApiConstants.noteParameterName, LoanApiConstants.localeParameterName,
LoanApiConstants.dateFormatParameterName));
LoanApiConstants.dateFormatParameterName,LoanApiConstants.disbursementDataParameterName,LoanApiConstants.disbursementDateParameterName));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
@@ -68,10 +68,14 @@ public final class LoanApplicationTransitionApiJsonValidator {
.extractLocalDateNamed(LoanApiConstants.approvedOnDateParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.approvedOnDateParameterName).value(approvedOnDate).notNull();
final LocalDate expectedDisbursementDate = this.fromApiJsonHelper
.extractLocalDateNamed(LoanApiConstants.disbursementDateParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDateParameterName).value(expectedDisbursementDate).ignoreIfNull();
final String note = this.fromApiJsonHelper.extractStringNamed(LoanApiConstants.noteParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateRejection(final String json) {
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.portfolio.loanaccount.service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -73,8 +74,10 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationDateExce
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonHelper;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanApplicationTransitionApiJsonValidator;
@@ -99,6 +102,7 @@ import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@Service
@@ -134,6 +138,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository;
private final BusinessEventNotifierService businessEventNotifierService;
private final ConfigurationDomainService configurationDomainService;
private final LoanScheduleAssembler loanScheduleAssembler;
@Autowired
public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
@@ -151,7 +157,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository,
final LoanReadPlatformService loanReadPlatformService, final LoanAccountDomainService loanAccountDomainService,
final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository,
final BusinessEventNotifierService businessEventNotifierService, final ConfigurationDomainService configurationDomainService) {
final BusinessEventNotifierService businessEventNotifierService, final ConfigurationDomainService configurationDomainService,
final LoanScheduleAssembler loanScheduleAssembler) {
this.context = context;
this.fromJsonHelper = fromJsonHelper;
this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator;
@@ -180,6 +187,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
this.accountNumberFormatRepository = accountNumberFormatRepository;
this.businessEventNotifierService = businessEventNotifierService;
this.configurationDomainService = configurationDomainService;
this.loanScheduleAssembler = loanScheduleAssembler;
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
@@ -732,29 +740,66 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
.build();
}
private void validateMultiDisbursementData(final JsonCommand command, LocalDate expectedDisbursementDate){
final String json = command.json();
final JsonElement element = this.fromJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
final JsonArray disbursementDataArray = command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName);
if(disbursementDataArray == null || disbursementDataArray.size() == 0){
final String errorMessage = "For this loan product, disbursement details must be provided";
throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage);
}
final BigDecimal principal = this.fromJsonHelper.extractBigDecimalWithLocaleNamed("approvedLoanAmount", element);
fromApiJsonDeserializer.validateLoanMultiDisbursementdate(element, baseDataValidator, expectedDisbursementDate, principal);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
@Transactional
@Override
public CommandProcessingResult approveApplication(final Long loanId, final JsonCommand command) {
final AppUser currentUser = getAppUserIfPresent();
LocalDate expectedDisbursementDate = null;
this.loanApplicationTransitionApiJsonValidator.validateApproval(command.json());
final Loan loan = retrieveLoanBy(loanId);
checkClientOrGroupActive(loan);
final JsonArray disbursementDataArray = command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName);
final Map<String, Object> changes = loan.loanApplicationApproval(currentUser, command, defaultLoanLifecycleStateMachine());
expectedDisbursementDate = command.localDateValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName);
if(expectedDisbursementDate == null){
expectedDisbursementDate = loan.getExpectedDisbursedOnLocalDate();
}
if(loan.loanProduct().isMultiDisburseLoan()){
this.validateMultiDisbursementData(command, expectedDisbursementDate);
}
checkClientOrGroupActive(loan);
// validate expected disbursement date against meeting date
if (loan.isSyncDisbursementWithMeeting() && (loan.isGroupLoan() || loan.isJLGLoan())) {
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
CalendarEntityType.LOANS.getValue());
final Calendar calendar = calendarInstance.getCalendar();
this.loanScheduleAssembler.validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar);
}
final Map<String, Object> changes = loan.loanApplicationApproval(currentUser, command, disbursementDataArray, 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, currentUser);
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
}
this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
final Note note = Note.loanNote(loan, noteText);
@@ -298,6 +298,7 @@ public class LoanAssembler {
do {
final JsonObject jsonObject = disbursementDataArray.get(i).getAsJsonObject();
Date expectedDisbursementDate = null;
Date actualDisbursementDate = null;
BigDecimal principal = null;
if (jsonObject.has(LoanApiConstants.disbursementDateParameterName)) {
@@ -312,7 +313,8 @@ public class LoanAssembler {
&& StringUtils.isNotBlank((jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString()))) {
principal = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName).getAsBigDecimal();
}
disbursementDatas.add(new LoanDisbursementDetails(expectedDisbursementDate, null, principal));
disbursementDatas.add(new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate, principal));
i++;
} while (i < disbursementDataArray.size());
}
@@ -907,7 +907,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
this.lastDueDate = this.disbursement.disbursementDate();
this.outstandingLoanPrincipalBalance = this.disbursement.amount();
this.outstandingLoanPrincipalBalance = this.disbursement.amount();
this.disbursementData = disbursementData;
this.excludePastUndisbursed = isInterestRecalculationEnabled;
}
@@ -1398,7 +1398,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
private static final class LoanDisbursementDetailMapper implements RowMapper<DisbursementData> {
public String schema() {
return "dd.id as id,dd.expected_disburse_date as expectedDisbursementdate,dd.disbursedon_date as actualDisbursementdate,dd.principal as principal,dd.approved_principal as approvedPrincipal "
return "dd.id as id,dd.expected_disburse_date as expectedDisbursementdate,dd.disbursedon_date as actualDisbursementdate,dd.principal as principal "
+ "from m_loan_disbursement_detail dd";
}
@@ -1408,10 +1408,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LocalDate expectedDisbursementdate = JdbcSupport.getLocalDate(rs, "expectedDisbursementdate");
final LocalDate actualDisbursementdate = JdbcSupport.getLocalDate(rs, "actualDisbursementdate");
final BigDecimal principal = rs.getBigDecimal("principal");
final BigDecimal approvedPrincipal = rs.getBigDecimal("approvedPrincipal");
final DisbursementData disbursementData = new DisbursementData(id, expectedDisbursementdate, actualDisbursementdate, principal,
approvedPrincipal);
final DisbursementData disbursementData = new DisbursementData(id, expectedDisbursementdate, actualDisbursementdate, principal);
return disbursementData;
}
@@ -0,0 +1,2 @@
ALTER TABLE `m_loan_disbursement_detail`
DROP COLUMN approved_principal;