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
@@ -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")