Mifosx-43 DepositAccount Approval
This commit is contained in:
+52
@@ -20,7 +20,10 @@ import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosng.platform.api.commands.DepositAccountCommand;
|
||||
import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
|
||||
import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
|
||||
import org.mifosng.platform.api.data.DepositAccountData;
|
||||
import org.mifosng.platform.api.data.DepositProductLookup;
|
||||
import org.mifosng.platform.api.data.EntityIdentifier;
|
||||
@@ -28,6 +31,7 @@ import org.mifosng.platform.api.data.EnumOptionData;
|
||||
import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
|
||||
import org.mifosng.platform.api.infrastructure.ApiJsonSerializerService;
|
||||
import org.mifosng.platform.api.infrastructure.ApiParameterHelper;
|
||||
import org.mifosng.platform.exceptions.UnrecognizedQueryParamException;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
import org.mifosng.platform.saving.service.DepositAccountReadPlatformService;
|
||||
import org.mifosng.platform.saving.service.DepositAccountWritePlatformService;
|
||||
@@ -191,4 +195,52 @@ public class DepositAccountsApiResource {
|
||||
|
||||
return new DepositAccountData(account, interestCompoundedEveryPeriodTypeOptions, productOptions);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public Response SubmitDepositApplication(@PathParam("accountId") final Long accountId,
|
||||
@QueryParam("command") final String commandParam, final String jsonRequestBody) {
|
||||
|
||||
DepositStateTransitionCommand command=apiDataConversionService.convertJsonToDepositStateTransitionCommand(accountId, jsonRequestBody);
|
||||
|
||||
Response response=null;
|
||||
|
||||
if (is(commandParam, "approve")) {
|
||||
|
||||
EntityIdentifier identifier = this.depositAccountWritePlatformService.approveDepositApplication(command);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
}
|
||||
|
||||
else if (is(commandParam, "reject")) {
|
||||
|
||||
EntityIdentifier identifier = this.depositAccountWritePlatformService.rejectDepositApplication(command);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
|
||||
} else if (is(commandParam, "withdrewbyclient")) {
|
||||
|
||||
EntityIdentifier identifier = this.depositAccountWritePlatformService.withdrawDepositApplication(command);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
|
||||
}
|
||||
|
||||
UndoStateTransitionCommand undoCommand = new UndoStateTransitionCommand(accountId);
|
||||
|
||||
if (is(commandParam, "undoapproval")) {
|
||||
EntityIdentifier identifier = this.depositAccountWritePlatformService.undoDepositApproval(undoCommand);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private boolean is(final String commandParam, final String commandValue) {
|
||||
return StringUtils.isNotBlank(commandParam)
|
||||
&& commandParam.trim().equalsIgnoreCase(commandValue);
|
||||
}
|
||||
}
|
||||
@@ -187,9 +187,7 @@ public class LoansApiResource {
|
||||
@QueryParam("command") final String commandParam,
|
||||
final String jsonRequestBody) {
|
||||
|
||||
LoanStateTransitionCommand command = this.apiDataConversionService
|
||||
.convertJsonToLoanStateTransitionCommand(loanId,
|
||||
jsonRequestBody);
|
||||
LoanStateTransitionCommand command = this.apiDataConversionService.convertJsonToLoanStateTransitionCommand(loanId, jsonRequestBody);
|
||||
|
||||
Response response = null;
|
||||
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.mifosng.platform.api.commands;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class DepositStateTransitionCommand {
|
||||
|
||||
private final Long accountId;
|
||||
private final LocalDate eventDate;
|
||||
|
||||
public DepositStateTransitionCommand(final Long accountId, final LocalDate enentDate) {
|
||||
this.accountId=accountId;
|
||||
this.eventDate=enentDate;
|
||||
}
|
||||
|
||||
public Long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public LocalDate getEventDate() {
|
||||
return eventDate;
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -12,6 +12,11 @@ public class UndoStateTransitionCommand {
|
||||
this.loanId = loanId;
|
||||
this.note = note;
|
||||
}
|
||||
|
||||
public UndoStateTransitionCommand(final Long loanId) {
|
||||
this.loanId = loanId;
|
||||
this.note=null;
|
||||
}
|
||||
|
||||
public Long getLoanId() {
|
||||
return this.loanId;
|
||||
|
||||
+3
@@ -6,6 +6,7 @@ import org.mifosng.platform.api.commands.ChargeCommand;
|
||||
import org.mifosng.platform.api.commands.ClientCommand;
|
||||
import org.mifosng.platform.api.commands.DepositAccountCommand;
|
||||
import org.mifosng.platform.api.commands.DepositProductCommand;
|
||||
import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
|
||||
import org.mifosng.platform.api.commands.FundCommand;
|
||||
import org.mifosng.platform.api.commands.GroupCommand;
|
||||
import org.mifosng.platform.api.commands.LoanProductCommand;
|
||||
@@ -57,4 +58,6 @@ public interface ApiDataConversionService {
|
||||
NoteCommand convertJsonToNoteCommand(Long resourceIdentifier, Long clientId, String jsonRequestBody);
|
||||
|
||||
DepositAccountCommand convertJsonToDepositAccountCommand(Long resourceIdentifier, String jsonRequestBody);
|
||||
|
||||
DepositStateTransitionCommand convertJsonToDepositStateTransitionCommand(Long resourceIdentifier, String jsonRequestBody);
|
||||
}
|
||||
+23
@@ -23,6 +23,7 @@ import org.mifosng.platform.api.commands.ChargeCommand;
|
||||
import org.mifosng.platform.api.commands.ClientCommand;
|
||||
import org.mifosng.platform.api.commands.DepositAccountCommand;
|
||||
import org.mifosng.platform.api.commands.DepositProductCommand;
|
||||
import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
|
||||
import org.mifosng.platform.api.commands.FundCommand;
|
||||
import org.mifosng.platform.api.commands.GroupCommand;
|
||||
import org.mifosng.platform.api.commands.LoanProductCommand;
|
||||
@@ -1004,4 +1005,26 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
|
||||
interestCompoundedEvery, interestCompoundedEveryPeriodType,
|
||||
canRenew, canPreClose, preClosureInterestRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DepositStateTransitionCommand convertJsonToDepositStateTransitionCommand(
|
||||
Long resourceIdentifier, String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
Type typeOfMap = new TypeToken<Map<String, Object>>(){}.getType();
|
||||
Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
|
||||
|
||||
Set<String> supportedParams = new HashSet<String>( Arrays.asList("eventDate", "dateFormat"));
|
||||
|
||||
checkForUnsupportedParameters(requestMap, supportedParams);
|
||||
|
||||
Set<String> modifiedParameters = new HashSet<String>();
|
||||
|
||||
LocalDate eventDate = extractLocalDateParameter("eventDate", requestMap, modifiedParameters);
|
||||
|
||||
return new DepositStateTransitionCommand(resourceIdentifier, eventDate);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.mifosng.platform.exceptions;
|
||||
|
||||
public class InvalidDepositStateTransitionException extends
|
||||
AbstractPlatformDomainRuleException {
|
||||
|
||||
public InvalidDepositStateTransitionException(final String action, String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) {
|
||||
super("error.msg.deposit." + action + "." + postFix, defaultUserMessage, defaultUserMessageArgs);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
}
|
||||
@@ -63,11 +63,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
@Embedded
|
||||
private final LoanProductRelatedDetail loanRepaymentScheduleDetail;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "term_frequency", nullable = false)
|
||||
private Integer termFrequency;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(name = "term_period_frequency_enum", nullable = false)
|
||||
private PeriodFrequencyType termPeriodFrequencyType;
|
||||
@@ -83,7 +81,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
@Column(name = "rejectedon_date")
|
||||
private Date rejectedOnDate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "withdrawnon_date")
|
||||
private Date withdrawnOnDate;
|
||||
@@ -523,7 +520,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLoanOverpayment(@SuppressWarnings("unused") final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
private void handleLoanOverpayment(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
|
||||
+136
-25
@@ -20,20 +20,20 @@ import org.mifosng.platform.api.commands.DepositAccountCommand;
|
||||
import org.mifosng.platform.client.domain.Client;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
import org.mifosng.platform.exceptions.InvalidDepositStateTransitionException;
|
||||
import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
import org.mifosng.platform.savingproduct.domain.DepositProduct;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
import org.mifosng.platform.user.domain.AppUser;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_deposit_account", uniqueConstraints = @UniqueConstraint(name="deposit_acc_external_id", columnNames = { "external_id" }))
|
||||
public class DepositAccount extends AbstractPersistable<Long> {
|
||||
public class DepositAccount extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "client_id", nullable = false)
|
||||
private Client client;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "product_id")
|
||||
private final DepositProduct product;
|
||||
@@ -41,81 +41,81 @@ public class DepositAccount extends AbstractPersistable<Long> {
|
||||
@Column(name = "external_id")
|
||||
private String externalId;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Embedded
|
||||
private MonetaryCurrency currency;
|
||||
|
||||
@Column(name = "deposit_amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal depositAmount;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "maturity_nominal_interest_rate", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal interestRate;
|
||||
|
||||
@Column(name = "tenure_months", nullable=false)
|
||||
private Integer tenureInMonths;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "interest_compounded_every", nullable = false)
|
||||
private Integer interestCompoundedEvery;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(name = "interest_compounded_every_period_enum", nullable = false)
|
||||
private PeriodFrequencyType interestCompoundedFrequencyType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "projected_commencement_date")
|
||||
private Date projectedCommencementDate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "actual_commencement_date")
|
||||
private Date actualCommencementDate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "projected_maturity_date")
|
||||
private Date projectedMaturityDate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "actual_maturity_date")
|
||||
private Date actualMaturityDate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "projected_interest_accrued_on_maturity", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal projectedInterestAccruedOnMaturity;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "actual_interest_accrued", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal interestAccrued;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "projected_total_maturity_amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal projectedTotalOnMaturity;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "actual_total_amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal total;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "pre_closure_interest_rate", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal preClosureInterestRate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "is_renewal_allowed", nullable = false)
|
||||
private boolean renewalAllowed = false;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "is_preclosure_allowed", nullable = false)
|
||||
private boolean preClosureAllowed = false;
|
||||
|
||||
@Column(name = "is_deleted", nullable=false)
|
||||
private boolean deleted = false;
|
||||
|
||||
public static DepositAccount openNew(
|
||||
@Column(name = "status_enum", nullable = false)
|
||||
private Integer depositStatus;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "closedon_date")
|
||||
private Date closedOnDate;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "rejectedon_date")
|
||||
private Date rejectedOnDate;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "withdrawnon_date")
|
||||
private Date withdrawnOnDate;
|
||||
|
||||
public DepositAccount openNew(
|
||||
final Client client, final DepositProduct product,
|
||||
final String externalId, final Money deposit, final BigDecimal maturityInterestRate, final Integer tenureInMonths,
|
||||
final Integer interestCompoundedEvery,
|
||||
@@ -123,20 +123,30 @@ public class DepositAccount extends AbstractPersistable<Long> {
|
||||
final LocalDate commencementDate,
|
||||
final boolean renewalAllowed,
|
||||
final boolean preClosureAllowed,
|
||||
final FixedTermDepositInterestCalculator fixedTermDepositInterestCalculator) {
|
||||
final FixedTermDepositInterestCalculator fixedTermDepositInterestCalculator,
|
||||
final DepositLifecycleStateMachine depositLifecycleStateMachine) {
|
||||
|
||||
Money futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityFor(deposit, tenureInMonths,
|
||||
maturityInterestRate, interestCompoundedEvery, interestCompoundedFrequencyPeriodType);
|
||||
|
||||
|
||||
DepositStatus from = null;
|
||||
if (depositStatus != null) {
|
||||
from = DepositStatus.fromInt(depositStatus);
|
||||
}
|
||||
|
||||
DepositStatus statusEnum = depositLifecycleStateMachine.transition(DepositEvent.DEPOSIT_CREATED, from);
|
||||
depositStatus = statusEnum.getValue();
|
||||
|
||||
return new DepositAccount(client, product, externalId, deposit, maturityInterestRate, tenureInMonths,
|
||||
interestCompoundedEvery, interestCompoundedFrequencyPeriodType, commencementDate, renewalAllowed, preClosureAllowed, futureValueOnMaturity);
|
||||
interestCompoundedEvery, interestCompoundedFrequencyPeriodType, commencementDate, renewalAllowed, preClosureAllowed, futureValueOnMaturity,depositStatus);
|
||||
}
|
||||
|
||||
protected DepositAccount() {
|
||||
public DepositAccount() {
|
||||
this.product = null;
|
||||
}
|
||||
|
||||
private DepositAccount(
|
||||
public DepositAccount(
|
||||
final Client client, final DepositProduct product,
|
||||
final String externalId, final Money deposit, final BigDecimal interestRate, final Integer termInMonths,
|
||||
final Integer interestCompoundedEvery,
|
||||
@@ -144,7 +154,8 @@ public class DepositAccount extends AbstractPersistable<Long> {
|
||||
final LocalDate commencementDate,
|
||||
final boolean renewalAllowed,
|
||||
final boolean preClosureAllowed,
|
||||
final Money futureValueOnMaturity) {
|
||||
final Money futureValueOnMaturity,
|
||||
final Integer depositStatus) {
|
||||
this.client = client;
|
||||
this.product = product;
|
||||
this.externalId = externalId;
|
||||
@@ -170,6 +181,7 @@ public class DepositAccount extends AbstractPersistable<Long> {
|
||||
// derived fields
|
||||
this.projectedInterestAccruedOnMaturity = futureValueOnMaturity.minus(deposit).getAmount();
|
||||
this.projectedTotalOnMaturity = futureValueOnMaturity.getAmount();
|
||||
this.depositStatus=depositStatus;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
@@ -189,4 +201,103 @@ public class DepositAccount extends AbstractPersistable<Long> {
|
||||
public void update(final DepositAccountCommand command) {
|
||||
|
||||
}
|
||||
|
||||
public void approve(final LocalDate actualCommencementDate, DepositLifecycleStateMachine depositLifecycleStateMachine) {
|
||||
|
||||
DepositStatus statusEnum = depositLifecycleStateMachine.transition(DepositEvent.DEPOSIT_APPROVED, DepositStatus.fromInt(this.depositStatus));
|
||||
this.depositStatus = statusEnum.getValue();
|
||||
|
||||
this.actualCommencementDate = actualCommencementDate
|
||||
.toDateTimeAtCurrentTime().toDate();
|
||||
|
||||
if(this.actualCommencementDate != null)
|
||||
this.actualMaturityDate = new LocalDate(this.actualCommencementDate).plusMonths(this.tenureInMonths).toDate();
|
||||
|
||||
LocalDate submittalDate = new LocalDate(this.projectedCommencementDate);
|
||||
if (actualCommencementDate.isBefore(submittalDate)) {
|
||||
final String errorMessage = "The date on which a deposit is approved cannot be before its submittal date: "
|
||||
+ submittalDate.toString();
|
||||
throw new InvalidDepositStateTransitionException("approval",
|
||||
"cannot.be.before.submittal.date", errorMessage,
|
||||
getActualCommencementDate(), submittalDate);
|
||||
}
|
||||
if (actualCommencementDate.isAfter(new LocalDate())) {
|
||||
final String errorMessage = "The date on which a deposit is approved cannot be in the future.";
|
||||
throw new InvalidDepositStateTransitionException("approval",
|
||||
"cannot.be.a.future.date", errorMessage,
|
||||
getActualCommencementDate());
|
||||
}
|
||||
}
|
||||
|
||||
public LocalDate getActualCommencementDate() {
|
||||
LocalDate date=null;
|
||||
if(this.actualCommencementDate!=null){
|
||||
date = new LocalDate(this.actualCommencementDate);
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
public LocalDate getProjectedCommencementDate() {
|
||||
LocalDate date=null;
|
||||
if(this.projectedCommencementDate!=null){
|
||||
date = new LocalDate(this.projectedCommencementDate);
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
public void reject(LocalDate rejectedOn, DepositLifecycleStateMachine depositLifecycleStateMachine) {
|
||||
|
||||
DepositStatus statusEnum = depositLifecycleStateMachine.transition(DepositEvent.DEPOSIT_REJECTED, DepositStatus.fromInt(this.depositStatus));
|
||||
this.depositStatus = statusEnum.getValue();
|
||||
|
||||
this.rejectedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate();
|
||||
this.closedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate();
|
||||
|
||||
if (rejectedOn.isBefore(getProjectedCommencementDate())) {
|
||||
|
||||
final String errorMessage = "The date on which a deposit is rejected cannot be before its submittal date: " + getProjectedCommencementDate().toString();
|
||||
throw new InvalidDepositStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, rejectedOn, getProjectedCommencementDate());
|
||||
|
||||
}
|
||||
if (rejectedOn.isAfter(new LocalDate())) {
|
||||
|
||||
final String errorMessage = "The date on which a deposit is rejected cannot be in the future.";
|
||||
throw new InvalidDepositStateTransitionException("reject", "cannot.be.a.future.date", errorMessage, rejectedOn);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void withdraw(LocalDate withdrawnOn, DepositLifecycleStateMachine depositLifecycleStateMachine) {
|
||||
|
||||
DepositStatus statusEnum = depositLifecycleStateMachine.transition(DepositEvent.DEPOSIT_WITHDRAWN, DepositStatus.fromInt(this.depositStatus));
|
||||
this.depositStatus = statusEnum.getValue();
|
||||
|
||||
this.withdrawnOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate();
|
||||
this.closedOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate();
|
||||
|
||||
if (withdrawnOn.isBefore(getProjectedCommencementDate())) {
|
||||
|
||||
final String errorMessage = "The date on which a deposit is rejected cannot be before its submittal date: " + getProjectedCommencementDate().toString();
|
||||
throw new InvalidDepositStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, withdrawnOn, getProjectedCommencementDate());
|
||||
|
||||
}
|
||||
if (withdrawnOn.isAfter(new LocalDate())) {
|
||||
|
||||
final String errorMessage = "The date on which a deposit is rejected cannot be in the future.";
|
||||
throw new InvalidDepositStateTransitionException("reject", "cannot.be.a.future.date", errorMessage, withdrawnOn);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void undoDepositApproval(DepositLifecycleStateMachine depositLifecycleStateMachine) {
|
||||
|
||||
DepositStatus statusEnum = depositLifecycleStateMachine.transition(DepositEvent.DEPOSIT_APPROVAL_UNDO, DepositStatus.fromInt(this.depositStatus));
|
||||
this.depositStatus = statusEnum.getValue();
|
||||
|
||||
this.actualCommencementDate = null;
|
||||
this.actualMaturityDate=null;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.mifosng.platform.saving.domain;
|
||||
|
||||
public enum DepositEvent {
|
||||
|
||||
DEPOSIT_CREATED, DEPOSIT_REJECTED, DEPOSIT_APPROVED, DEPOSIT_APPROVAL_UNDO, DEPOSIT_WITHDRAWN, DEPOSIT_MATURED;
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package org.mifosng.platform.saving.domain;
|
||||
|
||||
|
||||
public interface DepositLifecycleStateMachine {
|
||||
|
||||
DepositStatus transition(DepositEvent loanEvent, DepositStatus from);
|
||||
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package org.mifosng.platform.saving.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DepositLifecycleStateMachineImpl implements
|
||||
DepositLifecycleStateMachine {
|
||||
|
||||
private final List<DepositStatus> allowedDepositStatuses;
|
||||
|
||||
public DepositLifecycleStateMachineImpl(List<DepositStatus> allowedDepositStatuses) {
|
||||
this.allowedDepositStatuses=allowedDepositStatuses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DepositStatus transition(DepositEvent depositEvent, DepositStatus from) {
|
||||
|
||||
DepositStatus newState = null;
|
||||
|
||||
switch (depositEvent) {
|
||||
case DEPOSIT_CREATED:
|
||||
if (from == null) {
|
||||
newState = stateOf(DepositStatus.SUBMITED_AND_PENDING_APPROVAL, allowedDepositStatuses);
|
||||
}
|
||||
break;
|
||||
case DEPOSIT_REJECTED:
|
||||
if (from.hasStateOf(DepositStatus.SUBMITED_AND_PENDING_APPROVAL)) {
|
||||
newState = stateOf(DepositStatus.REJECTED, allowedDepositStatuses);
|
||||
}
|
||||
break;
|
||||
case DEPOSIT_APPROVED:
|
||||
if (from.hasStateOf(DepositStatus.SUBMITED_AND_PENDING_APPROVAL)) {
|
||||
newState = stateOf(DepositStatus.APPROVED, allowedDepositStatuses);
|
||||
}
|
||||
break;
|
||||
case DEPOSIT_WITHDRAWN:
|
||||
if (this.anyOfAllowedWhenComingFrom(from, DepositStatus.SUBMITED_AND_PENDING_APPROVAL, DepositStatus.APPROVED)) {
|
||||
newState = stateOf(DepositStatus.WITHDRAWN_BY_CLIENT, allowedDepositStatuses);
|
||||
}
|
||||
break;
|
||||
case DEPOSIT_APPROVAL_UNDO:
|
||||
if (from.hasStateOf(DepositStatus.APPROVED)) {
|
||||
newState = stateOf(DepositStatus.SUBMITED_AND_PENDING_APPROVAL, allowedDepositStatuses);
|
||||
}
|
||||
break;
|
||||
case DEPOSIT_MATURED:
|
||||
if (from.hasStateOf(DepositStatus.APPROVED) ){
|
||||
newState = stateOf(DepositStatus.MATURED, allowedDepositStatuses);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
private DepositStatus stateOf(DepositStatus state, List<DepositStatus> allowedLoanStatuses) {
|
||||
DepositStatus match = null;
|
||||
for (DepositStatus depositStatus : allowedLoanStatuses) {
|
||||
if (depositStatus.hasStateOf(state)) {
|
||||
match = depositStatus;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
private boolean anyOfAllowedWhenComingFrom(final DepositStatus state, final DepositStatus... allowedStates) {
|
||||
boolean allowed = false;
|
||||
|
||||
for (DepositStatus allowedState : allowedStates) {
|
||||
if (state.hasStateOf(allowedState)) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return allowed;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package org.mifosng.platform.saving.domain;
|
||||
|
||||
|
||||
|
||||
public enum DepositStatus {
|
||||
|
||||
INVALID(0, "depositStatusType.invalid"), //
|
||||
SUBMITED_AND_PENDING_APPROVAL(100, "depositStatusType.submitted.and.pending.approval"), //
|
||||
APPROVED(200, "depositStatusType.approved"), //
|
||||
ACTIVE(300, "depositStatusType.active"), //
|
||||
WITHDRAWN_BY_CLIENT(400, "depositStatusType.withdrawn.by.client"), //
|
||||
REJECTED(500, "depositStatusType.rejected"), //
|
||||
MATURED(700,"depositStatusType.matured"),
|
||||
CLOSED(600, "depositStatusType.closed");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
private DepositStatus(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public boolean hasStateOf(final DepositStatus state) {
|
||||
return this.value.equals(state.getValue());
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public boolean isSubmittedAndPendingApproval() {
|
||||
return this.value.equals(DepositStatus.SUBMITED_AND_PENDING_APPROVAL.getValue());
|
||||
}
|
||||
|
||||
public boolean isApproved() {
|
||||
return this.value.equals(DepositStatus.APPROVED.getValue());
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this.value.equals(DepositStatus.ACTIVE.getValue());
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return this.value.equals(DepositStatus.CLOSED.getValue());
|
||||
}
|
||||
|
||||
public boolean isWithdrawnByClient() {
|
||||
return this.value.equals(DepositStatus.WITHDRAWN_BY_CLIENT.getValue());
|
||||
}
|
||||
|
||||
public boolean isRejected() {
|
||||
return this.value.equals(DepositStatus.REJECTED.getValue());
|
||||
}
|
||||
|
||||
public static DepositStatus fromInt(final Integer statusValue) {
|
||||
|
||||
DepositStatus enumeration = DepositStatus.INVALID;
|
||||
switch (statusValue) {
|
||||
case 100:
|
||||
enumeration = DepositStatus.SUBMITED_AND_PENDING_APPROVAL;
|
||||
break;
|
||||
case 200:
|
||||
enumeration = DepositStatus.APPROVED;
|
||||
break;
|
||||
case 300:
|
||||
enumeration = DepositStatus.ACTIVE;
|
||||
break;
|
||||
case 400:
|
||||
enumeration = DepositStatus.WITHDRAWN_BY_CLIENT;
|
||||
break;
|
||||
case 500:
|
||||
enumeration = DepositStatus.REJECTED;
|
||||
break;
|
||||
case 600:
|
||||
enumeration = DepositStatus.CLOSED;
|
||||
break;
|
||||
case 700:
|
||||
enumeration = DepositStatus.MATURED;
|
||||
break;
|
||||
}
|
||||
return enumeration;
|
||||
}
|
||||
|
||||
}
|
||||
+12
-2
@@ -1,6 +1,8 @@
|
||||
package org.mifosng.platform.saving.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosng.platform.api.commands.DepositAccountCommand;
|
||||
import org.mifosng.platform.client.domain.Client;
|
||||
@@ -10,6 +12,9 @@ import org.mifosng.platform.exceptions.ClientNotFoundException;
|
||||
import org.mifosng.platform.exceptions.DepositProductNotFoundException;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
import org.mifosng.platform.saving.domain.DepositAccount;
|
||||
import org.mifosng.platform.saving.domain.DepositLifecycleStateMachine;
|
||||
import org.mifosng.platform.saving.domain.DepositLifecycleStateMachineImpl;
|
||||
import org.mifosng.platform.saving.domain.DepositStatus;
|
||||
import org.mifosng.platform.saving.domain.FixedTermDepositInterestCalculator;
|
||||
import org.mifosng.platform.savingproduct.domain.DepositProduct;
|
||||
import org.mifosng.platform.savingproduct.domain.DepositProductRepository;
|
||||
@@ -80,13 +85,18 @@ public class DepositAccountAssembler {
|
||||
}
|
||||
// end of details allowed to be overriden from product
|
||||
|
||||
DepositAccount account = DepositAccount.openNew(client, product, command.getExternalId(),
|
||||
DepositAccount account =new DepositAccount().openNew(client, product, command.getExternalId(),
|
||||
deposit,
|
||||
maturityInterestRate,
|
||||
tenureInMonths, compoundingInterestEvery, compoundingInterestFrequency,
|
||||
command.getCommencementDate(),
|
||||
renewalAllowed, preClosureAllowed, this.fixedTermDepositInterestCalculator);
|
||||
renewalAllowed, preClosureAllowed, this.fixedTermDepositInterestCalculator, defaultDepositLifecycleStateMachine());
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
private DepositLifecycleStateMachine defaultDepositLifecycleStateMachine() {
|
||||
List<DepositStatus> allowedDepositStatuses = Arrays.asList(DepositStatus.values());
|
||||
return new DepositLifecycleStateMachineImpl(allowedDepositStatuses);
|
||||
}
|
||||
}
|
||||
+15
@@ -1,6 +1,8 @@
|
||||
package org.mifosng.platform.saving.service;
|
||||
|
||||
import org.mifosng.platform.api.commands.DepositAccountCommand;
|
||||
import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
|
||||
import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
|
||||
import org.mifosng.platform.api.data.EntityIdentifier;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@@ -14,4 +16,17 @@ public interface DepositAccountWritePlatformService {
|
||||
|
||||
@PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')")
|
||||
EntityIdentifier deleteDepositAccount(Long productId);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')")
|
||||
EntityIdentifier approveDepositApplication(DepositStateTransitionCommand command);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')")
|
||||
EntityIdentifier rejectDepositApplication(DepositStateTransitionCommand command);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')")
|
||||
EntityIdentifier withdrawDepositApplication(DepositStateTransitionCommand command);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')")
|
||||
EntityIdentifier undoDepositApproval(UndoStateTransitionCommand undoCommand);
|
||||
|
||||
}
|
||||
|
||||
+111
@@ -1,13 +1,24 @@
|
||||
package org.mifosng.platform.saving.service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.commands.DepositAccountCommand;
|
||||
import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
|
||||
import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
|
||||
import org.mifosng.platform.api.data.EntityIdentifier;
|
||||
import org.mifosng.platform.exceptions.DepositAccountNotFoundException;
|
||||
import org.mifosng.platform.exceptions.NoAuthorizationException;
|
||||
import org.mifosng.platform.exceptions.PlatformDataIntegrityException;
|
||||
import org.mifosng.platform.exceptions.ProductNotFoundException;
|
||||
import org.mifosng.platform.saving.domain.DepositAccount;
|
||||
import org.mifosng.platform.saving.domain.DepositAccountRepository;
|
||||
import org.mifosng.platform.saving.domain.DepositLifecycleStateMachine;
|
||||
import org.mifosng.platform.saving.domain.DepositLifecycleStateMachineImpl;
|
||||
import org.mifosng.platform.saving.domain.DepositStatus;
|
||||
import org.mifosng.platform.security.PlatformSecurityContext;
|
||||
import org.mifosng.platform.user.domain.AppUser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -112,4 +123,104 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
|
||||
|
||||
return new EntityIdentifier(accountId);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier approveDepositApplication(DepositStateTransitionCommand command) {
|
||||
|
||||
AppUser currentUser = context.authenticatedUser();
|
||||
|
||||
DepositStateTransitionCommandValidator validator = new DepositStateTransitionCommandValidator(command);
|
||||
validator.validate();
|
||||
|
||||
|
||||
DepositAccount account = this.depositAccountRepository.findOne(command.getAccountId());
|
||||
if (account == null || account.isDeleted()) {
|
||||
throw new DepositAccountNotFoundException(command.getAccountId());
|
||||
}
|
||||
|
||||
LocalDate eventDate = command.getEventDate();
|
||||
if (this.isBeforeToday(eventDate) && currentUser.canNotApproveLoanInPast()) {
|
||||
throw new NoAuthorizationException("User has no authority to approve deposit with a date in the past.");
|
||||
}
|
||||
|
||||
account.approve(eventDate, defaultDepositLifecycleStateMachine());
|
||||
this.depositAccountRepository.save(account);
|
||||
|
||||
return new EntityIdentifier(account.getId());
|
||||
|
||||
}
|
||||
|
||||
private boolean isBeforeToday(final LocalDate date) {
|
||||
return date.isBefore(new LocalDate());
|
||||
}
|
||||
private DepositLifecycleStateMachine defaultDepositLifecycleStateMachine() {
|
||||
List<DepositStatus> allowedDepositStatuses = Arrays.asList(DepositStatus.values());
|
||||
return new DepositLifecycleStateMachineImpl(allowedDepositStatuses);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier rejectDepositApplication(DepositStateTransitionCommand command) {
|
||||
|
||||
AppUser currentUser = context.authenticatedUser();
|
||||
|
||||
DepositStateTransitionCommandValidator validator = new DepositStateTransitionCommandValidator(command);
|
||||
validator.validate();
|
||||
|
||||
DepositAccount account = this.depositAccountRepository.findOne(command.getAccountId());
|
||||
if (account == null || account.isDeleted()) {
|
||||
throw new DepositAccountNotFoundException(command.getAccountId());
|
||||
}
|
||||
|
||||
LocalDate eventDate = command.getEventDate();
|
||||
if (this.isBeforeToday(eventDate) && currentUser.canNotApproveLoanInPast()) {
|
||||
throw new NoAuthorizationException("User has no authority to approve deposit with a date in the past.");
|
||||
}
|
||||
|
||||
account.reject(eventDate, defaultDepositLifecycleStateMachine());
|
||||
this.depositAccountRepository.save(account);
|
||||
|
||||
return new EntityIdentifier(account.getId());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier withdrawDepositApplication(DepositStateTransitionCommand command) {
|
||||
|
||||
AppUser currentUser = context.authenticatedUser();
|
||||
|
||||
DepositStateTransitionCommandValidator validator = new DepositStateTransitionCommandValidator(command);
|
||||
validator.validate();
|
||||
|
||||
DepositAccount account = this.depositAccountRepository.findOne(command.getAccountId());
|
||||
if (account == null || account.isDeleted()) {
|
||||
throw new DepositAccountNotFoundException(command.getAccountId());
|
||||
}
|
||||
|
||||
LocalDate eventDate = command.getEventDate();
|
||||
if (this.isBeforeToday(eventDate) && currentUser.canNotApproveLoanInPast()) {
|
||||
throw new NoAuthorizationException("User has no authority to approve deposit with a date in the past.");
|
||||
}
|
||||
|
||||
account.withdraw(eventDate, defaultDepositLifecycleStateMachine());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier undoDepositApproval(UndoStateTransitionCommand command) {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
DepositAccount account = this.depositAccountRepository.findOne(command.getLoanId());
|
||||
if (account == null || account.isDeleted()) {
|
||||
throw new DepositAccountNotFoundException(command.getLoanId());
|
||||
}
|
||||
|
||||
account.undoDepositApproval(defaultDepositLifecycleStateMachine());
|
||||
this.depositAccountRepository.save(account);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package org.mifosng.platform.saving.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosng.platform.DataValidatorBuilder;
|
||||
import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
|
||||
import org.mifosng.platform.api.data.ApiParameterError;
|
||||
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
|
||||
|
||||
public class DepositStateTransitionCommandValidator {
|
||||
|
||||
private final DepositStateTransitionCommand command;
|
||||
|
||||
public DepositStateTransitionCommandValidator(final DepositStateTransitionCommand command) {
|
||||
this.command=command;
|
||||
}
|
||||
|
||||
public void validate(){
|
||||
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("deposit.transition");
|
||||
baseDataValidator.reset().parameter("accountId").value(command.getAccountId()).notNull().integerGreaterThanZero();
|
||||
baseDataValidator.reset().parameter("eventDate").value(command.getEventDate()).notNull();
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user