Merge pull request #640 from conradsp/develop

FINERACT-794 Open Banking APIs for Interoperability layer
This commit is contained in:
Steve Conrad
2019-10-12 10:02:31 -05:00
committed by GitHub
21 changed files with 738 additions and 90 deletions
@@ -32,14 +32,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.interoperation.data.InteropIdentifierRequestData;
import org.apache.fineract.interoperation.data.InteropIdentifierResponseData;
import org.apache.fineract.interoperation.data.InteropQuoteRequestData;
import org.apache.fineract.interoperation.data.InteropQuoteResponseData;
import org.apache.fineract.interoperation.data.InteropTransactionRequestData;
import org.apache.fineract.interoperation.data.InteropTransactionRequestResponseData;
import org.apache.fineract.interoperation.data.InteropTransferRequestData;
import org.apache.fineract.interoperation.data.InteropTransferResponseData;
import org.apache.fineract.interoperation.data.*;
import org.apache.fineract.interoperation.domain.InteropIdentifierType;
import org.apache.fineract.interoperation.domain.InteropTransferActionType;
import org.apache.fineract.interoperation.service.InteropService;
@@ -47,18 +40,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_QUOTE;
import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_REQUEST;
import static org.apache.fineract.interoperation.util.InteropUtil.ROOT_PATH;
@@ -100,16 +89,63 @@ public class InteropApiResource {
return "OK";
}
@GET
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Path("accounts/{accountId}")
@ApiOperation(value = "Query Interoperation Account details", httpMethod = "GET", notes = "")
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropAccountData.class)})
public String getAccountDetails(@PathParam("accountId") @ApiParam(value = "accountId") String accountId, @Context UriInfo uriInfo) {
InteropAccountData result = interopService.getAccountDetails(accountId);
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return jsonSerializer.serialize(settings, result);
}
@GET
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Path("accounts/{accountId}/transactions")
@ApiOperation(value = "Query transactions by Account Id", httpMethod = "GET", notes = "")
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropTransactionsData.class)})
public String getAccountTransactions(@PathParam("accountId") @ApiParam(value = "accountId") String accountId,
@DefaultValue("true") @QueryParam("debit") @ApiParam(value = "debit") boolean debit,
@DefaultValue("false") @QueryParam("credit") @ApiParam(value = "credit") boolean credit,
@QueryParam("fromBookingDateTime") @ApiParam(value = "fromBookingDateTime") String fromBookingDateTime,
@QueryParam("toBookingDateTime") @ApiParam(value = "toBookingDateTime") String toBookingDateTime,
@Context UriInfo uriInfo) {
LocalDateTime transactionsFrom = fromBookingDateTime == null ? null : LocalDateTime.parse(fromBookingDateTime, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
LocalDateTime transactionsTo = toBookingDateTime == null ? null : LocalDateTime.parse(toBookingDateTime, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
InteropTransactionsData result = interopService.getAccountTransactions(accountId, debit, credit, transactionsFrom, transactionsTo);
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return jsonSerializer.serialize(settings, result);
}
@GET
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Path("accounts/{accountId}/identifiers")
@ApiOperation(value = "Query Interoperation secondary identifiers by Account Id", httpMethod = "GET", notes = "")
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifiersResponseData.class)})
public String getAccountIdentifiers(@PathParam("accountId") @ApiParam(value = "accountId") String accountId, @Context UriInfo uriInfo) {
InteropIdentifiersResponseData result = interopService.getAccountIdentifiers(accountId);
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return jsonSerializer.serialize(settings, result);
}
@GET
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Path("parties/{idType}/{idValue}")
@ApiOperation(value = "Query Interoperation Account by secondary identifier", httpMethod = "GET", notes = "")
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)})
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierAccountResponseData.class)})
public String getAccountByIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType,
@PathParam("idValue") @ApiParam(value = "idValue") String idValue,
@Context UriInfo uriInfo) {
InteropIdentifierResponseData result = interopService.getAccountByIdentifier(idType, idValue, null);
InteropIdentifierAccountResponseData result = interopService.getAccountByIdentifier(idType, idValue, null);
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return jsonSerializer.serialize(settings, result);
@@ -120,12 +156,12 @@ public class InteropApiResource {
@Produces({MediaType.APPLICATION_JSON})
@Path("parties/{idType}/{idValue}/{subIdOrType}")
@ApiOperation(value = "Query Interoperation Account by secondary identifier", httpMethod = "GET", notes = "")
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)})
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierAccountResponseData.class)})
public String getAccountByIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType,
@PathParam("idValue") @ApiParam(value = "idValue") String idValue,
@PathParam("subIdOrType") @ApiParam(value = "subIdOrType") String subIdOrType,
@Context UriInfo uriInfo) {
InteropIdentifierResponseData result = interopService.getAccountByIdentifier(idType, idValue, subIdOrType);
InteropIdentifierAccountResponseData result = interopService.getAccountByIdentifier(idType, idValue, subIdOrType);
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return jsonSerializer.serialize(settings, result);
@@ -138,14 +174,14 @@ public class InteropApiResource {
@ApiOperation(value = "Interoperation Identifier registration", httpMethod = "POST", notes = "")
@ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body",
dataTypeClass = InteropIdentifierRequestData.class)})
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)})
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierAccountResponseData.class)})
public String registerAccountIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType,
@PathParam("idValue") @ApiParam(value = "idValue") String idValue,
@ApiParam(hidden = true) String identifierJson, @Context UriInfo uriInfo)
throws Throwable {
CommandWrapper commandRequest = new InteropWrapperBuilder().registerAccountIdentifier(idType, idValue, null).withJson(identifierJson).build();
InteropIdentifierResponseData result = (InteropIdentifierResponseData) commandsSourceService.logCommandSource(commandRequest);
InteropIdentifierAccountResponseData result = (InteropIdentifierAccountResponseData) commandsSourceService.logCommandSource(commandRequest);
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return jsonSerializer.serialize(settings, result);
@@ -158,7 +194,7 @@ public class InteropApiResource {
@ApiOperation(value = "Interoperation Identifier registration", httpMethod = "POST", notes = "")
@ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body",
dataTypeClass = InteropIdentifierRequestData.class)})
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)})
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierAccountResponseData.class)})
public String registerAccountIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType,
@PathParam("idValue") @ApiParam(value = "idValue") String idValue,
@PathParam("subIdOrType") @ApiParam(value = "subIdOrType") String subIdOrType,
@@ -166,7 +202,7 @@ public class InteropApiResource {
throws Throwable {
CommandWrapper commandRequest = new InteropWrapperBuilder().registerAccountIdentifier(idType, idValue, subIdOrType).withJson(identifierJson).build();
InteropIdentifierResponseData result = (InteropIdentifierResponseData) commandsSourceService.logCommandSource(commandRequest);
InteropIdentifierAccountResponseData result = (InteropIdentifierAccountResponseData) commandsSourceService.logCommandSource(commandRequest);
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return jsonSerializer.serialize(settings, result);
@@ -179,14 +215,14 @@ public class InteropApiResource {
@ApiOperation(value = "Allow Interoperation Identifier registration", httpMethod = "DELETE", notes = "")
@ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body",
dataTypeClass = InteropIdentifierRequestData.class)})
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)})
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierAccountResponseData.class)})
public String deleteAccountIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType,
@PathParam("idValue") @ApiParam(value = "idValue") String idValue,
@Context UriInfo uriInfo)
throws Throwable {
CommandWrapper commandRequest = new InteropWrapperBuilder().deleteAccountIdentifier(idType, idValue, null).build();
InteropIdentifierResponseData result = (InteropIdentifierResponseData) commandsSourceService.logCommandSource(commandRequest);
InteropIdentifierAccountResponseData result = (InteropIdentifierAccountResponseData) commandsSourceService.logCommandSource(commandRequest);
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return jsonSerializer.serialize(settings, result);
@@ -199,7 +235,7 @@ public class InteropApiResource {
@ApiOperation(value = "Allow Interoperation Identifier registration", httpMethod = "DELETE", notes = "")
@ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body",
dataTypeClass = InteropIdentifierRequestData.class)})
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)})
@ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierAccountResponseData.class)})
public String deleteAccountIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType,
@PathParam("idValue") @ApiParam(value = "idValue") String idValue,
@PathParam("subIdOrType") @ApiParam(value = "subIdOrType") String subIdOrType,
@@ -207,7 +243,7 @@ public class InteropApiResource {
throws Throwable {
CommandWrapper commandRequest = new InteropWrapperBuilder().deleteAccountIdentifier(idType, idValue, subIdOrType).build();
InteropIdentifierResponseData result = (InteropIdentifierResponseData) commandsSourceService.logCommandSource(commandRequest);
InteropIdentifierAccountResponseData result = (InteropIdentifierAccountResponseData) commandsSourceService.logCommandSource(commandRequest);
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return jsonSerializer.serialize(settings, result);
@@ -0,0 +1,142 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.interoperation.data;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.interoperation.domain.InteropIdentifier;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimelineData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountSubStatusEnum;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
import org.apache.fineract.portfolio.savings.domain.SavingsProduct;
import org.joda.time.LocalDate;
import javax.validation.constraints.NotNull;
import java.beans.Transient;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class InteropAccountData extends CommandProcessingResult {
@NotNull
private final String accountId;
@NotNull
private final String savingProductId;
@NotNull
private final String productName;
@NotNull
private final String shortProductName;
@NotNull
private final String currency;
@NotNull
private final BigDecimal accountBalance;
@NotNull
private final BigDecimal availableBalance;
@NotNull
private final SavingsAccountStatusType status;
private final SavingsAccountSubStatusEnum subStatus;
private final AccountType accountType; //differentiate Individual, JLG or Group account
private final DepositAccountType depositType; //differentiate deposit accounts Savings, FD and RD accounts
@NotNull
private final LocalDate activatedOn;
private final LocalDate statusUpdateOn;
private final LocalDate withdrawnOn;
private final LocalDate balanceOn;
@NotNull
private List<InteropIdentifierData> identifiers;
InteropAccountData(Long resourceId, Long officeId, Long commandId, Map<String, Object> changesOnly, String accountId,
String productId, String productName, String shortProductName, String currency, BigDecimal accountBalance,
BigDecimal availableBalance, SavingsAccountStatusType status, SavingsAccountSubStatusEnum subStatus,
AccountType accountType, DepositAccountType depositType, LocalDate activatedOn, LocalDate statusUpdateOn,
LocalDate withdrawnOn, LocalDate balanceOn, List<InteropIdentifierData> identifiers) {
super(resourceId, officeId, commandId, changesOnly);
this.accountId = accountId;
this.savingProductId = productId;
this.productName = productName;
this.shortProductName = shortProductName;
this.currency = currency;
this.accountBalance = accountBalance;
this.availableBalance = availableBalance;
this.status = status;
this.subStatus = subStatus;
this.accountType = accountType;
this.depositType = depositType;
this.activatedOn = activatedOn;
this.statusUpdateOn = statusUpdateOn;
this.withdrawnOn = withdrawnOn;
this.balanceOn = balanceOn;
this.identifiers = identifiers;
}
InteropAccountData(String accountId, String productId, String productName, String shortProductName, String currency,
BigDecimal accountBalance, BigDecimal availableBalance, SavingsAccountStatusType status, SavingsAccountSubStatusEnum subStatus,
AccountType accountType, DepositAccountType depositType, LocalDate activatedOn, LocalDate statusUpdateOn,
LocalDate withdrawnOn, LocalDate balanceOn, List<InteropIdentifierData> identifiers) {
this(null, null, null, null, accountId, productId, productName, shortProductName, currency, accountBalance,
availableBalance, status, subStatus, accountType, depositType, activatedOn, statusUpdateOn, withdrawnOn, balanceOn,
identifiers);
}
public static InteropAccountData build(SavingsAccount account) {
if (account == null)
return null;
List<InteropIdentifierData> ids = new ArrayList<>();
for (InteropIdentifier identifier : account.getIdentifiers()) {
ids.add(InteropIdentifierData.build(identifier));
}
SavingsProduct product = account.savingsProduct();
SavingsAccountSubStatusEnum subStatus = SavingsAccountSubStatusEnum.fromInt(account.getSubStatus());
return new InteropAccountData(account.getExternalId(), product.getId().toString(), product.getName(),
product.getShortName(), account.getCurrency().getCode(), account.getAccountBalance(), account.getWithdrawableBalance(),
account.getStatus(), subStatus, account.getAccountType(), account.depositAccountType(), account.getActivationLocalDate(),
calcStatusUpdateOn(account), account.getWithdrawnOnDate(), account.retrieveLastTransactionDate(), ids);
}
private static LocalDate calcStatusUpdateOn(@NotNull SavingsAccount account) {
LocalDate date = account.getClosedOnDate();
if (date != null)
return date;
if ((date = account.getWithdrawnOnDate()) != null)
return date;
if ((date = account.getActivationLocalDate()) != null)
return date;
if ((date = account.getRejectedOnDate()) != null)
return date;
if ((date = account.getApprovedOnDate()) != null)
return date;
if ((date = account.getSubmittedOnDate()) != null)
return date;
return null;
}
}
@@ -24,26 +24,26 @@ import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Map;
public class InteropIdentifierResponseData extends CommandProcessingResult {
public class InteropIdentifierAccountResponseData extends CommandProcessingResult {
@NotEmpty
private String accountId;
protected InteropIdentifierResponseData(Long resourceId, Long officeId, Long commandId, Map<String, Object> changesOnly, @NotNull String accountId) {
protected InteropIdentifierAccountResponseData(Long resourceId, Long officeId, Long commandId, Map<String, Object> changesOnly, @NotNull String accountId) {
super(resourceId, officeId, commandId, changesOnly);
this.accountId = accountId;
}
protected static InteropIdentifierResponseData build(Long resourceId, Long officeId, Long commandId, Map<String, Object> changesOnly, @NotNull String accountId) {
return new InteropIdentifierResponseData(resourceId, officeId, commandId, changesOnly, accountId);
protected static InteropIdentifierAccountResponseData build(Long resourceId, Long officeId, Long commandId, Map<String, Object> changesOnly, @NotNull String accountId) {
return new InteropIdentifierAccountResponseData(resourceId, officeId, commandId, changesOnly, accountId);
}
protected static InteropIdentifierResponseData build(Long commandId, @NotNull String accountId) {
protected static InteropIdentifierAccountResponseData build(Long commandId, @NotNull String accountId) {
return build(null, null, commandId, null, accountId);
}
public static InteropIdentifierResponseData build(@NotNull String accountId) {
public static InteropIdentifierAccountResponseData build(@NotNull String accountId) {
return build(null, accountId);
}
@@ -0,0 +1,75 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.interoperation.data;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.interoperation.domain.InteropActionState;
import org.apache.fineract.interoperation.domain.InteropIdentifier;
import org.apache.fineract.interoperation.domain.InteropIdentifierType;
import org.apache.fineract.interoperation.domain.InteropTransactionRole;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import static org.apache.fineract.interoperation.util.InteropUtil.*;
public class InteropIdentifierData {
@NotNull
private final InteropIdentifierType idType;
@NotNull
private final String idValue;
private final String subIdOrType;
public InteropIdentifierData(@NotNull InteropIdentifierType idType, @NotNull String idValue, String subIdOrType) {
this.idType = idType;
this.idValue = idValue;
this.subIdOrType = subIdOrType;
}
protected InteropIdentifierData(@NotNull InteropIdentifierType idType, @NotNull String idValue) {
this(idType, idValue, null);
}
public InteropIdentifierType getIdType() {
return idType;
}
public String getIdValue() {
return idValue;
}
public String getSubIdOrType() {
return subIdOrType;
}
public static InteropIdentifierData build(InteropIdentifier identifier) {
return new InteropIdentifierData(identifier.getType(), identifier.getValue(), identifier.getSubValueOrType());
}
}
@@ -0,0 +1,53 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.interoperation.data;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.interoperation.domain.InteropIdentifier;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class InteropIdentifiersResponseData extends CommandProcessingResult {
@NotNull
private List<InteropIdentifierData> identifiers;
protected InteropIdentifiersResponseData(Long resourceId, Long officeId, Long commandId, Map<String, Object> changesOnly, @NotNull List<InteropIdentifierData> identifiers) {
super(resourceId, officeId, commandId, changesOnly);
this.identifiers = identifiers;
}
protected InteropIdentifiersResponseData(@NotNull List<InteropIdentifierData> identifiers) {
this(null, null, null, null, identifiers);
}
public static InteropIdentifiersResponseData build(SavingsAccount account) {
List<InteropIdentifierData> result = new ArrayList<>();
if (account != null) {
for (InteropIdentifier identifier : account.getIdentifiers()) {
result.add(InteropIdentifierData.build(identifier));
}
}
return new InteropIdentifiersResponseData(result);
}
}
@@ -0,0 +1,31 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.interoperation.data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class InteropRefundData {
@Size(max = 36)
@NotNull
private String originalTransactionId; // mandatory payment id
@Size(max = 128)
private String refundReason; // optional, 128
}
@@ -25,6 +25,10 @@ import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.interoperation.domain.InteropTransactionRole;
import org.codehaus.jackson.map.annotate.JsonDeserialize;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.ext.JodaDeserializers;
import org.codehaus.jackson.map.ext.JodaSerializers;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
@@ -53,6 +57,7 @@ public class InteropRequestData {
private GeoCodeData geoCode;
@JsonDeserialize(using = JodaDeserializers.LocalDateTimeDeserializer.class)
private LocalDateTime expiration;
private List<ExtensionData> extensionList;
@@ -0,0 +1,149 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.interoperation.data;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.interoperation.domain.InteropIdentifier;
import org.apache.fineract.interoperation.util.MathUtil;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.note.domain.Note;
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
import org.apache.fineract.portfolio.savings.domain.*;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
import org.joda.time.LocalDate;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class InteropTransactionData extends CommandProcessingResult {
// private final SavingsAccountTransactionEnumData transactionType;
// private final PaymentDetailData paymentDetailData;
// private final BigDecimal outstandingChargeAmount;
// private final boolean reversed;
// private final AccountTransferData transfer;
// private final LocalDate submittedOnDate;
// private final boolean interestedPostedAsOn;
// private final String submittedByUsername;
// // templates
// final Collection<PaymentTypeData> paymentTypeOptions;
// //import fields
// private Long paymentTypeId;
// private String checkNumber;
// private String routingCode;
// private String receiptNumber;
// private String bankNumber;
// private String transactionReference;
// private String statementReference;
// private CreditDebitType creditDebit;
// private TransactionStatus status;
// private String transactionInformation;
// private String addressLine;
@NotNull
private final String accountId;
@NotNull
private final String savingTransactionId;
@NotNull
private final SavingsAccountTransactionType transactionType;
@NotNull
private final BigDecimal amount;
private final BigDecimal chargeAmount;
@NotNull
private final String currency;
@NotNull
private final BigDecimal accountBalance;
@NotNull
private final LocalDate bookingDateTime;
@NotNull
private final LocalDate valueDateTime;
private final String note;
public InteropTransactionData(Long entityId, String accountId, String transactionId, SavingsAccountTransactionType transactionType, BigDecimal amount, BigDecimal chargeAmount,
String currency, BigDecimal accountBalance, LocalDate bookingDateTime, LocalDate valueDateTime, String note) {
super(entityId);
this.accountId = accountId;
this.savingTransactionId = transactionId;
this.transactionType = transactionType;
this.amount = amount;
this.chargeAmount = chargeAmount;
this.currency = currency;
this.accountBalance = accountBalance;
this.bookingDateTime = bookingDateTime;
this.valueDateTime = valueDateTime;
this.note = note;
}
public static InteropTransactionData build(SavingsAccountTransaction transaction) {
if (transaction == null)
return null;
SavingsAccount savingsAccount = transaction.getSavingsAccount();
String transactionId = transaction.getId().toString();
SavingsAccountTransactionType transactionType = SavingsAccountTransactionType.fromInt(transaction.getTypeOf());
BigDecimal amount = transaction.getAmount();
BigDecimal chargeAmount = null;
for (SavingsAccountChargePaidBy charge : transaction.getSavingsAccountChargesPaid()) {
chargeAmount = MathUtil.add(chargeAmount, charge.getAmount());
}
String currency = savingsAccount.getCurrency().getCode();
BigDecimal runningBalance = transaction.getRunningBalance(savingsAccount.getCurrency()).getAmount();
LocalDate bookingDateTime = transaction.getTransactionLocalDate();
LocalDate endOfBalanceLocalDate = transaction.getEndOfBalanceLocalDate();
LocalDate valueDateTime = endOfBalanceLocalDate == null ? bookingDateTime : endOfBalanceLocalDate;
StringBuilder sb = new StringBuilder();
int currLength = 0;
for (Note note : transaction.getNotes()) {
String s = note.getNote();
if (s == null)
continue;
int availableLength = 500 - currLength;
if (availableLength <= 1)
break;
if (currLength > 0) {
sb.append(' ');
availableLength--;
}
if (s.length() > availableLength)
s = s.substring(availableLength);
sb.append(s);
currLength = sb.length();
}
if (currLength == 0) {
sb.append(SavingsEnumerations.transactionType(transactionType).getValue());
}
return new InteropTransactionData(savingsAccount.getId(), savingsAccount.getExternalId(), transactionId, transactionType, amount, chargeAmount, currency,
runningBalance, bookingDateTime, valueDateTime, sb.toString());
}
}
@@ -25,17 +25,15 @@ import org.apache.fineract.interoperation.domain.InteropInitiatorType;
import org.apache.fineract.interoperation.domain.InteropTransactionRole;
import org.apache.fineract.interoperation.domain.InteropTransactionScenario;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_INITIATOR;
import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_INITIATOR_TYPE;
import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_SCENARIO;
import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_SUB_SCENARIO;
import static org.apache.fineract.interoperation.util.InteropUtil.*;
public class InteropTransactionTypeData {
public static final String[] PARAMS = {PARAM_SCENARIO, PARAM_SUB_SCENARIO, PARAM_INITIATOR, PARAM_INITIATOR_TYPE};
public static final String[] PARAMS = {PARAM_SCENARIO, PARAM_SUB_SCENARIO, PARAM_INITIATOR, PARAM_INITIATOR_TYPE, PARAM_REFUND_INFO, PARAM_BALANCE_OF_PAYMENTS};
@NotNull
private final InteropTransactionScenario scenario;
@@ -45,12 +43,24 @@ public class InteropTransactionTypeData {
private final InteropTransactionRole initiator;
@NotNull
private final InteropInitiatorType initiatorType;
//TODO: SKIP FOR NOW
@Valid
private InteropRefundData refundInfo;
public InteropTransactionTypeData(InteropTransactionScenario scenario, String subScenario, InteropTransactionRole initiator, InteropInitiatorType initiatorType) {
private String balanceOfPayments; // 3 digits number, see https://www.imf.org/external/np/sta/bopcode/
InteropTransactionTypeData(InteropTransactionScenario scenario, String subScenario, InteropTransactionRole initiator, InteropInitiatorType initiatorType,
InteropRefundData refundInfo, String balanceOfPayments) {
this.scenario = scenario;
this.subScenario = subScenario;
this.initiator = initiator;
this.initiatorType = initiatorType;
this.refundInfo = refundInfo;
this.balanceOfPayments = balanceOfPayments;
}
private InteropTransactionTypeData(InteropTransactionScenario scenario, String subScenario, InteropTransactionRole initiator, InteropInitiatorType initiatorType) {
this(scenario, subScenario, initiator, initiatorType, null, null);
}
public InteropTransactionScenario getScenario() {
@@ -0,0 +1,54 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.interoperation.data;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.interoperation.domain.InteropIdentifier;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountSubStatusEnum;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
import org.apache.fineract.portfolio.savings.domain.SavingsProduct;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class InteropTransactionsData extends CommandProcessingResult {
List<InteropTransactionData> transactions;
public InteropTransactionsData(Long entityId, List<InteropTransactionData> transactions) {
super(entityId);
this.transactions = transactions;
}
public static InteropTransactionsData build(SavingsAccount account, @NotNull Predicate<SavingsAccountTransaction> filter) {
if (account == null)
return null;
List<InteropTransactionData> trans = account.getTransactions().stream().filter(filter).sorted((t1, t2) -> {
int i = t2.getDateOf().compareTo(t1.getDateOf());
return i != 0 ? i : Long.signum(t2.getId() - t1.getId());
}).map(InteropTransactionData::build).collect(Collectors.toList());
return new InteropTransactionsData(account.getId(), trans);
}
}
@@ -19,26 +19,33 @@
package org.apache.fineract.interoperation.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.interoperation.data.InteropIdentifierResponseData;
import org.apache.fineract.interoperation.data.InteropQuoteResponseData;
import org.apache.fineract.interoperation.data.InteropTransactionRequestResponseData;
import org.apache.fineract.interoperation.data.InteropTransferResponseData;
import org.apache.fineract.interoperation.data.*;
import org.apache.fineract.interoperation.domain.InteropIdentifierType;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
public interface InteropService {
@NotNull
InteropIdentifierResponseData getAccountByIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, String subIdOrType);
InteropIdentifiersResponseData getAccountIdentifiers(@NotNull String accountId);
@NotNull
InteropIdentifierResponseData registerAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue,
String subIdOrType, @NotNull JsonCommand command);
InteropAccountData getAccountDetails(@NotNull String accountId);
@NotNull
InteropIdentifierResponseData deleteAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue,
String subIdOrType);
InteropTransactionsData getAccountTransactions(@NotNull String accountId, boolean debit, boolean credit, LocalDateTime transactionsFrom, LocalDateTime transactionsTo);
@NotNull
InteropIdentifierAccountResponseData getAccountByIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, String subIdOrType);
@NotNull
InteropIdentifierAccountResponseData registerAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue,
String subIdOrType, @NotNull JsonCommand command);
@NotNull
InteropIdentifierAccountResponseData deleteAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue,
String subIdOrType);
InteropTransactionRequestResponseData getTransactionRequest(@NotNull String transactionCode, @NotNull String requestCode);
@@ -21,6 +21,7 @@ package org.apache.fineract.interoperation.service;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.interoperation.data.*;
import org.apache.fineract.interoperation.domain.InteropIdentifier;
@@ -68,13 +69,14 @@ import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.function.Predicate;
import static org.apache.fineract.interoperation.util.InteropUtil.DEFAULT_LOCALE;
import static org.apache.fineract.interoperation.util.InteropUtil.DEFAULT_ROUTING_CODE;
import static org.apache.fineract.interoperation.util.InteropUtil.ISO8601_DATE_TIME_FORMAT;
@Service
public class InteropServiceImpl implements InteropService {
@@ -125,19 +127,56 @@ public class InteropServiceImpl implements InteropService {
}
@NotNull
@Override
@Transactional
public InteropIdentifierResponseData getAccountByIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, String subIdOrType) {
public InteropAccountData getAccountDetails(@NotNull String accountId) {
SavingsAccount savingsAccount = validateAndGetSavingAccount(accountId);
return InteropAccountData.build(savingsAccount);
}
@NotNull
@Override
@Transactional
public InteropTransactionsData getAccountTransactions(@NotNull String accountId, boolean debit, boolean credit, java.time.LocalDateTime transactionsFrom, java.time.LocalDateTime transactionsTo) {
SavingsAccount savingsAccount = validateAndGetSavingAccount(accountId);
ZoneId zoneId = ZoneId.of(ThreadLocalContextUtil.getTenant().getTimezoneId());
Predicate<SavingsAccountTransaction> transFilter = t -> {
SavingsAccountTransactionType transactionType = SavingsAccountTransactionType.fromInt(t.getTypeOf());
if (debit != transactionType.isDebit() && credit != transactionType.isCredit())
return false;
if (transactionsFrom == null && transactionsTo == null)
return true;
java.time.LocalDateTime transactionDate = t.getTransactionLocalDate().toDateTimeAtStartOfDay().toDate().toInstant().atZone(zoneId).toLocalDateTime();
return (transactionsTo == null || transactionsTo.compareTo(transactionDate) > 0)
&& (transactionsFrom == null || transactionsFrom.compareTo(transactionDate.withHour(23).withMinute(59).withSecond(59)) <= 0);
};
return InteropTransactionsData.build(savingsAccount, transFilter);
}
@NotNull
@Override
@Transactional
public InteropIdentifiersResponseData getAccountIdentifiers(@NotNull String accountId) {
SavingsAccount savingsAccount = validateAndGetSavingAccount(accountId);
return InteropIdentifiersResponseData.build(savingsAccount);
}
@NotNull
@Transactional
public InteropIdentifierAccountResponseData getAccountByIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, String subIdOrType) {
InteropIdentifier identifier = findIdentifier(idType, idValue, subIdOrType);
if (identifier == null)
throw new UnsupportedOperationException("Account not found for identifier " + idType + "/" + idValue + (subIdOrType == null ? "" : ("/" + subIdOrType)));
return InteropIdentifierResponseData.build(identifier.getAccount().getExternalId());
return InteropIdentifierAccountResponseData.build(identifier.getAccount().getExternalId());
}
@NotNull
@Transactional(propagation = Propagation.MANDATORY)
public InteropIdentifierResponseData registerAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue,
String subIdOrType, @NotNull JsonCommand command) {
public InteropIdentifierAccountResponseData registerAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue,
String subIdOrType, @NotNull JsonCommand command) {
InteropIdentifierRequestData request = dataValidator.validateAndParseCreateIdentifier(idType, idValue, subIdOrType, command);
//TODO: error handling
SavingsAccount savingsAccount = validateAndGetSavingAccount(request.getAccountId());
@@ -149,13 +188,13 @@ public class InteropServiceImpl implements InteropService {
identifierRepository.save(identifier);
return InteropIdentifierResponseData.build(savingsAccount.getExternalId());
return InteropIdentifierAccountResponseData.build(savingsAccount.getExternalId());
}
@NotNull
@Transactional(propagation = Propagation.MANDATORY)
public InteropIdentifierResponseData deleteAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue,
String subIdOrType) {
public InteropIdentifierAccountResponseData deleteAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue,
String subIdOrType) {
InteropIdentifier identifier = findIdentifier(idType, idValue, subIdOrType);
if (identifier == null)
throw new UnsupportedOperationException("Account not found for identifier " + idType + "/" + idValue + (subIdOrType == null ? "" : ("/" + subIdOrType)));
@@ -164,7 +203,7 @@ public class InteropServiceImpl implements InteropService {
identifierRepository.delete(identifier);
return InteropIdentifierResponseData.build(accountId);
return InteropIdentifierAccountResponseData.build(accountId);
}
@Override
@@ -64,6 +64,8 @@ public class InteropUtil {
public static final String PARAM_SUB_SCENARIO = "subScenario";
public static final String PARAM_INITIATOR = "initiator";
public static final String PARAM_INITIATOR_TYPE = "initiatorType";
public static final String PARAM_REFUND_INFO = "refundInfo";
public static final String PARAM_BALANCE_OF_PAYMENTS = "balanceOfPayments";
public static final String PARAM_EXPIRATION = "expiration";
public static final String PARAM_LATITUDE = "latitude";
@@ -184,4 +184,7 @@ public class Note extends AbstractAuditableCustom<AppUser, Long> {
return !this.client.identifiedBy(clientId);
}
public String getNote() {
return note;
}
}
@@ -206,10 +206,10 @@ public enum SavingsAccountTransactionType {
}
public boolean isDebit() {
return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isPayCharge() || isIncomeFromInterest() || isWithHoldTax() || isEscheat() || isAmountOnHold();
return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isPayCharge() || isIncomeFromInterest() || isWithHoldTax() || isEscheat();
}
public boolean isCredit() {
return isDeposit() || isInterestPosting() || isDividendPayout() || isAmountRelease();
return isDeposit() || isInterestPosting() || isDividendPayout();
}
}
@@ -78,6 +78,7 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.security.service.RandomPasswordGenerator;
import org.apache.fineract.interoperation.domain.InteropIdentifier;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
@@ -328,6 +329,10 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
@Column(name = "total_savings_amount_on_hold", scale = 6, precision = 19, nullable = true)
private BigDecimal savingsOnHoldAmount;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "account", orphanRemoval = true, fetch=FetchType.LAZY)
protected List<InteropIdentifier> identifiers = new ArrayList<>();
protected SavingsAccount() {
//
}
@@ -473,8 +478,13 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
public boolean isClosed() {
return SavingsAccountStatusType.fromInt(this.status).isClosed();
}
public List<InteropIdentifier> getIdentifiers() {
return identifiers;
}
public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,final LocalDate postInterestOnDate) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, final LocalDate postInterestOnDate) {
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate);
Money interestPostedToDate = Money.zero(this.currency);
@@ -948,6 +958,10 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
return activationLocalDate;
}
public LocalDate getWithdrawnOnDate() {
return withdrawnOnDate == null ? null : new LocalDate(withdrawnOnDate);
}
// startInterestCalculationDate is set during migration so that there is no
// interference with interest posting of previous system
public LocalDate getStartInterestCalculationDate() {
@@ -1030,7 +1044,6 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
public BigDecimal calculateWithdrawalFee(final BigDecimal transactionAmount) {
BigDecimal result = BigDecimal.ZERO;
System.out.println("isWithdrawalFeeApplicableForTransfer() = " + isWithdrawalFeeApplicableForTransfer());
if (isWithdrawalFeeApplicableForTransfer()) {
for (SavingsAccountCharge charge : this.charges()) {
if (charge.isWithdrawalFee() && charge.isActive()) {
@@ -1206,7 +1219,7 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
return transactionDate.isAfter(DateUtils.getLocalDateOfTenant());
}
protected BigDecimal getAccountBalance() {
public BigDecimal getAccountBalance() {
return this.summary.getAccountBalance(this.currency).getAmount();
}
@@ -1690,7 +1703,15 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
}
public LocalDate getSubmittedOnDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.submittedOnDate), null);
return submittedOnDate == null ? null : new LocalDate(submittedOnDate);
}
public LocalDate getApprovedOnDate() {
return approvedOnDate == null ? null : new LocalDate(approvedOnDate);
}
public LocalDate getRejectedOnDate() {
return rejectedOnDate == null ? null : new LocalDate(rejectedOnDate);
}
public void removeSavingsOfficer(final LocalDate unassignDate) {
@@ -2664,6 +2685,10 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
return getActivationLocalDate() == null ? getSubmittedOnLocalDate() : getActivationLocalDate();
}
public AccountType getAccountType() {
return AccountType.fromInt(accountType);
}
public DepositAccountType depositAccountType() {
return DepositAccountType.fromInt(depositType);
}
@@ -3055,7 +3080,11 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
actualChanges.put(SavingsApiConstants.subStatusParamName, SavingsEnumerations.subStatus(this.sub_status));
return actualChanges;
}
public SavingsAccountStatusType getStatus() {
return SavingsAccountStatusType.fromInt(status);
}
public Integer getSubStatus() {
return this.sub_status;
}
@@ -45,6 +45,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.portfolio.note.domain.Note;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
@@ -125,6 +126,10 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
@Column(name = "release_id_of_hold_amount", length = 20)
private Long releaseIdOfHoldAmountTransaction;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "savings_account_transaction_id", referencedColumnName = "id")
private List<Note> notes = new ArrayList<>();
protected SavingsAccountTransaction() {
this.dateOf = null;
this.typeOf = null;
@@ -432,12 +437,8 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
return new LocalDate(this.dateOf);
}
private LocalDate getEndOfBalanceLocalDate() {
LocalDate endDate = null;
if (this.balanceEndDate != null) {
endDate = new LocalDate(this.balanceEndDate);
}
return endDate;
public LocalDate getEndOfBalanceLocalDate() {
return balanceEndDate == null ? null : new LocalDate(balanceEndDate);
}
public boolean isAcceptableForDailyBalance(final LocalDateInterval interestPeriodInterval) {
@@ -756,6 +757,10 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
return this.taxDetails;
}
public List<Note> getNotes() {
return notes;
}
public void updateAmount(final Money amount) {
this.amount = amount.getAmount();
}
@@ -791,8 +796,4 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
public boolean isAmountOnHoldNotReleased() {
return (isAmountOnHold() && getReleaseIdOfHoldAmountTransaction() == null);
}
public Date getCreatedDate() {
return createdDate;
}
}
@@ -107,7 +107,7 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
@Column(name = "short_name", nullable = false, unique = true)
protected String shortName;
@Column(name = "description", length = 500, nullable = true)
@Column(name = "description", length = 500, nullable = false)
protected String description;
@Embedded
@@ -708,6 +708,10 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
return this.minBalanceForInterestCalculation;
}
public String getName() {
return name;
}
public String getShortName() {
return this.shortName;
}
@@ -367,12 +367,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final LocalDate todaysDate = DateUtils.getLocalDateOfTenant();
final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
fmt.withZone(DateUtils.getDateTimeZoneOfTenant());
while (todaysDate.isAfter(savingsAccountCharge.getDueLocalDate())) {
this.payCharge(savingsAccountCharge, savingsAccountCharge.getDueLocalDate(), savingsAccountCharge.amount(),
fmt, user);
}
return new CommandProcessingResultBuilder() //
.withEntityId(savingsAccountCharge.getId()) //
.withOfficeId(savingsAccountCharge.savingsAccount().officeId()) //
@@ -1551,7 +1551,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
&& transaction.getTransactionLocalDate().isAfter(new LocalDate(transferDate)))
|| transaction.getTransactionLocalDate().isAfter(transferDate)) {
throw new GeneralPlatformDomainRuleException(TransferApiConstants.transferClientSavingsException,
TransferApiConstants.transferClientSavingsException, new LocalDate(transaction.getCreatedDate()),
TransferApiConstants.transferClientSavingsException, new LocalDate(transaction.createdDate()),
transferDate);
}
@@ -25,7 +25,11 @@ USE `tn03`;
-- user+roles
-- client
SET @client_name = 'InteropCustomer';
SET @saving_account_no = '9062b90de19b43989005';
SET @saving_account_ext_id = '9062b90de19b43989005d9';
SET @IBAN = 'IC11in02tn03' + @saving_account_ext_id;
SET @MSISDN = '27710203999';
INSERT INTO `m_client` (`account_no`, `external_id`, `status_enum`, `sub_status`, `activation_date`, `office_joining_date`,
`office_id`, `transfer_to_office_id`, `staff_id`, `firstname`, `middlename`, `lastname`, `fullname`,
@@ -36,8 +40,8 @@ INSERT INTO `m_client` (`account_no`, `external_id`, `status_enum`, `sub_status`
`withdraw_on_userid`, `reactivated_on_date`, `reactivated_on_userid`, `legal_form_enum`, `reopened_on_date`,
`reopened_by_userid`)
VALUES (@saving_account_no, NULL, 300, NULL, ADDDATE(curdate(), -100), NULL, 1, NULL, NULL, NULL, NULL, NULL,
'InteropCustomer', 'InteropCustomer', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ADDDATE(curdate(), -100),
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL, NULL);
@client_name, @client_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ADDDATE(curdate(), -100),
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL, NULL);
-- saving product, account
SET @last_saving_prod_id = -1;
@@ -48,7 +52,7 @@ SET @saving_prod_id = -1;
SELECT id INTO @saving_prod_id FROM m_savings_product WHERE name = @saving_prod_name;
SET @client_id = -1;
SELECT id INTO @client_id FROM m_client WHERE fullname = 'InteropCustomer';
SELECT id INTO @client_id FROM m_client WHERE fullname = @client_name;
INSERT INTO `m_savings_account`
(`account_no`, `external_id`, `client_id`, `group_id`, `product_id`, `field_officer_id`, `status_enum`,
@@ -59,7 +63,7 @@ INSERT INTO `m_savings_account`
`interest_calculation_days_in_year_type_enum`, `min_required_opening_balance`, `withdrawal_fee_for_transfer`,
`allow_overdraft`, `account_balance_derived`, `min_required_balance`, `enforce_min_required_balance`,
`version`, `withhold_tax`)
VALUES (@saving_account_no, '9062b90de19b43989005d9', @client_id, NULL, @saving_prod_id, NULL, 300, 0, 1, 100, ADDDATE(curdate(), -100),
VALUES (@saving_account_no, @saving_account_ext_id, @client_id, NULL, @saving_prod_id, NULL, 300, 0, 1, 100, ADDDATE(curdate(), -100),
NULL, ADDDATE(curdate(), -100), NULL, ADDDATE(curdate(), -100), NULL, 'TZS', 2, NULL, 1.000000, 1, 4, 1, -- 29. - 4
360, NULL, 1, 1, 100000000.000000, 0.000000, 1, 1, 0);
@@ -68,10 +72,10 @@ SET @saving_acc_id = -1;
SELECT id INTO @saving_acc_id FROM m_savings_account WHERE account_no = @saving_account_no;
INSERT INTO interop_identifier (id, account_id, type, a_value, sub_value_or_type, created_by, created_on, modified_by, modified_on)
VALUES (NULL, @saving_acc_id, 'IBAN', 'IC11in02tn039062b90de19b43989005d9', NULL, 'operator', CURDATE(), 'operator',
VALUES (NULL, @saving_acc_id, 'IBAN', @IBAN, NULL, 'operator', CURDATE(), 'operator',
CURDATE());
INSERT INTO interop_identifier (id, account_id, type, a_value, sub_value_or_type, created_by, created_on, modified_by, modified_on)
VALUES (NULL, @saving_acc_id, 'MSISDN', '27710203999', NULL, 'operator', CURDATE(), 'operator', CURDATE());
VALUES (NULL, @saving_acc_id, 'MSISDN', @MSISDN, NULL, 'operator', CURDATE(), 'operator', CURDATE());
-- charge, mapping
-- gl_account, mappings
@@ -25,7 +25,11 @@ USE `tn04`;
-- user+roles
-- client
SET @client_name = 'InteropMerchant';
SET @saving_account_no = 'a6b6c10b2aaa4778ac2f';
SET @saving_account_ext_id = 'a6b6c10b2aaa4778ac2fc9';
SET @IBAN = 'IC11in02tn04' + @saving_account_ext_id;
SET @MSISDN = '27710204999';
INSERT INTO `m_client` (`account_no`, `external_id`, `status_enum`, `sub_status`, `activation_date`, `office_joining_date`,
`office_id`, `transfer_to_office_id`, `staff_id`, `firstname`, `middlename`, `lastname`, `fullname`,
@@ -36,7 +40,7 @@ INSERT INTO `m_client` (`account_no`, `external_id`, `status_enum`, `sub_status`
`withdraw_on_userid`, `reactivated_on_date`, `reactivated_on_userid`, `legal_form_enum`, `reopened_on_date`,
`reopened_by_userid`)
VALUES (@saving_account_no, NULL, 300, NULL, ADDDATE(curdate(), -100), NULL, 1, NULL, NULL, NULL, NULL, NULL,
'InteropMerchant', 'InteropMerchant', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ADDDATE(curdate(), -100),
@client_name, @client_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ADDDATE(curdate(), -100),
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL, NULL);
-- saving product, account
@@ -48,7 +52,7 @@ SET @saving_prod_id = -1;
SELECT id INTO @saving_prod_id FROM m_savings_product WHERE name = @saving_prod_name;
SET @client_id = -1;
SELECT id INTO @client_id FROM m_client WHERE fullname = 'InteropMerchant';
SELECT id INTO @client_id FROM m_client WHERE fullname = @client_name;
INSERT INTO `m_savings_account`
(`account_no`, `external_id`, `client_id`, `group_id`, `product_id`, `field_officer_id`, `status_enum`,
@@ -59,7 +63,7 @@ INSERT INTO `m_savings_account`
`interest_calculation_days_in_year_type_enum`, `min_required_opening_balance`, `withdrawal_fee_for_transfer`,
`allow_overdraft`, `account_balance_derived`, `min_required_balance`, `enforce_min_required_balance`,
`version`, `withhold_tax`)
VALUES (@saving_account_no, 'a6b6c10b2aaa4778ac2fc9', @client_id, NULL, @saving_prod_id, NULL, 300, 0, 1, 100, ADDDATE(curdate(), -100),
VALUES (@saving_account_no, @saving_account_ext_id, @client_id, NULL, @saving_prod_id, NULL, 300, 0, 1, 100, ADDDATE(curdate(), -100),
NULL, ADDDATE(curdate(), -100), NULL, ADDDATE(curdate(), -100), NULL, 'TZS', 2, NULL, 1.000000, 1, 4, 1, -- 29. - 4
360, NULL, 1, 1, 100000000.000000, 0.000000, 1, 1, 0);
@@ -68,10 +72,10 @@ SET @saving_acc_id = -1;
SELECT id INTO @saving_acc_id FROM m_savings_account WHERE account_no = @saving_account_no;
INSERT INTO interop_identifier (id, account_id, type, a_value, sub_value_or_type, created_by, created_on, modified_by, modified_on)
VALUES (NULL, @saving_acc_id, 'IBAN', 'IC11in02tn04a6b6c10b2aaa4778ac2fc9', NULL, 'operator', CURDATE(), 'operator',
VALUES (NULL, @saving_acc_id, 'IBAN', @IBAN, NULL, 'operator', CURDATE(), 'operator',
CURDATE());
INSERT INTO interop_identifier (id, account_id, type, a_value, sub_value_or_type, created_by, created_on, modified_by, modified_on)
VALUES (NULL, @saving_acc_id, 'MSISDN', '27710204999', NULL, 'operator', CURDATE(), 'operator', CURDATE());
VALUES (NULL, @saving_acc_id, 'MSISDN', @MSISDN, NULL, 'operator', CURDATE(), 'operator', CURDATE());
-- charge, mapping
-- gl_account, mappings