From 013276d9e4a6e2d609f146ad73ce573e32d63b6f Mon Sep 17 00:00:00 2001
From: Keith Woodlock
Date: Tue, 18 Dec 2012 20:41:12 +0000
Subject: [PATCH] MIFOSX-97: support audit for loan application creation, loan
application modification, approval, undo approval, disbursal and undo of
disbursal.
---
.../commands/domain/CommandWrapper.java | 28 +
.../SynchronousCommandProcessingService.java | 23 +
...GsonPortfolioApiJsonSerializerService.java | 18 +-
.../infrastructure/core/api/JsonCommand.java | 4 +
.../infrastructure/core/api/JsonQuery.java | 261 ++++
.../PortfolioApiDataConversionService.java | 6 -
...PortfolioApiDataConversionServiceImpl.java | 126 +-
.../PortfolioApiJsonSerializerService.java | 4 -
.../core/serialization/FromJsonHelper.java | 24 +-
.../loanaccount/api/LoansApiResource.java | 117 +-
.../command/LoanApplicationCommand.java | 70 +-
.../LoanApplicationCommandValidator.java | 78 --
.../command/LoanChargeCommand.java | 48 +-
.../command/LoanStateTransitionCommand.java | 82 +-
.../LoanStateTransitionCommandValidator.java | 32 -
.../portfolio/loanaccount/domain/Loan.java | 532 ++++++--
.../loanaccount/domain/LoanCharge.java | 82 ++
.../loanaccount/domain/LoanStatus.java | 168 +--
...rawsFromLoanApplicationCommandHandler.java | 27 +
.../ApproveLoanApplicationCommandHandler.java | 27 +
.../CreateLoanApplicationCommandHandler.java | 27 +
.../DeleteLoanApplicationCommandHandler.java | 27 +
.../handler/DisburseLoanCommandHandler.java | 27 +
.../RejectLoanApplicationCommandHandler.java | 27 +
...ApprovalLoanApplicationCommandHandler.java | 27 +
.../UndoDisbursalLoanCommandHandler.java | 27 +
.../UpdateLoanApplicationCommandHandler.java | 27 +
.../command/CalculateLoanScheduleCommand.java | 125 --
...CalculateLoanScheduleCommandValidator.java | 101 --
.../loanschedule/data/LoanScheduleData.java | 551 ++++----
.../loanschedule/domain/AprCalculator.java | 40 +-
...ingBalanceMethodLoanScheduleGenerator.java | 85 +-
.../DefaultLoanScheduleGeneratorFactory.java | 37 +-
.../FlatMethodLoanScheduleGenerator.java | 223 ++-
.../loanschedule/domain/LoanSchedule.java | 105 ++
.../domain/LoanScheduleGenerator.java | 8 +-
.../query/CalculateLoanScheduleQuery.java | 207 +++
.../service/CalculationPlatformService.java | 9 -
.../CalculationPlatformServiceImpl.java | 71 -
.../service/LoanScheduleAssembler.java | 108 ++
...oanScheduleCalculationPlatformService.java | 9 +
...cheduleCalculationPlatformServiceImpl.java | 38 +
...nScheduleQueryFromApiJsonDeserializer.java | 114 ++
...icationCommandFromApiJsonDeserializer.java | 142 ++
...nsitionCommandFromApiJsonDeserializer.java | 61 +
.../loanaccount/service/LoanAssembler.java | 293 ++--
.../service/LoanChargeAssembler.java | 138 +-
.../LoanProductRelatedDetailAssembler.java | 83 +-
.../service/LoanWritePlatformService.java | 81 +-
...WritePlatformServiceJpaRepositoryImpl.java | 1201 ++++++++---------
.../loanproduct/domain/LoanProduct.java | 4 +-
.../domain/LoanProductRelatedDetail.java | 43 +-
...WritePlatformServiceJpaRepositoryImpl.java | 2 +-
53 files changed, 3469 insertions(+), 2356 deletions(-)
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonQuery.java
delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanApplicationCommandValidator.java
delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanStateTransitionCommandValidator.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/ApplicantWithdrawsFromLoanApplicationCommandHandler.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/ApproveLoanApplicationCommandHandler.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/CreateLoanApplicationCommandHandler.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/DeleteLoanApplicationCommandHandler.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/DisburseLoanCommandHandler.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/RejectLoanApplicationCommandHandler.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/UndoApprovalLoanApplicationCommandHandler.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/UndoDisbursalLoanCommandHandler.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/UpdateLoanApplicationCommandHandler.java
delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/command/CalculateLoanScheduleCommand.java
delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/command/CalculateLoanScheduleCommandValidator.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/query/CalculateLoanScheduleQuery.java
delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/CalculationPlatformService.java
delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/CalculationPlatformServiceImpl.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformService.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonDeserializer.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonDeserializer.java
create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanStateTransitionCommandFromApiJsonDeserializer.java
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
index bc3076a9f..7a5315acf 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
@@ -163,4 +163,32 @@ public class CommandWrapper {
public boolean isIdentifiersSubResource() {
return "IDENTIFIERS".equalsIgnoreCase(this.subResource);
}
+
+ public boolean isLoanResource() {
+ return this.resource.equalsIgnoreCase("LOANS");
+ }
+
+ public boolean isApproveLoanApplication() {
+ return this.actionName.equalsIgnoreCase("APPROVE") && this.entityName.equalsIgnoreCase("LOAN");
+ }
+
+ public boolean isUndoApprovalOfLoanApplication() {
+ return this.actionName.equalsIgnoreCase("APPROVALUNDO") && this.entityName.equalsIgnoreCase("LOAN");
+ }
+
+ public boolean isApplicantWithdrawalFromLoanApplication() {
+ return this.actionName.equalsIgnoreCase("WITHDRAW") && this.entityName.equalsIgnoreCase("LOAN");
+ }
+
+ public boolean isRejectionOfLoanApplication() {
+ return this.actionName.equalsIgnoreCase("REJECT") && this.entityName.equalsIgnoreCase("LOAN");
+ }
+
+ public boolean isDisbursementOfLoan() {
+ return this.actionName.equalsIgnoreCase("DISBURSE") && this.entityName.equalsIgnoreCase("LOAN");
+ }
+
+ public boolean isUndoDisbursementOfLoan() {
+ return this.actionName.equalsIgnoreCase("DISBURSALUNDO") && this.entityName.equalsIgnoreCase("LOAN");
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
index 82f977fdb..d662f620b 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
@@ -210,6 +210,29 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
+ } else if (wrapper.isLoanResource()) {
+
+ if (wrapper.isApproveLoanApplication()) {
+ handler = applicationContext.getBean("approveLoanApplicationCommandHandler", NewCommandSourceHandler.class);
+ } else if (wrapper.isUndoApprovalOfLoanApplication()) {
+ handler = applicationContext.getBean("undoApprovalLoanApplicationCommandHandler", NewCommandSourceHandler.class);
+ } else if (wrapper.isApplicantWithdrawalFromLoanApplication()) {
+ handler = applicationContext.getBean("applicantWithdrawsFromLoanApplicationCommandHandler", NewCommandSourceHandler.class);
+ } else if (wrapper.isRejectionOfLoanApplication()) {
+ handler = applicationContext.getBean("rejectLoanApplicationCommandHandler", NewCommandSourceHandler.class);
+ } else if (wrapper.isDisbursementOfLoan()) {
+ handler = applicationContext.getBean("disburseLoanCommandHandler", NewCommandSourceHandler.class);
+ } else if (wrapper.isUndoDisbursementOfLoan()) {
+ handler = applicationContext.getBean("undoDisbursalLoanCommandHandler", NewCommandSourceHandler.class);
+ } else if (wrapper.isCreate()) {
+ handler = applicationContext.getBean("createLoanApplicationCommandHandler", NewCommandSourceHandler.class);
+ } else if (wrapper.isUpdate()) {
+ handler = applicationContext.getBean("updateLoanApplicationCommandHandler", NewCommandSourceHandler.class);
+ } else if (wrapper.isDelete()) {
+ handler = applicationContext.getBean("deleteLoanApplicationCommandHandler", NewCommandSourceHandler.class);
+ } else {
+ throw new UnsupportedCommandException(wrapper.commandName());
+ }
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java
index da9554ceb..365f8cc19 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java
@@ -18,7 +18,6 @@ import org.mifosplatform.portfolio.group.data.GroupData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
import org.mifosplatform.portfolio.loanaccount.gaurantor.data.GuarantorData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountData;
import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData;
import org.mifosplatform.portfolio.savingsaccountproduct.data.SavingProductData;
@@ -59,8 +58,8 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi
"interestCompoundedEveryPeriodType", "renewalAllowed", "preClosureAllowed", "preClosureInterestRate", "withdrawnonDate",
"rejectedonDate", "closedonDate", "transactions", "permissions", "isInterestWithdrawable", "interestPaid",
"interestCompoundingAllowed", "availableInterestForWithdrawal", "availableWithdrawalAmount", "todaysDate",
- "isLockinPeriodAllowed", "lockinPeriod", "lockinPeriodType", "printFDdetailsLocation","availableInterest", "interestPostedAmount",
- "lastInterestPostedDate", "nextInterestPostedDate","fatherName","address","imageKey"));
+ "isLockinPeriodAllowed", "lockinPeriod", "lockinPeriodType", "printFDdetailsLocation", "availableInterest",
+ "interestPostedAmount", "lastInterestPostedDate", "nextInterestPostedDate", "fatherName", "address", "imageKey"));
private static final Set DOCUMENT_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "parentEntityType",
"parentEntityId", "name", "fileName", "type", "size", "description"));
@@ -79,9 +78,6 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi
private static final Set GROUP_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "officeId", "name", "externalId",
"clientMembers", "allowedClients", "allowedOffices"));
- private static final Set LOAN_SCHEDULE_DATA_PARAMETERS = new HashSet(Arrays.asList("periods",
- "cumulativePrincipalDisbursed"));
-
private static final Set LOAN_TRANSACTION_NEW_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "type", "date",
"currency", "amount"));
@@ -103,7 +99,7 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi
private static final Set GUARANTOR_DATA_PARAMETERS = new HashSet(Arrays.asList("externalGuarantor", "existingClientId",
"firstname", "lastname", "addressLine1", "addressLine2", "city", "state", "zip", "country", "mobileNumber", "housePhoneNumber",
"comment", "dob"));
-
+
private static final Set SAVING_SCHEDULE_DATA_PARAMETERS = new HashSet(Arrays.asList("periods", "cumulativeDepositDue"));
private final GoogleGsonSerializerHelper helper;
@@ -210,14 +206,6 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi
return helper.serializedJsonFrom(gsonDeserializer, group);
}
- @Override
- public String serializeLoanScheduleDataToJson(final boolean prettyPrint, final Set responseParameters,
- final LoanScheduleData loanSchedule) {
- final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(LOAN_SCHEDULE_DATA_PARAMETERS,
- prettyPrint, responseParameters);
- return helper.serializedJsonFrom(gsonDeserializer, loanSchedule);
- }
-
@Override
public String serializeLoanTransactionDataToJson(final boolean prettyPrint, final Set responseParameters,
final LoanTransactionData transaction) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java
index e9c2b8bd3..e68715ac1 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java
@@ -55,6 +55,10 @@ public final class JsonCommand {
return this.jsonCommand;
}
+ public JsonElement parsedJson() {
+ return this.parsedCommand;
+ }
+
public Long commandId() {
return this.commandId;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonQuery.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonQuery.java
new file mode 100644
index 000000000..bdfedd1a1
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonQuery.java
@@ -0,0 +1,261 @@
+package org.mifosplatform.infrastructure.core.api;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.StringUtils;
+import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
+import org.mifosplatform.infrastructure.security.domain.BasicPasswordEncodablePlatformUser;
+import org.mifosplatform.infrastructure.security.domain.PlatformUser;
+import org.mifosplatform.infrastructure.security.service.PlatformPasswordEncoder;
+
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Immutable representation of a query.
+ *
+ * Wraps the provided JSON with convenience functions for extracting parameter
+ * values.
+ */
+public final class JsonQuery {
+
+ private final String jsonQuery;
+ private final JsonElement parsedQuery;
+ private final FromJsonHelper fromApiJsonHelper;
+
+ public static JsonQuery from(final String jsonCommand, final JsonElement parsedQuery, final FromJsonHelper fromApiJsonHelper) {
+ return new JsonQuery(jsonCommand, parsedQuery, fromApiJsonHelper);
+ }
+
+ public JsonQuery(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper) {
+ this.jsonQuery = jsonCommand;
+ this.parsedQuery = parsedCommand;
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ }
+
+ public String json() {
+ return this.jsonQuery;
+ }
+
+ public JsonElement parsedJson() {
+ return this.parsedQuery;
+ }
+
+ private boolean differenceExists(final LocalDate baseValue, final LocalDate workingCopyValue) {
+ boolean differenceExists = false;
+
+ if (baseValue != null) {
+ differenceExists = !baseValue.equals(workingCopyValue);
+ } else {
+ differenceExists = workingCopyValue != null;
+ }
+
+ return differenceExists;
+ }
+
+ private boolean differenceExists(final String baseValue, final String workingCopyValue) {
+ boolean differenceExists = false;
+
+ if (StringUtils.isNotBlank(baseValue)) {
+ differenceExists = !baseValue.equals(workingCopyValue);
+ } else {
+ differenceExists = StringUtils.isNotBlank(workingCopyValue);
+ }
+
+ return differenceExists;
+ }
+
+ private boolean differenceExists(final String[] baseValue, final String[] workingCopyValue) {
+ Arrays.sort(baseValue);
+ Arrays.sort(workingCopyValue);
+ return !Arrays.equals(baseValue, workingCopyValue);
+ }
+
+ private boolean differenceExists(final Number baseValue, final Number workingCopyValue) {
+ boolean differenceExists = false;
+
+ if (baseValue != null) {
+ differenceExists = !baseValue.equals(workingCopyValue);
+ } else {
+ differenceExists = workingCopyValue != null;
+ }
+
+ return differenceExists;
+ }
+
+ private boolean differenceExists(final BigDecimal baseValue, final BigDecimal workingCopyValue) {
+ boolean differenceExists = false;
+
+ if (baseValue != null) {
+ differenceExists = baseValue.compareTo(workingCopyValue) != 0;
+ } else {
+ differenceExists = workingCopyValue != null;
+ }
+
+ return differenceExists;
+ }
+
+ private boolean differenceExists(final Boolean baseValue, final Boolean workingCopyValue) {
+ boolean differenceExists = false;
+
+ if (baseValue != null) {
+ differenceExists = !baseValue.equals(workingCopyValue);
+ } else {
+ differenceExists = workingCopyValue != null;
+ }
+
+ return differenceExists;
+ }
+
+ private boolean parameterExists(final String parameterName) {
+ return this.fromApiJsonHelper.parameterExists(parameterName, parsedQuery);
+ }
+
+ public boolean hasParameter(final String parameterName) {
+ return parameterExists(parameterName);
+ }
+
+ public String dateFormat() {
+ return stringValueOfParameterNamed("dateFormat");
+ }
+
+ public String locale() {
+ return stringValueOfParameterNamed("locale");
+ }
+
+ public Map mapValueOfParameterNamed(final String parameterName) {
+ final Type typeOfMap = new TypeToken
*
*
- * When amortized using equal principal payments, the principal component of each installment is fixed and interest due
- * is calculated from the outstanding principal balance resulting in a different total payment due for each installment.
+ * When amortized using equal principal payments, the principal
+ * component of each installment is fixed and interest due is
+ * calculated from the outstanding principal balance resulting in a
+ * different total payment due for each installment.
*
*
*
- * When amortized using equal installments, the total payment due for each installment is fixed and
- * is calculated using the excel like pmt function. The interest due is calculated from the outstanding principal balance
- * which results in a principal component that is total payment due minus interest due.
- *
+ * When amortized using equal installments, the total payment due
+ * for each installment is fixed and is calculated using the excel like
+ * pmt function. The interest due is calculated from the
+ * outstanding principal balance which results in a principal
+ * component that is total payment due minus interest due.
+ *
*/
public class DecliningBalanceMethodLoanScheduleGenerator implements LoanScheduleGenerator {
- private final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
- private final PeriodicInterestRateCalculator periodicInterestRateCalculator = new PeriodicInterestRateCalculator();
- private final AmortizationLoanScheduleGeneratorFactory amortizationLoanScheduleGeneratorFactory = new AmortizationLoanScheduleGeneratorFactory();
-
- @Override
- public LoanScheduleData generate(
- final ApplicationCurrency applicationCurrency,
- final LoanProductRelatedDetail loanScheduleInfo,
- final Integer loanTermFrequency,
- final PeriodFrequencyType loanTermFrequencyType,
- final LocalDate disbursementDate,
- final LocalDate firstRepaymentDate,
- final LocalDate interestCalculatedFrom,
- final Set loanCharges) {
-
- // 1. generate valid set of 'due dates' based on some of the 'loan attributes'
- final List scheduledDates = this.scheduledDateGenerator.generate(loanScheduleInfo, disbursementDate, firstRepaymentDate);
-
- final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate = this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(loanScheduleInfo, scheduledDates);
-
- // 2. determine the 'periodic' interest rate based on the 'repayment periods' so we can use
- final BigDecimal periodInterestRateForRepaymentPeriod = this.periodicInterestRateCalculator.calculateFrom(loanScheduleInfo);
-
- // Determine with 'amortisation' approach to use
- final AmortizationLoanScheduleGenerator generator = this.amortizationLoanScheduleGeneratorFactory.createGenerator(loanScheduleInfo.getAmortizationMethod());
-
- return generator.generate(applicationCurrency,
- loanScheduleInfo,
- disbursementDate,
- interestCalculatedFrom,
- periodInterestRateForRepaymentPeriod,
- idealDisbursementDateBasedOnFirstRepaymentDate,
- scheduledDates, loanCharges);
- }
+ private final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
+ private final PeriodicInterestRateCalculator periodicInterestRateCalculator = new PeriodicInterestRateCalculator();
+ private final AmortizationLoanScheduleGeneratorFactory amortizationLoanScheduleGeneratorFactory = new AmortizationLoanScheduleGeneratorFactory();
+
+ @Override
+ public LoanScheduleData generate(final ApplicationCurrency applicationCurrency, final LoanProductRelatedDetail loanScheduleInfo,
+ final Integer loanTermFrequency, final PeriodFrequencyType loanTermFrequencyType, final LocalDate disbursementDate,
+ final LocalDate firstRepaymentDate, final LocalDate interestCalculatedFrom, final Set loanCharges) {
+
+ // 1. generate valid set of 'due dates' based on some of the 'loan
+ // attributes'
+ final List scheduledDates = this.scheduledDateGenerator.generate(loanScheduleInfo, disbursementDate, firstRepaymentDate);
+
+ final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate = this.scheduledDateGenerator
+ .idealDisbursementDateBasedOnFirstRepaymentDate(loanScheduleInfo, scheduledDates);
+
+ // 2. determine the 'periodic' interest rate based on the 'repayment
+ // periods' so we can use
+ final BigDecimal periodInterestRateForRepaymentPeriod = this.periodicInterestRateCalculator.calculateFrom(loanScheduleInfo);
+
+ // Determine with 'amortisation' approach to use
+ final AmortizationLoanScheduleGenerator generator = this.amortizationLoanScheduleGeneratorFactory.createGenerator(loanScheduleInfo
+ .getAmortizationMethod());
+
+ return generator.generate(applicationCurrency, loanScheduleInfo, disbursementDate, interestCalculatedFrom,
+ periodInterestRateForRepaymentPeriod, idealDisbursementDateBasedOnFirstRepaymentDate, scheduledDates, loanCharges);
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultLoanScheduleGeneratorFactory.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultLoanScheduleGeneratorFactory.java
index f0692608d..9604efb82 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultLoanScheduleGeneratorFactory.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultLoanScheduleGeneratorFactory.java
@@ -1,26 +1,27 @@
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
+import org.springframework.stereotype.Component;
-public class DefaultLoanScheduleGeneratorFactory implements
- LoanScheduleGeneratorFactory {
+@Component
+public class DefaultLoanScheduleGeneratorFactory implements LoanScheduleGeneratorFactory {
- @Override
- public LoanScheduleGenerator create(final InterestMethod interestMethod) {
+ @Override
+ public LoanScheduleGenerator create(final InterestMethod interestMethod) {
- LoanScheduleGenerator loanScheduleGenerator = null;
+ LoanScheduleGenerator loanScheduleGenerator = null;
- switch (interestMethod) {
- case FLAT:
- loanScheduleGenerator = new FlatMethodLoanScheduleGenerator();
- break;
- case DECLINING_BALANCE:
- loanScheduleGenerator = new DecliningBalanceMethodLoanScheduleGenerator();
- break;
- case INVALID:
- break;
- }
-
- return loanScheduleGenerator;
- }
+ switch (interestMethod) {
+ case FLAT:
+ loanScheduleGenerator = new FlatMethodLoanScheduleGenerator();
+ break;
+ case DECLINING_BALANCE:
+ loanScheduleGenerator = new DecliningBalanceMethodLoanScheduleGenerator();
+ break;
+ case INVALID:
+ break;
+ }
+
+ return loanScheduleGenerator;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FlatMethodLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FlatMethodLoanScheduleGenerator.java
index e383db13c..437669528 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FlatMethodLoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FlatMethodLoanScheduleGenerator.java
@@ -22,130 +22,117 @@ import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
- private final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
- private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
-
- @Override
- public LoanScheduleData generate(
- final ApplicationCurrency currency,
- final LoanProductRelatedDetail loanScheduleInfo,
- final Integer loanTermFrequency,
- final PeriodFrequencyType loanTermFrequencyType,
- final LocalDate disbursementDate,
- final LocalDate firstRepaymentDate,
- final LocalDate interestCalculatedFrom,
- final Set loanCharges) {
-
- final Collection periods = new ArrayList();
-
- final List scheduledDates = this.scheduledDateGenerator.generate(loanScheduleInfo, disbursementDate, firstRepaymentDate);
+ private final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
+ private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
- MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
-
- BigDecimal loanTermPeriodsInYear = BigDecimal.valueOf(this.paymentPeriodsInOneYearCalculator.calculate(loanTermFrequencyType));
- BigDecimal interestRateForLoanTerm = loanScheduleInfo
- .getAnnualNominalInterestRate()
- .divide(loanTermPeriodsInYear, mc)
- .divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc)
- .multiply(BigDecimal.valueOf(loanTermFrequency));
-
- final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
- Money totalInterestForLoanTerm = loanScheduleInfo.getPrincipal().multiplyRetainScale(interestRateForLoanTerm, RoundingMode.HALF_EVEN);
+ @Override
+ public LoanScheduleData generate(final ApplicationCurrency currency, final LoanProductRelatedDetail loanScheduleInfo,
+ final Integer loanTermFrequency, final PeriodFrequencyType loanTermFrequencyType, final LocalDate disbursementDate,
+ final LocalDate firstRepaymentDate, final LocalDate interestCalculatedFrom, final Set loanCharges) {
- Money interestPerInstallment = totalInterestForLoanTerm.dividedBy(Long.valueOf(loanScheduleInfo.getNumberOfRepayments()), RoundingMode.HALF_EVEN);
-
- Money principalPerInstallment = loanScheduleInfo.getPrincipal()
- .dividedBy(loanScheduleInfo.getNumberOfRepayments(),
- RoundingMode.HALF_EVEN);
+ final Collection periods = new ArrayList();
- Money outstandingBalance = loanScheduleInfo.getPrincipal();
- Money principalDisbursed = loanScheduleInfo.getPrincipal();
- Money totalPrincipal = Money.zero(outstandingBalance.getCurrency());
- Money totalInterest = Money.zero(outstandingBalance.getCurrency());
-
- BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
- for (LoanCharge loanCharge : loanCharges) {
- if (loanCharge.isDueAtDisbursement()) {
- chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
- }
- }
-
- BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement;
-
- // create entries of disbursement period on loan schedule
- final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false);
- periods.add(disbursementPeriod);
-
- int loanTermInDays = Integer.valueOf(0);
- BigDecimal cumulativePrincipalDisbursed = principalDisbursed.getAmount();
- BigDecimal cumulativePrincipalDue = BigDecimal.ZERO;
- BigDecimal cumulativeInterestExpected = BigDecimal.ZERO;
- BigDecimal totalExpectedRepayment = chargesDueAtTimeOfDisbursement;
-
- LocalDate startDate = disbursementDate;
- int periodNumber = 1;
- for (LocalDate scheduledDueDate : scheduledDates) {
- totalPrincipal = totalPrincipal.plus(principalPerInstallment);
- totalInterest = totalInterest.plus(interestPerInstallment);
-
- // number of days from startDate to this scheduledDate
- int daysInPeriod = Days.daysBetween(startDate.toDateMidnight().toDateTime(), scheduledDueDate.toDateMidnight().toDateTime()).getDays();
+ final List scheduledDates = this.scheduledDateGenerator.generate(loanScheduleInfo, disbursementDate, firstRepaymentDate);
- if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) {
- final Money difference = totalPrincipal.minus(loanScheduleInfo.getPrincipal());
- if (difference.isLessThanZero()) {
- principalPerInstallment = principalPerInstallment.plus(difference.abs());
- } else if (difference.isGreaterThanZero()) {
- principalPerInstallment = principalPerInstallment.minus(difference.abs());
- }
-
- final Money interestDifference = totalInterest.minus(totalInterestForLoanTerm);
- if (interestDifference.isLessThanZero()) {
- interestPerInstallment = interestPerInstallment.plus(interestDifference.abs());
- } else if (interestDifference.isGreaterThanZero()) {
- interestPerInstallment = interestPerInstallment.minus(interestDifference.abs());
- }
- }
+ MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
- outstandingBalance = outstandingBalance.minus(principalPerInstallment);
+ BigDecimal loanTermPeriodsInYear = BigDecimal.valueOf(this.paymentPeriodsInOneYearCalculator.calculate(loanTermFrequencyType));
+ BigDecimal interestRateForLoanTerm = loanScheduleInfo.getAnnualNominalInterestRate().divide(loanTermPeriodsInYear, mc)
+ .divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc).multiply(BigDecimal.valueOf(loanTermFrequency));
- Money feeChargesForInstallment = Money.zero(monetaryCurrency);
- Money penaltyChargesForInstallment = Money.zero(monetaryCurrency);
-
- Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment).plus(feeChargesForInstallment).plus(penaltyChargesForInstallment);
- cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount()).add(penaltyChargesForInstallment.getAmount());
-
- LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
- scheduledDueDate,
- principalPerInstallment.getAmount(),
- outstandingBalance.getAmount(),
- interestPerInstallment.getAmount(),
- feeChargesForInstallment.getAmount(),
- penaltyChargesForInstallment.getAmount(),
- totalInstallmentDue.getAmount());
+ final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
+ Money totalInterestForLoanTerm = loanScheduleInfo.getPrincipal().multiplyRetainScale(interestRateForLoanTerm,
+ RoundingMode.HALF_EVEN);
- periods.add(installment);
-
- // handle cumulative fields
- loanTermInDays += daysInPeriod;
- cumulativePrincipalDue = cumulativePrincipalDue.add(principalPerInstallment.getAmount());
- cumulativeInterestExpected = cumulativeInterestExpected.add(interestPerInstallment.getAmount());
- totalExpectedRepayment = totalExpectedRepayment.add(totalInstallmentDue.getAmount());
- startDate = scheduledDueDate;
+ Money interestPerInstallment = totalInterestForLoanTerm.dividedBy(Long.valueOf(loanScheduleInfo.getNumberOfRepayments()),
+ RoundingMode.HALF_EVEN);
- periodNumber++;
- }
-
- final BigDecimal cumulativePrincipalOutstanding = cumulativePrincipalDisbursed.subtract(cumulativePrincipalDue);
-
- CurrencyData currencyData = new CurrencyData(
- currency.getCode(),
- currency.getName(),
- monetaryCurrency.getDigitsAfterDecimal(),
- currency.getDisplaySymbol(),
- currency.getNameCode());
-
- return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
- cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
- }
+ Money principalPerInstallment = loanScheduleInfo.getPrincipal().dividedBy(loanScheduleInfo.getNumberOfRepayments(),
+ RoundingMode.HALF_EVEN);
+
+ Money outstandingBalance = loanScheduleInfo.getPrincipal();
+ Money principalDisbursed = loanScheduleInfo.getPrincipal();
+ Money totalPrincipal = Money.zero(outstandingBalance.getCurrency());
+ Money totalInterest = Money.zero(outstandingBalance.getCurrency());
+
+ BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
+ for (LoanCharge loanCharge : loanCharges) {
+ if (loanCharge.isDueAtDisbursement()) {
+ chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
+ }
+ }
+
+ BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement;
+
+ // create entries of disbursement period on loan schedule
+ final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate,
+ principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false);
+ periods.add(disbursementPeriod);
+
+ int loanTermInDays = Integer.valueOf(0);
+ BigDecimal cumulativePrincipalDisbursed = principalDisbursed.getAmount();
+ BigDecimal cumulativePrincipalDue = BigDecimal.ZERO;
+ BigDecimal cumulativeInterestExpected = BigDecimal.ZERO;
+ BigDecimal totalExpectedRepayment = chargesDueAtTimeOfDisbursement;
+
+ LocalDate startDate = disbursementDate;
+ int periodNumber = 1;
+ for (LocalDate scheduledDueDate : scheduledDates) {
+ totalPrincipal = totalPrincipal.plus(principalPerInstallment);
+ totalInterest = totalInterest.plus(interestPerInstallment);
+
+ // number of days from startDate to this scheduledDate
+ int daysInPeriod = Days.daysBetween(startDate.toDateMidnight().toDateTime(), scheduledDueDate.toDateMidnight().toDateTime())
+ .getDays();
+
+ if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) {
+ final Money difference = totalPrincipal.minus(loanScheduleInfo.getPrincipal());
+ if (difference.isLessThanZero()) {
+ principalPerInstallment = principalPerInstallment.plus(difference.abs());
+ } else if (difference.isGreaterThanZero()) {
+ principalPerInstallment = principalPerInstallment.minus(difference.abs());
+ }
+
+ final Money interestDifference = totalInterest.minus(totalInterestForLoanTerm);
+ if (interestDifference.isLessThanZero()) {
+ interestPerInstallment = interestPerInstallment.plus(interestDifference.abs());
+ } else if (interestDifference.isGreaterThanZero()) {
+ interestPerInstallment = interestPerInstallment.minus(interestDifference.abs());
+ }
+ }
+
+ outstandingBalance = outstandingBalance.minus(principalPerInstallment);
+
+ Money feeChargesForInstallment = Money.zero(monetaryCurrency);
+ Money penaltyChargesForInstallment = Money.zero(monetaryCurrency);
+
+ Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment).plus(feeChargesForInstallment)
+ .plus(penaltyChargesForInstallment);
+ cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount()).add(
+ penaltyChargesForInstallment.getAmount());
+
+ LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate, scheduledDueDate,
+ principalPerInstallment.getAmount(), outstandingBalance.getAmount(), interestPerInstallment.getAmount(),
+ feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount(), totalInstallmentDue.getAmount());
+
+ periods.add(installment);
+
+ // handle cumulative fields
+ loanTermInDays += daysInPeriod;
+ cumulativePrincipalDue = cumulativePrincipalDue.add(principalPerInstallment.getAmount());
+ cumulativeInterestExpected = cumulativeInterestExpected.add(interestPerInstallment.getAmount());
+ totalExpectedRepayment = totalExpectedRepayment.add(totalInstallmentDue.getAmount());
+ startDate = scheduledDueDate;
+
+ periodNumber++;
+ }
+
+ final BigDecimal cumulativePrincipalOutstanding = cumulativePrincipalDisbursed.subtract(cumulativePrincipalDue);
+
+ CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(), monetaryCurrency.getDigitsAfterDecimal(),
+ currency.getDisplaySymbol(), currency.getNameCode());
+
+ return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
+ cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java
new file mode 100644
index 000000000..06aecdfd6
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java
@@ -0,0 +1,105 @@
+package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
+
+import java.math.BigDecimal;
+import java.util.Set;
+
+import org.joda.time.LocalDate;
+import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
+import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
+import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
+import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
+import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
+import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
+import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
+
+/**
+ * Domain representation of a Loan Schedule (not used for persistence)
+ */
+public final class LoanSchedule {
+
+ private final LoanScheduleGenerator loanScheduleGenerator;
+ private final ApplicationCurrency applicationCurrency;
+ private final BigDecimal principal;
+ private final BigDecimal nominalInterestRatePerPeriod;
+ private final PeriodFrequencyType interestRatePeriodFrequencyType;
+ private final BigDecimal nominalAnnualInterestRate;
+ private final InterestMethod interestMethod;
+ private final InterestCalculationPeriodMethod interestCalculationPeriodMethod;
+ private final Integer repaymentEvery;
+ private final PeriodFrequencyType repaymentPeriodFrequencyType;
+ private final Integer numberOfRepayments;
+ private final AmortizationMethod amortizationMethod;
+ private final Integer loanTermFrequency;
+ private final PeriodFrequencyType loanTermPeriodFrequencyType;
+ private final LocalDate disbursementDate;
+ private final LocalDate repaymentStartFromDate;
+ private final LocalDate interestChargedFromDate;
+ private final BigDecimal inArrearsTolerance;
+
+ private final Set loanCharges;
+
+ public LoanSchedule(final LoanScheduleGenerator loanScheduleGenerator, final ApplicationCurrency applicationCurrency,
+ final BigDecimal principal, final BigDecimal nominalInterestRatePerPeriod,
+ final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal nominalAnnualInterestRate,
+ final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
+ final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments,
+ final AmortizationMethod amortizationMethod, final Integer loanTermFrequency,
+ final PeriodFrequencyType loanTermPeriodFrequencyType, final Set loanCharges, final LocalDate disbursementDate,
+ final LocalDate repaymentStartFromDate, final LocalDate interestChargedFromDate,
+ final BigDecimal inArrearsTolerance) {
+ this.loanScheduleGenerator = loanScheduleGenerator;
+ this.applicationCurrency = applicationCurrency;
+ this.principal = principal;
+ this.nominalInterestRatePerPeriod = nominalInterestRatePerPeriod;
+ this.interestRatePeriodFrequencyType = interestRatePeriodFrequencyType;
+ this.nominalAnnualInterestRate = nominalAnnualInterestRate;
+ this.interestMethod = interestMethod;
+ this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
+ this.repaymentEvery = repaymentEvery;
+ this.repaymentPeriodFrequencyType = repaymentPeriodFrequencyType;
+ this.numberOfRepayments = numberOfRepayments;
+ this.amortizationMethod = amortizationMethod;
+ this.loanTermFrequency = loanTermFrequency;
+ this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
+ this.loanCharges = loanCharges;
+ this.disbursementDate = disbursementDate;
+ this.repaymentStartFromDate = repaymentStartFromDate;
+ this.interestChargedFromDate = interestChargedFromDate;
+ this.inArrearsTolerance = inArrearsTolerance;
+ }
+
+ public LoanScheduleData generate() {
+ return loanScheduleGenerator.generate(applicationCurrency, loanProductRelatedDetail(), loanTermFrequency,
+ loanTermPeriodFrequencyType, disbursementDate, repaymentStartFromDate, interestChargedFromDate, loanCharges);
+ }
+
+ public LoanProductRelatedDetail loanProductRelatedDetail() {
+ final MonetaryCurrency currency = new MonetaryCurrency(applicationCurrency.getCode(), applicationCurrency.getDecimalPlaces());
+
+ return LoanProductRelatedDetail.createFrom(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
+ nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType,
+ numberOfRepayments, amortizationMethod, this.inArrearsTolerance);
+ }
+
+ public Integer getLoanTermFrequency() {
+ return this.loanTermFrequency;
+ }
+
+ public PeriodFrequencyType getLoanTermPeriodFrequencyType() {
+ return this.loanTermPeriodFrequencyType;
+ }
+
+ public LocalDate getDisbursementDate() {
+ return this.disbursementDate;
+ }
+
+ public LocalDate getRepaymentStartFromDate() {
+ return this.repaymentStartFromDate;
+ }
+
+ public LocalDate getInterestChargedFromDate() {
+ return this.interestChargedFromDate;
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
index d66314aab..b0661b01a 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
@@ -11,10 +11,8 @@ import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
public interface LoanScheduleGenerator {
- LoanScheduleData generate(ApplicationCurrency applicationCurrency,
- LoanProductRelatedDetail loanScheduleInfo,
- Integer loanTermFrequency,
- PeriodFrequencyType loanTermFrequencyType,
- LocalDate disbursementDate, LocalDate firstRepaymentDate, LocalDate interestCalculatedFrom, Set loanCharges);
+ LoanScheduleData generate(ApplicationCurrency applicationCurrency, LoanProductRelatedDetail loanScheduleInfo,
+ Integer loanTermFrequency, PeriodFrequencyType loanTermFrequencyType, LocalDate disbursementDate, LocalDate firstRepaymentDate,
+ LocalDate interestCalculatedFrom, Set loanCharges);
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/query/CalculateLoanScheduleQuery.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/query/CalculateLoanScheduleQuery.java
new file mode 100644
index 000000000..d59cb9a62
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/query/CalculateLoanScheduleQuery.java
@@ -0,0 +1,207 @@
+package org.mifosplatform.portfolio.loanaccount.loanschedule.query;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.data.ApiParameterError;
+import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
+import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
+
+/**
+ * Immutable query used when auto-calculating loan schedules based on loan
+ * terms.
+ */
+public class CalculateLoanScheduleQuery {
+
+ private final Long productId;
+ private final BigDecimal principal;
+ private final Integer loanTermFrequency;
+ private final Integer loanTermFrequencyType;
+ private final Integer repaymentEvery;
+ private final Integer repaymentFrequencyType;
+ private final Integer numberOfRepayments;
+ private final BigDecimal interestRatePerPeriod;
+ private final Integer interestRateFrequencyType;
+ private final Integer amortizationType;
+ private final Integer interestType;
+ private final Integer interestCalculationPeriodType;
+ private final LocalDate expectedDisbursementDate;
+ private final LocalDate repaymentsStartingFromDate;
+ private final LocalDate interestChargedFromDate;
+ private final LoanChargeCommand[] charges;
+
+ public CalculateLoanScheduleQuery(final Long productId, final BigDecimal principal, final BigDecimal interestRatePerPeriod,
+ Integer interestRateFrequencyMethod, final Integer interestMethod, final Integer interestCalculationPeriodMethod,
+ final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod,
+ final Integer loanTermFrequency, final Integer loanTermFrequencyType, final LocalDate expectedDisbursementDate,
+ final LocalDate repaymentsStartingFromDate, final LocalDate interestCalculatedFromDate, final LoanChargeCommand[] charges) {
+
+ this.productId = productId;
+ this.principal = principal;
+ this.interestRatePerPeriod = interestRatePerPeriod;
+ this.interestRateFrequencyType = interestRateFrequencyMethod;
+ this.interestType = interestMethod;
+ this.interestCalculationPeriodType = interestCalculationPeriodMethod;
+ this.repaymentEvery = repaymentEvery;
+ this.repaymentFrequencyType = repaymentFrequency;
+ this.numberOfRepayments = numberOfRepayments;
+ this.amortizationType = amortizationMethod;
+ this.loanTermFrequency = loanTermFrequency;
+ this.loanTermFrequencyType = loanTermFrequencyType;
+
+ this.expectedDisbursementDate = expectedDisbursementDate;
+ this.repaymentsStartingFromDate = repaymentsStartingFromDate;
+ this.interestChargedFromDate = interestCalculatedFromDate;
+ this.charges = charges;
+ }
+
+ public Long getProductId() {
+ return productId;
+ }
+
+ public BigDecimal getPrincipal() {
+ return principal;
+ }
+
+ public Integer getLoanTermFrequency() {
+ return loanTermFrequency;
+ }
+
+ public Integer getLoanTermFrequencyType() {
+ return loanTermFrequencyType;
+ }
+
+ public Integer getRepaymentEvery() {
+ return repaymentEvery;
+ }
+
+ public Integer getRepaymentFrequencyType() {
+ return repaymentFrequencyType;
+ }
+
+ public Integer getNumberOfRepayments() {
+ return numberOfRepayments;
+ }
+
+ public BigDecimal getInterestRatePerPeriod() {
+ return interestRatePerPeriod;
+ }
+
+ public Integer getInterestRateFrequencyType() {
+ return interestRateFrequencyType;
+ }
+
+ public Integer getAmortizationType() {
+ return amortizationType;
+ }
+
+ public Integer getInterestType() {
+ return interestType;
+ }
+
+ public Integer getInterestCalculationPeriodType() {
+ return interestCalculationPeriodType;
+ }
+
+ public LocalDate getExpectedDisbursementDate() {
+ return expectedDisbursementDate;
+ }
+
+ public LocalDate getRepaymentsStartingFromDate() {
+ return repaymentsStartingFromDate;
+ }
+
+ public LocalDate getInterestChargedFromDate() {
+ return interestChargedFromDate;
+ }
+
+ public LoanChargeCommand[] getCharges() {
+ return charges;
+ }
+
+ public void validate() {
+
+ List dataValidationErrors = new ArrayList();
+
+ DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
+
+ baseDataValidator.reset().parameter("principal").value(this.principal).notNull().positiveAmount();
+
+ baseDataValidator.reset().parameter("loanTermFrequency").value(this.loanTermFrequency).notNull().integerGreaterThanZero();
+ baseDataValidator.reset().parameter("loanTermFrequencyType").value(this.loanTermFrequencyType).notNull().inMinMaxRange(0, 4);
+
+ baseDataValidator.reset().parameter("numberOfRepayments").value(this.numberOfRepayments).notNull().integerGreaterThanZero();
+ baseDataValidator.reset().parameter("repaymentEvery").value(this.repaymentEvery).notNull().integerGreaterThanZero();
+ baseDataValidator.reset().parameter("repaymentFrequencyType").value(this.repaymentFrequencyType).notNull().inMinMaxRange(0, 3);
+
+ // FIXME - this constraint doesnt really need to be here. should be
+ // possible to express loan term as say 12 months whilst also saying
+ // - that the repayment structure is 6 repayments every bi-monthly.
+ if (this.loanTermFrequencyType != null && !this.loanTermFrequencyType.equals(this.repaymentFrequencyType)) {
+ ApiParameterError error = ApiParameterError.parameterError(
+ "validation.msg.loan.loanTermFrequencyType.not.the.same.as.repaymentFrequencyType",
+ "The parameters loanTermFrequencyType and repaymentFrequencyType must be the same.", "loanTermFrequencyType",
+ this.loanTermFrequencyType, this.repaymentFrequencyType);
+ dataValidationErrors.add(error);
+ } else {
+ if (this.loanTermFrequency != null && this.repaymentEvery != null && this.numberOfRepayments != null) {
+ int suggestsedLoanTerm = this.repaymentEvery * this.numberOfRepayments;
+ if (this.loanTermFrequency.intValue() < suggestsedLoanTerm) {
+ ApiParameterError error = ApiParameterError
+ .parameterError(
+ "validation.msg.loan.loanTermFrequency.less.than.repayment.structure.suggests",
+ "The parameter loanTermFrequency is less than the suggest loan term as indicated by numberOfRepayments and repaymentEvery.",
+ "loanTermFrequency", this.loanTermFrequency, this.numberOfRepayments, this.repaymentEvery);
+ dataValidationErrors.add(error);
+ }
+ }
+ }
+
+ baseDataValidator.reset().parameter("interestRatePerPeriod").value(this.interestRatePerPeriod).notNull();
+ baseDataValidator.reset().parameter("interestRateFrequencyType").value(this.interestRateFrequencyType).notNull()
+ .inMinMaxRange(0, 3);
+ baseDataValidator.reset().parameter("amortizationType").value(this.amortizationType).notNull().inMinMaxRange(0, 1);
+ baseDataValidator.reset().parameter("interestType").value(this.interestType).notNull().inMinMaxRange(0, 1);
+ baseDataValidator.reset().parameter("interestCalculationPeriodType").value(this.interestCalculationPeriodType).notNull()
+ .inMinMaxRange(0, 1);
+
+ baseDataValidator.reset().parameter("expectedDisbursementDate").value(this.expectedDisbursementDate).notNull();
+
+ if (this.expectedDisbursementDate != null) {
+ if (this.repaymentsStartingFromDate != null && this.expectedDisbursementDate.isAfter(this.repaymentsStartingFromDate)) {
+ 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", this.expectedDisbursementDate, this.repaymentsStartingFromDate);
+ dataValidationErrors.add(error);
+ }
+ }
+
+ if (this.repaymentsStartingFromDate != null && this.interestChargedFromDate == 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.",
+ "interestChargedFromDate", this.repaymentsStartingFromDate);
+ dataValidationErrors.add(error);
+ } else if (this.repaymentsStartingFromDate == null && this.interestChargedFromDate != null) {
+
+ // validate interestCalculatedFromDate is after or on
+ // repaymentsStartingFromDate
+ if (this.expectedDisbursementDate.isAfter(this.interestChargedFromDate)) {
+ ApiParameterError error = ApiParameterError.parameterError(
+ "validation.msg.loan.interestChargedFromDate.cannot.be.before.disbursement.date",
+ "The parameter interestCalculatedFromDate cannot be before the date given for expected disbursement.",
+ "interestChargedFromDate", this.interestChargedFromDate, this.expectedDisbursementDate);
+ dataValidationErrors.add(error);
+ }
+ }
+
+ if (!dataValidationErrors.isEmpty()) { 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/mifosplatform/portfolio/loanaccount/loanschedule/service/CalculationPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/CalculationPlatformService.java
deleted file mode 100644
index dbee57ef0..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/CalculationPlatformService.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
-
-import org.mifosplatform.portfolio.loanaccount.loanschedule.command.CalculateLoanScheduleCommand;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-
-public interface CalculationPlatformService {
-
- LoanScheduleData calculateLoanSchedule(CalculateLoanScheduleCommand command);
-}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/CalculationPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/CalculationPlatformServiceImpl.java
deleted file mode 100644
index df4950f22..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/CalculationPlatformServiceImpl.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
-
-import java.util.Set;
-
-import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
-import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
-import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepository;
-import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.command.CalculateLoanScheduleCommand;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.command.CalculateLoanScheduleCommandValidator;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultLoanScheduleGeneratorFactory;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
-import org.mifosplatform.portfolio.loanaccount.service.LoanChargeAssembler;
-import org.mifosplatform.portfolio.loanaccount.service.LoanProductRelatedDetailAssembler;
-import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
-import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-@Service
-public class CalculationPlatformServiceImpl implements CalculationPlatformService {
-
- private final LoanScheduleGeneratorFactory loanScheduleFactory;
- private final ApplicationCurrencyRepository applicationCurrencyRepository;
- private final PlatformSecurityContext context;
- private final LoanProductRelatedDetailAssembler loanProductRelatedDetailAssembler;
- private final LoanChargeAssembler loanChargeAssembler;
-
- @Autowired
- public CalculationPlatformServiceImpl(
- final PlatformSecurityContext context,
- final ApplicationCurrencyRepository applicationCurrencyRepository,
- final LoanProductRelatedDetailAssembler loanProductRelatedDetailAssembler,
- final LoanChargeAssembler loanChargeAssembler) {
- this.context = context;
- this.applicationCurrencyRepository = applicationCurrencyRepository;
- this.loanScheduleFactory = new DefaultLoanScheduleGeneratorFactory();
- this.loanProductRelatedDetailAssembler = loanProductRelatedDetailAssembler;
- this.loanChargeAssembler = loanChargeAssembler;
- }
-
- @Override
- public LoanScheduleData calculateLoanSchedule(final CalculateLoanScheduleCommand command) {
-
- context.authenticatedUser();
-
- final CalculateLoanScheduleCommandValidator validator = new CalculateLoanScheduleCommandValidator(command);
- validator.validate();
-
- final LoanProductRelatedDetail loanScheduleRelatedDetails = this.loanProductRelatedDetailAssembler.assembleFrom(command);
-
- final Integer loanTermFrequency = command.getLoanTermFrequency();
- final PeriodFrequencyType loanTermFrequencyType = PeriodFrequencyType.fromInt(command.getLoanTermFrequencyType());
- final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestType());
-
- final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(interestMethod);
-
- final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(loanScheduleRelatedDetails.getCurrency().getCode());
-
- final Set loanCharges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanScheduleRelatedDetails.getPrincipal().getAmount());
-
- return loanScheduleGenerator.generate(applicationCurrency,
- loanScheduleRelatedDetails, loanTermFrequency,
- loanTermFrequencyType, command.getExpectedDisbursementDate(),
- command.getRepaymentsStartingFromDate(),
- command.getInterestChargedFromDate(), loanCharges);
- }
-}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
new file mode 100644
index 000000000..3a21e6c20
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
@@ -0,0 +1,108 @@
+package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
+
+import java.math.BigDecimal;
+import java.util.Set;
+
+import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
+import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
+import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepository;
+import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
+import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanSchedule;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
+import org.mifosplatform.portfolio.loanaccount.service.LoanChargeAssembler;
+import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
+import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
+import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
+import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
+import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.google.gson.JsonElement;
+
+@Service
+public class LoanScheduleAssembler {
+
+ private final FromJsonHelper fromApiJsonHelper;
+ private final LoanProductRepository loanProductRepository;
+ private final ApplicationCurrencyRepository applicationCurrencyRepository;
+ private final LoanChargeAssembler loanChargeAssembler;
+ private final LoanScheduleGeneratorFactory loanScheduleFactory;
+ private final AprCalculator aprCalculator;
+
+ @Autowired
+ public LoanScheduleAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository,
+ final ApplicationCurrencyRepository applicationCurrencyRepository, final LoanScheduleGeneratorFactory loanScheduleFactory,
+ final AprCalculator aprCalculator, final LoanChargeAssembler loanChargeAssembler) {
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ this.loanProductRepository = loanProductRepository;
+ this.applicationCurrencyRepository = applicationCurrencyRepository;
+ this.loanScheduleFactory = loanScheduleFactory;
+ this.aprCalculator = aprCalculator;
+ this.loanChargeAssembler = loanChargeAssembler;
+ }
+
+ public LoanSchedule fromJson(final String json) {
+ final JsonElement element = fromApiJsonHelper.parse(json);
+ return fromJson(element);
+ }
+
+ public LoanSchedule fromJson(final JsonElement element) {
+ return fromJson(element, null);
+ }
+
+ public LoanSchedule fromJson(final JsonElement element, final BigDecimal inArrearsTolerance) {
+ final Long loanProductId = fromApiJsonHelper.extractLongNamed("productId", element);
+
+ final LoanProduct loanProduct = this.loanProductRepository.findOne(loanProductId);
+ if (loanProduct == null) { throw new LoanProductNotFoundException(loanProductId); }
+
+ final MonetaryCurrency currency = loanProduct.getCurrency();
+ final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(currency.getCode());
+
+ final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
+ final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
+ final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestRateFrequencyType", element);
+ final Integer interestType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
+ final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestCalculationPeriodType",
+ element);
+
+ final InterestMethod interestMethod = InterestMethod.fromInt(interestType);
+ final PeriodFrequencyType interestRatePeriodFrequencyType = PeriodFrequencyType.fromInt(interestRateFrequencyType);
+ final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod
+ .fromInt(interestCalculationPeriodType);
+
+ final BigDecimal defaultAnnualNominalInterestRate = this.aprCalculator.calculateFrom(interestRatePeriodFrequencyType,
+ interestRatePerPeriod);
+
+ final Integer repaymentEvery = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
+ final Integer repaymentFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyType", element);
+ final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
+ final Integer amortizationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
+ final PeriodFrequencyType repaymentPeriodFrequencyType = PeriodFrequencyType.fromInt(repaymentFrequencyType);
+ final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(amortizationType);
+
+ final Integer loanTermFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequency", element);
+ final Integer loanTermFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequencyType", element);
+ final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(loanTermFrequencyType);
+
+ final LocalDate expectedDisbursementDate = fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", element);
+ final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", element);
+ final LocalDate interestChargedFromDate = fromApiJsonHelper.extractLocalDateNamed("interestChargedFromDate", element);
+
+ final Set loanCharges = this.loanChargeAssembler.fromParsedJson(element);
+
+ final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(interestMethod);
+
+ return new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, interestRatePerPeriod,
+ interestRatePeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod,
+ repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, loanTermFrequency,
+ loanTermPeriodFrequencyType, loanCharges, expectedDisbursementDate, repaymentsStartingFromDate, interestChargedFromDate, inArrearsTolerance);
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformService.java
new file mode 100644
index 000000000..9c427f3eb
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformService.java
@@ -0,0 +1,9 @@
+package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
+
+import org.mifosplatform.infrastructure.core.api.JsonQuery;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
+
+public interface LoanScheduleCalculationPlatformService {
+
+ LoanScheduleData calculateLoanSchedule(JsonQuery query);
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
new file mode 100644
index 000000000..9a511d3b6
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
@@ -0,0 +1,38 @@
+package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
+
+import org.mifosplatform.infrastructure.core.api.JsonQuery;
+import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanSchedule;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.query.CalculateLoanScheduleQuery;
+import org.mifosplatform.portfolio.loanaccount.serialization.CalculateLoanScheduleQueryFromApiJsonDeserializer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleCalculationPlatformService {
+
+ private final PlatformSecurityContext context;
+ private final CalculateLoanScheduleQueryFromApiJsonDeserializer fromApiJsonDeserializer;
+ private final LoanScheduleAssembler loanScheduleAssembler;
+
+ @Autowired
+ public LoanScheduleCalculationPlatformServiceImpl(final PlatformSecurityContext context,
+ final CalculateLoanScheduleQueryFromApiJsonDeserializer fromApiJsonDeserializer,
+ final LoanScheduleAssembler loanScheduleAssembler) {
+ this.context = context;
+ this.fromApiJsonDeserializer = fromApiJsonDeserializer;
+ this.loanScheduleAssembler = loanScheduleAssembler;
+ }
+
+ @Override
+ public LoanScheduleData calculateLoanSchedule(final JsonQuery query) {
+ context.authenticatedUser();
+
+ CalculateLoanScheduleQuery calculateLoanScheduleQuery = this.fromApiJsonDeserializer.commandFromApiJson(query.json());
+ calculateLoanScheduleQuery.validate();
+
+ final LoanSchedule loanSchedule = this.loanScheduleAssembler.fromJson(query.parsedJson());
+ return loanSchedule.generate();
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonDeserializer.java
new file mode 100644
index 000000000..b95788559
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonDeserializer.java
@@ -0,0 +1,114 @@
+package org.mifosplatform.portfolio.loanaccount.serialization;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
+import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
+import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
+import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
+import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.query.CalculateLoanScheduleQuery;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Implementation of {@link FromApiJsonDeserializer} for
+ * {@link CalculateLoanScheduleQuery}'s.
+ */
+@Component
+public final class CalculateLoanScheduleQueryFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer {
+
+ /**
+ * The parameters supported for this command.
+ */
+ final Set supportedParameters = new HashSet(Arrays.asList("clientId", "groupId", "productId", "externalId", "fundId",
+ "transactionProcessingStrategyId", "principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery",
+ "numberOfRepayments", "loanTermFrequency", "loanTermFrequencyType", "charges", "repaymentFrequencyType",
+ "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "expectedDisbursementDate",
+ "repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote", "locale", "dateFormat",
+ "loanOfficerId", "id"));
+
+ private final FromJsonHelper fromApiJsonHelper;
+
+ @Autowired
+ public CalculateLoanScheduleQueryFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ }
+
+ @Override
+ public CalculateLoanScheduleQuery commandFromApiJson(final String json) {
+
+ if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
+
+ final Type typeOfMap = new TypeToken>() {}.getType();
+ fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
+
+ final JsonElement element = fromApiJsonHelper.parse(json);
+ final Long productId = fromApiJsonHelper.extractLongNamed("productId", element);
+ final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
+ final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
+ final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestRateFrequencyType", element);
+ final Integer interestType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
+ final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestCalculationPeriodType",
+ element);
+
+ final Integer repaymentEvery = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
+ final Integer repaymentFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyType", element);
+ final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
+ final Integer amortizationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
+ final Integer loanTermFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequency", element);
+ final Integer loanTermFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequencyType", element);
+ final LocalDate expectedDisbursementDate = fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", element);
+ final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", element);
+ final LocalDate interestChargedFromDate = fromApiJsonHelper.extractLocalDateNamed("interestChargedFromDate", element);
+
+ // ///
+ LoanChargeCommand[] charges = null;
+ if (element.isJsonObject()) {
+ final JsonObject topLevelJsonElement = element.getAsJsonObject();
+ final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
+ final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
+ if (topLevelJsonElement.has("charges") && topLevelJsonElement.get("charges").isJsonArray()) {
+
+ final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
+ charges = new LoanChargeCommand[array.size()];
+ for (int i = 0; i < array.size(); i++) {
+
+ final JsonObject loanChargeElement = array.get(i).getAsJsonObject();
+ final Set parametersPassedInForChargesCommand = new HashSet();
+
+ final Long id = fromApiJsonHelper.extractLongNamed("id", loanChargeElement);
+ final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
+ final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement, locale);
+ final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed("chargeTimeType", loanChargeElement, locale);
+ final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", loanChargeElement,
+ locale);
+ final LocalDate specifiedDueDate = fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", loanChargeElement,
+ dateFormat, locale);
+
+ charges[i] = new LoanChargeCommand(parametersPassedInForChargesCommand, id, null, chargeId, amount, chargeTimeType,
+ chargeCalculationType, specifiedDueDate);
+ }
+ }
+ }
+ // /
+
+ return new CalculateLoanScheduleQuery(productId, principal, interestRatePerPeriod, interestRateFrequencyType, interestType,
+ interestCalculationPeriodType, repaymentEvery, repaymentFrequencyType, numberOfRepayments, amortizationType,
+ loanTermFrequency, loanTermFrequencyType, expectedDisbursementDate, repaymentsStartingFromDate, interestChargedFromDate,
+ charges);
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonDeserializer.java
new file mode 100644
index 000000000..461676c40
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonDeserializer.java
@@ -0,0 +1,142 @@
+package org.mifosplatform.portfolio.loanaccount.serialization;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
+import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
+import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
+import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
+import org.mifosplatform.portfolio.loanaccount.command.LoanApplicationCommand;
+import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Implementation of {@link FromApiJsonDeserializer} for
+ * {@link LoanApplicationCommand}'s.
+ */
+@Component
+public final class LoanApplicationCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer {
+
+ /**
+ * The parameters supported for this command.
+ */
+ final Set supportedParameters = new HashSet(Arrays.asList("clientId", "groupId", "productId", "externalId", "fundId",
+ "transactionProcessingStrategyId", "principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery",
+ "numberOfRepayments", "loanTermFrequency", "loanTermFrequencyType", "charges", "repaymentFrequencyType",
+ "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "expectedDisbursementDate",
+ "repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote", "locale", "dateFormat",
+ "loanOfficerId", "id"));
+
+ private final FromJsonHelper fromApiJsonHelper;
+
+ @Autowired
+ public LoanApplicationCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ }
+
+ @Override
+ public LoanApplicationCommand commandFromApiJson(final String json) {
+
+ if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
+
+ final Type typeOfMap = new TypeToken>() {}.getType();
+ fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
+
+ final JsonElement element = fromApiJsonHelper.parse(json);
+ final Long clientId = fromApiJsonHelper.extractLongNamed("clientId", element);
+ final Long groupId = fromApiJsonHelper.extractLongNamed("groupId", element);
+ final Long productId = fromApiJsonHelper.extractLongNamed("productId", element);
+ final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element);
+ final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element);
+ final Long loanOfficerId = fromApiJsonHelper.extractLongNamed("loanOfficerId", element);
+ final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
+
+ final LocalDate submittedOnDate = fromApiJsonHelper.extractLocalDateNamed("submittedOnDate", element);
+ final String submittedOnNote = fromApiJsonHelper.extractStringNamed("submittedOnNote", element);
+
+ final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
+ final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
+ final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestRateFrequencyType", element);
+ final Integer interestType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
+ final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestCalculationPeriodType",
+ element);
+
+ // final InterestMethod interestMethod =
+ // InterestMethod.fromInt(interestType);
+ // final PeriodFrequencyType interestRatePeriodFrequencyType =
+ // PeriodFrequencyType.fromInt(interestRateFrequencyType);
+ // final InterestCalculationPeriodMethod interestCalculationPeriodMethod
+ // = InterestCalculationPeriodMethod
+ // .fromInt(interestCalculationPeriodType);
+
+ final Integer amortizationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
+ final Integer repaymentEvery = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
+ final Integer repaymentFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyType", element);
+ final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
+ // final PeriodFrequencyType repaymentPeriodFrequencyType =
+ // PeriodFrequencyType.fromInt(repaymentFrequencyType);
+ // final AmortizationMethod amortizationMethod =
+ // AmortizationMethod.fromInt(amortizationType);
+
+ final Integer loanTermFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequency", element);
+ final Integer loanTermFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequencyType", element);
+ // final PeriodFrequencyType loanTermPeriodFrequencyType =
+ // PeriodFrequencyType.fromInt(loanTermFrequencyType);
+
+ final LocalDate expectedDisbursementDate = fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", element);
+ final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", element);
+ final LocalDate interestChargedFromDate = fromApiJsonHelper.extractLocalDateNamed("interestChargedFromDate", element);
+
+ final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
+
+ // ///
+ LoanChargeCommand[] charges = null;
+ if (element.isJsonObject()) {
+ final JsonObject topLevelJsonElement = element.getAsJsonObject();
+ final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
+ final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
+ if (topLevelJsonElement.has("charges") && topLevelJsonElement.get("charges").isJsonArray()) {
+
+ final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
+ charges = new LoanChargeCommand[array.size()];
+ for (int i = 0; i < array.size(); i++) {
+
+ final JsonObject loanChargeElement = array.get(i).getAsJsonObject();
+ final Set parametersPassedInForChargesCommand = new HashSet();
+
+ final Long id = fromApiJsonHelper.extractLongNamed("id", loanChargeElement);
+ final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
+ final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement, locale);
+ final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed("chargeTimeType", loanChargeElement, locale);
+ final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", loanChargeElement,
+ locale);
+ final LocalDate specifiedDueDate = fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", loanChargeElement,
+ dateFormat, locale);
+
+ charges[i] = new LoanChargeCommand(parametersPassedInForChargesCommand, id, null, chargeId, amount, chargeTimeType,
+ chargeCalculationType, specifiedDueDate);
+ }
+ }
+ }
+
+ return new LoanApplicationCommand(new HashSet(), null, clientId, groupId, productId, externalId, fundId,
+ transactionProcessingStrategyId, submittedOnDate, submittedOnNote, expectedDisbursementDate, repaymentsStartingFromDate,
+ interestChargedFromDate, principal, interestRatePerPeriod, interestRateFrequencyType, interestType,
+ interestCalculationPeriodType, repaymentEvery, repaymentFrequencyType, numberOfRepayments, amortizationType,
+ loanTermFrequency, loanTermFrequencyType, inArrearsTolerance, charges, loanOfficerId);
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanStateTransitionCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanStateTransitionCommandFromApiJsonDeserializer.java
new file mode 100644
index 000000000..e4285931a
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanStateTransitionCommandFromApiJsonDeserializer.java
@@ -0,0 +1,61 @@
+package org.mifosplatform.portfolio.loanaccount.serialization;
+
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
+import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
+import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
+import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
+import org.mifosplatform.portfolio.loanaccount.command.LoanStateTransitionCommand;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Implementation of {@link FromApiJsonDeserializer} for
+ * {@link LoanStateTransitionCommand}'s.
+ */
+@Component
+public final class LoanStateTransitionCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer {
+
+ /**
+ * The parameters supported for this command.
+ */
+ final Set supportedParameters = new HashSet(Arrays.asList("eventDate", "note", "locale", "dateFormat", "status",
+ "approvedOnDate", "rejectedOnDate", "withdrawnOnDate", "disbursedOnDate", "expectedMaturityDate"));
+
+ private final FromJsonHelper fromApiJsonHelper;
+
+ @Autowired
+ public LoanStateTransitionCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ }
+
+ @Override
+ public LoanStateTransitionCommand commandFromApiJson(final String json) {
+
+ if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
+
+ final Type typeOfMap = new TypeToken>() {}.getType();
+ fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
+
+ final JsonElement element = fromApiJsonHelper.parse(json);
+ final LocalDate eventDate = fromApiJsonHelper.extractLocalDateNamed("eventDate", element);
+ final String note = fromApiJsonHelper.extractStringNamed("note", element);
+
+ final LocalDate approvedOnDate = fromApiJsonHelper.extractLocalDateNamed("approvedOnDate", element);
+ final LocalDate rejectedOnDate = fromApiJsonHelper.extractLocalDateNamed("rejectedOnDate", element);
+ final LocalDate withdrawnOnDate = fromApiJsonHelper.extractLocalDateNamed("withdrawnOnDate", element);
+ final LocalDate disbursedOnDate = fromApiJsonHelper.extractLocalDateNamed("disbursedOnDate", element);
+
+ return new LoanStateTransitionCommand(eventDate, note, approvedOnDate, rejectedOnDate, withdrawnOnDate, disbursedOnDate);
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java
index a6fb7c51a..67c868c49 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java
@@ -5,7 +5,9 @@ import java.util.Arrays;
import java.util.List;
import java.util.Set;
-import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
+import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.api.JsonCommand;
+import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.organisation.staff.domain.StaffRepository;
import org.mifosplatform.organisation.staff.exception.StaffNotFoundException;
@@ -19,207 +21,140 @@ import org.mifosplatform.portfolio.fund.exception.FundNotFoundException;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.domain.GroupRepository;
import org.mifosplatform.portfolio.group.exception.GroupNotFoundException;
-import org.mifosplatform.portfolio.loanaccount.command.LoanApplicationCommand;
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
-import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.service.CalculationPlatformService;
-import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
-import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
-import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanSchedule;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
-import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import com.google.gson.JsonElement;
+
@Service
public class LoanAssembler {
- private final LoanProductRepository loanProductRepository;
- private final ClientRepository clientRepository;
- private final GroupRepository groupRepository;
- private final AprCalculator aprCalculator = new AprCalculator();
- private final FundRepository fundRepository;
- private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
- private final StaffRepository staffRepository;
- private final CalculationPlatformService calculationPlatformService;
- private final LoanChargeAssembler loanChargeAssembler;
-
- @Autowired
- public LoanAssembler(
- final LoanProductRepository loanProductRepository,
- final ClientRepository clientRepository,
- final GroupRepository groupRepository,
- final FundRepository fundRepository,
- final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
- final StaffRepository staffRepository,
- final CalculationPlatformService calculationPlatformService,
- final LoanChargeAssembler loanChargeAssembler) {
- this.loanProductRepository = loanProductRepository;
- this.clientRepository = clientRepository;
- this.groupRepository = groupRepository;
- this.fundRepository = fundRepository;
- this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
- this.staffRepository = staffRepository;
- this.calculationPlatformService = calculationPlatformService;
- this.loanChargeAssembler = loanChargeAssembler;
- }
-
- public Loan assembleFrom(final LoanApplicationCommand command) {
-
- LoanProduct loanProduct = this.loanProductRepository.findOne(command.getProductId());
- if (loanProduct == null) {
- throw new LoanProductNotFoundException(command.getProductId());
- }
+ private final LoanProductRepository loanProductRepository;
+ private final ClientRepository clientRepository;
+ private final GroupRepository groupRepository;
+ private final FundRepository fundRepository;
+ private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
+ private final StaffRepository staffRepository;
+ private final LoanChargeAssembler loanChargeAssembler;
+ private final LoanScheduleAssembler loanScheduleAssembler;
+ private final FromJsonHelper fromApiJsonHelper;
- Client client = null;
- if (command.getClientId() != null) {
- client = this.clientRepository.findOne(command.getClientId());
- if (client == null || client.isDeleted()) {
- throw new ClientNotFoundException(command.getClientId());
- }
- }
-
- Group group = null;
- if (command.getGroupId() != null) {
- group = this.groupRepository.findOne(command.getGroupId());
- if (group == null || group.isDeleted()) {
- throw new GroupNotFoundException(command.getGroupId());
- }
- }
-
- final MonetaryCurrency currency = loanProduct.getCurrency();
- final Integer loanTermFrequency = command.getLoanTermFrequency();
- final PeriodFrequencyType loanTermFrequencyType = PeriodFrequencyType.fromInt(command.getLoanTermFrequencyType());
-
- final LoanProductRelatedDetail loanRepaymentScheduleDetail = assembleLoanProductRelatedDetailFrom(command, currency);
+ @Autowired
+ public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository,
+ final ClientRepository clientRepository, final GroupRepository groupRepository, final FundRepository fundRepository,
+ final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
+ final StaffRepository staffRepository, final LoanScheduleAssembler loanScheduleAssembler,
+ final LoanChargeAssembler loanChargeAssembler) {
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ this.loanProductRepository = loanProductRepository;
+ this.clientRepository = clientRepository;
+ this.groupRepository = groupRepository;
+ this.fundRepository = fundRepository;
+ this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
+ this.staffRepository = staffRepository;
+ this.loanScheduleAssembler = loanScheduleAssembler;
+ this.loanChargeAssembler = loanChargeAssembler;
+ }
- // associating fund with loan product at creation is optional for now.
- final Fund fund = findFundByIdIfProvided(command.getFundId());
- final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
+ public Loan assembleFrom(final JsonCommand command) {
+ final JsonElement element = command.parsedJson();
- //optionally associate a loan officer to the loan
- final Staff loanOfficer= findLoanOfficerByIdIfProvided(command.getLoanOfficerId());
-
- // optionally, see if charges are associated with loan on creation (through loan product or by being directly added)
- final Set loanCharges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanProduct.getCharges(), command.getPrincipal());
+ final Long clientId = fromApiJsonHelper.extractLongNamed("clientId", element);
+ final Long groupId = fromApiJsonHelper.extractLongNamed("groupId", element);
- Loan loan = null;
- if (group != null) {
- loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, group, loanRepaymentScheduleDetail, loanCharges);
- loan.setExternalId(command.getExternalId());
- }
-
- if (client != null) {
- loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail, loanCharges);
- loan.setExternalId(command.getExternalId());
- }
-
- if (loan == null) {
- // FIXME - kw - put in appropriate exception here.
- throw new RuntimeException();
- }
+ return assembleApplication(element, clientId, groupId);
+ }
- final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command.toCalculateLoanScheduleCommand());
-
- for (LoanSchedulePeriodData scheduledLoanInstallment : loanSchedule.getPeriods()) {
- if (scheduledLoanInstallment.isRepaymentPeriod()) {
-
- final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
- loan,
- scheduledLoanInstallment.periodNumber(),
- scheduledLoanInstallment.periodFromDate(),
- scheduledLoanInstallment.periodDueDate(),
- scheduledLoanInstallment.principalDue(),
- scheduledLoanInstallment.interestDue(),
- scheduledLoanInstallment.feeChargesDue(),
- scheduledLoanInstallment.penaltyChargesDue());
-
- loan.addRepaymentScheduleInstallment(installment);
- }
- }
+ private Loan assembleApplication(final JsonElement element, final Long clientId, final Long groupId) {
- loan.submitApplication(loanTermFrequency, loanTermFrequencyType,
- command.getSubmittedOnDate(), command.getExpectedDisbursementDate(),
- command.getRepaymentsStartingFromDate(), command.getInterestChargedFromDate(),
- defaultLoanLifecycleStateMachine());
-
- return loan;
- }
+ final Long productId = fromApiJsonHelper.extractLongNamed("productId", element);
+ final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element);
+ final Long loanOfficerId = fromApiJsonHelper.extractLongNamed("loanOfficerId", element);
+ final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
- public LoanProductRelatedDetail assembleLoanProductRelatedDetailFrom(final LoanApplicationCommand command, MonetaryCurrency currency) {
- final BigDecimal defaultNominalInterestRatePerPeriod = command.getInterestRatePerPeriod();
- final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyType());
-
- BigDecimal defaultAnnualNominalInterestRate = aprCalculator.calculateFrom(interestPeriodFrequencyType, command.getInterestRatePerPeriod());
-
- final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestType());
- final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodType());
-
-
- final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
- final Integer repayEvery = command.getRepaymentEvery();
- final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequencyType());
-
- final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationType());
-
- LoanProductRelatedDetail loanRepaymentScheduleDetail = new LoanProductRelatedDetail(currency,
- command.getPrincipal(), defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
- interestMethod, interestCalculationPeriodMethod,
- repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, command.getInArrearsTolerance());
- return loanRepaymentScheduleDetail;
- }
+ final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
+ if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
- private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
- List allowedLoanStatuses = Arrays.asList(LoanStatus.values());
- return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
- }
-
- public Fund findFundByIdIfProvided(final Long fundId) {
- Fund fund = null;
- if (fundId != null) {
- fund = this.fundRepository.findOne(fundId);
- if (fund == null) {
- throw new FundNotFoundException(fundId);
- }
- }
- return fund;
- }
-
- public Staff findLoanOfficerByIdIfProvided(final Long loanOfficerId) {
- Staff staff = null;
- if (loanOfficerId != null) {
- staff = this.staffRepository.findOne(loanOfficerId);
- if (staff == null) {
- throw new StaffNotFoundException(loanOfficerId);
- } else if (staff.isNotLoanOfficer()) {
- throw new StaffRoleException(loanOfficerId,
- StaffRoleException.STAFF_ROLE.LOAN_OFFICER);
- }
- }
- return staff;
- }
-
- public LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
- LoanTransactionProcessingStrategy strategy = null;
- if (transactionProcessingStrategyId != null) {
- strategy = this.loanTransactionProcessingStrategyRepository.findOne(transactionProcessingStrategyId);
- if (strategy == null) {
- throw new LoanTransactionProcessingStrategyNotFoundException(transactionProcessingStrategyId);
- }
- }
- return strategy;
- }
+ final Fund fund = findFundByIdIfProvided(fundId);
+ final Staff loanOfficer = findLoanOfficerByIdIfProvided(loanOfficerId);
+ final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
+
+ final Set loanCharges = this.loanChargeAssembler.fromParsedJson(element);
+
+ final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
+ LoanSchedule loanSchedule = this.loanScheduleAssembler.fromJson(element, inArrearsTolerance);
+
+ Loan loanApplication = null;
+ if (clientId != null) {
+ final Client client = this.clientRepository.findOne(clientId);
+ if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
+
+ loanApplication = Loan.newIndividualLoanApplication(client, loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy,
+ loanSchedule, loanCharges);
+ }
+
+ if (groupId != null) {
+ final Group group = this.groupRepository.findOne(groupId);
+ if (group == null || group.isDeleted()) { throw new GroupNotFoundException(groupId); }
+
+ loanApplication = Loan.newGroupLoanApplication(group, loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy,
+ loanSchedule, loanCharges);
+ }
+
+ final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element);
+ final LocalDate submittedOnDate = fromApiJsonHelper.extractLocalDateNamed("submittedOnDate", element);
+
+ if (loanApplication == null) { throw new IllegalStateException("No loan application exists for either a client or group (or both)."); }
+
+ loanApplication.loanApplicationSubmittal(loanSchedule, defaultLoanLifecycleStateMachine(), submittedOnDate, externalId);
+
+ return loanApplication;
+ }
+
+ private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
+ List allowedLoanStatuses = Arrays.asList(LoanStatus.values());
+ return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
+ }
+
+ public Fund findFundByIdIfProvided(final Long fundId) {
+ Fund fund = null;
+ if (fundId != null) {
+ fund = this.fundRepository.findOne(fundId);
+ if (fund == null) { throw new FundNotFoundException(fundId); }
+ }
+ return fund;
+ }
+
+ public Staff findLoanOfficerByIdIfProvided(final Long loanOfficerId) {
+ Staff staff = null;
+ if (loanOfficerId != null) {
+ staff = this.staffRepository.findOne(loanOfficerId);
+ if (staff == null) {
+ throw new StaffNotFoundException(loanOfficerId);
+ } else if (staff.isNotLoanOfficer()) { throw new StaffRoleException(loanOfficerId, StaffRoleException.STAFF_ROLE.LOAN_OFFICER); }
+ }
+ return staff;
+ }
+
+ public LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
+ LoanTransactionProcessingStrategy strategy = null;
+ if (transactionProcessingStrategyId != null) {
+ strategy = this.loanTransactionProcessingStrategyRepository.findOne(transactionProcessingStrategyId);
+ if (strategy == null) { throw new LoanTransactionProcessingStrategyNotFoundException(transactionProcessingStrategyId); }
+ }
+ return strategy;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java
index 8308c3730..eaa718934 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java
@@ -2,81 +2,99 @@ package org.mifosplatform.portfolio.loanaccount.service;
import java.math.BigDecimal;
import java.util.HashSet;
+import java.util.Locale;
import java.util.Set;
+import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.charge.domain.Charge;
+import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
import org.mifosplatform.portfolio.charge.domain.ChargeRepository;
+import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
import org.mifosplatform.portfolio.charge.exception.ChargeIsNotActiveException;
import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException;
import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException;
-import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
@Service
public class LoanChargeAssembler {
- private final ChargeRepository chargeRepository;
- private final LoanChargeRepository loanChargeRepository;
+ private final FromJsonHelper fromApiJsonHelper;
+ private final ChargeRepository chargeRepository;
+ private final LoanChargeRepository loanChargeRepository;
- @Autowired
- public LoanChargeAssembler(final ChargeRepository chargeRepository, final LoanChargeRepository loanChargeRepository) {
- this.chargeRepository = chargeRepository;
- this.loanChargeRepository = loanChargeRepository;
- }
-
- public Set assembleFrom(final LoanChargeCommand[] charges, final BigDecimal loanPrincipal) {
-
- final Set loanCharges = new HashSet();
-
- if (charges != null) {
- for (LoanChargeCommand loanChargeCommand : charges) {
- if (loanChargeCommand.getId() == null) {
- final Long chargeDefinitionId = loanChargeCommand.getChargeId();
- final Charge chargeDefinition = this.chargeRepository.findOne(chargeDefinitionId);
- if (chargeDefinition == null || chargeDefinition.isDeleted()) {
- throw new ChargeNotFoundException(chargeDefinitionId);
- }
-
- if (!chargeDefinition.isActive()) {
- throw new ChargeIsNotActiveException(chargeDefinitionId, chargeDefinition.getName());
- }
- final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, loanChargeCommand, loanPrincipal);
- loanCharges.add(loanCharge);
- } else {
- final Long loanChargeId = loanChargeCommand.getId();
- final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
- if (loanCharge == null) {
- throw new LoanChargeNotFoundException(loanChargeId);
- }
-
- loanCharge.update(loanChargeCommand, loanPrincipal);
-
- loanCharges.add(loanCharge);
- }
- }
- }
-
- return loanCharges;
- }
+ @Autowired
+ public LoanChargeAssembler(final FromJsonHelper fromApiJsonHelper, final ChargeRepository chargeRepository,
+ final LoanChargeRepository loanChargeRepository) {
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ this.chargeRepository = chargeRepository;
+ this.loanChargeRepository = loanChargeRepository;
+ }
- public Set assembleFrom(final LoanChargeCommand[] charges, final Set chargesInheritedFromProduct, final BigDecimal loanPrincipal) {
-
- Set loanCharges = new HashSet();
-
- if (charges != null) {
- loanCharges = assembleFrom(charges, loanPrincipal);
- }
- // TODO - kw - dont inherit charges from product by default, user may remove charges (that were default) during creation - expect
- // charges to be passed along in request at time of creation
-// else {
-// for (Charge productCharge : chargesInheritedFromProduct) {
-// loanCharges.add(LoanCharge.createNew(productCharge));
-// }
-// }
-
- return loanCharges;
- }
+ public Set fromParsedJson(final JsonElement element) {
+
+ final Set loanCharges = new HashSet();
+
+ final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
+
+ if (element.isJsonObject()) {
+ final JsonObject topLevelJsonElement = element.getAsJsonObject();
+ final String dateFormat = fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
+ final Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
+ if (topLevelJsonElement.has("charges") && topLevelJsonElement.get("charges").isJsonArray()) {
+ final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
+ for (int i = 0; i < array.size(); i++) {
+
+ final JsonObject loanChargeElement = array.get(i).getAsJsonObject();
+
+ final Long id = fromApiJsonHelper.extractLongNamed("id", loanChargeElement);
+ final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
+ final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed("amount", loanChargeElement, locale);
+ final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed("chargeTimeType", loanChargeElement, locale);
+ final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", loanChargeElement,
+ locale);
+ final LocalDate specifiedDueDate = fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", loanChargeElement,
+ dateFormat, locale);
+
+ if (id == null) {
+ final Charge chargeDefinition = this.chargeRepository.findOne(chargeId);
+ if (chargeDefinition == null || chargeDefinition.isDeleted()) { throw new ChargeNotFoundException(chargeId); }
+
+ if (!chargeDefinition.isActive()) { throw new ChargeIsNotActiveException(chargeId, chargeDefinition.getName()); }
+ ChargeTimeType chargeTime = null;
+ if (chargeTimeType != null) {
+ ChargeTimeType.fromInt(chargeTimeType);
+ }
+ ChargeCalculationType chargeCalculation = null;
+ if (chargeCalculationType != null) {
+ ChargeCalculationType.fromInt(chargeCalculationType);
+ }
+ final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
+ chargeCalculation, specifiedDueDate);
+ loanCharges.add(loanCharge);
+ } else {
+ final Long loanChargeId = id;
+ final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
+ if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); }
+
+ loanCharge.update(amount, specifiedDueDate, principal);
+
+ loanCharges.add(loanCharge);
+ }
+ }
+ } else {
+ // no charges passed, use existing ones against loan
+ }
+
+ }
+
+ return loanCharges;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanProductRelatedDetailAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanProductRelatedDetailAssembler.java
index 1c1b15a0e..6ba2b9909 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanProductRelatedDetailAssembler.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanProductRelatedDetailAssembler.java
@@ -3,8 +3,8 @@ package org.mifosplatform.portfolio.loanaccount.service;
import java.math.BigDecimal;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.command.CalculateLoanScheduleCommand;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.query.CalculateLoanScheduleQuery;
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
@@ -18,48 +18,45 @@ import org.springframework.stereotype.Service;
@Service
public class LoanProductRelatedDetailAssembler {
-
- private final LoanProductRepository loanProductRepository;
- private final AprCalculator aprCalculator;
- @Autowired
- public LoanProductRelatedDetailAssembler(
- final LoanProductRepository loanProductRepository,
- final AprCalculator aprCalculator) {
- this.loanProductRepository = loanProductRepository;
- this.aprCalculator = aprCalculator;
- }
+ private final LoanProductRepository loanProductRepository;
+ private final AprCalculator aprCalculator;
- public LoanProductRelatedDetail assembleFrom(final CalculateLoanScheduleCommand command) {
-
- final BigDecimal principalAmount = command.getPrincipal();
- final BigDecimal defaultNominalInterestRatePerPeriod = command.getInterestRatePerPeriod();
- final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyType());
- final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestType());
- final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodType());
-
- final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
- final Integer repayEvery = command.getRepaymentEvery();
- final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequencyType());
-
- final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationType());
-
- final BigDecimal defaultAnnualNominalInterestRate = this.aprCalculator.calculateFrom(interestPeriodFrequencyType, defaultNominalInterestRatePerPeriod);
-
- final LoanProduct loanProduct = this.loanProductRepository.findOne(command.getProductId());
- if (loanProduct == null) {
- throw new LoanProductNotFoundException(command.getProductId());
- }
-
- final MonetaryCurrency currency = loanProduct.getCurrency();
-
- // in arrerars tolerance isnt relevant when auto-calculating a loan schedule
- final BigDecimal inArrearsTolerance = BigDecimal.ZERO;
-
- return new LoanProductRelatedDetail(currency, principalAmount,
- defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
- interestMethod, interestCalculationPeriodMethod,
- repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod,
- inArrearsTolerance);
- }
+ @Autowired
+ public LoanProductRelatedDetailAssembler(final LoanProductRepository loanProductRepository, final AprCalculator aprCalculator) {
+ this.loanProductRepository = loanProductRepository;
+ this.aprCalculator = aprCalculator;
+ }
+
+ public LoanProductRelatedDetail assembleFrom(final CalculateLoanScheduleQuery command) {
+
+ final BigDecimal principalAmount = command.getPrincipal();
+ final BigDecimal defaultNominalInterestRatePerPeriod = command.getInterestRatePerPeriod();
+ final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyType());
+ final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestType());
+ final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command
+ .getInterestCalculationPeriodType());
+
+ final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
+ final Integer repayEvery = command.getRepaymentEvery();
+ final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequencyType());
+
+ final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationType());
+
+ final BigDecimal defaultAnnualNominalInterestRate = this.aprCalculator.calculateFrom(interestPeriodFrequencyType,
+ defaultNominalInterestRatePerPeriod);
+
+ final LoanProduct loanProduct = this.loanProductRepository.findOne(command.getProductId());
+ if (loanProduct == null) { throw new LoanProductNotFoundException(command.getProductId()); }
+
+ final MonetaryCurrency currency = loanProduct.getCurrency();
+
+ // in arrerars tolerance isnt relevant when auto-calculating a loan
+ // schedule
+ final BigDecimal inArrearsTolerance = BigDecimal.ZERO;
+
+ return new LoanProductRelatedDetail(currency, principalAmount, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
+ defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType,
+ defaultNumberOfInstallments, amortizationMethod, inArrearsTolerance);
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java
index d87dcbf0a..80d0c3d49 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java
@@ -1,73 +1,62 @@
package org.mifosplatform.portfolio.loanaccount.service;
+import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
import org.mifosplatform.organisation.staff.command.BulkTransferLoanOfficerCommand;
import org.mifosplatform.portfolio.loanaccount.command.AdjustLoanTransactionCommand;
-import org.mifosplatform.portfolio.loanaccount.command.LoanApplicationCommand;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
-import org.mifosplatform.portfolio.loanaccount.command.LoanStateTransitionCommand;
import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand;
-import org.mifosplatform.portfolio.loanaccount.command.UndoStateTransitionCommand;
import org.springframework.security.access.prepost.PreAuthorize;
public interface LoanWritePlatformService {
-
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CREATE_LOAN', 'CREATEHISTORIC_LOAN')")
- EntityIdentifier submitLoanApplication(LoanApplicationCommand command);
-
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'UPDATE_LOAN', 'UPDATEHISTORIC_LOAN')")
- EntityIdentifier modifyLoanApplication(LoanApplicationCommand command);
-
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'DELETE_LOAN')")
- EntityIdentifier deleteLoan(Long loanId);
-
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'APPROVE_LOAN', 'APPROVEINPAST_LOAN')")
- EntityIdentifier approveLoanApplication(LoanStateTransitionCommand command);
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'APPROVALUNDO_LOAN')")
- EntityIdentifier undoLoanApproval(UndoStateTransitionCommand command);
+ EntityIdentifier submitLoanApplication(JsonCommand command);
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'REJECT_LOAN', 'REJECTINPAST_LOAN')")
- EntityIdentifier rejectLoan(LoanStateTransitionCommand command);
+ EntityIdentifier modifyLoanApplication(Long loanId, JsonCommand command);
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'WITHDRAW_LOAN', 'WITHDRAWINPAST_LOAN')")
- EntityIdentifier withdrawLoan(LoanStateTransitionCommand command);
-
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'DISBURSE_LOAN', 'DISBURSEINPAST_LOAN')")
- EntityIdentifier disburseLoan(LoanStateTransitionCommand command);
+ EntityIdentifier deleteLoan(Long loanId);
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'DISBURSALUNDO_LOAN')")
- public EntityIdentifier undoLoanDisbursal(UndoStateTransitionCommand command);
-
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'REPAYMENT_LOAN', 'REPAYMENTINPAST_LOAN')")
- public EntityIdentifier makeLoanRepayment(LoanTransactionCommand command);
+ EntityIdentifier approveLoanApplication(Long loanId, JsonCommand command);
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'ADJUST_LOAN')")
- EntityIdentifier adjustLoanTransaction(AdjustLoanTransactionCommand command);
+ EntityIdentifier undoLoanApplicationApproval(Long loanId, JsonCommand command);
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'WAIVEINTERESTPORTION_LOAN')")
- EntityIdentifier waiveInterestOnLoan(LoanTransactionCommand command);
-
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'WRITEOFF_LOAN')")
- EntityIdentifier writeOff(LoanTransactionCommand command);
-
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CLOSE_LOAN')")
- EntityIdentifier closeLoan(LoanTransactionCommand command);
+ EntityIdentifier rejectLoanApplication(Long loanId, JsonCommand command);
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CLOSEASRESCHEDULED_LOAN')")
- EntityIdentifier closeAsRescheduled(LoanTransactionCommand command);
-
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CREATE_LOANCHARGE')")
+ EntityIdentifier applicantWithdrawsFromLoanApplication(Long loanId, JsonCommand command);
+
+ EntityIdentifier disburseLoan(Long loanId, JsonCommand command);
+
+ public EntityIdentifier undoLoanDisbursal(Long loanId, JsonCommand command);
+
+ @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'REPAYMENT_LOAN', 'REPAYMENTINPAST_LOAN')")
+ public EntityIdentifier makeLoanRepayment(LoanTransactionCommand command);
+
+ @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'ADJUST_LOAN')")
+ EntityIdentifier adjustLoanTransaction(AdjustLoanTransactionCommand command);
+
+ @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'WAIVEINTERESTPORTION_LOAN')")
+ EntityIdentifier waiveInterestOnLoan(LoanTransactionCommand command);
+
+ @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'WRITEOFF_LOAN')")
+ EntityIdentifier writeOff(LoanTransactionCommand command);
+
+ @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CLOSE_LOAN')")
+ EntityIdentifier closeLoan(LoanTransactionCommand command);
+
+ @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CLOSEASRESCHEDULED_LOAN')")
+ EntityIdentifier closeAsRescheduled(LoanTransactionCommand command);
+
+ @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CREATE_LOANCHARGE')")
EntityIdentifier addLoanCharge(LoanChargeCommand command);
- @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'UPDATE_LOANCHARGE')")
+ @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'UPDATE_LOANCHARGE')")
EntityIdentifier updateLoanCharge(LoanChargeCommand command);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'DELETE_LOANCHARGE')")
EntityIdentifier deleteLoanCharge(final Long loanId, final Long loanChargeId);
-
+
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'WAIVE_LOANCHARGE')")
- EntityIdentifier waiveLoanCharge(LoanChargeCommand command);
+ EntityIdentifier waiveLoanCharge(LoanChargeCommand command);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'BULKREASSIGN_LOAN')")
EntityIdentifier bulkLoanReassignment(final BulkTransferLoanOfficerCommand command);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 6525193c7..f2c2f5680 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -1,16 +1,20 @@
package org.mifosplatform.portfolio.loanaccount.service;
-import java.math.BigDecimal;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.api.JsonCommand;
+import org.mifosplatform.infrastructure.core.api.JsonQuery;
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
+import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.security.exception.NoAuthorizationException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
+import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
+import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepository;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.organisation.staff.command.BulkTransferLoanOfficerCommand;
@@ -30,20 +34,16 @@ import org.mifosplatform.portfolio.fund.domain.Fund;
import org.mifosplatform.portfolio.loanaccount.command.AdjustLoanTransactionCommand;
import org.mifosplatform.portfolio.loanaccount.command.AdjustLoanTransactionCommandValidator;
import org.mifosplatform.portfolio.loanaccount.command.LoanApplicationCommand;
-import org.mifosplatform.portfolio.loanaccount.command.LoanApplicationCommandValidator;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommandValidator;
import org.mifosplatform.portfolio.loanaccount.command.LoanStateTransitionCommand;
-import org.mifosplatform.portfolio.loanaccount.command.LoanStateTransitionCommandValidator;
import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand;
import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommandValidator;
-import org.mifosplatform.portfolio.loanaccount.command.UndoStateTransitionCommand;
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
-import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
@@ -52,570 +52,542 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.command.CalculateLoanScheduleCommand;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.service.CalculationPlatformService;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.query.CalculateLoanScheduleQuery;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
+import org.mifosplatform.portfolio.loanaccount.serialization.CalculateLoanScheduleQueryFromApiJsonDeserializer;
+import org.mifosplatform.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonDeserializer;
+import org.mifosplatform.portfolio.loanaccount.serialization.LoanStateTransitionCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
-import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import com.google.gson.JsonElement;
+
@Service
public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
- private final PlatformSecurityContext context;
- private final LoanRepository loanRepository;
- private final NoteRepository noteRepository;
- private final CalculationPlatformService calculationPlatformService;
- private final LoanTransactionRepository loanTransactionRepository;
- private final LoanAssembler loanAssembler;
- private final ClientRepository clientRepository;
- private final LoanProductRepository loanProductRepository;
+ private final PlatformSecurityContext context;
+ private final FromJsonHelper fromJsonHelper;
+ private final LoanApplicationCommandFromApiJsonDeserializer fromApiJsonDeserializer;
+ private final CalculateLoanScheduleQueryFromApiJsonDeserializer calculateLoanScheduleQueryFromApiJsonDeserializer;
+ private final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer;
+ private final LoanRepository loanRepository;
+ private final NoteRepository noteRepository;
+ private final LoanScheduleCalculationPlatformService calculationPlatformService;
+ private final LoanTransactionRepository loanTransactionRepository;
+ private final LoanAssembler loanAssembler;
+ private final ClientRepository clientRepository;
+ private final LoanProductRepository loanProductRepository;
private final ChargeRepository chargeRepository;
private final LoanChargeRepository loanChargeRepository;
- private final LoanChargeAssembler loanChargeAssembler;
+ private final LoanChargeAssembler loanChargeAssembler;
+ private final AprCalculator aprCalculator;
+ private final ApplicationCurrencyRepository applicationCurrencyRepository;
- @Autowired
- public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
- final LoanAssembler loanAssembler, final LoanChargeAssembler loanChargeAssembler,
- final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository,
- final NoteRepository noteRepository, final CalculationPlatformService calculationPlatformService,
- final ClientRepository clientRepository, final LoanProductRepository loanProductRepository,
- final ChargeRepository chargeRepository, final LoanChargeRepository loanChargeRepository) {
- this.context = context;
- this.loanAssembler = loanAssembler;
- this.loanChargeAssembler = loanChargeAssembler;
- this.loanRepository = loanRepository;
- this.loanTransactionRepository = loanTransactionRepository;
- this.noteRepository = noteRepository;
- this.calculationPlatformService = calculationPlatformService;
- this.clientRepository = clientRepository;
- this.loanProductRepository = loanProductRepository;
+ @Autowired
+ public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
+ final LoanApplicationCommandFromApiJsonDeserializer fromApiJsonDeserializer,
+ final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer,
+ final CalculateLoanScheduleQueryFromApiJsonDeserializer calculateLoanScheduleQueryFromApiJsonDeserializer,
+ final AprCalculator aprCalculator, final LoanAssembler loanAssembler, final LoanChargeAssembler loanChargeAssembler,
+ final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository,
+ final NoteRepository noteRepository, final LoanScheduleCalculationPlatformService calculationPlatformService,
+ final ClientRepository clientRepository, final LoanProductRepository loanProductRepository,
+ final ChargeRepository chargeRepository, final LoanChargeRepository loanChargeRepository,
+ final ApplicationCurrencyRepository applicationCurrencyRepository) {
+ this.context = context;
+ this.fromJsonHelper = fromJsonHelper;
+ this.fromApiJsonDeserializer = fromApiJsonDeserializer;
+ this.loanStateTransitionCommandFromApiJsonDeserializer = loanStateTransitionCommandFromApiJsonDeserializer;
+ this.calculateLoanScheduleQueryFromApiJsonDeserializer = calculateLoanScheduleQueryFromApiJsonDeserializer;
+ this.aprCalculator = aprCalculator;
+ this.loanAssembler = loanAssembler;
+ this.loanChargeAssembler = loanChargeAssembler;
+ this.loanRepository = loanRepository;
+ this.loanTransactionRepository = loanTransactionRepository;
+ this.noteRepository = noteRepository;
+ this.calculationPlatformService = calculationPlatformService;
+ this.clientRepository = clientRepository;
+ this.loanProductRepository = loanProductRepository;
this.chargeRepository = chargeRepository;
this.loanChargeRepository = loanChargeRepository;
- }
-
- private boolean isBeforeToday(final LocalDate date) {
- return date.isBefore(new LocalDate());
- }
-
- private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
- List allowedLoanStatuses = Arrays.asList(LoanStatus.values());
- return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
- }
-
- @Transactional
- @Override
- public EntityIdentifier submitLoanApplication(final LoanApplicationCommand command) {
-
- AppUser currentUser = context.authenticatedUser();
-
- LoanApplicationCommandValidator validator = new LoanApplicationCommandValidator(command);
- validator.validate();
-
- LocalDate submittedOn = command.getSubmittedOnDate();
- if (this.isBeforeToday(submittedOn) && currentUser.hasNotPermissionForAnyOf("ALL_FUNCTIONS", "CREATEHISTORIC_LOAN", "PORTFOLIO_MANAGEMENT_SUPER_USER")) {
- throw new NoAuthorizationException("Cannot add backdated loan.");
- }
-
- Loan loan = loanAssembler.assembleFrom(command);
-
- this.loanRepository.save(loan);
-
- if (StringUtils.isNotBlank(command.getSubmittedOnNote())) {
- Note note = Note.loanNote(loan, command.getSubmittedOnNote());
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier modifyLoanApplication(final LoanApplicationCommand command) {
- AppUser currentUser = context.authenticatedUser();
-
- LoanApplicationCommandValidator validator = new LoanApplicationCommandValidator(command);
- validator.validate();
-
- final LocalDate submittedOn = command.getSubmittedOnDate();
- if (this.isBeforeToday(submittedOn) && currentUser.hasNotPermissionForAnyOf("ALL_FUNCTIONS", "UPDATEHISTORIC_LOAN", "PORTFOLIO_MANAGEMENT_SUPER_USER")) {
- throw new NoAuthorizationException("Cannot modify backdated loan.");
- }
-
- final Loan loan = retrieveLoanBy(command.getLoanId());
-
- final LoanProduct loanProduct = this.loanProductRepository.findOne(command.getProductId());
- if (loanProduct == null) {
- throw new LoanProductNotFoundException(command.getProductId());
- }
-
- final Client client = this.clientRepository.findOne(command.getClientId());
- if (client == null || client.isDeleted()) {
- throw new ClientNotFoundException(command.getClientId());
- }
-
- final Fund fund = this.loanAssembler.findFundByIdIfProvided(command.getFundId());
- final LoanTransactionProcessingStrategy strategy = this.loanAssembler.findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
- final Set charges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanProduct.getCharges(), loan.getPrincpal().getAmount());
-
- final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command.toCalculateLoanScheduleCommand());
-
- // FIXME - kw - restrict ability to modify loan to modifying the 'loan application' stage - once it has been disbursed, should be allowed to use this feature
- // - other facilities are in place such as 'undo disbursal'
- loan.modifyLoanApplication(command, client, loanProduct, fund, strategy, loanSchedule, charges, defaultLoanLifecycleStateMachine());
-
- this.loanRepository.save(loan);
-
- if (StringUtils.isNotBlank(command.getSubmittedOnNote())) {
- Note note = Note.loanNote(loan, command.getSubmittedOnNote());
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier deleteLoan(final Long loanId) {
-
- context.authenticatedUser();
-
- final Loan loan = retrieveLoanBy(loanId);
-
- if (loan.isNotSubmittedAndPendingApproval()) {
- throw new LoanNotInSubmittedAndPendingApprovalStateCannotBeDeleted(loanId);
- }
-
- List relatedNotes = this.noteRepository.findByLoanId(loan.getId());
- this.noteRepository.deleteInBatch(relatedNotes);
-
- this.loanRepository.delete(loanId);
-
- return new EntityIdentifier(loanId);
- }
-
- @Transactional
- @Override
- public EntityIdentifier approveLoanApplication(final LoanStateTransitionCommand command) {
-
- AppUser currentUser = context.authenticatedUser();
-
- LoanStateTransitionCommandValidator validator = new LoanStateTransitionCommandValidator(command);
- validator.validate();
-
- final Loan loan = retrieveLoanBy(command.getLoanId());
-
- LocalDate eventDate = command.getEventDate();
- if (this.isBeforeToday(eventDate) && currentUser.canNotApproveLoanInPast()) {
- throw new NoAuthorizationException("User has no authority to approve loan with a date in the past.");
- }
-
- loan.approve(eventDate, defaultLoanLifecycleStateMachine());
- this.loanRepository.save(loan);
-
- String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- Note note = Note.loanNote(loan, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier undoLoanApproval(final UndoStateTransitionCommand command) {
-
- context.authenticatedUser();
-
- final Loan loan = retrieveLoanBy(command.getLoanId());
-
- loan.undoApproval(defaultLoanLifecycleStateMachine());
- this.loanRepository.save(loan);
-
- String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- Note note = Note.loanNote(loan, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier rejectLoan(final LoanStateTransitionCommand command) {
-
- AppUser currentUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
-
- LoanStateTransitionCommandValidator validator = new LoanStateTransitionCommandValidator(command);
- validator.validate();
-
- final Loan loan = retrieveLoanBy(command.getLoanId());
-
- LocalDate eventDate = command.getEventDate();
- if (this.isBeforeToday(eventDate) && currentUser.canNotRejectLoanInPast()) {
- throw new NoAuthorizationException("User has no authority to reject loan with a date in the past.");
- }
-
- loan.reject(eventDate, defaultLoanLifecycleStateMachine());
- this.loanRepository.save(loan);
-
- String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- Note note = Note.loanNote(loan, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier withdrawLoan(final LoanStateTransitionCommand command) {
-
- AppUser currentUser = context.authenticatedUser();
-
- LoanStateTransitionCommandValidator validator = new LoanStateTransitionCommandValidator(command);
- validator.validate();
-
- final Loan loan = retrieveLoanBy(command.getLoanId());
-
- LocalDate eventDate = command.getEventDate();
- if (this.isBeforeToday(eventDate) && currentUser.canNotWithdrawByClientLoanInPast()) {
- throw new NoAuthorizationException("User has no authority to mark loan as withdrawn by client with a date in the past.");
- }
-
- loan.withdraw(eventDate, defaultLoanLifecycleStateMachine());
- this.loanRepository.save(loan);
-
- String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- Note note = Note.loanNote(loan, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier disburseLoan(final LoanStateTransitionCommand command) {
-
- AppUser currentUser = context.authenticatedUser();
-
- LoanStateTransitionCommandValidator validator = new LoanStateTransitionCommandValidator(command);
- validator.validate();
-
- final Loan loan = retrieveLoanBy(command.getLoanId());
-
- String noteText = command.getNote();
- LocalDate actualDisbursementDate = command.getEventDate();
- if (this.isBeforeToday(actualDisbursementDate) && currentUser.canNotDisburseLoanInPast()) {
- throw new NoAuthorizationException("User has no authority to disburse loan with a date in the past.");
- }
-
- if (loan.isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) {
-
- LocalDate repaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate();
- LocalDate interestCalculatedFromDate = loan.getInterestChargedFromDate();
-
- BigDecimal principalAsDecimal = loan.repaymentScheduleDetail().getPrincipal().getAmount();
- BigDecimal interestRatePerYear = loan.repaymentScheduleDetail().getAnnualNominalInterestRate();
- Integer numberOfInstallments = loan.repaymentScheduleDetail().getNumberOfRepayments();
-
- Integer repaidEvery = loan.repaymentScheduleDetail().getRepayEvery();
- Integer selectedRepaymentFrequency = loan.repaymentScheduleDetail().getRepaymentPeriodFrequencyType().getValue();
- Integer selectedAmortizationMethod = loan.repaymentScheduleDetail().getAmortizationMethod().getValue();
-
- // FIXME - KW - use values from loan table here instead of inferring.
- Integer loanTermFrequency = repaidEvery * numberOfInstallments;
- Integer loanTermFrequencyType = selectedRepaymentFrequency;
-
- // use annual percentage rate to re-calculate loan schedule for late disbursement
- BigDecimal interestRatePerPeriod = interestRatePerYear;
- Integer interestRateFrequencyMethod = PeriodFrequencyType.YEARS.getValue();
-
- Integer interestMethod = loan.repaymentScheduleDetail().getInterestMethod().getValue();
- Integer interestCalculationInPeriod = loan.repaymentScheduleDetail().getInterestCalculationPeriodMethod().getValue();
-
- Set loanCharges = loan.getCharges();
- List commands = new ArrayList();
- for (LoanCharge loanCharge : loanCharges) {
- commands.add(loanCharge.toCommand());
- }
-
- LoanChargeCommand[] loanChargeCommands = commands.toArray(new LoanChargeCommand[commands.size()]);
-
- CalculateLoanScheduleCommand calculateCommand = new CalculateLoanScheduleCommand(
- loan.loanProduct().getId(),
- principalAsDecimal,
- interestRatePerPeriod, interestRateFrequencyMethod, interestMethod, interestCalculationInPeriod,
- repaidEvery, selectedRepaymentFrequency, numberOfInstallments, selectedAmortizationMethod,
- loanTermFrequency, loanTermFrequencyType,
- actualDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate, loanChargeCommands);
-
- LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateCommand);
-
- List modifiedLoanRepaymentSchedule = new ArrayList();
-
- for (LoanSchedulePeriodData scheduledLoanPeriod : loanSchedule.getPeriods()) {
- if (scheduledLoanPeriod.isRepaymentPeriod()) {
- LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
- loan,
- scheduledLoanPeriod.periodNumber(),
- scheduledLoanPeriod.periodFromDate(),
- scheduledLoanPeriod.periodDueDate(),
- scheduledLoanPeriod.principalDue(),
- scheduledLoanPeriod.interestDue(),
- scheduledLoanPeriod.feeChargesDue(),
- scheduledLoanPeriod.penaltyChargesDue());
-
- modifiedLoanRepaymentSchedule.add(installment);
- }
- }
- loan.disburseWithModifiedRepaymentSchedule(actualDisbursementDate, modifiedLoanRepaymentSchedule, defaultLoanLifecycleStateMachine());
- } else {
- loan.disburse(actualDisbursementDate, defaultLoanLifecycleStateMachine(), true);
- }
-
- this.loanRepository.save(loan);
-
- if (StringUtils.isNotBlank(noteText)) {
- Note note = Note.loanNote(loan, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier undoLoanDisbursal(final UndoStateTransitionCommand command) {
-
- context.authenticatedUser();
-
- Loan loan = this.loanRepository.findOne(command.getLoanId());
- if (loan == null) {
- throw new LoanNotFoundException(command.getLoanId());
- }
-
- loan.undoDisbursal(defaultLoanLifecycleStateMachine());
-
- this.loanRepository.save(loan);
-
- String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- Note note = Note.loanNote(loan, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier makeLoanRepayment(final LoanTransactionCommand command) {
-
- AppUser currentUser = context.authenticatedUser();
-
- LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
- validator.validate();
-
- Loan loan = this.loanRepository.findOne(command.getLoanId());
- if (loan == null) {
- throw new LoanNotFoundException(command.getLoanId());
- }
-
- LocalDate transactionDate = command.getTransactionDate();
- if (this.isBeforeToday(transactionDate) && currentUser.canNotMakeRepaymentOnLoanInPast()) {
- throw new NoAuthorizationException("error.msg.no.permission.to.make.repayment.on.loan.in.past");
- }
-
- Money repayment = Money.of(loan.repaymentScheduleDetail()
- .getPrincipal().getCurrency(),
- command.getTransactionAmount());
-
- LoanTransaction loanRepayment = LoanTransaction.repayment(repayment, transactionDate);
- loan.makeRepayment(loanRepayment, defaultLoanLifecycleStateMachine());
- this.loanTransactionRepository.save(loanRepayment);
- this.loanRepository.save(loan);
-
- String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- Note note = Note.loanTransactionNote(loan, loanRepayment, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier adjustLoanTransaction(final AdjustLoanTransactionCommand command) {
-
- context.authenticatedUser();
-
- AdjustLoanTransactionCommandValidator validator = new AdjustLoanTransactionCommandValidator(command);
- validator.validate();
-
- Loan loan = this.loanRepository.findOne(command.getLoanId());
- if (loan == null) {
- throw new LoanNotFoundException(command.getLoanId());
- }
-
- LoanTransaction transactionToAdjust = this.loanTransactionRepository.findOne(command.getTransactionId());
- if (transactionToAdjust == null) {
- throw new LoanTransactionNotFoundException(command.getTransactionId());
- }
-
- final MonetaryCurrency currency = loan.repaymentScheduleDetail().getPrincipal().getCurrency();
- final Money transactionAmount = Money.of(currency, command.getTransactionAmount());
-
- // adjustment is only supported for repayments and waivers at present
- LocalDate transactionDate = command.getTransactionDate();
- LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmount, transactionDate);
- if (transactionToAdjust.isInterestWaiver()) {
- newTransactionDetail = LoanTransaction.waiver(loan, transactionAmount, transactionDate);
- }
-
- loan.adjustExistingTransaction(transactionToAdjust, newTransactionDetail, defaultLoanLifecycleStateMachine());
-
- if (newTransactionDetail.isGreaterThanZero(currency)) {
- this.loanTransactionRepository.save(newTransactionDetail);
- }
-
- this.loanRepository.save(loan);
-
- String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- Note note = Note.loanTransactionNote(loan, newTransactionDetail, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier waiveInterestOnLoan(final LoanTransactionCommand command) {
-
- context.authenticatedUser();
-
- final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
- validator.validate();
-
- final Loan loan = this.loanRepository.findOne(command.getLoanId());
- if (loan == null) {
- throw new LoanNotFoundException(command.getLoanId());
- }
-
- final LoanTransaction waiveTransaction = loan.waiveInterest(command.getTransactionAmount(), command.getTransactionDate(), defaultLoanLifecycleStateMachine());
-
- this.loanTransactionRepository.save(waiveTransaction);
- this.loanRepository.save(loan);
-
- final String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- final Note note = Note.loanTransactionNote(loan, waiveTransaction, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier writeOff(final LoanTransactionCommand command) {
- context.authenticatedUser();
-
- final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
- validator.validateNonMonetaryTransaction();
-
- final Loan loan = this.loanRepository.findOne(command.getLoanId());
- if (loan == null) {
- throw new LoanNotFoundException(command.getLoanId());
- }
-
- final LoanTransaction writeoff = loan.closeAsWrittenOff(command.getTransactionDate(), defaultLoanLifecycleStateMachine());
-
- this.loanTransactionRepository.save(writeoff);
- this.loanRepository.save(loan);
-
- final String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- final Note note = Note.loanTransactionNote(loan, writeoff, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier closeLoan(final LoanTransactionCommand command) {
-
- context.authenticatedUser();
-
- final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
- validator.validateNonMonetaryTransaction();
-
- final Loan loan = this.loanRepository.findOne(command.getLoanId());
- if (loan == null) {
- throw new LoanNotFoundException(command.getLoanId());
- }
-
- final LoanTransaction possibleClosingTransaction = loan.close(command.getTransactionDate(), defaultLoanLifecycleStateMachine());
- if (possibleClosingTransaction != null) {
- this.loanTransactionRepository.save(possibleClosingTransaction);
- }
- this.loanRepository.save(loan);
-
- final String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- final Note note = Note.loanNote(loan, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
- @Override
- public EntityIdentifier closeAsRescheduled(final LoanTransactionCommand command) {
- context.authenticatedUser();
-
- final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
- validator.validateNonMonetaryTransaction();
-
- final Loan loan = this.loanRepository.findOne(command.getLoanId());
- if (loan == null) {
- throw new LoanNotFoundException(command.getLoanId());
- }
-
- loan.closeAsMarkedForReschedule(command.getTransactionDate(), defaultLoanLifecycleStateMachine());
-
- this.loanRepository.save(loan);
-
- final String noteText = command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- final Note note = Note.loanNote(loan, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loan.getId());
- }
-
- @Transactional
+ this.applicationCurrencyRepository = applicationCurrencyRepository;
+ }
+
+ private boolean isBeforeToday(final LocalDate date) {
+ return date.isBefore(new LocalDate());
+ }
+
+ private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
+ List allowedLoanStatuses = Arrays.asList(LoanStatus.values());
+ return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier submitLoanApplication(final JsonCommand command) {
+
+ context.authenticatedUser();
+
+ LoanApplicationCommand loanApplicationCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
+ loanApplicationCommand.validate();
+
+ CalculateLoanScheduleQuery calculateLoanScheduleQuery = this.calculateLoanScheduleQueryFromApiJsonDeserializer
+ .commandFromApiJson(command.json());
+ calculateLoanScheduleQuery.validate();
+
+ final Loan newLoanApplication = loanAssembler.assembleFrom(command);
+
+ this.loanRepository.save(newLoanApplication);
+
+ final String submittedOnNote = command.stringValueOfParameterNamed("submittedOnNote");
+ if (StringUtils.isNotBlank(submittedOnNote)) {
+ Note note = Note.loanNote(newLoanApplication, submittedOnNote);
+ this.noteRepository.save(note);
+ }
+
+ return EntityIdentifier.resourceResult(newLoanApplication.getId(), command.commandId());
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier modifyLoanApplication(final Long loanId, final JsonCommand command) {
+
+ context.authenticatedUser();
+
+ LoanApplicationCommand loanApplicationCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
+ loanApplicationCommand.validate();
+
+ CalculateLoanScheduleQuery calculateLoanScheduleQuery = this.calculateLoanScheduleQueryFromApiJsonDeserializer
+ .commandFromApiJson(command.json());
+ calculateLoanScheduleQuery.validate();
+
+ final Loan existingLoanApplication = retrieveLoanBy(loanId);
+
+ final Map changes = existingLoanApplication.modifyLoanApplication(command, loanApplicationCommand.getCharges(),
+ this.aprCalculator);
+
+ final String clientIdParamName = "clientId";
+ if (changes.containsKey(clientIdParamName)) {
+ final Long clientId = command.longValueOfParameterNamed(clientIdParamName);
+ final Client client = this.clientRepository.findOne(clientId);
+ if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
+
+ existingLoanApplication.updateClient(client);
+ }
+
+ final String productIdParamName = "productId";
+ if (changes.containsKey(productIdParamName)) {
+ final Long productId = command.longValueOfParameterNamed(productIdParamName);
+ final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
+ if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
+
+ existingLoanApplication.updateLoanProduct(loanProduct);
+ }
+
+ final String fundIdParamName = "fundId";
+ if (changes.containsKey(fundIdParamName)) {
+ final Long fundId = command.longValueOfParameterNamed(fundIdParamName);
+ final Fund fund = this.loanAssembler.findFundByIdIfProvided(fundId);
+
+ existingLoanApplication.updateFund(fund);
+ }
+
+ final String strategyIdParamName = "transactionProcessingStrategyId";
+ if (changes.containsKey(strategyIdParamName)) {
+ final Long strategyId = command.longValueOfParameterNamed(strategyIdParamName);
+ final LoanTransactionProcessingStrategy strategy = this.loanAssembler.findStrategyByIdIfProvided(strategyId);
+
+ existingLoanApplication.updateTransactionProcessingStrategy(strategy);
+ }
+
+ final String chargesParamName = "charges";
+ if (changes.containsKey(chargesParamName)) {
+ final Set loanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson());
+ existingLoanApplication.updateLoanCharges(loanCharges);
+ }
+
+ if (changes.containsKey("recalculateLoanSchedule")) {
+ changes.remove("recalculateLoanSchedule");
+
+ final JsonElement parsedQuery = this.fromJsonHelper.parse(command.json());
+ final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromJsonHelper);
+
+ final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query);
+ existingLoanApplication.updateLoanSchedule(loanSchedule);
+ existingLoanApplication.updateLoanScheduleDependentDerivedFields();
+ }
+
+ this.loanRepository.save(existingLoanApplication);
+
+ final String submittedOnNote = command.stringValueOfParameterNamed("submittedOnNote");
+ if (StringUtils.isNotBlank(submittedOnNote)) {
+ Note note = Note.loanNote(existingLoanApplication, submittedOnNote);
+ this.noteRepository.save(note);
+ }
+
+ return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier deleteLoan(final Long loanId) {
+
+ context.authenticatedUser();
+
+ final Loan loan = retrieveLoanBy(loanId);
+
+ if (loan.isNotSubmittedAndPendingApproval()) { throw new LoanNotInSubmittedAndPendingApprovalStateCannotBeDeleted(loanId); }
+
+ List relatedNotes = this.noteRepository.findByLoanId(loan.getId());
+ this.noteRepository.deleteInBatch(relatedNotes);
+
+ this.loanRepository.delete(loanId);
+
+ return new EntityIdentifier(loanId);
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier approveLoanApplication(final Long loanId, final JsonCommand command) {
+
+ AppUser currentUser = context.authenticatedUser();
+
+ final LoanStateTransitionCommand approveLoanApplication = this.loanStateTransitionCommandFromApiJsonDeserializer
+ .commandFromApiJson(command.json());
+ approveLoanApplication.validate();
+
+ final Loan loan = retrieveLoanBy(loanId);
+
+ final LocalDate approvedOnLocalDate = approveLoanApplication.getApprovedOnDate();
+ if (this.isBeforeToday(approvedOnLocalDate) && currentUser.canNotApproveLoanInPast()) { throw new NoAuthorizationException(
+ "User has no authority to approve loan with a date in the past."); }
+
+ final Map changes = loan.loanApplicationApproval(command, defaultLoanLifecycleStateMachine());
+ this.loanRepository.save(loan);
+
+ final String noteText = command.stringValueOfParameterNamed("note");
+ if (StringUtils.isNotBlank(noteText)) {
+ Note note = Note.loanNote(loan, noteText);
+ changes.put("note", noteText);
+ this.noteRepository.save(note);
+ }
+
+ return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier undoLoanApplicationApproval(final Long loanId, final JsonCommand command) {
+
+ context.authenticatedUser();
+
+ final Loan loan = retrieveLoanBy(loanId);
+
+ final Map changes = loan.undoApproval(defaultLoanLifecycleStateMachine());
+ this.loanRepository.save(loan);
+
+ String noteText = command.stringValueOfParameterNamed("note");
+ if (StringUtils.isNotBlank(noteText)) {
+ Note note = Note.loanNote(loan, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier rejectLoanApplication(final Long loanId, final JsonCommand command) {
+
+ final AppUser currentUser = context.authenticatedUser();
+
+ final LoanStateTransitionCommand rejectLoanApplication = this.loanStateTransitionCommandFromApiJsonDeserializer
+ .commandFromApiJson(command.json());
+ rejectLoanApplication.validate();
+
+ final Loan loan = retrieveLoanBy(loanId);
+
+ final LocalDate eventDate = rejectLoanApplication.getRejectedOnDate();
+ if (this.isBeforeToday(eventDate) && currentUser.canNotRejectLoanInPast()) { throw new NoAuthorizationException(
+ "User has no authority to reject loan with a date in the past."); }
+
+ final Map changes = loan.loanApplicationRejection(command, defaultLoanLifecycleStateMachine());
+ this.loanRepository.save(loan);
+
+ String noteText = command.stringValueOfParameterNamed("note");
+ if (StringUtils.isNotBlank(noteText)) {
+ Note note = Note.loanNote(loan, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier applicantWithdrawsFromLoanApplication(final Long loanId, final JsonCommand command) {
+
+ final AppUser currentUser = context.authenticatedUser();
+
+ final LoanStateTransitionCommand applicantWithdrawsFromLoanApplication = this.loanStateTransitionCommandFromApiJsonDeserializer
+ .commandFromApiJson(command.json());
+ applicantWithdrawsFromLoanApplication.validate();
+
+ final Loan loan = retrieveLoanBy(loanId);
+
+ final LocalDate eventDate = applicantWithdrawsFromLoanApplication.getWithdrawnOnDate();
+ if (this.isBeforeToday(eventDate) && currentUser.canNotWithdrawByClientLoanInPast()) { throw new NoAuthorizationException(
+ "User has no authority to mark loan as withdrawn by applicant with a date in the past."); }
+
+ final Map changes = loan.loanApplicationWithdrawnByApplicant(command, defaultLoanLifecycleStateMachine());
+ this.loanRepository.save(loan);
+
+ String noteText = command.stringValueOfParameterNamed("note");
+ if (StringUtils.isNotBlank(noteText)) {
+ Note note = Note.loanNote(loan, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier disburseLoan(final Long loanId, final JsonCommand command) {
+
+ final AppUser currentUser = context.authenticatedUser();
+
+ final LoanStateTransitionCommand disburseLoan = this.loanStateTransitionCommandFromApiJsonDeserializer.commandFromApiJson(command
+ .json());
+ disburseLoan.validate();
+
+ final Loan loan = retrieveLoanBy(loanId);
+
+ final String noteText = command.stringValueOfParameterNamed("note");
+ final LocalDate actualDisbursementDate = disburseLoan.getDisbursedOnDate();
+ if (this.isBeforeToday(actualDisbursementDate) && currentUser.canNotDisburseLoanInPast()) { throw new NoAuthorizationException(
+ "User has no authority to disburse loan with a date in the past."); }
+
+ final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getPrincpal().getCurrencyCode());
+
+ final Map changes = loan.disburse(command, defaultLoanLifecycleStateMachine(), currency);
+ this.loanRepository.save(loan);
+
+ if (StringUtils.isNotBlank(noteText)) {
+ Note note = Note.loanNote(loan, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier undoLoanDisbursal(final Long loanId, final JsonCommand command) {
+
+ context.authenticatedUser();
+
+ final Loan loan = retrieveLoanBy(loanId);
+
+ final Map changes = loan.undoDisbursal(defaultLoanLifecycleStateMachine());
+ this.loanRepository.save(loan);
+
+ final String noteText = command.stringValueOfParameterNamed("note");
+ if (StringUtils.isNotBlank(noteText)) {
+ Note note = Note.loanNote(loan, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return EntityIdentifier.resourceResult(loanId, command.commandId(), changes);
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier makeLoanRepayment(final LoanTransactionCommand command) {
+
+ AppUser currentUser = context.authenticatedUser();
+
+ LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
+ validator.validate();
+
+ Loan loan = this.loanRepository.findOne(command.getLoanId());
+ if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); }
+
+ LocalDate transactionDate = command.getTransactionDate();
+ if (this.isBeforeToday(transactionDate) && currentUser.canNotMakeRepaymentOnLoanInPast()) { throw new NoAuthorizationException(
+ "error.msg.no.permission.to.make.repayment.on.loan.in.past"); }
+
+ Money repayment = Money.of(loan.repaymentScheduleDetail().getPrincipal().getCurrency(), command.getTransactionAmount());
+
+ LoanTransaction loanRepayment = LoanTransaction.repayment(repayment, transactionDate);
+ loan.makeRepayment(loanRepayment, defaultLoanLifecycleStateMachine());
+ this.loanTransactionRepository.save(loanRepayment);
+ this.loanRepository.save(loan);
+
+ String noteText = command.getNote();
+ if (StringUtils.isNotBlank(noteText)) {
+ Note note = Note.loanTransactionNote(loan, loanRepayment, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return new EntityIdentifier(loan.getId());
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier adjustLoanTransaction(final AdjustLoanTransactionCommand command) {
+
+ context.authenticatedUser();
+
+ AdjustLoanTransactionCommandValidator validator = new AdjustLoanTransactionCommandValidator(command);
+ validator.validate();
+
+ Loan loan = this.loanRepository.findOne(command.getLoanId());
+ if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); }
+
+ LoanTransaction transactionToAdjust = this.loanTransactionRepository.findOne(command.getTransactionId());
+ if (transactionToAdjust == null) { throw new LoanTransactionNotFoundException(command.getTransactionId()); }
+
+ final MonetaryCurrency currency = loan.repaymentScheduleDetail().getPrincipal().getCurrency();
+ final Money transactionAmount = Money.of(currency, command.getTransactionAmount());
+
+ // adjustment is only supported for repayments and waivers at present
+ LocalDate transactionDate = command.getTransactionDate();
+ LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmount, transactionDate);
+ if (transactionToAdjust.isInterestWaiver()) {
+ newTransactionDetail = LoanTransaction.waiver(loan, transactionAmount, transactionDate);
+ }
+
+ loan.adjustExistingTransaction(transactionToAdjust, newTransactionDetail, defaultLoanLifecycleStateMachine());
+
+ if (newTransactionDetail.isGreaterThanZero(currency)) {
+ this.loanTransactionRepository.save(newTransactionDetail);
+ }
+
+ this.loanRepository.save(loan);
+
+ String noteText = command.getNote();
+ if (StringUtils.isNotBlank(noteText)) {
+ Note note = Note.loanTransactionNote(loan, newTransactionDetail, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return new EntityIdentifier(loan.getId());
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier waiveInterestOnLoan(final LoanTransactionCommand command) {
+
+ context.authenticatedUser();
+
+ final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
+ validator.validate();
+
+ final Loan loan = this.loanRepository.findOne(command.getLoanId());
+ if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); }
+
+ final LoanTransaction waiveTransaction = loan.waiveInterest(command.getTransactionAmount(), command.getTransactionDate(),
+ defaultLoanLifecycleStateMachine());
+
+ this.loanTransactionRepository.save(waiveTransaction);
+ this.loanRepository.save(loan);
+
+ final String noteText = command.getNote();
+ if (StringUtils.isNotBlank(noteText)) {
+ final Note note = Note.loanTransactionNote(loan, waiveTransaction, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return new EntityIdentifier(loan.getId());
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier writeOff(final LoanTransactionCommand command) {
+ context.authenticatedUser();
+
+ final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
+ validator.validateNonMonetaryTransaction();
+
+ final Loan loan = this.loanRepository.findOne(command.getLoanId());
+ if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); }
+
+ final LoanTransaction writeoff = loan.closeAsWrittenOff(command.getTransactionDate(), defaultLoanLifecycleStateMachine());
+
+ this.loanTransactionRepository.save(writeoff);
+ this.loanRepository.save(loan);
+
+ final String noteText = command.getNote();
+ if (StringUtils.isNotBlank(noteText)) {
+ final Note note = Note.loanTransactionNote(loan, writeoff, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return new EntityIdentifier(loan.getId());
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier closeLoan(final LoanTransactionCommand command) {
+
+ context.authenticatedUser();
+
+ final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
+ validator.validateNonMonetaryTransaction();
+
+ final Loan loan = this.loanRepository.findOne(command.getLoanId());
+ if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); }
+
+ final LoanTransaction possibleClosingTransaction = loan.close(command.getTransactionDate(), defaultLoanLifecycleStateMachine());
+ if (possibleClosingTransaction != null) {
+ this.loanTransactionRepository.save(possibleClosingTransaction);
+ }
+ this.loanRepository.save(loan);
+
+ final String noteText = command.getNote();
+ if (StringUtils.isNotBlank(noteText)) {
+ final Note note = Note.loanNote(loan, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return new EntityIdentifier(loan.getId());
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier closeAsRescheduled(final LoanTransactionCommand command) {
+ context.authenticatedUser();
+
+ final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
+ validator.validateNonMonetaryTransaction();
+
+ final Loan loan = this.loanRepository.findOne(command.getLoanId());
+ if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); }
+
+ loan.closeAsMarkedForReschedule(command.getTransactionDate(), defaultLoanLifecycleStateMachine());
+
+ this.loanRepository.save(loan);
+
+ final String noteText = command.getNote();
+ if (StringUtils.isNotBlank(noteText)) {
+ final Note note = Note.loanNote(loan, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return new EntityIdentifier(loan.getId());
+ }
+
+ @Transactional
@Override
public EntityIdentifier addLoanCharge(final LoanChargeCommand command) {
this.context.authenticatedUser();
@@ -624,22 +596,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
validator.validateForCreate();
final Loan loan = this.loanRepository.findOne(command.getLoanId());
- if (loan == null) {
- throw new LoanNotFoundException(command.getLoanId());
- }
+ if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); }
final Charge chargeDefinition = this.chargeRepository.findOne(command.getChargeId());
- if (chargeDefinition == null || chargeDefinition.isDeleted()) {
- throw new ChargeNotFoundException(command.getChargeId());
- }
+ if (chargeDefinition == null || chargeDefinition.isDeleted()) { throw new ChargeNotFoundException(command.getChargeId()); }
+
+ if (!chargeDefinition.isActive()) { throw new ChargeIsNotActiveException(chargeDefinition.getId(), chargeDefinition.getName()); }
- if (!chargeDefinition.isActive()) {
- throw new ChargeIsNotActiveException(chargeDefinition.getId(), chargeDefinition.getName());
- }
-
final LoanCharge loanCharge = LoanCharge.createNew(loan, chargeDefinition, command);
- if (!loan.hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())){
+ if (!loan.hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())) {
String errorMessage = "Charge and Loan must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.loan", errorMessage);
}
@@ -650,7 +616,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
return new EntityIdentifier(loanCharge.getId());
}
- @Transactional
+ @Transactional
@Override
public EntityIdentifier updateLoanCharge(final LoanChargeCommand command) {
@@ -661,81 +627,76 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Long loanId = command.getLoanId();
final Loan loan = retrieveLoanBy(loanId);
-
+
final Long loanChargeId = command.getId();
- final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
-
+ final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
+
loan.updateLoanCharge(loanCharge, command);
-
+
this.loanRepository.save(loan);
return new EntityIdentifier(loanCharge.getId());
}
-
- @Transactional
- @Override
- public EntityIdentifier waiveLoanCharge(final LoanChargeCommand command) {
-
- this.context.authenticatedUser();
-// LoanChargeCommandValidator validator = new LoanChargeCommandValidator(command);
-// validator.validateForUpdate();
+ @Transactional
+ @Override
+ public EntityIdentifier waiveLoanCharge(final LoanChargeCommand command) {
+
+ this.context.authenticatedUser();
+
+ // LoanChargeCommandValidator validator = new
+ // LoanChargeCommandValidator(command);
+ // validator.validateForUpdate();
final Long loanId = command.getLoanId();
final Loan loan = retrieveLoanBy(loanId);
-
- final Long loanChargeId = command.getId();
- final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
-
- final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine());
-
- this.loanTransactionRepository.save(waiveTransaction);
- this.loanRepository.save(loan);
-
- final String noteText = ""; //command.getNote();
- if (StringUtils.isNotBlank(noteText)) {
- final Note note = Note.loanTransactionNote(loan, waiveTransaction, noteText);
- this.noteRepository.save(note);
- }
-
- return new EntityIdentifier(loanCharge.getId());
- }
- @Transactional
+ final Long loanChargeId = command.getId();
+ final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
+
+ final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine());
+
+ this.loanTransactionRepository.save(waiveTransaction);
+ this.loanRepository.save(loan);
+
+ final String noteText = ""; // command.getNote();
+ if (StringUtils.isNotBlank(noteText)) {
+ final Note note = Note.loanTransactionNote(loan, waiveTransaction, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return new EntityIdentifier(loanCharge.getId());
+ }
+
+ @Transactional
@Override
public EntityIdentifier deleteLoanCharge(final Long loanId, final Long loanChargeId) {
this.context.authenticatedUser();
final Loan loan = retrieveLoanBy(loanId);
-
+
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
-
+
loan.removeLoanCharge(loanCharge);
this.loanRepository.save(loan);
return new EntityIdentifier(loanCharge.getId());
}
-
- private Loan retrieveLoanBy(final Long loanId) {
- final Loan loan = this.loanRepository.findOne(loanId);
- if (loan == null) {
- throw new LoanNotFoundException(loanId);
- }
- return loan;
- }
- private LoanCharge retrieveLoanChargeBy(final Long loanId, final Long loanChargeId) {
- final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
- if (loanCharge == null) {
- throw new LoanChargeNotFoundException(loanChargeId);
- }
-
- if (loanCharge.hasNotLoanIdentifiedBy(loanId)) {
- throw new LoanChargeNotFoundException(loanChargeId, loanId);
- }
- return loanCharge;
- }
+ private Loan retrieveLoanBy(final Long loanId) {
+ final Loan loan = this.loanRepository.findOne(loanId);
+ if (loan == null) { throw new LoanNotFoundException(loanId); }
+ return loan;
+ }
+
+ private LoanCharge retrieveLoanChargeBy(final Long loanId, final Long loanChargeId) {
+ final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
+ if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); }
+
+ if (loanCharge.hasNotLoanIdentifiedBy(loanId)) { throw new LoanChargeNotFoundException(loanChargeId, loanId); }
+ return loanCharge;
+ }
@Transactional
@Override
@@ -751,9 +712,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Loan loan = retrieveLoanBy(command.getLoanId());
- if (!loan.hasLoanOfficer(fromLoanOfficer)){
- throw new LoanOfficerAssignmentException(loan.getId(), fromLoanOfficer.getId());
- }
+ if (!loan.hasLoanOfficer(fromLoanOfficer)) { throw new LoanOfficerAssignmentException(loan.getId(), fromLoanOfficer.getId()); }
loan.reassignLoanOfficer(toLoanOfficer, command.getAssignmentDate());
@@ -774,14 +733,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
Staff fromLoanOfficer = loanAssembler.findLoanOfficerByIdIfProvided(command.getFromLoanOfficerId());
Staff toLoanOfficer = loanAssembler.findLoanOfficerByIdIfProvided(command.getToLoanOfficerId());
- for (String loanIdString : command.getLoans()){
+ for (String loanIdString : command.getLoans()) {
final Long loanId = Long.valueOf(loanIdString);
final Loan loan = retrieveLoanBy(loanId);
-
- if (!loan.hasLoanOfficer(fromLoanOfficer)){
- throw new LoanOfficerAssignmentException(loan.getId(), fromLoanOfficer.getId());
- }
+
+ if (!loan.hasLoanOfficer(fromLoanOfficer)) { throw new LoanOfficerAssignmentException(loan.getId(), fromLoanOfficer.getId()); }
loan.reassignLoanOfficer(toLoanOfficer, command.getAssignmentDate());
this.loanRepository.save(loan);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java
index 7811debc7..7e411f004 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java
@@ -138,9 +138,9 @@ public class LoanProduct extends AbstractAuditableCustom {
this.charges = charges;
}
- public Map update(final JsonCommand command) {
+ public Map update(final JsonCommand command, final AprCalculator aprCalculator) {
- final Map actualChanges = this.loanProductRelatedDetail.update(command);
+ final Map actualChanges = this.loanProductRelatedDetail.update(command, aprCalculator);
final String nameParamName = "name";
if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
index 3c30ae8f7..7edc826ac 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
@@ -14,6 +14,7 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
/**
* LoanRepaymentScheduleDetail encapsulates all the details of a
@@ -31,6 +32,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "nominal_interest_rate_per_period", scale = 6, precision = 19, nullable = false)
private BigDecimal nominalInterestRatePerPeriod;
+ // FIXME - move away form JPA ordinal use for enums using just integer -
+ // requires sql patch for existing users of software.
@Enumerated(EnumType.ORDINAL)
@Column(name = "interest_period_frequency_enum", nullable = false)
private PeriodFrequencyType interestPeriodFrequencyType;
@@ -38,10 +41,14 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "annual_nominal_interest_rate", scale = 6, precision = 19, nullable = false)
private BigDecimal annualNominalInterestRate;
+ // FIXME - move away form JPA ordinal use for enums using just integer -
+ // requires sql patch for existing users of software.
@Enumerated(EnumType.ORDINAL)
@Column(name = "interest_method_enum", nullable = false)
private InterestMethod interestMethod;
+ // FIXME - move away form JPA ordinal use for enums using just integer -
+ // requires sql patch for existing users of software.
@Enumerated(EnumType.ORDINAL)
@Column(name = "interest_calculated_in_period_enum", nullable = false)
private InterestCalculationPeriodMethod interestCalculationPeriodMethod;
@@ -49,6 +56,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "repay_every", nullable = false)
private Integer repayEvery;
+ // FIXME - move away form JPA ordinal use for enums using just integer -
+ // requires sql patch for existing users of software.
@Enumerated(EnumType.ORDINAL)
@Column(name = "repayment_period_frequency_enum", nullable = false)
private PeriodFrequencyType repaymentPeriodFrequencyType;
@@ -56,6 +65,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "number_of_repayments", nullable = false)
private Integer numberOfRepayments;
+ // FIXME - move away form JPA ordinal use for enums using just integer -
+ // requires sql patch for existing users of software.
@Enumerated(EnumType.ORDINAL)
@Column(name = "amortization_method_enum", nullable = false)
private AmortizationMethod amortizationMethod;
@@ -63,6 +74,18 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "arrearstolerance_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal inArrearsTolerance;
+ public static LoanProductRelatedDetail createFrom(final MonetaryCurrency currency, final BigDecimal principal,
+ final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
+ final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod,
+ final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repaymentEvery,
+ final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments,
+ final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance) {
+
+ return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
+ nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType,
+ numberOfRepayments, amortizationMethod, inArrearsTolerance);
+ }
+
protected LoanProductRelatedDetail() {
//
}
@@ -142,7 +165,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
return amortizationMethod;
}
- public Map update(final JsonCommand command) {
+ public Map update(final JsonCommand command, final AprCalculator aprCalculator) {
final Map actualChanges = new LinkedHashMap(20);
@@ -168,6 +191,18 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal);
}
+ final Map loanApplicationAttributeChanges = updateLoanApplicationAttributes(command, aprCalculator);
+
+ actualChanges.putAll(loanApplicationAttributeChanges);
+
+ return actualChanges;
+ }
+
+ public Map updateLoanApplicationAttributes(final JsonCommand command, final AprCalculator aprCalculator) {
+ final Map actualChanges = new LinkedHashMap(20);
+
+ final String localeAsInput = command.locale();
+
final String principalParamName = "principal";
if (command.isChangeInBigDecimalParameterNamed(principalParamName, this.principal)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(principalParamName);
@@ -222,6 +257,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
actualChanges.put(interestRatePerPeriodParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.nominalInterestRatePerPeriod = newValue;
+ updateInterestRateDerivedFields(aprCalculator);
}
final String interestRateFrequencyTypeParamName = "interestRateFrequencyType";
@@ -230,6 +266,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
actualChanges.put(interestRateFrequencyTypeParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.interestPeriodFrequencyType = PeriodFrequencyType.fromInt(newValue);
+ updateInterestRateDerivedFields(aprCalculator);
}
final String interestTypeParamName = "interestType";
@@ -251,4 +288,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
return actualChanges;
}
+
+ private void updateInterestRateDerivedFields(final AprCalculator aprCalculator) {
+ this.annualNominalInterestRate = aprCalculator.calculateFrom(this.interestPeriodFrequencyType, this.nominalInterestRatePerPeriod);
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
index 016281789..e9e6dc216 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
@@ -108,7 +108,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final LoanProduct product = this.loanProductRepository.findOne(loanProductId);
if (product == null) { throw new LoanProductNotFoundException(loanProductId); }
- final Map changes = product.update(command);
+ final Map changes = product.update(command, this.aprCalculator);
// associating fund with loan product at creation is optional for now.
if (changes.containsKey("fundId")) {