MIFOSX-97: support audit for loan application creation, loan application modification, approval, undo approval, disbursal and undo of disbursal.
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
+23
@@ -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());
|
||||
}
|
||||
|
||||
+3
-15
@@ -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<String> DOCUMENT_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "parentEntityType",
|
||||
"parentEntityId", "name", "fileName", "type", "size", "description"));
|
||||
@@ -79,9 +78,6 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi
|
||||
private static final Set<String> GROUP_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "officeId", "name", "externalId",
|
||||
"clientMembers", "allowedClients", "allowedOffices"));
|
||||
|
||||
private static final Set<String> LOAN_SCHEDULE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("periods",
|
||||
"cumulativePrincipalDisbursed"));
|
||||
|
||||
private static final Set<String> LOAN_TRANSACTION_NEW_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "type", "date",
|
||||
"currency", "amount"));
|
||||
|
||||
@@ -103,7 +99,7 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi
|
||||
private static final Set<String> GUARANTOR_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("externalGuarantor", "existingClientId",
|
||||
"firstname", "lastname", "addressLine1", "addressLine2", "city", "state", "zip", "country", "mobileNumber", "housePhoneNumber",
|
||||
"comment", "dob"));
|
||||
|
||||
|
||||
private static final Set<String> SAVING_SCHEDULE_DATA_PARAMETERS = new HashSet<String>(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<String> 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<String> responseParameters,
|
||||
final LoanTransactionData transaction) {
|
||||
|
||||
+4
@@ -55,6 +55,10 @@ public final class JsonCommand {
|
||||
return this.jsonCommand;
|
||||
}
|
||||
|
||||
public JsonElement parsedJson() {
|
||||
return this.parsedCommand;
|
||||
}
|
||||
|
||||
public Long commandId() {
|
||||
return this.commandId;
|
||||
}
|
||||
|
||||
+261
@@ -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<String, Boolean> mapValueOfParameterNamed(final String parameterName) {
|
||||
final Type typeOfMap = new TypeToken<Map<String, Boolean>>() {}.getType();
|
||||
|
||||
if (parsedQuery.getAsJsonObject().has(parameterName)) {
|
||||
parsedQuery.getAsJsonObject().get(parameterName);
|
||||
}
|
||||
|
||||
return this.fromApiJsonHelper.extractMap(typeOfMap, jsonQuery);
|
||||
}
|
||||
|
||||
public boolean isChangeInLongParameterNamed(final String parameterName, final Long existingValue) {
|
||||
boolean isChanged = false;
|
||||
if (parameterExists(parameterName)) {
|
||||
final Long workingValue = longValueOfParameterNamed(parameterName);
|
||||
isChanged = differenceExists(existingValue, workingValue);
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
public Long longValueOfParameterNamed(final String parameterName) {
|
||||
return this.fromApiJsonHelper.extractLongNamed(parameterName, parsedQuery);
|
||||
}
|
||||
|
||||
public boolean isChangeInLocalDateParameterNamed(final String parameterName, final LocalDate existingValue) {
|
||||
boolean isChanged = false;
|
||||
if (parameterExists(parameterName)) {
|
||||
final LocalDate workingValue = localDateValueOfParameterNamed(parameterName);
|
||||
isChanged = differenceExists(existingValue, workingValue);
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
public LocalDate localDateValueOfParameterNamed(final String parameterName) {
|
||||
return this.fromApiJsonHelper.extractLocalDateNamed(parameterName, parsedQuery);
|
||||
}
|
||||
|
||||
public boolean isChangeInStringParameterNamed(final String parameterName, final String existingValue) {
|
||||
boolean isChanged = false;
|
||||
if (parameterExists(parameterName)) {
|
||||
final String workingValue = stringValueOfParameterNamed(parameterName);
|
||||
isChanged = differenceExists(existingValue, workingValue);
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
public String stringValueOfParameterNamed(final String parameterName) {
|
||||
final String value = this.fromApiJsonHelper.extractStringNamed(parameterName, parsedQuery);
|
||||
return StringUtils.defaultIfEmpty(value, "");
|
||||
}
|
||||
|
||||
public boolean isChangeInBigDecimalParameterNamed(final String parameterName, final BigDecimal existingValue) {
|
||||
boolean isChanged = false;
|
||||
if (parameterExists(parameterName)) {
|
||||
final BigDecimal workingValue = bigDecimalValueOfParameterNamed(parameterName);
|
||||
isChanged = differenceExists(existingValue, workingValue);
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
public BigDecimal bigDecimalValueOfParameterNamed(final String parameterName) {
|
||||
return this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(parameterName, parsedQuery);
|
||||
}
|
||||
|
||||
public boolean isChangeInIntegerParameterNamed(final String parameterName, final Integer existingValue) {
|
||||
boolean isChanged = false;
|
||||
if (parameterExists(parameterName)) {
|
||||
final Integer workingValue = integerValueOfParameterNamed(parameterName);
|
||||
isChanged = differenceExists(existingValue, workingValue);
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
public Integer integerValueOfParameterNamed(final String parameterName) {
|
||||
return this.fromApiJsonHelper.extractIntegerWithLocaleNamed(parameterName, parsedQuery);
|
||||
}
|
||||
|
||||
public boolean isChangeInBooleanParameterNamed(final String parameterName, final Boolean existingValue) {
|
||||
boolean isChanged = false;
|
||||
if (parameterExists(parameterName)) {
|
||||
final Boolean workingValue = booleanObjectValueOfParameterNamed(parameterName);
|
||||
isChanged = differenceExists(existingValue, workingValue);
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Boolean} that could possibly be null.
|
||||
*/
|
||||
public Boolean booleanObjectValueOfParameterNamed(final String parameterName) {
|
||||
return this.fromApiJsonHelper.extractBooleanNamed(parameterName, parsedQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* always returns true or false
|
||||
*/
|
||||
public boolean booleanPrimitiveValueOfParameterNamed(final String parameterName) {
|
||||
final Boolean value = this.fromApiJsonHelper.extractBooleanNamed(parameterName, parsedQuery);
|
||||
return (Boolean) ObjectUtils.defaultIfNull(value, Boolean.FALSE);
|
||||
}
|
||||
|
||||
public boolean isChangeInArrayParameterNamed(final String parameterName, final String[] existingValue) {
|
||||
boolean isChanged = false;
|
||||
if (parameterExists(parameterName)) {
|
||||
final String[] workingValue = arrayValueOfParameterNamed(parameterName);
|
||||
isChanged = differenceExists(existingValue, workingValue);
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
public String[] arrayValueOfParameterNamed(final String parameterName) {
|
||||
return this.fromApiJsonHelper.extractArrayNamed(parameterName, parsedQuery);
|
||||
}
|
||||
|
||||
public boolean isChangeInPasswordParameterNamed(final String parameterName, final String existingValue,
|
||||
final PlatformPasswordEncoder platformPasswordEncoder, final Long saltValue) {
|
||||
boolean isChanged = false;
|
||||
if (parameterExists(parameterName)) {
|
||||
final String workingValue = passwordValueOfParameterNamed(parameterName, platformPasswordEncoder, saltValue);
|
||||
isChanged = differenceExists(existingValue, workingValue);
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
public String passwordValueOfParameterNamed(final String parameterName, final PlatformPasswordEncoder platformPasswordEncoder,
|
||||
final Long saltValue) {
|
||||
final String passwordPlainText = stringValueOfParameterNamed(parameterName);
|
||||
|
||||
final PlatformUser dummyPlatformUser = new BasicPasswordEncodablePlatformUser(saltValue, "", passwordPlainText);
|
||||
return platformPasswordEncoder.encode(dummyPlatformUser);
|
||||
}
|
||||
}
|
||||
-6
@@ -5,9 +5,7 @@ import org.mifosplatform.portfolio.client.command.NoteCommand;
|
||||
import org.mifosplatform.portfolio.client.data.ClientData;
|
||||
import org.mifosplatform.portfolio.group.command.GroupCommand;
|
||||
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.gaurantor.command.GuarantorCommand;
|
||||
import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountCommand;
|
||||
@@ -29,12 +27,8 @@ public interface PortfolioApiDataConversionService {
|
||||
|
||||
GroupCommand convertJsonToGroupCommand(Long resourceIdentifier, String json);
|
||||
|
||||
LoanApplicationCommand convertApiRequestJsonToLoanApplicationCommand(Long resourceIdentifier, String json);
|
||||
|
||||
LoanChargeCommand convertJsonToLoanChargeCommand(Long loanChargeId, Long loanId, String json);
|
||||
|
||||
LoanStateTransitionCommand convertJsonToLoanStateTransitionCommand(Long resourceIdentifier, String json);
|
||||
|
||||
LoanTransactionCommand convertJsonToLoanTransactionCommand(Long resourceIdentifier, String json);
|
||||
|
||||
AdjustLoanTransactionCommand convertJsonToAdjustLoanTransactionCommand(Long loanId, Long transactionId, String json);
|
||||
|
||||
+4
-122
@@ -21,7 +21,6 @@ import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper;
|
||||
import org.mifosplatform.organisation.staff.command.BulkTransferLoanOfficerCommand;
|
||||
import org.mifosplatform.portfolio.client.command.ClientCommand;
|
||||
import org.mifosplatform.portfolio.client.command.NoteCommand;
|
||||
@@ -29,9 +28,7 @@ import org.mifosplatform.portfolio.client.data.ClientData;
|
||||
import org.mifosplatform.portfolio.client.serialization.ClientCommandFromApiJsonDeserializer;
|
||||
import org.mifosplatform.portfolio.group.command.GroupCommand;
|
||||
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.gaurantor.command.GuarantorCommand;
|
||||
import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountCommand;
|
||||
@@ -178,106 +175,10 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
|
||||
return new GroupCommand(modifiedParameters, resourceIdentifier, externalId, name, officeId, clientMembers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanApplicationCommand convertApiRequestJsonToLoanApplicationCommand(final Long resourceIdentifier, final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
final Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
|
||||
|
||||
final Set<String> supportedParams = new HashSet<String>(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"));
|
||||
|
||||
checkForUnsupportedParameters(requestMap, supportedParams);
|
||||
|
||||
final Set<String> parametersPassedInCommand = new HashSet<String>();
|
||||
|
||||
final JsonParser parser = new JsonParser();
|
||||
final JsonElement element = parser.parse(json);
|
||||
final JsonParserHelper helper = new JsonParserHelper();
|
||||
|
||||
final Long clientId = helper.extractLongNamed("clientId", element, parametersPassedInCommand);
|
||||
final Long groupId = helper.extractLongNamed("groupId", element, parametersPassedInCommand);
|
||||
final Long productId = helper.extractLongNamed("productId", element, parametersPassedInCommand);
|
||||
final Long fundId = helper.extractLongNamed("fundId", element, parametersPassedInCommand);
|
||||
final Long loanOfficerId = helper.extractLongNamed("loanOfficerId", element, parametersPassedInCommand);
|
||||
final Long transactionProcessingStrategyId = helper.extractLongNamed("transactionProcessingStrategyId", element,
|
||||
parametersPassedInCommand);
|
||||
final String externalId = helper.extractStringNamed("externalId", element, parametersPassedInCommand);
|
||||
final BigDecimal principal = helper.extractBigDecimalWithLocaleNamed("principal", element, parametersPassedInCommand);
|
||||
final BigDecimal inArrearsToleranceValue = helper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element,
|
||||
parametersPassedInCommand);
|
||||
final BigDecimal interestRatePerPeriod = helper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element,
|
||||
parametersPassedInCommand);
|
||||
|
||||
final Integer repaymentEvery = helper.extractIntegerWithLocaleNamed("repaymentEvery", element, parametersPassedInCommand);
|
||||
final Integer numberOfRepayments = helper.extractIntegerWithLocaleNamed("numberOfRepayments", element, parametersPassedInCommand);
|
||||
final Integer repaymentFrequencyType = helper.extractIntegerWithLocaleNamed("repaymentFrequencyType", element,
|
||||
parametersPassedInCommand);
|
||||
final Integer loanTermFrequency = helper.extractIntegerWithLocaleNamed("loanTermFrequency", element, parametersPassedInCommand);
|
||||
final Integer loanTermFrequencyType = helper.extractIntegerWithLocaleNamed("loanTermFrequencyType", element,
|
||||
parametersPassedInCommand);
|
||||
final Integer interestRateFrequencyType = helper.extractIntegerWithLocaleNamed("interestRateFrequencyType", element,
|
||||
parametersPassedInCommand);
|
||||
final Integer amortizationType = helper.extractIntegerWithLocaleNamed("amortizationType", element, parametersPassedInCommand);
|
||||
final Integer interestType = helper.extractIntegerWithLocaleNamed("interestType", element, parametersPassedInCommand);
|
||||
final Integer interestCalculationPeriodType = helper.extractIntegerWithLocaleNamed("interestCalculationPeriodType", element,
|
||||
parametersPassedInCommand);
|
||||
|
||||
final LocalDate expectedDisbursementDate = helper.extractLocalDateNamed("expectedDisbursementDate", element,
|
||||
parametersPassedInCommand);
|
||||
final LocalDate repaymentsStartingFromDate = helper.extractLocalDateNamed("repaymentsStartingFromDate", element,
|
||||
parametersPassedInCommand);
|
||||
final LocalDate interestChargedFromDate = helper.extractLocalDateNamed("interestChargedFromDate", element,
|
||||
parametersPassedInCommand);
|
||||
final LocalDate submittedOnDate = helper.extractLocalDateNamed("submittedOnDate", element, parametersPassedInCommand);
|
||||
|
||||
final String submittedOnNote = helper.extractStringNamed("submittedOnNote", element, parametersPassedInCommand);
|
||||
|
||||
LoanChargeCommand[] charges = null;
|
||||
if (element.isJsonObject()) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final String dateFormat = helper.extractDateFormatParameter(topLevelJsonElement);
|
||||
final Locale locale = helper.extractLocaleParameter(topLevelJsonElement);
|
||||
if (topLevelJsonElement.has("charges") && topLevelJsonElement.get("charges").isJsonArray()) {
|
||||
|
||||
parametersPassedInCommand.add("charges");
|
||||
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<String> parametersPassedInForChargesCommand = new HashSet<String>();
|
||||
|
||||
final Long id = helper.extractLongNamed("id", loanChargeElement, parametersPassedInForChargesCommand);
|
||||
final Long chargeId = helper.extractLongNamed("chargeId", loanChargeElement, parametersPassedInForChargesCommand);
|
||||
final BigDecimal amount = helper.extractBigDecimalNamed("amount", loanChargeElement, locale,
|
||||
parametersPassedInForChargesCommand);
|
||||
final Integer chargeTimeType = helper.extractIntegerNamed("chargeTimeType", loanChargeElement, locale,
|
||||
parametersPassedInForChargesCommand);
|
||||
final Integer chargeCalculationType = helper.extractIntegerNamed("chargeCalculationType", loanChargeElement, locale,
|
||||
parametersPassedInForChargesCommand);
|
||||
final LocalDate specifiedDueDate = helper.extractLocalDateNamed("specifiedDueDate", loanChargeElement, dateFormat,
|
||||
locale, parametersPassedInForChargesCommand);
|
||||
|
||||
charges[i] = new LoanChargeCommand(parametersPassedInForChargesCommand, id, null, chargeId, amount, chargeTimeType,
|
||||
chargeCalculationType, specifiedDueDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new LoanApplicationCommand(parametersPassedInCommand, resourceIdentifier, clientId, groupId, productId, externalId, fundId,
|
||||
transactionProcessingStrategyId, submittedOnDate, submittedOnNote, expectedDisbursementDate, repaymentsStartingFromDate,
|
||||
interestChargedFromDate, principal, interestRatePerPeriod, interestRateFrequencyType, interestType,
|
||||
interestCalculationPeriodType, repaymentEvery, repaymentFrequencyType, numberOfRepayments, amortizationType,
|
||||
loanTermFrequency, loanTermFrequencyType, inArrearsToleranceValue, charges, loanOfficerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated - to be removed soon
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public LoanChargeCommand convertJsonToLoanChargeCommand(final Long loanChargeId, final Long loanId, final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
@@ -302,25 +203,6 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
|
||||
submittedOnDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanStateTransitionCommand convertJsonToLoanStateTransitionCommand(final Long resourceIdentifier, final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
|
||||
|
||||
Set<String> supportedParams = new HashSet<String>(Arrays.asList("eventDate", "note", "locale", "dateFormat"));
|
||||
|
||||
checkForUnsupportedParameters(requestMap, supportedParams);
|
||||
|
||||
Set<String> modifiedParameters = new HashSet<String>();
|
||||
|
||||
LocalDate eventDate = extractLocalDateParameter("eventDate", requestMap, modifiedParameters);
|
||||
String note = extractStringParameter("note", requestMap, modifiedParameters);
|
||||
|
||||
return new LoanStateTransitionCommand(resourceIdentifier, eventDate, note);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanTransactionCommand convertJsonToLoanTransactionCommand(final Long resourceIdentifier, final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
-4
@@ -15,7 +15,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;
|
||||
@@ -52,8 +51,6 @@ public interface PortfolioApiJsonSerializerService {
|
||||
|
||||
String serializeGroupDataToJson(boolean prettyPrint, Set<String> responseParameters, GroupData group);
|
||||
|
||||
String serializeLoanScheduleDataToJson(boolean prettyPrint, Set<String> responseParameters, LoanScheduleData loanSchedule);
|
||||
|
||||
String serializeLoanTransactionDataToJson(boolean prettyPrint, Set<String> responseParameters, LoanTransactionData newTransactionData);
|
||||
|
||||
String serializeLoanReassignmentDataToJson(boolean prettyPrint, Set<String> responseParameters,
|
||||
@@ -77,5 +74,4 @@ public interface PortfolioApiJsonSerializerService {
|
||||
|
||||
String serializeSavingScheduleDataToJson(boolean prettyPrint, Set<String> responseParameters, SavingScheduleData savingScheduleData);
|
||||
|
||||
|
||||
}
|
||||
+23
-1
@@ -17,6 +17,7 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
@Component
|
||||
@@ -98,6 +99,11 @@ public class FromJsonHelper {
|
||||
public LocalDate extractLocalDateNamed(final String parameterName, JsonElement element) {
|
||||
return helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet<String>());
|
||||
}
|
||||
|
||||
public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element, final String dateFormat,
|
||||
final Locale locale) {
|
||||
return helperDelegator.extractLocalDateNamed(parameterName, element.getAsJsonObject(), dateFormat, locale, new HashSet<String>());
|
||||
}
|
||||
|
||||
public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element,
|
||||
final Set<String> parametersPassedInRequest) {
|
||||
@@ -118,11 +124,15 @@ public class FromJsonHelper {
|
||||
return helperDelegator.extractBigDecimalWithLocaleNamed(parameterName, element, parametersPassedInRequest);
|
||||
}
|
||||
|
||||
public BigDecimal extractBigDecimalNamed(final String parameterName, final JsonElement element, final Locale locale) {
|
||||
return helperDelegator.extractBigDecimalNamed(parameterName, element.getAsJsonObject(), locale, new HashSet<String>());
|
||||
}
|
||||
|
||||
public BigDecimal extractBigDecimalNamed(final String parameterName, final JsonElement element,
|
||||
final Set<String> parametersPassedInRequest) {
|
||||
return helperDelegator.extractBigDecimalNamed(parameterName, element.getAsJsonObject(), Locale.US, parametersPassedInRequest);
|
||||
}
|
||||
|
||||
|
||||
public Integer extractIntegerWithLocaleNamed(final String parameterName, final JsonElement element) {
|
||||
return helperDelegator.extractIntegerWithLocaleNamed(parameterName, element.getAsJsonObject(), new HashSet<String>());
|
||||
}
|
||||
@@ -131,9 +141,21 @@ public class FromJsonHelper {
|
||||
final Set<String> parametersPassedInRequest) {
|
||||
return helperDelegator.extractIntegerWithLocaleNamed(parameterName, element.getAsJsonObject(), parametersPassedInRequest);
|
||||
}
|
||||
|
||||
public Integer extractIntegerNamed(final String parameterName, final JsonElement element, final Locale locale) {
|
||||
return helperDelegator.extractIntegerNamed(parameterName, element.getAsJsonObject(), locale, new HashSet<String>());
|
||||
}
|
||||
|
||||
public Integer extractIntegerNamed(final String parameterName, final JsonElement element,
|
||||
final Set<String> parametersPassedInRequest) {
|
||||
return helperDelegator.extractIntegerNamed(parameterName, element.getAsJsonObject(), Locale.US, parametersPassedInRequest);
|
||||
}
|
||||
|
||||
public Locale extractLocaleParameter(final JsonObject element) {
|
||||
return helperDelegator.extractLocaleParameter(element);
|
||||
}
|
||||
|
||||
public String extractDateFormatParameter(final JsonObject element) {
|
||||
return helperDelegator.extractDateFormatParameter(element);
|
||||
}
|
||||
}
|
||||
+58
-59
@@ -21,8 +21,10 @@ import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
|
||||
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonQuery;
|
||||
import org.mifosplatform.infrastructure.core.api.PortfolioApiDataConversionService;
|
||||
import org.mifosplatform.infrastructure.core.api.PortfolioApiJsonSerializerService;
|
||||
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
|
||||
@@ -30,6 +32,7 @@ import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
@@ -45,11 +48,8 @@ import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.fund.data.FundData;
|
||||
import org.mifosplatform.portfolio.fund.service.FundReadPlatformService;
|
||||
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.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanAccountData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanBasicDetailsData;
|
||||
@@ -58,9 +58,8 @@ import org.mifosplatform.portfolio.loanaccount.data.LoanPermissionData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
|
||||
import org.mifosplatform.portfolio.loanaccount.gaurantor.data.GuarantorData;
|
||||
import org.mifosplatform.portfolio.loanaccount.gaurantor.service.GuarantorReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.command.CalculateLoanScheduleCommand;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.CalculationPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
||||
@@ -72,6 +71,8 @@ import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
@Path("/loans")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
@@ -88,7 +89,7 @@ public class LoansApiResource {
|
||||
"interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "loanOfficerId",
|
||||
"loanOfficerName", "loanOfficerOptions", "chargeTemplate"));
|
||||
|
||||
// private final String resourceNameForPermissions = "OFFICE";
|
||||
private final String resourceNameForPermissions = "LOAN";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
@@ -97,25 +98,31 @@ public class LoansApiResource {
|
||||
private final LoanDropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final FundReadPlatformService fundReadPlatformService;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
private final CalculationPlatformService calculationPlatformService;
|
||||
private final LoanScheduleCalculationPlatformService calculationPlatformService;
|
||||
private final StaffReadPlatformService staffReadPlatformService;
|
||||
private final OfficeReadPlatformService officeReadPlatformService;
|
||||
private final GuarantorReadPlatformService guarantorReadPlatformService;
|
||||
private final PortfolioApiJsonSerializerService apiJsonSerializerService;
|
||||
private final PortfolioApiDataConversionService apiDataConversionService;
|
||||
private final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer;
|
||||
private final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
|
||||
final LoanWritePlatformService loanWritePlatformService, final LoanProductReadPlatformService loanProductReadPlatformService,
|
||||
final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService,
|
||||
final ChargeReadPlatformService chargeReadPlatformService, final CalculationPlatformService calculationPlatformService,
|
||||
final ChargeReadPlatformService chargeReadPlatformService,
|
||||
final LoanScheduleCalculationPlatformService calculationPlatformService,
|
||||
final StaffReadPlatformService staffReadPlatformService, final OfficeReadPlatformService officeReadPlatformService,
|
||||
final GuarantorReadPlatformService guarantorReadPlatformService,
|
||||
final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer,
|
||||
final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper, final PortfolioApiJsonSerializerService apiJsonSerializerService,
|
||||
final PortfolioApiDataConversionService apiDataConversionService) {
|
||||
final PortfolioApiDataConversionService apiDataConversionService, final FromJsonHelper fromJsonHelper,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
|
||||
this.context = context;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
this.loanWritePlatformService = loanWritePlatformService;
|
||||
@@ -128,9 +135,12 @@ public class LoansApiResource {
|
||||
this.officeReadPlatformService = officeReadPlatformService;
|
||||
this.guarantorReadPlatformService = guarantorReadPlatformService;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
this.loanScheduleToApiJsonSerializer = loanScheduleToApiJsonSerializer;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
this.apiJsonSerializerService = apiJsonSerializerService;
|
||||
this.apiDataConversionService = apiDataConversionService;
|
||||
this.fromJsonHelper = fromJsonHelper;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
}
|
||||
|
||||
@GET
|
||||
@@ -140,7 +150,7 @@ public class LoansApiResource {
|
||||
public String retrieveDetailsForNewLoanApplicationStepOne(@QueryParam("clientId") final Long clientId,
|
||||
@QueryParam("groupId") final Long groupId, @QueryParam("productId") final Long productId, @Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission("LOAN");
|
||||
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
|
||||
|
||||
// tempate related
|
||||
Collection<LoanProductData> productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup();
|
||||
@@ -193,7 +203,7 @@ public class LoansApiResource {
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveLoanAccountDetails(@PathParam("loanId") final Long loanId, @Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission("LOAN");
|
||||
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
|
||||
|
||||
final LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService.retrieveLoanAccountDetails(loanId);
|
||||
|
||||
@@ -319,94 +329,83 @@ public class LoansApiResource {
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String calculateLoanScheduleOrSubmitLoanApplication(@QueryParam("command") final String commandParam,
|
||||
@Context final UriInfo uriInfo, final String jsonRequestBody) {
|
||||
|
||||
final LoanApplicationCommand command = this.apiDataConversionService.convertApiRequestJsonToLoanApplicationCommand(null,
|
||||
jsonRequestBody);
|
||||
@Context final UriInfo uriInfo, final String apiRequestBodyAsJson) {
|
||||
|
||||
if (is(commandParam, "calculateLoanSchedule")) {
|
||||
CalculateLoanScheduleCommand calculateLoanScheduleCommand = command.toCalculateLoanScheduleCommand();
|
||||
return calculateLoanSchedule(uriInfo, calculateLoanScheduleCommand);
|
||||
|
||||
final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson);
|
||||
final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper);
|
||||
|
||||
final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.loanScheduleToApiJsonSerializer.serialize(settings, loanSchedule, new HashSet<String>());
|
||||
}
|
||||
|
||||
final EntityIdentifier identifier = this.loanWritePlatformService.submitLoanApplication(command);
|
||||
final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("CREATE", "LOAN", "CREATE", "loans", null,
|
||||
apiRequestBodyAsJson);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(identifier);
|
||||
}
|
||||
|
||||
private String calculateLoanSchedule(final UriInfo uriInfo, final CalculateLoanScheduleCommand command) {
|
||||
|
||||
final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command);
|
||||
|
||||
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
|
||||
|
||||
return this.apiJsonSerializerService.serializeLoanScheduleDataToJson(prettyPrint, responseParameters, loanSchedule);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{loanId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String modifyLoanApplication(@PathParam("loanId") final Long loanId, final String jsonRequestBody) {
|
||||
public String modifyLoanApplication(@PathParam("loanId") final Long loanId, final String apiRequestBodyAsJson) {
|
||||
|
||||
final LoanApplicationCommand command = this.apiDataConversionService.convertApiRequestJsonToLoanApplicationCommand(loanId,
|
||||
jsonRequestBody);
|
||||
final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("UPDATE", "LOAN", "UPDATE", "loans",
|
||||
loanId, apiRequestBodyAsJson);
|
||||
|
||||
final EntityIdentifier identifier = this.loanWritePlatformService.modifyLoanApplication(command);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(identifier);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{loanId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public Response deleteLoanApplication(@PathParam("loanId") final Long loanId) {
|
||||
public String deleteLoanApplication(@PathParam("loanId") final Long loanId) {
|
||||
|
||||
EntityIdentifier identifier = this.loanWritePlatformService.deleteLoan(loanId);
|
||||
final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("DELETE", "LOAN", "DELETE", "loans",
|
||||
loanId, "{}");
|
||||
|
||||
return Response.ok().entity(identifier).build();
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{loanId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public Response stateTransitions(@PathParam("loanId") final Long loanId, @QueryParam("command") final String commandParam,
|
||||
final String jsonRequestBody) {
|
||||
public String stateTransitions(@PathParam("loanId") final Long loanId, @QueryParam("command") final String commandParam,
|
||||
final String apiRequestBodyAsJson) {
|
||||
|
||||
LoanStateTransitionCommand command = this.apiDataConversionService.convertJsonToLoanStateTransitionCommand(loanId, jsonRequestBody);
|
||||
|
||||
Response response = null;
|
||||
EntityIdentifier result = null;
|
||||
|
||||
if (is(commandParam, "reject")) {
|
||||
EntityIdentifier identifier = this.loanWritePlatformService.rejectLoan(command);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource("REJECT", "LOAN", "N/A", "loans", loanId,
|
||||
apiRequestBodyAsJson);
|
||||
} else if (is(commandParam, "withdrewbyclient")) {
|
||||
EntityIdentifier identifier = this.loanWritePlatformService.withdrawLoan(command);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource("WITHDRAW", "LOAN", "N/A", "loans", loanId,
|
||||
apiRequestBodyAsJson);
|
||||
} else if (is(commandParam, "approve")) {
|
||||
EntityIdentifier identifier = this.loanWritePlatformService.approveLoanApplication(command);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource("APPROVE", "LOAN", "N/A", "loans", loanId,
|
||||
apiRequestBodyAsJson);
|
||||
} else if (is(commandParam, "disburse")) {
|
||||
EntityIdentifier identifier = this.loanWritePlatformService.disburseLoan(command);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource("DISBURSE", "LOAN", "N/A", "loans", loanId,
|
||||
apiRequestBodyAsJson);
|
||||
}
|
||||
|
||||
UndoStateTransitionCommand undoCommand = new UndoStateTransitionCommand(loanId, command.getNote());
|
||||
|
||||
if (is(commandParam, "undoapproval")) {
|
||||
EntityIdentifier identifier = this.loanWritePlatformService.undoLoanApproval(undoCommand);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource("APPROVALUNDO", "LOAN", "N/A", "loans", loanId,
|
||||
apiRequestBodyAsJson);
|
||||
} else if (is(commandParam, "undodisbursal")) {
|
||||
EntityIdentifier identifier = this.loanWritePlatformService.undoLoanDisbursal(undoCommand);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource("DISBURSALUNDO", "LOAN", "N/A", "loans", loanId,
|
||||
apiRequestBodyAsJson);
|
||||
}
|
||||
|
||||
if (response == null) { throw new UnrecognizedQueryParamException("command", commandParam); }
|
||||
if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam); }
|
||||
|
||||
return response;
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
private boolean is(final String commandParam, final String commandValue) {
|
||||
|
||||
+56
-14
@@ -1,11 +1,15 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.command;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.command.CalculateLoanScheduleCommand;
|
||||
import org.mifosplatform.portfolio.loanproduct.command.LoanProductCommand;
|
||||
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.loanschedule.query.CalculateLoanScheduleQuery;
|
||||
|
||||
/**
|
||||
* Immutable command for submitting new loan application.
|
||||
@@ -90,8 +94,8 @@ public class LoanApplicationCommand {
|
||||
this.charges = charges;
|
||||
}
|
||||
|
||||
public CalculateLoanScheduleCommand toCalculateLoanScheduleCommand() {
|
||||
return new CalculateLoanScheduleCommand(this.productId, this.principal, this.interestRatePerPeriod, this.interestRateFrequencyType,
|
||||
public CalculateLoanScheduleQuery toCalculateLoanScheduleCommand() {
|
||||
return new CalculateLoanScheduleQuery(this.productId, this.principal, this.interestRatePerPeriod, this.interestRateFrequencyType,
|
||||
this.interestType, this.interestCalculationPeriodType, this.repaymentEvery, this.repaymentFrequencyType,
|
||||
this.numberOfRepayments, this.amortizationType, this.loanTermFrequency, this.loanTermFrequencyType,
|
||||
this.expectedDisbursementDate, this.repaymentsStartingFromDate, this.interestChargedFromDate, this.charges);
|
||||
@@ -197,16 +201,6 @@ public class LoanApplicationCommand {
|
||||
return charges;
|
||||
}
|
||||
|
||||
public LoanProductCommand toLoanProductCommand() {
|
||||
|
||||
// FIXME - KW - CHARGE - FIX UP FOR CHARGES - take look at loan product
|
||||
// charges also and align usage with other loan areas.
|
||||
return new LoanProductCommand(null, null, this.fundId, this.transactionProcessingStrategyId, null, null, this.principal,
|
||||
this.inArrearsTolerance, this.numberOfRepayments, this.repaymentEvery, this.interestRatePerPeriod,
|
||||
this.repaymentFrequencyType, this.interestRateFrequencyType, this.amortizationType, this.interestType,
|
||||
this.interestCalculationPeriodType, null);
|
||||
}
|
||||
|
||||
public boolean isClientChanged() {
|
||||
return this.modifiedParameters.contains("clientId");
|
||||
}
|
||||
@@ -258,4 +252,52 @@ public class LoanApplicationCommand {
|
||||
public Long getLoanOfficerId() {
|
||||
return loanOfficerId;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
|
||||
if (this.groupId != null) {
|
||||
baseDataValidator.reset().parameter("clientId").value(this.clientId).mustBeBlankWhenParameterProvided("groupId", this.groupId)
|
||||
.longGreaterThanZero();
|
||||
}
|
||||
if (this.clientId != null) {
|
||||
baseDataValidator.reset().parameter("groupId").value(this.groupId).mustBeBlankWhenParameterProvided("clientId", this.clientId)
|
||||
.longGreaterThanZero();
|
||||
}
|
||||
|
||||
if (this.submittedOnDate == null) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.submitted.on.date.cannot.be.blank",
|
||||
"The parameter submittedOnDate cannot be empty.", "submittedOnDate");
|
||||
dataValidationErrors.add(error);
|
||||
} else {
|
||||
if (this.submittedOnDate.isAfter(this.expectedDisbursementDate)) {
|
||||
ApiParameterError error = ApiParameterError.parameterError(
|
||||
"validation.msg.loan.submitted.on.date.cannot.be.after.expectedDisbursementDate",
|
||||
"The date of parameter submittedOnDate cannot fall after the date given for expectedDisbursementDate.",
|
||||
"submittedOnDate", this.submittedOnDate, this.expectedDisbursementDate);
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.transactionProcessingStrategyId == null) {
|
||||
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(this.transactionProcessingStrategyId).notNull()
|
||||
.inMinMaxRange(2, 2);
|
||||
}
|
||||
|
||||
if (this.charges != null) {
|
||||
for (LoanChargeCommand loanChargeCommand : this.charges) {
|
||||
try {
|
||||
LoanChargeCommandValidator validator = new LoanChargeCommandValidator(loanChargeCommand);
|
||||
validator.validateForCreate();
|
||||
} catch (PlatformApiDataValidationException e) {
|
||||
dataValidationErrors.addAll(e.getErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.loanschedule.command.CalculateLoanScheduleCommand;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.command.CalculateLoanScheduleCommandValidator;
|
||||
|
||||
public class LoanApplicationCommandValidator {
|
||||
|
||||
private final LoanApplicationCommand command;
|
||||
|
||||
public LoanApplicationCommandValidator(LoanApplicationCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
// if (command.getLoanSchedule() == null) {
|
||||
// ApiParameterError error = ApiParameterError.parameterError("validation.msg.submit.loan.loan.schedule.cannot.be.blank",
|
||||
// "The parameter loanSchedule cannot be empty.", "loanSchedule");
|
||||
// dataValidationErrors.add(error);
|
||||
// }
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
|
||||
if (command.getGroupId() != null){
|
||||
baseDataValidator.reset().parameter("clientId").value(command.getClientId()).mustBeBlankWhenParameterProvided("groupId", command.getGroupId()).longGreaterThanZero();
|
||||
}
|
||||
if (command.getClientId() != null){
|
||||
baseDataValidator.reset().parameter("groupId").value(command.getGroupId()).mustBeBlankWhenParameterProvided("clientId", command.getClientId()).longGreaterThanZero();
|
||||
}
|
||||
|
||||
if (command.getSubmittedOnDate() == null) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.submitted.on.date.cannot.be.blank",
|
||||
"The parameter submittedOnDate cannot be empty.", "submittedOnDate");
|
||||
dataValidationErrors.add(error);
|
||||
} else {
|
||||
if (command.getSubmittedOnDate().isAfter(command.getExpectedDisbursementDate())) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.submitted.on.date.cannot.be.after.expectedDisbursementDate",
|
||||
"The date of parameter submittedOnDate cannot fall after the date given for expectedDisbursementDate.", "submittedOnDate",
|
||||
command.getSubmittedOnDate(), command.getExpectedDisbursementDate());
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.getTransactionProcessingStrategyId() == null) {
|
||||
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(command.getTransactionProcessingStrategyId()).notNull().inMinMaxRange(2, 2);
|
||||
}
|
||||
|
||||
try {
|
||||
// reuse calculate loan schedule validator for now
|
||||
CalculateLoanScheduleCommand calculateLoanScheduleCommand = this.command.toCalculateLoanScheduleCommand();
|
||||
CalculateLoanScheduleCommandValidator validator = new CalculateLoanScheduleCommandValidator(calculateLoanScheduleCommand);
|
||||
validator.validate();
|
||||
} catch (PlatformApiDataValidationException e) {
|
||||
dataValidationErrors.addAll(e.getErrors());
|
||||
}
|
||||
|
||||
if (this.command.getCharges() != null){
|
||||
for (LoanChargeCommand loanChargeCommand : this.command.getCharges()){
|
||||
try {
|
||||
LoanChargeCommandValidator validator = new LoanChargeCommandValidator(loanChargeCommand);
|
||||
validator.validateForCreate();
|
||||
} catch (PlatformApiDataValidationException e) {
|
||||
dataValidationErrors.addAll(e.getErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-27
@@ -21,25 +21,19 @@ public class LoanChargeCommand {
|
||||
private final Integer chargeCalculationType;
|
||||
|
||||
/**
|
||||
* Used to capture what parameters were passed in the json api request.
|
||||
* It does not indicate that these values are modified from their original values when tyring to update.
|
||||
* Used to capture what parameters were passed in the json api request. It
|
||||
* does not indicate that these values are modified from their original
|
||||
* values when tyring to update.
|
||||
*/
|
||||
private final Set<String> requestParameters;
|
||||
|
||||
public static LoanChargeCommand forWaiver(final Long id, final Long loanId) {
|
||||
final Set<String> parametersPassedInCommand = new HashSet<String>();
|
||||
return new LoanChargeCommand(parametersPassedInCommand, id, loanId, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public LoanChargeCommand(
|
||||
final Set<String> parametersPassedInCommand,
|
||||
final Long id,
|
||||
final Long loanId,
|
||||
final Long chargeId,
|
||||
final BigDecimal amount,
|
||||
final Integer chargeTimeType,
|
||||
final Integer chargeCalculationType,
|
||||
final LocalDate specifiedDueDate) {
|
||||
public static LoanChargeCommand forWaiver(final Long id, final Long loanId) {
|
||||
final Set<String> parametersPassedInCommand = new HashSet<String>();
|
||||
return new LoanChargeCommand(parametersPassedInCommand, id, loanId, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public LoanChargeCommand(final Set<String> parametersPassedInCommand, final Long id, final Long loanId, final Long chargeId,
|
||||
final BigDecimal amount, final Integer chargeTimeType, final Integer chargeCalculationType, final LocalDate specifiedDueDate) {
|
||||
this.requestParameters = parametersPassedInCommand;
|
||||
this.id = id;
|
||||
this.chargeId = chargeId;
|
||||
@@ -47,7 +41,7 @@ public class LoanChargeCommand {
|
||||
this.amount = amount;
|
||||
this.chargeTimeType = chargeTimeType;
|
||||
this.chargeCalculationType = chargeCalculationType;
|
||||
this.specifiedDueDate = specifiedDueDate;
|
||||
this.specifiedDueDate = specifiedDueDate;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
@@ -69,28 +63,28 @@ public class LoanChargeCommand {
|
||||
public Integer getChargeTimeType() {
|
||||
return chargeTimeType;
|
||||
}
|
||||
|
||||
public LocalDate getSpecifiedDueDate() {
|
||||
return specifiedDueDate;
|
||||
}
|
||||
|
||||
public Integer getChargeCalculationType() {
|
||||
public LocalDate getSpecifiedDueDate() {
|
||||
return specifiedDueDate;
|
||||
}
|
||||
|
||||
public Integer getChargeCalculationType() {
|
||||
return chargeCalculationType;
|
||||
}
|
||||
|
||||
public boolean isAmountChanged(){
|
||||
public boolean isAmountChanged() {
|
||||
return this.requestParameters.contains("amount");
|
||||
}
|
||||
|
||||
public boolean isChargeTimeTypeChanged(){
|
||||
public boolean isChargeTimeTypeChanged() {
|
||||
return this.requestParameters.contains("chargeTimeType");
|
||||
}
|
||||
|
||||
public boolean isSpecifiedDueDateChanged(){
|
||||
|
||||
public boolean isSpecifiedDueDateChanged() {
|
||||
return this.requestParameters.contains("specifiedDueDate");
|
||||
}
|
||||
|
||||
public boolean isChargeCalculationTypeChanged(){
|
||||
public boolean isChargeCalculationTypeChanged() {
|
||||
return this.requestParameters.contains("chargeCalculationType");
|
||||
}
|
||||
}
|
||||
+64
-18
@@ -1,31 +1,77 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.command;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Immutable command for any of the loan state transitions eg. reject, withdrawn by client, approve, disburse
|
||||
* Immutable command for any of the loan state transitions eg. reject, withdrawn
|
||||
* by client, approve, disburse
|
||||
*/
|
||||
public class LoanStateTransitionCommand {
|
||||
|
||||
private final Long loanId;
|
||||
private final LocalDate eventDate;
|
||||
private final String note;
|
||||
private final LocalDate eventDate;
|
||||
private final String note;
|
||||
private final LocalDate approvedOnDate;
|
||||
private final LocalDate rejectedOnDate;
|
||||
private final LocalDate withdrawnOnDate;
|
||||
private final LocalDate disbursedOnDate;
|
||||
|
||||
public LoanStateTransitionCommand(final Long loanId, final LocalDate eventDate, final String note) {
|
||||
this.loanId = loanId;
|
||||
this.eventDate = eventDate;
|
||||
this.note = note;
|
||||
}
|
||||
public LoanStateTransitionCommand(final LocalDate eventDate, final String note, final LocalDate approvedOnDate,
|
||||
final LocalDate rejectedOnDate, final LocalDate withdrawnOnDate, final LocalDate disbursedOnDate) {
|
||||
this.eventDate = eventDate;
|
||||
this.note = note;
|
||||
this.approvedOnDate = approvedOnDate;
|
||||
this.rejectedOnDate = rejectedOnDate;
|
||||
this.withdrawnOnDate = withdrawnOnDate;
|
||||
this.disbursedOnDate = disbursedOnDate;
|
||||
}
|
||||
|
||||
public Long getLoanId() {
|
||||
return loanId;
|
||||
}
|
||||
public LocalDate getApprovedOnDate() {
|
||||
LocalDate dateValue = this.approvedOnDate;
|
||||
if (this.approvedOnDate == null) {
|
||||
dateValue = eventDate;
|
||||
}
|
||||
return dateValue;
|
||||
}
|
||||
|
||||
public LocalDate getEventDate() {
|
||||
return eventDate;
|
||||
}
|
||||
public LocalDate getRejectedOnDate() {
|
||||
LocalDate dateValue = this.rejectedOnDate;
|
||||
if (this.rejectedOnDate == null) {
|
||||
dateValue = eventDate;
|
||||
}
|
||||
return dateValue;
|
||||
}
|
||||
|
||||
public String getNote() {
|
||||
return note;
|
||||
}
|
||||
public LocalDate getWithdrawnOnDate() {
|
||||
LocalDate dateValue = this.withdrawnOnDate;
|
||||
if (this.withdrawnOnDate == null) {
|
||||
dateValue = eventDate;
|
||||
}
|
||||
return dateValue;
|
||||
}
|
||||
|
||||
public LocalDate getDisbursedOnDate() {
|
||||
LocalDate dateValue = this.disbursedOnDate;
|
||||
if (this.disbursedOnDate == null) {
|
||||
dateValue = eventDate;
|
||||
}
|
||||
return dateValue;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transition");
|
||||
|
||||
baseDataValidator.reset().parameter("eventDate").value(this.eventDate).notNull();
|
||||
baseDataValidator.reset().parameter("note").value(this.note).notExceedingLengthOf(1000);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
|
||||
public class LoanStateTransitionCommandValidator {
|
||||
|
||||
private final LoanStateTransitionCommand command;
|
||||
|
||||
public LoanStateTransitionCommandValidator(LoanStateTransitionCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transition");
|
||||
|
||||
baseDataValidator.reset().parameter("loanId").value(command.getLoanId()).notNull().integerGreaterThanZero();
|
||||
baseDataValidator.reset().parameter("eventDate").value(command.getEventDate()).notNull();
|
||||
baseDataValidator.reset().parameter("note").value(command.getNote()).notExceedingLengthOf(1000);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+383
-149
@@ -2,9 +2,13 @@ package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
@@ -26,7 +30,11 @@ import org.apache.commons.lang.StringUtils;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
@@ -34,17 +42,24 @@ import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedExcep
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.fund.domain.Fund;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.loanaccount.command.LoanApplicationCommand;
|
||||
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
|
||||
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.domain.DefaultLoanScheduleGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanSchedule;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
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.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
|
||||
@Entity
|
||||
@@ -68,7 +83,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
@JoinColumn(name = "product_id")
|
||||
private LoanProduct loanProduct;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "fund_id", nullable = true)
|
||||
private Fund fund;
|
||||
@@ -87,11 +101,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
@Embedded
|
||||
private LoanProductRelatedDetail loanRepaymentScheduleDetail;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "term_frequency", nullable = false)
|
||||
private Integer termFrequency;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "term_period_frequency_enum", nullable = false)
|
||||
private Integer termPeriodFrequencyType;
|
||||
|
||||
@@ -178,6 +190,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
@Transient
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessor = new LoanRepaymentScheduleTransactionProcessorFactory();
|
||||
|
||||
@Deprecated
|
||||
public static Loan createNew(final Fund fund, final Staff loanOfficer,
|
||||
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProduct loanProduct, final Client client,
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges) {
|
||||
@@ -185,6 +198,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
loanCharges);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Loan createNew(final Fund fund, final Staff loanOfficer,
|
||||
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProduct loanProduct, final Group group,
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges) {
|
||||
@@ -192,13 +206,24 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
loanCharges);
|
||||
}
|
||||
|
||||
public static Loan newIndividualLoanApplication(final Client client, final LoanProduct loanProduct, final Fund fund,
|
||||
final Staff officer, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
|
||||
final Set<LoanCharge> loanCharges) {
|
||||
final LoanStatus status = null;
|
||||
LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
|
||||
return new Loan(client, fund, officer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, status, loanCharges);
|
||||
}
|
||||
|
||||
public static Loan newGroupLoanApplication(final Group group, final LoanProduct loanProduct, final Fund fund, final Staff officer,
|
||||
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
|
||||
final Set<LoanCharge> loanCharges) {
|
||||
final LoanStatus status = null;
|
||||
LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
|
||||
return new Loan(group, fund, officer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, status, loanCharges);
|
||||
}
|
||||
|
||||
protected Loan() {
|
||||
this.client = null;
|
||||
this.group = null;
|
||||
this.loanProduct = null;
|
||||
this.loanRepaymentScheduleDetail = null;
|
||||
this.charges = null;
|
||||
this.loanOfficerHistory = null;
|
||||
//
|
||||
}
|
||||
|
||||
private Loan(final Client client, Fund fund, Staff loanOfficer, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
||||
@@ -472,79 +497,31 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
return this.loanRepaymentScheduleDetail;
|
||||
}
|
||||
|
||||
public void modifyLoanApplication(final LoanApplicationCommand command, final Client client, final LoanProduct loanProduct,
|
||||
final Fund fund, final LoanTransactionProcessingStrategy strategy, final LoanScheduleData modifiedLoanSchedule,
|
||||
final Set<LoanCharge> charges, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
public void updateClient(final Client client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
// FIXME - KW - whilst the methods are named isXXXChanged, this really
|
||||
// only is asking has the parameter for that field been passed in
|
||||
// request,
|
||||
// an additional check to see if the value passed is different to the
|
||||
// current value may bee need for all these like it is for
|
||||
// expectedDisbursementDate
|
||||
if (command.isClientChanged()) {
|
||||
this.client = client;
|
||||
}
|
||||
if (command.isProductChanged()) {
|
||||
this.loanProduct = loanProduct;
|
||||
}
|
||||
if (command.isFundChanged()) {
|
||||
this.fund = fund;
|
||||
}
|
||||
if (command.isTransactionStrategyChanged()) {
|
||||
this.transactionProcessingStrategy = strategy;
|
||||
}
|
||||
public void updateLoanProduct(final LoanProduct loanProduct) {
|
||||
this.loanProduct = loanProduct;
|
||||
}
|
||||
|
||||
if (command.isTermFrequencyChanged()) {
|
||||
this.termFrequency = command.getLoanTermFrequency();
|
||||
}
|
||||
public void updateFund(final Fund fund) {
|
||||
this.fund = fund;
|
||||
}
|
||||
|
||||
if (command.isTermFrequencyTypeChanged()) {
|
||||
this.termPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getLoanTermFrequencyType()).getValue();
|
||||
}
|
||||
public void updateTransactionProcessingStrategy(final LoanTransactionProcessingStrategy strategy) {
|
||||
this.transactionProcessingStrategy = strategy;
|
||||
}
|
||||
|
||||
if (command.isSubmittedOnDateChanged()) {
|
||||
this.submittedOnDate = command.getSubmittedOnDate().toDate();
|
||||
}
|
||||
public void updateLoanCharges(final Set<LoanCharge> loanCharges) {
|
||||
this.charges.clear();
|
||||
this.charges.addAll(associateChargesWithThisLoan(loanCharges));
|
||||
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement();
|
||||
}
|
||||
|
||||
if (command.isExpectedDisbursementDatePassed()) {
|
||||
if (dateHasChanged(getExpectedDisbursedOnLocalDate(), command.getExpectedDisbursementDate())) {
|
||||
this.expectedDisbursedOnDate = command.getExpectedDisbursementDate().toDate();
|
||||
removeFirstDisbursementTransaction();
|
||||
disburse(command.getExpectedDisbursementDate(), loanLifecycleStateMachine, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.isRepaymentsStartingFromDateChanged()) {
|
||||
if (command.getRepaymentsStartingFromDate() != null) {
|
||||
this.expectedFirstRepaymentOnDate = command.getRepaymentsStartingFromDate().toDate();
|
||||
} else {
|
||||
this.expectedFirstRepaymentOnDate = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (command.isInterestChargedFromDateChanged()) {
|
||||
if (command.getInterestChargedFromDate() != null) {
|
||||
this.interestChargedFromDate = command.getInterestChargedFromDate().toDate();
|
||||
} else {
|
||||
this.interestChargedFromDate = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (command.isChargesChanged()) {
|
||||
this.charges.clear();
|
||||
this.charges.addAll(associateChargesWithThisLoan(charges));
|
||||
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement();
|
||||
}
|
||||
|
||||
// FIXME - kw - break modify loan application at present.
|
||||
// this.loanRepaymentScheduleDetail.update(command.toLoanProductCommand());
|
||||
|
||||
// FIXME - rewrite over loan schedule by default for now but worth
|
||||
// putting in check to see if required
|
||||
// i.e. only a client change wouldn't require it, only if one of
|
||||
// parameters related to loan schedule calculation is changed.
|
||||
public void updateLoanSchedule(final LoanScheduleData modifiedLoanSchedule) {
|
||||
this.repaymentScheduleInstallments.clear();
|
||||
|
||||
for (LoanSchedulePeriodData scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
|
||||
|
||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||
@@ -567,17 +544,147 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.repaymentScheduleInstallments, this.charges);
|
||||
}
|
||||
|
||||
private boolean dateHasChanged(final LocalDate originalLocalDate, final LocalDate providedLocalDate) {
|
||||
public void updateLoanScheduleDependentDerivedFields() {
|
||||
this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
|
||||
}
|
||||
|
||||
boolean dateHasChanged = false;
|
||||
public Map<String, Object> modifyLoanApplication(final JsonCommand command, final LoanChargeCommand[] loanChargeCommands,
|
||||
final AprCalculator aprCalculator) {
|
||||
|
||||
if (originalLocalDate != null) {
|
||||
dateHasChanged = !originalLocalDate.equals(providedLocalDate);
|
||||
} else {
|
||||
dateHasChanged = (providedLocalDate != null);
|
||||
final Map<String, Object> actualChanges = this.loanRepaymentScheduleDetail.updateLoanApplicationAttributes(command, aprCalculator);
|
||||
if (!actualChanges.isEmpty()) {
|
||||
boolean recalculateLoanSchedule = !(actualChanges.size() == 1 && actualChanges.containsKey("inArrearsTolerance"));
|
||||
actualChanges.put("recalculateLoanSchedule", recalculateLoanSchedule);
|
||||
}
|
||||
|
||||
return dateHasChanged;
|
||||
final String dateFormatAsInput = command.dateFormat();
|
||||
final String localeAsInput = command.locale();
|
||||
|
||||
final String externalIdParamName = "externalId";
|
||||
if (command.isChangeInStringParameterNamed(externalIdParamName, this.externalId)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(externalIdParamName);
|
||||
actualChanges.put(externalIdParamName, newValue);
|
||||
this.externalId = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
final String clientIdParamName = "clientId";
|
||||
if (command.isChangeInLongParameterNamed(clientIdParamName, this.client.getId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(clientIdParamName);
|
||||
actualChanges.put(clientIdParamName, newValue);
|
||||
}
|
||||
|
||||
final String productIdParamName = "productId";
|
||||
if (command.isChangeInLongParameterNamed(productIdParamName, this.loanProduct.getId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(productIdParamName);
|
||||
actualChanges.put(productIdParamName, newValue);
|
||||
actualChanges.put("recalculateLoanSchedule", true);
|
||||
}
|
||||
|
||||
Long existingFundId = null;
|
||||
if (this.fund != null) {
|
||||
existingFundId = this.fund.getId();
|
||||
}
|
||||
final String fundIdParamName = "fundId";
|
||||
if (command.isChangeInLongParameterNamed(fundIdParamName, existingFundId)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(fundIdParamName);
|
||||
actualChanges.put(fundIdParamName, newValue);
|
||||
}
|
||||
|
||||
final String strategyIdParamName = "transactionProcessingStrategyId";
|
||||
if (command.isChangeInLongParameterNamed(strategyIdParamName, this.transactionProcessingStrategy.getId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(strategyIdParamName);
|
||||
actualChanges.put(strategyIdParamName, newValue);
|
||||
}
|
||||
|
||||
final String chargesParamName = "charges";
|
||||
if (command.parameterExists(chargesParamName)) {
|
||||
LoanChargeCommand[] existingLoanChargeData = getLoanCharges();
|
||||
if (differenceExistsBetween(existingLoanChargeData, loanChargeCommands)) {
|
||||
actualChanges.put(chargesParamName, loanChargeCommands);
|
||||
actualChanges.put("recalculateLoanSchedule", true);
|
||||
}
|
||||
}
|
||||
|
||||
final String submittedOnDateParamName = "submittedOnDate";
|
||||
if (command.isChangeInLocalDateParameterNamed(submittedOnDateParamName, getSubmittedOnDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(submittedOnDateParamName);
|
||||
actualChanges.put(submittedOnDateParamName, valueAsInput);
|
||||
actualChanges.put("dateFormat", dateFormatAsInput);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(submittedOnDateParamName);
|
||||
this.submittedOnDate = newValue.toDate();
|
||||
}
|
||||
|
||||
final String expectedDisbursementDateParamName = "expectedDisbursementDate";
|
||||
if (command.isChangeInLocalDateParameterNamed(expectedDisbursementDateParamName, getExpectedDisbursedOnLocalDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(expectedDisbursementDateParamName);
|
||||
actualChanges.put(expectedDisbursementDateParamName, valueAsInput);
|
||||
actualChanges.put("dateFormat", dateFormatAsInput);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
actualChanges.put("recalculateLoanSchedule", true);
|
||||
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(expectedDisbursementDateParamName);
|
||||
this.expectedDisbursedOnDate = newValue.toDate();
|
||||
removeFirstDisbursementTransaction();
|
||||
disburse(getExpectedDisbursedOnLocalDate());
|
||||
}
|
||||
|
||||
final String repaymentsStartingFromDateParamName = "repaymentsStartingFromDate";
|
||||
if (command.isChangeInLocalDateParameterNamed(repaymentsStartingFromDateParamName, getExpectedFirstRepaymentOnDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(repaymentsStartingFromDateParamName);
|
||||
actualChanges.put(repaymentsStartingFromDateParamName, valueAsInput);
|
||||
actualChanges.put("dateFormat", dateFormatAsInput);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
actualChanges.put("recalculateLoanSchedule", true);
|
||||
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(repaymentsStartingFromDateParamName);
|
||||
if (newValue != null) {
|
||||
this.expectedFirstRepaymentOnDate = newValue.toDate();
|
||||
} else {
|
||||
this.expectedFirstRepaymentOnDate = null;
|
||||
}
|
||||
}
|
||||
|
||||
final String interestChargedFromDateParamName = "interestChargedFromDate";
|
||||
if (command.isChangeInLocalDateParameterNamed(interestChargedFromDateParamName, getInterestChargedFromDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(interestChargedFromDateParamName);
|
||||
actualChanges.put(interestChargedFromDateParamName, valueAsInput);
|
||||
actualChanges.put("dateFormat", dateFormatAsInput);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
actualChanges.put("recalculateLoanSchedule", true);
|
||||
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(interestChargedFromDateParamName);
|
||||
if (newValue != null) {
|
||||
this.interestChargedFromDate = newValue.toDate();
|
||||
} else {
|
||||
this.interestChargedFromDate = null;
|
||||
}
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
private boolean differenceExistsBetween(final LoanChargeCommand[] existingLoanChargeData, final LoanChargeCommand[] newLoanChargeData) {
|
||||
Arrays.sort(existingLoanChargeData);
|
||||
Arrays.sort(newLoanChargeData);
|
||||
return !Arrays.equals(existingLoanChargeData, newLoanChargeData);
|
||||
}
|
||||
|
||||
private LoanChargeCommand[] getLoanCharges() {
|
||||
|
||||
LoanChargeCommand[] existingLoanCharges = null;
|
||||
|
||||
if (this.charges != null && !this.charges.isEmpty()) {
|
||||
List<LoanChargeCommand> loanChargesList = new ArrayList<LoanChargeCommand>();
|
||||
for (LoanCharge loanCharge : this.charges) {
|
||||
loanChargesList.add(loanCharge.toCommand());
|
||||
}
|
||||
|
||||
existingLoanCharges = loanChargesList.toArray(new LoanChargeCommand[loanChargesList.size()]);
|
||||
}
|
||||
|
||||
return existingLoanCharges;
|
||||
}
|
||||
|
||||
private void removeFirstDisbursementTransaction() {
|
||||
@@ -589,9 +696,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
}
|
||||
|
||||
public void submitApplication(final Integer loanTermFrequency, final PeriodFrequencyType loanTermFrequencyType,
|
||||
final LocalDate submittedOn, final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate,
|
||||
final LocalDate interestChargedFromDate, final LoanLifecycleStateMachine lifecycleStateMachine) {
|
||||
public void loanApplicationSubmittal(final LoanSchedule loanSchedule, final LoanLifecycleStateMachine lifecycleStateMachine,
|
||||
final LocalDate submittedOn, final String externalId) {
|
||||
|
||||
final LoanScheduleData loanScheduleData = loanSchedule.generate();
|
||||
updateLoanSchedule(loanScheduleData);
|
||||
|
||||
LoanStatus from = null;
|
||||
if (this.loanStatus != null) {
|
||||
@@ -601,22 +710,22 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
LoanStatus statusEnum = lifecycleStateMachine.transition(LoanEvent.LOAN_CREATED, from);
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
|
||||
this.termFrequency = loanTermFrequency;
|
||||
this.termPeriodFrequencyType = loanTermFrequencyType.getValue();
|
||||
this.termFrequency = loanSchedule.getLoanTermFrequency();
|
||||
this.termPeriodFrequencyType = loanSchedule.getLoanTermPeriodFrequencyType().getValue();
|
||||
|
||||
this.submittedOnDate = submittedOn.toDate();
|
||||
this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
|
||||
if (expectedDisbursementDate != null) {
|
||||
// can be null during bulk upload of loans
|
||||
this.expectedDisbursedOnDate = expectedDisbursementDate.toDate();
|
||||
|
||||
if (loanSchedule.getDisbursementDate() != null) {
|
||||
this.expectedDisbursedOnDate = loanSchedule.getDisbursementDate().toDate();
|
||||
}
|
||||
|
||||
if (repaymentsStartingFromDate != null) {
|
||||
this.expectedFirstRepaymentOnDate = repaymentsStartingFromDate.toDate();
|
||||
if (loanSchedule.getRepaymentStartFromDate() != null) {
|
||||
this.expectedFirstRepaymentOnDate = loanSchedule.getRepaymentStartFromDate().toDate();
|
||||
}
|
||||
|
||||
if (interestChargedFromDate != null) {
|
||||
this.interestChargedFromDate = interestChargedFromDate.toDate();
|
||||
if (loanSchedule.getInterestChargedFromDate() != null) {
|
||||
this.interestChargedFromDate = loanSchedule.getInterestChargedFromDate().toDate();
|
||||
}
|
||||
|
||||
if (submittedOn.isAfter(new LocalDate())) {
|
||||
@@ -637,6 +746,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
|
||||
}
|
||||
}
|
||||
|
||||
this.externalId = externalId;
|
||||
}
|
||||
|
||||
private LocalDate determineExpectedMaturityDate() {
|
||||
@@ -644,13 +755,30 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
return this.repaymentScheduleInstallments.get(numberOfInstallments - 1).getDueDate();
|
||||
}
|
||||
|
||||
public void reject(final LocalDate rejectedOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
public Map<String, Object> loanApplicationRejection(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECTED, LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
|
||||
|
||||
this.rejectedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate();
|
||||
this.closedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate();
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECTED, LoanStatus.fromInt(this.loanStatus));
|
||||
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
||||
}
|
||||
|
||||
LocalDate rejectedOn = command.localDateValueOfParameterNamed("rejectedOnDate");
|
||||
if (rejectedOn == null) {
|
||||
rejectedOn = command.localDateValueOfParameterNamed("eventDate");
|
||||
}
|
||||
|
||||
final Locale locale = new Locale(command.locale());
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||
|
||||
this.rejectedOnDate = rejectedOn.toDate();
|
||||
this.closedOnDate = rejectedOn.toDate();
|
||||
actualChanges.put("locale", command.locale());
|
||||
actualChanges.put("dateFormat", command.dateFormat());
|
||||
actualChanges.put("rejectedOnDate", rejectedOn.toString(fmt));
|
||||
actualChanges.put("closedOnDate", rejectedOn.toString(fmt));
|
||||
|
||||
if (rejectedOn.isBefore(getSubmittedOnDate())) {
|
||||
final String errorMessage = "The date on which a loan is rejected cannot be before its submittal date: "
|
||||
@@ -662,34 +790,73 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
final String errorMessage = "The date on which a loan is rejected cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("reject", "cannot.be.a.future.date", errorMessage, rejectedOn);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public void withdraw(final LocalDate withdrawnOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
public Map<String, Object> loanApplicationWithdrawnByApplicant(final JsonCommand command,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAWN, LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
|
||||
|
||||
this.withdrawnOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate();
|
||||
this.closedOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate();
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAWN, LoanStatus.fromInt(this.loanStatus));
|
||||
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
||||
}
|
||||
|
||||
LocalDate withdrawnOn = command.localDateValueOfParameterNamed("withdrawnOnDate");
|
||||
if (withdrawnOn == null) {
|
||||
withdrawnOn = command.localDateValueOfParameterNamed("eventDate");
|
||||
}
|
||||
|
||||
final Locale locale = new Locale(command.locale());
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||
|
||||
this.withdrawnOnDate = withdrawnOn.toDate();
|
||||
this.closedOnDate = withdrawnOn.toDate();
|
||||
actualChanges.put("locale", command.locale());
|
||||
actualChanges.put("dateFormat", command.dateFormat());
|
||||
actualChanges.put("withdrawnOnDate", withdrawnOn.toString(fmt));
|
||||
actualChanges.put("closedOnDate", withdrawnOn.toString(fmt));
|
||||
|
||||
if (withdrawnOn.isBefore(getSubmittedOnDate())) {
|
||||
final String errorMessage = "The date on which a loan is withdrawn cannot be before its submittal date: "
|
||||
+ getSubmittedOnDate().toString();
|
||||
throw new InvalidLoanStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, withdrawnOn,
|
||||
throw new InvalidLoanStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, command,
|
||||
getSubmittedOnDate());
|
||||
}
|
||||
|
||||
if (withdrawnOn.isAfter(new LocalDate())) {
|
||||
final String errorMessage = "The date on which a loan is withdrawn cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("reject", "cannot.be.a.future.date", errorMessage, withdrawnOn);
|
||||
throw new InvalidLoanStateTransitionException("reject", "cannot.be.a.future.date", errorMessage, command);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public void approve(final LocalDate approvedOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
public Map<String, Object> loanApplicationApproval(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
|
||||
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
||||
}
|
||||
|
||||
this.approvedOnDate = approvedOn.toDateTimeAtCurrentTime().toDate();
|
||||
LocalDate approvedOn = command.localDateValueOfParameterNamed("approvedOnDate");
|
||||
if (approvedOn == null) {
|
||||
approvedOn = command.localDateValueOfParameterNamed("eventDate");
|
||||
}
|
||||
|
||||
final Locale locale = new Locale(command.locale());
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||
|
||||
this.approvedOnDate = approvedOn.toDate();
|
||||
actualChanges.put("locale", command.locale());
|
||||
actualChanges.put("dateFormat", command.dateFormat());
|
||||
actualChanges.put("approvedOnDate", approvedOn.toString(fmt));
|
||||
|
||||
final LocalDate submittalDate = new LocalDate(this.submittedOnDate);
|
||||
if (approvedOn.isBefore(submittalDate)) {
|
||||
@@ -708,47 +875,112 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.loanofficer, approvedOn);
|
||||
this.loanOfficerHistory.add(loanOfficerAssignmentHistory);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public void undoApproval(LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
public Map<String, Object> undoApproval(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVAL_UNDO,
|
||||
LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
|
||||
|
||||
final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus);
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVAL_UNDO, currentStatus);
|
||||
if (!statusEnum.hasStateOf(currentStatus)) {
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
||||
}
|
||||
|
||||
this.approvedOnDate = null;
|
||||
actualChanges.put("approvedOnDate", "");
|
||||
|
||||
this.loanOfficerHistory.clear();
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public void disburseWithModifiedRepaymentSchedule(final LocalDate disbursedOn,
|
||||
final List<LoanRepaymentScheduleInstallment> modifiedLoanRepaymentSchedule, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
this.repaymentScheduleInstallments.clear();
|
||||
for (LoanRepaymentScheduleInstallment modifiedInstallment : modifiedLoanRepaymentSchedule) {
|
||||
modifiedInstallment.updateLoan(this);
|
||||
this.repaymentScheduleInstallments.add(modifiedInstallment);
|
||||
}
|
||||
disburse(disbursedOn, loanLifecycleStateMachine, true);
|
||||
private void disburse(final LocalDate expectedDisbursedOnLocalDate) {
|
||||
this.disbursedOnDate = expectedDisbursedOnLocalDate.toDate();
|
||||
this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
|
||||
|
||||
handleDisbursementTransaction(expectedDisbursedOnLocalDate);
|
||||
}
|
||||
|
||||
public void disburse(final LocalDate disbursedOn, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final boolean statusTransition) {
|
||||
public Map<String, Object> disburse(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final ApplicationCurrency currency) {
|
||||
|
||||
if (statusTransition) {
|
||||
// ensure loan is in pre-disbursal state
|
||||
updateLoanToPreDisbursalState();
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
|
||||
|
||||
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, LoanStatus.fromInt(this.loanStatus));
|
||||
updateLoanToPreDisbursalState();
|
||||
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, LoanStatus.fromInt(this.loanStatus));
|
||||
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
||||
}
|
||||
|
||||
this.disbursedOnDate = disbursedOn.toDateTimeAtCurrentTime().toDate();
|
||||
this.expectedMaturityDate = this.repaymentScheduleInstallments.get(this.repaymentScheduleInstallments.size() - 1).getDueDate()
|
||||
.toDateMidnight().toDate();
|
||||
LocalDate disbursedOn = command.localDateValueOfParameterNamed("disbursedOnDate");
|
||||
if (disbursedOn == null) {
|
||||
disbursedOn = command.localDateValueOfParameterNamed("eventDate");
|
||||
}
|
||||
|
||||
// add disbursement transaction to track outgoing money from mfi to
|
||||
// client
|
||||
LoanTransaction loanTransaction = LoanTransaction.disbursement(this.loanRepaymentScheduleDetail.getPrincipal(), disbursedOn);
|
||||
final Locale locale = new Locale(command.locale());
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||
|
||||
this.disbursedOnDate = disbursedOn.toDate();
|
||||
this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
|
||||
|
||||
actualChanges.put("locale", command.locale());
|
||||
actualChanges.put("dateFormat", command.dateFormat());
|
||||
actualChanges.put("disbursedOnDate", disbursedOn.toString(fmt));
|
||||
actualChanges.put("expectedMaturityDate", LocalDate.fromDateFields(this.expectedDisbursedOnDate).toString(fmt));
|
||||
|
||||
handleDisbursementTransaction(disbursedOn);
|
||||
|
||||
if (isRepaymentScheduleRegenerationRequiredForDisbursement(disbursedOn)) {
|
||||
regenerateRepaymentSchedule(currency);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ability to regenerate the repayment schedule based on the loans current
|
||||
* details/state.
|
||||
*/
|
||||
private void regenerateRepaymentSchedule(final ApplicationCurrency applicationCurrency) {
|
||||
|
||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||
final LoanScheduleGenerator loanScheduleGenerator = new DefaultLoanScheduleGeneratorFactory().create(interestMethod);
|
||||
|
||||
final BigDecimal principal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount();
|
||||
final BigDecimal inArrearsTolerance = this.loanRepaymentScheduleDetail.getInArrearsTolerance().getAmount();
|
||||
final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod();
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
|
||||
final BigDecimal defaultAnnualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = this.loanRepaymentScheduleDetail
|
||||
.getInterestCalculationPeriodMethod();
|
||||
final Integer repaymentEvery = this.loanRepaymentScheduleDetail.getRepayEvery();
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
|
||||
final Integer numberOfRepayments = this.loanRepaymentScheduleDetail.getNumberOfRepayments();
|
||||
final AmortizationMethod amortizationMethod = this.loanRepaymentScheduleDetail.getAmortizationMethod();
|
||||
final Integer loanTermFrequency = this.termFrequency;
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType);
|
||||
|
||||
final LoanSchedule loanSchedule = new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, interestRatePerPeriod,
|
||||
interestRatePeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod,
|
||||
repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, loanTermFrequency,
|
||||
loanTermPeriodFrequencyType, this.charges, this.getDisbursementDate(), this.getExpectedFirstRepaymentOnDate(),
|
||||
this.getInterestChargedFromDate(), inArrearsTolerance);
|
||||
|
||||
final LoanScheduleData generatedData = loanSchedule.generate();
|
||||
|
||||
updateLoanSchedule(generatedData);
|
||||
}
|
||||
|
||||
private void handleDisbursementTransaction(LocalDate disbursedOn) {
|
||||
// track disbursement transaction
|
||||
final LoanTransaction loanTransaction = LoanTransaction.disbursement(this.loanRepaymentScheduleDetail.getPrincipal(), disbursedOn);
|
||||
loanTransaction.updateLoan(this);
|
||||
this.loanTransactions.add(loanTransaction);
|
||||
|
||||
@@ -769,7 +1001,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
}
|
||||
|
||||
if (disbursedOn.isBefore(getApprovedOnDate())) {
|
||||
if (getApprovedOnDate() != null && disbursedOn.isBefore(getApprovedOnDate())) {
|
||||
final String errorMessage = "The date on which a loan is disbursed cannot be before its approval date: "
|
||||
+ getApprovedOnDate().toString();
|
||||
throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.before.approval.date", errorMessage, disbursedOn,
|
||||
@@ -790,13 +1022,23 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
}
|
||||
|
||||
public void undoDisbursal(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
public Map<String, Object> undoDisbursal(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO,
|
||||
LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
|
||||
|
||||
final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus);
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, currentStatus);
|
||||
if (!statusEnum.hasStateOf(currentStatus)) {
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
||||
}
|
||||
|
||||
this.approvedOnDate = null;
|
||||
actualChanges.put("disbursedOnDate", "");
|
||||
|
||||
updateLoanToPreDisbursalState();
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
private void updateLoanToPreDisbursalState() {
|
||||
@@ -1489,14 +1731,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
|
||||
public boolean isRepaymentScheduleRegenerationRequiredForDisbursement(final LocalDate actualDisbursementDate) {
|
||||
|
||||
boolean regenerationRequired = false;
|
||||
|
||||
if (isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate)) {
|
||||
regenerationRequired = true;
|
||||
}
|
||||
|
||||
return regenerationRequired;
|
||||
return isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate);
|
||||
}
|
||||
|
||||
private Money getTotalPaidInRepayments() {
|
||||
@@ -1655,5 +1890,4 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
||||
+82
@@ -86,6 +86,14 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
return new LoanCharge(chargeDefinition, command, loanPrincipal);
|
||||
}
|
||||
|
||||
/*
|
||||
* loanPrincipal is required for charges that are percentage based
|
||||
*/
|
||||
public static LoanCharge createNewWithoutLoan(final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate specifiedDueDate) {
|
||||
return new LoanCharge(chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, specifiedDueDate);
|
||||
}
|
||||
|
||||
public static LoanCharge createNew(final Charge chargeDefinition) {
|
||||
return new LoanCharge(null, chargeDefinition);
|
||||
}
|
||||
@@ -94,6 +102,45 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
//
|
||||
}
|
||||
|
||||
public LoanCharge(final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate specifiedDueDate) {
|
||||
this.loan = null;
|
||||
this.charge = chargeDefinition;
|
||||
this.penaltyCharge = chargeDefinition.isPenalty();
|
||||
|
||||
this.chargeTime = chargeDefinition.getChargeTime();
|
||||
if (chargeTime != null) {
|
||||
this.chargeTime = chargeTime.getValue();
|
||||
}
|
||||
|
||||
if (ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.SPECIFIED_DUE_DATE)) {
|
||||
|
||||
if (specifiedDueDate == null) {
|
||||
final String defaultUserMessage = "Loan charge is missing specified due date";
|
||||
throw new LoanChargeWithoutMandatoryFieldException("loancharge", "specifiedDueDate", defaultUserMessage,
|
||||
chargeDefinition.getId(), chargeDefinition.getName());
|
||||
}
|
||||
|
||||
this.dueForCollectionAsOfDate = specifiedDueDate.toDate();
|
||||
} else {
|
||||
this.dueForCollectionAsOfDate = null;
|
||||
}
|
||||
|
||||
this.chargeCalculation = chargeDefinition.getChargeCalculation();
|
||||
if (chargeCalculation != null) {
|
||||
this.chargeCalculation = chargeCalculation.getValue();
|
||||
}
|
||||
|
||||
BigDecimal chargeAmount = chargeDefinition.getAmount();
|
||||
if (amount != null) {
|
||||
chargeAmount = amount;
|
||||
}
|
||||
|
||||
populateDerivedFields(loanPrincipal, chargeAmount);
|
||||
this.paid = determineIfFullyPaid();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public LoanCharge(final Charge chargeDefinition, final LoanChargeCommand command, final BigDecimal loanPrincipal) {
|
||||
this.loan = null;
|
||||
this.charge = chargeDefinition;
|
||||
@@ -133,6 +180,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
this.paid = determineIfFullyPaid();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private LoanCharge(final Loan loan, final Charge chargeDefinition, final LoanChargeCommand command) {
|
||||
this.loan = loan;
|
||||
this.charge = chargeDefinition;
|
||||
@@ -270,6 +318,40 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
public void update(final Loan loan) {
|
||||
this.loan = loan;
|
||||
}
|
||||
|
||||
public void update(final BigDecimal amount, final LocalDate specifiedDueDate, final BigDecimal loanPrincipal) {
|
||||
if (specifiedDueDate != null) {
|
||||
this.dueForCollectionAsOfDate = specifiedDueDate.toDate();
|
||||
}
|
||||
|
||||
if (amount != null) {
|
||||
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
|
||||
case INVALID:
|
||||
break;
|
||||
case FLAT:
|
||||
this.amount = amount;
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
this.percentage = amount;
|
||||
this.amountPercentageAppliedTo = loanPrincipal;
|
||||
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
|
||||
this.amountOutstanding = calculateOutstanding();
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
this.percentage = amount;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = amount;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void update(final LoanChargeCommand command, final BigDecimal loanPrincipal) {
|
||||
|
||||
|
||||
+84
-84
@@ -4,106 +4,106 @@ package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
* Enum representation of loan status states.
|
||||
*/
|
||||
public enum LoanStatus {
|
||||
|
||||
INVALID(0, "loanStatusType.invalid"), //
|
||||
SUBMITED_AND_PENDING_APPROVAL(100, "loanStatusType.submitted.and.pending.approval"), //
|
||||
APPROVED(200, "loanStatusType.approved"), //
|
||||
ACTIVE(300, "loanStatusType.active"), //
|
||||
WITHDRAWN_BY_CLIENT(400, "loanStatusType.withdrawn.by.client"), //
|
||||
REJECTED(500, "loanStatusType.rejected"), //
|
||||
CLOSED_OBLIGATIONS_MET(600, "loanStatusType.closed.obligations.met"), //
|
||||
CLOSED_WRITTEN_OFF(601, "loanStatusType.closed.written.off"), //
|
||||
CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT(602, "loanStatusType.closed.reschedule.outstanding.amount"), //
|
||||
OVERPAID(700, "loanStatusType.overpaid");
|
||||
|
||||
INVALID(0, "loanStatusType.invalid"), //
|
||||
SUBMITED_AND_PENDING_APPROVAL(100, "loanStatusType.submitted.and.pending.approval"), //
|
||||
APPROVED(200, "loanStatusType.approved"), //
|
||||
ACTIVE(300, "loanStatusType.active"), //
|
||||
WITHDRAWN_BY_CLIENT(400, "loanStatusType.withdrawn.by.client"), //
|
||||
REJECTED(500, "loanStatusType.rejected"), //
|
||||
CLOSED_OBLIGATIONS_MET(600, "loanStatusType.closed.obligations.met"), //
|
||||
CLOSED_WRITTEN_OFF(601, "loanStatusType.closed.written.off"), //
|
||||
CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT(602, "loanStatusType.closed.reschedule.outstanding.amount"), //
|
||||
OVERPAID(700, "loanStatusType.overpaid");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
public static LoanStatus fromInt(final Integer statusValue) {
|
||||
private final String code;
|
||||
|
||||
LoanStatus enumeration = LoanStatus.INVALID;
|
||||
switch (statusValue) {
|
||||
case 100:
|
||||
enumeration = LoanStatus.SUBMITED_AND_PENDING_APPROVAL;
|
||||
break;
|
||||
case 200:
|
||||
enumeration = LoanStatus.APPROVED;
|
||||
break;
|
||||
case 300:
|
||||
enumeration = LoanStatus.ACTIVE;
|
||||
break;
|
||||
case 400:
|
||||
enumeration = LoanStatus.WITHDRAWN_BY_CLIENT;
|
||||
break;
|
||||
case 500:
|
||||
enumeration = LoanStatus.REJECTED;
|
||||
break;
|
||||
case 600:
|
||||
enumeration = LoanStatus.CLOSED_OBLIGATIONS_MET;
|
||||
break;
|
||||
case 601:
|
||||
enumeration = LoanStatus.CLOSED_WRITTEN_OFF;
|
||||
break;
|
||||
case 602:
|
||||
enumeration = LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT;
|
||||
break;
|
||||
case 700:
|
||||
enumeration = LoanStatus.OVERPAID;
|
||||
break;
|
||||
}
|
||||
return enumeration;
|
||||
public static LoanStatus fromInt(final Integer statusValue) {
|
||||
|
||||
LoanStatus enumeration = LoanStatus.INVALID;
|
||||
switch (statusValue) {
|
||||
case 100:
|
||||
enumeration = LoanStatus.SUBMITED_AND_PENDING_APPROVAL;
|
||||
break;
|
||||
case 200:
|
||||
enumeration = LoanStatus.APPROVED;
|
||||
break;
|
||||
case 300:
|
||||
enumeration = LoanStatus.ACTIVE;
|
||||
break;
|
||||
case 400:
|
||||
enumeration = LoanStatus.WITHDRAWN_BY_CLIENT;
|
||||
break;
|
||||
case 500:
|
||||
enumeration = LoanStatus.REJECTED;
|
||||
break;
|
||||
case 600:
|
||||
enumeration = LoanStatus.CLOSED_OBLIGATIONS_MET;
|
||||
break;
|
||||
case 601:
|
||||
enumeration = LoanStatus.CLOSED_WRITTEN_OFF;
|
||||
break;
|
||||
case 602:
|
||||
enumeration = LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT;
|
||||
break;
|
||||
case 700:
|
||||
enumeration = LoanStatus.OVERPAID;
|
||||
break;
|
||||
}
|
||||
return enumeration;
|
||||
}
|
||||
|
||||
private LoanStatus(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
|
||||
public boolean hasStateOf(final LoanStatus state) {
|
||||
return this.value.equals(state.getValue());
|
||||
}
|
||||
return this.value.equals(state.getValue());
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
public boolean isSubmittedAndPendingApproval() {
|
||||
return this.value.equals(LoanStatus.SUBMITED_AND_PENDING_APPROVAL.getValue());
|
||||
}
|
||||
|
||||
public boolean isApproved() {
|
||||
return this.value.equals(LoanStatus.APPROVED.getValue());
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this.value.equals(LoanStatus.ACTIVE.getValue());
|
||||
}
|
||||
public boolean isSubmittedAndPendingApproval() {
|
||||
return this.value.equals(LoanStatus.SUBMITED_AND_PENDING_APPROVAL.getValue());
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return isClosedObligationsMet() || isClosedWrittenOff() || isClosedWithOutsandingAmountMarkedForReschedule();
|
||||
}
|
||||
|
||||
public boolean isClosedObligationsMet() {
|
||||
return this.value.equals(LoanStatus.CLOSED_OBLIGATIONS_MET.getValue());
|
||||
}
|
||||
|
||||
public boolean isClosedWrittenOff() {
|
||||
return this.value.equals(LoanStatus.CLOSED_WRITTEN_OFF.getValue());
|
||||
}
|
||||
public boolean isApproved() {
|
||||
return this.value.equals(LoanStatus.APPROVED.getValue());
|
||||
}
|
||||
|
||||
public boolean isClosedWithOutsandingAmountMarkedForReschedule() {
|
||||
return this.value.equals(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT.getValue());
|
||||
}
|
||||
|
||||
public boolean isWithdrawnByClient() {
|
||||
return this.value.equals(LoanStatus.WITHDRAWN_BY_CLIENT.getValue());
|
||||
}
|
||||
public boolean isActive() {
|
||||
return this.value.equals(LoanStatus.ACTIVE.getValue());
|
||||
}
|
||||
|
||||
public boolean isRejected() {
|
||||
return this.value.equals(LoanStatus.REJECTED.getValue());
|
||||
}
|
||||
public boolean isClosed() {
|
||||
return isClosedObligationsMet() || isClosedWrittenOff() || isClosedWithOutsandingAmountMarkedForReschedule();
|
||||
}
|
||||
|
||||
public boolean isClosedObligationsMet() {
|
||||
return this.value.equals(LoanStatus.CLOSED_OBLIGATIONS_MET.getValue());
|
||||
}
|
||||
|
||||
public boolean isClosedWrittenOff() {
|
||||
return this.value.equals(LoanStatus.CLOSED_WRITTEN_OFF.getValue());
|
||||
}
|
||||
|
||||
public boolean isClosedWithOutsandingAmountMarkedForReschedule() {
|
||||
return this.value.equals(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT.getValue());
|
||||
}
|
||||
|
||||
public boolean isWithdrawnByClient() {
|
||||
return this.value.equals(LoanStatus.WITHDRAWN_BY_CLIENT.getValue());
|
||||
}
|
||||
|
||||
public boolean isRejected() {
|
||||
return this.value.equals(LoanStatus.REJECTED.getValue());
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class ApplicantWithdrawsFromLoanApplicationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public ApplicantWithdrawsFromLoanApplicationCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.applicantWithdrawsFromLoanApplication(command.resourceId(), command);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class ApproveLoanApplicationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public ApproveLoanApplicationCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.approveLoanApplication(command.resourceId(), command);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class CreateLoanApplicationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CreateLoanApplicationCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.submitLoanApplication(command);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class DeleteLoanApplicationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public DeleteLoanApplicationCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.deleteLoan(command.resourceId());
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class DisburseLoanCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public DisburseLoanCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.disburseLoan(command.resourceId(), command);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class RejectLoanApplicationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public RejectLoanApplicationCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.rejectLoanApplication(command.resourceId(), command);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class UndoApprovalLoanApplicationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UndoApprovalLoanApplicationCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.undoLoanApplicationApproval(command.resourceId(), command);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class UndoDisbursalLoanCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UndoDisbursalLoanCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.undoLoanDisbursal(command.resourceId(), command);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.EntityIdentifier;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class UpdateLoanApplicationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UpdateLoanApplicationCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.modifyLoanApplication(command.resourceId(), command);
|
||||
}
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.command;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
|
||||
|
||||
/**
|
||||
* Immutable command used when auto-calculating loan schedules based on loan terms.
|
||||
*/
|
||||
public class CalculateLoanScheduleCommand {
|
||||
|
||||
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 CalculateLoanScheduleCommand(
|
||||
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;
|
||||
}
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
|
||||
public class CalculateLoanScheduleCommandValidator {
|
||||
|
||||
private final CalculateLoanScheduleCommand command;
|
||||
|
||||
public CalculateLoanScheduleCommandValidator(CalculateLoanScheduleCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
|
||||
baseDataValidator.reset().parameter("principal").value(command.getPrincipal()).notNull().positiveAmount();
|
||||
|
||||
baseDataValidator.reset().parameter("loanTermFrequency").value(command.getLoanTermFrequency()).notNull().integerGreaterThanZero();
|
||||
baseDataValidator.reset().parameter("loanTermFrequencyType").value(command.getLoanTermFrequencyType()).notNull().inMinMaxRange(0, 4);
|
||||
|
||||
baseDataValidator.reset().parameter("numberOfRepayments").value(command.getNumberOfRepayments()).notNull().integerGreaterThanZero();
|
||||
baseDataValidator.reset().parameter("repaymentEvery").value(command.getRepaymentEvery()).notNull().integerGreaterThanZero();
|
||||
baseDataValidator.reset().parameter("repaymentFrequencyType").value(command.getRepaymentFrequencyType()).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 (command.getLoanTermFrequencyType() != null && !command.getLoanTermFrequencyType().equals(command.getRepaymentFrequencyType())) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.loanTermFrequencyType.not.the.same.as.repaymentFrequencyType",
|
||||
"The parameters loanTermFrequencyType and repaymentFrequencyType must be the same.", "loanTermFrequencyType",
|
||||
command.getLoanTermFrequencyType(), command.getRepaymentFrequencyType());
|
||||
dataValidationErrors.add(error);
|
||||
} else {
|
||||
if (command.getLoanTermFrequency() != null && command.getRepaymentEvery() != null && command.getNumberOfRepayments() != null) {
|
||||
int suggestsedLoanTerm = command.getRepaymentEvery() * command.getNumberOfRepayments();
|
||||
if (command.getLoanTermFrequency().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",
|
||||
command.getLoanTermFrequency(), command.getNumberOfRepayments(), command.getRepaymentEvery());
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(command.getInterestRatePerPeriod()).notNull();
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").value(command.getInterestRateFrequencyType()).notNull().inMinMaxRange(0, 3);
|
||||
baseDataValidator.reset().parameter("amortizationType").value(command.getAmortizationType()).notNull().inMinMaxRange(0, 1);
|
||||
baseDataValidator.reset().parameter("interestType").value(command.getInterestType()).notNull().inMinMaxRange(0, 1);
|
||||
baseDataValidator.reset().parameter("interestCalculationPeriodType").value(command.getInterestCalculationPeriodType()).notNull().inMinMaxRange(0, 1);
|
||||
|
||||
baseDataValidator.reset().parameter("expectedDisbursementDate").value(command.getExpectedDisbursementDate()).notNull();
|
||||
|
||||
if (command.getExpectedDisbursementDate() != null) {
|
||||
if (command.getRepaymentsStartingFromDate() != null
|
||||
&& command.getExpectedDisbursementDate().isAfter(command.getRepaymentsStartingFromDate())) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.expectedDisbursementDate.cannot.be.after.first.repayment.date",
|
||||
"The parameter expectedDisbursementDate has a date which falls after the given first repayment date.", "expectedDisbursementDate",
|
||||
command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate());
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.getRepaymentsStartingFromDate() != null && command.getInterestChargedFromDate() == 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", command.getRepaymentsStartingFromDate());
|
||||
dataValidationErrors.add(error);
|
||||
} else if (command.getRepaymentsStartingFromDate() == null && command.getInterestChargedFromDate() != null) {
|
||||
|
||||
// validate interestCalculatedFromDate is after or on repaymentsStartingFromDate
|
||||
if (command.getExpectedDisbursementDate().isAfter(command.getInterestChargedFromDate())) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.interestChargedFromDate.cannot.be.before.disbursement.date",
|
||||
"The parameter interestCalculatedFromDate cannot be before the date given for expected disbursement.", "interestChargedFromDate", command.getInterestChargedFromDate(), command.getExpectedDisbursementDate());
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
|
||||
// if (command.getCharges() != null) {
|
||||
// for (LoanChargeCommand loanCharge : command.getCharges()) {
|
||||
// try {
|
||||
// LoanChargeCommandValidator validator = new LoanChargeCommandValidator(loanCharge);
|
||||
// validator.validateForCreate();
|
||||
// } catch (PlatformApiDataValidationException e) {
|
||||
// dataValidationErrors.addAll(e.getErrors());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+267
-284
@@ -5,301 +5,284 @@ import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
|
||||
|
||||
/**
|
||||
* Immutable data object to represent aspects of a loan schedule such as:
|
||||
*
|
||||
* <ul>
|
||||
* <li>summary information - the totals for each part of schedule monitored</li>
|
||||
* <li>repayment schedule - the principal due, outstanding balance and cost of loan items such as interest and charges</li>
|
||||
* <li>summary information - the totals for each part of schedule monitored</li>
|
||||
* <li>repayment schedule - the principal due, outstanding balance and cost of
|
||||
* loan items such as interest and charges</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class LoanScheduleData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Integer loanTermInDays;
|
||||
|
||||
/**
|
||||
* The currency associated with all monetary values in loan schedule.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final CurrencyData currency;
|
||||
|
||||
/**
|
||||
* The cumulative total of all money (principal) disbursed to the loan applicant.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePrincipalDisbursed;
|
||||
|
||||
/**
|
||||
* The cumulative total of all <code>principalDue</code> for each period of loan schedule. (originalPrincipal)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePrincipalDue;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePrincipalPaid;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePrincipalWrittenOff;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePrincipalOutstanding;
|
||||
|
||||
/**
|
||||
* The cumulative total of interest expected on any principal disbursed. (originalInterest)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeInterestExpected;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeInterestPaid;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeInterestWaived;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeInterestWrittenOff;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeInterestOutstanding;
|
||||
|
||||
/**
|
||||
* The cumulative total of all fee charges applied on the loan to date.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeFeeChargesToDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeFeeChargesPaid;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeFeeChargesWaived;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeFeeChargesWrittenOff;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeFeeChargesOutstanding;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePenaltyChargesToDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePenaltyChargesPaid;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePenaltyChargesWaived;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePenaltyChargesWrittenOff;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePenaltyChargesOutstanding;
|
||||
|
||||
/**
|
||||
* The cumulative total of all costs on the loan.
|
||||
* Costs tracked are:
|
||||
* <ul>
|
||||
* <li>Interest</li>
|
||||
* <li>Charges (fees and penalties)</li>
|
||||
* </ul>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalCostOfLoan;
|
||||
|
||||
/**
|
||||
* The sum of <code>principalDisbursed</code> and <code>totalCostOfLoan</code>. (originalTotal)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalExpectedRepayment;
|
||||
|
||||
/**
|
||||
* The cumulative sum of all repayments to date. (totalPaid)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalPaidToDate;
|
||||
|
||||
/**
|
||||
* The cumulative sum of all waivers to date.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalWaivedToDate;
|
||||
|
||||
/**
|
||||
* The cumulative sum of all waivers to date.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalWrittenOffToDate;
|
||||
|
||||
/**
|
||||
* The cumulative sum of all principal, interest & charges outstanding.
|
||||
*/
|
||||
private final BigDecimal totalOutstanding;
|
||||
|
||||
/**
|
||||
* The amount by which this loan is is arrears.
|
||||
*
|
||||
* Different to outstanding as arrears is based on whether a portion of the total outstanding is late and above any tolerance setting.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalInArrears;
|
||||
|
||||
/**
|
||||
* <code>periods</code> is collection of data objects containing specific
|
||||
* information to each period of the loan schedule including disbursement and
|
||||
* repayment information.
|
||||
*/
|
||||
private final Collection<LoanSchedulePeriodData> periods;
|
||||
@SuppressWarnings("unused")
|
||||
private final Integer loanTermInDays;
|
||||
|
||||
public LoanScheduleData() {
|
||||
this.cumulativePrincipalDisbursed = null;
|
||||
this.cumulativePrincipalPaid = null;
|
||||
this.cumulativePrincipalWrittenOff = null;
|
||||
this.cumulativePrincipalDue = null;
|
||||
this.cumulativePrincipalOutstanding = null;
|
||||
|
||||
this.cumulativeInterestExpected = null;
|
||||
this.cumulativeInterestPaid = null;
|
||||
this.cumulativeInterestWaived = null;
|
||||
this.cumulativeInterestWrittenOff = null;
|
||||
this.cumulativeInterestOutstanding = null;
|
||||
|
||||
this.cumulativeFeeChargesToDate = null;
|
||||
this.cumulativeFeeChargesPaid = null;
|
||||
this.cumulativeFeeChargesWaived = null;
|
||||
this.cumulativeFeeChargesWrittenOff = null;
|
||||
this.cumulativeFeeChargesOutstanding = null;
|
||||
|
||||
this.cumulativePenaltyChargesToDate = null;
|
||||
this.cumulativePenaltyChargesPaid = null;
|
||||
this.cumulativePenaltyChargesWaived = null;
|
||||
this.cumulativePenaltyChargesWrittenOff = null;
|
||||
this.cumulativePenaltyChargesOutstanding = null;
|
||||
|
||||
this.totalCostOfLoan = null;
|
||||
|
||||
this.totalExpectedRepayment = null;
|
||||
this.totalPaidToDate = null;
|
||||
this.totalWaivedToDate = null;
|
||||
this.totalWrittenOffToDate = null;
|
||||
this.totalOutstanding = null;
|
||||
this.totalInArrears = null;
|
||||
|
||||
this.currency = null;
|
||||
this.periods = null;
|
||||
this.loanTermInDays = null;
|
||||
}
|
||||
/**
|
||||
* The currency associated with all monetary values in loan schedule.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final CurrencyData currency;
|
||||
|
||||
public LoanScheduleData(
|
||||
final CurrencyData currency,
|
||||
final Collection<LoanSchedulePeriodData> periods,
|
||||
final Integer loanTermInDays,
|
||||
final BigDecimal cumulativePrincipalDisbursed,
|
||||
final BigDecimal cumulativePrincipalDue,
|
||||
final BigDecimal cumulativePrincipalOutstanding,
|
||||
final BigDecimal cumulativeInterestExpected,
|
||||
final BigDecimal cumulativeFeeChargesToDate,
|
||||
final BigDecimal totalExpectedRepayment) {
|
||||
this.currency = currency;
|
||||
this.periods = periods;
|
||||
this.loanTermInDays = loanTermInDays;
|
||||
|
||||
this.cumulativePrincipalDisbursed = cumulativePrincipalDisbursed;
|
||||
this.cumulativePrincipalDue = cumulativePrincipalDue;
|
||||
this.cumulativePrincipalPaid = null;
|
||||
this.cumulativePrincipalWrittenOff = null;
|
||||
this.cumulativePrincipalOutstanding = cumulativePrincipalOutstanding;
|
||||
|
||||
this.cumulativeInterestExpected = cumulativeInterestExpected;
|
||||
this.cumulativeInterestPaid = null;
|
||||
this.cumulativeInterestWaived = null;
|
||||
this.cumulativeInterestWrittenOff = null;
|
||||
this.cumulativeInterestOutstanding = null;
|
||||
|
||||
this.cumulativeFeeChargesToDate = cumulativeFeeChargesToDate;
|
||||
this.cumulativeFeeChargesPaid = null;
|
||||
this.cumulativeFeeChargesWaived = null;
|
||||
this.cumulativeFeeChargesWrittenOff = null;
|
||||
this.cumulativeFeeChargesOutstanding = null;
|
||||
|
||||
this.cumulativePenaltyChargesToDate = null;
|
||||
this.cumulativePenaltyChargesPaid = null;
|
||||
this.cumulativePenaltyChargesWaived = null;
|
||||
this.cumulativePenaltyChargesWrittenOff = null;
|
||||
this.cumulativePenaltyChargesOutstanding = null;
|
||||
|
||||
if (cumulativeFeeChargesToDate != null) {
|
||||
this.totalCostOfLoan = cumulativeInterestExpected.add(cumulativeFeeChargesToDate);
|
||||
} else if (cumulativeInterestExpected != null) {
|
||||
this.totalCostOfLoan = cumulativeInterestExpected;
|
||||
} else {
|
||||
this.totalCostOfLoan = null;
|
||||
}
|
||||
|
||||
this.totalExpectedRepayment = totalExpectedRepayment;
|
||||
this.totalPaidToDate = BigDecimal.ZERO;
|
||||
this.totalWaivedToDate = BigDecimal.ZERO;
|
||||
this.totalWrittenOffToDate = BigDecimal.ZERO;
|
||||
this.totalOutstanding = totalExpectedRepayment;
|
||||
this.totalInArrears = BigDecimal.ZERO;
|
||||
}
|
||||
/**
|
||||
* The cumulative total of all money (principal) disbursed to the loan
|
||||
* applicant.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePrincipalDisbursed;
|
||||
|
||||
/*
|
||||
* Used when fully populating
|
||||
*/
|
||||
public LoanScheduleData(final CurrencyData currency, final Collection<LoanSchedulePeriodData> periods,
|
||||
final Integer loanTermInDays,
|
||||
final BigDecimal cumulativePrincipalDisbursed,
|
||||
final BigDecimal cumulativePrincipalDue,
|
||||
final BigDecimal cumulativePrincipalPaid,
|
||||
final BigDecimal cumulativePrincipalWrittenOff,
|
||||
final BigDecimal cumulativePrincipalOutstanding,
|
||||
final BigDecimal cumulativeInterestExpected,
|
||||
final BigDecimal cumulativeInterestPaid,
|
||||
final BigDecimal cumulativeInterestWaived,
|
||||
final BigDecimal cumulativeInterestWrittenOff,
|
||||
final BigDecimal cumulativeInterestOutstanding,
|
||||
final BigDecimal cumulativeFeeChargesToDate,
|
||||
final BigDecimal cumulativeFeeChargesPaid,
|
||||
final BigDecimal cumulativeFeeChargesWaived,
|
||||
final BigDecimal cumulativeFeeChargesWrittenOff,
|
||||
final BigDecimal cumulativeFeeChargesOutstanding,
|
||||
final BigDecimal cumulativePenaltyChargesToDate,
|
||||
final BigDecimal cumulativePenaltyChargesPaid,
|
||||
final BigDecimal cumulativePenaltyChargesWaived,
|
||||
final BigDecimal cumulativePenaltyChargesWrittenOff,
|
||||
final BigDecimal cumulativePenaltyChargesOutstanding,
|
||||
final BigDecimal totalCostOfLoan,
|
||||
final BigDecimal totalExpectedRepayment,
|
||||
final BigDecimal totalPaidToDate,
|
||||
final BigDecimal totalWaivedToDate,
|
||||
final BigDecimal totalWrittenOffToDate,
|
||||
final BigDecimal totalOutstanding,
|
||||
final BigDecimal totalInArrears) {
|
||||
this.currency = currency;
|
||||
this.periods = periods;
|
||||
this.loanTermInDays = loanTermInDays;
|
||||
|
||||
this.cumulativePrincipalDisbursed = cumulativePrincipalDisbursed;
|
||||
this.cumulativePrincipalDue = cumulativePrincipalDue;
|
||||
this.cumulativePrincipalPaid = cumulativePrincipalPaid;
|
||||
this.cumulativePrincipalWrittenOff = cumulativePrincipalWrittenOff;
|
||||
this.cumulativePrincipalOutstanding = cumulativePrincipalOutstanding;
|
||||
this.cumulativeInterestExpected = cumulativeInterestExpected;
|
||||
this.cumulativeInterestPaid = cumulativeInterestPaid;
|
||||
this.cumulativeInterestWrittenOff = cumulativeInterestWrittenOff;
|
||||
this.cumulativeInterestWaived = cumulativeInterestWaived;
|
||||
this.cumulativeInterestOutstanding = cumulativeInterestOutstanding;
|
||||
this.cumulativeFeeChargesToDate = cumulativeFeeChargesToDate;
|
||||
this.cumulativeFeeChargesPaid = cumulativeFeeChargesPaid;
|
||||
this.cumulativeFeeChargesWaived = cumulativeFeeChargesWaived;
|
||||
this.cumulativeFeeChargesWrittenOff = cumulativeFeeChargesWrittenOff;
|
||||
this.cumulativeFeeChargesOutstanding = cumulativeFeeChargesOutstanding;
|
||||
this.cumulativePenaltyChargesToDate = cumulativePenaltyChargesToDate;
|
||||
this.cumulativePenaltyChargesPaid = cumulativePenaltyChargesPaid;
|
||||
this.cumulativePenaltyChargesWaived = cumulativePenaltyChargesWaived;
|
||||
this.cumulativePenaltyChargesWrittenOff = cumulativePenaltyChargesWrittenOff;
|
||||
this.cumulativePenaltyChargesOutstanding = cumulativePenaltyChargesOutstanding;
|
||||
this.totalCostOfLoan = totalCostOfLoan;
|
||||
this.totalExpectedRepayment = totalExpectedRepayment;
|
||||
this.totalPaidToDate = totalPaidToDate;
|
||||
this.totalWaivedToDate = totalWaivedToDate;
|
||||
this.totalWrittenOffToDate = totalWrittenOffToDate;
|
||||
this.totalOutstanding = totalOutstanding;
|
||||
this.totalInArrears = totalInArrears;
|
||||
}
|
||||
/**
|
||||
* The cumulative total of all <code>principalDue</code> for each period of
|
||||
* loan schedule. (originalPrincipal)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePrincipalDue;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePrincipalPaid;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePrincipalWrittenOff;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePrincipalOutstanding;
|
||||
|
||||
public Collection<LoanSchedulePeriodData> getPeriods() {
|
||||
return periods;
|
||||
}
|
||||
/**
|
||||
* The cumulative total of interest expected on any principal disbursed.
|
||||
* (originalInterest)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeInterestExpected;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeInterestPaid;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeInterestWaived;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeInterestWrittenOff;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeInterestOutstanding;
|
||||
|
||||
public BigDecimal totalOutstanding() {
|
||||
return this.totalOutstanding;
|
||||
}
|
||||
/**
|
||||
* The cumulative total of all fee charges applied on the loan to date.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeFeeChargesToDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeFeeChargesPaid;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeFeeChargesWaived;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeFeeChargesWrittenOff;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativeFeeChargesOutstanding;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePenaltyChargesToDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePenaltyChargesPaid;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePenaltyChargesWaived;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePenaltyChargesWrittenOff;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal cumulativePenaltyChargesOutstanding;
|
||||
|
||||
/**
|
||||
* The cumulative total of all costs on the loan. Costs tracked are:
|
||||
* <ul>
|
||||
* <li>Interest</li>
|
||||
* <li>Charges (fees and penalties)</li>
|
||||
* </ul>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalCostOfLoan;
|
||||
|
||||
/**
|
||||
* The sum of <code>principalDisbursed</code> and
|
||||
* <code>totalCostOfLoan</code>. (originalTotal)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalExpectedRepayment;
|
||||
|
||||
/**
|
||||
* The cumulative sum of all repayments to date. (totalPaid)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalPaidToDate;
|
||||
|
||||
/**
|
||||
* The cumulative sum of all waivers to date.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalWaivedToDate;
|
||||
|
||||
/**
|
||||
* The cumulative sum of all waivers to date.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalWrittenOffToDate;
|
||||
|
||||
/**
|
||||
* The cumulative sum of all principal, interest & charges outstanding.
|
||||
*/
|
||||
private final BigDecimal totalOutstanding;
|
||||
|
||||
/**
|
||||
* The amount by which this loan is is arrears.
|
||||
*
|
||||
* Different to outstanding as arrears is based on whether a portion of the
|
||||
* total outstanding is late and above any tolerance setting.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalInArrears;
|
||||
|
||||
/**
|
||||
* <code>periods</code> is collection of data objects containing specific
|
||||
* information to each period of the loan schedule including disbursement
|
||||
* and repayment information.
|
||||
*/
|
||||
private final Collection<LoanSchedulePeriodData> periods;
|
||||
|
||||
public LoanScheduleData() {
|
||||
this.cumulativePrincipalDisbursed = null;
|
||||
this.cumulativePrincipalPaid = null;
|
||||
this.cumulativePrincipalWrittenOff = null;
|
||||
this.cumulativePrincipalDue = null;
|
||||
this.cumulativePrincipalOutstanding = null;
|
||||
|
||||
this.cumulativeInterestExpected = null;
|
||||
this.cumulativeInterestPaid = null;
|
||||
this.cumulativeInterestWaived = null;
|
||||
this.cumulativeInterestWrittenOff = null;
|
||||
this.cumulativeInterestOutstanding = null;
|
||||
|
||||
this.cumulativeFeeChargesToDate = null;
|
||||
this.cumulativeFeeChargesPaid = null;
|
||||
this.cumulativeFeeChargesWaived = null;
|
||||
this.cumulativeFeeChargesWrittenOff = null;
|
||||
this.cumulativeFeeChargesOutstanding = null;
|
||||
|
||||
this.cumulativePenaltyChargesToDate = null;
|
||||
this.cumulativePenaltyChargesPaid = null;
|
||||
this.cumulativePenaltyChargesWaived = null;
|
||||
this.cumulativePenaltyChargesWrittenOff = null;
|
||||
this.cumulativePenaltyChargesOutstanding = null;
|
||||
|
||||
this.totalCostOfLoan = null;
|
||||
|
||||
this.totalExpectedRepayment = null;
|
||||
this.totalPaidToDate = null;
|
||||
this.totalWaivedToDate = null;
|
||||
this.totalWrittenOffToDate = null;
|
||||
this.totalOutstanding = null;
|
||||
this.totalInArrears = null;
|
||||
|
||||
this.currency = null;
|
||||
this.periods = null;
|
||||
this.loanTermInDays = null;
|
||||
}
|
||||
|
||||
public LoanScheduleData(final CurrencyData currency, final Collection<LoanSchedulePeriodData> periods, final Integer loanTermInDays,
|
||||
final BigDecimal cumulativePrincipalDisbursed, final BigDecimal cumulativePrincipalDue,
|
||||
final BigDecimal cumulativePrincipalOutstanding, final BigDecimal cumulativeInterestExpected,
|
||||
final BigDecimal cumulativeFeeChargesToDate, final BigDecimal totalExpectedRepayment) {
|
||||
this.currency = currency;
|
||||
this.periods = periods;
|
||||
this.loanTermInDays = loanTermInDays;
|
||||
|
||||
this.cumulativePrincipalDisbursed = cumulativePrincipalDisbursed;
|
||||
this.cumulativePrincipalDue = cumulativePrincipalDue;
|
||||
this.cumulativePrincipalPaid = null;
|
||||
this.cumulativePrincipalWrittenOff = null;
|
||||
this.cumulativePrincipalOutstanding = cumulativePrincipalOutstanding;
|
||||
|
||||
this.cumulativeInterestExpected = cumulativeInterestExpected;
|
||||
this.cumulativeInterestPaid = null;
|
||||
this.cumulativeInterestWaived = null;
|
||||
this.cumulativeInterestWrittenOff = null;
|
||||
this.cumulativeInterestOutstanding = null;
|
||||
|
||||
this.cumulativeFeeChargesToDate = cumulativeFeeChargesToDate;
|
||||
this.cumulativeFeeChargesPaid = null;
|
||||
this.cumulativeFeeChargesWaived = null;
|
||||
this.cumulativeFeeChargesWrittenOff = null;
|
||||
this.cumulativeFeeChargesOutstanding = null;
|
||||
|
||||
this.cumulativePenaltyChargesToDate = null;
|
||||
this.cumulativePenaltyChargesPaid = null;
|
||||
this.cumulativePenaltyChargesWaived = null;
|
||||
this.cumulativePenaltyChargesWrittenOff = null;
|
||||
this.cumulativePenaltyChargesOutstanding = null;
|
||||
|
||||
if (cumulativeFeeChargesToDate != null) {
|
||||
this.totalCostOfLoan = cumulativeInterestExpected.add(cumulativeFeeChargesToDate);
|
||||
} else if (cumulativeInterestExpected != null) {
|
||||
this.totalCostOfLoan = cumulativeInterestExpected;
|
||||
} else {
|
||||
this.totalCostOfLoan = null;
|
||||
}
|
||||
|
||||
this.totalExpectedRepayment = totalExpectedRepayment;
|
||||
this.totalPaidToDate = BigDecimal.ZERO;
|
||||
this.totalWaivedToDate = BigDecimal.ZERO;
|
||||
this.totalWrittenOffToDate = BigDecimal.ZERO;
|
||||
this.totalOutstanding = totalExpectedRepayment;
|
||||
this.totalInArrears = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used when fully populating
|
||||
*/
|
||||
public LoanScheduleData(final CurrencyData currency, final Collection<LoanSchedulePeriodData> periods, final Integer loanTermInDays,
|
||||
final BigDecimal cumulativePrincipalDisbursed, final BigDecimal cumulativePrincipalDue,
|
||||
final BigDecimal cumulativePrincipalPaid, final BigDecimal cumulativePrincipalWrittenOff,
|
||||
final BigDecimal cumulativePrincipalOutstanding, final BigDecimal cumulativeInterestExpected,
|
||||
final BigDecimal cumulativeInterestPaid, final BigDecimal cumulativeInterestWaived,
|
||||
final BigDecimal cumulativeInterestWrittenOff, final BigDecimal cumulativeInterestOutstanding,
|
||||
final BigDecimal cumulativeFeeChargesToDate, final BigDecimal cumulativeFeeChargesPaid,
|
||||
final BigDecimal cumulativeFeeChargesWaived, final BigDecimal cumulativeFeeChargesWrittenOff,
|
||||
final BigDecimal cumulativeFeeChargesOutstanding, final BigDecimal cumulativePenaltyChargesToDate,
|
||||
final BigDecimal cumulativePenaltyChargesPaid, final BigDecimal cumulativePenaltyChargesWaived,
|
||||
final BigDecimal cumulativePenaltyChargesWrittenOff, final BigDecimal cumulativePenaltyChargesOutstanding,
|
||||
final BigDecimal totalCostOfLoan, final BigDecimal totalExpectedRepayment, final BigDecimal totalPaidToDate,
|
||||
final BigDecimal totalWaivedToDate, final BigDecimal totalWrittenOffToDate, final BigDecimal totalOutstanding,
|
||||
final BigDecimal totalInArrears) {
|
||||
this.currency = currency;
|
||||
this.periods = periods;
|
||||
this.loanTermInDays = loanTermInDays;
|
||||
|
||||
this.cumulativePrincipalDisbursed = cumulativePrincipalDisbursed;
|
||||
this.cumulativePrincipalDue = cumulativePrincipalDue;
|
||||
this.cumulativePrincipalPaid = cumulativePrincipalPaid;
|
||||
this.cumulativePrincipalWrittenOff = cumulativePrincipalWrittenOff;
|
||||
this.cumulativePrincipalOutstanding = cumulativePrincipalOutstanding;
|
||||
this.cumulativeInterestExpected = cumulativeInterestExpected;
|
||||
this.cumulativeInterestPaid = cumulativeInterestPaid;
|
||||
this.cumulativeInterestWrittenOff = cumulativeInterestWrittenOff;
|
||||
this.cumulativeInterestWaived = cumulativeInterestWaived;
|
||||
this.cumulativeInterestOutstanding = cumulativeInterestOutstanding;
|
||||
this.cumulativeFeeChargesToDate = cumulativeFeeChargesToDate;
|
||||
this.cumulativeFeeChargesPaid = cumulativeFeeChargesPaid;
|
||||
this.cumulativeFeeChargesWaived = cumulativeFeeChargesWaived;
|
||||
this.cumulativeFeeChargesWrittenOff = cumulativeFeeChargesWrittenOff;
|
||||
this.cumulativeFeeChargesOutstanding = cumulativeFeeChargesOutstanding;
|
||||
this.cumulativePenaltyChargesToDate = cumulativePenaltyChargesToDate;
|
||||
this.cumulativePenaltyChargesPaid = cumulativePenaltyChargesPaid;
|
||||
this.cumulativePenaltyChargesWaived = cumulativePenaltyChargesWaived;
|
||||
this.cumulativePenaltyChargesWrittenOff = cumulativePenaltyChargesWrittenOff;
|
||||
this.cumulativePenaltyChargesOutstanding = cumulativePenaltyChargesOutstanding;
|
||||
this.totalCostOfLoan = totalCostOfLoan;
|
||||
this.totalExpectedRepayment = totalExpectedRepayment;
|
||||
this.totalPaidToDate = totalPaidToDate;
|
||||
this.totalWaivedToDate = totalWaivedToDate;
|
||||
this.totalWrittenOffToDate = totalWrittenOffToDate;
|
||||
this.totalOutstanding = totalOutstanding;
|
||||
this.totalInArrears = totalInArrears;
|
||||
}
|
||||
|
||||
public Collection<LoanSchedulePeriodData> getPeriods() {
|
||||
return periods;
|
||||
}
|
||||
|
||||
public BigDecimal totalOutstanding() {
|
||||
return this.totalOutstanding;
|
||||
}
|
||||
}
|
||||
+20
-20
@@ -8,25 +8,25 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
public class AprCalculator {
|
||||
|
||||
public BigDecimal calculateFrom(PeriodFrequencyType interestPeriodFrequencyType, BigDecimal interestRatePerPeriod) {
|
||||
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.ZERO;
|
||||
switch (interestPeriodFrequencyType) {
|
||||
case DAYS:
|
||||
break;
|
||||
case WEEKS:
|
||||
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(52));
|
||||
break;
|
||||
case MONTHS:
|
||||
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(12));
|
||||
break;
|
||||
case YEARS:
|
||||
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(1));
|
||||
break;
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return defaultAnnualNominalInterestRate;
|
||||
}
|
||||
public BigDecimal calculateFrom(PeriodFrequencyType interestPeriodFrequencyType, BigDecimal interestRatePerPeriod) {
|
||||
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.ZERO;
|
||||
switch (interestPeriodFrequencyType) {
|
||||
case DAYS:
|
||||
break;
|
||||
case WEEKS:
|
||||
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(52));
|
||||
break;
|
||||
case MONTHS:
|
||||
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(12));
|
||||
break;
|
||||
case YEARS:
|
||||
defaultAnnualNominalInterestRate = interestRatePerPeriod.multiply(BigDecimal.valueOf(1));
|
||||
break;
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return defaultAnnualNominalInterestRate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+43
-42
@@ -14,55 +14,56 @@ import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Declining balance can amortized (see {@link AmortizationMethod}) in two ways at present:
|
||||
* <ol><li>Equal principal payments</li><li>Equal installment payments</li></ol>
|
||||
* Declining balance can be amortized (see {@link AmortizationMethod}) in two ways
|
||||
* at present:
|
||||
* <ol>
|
||||
* <li>Equal principal payments</li>
|
||||
* <li>Equal installment payments</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* When amortized using <i>equal principal payments</i>, the <b>principal component</b> of each installment is fixed and <b>interest due</b>
|
||||
* is calculated from the <b>outstanding principal balance</b> resulting in a different <b>total payment due</b> for each installment.
|
||||
* When amortized using <i>equal principal payments</i>, the <b>principal
|
||||
* component</b> of each installment is fixed and <b>interest due</b> is
|
||||
* calculated from the <b>outstanding principal balance</b> resulting in a
|
||||
* different <b>total payment due</b> for each installment.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* When amortized using <i>equal installments</i>, the <b>total payment due</b> for each installment is fixed and
|
||||
* is calculated using the excel like <code>pmt</code> function. The <b>interest due</b> is calculated from the <b>outstanding principal balance</b>
|
||||
* which results in a <b>principal component</b> that is <b>total payment due</b> minus <b>interest due</b>.
|
||||
* </p>
|
||||
* When amortized using <i>equal installments</i>, the <b>total payment due</b>
|
||||
* for each installment is fixed and is calculated using the excel like
|
||||
* <code>pmt</code> function. The <b>interest due</b> is calculated from the
|
||||
* <b>outstanding principal balance</b> which results in a <b>principal
|
||||
* component</b> that is <b>total payment due</b> minus <b>interest due</b>.
|
||||
* </p>
|
||||
*/
|
||||
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<LoanCharge> loanCharges) {
|
||||
|
||||
// 1. generate valid set of 'due dates' based on some of the 'loan attributes'
|
||||
final List<LocalDate> 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<LoanCharge> loanCharges) {
|
||||
|
||||
// 1. generate valid set of 'due dates' based on some of the 'loan
|
||||
// attributes'
|
||||
final List<LocalDate> 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);
|
||||
}
|
||||
}
|
||||
+19
-18
@@ -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;
|
||||
}
|
||||
}
|
||||
+105
-118
@@ -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<LoanCharge> loanCharges) {
|
||||
|
||||
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
|
||||
|
||||
final List<LocalDate> 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<LoanCharge> loanCharges) {
|
||||
|
||||
Money interestPerInstallment = totalInterestForLoanTerm.dividedBy(Long.valueOf(loanScheduleInfo.getNumberOfRepayments()), RoundingMode.HALF_EVEN);
|
||||
|
||||
Money principalPerInstallment = loanScheduleInfo.getPrincipal()
|
||||
.dividedBy(loanScheduleInfo.getNumberOfRepayments(),
|
||||
RoundingMode.HALF_EVEN);
|
||||
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
|
||||
|
||||
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<LocalDate> 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);
|
||||
}
|
||||
}
|
||||
+105
@@ -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<LoanCharge> 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<LoanCharge> 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;
|
||||
}
|
||||
}
|
||||
+3
-5
@@ -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<LoanCharge> loanCharges);
|
||||
LoanScheduleData generate(ApplicationCurrency applicationCurrency, LoanProductRelatedDetail loanScheduleInfo,
|
||||
Integer loanTermFrequency, PeriodFrequencyType loanTermFrequencyType, LocalDate disbursementDate, LocalDate firstRepaymentDate,
|
||||
LocalDate interestCalculatedFrom, Set<LoanCharge> loanCharges);
|
||||
|
||||
}
|
||||
+207
@@ -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<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
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); }
|
||||
|
||||
}
|
||||
}
|
||||
-9
@@ -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);
|
||||
}
|
||||
-71
@@ -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<LoanCharge> loanCharges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanScheduleRelatedDetails.getPrincipal().getAmount());
|
||||
|
||||
return loanScheduleGenerator.generate(applicationCurrency,
|
||||
loanScheduleRelatedDetails, loanTermFrequency,
|
||||
loanTermFrequencyType, command.getExpectedDisbursementDate(),
|
||||
command.getRepaymentsStartingFromDate(),
|
||||
command.getInterestChargedFromDate(), loanCharges);
|
||||
}
|
||||
}
|
||||
+108
@@ -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<LoanCharge> 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);
|
||||
}
|
||||
}
|
||||
+9
@@ -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);
|
||||
}
|
||||
+38
@@ -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();
|
||||
}
|
||||
}
|
||||
+114
@@ -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<CalculateLoanScheduleQuery> {
|
||||
|
||||
/**
|
||||
* The parameters supported for this command.
|
||||
*/
|
||||
final Set<String> supportedParameters = new HashSet<String>(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<Map<String, Object>>() {}.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<String> parametersPassedInForChargesCommand = new HashSet<String>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+142
@@ -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<LoanApplicationCommand> {
|
||||
|
||||
/**
|
||||
* The parameters supported for this command.
|
||||
*/
|
||||
final Set<String> supportedParameters = new HashSet<String>(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<Map<String, Object>>() {}.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<String> parametersPassedInForChargesCommand = new HashSet<String>();
|
||||
|
||||
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<String>(), 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);
|
||||
}
|
||||
}
|
||||
+61
@@ -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<LoanStateTransitionCommand> {
|
||||
|
||||
/**
|
||||
* The parameters supported for this command.
|
||||
*/
|
||||
final Set<String> supportedParameters = new HashSet<String>(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<Map<String, Object>>() {}.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);
|
||||
}
|
||||
}
|
||||
+114
-179
@@ -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<LoanCharge> 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<LoanStatus> 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<LoanCharge> 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<LoanStatus> 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;
|
||||
}
|
||||
}
|
||||
+78
-60
@@ -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<LoanCharge> assembleFrom(final LoanChargeCommand[] charges, final BigDecimal loanPrincipal) {
|
||||
|
||||
final Set<LoanCharge> loanCharges = new HashSet<LoanCharge>();
|
||||
|
||||
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<LoanCharge> assembleFrom(final LoanChargeCommand[] charges, final Set<Charge> chargesInheritedFromProduct, final BigDecimal loanPrincipal) {
|
||||
|
||||
Set<LoanCharge> loanCharges = new HashSet<LoanCharge>();
|
||||
|
||||
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<LoanCharge> fromParsedJson(final JsonElement element) {
|
||||
|
||||
final Set<LoanCharge> loanCharges = new HashSet<LoanCharge>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+40
-43
@@ -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);
|
||||
}
|
||||
}
|
||||
+35
-46
@@ -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);
|
||||
|
||||
+579
-622
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -138,9 +138,9 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.charges = charges;
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command) {
|
||||
public Map<String, Object> update(final JsonCommand command, final AprCalculator aprCalculator) {
|
||||
|
||||
final Map<String, Object> actualChanges = this.loanProductRelatedDetail.update(command);
|
||||
final Map<String, Object> actualChanges = this.loanProductRelatedDetail.update(command, aprCalculator);
|
||||
|
||||
final String nameParamName = "name";
|
||||
if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
|
||||
|
||||
+42
-1
@@ -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<String, Object> update(final JsonCommand command) {
|
||||
public Map<String, Object> update(final JsonCommand command, final AprCalculator aprCalculator) {
|
||||
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(20);
|
||||
|
||||
@@ -168,6 +191,18 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal);
|
||||
}
|
||||
|
||||
final Map<String, Object> loanApplicationAttributeChanges = updateLoanApplicationAttributes(command, aprCalculator);
|
||||
|
||||
actualChanges.putAll(loanApplicationAttributeChanges);
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public Map<String, Object> updateLoanApplicationAttributes(final JsonCommand command, final AprCalculator aprCalculator) {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(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);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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<String, Object> changes = product.update(command);
|
||||
final Map<String, Object> changes = product.update(command, this.aprCalculator);
|
||||
|
||||
// associating fund with loan product at creation is optional for now.
|
||||
if (changes.containsKey("fundId")) {
|
||||
|
||||
Reference in New Issue
Block a user