tidy up calculate loan schedule validation.
This commit is contained in:
@@ -19,8 +19,6 @@ import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.mifosng.data.LoanSchedule;
|
||||
import org.mifosng.data.MoneyData;
|
||||
import org.mifosng.data.ScheduledLoanInstallment;
|
||||
import org.mifosng.data.command.CalculateLoanScheduleCommand;
|
||||
import org.mifosng.data.command.ImportLoanCommand;
|
||||
import org.mifosng.data.command.ImportLoanRepaymentsCommand;
|
||||
@@ -32,25 +30,21 @@ import org.mifosng.platform.api.commands.ClientCommand;
|
||||
import org.mifosng.platform.api.commands.ImportClientCommand;
|
||||
import org.mifosng.platform.client.domain.Client;
|
||||
import org.mifosng.platform.client.domain.ClientRepository;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
import org.mifosng.platform.exceptions.UnAuthenticatedUserException;
|
||||
import org.mifosng.platform.loan.domain.AmortizationMethod;
|
||||
import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
|
||||
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosng.platform.loan.domain.InterestMethod;
|
||||
import org.mifosng.platform.loan.domain.Loan;
|
||||
import org.mifosng.platform.loan.domain.LoanLifecycleStateMachine;
|
||||
import org.mifosng.platform.loan.domain.LoanProduct;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosng.platform.loan.domain.LoanRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanStatus;
|
||||
import org.mifosng.platform.loan.domain.LoanStatusRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanTransaction;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
import org.mifosng.platform.loan.service.CalculationPlatformService;
|
||||
import org.mifosng.platform.loan.service.LoanAssembler;
|
||||
import org.mifosng.platform.organisation.domain.Office;
|
||||
import org.mifosng.platform.organisation.domain.OfficeRepository;
|
||||
import org.mifosng.platform.organisation.domain.Organisation;
|
||||
@@ -75,11 +69,13 @@ public class ImportPlatformServiceImpl implements ImportPlatformService {
|
||||
private final CalculationPlatformService calculationPlatformService;
|
||||
private final LoanRepository loanRepository;
|
||||
private final LoanStatusRepository loanStatusRepository;
|
||||
private final LoanAssembler loanAssembler;
|
||||
|
||||
@Autowired
|
||||
public ImportPlatformServiceImpl(
|
||||
public ImportPlatformServiceImpl(final LoanAssembler loanAssembler,
|
||||
final OfficeRepository officeRepository, final ClientRepository clientRepository, final LoanProductRepository loanProductRepository,
|
||||
final CalculationPlatformService calculationPlatformService, final LoanRepository loanRepository, final LoanStatusRepository loanStatusRepository) {
|
||||
this.loanAssembler = loanAssembler;
|
||||
this.officeRepository = officeRepository;
|
||||
this.clientRepository = clientRepository;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
@@ -138,75 +134,10 @@ public class ImportPlatformServiceImpl implements ImportPlatformService {
|
||||
|
||||
SubmitLoanApplicationCommand command = combinedCommand.getSubmitLoanApplicationCommand();
|
||||
|
||||
// most of this code can be extracted out into an Assembler
|
||||
LoanProduct loanProduct = this.loanProductRepository.findOne(command
|
||||
.getProductId());
|
||||
Client client = this.clientRepository.findOne(command.getApplicantId());
|
||||
|
||||
MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal());
|
||||
|
||||
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(command
|
||||
.getInterestRatePerPeriod().doubleValue());
|
||||
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
|
||||
|
||||
// apr calculator
|
||||
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.ZERO;
|
||||
switch (interestPeriodFrequencyType) {
|
||||
case DAYS:
|
||||
break;
|
||||
case WEEKS:
|
||||
defaultAnnualNominalInterestRate = command.getInterestRatePerPeriod().multiply(BigDecimal.valueOf(52));
|
||||
break;
|
||||
case MONTHS:
|
||||
defaultAnnualNominalInterestRate = command.getInterestRatePerPeriod().multiply(BigDecimal.valueOf(12));
|
||||
break;
|
||||
case YEARS:
|
||||
defaultAnnualNominalInterestRate = command.getInterestRatePerPeriod();
|
||||
break;
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
|
||||
|
||||
final Integer repayEvery = command.getRepaymentEvery();
|
||||
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType
|
||||
.fromInt(command.getRepaymentFrequency());
|
||||
final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
|
||||
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
|
||||
|
||||
LoanProductRelatedDetail loanRepaymentScheduleDetail = new LoanProductRelatedDetail(currency, command.getPrincipal(),
|
||||
defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod,
|
||||
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, command.getInArrearsToleranceAmount());
|
||||
|
||||
LoanSchedule loanSchedule = command.getLoanSchedule();
|
||||
List<ScheduledLoanInstallment> loanRepaymentSchedule = loanSchedule
|
||||
.getScheduledLoanInstallments();
|
||||
|
||||
Loan loan = Loan.createNew(currentUser.getOrganisation(), loanProduct, client, loanRepaymentScheduleDetail);
|
||||
loan.setExternalId(command.getExternalId());
|
||||
|
||||
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
|
||||
|
||||
MoneyData readPrincipalDue = scheduledLoanInstallment
|
||||
.getPrincipalDue();
|
||||
|
||||
MoneyData readInterestDue = scheduledLoanInstallment
|
||||
.getInterestDue();
|
||||
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
loan, scheduledLoanInstallment.getInstallmentNumber(),
|
||||
scheduledLoanInstallment.getPeriodStart(),
|
||||
scheduledLoanInstallment.getPeriodEnd(), readPrincipalDue.getAmount(),
|
||||
readInterestDue.getAmount());
|
||||
loan.addRepaymentScheduleInstallment(installment);
|
||||
}
|
||||
Loan loan = loanAssembler.assembleFrom(command, currentUser.getOrganisation());
|
||||
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine();
|
||||
|
||||
loan.submitApplication(command.getSubmittedOnDate(), command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate(), command.getInterestCalculatedFromDate(), loanLifecycleStateMachine);
|
||||
|
||||
LoanStateTransitionCommand approveLoanCommand = combinedCommand.getApproveLoanCommand();
|
||||
if (approveLoanCommand != null) {
|
||||
loan.approve(approveLoanCommand.getEventDate(), loanLifecycleStateMachine);
|
||||
|
||||
@@ -109,42 +109,42 @@ public class LoansApiResource {
|
||||
@QueryParam("command") final String commandParam,
|
||||
final SubmitLoanApplicationCommand command) {
|
||||
|
||||
LocalDate expectedDisbursementDate = apiDataConversionService
|
||||
.convertFrom(command.getExpectedDisbursementDateFormatted(),
|
||||
"expectedDisbursementDateFormatted",
|
||||
LocalDate expectedDisbursementLocalDate = apiDataConversionService
|
||||
.convertFrom(command.getExpectedDisbursementDate(),
|
||||
"expectedDisbursementDate",
|
||||
command.getDateFormat());
|
||||
LocalDate repaymentsStartingFromDate = apiDataConversionService
|
||||
.convertFrom(command.getRepaymentsStartingFromDateFormatted(),
|
||||
"repaymentsStartingFromDateFormatted",
|
||||
LocalDate repaymentsStartingFromLocalDate = apiDataConversionService
|
||||
.convertFrom(command.getRepaymentsStartingFromDate(),
|
||||
"repaymentsStartingFromDate",
|
||||
command.getDateFormat());
|
||||
LocalDate interestCalculatedFromDate = apiDataConversionService
|
||||
.convertFrom(command.getInterestCalculatedFromDateFormatted(),
|
||||
"interestCalculatedFromDateFormatted",
|
||||
LocalDate interestChargedFromLocalDate = apiDataConversionService
|
||||
.convertFrom(command.getInterestChargedFromDate(),
|
||||
"interestChargedFromDate",
|
||||
command.getDateFormat());
|
||||
LocalDate submittedOnDate = apiDataConversionService.convertFrom(
|
||||
command.getSubmittedOnDateFormatted(),
|
||||
"submittedOnDateFormatted", command.getDateFormat());
|
||||
command.setExpectedDisbursementDate(expectedDisbursementDate);
|
||||
command.setRepaymentsStartingFromDate(repaymentsStartingFromDate);
|
||||
command.setInterestCalculatedFromDate(interestCalculatedFromDate);
|
||||
command.setSubmittedOnDate(submittedOnDate);
|
||||
LocalDate submittedOnLocalDate = apiDataConversionService.convertFrom(
|
||||
command.getSubmittedOnDate(),
|
||||
"submittedOnDate", command.getDateFormat());
|
||||
|
||||
command.setExpectedDisbursementLocalDate(expectedDisbursementLocalDate);
|
||||
command.setRepaymentsStartingFromLocalDate(repaymentsStartingFromLocalDate);
|
||||
command.setInterestChargedFromLocalDate(interestChargedFromLocalDate);
|
||||
command.setSubmittedOnLocalDate(submittedOnLocalDate);
|
||||
|
||||
// FIXME - pass in locale through query string or in 'request body'
|
||||
Locale clientApplicationLocale = Locale.UK;
|
||||
BigDecimal principal = this.apiDataConversionService.convertFrom(
|
||||
command.getPrincipalFormatted(), "principalFormatted",
|
||||
clientApplicationLocale);
|
||||
BigDecimal interestRatePerPeriod = this.apiDataConversionService
|
||||
.convertFrom(command.getInterestRatePerPeriodFormatted(),
|
||||
"interestRatePerPeriodFormatted",
|
||||
clientApplicationLocale);
|
||||
BigDecimal inArrearsToleranceAmount = this.apiDataConversionService
|
||||
.convertFrom(command.getInArrearsToleranceAmountFormatted(),
|
||||
"inArrearsToleranceAmountFormatted",
|
||||
clientApplicationLocale);
|
||||
command.setPrincipal(principal);
|
||||
command.setInterestRatePerPeriod(interestRatePerPeriod);
|
||||
command.setInArrearsToleranceAmount(inArrearsToleranceAmount);
|
||||
Locale clientApplicationLocale = this.apiDataConversionService.localeFromString(command.getLocale());
|
||||
BigDecimal principalValue = this.apiDataConversionService.convertFrom(command.getPrincipal(), "principal", clientApplicationLocale);
|
||||
BigDecimal inArrearsToleranceValue = this.apiDataConversionService.convertFrom(command.getInArrearsTolerance(), "inArrearsTolerance", clientApplicationLocale);
|
||||
BigDecimal interestRatePerPeriodValue = this.apiDataConversionService.convertFrom(command.getInterestRatePerPeriod(),"interestRatePerPeriod", clientApplicationLocale);
|
||||
|
||||
Integer digitsAfterDecimalValue = this.apiDataConversionService.convertToInteger(command.getDigitsAfterDecimal(), "digitsAfterDecimal", clientApplicationLocale);
|
||||
Integer repaymentEveryValue = this.apiDataConversionService.convertToInteger(command.getRepaymentEvery(), "repaymentEvery", clientApplicationLocale);
|
||||
Integer numberOfRepaymentsValue = this.apiDataConversionService.convertToInteger(command.getNumberOfRepayments(), "numberOfRepayments", clientApplicationLocale);
|
||||
|
||||
command.setPrincipalValue(principalValue);
|
||||
command.setInArrearsToleranceValue(inArrearsToleranceValue);
|
||||
command.setInterestRatePerPeriodValue(interestRatePerPeriodValue);
|
||||
command.setDigitsAfterDecimalValue(digitsAfterDecimalValue);
|
||||
command.setRepaymentEveryValue(repaymentEveryValue);
|
||||
command.setNumberOfRepaymentsValue(numberOfRepaymentsValue);
|
||||
|
||||
CalculateLoanScheduleCommand calculateLoanScheduleCommand = command
|
||||
.toCalculateLoanScheduleCommand();
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom<Ap
|
||||
Money interest = getInterest(totalAvailable.getCurrency());
|
||||
Money principal = getPrincipal(totalAvailable.getCurrency());
|
||||
|
||||
// TODO - configuration around order components of loan are paid off in.
|
||||
// TODO - NEWFEATURE - configuration around order components of loan are paid off in.
|
||||
// pay off interest
|
||||
if (remaining.isGreaterThanOrEqualTo(interest)) {
|
||||
this.interestCompleted = interest.getAmount();
|
||||
|
||||
+25
-174
@@ -3,13 +3,10 @@ package org.mifosng.platform.loan.service;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosng.data.ApiParameterError;
|
||||
import org.mifosng.data.command.CalculateLoanScheduleCommand;
|
||||
import org.mifosng.platform.DataValidatorBuilder;
|
||||
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
|
||||
import org.mifosng.platform.loan.domain.AmortizationMethod;
|
||||
import org.mifosng.platform.loan.domain.InterestMethod;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
|
||||
public class CalculateLoanScheduleCommandValidator {
|
||||
|
||||
@@ -20,199 +17,53 @@ public class CalculateLoanScheduleCommandValidator {
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
if (StringUtils.isBlank(command.getCurrencyCode())) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.currency.cannot.be.empty", "The parameter currencyCode cannot be blank.", "currencyCode");
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
|
||||
if (command.getDigitsAfterDecimal() != null) {
|
||||
|
||||
if (command.getDigitsAfterDecimal() < 0 || command.getDigitsAfterDecimal() > 6) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.currency.digitsAfterDecimal.must.be.between.zero.and.six.inclusive", "The parameter digitsAfterDecimal must be a value between 1 and 6.", "digitsAfterDecimal", command.getDigitsAfterDecimal(), 1, 6);
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
} else {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.currency.digitsAfterDecimal.cannot.be.empty", "The parameter digitsAfterDecimal cannot be blank.", "digitsAfterDecimal");
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
baseDataValidator.reset().parameter("currencyCode").value(command.getCurrencyCode()).notBlank();
|
||||
baseDataValidator.reset().parameter("digitsAfterDecimal").value(command.getDigitsAfterDecimalValue()).notNull().inMinMaxRange(0, 6);
|
||||
baseDataValidator.reset().parameter("principal").value(command.getPrincipalValue()).notNull().positiveAmount();
|
||||
baseDataValidator.reset().parameter("repaymentFrequencyType").value(command.getRepaymentFrequencyType()).notNull().inMinMaxRange(0, 3);
|
||||
baseDataValidator.reset().parameter("repaymentEvery").value(command.getRepaymentEveryValue()).notNull().greaterThanZero();
|
||||
baseDataValidator.reset().parameter("numberOfRepayments").value(command.getNumberOfRepaymentsValue()).notNull().greaterThanZero();
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(command.getInterestRatePerPeriodValue()).notNull();
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").value(command.getInterestRateFrequencyType()).notNull().inMinMaxRange(0, 3);
|
||||
baseDataValidator.reset().parameter("amortizationType").value(command.getAmortizationType()).notNull().inMinMaxRange(0, 1);
|
||||
baseDataValidator.reset().parameter("interestType").value(command.getInterestType()).notNull().inMinMaxRange(0, 1);
|
||||
baseDataValidator.reset().parameter("interestCalculationPeriodType").value(command.getInterestCalculationPeriodType()).notNull().inMinMaxRange(0, 1);
|
||||
|
||||
// FIXME - validate the number of digits before and after decimal provided (before is limited by database field, after is limited by number selected for product (and ultimately by the database field))
|
||||
if (command.getPrincipal() != null) {
|
||||
if (command.getPrincipal().doubleValue() <= Double.valueOf("0.0")) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.principal.amount.must.be.greater.than.zero", "The parameter principalMoney must be greater than zero.", "principalMoney");
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
} else {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.principal.amount.cannot.be.empty", "The parameter principalMoney cannot be blank.", "principalMoney");
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
baseDataValidator.reset().parameter("expectedDisbursementDate").value(command.getExpectedDisbursementLocalDate()).notNull();
|
||||
|
||||
if (command.getRepaymentFrequency() != null) {
|
||||
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequency());
|
||||
ApiParameterError error = null;
|
||||
switch (frequencyType) {
|
||||
case DAYS:
|
||||
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 365);
|
||||
break;
|
||||
case WEEKS:
|
||||
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 52);
|
||||
break;
|
||||
case MONTHS:
|
||||
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 12);
|
||||
break;
|
||||
case YEARS:
|
||||
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 2);
|
||||
break;
|
||||
default:
|
||||
error = ApiParameterError.parameterError("validation.msg.product.repayment.frequency.invalid", "The parameter repaymentFrequency is invalid.", "repaymentFrequency", command.getRepaymentFrequency());
|
||||
break;
|
||||
}
|
||||
if (error != null) {
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
} else {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.repayment.frequency.cannot.be.empty", "The parameter repaymentFrequency cannot be empty.", "repaymentFrequency");
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (command.getNumberOfRepayments() != null) {
|
||||
if (command.getNumberOfRepayments() < 1 || command.getNumberOfRepayments() > 100) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.number.of.repayments.is.out.of.range", "The parameter numberOfRepayments must between {1} and {2}.", "numberOfRepayments", command.getNumberOfRepayments(), 1, 100);
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
} else {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.number.of.repayments.cannot.be.empty", "The parameter numberOfRepayments cannot be empty.", "numberOfRepayments");
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (command.getAmortizationMethod() != null) {
|
||||
AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
|
||||
ApiParameterError error = null;
|
||||
switch (amortizationMethod) {
|
||||
case EQUAL_PRINCIPAL:
|
||||
break;
|
||||
case EQUAL_INSTALLMENTS:
|
||||
break;
|
||||
default:
|
||||
error = ApiParameterError.parameterError("validation.msg.product.amortization.method.invalid", "The parameter amortizationMethod is invalid.", "amortizationMethod", command.getAmortizationMethod());
|
||||
dataValidationErrors.add(error);
|
||||
break;
|
||||
}
|
||||
if (error != null) {
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
} else {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.amortization.method.cannot.be.empty", "The parameter amortizationMethod cannot be empty.", "amortizationMethod");
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (command.getExpectedDisbursementDate() == null) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.expectedDisbursementDate.method.cannot.be.blank", "The parameter expectedDisbursementDate cannot be empty.", "expectedDisbursementDate");
|
||||
dataValidationErrors.add(error);
|
||||
} else {
|
||||
if (command.getRepaymentsStartingFromDate() != null
|
||||
&& command.getExpectedDisbursementDate().isAfter(command.getRepaymentsStartingFromDate())) {
|
||||
if (command.getExpectedDisbursementLocalDate() != null) {
|
||||
if (command.getRepaymentsStartingFromLocalDate() != null
|
||||
&& command.getExpectedDisbursementLocalDate().isAfter(command.getRepaymentsStartingFromLocalDate())) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.expectedDisbursementDate.cannot.be.after.first.repayment.date",
|
||||
"The parameter expectedDisbursementDate has a date which falls after the given first repayment date.", "expectedDisbursementDate",
|
||||
command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate());
|
||||
command.getExpectedDisbursementLocalDate(), command.getRepaymentsStartingFromLocalDate());
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.getRepaymentsStartingFromDate() != null && command.getInterestCalculatedFromDate() == null) {
|
||||
if (command.getRepaymentsStartingFromLocalDate() != null && command.getInterestChargedFromLocalDate() == null) {
|
||||
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.interestCalculatedFromDate.must.be.entered.when.using.repayments.startfrom.field",
|
||||
"The parameter interestCalculatedFromDate cannot be empty when first repayment date is provided.", "interestCalculatedFromDate", command.getRepaymentsStartingFromDate());
|
||||
"The parameter interestCalculatedFromDate cannot be empty when first repayment date is provided.", "interestCalculatedFromDate", command.getRepaymentsStartingFromLocalDate());
|
||||
dataValidationErrors.add(error);
|
||||
} else if (command.getRepaymentsStartingFromDate() == null && command.getInterestCalculatedFromDate() != null) {
|
||||
} else if (command.getRepaymentsStartingFromLocalDate() == null && command.getInterestChargedFromLocalDate() != null) {
|
||||
|
||||
// validate interestCalculatedFromDate is after or on repaymentsStartingFromDate
|
||||
if (command.getExpectedDisbursementDate().isAfter(command.getInterestCalculatedFromDate())) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.interestCalculatedFromDate.cannot.be.before.disbursement.date",
|
||||
"The parameter interestCalculatedFromDate cannot be before the date given for expected disbursement.", "interestCalculatedFromDate", command.getInterestCalculatedFromDate(), command.getExpectedDisbursementDate());
|
||||
if (command.getExpectedDisbursementLocalDate().isAfter(command.getInterestChargedFromLocalDate())) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.interestChargedFromDate.cannot.be.before.disbursement.date",
|
||||
"The parameter interestCalculatedFromDate cannot be before the date given for expected disbursement.", "interestChargedFromDate", command.getInterestChargedFromLocalDate(), command.getExpectedDisbursementLocalDate());
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.getInterestRatePerPeriod()!= null) {
|
||||
if (command.getInterestRatePerPeriod().doubleValue() < Double.valueOf("0.0")) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.rate.per.period.amount.cannot.be.negative",
|
||||
"The parameter interestRatePerPeriodFormatted cannot be less than zero.", "interestRatePerPeriodFormatted",
|
||||
command.getInterestRatePerPeriod());
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
} else {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.rate.per.period.amount.cannot.be.empty",
|
||||
"The parameter interestRatePerPeriodFormatted cannot be empty.", "interestRatePerPeriodFormatted",
|
||||
command.getInterestRatePerPeriod());
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
// interest rate period type
|
||||
if (command.getInterestRateFrequencyMethod() != null) {
|
||||
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
|
||||
ApiParameterError error = null;
|
||||
switch (frequencyType) {
|
||||
case DAYS:
|
||||
break;
|
||||
case WEEKS:
|
||||
break;
|
||||
case MONTHS:
|
||||
break;
|
||||
case YEARS:
|
||||
break;
|
||||
default:
|
||||
error = ApiParameterError.parameterError("validation.msg.product.interest.frequency.invalid",
|
||||
"The parameter interestRateFrequencyMethod cannot be empty.", "interestRateFrequencyMethod", command.getInterestRateFrequencyMethod());
|
||||
break;
|
||||
}
|
||||
if (error != null) {
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
} else {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.frequency.cannot.be.empty",
|
||||
"The parameter interestRateFrequencyMethod cannot be empty.", "interestRateFrequencyMethod");
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (command.getInterestMethod() != null) {
|
||||
InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
|
||||
ApiParameterError error = null;
|
||||
switch (interestMethod) {
|
||||
case DECLINING_BALANCE:
|
||||
break;
|
||||
case FLAT:
|
||||
break;
|
||||
default:
|
||||
error = ApiParameterError.parameterError("validation.msg.product.interest.method.invalid",
|
||||
"The parameter interestMethod is invalid.", "interestMethod", command.getInterestMethod());
|
||||
break;
|
||||
}
|
||||
if (error != null) {
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
} else {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.method.cannot.be.empty",
|
||||
"The parameter interestMethod cannot be empty.", "interestMethod");
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
private ApiParameterError validateNumberExistsAndInRange(String errorCodePreFix, String fieldName, Integer value, int min, int max) {
|
||||
ApiParameterError error = null;
|
||||
if (value == null) {
|
||||
error = ApiParameterError.parameterError(errorCodePreFix + ".cannot.be.empty", "The parameter " + fieldName + " cannot be blank.", fieldName);
|
||||
} else {
|
||||
if (value < min || value > max) {
|
||||
error = ApiParameterError.parameterError(errorCodePreFix + ".outside.allowed.range", "The parameter " + fieldName + " must be between {0} and {1} inclusive.", fieldName, min, max);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
}
|
||||
+13
-14
@@ -51,20 +51,19 @@ public class CalculationPlatformServiceImpl implements
|
||||
CalculateLoanScheduleCommandValidator validator = new CalculateLoanScheduleCommandValidator(command);
|
||||
validator.validate();
|
||||
|
||||
// assemble 'loan data' into 'domain' object [LoanProductRelatedDetail]
|
||||
final BigDecimal principalAmount = BigDecimal.valueOf(command.getPrincipal().doubleValue());
|
||||
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(command.getInterestRatePerPeriod().doubleValue());
|
||||
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
|
||||
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
|
||||
final Integer repayEvery = command.getRepaymentEvery();
|
||||
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequency());
|
||||
final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
|
||||
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
|
||||
final BigDecimal principalAmount = command.getPrincipalValue();
|
||||
final BigDecimal defaultNominalInterestRatePerPeriod = command.getInterestRatePerPeriodValue();
|
||||
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyType());
|
||||
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestType());
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodType());
|
||||
final Integer repayEvery = command.getRepaymentEveryValue();
|
||||
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequencyType());
|
||||
final Integer defaultNumberOfInstallments = command.getNumberOfRepaymentsValue();
|
||||
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationType());
|
||||
|
||||
final BigDecimal defaultAnnualNominalInterestRate = this.aprCalculator.calculateFrom(interestPeriodFrequencyType, defaultNominalInterestRatePerPeriod);
|
||||
|
||||
final MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal());
|
||||
final MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimalValue());
|
||||
|
||||
// in arrerars tolerance isnt relevant when auto-calculating a loan schedule
|
||||
final BigDecimal inArrearsTolerance = BigDecimal.ZERO;
|
||||
@@ -78,11 +77,11 @@ public class CalculationPlatformServiceImpl implements
|
||||
LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(interestMethod);
|
||||
|
||||
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(command.getCurrencyCode());
|
||||
CurrencyData currencyData = new CurrencyData(applicationCurrency.getCode(), applicationCurrency.getName(), command.getDigitsAfterDecimal(),
|
||||
CurrencyData currencyData = new CurrencyData(applicationCurrency.getCode(), applicationCurrency.getName(), command.getDigitsAfterDecimalValue(),
|
||||
applicationCurrency.getDisplaySymbol(), applicationCurrency.getNameCode());
|
||||
|
||||
return loanScheduleGenerator.generate(loanScheduleInfo, command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate(),
|
||||
command.getInterestCalculatedFromDate(), currencyData);
|
||||
return loanScheduleGenerator.generate(loanScheduleInfo, command.getExpectedDisbursementLocalDate(), command.getRepaymentsStartingFromLocalDate(),
|
||||
command.getInterestChargedFromLocalDate(), currencyData);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
package org.mifosng.platform.loan.service;
|
||||
|
||||
import static org.mifosng.platform.Specifications.clientsThatMatch;
|
||||
import static org.mifosng.platform.Specifications.productThatMatches;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosng.data.LoanSchedule;
|
||||
import org.mifosng.data.MoneyData;
|
||||
import org.mifosng.data.ScheduledLoanInstallment;
|
||||
import org.mifosng.data.command.SubmitLoanApplicationCommand;
|
||||
import org.mifosng.platform.client.domain.Client;
|
||||
import org.mifosng.platform.client.domain.ClientRepository;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.exceptions.ClientNotFoundException;
|
||||
import org.mifosng.platform.exceptions.LoanProductNotFoundException;
|
||||
import org.mifosng.platform.loan.domain.AmortizationMethod;
|
||||
import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
|
||||
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosng.platform.loan.domain.InterestMethod;
|
||||
import org.mifosng.platform.loan.domain.Loan;
|
||||
import org.mifosng.platform.loan.domain.LoanLifecycleStateMachine;
|
||||
import org.mifosng.platform.loan.domain.LoanProduct;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosng.platform.loan.domain.LoanStatus;
|
||||
import org.mifosng.platform.loan.domain.LoanStatusRepository;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
import org.mifosng.platform.loanschedule.domain.AprCalculator;
|
||||
import org.mifosng.platform.organisation.domain.Organisation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class LoanAssembler {
|
||||
|
||||
private final LoanStatusRepository loanStatusRepository;
|
||||
private final LoanProductRepository loanProductRepository;
|
||||
private final ClientRepository clientRepository;
|
||||
private final AprCalculator aprCalculator = new AprCalculator();
|
||||
|
||||
@Autowired
|
||||
public LoanAssembler(final LoanStatusRepository loanStatusRepository,
|
||||
final LoanProductRepository loanProductRepository,
|
||||
final ClientRepository clientRepository) {
|
||||
this.loanStatusRepository = loanStatusRepository;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
this.clientRepository = clientRepository;
|
||||
}
|
||||
|
||||
public Loan assembleFrom(SubmitLoanApplicationCommand command, Organisation organisation) {
|
||||
LoanProduct loanProduct = this.loanProductRepository.findOne(productThatMatches(organisation, command.getProductId()));
|
||||
if (loanProduct == null) {
|
||||
throw new LoanProductNotFoundException(command.getProductId());
|
||||
}
|
||||
Client client = this.clientRepository.findOne(clientsThatMatch(organisation, command.getClientId()));
|
||||
if (client == null) {
|
||||
throw new ClientNotFoundException(command.getClientId());
|
||||
}
|
||||
|
||||
// FIXME - should probably fetch applicationCurrency with currencycode to check its validity.
|
||||
MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimalValue());
|
||||
|
||||
final BigDecimal defaultNominalInterestRatePerPeriod = command.getInterestRatePerPeriodValue();
|
||||
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyType());
|
||||
|
||||
BigDecimal defaultAnnualNominalInterestRate = aprCalculator.calculateFrom(interestPeriodFrequencyType, command.getInterestRatePerPeriodValue());
|
||||
|
||||
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestType());
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodType());
|
||||
|
||||
final Integer repayEvery = command.getRepaymentEveryValue();
|
||||
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequencyType());
|
||||
final Integer defaultNumberOfInstallments = command.getNumberOfRepaymentsValue();
|
||||
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationType());
|
||||
|
||||
LoanProductRelatedDetail loanRepaymentScheduleDetail = new LoanProductRelatedDetail(currency,
|
||||
command.getPrincipalValue(), defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
|
||||
interestMethod, interestCalculationPeriodMethod,
|
||||
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, command.getInArrearsToleranceValue());
|
||||
|
||||
LoanSchedule loanSchedule = command.getLoanSchedule();
|
||||
List<ScheduledLoanInstallment> loanRepaymentSchedule = loanSchedule.getScheduledLoanInstallments();
|
||||
|
||||
Loan loan = Loan.createNew(organisation, loanProduct, client, loanRepaymentScheduleDetail);
|
||||
loan.setExternalId(command.getExternalId());
|
||||
|
||||
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
|
||||
|
||||
MoneyData readPrincipalDue = scheduledLoanInstallment.getPrincipalDue();
|
||||
MoneyData readInterestDue = scheduledLoanInstallment.getInterestDue();
|
||||
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
loan, scheduledLoanInstallment.getInstallmentNumber(),
|
||||
scheduledLoanInstallment.getPeriodStart(),
|
||||
scheduledLoanInstallment.getPeriodEnd(), readPrincipalDue.getAmount(),
|
||||
readInterestDue.getAmount());
|
||||
loan.addRepaymentScheduleInstallment(installment);
|
||||
}
|
||||
|
||||
loan.submitApplication(command.getSubmittedOnLocalDate(), command.getExpectedDisbursementLocalDate(), command.getRepaymentsStartingFromLocalDate(), command.getInterestChargedFromLocalDate(), defaultLoanLifecycleStateMachine());
|
||||
|
||||
return loan;
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
List<LoanStatus> allowedLoanStatuses = this.loanStatusRepository.findAll();
|
||||
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
|
||||
}
|
||||
}
|
||||
+2
@@ -37,7 +37,9 @@ public interface LoanWritePlatformService {
|
||||
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_MAKE_LOAN_REPAYMENT_ROLE', 'CAN_MAKE_LOAN_REPAYMENT_IN_THE_PAST_ROLE')")
|
||||
public EntityIdentifier makeLoanRepayment(LoanTransactionCommand command);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')")
|
||||
EntityIdentifier adjustLoanTransaction(AdjustLoanTransactionCommand command);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')")
|
||||
EntityIdentifier waiveLoanAmount(LoanTransactionCommand command);
|
||||
}
|
||||
+7
-63
@@ -2,7 +2,6 @@ package org.mifosng.platform.loan.service;
|
||||
|
||||
import static org.mifosng.platform.Specifications.loansThatMatch;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -10,7 +9,6 @@ import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.data.EntityIdentifier;
|
||||
import org.mifosng.data.LoanSchedule;
|
||||
import org.mifosng.data.MoneyData;
|
||||
import org.mifosng.data.ScheduledLoanInstallment;
|
||||
import org.mifosng.data.command.AdjustLoanTransactionCommand;
|
||||
import org.mifosng.data.command.CalculateLoanScheduleCommand;
|
||||
@@ -18,8 +16,6 @@ import org.mifosng.data.command.LoanStateTransitionCommand;
|
||||
import org.mifosng.data.command.LoanTransactionCommand;
|
||||
import org.mifosng.data.command.SubmitLoanApplicationCommand;
|
||||
import org.mifosng.data.command.UndoStateTransitionCommand;
|
||||
import org.mifosng.platform.client.domain.Client;
|
||||
import org.mifosng.platform.client.domain.ClientRepository;
|
||||
import org.mifosng.platform.client.domain.Note;
|
||||
import org.mifosng.platform.client.domain.NoteRepository;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
@@ -27,15 +23,9 @@ import org.mifosng.platform.currency.domain.Money;
|
||||
import org.mifosng.platform.exceptions.LoanNotFoundException;
|
||||
import org.mifosng.platform.exceptions.NoAuthorizationException;
|
||||
import org.mifosng.platform.exceptions.PlatformDomainRuleException;
|
||||
import org.mifosng.platform.loan.domain.AmortizationMethod;
|
||||
import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
|
||||
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosng.platform.loan.domain.InterestMethod;
|
||||
import org.mifosng.platform.loan.domain.Loan;
|
||||
import org.mifosng.platform.loan.domain.LoanLifecycleStateMachine;
|
||||
import org.mifosng.platform.loan.domain.LoanProduct;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosng.platform.loan.domain.LoanRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanStatus;
|
||||
@@ -43,7 +33,6 @@ import org.mifosng.platform.loan.domain.LoanStatusRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanTransaction;
|
||||
import org.mifosng.platform.loan.domain.LoanTransactionRepository;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
import org.mifosng.platform.loanschedule.domain.AprCalculator;
|
||||
import org.mifosng.platform.security.PlatformSecurityContext;
|
||||
import org.mifosng.platform.user.domain.AppUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -56,24 +45,21 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final LoanRepository loanRepository;
|
||||
private final LoanProductRepository loanProductRepository;
|
||||
private final LoanStatusRepository loanStatusRepository;
|
||||
private final ClientRepository clientRepository;
|
||||
private final NoteRepository noteRepository;
|
||||
private final CalculationPlatformService calculationPlatformService;
|
||||
private final AprCalculator aprCalculator = new AprCalculator();
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final LoanAssembler loanAssembler;
|
||||
|
||||
@Autowired
|
||||
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanAssembler loanAssembler,
|
||||
final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository,
|
||||
final LoanStatusRepository loanStatusRepository, final LoanProductRepository loanProductRepository,
|
||||
final ClientRepository clientRepository, final NoteRepository noteRepository, final CalculationPlatformService calculationPlatformService) {
|
||||
final LoanStatusRepository loanStatusRepository,
|
||||
final NoteRepository noteRepository, final CalculationPlatformService calculationPlatformService) {
|
||||
this.context = context;
|
||||
this.loanAssembler = loanAssembler;
|
||||
this.loanRepository = loanRepository;
|
||||
this.loanTransactionRepository = loanTransactionRepository;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
this.clientRepository = clientRepository;
|
||||
this.loanStatusRepository = loanStatusRepository;
|
||||
this.noteRepository = noteRepository;
|
||||
this.calculationPlatformService = calculationPlatformService;
|
||||
@@ -97,55 +83,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
SubmitLoanApplicationCommandValidator validator = new SubmitLoanApplicationCommandValidator(command);
|
||||
validator.validate();
|
||||
|
||||
LocalDate submittedOn = command.getSubmittedOnDate();
|
||||
LocalDate submittedOn = command.getSubmittedOnLocalDate();
|
||||
if (this.isBeforeToday(submittedOn) && currentUser.hasNotPermissionForAnyOf("CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE", "PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE")) {
|
||||
throw new NoAuthorizationException("Cannot add backdated loan.");
|
||||
}
|
||||
|
||||
// FIXME - looks like assembly of LoanProductRelatedDetail is common with loan product and calculate loan schedule areas. remove duplication into an assembler.
|
||||
LoanProduct loanProduct = this.loanProductRepository.findOne(command.getProductId());
|
||||
Client client = this.clientRepository.findOne(command.getApplicantId());
|
||||
Loan loan = loanAssembler.assembleFrom(command, currentUser.getOrganisation());
|
||||
|
||||
MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal());
|
||||
|
||||
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(command.getInterestRatePerPeriod().doubleValue());
|
||||
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
|
||||
|
||||
BigDecimal defaultAnnualNominalInterestRate = aprCalculator.calculateFrom(interestPeriodFrequencyType, command.getInterestRatePerPeriod());
|
||||
|
||||
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
|
||||
|
||||
final Integer repayEvery = command.getRepaymentEvery();
|
||||
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequency());
|
||||
final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
|
||||
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
|
||||
|
||||
LoanProductRelatedDetail loanRepaymentScheduleDetail = new LoanProductRelatedDetail(currency,
|
||||
command.getPrincipal(), defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
|
||||
interestMethod, interestCalculationPeriodMethod,
|
||||
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, command.getInArrearsToleranceAmount());
|
||||
|
||||
LoanSchedule loanSchedule = command.getLoanSchedule();
|
||||
List<ScheduledLoanInstallment> loanRepaymentSchedule = loanSchedule.getScheduledLoanInstallments();
|
||||
|
||||
Loan loan = Loan.createNew(currentUser.getOrganisation(), loanProduct, client, loanRepaymentScheduleDetail);
|
||||
loan.setExternalId(command.getExternalId());
|
||||
|
||||
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
|
||||
|
||||
MoneyData readPrincipalDue = scheduledLoanInstallment.getPrincipalDue();
|
||||
MoneyData readInterestDue = scheduledLoanInstallment.getInterestDue();
|
||||
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
loan, scheduledLoanInstallment.getInstallmentNumber(),
|
||||
scheduledLoanInstallment.getPeriodStart(),
|
||||
scheduledLoanInstallment.getPeriodEnd(), readPrincipalDue.getAmount(),
|
||||
readInterestDue.getAmount());
|
||||
loan.addRepaymentScheduleInstallment(installment);
|
||||
}
|
||||
|
||||
loan.submitApplication(submittedOn, command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate(), command.getInterestCalculatedFromDate(), defaultLoanLifecycleStateMachine());
|
||||
this.loanRepository.save(loan);
|
||||
|
||||
if (StringUtils.isNotBlank(command.getSubmittedOnNote())) {
|
||||
|
||||
+16
-16
@@ -25,15 +25,15 @@ public class SubmitLoanApplicationCommandValidator {
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (command.getSubmittedOnDate() == null) {
|
||||
if (command.getSubmittedOnLocalDate() == null) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.submitted.on.date.cannot.be.blank",
|
||||
"The parameter submittedOnDateFormatted cannot be empty.", "submittedOnDateFormatted");
|
||||
"The parameter submittedOnDate cannot be empty.", "submittedOnDateFormatted");
|
||||
dataValidationErrors.add(error);
|
||||
} else {
|
||||
if (command.getSubmittedOnDate().isAfter(command.getExpectedDisbursementDate())) {
|
||||
if (command.getSubmittedOnLocalDate().isAfter(command.getExpectedDisbursementLocalDate())) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.submitted.on.date.cannot.be.after.expectedDisbursementDate",
|
||||
"The date of parameter submittedOnDateFormatted cannot fall after the date given for expectedDisbursementDateFormatted.", "submittedOnDateFormatted",
|
||||
command.getSubmittedOnDateFormatted(), command.getExpectedDisbursementDateFormatted());
|
||||
"The date of parameter submittedOnDate cannot fall after the date given for expectedDisbursementDate.", "submittedOnDate",
|
||||
command.getSubmittedOnDate(), command.getExpectedDisbursementDate());
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
@@ -41,17 +41,17 @@ public class SubmitLoanApplicationCommandValidator {
|
||||
try {
|
||||
// reuse calculate loan schedule validator for now
|
||||
CalculateLoanScheduleCommand calculateLoanScheduleCommand = new CalculateLoanScheduleCommand(
|
||||
command.getCurrencyCode(), command.getDigitsAfterDecimal(),
|
||||
command.getPrincipal(), command.getInterestRatePerPeriod(),
|
||||
command.getInterestRateFrequencyMethod(),
|
||||
command.getInterestMethod(), command.getInterestCalculationPeriodMethod(),
|
||||
command.getRepaymentEvery(),
|
||||
command.getRepaymentFrequency(),
|
||||
command.getNumberOfRepayments(),
|
||||
command.getAmortizationMethod(),
|
||||
command.getExpectedDisbursementDate(),
|
||||
command.getRepaymentsStartingFromDate(),
|
||||
command.getInterestCalculatedFromDate());
|
||||
command.getCurrencyCode(), command.getDigitsAfterDecimalValue(),
|
||||
command.getPrincipalValue(), command.getInterestRatePerPeriodValue(),
|
||||
command.getInterestRateFrequencyType(),
|
||||
command.getInterestType(), command.getInterestCalculationPeriodType(),
|
||||
command.getRepaymentEveryValue(),
|
||||
command.getRepaymentFrequencyType(),
|
||||
command.getNumberOfRepaymentsValue(),
|
||||
command.getAmortizationType(),
|
||||
command.getExpectedDisbursementLocalDate(),
|
||||
command.getRepaymentsStartingFromLocalDate(),
|
||||
command.getInterestChargedFromLocalDate());
|
||||
|
||||
CalculateLoanScheduleCommandValidator validator = new CalculateLoanScheduleCommandValidator(
|
||||
calculateLoanScheduleCommand);
|
||||
|
||||
-2
@@ -9,7 +9,6 @@ import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
public class PmtCalculator {
|
||||
|
||||
public Money calculatePaymentForOnePeriodFrom(LoanProductRelatedDetail loanScheduleInfo, BigDecimal periodInterestRateForRepaymentPeriod, MonetaryCurrency monetaryCurrency) {
|
||||
// TODO - check if going to doubles is a problem
|
||||
double interestRateFraction = periodInterestRateForRepaymentPeriod.doubleValue();
|
||||
|
||||
double futureValue = 0;
|
||||
@@ -24,7 +23,6 @@ public class PmtCalculator {
|
||||
}
|
||||
|
||||
public Money calculateTotalRepaymentFrom(LoanProductRelatedDetail loanScheduleInfo, BigDecimal periodInterestRateForRepaymentPeriod, MonetaryCurrency monetaryCurrency) {
|
||||
// TODO - check if going to doubles is a problem
|
||||
double interestRateFraction = periodInterestRateForRepaymentPeriod.doubleValue();
|
||||
|
||||
double futureValue = 0;
|
||||
|
||||
-1
@@ -44,7 +44,6 @@ public class RoleReadPlatformServiceImpl implements RoleReadPlatformService {
|
||||
return this.jdbcTemplate.query(sql, mapper, new Object[] {currentUser.getOrganisation().getId() });
|
||||
}
|
||||
|
||||
// FIXME - does it make sense to use JDBC over repository for this?
|
||||
@Override
|
||||
public RoleData retrieveRole(final Long id) {
|
||||
AppUser currentUser = context.authenticatedUser();
|
||||
|
||||
Reference in New Issue
Block a user