diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 63d9e8b47..11e6ee558 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -8545,6 +8545,9 @@ Request Body:
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanApplicationApprovalTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanApplicationApprovalTest.java
index a2f78f53c..ac0fe9e7d 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanApplicationApprovalTest.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanApplicationApprovalTest.java
@@ -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 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 detail = new HashMap<>();
+ detail.put("expectedDisbursementDate", date);
+ detail.put("principal", amount);
+
+ return detail;
+ }
+
+ @Test
+ public void loanApplicationApprovalAndValidationForMultiDisburseLoans() {
+
+ List 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 createTranches) {
+ final String proposedAmount = "5000";
+ final String approvalAmount = "2000";
+ final String approveDate = "1 March 2014";
+ final String expectedDisbursementDate = "1 March 2014";
+
+ List 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 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 approveTranche1 = new ArrayList<>();
+ approveTranche1.add(createTrancheDetail("1 March 2014", "5000"));
+ approveTranche1.add(createTrancheDetail("23 March 2014", "5000"));
+
+ List 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 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 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 approveTranche5 = new ArrayList<>();
+
+ 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-----------------------------------------------------------");
+ this.loanTransactionHelper = new LoanTransactionHelper(
+ this.requestSpec, this.responseSpecForStatusCode400);
+
+ /* Tranches with same expected disbursement date */
+ List> 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 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);
+ }
+
}
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java
index d383a841f..b1d095303 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanProductTestBuilder.java
@@ -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";
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanStatusChecker.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanStatusChecker.java
index 0c7bc5ac7..f59fbd98b 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanStatusChecker.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanStatusChecker.java
@@ -46,7 +46,7 @@ public class LoanStatusChecker {
assertTrue(getStatus(loanStatusHashMap, "overpaid"));
}
- public static HashMap getStatusOfLoan(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+ public static HashMap 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");
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 e52b0349e..26cd4fab5 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
@@ -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 tranches) {
return performLoanTransaction(createLoanOperationURL(APPROVE_LOAN_COMMAND, loanID),
- getApproveLoanAsJSON(approvalDate, approvalAmount));
+ getApproveLoanAsJSON(approvalDate, expectedDisbursementDate, approvalAmount, tranches));
+ }
+
+ public List> approveLoanForTranches(final String approvalDate, final String expectedDisbursementDate, final String approvalAmount, final Integer loanID,
+ List tranches, final String responseAttribute) {
+ return (List>) 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 map = new HashMap<>();
+ private String getApproveLoanAsJSON(final String approvalDate, final String expectedDisbursementDate, final String approvalAmount,
+ List tranches) {
+ final HashMap 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);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
index 03d2522f6..f088e3488 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
@@ -488,9 +488,13 @@ public class CommandWrapper {
}
public boolean isUpdateDisbursementDate() {
- return this.actionName.equalsIgnoreCase("UPDATE") && this.entityName.equalsIgnoreCase("DISBURSEMENTDETAIL");
+ return this.actionName.equalsIgnoreCase("UPDATE") && this.entityName.equalsIgnoreCase("DISBURSEMENTDETAIL") && this.entityId != null;
}
-
+
+ public boolean addAndDeleteDisbursementDetails() {
+ return this.actionName.equalsIgnoreCase("UPDATE") && this.entityName.equalsIgnoreCase("DISBURSEMENTDETAIL") && this.entityId == null;
+ }
+
public boolean isStandingInstruction() {
return this.entityName.equalsIgnoreCase("STANDINGINSTRUCTION");
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
index c7997a2d1..6d1f7f3ea 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
@@ -740,6 +740,15 @@ public class CommandWrapperBuilder {
return this;
}
+ public CommandWrapperBuilder addAndDeleteDisbursementDetails(final Long loanId) {
+ this.actionName = "UPDATE";
+ this.entityName = "DISBURSEMENTDETAIL";
+ this.entityId = null;
+ this.loanId = loanId;
+ this.href = "/loans/" + loanId + "/editdisbursementdetails/";
+ return this;
+ }
+
public CommandWrapperBuilder deleteLoanApplication(final Long loanId) {
this.actionName = "DELETE";
this.entityName = "LOAN";
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
index 0a6784c94..abbcdadb4 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
@@ -445,6 +445,9 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
if (wrapper.isUpdateDisbursementDate()) {
handler = this.applicationContext.getBean("updateLoanDisbuseDateCommandHandler", NewCommandSourceHandler.class);
}
+ if(wrapper.addAndDeleteDisbursementDetails()){
+ handler = this.applicationContext.getBean("addAndDeleteLoanDisburseDetailsCommandHandler", NewCommandSourceHandler.class);
+ }
} else if (wrapper.isGLAccountResource()) {
if (wrapper.isCreate()) {
handler = this.applicationContext.getBean("createGLAccountCommandHandler", NewCommandSourceHandler.class);
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 a6b17f7de..b5b8fcf33 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
@@ -12,6 +12,8 @@ public interface LoanApiConstants {
public static final String disbursementDataParameterName = "disbursementData";
public static final String disbursementDateParameterName = "expectedDisbursementDate";
public static final String disbursementPrincipalParameterName = "principal";
+ public static final String updatedDisbursementDateParameterName = "updatedExpectedDisbursementDate";
+ public static final String updatedDisbursementPrincipalParameterName = "updatedPrincipal";
public static final String disbursementIdParameterName = "id";
public static final String principalDisbursedParameterName = "transactionAmount";
@@ -37,5 +39,9 @@ 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";
+ public static final String DISBURSEMENT_DATES_NOT_IN_ORDER = "disbursements.should.be.ordered.based.on.their.disbursement.dates";
+ public static final String DISBURSEMENT_DATE_BEFORE_ERROR = "disbursement.date.of.tranche.cannot.be.before.expected.disbursement.date";
+
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanDisbursementDetailApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanDisbursementDetailApiResource.java
index fbf30f885..95fb1fd81 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanDisbursementDetailApiResource.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanDisbursementDetailApiResource.java
@@ -11,10 +11,12 @@ import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
+import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
@@ -66,15 +68,29 @@ public class LoanDisbursementDetailApiResource {
@Produces({ MediaType.APPLICATION_JSON })
public String updateDisbursementDate(@PathParam("loanId") final Long loanId, @PathParam("disbursementId") final Long disbursementId,
final String apiRequestBodyAsJson) {
-
- final CommandWrapper commandRequest = new CommandWrapperBuilder().updateDisbusementDate(loanId, disbursementId)
- .withJson(apiRequestBodyAsJson).build();
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder().updateDisbusementDate(loanId, disbursementId)
+ .withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
+
+ @PUT
+ @Path("editDisbursements")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String addAndDeleteDisbursementDetail(@PathParam("loanId") final Long loanId, final String apiRequestBodyAsJson) {
+ CommandWrapper commandRequest = new CommandWrapperBuilder().addAndDeleteDisbursementDetails(loanId)
+ .withJson(apiRequestBodyAsJson).build();
+
+ final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+ return this.toApiJsonSerializer.serialize(result);
+ }
+
@GET
@Path("{disbursementId}")
@Consumes({ MediaType.APPLICATION_JSON })
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/DisbursementData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/DisbursementData.java
index 04872b543..7ed6a6bd8 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/DisbursementData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/DisbursementData.java
@@ -19,16 +19,13 @@ public class DisbursementData implements Comparable {
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 {
public BigDecimal amount() {
return this.principal;
}
-
+
public boolean isDisbursed() {
return this.actualDisbursementDate != null;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApplicationTimelineData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApplicationTimelineData.java
index ab8209602..17f331f8d 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApplicationTimelineData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApplicationTimelineData.java
@@ -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);
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/RepaymentScheduleRelatedLoanData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/RepaymentScheduleRelatedLoanData.java
index 6c241eb5c..f70fc3c53 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/RepaymentScheduleRelatedLoanData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/RepaymentScheduleRelatedLoanData.java
@@ -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);
}
}
\ 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 2d8dc1e19..5add0092a 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
@@ -1392,11 +1392,13 @@ public class Loan extends AbstractPersistable {
this.maxOutstandingLoanBalance = command
.bigDecimalValueOfParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName);
}
- if (this.disbursementDetails.isEmpty()) {
+ 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);
}
- if (disbursementDetails.size() > loanProduct.maxTrancheCount()) {
+ if (disbursementDataArray.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());
@@ -1531,7 +1533,7 @@ public class Loan extends AbstractPersistable {
return amount;
}
- private void updateDisbursementDetails(final JsonCommand command, final Map actualChanges) {
+ public void updateDisbursementDetails(final JsonCommand command, final Map actualChanges) {
List list = fetchDisbursementIds();
@@ -1588,22 +1590,27 @@ public class Loan extends AbstractPersistable {
&& StringUtils.isNotBlank((jsonObject.get(LoanApiConstants.disbursementIdParameterName).getAsString()))) {
id = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementIdParameterName).getAsLong();
}
- LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, null, principal);
- if (id == null) {
- disbursementDetails.updateLoan(this);
- this.disbursementDetails.add(disbursementDetails);
- actualChanges.put(LoanApiConstants.disbursementDataParameterName, expectedDisbursementDate + "-" + principal);
- actualChanges.put("recalculateLoanSchedule", true);
- } else {
- list.remove(id);
+ if(id != null){
LoanDisbursementDetails loanDisbursementDetail = fetchLoanDisbursementsById(id);
- if (!loanDisbursementDetail.equals(disbursementDetails)) {
- loanDisbursementDetail.copy(disbursementDetails);
- actualChanges.put("disbursementDetailId", id);
- actualChanges.put("recalculateLoanSchedule", true);
+ list.remove(id);
+ if(loanDisbursementDetail.actualDisbursementDate() == null){
+ Date actualDisbursementDate = null;
+ LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate, principal);
+ if (!loanDisbursementDetail.equals(disbursementDetails)) {
+ loanDisbursementDetail.copy(disbursementDetails);
+ actualChanges.put("disbursementDetailId", id);
+ actualChanges.put("recalculateLoanSchedule", true);
+ }
}
}
- i++;
+ else{
+ Date actualDisbursementDate = null;
+ LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate, principal);
+ disbursementDetails.updateLoan(this);
+ this.disbursementDetails.add(disbursementDetails);
+ actualChanges.put(LoanApiConstants.disbursementDataParameterName, expectedDisbursementDate + "-" + principal);
+ actualChanges.put("recalculateLoanSchedule", true);
+ }i++;
} while (i < disbursementDataArray.size());
for (Long id : list) {
this.disbursementDetails.remove(fetchLoanDisbursementsById(id));
@@ -1611,7 +1618,6 @@ public class Loan extends AbstractPersistable {
}
}
}
-
}
public LoanDisbursementDetails fetchLoanDisbursementsById(Long id) {
@@ -1865,6 +1871,7 @@ public class Loan extends AbstractPersistable {
}
public Map loanApplicationApproval(final AppUser currentUser, final JsonCommand command,
+ final JsonArray disbursementDataArray,
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
validateAccountStatus(LoanEvent.LOAN_APPROVED);
@@ -1899,11 +1906,14 @@ public class Loan extends AbstractPersistable {
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 +1931,36 @@ public class Loan extends AbstractPersistable {
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 +1974,18 @@ public class Loan extends AbstractPersistable {
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);
@@ -2135,12 +2179,25 @@ public class Loan extends AbstractPersistable {
} else {
for (LoanDisbursementDetails disbursementDetails : details) {
disbursementDetails.updateActualDisbursementDate(actualDisbursementDate.toDate());
- diff = diff.add(disbursementDetails.principal().subtract(principalDisbursed));
disbursementDetails.updatePrincipal(principalDisbursed);
}
}
- this.loanRepaymentScheduleDetail.setPrincipal(this.loanRepaymentScheduleDetail.getPrincipal().minus(diff).getAmount());
- if (diff.compareTo(BigDecimal.ZERO) == -1) {
+ if(this.loanProduct().isMultiDisburseLoan()){
+ Collection loanDisburseDetails = this.getDisbursementDetails();
+ BigDecimal setPrincipalAmount = BigDecimal.ZERO;
+ for (LoanDisbursementDetails disbursementDetails : loanDisburseDetails) {
+ setPrincipalAmount = setPrincipalAmount.add(disbursementDetails.principal());
+ }
+ this.loanRepaymentScheduleDetail.setPrincipal(setPrincipalAmount);
+ if(setPrincipalAmount.compareTo(this.approvedPrincipal) == 1){
+ final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved principal ";
+ throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.principal", principalDisbursed,
+ this.approvedPrincipal);
+ }
+ }else{
+ this.loanRepaymentScheduleDetail.setPrincipal(this.loanRepaymentScheduleDetail.getPrincipal().minus(diff).getAmount());
+ }
+ if (!(this.loanProduct().isMultiDisburseLoan()) && diff.compareTo(BigDecimal.ZERO) == -1) {
final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved amount ";
throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.amount", principalDisbursed,
this.loanRepaymentScheduleDetail.getPrincipal().getAmount());
@@ -2442,7 +2499,6 @@ public class Loan extends AbstractPersistable {
if (this.loanProduct.isMultiDisburseLoan()) {
for (final LoanDisbursementDetails details : this.disbursementDetails) {
details.updateActualDisbursementDate(null);
- details.resetPrincipal();
}
}
boolean isEmiAmountChanged = this.loanTermVariations.size() > 0;
@@ -3488,7 +3544,7 @@ public class Loan extends AbstractPersistable {
boolean isRegenerationRequired = false;
if (this.loanProduct.isMultiDisburseLoan()) {
LoanDisbursementDetails details = fetchLastDisburseDetail();
- if (!details.expectedDisbursementDate().equals(details.actualDisbursementDate())) {
+ if (details != null && !(details.expectedDisbursementDate().equals(details.actualDisbursementDate()))) {
isRegenerationRequired = true;
}
}
@@ -4074,6 +4130,9 @@ public class Loan extends AbstractPersistable {
return AccountType.fromInt(this.loanType).isGroupAccount();
}
+ public boolean isJLGLoan(){
+ return AccountType.fromInt(this.loanType).isJLGAccount();
+ }
public void updateInterestRateFrequencyType() {
this.loanRepaymentScheduleDetail.updatenterestPeriodFrequencyType(this.loanProduct.getInterestPeriodFrequencyType());
}
@@ -4414,19 +4473,30 @@ public class Loan extends AbstractPersistable {
return this.disbursementDetails;
}
- public ChangedTransactionDetail updateDisbursementDateForTranche(final LoanDisbursementDetails disbursementDetails,
+ public ChangedTransactionDetail updateDisbursementDateAndAmountForTranche(final LoanDisbursementDetails disbursementDetails,
final JsonCommand command, final List existingTransactionIds, final List existingReversedTransactionIds,
final Map actualChanges, final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) {
+ final Locale locale = command.extractLocale();
validateAccountStatus(LoanEvent.LOAN_EDIT_MULTI_DISBURSE_DATE);
- final LocalDate expectedDisbursementDate = command.localDateValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName);
- disbursementDetails.updateExpectedDisbursementDate(expectedDisbursementDate.toDate());
+ final BigDecimal principal = command.bigDecimalValueOfParameterNamed(LoanApiConstants.updatedDisbursementPrincipalParameterName, locale);
+ final LocalDate expectedDisbursementDate = command.localDateValueOfParameterNamed(LoanApiConstants.updatedDisbursementDateParameterName);
+ disbursementDetails.updateExpectedDisbursementDateAndAmount(expectedDisbursementDate.toDate(), principal);
actualChanges.put(LoanApiConstants.disbursementDateParameterName,
command.stringValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName));
actualChanges.put(LoanApiConstants.disbursementIdParameterName,
command.stringValueOfParameterNamed(LoanApiConstants.disbursementIdParameterName));
+ actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName,
+ command.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementPrincipalParameterName, locale));
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
+ Collection loanDisburseDetails = this.getDisbursementDetails();
+ BigDecimal setPrincipalAmount = BigDecimal.ZERO;
+ for (LoanDisbursementDetails details : loanDisburseDetails) {
+ setPrincipalAmount = setPrincipalAmount.add(details.principal());
+ }
+
+ this.loanRepaymentScheduleDetail.setPrincipal(setPrincipalAmount);
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
} else {
@@ -4560,7 +4630,7 @@ public class Loan extends AbstractPersistable {
return changedTransactionDetail;
}
- private void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO, final AppUser currentUser) {
+ public void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO, final AppUser currentUser) {
LocalDate lastTransactionDate = getLastUserTransactionDate();
generatorDTO.setLastTransactionDate(lastTransactionDate);
@@ -4828,7 +4898,7 @@ public class Loan extends AbstractPersistable {
}
disbursementData.add(new DisbursementData(loanDisbursementDetails.getId(), expectedDisbursementDate, actualDisbursementDate,
- loanDisbursementDetails.principal(), loanDisbursementDetails.approvedPrincipal()));
+ loanDisbursementDetails.principal()));
}
return disbursementData;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanDisbursementDetails.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanDisbursementDetails.java
index 697dea93b..29052206e 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanDisbursementDetails.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanDisbursementDetails.java
@@ -39,9 +39,6 @@ public class LoanDisbursementDetails extends AbstractPersistable {
@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 {
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 {
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 {
return this.principal;
}
- public BigDecimal approvedPrincipal() {
- return this.approvedPrincipal;
- }
-
public void updatePrincipal(BigDecimal principal) {
this.principal = principal;
}
@@ -115,19 +106,16 @@ public class LoanDisbursementDetails extends AbstractPersistable {
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) {
+ public void updateExpectedDisbursementDateAndAmount(Date expectedDisbursementDate, BigDecimal principal) {
this.expectedDisbursementDate = expectedDisbursementDate;
+ this.principal = principal;
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java
new file mode 100644
index 000000000..07c89a57f
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java
@@ -0,0 +1,32 @@
+/**
+ * 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.handler;
+
+import org.mifosplatform.commands.handler.NewCommandSourceHandler;
+import org.mifosplatform.infrastructure.core.api.JsonCommand;
+import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
+import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class AddAndDeleteLoanDisburseDetailsCommandHandler implements NewCommandSourceHandler {
+
+ private final LoanWritePlatformService writePlatformService;
+
+ @Autowired
+ public AddAndDeleteLoanDisburseDetailsCommandHandler(final LoanWritePlatformService writePlatformService) {
+ this.writePlatformService = writePlatformService;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(final JsonCommand command) {
+
+ return this.writePlatformService.addAndDeleteLoanDisburseDetails(command.getLoanId(), command);
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/UpdateLoanDisbuseDateCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/UpdateLoanDisbuseDateCommandHandler.java
index 767de6383..85cd204ac 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/UpdateLoanDisbuseDateCommandHandler.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/UpdateLoanDisbuseDateCommandHandler.java
@@ -26,7 +26,7 @@ public class UpdateLoanDisbuseDateCommandHandler implements NewCommandSourceHand
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
-
- return this.writePlatformService.updateDisbursementDateForTranche(command.getLoanId(), command.entityId(), command);
+
+ return this.writePlatformService.updateDisbursementDateAndAmountForTranche(command.getLoanId(), command.entityId(), command);
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
index 02673ec6b..9f3fa0d48 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
@@ -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()) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
index 1aa6bd6e9..d36b7f9ef 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
@@ -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";
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
index 72fae1c21..ca13948d1 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
@@ -17,6 +17,7 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
@@ -727,7 +728,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 +832,154 @@ 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 expectedDisbursementDates = new ArrayList<>();
- if (variationArray != null && variationArray.size() > 0) {
- int i = 0;
- do {
- final JsonObject jsonObject = variationArray.get(i).getAsJsonObject();
+ private void validateDisbursementsAreDatewiseOrdered(JsonElement element, final DataValidatorBuilder baseDataValidator) {
+ final JsonObject topLevelJsonElement = element.getAsJsonObject();
+ final Locale locale = this.fromApiJsonHelper
+ .extractLocaleParameter(topLevelJsonElement);
+ final String dateFormat = this.fromApiJsonHelper
+ .extractDateFormatParameter(topLevelJsonElement);
+ final JsonArray variationArray = this.fromApiJsonHelper
+ .extractJsonArrayNamed(
+ LoanApiConstants.disbursementDataParameterName,
+ element);
+ for(int i=0 ; i< variationArray.size();i++){
+ final JsonObject jsonObject1 = variationArray.get(i)
+ .getAsJsonObject();
+ LocalDate date1 = this.fromApiJsonHelper
+ .extractLocalDateNamed(
+ LoanApiConstants.disbursementDateParameterName,
+ jsonObject1, dateFormat, locale);
+
+ for(int j=i+1;j 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);
+ }
+ if(expectedDisbursementDate.isBefore(expectedDisbursement)){
+ baseDataValidator
+ .reset()
+ .parameter(
+ LoanApiConstants.disbursementDataParameterName)
+ .failWithCode(
+ LoanApiConstants.DISBURSEMENT_DATE_BEFORE_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);
+ }
- }
+ baseDataValidator
+ .reset()
+ .parameter(
+ LoanApiConstants.disbursementDataParameterName)
+ .parameterAtIndexArray(
+ LoanApiConstants.disbursementDateParameterName,
+ i).value(expectedDisbursementDate)
+ .notNull();
- }
+ 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) == 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 dataValidationErrors) {
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 e293d57f5..b4eb9c70a 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
@@ -49,7 +49,7 @@ public final class LoanApplicationTransitionApiJsonValidator {
final Set 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