Mifosx-43 fixed bugs from pull #21

This commit is contained in:
Madhukar
2012-08-30 10:01:07 +01:00
committed by Keith Woodlock
parent 3cc06530da
commit 2132a5dccb
14 changed files with 311 additions and 32 deletions
@@ -22,6 +22,7 @@ import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.api.commands.DepositAccountCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionApprovalCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionCommand; import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
import org.mifosng.platform.api.commands.UndoStateTransitionCommand; import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
import org.mifosng.platform.api.data.DepositAccountData; import org.mifosng.platform.api.data.DepositAccountData;
@@ -67,7 +68,7 @@ public class DepositAccountsApiResource {
"deposit", "maturityInterestRate", "tenureInMonths", "deposit", "maturityInterestRate", "tenureInMonths",
"interestCompoundedEvery", "interestCompoundedEvery",
"interestCompoundedEveryPeriodType", "renewalAllowed", "interestCompoundedEveryPeriodType", "renewalAllowed",
"preClosureAllowed", "preClosureInterestRate")); "preClosureAllowed", "preClosureInterestRate", "statusEnum","withdrawnonDate","rejectedonDate","closedonDate"));
@POST @POST
@@ -187,23 +188,26 @@ public class DepositAccountsApiResource {
public Response SubmitDepositApplication(@PathParam("accountId") final Long accountId, public Response SubmitDepositApplication(@PathParam("accountId") final Long accountId,
@QueryParam("command") final String commandParam, final String jsonRequestBody) { @QueryParam("command") final String commandParam, final String jsonRequestBody) {
DepositStateTransitionCommand command=apiDataConversionService.convertJsonToDepositStateTransitionCommand(accountId, jsonRequestBody);
Response response=null; Response response=null;
if (is(commandParam, "approve")) { if (is(commandParam, "approve")) {
DepositStateTransitionApprovalCommand command=apiDataConversionService.convertJsonToDepositStateTransitionApprovalCommand(accountId, jsonRequestBody);
EntityIdentifier identifier = this.depositAccountWritePlatformService.approveDepositApplication(command); EntityIdentifier identifier = this.depositAccountWritePlatformService.approveDepositApplication(command);
response = Response.ok().entity(identifier).build(); response = Response.ok().entity(identifier).build();
} }
else if (is(commandParam, "reject")) { else if (is(commandParam, "reject")) {
DepositStateTransitionCommand command=apiDataConversionService.convertJsonToDepositStateTransitionCommand(accountId, jsonRequestBody);
EntityIdentifier identifier = this.depositAccountWritePlatformService.rejectDepositApplication(command); EntityIdentifier identifier = this.depositAccountWritePlatformService.rejectDepositApplication(command);
response = Response.ok().entity(identifier).build(); response = Response.ok().entity(identifier).build();
} else if (is(commandParam, "withdrewbyclient")) { } else if (is(commandParam, "withdrewbyclient")) {
DepositStateTransitionCommand command=apiDataConversionService.convertJsonToDepositStateTransitionCommand(accountId, jsonRequestBody);
EntityIdentifier identifier = this.depositAccountWritePlatformService.withdrawDepositApplication(command); EntityIdentifier identifier = this.depositAccountWritePlatformService.withdrawDepositApplication(command);
response = Response.ok().entity(identifier).build(); response = Response.ok().entity(identifier).build();
@@ -0,0 +1,57 @@
package org.mifosng.platform.api.commands;
import java.math.BigDecimal;
import org.joda.time.LocalDate;
public class DepositStateTransitionApprovalCommand {
private final Long accountId;
private final Long productId;
private final LocalDate eventDate;
private final BigDecimal depositAmount;
private final Integer tenureInMonths;
private final Integer interestCompoundedEveryPeriodType;
private final Integer interestCompoundedEvery;
public DepositStateTransitionApprovalCommand(Long resourceIdentifier, Long productId, LocalDate eventDate, Integer tenureInMonths, BigDecimal depositAmount, Integer interestCompoundedEveryPeriodType, Integer interestCompoundedEvery) {
this.accountId=resourceIdentifier;
this.eventDate=eventDate;
this.depositAmount=depositAmount;
this.tenureInMonths=tenureInMonths;
this.interestCompoundedEveryPeriodType=interestCompoundedEveryPeriodType;
this.productId=productId;
this.interestCompoundedEvery=interestCompoundedEvery;
}
public Long getAccountId() {
return accountId;
}
public LocalDate getEventDate() {
return eventDate;
}
public BigDecimal getDepositAmount() {
return depositAmount;
}
public Integer getTenureInMonths() {
return tenureInMonths;
}
public Integer getInterestCompoundedEveryPeriodType() {
return interestCompoundedEveryPeriodType;
}
public Long getProductId() {
return productId;
}
public Integer getInterestCompoundedEvery() {
return interestCompoundedEvery;
}
}
@@ -29,6 +29,9 @@ public class DepositAccountData {
private final LocalDate actualCommencementDate; private final LocalDate actualCommencementDate;
private final LocalDate projectedMaturityDate; private final LocalDate projectedMaturityDate;
private final LocalDate actualMaturityDate; private final LocalDate actualMaturityDate;
private final LocalDate withdrawnonDate;
private final LocalDate rejectedonDate;
private final LocalDate closedonDate;
private final BigDecimal projectedInterestAccrued; private final BigDecimal projectedInterestAccrued;
private final BigDecimal actualInterestAccrued; private final BigDecimal actualInterestAccrued;
private final BigDecimal projectedMaturityAmount; private final BigDecimal projectedMaturityAmount;
@@ -41,7 +44,6 @@ public class DepositAccountData {
private final BigDecimal preClosureInterestRate; private final BigDecimal preClosureInterestRate;
private final List<EnumOptionData> interestCompoundedEveryPeriodTypeOptions; private final List<EnumOptionData> interestCompoundedEveryPeriodTypeOptions;
private final List<DepositProductLookup> productOptions; private final List<DepositProductLookup> productOptions;
/* /*
@@ -81,6 +83,10 @@ public class DepositAccountData {
this.interestCompoundedEveryPeriodTypeOptions = new ArrayList<EnumOptionData>(); this.interestCompoundedEveryPeriodTypeOptions = new ArrayList<EnumOptionData>();
this.productOptions = new ArrayList<DepositProductLookup>(); this.productOptions = new ArrayList<DepositProductLookup>();
this.withdrawnonDate=null;
this.closedonDate=null;
this.rejectedonDate=null;
} }
public DepositAccountData( public DepositAccountData(
@@ -114,6 +120,10 @@ public class DepositAccountData {
this.interestCompoundedEveryPeriodTypeOptions = interestCompoundedEveryPeriodTypeOptions; this.interestCompoundedEveryPeriodTypeOptions = interestCompoundedEveryPeriodTypeOptions;
this.productOptions = new ArrayList<DepositProductLookup>(productOptions); this.productOptions = new ArrayList<DepositProductLookup>(productOptions);
this.withdrawnonDate=account.getWithdrawnonDate();
this.rejectedonDate=account.getRejectedonDate();
this.closedonDate=account.getClosedonDate();
} }
public DepositAccountData( public DepositAccountData(
@@ -139,7 +149,10 @@ public class DepositAccountData {
final EnumOptionData interestCompoundedEveryPeriodType, final EnumOptionData interestCompoundedEveryPeriodType,
final boolean renewalAllowed, final boolean renewalAllowed,
final boolean preClosureAllowed, final boolean preClosureAllowed,
final BigDecimal preClosureInterestRate) { final BigDecimal preClosureInterestRate,
final LocalDate withdrawnonDate,
final LocalDate rejectedonDate,
final LocalDate closedonDate) {
this.id=id; this.id=id;
this.externalId = externalId; this.externalId = externalId;
this.status = status; this.status = status;
@@ -167,6 +180,10 @@ public class DepositAccountData {
this.interestCompoundedEveryPeriodTypeOptions = new ArrayList<EnumOptionData>(); this.interestCompoundedEveryPeriodTypeOptions = new ArrayList<EnumOptionData>();
this.productOptions = new ArrayList<DepositProductLookup>(); this.productOptions = new ArrayList<DepositProductLookup>();
this.withdrawnonDate = withdrawnonDate;
this.rejectedonDate = rejectedonDate;
this.closedonDate = closedonDate;
} }
public DepositAccountData( public DepositAccountData(
@@ -209,6 +226,10 @@ public class DepositAccountData {
this.interestCompoundedEveryPeriodTypeOptions = new ArrayList<EnumOptionData>(); this.interestCompoundedEveryPeriodTypeOptions = new ArrayList<EnumOptionData>();
this.productOptions = new ArrayList<DepositProductLookup>(); this.productOptions = new ArrayList<DepositProductLookup>();
this.withdrawnonDate=null;
this.closedonDate=null;
this.rejectedonDate=null;
} }
public Long getId() { public Long getId() {
@@ -314,4 +335,16 @@ public class DepositAccountData {
public List<DepositProductLookup> getProductOptions() { public List<DepositProductLookup> getProductOptions() {
return productOptions; return productOptions;
} }
public LocalDate getWithdrawnonDate() {
return withdrawnonDate;
}
public LocalDate getRejectedonDate() {
return rejectedonDate;
}
public LocalDate getClosedonDate() {
return closedonDate;
}
} }
@@ -6,6 +6,7 @@ import org.mifosng.platform.api.commands.ChargeCommand;
import org.mifosng.platform.api.commands.ClientCommand; import org.mifosng.platform.api.commands.ClientCommand;
import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.api.commands.DepositAccountCommand;
import org.mifosng.platform.api.commands.DepositProductCommand; import org.mifosng.platform.api.commands.DepositProductCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionApprovalCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionCommand; import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
import org.mifosng.platform.api.commands.FundCommand; import org.mifosng.platform.api.commands.FundCommand;
import org.mifosng.platform.api.commands.GroupCommand; import org.mifosng.platform.api.commands.GroupCommand;
@@ -60,4 +61,6 @@ public interface ApiDataConversionService {
DepositAccountCommand convertJsonToDepositAccountCommand(Long resourceIdentifier, String jsonRequestBody); DepositAccountCommand convertJsonToDepositAccountCommand(Long resourceIdentifier, String jsonRequestBody);
DepositStateTransitionCommand convertJsonToDepositStateTransitionCommand(Long resourceIdentifier, String jsonRequestBody); DepositStateTransitionCommand convertJsonToDepositStateTransitionCommand(Long resourceIdentifier, String jsonRequestBody);
DepositStateTransitionApprovalCommand convertJsonToDepositStateTransitionApprovalCommand(Long resourceIdentifier, String jsonRequestBody);
} }
@@ -23,6 +23,7 @@ import org.mifosng.platform.api.commands.ChargeCommand;
import org.mifosng.platform.api.commands.ClientCommand; import org.mifosng.platform.api.commands.ClientCommand;
import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.api.commands.DepositAccountCommand;
import org.mifosng.platform.api.commands.DepositProductCommand; import org.mifosng.platform.api.commands.DepositProductCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionApprovalCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionCommand; import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
import org.mifosng.platform.api.commands.FundCommand; import org.mifosng.platform.api.commands.FundCommand;
import org.mifosng.platform.api.commands.GroupCommand; import org.mifosng.platform.api.commands.GroupCommand;
@@ -1027,4 +1028,31 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
return new DepositStateTransitionCommand(resourceIdentifier, eventDate); return new DepositStateTransitionCommand(resourceIdentifier, eventDate);
} }
@Override
public DepositStateTransitionApprovalCommand convertJsonToDepositStateTransitionApprovalCommand(
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("locale","eventDate", "dateFormat", "tenureInMonths", "depositAmount", "interestCompoundedEveryPeriodType", "productId","interestCompoundedEvery"));
checkForUnsupportedParameters(requestMap, supportedParams);
Set<String> modifiedParameters = new HashSet<String>();
LocalDate eventDate = extractLocalDateParameter("eventDate", requestMap, modifiedParameters);
BigDecimal depositAmount=extractBigDecimalParameter("depositAmount", requestMap, modifiedParameters);
Integer tenureInMonths = extractIntegerParameter("tenureInMonths", requestMap, modifiedParameters);
Integer interestCompoundedEveryPeriodType = extractIntegerParameter("interestCompoundedEveryPeriodType", requestMap, modifiedParameters);
Integer interestCompoundedEvery = extractIntegerParameter("interestCompoundedEvery", requestMap, modifiedParameters);
Long productId = extractLongParameter("productId", requestMap, modifiedParameters);
return new DepositStateTransitionApprovalCommand(resourceIdentifier, productId, eventDate, tenureInMonths, depositAmount, interestCompoundedEveryPeriodType,interestCompoundedEvery);
}
} }
@@ -101,7 +101,8 @@ public class GoogleGsonApiJsonSerializerService implements
"id", "externalId", "clientId", "clientName", "productId", "productName", "status", "id", "externalId", "clientId", "clientName", "productId", "productName", "status",
"currency", "deposit", "maturityInterestRate", "tenureInMonths", "currency", "deposit", "maturityInterestRate", "tenureInMonths",
"interestCompoundedEvery", "interestCompoundedEveryPeriodType", "interestCompoundedEvery", "interestCompoundedEveryPeriodType",
"renewalAllowed","preClosureAllowed","preClosureInterestRate" "renewalAllowed","preClosureAllowed","preClosureInterestRate",
"withdrawnonDate","rejectedonDate","closedonDate"
) )
); );
@@ -103,15 +103,15 @@ public class DepositAccount extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "status_enum", nullable = false) @Column(name = "status_enum", nullable = false)
private Integer depositStatus; private Integer depositStatus;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.DATE)
@Column(name = "closedon_date") @Column(name = "closedon_date")
private Date closedOnDate; private Date closedOnDate;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.DATE)
@Column(name = "rejectedon_date") @Column(name = "rejectedon_date")
private Date rejectedOnDate; private Date rejectedOnDate;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.DATE)
@Column(name = "withdrawnon_date") @Column(name = "withdrawnon_date")
private Date withdrawnOnDate; private Date withdrawnOnDate;
@@ -202,30 +202,37 @@ public class DepositAccount extends AbstractAuditableCustom<AppUser, Long> {
} }
public void approve(final LocalDate actualCommencementDate, DepositLifecycleStateMachine depositLifecycleStateMachine) { public void approve(final LocalDate actualCommencementDate, DepositLifecycleStateMachine depositLifecycleStateMachine, DepositApprovalData depositApprovalData, BigDecimal depositAmount) {
DepositStatus statusEnum = depositLifecycleStateMachine.transition(DepositEvent.DEPOSIT_APPROVED, DepositStatus.fromInt(this.depositStatus)); DepositStatus statusEnum = depositLifecycleStateMachine.transition(DepositEvent.DEPOSIT_APPROVED, DepositStatus.fromInt(this.depositStatus));
this.depositStatus = statusEnum.getValue(); this.depositStatus = statusEnum.getValue();
this.actualCommencementDate = actualCommencementDate this.tenureInMonths = depositApprovalData.getTenureInMonths();
.toDateTimeAtCurrentTime().toDate(); this.depositAmount = depositAmount;
this.interestCompoundedFrequencyType = depositApprovalData.getInterestCompoundedEveryPeriodType();
this.actualCommencementDate = actualCommencementDate.toDateTimeAtCurrentTime().toDate();
if(this.actualCommencementDate != null) if(this.actualCommencementDate != null)
this.actualMaturityDate = new LocalDate(this.actualCommencementDate).plusMonths(this.tenureInMonths).toDate(); this.actualMaturityDate = new LocalDate(this.actualCommencementDate).plusMonths(this.tenureInMonths).toDate();
FixedTermDepositInterestCalculator fixedTermDepositInterestCalculator = depositApprovalData.getFixedTermDepositInterestCalculator();
Money futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityFor(depositApprovalData.getDeposit(), depositApprovalData.getTenureInMonths(),
depositApprovalData.getMaturityInterestRate(), depositApprovalData.getInterestCompoundedEvery(), depositApprovalData.getInterestCompoundedEveryPeriodType());
this.projectedInterestAccruedOnMaturity = futureValueOnMaturity.minus(depositApprovalData.getDeposit()).getAmount();
this.projectedTotalOnMaturity = futureValueOnMaturity.getAmount();
LocalDate submittalDate = new LocalDate(this.projectedCommencementDate); LocalDate submittalDate = new LocalDate(this.projectedCommencementDate);
if (actualCommencementDate.isBefore(submittalDate)) { if (actualCommencementDate.isBefore(submittalDate)) {
final String errorMessage = "The date on which a deposit is approved cannot be before its submittal date: " final String errorMessage = "The date on which a deposit is approved cannot be before its submittal date: " + submittalDate.toString();
+ submittalDate.toString(); throw new InvalidDepositStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage, getActualCommencementDate(), submittalDate);
throw new InvalidDepositStateTransitionException("approval",
"cannot.be.before.submittal.date", errorMessage,
getActualCommencementDate(), submittalDate);
} }
if (actualCommencementDate.isAfter(new LocalDate())) { if (actualCommencementDate.isAfter(new LocalDate())) {
final String errorMessage = "The date on which a deposit is approved cannot be in the future."; final String errorMessage = "The date on which a deposit is approved cannot be in the future.";
throw new InvalidDepositStateTransitionException("approval", throw new InvalidDepositStateTransitionException("approval", "cannot.be.a.future.date", errorMessage, getActualCommencementDate());
"cannot.be.a.future.date", errorMessage,
getActualCommencementDate());
} }
} }
@@ -0,0 +1,47 @@
package org.mifosng.platform.saving.domain;
import java.math.BigDecimal;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
public class DepositApprovalData {
private final Integer tenureInMonths;
private final Money deposit;
private final BigDecimal maturityInterestRate;
private final Integer interestCompoundedEvery;
private final PeriodFrequencyType interestCompoundedEveryPeriodType;
private final FixedTermDepositInterestCalculator fixedTermDepositInterestCalculator;
public DepositApprovalData(final Integer tenureInMonths,final Money deposit,final BigDecimal maturityInterestRate,final Integer interestCompoundedEvery,final PeriodFrequencyType interestCompoundedEveryPeriodType,final FixedTermDepositInterestCalculator fixedTermDepositInterestCalculator) {
this.tenureInMonths=tenureInMonths;
this.deposit=deposit;
this.maturityInterestRate=maturityInterestRate;
this.interestCompoundedEvery=interestCompoundedEvery;
this.interestCompoundedEveryPeriodType=interestCompoundedEveryPeriodType;
this.fixedTermDepositInterestCalculator=fixedTermDepositInterestCalculator;
}
public Integer getTenureInMonths() {
return tenureInMonths;
}
public Money getDeposit() {
return deposit;
}
public BigDecimal getMaturityInterestRate() {
return maturityInterestRate;
}
public Integer getInterestCompoundedEvery() {
return interestCompoundedEvery;
}
public PeriodFrequencyType getInterestCompoundedEveryPeriodType() {
return interestCompoundedEveryPeriodType;
}
public FixedTermDepositInterestCalculator getFixedTermDepositInterestCalculator() {
return fixedTermDepositInterestCalculator;
}
}
@@ -2,6 +2,8 @@ package org.mifosng.platform.saving.domain;
import java.util.List; import java.util.List;
import org.mifosng.platform.exceptions.InvalidDepositStateTransitionException;
public class DepositLifecycleStateMachineImpl implements public class DepositLifecycleStateMachineImpl implements
DepositLifecycleStateMachine { DepositLifecycleStateMachine {
@@ -26,28 +28,46 @@ public class DepositLifecycleStateMachineImpl implements
if (from.hasStateOf(DepositStatus.SUBMITED_AND_PENDING_APPROVAL)) { if (from.hasStateOf(DepositStatus.SUBMITED_AND_PENDING_APPROVAL)) {
newState = stateOf(DepositStatus.REJECTED, allowedDepositStatuses); newState = stateOf(DepositStatus.REJECTED, allowedDepositStatuses);
} }
else{
final String errorMessage = "The DepositApplication can not be rejected from "+from.getCode() ;
throw new InvalidDepositStateTransitionException("rejection", "failed", errorMessage,from);
}
break; break;
case DEPOSIT_APPROVED: case DEPOSIT_APPROVED:
if (from.hasStateOf(DepositStatus.SUBMITED_AND_PENDING_APPROVAL)) { if (from.hasStateOf(DepositStatus.SUBMITED_AND_PENDING_APPROVAL)) {
newState = stateOf(DepositStatus.APPROVED, allowedDepositStatuses); newState = stateOf(DepositStatus.APPROVED, allowedDepositStatuses);
} }
else{
final String errorMessage = "The DepositApplication can not be approved from "+from.getCode() ;
throw new InvalidDepositStateTransitionException("approve", "failed", errorMessage,from);
}
break; break;
case DEPOSIT_WITHDRAWN: case DEPOSIT_WITHDRAWN:
if (this.anyOfAllowedWhenComingFrom(from, DepositStatus.SUBMITED_AND_PENDING_APPROVAL, DepositStatus.APPROVED)) { if (this.anyOfAllowedWhenComingFrom(from, DepositStatus.SUBMITED_AND_PENDING_APPROVAL, DepositStatus.APPROVED)) {
newState = stateOf(DepositStatus.WITHDRAWN_BY_CLIENT, allowedDepositStatuses); newState = stateOf(DepositStatus.WITHDRAWN_BY_CLIENT, allowedDepositStatuses);
} }
else{
final String errorMessage = "The DepositApplication can not be withdrawn from "+from.getCode() ;
throw new InvalidDepositStateTransitionException("withdrawbyclient", "failed", errorMessage,from);
}
break; break;
case DEPOSIT_APPROVAL_UNDO: case DEPOSIT_APPROVAL_UNDO:
if (from.hasStateOf(DepositStatus.APPROVED)) { if (from.hasStateOf(DepositStatus.APPROVED)) {
newState = stateOf(DepositStatus.SUBMITED_AND_PENDING_APPROVAL, allowedDepositStatuses); newState = stateOf(DepositStatus.SUBMITED_AND_PENDING_APPROVAL, allowedDepositStatuses);
} }
else{
final String errorMessage = "The DepositApplication can not be unapproved from "+from.getCode() ;
throw new InvalidDepositStateTransitionException("unapprove", "failed", errorMessage,from);
}
break; break;
case DEPOSIT_MATURED: case DEPOSIT_MATURED:
if (from.hasStateOf(DepositStatus.APPROVED) ){ if (from.hasStateOf(DepositStatus.APPROVED) ){
newState = stateOf(DepositStatus.MATURED, allowedDepositStatuses); newState = stateOf(DepositStatus.MATURED, allowedDepositStatuses);
} }
break; else{
default: final String errorMessage = "The DepositApplication can not be matured from "+from.getCode() ;
throw new InvalidDepositStateTransitionException("mature", "failed", errorMessage,from);
}
break; break;
} }
@@ -4,14 +4,18 @@ import java.math.BigDecimal;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.joda.time.LocalDate;
import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.api.commands.DepositAccountCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionApprovalCommand;
import org.mifosng.platform.client.domain.Client; import org.mifosng.platform.client.domain.Client;
import org.mifosng.platform.client.domain.ClientRepository; import org.mifosng.platform.client.domain.ClientRepository;
import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.ClientNotFoundException; import org.mifosng.platform.exceptions.ClientNotFoundException;
import org.mifosng.platform.exceptions.DepositProductNotFoundException; import org.mifosng.platform.exceptions.DepositProductNotFoundException;
import org.mifosng.platform.exceptions.NoAuthorizationException;
import org.mifosng.platform.loan.domain.PeriodFrequencyType; import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.saving.domain.DepositAccount; import org.mifosng.platform.saving.domain.DepositAccount;
import org.mifosng.platform.saving.domain.DepositApprovalData;
import org.mifosng.platform.saving.domain.DepositLifecycleStateMachine; import org.mifosng.platform.saving.domain.DepositLifecycleStateMachine;
import org.mifosng.platform.saving.domain.DepositLifecycleStateMachineImpl; import org.mifosng.platform.saving.domain.DepositLifecycleStateMachineImpl;
import org.mifosng.platform.saving.domain.DepositStatus; import org.mifosng.platform.saving.domain.DepositStatus;
@@ -95,6 +99,28 @@ public class DepositAccountAssembler {
return account; return account;
} }
public DepositApprovalData assembleFrom(DepositStateTransitionApprovalCommand command){
DepositProduct product = this.depositProductRepository.findOne(command.getProductId());
if (product == null || product.isDeleted()) {
throw new DepositProductNotFoundException(command.getProductId());
}
PeriodFrequencyType interestCompoundedEveryPeriodType= product.getInterestCompoundedEveryPeriodType();
if (command.getInterestCompoundedEveryPeriodType() != null) {
interestCompoundedEveryPeriodType = PeriodFrequencyType.fromInt(command.getInterestCompoundedEveryPeriodType());
}
Integer tenureInMonths = command.getTenureInMonths();
Money deposit = Money.of(product.getCurrency(), command.getDepositAmount());
BigDecimal maturityInterestRate = product.getMaturityDefaultInterestRate();
Integer interestCompoundedEvery = product.getInterestCompoundedEvery();
return new DepositApprovalData(tenureInMonths, deposit, maturityInterestRate, interestCompoundedEvery, interestCompoundedEveryPeriodType, this.fixedTermDepositInterestCalculator);
}
private DepositLifecycleStateMachine defaultDepositLifecycleStateMachine() { private DepositLifecycleStateMachine defaultDepositLifecycleStateMachine() {
List<DepositStatus> allowedDepositStatuses = Arrays.asList(DepositStatus.values()); List<DepositStatus> allowedDepositStatuses = Arrays.asList(DepositStatus.values());
return new DepositLifecycleStateMachineImpl(allowedDepositStatuses); return new DepositLifecycleStateMachineImpl(allowedDepositStatuses);
@@ -115,7 +115,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
private static final class DepositAccountMapper implements RowMapper<DepositAccountData> { private static final class DepositAccountMapper implements RowMapper<DepositAccountData> {
public String schema() { public String schema() {
return "da.id as id, da.external_id as externalId, da.client_id as clientId, da.product_id as productId," return "da.id as id, da.external_id as externalId, da.client_id as clientId, da.product_id as productId, "
+ " da.currency_code as currencyCode, da.currency_digits as currencyDigits, " + " da.currency_code as currencyCode, da.currency_digits as currencyDigits, "
+ " da.deposit_amount as depositAmount, da.status_enum as statusId, " + " da.deposit_amount as depositAmount, da.status_enum as statusId, "
+ " da.maturity_nominal_interest_rate as interestRate, da.tenure_months as termInMonths, da.projected_commencement_date as projectedCommencementDate," + " da.maturity_nominal_interest_rate as interestRate, da.tenure_months as termInMonths, da.projected_commencement_date as projectedCommencementDate,"
@@ -124,6 +124,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
+ " da.projected_total_maturity_amount as projectedMaturityAmount, da.actual_total_amount as actualMaturityAmount, " + " da.projected_total_maturity_amount as projectedMaturityAmount, da.actual_total_amount as actualMaturityAmount, "
+ " da.interest_compounded_every as interestCompoundedEvery, da.interest_compounded_every_period_enum as interestCompoundedEveryPeriodType, " + " da.interest_compounded_every as interestCompoundedEvery, da.interest_compounded_every_period_enum as interestCompoundedEveryPeriodType, "
+ " da.is_renewal_allowed as renewalAllowed, da.is_preclosure_allowed as preClosureAllowed, da.pre_closure_interest_rate as preClosureInterestRate, " + " da.is_renewal_allowed as renewalAllowed, da.is_preclosure_allowed as preClosureAllowed, da.pre_closure_interest_rate as preClosureInterestRate, "
+ " da.withdrawnon_date as withdrawnonDate, da.rejectedon_date as rejectedonDate, da.closedon_date as closedonDate, "
+ " c.firstname as firstname, c.lastname as lastname, pd.name as productName," + " c.firstname as firstname, c.lastname as lastname, pd.name as productName,"
+ " curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol" + " curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol"
+ " from m_deposit_account da " + " from m_deposit_account da "
@@ -177,11 +178,16 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
BigDecimal preClosureInterestRate = rs.getBigDecimal("preClosureInterestRate"); BigDecimal preClosureInterestRate = rs.getBigDecimal("preClosureInterestRate");
LocalDate withdrawnonDate = JdbcSupport.getLocalDate(rs, "withdrawnonDate");
LocalDate rejectedonDate = JdbcSupport.getLocalDate(rs, "rejectedonDate");
LocalDate closedonDate = JdbcSupport.getLocalDate(rs, "closedonDate");
return new DepositAccountData(id, externalId, status, clientId, clientName, productId, productName, currencyData, depositAmount, return new DepositAccountData(id, externalId, status, clientId, clientName, productId, productName, currencyData, depositAmount,
interestRate, termInMonths, projectedCommencementDate, actualCommencementDate, projectedMaturityDate, actualMaturityDate, interestRate, termInMonths, projectedCommencementDate, actualCommencementDate, projectedMaturityDate, actualMaturityDate,
projectedInterestAccrued, actualInterestAccrued, projectedMaturityAmount, actualMaturityAmount, projectedInterestAccrued, actualInterestAccrued, projectedMaturityAmount, actualMaturityAmount,
interestCompoundedEvery, interestCompoundedEveryPeriodType, interestCompoundedEvery, interestCompoundedEveryPeriodType,
renewalAllowed, preClosureAllowed, preClosureInterestRate); renewalAllowed, preClosureAllowed, preClosureInterestRate,
withdrawnonDate,rejectedonDate,closedonDate);
} }
} }
} }
@@ -1,6 +1,7 @@
package org.mifosng.platform.saving.service; package org.mifosng.platform.saving.service;
import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.api.commands.DepositAccountCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionApprovalCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionCommand; import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
import org.mifosng.platform.api.commands.UndoStateTransitionCommand; import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.api.data.EntityIdentifier;
@@ -18,7 +19,7 @@ public interface DepositAccountWritePlatformService {
EntityIdentifier deleteDepositAccount(Long productId); EntityIdentifier deleteDepositAccount(Long productId);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')") @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')")
EntityIdentifier approveDepositApplication(DepositStateTransitionCommand command); EntityIdentifier approveDepositApplication(DepositStateTransitionApprovalCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')") @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_DEPOSIT_ROLE', 'CAN_APPROVE_DEPOSIT_IN_THE_PAST_ROLE')")
EntityIdentifier rejectDepositApplication(DepositStateTransitionCommand command); EntityIdentifier rejectDepositApplication(DepositStateTransitionCommand command);
@@ -1,10 +1,12 @@
package org.mifosng.platform.saving.service; package org.mifosng.platform.saving.service;
import java.math.BigDecimal;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.api.commands.DepositAccountCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionApprovalCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionCommand; import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
import org.mifosng.platform.api.commands.UndoStateTransitionCommand; import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.api.data.EntityIdentifier;
@@ -14,6 +16,7 @@ import org.mifosng.platform.exceptions.PlatformDataIntegrityException;
import org.mifosng.platform.exceptions.ProductNotFoundException; import org.mifosng.platform.exceptions.ProductNotFoundException;
import org.mifosng.platform.saving.domain.DepositAccount; import org.mifosng.platform.saving.domain.DepositAccount;
import org.mifosng.platform.saving.domain.DepositAccountRepository; import org.mifosng.platform.saving.domain.DepositAccountRepository;
import org.mifosng.platform.saving.domain.DepositApprovalData;
import org.mifosng.platform.saving.domain.DepositLifecycleStateMachine; import org.mifosng.platform.saving.domain.DepositLifecycleStateMachine;
import org.mifosng.platform.saving.domain.DepositLifecycleStateMachineImpl; import org.mifosng.platform.saving.domain.DepositLifecycleStateMachineImpl;
import org.mifosng.platform.saving.domain.DepositStatus; import org.mifosng.platform.saving.domain.DepositStatus;
@@ -126,11 +129,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Transactional @Transactional
@Override @Override
public EntityIdentifier approveDepositApplication(DepositStateTransitionCommand command) { public EntityIdentifier approveDepositApplication(DepositStateTransitionApprovalCommand command) {
AppUser currentUser = context.authenticatedUser(); AppUser currentUser = context.authenticatedUser();
DepositStateTransitionCommandValidator validator = new DepositStateTransitionCommandValidator(command); DepositStateTransitionApprovalCommandValidator validator = new DepositStateTransitionApprovalCommandValidator(command);
validator.validate(); validator.validate();
@@ -139,12 +142,16 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
throw new DepositAccountNotFoundException(command.getAccountId()); throw new DepositAccountNotFoundException(command.getAccountId());
} }
DepositApprovalData depositApprovalData=this.depositAccountAssembler.assembleFrom(command);
LocalDate eventDate = command.getEventDate(); LocalDate eventDate = command.getEventDate();
if (this.isBeforeToday(eventDate) && currentUser.canNotApproveLoanInPast()) { if (this.isBeforeToday(eventDate) && currentUser.canNotApproveLoanInPast()) {
throw new NoAuthorizationException("User has no authority to approve deposit with a date in the past."); throw new NoAuthorizationException("User has no authority to approve deposit with a date in the past.");
} }
account.approve(eventDate, defaultDepositLifecycleStateMachine()); BigDecimal depositAmount = command.getDepositAmount();
account.approve(eventDate, defaultDepositLifecycleStateMachine(), depositApprovalData,depositAmount);
this.depositAccountRepository.save(account); this.depositAccountRepository.save(account);
return new EntityIdentifier(account.getId()); return new EntityIdentifier(account.getId());
@@ -204,7 +211,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
} }
account.withdraw(eventDate, defaultDepositLifecycleStateMachine()); account.withdraw(eventDate, defaultDepositLifecycleStateMachine());
return null; return new EntityIdentifier(account.getId());
} }
@Transactional @Transactional
@@ -221,6 +228,6 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.undoDepositApproval(defaultDepositLifecycleStateMachine()); account.undoDepositApproval(defaultDepositLifecycleStateMachine());
this.depositAccountRepository.save(account); this.depositAccountRepository.save(account);
return null; return new EntityIdentifier(account.getId());
} }
} }
@@ -0,0 +1,39 @@
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.DepositStateTransitionApprovalCommand;
import org.mifosng.platform.api.data.ApiParameterError;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
public class DepositStateTransitionApprovalCommandValidator {
private final DepositStateTransitionApprovalCommand command;
public DepositStateTransitionApprovalCommandValidator(final DepositStateTransitionApprovalCommand 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();
baseDataValidator.reset().parameter("depositAmount").value(command.getDepositAmount()).notNull().zeroOrPositiveAmount();
baseDataValidator.reset().parameter("tenureInMonths").value(command.getTenureInMonths()).ignoreIfNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("interestCompoundedEveryPeriodType").value(command.getInterestCompoundedEveryPeriodType()).ignoreIfNull().inMinMaxRange(2, 2);
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
}