update api-docs and loan functionality from api.

This commit is contained in:
Keith Woodlock
2013-02-05 11:06:43 +00:00
parent 3305221870
commit 297263f5a0
18 changed files with 1358 additions and 3673 deletions
@@ -9,8 +9,8 @@ import org.springframework.stereotype.Component;
/**
* <p>
* Used to process the query parameters provided in a API request to see features of
* the RESTful API are being asked for such as:
* Used to process the query parameters provided in a API request to see
* features of the RESTful API are being asked for such as:
* </p>
* <ul>
* <li>Pretty printing through pretty=true, defaults to false</li>
@@ -21,6 +21,23 @@ import org.springframework.stereotype.Component;
@Component
public class ApiRequestParameterHelper {
public ApiRequestJsonSerializationSettings process(final MultivaluedMap<String, String> queryParameters,
final Set<String> mandatoryResponseParameters) {
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(queryParameters);
if (!responseParameters.isEmpty()) {
responseParameters.addAll(mandatoryResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(queryParameters);
final boolean template = ApiParameterHelper.template(queryParameters);
final Long commandId = ApiParameterHelper.commandId(queryParameters);
final boolean makerCheckerable = ApiParameterHelper.makerCheckerable(queryParameters);
final boolean includeJson = ApiParameterHelper.includeJson(queryParameters);
return ApiRequestJsonSerializationSettings
.from(prettyPrint, responseParameters, template, commandId, makerCheckerable, includeJson);
}
public ApiRequestJsonSerializationSettings process(final MultivaluedMap<String, String> queryParameters) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(queryParameters);
@@ -29,7 +46,8 @@ public class ApiRequestParameterHelper {
final Long commandId = ApiParameterHelper.commandId(queryParameters);
final boolean makerCheckerable = ApiParameterHelper.makerCheckerable(queryParameters);
final boolean includeJson = ApiParameterHelper.includeJson(queryParameters);
return ApiRequestJsonSerializationSettings.from(prettyPrint, responseParameters, template, commandId, makerCheckerable, includeJson);
return ApiRequestJsonSerializationSettings
.from(prettyPrint, responseParameters, template, commandId, makerCheckerable, includeJson);
}
}
@@ -25,8 +25,9 @@ public class CreateDatatableEntryCommandHandler implements NewCommandSourceHandl
this.writePlatformService.newDatatableEntry(command.entityName(), command.getApptableId(), command);
// FIXME - kw - need to find office and either group/client/loan that
// this datatable is against.
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(command.getApptableId()).build();
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(command.getApptableId()) //
.build();
}
}
@@ -76,10 +76,11 @@ public class LoansApiResource {
"annualInterestRate", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType",
"interestCalculationPeriodType", "submittedOnDate", "approvedOnDate", "expectedDisbursementDate", "actualDisbursementDate",
"expectedFirstRepaymentOnDate", "interestChargedFromDate", "closedOnDate", "expectedMaturityDate", "status",
"lifeCycleStatusDate", "repaymentSchedule", "transactions", "permissions", "convenienceData", "charges", "productOptions",
"lifeCycleStatusDate", "repaymentSchedule", "transactions", "permissions", "convenienceData", "charges", "guarantor", "productOptions",
"amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "repaymentFrequencyTypeOptions",
"interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "loanOfficerId",
"loanOfficerName", "loanOfficerOptions", "chargeTemplate"));
"loanOfficerName", "loanOfficerOptions", "chargeTemplate", "transactionProcessingStrategyId",
"termFrequency", "termPeriodFrequencyType"));
private final String resourceNameForPermissions = "LOAN";
@@ -201,6 +202,7 @@ public class LoansApiResource {
Collection<LoanChargeData> charges = null;
Collection<GuarantorData> guarantors = null;
final Set<String> mandatoryResponseParameters = new HashSet<String>();
boolean convenienceDataRequired = false;
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
if (!associationParameters.isEmpty()) {
@@ -211,6 +213,7 @@ public class LoansApiResource {
}
if (associationParameters.contains("guarantor")) {
mandatoryResponseParameters.add("guarantor");
guarantors = this.guarantorReadPlatformService.retrieveGuarantorsForLoan(loanId);
if (CollectionUtils.isEmpty(guarantors)) {
guarantors = null; // set back to null so doesn't appear in JSON
@@ -218,6 +221,7 @@ public class LoansApiResource {
}
if (associationParameters.contains("transactions")) {
mandatoryResponseParameters.add("transactions");
final Collection<LoanTransactionData> currentLoanRepayments = this.loanReadPlatformService.retrieveLoanTransactions(loanId);
if (!CollectionUtils.isEmpty(currentLoanRepayments)) {
loanRepayments = currentLoanRepayments;
@@ -226,6 +230,10 @@ public class LoansApiResource {
if (associationParameters.contains("repaymentSchedule") || associationParameters.contains("permissions")) {
if (associationParameters.contains("repaymentSchedule")) {
mandatoryResponseParameters.add("repaymentSchedule");
}
DisbursementData singleDisbursement = loanBasicDetails.toDisburementData();
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, loanBasicDetails.getCurrency(),
singleDisbursement, loanBasicDetails.getTotalDisbursementCharges(), loanBasicDetails.getInArrearsTolerance());
@@ -236,7 +244,8 @@ public class LoansApiResource {
if (associationParameters.contains("permissions")) {
if (repaymentSchedule == null) { throw new IllegalStateException(); }
mandatoryResponseParameters.add("permissions");
// FIXME - KW - Waive feature was changed to waive interest at
// anytime so this permission checking is probably not needed -
// look into.
@@ -259,6 +268,7 @@ public class LoansApiResource {
}
if (associationParameters.contains("charges")) {
mandatoryResponseParameters.add("charges");
charges = this.loanChargeReadPlatformService.retrieveLoanCharges(loanId);
if (CollectionUtils.isEmpty(charges)) {
charges = null; // set back to null so doesnt appear in JSON
@@ -302,7 +312,7 @@ public class LoansApiResource {
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, null, guarantors);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters(), mandatoryResponseParameters);
return this.toApiJsonSerializer.serialize(settings, loanAccount, LOAN_DATA_PARAMETERS);
}
@@ -4,113 +4,111 @@ import java.util.List;
public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachine {
private final List<LoanStatus> allowedLoanStatuses;
private final List<LoanStatus> allowedLoanStatuses;
public DefaultLoanLifecycleStateMachine(List<LoanStatus> allowedLoanStatuses) {
this.allowedLoanStatuses = allowedLoanStatuses;
}
public DefaultLoanLifecycleStateMachine(List<LoanStatus> allowedLoanStatuses) {
this.allowedLoanStatuses = allowedLoanStatuses;
}
@Override
public LoanStatus transition(LoanEvent loanEvent, LoanStatus from) {
@Override
public LoanStatus transition(LoanEvent loanEvent, LoanStatus from) {
LoanStatus newState = null;
LoanStatus newState = from;
switch (loanEvent) {
case LOAN_CREATED:
if (from == null) {
newState = stateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL,
allowedLoanStatuses);
}
break;
case LOAN_REJECTED:
if (from.hasStateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL)) {
newState = stateOf(LoanStatus.REJECTED, allowedLoanStatuses);
}
break;
case LOAN_APPROVED:
if (from.hasStateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL)) {
newState = stateOf(LoanStatus.APPROVED, allowedLoanStatuses);
}
break;
case LOAN_WITHDRAWN:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.SUBMITED_AND_PENDING_APPROVAL, LoanStatus.APPROVED)) {
newState = stateOf(LoanStatus.WITHDRAWN_BY_CLIENT, allowedLoanStatuses);
}
break;
case LOAN_DISBURSED:
if (from.hasStateOf(LoanStatus.APPROVED)) {
newState = stateOf(LoanStatus.ACTIVE, allowedLoanStatuses);
}
break;
case LOAN_APPROVAL_UNDO:
if (from.hasStateOf(LoanStatus.APPROVED)) {
newState = stateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL, allowedLoanStatuses);
}
break;
case LOAN_DISBURSAL_UNDO:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE)) {
newState = stateOf(LoanStatus.APPROVED, allowedLoanStatuses);
}
break;
case LOAN_REPAYMENT_OR_WAIVER:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.OVERPAID)) {
newState = stateOf(LoanStatus.ACTIVE, allowedLoanStatuses);
} else {
newState = from;
}
break;
case REPAID_IN_FULL:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.OVERPAID)) {
newState = stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, allowedLoanStatuses);
}
break;
case WRITE_OFF_OUTSTANDING:
if (this.anyOfAllowedWhenComingFrom(from,LoanStatus.ACTIVE)) {
newState = stateOf(LoanStatus.CLOSED_WRITTEN_OFF, allowedLoanStatuses);
}
break;
case LOAN_RESCHEDULE:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE)) {
newState = stateOf(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT, allowedLoanStatuses);
}
break;
case INTERST_REBATE_OWED:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET)) {
newState = stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, allowedLoanStatuses);
}
break;
case LOAN_OVERPAYMENT:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.ACTIVE)) {
newState = stateOf(LoanStatus.OVERPAID, allowedLoanStatuses);
}
break;
}
switch (loanEvent) {
case LOAN_CREATED:
if (from == null) {
newState = stateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL, allowedLoanStatuses);
}
break;
case LOAN_REJECTED:
if (from.hasStateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL)) {
newState = stateOf(LoanStatus.REJECTED, allowedLoanStatuses);
}
break;
case LOAN_APPROVED:
if (from.hasStateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL)) {
newState = stateOf(LoanStatus.APPROVED, allowedLoanStatuses);
}
break;
case LOAN_WITHDRAWN:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.SUBMITED_AND_PENDING_APPROVAL, LoanStatus.APPROVED)) {
newState = stateOf(LoanStatus.WITHDRAWN_BY_CLIENT, allowedLoanStatuses);
}
break;
case LOAN_DISBURSED:
if (from.hasStateOf(LoanStatus.APPROVED)) {
newState = stateOf(LoanStatus.ACTIVE, allowedLoanStatuses);
}
break;
case LOAN_APPROVAL_UNDO:
if (from.hasStateOf(LoanStatus.APPROVED)) {
newState = stateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL, allowedLoanStatuses);
}
break;
case LOAN_DISBURSAL_UNDO:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE)) {
newState = stateOf(LoanStatus.APPROVED, allowedLoanStatuses);
}
break;
case LOAN_REPAYMENT_OR_WAIVER:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.OVERPAID)) {
newState = stateOf(LoanStatus.ACTIVE, allowedLoanStatuses);
} else {
newState = from;
}
break;
case REPAID_IN_FULL:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.OVERPAID)) {
newState = stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, allowedLoanStatuses);
}
break;
case WRITE_OFF_OUTSTANDING:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE)) {
newState = stateOf(LoanStatus.CLOSED_WRITTEN_OFF, allowedLoanStatuses);
}
break;
case LOAN_RESCHEDULE:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE)) {
newState = stateOf(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT, allowedLoanStatuses);
}
break;
case INTERST_REBATE_OWED:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET)) {
newState = stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, allowedLoanStatuses);
}
break;
case LOAN_OVERPAYMENT:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.ACTIVE)) {
newState = stateOf(LoanStatus.OVERPAID, allowedLoanStatuses);
}
break;
}
return newState;
}
return newState;
}
private LoanStatus stateOf(LoanStatus state, List<LoanStatus> allowedLoanStatuses) {
LoanStatus match = null;
for (LoanStatus loanStatus : allowedLoanStatuses) {
if (loanStatus.hasStateOf(state)) {
match = loanStatus;
break;
}
}
return match;
}
private LoanStatus stateOf(LoanStatus state, List<LoanStatus> allowedLoanStatuses) {
LoanStatus match = null;
for (LoanStatus loanStatus : allowedLoanStatuses) {
if (loanStatus.hasStateOf(state)) {
match = loanStatus;
break;
}
}
return match;
}
private boolean anyOfAllowedWhenComingFrom(final LoanStatus state,
final LoanStatus... allowedStates) {
boolean allowed = false;
private boolean anyOfAllowedWhenComingFrom(final LoanStatus state, final LoanStatus... allowedStates) {
boolean allowed = false;
for (LoanStatus allowedState : allowedStates) {
if (state.hasStateOf(allowedState)) {
allowed = true;
break;
}
}
for (LoanStatus allowedState : allowedStates) {
if (state.hasStateOf(allowedState)) {
allowed = true;
break;
}
}
return allowed;
}
return allowed;
}
}
@@ -803,32 +803,32 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
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");
}
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);
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));
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: "
+ getSubmittedOnDate().toString();
throw new InvalidLoanStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, rejectedOn,
getSubmittedOnDate());
}
if (rejectedOn.isAfter(new LocalDate())) {
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);
if (rejectedOn.isBefore(getSubmittedOnDate())) {
final String errorMessage = "The date on which a loan is rejected cannot be before its submittal date: "
+ getSubmittedOnDate().toString();
throw new InvalidLoanStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, rejectedOn,
getSubmittedOnDate());
}
if (rejectedOn.isAfter(new LocalDate())) {
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;
@@ -843,33 +843,33 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
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");
}
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);
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));
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, command,
getSubmittedOnDate());
}
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, 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, command);
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, command);
}
}
return actualChanges;
@@ -883,34 +883,37 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
}
// only do below if status has changed in the 'approval' case
LocalDate approvedOn = command.localDateValueOfParameterNamed("approvedOnDate");
String approvedOnDateChange = command.stringValueOfParameterNamed("approvedOnDate");
if (approvedOn == null) {
approvedOn = command.localDateValueOfParameterNamed("eventDate");
approvedOnDateChange = command.stringValueOfParameterNamed("eventDate");
}
LocalDate approvedOn = command.localDateValueOfParameterNamed("approvedOnDate");
if (approvedOn == null) {
approvedOn = command.localDateValueOfParameterNamed("eventDate");
}
this.approvedOnDate = approvedOn.toDate();
actualChanges.put("locale", command.locale());
actualChanges.put("dateFormat", command.dateFormat());
actualChanges.put("approvedOnDate", approvedOnDateChange);
this.approvedOnDate = approvedOn.toDate();
actualChanges.put("locale", command.locale());
actualChanges.put("dateFormat", command.dateFormat());
actualChanges.put("approvedOnDate", command.stringValueOfParameterNamed("approvedOnDate"));
final LocalDate submittalDate = new LocalDate(this.submittedOnDate);
if (approvedOn.isBefore(submittalDate)) {
final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: "
+ submittalDate.toString();
throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage, getApprovedOnDate(),
submittalDate);
}
if (approvedOn.isAfter(new LocalDate())) {
final String errorMessage = "The date on which a loan is approved cannot be in the future.";
throw new InvalidLoanStateTransitionException("approval", "cannot.be.a.future.date", errorMessage, getApprovedOnDate());
}
final LocalDate submittalDate = new LocalDate(this.submittedOnDate);
if (approvedOn.isBefore(submittalDate)) {
final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: "
+ submittalDate.toString();
throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage, getApprovedOnDate(),
submittalDate);
}
if (approvedOn.isAfter(new LocalDate())) {
final String errorMessage = "The date on which a loan is approved cannot be in the future.";
throw new InvalidLoanStateTransitionException("approval", "cannot.be.a.future.date", errorMessage, getApprovedOnDate());
}
if (this.loanofficer != null) {
final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this,
this.loanofficer, approvedOn);
this.loanOfficerHistory.add(loanOfficerAssignmentHistory);
if (this.loanofficer != null) {
final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this,
this.loanofficer, approvedOn);
this.loanOfficerHistory.add(loanOfficerAssignmentHistory);
}
}
return actualChanges;
@@ -925,13 +928,13 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (!statusEnum.hasStateOf(currentStatus)) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
this.approvedOnDate = null;
actualChanges.put("approvedOnDate", "");
this.loanOfficerHistory.clear();
}
this.approvedOnDate = null;
actualChanges.put("approvedOnDate", "");
this.loanOfficerHistory.clear();
return actualChanges;
}
@@ -952,28 +955,28 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
}
LocalDate disbursedOn = command.localDateValueOfParameterNamed("disbursedOnDate");
if (disbursedOn == null) {
disbursedOn = command.localDateValueOfParameterNamed("eventDate");
}
LocalDate disbursedOn = command.localDateValueOfParameterNamed("disbursedOnDate");
if (disbursedOn == null) {
disbursedOn = command.localDateValueOfParameterNamed("eventDate");
}
final Locale locale = new Locale(command.locale());
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final Locale locale = new Locale(command.locale());
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
this.disbursedOnDate = disbursedOn.toDate();
this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
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));
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);
handleDisbursementTransaction(disbursedOn);
if (isRepaymentScheduleRegenerationRequiredForDisbursement(disbursedOn)) {
regenerateRepaymentSchedule(currency);
if (isRepaymentScheduleRegenerationRequiredForDisbursement(disbursedOn)) {
regenerateRepaymentSchedule(currency);
}
}
return actualChanges;
@@ -1067,19 +1070,17 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (!statusEnum.hasStateOf(currentStatus)) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
this.approvedOnDate = null;
actualChanges.put("disbursedOnDate", "");
updateLoanToPreDisbursalState();
}
this.approvedOnDate = null;
actualChanges.put("disbursedOnDate", "");
updateLoanToPreDisbursalState();
return actualChanges;
}
private void updateLoanToPreDisbursalState() {
this.loanStatus = LoanStatus.APPROVED.getValue();
this.loanTransactions.clear();
this.disbursedOnDate = null;
@@ -2,6 +2,5 @@ package org.mifosplatform.portfolio.loanaccount.domain;
public interface LoanLifecycleStateMachine {
LoanStatus transition(LoanEvent loanEvent, LoanStatus from);
LoanStatus transition(LoanEvent loanEvent, LoanStatus from);
}
@@ -294,13 +294,15 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
"User has no authority to approve loan with a date in the past."); }
final Map<String, Object> changes = loan.loanApplicationApproval(command, defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
@@ -323,12 +325,14 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final Loan loan = retrieveLoanBy(loanId);
final Map<String, Object> changes = loan.undoApproval(defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
@@ -359,12 +363,14 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
"User has no authority to reject loan with a date in the past."); }
final Map<String, Object> changes = loan.loanApplicationRejection(command, defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
@@ -395,12 +401,14 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
"User has no authority to mark loan as withdrawn by applicant with a date in the past."); }
final Map<String, Object> changes = loan.loanApplicationWithdrawnByApplicant(command, defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
@@ -121,35 +121,37 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getPrincpal().getCurrencyCode());
final Map<String, Object> changes = loan.disburse(command, defaultLoanLifecycleStateMachine(), currency);
// variable stores Id's of all existing loan transactions (newly created
// loan transactions would not have an Id before save)
Set<Long> existingLoanTransactionIds = new HashSet<Long>();
for(LoanTransaction loanTransaction:loan.getLoanTransactions()){
if(!(loanTransaction.getId()==null)){
existingLoanTransactionIds.add(loanTransaction.getId());
}
}
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
// make a call to accounting
if (loan.isAccountingEnabledOnLoanProduct()) {
/***
* Variable holds list of all newly created loan transactions during
* this disbursal
**/
List<LoanTransaction> newLoanTransactions = new ArrayList<LoanTransaction>();
for (LoanTransaction loanTransaction : loan.getLoanTransactions()) {
if (!existingLoanTransactionIds.contains(loanTransaction.getId())) {
newLoanTransactions.add(loanTransaction);
if (!changes.isEmpty()) {
// variable stores Id's of all existing loan transactions (newly created
// loan transactions would not have an Id before save)
final Set<Long> existingLoanTransactionIds = new HashSet<Long>();
for(LoanTransaction loanTransaction:loan.getLoanTransactions()){
if(!(loanTransaction.getId()==null)){
existingLoanTransactionIds.add(loanTransaction.getId());
}
}
LoanDTO loanDTO = populateLoanDTO(loan, newLoanTransactions);
journalEntryWritePlatformService.createJournalEntriesForLoan(loanDTO);
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
// make a call to accounting
if (loan.isAccountingEnabledOnLoanProduct()) {
/***
* Variable holds list of all newly created loan transactions during
* this disbursal
**/
List<LoanTransaction> newLoanTransactions = new ArrayList<LoanTransaction>();
for (LoanTransaction loanTransaction : loan.getLoanTransactions()) {
if (!existingLoanTransactionIds.contains(loanTransaction.getId())) {
newLoanTransactions.add(loanTransaction);
}
}
LoanDTO loanDTO = populateLoanDTO(loan, newLoanTransactions);
journalEntryWritePlatformService.createJournalEntriesForLoan(loanDTO);
}
}
return new CommandProcessingResultBuilder() //
@@ -172,12 +174,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Loan loan = retrieveLoanBy(loanId);
final Map<String, Object> changes = loan.undoDisbursal(defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
@@ -74,7 +74,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
final InterestMethod interestMethod = InterestMethod.fromInt(command.integerValueOfParameterNamed("interestType"));
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command
.integerValueOfParameterNamed("interestCalculationPeriodType"));
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.integerValueOfParameterNamed("interestType"));
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.integerValueOfParameterNamed("amortizationType"));
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command
.integerValueOfParameterNamed("repaymentFrequencyType"));
final PeriodFrequencyType interestFrequencyType = PeriodFrequencyType.fromInt(command