Merge branch 'chandrikamohith-TrancheDisbursal' into develop
This commit is contained in:
@@ -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">
|
||||
|
||||
+187
-1
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -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";
|
||||
|
||||
+1
-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");
|
||||
|
||||
+22
-7
@@ -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);
|
||||
|
||||
+6
-2
@@ -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");
|
||||
}
|
||||
|
||||
+9
@@ -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";
|
||||
|
||||
+3
@@ -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);
|
||||
|
||||
+6
@@ -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";
|
||||
|
||||
|
||||
}
|
||||
|
||||
+19
-3
@@ -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 })
|
||||
|
||||
+2
-5
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
+2
-5
@@ -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);
|
||||
}
|
||||
}
|
||||
+101
-31
@@ -1392,11 +1392,13 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
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<Long> {
|
||||
return amount;
|
||||
}
|
||||
|
||||
private void updateDisbursementDetails(final JsonCommand command, final Map<String, Object> actualChanges) {
|
||||
public void updateDisbursementDetails(final JsonCommand command, final Map<String, Object> actualChanges) {
|
||||
|
||||
List<Long> list = fetchDisbursementIds();
|
||||
|
||||
@@ -1588,22 +1590,27 @@ 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);
|
||||
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<Long> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public LoanDisbursementDetails fetchLoanDisbursementsById(Long id) {
|
||||
@@ -1865,6 +1871,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 +1906,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 +1931,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 +1974,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);
|
||||
|
||||
@@ -2135,12 +2179,25 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
} 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<LoanDisbursementDetails> 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<Long> {
|
||||
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<Long> {
|
||||
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<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());
|
||||
}
|
||||
@@ -4414,19 +4473,30 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return this.disbursementDetails;
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail updateDisbursementDateForTranche(final LoanDisbursementDetails disbursementDetails,
|
||||
public ChangedTransactionDetail updateDisbursementDateAndAmountForTranche(final LoanDisbursementDetails disbursementDetails,
|
||||
final JsonCommand command, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final Map<String, Object> 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<LoanDisbursementDetails> 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<Long> {
|
||||
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<Long> {
|
||||
}
|
||||
|
||||
disbursementData.add(new DisbursementData(loanDisbursementDetails.getId(), expectedDisbursementDate, actualDisbursementDate,
|
||||
loanDisbursementDetails.principal(), loanDisbursementDetails.approvedPrincipal()));
|
||||
loanDisbursementDetails.principal()));
|
||||
}
|
||||
|
||||
return disbursementData;
|
||||
|
||||
+6
-18
@@ -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,19 +106,16 @@ 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) {
|
||||
public void updateExpectedDisbursementDateAndAmount(Date expectedDisbursementDate, BigDecimal principal) {
|
||||
this.expectedDisbursementDate = expectedDisbursementDate;
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -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);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
-5
@@ -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()) {
|
||||
|
||||
+2
-2
@@ -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";
|
||||
|
||||
+145
-55
@@ -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<LocalDate> 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<variationArray.size();j++){
|
||||
final JsonObject jsonObject2 = variationArray.get(j)
|
||||
.getAsJsonObject();
|
||||
LocalDate date2 = this.fromApiJsonHelper
|
||||
.extractLocalDateNamed(
|
||||
LoanApiConstants.disbursementDateParameterName,
|
||||
jsonObject2, dateFormat, locale);
|
||||
if(date1.isAfter(date2)){
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(
|
||||
LoanApiConstants.disbursementDataParameterName)
|
||||
.failWithCode(
|
||||
LoanApiConstants.DISBURSEMENT_DATES_NOT_IN_ORDER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalNamed(
|
||||
LoanApiConstants.disbursementPrincipalParameterName, jsonObject, locale);
|
||||
;
|
||||
public void validateLoanMultiDisbursementdate(final JsonElement element,
|
||||
final DataValidatorBuilder baseDataValidator,
|
||||
LocalDate expectedDisbursement, BigDecimal totalPrincipal) {
|
||||
|
||||
this.validateDisbursementsAreDatewiseOrdered(element, baseDataValidator);
|
||||
|
||||
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());
|
||||
}
|
||||
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);
|
||||
}
|
||||
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<ApiParameterError> dataValidationErrors) {
|
||||
|
||||
+6
-2
@@ -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) {
|
||||
|
||||
+22
-4
@@ -11,6 +11,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -30,17 +31,23 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@Component
|
||||
public final class LoanEventApiJsonValidator {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer;
|
||||
|
||||
|
||||
@Autowired
|
||||
public LoanEventApiJsonValidator(final FromJsonHelper fromApiJsonHelper) {
|
||||
public LoanEventApiJsonValidator(final FromJsonHelper fromApiJsonHelper,
|
||||
final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
@@ -363,12 +370,13 @@ public final class LoanEventApiJsonValidator {
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateUpdateDisbursementDate(final String json, LoanDisbursementDetails loanDisbursementDetails) {
|
||||
public void validateUpdateDisbursementDateAndAmount(final String json, LoanDisbursementDetails loanDisbursementDetails) {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Set<String> disbursementParameters = new HashSet<>(Arrays.asList(LoanApiConstants.disbursementDateParameterName,
|
||||
"locale", "dateFormat"));
|
||||
final Set<String> disbursementParameters = new HashSet<>(Arrays.asList("locale", "dateFormat", LoanApiConstants.disbursementDataParameterName,
|
||||
LoanApiConstants.approvedLoanAmountParameterName, LoanApiConstants.updatedDisbursementDateParameterName, LoanApiConstants.updatedDisbursementPrincipalParameterName,
|
||||
LoanApiConstants.disbursementDateParameterName));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
||||
@@ -380,12 +388,22 @@ public final class LoanEventApiJsonValidator {
|
||||
final LocalDate actualDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||
LoanApiConstants.disbursementDateParameterName, element);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDateParameterName).value(actualDisbursementDate).notNull();
|
||||
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
|
||||
final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.updatedDisbursementPrincipalParameterName,
|
||||
element,locale);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.disbursementPrincipalParameterName).value(principal).notNull();
|
||||
|
||||
final BigDecimal approvedPrincipal = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.approvedLoanAmountParameterName,
|
||||
element,locale);
|
||||
if (loanDisbursementDetails.actualDisbursementDate() != null) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDateParameterName)
|
||||
.failWithCode(LoanApiConstants.ALREADY_DISBURSED);
|
||||
}
|
||||
|
||||
|
||||
fromApiJsonDeserializer.validateLoanMultiDisbursementdate(element, baseDataValidator, actualDisbursementDate, approvedPrincipal);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
|
||||
+51
-6
@@ -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();
|
||||
}
|
||||
|
||||
public 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);
|
||||
|
||||
+3
-1
@@ -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());
|
||||
}
|
||||
|
||||
+3
-5
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -78,7 +78,7 @@ public interface LoanWritePlatformService {
|
||||
|
||||
CommandProcessingResult undoWriteOff(Long loanId);
|
||||
|
||||
CommandProcessingResult updateDisbursementDateForTranche(Long loanId, Long disbursementId, JsonCommand command);
|
||||
CommandProcessingResult updateDisbursementDateAndAmountForTranche(Long loanId, Long disbursementId, JsonCommand command);
|
||||
|
||||
void recalculateInterest();
|
||||
|
||||
@@ -88,4 +88,6 @@ public interface LoanWritePlatformService {
|
||||
|
||||
CommandProcessingResult makeLoanRefund(Long loanId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult addAndDeleteLoanDisburseDetails(Long loanId, JsonCommand command);
|
||||
|
||||
}
|
||||
+90
-12
@@ -123,11 +123,13 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.InvalidPaidInAdvanceAmountException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanDisbursalException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
|
||||
import org.mifosplatform.portfolio.loanaccount.guarantor.service.GuarantorDomainService;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||
@@ -159,6 +161,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
@Service
|
||||
@@ -2541,17 +2544,59 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
private void validateMultiDisbursementData(final JsonCommand command, LocalDate expectedDisbursementDate){
|
||||
final String json = command.json();
|
||||
final JsonElement element = this.fromApiJsonHelper.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.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("approvedLoanAmount", element);
|
||||
|
||||
loanApplicationCommandFromApiJsonHelper.validateLoanMultiDisbursementdate(element, baseDataValidator, expectedDisbursementDate, principal);
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult updateDisbursementDateForTranche(final Long loanId, final Long disbursementId, final JsonCommand command) {
|
||||
|
||||
AppUser currentUser = getAppUserIfPresent();
|
||||
|
||||
public CommandProcessingResult addAndDeleteLoanDisburseDetails(Long loanId, JsonCommand command) {
|
||||
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
checkClientOrGroupActive(loan);
|
||||
LoanDisbursementDetails loanDisbursementDetails = loan.fetchLoanDisbursementsById(disbursementId);
|
||||
this.loanEventApiJsonValidator.validateUpdateDisbursementDate(command.json(), loanDisbursementDetails);
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>();
|
||||
LocalDate expectedDisbursementDate = loan.getExpectedDisbursedOnLocalDate();
|
||||
validateMultiDisbursementData(command, expectedDisbursementDate);
|
||||
|
||||
loan.updateDisbursementDetails(command, actualChanges);
|
||||
|
||||
if (loan.getDisbursementDetails().isEmpty()) {
|
||||
final String errorMessage = "For this loan product, disbursement details must be provided";
|
||||
throw new MultiDisbursementDataRequiredException(
|
||||
LoanApiConstants.disbursementDataParameterName,
|
||||
errorMessage);
|
||||
}
|
||||
|
||||
if (loan.getDisbursementDetails().size() > loan.loanProduct()
|
||||
.maxTrancheCount()) {
|
||||
final String errorMessage = "Number of tranche shouldn't be greter than "
|
||||
+ loan.loanProduct().maxTrancheCount();
|
||||
throw new ExceedingTrancheCountException(
|
||||
LoanApiConstants.disbursementDataParameterName,
|
||||
errorMessage, loan.loanProduct().maxTrancheCount(),
|
||||
loan.getDisbursementDetails().size());
|
||||
}
|
||||
LoanDisbursementDetails updateDetails = null;
|
||||
return processLoanDisbursementDetail(loan, loanId, command, updateDetails);
|
||||
|
||||
}
|
||||
|
||||
private CommandProcessingResult processLoanDisbursementDetail(final Loan loan, Long loanId, JsonCommand command,
|
||||
LoanDisbursementDetails loanDisbursementDetails){
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
final LocalDate calculatedRepaymentsStartingFromDate = this.loanAccountDomainService.getCalculatedRepaymentsStartingFromDate(
|
||||
loan.getDisbursementDate(), loan, calendarInstance);
|
||||
@@ -2580,12 +2625,32 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||
lastTransactionDate);
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.updateDisbursementDateForTranche(loanDisbursementDetails, command,
|
||||
existingTransactionIds, existingReversedTransactionIds, changes, scheduleGeneratorDTO, currentUser);
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
AppUser currentUser = getAppUserIfPresent();
|
||||
|
||||
if(command.entityId() != null){
|
||||
|
||||
changedTransactionDetail = loan.updateDisbursementDateAndAmountForTranche(loanDisbursementDetails, command,
|
||||
existingTransactionIds, existingReversedTransactionIds, changes, scheduleGeneratorDTO, currentUser);
|
||||
}else{
|
||||
Collection<LoanDisbursementDetails> loanDisburseDetails = loan.getDisbursementDetails();
|
||||
BigDecimal setAmount = BigDecimal.ZERO;
|
||||
for (LoanDisbursementDetails details : loanDisburseDetails) {
|
||||
setAmount = setAmount.add(details.principal());
|
||||
}
|
||||
|
||||
loan.repaymentScheduleDetail().setPrincipal(setAmount);
|
||||
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
loan.regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
|
||||
} else {
|
||||
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
|
||||
}
|
||||
}
|
||||
|
||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
if (changedTransactionDetail != null) {
|
||||
if (command.entityId() != null && changedTransactionDetail != null) {
|
||||
for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
@@ -2600,8 +2665,21 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.withClientId(loan.getClientId()) //
|
||||
.withGroupId(loan.getGroupId()) //
|
||||
.withLoanId(loanId) //
|
||||
.with(changes).build();
|
||||
.with(changes).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult updateDisbursementDateAndAmountForTranche(final Long loanId, final Long disbursementId, final JsonCommand command) {
|
||||
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
checkClientOrGroupActive(loan);
|
||||
LoanDisbursementDetails loanDisbursementDetails = loan.fetchLoanDisbursementsById(disbursementId);
|
||||
this.loanEventApiJsonValidator.validateUpdateDisbursementDateAndAmount(command.json(), loanDisbursementDetails);
|
||||
|
||||
return processLoanDisbursementDetail(loan, loanId, command, loanDisbursementDetails);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public LoanTransaction disburseLoanAmountToSavings(final Long loanId, Long loanChargeId, final JsonCommand command,
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `m_loan_disbursement_detail`
|
||||
DROP COLUMN approved_principal;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `m_loan_disbursement_detail`
|
||||
DROP COLUMN approved_principal;
|
||||
Reference in New Issue
Block a user