add state transitions around loan to api.

This commit is contained in:
Keith Woodlock
2012-05-16 22:02:04 +01:00
parent a006e60681
commit 8bc0b7461f
12 changed files with 229 additions and 450 deletions
@@ -11,6 +11,8 @@ import org.joda.time.LocalDate;
public class LoanStateTransitionCommand {
private Long loanId;
private String dateFormat;
private String eventDateFormatted;
private LocalDate eventDate;
private String comment;
@@ -48,4 +50,20 @@ public class LoanStateTransitionCommand {
public void setEventDate(LocalDate eventDate) {
this.eventDate = eventDate;
}
public String getDateFormat() {
return dateFormat;
}
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
public String getEventDateFormatted() {
return eventDateFormatted;
}
public void setEventDateFormatted(String eventDateFormatted) {
this.eventDateFormatted = eventDateFormatted;
}
}
@@ -5,7 +5,6 @@ import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.springframework.security.oauth.consumer.ProtectedResourceDetails;
@@ -15,16 +14,6 @@ public interface CommonRestOperations {
void updateProtectedResource(ProtectedResourceDetails loadProtectedResourceDetailsById);
EntityIdentifier deleteLoan(Long loanId);
EntityIdentifier approveLoan(LoanStateTransitionCommand command);
EntityIdentifier undoLoanApproval(UndoLoanApprovalCommand command);
EntityIdentifier rejectLoan(LoanStateTransitionCommand command);
EntityIdentifier withdrawLoan(LoanStateTransitionCommand command);
EntityIdentifier disburseLoan(LoanStateTransitionCommand command);
EntityIdentifier undloLoanDisbursal(UndoLoanDisbursalCommand command);
@@ -99,97 +99,7 @@ public class CommonRestOperationsImpl implements CommonRestOperations {
}
return errorList;
}
@Override
public EntityIdentifier deleteLoan(Long loanId) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/loan/").concat(loanId.toString()));
ResponseEntity<EntityIdentifier> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.DELETE, null,
EntityIdentifier.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public EntityIdentifier approveLoan(final LoanStateTransitionCommand command) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/loan/approve"));
ResponseEntity<EntityIdentifier> s = this.oauthRestServiceTemplate
.postForEntity(restUri,
loanStateTransitionApplicationRequest(command),
EntityIdentifier.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public EntityIdentifier undoLoanApproval(
final UndoLoanApprovalCommand command) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/loan/undoapproval"));
ResponseEntity<EntityIdentifier> s = this.oauthRestServiceTemplate
.postForEntity(restUri, undoLoanApprovalRequest(command),
EntityIdentifier.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public EntityIdentifier rejectLoan(final LoanStateTransitionCommand command) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/loan/reject"));
ResponseEntity<EntityIdentifier> s = this.oauthRestServiceTemplate
.postForEntity(restUri,
loanStateTransitionApplicationRequest(command),
EntityIdentifier.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public EntityIdentifier withdrawLoan(
final LoanStateTransitionCommand command) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/loan/withdraw"));
ResponseEntity<EntityIdentifier> s = this.oauthRestServiceTemplate
.postForEntity(restUri,
loanStateTransitionApplicationRequest(command),
EntityIdentifier.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public EntityIdentifier disburseLoan(
final LoanStateTransitionCommand command) {
@@ -19,7 +19,6 @@ import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
@@ -97,65 +96,6 @@ public class LoanTransactionController {
}
}
@RequestMapping(value = "/portfolio/loan/{loanId}/delete", method = RequestMethod.POST)
public @ResponseBody EntityIdentifier deleteLoan(@PathVariable("loanId") final Long loanId) {
return this.commonRestOperations.deleteLoan(loanId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/approve", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier approveLoan(
@PathVariable("loanId") final Long loanId,
@RequestParam(value="eventDate", required=false) String eventDate,
@RequestParam(value="comment", required=false) String comment) {
LocalDate approvalDate = parseStringToLocalDate(eventDate, "eventDate");
LoanStateTransitionCommand command = new LoanStateTransitionCommand(loanId, approvalDate, comment);
return this.commonRestOperations.approveLoan(command);
}
@RequestMapping(value = "/portfolio/loan/{loanId}/undoapproval", method = RequestMethod.POST)
public String undoLoanApproval(@PathVariable("loanId") final Long loanId) {
UndoLoanApprovalCommand command = new UndoLoanApprovalCommand(loanId);
EntityIdentifier clientAccountId = this.commonRestOperations
.undoLoanApproval(command);
return "redirect:/portfolio/client/" + clientAccountId.getEntityId();
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/reject", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier rejectLoan(
@PathVariable("loanId") final Long loanId,
@RequestParam(value="eventDate", required=false) String eventDate,
@RequestParam(value="comment", required=false) String comment) {
LocalDate rejectionDate = parseStringToLocalDate(eventDate, "eventDate");
LoanStateTransitionCommand command = new LoanStateTransitionCommand(loanId, rejectionDate, comment);
return this.commonRestOperations.rejectLoan(command);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/withdraw", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier loanWithdrawnByClient(
@PathVariable("loanId") final Long loanId,
@RequestParam(value="eventDate", required=false) String eventDate,
@RequestParam(value="comment", required=false) String comment) {
LocalDate withdrawnOnDate = parseStringToLocalDate(eventDate, "eventDate");
LoanStateTransitionCommand command = new LoanStateTransitionCommand(loanId, withdrawnOnDate, comment);
return this.commonRestOperations.withdrawLoan(command);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loan/{loanId}/disburse", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier disburseLoan(
@@ -251,7 +251,7 @@ $(document).ready(function() {
}).dialog('open');
}
function popupDialogWithPostOnlyFormView(url, titleCode, templateSelector, width, height, tabIndex, minOffset, defaultOffset, maxOffset) {
function popupDialogWithPostOnlyFormView(postUrl, titleCode, templateSelector, width, height, tabIndex, minOffset, defaultOffset, maxOffset) {
var dialogDiv = $("<div id='dialog-form'></div>");
var data = new Object();
@@ -267,13 +267,17 @@ $(document).ready(function() {
$('.multiSelectedItems option').each(function(i) {
$(this).attr("selected", "selected");
});
var form_data = $('#entityform').serialize();
var newFormData = JSON.stringify($('#entityform').serializeObject());
console.log(newFormData);
var jqxhr = $.ajax({
url: url,
url: postUrl,
type: 'POST',
data: form_data,
contentType: 'application/json',
dataType: 'json',
cache: false,
data: newFormData,
success: function(data, textStatus, jqXHR) {
dialogDiv.dialog("close");
$newtabs.tabs('load', tabIndex);
@@ -505,29 +509,27 @@ $(document).ready(function() {
offsetToDisbursalDate = data.maxDisbursedOnOffsetFromToday;
$('.rejectloan').button().click(function(e) {
var linkId = this.id;
var loanId = linkId.replace("rejectbtn", "");
var url = '${rootContext}portfolio/loan/' + loanId + '/reject';
var templateSelector = "#rejectLoanFormTemplate";
var postUrl = 'http://localhost:8080/mifosng-provider/api/v1/loans/' + loanId + '?command=reject';
var templateSelector = "#stateTransitionLoanFormTemplate";
var width = 500;
var height = 350;
var defaultOffset = offsetToSubmittedDate;
popupDialogWithPostOnlyFormView(url, 'dialog.title.reject.loan', templateSelector, width, height, currentTabIndex, offsetToSubmittedDate, defaultOffset, maxOffset);
popupDialogWithPostOnlyFormView(postUrl, 'dialog.title.reject.loan', templateSelector, width, height, currentTabIndex, offsetToSubmittedDate, defaultOffset, maxOffset);
e.preventDefault();
});
$('button.rejectloan span').text(jQuery.i18n.prop('dialog.button.reject.loan'));
$('.withdrawnbyapplicantloan').button().click(function(e) {
var linkId = this.id;
var loanId = linkId.replace("withdrawnbyapplicantloanbtn", "");
var url = '${rootContext}portfolio/loan/' + loanId + '/withdraw';
var templateSelector = "#withdrawnByClientLoanFormTemplate";
var postUrl = 'http://localhost:8080/mifosng-provider/api/v1/loans/' + loanId + '?command=withdrewbyclient';
var templateSelector = "#stateTransitionLoanFormTemplate";
var width = 500;
var height = 350;
var defaultOffset = offsetToSubmittedDate;
popupDialogWithPostOnlyFormView(url, 'dialog.title.loan.withdrawn.by.client', templateSelector, width, height, currentTabIndex, offsetToSubmittedDate, defaultOffset, maxOffset)
popupDialogWithPostOnlyFormView(postUrl, 'dialog.title.loan.withdrawn.by.client', templateSelector, width, height, currentTabIndex, offsetToSubmittedDate, defaultOffset, maxOffset)
e.preventDefault();
});
$('button.withdrawnbyapplicantloan span').text(jQuery.i18n.prop('dialog.button.withdrawn.by.client.loan'));
@@ -536,12 +538,12 @@ $(document).ready(function() {
var linkId = this.id;
var loanId = linkId.replace("approvebtn", "");
var url = '${rootContext}portfolio/loan/' + loanId + '/approve';
var templateSelector = "#approveLoanFormTemplate";
var postUrl = 'http://localhost:8080/mifosng-provider/api/v1/loans/' + loanId + '?command=approve';
var templateSelector = "#stateTransitionLoanFormTemplate";
var width = 500;
var height = 350;
var defaultOffset = offsetToSubmittedDate;
popupDialogWithPostOnlyFormView(url, 'dialog.title.approve.loan', templateSelector, width, height, currentTabIndex, offsetToSubmittedDate, defaultOffset, maxOffset)
popupDialogWithPostOnlyFormView(postUrl, 'dialog.title.approve.loan', templateSelector, width, height, currentTabIndex, offsetToSubmittedDate, defaultOffset, maxOffset)
e.preventDefault();
});
$('button.approveloan span').text(jQuery.i18n.prop('dialog.button.approve.loan'));
@@ -565,10 +567,10 @@ $(document).ready(function() {
var linkId = this.id;
var loanId = linkId.replace("undoapprovebtn", "");
var url = '${rootContext}portfolio/loan/' + loanId + '/undoapproval';
var postUrl = 'http://localhost:8080/mifosng-provider/api/v1/loans/' + loanId + '/undo?command=undoapproval';
var width = 400;
var height = 225;
popupConfirmationDialogAndPost(url, 'dialog.title.undo.loan.approval', width, height, currentTabIndex);
popupConfirmationDialogAndPost(postUrl, 'POST', 'dialog.title.undo.loan.approval', width, height, currentTabIndex);
e.preventDefault();
});
$('button.undoapproveloan span').text(jQuery.i18n.prop('dialog.button.undo.loan.approval'));
@@ -577,10 +579,10 @@ $(document).ready(function() {
var linkId = this.id;
var loanId = linkId.replace("undodisbursalbtn", "");
var url = '${rootContext}portfolio/loan/' + loanId + '/undodisbursal';
var postUrl = 'http://localhost:8080/mifosng-provider/api/v1/loans/' + loanId + '?command=undodisbursal';
var width = 400;
var height = 150;
popupConfirmationDialogAndPost(url, 'dialog.title.undo.loan.disbursal', width, height, currentTabIndex);
popupConfirmationDialogAndPost(postUrl, 'POST', 'dialog.title.undo.loan.disbursal', width, height, currentTabIndex);
e.preventDefault();
});
$('button.undodisbursalloan span').text(jQuery.i18n.prop('dialog.button.undo.loan.disbursal'));
@@ -589,12 +591,12 @@ $(document).ready(function() {
var linkId = this.id;
var loanId = linkId.replace("disbursebtn", "");
var url = '${rootContext}portfolio/loan/' + loanId + '/disburse';
var templateSelector = "#disburseLoanFormTemplate";
var postUrl = 'http://localhost:8080/mifosng-provider/api/v1/loans/' + loanId + '?command=disburse';
var templateSelector = "#stateTransitionLoanFormTemplate";
var width = 500;
var height = 350;
var defaultOffset = offsetToApprovalDate;
popupDialogWithPostOnlyFormView(url, 'dialog.title.disburse.loan', templateSelector, width, height, currentTabIndex, offsetToSubmittedDate, defaultOffset, maxOffset)
popupDialogWithPostOnlyFormView(postUrl, 'dialog.title.disburse.loan', templateSelector, width, height, currentTabIndex, offsetToSubmittedDate, defaultOffset, maxOffset)
e.preventDefault();
});
$('button.disburseloan span').text(jQuery.i18n.prop('dialog.button.disburse.loan'));
@@ -603,7 +605,7 @@ $(document).ready(function() {
var linkId = this.id;
var loanId = linkId.replace("repaymentbtn", "");
var url = '${rootContext}portfolio/loan/' + loanId + '/repayment';
var url = 'http://localhost:8080/mifosng-provider/api/v1/loans/' + loanId;
var templateSelector = "#transactionLoanFormTemplate";
var width = 500;
var height = 350;
@@ -799,51 +799,16 @@
</sec:authorize>
</script>
<script id="rejectLoanFormTemplate" type="text/x-jquery-tmpl">
<script id="stateTransitionLoanFormTemplate" type="text/x-jquery-tmpl">
<form id="entityform">
<div id="formerrors"></div>
<label for="eventDate"><spring:message code="label.loan.rejected.on"/></label>
<input id="eventDate" name="eventDate" cssClass="text ui-widget-content ui-corner-all" title="" class="datepickerfield" />
<input type="hidden" id="dateFormat" name="dateFormat" value="dd MMMM yyyy" />
<label for="eventDateFormatted"><spring:message code="label.loan.rejected.on"/></label>
<input id="eventDateFormatted" name="eventDateFormatted" title="" class="datepickerfield" />
<label for="comment"><spring:message code="label.loan.action.note"/></label>
<textarea rows="3" cols="53" id="comment" name="comment" cssClass="text ui-widget-content ui-corner-all"></textarea>
</form>
</script>
<script id="withdrawnByClientLoanFormTemplate" type="text/x-jquery-tmpl">
<form id="entityform">
<div id="formerrors"></div>
<label for="eventDate"><spring:message code="label.loan.withdrawn.by.client.on"/></label>
<input id="eventDate" name="eventDate" cssClass="text ui-widget-content ui-corner-all" title="" class="datepickerfield" />
<label for="comment"><spring:message code="label.loan.action.note"/></label>
<textarea rows="3" cols="53" id="comment" name="comment" cssClass="text ui-widget-content ui-corner-all"></textarea>
</form>
</script>
<script id="approveLoanFormTemplate" type="text/x-jquery-tmpl">
<form id="entityform">
<div id="formerrors"></div>
<label for="eventDate"><spring:message code="label.loan.approved.on"/></label>
<input id="eventDate" name="eventDate" cssClass="text ui-widget-content ui-corner-all" title="" class="datepickerfield" />
<label for="comment"><spring:message code="label.loan.action.note"/></label>
<textarea rows="3" cols="53" id="comment" name="comment" cssClass="text ui-widget-content ui-corner-all"></textarea>
</form>
</script>
<script id="disburseLoanFormTemplate" type="text/x-jquery-tmpl">
<form id="entityform">
<div id="formerrors"></div>
<label for="eventDate"><spring:message code="label.loan.disbursed.on"/></label>
<input id="eventDate" name="eventDate" cssClass="text ui-widget-content ui-corner-all" title="" class="datepickerfield" />
<label for="comment"><spring:message code="label.loan.action.note"/></label>
<textarea rows="3" cols="53" id="comment" name="comment" cssClass="text ui-widget-content ui-corner-all"></textarea>
<textarea rows="3" cols="53" id="comment" name="comment"></textarea>
</form>
</script>
@@ -4,7 +4,6 @@ import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.data.command.UpdateUsernamePasswordCommand;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -13,18 +12,6 @@ public interface WritePlatformService {
void updateUsernamePasswordOnFirstTimeLogin(UpdateUsernamePasswordCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_LOAN_ROLE', 'CAN_APPROVE_LOAN_IN_THE_PAST_ROLE')")
EntityIdentifier approveLoanApplication(LoanStateTransitionCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_UNDO_LOAN_APPROVAL_ROLE')")
EntityIdentifier undoLoanApproval(UndoLoanApprovalCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_REJECT_LOAN_ROLE', 'CAN_REJECT_LOAN_IN_THE_PAST_ROLE')")
EntityIdentifier rejectLoan(LoanStateTransitionCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_WITHDRAW_LOAN_ROLE', 'CAN_WITHDRAW_LOAN_IN_THE_PAST_ROLE')")
EntityIdentifier withdrawLoan(LoanStateTransitionCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_DISBURSE_LOAN_ROLE', 'CAN_DISBURSE_LOAN_IN_THE_PAST_ROLE')")
EntityIdentifier disburseLoan(LoanStateTransitionCommand command);
@@ -141,123 +141,6 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi
return Arrays.asList(errorResponse);
}
@Transactional
@Override
public EntityIdentifier approveLoanApplication(final LoanStateTransitionCommand command) {
AppUser currentUser = extractAuthenticatedUser();
LoanStateTransitionCommandValidator validator = new LoanStateTransitionCommandValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new ApplicationDomainRuleException(loanIdentifierDoesNotExistError(command.getLoanId()), "No loan exists with id: " + command.getLoanId());
}
if (this.isBeforeToday(command.getEventDate()) && currentUser.canNotApproveLoanInPast()) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission.to.approve.loan.in.past", "eventDate");
throw new ApplicationDomainRuleException(Arrays.asList(errorResponse));
}
loan.approve(command.getEventDate(), defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanNote(currentUser.getOrganisation(), loan, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier undoLoanApproval(final UndoLoanApprovalCommand command) {
AppUser currentUser = extractAuthenticatedUser();
Loan loan = this.loanRepository.findOne(loansThatMatch(
currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new ApplicationDomainRuleException(loanIdentifierDoesNotExistError(command.getLoanId()), "No loan exists with id: " + command.getLoanId());
}
loan.undoApproval(defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
Note note = Note.loanNote(currentUser.getOrganisation(), loan, "Undo of approval.");
this.noteRepository.save(note);
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier rejectLoan(final LoanStateTransitionCommand command) {
AppUser currentUser = (AppUser) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
LoanStateTransitionCommandValidator validator = new LoanStateTransitionCommandValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(
currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new ApplicationDomainRuleException(loanIdentifierDoesNotExistError(command.getLoanId()), "No loan exists with id: " + command.getLoanId());
}
if (this.isBeforeToday(command.getEventDate()) && currentUser.canNotRejectLoanInPast()) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission.to.reject.loan.in.past", "eventDate");
throw new ApplicationDomainRuleException(Arrays.asList(errorResponse));
}
loan.reject(command.getEventDate(), defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanNote(currentUser.getOrganisation(), loan, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier withdrawLoan(final LoanStateTransitionCommand command) {
AppUser currentUser = extractAuthenticatedUser();
LoanStateTransitionCommandValidator validator = new LoanStateTransitionCommandValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(
currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new ApplicationDomainRuleException(loanIdentifierDoesNotExistError(command.getLoanId()), "No loan exists with id: " + command.getLoanId());
}
if (this.isBeforeToday(command.getEventDate()) && currentUser.canNotWithdrawByClientLoanInPast()) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission.to.withdraw.loan.in.past", "eventDate");
throw new ApplicationDomainRuleException(Arrays.asList(errorResponse));
}
loan.withdraw(command.getEventDate(), defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanNote(currentUser.getOrganisation(), loan, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier disburseLoan(final LoanStateTransitionCommand command) {
@@ -21,7 +21,10 @@ import org.mifosng.data.LoanAccountData;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.NewLoanWorkflowStepOneData;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.platform.ReadPlatformService;
import org.mifosng.platform.WritePlatformService;
import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
@@ -151,4 +154,54 @@ public class LoansApiResource {
return Response.ok().entity(identifier).build();
}
@POST
@Path("{loanId}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Produces({ MediaType.APPLICATION_JSON})
public Response rejectLoanApplication(@PathParam("loanId") final Long loanId, @QueryParam("command") final String commandParam,
final LoanStateTransitionCommand command) {
LocalDate eventDate = this.apiDataConversionService.convertFrom(command.getEventDateFormatted(), "eventDateFormatted", command.getDateFormat());
command.setLoanId(loanId);
command.setEventDate(eventDate);
if (StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase("reject")) {
EntityIdentifier identifier = this.loanWritePlatformService.rejectLoan(command);
return Response.ok().entity(identifier).build();
} else if (StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase("withdrewbyclient")) {
EntityIdentifier identifier = this.loanWritePlatformService.withdrawLoan(command);
return Response.ok().entity(identifier).build();
} else if (StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase("approve")) {
EntityIdentifier identifier = this.loanWritePlatformService.approveLoanApplication(command);
return Response.ok().entity(identifier).build();
} else if (StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase("undoapproval")) {
UndoLoanApprovalCommand undoCommand = new UndoLoanApprovalCommand(loanId);
EntityIdentifier identifier = this.loanWritePlatformService.undoLoanApproval(undoCommand);
return Response.ok().entity(identifier).build();
}
return Response.ok().build();
}
@POST
@Path("{loanId}/undo")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Produces({ MediaType.APPLICATION_JSON})
public Response rejectLoanApplication(@PathParam("loanId") final Long loanId, @QueryParam("command") final String commandParam) {
if (StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase("undoapproval")) {
UndoLoanApprovalCommand undoCommand = new UndoLoanApprovalCommand(loanId);
EntityIdentifier identifier = this.loanWritePlatformService.undoLoanApproval(undoCommand);
return Response.ok().entity(identifier).build();
} else if (StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase("undodisbursal")) {
UndoLoanDisbursalCommand undoCommand = new UndoLoanDisbursalCommand(loanId);
EntityIdentifier identifier = this.writePlatformService.undloLoanDisbursal(undoCommand);
return Response.ok().entity(identifier).build();
}
return Response.ok().build();
}
}
@@ -1,7 +1,9 @@
package org.mifosng.platform.loan.service;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.springframework.security.access.prepost.PreAuthorize;
public interface LoanWritePlatformService {
@@ -11,4 +13,16 @@ public interface LoanWritePlatformService {
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_DELETE_LOAN_THAT_IS_SUBMITTED_AND_NOT_APPROVED')")
EntityIdentifier deleteLoan(Long loanId);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_LOAN_ROLE', 'CAN_APPROVE_LOAN_IN_THE_PAST_ROLE')")
EntityIdentifier approveLoanApplication(LoanStateTransitionCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_UNDO_LOAN_APPROVAL_ROLE')")
EntityIdentifier undoLoanApproval(UndoLoanApprovalCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_REJECT_LOAN_ROLE', 'CAN_REJECT_LOAN_IN_THE_PAST_ROLE')")
EntityIdentifier rejectLoan(LoanStateTransitionCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_WITHDRAW_LOAN_ROLE', 'CAN_WITHDRAW_LOAN_IN_THE_PAST_ROLE')")
EntityIdentifier withdrawLoan(LoanStateTransitionCommand command);
}
@@ -11,7 +11,10 @@ import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.MoneyData;
import org.mifosng.data.ScheduledLoanInstallment;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.platform.LoanStateTransitionCommandValidator;
import org.mifosng.platform.client.domain.Client;
import org.mifosng.platform.client.domain.ClientRepository;
import org.mifosng.platform.client.domain.Note;
@@ -38,6 +41,7 @@ import org.mifosng.platform.loanschedule.domain.AprCalculator;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -168,4 +172,111 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
return new EntityIdentifier(clientId);
}
@Transactional
@Override
public EntityIdentifier approveLoanApplication(final LoanStateTransitionCommand command) {
AppUser currentUser = context.authenticatedUser();
LoanStateTransitionCommandValidator validator = new LoanStateTransitionCommandValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new PlatformResourceNotFoundException("error.msg.loan.id.invalid", "Loan with identifier {0} does not exist", command.getLoanId());
}
if (this.isBeforeToday(command.getEventDate()) && currentUser.canNotApproveLoanInPast()) {
throw new NoAuthorizationException("User has no authority to approve loan with a date in the past.");
}
loan.approve(command.getEventDate(), defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanNote(currentUser.getOrganisation(), loan, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier undoLoanApproval(final UndoLoanApprovalCommand command) {
AppUser currentUser = context.authenticatedUser();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new PlatformResourceNotFoundException("error.msg.loan.id.invalid", "Loan with identifier {0} does not exist", command.getLoanId());
}
loan.undoApproval(defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
Note note = Note.loanNote(currentUser.getOrganisation(), loan, "Undo of approval.");
this.noteRepository.save(note);
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier rejectLoan(final LoanStateTransitionCommand command) {
AppUser currentUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
LoanStateTransitionCommandValidator validator = new LoanStateTransitionCommandValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new PlatformResourceNotFoundException("error.msg.loan.id.invalid", "Loan with identifier {0} does not exist", command.getLoanId());
}
if (this.isBeforeToday(command.getEventDate()) && currentUser.canNotRejectLoanInPast()) {
throw new NoAuthorizationException("User has no authority to reject loan with a date in the past.");
}
loan.reject(command.getEventDate(), defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanNote(currentUser.getOrganisation(), loan, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier withdrawLoan(final LoanStateTransitionCommand command) {
AppUser currentUser = context.authenticatedUser();
LoanStateTransitionCommandValidator validator = new LoanStateTransitionCommandValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new PlatformResourceNotFoundException("error.msg.loan.id.invalid", "Loan with identifier {0} does not exist", command.getLoanId());
}
if (this.isBeforeToday(command.getEventDate()) && currentUser.canNotWithdrawByClientLoanInPast()) {
throw new NoAuthorizationException("User has no authority to mark loan as withdrawn by client with a date in the past.");
}
loan.withdraw(command.getEventDate(), defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanNote(currentUser.getOrganisation(), loan, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
}
@@ -22,7 +22,6 @@ import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.platform.ReadPlatformService;
import org.mifosng.platform.ReadPlatformServiceImpl;
@@ -49,98 +48,6 @@ public class LoanResource {
@Autowired
private WritePlatformService writePlatformService;
@POST
@Path("approve")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response approveLoanApplication(final LoanStateTransitionCommand command) {
try {
EntityIdentifier identifier = this.writePlatformService.approveLoanApplication(command);
return Response.ok().entity(identifier).build();
} catch (UnAuthenticatedUserException e) {
throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).build());
} catch (AccessDeniedException e) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id");
ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse));
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build());
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
}
@POST
@Path("undoapproval")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response undoLoanApproval(final UndoLoanApprovalCommand command) {
try {
EntityIdentifier identifier = this.writePlatformService.undoLoanApproval(command);
return Response.ok().entity(identifier).build();
} catch (UnAuthenticatedUserException e) {
throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).build());
} catch (AccessDeniedException e) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id");
ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse));
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build());
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
}
@POST
@Path("reject")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response rejectLoanApplication(final LoanStateTransitionCommand command) {
try {
EntityIdentifier identifier = this.writePlatformService.rejectLoan(command);
return Response.ok().entity(identifier).build();
} catch (UnAuthenticatedUserException e) {
throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).build());
} catch (AccessDeniedException e) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id");
ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse));
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build());
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
}
@POST
@Path("withdraw")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response withdrawLoanApplication(final LoanStateTransitionCommand command) {
try {
EntityIdentifier identifier = this.writePlatformService.withdrawLoan(command);
return Response.ok().entity(identifier).build();
} catch (UnAuthenticatedUserException e) {
throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).build());
} catch (AccessDeniedException e) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id");
ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse));
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build());
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
}
@POST
@Path("disburse")