diff --git a/mifosng-data/src/main/java/org/mifosng/data/ErrorResponse.java b/mifosng-data/src/main/java/org/mifosng/data/ErrorResponse.java index df7c7de94..911fe632c 100644 --- a/mifosng-data/src/main/java/org/mifosng/data/ErrorResponse.java +++ b/mifosng-data/src/main/java/org/mifosng/data/ErrorResponse.java @@ -2,9 +2,10 @@ package org.mifosng.data; import javax.xml.bind.annotation.XmlRootElement; -/* - * TODO - Arguments is still not correctly supported +/** + * Being removed in favour of {@link ApiGlobalErrorResponse} and {@link ApiParameterError} */ +@Deprecated @XmlRootElement(name="errorResponse") public class ErrorResponse { diff --git a/mifosng-data/src/main/java/org/mifosng/data/command/CalculateLoanScheduleCommand.java b/mifosng-data/src/main/java/org/mifosng/data/command/CalculateLoanScheduleCommand.java index 7390a49d1..08db1e59f 100644 --- a/mifosng-data/src/main/java/org/mifosng/data/command/CalculateLoanScheduleCommand.java +++ b/mifosng-data/src/main/java/org/mifosng/data/command/CalculateLoanScheduleCommand.java @@ -9,13 +9,28 @@ import org.joda.time.LocalDate; @XmlRootElement public class CalculateLoanScheduleCommand { - private CommonLoanProperties commonLoanProperties; + private String currencyCode; + private Integer digitsAfterDecimal; + private BigDecimal principal; + private BigDecimal inArrearsToleranceAmount; + + private Integer repaymentEvery; + private Integer repaymentFrequency; + private Integer numberOfRepayments; + + private BigDecimal interestRatePerPeriod; + private Integer interestRateFrequencyMethod; + private Integer amortizationMethod; + private Integer interestMethod; + private Integer interestCalculationPeriodMethod; + private boolean flexibleRepaymentSchedule = false; + private boolean interestRebateAllowed = false; private LocalDate expectedDisbursementDate; private LocalDate repaymentsStartingFromDate; private LocalDate interestCalculatedFromDate; - public CalculateLoanScheduleCommand() { + protected CalculateLoanScheduleCommand() { // } @@ -23,132 +38,139 @@ public class CalculateLoanScheduleCommand { final Integer digitsAfterDecimal, final Number principal, final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod, final Integer interestCalculationPeriodMethod, final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod, - final boolean flexibleRepaymentSchedule, - final boolean interestRebateAllowed, final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate, final LocalDate interestCalculatedFromDate) { - commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod, - interestMethod, interestCalculationPeriodMethod, - repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, Integer.valueOf(0), - flexibleRepaymentSchedule, interestRebateAllowed); + this.currencyCode = currencyCode; + this.digitsAfterDecimal = digitsAfterDecimal; + this.principal = BigDecimal.valueOf(principal.doubleValue()); + this.interestRatePerPeriod = BigDecimal.valueOf(interestRatePerPeriod.doubleValue()); + this.interestRateFrequencyMethod = interestRateFrequencyMethod; + this.interestMethod = interestMethod; + this.interestCalculationPeriodMethod = interestCalculationPeriodMethod; + this.repaymentEvery = repaymentEvery; + this.repaymentFrequency = repaymentFrequency; + this.numberOfRepayments = numberOfRepayments; + this.amortizationMethod = amortizationMethod; + this.flexibleRepaymentSchedule = false; + this.interestRebateAllowed = false; this.expectedDisbursementDate = expectedDisbursementDate; this.repaymentsStartingFromDate = repaymentsStartingFromDate; this.interestCalculatedFromDate = interestCalculatedFromDate; } - + public String getCurrencyCode() { - return this.commonLoanProperties.getCurrencyCode(); + return currencyCode; } - public void setCurrencyCode(final String currencyCode) { - this.commonLoanProperties.setCurrencyCode(currencyCode); + public void setCurrencyCode(String currencyCode) { + this.currencyCode = currencyCode; } public Integer getDigitsAfterDecimal() { - return this.commonLoanProperties.getDigitsAfterDecimal(); + return digitsAfterDecimal; } - public void setDigitsAfterDecimal(final Integer digitsAfterDecimal) { - this.commonLoanProperties.setDigitsAfterDecimal(digitsAfterDecimal); - } - - public Integer getRepaymentEvery() { - return this.commonLoanProperties.getRepaymentEvery(); - } - - public void setRepaymentEvery(final Integer repaymentEvery) { - this.commonLoanProperties.setRepaymentEvery(repaymentEvery); - } - - public Integer getRepaymentFrequency() { - return this.commonLoanProperties.getRepaymentFrequency(); - } - - public void setRepaymentFrequency(final Integer repaymentFrequency) { - this.commonLoanProperties.setRepaymentFrequency(repaymentFrequency); - } - - public Boolean isFlexibleRepaymentSchedule() { - return this.commonLoanProperties.isFlexibleRepaymentSchedule(); - } - - public void setFlexibleRepaymentSchedule( - final Boolean flexibleRepaymentSchedule) { - this.commonLoanProperties.setFlexibleRepaymentSchedule(flexibleRepaymentSchedule); - } - - public Boolean isInterestRebateAllowed() { - return this.commonLoanProperties.isInterestRebateAllowed(); - } - - public void setInterestRebateAllowed(final Boolean interestRebateAllowed) { - this.commonLoanProperties.setInterestRebateAllowed(interestRebateAllowed); + public void setDigitsAfterDecimal(Integer digitsAfterDecimal) { + this.digitsAfterDecimal = digitsAfterDecimal; } public BigDecimal getPrincipal() { - return this.commonLoanProperties.getPrincipal(); + return principal; } - public void setPrincipal(final BigDecimal principal) { - this.commonLoanProperties.setPrincipal(principal); + public void setPrincipal(BigDecimal principal) { + this.principal = principal; } -// public Boolean getFlexibleRepaymentSchedule() { -// return this.commonLoanProperties.getFlexibleRepaymentSchedule(); -// } -// -// public Boolean getInterestRebateAllowed() { -// return this.commonLoanProperties.getInterestRebateAllowed(); -// } - - public Integer getInterestRateFrequencyMethod() { - return commonLoanProperties.getInterestRateFrequencyMethod(); + public BigDecimal getInArrearsToleranceAmount() { + return inArrearsToleranceAmount; } - public void setInterestRateFrequencyMethod(Integer interestRateFrequencyMethod) { - this.commonLoanProperties.setInterestRateFrequencyMethod(interestRateFrequencyMethod); + public void setInArrearsToleranceAmount(BigDecimal inArrearsToleranceAmount) { + this.inArrearsToleranceAmount = inArrearsToleranceAmount; } - public Integer getInterestMethod() { - return commonLoanProperties.getInterestMethod(); + public Integer getRepaymentEvery() { + return repaymentEvery; } - public void setInterestMethod(Integer interestMethod) { - this.commonLoanProperties.setInterestMethod(interestMethod); + public void setRepaymentEvery(Integer repaymentEvery) { + this.repaymentEvery = repaymentEvery; } - public Integer getAmortizationMethod() { - return commonLoanProperties.getAmortizationMethod(); + public Integer getRepaymentFrequency() { + return repaymentFrequency; } - public void setAmortizationMethod(Integer amortizationMethod) { - this.commonLoanProperties.setAmortizationMethod(amortizationMethod); + public void setRepaymentFrequency(Integer repaymentFrequency) { + this.repaymentFrequency = repaymentFrequency; } public Integer getNumberOfRepayments() { - return commonLoanProperties.getNumberOfRepayments(); + return numberOfRepayments; } public void setNumberOfRepayments(Integer numberOfRepayments) { - this.commonLoanProperties.setNumberOfRepayments(numberOfRepayments); + this.numberOfRepayments = numberOfRepayments; } public BigDecimal getInterestRatePerPeriod() { - return commonLoanProperties.getInterestRatePerPeriod(); + return interestRatePerPeriod; } public void setInterestRatePerPeriod(BigDecimal interestRatePerPeriod) { - this.commonLoanProperties.setInterestRatePerPeriod(interestRatePerPeriod); + this.interestRatePerPeriod = interestRatePerPeriod; } - public CommonLoanProperties getCommonLoanProperties() { - return commonLoanProperties; + public Integer getInterestRateFrequencyMethod() { + return interestRateFrequencyMethod; } - public void setCommonLoanProperties(CommonLoanProperties commonLoanProperties) { - this.commonLoanProperties = commonLoanProperties; + public void setInterestRateFrequencyMethod(Integer interestRateFrequencyMethod) { + this.interestRateFrequencyMethod = interestRateFrequencyMethod; + } + + public Integer getAmortizationMethod() { + return amortizationMethod; + } + + public void setAmortizationMethod(Integer amortizationMethod) { + this.amortizationMethod = amortizationMethod; + } + + public Integer getInterestMethod() { + return interestMethod; + } + + public void setInterestMethod(Integer interestMethod) { + this.interestMethod = interestMethod; + } + + public Integer getInterestCalculationPeriodMethod() { + return interestCalculationPeriodMethod; + } + + public void setInterestCalculationPeriodMethod( + Integer interestCalculationPeriodMethod) { + this.interestCalculationPeriodMethod = interestCalculationPeriodMethod; + } + + public boolean isFlexibleRepaymentSchedule() { + return flexibleRepaymentSchedule; + } + + public void setFlexibleRepaymentSchedule(boolean flexibleRepaymentSchedule) { + this.flexibleRepaymentSchedule = flexibleRepaymentSchedule; + } + + public boolean isInterestRebateAllowed() { + return interestRebateAllowed; + } + + public void setInterestRebateAllowed(boolean interestRebateAllowed) { + this.interestRebateAllowed = interestRebateAllowed; } public LocalDate getExpectedDisbursementDate() { @@ -174,12 +196,4 @@ public class CalculateLoanScheduleCommand { public void setInterestCalculatedFromDate(LocalDate interestCalculatedFromDate) { this.interestCalculatedFromDate = interestCalculatedFromDate; } - - public Integer getInterestCalculationPeriodMethod() { - return this.commonLoanProperties.getInterestCalculationPeriodMethod(); - } - - public void setInterestCalculationPeriodMethod(final Integer interestCalculationPeriodMethod) { - this.commonLoanProperties.setInterestCalculationPeriodMethod(interestCalculationPeriodMethod); - } } \ No newline at end of file diff --git a/mifosng-data/src/main/java/org/mifosng/data/command/LoanProductCommandData.java b/mifosng-data/src/main/java/org/mifosng/data/command/LoanProductCommandData.java deleted file mode 100644 index 0cfdf73f2..000000000 --- a/mifosng-data/src/main/java/org/mifosng/data/command/LoanProductCommandData.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.mifosng.data.command; - -import java.math.BigDecimal; - -public interface LoanProductCommandData { - - String getName(); - - String getDescription(); - - String getCurrencyCode(); - - Integer getDigitsAfterDecimal(); - - Integer getRepaymentEvery(); - - Integer getRepaymentFrequency(); - - Boolean isFlexibleRepaymentSchedule(); - - Boolean isInterestRebateAllowed(); - - BigDecimal getPrincipal(); - -// Boolean getFlexibleRepaymentSchedule(); - -// Boolean getInterestRebateAllowed(); - - Integer getInterestRateFrequencyMethod(); - - Integer getInterestMethod(); - - Integer getAmortizationMethod(); - - Integer getNumberOfRepayments(); - - BigDecimal getInterestRatePerPeriod(); - - CommonLoanProperties getCommonLoanProperties(); - - BigDecimal getInArrearsToleranceAmount(); - - Integer getInterestCalculationPeriodMethod(); -} \ No newline at end of file diff --git a/mifosng-data/src/main/java/org/mifosng/data/command/SubmitLoanApplicationCommand.java b/mifosng-data/src/main/java/org/mifosng/data/command/SubmitLoanApplicationCommand.java index 1bf2608a3..d076dd7df 100644 --- a/mifosng-data/src/main/java/org/mifosng/data/command/SubmitLoanApplicationCommand.java +++ b/mifosng-data/src/main/java/org/mifosng/data/command/SubmitLoanApplicationCommand.java @@ -9,20 +9,45 @@ import org.mifosng.data.LoanSchedule; @XmlRootElement public class SubmitLoanApplicationCommand { - + private Long applicantId; private Long productId; + // product + private String currencyCode; + private Integer digitsAfterDecimal; + private String principalFormatted; + private BigDecimal principal; + private String inArrearsToleranceAmountFormatted; + private BigDecimal inArrearsToleranceAmount; + + private Integer repaymentEvery; + private Integer repaymentFrequency; + private Integer numberOfRepayments; + + private String interestRatePerPeriodFormatted; + private BigDecimal interestRatePerPeriod; + private Integer interestRateFrequencyMethod; + private Integer amortizationMethod; + private Integer interestMethod; + private Integer interestCalculationPeriodMethod; + private boolean flexibleRepaymentSchedule = false; + private boolean interestRebateAllowed = false; + // + + private String expectedDisbursementDateFormatted; + private String repaymentsStartingFromDateFormatted; + private String interestCalculatedFromDateFormatted; private LocalDate expectedDisbursementDate; private LocalDate repaymentsStartingFromDate; private LocalDate interestCalculatedFromDate; + private String dateFormat; + private String submittedOnDateFormatted; private LocalDate submittedOnDate; private String submittedOnNote; private String externalId; - private CommonLoanProperties commonLoanProperties; - private LoanSchedule loanSchedule; protected SubmitLoanApplicationCommand() { @@ -38,22 +63,34 @@ public class SubmitLoanApplicationCommand { final LoanSchedule loanSchedule, final String currencyCode, final Integer digitsAfterDecimal, final Number principal, final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod, final Integer interestCalculationPeriodMethod, - final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod, final Number toleranceAmount, - final boolean flexibleRepaymentSchedule, - final boolean interestRebateAllowed) { + final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod, final Number toleranceAmount) { this.applicantId = applicantId; this.productId = productId; this.submittedOnDate = submittedOnDate; this.submittedOnNote = submittedOnNote; - - commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod, - interestMethod, interestCalculationPeriodMethod, - repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount, flexibleRepaymentSchedule, interestRebateAllowed); - this.expectedDisbursementDate = expectedDisbursementDate; this.repaymentsStartingFromDate = repaymentsStartingFromDate; this.interestCalculatedFromDate = interestCalculatedFromDate; this.loanSchedule = loanSchedule; + this.inArrearsToleranceAmount = BigDecimal.valueOf(toleranceAmount.doubleValue()); + + this.currencyCode = currencyCode; + this.digitsAfterDecimal = digitsAfterDecimal; + this.principal = BigDecimal.valueOf(principal.doubleValue()); + this.interestRatePerPeriod = BigDecimal.valueOf(interestRatePerPeriod.doubleValue()); + this.interestRateFrequencyMethod = interestRateFrequencyMethod; + this.interestMethod = interestMethod; + this.interestCalculationPeriodMethod = interestCalculationPeriodMethod; + this.repaymentEvery = repaymentEvery; + this.repaymentFrequency = repaymentFrequency; + this.numberOfRepayments = numberOfRepayments; + this.amortizationMethod = amortizationMethod; + this.flexibleRepaymentSchedule = false; + this.interestRebateAllowed = false; + } + + public CalculateLoanScheduleCommand toCalculateLoanScheduleCommand() { + return new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate); } public Long getApplicantId() { @@ -122,125 +159,117 @@ public class SubmitLoanApplicationCommand { this.externalId = externalId; } - public CommonLoanProperties getCommonLoanProperties() { - return commonLoanProperties; - } - - public void setCommonLoanProperties(CommonLoanProperties commonLoanProperties) { - this.commonLoanProperties = commonLoanProperties; - } - public String getCurrencyCode() { - return this.commonLoanProperties.getCurrencyCode(); + return currencyCode; } - public void setCurrencyCode(final String currencyCode) { - this.commonLoanProperties.setCurrencyCode(currencyCode); + public void setCurrencyCode(String currencyCode) { + this.currencyCode = currencyCode; } public Integer getDigitsAfterDecimal() { - return this.commonLoanProperties.getDigitsAfterDecimal(); + return digitsAfterDecimal; } - public void setDigitsAfterDecimal(final Integer digitsAfterDecimal) { - this.commonLoanProperties.setDigitsAfterDecimal(digitsAfterDecimal); - } - - public Integer getRepaymentEvery() { - return this.commonLoanProperties.getRepaymentEvery(); - } - - public void setRepaymentEvery(final Integer repaymentEvery) { - this.commonLoanProperties.setRepaymentEvery(repaymentEvery); - } - - public Integer getRepaymentFrequency() { - return this.commonLoanProperties.getRepaymentFrequency(); - } - - public void setRepaymentFrequency(final Integer repaymentFrequency) { - this.commonLoanProperties.setRepaymentFrequency(repaymentFrequency); - } - - public Boolean isFlexibleRepaymentSchedule() { - return this.commonLoanProperties.isFlexibleRepaymentSchedule(); - } - - public void setFlexibleRepaymentSchedule( - final Boolean flexibleRepaymentSchedule) { - this.commonLoanProperties.setFlexibleRepaymentSchedule(flexibleRepaymentSchedule); - } - - public Boolean isInterestRebateAllowed() { - return this.commonLoanProperties.isInterestRebateAllowed(); - } - - public void setInterestRebateAllowed(final Boolean interestRebateAllowed) { - this.commonLoanProperties.setInterestRebateAllowed(interestRebateAllowed); + public void setDigitsAfterDecimal(Integer digitsAfterDecimal) { + this.digitsAfterDecimal = digitsAfterDecimal; } public BigDecimal getPrincipal() { - return this.commonLoanProperties.getPrincipal(); + return principal; } - public void setPrincipal(final BigDecimal principal) { - this.commonLoanProperties.setPrincipal(principal); + public void setPrincipal(BigDecimal principal) { + this.principal = principal; } -// public Boolean getFlexibleRepaymentSchedule() { -// return this.commonLoanProperties.getFlexibleRepaymentSchedule(); -// } -// -// public Boolean getInterestRebateAllowed() { -// return this.commonLoanProperties.getInterestRebateAllowed(); -// } - - public Integer getInterestRateFrequencyMethod() { - return commonLoanProperties.getInterestRateFrequencyMethod(); - } - - public void setInterestRateFrequencyMethod(Integer interestRateFrequencyMethod) { - this.commonLoanProperties.setInterestRateFrequencyMethod(interestRateFrequencyMethod); - } - - public Integer getInterestMethod() { - return commonLoanProperties.getInterestMethod(); - } - - public void setInterestMethod(Integer interestMethod) { - this.commonLoanProperties.setInterestMethod(interestMethod); - } - - public Integer getAmortizationMethod() { - return commonLoanProperties.getAmortizationMethod(); - } - - public void setAmortizationMethod(Integer amortizationMethod) { - this.commonLoanProperties.setAmortizationMethod(amortizationMethod); - } - - public Integer getNumberOfRepayments() { - return commonLoanProperties.getNumberOfRepayments(); - } - - public void setNumberOfRepayments(Integer numberOfRepayments) { - this.commonLoanProperties.setNumberOfRepayments(numberOfRepayments); - } - - public BigDecimal getInterestRatePerPeriod() { - return commonLoanProperties.getInterestRatePerPeriod(); - } - - public void setInterestRatePerPeriod(BigDecimal interestRatePerPeriod) { - this.commonLoanProperties.setInterestRatePerPeriod(interestRatePerPeriod); - } - public BigDecimal getInArrearsToleranceAmount() { - return this.commonLoanProperties.getInArrearsToleranceAmount(); + return inArrearsToleranceAmount; } public void setInArrearsToleranceAmount(BigDecimal inArrearsToleranceAmount) { - this.commonLoanProperties.setInArrearsToleranceAmount(inArrearsToleranceAmount); + this.inArrearsToleranceAmount = inArrearsToleranceAmount; + } + + public Integer getRepaymentEvery() { + return repaymentEvery; + } + + public void setRepaymentEvery(Integer repaymentEvery) { + this.repaymentEvery = repaymentEvery; + } + + public Integer getRepaymentFrequency() { + return repaymentFrequency; + } + + public void setRepaymentFrequency(Integer repaymentFrequency) { + this.repaymentFrequency = repaymentFrequency; + } + + public Integer getNumberOfRepayments() { + return numberOfRepayments; + } + + public void setNumberOfRepayments(Integer numberOfRepayments) { + this.numberOfRepayments = numberOfRepayments; + } + + public BigDecimal getInterestRatePerPeriod() { + return interestRatePerPeriod; + } + + public void setInterestRatePerPeriod(BigDecimal interestRatePerPeriod) { + this.interestRatePerPeriod = interestRatePerPeriod; + } + + public Integer getInterestRateFrequencyMethod() { + return interestRateFrequencyMethod; + } + + public void setInterestRateFrequencyMethod(Integer interestRateFrequencyMethod) { + this.interestRateFrequencyMethod = interestRateFrequencyMethod; + } + + public Integer getAmortizationMethod() { + return amortizationMethod; + } + + public void setAmortizationMethod(Integer amortizationMethod) { + this.amortizationMethod = amortizationMethod; + } + + public Integer getInterestMethod() { + return interestMethod; + } + + public void setInterestMethod(Integer interestMethod) { + this.interestMethod = interestMethod; + } + + public Integer getInterestCalculationPeriodMethod() { + return interestCalculationPeriodMethod; + } + + public void setInterestCalculationPeriodMethod( + Integer interestCalculationPeriodMethod) { + this.interestCalculationPeriodMethod = interestCalculationPeriodMethod; + } + + public boolean isFlexibleRepaymentSchedule() { + return flexibleRepaymentSchedule; + } + + public void setFlexibleRepaymentSchedule(boolean flexibleRepaymentSchedule) { + this.flexibleRepaymentSchedule = flexibleRepaymentSchedule; + } + + public boolean isInterestRebateAllowed() { + return interestRebateAllowed; + } + + public void setInterestRebateAllowed(boolean interestRebateAllowed) { + this.interestRebateAllowed = interestRebateAllowed; } public LocalDate getInterestCalculatedFromDate() { @@ -250,12 +279,73 @@ public class SubmitLoanApplicationCommand { public void setInterestCalculatedFromDate(LocalDate interestCalculatedFromDate) { this.interestCalculatedFromDate = interestCalculatedFromDate; } - - public Integer getInterestCalculationPeriodMethod() { - return this.commonLoanProperties.getInterestCalculationPeriodMethod(); + + public String getExpectedDisbursementDateFormatted() { + return expectedDisbursementDateFormatted; } - public void setInterestCalculationPeriodMethod(final Integer interestCalculationPeriodMethod) { - this.commonLoanProperties.setInterestCalculationPeriodMethod(interestCalculationPeriodMethod); + public void setExpectedDisbursementDateFormatted( + String expectedDisbursementDateFormatted) { + this.expectedDisbursementDateFormatted = expectedDisbursementDateFormatted; + } + + public String getRepaymentsStartingFromDateFormatted() { + return repaymentsStartingFromDateFormatted; + } + + public void setRepaymentsStartingFromDateFormatted( + String repaymentsStartingFromDateFormatted) { + this.repaymentsStartingFromDateFormatted = repaymentsStartingFromDateFormatted; + } + + public String getInterestCalculatedFromDateFormatted() { + return interestCalculatedFromDateFormatted; + } + + public void setInterestCalculatedFromDateFormatted( + String interestCalculatedFromDateFormatted) { + this.interestCalculatedFromDateFormatted = interestCalculatedFromDateFormatted; + } + + public String getDateFormat() { + return dateFormat; + } + + public void setDateFormat(String dateFormat) { + this.dateFormat = dateFormat; + } + + public String getSubmittedOnDateFormatted() { + return submittedOnDateFormatted; + } + + public void setSubmittedOnDateFormatted(String submittedOnDateFormatted) { + this.submittedOnDateFormatted = submittedOnDateFormatted; + } + + public String getPrincipalFormatted() { + return principalFormatted; + } + + public void setPrincipalFormatted(String principalFormatted) { + this.principalFormatted = principalFormatted; + } + + public String getInArrearsToleranceAmountFormatted() { + return inArrearsToleranceAmountFormatted; + } + + public void setInArrearsToleranceAmountFormatted( + String inArrearsToleranceAmountFormatted) { + this.inArrearsToleranceAmountFormatted = inArrearsToleranceAmountFormatted; + } + + public String getInterestRatePerPeriodFormatted() { + return interestRatePerPeriodFormatted; + } + + public void setInterestRatePerPeriodFormatted( + String interestRatePerPeriodFormatted) { + this.interestRatePerPeriodFormatted = interestRatePerPeriodFormatted; } } \ No newline at end of file diff --git a/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/CommonRestOperations.java b/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/CommonRestOperations.java index 5f6efe356..510294eb0 100644 --- a/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/CommonRestOperations.java +++ b/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/CommonRestOperations.java @@ -1,16 +1,10 @@ package org.mifosng.ui; -import java.util.Collection; - import org.mifosng.data.EntityIdentifier; -import org.mifosng.data.LoanProductData; import org.mifosng.data.LoanRepaymentData; -import org.mifosng.data.LoanSchedule; import org.mifosng.data.command.AdjustLoanTransactionCommand; -import org.mifosng.data.command.CalculateLoanScheduleCommand; import org.mifosng.data.command.LoanStateTransitionCommand; import org.mifosng.data.command.LoanTransactionCommand; -import org.mifosng.data.command.SubmitLoanApplicationCommand; import org.mifosng.data.command.UndoLoanApprovalCommand; import org.mifosng.data.command.UndoLoanDisbursalCommand; import org.springframework.security.oauth.consumer.ProtectedResourceDetails; @@ -21,12 +15,6 @@ public interface CommonRestOperations { void updateProtectedResource(ProtectedResourceDetails loadProtectedResourceDetailsById); - Collection retrieveAllLoanProducts(); - - LoanSchedule calculateLoanSchedule(CalculateLoanScheduleCommand command); - - Long submitLoanApplication(SubmitLoanApplicationCommand command); - EntityIdentifier deleteLoan(Long loanId); EntityIdentifier approveLoan(LoanStateTransitionCommand command); diff --git a/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/CommonRestOperationsImpl.java b/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/CommonRestOperationsImpl.java index 4e4267a85..81a2e0cc1 100644 --- a/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/CommonRestOperationsImpl.java +++ b/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/CommonRestOperationsImpl.java @@ -2,7 +2,6 @@ package org.mifosng.ui; import java.net.URI; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import org.mifosng.configuration.ApplicationConfigurationService; @@ -10,15 +9,10 @@ import org.mifosng.data.ClientList; import org.mifosng.data.EntityIdentifier; import org.mifosng.data.ErrorResponse; import org.mifosng.data.ErrorResponseList; -import org.mifosng.data.LoanProductData; -import org.mifosng.data.LoanProductList; import org.mifosng.data.LoanRepaymentData; -import org.mifosng.data.LoanSchedule; import org.mifosng.data.command.AdjustLoanTransactionCommand; -import org.mifosng.data.command.CalculateLoanScheduleCommand; import org.mifosng.data.command.LoanStateTransitionCommand; import org.mifosng.data.command.LoanTransactionCommand; -import org.mifosng.data.command.SubmitLoanApplicationCommand; import org.mifosng.data.command.UndoLoanApprovalCommand; import org.mifosng.data.command.UndoLoanDisbursalCommand; import org.mifosng.ui.loanproduct.ClientValidationException; @@ -81,18 +75,6 @@ public class CommonRestOperationsImpl implements CommonRestOperations { return requestEntity; } - @Override - public Collection retrieveAllLoanProducts() { - URI restUri = URI.create(getBaseServerUrl().concat( - "api/protected/product/loan/all")); - - ResponseEntity s = this.oauthRestServiceTemplate - .exchange(restUri, HttpMethod.GET, emptyRequest(), - LoanProductList.class); - - return s.getBody().getProducts(); - } - private ErrorResponseList parseErrors(HttpStatusCodeException e) { XStream xstream = new XStream(); @@ -118,43 +100,6 @@ public class CommonRestOperationsImpl implements CommonRestOperations { return errorList; } - @Override - public LoanSchedule calculateLoanSchedule( - final CalculateLoanScheduleCommand command) { - try { - URI restUri = URI.create(getBaseServerUrl().concat( - "api/protected/loan/calculate")); - - ResponseEntity s = this.oauthRestServiceTemplate - .exchange(restUri, HttpMethod.POST, - calculateLoanRepaymentScheduleRequest(command), - LoanSchedule.class); - - return s.getBody(); - } catch (HttpStatusCodeException e) { - ErrorResponseList errorList = parseErrors(e); - throw new ClientValidationException(errorList.getErrors()); - } - } - - @Override - public Long submitLoanApplication(final SubmitLoanApplicationCommand command) { - try { - URI restUri = URI.create(getBaseServerUrl().concat( - "api/protected/loan/new")); - - ResponseEntity s = this.oauthRestServiceTemplate - .postForEntity(restUri, - submitLoanApplicationRequest(command), - EntityIdentifier.class); - - return s.getBody().getEntityId(); - } catch (HttpStatusCodeException e) { - ErrorResponseList errorList = parseErrors(e); - throw new ClientValidationException(errorList.getErrors()); - } - } - @Override public EntityIdentifier deleteLoan(Long loanId) { try { @@ -437,24 +382,4 @@ public class CommonRestOperationsImpl implements CommonRestOperations { return new HttpEntity(command, requestHeaders); } - - private HttpEntity submitLoanApplicationRequest( - final SubmitLoanApplicationCommand command) { - HttpHeaders requestHeaders = new HttpHeaders(); - requestHeaders.set("Accept", "application/xml"); - requestHeaders.set("Content-Type", "application/xml"); - - return new HttpEntity(command, - requestHeaders); - } - - private HttpEntity calculateLoanRepaymentScheduleRequest( - final CalculateLoanScheduleCommand command) { - HttpHeaders requestHeaders = new HttpHeaders(); - requestHeaders.set("Accept", "application/xml"); - requestHeaders.set("Content-Type", "application/xml"); - - return new HttpEntity(command, - requestHeaders); - } } \ No newline at end of file diff --git a/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/loan/CashflowController.java b/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/loan/CashflowController.java index f613e882e..95a3211d8 100644 --- a/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/loan/CashflowController.java +++ b/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/loan/CashflowController.java @@ -13,6 +13,10 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +/** + * @deprecated - some initial work around cashflow for CreoCore that can be deleted later. + */ +@Deprecated @Controller public class CashflowController { diff --git a/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/loan/LoanController.java b/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/loan/LoanController.java deleted file mode 100644 index 8bd49369e..000000000 --- a/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/loan/LoanController.java +++ /dev/null @@ -1,179 +0,0 @@ -package org.mifosng.ui.loan; - -import java.math.BigDecimal; -import java.text.NumberFormat; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Locale; - -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; -import org.joda.time.LocalDate; -import org.joda.time.format.DateTimeFormat; -import org.mifosng.data.EntityIdentifier; -import org.mifosng.data.ErrorResponse; -import org.mifosng.data.LoanSchedule; -import org.mifosng.data.command.CalculateLoanScheduleCommand; -import org.mifosng.data.command.SubmitLoanApplicationCommand; -import org.mifosng.ui.CommonRestOperations; -import org.mifosng.ui.loanproduct.ClientValidationException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; - -@Controller -public class LoanController { - - private final CommonRestOperations commonRestOperations; - - @Autowired - public LoanController(final CommonRestOperations commonRestOperations) { - this.commonRestOperations = commonRestOperations; - } - - @ExceptionHandler(AccessDeniedException.class) - public String accessDeniedException() { - return "unAuthorizedAction"; - } - - @ExceptionHandler(ClientValidationException.class) - public @ResponseBody Collection validationException(ClientValidationException ex, HttpServletResponse response) { - - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - response.setContentType("application/json"); - - return ex.getValidationErrors(); - } - - private LocalDate parseStringToLocalDate(String eventDate, String dateFieldIdentifier) { - LocalDate eventLocalDate = null; - if (StringUtils.isNotBlank(eventDate)) { - try { - Locale locale = LocaleContextHolder.getLocale(); - eventLocalDate = DateTimeFormat.forPattern("dd MMMM yyyy").withLocale(locale).parseLocalDate(eventDate.toLowerCase(locale)); - } catch (IllegalArgumentException e) { - List validationErrors = new ArrayList(); - validationErrors.add(new ErrorResponse("validation.msg.invalid.date.format", dateFieldIdentifier, eventDate)); - throw new ClientValidationException(validationErrors); - } - } - - return eventLocalDate; - } - - private BigDecimal parseStringToBigDecimal(String source) { - try { - BigDecimal number = null; - - if (StringUtils.isNotBlank(source)) { - String sourceWithoutSpaces = source.replaceAll(" ", ""); - Locale locale = LocaleContextHolder.getLocale(); - NumberFormat format = NumberFormat.getNumberInstance(locale); - Number parsedNumber = format.parse(sourceWithoutSpaces); - number = BigDecimal.valueOf(Double.valueOf(parsedNumber - .doubleValue())); - } - - return number; - } catch (ParseException e) { - List validationErrors = new ArrayList(); - validationErrors.add(new ErrorResponse("validation.msg.invalid.number.format", "amount", source)); - throw new ClientValidationException(validationErrors); - } - } - - @RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/client/{clientId}/loan/new", method = RequestMethod.POST) - public @ResponseBody EntityIdentifier submitNewLoanApplication( - @RequestParam(value="clientId", required=false) Long applicantId, - @RequestParam(value="productId", required=false) Long productId, - @RequestParam(value="submittedOnDate", required=false) String submittedOnDateStr, - @RequestParam(value="submittedOnNote", required=false) String submittedOnNote, - @RequestParam(value="currencyCode", required=false) String currencyCode, - @RequestParam(value="digitsAfterDecimal", required=false) Integer digitsAfterDecimal, - @RequestParam(value="principalMoney", required=false) String principalMoney, - @RequestParam(value="repaidEvery", required=false) Integer repaymentEvery, - @RequestParam(value="selectedRepaymentFrequencyOption", required=false) Integer repaymentFrequency, - @RequestParam(value="numberOfRepayments", required=false) Integer numberOfRepayments, - @RequestParam(value="selectedAmortizationMethodOption", required=false) Integer amortizationMethod, - @RequestParam(value="inArrearsTolerance", required=false) String inArrearsTolerance, - @RequestParam(value="nominalInterestRate", required=false) String nominalInterestRate, - @RequestParam(value="selectedInterestFrequencyOption", required=false) Integer interestRateFrequencyMethod, - @RequestParam(value="selectedInterestMethodOption", required=false) Integer interestMethod, - @RequestParam(value="interestRateCalculatedInPeriod", required=false) Integer interestRateCalculatedInPeriod, - @RequestParam(value="expectedDisbursementDate", required=false) String expectedDisbursementDateStr, - @RequestParam(value="repaymentsStartingFromDate", required=false) String repaymentsStartingFromDateStr, - @RequestParam(value="interestCalculatedFromDate", required=false) String interestCalculatedFromDateStr - ) { - - final LocalDate submittedOnDate = parseStringToLocalDate(submittedOnDateStr, "submittedOnDate"); - final Number toleranceAmount = parseStringToBigDecimal(inArrearsTolerance); - - final Number principal = parseStringToBigDecimal(principalMoney); - final Number interestRatePerPeriod = parseStringToBigDecimal(nominalInterestRate); - final boolean flexibleRepaymentSchedule = false; - final boolean interestRebateAllowed = false; - - final LocalDate expectedDisbursementDate = parseStringToLocalDate(expectedDisbursementDateStr, "expectedDisbursementDate"); - final LocalDate repaymentsStartingFromDate = parseStringToLocalDate(repaymentsStartingFromDateStr, "repaymentsStartingFromDate"); - final LocalDate interestCalculatedFromDate = parseStringToLocalDate(interestCalculatedFromDateStr, "interestCalculatedFromDate"); - - CalculateLoanScheduleCommand calculateLoanScheduleCommand = new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, - interestRateFrequencyMethod, interestMethod, interestRateCalculatedInPeriod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, - flexibleRepaymentSchedule, interestRebateAllowed, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate); - - LoanSchedule loanSchedule = this.commonRestOperations.calculateLoanSchedule(calculateLoanScheduleCommand); - - SubmitLoanApplicationCommand command = new SubmitLoanApplicationCommand(applicantId, productId, - submittedOnDate, submittedOnNote, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate, - loanSchedule, currencyCode, digitsAfterDecimal, principal, - interestRatePerPeriod, interestRateFrequencyMethod, interestMethod, interestRateCalculatedInPeriod, - repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, - toleranceAmount, flexibleRepaymentSchedule, interestRebateAllowed); - - Long identifier = this.commonRestOperations.submitLoanApplication(command); - - return new EntityIdentifier(identifier); - } - - @RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loanschedule/calculate", method = RequestMethod.POST) - public @ResponseBody LoanSchedule calculateLoanSchedule( - @RequestParam(value="currencyCode", required=false) String currencyCode, - @RequestParam(value="digitsAfterDecimal", required=false) Integer digitsAfterDecimal, - @RequestParam(value="principalMoney", required=false) String principalMoney, - @RequestParam(value="repaidEvery", required=false) Integer repaymentEvery, - @RequestParam(value="selectedRepaymentFrequencyOption", required=false) Integer repaymentFrequency, - @RequestParam(value="numberOfRepayments", required=false) Integer numberOfRepayments, - @RequestParam(value="selectedAmortizationMethodOption", required=false) Integer amortizationMethod, - @RequestParam(value="nominalInterestRate", required=false) String nominalInterestRate, - @RequestParam(value="selectedInterestFrequencyOption", required=false) Integer interestRateFrequencyMethod, - @RequestParam(value="selectedInterestMethodOption", required=false) Integer interestMethod, - @RequestParam(value="interestRateCalculatedInPeriod", required=false) Integer interestRateCalculatedInPeriod, - @RequestParam(value="expectedDisbursementDate", required=false) String expectedDisbursementDateStr, - @RequestParam(value="repaymentsStartingFromDate", required=false) String repaymentsStartingFromDateStr, - @RequestParam(value="interestCalculatedFromDate", required=false) String interestCalculatedFromDateStr - ) { - - final Number principal = parseStringToBigDecimal(principalMoney); - final Number interestRatePerPeriod = parseStringToBigDecimal(nominalInterestRate); - final boolean flexibleRepaymentSchedule = false; - final boolean interestRebateAllowed = false; - final LocalDate expectedDisbursementDate = parseStringToLocalDate(expectedDisbursementDateStr, "expectedDisbursementDate"); - final LocalDate repaymentsStartingFromDate = parseStringToLocalDate(repaymentsStartingFromDateStr, "repaymentsStartingFromDate"); - final LocalDate interestCalculatedFromDate = parseStringToLocalDate(interestCalculatedFromDateStr, "interestCalculatedFromDate"); - - CalculateLoanScheduleCommand command = new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, - interestRateFrequencyMethod, interestMethod, interestRateCalculatedInPeriod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, - flexibleRepaymentSchedule, interestRebateAllowed, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate); - - return this.commonRestOperations.calculateLoanSchedule(command); - } -} \ No newline at end of file diff --git a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/client/viewClientAccount.jsp b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/client/viewClientAccount.jsp index 91fadb97e..3a7a65515 100644 --- a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/client/viewClientAccount.jsp +++ b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/client/viewClientAccount.jsp @@ -206,7 +206,7 @@ $(document).ready(function() { return o; }; - function popupConfirmationDialogAndPost(url, titleCode, width, height, tabIndex, redirectUrl) { + function popupConfirmationDialogAndPost(url, submitType, titleCode, width, height, tabIndex, redirectUrl) { var dialogDiv = $("
" + jQuery.i18n.prop('text.confirmation.required') + "
"); var confirmButton = jQuery.i18n.prop('dialog.button.confirm'); @@ -216,7 +216,9 @@ $(document).ready(function() { buttonsOpts[confirmButton] = function() { var jqxhr = $.ajax({ url: url, - type: 'POST', + type: submitType, + contentType: 'application/json', + dataType: 'json', success: function(data, textStatus, jqXHR) { dialogDiv.dialog("close"); if (tabIndex > 0) { @@ -322,70 +324,70 @@ $(document).ready(function() { dataType: 'json', cache: false, success: function(data, textStatus, jqXHR) { - console.log(data); - var formHtml = $(templateSelector).render(data); - - dialogDiv.append(formHtml); - - var saveButton = jQuery.i18n.prop('dialog.button.save'); - var cancelButton = jQuery.i18n.prop('dialog.button.cancel'); - - var buttonsOpts = {}; - buttonsOpts[saveButton] = function() { + console.log(data); + var formHtml = $(templateSelector).render(data); - $('#notSelectedItems option').each(function(i) { + dialogDiv.append(formHtml); + + var saveButton = jQuery.i18n.prop('dialog.button.save'); + var cancelButton = jQuery.i18n.prop('dialog.button.cancel'); + + var buttonsOpts = {}; + buttonsOpts[saveButton] = function() { + + $('#notSelectedItems option').each(function(i) { + $(this).attr("selected", "selected"); + }); + + $('#selectedItems option').each(function(i) { $(this).attr("selected", "selected"); - }); - - $('#selectedItems option').each(function(i) { - $(this).attr("selected", "selected"); - }); - - var newFormData = JSON.stringify($('#entityform').serializeObject()); - console.log(newFormData); - - var jqxhr = $.ajax({ - url: postUrl, - type: submitType, - contentType: 'application/json', - dataType: 'json', - data: newFormData, - success: saveSuccessFunction, - error: function(jqXHR, textStatus, errorThrown) { - handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors"); - } - }); - }; - - buttonsOpts[cancelButton] = function() {$(this).dialog( "close" );}; - - dialogDiv.dialog({ - title: jQuery.i18n.prop(titleCode), - width: width, - height: height, - modal: true, - buttons: buttonsOpts, - close: function() { - // if i dont do this, theres a problem with errors being appended to dialog view second time round - $(this).remove(); - }, - open: function (event, ui) { - - $('#add').click(function() { - return !$('#notSelectedItems option:selected').remove().appendTo('#selectedItems'); - }); - - $('#remove').click(function() { - return !$('#selectedItems option:selected').remove().appendTo('#notSelectedItems'); - }); - - $('.datepickerfield').datepicker({constrainInput: true, maxDate: 0, dateFormat: 'dd MM yy'}); - - $("#entityform textarea").first().focus(); - $('#entityform input').first().focus(); - } - }).dialog('open'); - } + }); + + var newFormData = JSON.stringify($('#entityform').serializeObject()); + console.log(newFormData); + + var jqxhr = $.ajax({ + url: postUrl, + type: submitType, + contentType: 'application/json', + dataType: 'json', + data: newFormData, + success: saveSuccessFunction, + error: function(jqXHR, textStatus, errorThrown) { + handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors"); + } + }); + }; + + buttonsOpts[cancelButton] = function() {$(this).dialog( "close" );}; + + dialogDiv.dialog({ + title: jQuery.i18n.prop(titleCode), + width: width, + height: height, + modal: true, + buttons: buttonsOpts, + close: function() { + // if i dont do this, theres a problem with errors being appended to dialog view second time round + $(this).remove(); + }, + open: function (event, ui) { + + $('#add').click(function() { + return !$('#notSelectedItems option:selected').remove().appendTo('#selectedItems'); + }); + + $('#remove').click(function() { + return !$('#selectedItems option:selected').remove().appendTo('#notSelectedItems'); + }); + + $('.datepickerfield').datepicker({constrainInput: true, maxDate: 0, dateFormat: 'dd MM yy'}); + + $("#entityform textarea").first().focus(); + $('#entityform input').first().focus(); + } + }).dialog('open'); + } }); jqxhr.error(function(jqXHR, textStatus, errorThrown) { @@ -435,19 +437,6 @@ $(document).ready(function() { // retrieve accounts summary info refreshLoanSummaryInfo(); - // retrieve additional info - var additionalFieldsParams = { - url: "http://localhost:8080/mifosng-provider/", - datasetType: "portfolio_client", - datasetPKValue: data.id, - datasetTypeDiv: "clientadditionaldata", - headingPrefix: "", - headingClass: "", - labelClass: "longrowlabel", - valueClass: "rowvalue" - }; - jQuery.stretchyData.displayAllExtraData(additionalFieldsParams); - // bind click listeners to buttons. $('.casflowbtn').button().click(function(e) { var linkId = this.id; @@ -489,24 +478,25 @@ $(document).ready(function() { refreshNoteWidget(); - } else { - - var tableHtml = $("#loanDataTabTemplate").render(data); - - var currentTab = $("#newtabs").children(".ui-tabs-panel").not(".ui-tabs-hide"); - currentTab.html(tableHtml); - + // retrieve additional info var additionalFieldsParams = { url: "http://localhost:8080/mifosng-provider/", - datasetType: "portfolio_loan", + datasetType: "portfolio_client", datasetPKValue: data.id, - datasetTypeDiv: "loanadditionaldata" + data.id, + datasetTypeDiv: "clientadditionaldata", headingPrefix: "", headingClass: "", labelClass: "longrowlabel", valueClass: "rowvalue" }; jQuery.stretchyData.displayAllExtraData(additionalFieldsParams); + + } else { + + var tableHtml = $("#loanDataTabTemplate").render(data); + + var currentTab = $("#newtabs").children(".ui-tabs-panel").not(".ui-tabs-hide"); + currentTab.html(tableHtml); var curTabID = currentTab.prop("id") @@ -560,13 +550,13 @@ $(document).ready(function() { var linkId = this.id; var loanId = linkId.replace("deletebtn", ""); - var url = '${rootContext}portfolio/loan/' + loanId + '/delete'; + var url = 'http://localhost:8080/mifosng-provider/api/v1/loans/' + loanId; var width = 400; var height = 225; var redirectUrl = '${clientUrl}'; - popupConfirmationDialogAndPost(url, 'dialog.title.confirmation.required', width, height, 0, redirectUrl); + popupConfirmationDialogAndPost(url, 'DELETE', 'dialog.title.confirmation.required', width, height, 0, redirectUrl); e.preventDefault(); }); $('button.deleteloan span').text(jQuery.i18n.prop('dialog.button.delete.loan')); @@ -660,6 +650,19 @@ $(document).ready(function() { var curTabID = curTab.prop("id") } }); + + // additional data + var additionalFieldsParams = { + url: "http://localhost:8080/mifosng-provider/", + datasetType: "portfolio_loan", + datasetPKValue: data.id, + datasetTypeDiv: "loanadditionaldata" + data.id, + headingPrefix: "", + headingClass: "", + labelClass: "longrowlabel", + valueClass: "rowvalue" + }; + jQuery.stretchyData.displayAllExtraData(additionalFieldsParams); } } } diff --git a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/newloanapplication.jsp b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/newloanapplication.jsp index df8090c32..a44af1d67 100644 --- a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/newloanapplication.jsp +++ b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/newloanapplication.jsp @@ -173,13 +173,13 @@ $.views.registerHelpers({ }; function calculateAnnualPercentageRate() { - var periodInterestRate = parseFloat($('#nominalInterestRate').val()); + var periodInterestRate = parseFloat($('#interestRatePerPeriodFormatted').val()); if (isNaN(periodInterestRate)) { periodInterestRate = 0; } var periodsInYear = 12; - var periodType = $('#selectedInterestFrequencyOption').val(); + var periodType = $('#interestRateFrequencyMethod').val(); if (periodType == 3) { periodsInYear = 1; } else if (periodType == 2) { @@ -220,15 +220,15 @@ $.views.registerHelpers({ calculateLoanSchedule(); // change detection - $('#principalMoney').change(function() { + $('#principalFormatted').change(function() { calculateLoanSchedule(); }); - $('#repaidEvery').change(function() { + $('#repaymentEvery').change(function() { calculateLoanSchedule(); }); - $('#selectedRepaymentFrequencyOption').change(function() { + $('#repaymentFrequency').change(function() { calculateLoanSchedule(); }); @@ -236,37 +236,37 @@ $.views.registerHelpers({ calculateLoanSchedule(); }); - $('#expectedDisbursementDate').change(function() { + $('#expectedDisbursementDateFormatted').change(function() { calculateLoanSchedule(); }); - $('#repaymentsStartingFromDate').change(function() { + $('#repaymentsStartingFromDateFormatted').change(function() { calculateLoanSchedule(); }); - $('#nominalInterestRate').change(function() { + $('#interestRatePerPeriodFormatted').change(function() { calculateAnnualPercentageRate(); calculateLoanSchedule(); }); - $('#selectedInterestFrequencyOption').change(function() { + $('#interestRateFrequencyMethod').change(function() { calculateAnnualPercentageRate(); calculateLoanSchedule(); }); - $('#selectedAmortizationMethodOption').change(function() { + $('#amortizationMethod').change(function() { calculateLoanSchedule(); }); - $('#selectedInterestMethodOption').change(function() { + $('#interestMethod').change(function() { calculateLoanSchedule(); }); - $('#interestRateCalculatedInPeriod').change(function() { + $('#interestCalculationPeriodMethod').change(function() { calculateLoanSchedule(); }); - $('#interestCalculatedFromDate').change(function() { + $('#interestCalculatedFromDateFormatted').change(function() { calculateLoanSchedule(); }); @@ -291,14 +291,17 @@ $.views.registerHelpers({ } function calculateLoanSchedule() { - var calculateLoanScheduleurl = '${rootContext}portfolio/loanschedule/calculate'; + var calculateLoanScheduleurl = 'http://localhost:8080/mifosng-provider/api/v1/loans?command=calculateLoanSchedule'; - var form_data = $('#entityform').serialize(); - + var newFormData = JSON.stringify($('#entityform').serializeObject()); + console.log(newFormData); + var jqxhr2 = $.ajax({ url: calculateLoanScheduleurl, type: 'POST', - data: form_data, + contentType: 'application/json', + dataType: 'json', + data: newFormData, success: function(data, textStatus, jqXHR) { removeErrors("#formerrors"); var loanScheduleHtml = $("#newLoanScheduleTemplate").render(data); @@ -313,17 +316,20 @@ $.views.registerHelpers({ } function submitLoanApplication() { - var submitLoanApplicationUrl = '${rootContext}portfolio/client/${clientId}/loan/new'; + var submitLoanApplicationUrl = 'http://localhost:8080/mifosng-provider/api/v1/loans'; - var form_data = $('#entityform').serialize(); - + var newFormData = JSON.stringify($('#entityform').serializeObject()); + console.log(newFormData); + var jqxhr2 = $.ajax({ url: submitLoanApplicationUrl, type: 'POST', - data: form_data, + contentType: 'application/json', + dataType: 'json', + data: newFormData, success: function(data, textStatus, jqXHR) { - var url = '${rootContext}portfolio/client/${clientId}'; - window.location.href = url; + var localAppUrlToSwitchToClientPage = '${rootContext}portfolio/client/${clientId}'; + window.location.href = localAppUrlToSwitchToClientPage; } }); @@ -331,9 +337,8 @@ $.views.registerHelpers({ handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors"); }); } - - - + + // on page load var jqxhr = $.ajax({ url: 'http://localhost:8080/mifosng-provider/api/v1/loans/template?clientId=${clientId}', type: 'GET', diff --git a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/view-templates.jsp b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/view-templates.jsp index 57659b09c..805df0753 100644 --- a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/view-templates.jsp +++ b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/view-templates.jsp @@ -54,7 +54,7 @@
- + @@ -103,16 +103,16 @@ - - + + - + - {{#each selectedProduct.repaymentFrequencyOptions}} {{#if $ctx.number($parent.parent.data.selectedProduct.repaymentPeriodFrequency)===$ctx.number(id)}} @@ -149,9 +149,9 @@ - + - {{#each selectedProduct.interestFrequencyOptions}} {{#if $ctx.number($parent.parent.data.selectedProduct.interestRatePeriod)===$ctx.number(id)}} @@ -171,7 +171,7 @@ - {{#each selectedProduct.possibleAmortizationOptions}} {{#if $ctx.number($parent.parent.data.selectedProduct.amortizationMethod)===$ctx.number(id)}} @@ -185,7 +185,7 @@ - {{#each selectedProduct.possibleInterestOptions}} {{#if $ctx.number($parent.parent.data.selectedProduct.interestMethod)===$ctx.number(id)}} @@ -199,7 +199,7 @@ - {{#each selectedProduct.possibleInterestRateCalculatedInPeriodOptions}} {{#if $ctx.number($parent.parent.data.selectedProduct.interestRateCalculatedInPeriod)===$ctx.number(id)}} @@ -213,14 +213,14 @@ - + - - + + diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/CalculateLoanScheduleCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/CalculateLoanScheduleCommandValidator.java deleted file mode 100644 index 3a5862f6a..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/CalculateLoanScheduleCommandValidator.java +++ /dev/null @@ -1,209 +0,0 @@ -package org.mifosng.platform; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang.StringUtils; -import org.mifosng.data.ErrorResponse; -import org.mifosng.data.command.CalculateLoanScheduleCommand; -import org.mifosng.platform.exceptions.NewDataValidationException; -import org.mifosng.platform.loan.domain.AmortizationMethod; -import org.mifosng.platform.loan.domain.InterestMethod; -import org.mifosng.platform.loan.domain.PeriodFrequencyType; - -public class CalculateLoanScheduleCommandValidator { - - private final CalculateLoanScheduleCommand command; - - public CalculateLoanScheduleCommandValidator(CalculateLoanScheduleCommand command) { - this.command = command; - } - - public void validate() { - List dataValidationErrors = new ArrayList(); - - if (StringUtils.isBlank(command.getCurrencyCode())) { - ErrorResponse error = new ErrorResponse("validation.msg.product.currency.cannot.be.empty", "currencyCode"); - dataValidationErrors.add(error); - } - - if (command.getDigitsAfterDecimal() != null) { - - if (command.getDigitsAfterDecimal() < 0 || command.getDigitsAfterDecimal() > 6) { - ErrorResponse error = new ErrorResponse("validation.msg.product.currency.digitsAfterDecimal.must.be.between.zero.and.six.inclusive", "selectedDigitsAfterDecimal", command.getDigitsAfterDecimal()); - dataValidationErrors.add(error); - } - } else { - ErrorResponse error = new ErrorResponse("validation.msg.product.currency.digitsAfterDecimal.cannot.be.empty", "selectedDigitsAfterDecimal"); - dataValidationErrors.add(error); - } - - if (command.getPrincipal() != null) { - if (command.getPrincipal().doubleValue() <= Double.valueOf("0.0")) { - ErrorResponse error = new ErrorResponse("validation.msg.product.principal.amount.must.be.greater.than.zero", "principalMoney"); - dataValidationErrors.add(error); - } - // TODO - check number of digits before decimal and after decimal - } else { - ErrorResponse error = new ErrorResponse("validation.msg.product.principal.amount.cannot.be.empty", "principalMoney"); - dataValidationErrors.add(error); - } - - if (command.getRepaymentFrequency() != null) { - PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequency()); - ErrorResponse 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 = new ErrorResponse("validation.msg.product.repayment.frequency.invalid", "selectedRepaymentFrequencyOption"); - break; - } - if (error != null) { - dataValidationErrors.add(error); - } - } else { - ErrorResponse error = new ErrorResponse("validation.msg.product.repayment.frequency.cannot.be.empty", "selectedRepaymentFrequencyOption"); - dataValidationErrors.add(error); - } - - if (command.getNumberOfRepayments() != null) { - if (command.getNumberOfRepayments() < 1 || command.getNumberOfRepayments() > 100) { - ErrorResponse error = new ErrorResponse("validation.msg.product.number.of.repayments.is.out.of.range", "numberOfRepayments"); - dataValidationErrors.add(error); - } - } else { - ErrorResponse error = new ErrorResponse("validation.msg.product.number.of.repayments.cannot.be.empty", "numberOfRepayments"); - dataValidationErrors.add(error); - } - - if (command.getAmortizationMethod() != null) { - AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod()); - ErrorResponse error = null; - switch (amortizationMethod) { - case EQUAL_PRINCIPAL: - break; - case EQUAL_INSTALLMENTS: - break; - default: - error = new ErrorResponse("validation.msg.product.amortization.method.invalid", "selectedAmortizationMethodOption"); - break; - } - if (error != null) { - dataValidationErrors.add(error); - } - } else { - ErrorResponse error = new ErrorResponse("validation.msg.product.amortization.method.cannot.be.empty", "selectedAmortizationMethodOption"); - dataValidationErrors.add(error); - } - - - if (command.getExpectedDisbursementDate() == null) { - ErrorResponse error = new ErrorResponse("validation.msg.loan.expectedDisbursementDate.method.cannot.be.blank", "expectedDisbursementDate"); - dataValidationErrors.add(error); - } else { - - if (command.getRepaymentsStartingFromDate() != null - && command.getExpectedDisbursementDate().isAfter(command.getRepaymentsStartingFromDate())) { - - ErrorResponse error = new ErrorResponse("validation.msg.loan.expectedDisbursementDate.cannot.be.after.first.repayment.date", "expectedDisbursementDate"); - dataValidationErrors.add(error); - } - } - - if (command.getRepaymentsStartingFromDate() != null && command.getInterestCalculatedFromDate() == null) { - - ErrorResponse error = new ErrorResponse("validation.msg.loan.interestCalculatedFromDate.must.be.entered.when.using.repayments.startfrom.field", "interestCalculatedFromDate"); - dataValidationErrors.add(error); - } else if (command.getRepaymentsStartingFromDate() == null && command.getInterestCalculatedFromDate() != null) { - - // validate interestCalculatedFromDate is after or on repaymentsStartingFromDate - - if (command.getExpectedDisbursementDate().isAfter(command.getInterestCalculatedFromDate())) { - ErrorResponse error = new ErrorResponse("validation.msg.loan.interestCalculatedFromDate.cannot.be.before.disbursement.date", "interestCalculatedFromDate"); - dataValidationErrors.add(error); - } - } - - if (command.getInterestRatePerPeriod()!= null) { - if (command.getInterestRatePerPeriod().doubleValue() < Double.valueOf("0.0")) { - ErrorResponse error = new ErrorResponse("validation.msg.product.interest.rate.per.period.amount.cannot.be.negative", "nominalInterestRate"); - dataValidationErrors.add(error); - } - } else { - ErrorResponse error = new ErrorResponse("validation.msg.product.interest.rate.per.period.amount.cannot.be.empty", "nominalInterestRate"); - dataValidationErrors.add(error); - } - - // interest rate period type - if (command.getInterestRateFrequencyMethod() != null) { - PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod()); - ErrorResponse error = null; - switch (frequencyType) { - case DAYS: - break; - case WEEKS: - break; - case MONTHS: - break; - case YEARS: - break; - default: - error = new ErrorResponse("validation.msg.product.interest.frequency.invalid", "selectedInterestFrequencyOption"); - break; - } - if (error != null) { - dataValidationErrors.add(error); - } - } else { - ErrorResponse error = new ErrorResponse("validation.msg.product.interest.frequency.cannot.be.empty", "selectedInterestFrequencyOption"); - dataValidationErrors.add(error); - } - - if (command.getInterestMethod() != null) { - InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod()); - ErrorResponse error = null; - switch (interestMethod) { - case DECLINING_BALANCE: - break; - case FLAT: - break; - default: - error = new ErrorResponse("validation.msg.product.interest.method.invalid", "selectedInterestMethodOption"); - break; - } - if (error != null) { - dataValidationErrors.add(error); - } - } else { - ErrorResponse error = new ErrorResponse("validation.msg.product.interest.method.cannot.be.empty", "selectedInterestMethodOption"); - dataValidationErrors.add(error); - } - - if (!dataValidationErrors.isEmpty()) { - throw new NewDataValidationException(dataValidationErrors, "Validation errors exist."); - } - } - - private ErrorResponse validateNumberExistsAndInRange(String errorCodePreFix, String fieldName, Integer value, int min, int max) { - ErrorResponse error = null; - if (value == null) { - error = new ErrorResponse(errorCodePreFix + ".cannot.be.empty", fieldName); - } else { - if (value < min || value > max) { - error = new ErrorResponse(errorCodePreFix + ".outside.allowed.range", fieldName); - } - } - return error; - } -} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/ImportPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/ImportPlatformServiceImpl.java index 09af3b15d..297e88806 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/ImportPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/ImportPlatformServiceImpl.java @@ -51,6 +51,7 @@ 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.organisation.domain.Office; import org.mifosng.platform.organisation.domain.OfficeRepository; import org.mifosng.platform.organisation.domain.Organisation; @@ -452,7 +453,6 @@ public class ImportPlatformServiceImpl implements ImportPlatformService { String currencyCode = matchingProduct.getCurrency().getCode(); int digitsAfterDecimal = matchingProduct.getCurrency().getDigitsAfterDecimal(); - boolean flexibleRepaymentSchedule = matchingProduct.isFlexibleRepaymentSchedule(); Integer repaymentEvery = matchingProduct.getRepayEvery(); Integer repaymentFrequency = matchingProduct.getRepaymentPeriodFrequencyType().getValue(); Integer amortizationMethod = matchingProduct.getAmortizationMethod().getValue(); @@ -469,8 +469,7 @@ public class ImportPlatformServiceImpl implements ImportPlatformService { interestRatePeriodFrequency, interestMethod, interestCalculationPeriodMethod, repaymentEvery, - repaymentFrequency, numberOfRepayments, amortizationMethod, flexibleRepaymentSchedule, - matchingProduct.isInterestRebateAllowed(), expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate); + repaymentFrequency, numberOfRepayments, amortizationMethod, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate); LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateLoanScheduleCommand); @@ -478,8 +477,7 @@ public class ImportPlatformServiceImpl implements ImportPlatformService { expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate, loanSchedule, currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRatePeriodFrequency, interestMethod, interestCalculationPeriodMethod, - repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount.getAmount(), - flexibleRepaymentSchedule, matchingProduct.isInterestRebateAllowed()); + repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount.getAmount()); parsedNewLoanDetails.setExternalId(loanExternalId); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/ReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/ReadPlatformServiceImpl.java index 74b5c587f..4ccbd13ef 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/ReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/ReadPlatformServiceImpl.java @@ -5,18 +5,14 @@ import static org.mifosng.platform.Specifications.loansThatMatch; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.Collection; import javax.sql.DataSource; import org.joda.time.LocalDate; -import org.mifosng.data.ClientData; import org.mifosng.data.CurrencyData; -import org.mifosng.data.LoanProductData; import org.mifosng.data.LoanRepaymentData; import org.mifosng.data.MoneyData; -import org.mifosng.data.NewLoanWorkflowStepOneData; import org.mifosng.data.OrganisationReadModel; import org.mifosng.platform.client.service.ClientReadPlatformService; import org.mifosng.platform.currency.domain.ApplicationCurrency; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformService.java index 2622abe63..5a36d79bf 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformService.java @@ -4,7 +4,6 @@ import org.mifosng.data.EntityIdentifier; import org.mifosng.data.command.AdjustLoanTransactionCommand; import org.mifosng.data.command.LoanStateTransitionCommand; import org.mifosng.data.command.LoanTransactionCommand; -import org.mifosng.data.command.SubmitLoanApplicationCommand; import org.mifosng.data.command.UndoLoanApprovalCommand; import org.mifosng.data.command.UndoLoanDisbursalCommand; import org.mifosng.data.command.UpdateUsernamePasswordCommand; @@ -14,12 +13,6 @@ public interface WritePlatformService { void updateUsernamePasswordOnFirstTimeLogin(UpdateUsernamePasswordCommand command); - @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_DELETE_LOAN_THAT_IS_SUBMITTED_AND_NOT_APPROVED')") - EntityIdentifier deleteLoan(Long loanId); - - @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_SUBMIT_NEW_LOAN_APPLICATION_ROLE', 'CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE')") - EntityIdentifier submitLoanApplication(SubmitLoanApplicationCommand command); - @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_LOAN_ROLE', 'CAN_APPROVE_LOAN_IN_THE_PAST_ROLE')") EntityIdentifier approveLoanApplication(LoanStateTransitionCommand command); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformServiceJpaRepositoryImpl.java index 5a9b7b09c..e12812ad5 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/WritePlatformServiceJpaRepositoryImpl.java @@ -3,7 +3,6 @@ package org.mifosng.platform; import static org.mifosng.platform.Specifications.loansThatMatch; import static org.mifosng.platform.Specifications.usersThatMatch; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -13,39 +12,27 @@ import org.joda.time.LocalDate; import org.mifosng.data.EntityIdentifier; import org.mifosng.data.ErrorResponse; 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; import org.mifosng.data.command.LoanStateTransitionCommand; import org.mifosng.data.command.LoanTransactionCommand; -import org.mifosng.data.command.SubmitLoanApplicationCommand; import org.mifosng.data.command.UndoLoanApprovalCommand; import org.mifosng.data.command.UndoLoanDisbursalCommand; import org.mifosng.data.command.UpdateUsernamePasswordCommand; -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; import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.exceptions.ApplicationDomainRuleException; -import org.mifosng.platform.exceptions.NoAuthorizationException; import org.mifosng.platform.exceptions.UnAuthenticatedUserException; import org.mifosng.platform.infrastructure.BasicPasswordEncodablePlatformUser; import org.mifosng.platform.infrastructure.PlatformPasswordEncoder; import org.mifosng.platform.infrastructure.PlatformUser; import org.mifosng.platform.infrastructure.UsernameAlreadyExistsException; -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.LoanBuilder; 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; @@ -53,6 +40,7 @@ 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.loan.service.CalculationPlatformService; import org.mifosng.platform.user.domain.AppUser; import org.mifosng.platform.user.domain.AppUserRepository; import org.mifosng.platform.user.domain.PlatformUserRepository; @@ -69,8 +57,6 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi private final PlatformUserRepository platformUserRepository; private final PlatformPasswordEncoder platformPasswordEncoder; - private final ClientRepository clientRepository; - private final LoanProductRepository loanProductRepository; private final LoanRepository loanRepository; private final LoanStatusRepository loanStatusRepository; private final CalculationPlatformService calculationPlatformService; @@ -81,9 +67,7 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi public WritePlatformServiceJpaRepositoryImpl( final PlatformUserRepository platformUserRepository, final PlatformPasswordEncoder platformPasswordEncoder, - final ClientRepository clientRepository, final NoteRepository noteRepository, - final LoanProductRepository loanProductRepository, final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository, final LoanStatusRepository loanStatusRepository, @@ -91,9 +75,7 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi ) { this.platformUserRepository = platformUserRepository; this.platformPasswordEncoder = platformPasswordEncoder; - this.clientRepository = clientRepository; this.noteRepository = noteRepository; - this.loanProductRepository = loanProductRepository; this.loanRepository = loanRepository; this.loanTransactionRepository = loanTransactionRepository; this.loanStatusRepository = loanStatusRepository; @@ -145,105 +127,6 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi return currentUser; } - @Transactional - @Override - public EntityIdentifier submitLoanApplication(final SubmitLoanApplicationCommand command) { - - AppUser currentUser = extractAuthenticatedUser(); - - SubmitLoanApplicationCommandValidator validator = new SubmitLoanApplicationCommandValidator(command); - validator.validate(); - - LocalDate submittedOn = command.getSubmittedOnDate(); - if (this.isBeforeToday(submittedOn) && currentUser.hasNotPermissionForAnyOf("CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE", "PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE")) { - throw new NoAuthorizationException("Cannot add backdated loan."); - } - - 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().multiply(BigDecimal.valueOf(1)); - 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()); - final boolean flexibleRepaymentSchedule = command.isFlexibleRepaymentSchedule(); - final boolean interestRebateAllowed = command.isInterestRebateAllowed(); - - LoanProductRelatedDetail loanRepaymentScheduleDetail = new LoanProductRelatedDetail(currency, - command.getPrincipal(), defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, - interestMethod, interestCalculationPeriodMethod, - repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, command.getInArrearsToleranceAmount(), - flexibleRepaymentSchedule, interestRebateAllowed); - - LoanSchedule loanSchedule = command.getLoanSchedule(); - List loanRepaymentSchedule = loanSchedule.getScheduledLoanInstallments(); - - Loan loan = new LoanBuilder() - .with(currentUser.getOrganisation()) - .with(loanProduct).with(client) - .with(loanRepaymentScheduleDetail) - .build(); - - 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())) { - Note note = Note.loanNote(currentUser.getOrganisation(), loan, command.getSubmittedOnNote()); - this.noteRepository.save(note); - } - - return new EntityIdentifier(loan.getId()); - } - private boolean isBeforeToday(final LocalDate date) { return date.isBefore(new LocalDate()); } @@ -253,34 +136,6 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses); } - @Transactional - @Override - public EntityIdentifier deleteLoan(Long loanId) { - - AppUser currentUser = extractAuthenticatedUser(); - - Loan loan = this.loanRepository.findOne(loansThatMatch( - currentUser.getOrganisation(), loanId)); - - if (loan == null) { - throw new ApplicationDomainRuleException(loanIdentifierDoesNotExistError(loanId), "No loan exists with id: " + loanId); - } - - if (loan.isNotSubmittedAndPendingApproval()) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.cannot.delete.loan.in.its.present.state", "eventDate"); - throw new ApplicationDomainRuleException(Arrays.asList(errorResponse), "Loan must be in pending approval state."); - } - - Long clientId = loan.getClient().getId(); - - List relatedNotes = this.noteRepository.findByLoanId(loan.getId()); - this.noteRepository.deleteInBatch(relatedNotes); - - this.loanRepository.delete(loanId); - - return new EntityIdentifier(clientId); - } - private List loanIdentifierDoesNotExistError(Long loanId) { ErrorResponse errorResponse = new ErrorResponse("error.msg.no.loan.with.identifier.exists", "id", loanId); return Arrays.asList(errorResponse); @@ -444,7 +299,6 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi Integer repaidEvery = loan.getLoanRepaymentScheduleDetail().getRepayEvery(); Integer selectedRepaymentFrequency = loan.getLoanRepaymentScheduleDetail().getRepaymentPeriodFrequencyType().getValue(); Integer selectedRepaymentSchedule = loan.getLoanRepaymentScheduleDetail().getAmortizationMethod().getValue(); - boolean flexibleRepaymentSchedule = loan.isFlexibleRepaymentSchedule(); // use annual percentage rate to re-calculate loan schedule for late disbursement Number interestRatePerPeriod = interestRatePerYear; @@ -452,12 +306,11 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi Integer interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod().getValue(); Integer interestCalculationInPeriod = loan.getLoanRepaymentScheduleDetail().getInterestCalculationPeriodMethod().getValue(); - boolean interestRebateAllowed = loan.getLoanRepaymentScheduleDetail().isInterestRebateAllowed(); CalculateLoanScheduleCommand calculateCommand = new CalculateLoanScheduleCommand(currencyCode, currencyDigits, principalAsDecimal, interestRatePerPeriod, interestRateFrequencyMethod, interestMethod, interestCalculationInPeriod, repaidEvery, selectedRepaymentFrequency, numberOfInstallments, - selectedRepaymentSchedule, flexibleRepaymentSchedule, interestRebateAllowed, actualDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate); + selectedRepaymentSchedule, actualDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate); LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateCommand); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java index 4bd8cc516..9a8739b83 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java @@ -1,7 +1,12 @@ package org.mifosng.platform.api; +import java.math.BigDecimal; +import java.util.Locale; + import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -9,10 +14,20 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosng.data.EntityIdentifier; import org.mifosng.data.LoanAccountData; +import org.mifosng.data.LoanSchedule; import org.mifosng.data.NewLoanWorkflowStepOneData; +import org.mifosng.data.command.CalculateLoanScheduleCommand; +import org.mifosng.data.command.SubmitLoanApplicationCommand; import org.mifosng.platform.ReadPlatformService; +import org.mifosng.platform.WritePlatformService; +import org.mifosng.platform.api.infrastructure.ApiDataConversionService; +import org.mifosng.platform.loan.service.CalculationPlatformService; import org.mifosng.platform.loan.service.LoanReadPlatformService; +import org.mifosng.platform.loan.service.LoanWritePlatformService; import org.mifosng.platform.user.domain.AppUser; import org.mifosng.platform.user.domain.AppUserRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -31,9 +46,21 @@ public class LoansApiResource { @Autowired private LoanReadPlatformService loanReadPlatformService; + @Autowired + private LoanWritePlatformService loanWritePlatformService; + @Autowired private ReadPlatformService readPlatformService; + @Autowired + private WritePlatformService writePlatformService; + + @Autowired + private CalculationPlatformService calculationPlatformService; + + @Autowired + private ApiDataConversionService apiDataConversionService; + @Autowired private AppUserRepository appUserRepository; @@ -51,6 +78,8 @@ public class LoansApiResource { @Produces({ MediaType.APPLICATION_JSON}) public Response retrieveDetailsForNewLoanApplicationStepOne(@QueryParam("clientId") final Long clientId, @QueryParam("productId") final Long productId) { + hardcodeUserIntoSecurityContext(); + NewLoanWorkflowStepOneData workflowData = this.loanReadPlatformService.retrieveClientAndProductDetails(clientId, productId); return Response.ok().entity(workflowData).build(); @@ -68,4 +97,58 @@ public class LoansApiResource { return Response.ok().entity(loanAccount).build(); } + + @POST + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) + @Produces({MediaType.APPLICATION_JSON}) + public Response calculateLoanSchedule(@QueryParam("command") final String commandParam, final SubmitLoanApplicationCommand command) { + + hardcodeUserIntoSecurityContext(); + + LocalDate expectedDisbursementDate = apiDataConversionService.convertFrom(command.getExpectedDisbursementDateFormatted(), "expectedDisbursementDateFormatted", command.getDateFormat()); + LocalDate repaymentsStartingFromDate = apiDataConversionService.convertFrom(command.getRepaymentsStartingFromDateFormatted(), "repaymentsStartingFromDateFormatted", command.getDateFormat()); + LocalDate interestCalculatedFromDate = apiDataConversionService.convertFrom(command.getInterestCalculatedFromDateFormatted(), "interestCalculatedFromDateFormatted", command.getDateFormat()); + LocalDate submittedOnDate = apiDataConversionService.convertFrom(command.getSubmittedOnDateFormatted(), "submittedOnDateFormatted", command.getDateFormat()); + command.setExpectedDisbursementDate(expectedDisbursementDate); + command.setRepaymentsStartingFromDate(repaymentsStartingFromDate); + command.setInterestCalculatedFromDate(interestCalculatedFromDate); + command.setSubmittedOnDate(submittedOnDate); + + // 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); + + CalculateLoanScheduleCommand calculateLoanScheduleCommand = command.toCalculateLoanScheduleCommand(); + LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateLoanScheduleCommand); + + // for now just auto generating the loan schedule and setting support for 'manual' loan schedule creation later. + command.setLoanSchedule(loanSchedule); + + if (StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase("calculateLoanSchedule")) { + + return Response.ok().entity(loanSchedule).build(); + } + + EntityIdentifier identifier = this.loanWritePlatformService.submitLoanApplication(command); + + return Response.ok().entity(identifier).build(); + } + + @DELETE + @Path("{loanId}") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) + @Produces({ MediaType.APPLICATION_JSON}) + public Response deleteLoanApplication(@PathParam("loanId") final Long loanId) { + + hardcodeUserIntoSecurityContext(); + + EntityIdentifier identifier = this.loanWritePlatformService.deleteLoan(loanId); + + return Response.ok().entity(identifier).build(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java index 4a7dce671..706600ea7 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java @@ -1,9 +1,14 @@ package org.mifosng.platform.api.infrastructure; +import java.math.BigDecimal; +import java.util.Locale; + import org.joda.time.LocalDate; public interface ApiDataConversionService { LocalDate convertFrom(String dateAsString, String parameterName, String dateFormat); + BigDecimal convertFrom(String principalFormatted, String parameterName, Locale clientApplicationLocale); + } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java index 3a04c45cc..5c968d6b4 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java @@ -1,5 +1,8 @@ package org.mifosng.platform.api.infrastructure; +import java.math.BigDecimal; +import java.text.NumberFormat; +import java.text.ParseException; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -33,4 +36,28 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { return eventLocalDate; } + + @Override + public BigDecimal convertFrom(String numericalValueFormatted, String parameterName, Locale clientApplicationLocale) { + + try { + BigDecimal number = null; + + if (StringUtils.isNotBlank(numericalValueFormatted)) { + String sourceWithoutSpaces = numericalValueFormatted.replaceAll(" ", ""); + NumberFormat format = NumberFormat.getNumberInstance(clientApplicationLocale); + Number parsedNumber = format.parse(sourceWithoutSpaces); + number = BigDecimal.valueOf(Double.valueOf(parsedNumber.doubleValue())); + } + + return number; + } catch (ParseException e) { + + List dataValidationErrors = new ArrayList(); + ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.number.format", "The parameter " + parameterName + " is invalid.", parameterName, numericalValueFormatted); + dataValidationErrors.add(error); + + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); + } + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformDomainRuleException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformDomainRuleException.java new file mode 100644 index 000000000..6cc5dc989 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformDomainRuleException.java @@ -0,0 +1,29 @@ +package org.mifosng.platform.exceptions; + +/** + * A {@link RuntimeException} thrown when valid api request end up violating some domain rule. + */ +public class PlatformDomainRuleException extends RuntimeException { + + private final String globalisationMessageCode; + private final String defaultUserMessage; + private final Object[] defaultUserMessageArgs; + + public PlatformDomainRuleException(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) { + this.globalisationMessageCode = globalisationMessageCode; + this.defaultUserMessage = defaultUserMessage; + this.defaultUserMessageArgs = defaultUserMessageArgs; + } + + public String getGlobalisationMessageCode() { + return globalisationMessageCode; + } + + public String getDefaultUserMessage() { + return defaultUserMessage; + } + + public Object[] getDefaultUserMessageArgs() { + return defaultUserMessageArgs; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DerivedLoanDataProcessor.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DerivedLoanDataProcessor.java index 6fa31ea43..397bc0eef 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DerivedLoanDataProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DerivedLoanDataProcessor.java @@ -70,6 +70,10 @@ public class DerivedLoanDataProcessor { Collections.sort(loanRepayments, new LoanRepaymentDataComparator()); + if (loanRepayments.isEmpty()) { + loanRepayments = null; + } + return new DerivedLoanData(processRepaymentScheduleData, summaryData, loanRepayments); } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java index a67b2af0f..b3bebc158 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java @@ -131,6 +131,12 @@ public class Loan extends AbstractAuditableCustom { @Transient private final InterestRebateCalculatorFactory interestRebateCalculatorFactory = new DailyEquivalentInterestRebateCalculatorFactory(); + public static Loan createNew(Organisation organisation, + LoanProduct loanProduct, Client client, + LoanProductRelatedDetail loanRepaymentScheduleDetail) { + return new Loan(organisation, client, loanProduct, loanRepaymentScheduleDetail, null); + } + public Loan() { this.organisation = null; this.client = null; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanBuilder.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanBuilder.java index d315845e5..84a478715 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanBuilder.java @@ -3,6 +3,10 @@ package org.mifosng.platform.loan.domain; import org.mifosng.platform.client.domain.Client; import org.mifosng.platform.organisation.domain.Organisation; +/** + * remove in favour of static factory method on {@link Loan}. No advantage to builder patter in this case? + */ +@Deprecated public class LoanBuilder { private Organisation organisation; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculateLoanScheduleCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculateLoanScheduleCommandValidator.java new file mode 100644 index 000000000..b5d06874e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculateLoanScheduleCommandValidator.java @@ -0,0 +1,218 @@ +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.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 { + + private final CalculateLoanScheduleCommand command; + + public CalculateLoanScheduleCommandValidator(CalculateLoanScheduleCommand command) { + this.command = command; + } + + public void validate() { + List dataValidationErrors = new ArrayList(); + + 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); + } + + 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); + } + + // 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); + } + + 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())) { + 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()); + dataValidationErrors.add(error); + } + } + + if (command.getRepaymentsStartingFromDate() != null && command.getInterestCalculatedFromDate() == 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()); + dataValidationErrors.add(error); + } else if (command.getRepaymentsStartingFromDate() == null && command.getInterestCalculatedFromDate() != 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()); + 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; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/CalculationPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformService.java similarity index 89% rename from mifosng-provider/src/main/java/org/mifosng/platform/CalculationPlatformService.java rename to mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformService.java index 9fce71800..586937640 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/CalculationPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformService.java @@ -1,4 +1,4 @@ -package org.mifosng.platform; +package org.mifosng.platform.loan.service; import org.joda.time.LocalDate; import org.mifosng.data.LoanPayoffReadModel; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/CalculationPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformServiceImpl.java similarity index 98% rename from mifosng-provider/src/main/java/org/mifosng/platform/CalculationPlatformServiceImpl.java rename to mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformServiceImpl.java index b88fcf198..527b1351e 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/CalculationPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformServiceImpl.java @@ -1,4 +1,4 @@ -package org.mifosng.platform; +package org.mifosng.platform.loan.service; import java.math.BigDecimal; @@ -88,7 +88,6 @@ public class CalculationPlatformServiceImpl implements @Override public LoanPayoffReadModel calculatePayoffOn(final Long loanId, final LocalDate payoffDate) { - // TODO - use jdbc approach to calculating payoff if possible. final Loan loan = this.loanRepository.findOne(loanId); final LoanPayoffSummary payoffSummary = loan.getPayoffSummaryOn(payoffDate); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java new file mode 100644 index 000000000..79bde4024 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java @@ -0,0 +1,14 @@ +package org.mifosng.platform.loan.service; + +import org.mifosng.data.EntityIdentifier; +import org.mifosng.data.command.SubmitLoanApplicationCommand; +import org.springframework.security.access.prepost.PreAuthorize; + +public interface LoanWritePlatformService { + + @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_SUBMIT_NEW_LOAN_APPLICATION_ROLE', 'CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE')") + EntityIdentifier submitLoanApplication(SubmitLoanApplicationCommand command); + + @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_DELETE_LOAN_THAT_IS_SUBMITTED_AND_NOT_APPROVED')") + EntityIdentifier deleteLoan(Long loanId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..bea086818 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,171 @@ +package org.mifosng.platform.loan.service; + +import static org.mifosng.platform.Specifications.loansThatMatch; + +import java.math.BigDecimal; +import java.util.List; + +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.SubmitLoanApplicationCommand; +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; +import org.mifosng.platform.exceptions.NoAuthorizationException; +import org.mifosng.platform.exceptions.PlatformDomainRuleException; +import org.mifosng.platform.exceptions.PlatformResourceNotFoundException; +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.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; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService { + + 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 AprCalculator aprCalculator = new AprCalculator(); + + @Autowired + public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, + final LoanRepository loanRepository, final LoanStatusRepository loanStatusRepository, final LoanProductRepository loanProductRepository, + final ClientRepository clientRepository, final NoteRepository noteRepository) { + this.context = context; + this.loanRepository = loanRepository; + this.loanProductRepository = loanProductRepository; + this.clientRepository = clientRepository; + this.loanStatusRepository = loanStatusRepository; + this.noteRepository = noteRepository; + } + + private boolean isBeforeToday(final LocalDate date) { + return date.isBefore(new LocalDate()); + } + + private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { + List allowedLoanStatuses = this.loanStatusRepository.findAll(); + return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses); + } + + @Transactional + @Override + public EntityIdentifier submitLoanApplication(final SubmitLoanApplicationCommand command) { + + AppUser currentUser = context.authenticatedUser(); + + SubmitLoanApplicationCommandValidator validator = new SubmitLoanApplicationCommandValidator(command); + validator.validate(); + + LocalDate submittedOn = command.getSubmittedOnDate(); + 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()); + + 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()); + final boolean flexibleRepaymentSchedule = command.isFlexibleRepaymentSchedule(); + final boolean interestRebateAllowed = command.isInterestRebateAllowed(); + + LoanProductRelatedDetail loanRepaymentScheduleDetail = new LoanProductRelatedDetail(currency, + command.getPrincipal(), defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, + interestMethod, interestCalculationPeriodMethod, + repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, command.getInArrearsToleranceAmount(), + flexibleRepaymentSchedule, interestRebateAllowed); + + LoanSchedule loanSchedule = command.getLoanSchedule(); + List loanRepaymentSchedule = loanSchedule.getScheduledLoanInstallments(); + + Loan loan = Loan.createNew(currentUser.getOrganisation(), loanProduct, client, loanRepaymentScheduleDetail); + + 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())) { + Note note = Note.loanNote(currentUser.getOrganisation(), loan, command.getSubmittedOnNote()); + this.noteRepository.save(note); + } + + return new EntityIdentifier(loan.getId()); + } + + @Transactional + @Override + public EntityIdentifier deleteLoan(Long loanId) { + + AppUser currentUser = context.authenticatedUser(); + + Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), loanId)); + if (loan == null) { + throw new PlatformResourceNotFoundException("error.msg.loan.id.invalid", "Loan with identifier {0} does not exist", loanId); + } + + if (loan.isNotSubmittedAndPendingApproval()) { + throw new PlatformDomainRuleException("error.msg.cannot.delete.loan.in.its.present.state", "Loan with identifier {0} cannot be deleted in its current state.", loanId); + } + + Long clientId = loan.getClient().getId(); + + List relatedNotes = this.noteRepository.findByLoanId(loan.getId()); + this.noteRepository.deleteInBatch(relatedNotes); + + this.loanRepository.delete(loanId); + + return new EntityIdentifier(clientId); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/SubmitLoanApplicationCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/SubmitLoanApplicationCommandValidator.java similarity index 56% rename from mifosng-provider/src/main/java/org/mifosng/platform/SubmitLoanApplicationCommandValidator.java rename to mifosng-provider/src/main/java/org/mifosng/platform/loan/service/SubmitLoanApplicationCommandValidator.java index cac65acb5..7162519c7 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/SubmitLoanApplicationCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/SubmitLoanApplicationCommandValidator.java @@ -1,12 +1,12 @@ -package org.mifosng.platform; +package org.mifosng.platform.loan.service; import java.util.ArrayList; import java.util.List; -import org.mifosng.data.ErrorResponse; +import org.mifosng.data.ApiParameterError; import org.mifosng.data.command.CalculateLoanScheduleCommand; import org.mifosng.data.command.SubmitLoanApplicationCommand; -import org.mifosng.platform.exceptions.NewDataValidationException; +import org.mifosng.platform.exceptions.PlatformApiDataValidationException; public class SubmitLoanApplicationCommandValidator { @@ -17,19 +17,23 @@ public class SubmitLoanApplicationCommandValidator { } public void validate() { - List dataValidationErrors = new ArrayList(); + List dataValidationErrors = new ArrayList(); if (command.getLoanSchedule() == null) { - ErrorResponse error = new ErrorResponse("validation.msg.submit.loan.loan.schedule.cannot.be.blank", "loanSchedule"); + ApiParameterError error = ApiParameterError.parameterError("validation.msg.submit.loan.loan.schedule.cannot.be.blank", + "The parameter loanSchedule cannot be empty.", "loanSchedule"); dataValidationErrors.add(error); } if (command.getSubmittedOnDate() == null) { - ErrorResponse error = new ErrorResponse("validation.msg.loan.submitted.on.date.cannot.be.blank", "submittedOnDate"); + ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.submitted.on.date.cannot.be.blank", + "The parameter submittedOnDateFormatted cannot be empty.", "submittedOnDateFormatted"); dataValidationErrors.add(error); } else { if (command.getSubmittedOnDate().isAfter(command.getExpectedDisbursementDate())) { - ErrorResponse error = new ErrorResponse("validation.msg.loan.submitted.on.date.cannot.be.after.expectedDisbursementDate", "submittedOnDate"); + 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()); dataValidationErrors.add(error); } } @@ -45,8 +49,6 @@ public class SubmitLoanApplicationCommandValidator { command.getRepaymentFrequency(), command.getNumberOfRepayments(), command.getAmortizationMethod(), - command.isFlexibleRepaymentSchedule(), - command.isInterestRebateAllowed(), command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate(), command.getInterestCalculatedFromDate()); @@ -54,12 +56,12 @@ public class SubmitLoanApplicationCommandValidator { CalculateLoanScheduleCommandValidator validator = new CalculateLoanScheduleCommandValidator( calculateLoanScheduleCommand); validator.validate(); - } catch (NewDataValidationException e) { - dataValidationErrors.addAll(e.getValidationErrors()); + } catch (PlatformApiDataValidationException e) { + dataValidationErrors.addAll(e.getErrors()); } if (!dataValidationErrors.isEmpty()) { - throw new NewDataValidationException(dataValidationErrors, "Data validation errors exist."); + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java index e79462e15..91cf5b4b2 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java @@ -11,6 +11,8 @@ import org.mifosng.platform.loan.domain.AmortizationMethod; import org.mifosng.platform.loan.domain.InterestMethod; import org.mifosng.platform.loan.domain.PeriodFrequencyType; +// FIXME - There is a lot of common validation across parameters for LoanProduct create/update, loan schedule calculation and submit new loan application flows. +// - reduce duplicator across this validators. public class LoanProductCommandValidator { public void validateForUpdate(LoanProductCommand command) { @@ -67,7 +69,6 @@ public class LoanProductCommandValidator { ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.principal.amount.must.be.greater.than.zero", "The parameter amount must be greater than zero.", "amount"); dataValidationErrors.add(error); } - // TODO - check number of digits before decimal and after decimal } else { ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.principal.amount.cannot.be.empty", "The parameter amount cannot be blank.", "amount"); dataValidationErrors.add(error); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/rest/LoanOpenResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/rest/LoanOpenResource.java index ff50a1e2b..faeba3a44 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/rest/LoanOpenResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/rest/LoanOpenResource.java @@ -17,13 +17,19 @@ import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; import org.mifosng.data.LoanPayoffReadModel; import org.mifosng.data.MoneyData; -import org.mifosng.platform.CalculationPlatformService; +import org.mifosng.platform.loan.service.CalculationPlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import com.sun.jersey.api.json.JSONWithPadding; +/** + * Payoff functionality was something CreoCore was looking for e.g. if paid in full on date x what should be paid? full minus any rebate or incentive provided. + * + * NOTE: JSONP functionality not needed now due to use of CORS + */ +@Deprecated @Path("/open/loan/{loanId}") @Component @Scope("singleton") diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/rest/LoanResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/rest/LoanResource.java index a4811812a..9a135142a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/rest/LoanResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/rest/LoanResource.java @@ -5,7 +5,6 @@ import java.util.Arrays; import java.util.List; import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -20,15 +19,11 @@ import org.mifosng.data.EntityIdentifier; import org.mifosng.data.ErrorResponse; import org.mifosng.data.ErrorResponseList; import org.mifosng.data.LoanRepaymentData; -import org.mifosng.data.LoanSchedule; import org.mifosng.data.command.AdjustLoanTransactionCommand; -import org.mifosng.data.command.CalculateLoanScheduleCommand; import org.mifosng.data.command.LoanStateTransitionCommand; import org.mifosng.data.command.LoanTransactionCommand; -import org.mifosng.data.command.SubmitLoanApplicationCommand; import org.mifosng.data.command.UndoLoanApprovalCommand; import org.mifosng.data.command.UndoLoanDisbursalCommand; -import org.mifosng.platform.CalculationPlatformService; import org.mifosng.platform.ReadPlatformService; import org.mifosng.platform.ReadPlatformServiceImpl; import org.mifosng.platform.WritePlatformService; @@ -49,92 +44,12 @@ public class LoanResource { private final static Logger logger = LoggerFactory.getLogger(ReadPlatformServiceImpl.class); - @Autowired - private CalculationPlatformService calculationPlatformService; - @Autowired private ReadPlatformService readPlatformService; @Autowired private WritePlatformService writePlatformService; - @POST - @Path("calculate") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - public Response calculateLoanSchedule(final CalculateLoanScheduleCommand command) { - - try { - LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command); - - return Response.ok().entity(loanSchedule).build(); - } catch (UnAuthenticatedUserException e) { - throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).build()); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); - } catch (ApplicationDomainRuleException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); - } catch (NewDataValidationException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build()); - } - } - - @POST - @Path("new") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response submitLoanApplication(final SubmitLoanApplicationCommand command) { - - try { - EntityIdentifier identifier = this.writePlatformService.submitLoanApplication(command); - - return Response.ok().entity(identifier).build(); - } catch (UnAuthenticatedUserException e) { - throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).build()); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); - } catch (ApplicationDomainRuleException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); - } catch (NewDataValidationException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build()); - } - } - - @DELETE - @Path("{loanId}") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response deleteLoanApplication(@PathParam("loanId") final Long loanId) { - - try { - EntityIdentifier identifier = this.writePlatformService.deleteLoan(loanId); - - return Response.ok().entity(identifier).build(); - } catch (UnAuthenticatedUserException e) { - throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).build()); - } catch (AccessDeniedException e) { - ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id"); - ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse)); - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build()); - } catch (ApplicationDomainRuleException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build()); - } catch (NewDataValidationException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build()); - } catch (Exception e) { - logger.error(e.getMessage(), e); - - List allErrors = new ArrayList(); - ErrorResponse err = new ErrorResponse("unknown.error", "error", e.getMessage()); - allErrors.add(err); - - throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(allErrors)).build()); - } - } - @POST @Path("approve") @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })