MIFOSX-1842 Changes made for improvements to tranche disbursal loans.

This commit is contained in:
Chandrika
2015-02-24 17:36:11 +05:30
parent f6f04d7ae3
commit f720bd6627
15 changed files with 300 additions and 60 deletions
@@ -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");
}
@@ -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";
@@ -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);
@@ -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";
@@ -38,5 +40,8 @@ public interface LoanApiConstants {
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";
}
@@ -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 })
@@ -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,23 +1590,27 @@ public class Loan extends AbstractPersistable<Long> {
&& StringUtils.isNotBlank((jsonObject.get(LoanApiConstants.disbursementIdParameterName).getAsString()))) {
id = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementIdParameterName).getAsLong();
}
Date actualDisbursementDate = null;
LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate, 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));
@@ -2173,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());
@@ -3525,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;
}
}
@@ -4454,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 {
@@ -4600,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);
@@ -113,8 +113,9 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
this.actualDisbursementDate = actualDisbursementDate;
}
public void updateExpectedDisbursementDate(Date expectedDisbursementDate) {
public void updateExpectedDisbursementDateAndAmount(Date expectedDisbursementDate, BigDecimal principal) {
this.expectedDisbursementDate = expectedDisbursementDate;
this.principal = principal;
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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;
@@ -831,10 +832,49 @@ public final class LoanApplicationCommandFromApiJsonHelper {
"Validation errors exist.", dataValidationErrors); }
}
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);
}
}
}
}
public void validateLoanMultiDisbursementdate(final JsonElement element,
final DataValidatorBuilder baseDataValidator,
LocalDate expectedDisbursement, BigDecimal totalPrincipal) {
this.validateDisbursementsAreDatewiseOrdered(element, baseDataValidator);
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final Locale locale = this.fromApiJsonHelper
.extractLocaleParameter(topLevelJsonElement);
@@ -868,6 +908,14 @@ public final class LoanApplicationCommandFromApiJsonHelper {
.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(
@@ -906,15 +954,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
.failWithCode(
LoanApiConstants.DISBURSEMENT_DATE_START_WITH_ERROR);
}
/*if (tatalDisbursement.compareTo(totalPrincipal) != 0 && !(this.fromApiJsonHelper.parameterExists(
LoanApiConstants.approvedLoanAmountParameterName, element))) {
baseDataValidator
.reset()
.parameter(
LoanApiConstants.disbursementPrincipalParameterName)
.failWithCode(
LoanApiConstants.PRINCIPAL_AMOUNT_SHOULD_BE_SAME);
}*/
if (tatalDisbursement.compareTo(totalPrincipal) == 1) {
baseDataValidator
.reset()
@@ -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);
}
@@ -740,7 +740,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
.build();
}
private void validateMultiDisbursementData(final JsonCommand command, LocalDate expectedDisbursementDate){
public void validateMultiDisbursementData(final JsonCommand command, LocalDate expectedDisbursementDate){
final String json = command.json();
final JsonElement element = this.fromJsonHelper.parse(json);
@@ -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);
}
@@ -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,
@@ -0,0 +1,2 @@
ALTER TABLE `m_loan_disbursement_detail`
DROP COLUMN approved_principal;