FINERACT-1932: Fineract modularization - fineract-savings

This commit is contained in:
Jose Alberto Hernandez
2024-01-29 14:14:31 +01:00
committed by Adam Saghy
parent 1f00bb6d33
commit 02cf6749af
213 changed files with 352 additions and 78 deletions
@@ -0,0 +1,241 @@
/**
* 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.accounting.producttoaccountmapping.service;
import com.google.gson.JsonElement;
import java.util.HashMap;
import java.util.Map;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.springframework.stereotype.Component;
@Component
public class SavingsProductToGLAccountMappingHelper extends ProductToGLAccountMappingHelper {
public SavingsProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository,
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
final ChargeRepositoryWrapper chargeRepositoryWrapper, final GLAccountRepositoryWrapper accountRepositoryWrapper,
final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper) {
super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, chargeRepositoryWrapper, accountRepositoryWrapper,
paymentTypeRepositoryWrapper);
}
/***
* Set of abstractions for saving Saving Products to GL Account Mappings
***/
public void saveSavingsToAssetAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.ASSET, PortfolioProductType.SAVING);
}
public void saveSavingsToIncomeAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.INCOME, PortfolioProductType.SAVING);
}
public void saveSavingsToExpenseAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.EXPENSE, PortfolioProductType.SAVING);
}
public void saveSavingsToLiabilityAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.LIABILITY, PortfolioProductType.SAVING);
}
/***
* Set of abstractions for merging Savings Products to GL Account Mappings
***/
public void mergeSavingsToAssetAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.ASSET,
PortfolioProductType.SAVING);
}
public void mergeSavingsToIncomeAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.INCOME,
PortfolioProductType.SAVING);
}
public void mergeSavingsToExpenseAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.EXPENSE,
PortfolioProductType.SAVING);
}
public void mergeSavingsToLiabilityAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.LIABILITY,
PortfolioProductType.SAVING);
}
public void createOrmergeSavingsToLiabilityAccountMappingChanges(final JsonElement element, final String paramName,
final Long productId, final int accountTypeId, final Map<String, Object> changes) {
createOrmergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, changes, GLAccountType.LIABILITY,
PortfolioProductType.SAVING);
}
/*** Abstractions for payments channel related to savings products ***/
public void savePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
savePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.SAVING);
}
public void updatePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
updatePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.SAVING);
}
public void saveChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
// save both fee and penalty charges
saveChargesToGLAccountMappings(command, element, productId, changes, PortfolioProductType.SAVING, true);
saveChargesToGLAccountMappings(command, element, productId, changes, PortfolioProductType.SAVING, false);
}
public void updateChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
// update both fee and penalty charges
updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.SAVING, true);
updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.SAVING, false);
}
public Map<String, Object> populateChangesForNewSavingsProductToGLAccountMappingCreation(final JsonElement element,
final AccountingRuleType accountingRuleType) {
final Map<String, Object> changes = new HashMap<>();
final Long savingsReferenceId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(),
element);
final Long incomeFromFeesId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_FEES.getValue(),
element);
final Long incomeFromPenaltiesId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), element);
final Long interestOnSavingsId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(), element);
final Long savingsControlId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.SAVINGS_CONTROL.getValue(),
element);
final Long transfersInSuspenseAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), element);
final Long overdraftControlId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), element);
final Long incomeFromInterest = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(), element);
final Long writeOffId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(),
element);
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
changes.put(SavingProductAccountingParams.SAVINGS_CONTROL.getValue(), savingsControlId);
changes.put(SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(), savingsReferenceId);
changes.put(SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(), interestOnSavingsId);
changes.put(SavingProductAccountingParams.INCOME_FROM_FEES.getValue(), incomeFromFeesId);
changes.put(SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), incomeFromPenaltiesId);
changes.put(SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId);
changes.put(SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), overdraftControlId);
changes.put(SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(), incomeFromInterest);
changes.put(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(), writeOffId);
break;
case ACCRUAL_PERIODIC:
break;
case ACCRUAL_UPFRONT:
break;
}
return changes;
}
/**
* Examines and updates each account mapping for given loan product with changes passed in from the Json element
*
* @param savingsProductId
* @param changes
* @param element
* @param accountingRuleType
*/
public void handleChangesToSavingsProductToGLAccountMappings(final Long savingsProductId, final Map<String, Object> changes,
final JsonElement element, final AccountingRuleType accountingRuleType) {
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
// asset
mergeSavingsToAssetAccountMappingChanges(element, SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(),
savingsProductId, CashAccountsForSavings.SAVINGS_REFERENCE.getValue(),
CashAccountsForSavings.SAVINGS_REFERENCE.toString(), changes);
mergeSavingsToAssetAccountMappingChanges(element, SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
savingsProductId, CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.toString(), changes);
// income
mergeSavingsToIncomeAccountMappingChanges(element, SavingProductAccountingParams.INCOME_FROM_FEES.getValue(),
savingsProductId, CashAccountsForSavings.INCOME_FROM_FEES.getValue(),
CashAccountsForSavings.INCOME_FROM_FEES.toString(), changes);
mergeSavingsToIncomeAccountMappingChanges(element, SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(),
savingsProductId, CashAccountsForSavings.INCOME_FROM_PENALTIES.getValue(),
CashAccountsForSavings.INCOME_FROM_PENALTIES.toString(), changes);
mergeSavingsToIncomeAccountMappingChanges(element, SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(),
savingsProductId, CashAccountsForSavings.INCOME_FROM_INTEREST.getValue(),
CashAccountsForSavings.INCOME_FROM_INTEREST.toString(), changes);
// expenses
mergeSavingsToExpenseAccountMappingChanges(element, SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(),
savingsProductId, CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(),
CashAccountsForSavings.INTEREST_ON_SAVINGS.toString(), changes);
mergeSavingsToExpenseAccountMappingChanges(element, SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(),
savingsProductId, CashAccountsForSavings.LOSSES_WRITTEN_OFF.getValue(),
CashAccountsForSavings.LOSSES_WRITTEN_OFF.toString(), changes);
// liability
mergeSavingsToLiabilityAccountMappingChanges(element, SavingProductAccountingParams.SAVINGS_CONTROL.getValue(),
savingsProductId, CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
CashAccountsForSavings.SAVINGS_CONTROL.toString(), changes);
mergeSavingsToLiabilityAccountMappingChanges(element, SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(),
savingsProductId, CashAccountsForSavings.TRANSFERS_SUSPENSE.getValue(),
CashAccountsForSavings.TRANSFERS_SUSPENSE.toString(), changes);
createOrmergeSavingsToLiabilityAccountMappingChanges(element, SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue(),
savingsProductId, CashAccountsForSavings.ESCHEAT_LIABILITY.getValue(), changes);
break;
case ACCRUAL_PERIODIC:
break;
case ACCRUAL_UPFRONT:
break;
}
}
public void deleteSavingsProductToGLAccountMapping(final Long savingsProductId) {
deleteProductToGLAccountMapping(savingsProductId, PortfolioProductType.SAVING);
}
}
@@ -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 jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.interoperation.domain.InteropIdentifier;
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
import org.apache.fineract.portfolio.savings.DepositAccountType;
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.SavingsProduct;
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, long clientId) {
super(resourceId, officeId, commandId, changesOnly, clientId);
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, long clientId) {
this(null, null, null, null, accountId, productId, productName, shortProductName, currency, accountBalance, availableBalance,
status, subStatus, accountType, depositType, activatedOn, statusUpdateOn, withdrawnOn, balanceOn, identifiers, clientId);
}
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().getValue(), product.getId().toString(), product.getName(),
product.getShortName(), account.getCurrency().getCode(), account.getAccountBalance(), account.getWithdrawableBalance(),
account.getStatus(), subStatus, account.getAccountType(), account.depositAccountType(), account.getActivationDate(),
calcStatusUpdateOn(account), account.getWithdrawnOnDate(), account.retrieveLastTransactionDate(), ids,
account.getClient().getId());
}
private static LocalDate calcStatusUpdateOn(@NotNull SavingsAccount account) {
if (account.getClosedOnDate() != null) {
return account.getClosedOnDate();
}
if (account.getWithdrawnOnDate() != null) {
return account.getWithdrawnOnDate();
}
if (account.getActivationDate() != null) {
return account.getActivationDate();
}
if (account.getRejectedOnDate() != null) {
return account.getRejectedOnDate();
}
if (account.getApprovedOnDate() != null) {
return account.getApprovedOnDate();
}
if (account.getSubmittedOnDate() != null) {
return account.getSubmittedOnDate();
}
return null;
}
}
@@ -0,0 +1,60 @@
/**
* 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 jakarta.validation.constraints.NotNull;
import org.apache.fineract.interoperation.domain.InteropIdentifier;
import org.apache.fineract.interoperation.domain.InteropIdentifierType;
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.getSubType());
}
}
@@ -0,0 +1,23 @@
/**
* 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.domain;
public enum InteropActionState {
ACCEPTED, REJECTED
}
@@ -0,0 +1,23 @@
/**
* 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.domain;
public enum InteropAmountType {
SEND, RECEIVE
}
@@ -0,0 +1,162 @@
/**
* 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.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.Objects;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
@Entity
@Table(name = "interop_identifier", uniqueConstraints = {
@UniqueConstraint(name = "uk_hathor_identifier_account", columnNames = { "account_id", "type" }),
@UniqueConstraint(name = "uk_hathor_identifier_value", columnNames = { "type", "a_value", "sub_value_or_type" }) })
public class InteropIdentifier extends AbstractPersistableCustom {
@ManyToOne(optional = false)
@JoinColumn(name = "account_id", nullable = false)
private SavingsAccount account;
@Column(name = "type", nullable = false, length = 32)
@Enumerated(EnumType.STRING)
private InteropIdentifierType type;
@Column(name = "a_value", nullable = false, length = 128)
private String value;
@Column(name = "sub_value_or_type", length = 128)
private String subType;
@Column(name = "created_by", nullable = false, length = 32)
private String createdBy;
@Column(name = "created_on", nullable = false)
private LocalDateTime createdOn;
@Column(name = "modified_by", length = 32)
private String modifiedBy;
@Column(name = "modified_on")
private LocalDateTime modifiedOn;
protected InteropIdentifier() {}
public InteropIdentifier(@NotNull SavingsAccount account, @NotNull InteropIdentifierType type, @NotNull String value, String subType,
@NotNull String createdBy) {
this.account = account;
this.type = type;
this.value = value;
this.subType = subType;
this.createdBy = createdBy;
this.createdOn = DateUtils.getAuditLocalDateTime();
}
public InteropIdentifier(@NotNull SavingsAccount account, @NotNull InteropIdentifierType type, @NotNull String createdBy) {
this(account, type, null, null, createdBy);
}
public SavingsAccount getAccount() {
return account;
}
public InteropIdentifierType getType() {
return type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getSubType() {
return subType;
}
public void setSubType(String subType) {
this.subType = subType;
}
public String getCreatedBy() {
return createdBy;
}
public LocalDateTime getCreatedOn() {
return createdOn;
}
public String geModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(String modifiedBy) {
this.modifiedBy = modifiedBy;
}
public LocalDateTime getModifiedOn() {
return modifiedOn;
}
public void setModifiedOn(LocalDateTime modifiedOn) {
this.modifiedOn = modifiedOn;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || !(o instanceof InteropIdentifier)) {
return false;
}
InteropIdentifier that = (InteropIdentifier) o;
if (!account.equals(that.account)) {
return false;
}
if (type != that.type) {
return false;
}
if (!value.equals(that.value)) {
return false;
}
return Objects.equals(subType, that.subType);
}
@Override
public int hashCode() {
int result = type.hashCode();
result = 31 * result + value.hashCode();
result = 31 * result + (subType != null ? subType.hashCode() : 0);
return result;
}
}
@@ -0,0 +1,24 @@
/**
* 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.domain;
public enum InteropIdentifierType {
MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS
}
@@ -0,0 +1,23 @@
/**
* 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.domain;
public enum InteropInitiatorType {
CONSUMER, AGENT, BUSINESS, DEVICE
}
@@ -0,0 +1,34 @@
/**
* 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.domain;
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
public enum InteropTransactionRole {
PAYER, PAYEE,;
public boolean isWithdraw() {
return this == PAYER;
}
public SavingsAccountTransactionType getTransactionType() {
return this == PAYER ? SavingsAccountTransactionType.WITHDRAWAL : SavingsAccountTransactionType.DEPOSIT;
}
}
@@ -0,0 +1,24 @@
/**
* 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.domain;
public enum InteropTransactionScenario {
DEPOSIT, WITHDRAWAL, TRANSFER, PAYMENT, REFUND
}
@@ -0,0 +1,24 @@
/**
* 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.domain;
public enum InteropTransferActionType {
PREPARE, CREATE, RELEASE;
}
@@ -0,0 +1,82 @@
/**
* 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.portfolio.accountdetails.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimelineData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData;
@SuppressWarnings("unused")
public class SavingsSummaryCustom {
private final Long id;
private final String displayName;
private final String accountNo;
private final String externalId;
private final Long productId;
private final String productName;
private final String shortProductName;
private final SavingsAccountStatusEnumData status;
private final CurrencyData currency;
private final BigDecimal accountBalance;
// differentiate Individual, JLG or Group account
private final EnumOptionData accountType;
private final SavingsAccountApplicationTimelineData timeline;
private final SavingsAccountSubStatusEnumData subStatus;
private final LocalDate lastActiveTransactionDate;
// differentiate deposit accounts Savings, FD and RD accounts
private final EnumOptionData depositType;
public SavingsSummaryCustom(final Long id, final String displayName, final String accountNo, final String externalId,
final Long productId, final String productName, final String shortProductName, final SavingsAccountStatusEnumData status,
final CurrencyData currency, final BigDecimal accountBalance, final EnumOptionData accountType,
final SavingsAccountApplicationTimelineData timeline, final EnumOptionData depositType,
final SavingsAccountSubStatusEnumData subStatus, final LocalDate lastActiveTransactionDate) {
this.id = id;
this.displayName = displayName;
this.accountNo = accountNo;
this.externalId = externalId;
this.productId = productId;
this.productName = productName;
this.shortProductName = shortProductName;
this.status = status;
this.currency = currency;
this.accountBalance = accountBalance;
this.accountType = accountType;
this.timeline = timeline;
this.depositType = depositType;
this.subStatus = subStatus;
this.lastActiveTransactionDate = lastActiveTransactionDate;
}
public String getAccountNo() {
return accountNo;
}
public Long getId() {
return id;
}
}
@@ -0,0 +1,61 @@
/**
* 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.portfolio.charge.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class SavingsAccountChargeCannotBeWaivedException extends AbstractPlatformDomainRuleException {
/*** enum of reasons of why Savings Account Charge cannot be waived **/
public enum SavingsAccountChargeCannotBeWaivedReason {
ALREADY_PAID, ALREADY_WAIVED, SAVINGS_ACCOUNT_NOT_ACTIVE, SAVINGS_ACCOUNT_CLOSED;
public String errorMessage() {
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
return "This savings account charge has been completely paid";
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
return "This savings account charge has already been waived";
} else if (name().toString().equalsIgnoreCase("SAVINGS_ACCOUNT_NOT_ACTIVE")) {
return "This savings account charge cannot be waived as the Savings account associated with it is not active";
} else if (name().toString().equalsIgnoreCase("SAVINGS_ACCOUNT_CLOSED")) {
return "This savings account charge cannot be waived as the Savings account associated with it is closed";
}
return name().toString();
}
public String errorCode() {
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
return "error.msg.savings.account.charge.already.paid";
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
return "error.msg.savings.account.charge.already.waived";
} else if (name().toString().equalsIgnoreCase("SAVINGS_ACCOUNT_NOT_ACTIVE")) {
return "error.msg.savings.account.charge.associated.savings.account.not.active";
} else if (name().toString().equalsIgnoreCase("SAVINGS_ACCOUNT_CLOSED")) {
return "error.msg.savings.account.charge.associated.savings.account.closed";
}
return name().toString();
}
}
public SavingsAccountChargeCannotBeWaivedException(final SavingsAccountChargeCannotBeWaivedReason reason,
final Long savingsAccountChargeId) {
super(reason.errorCode(), reason.errorMessage(), savingsAccountChargeId);
}
}
@@ -0,0 +1,43 @@
/**
* 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.portfolio.charge.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when savings account charge does not exist.
*/
public class SavingsAccountChargeNotFoundException extends AbstractPlatformResourceNotFoundException {
public SavingsAccountChargeNotFoundException(final Long id) {
super("error.msg.savings.account.charge.id.invalid", "Savings Account charge with identifier " + id + " does not exist", id);
}
public SavingsAccountChargeNotFoundException(final Long id, final Long savingsAccountId) {
super("error.msg.savings.account.charge.id.invalid.for.given.savings.account",
"Savings Account charge with identifier " + id + " does not exist for Savings Account " + savingsAccountId, id,
savingsAccountId);
}
public SavingsAccountChargeNotFoundException(Long id, EmptyResultDataAccessException e) {
super("error.msg.savings.account.charge.id.invalid", "Savings Account charge with identifier " + id + " does not exist", id, e);
}
}
@@ -0,0 +1,33 @@
/**
* 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.portfolio.charge.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when currency mismatch occurs
*/
public class SavingsAccountChargeWithoutMandatoryFieldException extends AbstractPlatformDomainRuleException {
public SavingsAccountChargeWithoutMandatoryFieldException(final String entity, final String postFix, final String defaultUserMessage,
final Object... defaultUserMessageArgs) {
super("error.msg." + entity + "." + postFix + ".cannot.be.blank", defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -0,0 +1,41 @@
/**
* 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.portfolio.interestratechart;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public interface InterestIncentiveApiConstants {
String idParamName = "id";
String entityTypeParamName = "entityType";
String attributeNameParamName = "attributeName";
String conditionTypeParamName = "conditionType";
String attributeValueParamName = "attributeValue";
String incentiveTypeparamName = "incentiveType";
String amountParamName = "amount";
String deleteParamName = "delete";
String INCENTIVE_RESOURCE_NAME = "interest.rate.incentives";
Set<String> INTERESTRATE_INCENTIVE_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName, entityTypeParamName,
attributeNameParamName, conditionTypeParamName, attributeValueParamName, incentiveTypeparamName, amountParamName));
}
@@ -0,0 +1,69 @@
/**
* 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.portfolio.interestratechart;
public final class InterestRateChartApiConstants {
private InterestRateChartApiConstants() {
}
public static final String INTERESTRATE_CHART_RESOURCE_NAME = "interestchart";
// actions
public static final String summitalAction = ".summital";
// command
public static final String COMMAND_UNDO_TRANSACTION = "undo";
// general
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
// interest rate chart parameters
public static final String idParamName = "id";
public static final String nameParamName = "name";
public static final String descriptionParamName = "description";
public static final String fromDateParamName = "fromDate";
public static final String endDateParamName = "endDate";
public static final String productIdParamName = "productId";
public static final String productNameParamName = "productName";
public static final String isPrimaryGroupingByAmountParamName = "isPrimaryGroupingByAmount";
// interest rate chart Slabs parameters
// public static final String periodTypeParamName = "periodType";
// public static final String fromPeriodParamName = "fromPeriod";
// public static final String toPeriodParamName = "toPeriod";
// public static final String amountRangeFromParamName = "amountRangeFrom";
// public static final String amountRangeToParamName = "amountRangeTo";
// public static final String annualInterestRateParamName =
// "annualInterestRate";
// public static final String interestRateForFemaleParamName =
// "interestRateForFemale";
// public static final String interestRateForChildrenParamName =
// "interestRateForChildren";
// public static final String interestRateForSeniorCitizenParamName =
// "interestRateForSeniorCitizen";
// associations
public static final String chartSlabs = "chartSlabs";
// to delete chart Slabs from chart
public static final String deleteParamName = "delete";
}
@@ -0,0 +1,48 @@
/**
* 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.portfolio.interestratechart;
public final class InterestRateChartSlabApiConstants {
private InterestRateChartSlabApiConstants() {
}
public static final String INTERESTRATE_CHART_SLAB_RESOURCE_NAME = "chartslab";
// actions
// command
// general
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
// interest rate chart Slabs parameters
public static final String idParamName = "id";
public static final String descriptionParamName = "description";
public static final String periodTypeParamName = "periodType";
public static final String fromPeriodParamName = "fromPeriod";
public static final String toPeriodParamName = "toPeriod";
public static final String amountRangeFromParamName = "amountRangeFrom";
public static final String amountRangeToParamName = "amountRangeTo";
public static final String annualInterestRateParamName = "annualInterestRate";
public static final String currencyCodeParamName = "currencyCode";
public static final String incentivesParamName = "incentives";
}
@@ -0,0 +1,87 @@
/**
* 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.portfolio.interestratechart.data;
import java.math.BigDecimal;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
public final class InterestIncentiveData {
@SuppressWarnings("unused")
private final Long id;
private final EnumOptionData entityType;
private final EnumOptionData attributeName;
private final EnumOptionData conditionType;
private final String attributeValue;
private final String attributeValueDesc;
private final EnumOptionData incentiveType;
private final BigDecimal amount;
public static InterestIncentiveData instance(final Long id, final EnumOptionData entityType, final EnumOptionData attributeName,
final EnumOptionData conditionType, final String attributeValue, final String attributeValueDesc,
final EnumOptionData incentiveType, final BigDecimal amount) {
return new InterestIncentiveData(id, entityType, attributeName, conditionType, attributeValue, attributeValueDesc, incentiveType,
amount);
}
private InterestIncentiveData(final Long id, final EnumOptionData entityType, final EnumOptionData attributeName,
final EnumOptionData conditionType, final String attributeValue, final String attributeValueDesc,
final EnumOptionData incentiveType, final BigDecimal amount) {
this.id = id;
this.entityType = entityType;
this.attributeName = attributeName;
this.conditionType = conditionType;
this.attributeValue = attributeValue;
this.attributeValueDesc = attributeValueDesc;
this.incentiveType = incentiveType;
this.amount = amount;
}
public EnumOptionData entityType() {
return this.entityType;
}
public EnumOptionData attributeName() {
return this.attributeName;
}
public EnumOptionData conditionType() {
return this.conditionType;
}
public String attributeValue() {
return this.attributeValue;
}
public EnumOptionData incentiveType() {
return this.incentiveType;
}
public BigDecimal amount() {
return this.amount;
}
public String attributeValueDesc() {
return this.attributeValueDesc;
}
}
@@ -0,0 +1,206 @@
/**
* 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.portfolio.interestratechart.data;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.DateUtils;
/**
* Immutable data object representing a InterestRateChart.
*/
public final class InterestRateChartData {
private final Long id;
private final String name;
private final String description;
private final LocalDate fromDate;
private final LocalDate endDate;
private final Long productId;
private final String productName;
private final boolean isPrimaryGroupingByAmount;
// associations
private Collection<InterestRateChartSlabData> chartSlabs;
// template
private final Collection<EnumOptionData> periodTypes;
private final Collection<EnumOptionData> entityTypeOptions;
private final Collection<EnumOptionData> attributeNameOptions;
private final Collection<EnumOptionData> conditionTypeOptions;
private final Collection<EnumOptionData> incentiveTypeOptions;
private final Collection<CodeValueData> genderOptions;
private final Collection<CodeValueData> clientTypeOptions;
private final Collection<CodeValueData> clientClassificationOptions;
public static InterestRateChartData instance(Long id, String name, String description, LocalDate fromDate, LocalDate endDate,
boolean isPrimaryGroupingByAmount, Long savingsProductId, String savingsProductName) {
Collection<EnumOptionData> periodTypes = null;
Collection<InterestRateChartSlabData> chartSlabs = null;
final Collection<EnumOptionData> entityTypeOptions = null;
final Collection<EnumOptionData> attributeNameOptions = null;
final Collection<EnumOptionData> conditionTypeOptions = null;
final Collection<EnumOptionData> incentiveTypeOptions = null;
final Collection<CodeValueData> genderOptions = null;
final Collection<CodeValueData> clientTypeOptions = null;
final Collection<CodeValueData> clientClassificationOptions = null;
return new InterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, savingsProductId,
savingsProductName, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions,
incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
public static InterestRateChartData withSlabs(InterestRateChartData interestRateChartData,
Collection<InterestRateChartSlabData> chartSlabs) {
return new InterestRateChartData(interestRateChartData.id, interestRateChartData.name, interestRateChartData.description,
interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.isPrimaryGroupingByAmount,
interestRateChartData.productId, interestRateChartData.productName, chartSlabs, interestRateChartData.periodTypes,
interestRateChartData.entityTypeOptions, interestRateChartData.attributeNameOptions,
interestRateChartData.conditionTypeOptions, interestRateChartData.incentiveTypeOptions, interestRateChartData.genderOptions,
interestRateChartData.clientTypeOptions, interestRateChartData.clientClassificationOptions);
}
public static InterestRateChartData withTemplate(InterestRateChartData interestRateChartData, Collection<EnumOptionData> periodTypes,
final Collection<EnumOptionData> entityTypeOptions, final Collection<EnumOptionData> attributeNameOptions,
final Collection<EnumOptionData> conditionTypeOptions, final Collection<EnumOptionData> incentiveTypeOptions,
final Collection<CodeValueData> genderOptions, final Collection<CodeValueData> clientTypeOptions,
final Collection<CodeValueData> clientClassificationOptions) {
return new InterestRateChartData(interestRateChartData.id, interestRateChartData.name, interestRateChartData.description,
interestRateChartData.fromDate, interestRateChartData.endDate, interestRateChartData.isPrimaryGroupingByAmount,
interestRateChartData.productId, interestRateChartData.productName, interestRateChartData.chartSlabs, periodTypes,
entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions, genderOptions, clientTypeOptions,
clientClassificationOptions);
}
public static InterestRateChartData template(Collection<EnumOptionData> periodTypes, final Collection<EnumOptionData> entityTypeOptions,
final Collection<EnumOptionData> attributeNameOptions, final Collection<EnumOptionData> conditionTypeOptions,
final Collection<EnumOptionData> incentiveTypeOptions, final Collection<CodeValueData> genderOptions,
final Collection<CodeValueData> clientTypeOptions, final Collection<CodeValueData> clientClassificationOptions) {
final Long id = null;
final String name = null;
final String description = null;
final LocalDate fromDate = null;
final LocalDate endDate = null;
final boolean isPrimaryGroupingByAmount = false;
final Long savingsProductId = null;
final String savingsProductName = null;
final Collection<InterestRateChartSlabData> chartSlabs = null;
return new InterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, savingsProductId,
savingsProductName, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions,
incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
private InterestRateChartData(Long id, String name, String description, LocalDate fromDate, LocalDate endDate,
boolean isPrimaryGroupingByAmount, Long savingsProductId, String savingsProductName,
Collection<InterestRateChartSlabData> chartSlabs, Collection<EnumOptionData> periodTypes,
final Collection<EnumOptionData> entityTypeOptions, final Collection<EnumOptionData> attributeNameOptions,
final Collection<EnumOptionData> conditionTypeOptions, final Collection<EnumOptionData> incentiveTypeOptions,
final Collection<CodeValueData> genderOptions, final Collection<CodeValueData> clientTypeOptions,
final Collection<CodeValueData> clientClassificationOptions) {
this.id = id;
this.name = name;
this.description = description;
this.fromDate = fromDate;
this.endDate = endDate;
this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount;
this.chartSlabs = chartSlabs;
this.productId = savingsProductId;
this.productName = savingsProductName;
this.periodTypes = periodTypes;
this.attributeNameOptions = attributeNameOptions;
this.entityTypeOptions = entityTypeOptions;
this.conditionTypeOptions = conditionTypeOptions;
this.incentiveTypeOptions = incentiveTypeOptions;
this.genderOptions = genderOptions;
this.clientTypeOptions = clientTypeOptions;
this.clientClassificationOptions = clientClassificationOptions;
}
public void addChartSlab(final InterestRateChartSlabData chartSlab) {
if (this.chartSlabs == null) {
this.chartSlabs = new ArrayList<>();
}
this.chartSlabs.add(chartSlab);
}
public boolean isFromDateAfter(final LocalDate compareDate) {
return compareDate != null && DateUtils.isAfter(this.fromDate, compareDate);
}
public LocalDate endDate() {
return this.endDate;
}
public LocalDate fromDate() {
return this.fromDate;
}
public String name() {
return this.name;
}
public String description() {
return this.description;
}
public Collection<InterestRateChartSlabData> chartSlabs() {
return this.chartSlabs;
}
public Collection<EnumOptionData> periodTypes() {
return this.periodTypes;
}
public Collection<EnumOptionData> entityTypeOptions() {
return this.entityTypeOptions;
}
public Collection<EnumOptionData> attributeNameOptions() {
return this.attributeNameOptions;
}
public Collection<EnumOptionData> conditionTypeOptions() {
return this.conditionTypeOptions;
}
public Collection<EnumOptionData> incentiveTypeOptions() {
return this.incentiveTypeOptions;
}
public Collection<CodeValueData> genderOptions() {
return this.genderOptions;
}
public Collection<CodeValueData> clientTypeOptions() {
return this.clientTypeOptions;
}
public Collection<CodeValueData> clientClassificationOptions() {
return this.clientClassificationOptions;
}
public boolean isPrimaryGroupingByAmount() {
return this.isPrimaryGroupingByAmount;
}
}
@@ -0,0 +1,208 @@
/**
* 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.portfolio.interestratechart.data;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
/**
* Immutable data object representing a InterestRateChartSlab.
*/
public final class InterestRateChartSlabData {
private final Long id;
private final String description;
private final EnumOptionData periodType;
private final Integer fromPeriod;
private final Integer toPeriod;
private final BigDecimal amountRangeFrom;
private final BigDecimal amountRangeTo;
private final BigDecimal annualInterestRate;
private final CurrencyData currency;
// associations
private Set<InterestIncentiveData> incentives;
// template
private final Collection<EnumOptionData> periodTypes;
private final Collection<EnumOptionData> entityTypeOptions;
private final Collection<EnumOptionData> attributeNameOptions;
private final Collection<EnumOptionData> conditionTypeOptions;
private final Collection<EnumOptionData> incentiveTypeOptions;
private final Collection<CodeValueData> genderOptions;
private final Collection<CodeValueData> clientTypeOptions;
private final Collection<CodeValueData> clientClassificationOptions;
public static InterestRateChartSlabData instance(final Long id, final String description, final EnumOptionData periodType,
final Integer fromPeriod, final Integer toPeriod, final BigDecimal amountRangeFrom, final BigDecimal amountRangeTo,
final BigDecimal annualInterestRate, final CurrencyData currency) {
final Collection<EnumOptionData> periodTypes = null;
final Set<InterestIncentiveData> incentivesData = null;
final Collection<EnumOptionData> entityTypeOptions = null;
final Collection<EnumOptionData> attributeNameOptions = null;
final Collection<EnumOptionData> conditionTypeOptions = null;
final Collection<EnumOptionData> incentiveTypeOptions = null;
final Collection<CodeValueData> genderOptions = null;
final Collection<CodeValueData> clientTypeOptions = null;
final Collection<CodeValueData> clientClassificationOptions = null;
return new InterestRateChartSlabData(id, description, periodType, fromPeriod, toPeriod, amountRangeFrom, amountRangeTo,
annualInterestRate, currency, incentivesData, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions,
incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
public static InterestRateChartSlabData withTemplate(final InterestRateChartSlabData chartSlab,
final Collection<EnumOptionData> periodTypes, final Collection<EnumOptionData> entityTypeOptions,
final Collection<EnumOptionData> attributeNameOptions, final Collection<EnumOptionData> conditionTypeOptions,
final Collection<EnumOptionData> incentiveTypeOptions, final Collection<CodeValueData> genderOptions,
final Collection<CodeValueData> clientTypeOptions, final Collection<CodeValueData> clientClassificationOptions) {
return new InterestRateChartSlabData(chartSlab.id, chartSlab.description, chartSlab.periodType, chartSlab.fromPeriod,
chartSlab.toPeriod, chartSlab.amountRangeFrom, chartSlab.amountRangeTo, chartSlab.annualInterestRate, chartSlab.currency,
chartSlab.incentives, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions,
genderOptions, clientTypeOptions, clientClassificationOptions);
}
public static InterestRateChartSlabData template(final Collection<EnumOptionData> periodTypes,
final Collection<EnumOptionData> entityTypeOptions, final Collection<EnumOptionData> attributeNameOptions,
final Collection<EnumOptionData> conditionTypeOptions, final Collection<EnumOptionData> incentiveTypeOptions,
final Collection<CodeValueData> genderOptions, final Collection<CodeValueData> clientTypeOptions,
final Collection<CodeValueData> clientClassificationOptions) {
final Long id = null;
final String description = null;
final EnumOptionData periodType = null;
final Integer fromPeriod = null;
final Integer toPeriod = null;
final BigDecimal amountRangeFrom = null;
final BigDecimal amountRangeTo = null;
final BigDecimal annualInterestRate = null;
final CurrencyData currency = null;
final Set<InterestIncentiveData> incentivesData = null;
return new InterestRateChartSlabData(id, description, periodType, fromPeriod, toPeriod, amountRangeFrom, amountRangeTo,
annualInterestRate, currency, incentivesData, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions,
incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
private InterestRateChartSlabData(final Long id, final String description, final EnumOptionData periodType, final Integer fromPeriod,
final Integer toPeriod, final BigDecimal amountRangeFrom, final BigDecimal amountRangeTo, final BigDecimal annualInterestRate,
final CurrencyData currency, final Set<InterestIncentiveData> incentivesData, final Collection<EnumOptionData> periodTypes,
final Collection<EnumOptionData> entityTypeOptions, final Collection<EnumOptionData> attributeNameOptions,
final Collection<EnumOptionData> conditionTypeOptions, final Collection<EnumOptionData> incentiveTypeOptions,
final Collection<CodeValueData> genderOptions, final Collection<CodeValueData> clientTypeOptions,
final Collection<CodeValueData> clientClassificationOptions) {
this.id = id;
this.description = description;
this.periodType = periodType;
this.fromPeriod = fromPeriod;
this.toPeriod = toPeriod;
this.amountRangeFrom = amountRangeFrom;
this.amountRangeTo = amountRangeTo;
this.annualInterestRate = annualInterestRate;
this.currency = currency;
this.periodTypes = periodTypes;
this.incentives = incentivesData;
this.attributeNameOptions = attributeNameOptions;
this.entityTypeOptions = entityTypeOptions;
this.conditionTypeOptions = conditionTypeOptions;
this.incentiveTypeOptions = incentiveTypeOptions;
this.genderOptions = genderOptions;
this.clientTypeOptions = clientTypeOptions;
this.clientClassificationOptions = clientClassificationOptions;
}
public String description() {
return this.description;
}
public EnumOptionData periodType() {
return this.periodType;
}
public Integer fromPeriod() {
return this.fromPeriod;
}
public Integer toPeriod() {
return this.toPeriod;
}
public BigDecimal amountRangeFrom() {
return this.amountRangeFrom;
}
public BigDecimal amountRangeTo() {
return this.amountRangeTo;
}
public BigDecimal annualInterestRate() {
return this.annualInterestRate;
}
public CurrencyData currency() {
return this.currency;
}
public Collection<EnumOptionData> periodTypes() {
return this.periodTypes;
}
public void addIncentives(final InterestIncentiveData incentiveData) {
if (this.incentives == null) {
this.incentives = new HashSet<>();
}
this.incentives.add(incentiveData);
}
public Set<InterestIncentiveData> incentives() {
return this.incentives;
}
public Collection<EnumOptionData> entityTypeOptions() {
return this.entityTypeOptions;
}
public Collection<EnumOptionData> attributeNameOptions() {
return this.attributeNameOptions;
}
public Collection<EnumOptionData> conditionTypeOptions() {
return this.conditionTypeOptions;
}
public Collection<EnumOptionData> incentiveTypeOptions() {
return this.incentiveTypeOptions;
}
public Collection<CodeValueData> genderOptions() {
return this.genderOptions;
}
public Collection<CodeValueData> clientTypeOptions() {
return this.clientTypeOptions;
}
public Collection<CodeValueData> clientClassificationOptions() {
return this.clientClassificationOptions;
}
}
@@ -0,0 +1,78 @@
/**
* 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.portfolio.interestratechart.domain;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.util.Locale;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_interest_incentives")
public class InterestIncentives extends AbstractPersistableCustom {
@ManyToOne
@JoinColumn(name = "interest_rate_slab_id", nullable = false)
private InterestRateChartSlab interestRateChartSlab;
@Embedded
private InterestIncentivesFields interestIncentivesFields;
protected InterestIncentives() {
}
/*
* public InterestIncentives(final InterestRateChartSlab interestRateChartSlab, final InterestIncentivesFields
* interestIncentivesFields) { this.interestRateChartSlab = interestRateChartSlab; this.interestIncentivesFields =
* interestIncentivesFields; }
*/
public InterestRateChartSlab interestRateChartSlab() {
return this.interestRateChartSlab;
}
public void updateInterestRateChartSlab(InterestRateChartSlab interestRateChartSlab) {
this.interestRateChartSlab = interestRateChartSlab;
}
public InterestIncentives(final InterestRateChartSlab interestRateChartSlab, final InterestIncentivesFields interestIncentivesFields) {
this.interestRateChartSlab = interestRateChartSlab;
this.interestIncentivesFields = interestIncentivesFields;
if (this.interestRateChartSlab != null) {
interestRateChartSlab.addInterestIncentive(this);
}
}
public void update(final JsonCommand command, final Map<String, Object> actualChanges, final DataValidatorBuilder baseDataValidator,
final Locale locale) {
this.interestIncentivesFields.update(command, actualChanges, baseDataValidator, locale);
}
public InterestIncentivesFields interestIncentivesFields() {
return this.interestIncentivesFields;
}
}
@@ -0,0 +1,177 @@
/**
* 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.portfolio.interestratechart.domain;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.amountParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.attributeNameParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.attributeValueParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.conditionTypeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.entityTypeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.incentiveTypeparamName;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.math.BigDecimal;
import java.util.Locale;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.portfolio.common.domain.ConditionType;
import org.apache.fineract.portfolio.interestratechart.incentive.InterestIncentiveAttributeName;
import org.apache.fineract.portfolio.interestratechart.incentive.InterestIncentiveEntityType;
import org.apache.fineract.portfolio.interestratechart.incentive.InterestIncentiveType;
@Embeddable
public class InterestIncentivesFields {
@Column(name = "entiry_type", nullable = false)
private Integer entityType;
@Column(name = "attribute_name", nullable = false)
private Integer attributeName;
@Column(name = "condition_type", nullable = false)
private Integer conditionType;
@Column(name = "attribute_value", nullable = false)
private String attributeValue;
@Column(name = "incentive_type", nullable = false)
private Integer incentiveType;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
protected InterestIncentivesFields() {
}
public static InterestIncentivesFields createNew(final Integer entityType, final Integer attributeName, final Integer conditionType,
final String attributeValue, final Integer incentiveType, final BigDecimal amount,
final DataValidatorBuilder baseDataValidator) {
return new InterestIncentivesFields(entityType, attributeName, conditionType, attributeValue, incentiveType, amount,
baseDataValidator);
}
private InterestIncentivesFields(final Integer entityType, final Integer attributeName, final Integer conditionType,
final String attributeValue, final Integer incentiveType, final BigDecimal amount,
final DataValidatorBuilder baseDataValidator) {
this.entityType = entityType;
this.attributeName = attributeName;
this.attributeValue = attributeValue;
this.conditionType = conditionType;
this.incentiveType = incentiveType;
this.amount = amount;
validateIncentiveData(baseDataValidator);
}
public InterestIncentiveAttributeName attributeName() {
return InterestIncentiveAttributeName.fromInt(this.attributeName);
}
public ConditionType conditionType() {
return ConditionType.fromInt(this.conditionType);
}
public String attributeValue() {
return this.attributeValue;
}
public InterestIncentiveType incentiveType() {
return InterestIncentiveType.fromInt(this.incentiveType);
}
public BigDecimal amount() {
return this.amount;
}
public InterestIncentiveEntityType entiryType() {
return InterestIncentiveEntityType.fromInt(this.entityType);
}
public void update(final JsonCommand command, final Map<String, Object> actualChanges, final DataValidatorBuilder baseDataValidator,
final Locale locale) {
if (command.isChangeInIntegerParameterNamed(entityTypeParamName, this.entityType, locale)) {
final Integer newValue = command.integerValueOfParameterNamed(entityTypeParamName, locale);
actualChanges.put(entityTypeParamName, newValue);
this.entityType = newValue;
}
if (command.isChangeInIntegerParameterNamed(attributeNameParamName, this.attributeName, locale)) {
final Integer newValue = command.integerValueOfParameterNamed(attributeNameParamName, locale);
actualChanges.put(attributeNameParamName, newValue);
this.attributeName = newValue;
}
if (command.isChangeInIntegerParameterNamed(conditionTypeParamName, this.conditionType, locale)) {
final Integer newValue = command.integerValueOfParameterNamed(conditionTypeParamName, locale);
actualChanges.put(conditionTypeParamName, newValue);
this.conditionType = newValue;
}
if (command.isChangeInStringParameterNamed(attributeValueParamName, this.attributeValue)) {
final String newValue = command.stringValueOfParameterNamed(attributeValueParamName);
actualChanges.put(attributeValueParamName, newValue);
this.attributeValue = newValue;
}
if (command.isChangeInIntegerParameterNamed(incentiveTypeparamName, this.incentiveType, locale)) {
final Integer newValue = command.integerValueOfParameterNamed(incentiveTypeparamName, locale);
actualChanges.put(incentiveTypeparamName, newValue);
this.incentiveType = newValue;
}
if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount, locale)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName, locale);
actualChanges.put(amountParamName, newValue);
this.amount = newValue;
}
validateIncentiveData(baseDataValidator);
}
public void validateIncentiveData(final DataValidatorBuilder baseDataValidator) {
switch (attributeName()) {
case GENDER:
baseDataValidator.reset().parameter(attributeValueParamName).value(this.attributeValue).longGreaterThanZero();
baseDataValidator.reset().parameter(conditionTypeParamName).value(this.conditionType)
.isOneOfTheseValues(ConditionType.EQUAL.getValue(), ConditionType.NOT_EQUAL.getValue());
break;
case AGE:
baseDataValidator.reset().parameter(attributeValueParamName).value(this.attributeValue).longGreaterThanZero();
break;
case CLIENT_CLASSIFICATION:
baseDataValidator.reset().parameter(attributeValueParamName).value(this.attributeValue).longGreaterThanZero();
baseDataValidator.reset().parameter(conditionTypeParamName).value(this.conditionType)
.isOneOfTheseValues(ConditionType.EQUAL.getValue(), ConditionType.NOT_EQUAL.getValue());
break;
case CLIENT_TYPE:
baseDataValidator.reset().parameter(attributeValueParamName).value(this.attributeValue).longGreaterThanZero();
baseDataValidator.reset().parameter(conditionTypeParamName).value(this.conditionType)
.isOneOfTheseValues(ConditionType.EQUAL.getValue(), ConditionType.NOT_EQUAL.getValue());
break;
default:
break;
}
}
}
@@ -0,0 +1,341 @@
/**
* 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.portfolio.interestratechart.domain;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.INTERESTRATE_CHART_RESOURCE_NAME;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.deleteParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.idParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.amountRangeFromParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.amountRangeToParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.annualInterestRateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.fromPeriodParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.periodTypeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.toPeriodParamName;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants;
import org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
@Entity
@Table(name = "m_interest_rate_chart")
public class InterestRateChart extends AbstractPersistableCustom {
@Embedded
private InterestRateChartFields chartFields;
@OneToMany(mappedBy = "interestRateChart", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Set<InterestRateChartSlab> chartSlabs = new HashSet<>();
protected InterestRateChart() {
//
}
public static InterestRateChart createNew(InterestRateChartFields chartFields,
Collection<InterestRateChartSlab> interestRateChartSlabs) {
return new InterestRateChart(chartFields, new HashSet<>(interestRateChartSlabs));
}
private InterestRateChart(InterestRateChartFields chartFields, Set<InterestRateChartSlab> interestRateChartSlabs) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(INTERESTRATE_CHART_RESOURCE_NAME);
this.chartFields = chartFields;
// validate before setting the other fields
this.validateChartSlabs(baseDataValidator);
this.throwExceptionIfValidationWarningsExist(dataValidationErrors);
this.addChartSlabs(interestRateChartSlabs);
}
public void validateChartSlabs(DataValidatorBuilder baseDataValidator) {
Collection<InterestRateChartSlab> chartSlabs = this.setOfChartSlabs();
Integer tmpPeriodType = null;
List<InterestRateChartSlab> chartSlabsList = new ArrayList<>(chartSlabs);
boolean isPrimaryGroupingByAmount = this.chartFields.isPrimaryGroupingByAmount();
chartSlabsList.sort(new InterestRateChartSlabComparator<InterestRateChartSlab>(isPrimaryGroupingByAmount));
boolean isPeriodChart = !isPrimaryGroupingByAmount;
boolean isAmountChart = isPrimaryGroupingByAmount;
for (int i = 0; i < chartSlabsList.size(); i++) {
InterestRateChartSlab iSlabs = chartSlabsList.get(i);
if (!iSlabs.slabFields().isValidChart(isPrimaryGroupingByAmount)) {
if (isPrimaryGroupingByAmount) {
baseDataValidator.parameter(InterestRateChartSlabApiConstants.amountRangeFromParamName).failWithCode("cannot.be.blank");
} else {
baseDataValidator.parameter(InterestRateChartSlabApiConstants.fromPeriodParamName).failWithCode("cannot.be.blank");
}
} else if (i > 0) {
if (isPeriodChart ^ iSlabs.slabFields().fromPeriod() != null) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.range.incomplete");
isPeriodChart = isPeriodChart || iSlabs.slabFields().fromPeriod() != null;
}
if (isAmountChart ^ iSlabs.slabFields().getAmountRangeFrom() != null) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.amount.range.incomplete");
isAmountChart = isAmountChart || iSlabs.slabFields().getAmountRangeFrom() != null;
}
}
if (i == 0) {
tmpPeriodType = iSlabs.slabFields().periodType();
if (iSlabs.slabFields().isNotProperChartStart()) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.start.incorrect",
iSlabs.slabFields().fromPeriod(), iSlabs.slabFields().getAmountRangeFrom());
}
isAmountChart = isAmountChart || iSlabs.slabFields().getAmountRangeFrom() != null;
isPeriodChart = isPeriodChart || iSlabs.slabFields().fromPeriod() != null;
} else if (iSlabs.slabFields().periodType() != null && !iSlabs.slabFields().periodType().equals(tmpPeriodType)) {
baseDataValidator.parameter(periodTypeParamName).value(iSlabs.slabFields().periodType())
.failWithCode("period.type.is.not.same", tmpPeriodType);
}
if (i + 1 < chartSlabsList.size()) {
InterestRateChartSlab nextSlabs = chartSlabsList.get(i + 1);
if (iSlabs.slabFields().isValidChart(isPrimaryGroupingByAmount)
&& nextSlabs.slabFields().isValidChart(isPrimaryGroupingByAmount)) {
if (iSlabs.slabFields().isRateChartOverlapping(nextSlabs.slabFields(), isPrimaryGroupingByAmount)) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.overlapping",
iSlabs.slabFields().fromPeriod(), iSlabs.slabFields().toPeriod(), nextSlabs.slabFields().fromPeriod(),
nextSlabs.slabFields().toPeriod(), iSlabs.slabFields().getAmountRangeFrom(),
iSlabs.slabFields().getAmountRangeTo(), nextSlabs.slabFields().getAmountRangeFrom(),
nextSlabs.slabFields().getAmountRangeTo());
} else if (iSlabs.slabFields().isRateChartHasGap(nextSlabs.slabFields(), isPrimaryGroupingByAmount)) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.has.gap",
iSlabs.slabFields().fromPeriod(), iSlabs.slabFields().toPeriod(), nextSlabs.slabFields().fromPeriod(),
nextSlabs.slabFields().toPeriod(), iSlabs.slabFields().getAmountRangeFrom(),
iSlabs.slabFields().getAmountRangeTo(), nextSlabs.slabFields().getAmountRangeFrom(),
nextSlabs.slabFields().getAmountRangeTo());
}
if (isPrimaryGroupingByAmount) {
if (!iSlabs.slabFields().isAmountSame(nextSlabs.slabFields())) {
if (InterestRateChartSlabFields.isNotProperPeriodStart(nextSlabs.slabFields())) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.range.start.incorrect",
nextSlabs.slabFields().toPeriod());
}
if (iSlabs.slabFields().toPeriod() != null) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.period.range.end.incorrect",
iSlabs.slabFields().toPeriod());
}
}
} else if (!iSlabs.slabFields().isPeriodsSame(nextSlabs.slabFields())) {
if (InterestRateChartSlabFields.isNotProperAmountStart(nextSlabs.slabFields())) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.amount.range.start.incorrect",
nextSlabs.slabFields().getAmountRangeFrom());
}
if (iSlabs.slabFields().getAmountRangeTo() != null) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.amount.range.end.incorrect",
iSlabs.slabFields().getAmountRangeTo());
}
}
}
} else if (iSlabs.slabFields().isNotProperPriodEnd()) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.slabs.range.end.incorrect", iSlabs.slabFields().toPeriod(),
iSlabs.slabFields().getAmountRangeTo());
}
}
}
public void addChartSlabs(Collection<InterestRateChartSlab> interestRateChartSlabsSet) {
Set<InterestRateChartSlab> existingChartSlabs = setOfChartSlabs();
for (InterestRateChartSlab newChartSlabs : interestRateChartSlabsSet) {
newChartSlabs.setInterestRateChart(this);
existingChartSlabs.add(newChartSlabs);
}
}
public void addChartSlab(InterestRateChartSlab newChartSlab) {
newChartSlab.setInterestRateChart(this);
setOfChartSlabs().add(newChartSlab);
}
public Set<InterestRateChartSlab> setOfChartSlabs() {
if (this.chartSlabs == null) {
this.chartSlabs = new HashSet<>();
}
return this.chartSlabs;
}
public void update(JsonCommand command, final Map<String, Object> actualChanges) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(INTERESTRATE_CHART_RESOURCE_NAME);
this.update(command, actualChanges, baseDataValidator, null, null);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void update(JsonCommand command, final Map<String, Object> actualChanges, final DataValidatorBuilder baseDataValidator,
final Set<InterestRateChart> existingCharts, String currencyCode) {
this.chartFields.update(command, actualChanges, baseDataValidator);
// interestRateChartSlabs
if (command.hasParameter(InterestRateChartApiConstants.chartSlabs)) {
updateChartSlabs(command, actualChanges, baseDataValidator, currencyCode);
}
this.validateCharts(baseDataValidator, existingCharts);
}
private void validateCharts(final DataValidatorBuilder baseDataValidator, final Set<InterestRateChart> existingCharts) {
for (InterestRateChart existingChart : existingCharts) {
if (!existingChart.equals(this)) {
if (this.chartFields.isOverlapping(existingChart.chartFields)) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.overlapping.from.and.end.dates",
existingChart.getFromDate(), existingChart.getEndDate(), this.getFromDate(), this.getEndDate());
}
}
}
}
public void updateChartSlabs(JsonCommand command, final Map<String, Object> actualChanges, final DataValidatorBuilder baseDataValidator,
String currencyCode) {
final Map<String, Object> deleteChartSlabs = new HashMap<>();
final Map<String, Object> chartSlabsChanges = new HashMap<>();
final Locale locale = command.extractLocale();
if (command.hasParameter(InterestRateChartApiConstants.chartSlabs)) {
final JsonArray array = command.arrayOfParameterNamed(InterestRateChartApiConstants.chartSlabs);
if (array != null) {
for (int i = 0; i < array.size(); i++) {
final JsonObject chartSlabsElement = array.get(i).getAsJsonObject();
JsonCommand chartSlabsCommand = JsonCommand.fromExistingCommand(command, chartSlabsElement);
if (chartSlabsCommand.parameterExists(idParamName)) {
final Long chartSlabId = chartSlabsCommand.longValueOfParameterNamed(idParamName);
final InterestRateChartSlab chartSlab = this.findChartSlab(chartSlabId);
if (chartSlab == null) {
baseDataValidator.parameter(idParamName).value(chartSlabId).failWithCode("no.chart.slab.associated.with.id");
} else if (chartSlabsCommand.parameterExists(deleteParamName)) {
if (this.removeChartSlab(chartSlab)) {
deleteChartSlabs.put(idParamName, chartSlabId);
}
} else {
chartSlab.update(chartSlabsCommand, chartSlabsChanges, baseDataValidator, locale);
}
} else {
/**
* TODO: AA: Move this code to InterestRateChartSlabAssembler
*/
final String description = chartSlabsCommand.stringValueOfParameterNamed(descriptionParamName);
final Integer periodTypeId = chartSlabsCommand.integerValueOfParameterNamed(periodTypeParamName, locale);
final SavingsPeriodFrequencyType periodFrequencyType = SavingsPeriodFrequencyType.fromInt(periodTypeId);
final Integer fromPeriod = chartSlabsCommand.integerValueOfParameterNamed(fromPeriodParamName, locale);
final Integer toPeriod = chartSlabsCommand.integerValueOfParameterNamed(toPeriodParamName, locale);
final BigDecimal amountRangeFrom = chartSlabsCommand.bigDecimalValueOfParameterNamed(amountRangeFromParamName,
locale);
final BigDecimal amountRangeTo = chartSlabsCommand.bigDecimalValueOfParameterNamed(amountRangeToParamName, locale);
final BigDecimal annualInterestRate = chartSlabsCommand.bigDecimalValueOfParameterNamed(annualInterestRateParamName,
locale);
final InterestRateChartSlabFields slabFields = InterestRateChartSlabFields.createNew(description,
periodFrequencyType, fromPeriod, toPeriod, amountRangeFrom, amountRangeTo, annualInterestRate,
currencyCode);
final InterestRateChartSlab chartSlab = InterestRateChartSlab.createNew(slabFields, this);
chartSlab.slabFields().validateChartSlabPlatformRules(chartSlabsCommand, baseDataValidator, locale);
chartSlab.updateIncentives(chartSlabsCommand, actualChanges, baseDataValidator, chartSlab, locale);
this.addChartSlab(chartSlab);
}
}
}
}
// add chart slab changes to actual changes list.
if (!chartSlabsChanges.isEmpty()) {
actualChanges.put(InterestRateChartApiConstants.chartSlabs, chartSlabsChanges);
}
// add deleted chart Slabs to actual changes
if (!deleteChartSlabs.isEmpty()) {
actualChanges.put("deletedChartSlabs", deleteChartSlabs);
}
this.validateChartSlabs(baseDataValidator);
}
public InterestRateChartSlab findChartSlab(Long chartSlabId) {
final Set<InterestRateChartSlab> chartSlabs = setOfChartSlabs();
for (InterestRateChartSlab interestRateChartSlab : chartSlabs) {
if (interestRateChartSlab.getId().equals(chartSlabId)) {
return interestRateChartSlab;
}
}
return null;
}
private boolean removeChartSlab(InterestRateChartSlab chartSlab) {
final Set<InterestRateChartSlab> chartSlabs = setOfChartSlabs();
return chartSlabs.remove(chartSlab);
}
public LocalDate getFromDate() {
return this.chartFields.getFromDate();
}
public LocalDate getEndDate() {
return this.chartFields.getEndDate();
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
public InterestRateChartFields chartFields() {
return this.chartFields;
}
public boolean isApplicableChartFor(final LocalDate target) {
return this.chartFields.isApplicableChartFor(target);
}
}
@@ -0,0 +1,161 @@
/**
* 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.portfolio.interestratechart.domain;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.dateFormatParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.endDateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.isPrimaryGroupingByAmountParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.localeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.time.LocalDate;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
import org.apache.fineract.infrastructure.core.service.DateUtils;
@Embeddable
public class InterestRateChartFields {
@Column(name = "name", length = 100, unique = false, nullable = false)
private String name;
@Column(name = "description", nullable = true)
private String description;
@Column(name = "from_date", nullable = false)
private LocalDate fromDate;
@Column(name = "end_date", nullable = true)
private LocalDate endDate;
@Column(name = "is_primary_grouping_by_amount", nullable = false)
private boolean isPrimaryGroupingByAmount;
protected InterestRateChartFields() {
//
}
public static InterestRateChartFields createNew(String name, String description, LocalDate fromDate, LocalDate toDate,
boolean isPrimaryGroupingByAmount) {
return new InterestRateChartFields(name, description, fromDate, toDate, isPrimaryGroupingByAmount);
}
private InterestRateChartFields(String name, String description, LocalDate fromDate, LocalDate toDate,
boolean isPrimaryGroupingByAmount) {
this.name = name;
this.description = description;
this.fromDate = fromDate;
this.endDate = toDate;
this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount;
}
public void update(JsonCommand command, final Map<String, Object> actualChanges, final DataValidatorBuilder baseDataValidator) {
if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
final String newValue = command.stringValueOfParameterNamed(nameParamName);
actualChanges.put(nameParamName, newValue);
this.name = newValue;
}
if (command.isChangeInStringParameterNamed(descriptionParamName, this.description)) {
final String newValue = command.stringValueOfParameterNamed(descriptionParamName);
actualChanges.put(descriptionParamName, newValue);
this.description = newValue;
}
final String localeAsInput = command.locale();
final String dateFormat = command.dateFormat();
if (command.isChangeInLocalDateParameterNamed(fromDateParamName, getFromDate())) {
final String newValueAsString = command.stringValueOfParameterNamed(fromDateParamName);
actualChanges.put(fromDateParamName, newValueAsString);
actualChanges.put(localeParamName, localeAsInput);
actualChanges.put(dateFormatParamName, dateFormat);
this.fromDate = command.localDateValueOfParameterNamed(fromDateParamName);
}
if (command.isChangeInLocalDateParameterNamed(endDateParamName, getEndDate())) {
final String newValueAsString = command.stringValueOfParameterNamed(endDateParamName);
actualChanges.put(endDateParamName, newValueAsString);
actualChanges.put(localeParamName, localeAsInput);
actualChanges.put(dateFormatParamName, dateFormat);
this.endDate = command.localDateValueOfParameterNamed(endDateParamName);
}
if (command.isChangeInBooleanParameterNamed(isPrimaryGroupingByAmountParamName, this.isPrimaryGroupingByAmount)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isPrimaryGroupingByAmountParamName);
actualChanges.put(isPrimaryGroupingByAmountParamName, newValue);
this.isPrimaryGroupingByAmount = newValue;
}
if (isFromDateAfterToDate()) {
baseDataValidator.parameter(fromDateParamName).value(fromDate).failWithCode("from.date.is.after.to.date");
}
}
public boolean isFromDateAfterToDate() {
return isFromDateAfter(getEndDate());
}
public boolean isFromDateAfter(LocalDate compare) {
return compare != null && DateUtils.isAfter(getFromDate(), compare);
}
public LocalDate getFromDate() {
return this.fromDate;
}
public LocalDate getEndDate() {
return this.endDate;
}
public boolean isOverlapping(InterestRateChartFields that) {
final LocalDate thisFromDate = this.getFromDate();
LocalDate thisEndDate = this.getEndDate();
thisEndDate = thisEndDate == null ? DateUtils.getBusinessLocalDate() : thisEndDate;
final LocalDate thatFromDate = that.getFromDate();
LocalDate thatEndDate = that.getEndDate();
thatEndDate = thatEndDate == null ? DateUtils.getBusinessLocalDate() : thatEndDate;
final LocalDateInterval thisInterval = LocalDateInterval.create(thisFromDate, thisEndDate);
final LocalDateInterval thatInterval = LocalDateInterval.create(thatFromDate, thatEndDate);
if (thisInterval.containsPortionOf(thatInterval) || thatInterval.containsPortionOf(thisInterval)) {
return true;
}
return false;// no overlapping
}
public boolean isApplicableChartFor(final LocalDate target) {
final LocalDate endDate = this.endDate == null ? DateUtils.getBusinessLocalDate() : this.getEndDate();
final LocalDateInterval interval = LocalDateInterval.create(getFromDate(), endDate);
return interval.contains(target);
}
public boolean isPrimaryGroupingByAmount() {
return this.isPrimaryGroupingByAmount;
}
}
@@ -0,0 +1,26 @@
/**
* 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.portfolio.interestratechart.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface InterestRateChartRepository extends JpaRepository<InterestRateChart, Long>, JpaSpecificationExecutor<InterestRateChart> {
}
@@ -0,0 +1,61 @@
/**
* 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.portfolio.interestratechart.domain;
import org.apache.fineract.portfolio.interestratechart.exception.InterestRateChartNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* Wrapper for {@link InterestRateChartRepository} that is responsible for checking if {@link InterestRateChart} is
* returned when using <code>findOne</code> repository method and throwing an appropriate not found exception.
* </p>
*
* <p>
* This is to avoid need for checking and throwing in multiple areas of code base where
* {@link InterestRateChartRepository} is required.
* </p>
*/
@Service
public class InterestRateChartRepositoryWrapper {
private final InterestRateChartRepository repository;
@Autowired
public InterestRateChartRepositoryWrapper(final InterestRateChartRepository repository) {
this.repository = repository;
}
public InterestRateChart findOneWithNotFoundDetection(final Long intrestRateChartId) {
return this.repository.findById(intrestRateChartId).orElseThrow(() -> new InterestRateChartNotFoundException(intrestRateChartId));
}
public void save(final InterestRateChart interestRateChart) {
this.repository.save(interestRateChart);
}
public void delete(final InterestRateChart interestRateChart) {
this.repository.delete(interestRateChart);
}
public void saveAndFlush(final InterestRateChart interestRateChart) {
this.repository.saveAndFlush(interestRateChart);
}
}
@@ -0,0 +1,196 @@
/**
* 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.portfolio.interestratechart.domain;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.amountParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.attributeNameParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.attributeValueParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.conditionTypeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.entityTypeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.incentiveTypeparamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.deleteParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.idParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.INTERESTRATE_CHART_SLAB_RESOURCE_NAME;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants;
import org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants;
@Entity
@Table(name = "m_interest_rate_slab")
public class InterestRateChartSlab extends AbstractPersistableCustom {
@Embedded
private InterestRateChartSlabFields slabFields;
@ManyToOne(optional = false)
@JoinColumn(name = "interest_rate_chart_id", referencedColumnName = "id", nullable = false)
private InterestRateChart interestRateChart;
@OneToMany(mappedBy = "interestRateChartSlab", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Set<InterestIncentives> interestIncentives = new HashSet<>();
protected InterestRateChartSlab() {
//
}
public static InterestRateChartSlab createNew(InterestRateChartSlabFields slabFields, InterestRateChart interestRateChart) {
return new InterestRateChartSlab(slabFields, interestRateChart);
}
public void update(JsonCommand command, final Map<String, Object> actualChanges, final Locale locale) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(INTERESTRATE_CHART_SLAB_RESOURCE_NAME);
this.update(command, actualChanges, baseDataValidator, locale);
this.interestRateChart.validateChartSlabs(baseDataValidator);
this.throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void update(JsonCommand command, final Map<String, Object> actualChanges, final DataValidatorBuilder baseDataValidator,
final Locale locale) {
this.slabFields.update(command, actualChanges, baseDataValidator, locale);
updateIncentives(command, actualChanges, baseDataValidator, this, locale);
}
private InterestRateChartSlab(InterestRateChartSlabFields slabFields, InterestRateChart interestRateChart) {
this.slabFields = slabFields;
this.interestRateChart = interestRateChart;
if (this.interestRateChart != null) {
interestRateChart.addChartSlab(this);
}
}
public void setInterestRateChart(InterestRateChart interestRateChart) {
this.interestRateChart = interestRateChart;
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
public InterestRateChartSlabFields slabFields() {
return this.slabFields;
}
public Set<InterestIncentives> setOfInterestIncentives() {
if (this.interestIncentives == null) {
this.interestIncentives = new HashSet<>();
}
return this.interestIncentives;
}
public void addInterestIncentive(InterestIncentives interestIncentives) {
interestIncentives.updateInterestRateChartSlab(this);
setOfInterestIncentives().add(interestIncentives);
}
public InterestIncentives findInterestIncentive(Long interestIncentiveId) {
final Set<InterestIncentives> interestIncentives = setOfInterestIncentives();
for (InterestIncentives interestIncentive : interestIncentives) {
if (interestIncentive.getId().equals(interestIncentiveId)) {
return interestIncentive;
}
}
return null;
}
public boolean removeInterestIncentive(InterestIncentives incentive) {
final Set<InterestIncentives> incentives = setOfInterestIncentives();
return incentives.remove(incentive);
}
public void updateIncentives(JsonCommand command, final Map<String, Object> actualChanges, final DataValidatorBuilder baseDataValidator,
final InterestRateChartSlab chartSlab, final Locale locale) {
final Map<String, Object> deleteIncentives = new HashMap<>();
final Map<String, Object> IncentiveChanges = new HashMap<>();
if (command.hasParameter(InterestRateChartSlabApiConstants.incentivesParamName)) {
final JsonArray array = command.arrayOfParameterNamed(InterestRateChartSlabApiConstants.incentivesParamName);
if (array != null) {
for (int i = 0; i < array.size(); i++) {
final JsonObject incentiveElement = array.get(i).getAsJsonObject();
JsonCommand incentivesCommand = JsonCommand.fromExistingCommand(command, incentiveElement);
if (incentivesCommand.parameterExists(InterestIncentiveApiConstants.idParamName)) {
final Long interestIncentiveId = incentivesCommand
.longValueOfParameterNamed(InterestIncentiveApiConstants.idParamName);
final InterestIncentives interestIncentives = chartSlab.findInterestIncentive(interestIncentiveId);
if (interestIncentives == null) {
baseDataValidator.parameter(InterestIncentiveApiConstants.idParamName).value(interestIncentiveId)
.failWithCode("no.interest.incentive.associated.with.id");
} else if (incentivesCommand.parameterExists(deleteParamName)) {
if (chartSlab.removeInterestIncentive(interestIncentives)) {
deleteIncentives.put(idParamName, interestIncentiveId);
}
} else {
interestIncentives.update(incentivesCommand, IncentiveChanges, baseDataValidator, locale);
}
} else {
Integer entityType = incentivesCommand.integerValueOfParameterNamed(entityTypeParamName, locale);
Integer conditionType = incentivesCommand.integerValueOfParameterNamed(conditionTypeParamName, locale);
Integer attributeName = incentivesCommand.integerValueOfParameterNamed(attributeNameParamName, locale);
String attributeValue = incentivesCommand.stringValueOfParameterNamed(attributeValueParamName);
Integer incentiveType = incentivesCommand.integerValueOfParameterNamed(incentiveTypeparamName, locale);
BigDecimal amount = incentivesCommand.bigDecimalValueOfParameterNamed(amountParamName, locale);
InterestIncentivesFields incentivesFields = InterestIncentivesFields.createNew(entityType, attributeName,
conditionType, attributeValue, incentiveType, amount, baseDataValidator);
InterestIncentives incentives = new InterestIncentives(chartSlab, incentivesFields);
chartSlab.addInterestIncentive(incentives);
}
}
}
// add chart slab changes to actual changes list.
if (!IncentiveChanges.isEmpty()) {
actualChanges.put(InterestRateChartSlabApiConstants.incentivesParamName, IncentiveChanges);
}
// add deleted chart Slabs to actual changes
if (!deleteIncentives.isEmpty()) {
actualChanges.put("deletedIncentives", deleteIncentives);
}
}
}
}
@@ -0,0 +1,91 @@
/**
* 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.portfolio.interestratechart.domain;
import java.math.BigDecimal;
import java.util.Comparator;
import org.apache.fineract.portfolio.savings.domain.DepositAccountInterestRateChartSlabs;
/**
* Sort InterestRateChartSlab by input range
*/
public class InterestRateChartSlabComparator<T> implements Comparator<T> {
private final boolean isPrimaryGroupingByAmount;
public InterestRateChartSlabComparator(final boolean isPrimaryGroupingByAmount) {
this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount;
}
@Override
public int compare(final T o1, final T o2) {
int compareResult = 0;
InterestRateChartSlabFields slabs1 = null;
InterestRateChartSlabFields slabs2 = null;
if (o1 instanceof InterestRateChartSlab) {
slabs1 = ((InterestRateChartSlab) o1).slabFields();
slabs2 = ((InterestRateChartSlab) o2).slabFields();
} else if (o1 instanceof DepositAccountInterestRateChartSlabs) {
slabs1 = ((DepositAccountInterestRateChartSlabs) o1).slabFields();
slabs2 = ((DepositAccountInterestRateChartSlabs) o2).slabFields();
} else {
return compareResult;
}
if (slabs1.isPeriodsSame(slabs2) && slabs1.isAmountSame(slabs2)) {
compareResult = 0;
} else {
if (isPrimaryGroupingByAmount) {
if (slabs1.isAmountSame(slabs2)) {
compareResult = comparePeriods(slabs1, slabs2);
} else {
compareResult = compareAmounts(slabs1, slabs2);
}
} else {
if (slabs1.isPeriodsSame(slabs2)) {
compareResult = compareAmounts(slabs1, slabs2);
} else {
compareResult = comparePeriods(slabs1, slabs2);
}
}
}
return compareResult;
}
private int comparePeriods(final InterestRateChartSlabFields slabs1, InterestRateChartSlabFields slabs2) {
int compareResult = 0;
Integer periodFrom1 = slabs1.fromPeriod();
Integer periodFrom2 = slabs2.fromPeriod();
if (periodFrom1 != null && periodFrom2 != null) {
compareResult = periodFrom1.compareTo(periodFrom2);
}
return compareResult;
}
private int compareAmounts(final InterestRateChartSlabFields slabs1, InterestRateChartSlabFields slabs2) {
int compareResult = 0;
BigDecimal amountFrom1 = slabs1.getAmountRangeFrom();
BigDecimal amountFrom2 = slabs2.getAmountRangeFrom();
if (amountFrom1 != null && amountFrom2 != null) {
compareResult = amountFrom1.compareTo(amountFrom2);
}
return compareResult;
}
}
@@ -0,0 +1,376 @@
/**
* 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.portfolio.interestratechart.domain;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.amountRangeFromParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.amountRangeToParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.annualInterestRateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.fromPeriodParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.periodTypeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.toPeriodParamName;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
@Embeddable
public class InterestRateChartSlabFields {
@Column(name = "description", nullable = true)
private String description;
@Column(name = "period_type_enum", nullable = true)
private Integer periodType;
@Column(name = "from_period")
private Integer fromPeriod;
@Column(name = "to_period")
private Integer toPeriod;
@Column(name = "amount_range_from", scale = 6, precision = 19)
private BigDecimal amountRangeFrom;
public BigDecimal getAmountRangeFrom() {
return this.amountRangeFrom;
}
public BigDecimal getAmountRangeTo() {
return this.amountRangeTo;
}
@Column(name = "amount_range_to", scale = 6, precision = 19)
private BigDecimal amountRangeTo;
@Column(name = "annual_interest_rate", scale = 6, precision = 19, nullable = false)
private BigDecimal annualInterestRate;
@Column(name = "currency_code", nullable = false)
private String currencyCode;
protected InterestRateChartSlabFields() {
//
}
public static InterestRateChartSlabFields createNew(final String description, final SavingsPeriodFrequencyType periodFrequencyType,
final Integer fromPeriod, final Integer toPeriod, final BigDecimal amountRangeFrom, final BigDecimal amountRangeTo,
final BigDecimal annualInterestRate, final String currencyCode) {
return new InterestRateChartSlabFields(description, periodFrequencyType, fromPeriod, toPeriod, amountRangeFrom, amountRangeTo,
annualInterestRate, currencyCode);
}
private InterestRateChartSlabFields(final String description, final SavingsPeriodFrequencyType periodFrequencyType,
final Integer fromPeriod, final Integer toPeriod, final BigDecimal amountRangeFrom, final BigDecimal amountRangeTo,
final BigDecimal annualInterestRate, final String currencyCode) {
this.description = description;
this.periodType = (periodFrequencyType == null || periodFrequencyType.isInvalid()) ? null : periodFrequencyType.getValue();
this.fromPeriod = fromPeriod;
this.toPeriod = toPeriod;
this.amountRangeFrom = amountRangeFrom;
this.amountRangeTo = amountRangeTo;
this.annualInterestRate = annualInterestRate;
this.currencyCode = currencyCode;
}
public void update(final JsonCommand command, final Map<String, Object> actualChanges, final DataValidatorBuilder baseDataValidator,
final Locale locale) {
if (command.isChangeInStringParameterNamed(descriptionParamName, this.description)) {
final String newValue = command.stringValueOfParameterNamed(descriptionParamName);
actualChanges.put(descriptionParamName, newValue);
this.description = newValue;
}
if (command.isChangeInIntegerParameterNamed(periodTypeParamName, this.periodType, locale)) {
final Integer newValue = command.integerValueOfParameterNamed(periodTypeParamName, locale);
actualChanges.put(periodTypeParamName, newValue);
this.periodType = newValue;
}
if (command.isChangeInIntegerParameterNamed(fromPeriodParamName, this.fromPeriod, locale)) {
final Integer newValue = command.integerValueOfParameterNamed(fromPeriodParamName, locale);
actualChanges.put(fromPeriodParamName, newValue);
this.fromPeriod = newValue;
}
if (command.isChangeInIntegerParameterNamed(toPeriodParamName, this.toPeriod, locale)) {
final Integer newValue = command.integerValueOfParameterNamed(toPeriodParamName, locale);
actualChanges.put(toPeriodParamName, newValue);
this.toPeriod = newValue;
}
if (command.isChangeInBigDecimalParameterNamed(amountRangeFromParamName, this.amountRangeFrom, locale)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountRangeFromParamName, locale);
actualChanges.put(amountRangeFromParamName, newValue);
this.amountRangeFrom = newValue;
}
if (command.isChangeInBigDecimalParameterNamed(amountRangeToParamName, this.amountRangeTo, locale)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountRangeToParamName, locale);
actualChanges.put(amountRangeToParamName, newValue);
this.amountRangeTo = newValue;
}
if (command.isChangeInBigDecimalParameterNamed(annualInterestRateParamName, this.annualInterestRate, locale)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(annualInterestRateParamName, locale);
actualChanges.put(annualInterestRateParamName, newValue);
this.annualInterestRate = newValue;
}
validateChartSlabPlatformRules(command, baseDataValidator, locale);
}
public void validateChartSlabPlatformRules(final JsonCommand chartSlabsCommand, final DataValidatorBuilder baseDataValidator,
Locale locale) {
if (isFromPeriodGreaterThanToPeriod()) {
final Integer fromPeriod = chartSlabsCommand.integerValueOfParameterNamed(fromPeriodParamName, locale);
baseDataValidator.parameter(fromPeriodParamName).value(fromPeriod).failWithCode("from.period.is.greater.than.to.period");
}
if (isAmountRangeFromGreaterThanTo()) {
final BigDecimal amountRangeFrom = chartSlabsCommand.bigDecimalValueOfParameterNamed(amountRangeFromParamName, locale);
baseDataValidator.parameter(amountRangeFromParamName).value(amountRangeFrom)
.failWithCode("amount.range.from.is.greater.than.amount.range.to");
}
}
public boolean isFromPeriodGreaterThanToPeriod() {
boolean isGreater = false;
if (this.toPeriod != null && this.fromPeriod.compareTo(this.toPeriod) > 0) {
isGreater = true;
}
return isGreater;
}
public boolean isAmountRangeFromGreaterThanTo() {
boolean isGreater = false;
if (this.amountRangeFrom != null && this.amountRangeTo != null && this.amountRangeFrom.compareTo(this.amountRangeTo) > 0) {
isGreater = true;
}
return isGreater;
}
public Integer periodType() {
return this.periodType;
}
public Integer fromPeriod() {
return this.fromPeriod;
}
public Integer toPeriod() {
return this.toPeriod;
}
public boolean isRateChartHasGap(final InterestRateChartSlabFields that, final boolean isPrimaryGroupingByAmount) {
boolean isPeriodSame = isPeriodsSame(that);
boolean isAmountSame = isAmountSame(that);
boolean hasPeriods = this.fromPeriod != null || that.fromPeriod != null;
boolean hasAmounts = this.amountRangeFrom != null || that.amountRangeFrom != null;
if (isPrimaryGroupingByAmount) {
if (isAmountSame) {
if (hasPeriods) {
if (this.toPeriod == null) {
return true;
}
return isNotProperPeriodStart(that.fromPeriod);
}
} else {
return isNotProperAmountStart(that.amountRangeFrom) || isNotProperPeriodStart(that);
}
} else {
if (isPeriodSame) {
if (hasAmounts) {
if (this.amountRangeTo == null) {
return true;
}
return isNotProperAmountStart(that.amountRangeFrom);
}
} else {
return isNotProperPeriodStart(that.fromPeriod) || isNotProperAmountStart(that);
}
}
return false;
}
public boolean isValidChart(boolean isPrimaryGroupingByAmount) {
return (!isPrimaryGroupingByAmount && this.fromPeriod != null) || (isPrimaryGroupingByAmount && this.amountRangeFrom != null);
}
public boolean isNotProperChartStart() {
return isNotProperPeriodStart(this) || isNotProperAmountStart(this);
}
public static boolean isNotProperAmountStart(final InterestRateChartSlabFields interestRateChartSlabFields) {
return interestRateChartSlabFields.amountRangeFrom != null
&& (interestRateChartSlabFields.amountRangeFrom.compareTo(BigDecimal.ONE) != 0
&& interestRateChartSlabFields.amountRangeFrom.compareTo(BigDecimal.ZERO) != 0);
}
private boolean isNotProperAmountStart(final BigDecimal amount) {
return this.amountRangeTo == null || (amount != null && amount.compareTo(this.amountRangeTo.add(BigDecimal.ONE)) != 0);
}
private boolean isNotProperPeriodStart(final Integer period) {
return this.toPeriod == null || (period != null && period.compareTo(this.toPeriod + 1) != 0);
}
public static boolean isNotProperPeriodStart(InterestRateChartSlabFields interestRateChartSlabFields) {
return interestRateChartSlabFields.fromPeriod != null
&& !(interestRateChartSlabFields.fromPeriod.equals(1) || interestRateChartSlabFields.fromPeriod.equals(0));
}
public boolean isNotProperPriodEnd() {
return !(this.toPeriod == null && this.amountRangeTo == null);
}
public boolean isRateChartOverlapping(final InterestRateChartSlabFields that, final boolean isPrimaryGroupingByAmount) {
boolean isPeriodOverLapping = isPeriodOverlapping(that);
boolean isAmountOverLapping = isAmountOverlapping(that);
boolean isPeriodSame = isPeriodsSame(that);
boolean isAmountSame = isAmountSame(that);
boolean isOverlapping = false;
if (isPrimaryGroupingByAmount) {
isOverlapping = (isAmountOverLapping && !isAmountSame) || (isPeriodOverLapping && isAmountSame);
} else {
isOverlapping = (isPeriodOverLapping && !isPeriodSame) || (isAmountOverLapping && isPeriodSame);
}
return isOverlapping;
}
private boolean isPeriodOverlapping(final InterestRateChartSlabFields that) {
if (isIntegerSame(that.toPeriod, this.toPeriod)) {
return true;
} else if (isIntegerSame(that.fromPeriod, this.fromPeriod)) {
return true;
} else if (this.toPeriod == null) {
return true;
} else if (that.toPeriod == null) {
return that.fromPeriod <= this.toPeriod;
}
return this.fromPeriod <= that.toPeriod && that.fromPeriod <= this.toPeriod;
}
private boolean isAmountOverlapping(final InterestRateChartSlabFields that) {
if (isBigDecimalSame(that.amountRangeFrom, this.amountRangeFrom)) {
return true;
} else if (isBigDecimalSame(that.amountRangeTo, this.amountRangeTo)) {
return true;
} else if (this.amountRangeTo == null) {
return true;
} else if (that.amountRangeTo == null) {
return that.amountRangeFrom.compareTo(this.amountRangeTo) <= 0;
}
return this.amountRangeFrom.compareTo(that.amountRangeTo) <= 0 && that.amountRangeFrom.compareTo(this.amountRangeTo) <= 0;
}
public boolean isAmountSame(final InterestRateChartSlabFields that) {
return isBigDecimalSame(this.amountRangeFrom, that.amountRangeFrom) && isBigDecimalSame(this.amountRangeTo, that.amountRangeTo);
}
public boolean isPeriodsSame(final InterestRateChartSlabFields that) {
return isIntegerSame(this.fromPeriod, that.fromPeriod) && isIntegerSame(this.toPeriod, that.toPeriod);
}
public boolean isIntegerSame(final Integer obj1, final Integer obj2) {
if (obj1 == null || obj2 == null) {
if (Objects.equals(obj1, obj2)) {
return true;
}
return false;
}
return obj1.equals(obj2);
}
public boolean isBigDecimalSame(final BigDecimal obj1, final BigDecimal obj2) {
if (obj1 == null || obj2 == null) {
return Objects.compare(obj1, obj2, Comparator.nullsFirst(Comparator.naturalOrder())) == 0;
}
return obj1.compareTo(obj2) == 0;
}
public boolean isBetweenPeriod(final LocalDate periodStartDate, final LocalDate periodEndDate) {
final Integer compare = depositPeriod(periodStartDate, periodEndDate);
return isPeriodBetween(compare);
}
public boolean isAmountRangeProvided() {
return this.amountRangeFrom != null;
}
public BigDecimal annualInterestRate() {
return this.annualInterestRate;
}
public Integer depositPeriod(final LocalDate periodStartDate, final LocalDate periodEndDate) {
Integer actualDepositPeriod = 0;
final SavingsPeriodFrequencyType periodFrequencyType = SavingsPeriodFrequencyType.fromInt(periodType());
switch (periodFrequencyType) {
case DAYS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.DAYS.between(periodStartDate, periodEndDate));
break;
case WEEKS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.WEEKS.between(periodStartDate, periodEndDate));
break;
case MONTHS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.MONTHS.between(periodStartDate, periodEndDate));
break;
case YEARS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.YEARS.between(periodStartDate, periodEndDate));
break;
case INVALID:
actualDepositPeriod = 0;// default value
break;
}
return actualDepositPeriod;
}
public boolean isAmountBetween(final BigDecimal depositAmount) {
boolean returnValue = true;
if (amountRangeFrom != null && amountRangeTo != null) {
returnValue = depositAmount.compareTo(amountRangeFrom) >= 0 && depositAmount.compareTo(amountRangeTo) <= 0;
} else if (amountRangeFrom != null) {
returnValue = depositAmount.compareTo(amountRangeFrom) >= 0;
}
return returnValue;
}
public boolean isPeriodBetween(final Integer periods) {
boolean returnValue = true;
if (fromPeriod != null && toPeriod != null) {
returnValue = periods.compareTo(fromPeriod) >= 0 && periods.compareTo(toPeriod) <= 0;
} else if (fromPeriod != null) {
returnValue = periods.compareTo(fromPeriod) >= 0;
}
return returnValue;
}
}
@@ -0,0 +1,27 @@
/**
* 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.portfolio.interestratechart.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface InterestRateChartSlabRepository
extends JpaRepository<InterestRateChartSlab, Long>, JpaSpecificationExecutor<InterestRateChartSlab> {
}
@@ -0,0 +1,62 @@
/**
* 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.portfolio.interestratechart.domain;
import org.apache.fineract.portfolio.interestratechart.exception.InterestRateChartSlabNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* Wrapper for {@link InterestRateChartSlabRepository} that is responsible for checking if {@link InterestRateChartSlab}
* is returned when using <code>findOne</code> repository method and throwing an appropriate not found exception.
* </p>
*
* <p>
* This is to avoid need for checking and throwing in multiple areas of code base where
* {@link InterestRateChartSlabRepository} is required.
* </p>
*/
@Service
public class InterestRateChartSlabRepositoryWrapper {
private final InterestRateChartSlabRepository repository;
@Autowired
public InterestRateChartSlabRepositoryWrapper(final InterestRateChartSlabRepository repository) {
this.repository = repository;
}
public InterestRateChartSlab findOneWithNotFoundDetection(final Long chartSlabId) {
return this.repository.findById(chartSlabId).orElseThrow(() -> new InterestRateChartSlabNotFoundException(chartSlabId));
}
public void save(final InterestRateChartSlab chartSlab) {
this.repository.save(chartSlab);
}
public void delete(final InterestRateChartSlab chartSlab) {
this.repository.delete(chartSlab);
}
public void saveAndFlush(final InterestRateChartSlab chartSlab) {
this.repository.saveAndFlush(chartSlab);
}
}
@@ -0,0 +1,33 @@
/**
* 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.portfolio.interestratechart.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException;
public class InterestRateChartNotFoundException extends AbstractPlatformResourceNotFoundException {
public InterestRateChartNotFoundException(final Long id) {
super("error.msg.interest.rate.chart.id.invalid", "Interest rate chart with identifier " + id + " does not exist", id);
}
public InterestRateChartNotFoundException(Long id, EmptyResultDataAccessException e) {
super("error.msg.interest.rate.chart.id.invalid", "Interest rate chart with identifier " + id + " does not exist", id, e);
}
}
@@ -0,0 +1,34 @@
/**
* 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.portfolio.interestratechart.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class InterestRateChartSlabNotFoundException extends AbstractPlatformResourceNotFoundException {
public InterestRateChartSlabNotFoundException(final Long id) {
super("error.msg.interest.rate.chart.slab.id.invalid", "Interest rate chart slab with identifier " + id + " does not exist", id);
}
public InterestRateChartSlabNotFoundException(final Long id, final Long chartId) {
super("error.msg.interest.rate.chart.slab.id.invalid",
"Interest rate chart slab with identifier " + id + " does not exist in interest chart with identifier " + chartId, id,
chartId);
}
}
@@ -0,0 +1,50 @@
/**
* 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.portfolio.interestratechart.incentive;
import java.math.BigDecimal;
import org.apache.fineract.portfolio.common.domain.ConditionType;
public abstract class AttributeIncentiveCalculation {
public abstract BigDecimal calculateIncentive(IncentiveDTO incentiveDTO);
public boolean applyIncentive(ConditionType conditionType, Long attributeValue, Long actualValue) {
boolean applyIncentive = false;
int compareVal = actualValue.compareTo(attributeValue);
switch (conditionType) {
case LESSTHAN:
applyIncentive = compareVal < 0;
break;
case EQUAL:
applyIncentive = compareVal == 0;
break;
case NOT_EQUAL:
applyIncentive = compareVal != 0;
break;
case GRETERTHAN:
applyIncentive = compareVal > 0;
break;
default:
applyIncentive = false;
break;
}
return applyIncentive;
}
}
@@ -0,0 +1,39 @@
/**
* 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.portfolio.interestratechart.incentive;
public final class AttributeIncentiveCalculationFactory {
private AttributeIncentiveCalculationFactory() {
}
public static AttributeIncentiveCalculation findAttributeIncentiveCalculation(InterestIncentiveEntityType entityType) {
AttributeIncentiveCalculation attributeIncentiveCalculation = null;
switch (entityType) {
case CUSTOMER:
attributeIncentiveCalculation = new ClientAttributeIncentiveCalculation();
break;
default:
break;
}
return attributeIncentiveCalculation;
}
}
@@ -0,0 +1,85 @@
/**
* 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.portfolio.interestratechart.incentive;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.interestratechart.domain.InterestIncentivesFields;
public class ClientAttributeIncentiveCalculation extends AttributeIncentiveCalculation {
@Override
public BigDecimal calculateIncentive(IncentiveDTO incentiveDTO) {
final Client client = incentiveDTO.client();
BigDecimal interest = incentiveDTO.interest();
final InterestIncentivesFields incentivesFields = incentiveDTO.incentives();
boolean applyIncentive = false;
switch (incentivesFields.attributeName()) {
case GENDER:
if (client.genderId() != null) {
applyIncentive = applyIncentive(incentivesFields.conditionType(), Long.valueOf(incentivesFields.attributeValue()),
client.genderId());
}
break;
case AGE:
if (client.dateOfBirth() != null) {
final LocalDate dobLocalDate = client.dateOfBirth();
final int age = Math.toIntExact(ChronoUnit.YEARS.between(dobLocalDate, DateUtils.getBusinessLocalDate()));
applyIncentive = applyIncentive(incentivesFields.conditionType(), Long.valueOf(incentivesFields.attributeValue()),
(long) age);
}
break;
case CLIENT_TYPE:
if (client.clientTypeId() != null) {
applyIncentive = applyIncentive(incentivesFields.conditionType(), Long.valueOf(incentivesFields.attributeValue()),
client.clientTypeId());
}
break;
case CLIENT_CLASSIFICATION:
if (client.clientClassificationId() != null) {
applyIncentive = applyIncentive(incentivesFields.conditionType(), Long.valueOf(incentivesFields.attributeValue()),
client.clientClassificationId());
}
break;
default:
break;
}
if (applyIncentive) {
switch (incentivesFields.incentiveType()) {
case FIXED:
interest = incentivesFields.amount();
break;
case INCENTIVE:
interest = interest.add(incentivesFields.amount());
break;
default:
break;
}
}
return interest;
}
}
@@ -0,0 +1,48 @@
/**
* 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.portfolio.interestratechart.incentive;
import java.math.BigDecimal;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.interestratechart.domain.InterestIncentivesFields;
public class IncentiveDTO {
private final Client client;
private final BigDecimal interest;
private final InterestIncentivesFields incentives;
public IncentiveDTO(final Client client, final BigDecimal interest, final InterestIncentivesFields incentives) {
this.client = client;
this.interest = interest;
this.incentives = incentives;
}
public Client client() {
return this.client;
}
public BigDecimal interest() {
return this.interest;
}
public InterestIncentivesFields incentives() {
return this.incentives;
}
}
@@ -0,0 +1,108 @@
/**
* 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.portfolio.interestratechart.incentive;
import java.util.Arrays;
public enum InterestIncentiveAttributeName {
INVALID(1, "InterestIncentiveAttributeName.invalid"), //
GENDER(2, "InterestIncentiveAttributeName.gender"), //
AGE(3, "InterestIncentiveAttributeName.age"), //
CLIENT_TYPE(4, "InterestIncentiveAttributeName.clientType"), //
CLIENT_CLASSIFICATION(5, "InterestIncentiveAttributeName.clientClassification"); //
private final Integer value;
private final String code;
public static InterestIncentiveAttributeName fromInt(final Integer value) {
switch (value) {
case 2:
return GENDER;
case 3:
return AGE;
case 4:
return CLIENT_TYPE;
case 5:
return CLIENT_CLASSIFICATION;
default:
return INVALID;
}
}
InterestIncentiveAttributeName(final Integer value, final String code) {
this.value = value;
this.code = code;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
// TODO: why not just use the enum values... just more boilerplate code here!!
public boolean isGender() {
return GENDER.equals(this);
}
// TODO: why not just use the enum values... just more boilerplate code here!!
public boolean isAge() {
return AGE.equals(this);
}
// TODO: why not just use the enum values... just more boilerplate code here!!
public boolean isClientType() {
return CLIENT_TYPE.equals(this);
}
// TODO: why not just use the enum values... just more boilerplate code here!!
public boolean isClientClassification() {
return CLIENT_CLASSIFICATION.equals(this);
}
// TODO: why not just use the enum values... just more boilerplate code here!!
public boolean isInvalid() {
return INVALID.equals(this);
}
// TODO: why not just use the enum values... just more boilerplate code here!!
public static boolean isCodeValueAttribute(InterestIncentiveAttributeName attributeName) {
switch (attributeName) {
case GENDER:
case CLIENT_TYPE:
case CLIENT_CLASSIFICATION:
return true;
default:
return false;
}
}
// TODO: do we really need this?!?
public static Object[] integerValues() {
return Arrays.stream(values()).filter(value -> !INVALID.equals(value)).map(value -> value.value).toList().toArray();
}
}
@@ -0,0 +1,77 @@
/**
* 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.portfolio.interestratechart.incentive;
import java.util.Arrays;
public enum InterestIncentiveEntityType {
INVALID(1, "InterestIncentiveEntityType.invalid"), //
CUSTOMER(2, "InterestIncentiveEntityType.customer"), //
ACCOUNT(3, "InterestIncentiveEntityType.account"); //
private final Integer value;
private final String code;
public static InterestIncentiveEntityType fromInt(final Integer v) {
if (v == null) {
return INVALID;
}
switch (v) {
case 2:
return CUSTOMER;
case 3:
return ACCOUNT;
default:
return INVALID;
}
}
InterestIncentiveEntityType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
public boolean isCustomer() {
return CUSTOMER.equals(this);
}
public boolean isInvalid() {
return INVALID.equals(this);
}
// TODO: do we really need this?!?
public static Object[] integerValues() {
return Arrays.stream(values()).filter(value -> !INVALID.equals(value)).map(value -> value.value).toList().toArray();
}
}
@@ -0,0 +1,84 @@
/**
* 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.portfolio.interestratechart.incentive;
import java.util.Arrays;
public enum InterestIncentiveType {
INVALID(1, "InterestIncentiveType.invalid"), //
FIXED(2, "InterestIncentiveType.fixed"), //
INCENTIVE(3, "InterestIncentiveType.incentive"); //
private final Integer value;
private final String code;
public static InterestIncentiveType fromInt(final Integer v) {
if (v == null) {
return INVALID;
}
switch (v) {
case 2:
return FIXED;
case 3:
return INCENTIVE;
default:
return INVALID;
}
}
InterestIncentiveType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
// TODO: why not just use the enum values... just more boilerplate code here!!
public boolean isIncentive() {
return INCENTIVE.equals(this);
}
// TODO: why not just use the enum values... just more boilerplate code here!!
public boolean isFixed() {
return FIXED.equals(this);
}
// TODO: why not just use the enum values... just more boilerplate code here!!
public boolean isInvalid() {
return INVALID.equals(this);
}
// TODO: do we really need this?!?
public static Object[] integerValues() {
return Arrays.stream(values()).filter(value -> !INVALID.equals(value)).map(value -> value.value).toList().toArray();
}
}
@@ -0,0 +1,98 @@
/**
* 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.portfolio.interestratechart.service;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.INCENTIVE_RESOURCE_NAME;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.amountParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.attributeNameParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.attributeValueParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.conditionTypeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.entityTypeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestIncentiveApiConstants.incentiveTypeparamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.incentivesParamName;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.interestratechart.domain.InterestIncentives;
import org.apache.fineract.portfolio.interestratechart.domain.InterestIncentivesFields;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlab;
@RequiredArgsConstructor
public class InterestIncentiveAssembler {
private final FromJsonHelper fromApiJsonHelper;
public Collection<InterestIncentives> assembleIncentivesFrom(final JsonElement element, InterestRateChartSlab interestRateChartSlab,
final Locale locale) {
final Collection<InterestIncentives> interestIncentivesSet = new HashSet<>();
if (element.isJsonObject()) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
if (topLevelJsonElement.has(incentivesParamName) && topLevelJsonElement.get(incentivesParamName).isJsonArray()) {
final JsonArray array = topLevelJsonElement.get(incentivesParamName).getAsJsonArray();
for (int i = 0; i < array.size(); i++) {
final JsonObject incentiveElement = array.get(i).getAsJsonObject();
final InterestIncentives incentives = this.assembleFrom(incentiveElement, interestRateChartSlab, locale);
interestIncentivesSet.add(incentives);
}
}
}
return interestIncentivesSet;
}
private InterestIncentives assembleFrom(final JsonElement element, final InterestRateChartSlab interestRateChartSlab,
final Locale locale) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(INCENTIVE_RESOURCE_NAME);
InterestIncentivesFields incentivesFields = createInterestIncentiveFields(element, baseDataValidator, locale);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
return new InterestIncentives(interestRateChartSlab, incentivesFields);
}
private InterestIncentivesFields createInterestIncentiveFields(final JsonElement element, final DataValidatorBuilder baseDataValidator,
final Locale locale) {
Integer entityType = this.fromApiJsonHelper.extractIntegerNamed(entityTypeParamName, element, locale);
Integer conditionType = this.fromApiJsonHelper.extractIntegerNamed(conditionTypeParamName, element, locale);
Integer attributeName = this.fromApiJsonHelper.extractIntegerNamed(attributeNameParamName, element, locale);
String attributeValue = this.fromApiJsonHelper.extractStringNamed(attributeValueParamName, element);
Integer incentiveType = this.fromApiJsonHelper.extractIntegerNamed(incentiveTypeparamName, element, locale);
BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed(amountParamName, element, locale);
return InterestIncentivesFields.createNew(entityType, attributeName, conditionType, attributeValue, incentiveType, amount,
baseDataValidator);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -0,0 +1,99 @@
/**
* 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.portfolio.interestratechart.service;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.INTERESTRATE_CHART_RESOURCE_NAME;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.endDateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.fromDateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.isPrimaryGroupingByAmountParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.nameParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.currencyCodeParamName;
import com.google.gson.JsonElement;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChart;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartFields;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartRepositoryWrapper;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlab;
@RequiredArgsConstructor
public class InterestRateChartAssembler {
private final FromJsonHelper fromApiJsonHelper;
private final InterestRateChartRepositoryWrapper interestRateChartRepositoryWrapper;
private final InterestRateChartSlabAssembler chartSlabAssembler;
/**
* Assembles a new {@link InterestRateChart} from JSON Slabs passed in request
*/
public InterestRateChart assembleFrom(final JsonCommand command) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(INTERESTRATE_CHART_RESOURCE_NAME);
final JsonElement element = command.parsedJson();
final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element);
final InterestRateChart newChart = this.assembleFrom(element, currencyCode, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
return newChart;
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
public InterestRateChart assembleFrom(final JsonElement element, final String currencyCode,
final DataValidatorBuilder baseDataValidator) {
final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element);
final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element);
final LocalDate fromDate = this.fromApiJsonHelper.extractLocalDateNamed(fromDateParamName, element);
final LocalDate toDate = this.fromApiJsonHelper.extractLocalDateNamed(endDateParamName, element);
Boolean isPrimaryGroupingByAmount = this.fromApiJsonHelper.extractBooleanNamed(isPrimaryGroupingByAmountParamName, element);
if (isPrimaryGroupingByAmount == null) {
isPrimaryGroupingByAmount = false;
}
// assemble chart Slabs
final Collection<InterestRateChartSlab> newChartSlabs = this.chartSlabAssembler.assembleChartSlabsFrom(element, currencyCode);
final InterestRateChartFields fields = InterestRateChartFields.createNew(name, description, fromDate, toDate,
isPrimaryGroupingByAmount);
final InterestRateChart newChart = InterestRateChart.createNew(fields, newChartSlabs);
newChart.validateChartSlabs(baseDataValidator);
return newChart;
}
public InterestRateChart assembleFrom(final Long interestRateChartId) {
final InterestRateChart interestRateChart = this.interestRateChartRepositoryWrapper
.findOneWithNotFoundDetection(interestRateChartId);
return interestRateChart;
}
}
@@ -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.portfolio.interestratechart.service;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.chartSlabs;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.INTERESTRATE_CHART_SLAB_RESOURCE_NAME;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.amountRangeFromParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.amountRangeToParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.annualInterestRateParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.currencyCodeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.fromPeriodParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.periodTypeParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartSlabApiConstants.toPeriodParamName;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChart;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartRepositoryWrapper;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlab;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlabFields;
import org.apache.fineract.portfolio.interestratechart.exception.InterestRateChartSlabNotFoundException;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
@RequiredArgsConstructor
public class InterestRateChartSlabAssembler {
private final FromJsonHelper fromApiJsonHelper;
private final InterestRateChartRepositoryWrapper interestRateChartRepositoryWrapper;
private final InterestIncentiveAssembler incentiveAssembler;
/**
* Assembles a new {@link InterestRateChartSlab} from JSON Slabs passed in request
*/
public InterestRateChartSlab assembleFrom(final JsonCommand command) {
final JsonElement element = command.parsedJson();
final JsonObject elementObject = element.getAsJsonObject();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(elementObject);
final String currencyCode = command.stringValueOfParameterNamed(currencyCodeParamName);
final Long chartId = command.subentityId();// returns chart id
final InterestRateChart chart = this.interestRateChartRepositoryWrapper.findOneWithNotFoundDetection(chartId);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(INTERESTRATE_CHART_SLAB_RESOURCE_NAME);
final InterestRateChartSlab newChartSlab = assembleChartSlabs(chart, elementObject, currencyCode, locale);
// validate chart Slabs
newChartSlab.slabFields().validateChartSlabPlatformRules(command, baseDataValidator, locale);
chart.validateChartSlabs(baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
return newChartSlab;
}
private InterestRateChartSlab assembleChartSlabs(final InterestRateChart interestRateChart, final JsonElement element,
final String currencyCode, final Locale locale) {
final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element);
final Integer periodTypeId = this.fromApiJsonHelper.extractIntegerNamed(periodTypeParamName, element, locale);
final SavingsPeriodFrequencyType periodType = SavingsPeriodFrequencyType.fromInt(periodTypeId);
final Integer fromPeriod = this.fromApiJsonHelper.extractIntegerNamed(fromPeriodParamName, element, locale);
final Integer toPeriod = this.fromApiJsonHelper.extractIntegerNamed(toPeriodParamName, element, locale);
final BigDecimal amountRangeFrom = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeFromParamName, element, locale);
final BigDecimal amountRangeTo = this.fromApiJsonHelper.extractBigDecimalNamed(amountRangeToParamName, element, locale);
final BigDecimal annualInterestRate = this.fromApiJsonHelper.extractBigDecimalNamed(annualInterestRateParamName, element, locale);
final InterestRateChartSlabFields slabFields = InterestRateChartSlabFields.createNew(description, periodType, fromPeriod, toPeriod,
amountRangeFrom, amountRangeTo, annualInterestRate, currencyCode);
InterestRateChartSlab interestRateChartSlab = InterestRateChartSlab.createNew(slabFields, interestRateChart);
this.incentiveAssembler.assembleIncentivesFrom(element, interestRateChartSlab, locale);
return interestRateChartSlab;
}
public InterestRateChartSlab assembleFrom(final Long chartSlabId, final Long chartId) {
final InterestRateChart chart = this.interestRateChartRepositoryWrapper.findOneWithNotFoundDetection(chartId);
final InterestRateChartSlab interestRateChartSlab = chart.findChartSlab(chartSlabId);
if (interestRateChartSlab == null) {
throw new InterestRateChartSlabNotFoundException(chartSlabId, chartId);
}
return interestRateChartSlab;
}
public Collection<InterestRateChartSlab> assembleChartSlabsFrom(final JsonElement element, String currencyCode) {
final Collection<InterestRateChartSlab> interestRateChartSlabsSet = new HashSet<>();
if (element.isJsonObject()) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
if (topLevelJsonElement.has(chartSlabs) && topLevelJsonElement.get(chartSlabs).isJsonArray()) {
final JsonArray array = topLevelJsonElement.get(chartSlabs).getAsJsonArray();
for (int i = 0; i < array.size(); i++) {
final JsonObject interstRateChartElement = array.get(i).getAsJsonObject();
final InterestRateChartSlab chartSlab = this.assembleChartSlabs(null, interstRateChartElement, currencyCode, locale);
interestRateChartSlabsSet.add(chartSlab);
}
}
}
return interestRateChartSlabsSet;
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -0,0 +1,71 @@
/**
* 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.portfolio.savings;
import java.time.LocalDate;
import org.apache.fineract.portfolio.calendar.domain.CalendarFrequencyType;
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class DepositAccountUtils {
private DepositAccountUtils() {
}
private static final Logger LOG = LoggerFactory.getLogger(DepositAccountUtils.class);
public static final int GENERATE_MINIMUM_NUMBER_OF_FUTURE_INSTALMENTS = 5;
public static LocalDate calculateNextDepositDate(final LocalDate lastDepositDate, final PeriodFrequencyType frequency,
final int recurringEvery) {
LocalDate nextDepositDate = lastDepositDate;
switch (frequency) {
case DAYS:
nextDepositDate = lastDepositDate.plusDays(recurringEvery);
break;
case WEEKS:
nextDepositDate = lastDepositDate.plusWeeks(recurringEvery);
break;
case MONTHS:
nextDepositDate = lastDepositDate.plusMonths(recurringEvery);
break;
case YEARS:
nextDepositDate = lastDepositDate.plusYears(recurringEvery);
break;
case INVALID:
break;
case WHOLE_TERM:
LOG.error("TODO Implement calculateNextDepositDate for WHOLE_TERM");
break;
}
return nextDepositDate;
}
public static LocalDate calculateNextDepositDate(final LocalDate lastDepositDate, final String recurrence) {
final PeriodFrequencyType frequencyType = CalendarFrequencyType.from(CalendarUtils.getFrequency(recurrence));
Integer frequency = CalendarUtils.getInterval(recurrence);
frequency = frequency == -1 ? 1 : frequency;
return calculateNextDepositDate(lastDepositDate, frequencyType, frequency);
}
}
@@ -0,0 +1,59 @@
/**
* 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.portfolio.savings;
public class SavingsTransactionBooleanValues {
private final boolean isAccountTransfer;
private final boolean isRegularTransaction;
private final boolean isApplyWithdrawFee;
private final boolean isInterestTransfer;
private final boolean isExceptionForBalanceCheck;
public SavingsTransactionBooleanValues(final boolean isAccountTransfer, final boolean isRegularTransaction,
final boolean isApplyWithdrawFee, final boolean isInterestTransfer, final boolean isExceptionForBalanceCheck) {
this.isAccountTransfer = isAccountTransfer;
this.isRegularTransaction = isRegularTransaction;
this.isApplyWithdrawFee = isApplyWithdrawFee;
this.isInterestTransfer = isInterestTransfer;
this.isExceptionForBalanceCheck = isExceptionForBalanceCheck;
}
public boolean isAccountTransfer() {
return this.isAccountTransfer;
}
public boolean isRegularTransaction() {
return this.isRegularTransaction;
}
public boolean isApplyWithdrawFee() {
return this.isApplyWithdrawFee;
}
public boolean isInterestTransfer() {
return this.isInterestTransfer;
}
public boolean isExceptionForBalanceCheck() {
return this.isExceptionForBalanceCheck;
}
}
@@ -0,0 +1,74 @@
/**
* 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.portfolio.savings.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
public class SavingsApiSetConstants extends SavingsApiConstants {
/**
* These parameters will match the class level parameters of {@link SavingsProductData}. Where possible, we try to
* get response parameters to match those of request parameters.
*/
protected static final Set<String> SAVINGS_PRODUCT_RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(idParamName, nameParamName, shortNameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName,
inMultiplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName,
interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, feeAmountParamName,
feeOnMonthDayParamName, "currencyOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions",
"interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions",
"withdrawalFeeTypeOptions", nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
withHoldTaxParamName, taxGroupIdParamName, isDormancyTrackingActiveParamName, daysToInactiveParamName,
daysToDormancyParamName, daysToInactiveParamName, accountMappingForPaymentParamName));
/**
* These parameters will match the class level parameters of {@link SavingsAccountData}. Where possible, we try to
* get response parameters to match those of request parameters.
*/
protected static final Set<String> SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName,
accountNoParamName, externalIdParamName, statusParamName, activatedOnDateParamName, staffIdParamName, clientIdParamName,
"clientName", groupIdParamName, "groupName", "savingsProductId", "savingsProductName", "currency",
nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestCalculationTypeParamName,
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName,
withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, "summary", "transactions", "productOptions",
"interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions",
"interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions", "withdrawalFee",
"annualFee", onHoldFundsParamName, nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
datatables, savingsAmountOnHold, accountMappingForPaymentParamName, interestPostedTillDate));
protected static final Set<String> SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(idParamName, "accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName,
runningBalanceParamName, reversedParamName, noteParamName));
protected static final Set<String> SAVINGS_ACCOUNT_CHARGES_RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(chargeIdParamName, savingsAccountChargeIdParamName, chargeNameParamName, penaltyParamName,
chargeTimeTypeParamName, dueAsOfDateParamName, chargeCalculationTypeParamName, percentageParamName,
amountPercentageAppliedToParamName, currencyParamName, amountWaivedParamName, amountWrittenOffParamName,
amountOutstandingParamName, amountOrPercentageParamName, amountParamName, amountPaidParamName, chargeOptionsParamName));
protected static final Set<String> SAVINGS_ACCOUNT_ON_HOLD_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName,
amountParamName, onHoldTransactionTypeParamName, onHoldTransactionDateParamName, onHoldReversedParamName));
}
@@ -0,0 +1,97 @@
/**
* 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.portfolio.savings.data;
import java.time.LocalDate;
public final class ClosingOfSavingsAccounts {
private final transient Integer rowIndex;
private final transient Long accountId;
private final LocalDate closedOnDate;
private final Long onAccountClosureId;
private final Long toSavingsAccountId;
private final String dateFormat;
private final String accountType;
private final String locale;
private final String note;
public static ClosingOfSavingsAccounts importInstance(Long accountId, LocalDate closedOnDate, Long onAccountClosureId,
Long toSavingsAccountId, String accountType, Integer rowIndex, String locale, String dateFormat) {
return new ClosingOfSavingsAccounts(accountId, closedOnDate, onAccountClosureId, toSavingsAccountId, accountType, rowIndex, locale,
dateFormat);
}
private ClosingOfSavingsAccounts(Long accountId, LocalDate closedOnDate, Long onAccountClosureId, Long toSavingsAccountId,
String accountType, Integer rowIndex, String locale, String dateFormat) {
this.accountId = accountId;
this.closedOnDate = closedOnDate;
this.onAccountClosureId = onAccountClosureId;
this.toSavingsAccountId = toSavingsAccountId;
this.accountType = accountType;
this.rowIndex = rowIndex;
this.dateFormat = dateFormat;
this.locale = locale;
this.note = "";
}
public Integer getRowIndex() {
return rowIndex;
}
public Long getAccountId() {
return accountId;
}
public LocalDate getClosedOnDate() {
return closedOnDate;
}
public Long getOnAccountClosureId() {
return onAccountClosureId;
}
public Long getToSavingsAccountId() {
return toSavingsAccountId;
}
public String getDateFormat() {
return dateFormat;
}
public String getAccountType() {
return accountType;
}
public String getLocale() {
return locale;
}
public String getNote() {
return note;
}
}
@@ -0,0 +1,338 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashSet;
import lombok.Getter;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
/**
* Immutable data object representing abstract for Fixed and Recurring Deposit Accounts Accounts.
*/
@Getter
public class DepositAccountData {
protected final Long id;
protected final String accountNo;
protected final String externalId;
protected final Long groupId;
protected final String groupName;
protected final Long clientId;
protected final String clientName;
protected final Long depositProductId;
protected final String depositProductName;
protected final Long fieldOfficerId;
protected final String fieldOfficerName;
protected final SavingsAccountStatusEnumData status;
protected final SavingsAccountApplicationTimelineData timeline;
protected final CurrencyData currency;
protected final BigDecimal nominalAnnualInterestRate;
protected final EnumOptionData interestCompoundingPeriodType;
protected final EnumOptionData interestPostingPeriodType;
protected final EnumOptionData interestCalculationType;
protected final EnumOptionData interestCalculationDaysInYearType;
protected final BigDecimal minRequiredOpeningBalance;
protected final Integer lockinPeriodFrequency;
protected final EnumOptionData lockinPeriodFrequencyType;
protected final boolean withdrawalFeeForTransfers;
protected final EnumOptionData depositType;
protected final BigDecimal minBalanceForInterestCalculation;
protected final boolean withHoldTax;
protected final TaxGroupData taxGroup;
// associations
protected final SavingsAccountSummaryData summary;
protected final Collection<SavingsAccountTransactionData> transactions;
protected final Collection<SavingsAccountChargeData> charges;
protected final DepositAccountInterestRateChartData accountChart;
// template
protected final Collection<DepositProductData> productOptions;
protected final Collection<StaffData> fieldOfficerOptions;
protected final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions;
protected final Collection<EnumOptionData> interestPostingPeriodTypeOptions;
protected final Collection<EnumOptionData> interestCalculationTypeOptions;
protected final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions;
protected final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions;
protected final Collection<EnumOptionData> withdrawalFeeTypeOptions;
protected final Collection<ChargeData> chargeOptions;
protected final SavingsAccountChargeData withdrawalFee;
protected final SavingsAccountChargeData annualFee;
protected final DepositAccountInterestRateChartData chartTemplate;
// import fields
private Long productId;
public DepositAccountData(Long clientId, Long productId, Long fieldOfficerId, EnumOptionData interestCompoundingPeriodType,
EnumOptionData interestPostingPeriodType, EnumOptionData interestCalculationType,
EnumOptionData interestCalculationDaysInYearType, Integer lockinPeriodFrequency, EnumOptionData lockinPeriodFrequencyType,
String externalId, Collection<SavingsAccountChargeData> charges) {
this.id = null;
this.accountNo = null;
this.externalId = externalId;
this.groupId = null;
this.groupName = null;
this.clientId = clientId;
this.clientName = null;
this.depositProductId = null;
this.depositProductName = null;
this.fieldOfficerId = fieldOfficerId;
this.fieldOfficerName = null;
this.status = null;
this.timeline = null;
this.currency = null;
this.nominalAnnualInterestRate = null;
this.interestCompoundingPeriodType = interestCompoundingPeriodType;
this.interestPostingPeriodType = interestPostingPeriodType;
this.interestCalculationType = interestCalculationType;
this.interestCalculationDaysInYearType = interestCalculationDaysInYearType;
this.minRequiredOpeningBalance = null;
this.lockinPeriodFrequency = lockinPeriodFrequency;
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType;
this.withdrawalFeeForTransfers = false;
this.depositType = null;
this.minBalanceForInterestCalculation = null;
this.withHoldTax = false;
this.taxGroup = null;
this.summary = null;
this.transactions = null;
this.charges = charges;
this.accountChart = null;
this.productOptions = null;
this.fieldOfficerOptions = null;
this.interestCompoundingPeriodTypeOptions = null;
this.interestPostingPeriodTypeOptions = null;
this.interestCalculationTypeOptions = null;
this.interestCalculationDaysInYearTypeOptions = null;
this.lockinPeriodFrequencyTypeOptions = null;
this.withdrawalFeeTypeOptions = null;
this.chargeOptions = null;
this.withdrawalFee = null;
this.annualFee = null;
this.chartTemplate = null;
this.productId = productId;
}
public static DepositAccountData instance(final Long id, final String accountNo, final String externalId, final Long groupId,
final String groupName, final Long clientId, final String clientName, final Long productId, final String productName,
final Long fieldOfficerId, final String fieldOfficerName, final SavingsAccountStatusEnumData status,
final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal interestRate,
final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType,
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType,
final boolean withdrawalFeeForTransfers, final SavingsAccountSummaryData summary, final EnumOptionData depositType,
final BigDecimal minBalanceForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup) {
final Collection<DepositProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<SavingsAccountChargeData> charges = null;
final Collection<ChargeData> chargeOptions = null;
final DepositAccountInterestRateChartData accountChart = null;
final DepositAccountInterestRateChartData chartTemplate = null;
return new DepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, status, timeline, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions,
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges,
chargeOptions, accountChart, chartTemplate, depositType, minBalanceForInterestCalculation, withHoldTax, taxGroup);
}
public static DepositAccountData lookup(final Long id, final String accountNo, final EnumOptionData depositType) {
final String externalId = null;
final Long groupId = null;
final String groupName = null;
final Long clientId = null;
final String clientName = null;
final Long productId = null;
final String productName = null;
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null;
final SavingsAccountApplicationTimelineData timeline = null;
final CurrencyData currency = null;
final BigDecimal interestRate = null;
final EnumOptionData interestCompoundingPeriodType = null;
final EnumOptionData interestPostingPeriodType = null;
final EnumOptionData interestCalculationType = null;
final EnumOptionData interestCalculationDaysInYearType = null;
final BigDecimal minRequiredOpeningBalance = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final boolean withdrawalFeeForTransfers = false;
final SavingsAccountSummaryData summary = null;
final Collection<DepositProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<SavingsAccountChargeData> charges = null;
final Collection<ChargeData> chargeOptions = null;
final DepositAccountInterestRateChartData accountChart = null;
final DepositAccountInterestRateChartData chartTemplate = null;
final BigDecimal minBalanceForInterestCalculation = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
return new DepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, status, timeline, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions,
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges,
chargeOptions, accountChart, chartTemplate, depositType, minBalanceForInterestCalculation, withHoldTax, taxGroup);
}
protected DepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, final String groupName,
final Long clientId, final String clientName, final Long productId, final String productName, final Long fieldOfficerId,
final String fieldOfficerName, final SavingsAccountStatusEnumData status, final SavingsAccountApplicationTimelineData timeline,
final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers,
final SavingsAccountSummaryData summary, final Collection<SavingsAccountTransactionData> transactions,
final Collection<DepositProductData> productOptions, final Collection<StaffData> fieldOfficerOptions,
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions,
final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate,
final EnumOptionData depositType, final BigDecimal minBalanceForInterestCalculation, final boolean withHoldTax,
final TaxGroupData taxGroup) {
this.id = id;
this.accountNo = accountNo;
this.externalId = externalId;
this.groupId = groupId;
this.groupName = groupName;
this.clientId = clientId;
this.clientName = clientName;
this.depositProductId = productId;
this.depositProductName = productName;
this.fieldOfficerId = fieldOfficerId;
this.fieldOfficerName = fieldOfficerName;
this.status = status;
this.timeline = timeline;
this.currency = currency;
this.nominalAnnualInterestRate = nominalAnnualInterestRate;
this.interestCompoundingPeriodType = interestPeriodType;
this.interestPostingPeriodType = interestPostingPeriodType;
this.interestCalculationType = interestCalculationType;
this.interestCalculationDaysInYearType = interestCalculationDaysInYearType;
this.minRequiredOpeningBalance = minRequiredOpeningBalance;
this.lockinPeriodFrequency = lockinPeriodFrequency;
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType;
this.withdrawalFeeForTransfers = withdrawalFeeForTransfers;
this.summary = summary;
this.transactions = transactions;
this.productOptions = productOptions;
this.fieldOfficerOptions = fieldOfficerOptions;
this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions;
this.interestPostingPeriodTypeOptions = interestPostingPeriodTypeOptions;
this.interestCalculationTypeOptions = interestCalculationTypeOptions;
this.interestCalculationDaysInYearTypeOptions = interestCalculationDaysInYearTypeOptions;
this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions;
this.withdrawalFeeTypeOptions = withdrawalFeeTypeOptions;
this.charges = charges;// charges associated with Savings account
// charges available for adding to Savings account
this.chargeOptions = chargeOptions;
this.withdrawalFee = getWithdrawalFee();
this.annualFee = getAnnualFee();
this.accountChart = accountChart;
this.chartTemplate = chartTemplate;
this.depositType = depositType;
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.taxGroup = taxGroup;
this.withHoldTax = withHoldTax;
}
private SavingsAccountChargeData getWithdrawalFee() {
for (SavingsAccountChargeData charge : this.charges()) {
if (charge.isWithdrawalFee()) {
return charge;
}
}
return null;
}
private SavingsAccountChargeData getAnnualFee() {
for (SavingsAccountChargeData charge : this.charges()) {
if (charge.isAnnualFee()) {
return charge;
}
}
return null;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (!(obj instanceof DepositAccountData)) {
return false;
}
final DepositAccountData rhs = (DepositAccountData) obj;
return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).toHashCode();
}
public Collection<SavingsAccountChargeData> charges() {
return (this.charges == null) ? new HashSet<SavingsAccountChargeData>() : this.charges;
}
}
@@ -0,0 +1,74 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.portfolio.interestratechart.data.InterestIncentiveData;
public final class DepositAccountInterestIncentiveData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final EnumOptionData entityType;
@SuppressWarnings("unused")
private final EnumOptionData attributeName;
@SuppressWarnings("unused")
private final EnumOptionData conditionType;
@SuppressWarnings("unused")
private final String attributeValue;
@SuppressWarnings("unused")
private final String attributeValueDesc;
@SuppressWarnings("unused")
private final EnumOptionData incentiveType;
@SuppressWarnings("unused")
private final BigDecimal amount;
public static DepositAccountInterestIncentiveData instance(final Long id, final EnumOptionData entityType,
final EnumOptionData attributeName, final EnumOptionData conditionType, final String attributeValue,
final String attributeValueDesc, final EnumOptionData incentiveType, final BigDecimal amount) {
return new DepositAccountInterestIncentiveData(id, entityType, attributeName, conditionType, attributeValue, attributeValueDesc,
incentiveType, amount);
}
public static DepositAccountInterestIncentiveData from(final InterestIncentiveData incentiveData) {
final Long id = null;
return new DepositAccountInterestIncentiveData(id, incentiveData.entityType(), incentiveData.attributeName(),
incentiveData.conditionType(), incentiveData.attributeValue(), incentiveData.attributeValueDesc(),
incentiveData.incentiveType(), incentiveData.amount());
}
private DepositAccountInterestIncentiveData(final Long id, final EnumOptionData entityType, final EnumOptionData attributeName,
final EnumOptionData conditionType, final String attributeValue, final String attributeValueDesc,
final EnumOptionData incentiveType, final BigDecimal amount) {
this.id = id;
this.entityType = entityType;
this.attributeName = attributeName;
this.conditionType = conditionType;
this.attributeValue = attributeValue;
this.attributeValueDesc = attributeValueDesc;
this.incentiveType = incentiveType;
this.amount = amount;
}
}
@@ -0,0 +1,177 @@
/**
* 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.portfolio.savings.data;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartData;
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartSlabData;
/**
* Immutable data object representing a deposit account interest rate chart.
*/
public final class DepositAccountInterestRateChartData {
private final Long id;
private final String name;
private final String description;
private final LocalDate fromDate;
private final LocalDate endDate;
private final boolean isPrimaryGroupingByAmount;
private final Long accountId;
private final String accountNumber;
// associations
private Collection<DepositAccountInterestRateChartSlabData> chartSlabs;
// template
private final Collection<EnumOptionData> periodTypes;
private final Collection<EnumOptionData> entityTypeOptions;
private final Collection<EnumOptionData> attributeNameOptions;
private final Collection<EnumOptionData> conditionTypeOptions;
private final Collection<EnumOptionData> incentiveTypeOptions;
private final Collection<CodeValueData> genderOptions;
private final Collection<CodeValueData> clientTypeOptions;
private final Collection<CodeValueData> clientClassificationOptions;
public static DepositAccountInterestRateChartData instance(Long id, String name, String description, LocalDate fromDate,
LocalDate endDate, boolean isPrimaryGroupingByAmount, Long accountId, String accountNumber,
Collection<DepositAccountInterestRateChartSlabData> chartSlabs, Collection<EnumOptionData> periodTypes) {
final Collection<EnumOptionData> entityTypeOptions = null;
final Collection<EnumOptionData> attributeNameOptions = null;
final Collection<EnumOptionData> conditionTypeOptions = null;
final Collection<EnumOptionData> incentiveTypeOptions = null;
final Collection<CodeValueData> genderOptions = null;
final Collection<CodeValueData> clientTypeOptions = null;
final Collection<CodeValueData> clientClassificationOptions = null;
return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, accountId,
accountNumber, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions,
genderOptions, clientTypeOptions, clientClassificationOptions);
}
public static DepositAccountInterestRateChartData from(InterestRateChartData productChartData) {
final Long id = null;
final Long accountId = null;
final String accountNumber = null;
Collection<DepositAccountInterestRateChartSlabData> fromProdChartSlabs = new ArrayList<>();
Collection<InterestRateChartSlabData> productChartSlabDatas = productChartData.chartSlabs();
if (productChartSlabDatas != null) {
for (InterestRateChartSlabData productChartSlabData : productChartSlabDatas) {
fromProdChartSlabs.add(DepositAccountInterestRateChartSlabData.from(productChartSlabData));
}
}
return new DepositAccountInterestRateChartData(id, productChartData.name(), productChartData.description(),
productChartData.fromDate(), productChartData.endDate(), productChartData.isPrimaryGroupingByAmount(), accountId,
accountNumber, fromProdChartSlabs, productChartData.periodTypes(), productChartData.entityTypeOptions(),
productChartData.attributeNameOptions(), productChartData.conditionTypeOptions(), productChartData.incentiveTypeOptions(),
productChartData.genderOptions(), productChartData.clientTypeOptions(), productChartData.clientClassificationOptions());
}
public static DepositAccountInterestRateChartData withSlabs(DepositAccountInterestRateChartData interestRateChartData,
Collection<DepositAccountInterestRateChartSlabData> chartSlabs) {
return new DepositAccountInterestRateChartData(interestRateChartData.id, interestRateChartData.name,
interestRateChartData.description, interestRateChartData.fromDate, interestRateChartData.endDate,
interestRateChartData.isPrimaryGroupingByAmount, interestRateChartData.accountId, interestRateChartData.accountNumber,
chartSlabs, interestRateChartData.periodTypes, interestRateChartData.entityTypeOptions,
interestRateChartData.attributeNameOptions, interestRateChartData.conditionTypeOptions,
interestRateChartData.incentiveTypeOptions, interestRateChartData.genderOptions, interestRateChartData.clientTypeOptions,
interestRateChartData.clientClassificationOptions);
}
public static DepositAccountInterestRateChartData withTemplate(DepositAccountInterestRateChartData interestRateChartData,
Collection<EnumOptionData> periodTypes, final Collection<EnumOptionData> entityTypeOptions,
final Collection<EnumOptionData> attributeNameOptions, final Collection<EnumOptionData> conditionTypeOptions,
final Collection<EnumOptionData> incentiveTypeOptions, final Collection<CodeValueData> genderOptions,
final Collection<CodeValueData> clientTypeOptions, final Collection<CodeValueData> clientClassificationOptions) {
return new DepositAccountInterestRateChartData(interestRateChartData.id, interestRateChartData.name,
interestRateChartData.description, interestRateChartData.fromDate, interestRateChartData.endDate,
interestRateChartData.isPrimaryGroupingByAmount, interestRateChartData.accountId, interestRateChartData.accountNumber,
interestRateChartData.chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions,
incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
public static DepositAccountInterestRateChartData template(Collection<EnumOptionData> periodTypes,
final Collection<EnumOptionData> entityTypeOptions, final Collection<EnumOptionData> attributeNameOptions,
final Collection<EnumOptionData> conditionTypeOptions, final Collection<EnumOptionData> incentiveTypeOptions,
final Collection<CodeValueData> genderOptions, final Collection<CodeValueData> clientTypeOptions,
final Collection<CodeValueData> clientClassificationOptions) {
final Long id = null;
final String name = null;
final String description = null;
final LocalDate fromDate = null;
final LocalDate endDate = null;
final Long accountId = null;
final String accountNumber = null;
final boolean isPrimaryGroupingByAmount = false;
final Collection<DepositAccountInterestRateChartSlabData> chartSlabs = null;
return new DepositAccountInterestRateChartData(id, name, description, fromDate, endDate, isPrimaryGroupingByAmount, accountId,
accountNumber, chartSlabs, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions,
genderOptions, clientTypeOptions, clientClassificationOptions);
}
private DepositAccountInterestRateChartData(Long id, String name, String description, LocalDate fromDate, LocalDate endDate,
final boolean isPrimaryGroupingByAmount, Long accountId, String accountNumber,
Collection<DepositAccountInterestRateChartSlabData> chartSlabs, Collection<EnumOptionData> periodTypes,
final Collection<EnumOptionData> entityTypeOptions, final Collection<EnumOptionData> attributeNameOptions,
final Collection<EnumOptionData> conditionTypeOptions, final Collection<EnumOptionData> incentiveTypeOptions,
final Collection<CodeValueData> genderOptions, final Collection<CodeValueData> clientTypeOptions,
final Collection<CodeValueData> clientClassificationOptions) {
this.id = id;
this.name = name;
this.description = description;
this.fromDate = fromDate;
this.endDate = endDate;
this.isPrimaryGroupingByAmount = isPrimaryGroupingByAmount;
this.chartSlabs = chartSlabs;
this.accountId = accountId;
this.accountNumber = accountNumber;
this.periodTypes = periodTypes;
this.attributeNameOptions = attributeNameOptions;
this.entityTypeOptions = entityTypeOptions;
this.conditionTypeOptions = conditionTypeOptions;
this.incentiveTypeOptions = incentiveTypeOptions;
this.genderOptions = genderOptions;
this.clientTypeOptions = clientTypeOptions;
this.clientClassificationOptions = clientClassificationOptions;
}
public void addChartSlab(final DepositAccountInterestRateChartSlabData chartSlab) {
if (this.chartSlabs == null) {
this.chartSlabs = new ArrayList<>();
}
this.chartSlabs.add(chartSlab);
}
public boolean isFromDateAfter(final LocalDate compareDate) {
return compareDate != null && DateUtils.isAfter(this.fromDate, compareDate);
}
public LocalDate endDate() {
return this.endDate;
}
public LocalDate fromDate() {
return this.fromDate;
}
}
@@ -0,0 +1,146 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.interestratechart.data.InterestIncentiveData;
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartSlabData;
/**
* Immutable data object representing deposit accounts Interest rate Slabs.
*/
public final class DepositAccountInterestRateChartSlabData {
private final Long id;
private final String description;
private final EnumOptionData periodType;
private final Integer fromPeriod;
private final Integer toPeriod;
private final BigDecimal amountRangeFrom;
private final BigDecimal amountRangeTo;
private final BigDecimal annualInterestRate;
private final CurrencyData currency;
// associations
private Collection<DepositAccountInterestIncentiveData> incentives;
// template
@SuppressWarnings("unused")
private final Collection<EnumOptionData> periodTypes;
@SuppressWarnings("unused")
private final Collection<EnumOptionData> entityTypeOptions;
@SuppressWarnings("unused")
private final Collection<EnumOptionData> attributeNameOptions;
@SuppressWarnings("unused")
private final Collection<EnumOptionData> conditionTypeOptions;
@SuppressWarnings("unused")
private final Collection<EnumOptionData> incentiveTypeOptions;
@SuppressWarnings("unused")
private final Collection<CodeValueData> genderOptions;
@SuppressWarnings("unused")
private final Collection<CodeValueData> clientTypeOptions;
@SuppressWarnings("unused")
private final Collection<CodeValueData> clientClassificationOptions;
public static DepositAccountInterestRateChartSlabData instance(final Long id, final String description, final EnumOptionData periodType,
final Integer fromPeriod, final Integer toPeriod, final BigDecimal amountRangeFrom, final BigDecimal amountRangeTo,
final BigDecimal annualInterestRate, final CurrencyData currency) {
final Collection<EnumOptionData> periodTypes = null;
final Collection<EnumOptionData> entityTypeOptions = null;
final Collection<EnumOptionData> attributeNameOptions = null;
final Collection<EnumOptionData> conditionTypeOptions = null;
final Collection<EnumOptionData> incentiveTypeOptions = null;
final Collection<CodeValueData> genderOptions = null;
final Collection<CodeValueData> clientTypeOptions = null;
final Collection<CodeValueData> clientClassificationOptions = null;
final Collection<DepositAccountInterestIncentiveData> incentives = null;
return new DepositAccountInterestRateChartSlabData(id, description, periodType, fromPeriod, toPeriod, amountRangeFrom,
amountRangeTo, annualInterestRate, incentives, currency, periodTypes, entityTypeOptions, attributeNameOptions,
conditionTypeOptions, incentiveTypeOptions, genderOptions, clientTypeOptions, clientClassificationOptions);
}
public static DepositAccountInterestRateChartSlabData from(final InterestRateChartSlabData chartSlabData) {
final Long id = null;
Set<DepositAccountInterestIncentiveData> fromProdIncentives = new HashSet<>();
Set<InterestIncentiveData> productIncentiveData = chartSlabData.incentives();
if (productIncentiveData != null) {
for (InterestIncentiveData incentive : productIncentiveData) {
fromProdIncentives.add(DepositAccountInterestIncentiveData.from(incentive));
}
}
return new DepositAccountInterestRateChartSlabData(id, chartSlabData.description(), chartSlabData.periodType(),
chartSlabData.fromPeriod(), chartSlabData.toPeriod(), chartSlabData.amountRangeFrom(), chartSlabData.amountRangeTo(),
chartSlabData.annualInterestRate(), fromProdIncentives, chartSlabData.currency(), chartSlabData.periodTypes(),
chartSlabData.entityTypeOptions(), chartSlabData.attributeNameOptions(), chartSlabData.conditionTypeOptions(),
chartSlabData.incentiveTypeOptions(), chartSlabData.genderOptions(), chartSlabData.clientTypeOptions(),
chartSlabData.clientClassificationOptions());
}
public static DepositAccountInterestRateChartSlabData withTemplate(final DepositAccountInterestRateChartSlabData chartSlab,
final Collection<EnumOptionData> periodTypes, final Collection<EnumOptionData> entityTypeOptions,
final Collection<EnumOptionData> attributeNameOptions, final Collection<EnumOptionData> conditionTypeOptions,
final Collection<EnumOptionData> incentiveTypeOptions, final Collection<CodeValueData> genderOptions,
final Collection<CodeValueData> clientTypeOptions, final Collection<CodeValueData> clientClassificationOptions) {
return new DepositAccountInterestRateChartSlabData(chartSlab.id, chartSlab.description, chartSlab.periodType, chartSlab.fromPeriod,
chartSlab.toPeriod, chartSlab.amountRangeFrom, chartSlab.amountRangeTo, chartSlab.annualInterestRate, chartSlab.incentives,
chartSlab.currency, periodTypes, entityTypeOptions, attributeNameOptions, conditionTypeOptions, incentiveTypeOptions,
genderOptions, clientTypeOptions, clientClassificationOptions);
}
private DepositAccountInterestRateChartSlabData(final Long id, final String description, final EnumOptionData periodType,
final Integer fromPeriod, final Integer toPeriod, final BigDecimal amountRangeFrom, final BigDecimal amountRangeTo,
final BigDecimal annualInterestRate, final Collection<DepositAccountInterestIncentiveData> incentivesData,
final CurrencyData currency, final Collection<EnumOptionData> periodTypes, final Collection<EnumOptionData> entityTypeOptions,
final Collection<EnumOptionData> attributeNameOptions, final Collection<EnumOptionData> conditionTypeOptions,
final Collection<EnumOptionData> incentiveTypeOptions, final Collection<CodeValueData> genderOptions,
final Collection<CodeValueData> clientTypeOptions, final Collection<CodeValueData> clientClassificationOptions) {
this.id = id;
this.description = description;
this.periodType = periodType;
this.fromPeriod = fromPeriod;
this.toPeriod = toPeriod;
this.amountRangeFrom = amountRangeFrom;
this.amountRangeTo = amountRangeTo;
this.annualInterestRate = annualInterestRate;
this.currency = currency;
this.periodTypes = periodTypes;
this.incentives = incentivesData;
this.attributeNameOptions = attributeNameOptions;
this.entityTypeOptions = entityTypeOptions;
this.conditionTypeOptions = conditionTypeOptions;
this.incentiveTypeOptions = incentiveTypeOptions;
this.genderOptions = genderOptions;
this.clientTypeOptions = clientTypeOptions;
this.clientClassificationOptions = clientClassificationOptions;
}
public void addIncentives(final DepositAccountInterestIncentiveData incentive) {
if (this.incentives == null) {
this.incentives = new HashSet<>();
}
this.incentives.add(incentive);
}
}
@@ -0,0 +1,92 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
public final class DepositAccountOnHoldTransactionData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final BigDecimal amount;
@SuppressWarnings("unused")
private EnumOptionData transactionType;
@SuppressWarnings("unused")
private final LocalDate transactionDate;
@SuppressWarnings("unused")
private final boolean reversed;
@SuppressWarnings("unused")
private final Long savingsId;
@SuppressWarnings("unused")
private final String savingsAccountNo;
@SuppressWarnings("unused")
private final String savingsClientName;
@SuppressWarnings("unused")
private final Long loanId;
@SuppressWarnings("unused")
private final String loanAccountNo;
@SuppressWarnings("unused")
private final String loanClientName;
private DepositAccountOnHoldTransactionData(final Long id, final BigDecimal amount, final EnumOptionData transactionType,
final LocalDate transactionDate, final boolean reversed, final Long savingsId, final String savingsAccNo,
final String savingsClientName, final Long loanId, final String loanAccNo, final String loanClientName) {
this.id = id;
this.amount = amount;
this.transactionType = transactionType;
this.transactionDate = transactionDate;
this.reversed = reversed;
this.savingsId = savingsId;
this.savingsAccountNo = savingsAccNo;
this.savingsClientName = savingsClientName;
this.loanId = loanId;
this.loanAccountNo = loanAccNo;
this.loanClientName = loanClientName;
}
private DepositAccountOnHoldTransactionData(final Long id, final BigDecimal amount, final EnumOptionData transactionType,
final LocalDate transactionDate, final boolean reversed) {
this.id = id;
this.amount = amount;
this.transactionType = transactionType;
this.transactionDate = transactionDate;
this.reversed = reversed;
this.savingsAccountNo = null;
this.savingsId = 0L;
this.savingsClientName = null;
this.loanId = 0L;
this.loanAccountNo = null;
this.loanClientName = null;
}
public static DepositAccountOnHoldTransactionData instance(final Long id, final BigDecimal amount, final EnumOptionData transactionType,
final LocalDate transactionDate, final boolean reversed, final Long savingsId, final String savingsAccountNo,
final String savingsClientName, final Long loanId, final String loanAccountNo, final String loanClientName) {
return new DepositAccountOnHoldTransactionData(id, amount, transactionType, transactionDate, reversed, savingsId, savingsAccountNo,
savingsClientName, loanId, loanAccountNo, loanClientName);
}
public static DepositAccountOnHoldTransactionData instance(Long transactionId, BigDecimal amount, EnumOptionData transactionType,
LocalDate date, boolean transactionReversed) {
return new DepositAccountOnHoldTransactionData(transactionId, amount, transactionType, date, transactionReversed);
}
}
@@ -0,0 +1,456 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartData;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
/**
* Immutable data object represents a partial data of Deposit Proucts.
*/
public class DepositProductData {
protected final Long id;
protected final String name;
protected final String shortName;
protected final String description;
protected final CurrencyData currency;
protected final BigDecimal nominalAnnualInterestRate;
protected final EnumOptionData interestCompoundingPeriodType;
protected final EnumOptionData interestPostingPeriodType;
protected final EnumOptionData interestCalculationType;
protected final EnumOptionData interestCalculationDaysInYearType;
// protected final BigDecimal minRequiredOpeningBalance;
protected final Integer lockinPeriodFrequency;
protected final EnumOptionData lockinPeriodFrequencyType;
// protected final boolean withdrawalFeeForTransfers;
protected final BigDecimal minBalanceForInterestCalculation;
protected final boolean withHoldTax;
protected final TaxGroupData taxGroup;
// accounting
protected final EnumOptionData accountingRule;
protected final Map<String, Object> accountingMappings;
protected final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings;
protected final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings;
protected final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
// charges
protected final Collection<ChargeData> charges;
// interest rate charts
protected final Collection<InterestRateChartData> interestRateCharts;
protected final InterestRateChartData activeChart;
// template
protected final Collection<CurrencyData> currencyOptions;
protected final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions;
protected final Collection<EnumOptionData> interestPostingPeriodTypeOptions;
protected final Collection<EnumOptionData> interestCalculationTypeOptions;
protected final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions;
protected final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions;
protected final Collection<EnumOptionData> withdrawalFeeTypeOptions;
protected final Collection<PaymentTypeData> paymentTypeOptions;
protected final Collection<EnumOptionData> accountingRuleOptions;
protected final Map<String, List<GLAccountData>> accountingMappingOptions;
protected final Collection<ChargeData> chargeOptions;
protected final Collection<ChargeData> penaltyOptions;
protected final InterestRateChartData chartTemplate;
protected final Collection<TaxGroupData> taxGroupOptions;
public static DepositProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData accountingRule,
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
final Collection<TaxGroupData> taxGroupOptions) {
final Long id = null;
final String name = null;
final String shortName = null;
final String description = null;
final BigDecimal nominalAnnualInterestRate = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal minBalanceForInterestCalculation = null;
final Map<String, Object> accountingMappings = null;
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
final Collection<ChargeData> charges = null;
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
final Collection<InterestRateChartData> interestRateCharts = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
return new DepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
lockinPeriodFrequencyType, accountingRule, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation, withHoldTax, taxGroup,
taxGroupOptions);
}
public static DepositProductData withCharges(final DepositProductData existingProduct, final Collection<ChargeData> charges) {
return new DepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType,
existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency,
existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, existingProduct.accountingMappings,
existingProduct.paymentChannelToFundSourceMappings, existingProduct.currencyOptions,
existingProduct.interestCompoundingPeriodTypeOptions, existingProduct.interestPostingPeriodTypeOptions,
existingProduct.interestCalculationTypeOptions, existingProduct.interestCalculationDaysInYearTypeOptions,
existingProduct.lockinPeriodFrequencyTypeOptions, existingProduct.withdrawalFeeTypeOptions,
existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions,
charges, existingProduct.chargeOptions, existingProduct.penaltyOptions, existingProduct.feeToIncomeAccountMappings,
existingProduct.penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate,
existingProduct.minBalanceForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup,
existingProduct.taxGroupOptions);
}
/**
* Returns a {@link DepositProductData} that contains and exist {@link DepositProductData} data with further
* template data for dropdowns.
*
* @param taxGroupOptions
* TODO
*/
public static DepositProductData withTemplate(final DepositProductData existingProduct, final Collection<CurrencyData> currencyOptions,
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
Collection<TaxGroupData> taxGroupOptions) {
return new DepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType,
existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency,
existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, existingProduct.accountingMappings,
existingProduct.paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions,
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings,
existingProduct.interestRateCharts, chartTemplate, existingProduct.minBalanceForInterestCalculation,
existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions);
}
public static DepositProductData withAccountingDetails(final DepositProductData existingProduct,
final Map<String, Object> accountingMappings, final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings,
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings,
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings) {
final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<EnumOptionData> accountingRuleOptions = null;
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<ChargeData> penaltyOptions = null;
final Collection<TaxGroupData> taxGroupOptions = null;
return new DepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType,
existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency,
existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, accountingMappings,
paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, existingProduct.charges,
chargeOptions, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings,
existingProduct.interestRateCharts, existingProduct.chartTemplate, existingProduct.minBalanceForInterestCalculation,
existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions);
}
public static DepositProductData instance(final Long id, final String name, final String shortName, final String description,
final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final Integer lockinPeriodFrequency,
final EnumOptionData lockinPeriodFrequencyType, final EnumOptionData accountingType,
final BigDecimal minBalanceForInterestCalculation, boolean withHoldTax, TaxGroupData taxGroup) {
final Map<String, Object> accountingMappings = null;
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<EnumOptionData> accountingRuleOptions = null;
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<ChargeData> penaltyOptions = null;
final Collection<ChargeData> charges = null;
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
final Collection<InterestRateChartData> interestRateCharts = null;
final InterestRateChartData chartTemplate = null;
final Collection<TaxGroupData> taxGroupOptions = null;
return new DepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
lockinPeriodFrequencyType, accountingType, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation, withHoldTax, taxGroup,
taxGroupOptions);
}
public static DepositProductData lookup(final Long id, final String name) {
final String shortName = null;
final CurrencyData currency = null;
final String description = null;
final BigDecimal nominalAnnualInterestRate = null;
final EnumOptionData interestCompoundingPeriodType = null;
final EnumOptionData interestPostingPeriodType = null;
final EnumOptionData interestCalculationType = null;
final EnumOptionData interestCalculationDaysInYearType = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal minBalanceForInterestCalculation = null;
final EnumOptionData accountingType = null;
final Map<String, Object> accountingMappings = null;
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<EnumOptionData> accountingRuleOptions = null;
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
final Collection<ChargeData> charges = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<ChargeData> penaltyOptions = null;
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
final Collection<InterestRateChartData> interestRateCharts = null;
final InterestRateChartData chartTemplate = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
final Collection<TaxGroupData> taxGroupOptions = null;
return new DepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
lockinPeriodFrequencyType, accountingType, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation, withHoldTax, taxGroup,
taxGroupOptions);
}
public static DepositProductData withInterestChart(final DepositProductData existingProduct,
final Collection<InterestRateChartData> interestRateCharts) {
return new DepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType,
existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency,
existingProduct.lockinPeriodFrequencyType, existingProduct.accountingRule, existingProduct.accountingMappings,
existingProduct.paymentChannelToFundSourceMappings, existingProduct.currencyOptions,
existingProduct.interestCompoundingPeriodTypeOptions, existingProduct.interestPostingPeriodTypeOptions,
existingProduct.interestCalculationTypeOptions, existingProduct.interestCalculationDaysInYearTypeOptions,
existingProduct.lockinPeriodFrequencyTypeOptions, existingProduct.withdrawalFeeTypeOptions,
existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions,
existingProduct.charges, existingProduct.chargeOptions, existingProduct.penaltyOptions,
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, interestRateCharts,
existingProduct.chartTemplate, existingProduct.minBalanceForInterestCalculation, existingProduct.withHoldTax,
existingProduct.taxGroup, existingProduct.taxGroupOptions);
}
protected DepositProductData(final Long id, final String name, final String shortName, final String description,
final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final Integer lockinPeriodFrequency,
final EnumOptionData lockinPeriodFrequencyType, final EnumOptionData accountingType,
final Map<String, Object> accountingMappings, final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings,
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> charges,
final Collection<ChargeData> chargeOptions, final Collection<ChargeData> penaltyOptions,
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings,
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings,
final Collection<InterestRateChartData> interestRateCharts, final InterestRateChartData chartTemplate,
final BigDecimal minBalanceForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
final Collection<TaxGroupData> taxGroupOptions) {
this.id = id;
this.name = name;
this.shortName = shortName;
this.description = description;
this.currency = currency;
this.nominalAnnualInterestRate = nominalAnnualInterestRate;
this.interestCompoundingPeriodType = interestCompoundingPeriodType;
this.interestPostingPeriodType = interestPostingPeriodType;
this.interestCalculationType = interestCalculationType;
this.interestCalculationDaysInYearType = interestCalculationDaysInYearType;
this.accountingRule = accountingType;
this.lockinPeriodFrequency = lockinPeriodFrequency;
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType;
this.currencyOptions = currencyOptions;
this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions;
this.interestPostingPeriodTypeOptions = interestPostingPeriodTypeOptions;
this.interestCalculationTypeOptions = interestCalculationTypeOptions;
this.interestCalculationDaysInYearTypeOptions = interestCalculationDaysInYearTypeOptions;
this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions;
this.withdrawalFeeTypeOptions = withdrawalFeeTypeOptions;
this.paymentTypeOptions = paymentTypeOptions;
this.accountingMappingOptions = accountingMappingOptions;
this.accountingRuleOptions = accountingRuleOptions;
if (accountingMappings == null || accountingMappings.isEmpty()) {
this.accountingMappings = null;
} else {
this.accountingMappings = accountingMappings;
}
this.paymentChannelToFundSourceMappings = paymentChannelToFundSourceMappings;
this.charges = charges;// charges associated with Savings product
this.chargeOptions = chargeOptions;// charges available for adding to
// Savings product
this.penaltyOptions = penaltyOptions;// penalties available for adding
// to Savings product
this.feeToIncomeAccountMappings = feeToIncomeAccountMappings;
this.penaltyToIncomeAccountMappings = penaltyToIncomeAccountMappings;
this.interestRateCharts = interestRateCharts;
this.activeChart = activeChart(this.interestRateCharts);
this.chartTemplate = chartTemplate;
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.taxGroup = taxGroup;
this.withHoldTax = withHoldTax;
this.taxGroupOptions = taxGroupOptions;
}
public static InterestRateChartData activeChart(Collection<InterestRateChartData> interestRateCharts) {
InterestRateChartData activeChart = null;
if (interestRateCharts != null) {
for (InterestRateChartData chartData : interestRateCharts) {
if (activeChart == null) {
activeChart = chartData;
} else {
if (!activeChart.isFromDateAfter(chartData.endDate())) {
activeChart = chartData;
}
}
}
}
return activeChart;
}
public boolean hasAccountingEnabled() {
return this.accountingRule.getId() > AccountingRuleType.NONE.getValue();
}
public int accountingRuleTypeId() {
return this.accountingRule.getId().intValue();
}
public String getName() {
return this.name;
}
public EnumOptionData getLockinPeriodFrequencyType() {
return lockinPeriodFrequencyType;
}
public Integer getLockinPeriodFrequency() {
return lockinPeriodFrequency;
}
public Long getId() {
return id;
}
public BigDecimal getNominalAnnualInterestRate() {
return nominalAnnualInterestRate;
}
public EnumOptionData getInterestPostingPeriodType() {
return interestPostingPeriodType;
}
public EnumOptionData getInterestCalculationType() {
return interestCalculationType;
}
public EnumOptionData getInterestCalculationDaysInYearType() {
return interestCalculationDaysInYearType;
}
public BigDecimal getMinBalanceForInterestCalculation() {
return minBalanceForInterestCalculation;
}
public String getShortName() {
return shortName;
}
public CurrencyData getCurrency() {
return currency;
}
public EnumOptionData getInterestCompoundingPeriodType() {
return interestCompoundingPeriodType;
}
}
@@ -0,0 +1,544 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import lombok.Getter;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
/**
* Immutable data object representing a Fixed Deposit account.
*/
@Getter
public final class FixedDepositAccountData extends DepositAccountData {
private boolean preClosurePenalApplicable;
private BigDecimal preClosurePenalInterest;
private EnumOptionData preClosurePenalInterestOnType;
private Integer minDepositTerm;
private Integer maxDepositTerm;
private EnumOptionData minDepositTermType;
private EnumOptionData maxDepositTermType;
private Integer inMultiplesOfDepositTerm;
private EnumOptionData inMultiplesOfDepositTermType;
private BigDecimal depositAmount;
private BigDecimal maturityAmount;
private LocalDate maturityDate;
private Integer depositPeriod;
private EnumOptionData depositPeriodFrequency;
private BigDecimal activationCharge;
private Long transferToSavingsId;
// used for account close
private EnumOptionData onAccountClosure;
private final PortfolioAccountData linkedAccount;
private final Boolean transferInterestToSavings;
private final PortfolioAccountData transferToSavingsAccount;
private Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions;
private Collection<EnumOptionData> periodFrequencyTypeOptions;
private Collection<SavingsAccountData> savingsAccounts;
// for account close
private Collection<EnumOptionData> onAccountClosureOptions;
private Collection<PaymentTypeData> paymentTypeOptions;
private final Collection<EnumOptionData> maturityInstructionOptions;
// import fields
private transient Integer rowIndex;
private String dateFormat;
private String locale;
private LocalDate submittedOnDate;
private Long depositPeriodFrequencyId;
public static FixedDepositAccountData importInstance(Long clientId, Long productId, Long fieldOfficerId, LocalDate submittedOnDate,
EnumOptionData interestCompoundingPeriodTypeEnum, EnumOptionData interestPostingPeriodTypeEnum,
EnumOptionData interestCalculationTypeEnum, EnumOptionData interestCalculationDaysInYearTypeEnum, Integer lockinPeriodFrequency,
EnumOptionData lockinPeriodFrequencyTypeEnum, BigDecimal depositAmount, Integer depositPeriod, Long depositPeriodFrequencyId,
String externalId, Collection<SavingsAccountChargeData> charges, Integer rowIndex, String locale, String dateFormat) {
return new FixedDepositAccountData(clientId, productId, fieldOfficerId, submittedOnDate, interestCompoundingPeriodTypeEnum,
interestPostingPeriodTypeEnum, interestCalculationTypeEnum, interestCalculationDaysInYearTypeEnum, lockinPeriodFrequency,
lockinPeriodFrequencyTypeEnum, depositAmount, depositPeriod, depositPeriodFrequencyId, externalId, charges, rowIndex,
locale, dateFormat);
}
private FixedDepositAccountData(Long clientId, Long productId, Long fieldofficerId, LocalDate submittedOnDate,
EnumOptionData interestCompoundingPeriodType, EnumOptionData interestPostingPeriodType, EnumOptionData interestCalculationType,
EnumOptionData interestCalculationDaysInYearType, Integer lockinPeriodFrequency, EnumOptionData lockinPeriodFrequencyType,
BigDecimal depositAmount, Integer depositPeriod, Long depositPeriodFrequencyId, String externalId,
Collection<SavingsAccountChargeData> charges, Integer rowIndex, String locale, String dateFormat) {
super(clientId, productId, fieldofficerId, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, lockinPeriodFrequency, lockinPeriodFrequencyType, externalId, charges);
this.preClosurePenalApplicable = false;
this.preClosurePenalInterest = null;
this.preClosurePenalInterestOnType = null;
this.minDepositTerm = null;
this.maxDepositTerm = null;
this.minDepositTermType = null;
this.maxDepositTermType = null;
this.inMultiplesOfDepositTerm = null;
this.inMultiplesOfDepositTermType = null;
this.depositAmount = depositAmount;
this.maturityAmount = null;
this.maturityDate = null;
this.depositPeriod = depositPeriod;
this.depositPeriodFrequency = null;
this.activationCharge = null;
this.onAccountClosure = null;
this.linkedAccount = null;
this.transferToSavingsAccount = null;
this.transferInterestToSavings = null;
this.preClosurePenalInterestOnTypeOptions = null;
this.periodFrequencyTypeOptions = null;
this.savingsAccounts = null;
this.onAccountClosureOptions = null;
this.paymentTypeOptions = null;
this.rowIndex = rowIndex;
this.dateFormat = dateFormat;
this.locale = locale;
this.submittedOnDate = submittedOnDate;
this.depositPeriodFrequencyId = depositPeriodFrequencyId;
this.maturityInstructionOptions = null;
}
public static FixedDepositAccountData instance(final DepositAccountData depositAccountData, final boolean preClosurePenalApplicable,
final BigDecimal preClosurePenalInterest, final EnumOptionData preClosurePenalInterestOnType, final Integer minDepositTerm,
final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType,
final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount,
final BigDecimal maturityAmount, final LocalDate maturityDate, final Integer depositPeriod,
final EnumOptionData depositPeriodFrequency, final EnumOptionData onAccountClosure, final Boolean transferInterestToSavings,
final Long transferToSavingsId) {
final PortfolioAccountData linkedAccount = null;
final PortfolioAccountData transferToSavingsAccount = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final Collection<EnumOptionData> maturityInstructionOptions = null;
final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue());
final Collection<EnumOptionData> onAccountClosureOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<SavingsAccountData> savingsAccountDatas = null;
return new FixedDepositAccountData(depositAccountData.id, depositAccountData.accountNo, depositAccountData.externalId,
depositAccountData.groupId, depositAccountData.groupName, depositAccountData.clientId, depositAccountData.clientName,
depositAccountData.depositProductId, depositAccountData.depositProductName, depositAccountData.fieldOfficerId,
depositAccountData.fieldOfficerName, depositAccountData.status, depositAccountData.timeline, depositAccountData.currency,
depositAccountData.nominalAnnualInterestRate, depositAccountData.interestCompoundingPeriodType,
depositAccountData.interestPostingPeriodType, depositAccountData.interestCalculationType,
depositAccountData.interestCalculationDaysInYearType, depositAccountData.minRequiredOpeningBalance,
depositAccountData.lockinPeriodFrequency, depositAccountData.lockinPeriodFrequencyType,
depositAccountData.withdrawalFeeForTransfers, depositAccountData.minBalanceForInterestCalculation,
depositAccountData.summary, depositAccountData.transactions, depositAccountData.productOptions,
depositAccountData.fieldOfficerOptions, depositAccountData.interestCompoundingPeriodTypeOptions,
depositAccountData.interestPostingPeriodTypeOptions, depositAccountData.interestCalculationTypeOptions,
depositAccountData.interestCalculationDaysInYearTypeOptions, depositAccountData.lockinPeriodFrequencyTypeOptions,
depositAccountData.withdrawalFeeTypeOptions, depositAccountData.charges, depositAccountData.chargeOptions,
depositAccountData.accountChart, depositAccountData.chartTemplate, preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType,
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate,
depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions, depositType, onAccountClosure, onAccountClosureOptions,
paymentTypeOptions, savingsAccountDatas, linkedAccount, transferInterestToSavings, depositAccountData.withHoldTax,
depositAccountData.taxGroup, maturityInstructionOptions, transferToSavingsId, transferToSavingsAccount);
}
public static FixedDepositAccountData withInterestChart(final FixedDepositAccountData account,
final DepositAccountInterestRateChartData accountChart) {
return new FixedDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.clientId, account.clientName, account.depositProductId, account.depositProductName, account.fieldOfficerId,
account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate,
account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType,
account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.minBalanceForInterestCalculation,
account.summary, account.transactions, account.productOptions, account.fieldOfficerOptions,
account.interestCompoundingPeriodTypeOptions, account.interestPostingPeriodTypeOptions,
account.interestCalculationTypeOptions, account.interestCalculationDaysInYearTypeOptions,
account.lockinPeriodFrequencyTypeOptions, account.withdrawalFeeTypeOptions, account.charges, account.chargeOptions,
accountChart, account.chartTemplate, account.preClosurePenalApplicable, account.preClosurePenalInterest,
account.preClosurePenalInterestOnType, account.preClosurePenalInterestOnTypeOptions, account.minDepositTerm,
account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, account.inMultiplesOfDepositTerm,
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, account.periodFrequencyTypeOptions, account.depositType,
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts,
account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup,
account.maturityInstructionOptions, account.transferToSavingsId, account.transferToSavingsAccount);
}
public static FixedDepositAccountData associationsAndTemplate(final FixedDepositAccountData account, FixedDepositAccountData template,
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges,
final PortfolioAccountData linkedAccount, PortfolioAccountData transferToSavingsAccount) {
if (template == null) {
template = account;
}
return new FixedDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.clientId, account.clientName, account.depositProductId, account.depositProductName, account.fieldOfficerId,
account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate,
account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType,
account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.minBalanceForInterestCalculation,
account.summary, transactions, template.productOptions, template.fieldOfficerOptions,
template.interestCompoundingPeriodTypeOptions, template.interestPostingPeriodTypeOptions,
template.interestCalculationTypeOptions, template.interestCalculationDaysInYearTypeOptions,
template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions,
account.accountChart, account.chartTemplate, account.preClosurePenalApplicable, account.preClosurePenalInterest,
account.preClosurePenalInterestOnType, template.preClosurePenalInterestOnTypeOptions, account.minDepositTerm,
account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, account.inMultiplesOfDepositTerm,
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, template.periodFrequencyTypeOptions, account.depositType,
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, template.savingsAccounts,
linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup, account.maturityInstructionOptions,
account.transferToSavingsId, transferToSavingsAccount);
}
public static FixedDepositAccountData withTemplateOptions(final FixedDepositAccountData account,
final Collection<DepositProductData> productOptions, final Collection<StaffData> fieldOfficerOptions,
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges,
final Collection<ChargeData> chargeOptions, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<SavingsAccountData> savingsAccounts,
final Collection<EnumOptionData> maturityInstructionOptions) {
return new FixedDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.clientId, account.clientName, account.depositProductId, account.depositProductName, account.fieldOfficerId,
account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate,
account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType,
account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.minBalanceForInterestCalculation,
account.summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, account.accountChart,
account.chartTemplate, account.preClosurePenalApplicable, account.preClosurePenalInterest,
account.preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, account.minDepositTerm, account.maxDepositTerm,
account.minDepositTermType, account.maxDepositTermType, account.inMultiplesOfDepositTerm,
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, periodFrequencyTypeOptions, account.depositType,
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, savingsAccounts,
account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup, maturityInstructionOptions,
account.transferToSavingsId, account.transferToSavingsAccount);
}
public static FixedDepositAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
final String groupName) {
final Long id = null;
final String accountNo = null;
final String externalId = null;
final Long productId = null;
final String productName = null;
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null;
final SavingsAccountApplicationTimelineData timeline = null;
final CurrencyData currency = null;
final BigDecimal nominalAnnualInterestRate = null;
final EnumOptionData interestPeriodType = null;
final EnumOptionData interestPostingPeriodType = null;
final EnumOptionData interestCalculationType = null;
final EnumOptionData interestCalculationDaysInYearType = null;
final BigDecimal minRequiredOpeningBalance = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final boolean withdrawalFeeForTransfers = false;
final BigDecimal minBalanceForInterestCalculation = null;
final SavingsAccountSummaryData summary = null;
final Collection<SavingsAccountTransactionData> transactions = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
final Collection<DepositProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<SavingsAccountChargeData> charges = null;
final Collection<ChargeData> chargeOptions = null;
final DepositAccountInterestRateChartData accountChart = null;
final DepositAccountInterestRateChartData chartTemplate = null;
final boolean preClosurePenalApplicable = false;
final BigDecimal preClosurePenalInterest = null;
final EnumOptionData preClosurePenalInterestOnType = null;
final Integer minDepositTerm = null;
final Integer maxDepositTerm = null;
final EnumOptionData minDepositTermType = null;
final EnumOptionData maxDepositTermType = null;
final Integer inMultiplesOfDepositTerm = null;
final EnumOptionData inMultiplesOfDepositTermType = null;
final BigDecimal depositAmount = null;
final BigDecimal maturityAmount = null;
final LocalDate maturityDate = null;
final Integer depositPeriod = null;
final EnumOptionData depositPeriodFrequency = null;
final EnumOptionData onAccountClosure = null;
final PortfolioAccountData linkedAccount = null;
final PortfolioAccountData transferToSavingsAccount = null;
final Boolean transferInterestToSavings = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue());
final Collection<EnumOptionData> onAccountClosureOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<SavingsAccountData> savingsAccountDatas = null;
final Collection<EnumOptionData> maturityInstructionOptions = null;
final Long transferToSavingsId = null;
return new FixedDepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, minBalanceForInterestCalculation, summary,
transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, preClosurePenalApplicable,
preClosurePenalInterest, preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm,
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions, depositType,
onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount,
transferInterestToSavings, withHoldTax, taxGroup, maturityInstructionOptions, transferToSavingsId,
transferToSavingsAccount);
}
public static FixedDepositAccountData preClosureDetails(final Long accountId, BigDecimal maturityAmount,
final Collection<EnumOptionData> onAccountClosureOptions, final Collection<PaymentTypeData> paymentTypeOptions,
final Collection<SavingsAccountData> savingsAccountDatas) {
final Long groupId = null;
final String groupName = null;
final Long clientId = null;
final String clientName = null;
final String accountNo = null;
final String externalId = null;
final Long productId = null;
final String productName = null;
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null;
final SavingsAccountApplicationTimelineData timeline = null;
final CurrencyData currency = null;
final BigDecimal nominalAnnualInterestRate = null;
final EnumOptionData interestPeriodType = null;
final EnumOptionData interestPostingPeriodType = null;
final EnumOptionData interestCalculationType = null;
final EnumOptionData interestCalculationDaysInYearType = null;
final BigDecimal minRequiredOpeningBalance = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final boolean withdrawalFeeForTransfers = false;
final BigDecimal minBalanceForInterestCalculation = null;
final SavingsAccountSummaryData summary = null;
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<DepositProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<SavingsAccountChargeData> charges = null;
final Collection<ChargeData> chargeOptions = null;
final DepositAccountInterestRateChartData accountChart = null;
final DepositAccountInterestRateChartData chartTemplate = null;
final boolean preClosurePenalApplicable = false;
final BigDecimal preClosurePenalInterest = null;
final EnumOptionData preClosurePenalInterestOnType = null;
final Integer minDepositTerm = null;
final Integer maxDepositTerm = null;
final EnumOptionData minDepositTermType = null;
final EnumOptionData maxDepositTermType = null;
final Integer inMultiplesOfDepositTerm = null;
final EnumOptionData inMultiplesOfDepositTermType = null;
final BigDecimal depositAmount = null;
final LocalDate maturityDate = null;
final Integer depositPeriod = null;
final EnumOptionData depositPeriodFrequency = null;
final EnumOptionData onAccountClosure = null;
final Boolean transferInterestToSavings = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue());
final PortfolioAccountData linkedAccount = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
final Collection<EnumOptionData> maturityInstructionOptions = null;
final Long transferToSavingsId = null;
final PortfolioAccountData transferToSavingsAccount = null;
return new FixedDepositAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, minBalanceForInterestCalculation, summary,
transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, preClosurePenalApplicable,
preClosurePenalInterest, preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm,
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions, depositType,
onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount,
transferInterestToSavings, withHoldTax, taxGroup, maturityInstructionOptions, transferToSavingsId,
transferToSavingsAccount);
}
public static FixedDepositAccountData withClosureTemplateDetails(final FixedDepositAccountData account,
final Collection<EnumOptionData> onAccountClosureOptions, final Collection<PaymentTypeData> paymentTypeOptions,
final Collection<SavingsAccountData> savingsAccountDatas) {
return new FixedDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.clientId, account.clientName, account.depositProductId, account.depositProductName, account.fieldOfficerId,
account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate,
account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType,
account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.minBalanceForInterestCalculation,
account.summary, account.transactions, account.productOptions, account.fieldOfficerOptions,
account.interestCompoundingPeriodTypeOptions, account.interestPostingPeriodTypeOptions,
account.interestCalculationTypeOptions, account.interestCalculationDaysInYearTypeOptions,
account.lockinPeriodFrequencyTypeOptions, account.withdrawalFeeTypeOptions, account.charges, account.chargeOptions,
account.accountChart, account.chartTemplate, account.preClosurePenalApplicable, account.preClosurePenalInterest,
account.preClosurePenalInterestOnType, account.preClosurePenalInterestOnTypeOptions, account.minDepositTerm,
account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, account.inMultiplesOfDepositTerm,
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, account.periodFrequencyTypeOptions, account.depositType,
account.onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, account.linkedAccount,
account.transferInterestToSavings, account.withHoldTax, account.taxGroup, account.maturityInstructionOptions,
account.transferToSavingsId, account.transferToSavingsAccount);
}
private FixedDepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId,
final String groupName, final Long clientId, final String clientName, final Long productId, final String productName,
final Long fieldofficerId, final String fieldofficerName, final SavingsAccountStatusEnumData status,
final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate,
final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType,
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType,
final boolean withdrawalFeeForTransfers, final BigDecimal minBalanceForInterestCalculation,
final SavingsAccountSummaryData summary, final Collection<SavingsAccountTransactionData> transactions,
final Collection<DepositProductData> productOptions, final Collection<StaffData> fieldOfficerOptions,
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions,
final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate,
final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest,
final EnumOptionData preClosurePenalInterestOnType, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Integer minDepositTerm, final Integer maxDepositTerm, final EnumOptionData minDepositTermType,
final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm,
final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount, final BigDecimal maturityAmount,
final LocalDate maturityDate, final Integer depositPeriod, final EnumOptionData depositPeriodFrequency,
final Collection<EnumOptionData> periodFrequencyTypeOptions, final EnumOptionData depositType,
final EnumOptionData onAccountClosure, final Collection<EnumOptionData> onAccountClosureOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<SavingsAccountData> savingsAccountDatas,
final PortfolioAccountData linkedAccount, final Boolean transferInterestToSavings, final boolean withHoldTax,
final TaxGroupData taxGroup, final Collection<EnumOptionData> maturityInstructionOptions, final Long transferToSavingsId,
final PortfolioAccountData transferToSavingsAccount) {
super(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldofficerId, fieldofficerName,
status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges,
chargeOptions, accountChart, chartTemplate, depositType, minBalanceForInterestCalculation, withHoldTax, taxGroup);
this.preClosurePenalApplicable = preClosurePenalApplicable;
this.preClosurePenalInterest = preClosurePenalInterest;
this.preClosurePenalInterestOnType = preClosurePenalInterestOnType;
this.minDepositTerm = minDepositTerm;
this.maxDepositTerm = maxDepositTerm;
this.minDepositTermType = minDepositTermType;
this.maxDepositTermType = maxDepositTermType;
this.inMultiplesOfDepositTerm = inMultiplesOfDepositTerm;
this.inMultiplesOfDepositTermType = inMultiplesOfDepositTermType;
this.depositAmount = depositAmount;
this.maturityAmount = maturityAmount;
this.maturityDate = maturityDate;
this.depositPeriod = depositPeriod;
this.depositPeriodFrequency = depositPeriodFrequency;
this.onAccountClosure = onAccountClosure;
this.linkedAccount = linkedAccount;
this.transferToSavingsAccount = transferToSavingsAccount;
this.transferInterestToSavings = transferInterestToSavings;
// template
this.preClosurePenalInterestOnTypeOptions = preClosurePenalInterestOnTypeOptions;
this.periodFrequencyTypeOptions = periodFrequencyTypeOptions;
// account close template options
this.onAccountClosureOptions = onAccountClosureOptions;
this.paymentTypeOptions = paymentTypeOptions;
this.savingsAccounts = savingsAccountDatas;
this.maturityInstructionOptions = maturityInstructionOptions;
this.transferToSavingsId = transferToSavingsId;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (!(obj instanceof FixedDepositAccountData)) {
return false;
}
final FixedDepositAccountData rhs = (FixedDepositAccountData) obj;
return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).toHashCode();
}
public void setActivationCharge(BigDecimal activationCharge) {
this.activationCharge = activationCharge;
}
}
@@ -0,0 +1,448 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartData;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
/**
* Immutable data object representing a Fixed Deposit product.
*/
public final class FixedDepositProductData extends DepositProductData {
// additional fields
private boolean preClosurePenalApplicable;
private BigDecimal preClosurePenalInterest;
private EnumOptionData preClosurePenalInterestOnType;
private Integer minDepositTerm;
private Integer maxDepositTerm;
private EnumOptionData minDepositTermType;
private EnumOptionData maxDepositTermType;
private Integer inMultiplesOfDepositTerm;
private EnumOptionData inMultiplesOfDepositTermType;
private BigDecimal minDepositAmount;
private BigDecimal depositAmount;
private BigDecimal maxDepositAmount;
private Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions;
private Collection<EnumOptionData> periodFrequencyTypeOptions;
public static FixedDepositProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData accountingRule,
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<TaxGroupData> taxGroupOptions) {
final Long id = null;
final String name = null;
final String shortName = null;
final String description = null;
final BigDecimal nominalAnnualInterestRate = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal minBalanceForInterestCalculation = null;
final Map<String, Object> accountingMappings = null;
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
final Collection<ChargeData> charges = null;
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
final Collection<InterestRateChartData> interestRateCharts = null;
final boolean preClosurePenalApplicable = false;
final BigDecimal preClosurePenalInterest = null;
final EnumOptionData preClosurePenalInterestOnType = null;
final Integer minDepositTerm = null;
final Integer maxDepositTerm = null;
final EnumOptionData minDepositTermType = null;
final EnumOptionData maxDepositTermType = null;
final Integer inMultiplesOfDepositTerm = null;
final EnumOptionData inMultiplesOfDepositTermType = null;
final BigDecimal minDepositAmount = null;
final BigDecimal depositAmount = null;
final BigDecimal maxDepositAmount = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
return new FixedDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
lockinPeriodFrequency, lockinPeriodFrequencyType, minBalanceForInterestCalculation, accountingRule, accountingMappings,
paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate,
preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions,
minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm,
inMultiplesOfDepositTermType, minDepositAmount, depositAmount, maxDepositAmount, periodFrequencyTypeOptions, withHoldTax,
taxGroup, taxGroupOptions);
}
public static FixedDepositProductData withCharges(final FixedDepositProductData existingProduct, final Collection<ChargeData> charges) {
return new FixedDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType,
existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency,
existingProduct.lockinPeriodFrequencyType, existingProduct.minBalanceForInterestCalculation, existingProduct.accountingRule,
existingProduct.accountingMappings, existingProduct.paymentChannelToFundSourceMappings, existingProduct.currencyOptions,
existingProduct.interestCompoundingPeriodTypeOptions, existingProduct.interestPostingPeriodTypeOptions,
existingProduct.interestCalculationTypeOptions, existingProduct.interestCalculationDaysInYearTypeOptions,
existingProduct.lockinPeriodFrequencyTypeOptions, existingProduct.withdrawalFeeTypeOptions,
existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions,
charges, existingProduct.chargeOptions, existingProduct.penaltyOptions, existingProduct.feeToIncomeAccountMappings,
existingProduct.penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate,
existingProduct.preClosurePenalApplicable, existingProduct.preClosurePenalInterest,
existingProduct.preClosurePenalInterestOnType, existingProduct.preClosurePenalInterestOnTypeOptions,
existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType,
existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType,
existingProduct.minDepositAmount, existingProduct.depositAmount, existingProduct.maxDepositAmount,
existingProduct.periodFrequencyTypeOptions, existingProduct.withHoldTax, existingProduct.taxGroup,
existingProduct.taxGroupOptions);
}
/**
* Returns a {@link FixedDepositProductData} that contains and exist {@link FixedDepositProductData} data with
* further template data for dropdowns.
*
* @param taxGroupOptions
* TODO
*/
public static FixedDepositProductData withTemplate(final FixedDepositProductData existingProduct,
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<TaxGroupData> taxGroupOptions) {
return new FixedDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType,
existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency,
existingProduct.lockinPeriodFrequencyType, existingProduct.minBalanceForInterestCalculation, existingProduct.accountingRule,
existingProduct.accountingMappings, existingProduct.paymentChannelToFundSourceMappings, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
accountingRuleOptions, accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions,
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings,
existingProduct.interestRateCharts, chartTemplate, existingProduct.preClosurePenalApplicable,
existingProduct.preClosurePenalInterest, existingProduct.preClosurePenalInterestOnType,
preClosurePenalInterestOnTypeOptions, existingProduct.minDepositTerm, existingProduct.maxDepositTerm,
existingProduct.minDepositTermType, existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm,
existingProduct.inMultiplesOfDepositTermType, existingProduct.minDepositAmount, existingProduct.depositAmount,
existingProduct.maxDepositAmount, periodFrequencyTypeOptions, existingProduct.withHoldTax, existingProduct.taxGroup,
taxGroupOptions);
}
public static FixedDepositProductData withAccountingDetails(final FixedDepositProductData existingProduct,
final Map<String, Object> accountingMappings, final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings,
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings,
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings) {
final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<EnumOptionData> accountingRuleOptions = null;
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<ChargeData> penaltyOptions = null;
return new FixedDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType,
existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency,
existingProduct.lockinPeriodFrequencyType, existingProduct.minBalanceForInterestCalculation, existingProduct.accountingRule,
accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate,
existingProduct.preClosurePenalApplicable, existingProduct.preClosurePenalInterest,
existingProduct.preClosurePenalInterestOnType, existingProduct.preClosurePenalInterestOnTypeOptions,
existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType,
existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType,
existingProduct.minDepositAmount, existingProduct.depositAmount, existingProduct.maxDepositAmount,
existingProduct.periodFrequencyTypeOptions, existingProduct.withHoldTax, existingProduct.taxGroup,
existingProduct.taxGroupOptions);
}
public static FixedDepositProductData instance(final DepositProductData depositProductData, final boolean preClosurePenalApplicable,
final BigDecimal preClosurePenalInterest, final EnumOptionData preClosurePenalInterestOnType, final Integer minDepositTerm,
final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType,
final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal minDepositAmount,
final BigDecimal depositAmount, final BigDecimal maxDepositAmount) {
final Map<String, Object> accountingMappings = null;
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<EnumOptionData> accountingRuleOptions = null;
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<ChargeData> penaltyOptions = null;
final Collection<ChargeData> charges = null;
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
final Collection<InterestRateChartData> interestRateCharts = null;
final InterestRateChartData chartTemplate = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final Collection<TaxGroupData> taxGroupOptions = null;
return new FixedDepositProductData(depositProductData.id, depositProductData.name, depositProductData.shortName,
depositProductData.description, depositProductData.currency, depositProductData.nominalAnnualInterestRate,
depositProductData.interestCompoundingPeriodType, depositProductData.interestPostingPeriodType,
depositProductData.interestCalculationType, depositProductData.interestCalculationDaysInYearType,
depositProductData.lockinPeriodFrequency, depositProductData.lockinPeriodFrequencyType,
depositProductData.minBalanceForInterestCalculation, depositProductData.accountingRule, accountingMappings,
paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate,
preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions,
minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm,
inMultiplesOfDepositTermType, minDepositAmount, depositAmount, maxDepositAmount, periodFrequencyTypeOptions,
depositProductData.withHoldTax, depositProductData.taxGroup, taxGroupOptions);
}
public static FixedDepositProductData lookup(final Long id, final String name) {
final String shortName = null;
final CurrencyData currency = null;
final String description = null;
final BigDecimal nominalAnnualInterestRate = null;
final EnumOptionData interestCompoundingPeriodType = null;
final EnumOptionData interestPostingPeriodType = null;
final EnumOptionData interestCalculationType = null;
final EnumOptionData interestCalculationDaysInYearType = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal minBalanceForInterestCalculation = null;
final EnumOptionData accountingType = null;
final Map<String, Object> accountingMappings = null;
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<EnumOptionData> accountingRuleOptions = null;
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
final Collection<ChargeData> charges = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<ChargeData> penaltyOptions = null;
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
final Collection<InterestRateChartData> interestRateCharts = null;
final InterestRateChartData chartTemplate = null;
final boolean preClosurePenalApplicable = false;
final BigDecimal preClosurePenalInterest = null;
final EnumOptionData preClosurePenalInterestOnType = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Integer minDepositTerm = null;
final Integer maxDepositTerm = null;
final EnumOptionData minDepositTermType = null;
final EnumOptionData maxDepositTermType = null;
final Integer inMultiplesOfDepositTerm = null;
final EnumOptionData inMultiplesOfDepositTermType = null;
final BigDecimal minDepositAmount = null;
final BigDecimal depositAmount = null;
final BigDecimal maxDepositAmount = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
final Collection<TaxGroupData> taxGroupOptions = null;
return new FixedDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
lockinPeriodFrequency, lockinPeriodFrequencyType, minBalanceForInterestCalculation, accountingType, accountingMappings,
paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate,
preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions,
minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm,
inMultiplesOfDepositTermType, minDepositAmount, depositAmount, maxDepositAmount, periodFrequencyTypeOptions, withHoldTax,
taxGroup, taxGroupOptions);
}
public static FixedDepositProductData withInterestChart(final FixedDepositProductData existingProduct,
final Collection<InterestRateChartData> interestRateCharts) {
return new FixedDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType,
existingProduct.interestCalculationDaysInYearType, existingProduct.lockinPeriodFrequency,
existingProduct.lockinPeriodFrequencyType, existingProduct.minBalanceForInterestCalculation, existingProduct.accountingRule,
existingProduct.accountingMappings, existingProduct.paymentChannelToFundSourceMappings, existingProduct.currencyOptions,
existingProduct.interestCompoundingPeriodTypeOptions, existingProduct.interestPostingPeriodTypeOptions,
existingProduct.interestCalculationTypeOptions, existingProduct.interestCalculationDaysInYearTypeOptions,
existingProduct.lockinPeriodFrequencyTypeOptions, existingProduct.withdrawalFeeTypeOptions,
existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions,
existingProduct.charges, existingProduct.chargeOptions, existingProduct.penaltyOptions,
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, interestRateCharts,
existingProduct.chartTemplate, existingProduct.preClosurePenalApplicable, existingProduct.preClosurePenalInterest,
existingProduct.preClosurePenalInterestOnType, existingProduct.preClosurePenalInterestOnTypeOptions,
existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType,
existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType,
existingProduct.minDepositAmount, existingProduct.depositAmount, existingProduct.maxDepositAmount,
existingProduct.periodFrequencyTypeOptions, existingProduct.withHoldTax, existingProduct.taxGroup,
existingProduct.taxGroupOptions);
}
private FixedDepositProductData(final Long id, final String name, final String shortName, final String description,
final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final Integer lockinPeriodFrequency,
final EnumOptionData lockinPeriodFrequencyType, final BigDecimal minBalanceForInterestCalculation,
final EnumOptionData accountingType, final Map<String, Object> accountingMappings,
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings,
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> charges,
final Collection<ChargeData> chargeOptions, final Collection<ChargeData> penaltyOptions,
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings,
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings,
final Collection<InterestRateChartData> interestRateCharts, final InterestRateChartData chartTemplate,
final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest,
final EnumOptionData preClosurePenalInterestOnType, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Integer minDepositTerm, final Integer maxDepositTerm, final EnumOptionData minDepositTermType,
final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm,
final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal minDepositAmount, final BigDecimal depositAmount,
final BigDecimal maxDepositAmount, final Collection<EnumOptionData> periodFrequencyTypeOptions, final boolean withHoldTax,
final TaxGroupData taxGroup, final Collection<TaxGroupData> taxGroupOptions) {
super(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
lockinPeriodFrequencyType, accountingType, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation, withHoldTax, taxGroup,
taxGroupOptions);
// fixed deposit additional fields
this.preClosurePenalApplicable = preClosurePenalApplicable;
this.preClosurePenalInterest = preClosurePenalInterest;
this.preClosurePenalInterestOnType = preClosurePenalInterestOnType;
this.minDepositTerm = minDepositTerm;
this.maxDepositTerm = maxDepositTerm;
this.minDepositTermType = minDepositTermType;
this.maxDepositTermType = maxDepositTermType;
this.inMultiplesOfDepositTerm = inMultiplesOfDepositTerm;
this.inMultiplesOfDepositTermType = inMultiplesOfDepositTermType;
this.minDepositAmount = minDepositAmount;
this.depositAmount = depositAmount;
this.maxDepositAmount = maxDepositAmount;
// template
this.preClosurePenalInterestOnTypeOptions = preClosurePenalInterestOnTypeOptions;
this.periodFrequencyTypeOptions = periodFrequencyTypeOptions;
}
public Integer getMinDepositTerm() {
return minDepositTerm;
}
public EnumOptionData getMinDepositTermType() {
return minDepositTermType;
}
public EnumOptionData getMaxDepositTermType() {
return maxDepositTermType;
}
public Integer getMaxDepositTerm() {
return maxDepositTerm;
}
public Integer getInMultiplesOfDepositTerm() {
return inMultiplesOfDepositTerm;
}
public EnumOptionData getInMultiplesOfDepositTermType() {
return inMultiplesOfDepositTermType;
}
public BigDecimal getMinDepositAmount() {
return minDepositAmount;
}
public BigDecimal getDepositAmount() {
return depositAmount;
}
public BigDecimal getMaxDepositAmount() {
return maxDepositAmount;
}
public EnumOptionData getPreClosurePenalInterestOnType() {
return preClosurePenalInterestOnType;
}
public BigDecimal getPreClosurePenalInterest() {
return preClosurePenalInterest;
}
public boolean isPreClosurePenalApplicable() {
return preClosurePenalApplicable;
}
}
@@ -0,0 +1,74 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import java.util.List;
import org.apache.fineract.portfolio.accountdetails.data.SavingsSummaryCustom;
public class GSIMContainer {
private final BigDecimal gsimId;
private final BigDecimal groupId;
private final String accountNumber;
private final List<SavingsSummaryCustom> childGSIMAccounts;
private final BigDecimal parentBalance;
private final String savingsStatus;
public GSIMContainer(final BigDecimal gsimId, final BigDecimal groupId, final String accountNumber,
final List<SavingsSummaryCustom> childGSIMAccounts, final BigDecimal parentBalance, final String savingsStatus) {
this.gsimId = gsimId;
this.groupId = groupId;
this.accountNumber = accountNumber;
this.childGSIMAccounts = childGSIMAccounts;
this.parentBalance = parentBalance;
this.savingsStatus = savingsStatus;
}
public BigDecimal getGsimId() {
return gsimId;
}
public BigDecimal getGroupId() {
return groupId;
}
public String getAccountNumber() {
return accountNumber;
}
public List<SavingsSummaryCustom> getChildGSIMAccounts() {
return childGSIMAccounts;
}
public BigDecimal getparentBalance() {
return parentBalance;
}
public String getSavingsStatus() {
return savingsStatus;
}
}
@@ -0,0 +1,121 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
public final class GroupSavingsIndividualMonitoringAccountData {
private final BigDecimal gsimId;
private final BigDecimal groupId;
private final BigDecimal clientId;
private final String accountNumber;
private final BigDecimal childAccountId;
private final String childAccountNumber;
private final BigDecimal childDeposit;
private final BigDecimal parentDeposit;
private final Long childAccountsCount;
private final String savingsStatus;
private GroupSavingsIndividualMonitoringAccountData(final BigDecimal glimId, final BigDecimal groupId, final BigDecimal clientId,
final String accountNumber, final BigDecimal childAccountId, final String childAccountNumber, final BigDecimal childDeposit,
final BigDecimal parentDeposit, final Long childAccountsCount, final String savingsStatus) {
this.gsimId = glimId;
this.groupId = groupId;
this.clientId = clientId;
this.accountNumber = accountNumber;
this.childAccountId = childAccountId;
this.childAccountNumber = childAccountNumber;
this.childDeposit = childDeposit;
this.parentDeposit = parentDeposit;
this.childAccountsCount = childAccountsCount;
this.savingsStatus = savingsStatus;
}
public static GroupSavingsIndividualMonitoringAccountData getInstance(final BigDecimal glimId, final BigDecimal groupId,
final String accountNumber, final String childAccountNumber, final BigDecimal childDeposit, final BigDecimal parentDeposit,
final Long childAccountsCount, final String savingsStatus) {
return new GroupSavingsIndividualMonitoringAccountData(glimId, groupId, null, accountNumber, null, childAccountNumber, childDeposit,
parentDeposit, childAccountsCount, savingsStatus);
}
public static GroupSavingsIndividualMonitoringAccountData getInstance1(final BigDecimal glimId, final BigDecimal groupId,
final String accountNumber, final BigDecimal parentDeposit, final String savingsStatus) {
return new GroupSavingsIndividualMonitoringAccountData(glimId, groupId, null, accountNumber, null, null, null, parentDeposit, null,
savingsStatus);
}
public static GroupSavingsIndividualMonitoringAccountData getInstance2(final BigDecimal glimId, final BigDecimal groupId,
final BigDecimal clientId, final String accountNumber, final BigDecimal childAccountId, final String childAccountNumber,
final BigDecimal childDeposit, final BigDecimal parentDeposit, final Long childAccountsCount, final String savingsStatus) {
return new GroupSavingsIndividualMonitoringAccountData(glimId, groupId, clientId, accountNumber, childAccountId, childAccountNumber,
childDeposit, parentDeposit, childAccountsCount, savingsStatus);
}
public BigDecimal getGsimId() {
return gsimId;
}
public BigDecimal getGroupId() {
return groupId;
}
public BigDecimal getClientId() {
return clientId;
}
public String getAccountNumber() {
return accountNumber;
}
public BigDecimal getChildAccountId() {
return childAccountId;
}
public String getChildAccountNumber() {
return childAccountNumber;
}
public BigDecimal getChildDeposit() {
return childDeposit;
}
public BigDecimal getParentDeposit() {
return parentDeposit;
}
public Long getChildAccountsCount() {
return childAccountsCount;
}
public String getSavingsStatus() {
return savingsStatus;
}
}
@@ -0,0 +1,614 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import lombok.Getter;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
/**
* Immutable data object representing a Recurring Deposit account.
*/
@Getter
public final class RecurringDepositAccountData extends DepositAccountData {
// additional fields
private final boolean preClosurePenalApplicable;
private final BigDecimal preClosurePenalInterest;
private final EnumOptionData preClosurePenalInterestOnType;
private final Integer minDepositTerm;
private final Integer maxDepositTerm;
private final EnumOptionData minDepositTermType;
private final EnumOptionData maxDepositTermType;
private final Integer inMultiplesOfDepositTerm;
private final EnumOptionData inMultiplesOfDepositTermType;
private final BigDecimal depositAmount;
private final BigDecimal maturityAmount;
private final LocalDate maturityDate;
private final Integer depositPeriod;
private final EnumOptionData depositPeriodFrequency;
private final BigDecimal mandatoryRecommendedDepositAmount;
private final BigDecimal totalOverdueAmount;
private final Integer noOfOverdueInstallments;
private final boolean isMandatoryDeposit;
private final boolean allowWithdrawal;
private final boolean adjustAdvanceTowardsFuturePayments;
private final LocalDate expectedFirstDepositOnDate;
private final boolean isCalendarInherited;
private final Integer recurringFrequency;
private final EnumOptionData recurringFrequencyType;
// used for account close
private final EnumOptionData onAccountClosure;
private final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions;
private final Collection<EnumOptionData> periodFrequencyTypeOptions;
private final Collection<SavingsAccountData> savingsAccounts;
// for account close
private final Collection<EnumOptionData> onAccountClosureOptions;
private final Collection<PaymentTypeData> paymentTypeOptions;
// import fields
private transient Integer rowIndex;
private String dateFormat;
private String locale;
private LocalDate submittedOnDate;
private Long depositPeriodFrequencyId;
public static RecurringDepositAccountData importInstance(Long clientId, Long productId, Long fieldOfficerId, LocalDate submittedOnDate,
EnumOptionData interestCompoundingPeriodTypeEnum, EnumOptionData interestPostingPeriodTypeEnum,
EnumOptionData interestCalculationTypeEnum, EnumOptionData interestCalculationDaysInYearTypeEnum, Integer lockinPeriodFrequency,
EnumOptionData lockinPeriodFrequencyTypeEnum, BigDecimal depositAmount, Integer depositPeriod, Long depositPeriodFrequencyId,
LocalDate expectedFirstDepositOnDate, Integer recurringFrequency, EnumOptionData recurringFrequencyTypeEnum,
boolean isCalendarInherited, boolean isMandatoryDeposit, boolean allowWithdrawal, boolean adjustAdvanceTowardsFuturePayments,
String externalId, Collection<SavingsAccountChargeData> charges, Integer rowIndex, String locale, String dateFormat) {
return new RecurringDepositAccountData(clientId, productId, fieldOfficerId, submittedOnDate, interestCompoundingPeriodTypeEnum,
interestPostingPeriodTypeEnum, interestCalculationTypeEnum, interestCalculationDaysInYearTypeEnum, lockinPeriodFrequency,
lockinPeriodFrequencyTypeEnum, depositAmount, depositPeriod, depositPeriodFrequencyId, expectedFirstDepositOnDate,
recurringFrequency, recurringFrequencyTypeEnum, isCalendarInherited, isMandatoryDeposit, allowWithdrawal,
adjustAdvanceTowardsFuturePayments, externalId, charges, rowIndex, locale, dateFormat);
}
private RecurringDepositAccountData(Long clientId, Long productId, Long fieldofficerId, LocalDate submittedOnDate,
EnumOptionData interestCompoundingPeriodType, EnumOptionData interestPostingPeriodType, EnumOptionData interestCalculationType,
EnumOptionData interestCalculationDaysInYearType, Integer lockinPeriodFrequency, EnumOptionData lockinPeriodFrequencyType,
BigDecimal depositAmount, Integer depositPeriod, Long depositPeriodFrequencyId, LocalDate expectedFirstDepositOnDate,
Integer recurringFrequency, EnumOptionData recurringFrequencyType, boolean isCalendarInherited, boolean isMandatoryDeposit,
boolean allowWithdrawal, boolean adjustAdvanceTowardsFuturePayments, String externalId,
Collection<SavingsAccountChargeData> charges, Integer rowIndex, String locale, String dateFormat) {
super(clientId, productId, fieldofficerId, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, lockinPeriodFrequency, lockinPeriodFrequencyType, externalId, charges);
this.preClosurePenalApplicable = false;
this.preClosurePenalInterest = null;
this.preClosurePenalInterestOnType = null;
this.minDepositTerm = null;
this.maxDepositTerm = null;
this.minDepositTermType = null;
this.maxDepositTermType = null;
this.inMultiplesOfDepositTerm = null;
this.inMultiplesOfDepositTermType = null;
this.depositAmount = null;
this.maturityAmount = null;
this.maturityDate = null;
this.depositPeriod = depositPeriod;
this.depositPeriodFrequency = null;
this.mandatoryRecommendedDepositAmount = depositAmount;
this.totalOverdueAmount = null;
this.noOfOverdueInstallments = null;
this.isMandatoryDeposit = isMandatoryDeposit;
this.allowWithdrawal = allowWithdrawal;
this.adjustAdvanceTowardsFuturePayments = adjustAdvanceTowardsFuturePayments;
this.expectedFirstDepositOnDate = expectedFirstDepositOnDate;
this.isCalendarInherited = isCalendarInherited;
this.recurringFrequency = recurringFrequency;
this.recurringFrequencyType = recurringFrequencyType;
this.onAccountClosure = null;
this.preClosurePenalInterestOnTypeOptions = null;
this.periodFrequencyTypeOptions = null;
this.savingsAccounts = null;
this.onAccountClosureOptions = null;
this.paymentTypeOptions = null;
this.rowIndex = rowIndex;
this.dateFormat = dateFormat;
this.locale = locale;
this.submittedOnDate = submittedOnDate;
this.depositPeriodFrequencyId = depositPeriodFrequencyId;
}
public Integer getRowIndex() {
return rowIndex;
}
public static RecurringDepositAccountData instance(final DepositAccountData depositAccountData, final boolean preClosurePenalApplicable,
final BigDecimal preClosurePenalInterest, final EnumOptionData preClosurePenalInterestOnType, final Integer minDepositTerm,
final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType,
final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount,
final BigDecimal maturityAmount, final LocalDate maturityDate, final Integer depositPeriod,
final EnumOptionData depositPeriodFrequency, final BigDecimal mandatoryRecommendedDepositAmount,
final EnumOptionData onAccountClosure, final LocalDate expectedFirstDepositOnDate, final BigDecimal totalOverdueAmount,
final Integer noOfOverdueInstallments, final boolean isMandatoryDeposit, final boolean allowWithdrawal,
final boolean adjustAdvanceTowardsFuturePayments, final boolean isCalendarInherited) {
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue());
final Collection<EnumOptionData> onAccountClosureOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<SavingsAccountData> savingsAccountDatas = null;
final Integer recurringFrequency = null;
final EnumOptionData recurringFrequencyType = null;
return new RecurringDepositAccountData(depositAccountData.id, depositAccountData.accountNo, depositAccountData.externalId,
depositAccountData.groupId, depositAccountData.groupName, depositAccountData.clientId, depositAccountData.clientName,
depositAccountData.depositProductId, depositAccountData.depositProductName, depositAccountData.fieldOfficerId,
depositAccountData.fieldOfficerName, depositAccountData.status, depositAccountData.timeline, depositAccountData.currency,
depositAccountData.nominalAnnualInterestRate, depositAccountData.interestCompoundingPeriodType,
depositAccountData.interestPostingPeriodType, depositAccountData.interestCalculationType,
depositAccountData.interestCalculationDaysInYearType, depositAccountData.minRequiredOpeningBalance,
depositAccountData.lockinPeriodFrequency, depositAccountData.lockinPeriodFrequencyType,
depositAccountData.withdrawalFeeForTransfers, depositAccountData.minBalanceForInterestCalculation,
depositAccountData.summary, depositAccountData.transactions, depositAccountData.productOptions,
depositAccountData.fieldOfficerOptions, depositAccountData.interestCompoundingPeriodTypeOptions,
depositAccountData.interestPostingPeriodTypeOptions, depositAccountData.interestCalculationTypeOptions,
depositAccountData.interestCalculationDaysInYearTypeOptions, depositAccountData.lockinPeriodFrequencyTypeOptions,
depositAccountData.withdrawalFeeTypeOptions, depositAccountData.charges, depositAccountData.chargeOptions,
depositAccountData.accountChart, depositAccountData.chartTemplate, preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType,
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate,
depositPeriod, depositPeriodFrequency, mandatoryRecommendedDepositAmount, periodFrequencyTypeOptions, depositType,
onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, expectedFirstDepositOnDate,
totalOverdueAmount, noOfOverdueInstallments, isMandatoryDeposit, allowWithdrawal, adjustAdvanceTowardsFuturePayments,
isCalendarInherited, recurringFrequency, recurringFrequencyType, depositAccountData.withHoldTax,
depositAccountData.taxGroup);
}
public static RecurringDepositAccountData withInterestChartAndRecurringDetails(final RecurringDepositAccountData account,
final DepositAccountInterestRateChartData accountChart, final Integer recurringFrequency,
final EnumOptionData recurringFrequencyType) {
return new RecurringDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.clientId, account.clientName, account.depositProductId, account.depositProductName, account.fieldOfficerId,
account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate,
account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType,
account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.minBalanceForInterestCalculation,
account.summary, account.transactions, account.productOptions, account.fieldOfficerOptions,
account.interestCompoundingPeriodTypeOptions, account.interestPostingPeriodTypeOptions,
account.interestCalculationTypeOptions, account.interestCalculationDaysInYearTypeOptions,
account.lockinPeriodFrequencyTypeOptions, account.withdrawalFeeTypeOptions, account.charges, account.chargeOptions,
accountChart, account.chartTemplate, account.preClosurePenalApplicable, account.preClosurePenalInterest,
account.preClosurePenalInterestOnType, account.preClosurePenalInterestOnTypeOptions, account.minDepositTerm,
account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, account.inMultiplesOfDepositTerm,
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, account.mandatoryRecommendedDepositAmount,
account.periodFrequencyTypeOptions, account.depositType, account.onAccountClosure, account.onAccountClosureOptions,
account.paymentTypeOptions, account.savingsAccounts, account.expectedFirstDepositOnDate, account.totalOverdueAmount,
account.noOfOverdueInstallments, account.isMandatoryDeposit, account.allowWithdrawal,
account.adjustAdvanceTowardsFuturePayments, account.isCalendarInherited, recurringFrequency, recurringFrequencyType,
account.withHoldTax, account.taxGroup);
}
public static RecurringDepositAccountData withTemplateOptions(final RecurringDepositAccountData account,
final RecurringDepositAccountData template, final Collection<SavingsAccountTransactionData> transactions,
final Collection<SavingsAccountChargeData> charges) {
if (template == null) {
final Collection<DepositProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
return withTemplateOptions(account, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions, charges, chargeOptions,
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions);
}
return new RecurringDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.clientId, account.clientName, account.depositProductId, account.depositProductName, account.fieldOfficerId,
account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate,
account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType,
account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.minBalanceForInterestCalculation,
account.summary, transactions, template.productOptions, template.fieldOfficerOptions,
template.interestCompoundingPeriodTypeOptions, template.interestPostingPeriodTypeOptions,
template.interestCalculationTypeOptions, template.interestCalculationDaysInYearTypeOptions,
template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions,
account.accountChart, account.chartTemplate, account.preClosurePenalApplicable, account.preClosurePenalInterest,
account.preClosurePenalInterestOnType, template.preClosurePenalInterestOnTypeOptions, account.minDepositTerm,
account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, account.inMultiplesOfDepositTerm,
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, account.mandatoryRecommendedDepositAmount,
template.periodFrequencyTypeOptions, account.depositType, account.onAccountClosure, account.onAccountClosureOptions,
account.paymentTypeOptions, account.savingsAccounts, account.expectedFirstDepositOnDate, account.totalOverdueAmount,
account.noOfOverdueInstallments, account.isMandatoryDeposit, account.allowWithdrawal,
account.adjustAdvanceTowardsFuturePayments, account.isCalendarInherited, account.recurringFrequency,
account.recurringFrequencyType, account.withHoldTax, account.taxGroup);
}
public static RecurringDepositAccountData withTemplateOptions(final RecurringDepositAccountData account,
final Collection<DepositProductData> productOptions, final Collection<StaffData> fieldOfficerOptions,
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges,
final Collection<ChargeData> chargeOptions, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Collection<EnumOptionData> periodFrequencyTypeOptions) {
return new RecurringDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.clientId, account.clientName, account.depositProductId, account.depositProductName, account.fieldOfficerId,
account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate,
account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType,
account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.minBalanceForInterestCalculation,
account.summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, account.accountChart,
account.chartTemplate, account.preClosurePenalApplicable, account.preClosurePenalInterest,
account.preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, account.minDepositTerm, account.maxDepositTerm,
account.minDepositTermType, account.maxDepositTermType, account.inMultiplesOfDepositTerm,
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, account.mandatoryRecommendedDepositAmount,
periodFrequencyTypeOptions, account.depositType, account.onAccountClosure, account.onAccountClosureOptions,
account.paymentTypeOptions, account.savingsAccounts, account.expectedFirstDepositOnDate, account.totalOverdueAmount,
account.noOfOverdueInstallments, account.isMandatoryDeposit, account.allowWithdrawal,
account.adjustAdvanceTowardsFuturePayments, account.isCalendarInherited, account.recurringFrequency,
account.recurringFrequencyType, account.withHoldTax, account.taxGroup);
}
public static RecurringDepositAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
final String groupName) {
final Long id = null;
final String accountNo = null;
final String externalId = null;
final Long productId = null;
final String productName = null;
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null;
final SavingsAccountApplicationTimelineData timeline = null;
final CurrencyData currency = null;
final BigDecimal nominalAnnualInterestRate = null;
final EnumOptionData interestPeriodType = null;
final EnumOptionData interestPostingPeriodType = null;
final EnumOptionData interestCalculationType = null;
final EnumOptionData interestCalculationDaysInYearType = null;
final BigDecimal minRequiredOpeningBalance = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final boolean withdrawalFeeForTransfers = false;
final BigDecimal minBalanceForInterestCalculation = null;
final SavingsAccountSummaryData summary = null;
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<DepositProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<SavingsAccountChargeData> charges = null;
final Collection<ChargeData> chargeOptions = null;
final DepositAccountInterestRateChartData accountChart = null;
final DepositAccountInterestRateChartData chartTemplate = null;
final boolean preClosurePenalApplicable = false;
final BigDecimal preClosurePenalInterest = null;
final EnumOptionData preClosurePenalInterestOnType = null;
final Integer minDepositTerm = null;
final Integer maxDepositTerm = null;
final EnumOptionData minDepositTermType = null;
final EnumOptionData maxDepositTermType = null;
final Integer inMultiplesOfDepositTerm = null;
final EnumOptionData inMultiplesOfDepositTermType = null;
final BigDecimal depositAmount = null;
final BigDecimal maturityAmount = null;
final LocalDate maturityDate = null;
final Integer depositPeriod = null;
final EnumOptionData depositPeriodFrequency = null;
final BigDecimal mandatoryRecommendedDepositAmount = null;
final EnumOptionData onAccountClosure = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.RECURRING_DEPOSIT.getValue());
final Collection<EnumOptionData> onAccountClosureOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<SavingsAccountData> savingsAccountDatas = null;
final LocalDate expectedFirstDepositOnDate = null;
final BigDecimal totalOverdueAmount = null;
final Integer noOfOverdueInstallments = null;
final boolean isMandatoryDeposit = false;
final boolean allowWithdrawal = false;
final boolean adjustAdvanceTowardsFuturePayments = false;
final boolean isCalendarInherited = false;
final Integer recurringFrequency = null;
final EnumOptionData recurringFrequencyType = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
return new RecurringDepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, minBalanceForInterestCalculation, summary,
transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, preClosurePenalApplicable,
preClosurePenalInterest, preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm,
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, mandatoryRecommendedDepositAmount,
periodFrequencyTypeOptions, depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas,
expectedFirstDepositOnDate, totalOverdueAmount, noOfOverdueInstallments, isMandatoryDeposit, allowWithdrawal,
adjustAdvanceTowardsFuturePayments, isCalendarInherited, recurringFrequency, recurringFrequencyType, withHoldTax, taxGroup);
}
public static RecurringDepositAccountData preClosureDetails(final Long accountId, final BigDecimal maturityAmount,
final Collection<EnumOptionData> onAccountClosureOptions, final Collection<PaymentTypeData> paymentTypeOptions,
final Collection<SavingsAccountData> savingsAccountDatas) {
final String accountNo = null;
final String externalId = null;
final Long productId = null;
final String productName = null;
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null;
final SavingsAccountApplicationTimelineData timeline = null;
final CurrencyData currency = null;
final BigDecimal nominalAnnualInterestRate = null;
final EnumOptionData interestPeriodType = null;
final EnumOptionData interestPostingPeriodType = null;
final EnumOptionData interestCalculationType = null;
final EnumOptionData interestCalculationDaysInYearType = null;
final BigDecimal minRequiredOpeningBalance = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final boolean withdrawalFeeForTransfers = false;
final BigDecimal minBalanceForInterestCalculation = null;
final SavingsAccountSummaryData summary = null;
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<DepositProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<SavingsAccountChargeData> charges = null;
final Collection<ChargeData> chargeOptions = null;
final DepositAccountInterestRateChartData accountChart = null;
final DepositAccountInterestRateChartData chartTemplate = null;
final boolean preClosurePenalApplicable = false;
final BigDecimal preClosurePenalInterest = null;
final EnumOptionData preClosurePenalInterestOnType = null;
final Integer minDepositTerm = null;
final Integer maxDepositTerm = null;
final EnumOptionData minDepositTermType = null;
final EnumOptionData maxDepositTermType = null;
final Integer inMultiplesOfDepositTerm = null;
final EnumOptionData inMultiplesOfDepositTermType = null;
final BigDecimal depositAmount = null;
final LocalDate maturityDate = null;
final Integer depositPeriod = null;
final EnumOptionData depositPeriodFrequency = null;
final BigDecimal mandatoryRecommendedDepositAmount = null;
final EnumOptionData onAccountClosure = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.RECURRING_DEPOSIT.getValue());
final Long groupId = null;
final String groupName = null;
final Long clientId = null;
final String clientName = null;
final LocalDate expectedFirstDepositOnDate = null;
final BigDecimal totalOverdueAmount = null;
final Integer noOfOverdueInstallments = null;
final boolean isMandatoryDeposit = false;
final boolean allowWithdrawal = false;
final boolean adjustAdvanceTowardsFuturePayments = false;
final boolean isCalendarInherited = false;
final Integer recurringFrequency = null;
final EnumOptionData recurringFrequencyType = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
return new RecurringDepositAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, minBalanceForInterestCalculation, summary,
transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, preClosurePenalApplicable,
preClosurePenalInterest, preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm,
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, mandatoryRecommendedDepositAmount,
periodFrequencyTypeOptions, depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas,
expectedFirstDepositOnDate, totalOverdueAmount, noOfOverdueInstallments, isMandatoryDeposit, allowWithdrawal,
adjustAdvanceTowardsFuturePayments, isCalendarInherited, recurringFrequency, recurringFrequencyType, withHoldTax, taxGroup);
}
public static RecurringDepositAccountData withClosureTemplateDetails(final RecurringDepositAccountData account,
final Collection<EnumOptionData> onAccountClosureOptions, final Collection<PaymentTypeData> paymentTypeOptions,
final Collection<SavingsAccountData> savingsAccountDatas) {
return new RecurringDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.clientId, account.clientName, account.depositProductId, account.depositProductName, account.fieldOfficerId,
account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate,
account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType,
account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.minBalanceForInterestCalculation,
account.summary, account.transactions, account.productOptions, account.fieldOfficerOptions,
account.interestCompoundingPeriodTypeOptions, account.interestPostingPeriodTypeOptions,
account.interestCalculationTypeOptions, account.interestCalculationDaysInYearTypeOptions,
account.lockinPeriodFrequencyTypeOptions, account.withdrawalFeeTypeOptions, account.charges, account.chargeOptions,
account.accountChart, account.chartTemplate, account.preClosurePenalApplicable, account.preClosurePenalInterest,
account.preClosurePenalInterestOnType, account.preClosurePenalInterestOnTypeOptions, account.minDepositTerm,
account.maxDepositTerm, account.minDepositTermType, account.maxDepositTermType, account.inMultiplesOfDepositTerm,
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, account.mandatoryRecommendedDepositAmount,
account.periodFrequencyTypeOptions, account.depositType, account.onAccountClosure, onAccountClosureOptions,
paymentTypeOptions, savingsAccountDatas, account.expectedFirstDepositOnDate, account.totalOverdueAmount,
account.noOfOverdueInstallments, account.isMandatoryDeposit, account.allowWithdrawal,
account.adjustAdvanceTowardsFuturePayments, account.isCalendarInherited, account.recurringFrequency,
account.recurringFrequencyType, account.withHoldTax, account.taxGroup);
}
private RecurringDepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId,
final String groupName, final Long clientId, final String clientName, final Long productId, final String productName,
final Long fieldofficerId, final String fieldofficerName, final SavingsAccountStatusEnumData status,
final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate,
final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType,
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType,
final boolean withdrawalFeeForTransfers, final BigDecimal minBalanceForInterestCalculation,
final SavingsAccountSummaryData summary, final Collection<SavingsAccountTransactionData> transactions,
final Collection<DepositProductData> productOptions, final Collection<StaffData> fieldOfficerOptions,
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions,
final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate,
final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest,
final EnumOptionData preClosurePenalInterestOnType, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Integer minDepositTerm, final Integer maxDepositTerm, final EnumOptionData minDepositTermType,
final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm,
final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount, final BigDecimal maturityAmount,
final LocalDate maturityDate, final Integer depositPeriod, final EnumOptionData depositPeriodFrequency,
final BigDecimal mandatoryRecommendedDepositAmount, final Collection<EnumOptionData> periodFrequencyTypeOptions,
final EnumOptionData depositType, final EnumOptionData onAccountClosure,
final Collection<EnumOptionData> onAccountClosureOptions, final Collection<PaymentTypeData> paymentTypeOptions,
final Collection<SavingsAccountData> savingsAccountDatas, final LocalDate expectedFirstDepositOnDate,
final BigDecimal totalOverdueAmount, final Integer noOfOverdueInstallments, final boolean isMandatoryDeposit,
final boolean allowWithdrawal, final boolean adjustAdvanceTowardsFuturePayments, final boolean isCalendarInherited,
final Integer recurringFrequency, final EnumOptionData recurringFrequencyType, final boolean withHoldTax,
final TaxGroupData taxGroup) {
super(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldofficerId, fieldofficerName,
status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges,
chargeOptions, accountChart, chartTemplate, depositType, minBalanceForInterestCalculation, withHoldTax, taxGroup);
this.preClosurePenalApplicable = preClosurePenalApplicable;
this.preClosurePenalInterest = preClosurePenalInterest;
this.preClosurePenalInterestOnType = preClosurePenalInterestOnType;
this.minDepositTerm = minDepositTerm;
this.maxDepositTerm = maxDepositTerm;
this.minDepositTermType = minDepositTermType;
this.maxDepositTermType = maxDepositTermType;
this.inMultiplesOfDepositTerm = inMultiplesOfDepositTerm;
this.inMultiplesOfDepositTermType = inMultiplesOfDepositTermType;
this.depositAmount = depositAmount;
this.maturityAmount = maturityAmount;
this.maturityDate = maturityDate;
this.depositPeriod = depositPeriod;
this.depositPeriodFrequency = depositPeriodFrequency;
this.expectedFirstDepositOnDate = expectedFirstDepositOnDate;
this.mandatoryRecommendedDepositAmount = mandatoryRecommendedDepositAmount;
this.totalOverdueAmount = totalOverdueAmount;
this.noOfOverdueInstallments = noOfOverdueInstallments;
this.isMandatoryDeposit = isMandatoryDeposit;
this.allowWithdrawal = allowWithdrawal;
this.adjustAdvanceTowardsFuturePayments = adjustAdvanceTowardsFuturePayments;
this.isCalendarInherited = isCalendarInherited;
this.recurringFrequency = recurringFrequency;
this.recurringFrequencyType = recurringFrequencyType;
this.preClosurePenalInterestOnTypeOptions = preClosurePenalInterestOnTypeOptions;
this.periodFrequencyTypeOptions = periodFrequencyTypeOptions;
this.onAccountClosure = onAccountClosure;
this.savingsAccounts = savingsAccountDatas;
// account close template options
this.onAccountClosureOptions = onAccountClosureOptions;
this.paymentTypeOptions = paymentTypeOptions;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (!(obj instanceof RecurringDepositAccountData)) {
return false;
}
final RecurringDepositAccountData rhs = (RecurringDepositAccountData) obj;
return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).toHashCode();
}
public boolean isIsMandatoryDeposit() {
return this.isMandatoryDeposit;
}
public boolean isIsCalendarInherited() {
return this.isCalendarInherited;
}
}
@@ -0,0 +1,478 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartData;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
/**
* Immutable data object represent a Recurring Deposit product.
*/
public final class RecurringDepositProductData extends DepositProductData {
private boolean preClosurePenalApplicable;
private BigDecimal preClosurePenalInterest;
private EnumOptionData preClosurePenalInterestOnType;
private Integer minDepositTerm;
private Integer maxDepositTerm;
private EnumOptionData minDepositTermType;
private EnumOptionData maxDepositTermType;
private Integer inMultiplesOfDepositTerm;
private EnumOptionData inMultiplesOfDepositTermType;
private BigDecimal minDepositAmount;
private BigDecimal depositAmount;
private BigDecimal maxDepositAmount;
private boolean isMandatoryDeposit;
private boolean allowWithdrawal;
private boolean adjustAdvanceTowardsFuturePayments;
private Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions;
private Collection<EnumOptionData> periodFrequencyTypeOptions;
public static RecurringDepositProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData accountingRule,
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<TaxGroupData> taxGroupOptions) {
final Long id = null;
final String name = null;
final String shortName = null;
final String description = null;
final BigDecimal nominalAnnualInterestRate = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal minBalanceForInterestCalculation = null;
final Map<String, Object> accountingMappings = null;
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
final Collection<ChargeData> charges = null;
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
final Collection<InterestRateChartData> interestRateCharts = null;
final boolean preClosurePenalApplicable = false;
final BigDecimal preClosurePenalInterest = null;
final EnumOptionData preClosurePenalInterestOnType = null;
final boolean isMandatoryDeposit = false;
final boolean allowWithdrawal = false;
final boolean adjustAdvanceTowardsFuturePayments = false;
final Integer minDepositTerm = null;
final Integer maxDepositTerm = null;
final EnumOptionData minDepositTermType = null;
final EnumOptionData maxDepositTermType = null;
final Integer inMultiplesOfDepositTerm = null;
final EnumOptionData inMultiplesOfDepositTermType = null;
final BigDecimal minDepositAmount = null;
final BigDecimal depositAmount = null;
final BigDecimal maxDepositAmount = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
return new RecurringDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
lockinPeriodFrequency, lockinPeriodFrequencyType, minBalanceForInterestCalculation, accountingRule, accountingMappings,
paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate,
preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions,
minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm,
inMultiplesOfDepositTermType, isMandatoryDeposit, allowWithdrawal, adjustAdvanceTowardsFuturePayments,
periodFrequencyTypeOptions, minDepositAmount, depositAmount, maxDepositAmount, withHoldTax, taxGroup, taxGroupOptions);
}
public static RecurringDepositProductData withCharges(final RecurringDepositProductData existingProduct,
final Collection<ChargeData> charges) {
return new RecurringDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName,
existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate,
existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType,
existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType,
existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType,
existingProduct.minBalanceForInterestCalculation, existingProduct.accountingRule, existingProduct.accountingMappings,
existingProduct.paymentChannelToFundSourceMappings, existingProduct.currencyOptions,
existingProduct.interestCompoundingPeriodTypeOptions, existingProduct.interestPostingPeriodTypeOptions,
existingProduct.interestCalculationTypeOptions, existingProduct.interestCalculationDaysInYearTypeOptions,
existingProduct.lockinPeriodFrequencyTypeOptions, existingProduct.withdrawalFeeTypeOptions,
existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions,
charges, existingProduct.chargeOptions, existingProduct.penaltyOptions, existingProduct.feeToIncomeAccountMappings,
existingProduct.penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate,
existingProduct.preClosurePenalApplicable, existingProduct.preClosurePenalInterest,
existingProduct.preClosurePenalInterestOnType, existingProduct.preClosurePenalInterestOnTypeOptions,
existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType,
existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType,
existingProduct.isMandatoryDeposit, existingProduct.allowWithdrawal, existingProduct.adjustAdvanceTowardsFuturePayments,
existingProduct.periodFrequencyTypeOptions, existingProduct.minDepositAmount, existingProduct.depositAmount,
existingProduct.maxDepositAmount, existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.taxGroupOptions);
}
/**
* Returns a {@link RecurringDepositProductData} that contains and exist {@link RecurringDepositProductData} data
* with further template data for dropdowns.
*
* @param taxGroupOptions
* TODO
*/
public static RecurringDepositProductData withTemplate(final RecurringDepositProductData existingProduct,
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<TaxGroupData> taxGroupOptions) {
return new RecurringDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName,
existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate,
existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType,
existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType,
existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType,
existingProduct.minBalanceForInterestCalculation, existingProduct.accountingRule, existingProduct.accountingMappings,
existingProduct.paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions,
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings,
existingProduct.interestRateCharts, chartTemplate, existingProduct.preClosurePenalApplicable,
existingProduct.preClosurePenalInterest, existingProduct.preClosurePenalInterestOnType,
preClosurePenalInterestOnTypeOptions, existingProduct.minDepositTerm, existingProduct.maxDepositTerm,
existingProduct.minDepositTermType, existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm,
existingProduct.inMultiplesOfDepositTermType, existingProduct.isMandatoryDeposit, existingProduct.allowWithdrawal,
existingProduct.adjustAdvanceTowardsFuturePayments, periodFrequencyTypeOptions, existingProduct.minDepositAmount,
existingProduct.depositAmount, existingProduct.maxDepositAmount, existingProduct.withHoldTax, existingProduct.taxGroup,
taxGroupOptions);
}
public static RecurringDepositProductData withAccountingDetails(final RecurringDepositProductData existingProduct,
final Map<String, Object> accountingMappings, final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings,
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings,
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings) {
final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<EnumOptionData> accountingRuleOptions = null;
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<ChargeData> penaltyOptions = null;
return new RecurringDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName,
existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate,
existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType,
existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType,
existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType,
existingProduct.minBalanceForInterestCalculation, existingProduct.accountingRule, accountingMappings,
paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, existingProduct.charges,
chargeOptions, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings,
existingProduct.interestRateCharts, existingProduct.chartTemplate, existingProduct.preClosurePenalApplicable,
existingProduct.preClosurePenalInterest, existingProduct.preClosurePenalInterestOnType,
existingProduct.preClosurePenalInterestOnTypeOptions, existingProduct.minDepositTerm, existingProduct.maxDepositTerm,
existingProduct.minDepositTermType, existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm,
existingProduct.inMultiplesOfDepositTermType, existingProduct.isMandatoryDeposit, existingProduct.allowWithdrawal,
existingProduct.adjustAdvanceTowardsFuturePayments, existingProduct.periodFrequencyTypeOptions,
existingProduct.minDepositAmount, existingProduct.depositAmount, existingProduct.maxDepositAmount,
existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.taxGroupOptions);
}
public static RecurringDepositProductData instance(final DepositProductData depositProductData, final boolean preClosurePenalApplicable,
final BigDecimal preClosurePenalInterest, final EnumOptionData preClosurePenalInterestOnType, final Integer minDepositTerm,
final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType,
final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, final boolean isMandatoryDeposit,
boolean allowWithdrawal, boolean adjustAdvanceTowardsFuturePayments, final BigDecimal minDepositAmount,
final BigDecimal depositAmount, final BigDecimal maxDepositAmount) {
final Map<String, Object> accountingMappings = null;
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<EnumOptionData> accountingRuleOptions = null;
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<ChargeData> penaltyOptions = null;
final Collection<ChargeData> charges = null;
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
final Collection<InterestRateChartData> interestRateCharts = null;
final InterestRateChartData chartTemplate = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final Collection<TaxGroupData> taxGroupOptions = null;
return new RecurringDepositProductData(depositProductData.id, depositProductData.name, depositProductData.shortName,
depositProductData.description, depositProductData.currency, depositProductData.nominalAnnualInterestRate,
depositProductData.interestCompoundingPeriodType, depositProductData.interestPostingPeriodType,
depositProductData.interestCalculationType, depositProductData.interestCalculationDaysInYearType,
depositProductData.lockinPeriodFrequency, depositProductData.lockinPeriodFrequencyType,
depositProductData.minBalanceForInterestCalculation, depositProductData.accountingRule, accountingMappings,
paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate,
preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions,
minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm,
inMultiplesOfDepositTermType, isMandatoryDeposit, allowWithdrawal, adjustAdvanceTowardsFuturePayments,
periodFrequencyTypeOptions, minDepositAmount, depositAmount, maxDepositAmount, depositProductData.withHoldTax,
depositProductData.taxGroup, taxGroupOptions);
}
public static RecurringDepositProductData lookup(final Long id, final String name) {
final String shortName = null;
final CurrencyData currency = null;
final String description = null;
final BigDecimal nominalAnnualInterestRate = null;
final EnumOptionData interestCompoundingPeriodType = null;
final EnumOptionData interestPostingPeriodType = null;
final EnumOptionData interestCalculationType = null;
final EnumOptionData interestCalculationDaysInYearType = null;
final Integer lockinPeriodFrequency = null;
final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal minBalanceForInterestCalculation = null;
final EnumOptionData accountingType = null;
final Map<String, Object> accountingMappings = null;
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<EnumOptionData> accountingRuleOptions = null;
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
final Collection<ChargeData> charges = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<ChargeData> penaltyOptions = null;
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
final Collection<InterestRateChartData> interestRateCharts = null;
final InterestRateChartData chartTemplate = null;
final boolean preClosurePenalApplicable = false;
final BigDecimal preClosurePenalInterest = null;
final EnumOptionData preClosurePenalInterestOnType = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final boolean isMandatoryDeposit = false;
final boolean allowWithdrawal = false;
final boolean adjustAdvanceTowardsFuturePayments = false;
final Integer minDepositTerm = null;
final Integer maxDepositTerm = null;
final EnumOptionData minDepositTermType = null;
final EnumOptionData maxDepositTermType = null;
final Integer inMultiplesOfDepositTerm = null;
final EnumOptionData inMultiplesOfDepositTermType = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final BigDecimal minDepositAmount = null;
final BigDecimal depositAmount = null;
final BigDecimal maxDepositAmount = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
final Collection<TaxGroupData> taxGroupOptions = null;
return new RecurringDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
lockinPeriodFrequency, lockinPeriodFrequencyType, minBalanceForInterestCalculation, accountingType, accountingMappings,
paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate,
preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions,
minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm,
inMultiplesOfDepositTermType, isMandatoryDeposit, allowWithdrawal, adjustAdvanceTowardsFuturePayments,
periodFrequencyTypeOptions, minDepositAmount, depositAmount, maxDepositAmount, withHoldTax, taxGroup, taxGroupOptions);
}
public static RecurringDepositProductData withInterestChart(final RecurringDepositProductData product,
final Collection<InterestRateChartData> interestRateCharts) {
return new RecurringDepositProductData(product.id, product.name, product.shortName, product.description, product.currency,
product.nominalAnnualInterestRate, product.interestCompoundingPeriodType, product.interestPostingPeriodType,
product.interestCalculationType, product.interestCalculationDaysInYearType, product.lockinPeriodFrequency,
product.lockinPeriodFrequencyType, product.minBalanceForInterestCalculation, product.accountingRule,
product.accountingMappings, product.paymentChannelToFundSourceMappings, product.currencyOptions,
product.interestCompoundingPeriodTypeOptions, product.interestPostingPeriodTypeOptions,
product.interestCalculationTypeOptions, product.interestCalculationDaysInYearTypeOptions,
product.lockinPeriodFrequencyTypeOptions, product.withdrawalFeeTypeOptions, product.paymentTypeOptions,
product.accountingRuleOptions, product.accountingMappingOptions, product.charges, product.chargeOptions,
product.penaltyOptions, product.feeToIncomeAccountMappings, product.penaltyToIncomeAccountMappings, interestRateCharts,
product.chartTemplate, product.preClosurePenalApplicable, product.preClosurePenalInterest,
product.preClosurePenalInterestOnType, product.preClosurePenalInterestOnTypeOptions, product.minDepositTerm,
product.maxDepositTerm, product.minDepositTermType, product.maxDepositTermType, product.inMultiplesOfDepositTerm,
product.inMultiplesOfDepositTermType, product.isMandatoryDeposit, product.allowWithdrawal,
product.adjustAdvanceTowardsFuturePayments, product.periodFrequencyTypeOptions, product.minDepositAmount,
product.depositAmount, product.maxDepositAmount, product.withHoldTax, product.taxGroup, product.taxGroupOptions);
}
private RecurringDepositProductData(final Long id, final String name, final String shortName, final String description,
final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final Integer lockinPeriodFrequency,
final EnumOptionData lockinPeriodFrequencyType, final BigDecimal minBalanceForInterestCalculation,
final EnumOptionData accountingType, final Map<String, Object> accountingMappings,
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings,
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> charges,
final Collection<ChargeData> chargeOptions, final Collection<ChargeData> penaltyOptions,
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings,
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings,
final Collection<InterestRateChartData> interestRateCharts, final InterestRateChartData chartTemplate,
final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest,
final EnumOptionData preClosurePenalInterestOnType, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Integer minDepositTerm, final Integer maxDepositTerm, final EnumOptionData minDepositTermType,
final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm,
final EnumOptionData inMultiplesOfDepositTermType, final boolean isMandatoryDeposit, boolean allowWithdrawal,
boolean adjustAdvanceTowardsFuturePayments, final Collection<EnumOptionData> periodFrequencyTypeOptions,
final BigDecimal minDepositAmount, final BigDecimal depositAmount, final BigDecimal maxDepositAmount, final boolean withHoldTax,
final TaxGroupData taxGroup, final Collection<TaxGroupData> taxGroupOptions) {
super(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
lockinPeriodFrequencyType, accountingType, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation, withHoldTax, taxGroup,
taxGroupOptions);
this.preClosurePenalApplicable = preClosurePenalApplicable;
this.preClosurePenalInterest = preClosurePenalInterest;
this.preClosurePenalInterestOnType = preClosurePenalInterestOnType;
this.minDepositTerm = minDepositTerm;
this.maxDepositTerm = maxDepositTerm;
this.minDepositTermType = minDepositTermType;
this.maxDepositTermType = maxDepositTermType;
this.inMultiplesOfDepositTerm = inMultiplesOfDepositTerm;
this.inMultiplesOfDepositTermType = inMultiplesOfDepositTermType;
this.minDepositAmount = minDepositAmount;
this.depositAmount = depositAmount;
this.maxDepositAmount = maxDepositAmount;
this.isMandatoryDeposit = isMandatoryDeposit;
this.allowWithdrawal = allowWithdrawal;
this.adjustAdvanceTowardsFuturePayments = adjustAdvanceTowardsFuturePayments;
// template data
this.preClosurePenalInterestOnTypeOptions = preClosurePenalInterestOnTypeOptions;
this.periodFrequencyTypeOptions = periodFrequencyTypeOptions;
}
public EnumOptionData getMinDepositTermType() {
return minDepositTermType;
}
public boolean isPreClosurePenalApplicable() {
return preClosurePenalApplicable;
}
public BigDecimal getPreClosurePenalInterest() {
return preClosurePenalInterest;
}
public EnumOptionData getPreClosurePenalInterestOnType() {
return preClosurePenalInterestOnType;
}
public Integer getMinDepositTerm() {
return minDepositTerm;
}
public Integer getMaxDepositTerm() {
return maxDepositTerm;
}
public EnumOptionData getMaxDepositTermType() {
return maxDepositTermType;
}
public BigDecimal getMinDepositAmount() {
return minDepositAmount;
}
public BigDecimal getDepositAmount() {
return depositAmount;
}
public BigDecimal getMaxDepositAmount() {
return maxDepositAmount;
}
public Integer getInMultiplesOfDepositTerm() {
return inMultiplesOfDepositTerm;
}
public EnumOptionData getInMultiplesOfDepositTermType() {
return inMultiplesOfDepositTermType;
}
public boolean isMandatoryDeposit() {
return isMandatoryDeposit;
}
public boolean isAllowWithdrawal() {
return allowWithdrawal;
}
public boolean isAdjustAdvanceTowardsFuturePayments() {
return adjustAdvanceTowardsFuturePayments;
}
}
@@ -0,0 +1,57 @@
/**
* 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.portfolio.savings.data;
import java.time.LocalDate;
public final class SavingsAccountAnnualFeeData {
private final Long id;
private final Long accountId;
private final String accountNo;
private final LocalDate nextAnnualFeeDueDate;
public static SavingsAccountAnnualFeeData instance(final Long id, final Long accountId, final String accountNo,
final LocalDate nextAnnualFeeDueDate) {
return new SavingsAccountAnnualFeeData(id, accountId, accountNo, nextAnnualFeeDueDate);
}
private SavingsAccountAnnualFeeData(final Long id, final Long accountId, final String accountNo, final LocalDate nextAnnualFeeDueDate) {
this.id = id;
this.accountId = accountId;
this.accountNo = accountNo;
this.nextAnnualFeeDueDate = nextAnnualFeeDueDate;
}
public Long getId() {
return this.id;
}
public Long getAccountId() {
return this.accountId;
}
public LocalDate getNextAnnualFeeDueDate() {
return this.nextAnnualFeeDueDate;
}
public String getAccountNo() {
return this.accountNo;
}
}
@@ -0,0 +1,158 @@
/**
* 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.portfolio.savings.data;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.amountParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeIntervalParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.MonthDay;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SavingsAccountChargeDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public SavingsAccountChargeDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateAdd(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
SavingsAccountConstant.SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Long chargeId = this.fromApiJsonHelper.extractLongNamed(chargeIdParamName, element);
baseDataValidator.reset().parameter(chargeIdParamName).value(chargeId).notNull().integerGreaterThanZero();
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(amountParamName, element);
baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount();
if (this.fromApiJsonHelper.parameterExists(dueAsOfDateParamName, element)) {
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate).notNull();
}
if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) {
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element);
baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDay).notNull();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateUpdate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
SavingsAccountConstant.SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(amountParamName, element);
baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount();
if (this.fromApiJsonHelper.parameterExists(dueAsOfDateParamName, element)) {
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate).notNull();
}
if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) {
final String monthDayFormat = this.fromApiJsonHelper.extractMonthDayFormatParameter(element.getAsJsonObject());
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element.getAsJsonObject(),
monthDayFormat, locale);
baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDay).notNull();
}
if (this.fromApiJsonHelper.parameterExists(feeIntervalParamName, element)) {
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed(feeIntervalParamName, element, Locale.getDefault());
baseDataValidator.reset().parameter(feeIntervalParamName).value(feeInterval).notNull().inMinMaxRange(1, 12);
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validatePayCharge(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
SavingsAccountConstant.SAVINGS_ACCOUNT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(amountParamName, element);
baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount();
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element);
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate).notNull();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -0,0 +1,72 @@
/**
* 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.portfolio.savings.data;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
public class SavingsAccountConstant extends SavingsApiConstants {
/**
* These parameters will match the class level parameters of {@link SavingsProductData}. Where possible, we try to
* get response parameters to match those of request parameters.
*/
protected static final Set<String> SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, monthDayFormatParamName, staffIdParamName, isGSIM, isParentAccount, accountNoParamName,
externalIdParamName, clientIdParamName, groupIdParamName, productIdParamName, fieldOfficerIdParamName, submittedOnDateParamName,
nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName,
interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName,
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
// withdrawalFeeAmountParamName, withdrawalFeeTypeParamName,
withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, chargesParamName, allowOverdraftParamName,
overdraftLimitParamName, minRequiredBalanceParamName, enforceMinRequiredBalanceParamName, lienAllowedParamName,
maxAllowedLienLimitParamName, nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
withHoldTaxParamName, datatables, gsimApplicationId, gsimLastApplication));
/**
* These parameters will match the class level parameters of {@link SavingsAccountData}. Where possible, we try to
* get response parameters to match those of request parameters.
*/
protected static final Set<String> SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName,
paymentTypeIdParamName, transactionAccountNumberParamName, checkNumberParamName, routingCodeParamName,
receiptNumberParamName, bankNumberParamName, retailEntriesParamName, childAccountIdParamName, noteParamName));
protected static final Set<String> SAVINGS_ACCOUNT_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(idParamName, accountNoParamName));
protected static final Set<String> SAVINGS_ACCOUNT_ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(localeParamName, dateFormatParamName, activatedOnDateParamName));
protected static final Set<String> SAVINGS_ACCOUNT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(localeParamName, dateFormatParamName, closedOnDateParamName, noteParamName, paymentTypeIdParamName,
withdrawBalanceParamName, transactionAccountNumberParamName, checkNumberParamName, routingCodeParamName,
receiptNumberParamName, bankNumberParamName, postInterestValidationOnClosure));
protected static final Set<String> SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName,
feeOnMonthDayParamName, monthDayFormatParamName, feeIntervalParamName));
protected static final Set<String> SAVINGS_ACCOUNT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName, noteParamName));
}
@@ -0,0 +1,69 @@
/**
* 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.portfolio.savings.data;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.useradministration.domain.AppUser;
public class SavingsAccountDataDTO {
private final Client client;
private final Group group;
private final Long savingsProductId;
private final LocalDate applicationDate;
private final AppUser appliedBy;
private final DateTimeFormatter fmt;
public SavingsAccountDataDTO(final Client client, final Group group, final Long savingsProductId, final LocalDate applicationDate,
final AppUser appliedBy, final DateTimeFormatter fmt) {
this.client = client;
this.group = group;
this.savingsProductId = savingsProductId;
this.applicationDate = applicationDate;
this.appliedBy = appliedBy;
this.fmt = fmt;
}
public Client getClient() {
return this.client;
}
public Group getGroup() {
return this.group;
}
public Long getSavingsProduct() {
return this.savingsProductId;
}
public LocalDate getApplicationDate() {
return this.applicationDate;
}
public AppUser getAppliedBy() {
return this.appliedBy;
}
public DateTimeFormatter getFmt() {
return this.fmt;
}
}
@@ -0,0 +1,534 @@
/**
* 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.portfolio.savings.data;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountNoParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.amountParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.clientIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.externalIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeIntervalParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.fieldOfficerIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.groupIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.productIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.submittedOnDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.MonthDay;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SavingsAccountDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public SavingsAccountDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForSubmit(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
SavingsAccountConstant.SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Long clientId = this.fromApiJsonHelper.extractLongNamed(clientIdParamName, element);
if (clientId != null) {
baseDataValidator.reset().parameter(clientIdParamName).value(clientId).longGreaterThanZero();
}
final Long groupId = this.fromApiJsonHelper.extractLongNamed(groupIdParamName, element);
if (groupId != null) {
baseDataValidator.reset().parameter(groupIdParamName).value(groupId).longGreaterThanZero();
}
if (clientId == null && groupId == null) {
baseDataValidator.reset().parameter(clientIdParamName).value(clientId).notNull().integerGreaterThanZero();
}
final Long productId = this.fromApiJsonHelper.extractLongNamed(productIdParamName, element);
baseDataValidator.reset().parameter(productIdParamName).value(productId).notNull().integerGreaterThanZero();
if (this.fromApiJsonHelper.parameterExists(fieldOfficerIdParamName, element)) {
final Long fieldOfficerId = this.fromApiJsonHelper.extractLongNamed(fieldOfficerIdParamName, element);
baseDataValidator.reset().parameter(fieldOfficerIdParamName).value(fieldOfficerId).ignoreIfNull().integerGreaterThanZero();
}
final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(submittedOnDateParamName, element);
baseDataValidator.reset().parameter(submittedOnDateParamName).value(submittedOnDate).notNull();
if (this.fromApiJsonHelper.parameterExists(accountNoParamName, element)) {
final String accountNo = this.fromApiJsonHelper.extractStringNamed(accountNoParamName, element);
baseDataValidator.reset().parameter(accountNoParamName).value(accountNo).notBlank().notExceedingLengthOf(20);
}
if (this.fromApiJsonHelper.parameterExists(externalIdParamName, element)) {
final String externalId = this.fromApiJsonHelper.extractStringNamed(externalIdParamName, element);
baseDataValidator.reset().parameter(externalIdParamName).value(externalId).notExceedingLengthOf(100);
}
if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) {
final BigDecimal interestRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName,
element);
baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(interestRate).notNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(interestCompoundingPeriodTypeParamName, element)) {
final Integer interestCompoundingPeriodType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(interestCompoundingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull()
.isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(interestPostingPeriodTypeParamName, element)) {
final Integer interestPostingPeriodType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(interestPostingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestPostingPeriodTypeParamName).value(interestPostingPeriodType).notNull()
.isOneOfTheseValues(SavingsPostingInterestPeriodType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) {
final Integer interestCalculationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName,
element);
baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull()
.isOneOfTheseValues(SavingsInterestCalculationType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(interestCalculationDaysInYearTypeParamName, element)) {
final Integer interestCalculationDaysInYearType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(interestCalculationDaysInYearTypeParamName, element);
baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType)
.notNull().isOneOfTheseValues(SavingsInterestCalculationDaysInYearType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).integerZeroOrGreater();
if (lockinPeriodFrequency != null) {
final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull()
.inMinMaxRange(0, 3);
}
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3);
if (lockinPeriodFrequencyType != null) {
final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull()
.integerZeroOrGreater();
}
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) {
final Boolean isWithdrawalFeeApplicableForTransfers = this.fromApiJsonHelper
.extractBooleanNamed(withdrawalFeeForTransfersParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers)
.ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) {
final String withHoldTax = this.fromApiJsonHelper.extractStringNamed(withHoldTaxParamName, element);
baseDataValidator.reset().parameter(withHoldTaxParamName).value(withHoldTax).ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(SavingsApiConstants.datatables, element)) {
final JsonArray datatables = this.fromApiJsonHelper.extractJsonArrayNamed(SavingsApiConstants.datatables, element);
baseDataValidator.reset().parameter(SavingsApiConstants.datatables).value(datatables).notNull().jsonArrayNotEmpty();
}
validateSavingsCharges(element, baseDataValidator);
validateOverdraftParams(baseDataValidator, element);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void validateSavingsCharges(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
if (element.isJsonObject()) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
final String monthDayFormat = this.fromApiJsonHelper.extractMonthDayFormatParameter(topLevelJsonElement);
if (topLevelJsonElement.has(chargesParamName) && topLevelJsonElement.get(chargesParamName).isJsonArray()) {
final JsonArray array = topLevelJsonElement.get(chargesParamName).getAsJsonArray();
for (int i = 0; i < array.size(); i++) {
final JsonObject savingsChargeElement = array.get(i).getAsJsonObject();
// final Long id =
// this.fromApiJsonHelper.extractLongNamed(idParamName,
// savingsChargeElement);
final Long chargeId = this.fromApiJsonHelper.extractLongNamed(chargeIdParamName, savingsChargeElement);
baseDataValidator.reset().parameter(chargeIdParamName).value(chargeId).longGreaterThanZero();
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed(amountParamName, savingsChargeElement, locale);
baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount();
if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, savingsChargeElement)) {
final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, savingsChargeElement,
monthDayFormat, locale);
baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDay).notNull();
}
if (this.fromApiJsonHelper.parameterExists(feeIntervalParamName, savingsChargeElement)) {
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed(feeIntervalParamName, savingsChargeElement,
Locale.getDefault());
baseDataValidator.reset().parameter(feeIntervalParamName).value(feeInterval).notNull().inMinMaxRange(1, 12);
}
}
}
}
}
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
SavingsAccountConstant.SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
Long clientId = null;
if (this.fromApiJsonHelper.parameterExists(clientIdParamName, element)) {
clientId = this.fromApiJsonHelper.extractLongNamed(clientIdParamName, element);
baseDataValidator.reset().parameter(clientIdParamName).value(clientId).ignoreIfNull().longGreaterThanZero();
Long groupId = null;
if (this.fromApiJsonHelper.parameterExists(productIdParamName, element)) {
groupId = this.fromApiJsonHelper.extractLongNamed(groupIdParamName, element);
baseDataValidator.reset().parameter(groupIdParamName).value(groupId).ignoreIfNull().longGreaterThanZero();
}
if (clientId == null && groupId == null) {
// either clientId or groupId must exists if param passed for
// update.
baseDataValidator.reset().parameter(clientIdParamName).value(clientId).notNull().integerGreaterThanZero();
}
}
Long groupId = null;
if (this.fromApiJsonHelper.parameterExists(groupIdParamName, element)) {
groupId = this.fromApiJsonHelper.extractLongNamed(groupIdParamName, element);
baseDataValidator.reset().parameter(groupIdParamName).value(groupId).ignoreIfNull().longGreaterThanZero();
if (this.fromApiJsonHelper.parameterExists(clientIdParamName, element)) {
clientId = this.fromApiJsonHelper.extractLongNamed(clientIdParamName, element);
baseDataValidator.reset().parameter(clientIdParamName).value(clientId).ignoreIfNull().longGreaterThanZero();
}
if (clientId == null && groupId == null) {
// either clientId or groupId must exists if param passed for
// update.
baseDataValidator.reset().parameter(clientIdParamName).value(clientId).notNull().integerGreaterThanZero();
}
}
if (this.fromApiJsonHelper.parameterExists(productIdParamName, element)) {
final Long productId = this.fromApiJsonHelper.extractLongNamed(productIdParamName, element);
baseDataValidator.reset().parameter(productIdParamName).value(productId).notNull().integerGreaterThanZero();
}
if (this.fromApiJsonHelper.parameterExists(fieldOfficerIdParamName, element)) {
final Long fieldOfficerId = this.fromApiJsonHelper.extractLongNamed(fieldOfficerIdParamName, element);
baseDataValidator.reset().parameter(fieldOfficerIdParamName).value(fieldOfficerId).ignoreIfNull().integerGreaterThanZero();
}
if (this.fromApiJsonHelper.parameterExists(submittedOnDateParamName, element)) {
final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(submittedOnDateParamName, element);
baseDataValidator.reset().parameter(submittedOnDateParamName).value(submittedOnDate).notNull();
}
if (this.fromApiJsonHelper.parameterExists(accountNoParamName, element)) {
final String accountNo = this.fromApiJsonHelper.extractStringNamed(accountNoParamName, element);
baseDataValidator.reset().parameter(accountNoParamName).value(accountNo).notBlank().notExceedingLengthOf(20);
}
if (this.fromApiJsonHelper.parameterExists(externalIdParamName, element)) {
final String externalId = this.fromApiJsonHelper.extractStringNamed(externalIdParamName, element);
baseDataValidator.reset().parameter(externalIdParamName).value(externalId).notExceedingLengthOf(100);
}
if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) {
final BigDecimal interestRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName,
element);
baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(interestRate).notNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(interestCompoundingPeriodTypeParamName, element)) {
final Integer interestCompoundingPeriodType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(interestCompoundingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull()
.isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(interestPostingPeriodTypeParamName, element)) {
final Integer interestPostingPeriodType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(interestPostingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestPostingPeriodTypeParamName).value(interestPostingPeriodType).notNull()
.isOneOfTheseValues(SavingsPostingInterestPeriodType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) {
final Integer interestCalculationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName,
element);
baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull()
.isOneOfTheseValues(SavingsInterestCalculationType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(interestCalculationDaysInYearTypeParamName, element)) {
final Integer interestCalculationDaysInYearType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(interestCalculationDaysInYearTypeParamName, element);
baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType)
.notNull().isOneOfTheseValues(SavingsInterestCalculationDaysInYearType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).ignoreIfNull()
.zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull()
.integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3);
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) {
final Boolean isWithdrawalFeeApplicableForTransfers = this.fromApiJsonHelper
.extractBooleanNamed(withdrawalFeeForTransfersParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers)
.ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) {
final String withHoldTax = this.fromApiJsonHelper.extractStringNamed(withHoldTaxParamName, element);
baseDataValidator.reset().parameter(withHoldTaxParamName).value(withHoldTax).ignoreIfNull().validateForBooleanValue();
}
validateOverdraftParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
private void validateLienParams(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(lienAllowedParamName, element)) {
final Boolean lienAllowed = this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element);
baseDataValidator.reset().parameter(lienAllowedParamName).value(lienAllowed).ignoreIfNull().validateForBooleanValue();
}
if (BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element))) {
final BigDecimal maxAllowedLienLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxAllowedLienLimitParamName,
element);
baseDataValidator.reset().parameter(maxAllowedLienLimitParamName).value(maxAllowedLienLimit).ignoreIfNull()
.zeroOrPositiveAmount();
}
if (BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element))
&& BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(allowOverdraftParamName, element))) {
final BigDecimal maxAllowedLienLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxAllowedLienLimitParamName,
element);
final BigDecimal overdraftLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(overdraftLimitParamName, element);
if (overdraftLimit.compareTo(maxAllowedLienLimit) > 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("Overdraft.limit.can.not.be.greater.than.lien.limit");
}
}
}
private void validateOverdraftParams(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(allowOverdraftParamName, element)) {
final Boolean allowOverdraft = this.fromApiJsonHelper.extractBooleanNamed(allowOverdraftParamName, element);
baseDataValidator.reset().parameter(allowOverdraftParamName).value(allowOverdraft).ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(overdraftLimitParamName, element)) {
final BigDecimal overdraftLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(overdraftLimitParamName, element);
baseDataValidator.reset().parameter(overdraftLimitParamName).value(overdraftLimit).ignoreIfNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateOverdraftParamName, element)) {
final BigDecimal nominalAnnualInterestRateOverdraft = this.fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateOverdraftParamName, element);
baseDataValidator.reset().parameter(nominalAnnualInterestRateOverdraftParamName).value(nominalAnnualInterestRateOverdraft)
.ignoreIfNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(minOverdraftForInterestCalculationParamName, element)) {
final BigDecimal minOverdraftForInterestCalculation = this.fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(minOverdraftForInterestCalculationParamName, element);
baseDataValidator.reset().parameter(minOverdraftForInterestCalculationParamName).value(minOverdraftForInterestCalculation)
.ignoreIfNull().zeroOrPositiveAmount();
}
}
public void validateForAssignSavingsOfficer(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Set<String> supportedParameters = new HashSet<>(
Arrays.asList("fromSavingsOfficerId", "toSavingsOfficerId", "assignmentDate", "locale", "dateFormat"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Long toSavingsOfficerId = this.fromApiJsonHelper.extractLongNamed("toSavingsOfficerId", element);
baseDataValidator.reset().parameter("toSavingsOfficerId").value(toSavingsOfficerId).notNull().integerGreaterThanZero();
final String assignmentDateStr = this.fromApiJsonHelper.extractStringNamed("assignmentDate", element);
baseDataValidator.reset().parameter("assignmentDate").value(assignmentDateStr).notBlank();
if (!StringUtils.isBlank(assignmentDateStr)) {
final LocalDate assignmentDate = this.fromApiJsonHelper.extractLocalDateNamed("assignmentDate", element);
baseDataValidator.reset().parameter("assignmentDate").value(assignmentDate).notNull();
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
}
public void validateForUnAssignSavingsOfficer(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Set<String> supportedParameters = new HashSet<>(Arrays.asList("unassignedDate", "locale", "dateFormat"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String unassignedDateStr = this.fromApiJsonHelper.extractStringNamed("unassignedDate", element);
baseDataValidator.reset().parameter("unassignedDate").value(unassignedDateStr).notBlank();
if (!StringUtils.isBlank(unassignedDateStr)) {
final LocalDate unassignedDate = this.fromApiJsonHelper.extractLocalDateNamed("unassignedDate", element);
baseDataValidator.reset().parameter("unassignedDate").value(unassignedDate).notNull();
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
}
}
@@ -0,0 +1,78 @@
/**
* 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.portfolio.savings.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
public class SavingsAccountTransactionDTO {
private final DateTimeFormatter formatter;
private final LocalDate transactionDate;
private final BigDecimal transactionAmount;
private final PaymentDetail paymentDetail;
private final Long savingsAccountId;
private final Integer depositAccountType;
/**
* This constructor is used for bulk deposit transactions
*
* @param formatter
* @param transactionDate
* @param transactionAmount
* @param paymentDetail
* @param savingsAccountId
*/
public SavingsAccountTransactionDTO(DateTimeFormatter formatter, LocalDate transactionDate, BigDecimal transactionAmount,
PaymentDetail paymentDetail, Long savingsAccountId, final Integer depositAccountType) {
this.formatter = formatter;
this.transactionDate = transactionDate;
this.transactionAmount = transactionAmount;
this.paymentDetail = paymentDetail;
this.savingsAccountId = savingsAccountId;
this.depositAccountType = depositAccountType;
}
public DateTimeFormatter getFormatter() {
return this.formatter;
}
public LocalDate getTransactionDate() {
return this.transactionDate;
}
public BigDecimal getTransactionAmount() {
return this.transactionAmount;
}
public PaymentDetail getPaymentDetail() {
return this.paymentDetail;
}
public Long getSavingsAccountId() {
return this.savingsAccountId;
}
public Integer getAccountType() {
return this.depositAccountType;
}
}
@@ -0,0 +1,59 @@
/**
* 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.portfolio.savings.data;
import java.time.LocalDate;
public final class SavingsActivation {
private final transient Integer rowIndex;
private final LocalDate activatedOnDate;
private final String dateFormat;
private final String locale;
public static SavingsActivation importInstance(LocalDate activatedOnDate, Integer rowIndex, String locale, String dateFormat) {
return new SavingsActivation(activatedOnDate, rowIndex, locale, dateFormat);
}
private SavingsActivation(LocalDate activatedOnDate, Integer rowIndex, String locale, String dateFormat) {
this.activatedOnDate = activatedOnDate;
this.rowIndex = rowIndex;
this.dateFormat = dateFormat;
this.locale = locale;
}
public LocalDate getActivatedOnDate() {
return activatedOnDate;
}
public String getLocale() {
return locale;
}
public String getDateFormat() {
return dateFormat;
}
public Integer getRowIndex() {
return rowIndex;
}
}
@@ -0,0 +1,66 @@
/**
* 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.portfolio.savings.data;
import java.time.LocalDate;
public final class SavingsApproval {
private final transient Integer rowIndex;
private final LocalDate approvedOnDate;
private final String dateFormat;
private final String locale;
private final String note;
public static SavingsApproval importInstance(LocalDate approvedOnDate, Integer rowIndex, String locale, String dateFormat) {
return new SavingsApproval(approvedOnDate, rowIndex, locale, dateFormat);
}
private SavingsApproval(LocalDate approvedOnDate, Integer rowIndex, String locale, String dateFormat) {
this.approvedOnDate = approvedOnDate;
this.rowIndex = rowIndex;
this.dateFormat = dateFormat;
this.locale = locale;
this.note = "";
}
public LocalDate getApprovedOnDate() {
return approvedOnDate;
}
public String getLocale() {
return locale;
}
public String getDateFormat() {
return dateFormat;
}
public Integer getRowIndex() {
return rowIndex;
}
public String getNote() {
return note;
}
}
@@ -0,0 +1,704 @@
/**
* 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.portfolio.savings.data;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToDormancyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToEscheatParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToInactiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeAmountParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.time.MonthDay;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.savings.domain.SavingsProduct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SavingsProductDataValidator {
private final FromJsonHelper fromApiJsonHelper;
private static final Set<String> SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(
SavingsApiConstants.localeParamName, SavingsApiConstants.monthDayFormatParamName, nameParamName, shortNameParamName,
descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, inMultiplesOfParamName,
nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName,
interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName,
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, SavingsApiConstants.withdrawalFeeAmountParamName,
SavingsApiConstants.withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName,
SavingsApiConstants.accountingRuleParamName, SavingsApiConstants.chargesParamName,
SavingProductAccountingParams.INCOME_FROM_FEES.getValue(), SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(),
SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(),
SavingProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
SavingProductAccountingParams.SAVINGS_CONTROL.getValue(), SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(),
SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(), SavingProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
SavingProductAccountingParams.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(),
SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(), SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(),
SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue(), isDormancyTrackingActiveParamName, daysToDormancyParamName,
daysToInactiveParamName, daysToEscheatParamName, allowOverdraftParamName, overdraftLimitParamName,
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
SavingsApiConstants.minRequiredBalanceParamName, SavingsApiConstants.enforceMinRequiredBalanceParamName,
SavingsApiConstants.maxAllowedLienLimitParamName, SavingsApiConstants.lienAllowedParamName,
minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName));
@Autowired
public SavingsProductDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_PRODUCT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element);
baseDataValidator.reset().parameter(nameParamName).value(name).notBlank().notExceedingLengthOf(100);
final String shortName = this.fromApiJsonHelper.extractStringNamed(shortNameParamName, element);
baseDataValidator.reset().parameter(shortNameParamName).value(shortName).notBlank().notExceedingLengthOf(4);
final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element);
baseDataValidator.reset().parameter(currencyCodeParamName).value(currencyCode).notBlank();
final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(digitsAfterDecimalParamName, element);
baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6);
if (this.fromApiJsonHelper.parameterExists(inMultiplesOfParamName, element)) {
final Integer inMultiplesOf = this.fromApiJsonHelper.extractIntegerNamed(inMultiplesOfParamName, element, Locale.getDefault());
baseDataValidator.reset().parameter(inMultiplesOfParamName).value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater();
}
final BigDecimal nominalAnnualInterestRate = this.fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, element);
baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate).notNull()
.zeroOrPositiveAmount();
final Integer interestCompoundingPeriodType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(interestCompoundingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull()
.isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues());
final Integer interestPostingPeriodType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestPostingPeriodTypeParamName,
element);
baseDataValidator.reset().parameter(interestPostingPeriodTypeParamName).value(interestPostingPeriodType).notNull()
.isOneOfTheseValues(SavingsPostingInterestPeriodType.integerValues());
final Integer interestCalculationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName,
element);
baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull()
.isOneOfTheseValues(SavingsInterestCalculationType.integerValues());
final Integer interestCalculationDaysInYearType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(interestCalculationDaysInYearTypeParamName, element);
baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType).notNull()
.isOneOfTheseValues(SavingsInterestCalculationDaysInYearType.integerValues());
if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) {
final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element);
baseDataValidator.reset().parameter(descriptionParamName).value(description).ignoreIfNull().notExceedingLengthOf(500);
}
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).integerZeroOrGreater();
if (lockinPeriodFrequency != null) {
final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull()
.inMinMaxRange(0, 3);
}
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3);
if (lockinPeriodFrequencyType != null) {
final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull()
.integerZeroOrGreater();
}
}
/*
* if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) {
*
* final BigDecimal withdrawalFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed
* (withdrawalFeeAmountParamName, element); baseDataValidator.reset().parameter
* (withdrawalFeeAmountParamName).value (withdrawalFeeAmount).zeroOrPositiveAmount();
*
* if (withdrawalFeeAmount != null) { final Integer withdrawalFeeType =
* this.fromApiJsonHelper.extractIntegerSansLocaleNamed( withdrawalFeeTypeParamName, element);
* baseDataValidator.reset().parameter (withdrawalFeeTypeParamName).value(withdrawalFeeType)
* .isOneOfTheseValues(SavingsWithdrawalFeesType.integerValues()); } }
*
* if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) { final Integer
* withdrawalFeeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed (withdrawalFeeTypeParamName,
* element); baseDataValidator.reset().parameter
* (withdrawalFeeTypeParamName).value(withdrawalFeeType).ignoreIfNull() .isOneOfTheseValues(1, 2);
*
* if (withdrawalFeeType != null) { final BigDecimal withdrawalFeeAmount =
* this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed( withdrawalFeeAmountParamName, element);
* baseDataValidator.reset().parameter (withdrawalFeeAmountParamName).value(withdrawalFeeAmount).notNull()
* .zeroOrPositiveAmount(); } }
*/
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) {
final Boolean isWithdrawalFeeApplicableForTransfers = this.fromApiJsonHelper
.extractBooleanNamed(withdrawalFeeForTransfersParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers)
.ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(feeAmountParamName, element)) {
final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(feeAmountParamName, element);
baseDataValidator.reset().parameter(feeAmountParamName).value(annualFeeAmount).zeroOrPositiveAmount();
if (annualFeeAmount != null) {
final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element);
baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDayOfAnnualFee).notNull();
}
}
if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) {
final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element);
if (monthDayOfAnnualFee != null) {
final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(feeAmountParamName, element);
baseDataValidator.reset().parameter(feeAmountParamName).value(annualFeeAmount).notNull().zeroOrPositiveAmount();
}
}
// dormancy
final Boolean isDormancyActive = this.fromApiJsonHelper.extractBooleanNamed(isDormancyTrackingActiveParamName, element);
if (null != isDormancyActive && isDormancyActive) {
final Long daysToInact = this.fromApiJsonHelper.extractLongNamed(daysToInactiveParamName, element);
baseDataValidator.reset().parameter(daysToInactiveParamName).value(daysToInact).notNull().longGreaterThanZero();
final Long daysToDor = this.fromApiJsonHelper.extractLongNamed(daysToDormancyParamName, element);
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).notNull().longGreaterThanZero();
final Long daysToEsc = this.fromApiJsonHelper.extractLongNamed(daysToEscheatParamName, element);
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).notNull().longGreaterThanZero();
if (null != daysToInact && null != daysToDor && null != daysToEsc) {
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).longGreaterThanNumber(daysToInact);
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).longGreaterThanNumber(daysToDor);
}
}
// accounting related data validation
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (isCashBasedAccounting(accountingRuleType)) {
final Long savingsControlAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.SAVINGS_CONTROL.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.SAVINGS_CONTROL.getValue()).value(savingsControlAccountId)
.notNull().integerGreaterThanZero();
final Long savingsReferenceAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.SAVINGS_REFERENCE.getValue()).value(savingsReferenceAccountId)
.notNull().integerGreaterThanZero();
final Long transfersInSuspenseAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue())
.value(transfersInSuspenseAccountId).notNull().integerGreaterThanZero();
final Long interestOnSavingsAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue())
.value(interestOnSavingsAccountId).notNull().integerGreaterThanZero();
final Long incomeFromFeeId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_FEES.getValue(),
element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INCOME_FROM_FEES.getValue()).value(incomeFromFeeId).notNull()
.integerGreaterThanZero();
final Long incomeFromPenaltyId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
.notNull().integerGreaterThanZero();
final Long overdraftControlAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue())
.value(overdraftControlAccountId).notNull().integerGreaterThanZero();
final Long incomeFromInterest = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue()).value(incomeFromInterest)
.notNull().integerGreaterThanZero();
final Long writtenoff = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(),
element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue()).value(writtenoff).notNull()
.integerGreaterThanZero();
if (null != isDormancyActive && isDormancyActive) {
final Long escheatLiabilityAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue())
.value(escheatLiabilityAccountId).notNull().integerGreaterThanZero();
}
validatePaymentChannelFundSourceMappings(baseDataValidator, element);
validateChargeToIncomeAccountMappings(baseDataValidator, element);
}
validateOverdraftParams(baseDataValidator, element);
if (this.fromApiJsonHelper.parameterExists(minBalanceForInterestCalculationParamName, element)) {
final BigDecimal minBalanceForInterestCalculation = this.fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(minBalanceForInterestCalculationParamName, element);
baseDataValidator.reset().parameter(minBalanceForInterestCalculationParamName).value(minBalanceForInterestCalculation)
.ignoreIfNull().zeroOrPositiveAmount();
}
validateTaxWithHoldingParams(baseDataValidator, element, true);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUpdate(final String json, final SavingsProduct product) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_PRODUCT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
if (this.fromApiJsonHelper.parameterExists(nameParamName, element)) {
final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element);
baseDataValidator.reset().parameter(nameParamName).value(name).notBlank().notExceedingLengthOf(100);
}
if (this.fromApiJsonHelper.parameterExists(shortNameParamName, element)) {
final String shortName = this.fromApiJsonHelper.extractStringNamed(shortNameParamName, element);
baseDataValidator.reset().parameter(shortNameParamName).value(shortName).notBlank().notExceedingLengthOf(4);
}
if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) {
final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element);
baseDataValidator.reset().parameter(descriptionParamName).value(description).notBlank().notExceedingLengthOf(500);
}
if (this.fromApiJsonHelper.parameterExists(currencyCodeParamName, element)) {
final String currencyCode = this.fromApiJsonHelper.extractStringNamed(currencyCodeParamName, element);
baseDataValidator.reset().parameter(currencyCodeParamName).value(currencyCode).notBlank();
}
if (this.fromApiJsonHelper.parameterExists(digitsAfterDecimalParamName, element)) {
final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(digitsAfterDecimalParamName, element);
baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6);
}
if (this.fromApiJsonHelper.parameterExists(inMultiplesOfParamName, element)) {
final Integer inMultiplesOf = this.fromApiJsonHelper.extractIntegerNamed(inMultiplesOfParamName, element, Locale.getDefault());
baseDataValidator.reset().parameter(inMultiplesOfParamName).value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) {
final BigDecimal interestRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName,
element);
baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(interestRate).notNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(interestCompoundingPeriodTypeParamName, element)) {
final Integer interestCompoundingPeriodType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(interestCompoundingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull()
.isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) {
final Integer interestCalculationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName,
element);
baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull().inMinMaxRange(1,
2);
}
if (this.fromApiJsonHelper.parameterExists(interestCalculationDaysInYearTypeParamName, element)) {
final Integer interestCalculationDaysInYearType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(interestCalculationDaysInYearTypeParamName, element);
baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType)
.notNull().isOneOfTheseValues(360, 365);
}
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).ignoreIfNull()
.zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull()
.integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
final Integer lockinPeriodFrequencyType = this.fromApiJsonHelper
.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3);
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) {
final Boolean isWithdrawalFeeApplicableForTransfers = this.fromApiJsonHelper
.extractBooleanNamed(withdrawalFeeForTransfersParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers)
.ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(feeAmountParamName, element)) {
final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(feeAmountParamName, element);
baseDataValidator.reset().parameter(feeAmountParamName).value(annualFeeAmount).ignoreIfNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) {
final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element);
baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDayOfAnnualFee).ignoreIfNull();
}
final Long savingsControlAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.SAVINGS_CONTROL.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.SAVINGS_CONTROL.getValue()).value(savingsControlAccountId)
.ignoreIfNull().integerGreaterThanZero();
final Long savingsReferenceAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.SAVINGS_REFERENCE.getValue()).value(savingsReferenceAccountId)
.ignoreIfNull().integerGreaterThanZero();
final Long transfersInSuspenseAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue()).value(transfersInSuspenseAccountId)
.ignoreIfNull().integerGreaterThanZero();
final Long interestOnSavingsAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue()).value(interestOnSavingsAccountId)
.ignoreIfNull().integerGreaterThanZero();
final Long incomeFromFeeId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_FEES.getValue(),
element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INCOME_FROM_FEES.getValue()).value(incomeFromFeeId).ignoreIfNull()
.integerGreaterThanZero();
final Long incomeFromPenaltyId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
.ignoreIfNull().integerGreaterThanZero();
final Long overdraftAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue()).value(overdraftAccountId)
.ignoreIfNull().integerGreaterThanZero();
final Long incomeFromInterest = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue()).value(incomeFromInterest)
.ignoreIfNull().integerGreaterThanZero();
final Long writtenoff = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(),
element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue()).value(writtenoff).ignoreIfNull()
.integerGreaterThanZero();
// dormancy
final Boolean isDormancyActive = this.fromApiJsonHelper.parameterExists(isDormancyTrackingActiveParamName, element)
? this.fromApiJsonHelper.extractBooleanNamed(isDormancyTrackingActiveParamName, element)
: product.isDormancyTrackingActive();
if (null != isDormancyActive && isDormancyActive) {
final Long daysToInact = this.fromApiJsonHelper.parameterExists(daysToInactiveParamName, element)
? this.fromApiJsonHelper.extractLongNamed(daysToInactiveParamName, element)
: product.getDaysToInactive();
baseDataValidator.reset().parameter(daysToInactiveParamName).value(daysToInact).notNull().longGreaterThanZero();
final Long daysToDor = this.fromApiJsonHelper.parameterExists(daysToDormancyParamName, element)
? this.fromApiJsonHelper.extractLongNamed(daysToDormancyParamName, element)
: product.getDaysToDormancy();
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).notNull().longGreaterThanZero();
final Long daysToEsc = this.fromApiJsonHelper.parameterExists(daysToEscheatParamName, element)
? this.fromApiJsonHelper.extractLongNamed(daysToEscheatParamName, element)
: product.getDaysToEscheat();
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).notNull().longGreaterThanZero();
if (null != daysToInact && null != daysToDor && null != daysToEsc) {
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).longGreaterThanNumber(daysToInact);
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).longGreaterThanNumber(daysToDor);
}
if (this.fromApiJsonHelper.parameterExists(SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue(), element)) {
final Long escheatLiabilityAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue())
.value(escheatLiabilityAccountId).notNull().integerGreaterThanZero();
}
}
validatePaymentChannelFundSourceMappings(baseDataValidator, element);
validateChargeToIncomeAccountMappings(baseDataValidator, element);
validateOverdraftParams(baseDataValidator, element);
if (this.fromApiJsonHelper.parameterExists(minBalanceForInterestCalculationParamName, element)) {
final BigDecimal minBalanceForInterestCalculation = this.fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(minBalanceForInterestCalculationParamName, element);
baseDataValidator.reset().parameter(minBalanceForInterestCalculationParamName).value(minBalanceForInterestCalculation)
.ignoreIfNull().zeroOrPositiveAmount();
}
validateTaxWithHoldingParams(baseDataValidator, element, false);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
private boolean isCashBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.CASH_BASED.getValue().equals(accountingRuleType);
}
/**
* Validation for advanced accounting options
*/
private void validatePaymentChannelFundSourceMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(SavingProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element)) {
final JsonArray paymentChannelMappingArray = this.fromApiJsonHelper
.extractJsonArrayNamed(SavingProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element);
if (paymentChannelMappingArray != null && paymentChannelMappingArray.size() > 0) {
int i = 0;
do {
final JsonObject jsonObject = paymentChannelMappingArray.get(i).getAsJsonObject();
final Long paymentTypeId = jsonObject.get(SavingProductAccountingParams.PAYMENT_TYPE.getValue()).getAsLong();
final Long paymentSpecificFundAccountId = jsonObject.get(SavingProductAccountingParams.FUND_SOURCE.getValue())
.getAsLong();
baseDataValidator.reset()
.parameter(SavingProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue() + "[" + i + "]."
+ SavingProductAccountingParams.PAYMENT_TYPE.toString())
.value(paymentTypeId).notNull().integerGreaterThanZero();
baseDataValidator.reset()
.parameter(SavingProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue() + "[" + i + "]."
+ SavingProductAccountingParams.FUND_SOURCE.getValue())
.value(paymentSpecificFundAccountId).notNull().integerGreaterThanZero();
i++;
} while (i < paymentChannelMappingArray.size());
}
}
}
private void validateChargeToIncomeAccountMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
// validate for both fee and penalty charges
validateChargeToIncomeAccountMappings(baseDataValidator, element, true);
validateChargeToIncomeAccountMappings(baseDataValidator, element, true);
}
private void validateChargeToIncomeAccountMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element,
final boolean isPenalty) {
String parameterName;
if (isPenalty) {
parameterName = SavingProductAccountingParams.PENALTY_INCOME_ACCOUNT_MAPPING.getValue();
} else {
parameterName = SavingProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue();
}
if (this.fromApiJsonHelper.parameterExists(parameterName, element)) {
final JsonArray chargeToIncomeAccountMappingArray = this.fromApiJsonHelper.extractJsonArrayNamed(parameterName, element);
if (chargeToIncomeAccountMappingArray != null && chargeToIncomeAccountMappingArray.size() > 0) {
int i = 0;
do {
final JsonObject jsonObject = chargeToIncomeAccountMappingArray.get(i).getAsJsonObject();
final Long chargeId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.CHARGE_ID.getValue(),
jsonObject);
final Long incomeAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_ACCOUNT_ID.getValue(), jsonObject);
baseDataValidator.reset().parameter(parameterName + "[" + i + "]." + SavingProductAccountingParams.CHARGE_ID.getValue())
.value(chargeId).notNull().integerGreaterThanZero();
baseDataValidator.reset()
.parameter(parameterName + "[" + i + "]." + SavingProductAccountingParams.INCOME_ACCOUNT_ID.getValue())
.value(incomeAccountId).notNull().integerGreaterThanZero();
i++;
} while (i < chargeToIncomeAccountMappingArray.size());
}
}
}
private void validateOverdraftParams(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(allowOverdraftParamName, element)) {
final Boolean allowOverdraft = this.fromApiJsonHelper.extractBooleanNamed(allowOverdraftParamName, element);
baseDataValidator.reset().parameter(allowOverdraftParamName).value(allowOverdraft).ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(overdraftLimitParamName, element)) {
final BigDecimal overdraftLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(overdraftLimitParamName, element);
baseDataValidator.reset().parameter(overdraftLimitParamName).value(overdraftLimit).ignoreIfNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateOverdraftParamName, element)) {
final BigDecimal nominalAnnualInterestRateOverdraft = this.fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateOverdraftParamName, element);
baseDataValidator.reset().parameter(nominalAnnualInterestRateOverdraftParamName).value(nominalAnnualInterestRateOverdraft)
.ignoreIfNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(minOverdraftForInterestCalculationParamName, element)) {
final BigDecimal minOverdraftForInterestCalculation = this.fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(minOverdraftForInterestCalculationParamName, element);
baseDataValidator.reset().parameter(minOverdraftForInterestCalculationParamName).value(minOverdraftForInterestCalculation)
.ignoreIfNull().zeroOrPositiveAmount();
}
}
private void validateLienParams(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(lienAllowedParamName, element)) {
final Boolean lienAllowed = this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element);
baseDataValidator.reset().parameter(lienAllowedParamName).value(lienAllowed).ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(maxAllowedLienLimitParamName, element)) {
final BigDecimal maxAllowedLienLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxAllowedLienLimitParamName,
element);
baseDataValidator.reset().parameter(maxAllowedLienLimitParamName).value(maxAllowedLienLimit).ignoreIfNull()
.zeroOrPositiveAmount();
}
if (BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element))
&& BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(allowOverdraftParamName, element))) {
final BigDecimal maxAllowedLienLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxAllowedLienLimitParamName,
element);
final BigDecimal overdraftLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(overdraftLimitParamName, element);
if (overdraftLimit.compareTo(maxAllowedLienLimit) > 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("Overdraft.limit.can.not.be.greater.than.lien.limit");
}
}
}
private void validateTaxWithHoldingParams(final DataValidatorBuilder baseDataValidator, final JsonElement element,
final boolean isCreate) {
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) {
final String withHoldTax = this.fromApiJsonHelper.extractStringNamed(withHoldTaxParamName, element);
baseDataValidator.reset().parameter(withHoldTaxParamName).value(withHoldTax).ignoreIfNull().validateForBooleanValue();
}
Boolean withHoldTax = this.fromApiJsonHelper.extractBooleanNamed(withHoldTaxParamName, element);
if (withHoldTax == null) {
withHoldTax = false;
}
if (this.fromApiJsonHelper.parameterExists(taxGroupIdParamName, element)) {
final Long taxGroupId = this.fromApiJsonHelper.extractLongNamed(taxGroupIdParamName, element);
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).ignoreIfNull().longGreaterThanZero();
if (withHoldTax) {
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
}
} else if (withHoldTax && isCreate) {
final Long taxGroupId = null;
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
}
}
}
@@ -0,0 +1,34 @@
/**
* 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.portfolio.savings.data;
import java.time.LocalDate;
/**
* Immutable data object representing date
*/
@SuppressWarnings("unused")
public class TransactionDateData {
private final LocalDate date;
public TransactionDateData(final LocalDate date) {
this.date = date;
}
}
@@ -0,0 +1,50 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.portfolio.interestratechart.domain.InterestIncentivesFields;
@Entity
@Table(name = "m_deposit_account_interest_incentives")
public class DepositAccountInterestIncentive extends AbstractPersistableCustom {
@ManyToOne
@JoinColumn(name = "deposit_account_interest_rate_slab_id", nullable = false)
private DepositAccountInterestRateChartSlabs depositAccountInterestRateChartSlabs;
@Embedded
private InterestIncentivesFields interestIncentivesFields;
protected DepositAccountInterestIncentive() {
// TODO Auto-generated constructor stub
}
public DepositAccountInterestIncentive(final DepositAccountInterestRateChartSlabs depositAccountInterestRateChartSlabs,
final InterestIncentivesFields interestIncentivesFields) {
this.depositAccountInterestRateChartSlabs = depositAccountInterestRateChartSlabs;
this.interestIncentivesFields = interestIncentivesFields;
}
}
@@ -0,0 +1,63 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.portfolio.interestratechart.domain.InterestIncentivesFields;
@Entity
@Table(name = "m_savings_interest_incentives")
public class DepositAccountInterestIncentives extends AbstractPersistableCustom {
@ManyToOne
@JoinColumn(name = "deposit_account_interest_rate_slab_id", nullable = false)
private DepositAccountInterestRateChartSlabs depositAccountInterestRateChartSlabs;
@Embedded
private InterestIncentivesFields interestIncentivesFields;
protected DepositAccountInterestIncentives() {
}
private DepositAccountInterestIncentives(final DepositAccountInterestRateChartSlabs depositAccountInterestRateChartSlabs,
final InterestIncentivesFields interestIncentivesFields) {
this.depositAccountInterestRateChartSlabs = depositAccountInterestRateChartSlabs;
this.interestIncentivesFields = interestIncentivesFields;
}
public static DepositAccountInterestIncentives from(final DepositAccountInterestRateChartSlabs depositAccountInterestRateChartSlabs,
final InterestIncentivesFields interestIncentivesFields) {
return new DepositAccountInterestIncentives(depositAccountInterestRateChartSlabs, interestIncentivesFields);
}
public void updateDepositAccountInterestRateChartSlabs(DepositAccountInterestRateChartSlabs depositAccountInterestRateChartSlabs) {
this.depositAccountInterestRateChartSlabs = depositAccountInterestRateChartSlabs;
}
public InterestIncentivesFields interestIncentivesFields() {
return this.interestIncentivesFields;
}
}
@@ -0,0 +1,159 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChart;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartFields;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlab;
import org.apache.fineract.portfolio.interestratechart.incentive.AttributeIncentiveCalculation;
import org.apache.fineract.portfolio.interestratechart.incentive.AttributeIncentiveCalculationFactory;
import org.apache.fineract.portfolio.interestratechart.incentive.IncentiveDTO;
@Entity
@Table(name = "m_savings_account_interest_rate_chart")
public class DepositAccountInterestRateChart extends AbstractPersistableCustom {
@Embedded
private InterestRateChartFields chartFields;
@OneToOne
@JoinColumn(name = "savings_account_id", nullable = false)
private SavingsAccount account;
@OneToMany(mappedBy = "depositAccountInterestRateChart", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Set<DepositAccountInterestRateChartSlabs> chartSlabs = new HashSet<>();
protected DepositAccountInterestRateChart() {
//
}
public static DepositAccountInterestRateChart from(InterestRateChart productChart) {
final Set<InterestRateChartSlab> chartSlabs = productChart.setOfChartSlabs();
final Set<DepositAccountInterestRateChartSlabs> depostiChartSlabs = new HashSet<>();
for (InterestRateChartSlab interestRateChartSlab : chartSlabs) {
depostiChartSlabs.add(DepositAccountInterestRateChartSlabs.from(interestRateChartSlab, null));
}
final DepositAccountInterestRateChart depositChart = new DepositAccountInterestRateChart(productChart.chartFields(), null,
depostiChartSlabs);
// update deposit account interest rate chart ference to chart Slabs
depositChart.updateChartSlabsReference();
return depositChart;
}
private DepositAccountInterestRateChart(InterestRateChartFields chartFields, SavingsAccount account,
Set<DepositAccountInterestRateChartSlabs> chartSlabs) {
this.chartFields = chartFields;
this.account = account;
this.chartSlabs = chartSlabs;
}
private void updateChartSlabsReference() {
final Set<DepositAccountInterestRateChartSlabs> chartSlabs = setOfChartSlabs();
for (DepositAccountInterestRateChartSlabs chartSlab : chartSlabs) {
chartSlab.updateChartReference(this);
}
}
public Set<DepositAccountInterestRateChartSlabs> setOfChartSlabs() {
if (this.chartSlabs == null) {
this.chartSlabs = new HashSet<>();
}
return this.chartSlabs;
}
public DepositAccountInterestRateChartSlabs findChartSlab(Long chartSlabId) {
final Set<DepositAccountInterestRateChartSlabs> chartSlabs = setOfChartSlabs();
for (DepositAccountInterestRateChartSlabs interestRateChartSlab : chartSlabs) {
if (interestRateChartSlab.getId().equals(chartSlabId)) {
return interestRateChartSlab;
}
}
return null;
}
public LocalDate getFromDate() {
return this.chartFields.getFromDate();
}
public LocalDate getEndDate() {
return this.chartFields.getEndDate();
}
public Long savingsAccountId() {
return this.account == null ? null : this.account.getId();
}
public SavingsAccount savingsAccount() {
return this.account;
}
public InterestRateChartFields chartFields() {
return this.chartFields;
}
public void updateDepositAccountReference(final SavingsAccount account) {
this.account = account;
}
public BigDecimal getApplicableInterestRate(final BigDecimal depositAmount, final LocalDate periodStartDate,
final LocalDate periodEndDate, final Client client) {
BigDecimal effectiveInterestRate = BigDecimal.ZERO;
for (DepositAccountInterestRateChartSlabs slab : setOfChartSlabs()) {
if (slab.slabFields().isBetweenPeriod(periodStartDate, periodEndDate) && slab.slabFields().isAmountBetween(depositAmount)) {
effectiveInterestRate = slab.slabFields().annualInterestRate();
Set<DepositAccountInterestIncentives> depositInterestIncentives = slab.setOfIncentives();
for (DepositAccountInterestIncentives incentives : depositInterestIncentives) {
AttributeIncentiveCalculation attributeIncentiveCalculation = AttributeIncentiveCalculationFactory
.findAttributeIncentiveCalculation(incentives.interestIncentivesFields().entiryType());
IncentiveDTO incentiveDTO = new IncentiveDTO(client, effectiveInterestRate, incentives.interestIncentivesFields());
effectiveInterestRate = attributeIncentiveCalculation.calculateIncentive(incentiveDTO);
}
// effectiveInterestRate is zero or null then reset to default
// interest rate.
if (effectiveInterestRate == null || effectiveInterestRate.compareTo(BigDecimal.ZERO) == 0) {
effectiveInterestRate = slab.slabFields().annualInterestRate();
}
}
}
return effectiveInterestRate;
}
public boolean isPrimaryGroupingByAmount() {
return this.chartFields.isPrimaryGroupingByAmount();
}
}
@@ -0,0 +1,105 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.portfolio.interestratechart.domain.InterestIncentives;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlab;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlabFields;
@Entity
@Table(name = "m_savings_account_interest_rate_slab")
public class DepositAccountInterestRateChartSlabs extends AbstractPersistableCustom {
@Embedded
private InterestRateChartSlabFields slabFields;
@ManyToOne(optional = false)
@JoinColumn(name = "savings_account_interest_rate_chart_id", referencedColumnName = "id", nullable = false)
private DepositAccountInterestRateChart depositAccountInterestRateChart;
@OneToMany(mappedBy = "depositAccountInterestRateChartSlabs", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Set<DepositAccountInterestIncentives> interestIncentives = new HashSet<>();
protected DepositAccountInterestRateChartSlabs() {
//
}
private DepositAccountInterestRateChartSlabs(InterestRateChartSlabFields slabFields,
DepositAccountInterestRateChart depositAccountInterestRateChart,
final Set<DepositAccountInterestIncentives> interestIncentives) {
this.slabFields = slabFields;
this.depositAccountInterestRateChart = depositAccountInterestRateChart;
this.interestIncentives = interestIncentives;
}
public void setDepositAccountInterestRateChart(DepositAccountInterestRateChart depositAccountInterestRateChart) {
this.depositAccountInterestRateChart = depositAccountInterestRateChart;
}
public Long savingsProductId() {
return this.depositAccountInterestRateChart.savingsAccountId();
}
public InterestRateChartSlabFields slabFields() {
return this.slabFields;
}
public static DepositAccountInterestRateChartSlabs from(InterestRateChartSlab interestRateChartSlab,
DepositAccountInterestRateChart depositAccountInterestRateChart) {
InterestRateChartSlabFields slabFields = interestRateChartSlab.slabFields();
Set<DepositAccountInterestIncentives> depositInterestIncentives = new HashSet<>();
Set<InterestIncentives> incentives = interestRateChartSlab.setOfInterestIncentives();
for (InterestIncentives incentive : incentives) {
depositInterestIncentives.add(DepositAccountInterestIncentives.from(null, incentive.interestIncentivesFields()));
}
DepositAccountInterestRateChartSlabs chartSlabs = new DepositAccountInterestRateChartSlabs(slabFields,
depositAccountInterestRateChart, depositInterestIncentives);
chartSlabs.updateIncentiveReference();
return chartSlabs;
}
private void updateIncentiveReference() {
final Set<DepositAccountInterestIncentives> incentives = setOfIncentives();
for (DepositAccountInterestIncentives depositInterestIncentives : incentives) {
depositInterestIncentives.updateDepositAccountInterestRateChartSlabs(this);
}
}
public Set<DepositAccountInterestIncentives> setOfIncentives() {
if (this.interestIncentives == null) {
this.interestIncentives = new HashSet<>();
}
return this.interestIncentives;
}
public void updateChartReference(final DepositAccountInterestRateChart chart) {
this.depositAccountInterestRateChart = chart;
}
}
@@ -0,0 +1,127 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.infrastructure.core.service.DateUtils.getSystemZoneId;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.Optional;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.savings.DepositAccountOnHoldTransactionType;
@Entity
@Table(name = "m_deposit_account_on_hold_transaction")
public class DepositAccountOnHoldTransaction extends AbstractAuditableWithUTCDateTimeCustom {
@ManyToOne
@JoinColumn(name = "savings_account_id", nullable = true)
private SavingsAccount savingsAccount;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@Column(name = "transaction_type_enum", nullable = false)
private Integer transactionType;
@Column(name = "transaction_date", nullable = false)
private LocalDate transactionDate;
@Column(name = "is_reversed", nullable = false)
private boolean reversed;
@Deprecated
@Column(name = "created_date", nullable = true)
private LocalDateTime createdDateToRemove;
protected DepositAccountOnHoldTransaction() {}
private DepositAccountOnHoldTransaction(final SavingsAccount savingsAccount, final BigDecimal amount,
final DepositAccountOnHoldTransactionType transactionType, final LocalDate transactionDate, final boolean reversed) {
this.savingsAccount = savingsAccount;
this.amount = amount;
this.transactionType = transactionType.getValue();
this.transactionDate = transactionDate;
this.createdDateToRemove = null; // #audit backward compatibility deprecated
this.reversed = reversed;
}
public static DepositAccountOnHoldTransaction hold(final SavingsAccount savingsAccount, final BigDecimal amount,
final LocalDate transactionDate) {
boolean reversed = false;
return new DepositAccountOnHoldTransaction(savingsAccount, amount, DepositAccountOnHoldTransactionType.HOLD, transactionDate,
reversed);
}
public static DepositAccountOnHoldTransaction release(final SavingsAccount savingsAccount, final BigDecimal amount,
final LocalDate transactionDate) {
boolean reversed = false;
return new DepositAccountOnHoldTransaction(savingsAccount, amount, DepositAccountOnHoldTransactionType.RELEASE, transactionDate,
reversed);
}
public SavingsAccount getSavingsAccount() {
return savingsAccount;
}
public BigDecimal getAmount() {
return this.amount;
}
public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount);
}
public DepositAccountOnHoldTransactionType getTransactionType() {
return DepositAccountOnHoldTransactionType.fromInt(this.transactionType);
}
public LocalDate getTransactionDate() {
return this.transactionDate;
}
public MonetaryCurrency getCurrency() {
return getSavingsAccount().getCurrency();
}
public void reverseTransaction() {
this.reversed = true;
if (this.getTransactionType().isHold()) {
this.savingsAccount.releaseFunds(this.amount);
} else {
this.savingsAccount.holdFunds(this.amount);
}
}
@Override
public Optional<OffsetDateTime> getCreatedDate() {
// #audit backward compatibility keep system datetime
return Optional.ofNullable(super.getCreatedDate()
.orElse(createdDateToRemove == null ? null : createdDateToRemove.atZone(getSystemZoneId()).toOffsetDateTime()));
}
}
@@ -0,0 +1,329 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.dateFormatParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.depositAmountParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.depositPeriodFrequencyIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.depositPeriodParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.expectedFirstDepositOnDateParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.localeParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maturityInstructionIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferToSavingsIdParamName;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.savings.DepositAccountOnClosureType;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
@Entity
@Table(name = "m_deposit_account_term_and_preclosure")
public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom {
@Column(name = "deposit_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal depositAmount;
@Column(name = "maturity_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal maturityAmount;
@Column(name = "maturity_date", nullable = true)
private LocalDate maturityDate;
@Column(name = "expected_firstdepositon_date")
private LocalDate expectedFirstDepositOnDate;
@Column(name = "deposit_period", nullable = true)
private Integer depositPeriod;
@Column(name = "deposit_period_frequency_enum", nullable = true)
private Integer depositPeriodFrequency;
@Column(name = "on_account_closure_enum", nullable = true)
private Integer onAccountClosureType;
@Embedded
private DepositPreClosureDetail preClosureDetail;
@Embedded
protected DepositTermDetail depositTermDetail;
@OneToOne
@JoinColumn(name = "savings_account_id", nullable = false)
private SavingsAccount account;
@Column(name = "transfer_interest_to_linked_account", nullable = false)
private boolean transferInterestToLinkedAccount;
@Column(name = "transfer_to_savings_account_id")
private Long transferToSavingsAccountId;
protected DepositAccountTermAndPreClosure() {
}
public static DepositAccountTermAndPreClosure createNew(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
SavingsAccount account, BigDecimal depositAmount, BigDecimal maturityAmount, final LocalDate maturityDate,
Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency, final LocalDate expectedFirstDepositOnDate,
final DepositAccountOnClosureType accountOnClosureType, Boolean trasferInterest, Long transferToSavingsId) {
return new DepositAccountTermAndPreClosure(preClosureDetail, depositTermDetail, account, depositAmount, maturityAmount,
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType, trasferInterest,
transferToSavingsId);
}
private DepositAccountTermAndPreClosure(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
SavingsAccount account, BigDecimal depositAmount, BigDecimal maturityAmount, final LocalDate maturityDate,
Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency, final LocalDate expectedFirstDepositOnDate,
final DepositAccountOnClosureType accountOnClosureType, Boolean transferInterest, Long transferToSavingsId) {
this.depositAmount = depositAmount;
this.maturityAmount = maturityAmount;
this.maturityDate = maturityDate;
this.depositPeriod = depositPeriod;
this.depositPeriodFrequency = (depositPeriodFrequency == null) ? null : depositPeriodFrequency.getValue();
this.preClosureDetail = preClosureDetail;
this.depositTermDetail = depositTermDetail;
this.account = account;
this.expectedFirstDepositOnDate = expectedFirstDepositOnDate;
this.onAccountClosureType = (accountOnClosureType == null) ? null : accountOnClosureType.getValue();
this.transferInterestToLinkedAccount = transferInterest;
this.transferToSavingsAccountId = transferToSavingsId;
}
public Map<String, Object> update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
if (command.isChangeInBigDecimalParameterNamed(depositAmountParamName, this.depositAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(depositAmountParamName);
actualChanges.put(depositAmountParamName, newValue);
this.depositAmount = newValue;
}
if (command.isChangeInIntegerParameterNamed(depositPeriodParamName, this.depositPeriod)) {
final Integer newValue = command.integerValueOfParameterNamed(depositPeriodParamName);
actualChanges.put(depositPeriodParamName, newValue);
this.depositPeriod = newValue;
}
if (command.isChangeInIntegerParameterNamed(depositPeriodFrequencyIdParamName, this.depositPeriodFrequency)) {
final Integer newValue = command.integerValueOfParameterNamed(depositPeriodFrequencyIdParamName);
actualChanges.put(depositPeriodFrequencyIdParamName, SavingsEnumerations.depositTermFrequencyType(newValue));
this.depositPeriodFrequency = newValue;
}
final String localeAsInput = command.locale();
final String dateFormat = command.dateFormat();
if (command.isChangeInLocalDateParameterNamed(expectedFirstDepositOnDateParamName, this.getExpectedFirstDepositOnDate())) {
final String newValueAsString = command.stringValueOfParameterNamed(expectedFirstDepositOnDateParamName);
actualChanges.put(expectedFirstDepositOnDateParamName, newValueAsString);
actualChanges.put(localeParamName, localeAsInput);
actualChanges.put(dateFormatParamName, dateFormat);
this.expectedFirstDepositOnDate = command.localDateValueOfParameterNamed(expectedFirstDepositOnDateParamName);
}
if (command.isChangeInBooleanParameterNamed(transferInterestToSavingsParamName, this.transferInterestToLinkedAccount)) {
final Boolean newValue = command.booleanPrimitiveValueOfParameterNamed(transferInterestToSavingsParamName);
actualChanges.put(transferInterestToSavingsParamName, newValue);
this.transferInterestToLinkedAccount = newValue;
}
if (command.isChangeInIntegerParameterNamed(maturityInstructionIdParamName, this.onAccountClosureType)
|| command.integerValueOfParameterNamed(maturityInstructionIdParamName) == null) {
final Integer newValue = command.integerValueOfParameterNamed(maturityInstructionIdParamName);
actualChanges.put(maturityInstructionIdParamName, newValue);
this.onAccountClosureType = newValue != null ? DepositAccountOnClosureType.fromInt(newValue).getValue() : null;
}
if (command.isChangeInLongParameterNamed(transferToSavingsIdParamName, this.transferToSavingsAccountId)
|| command.integerValueOfParameterNamed(transferToSavingsIdParamName) == null) {
final Long newValue = command.longValueOfParameterNamed(transferToSavingsIdParamName);
actualChanges.put(transferToSavingsIdParamName, newValue);
this.transferToSavingsAccountId = newValue;
}
if (this.preClosureDetail != null) {
actualChanges.putAll(this.preClosureDetail.update(command, baseDataValidator));
}
if (this.depositTermDetail != null) {
actualChanges.putAll(this.depositTermDetail.update(command, baseDataValidator));
}
return actualChanges;
}
public DepositPreClosureDetail depositPreClosureDetail() {
return this.preClosureDetail;
}
public DepositTermDetail depositTermDetail() {
return this.depositTermDetail;
}
public BigDecimal depositAmount() {
return this.depositAmount;
}
public Integer depositPeriod() {
return this.depositPeriod;
}
public Integer depositPeriodFrequency() {
return this.depositPeriodFrequency;
}
public SavingsPeriodFrequencyType depositPeriodFrequencyType() {
return SavingsPeriodFrequencyType.fromInt(depositPeriodFrequency);
}
public void updateAccountReference(final SavingsAccount account) {
this.account = account;
}
public void updateMaturityDetails(final BigDecimal maturityAmount, final LocalDate maturityDate) {
this.maturityAmount = maturityAmount;
this.maturityDate = maturityDate;
}
public void updateMaturityDetails(final BigDecimal depositAmount, final BigDecimal interestPayable, final LocalDate maturityDate) {
this.depositAmount = depositAmount;
this.maturityAmount = this.depositAmount.add(interestPayable);
this.maturityDate = maturityDate;
}
public void updateDepositAmount(final BigDecimal depositAmount) {
this.depositAmount = depositAmount;
}
public LocalDate getMaturityDate() {
return this.maturityDate;
}
public LocalDate getExpectedFirstDepositOnDate() {
return this.expectedFirstDepositOnDate;
}
public boolean isPreClosurePenalApplicable() {
if (this.preClosureDetail != null) {
return this.preClosureDetail.preClosurePenalApplicable();
}
return false;
}
public Integer getActualDepositPeriod(final LocalDate interestPostingUpToDate, final SavingsPeriodFrequencyType periodFrequencyType) {
LocalDate depositFromDate = getExpectedFirstDepositOnDate();
if (depositFromDate == null) {
depositFromDate = this.account.accountSubmittedOrActivationDate();
}
Integer actualDepositPeriod = this.depositPeriod;
if (depositFromDate == null || getMaturityDate() == null || DateUtils.isEqual(interestPostingUpToDate, getMaturityDate())) {
return actualDepositPeriod;
}
final SavingsPeriodFrequencyType depositPeriodFrequencyType = periodFrequencyType;
switch (depositPeriodFrequencyType) {
case DAYS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.DAYS.between(depositFromDate, interestPostingUpToDate));
break;
case WEEKS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.WEEKS.between(depositFromDate, interestPostingUpToDate));
break;
case MONTHS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.MONTHS.between(depositFromDate, interestPostingUpToDate));
break;
case YEARS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.YEARS.between(depositFromDate, interestPostingUpToDate));
break;
case INVALID:
actualDepositPeriod = this.depositPeriod;// default value
break;
}
return actualDepositPeriod;
}
public BigDecimal maturityAmount() {
return this.maturityAmount;
}
public void updateOnAccountClosureStatus(final DepositAccountOnClosureType onClosureType) {
this.onAccountClosureType = onClosureType.getValue();
}
public boolean isReinvestOnClosure() {
return DepositAccountOnClosureType.fromInt(this.onAccountClosureType).isReinvest();
}
public boolean isTransferToSavingsOnClosure() {
return DepositAccountOnClosureType.fromInt(this.onAccountClosureType).isTransferToSavings();
}
public DepositAccountTermAndPreClosure copy(BigDecimal depositAmount) {
final SavingsAccount account = null;
final BigDecimal maturityAmount = null;
final BigDecimal actualDepositAmount = depositAmount;
final LocalDate maturityDate = null;
final Integer depositPeriod = this.depositPeriod;
final SavingsPeriodFrequencyType depositPeriodFrequency = SavingsPeriodFrequencyType.fromInt(this.depositPeriodFrequency);
final DepositPreClosureDetail preClosureDetail = this.preClosureDetail.copy();
final DepositTermDetail depositTermDetail = this.depositTermDetail.copy();
final LocalDate expectedFirstDepositOnDate = null;
final Boolean transferInterestToLinkedAccount = false;
final DepositAccountOnClosureType accountOnClosureType = null;
final Long transferToSavingsId = null;
return DepositAccountTermAndPreClosure.createNew(preClosureDetail, depositTermDetail, account, actualDepositAmount, maturityAmount,
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType,
transferInterestToLinkedAccount, transferToSavingsId);
}
public void updateExpectedFirstDepositDate(final LocalDate expectedFirstDepositOnDate) {
this.expectedFirstDepositOnDate = expectedFirstDepositOnDate;
}
public boolean isTransferInterestToLinkedAccount() {
return this.transferInterestToLinkedAccount;
}
public boolean isAfterExpectedFirstDepositDate(final LocalDate compareDate) {
return this.expectedFirstDepositOnDate != null && DateUtils.isAfter(compareDate, getExpectedFirstDepositOnDate());
}
public Integer getOnAccountClosureType() {
return onAccountClosureType;
}
public Long getTransferToSavingsAccountId() {
return transferToSavingsAccountId;
}
}
@@ -0,0 +1,136 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalApplicableParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalInterestOnTypeIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalInterestParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.localeParamName;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.portfolio.savings.PreClosurePenalInterestOnType;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
/**
* DepositPreClosureDetail encapsulates all the details of a {@link FixedDepositProduct} that are also used and
* persisted by a {@link FixedDepositAccount}.
*/
@Embeddable
public class DepositPreClosureDetail {
@Column(name = "pre_closure_penal_applicable")
private boolean preClosurePenalApplicable;
@Column(name = "pre_closure_penal_interest", scale = 6, precision = 19, nullable = true)
private BigDecimal preClosurePenalInterest;
@Column(name = "pre_closure_penal_interest_on_enum", nullable = true)
private Integer preClosurePenalInterestOnType;
public static DepositPreClosureDetail createFrom(final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest,
final PreClosurePenalInterestOnType preClosurePenalInterestType) {
return new DepositPreClosureDetail(preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestType);
}
protected DepositPreClosureDetail() {
//
}
private DepositPreClosureDetail(final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest,
final PreClosurePenalInterestOnType preClosurePenalInterestType) {
this.preClosurePenalApplicable = preClosurePenalApplicable;
this.preClosurePenalInterest = preClosurePenalInterest;
this.preClosurePenalInterestOnType = (preClosurePenalInterestType == null) ? null : preClosurePenalInterestType.getValue();
}
protected Map<String, Object> update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
final String localeAsInput = command.locale();
if (command.isChangeInBooleanParameterNamed(preClosurePenalApplicableParamName, this.preClosurePenalApplicable)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(preClosurePenalApplicableParamName);
actualChanges.put(preClosurePenalApplicableParamName, newValue);
this.preClosurePenalApplicable = newValue;
}
if (this.preClosurePenalApplicable) {
if (command.isChangeInBigDecimalParameterNamed(preClosurePenalInterestParamName, this.preClosurePenalInterest)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(preClosurePenalInterestParamName);
actualChanges.put(preClosurePenalInterestParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.preClosurePenalInterest = newValue;
}
if (command.isChangeInIntegerParameterNamed(preClosurePenalInterestOnTypeIdParamName, this.preClosurePenalInterestOnType)) {
final Integer newValue = command.integerValueOfParameterNamed(preClosurePenalInterestOnTypeIdParamName);
actualChanges.put(preClosurePenalInterestOnTypeIdParamName, SavingsEnumerations.preClosurePenaltyInterestOnType(newValue));
actualChanges.put(localeParamName, localeAsInput);
this.preClosurePenalInterestOnType = newValue;
}
if (this.preClosurePenalInterest == null) {
baseDataValidator.parameter(preClosurePenalInterestParamName).value(this.preClosurePenalInterest)
.cantBeBlankWhenParameterProvidedIs(preClosurePenalApplicableParamName, this.preClosurePenalApplicable);
}
if (this.preClosurePenalInterestOnType == null) {
baseDataValidator.parameter(preClosurePenalInterestOnTypeIdParamName).value(this.preClosurePenalInterestOnType)
.cantBeBlankWhenParameterProvidedIs(preClosurePenalApplicableParamName, this.preClosurePenalApplicable);
}
} else { // reset if pre-closure penal interest is not applicable
this.preClosurePenalInterest = null;
this.preClosurePenalInterestOnType = null;
}
return actualChanges;
}
public boolean preClosurePenalApplicable() {
return this.preClosurePenalApplicable;
}
public BigDecimal preClosurePenalInterest() {
return this.preClosurePenalInterest;
}
public Integer preClosurePenalInterestOnTypeId() {
return this.preClosurePenalInterestOnType;
}
public PreClosurePenalInterestOnType preClosurePenalInterestOnType() {
return PreClosurePenalInterestOnType.fromInt(preClosurePenalInterestOnType);
}
public DepositPreClosureDetail copy() {
final boolean preClosurePenalApplicable = this.preClosurePenalApplicable;
final BigDecimal preClosurePenalInterest = this.preClosurePenalInterest;
final PreClosurePenalInterestOnType preClosurePenalInterestType = PreClosurePenalInterestOnType
.fromInt(this.preClosurePenalInterestOnType);
return DepositPreClosureDetail.createFrom(preClosurePenalApplicable, preClosurePenalInterest, preClosurePenalInterestType);
}
}
@@ -0,0 +1,105 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
/**
* RecurringDepositProductAmountDetails encapsulates all recurring Deposit Amount of a {@link RecurringDepositProduct}.
*/
@Embeddable
public class DepositProductAmountDetails {
@Column(name = "min_deposit_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal minDepositAmount;
@Column(name = "max_deposit_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal maxDepositAmount;
@Column(name = "deposit_amount", scale = 6, precision = 19, nullable = false)
private BigDecimal depositAmount;
public static DepositProductAmountDetails createFrom(final BigDecimal minDepositAmount, final BigDecimal depositAmount,
final BigDecimal maxDepositAmount) {
return new DepositProductAmountDetails(minDepositAmount, depositAmount, maxDepositAmount);
}
protected DepositProductAmountDetails() {
//
}
public DepositProductAmountDetails(final BigDecimal minDepositAmount, final BigDecimal depositAmount,
final BigDecimal maxDepositAmount) {
this.minDepositAmount = minDepositAmount;
this.depositAmount = depositAmount;
this.maxDepositAmount = maxDepositAmount;
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(20);
final String localeAsInput = command.locale();
final String minDepositAmountParamName = "minDepositAmount";
if (command.isChangeInBigDecimalParameterNamedWithNullCheck(minDepositAmountParamName, this.minDepositAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(minDepositAmountParamName);
actualChanges.put(minDepositAmountParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.minDepositAmount = newValue;
}
final String maxDepositAmountParamName = "maxDepositAmount";
if (command.isChangeInBigDecimalParameterNamedWithNullCheck(maxDepositAmountParamName, this.maxDepositAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(maxDepositAmountParamName);
actualChanges.put(maxDepositAmountParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.maxDepositAmount = newValue;
}
final String depositAmountParamName = "depositAmount";
if (command.isChangeInBigDecimalParameterNamedWithNullCheck(depositAmountParamName, this.depositAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(depositAmountParamName);
actualChanges.put(depositAmountParamName, newValue);
actualChanges.put("locale", localeAsInput);
this.depositAmount = newValue;
}
return actualChanges;
}
public BigDecimal getMinDepositAmount() {
return this.minDepositAmount;
}
public BigDecimal getMaxDepositAmount() {
return this.maxDepositAmount;
}
public BigDecimal getDepositAmount() {
return this.depositAmount;
}
}
@@ -0,0 +1,527 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.adjustAdvanceTowardsFuturePaymentsParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.allowWithdrawalParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.chartsParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.depositAmountParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.depositMaxAmountParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.depositMinAmountParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermTypeIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isMandatoryDepositParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maxDepositTermParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maxDepositTermTypeIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.minDepositTermParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.minDepositTermTypeIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalApplicableParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalInterestOnTypeIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalInterestParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.idParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.exception.InvalidCurrencyException;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.charge.exception.ChargeCannotBeAppliedToException;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChart;
import org.apache.fineract.portfolio.interestratechart.service.InterestRateChartAssembler;
import org.apache.fineract.portfolio.savings.PreClosurePenalInterestOnType;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
import org.apache.fineract.portfolio.tax.domain.TaxGroupRepositoryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DepositProductAssembler {
private final ChargeRepositoryWrapper chargeRepository;
private final InterestRateChartAssembler chartAssembler;
private final TaxGroupRepositoryWrapper taxGroupRepository;
@Autowired
public DepositProductAssembler(final ChargeRepositoryWrapper chargeRepository, final InterestRateChartAssembler chartAssembler,
final TaxGroupRepositoryWrapper taxGroupRepository) {
this.chargeRepository = chargeRepository;
this.chartAssembler = chartAssembler;
this.taxGroupRepository = taxGroupRepository;
}
public FixedDepositProduct assembleFixedDepositProduct(final JsonCommand command) {
final String name = command.stringValueOfParameterNamed(nameParamName);
final String shortName = command.stringValueOfParameterNamed(shortNameParamName);
final String description = command.stringValueOfParameterNamed(descriptionParamName);
final String currencyCode = command.stringValueOfParameterNamed(currencyCodeParamName);
final Integer digitsAfterDecimal = command.integerValueOfParameterNamed(digitsAfterDecimalParamName);
final Integer inMultiplesOf = command.integerValueOfParameterNamed(inMultiplesOfParamName);
final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal, inMultiplesOf);
BigDecimal interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName);
SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = null;
final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName);
if (interestPeriodTypeValue != null) {
interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.fromInt(interestPeriodTypeValue);
}
SavingsPostingInterestPeriodType interestPostingPeriodType = null;
final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName);
if (interestPostingPeriodTypeValue != null) {
interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodTypeValue);
}
SavingsInterestCalculationType interestCalculationType = null;
final Integer interestCalculationTypeValue = command.integerValueOfParameterNamed(interestCalculationTypeParamName);
if (interestCalculationTypeValue != null) {
interestCalculationType = SavingsInterestCalculationType.fromInt(interestCalculationTypeValue);
}
SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType = null;
final Integer interestCalculationDaysInYearTypeValue = command
.integerValueOfParameterNamed(interestCalculationDaysInYearTypeParamName);
if (interestCalculationDaysInYearTypeValue != null) {
interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(interestCalculationDaysInYearTypeValue);
}
final Integer lockinPeriodFrequency = command.integerValueOfParameterNamedDefaultToNullIfZero(lockinPeriodFrequencyParamName);
SavingsPeriodFrequencyType lockinPeriodFrequencyType = null;
final Integer lockinPeriodFrequencyTypeValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName);
if (lockinPeriodFrequencyTypeValue != null) {
lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
}
final BigDecimal minBalanceForInterestCalculation = command
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minBalanceForInterestCalculationParamName);
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
final DepositPreClosureDetail preClosureDetail = this.assemblePreClosureDetail(command);
final DepositTermDetail depositTermDetail = this.assembleDepositTermDetail(command);
final DepositProductAmountDetails depositProductAmountDetails = this.assembleDepositAmountDetails(command);
final DepositProductTermAndPreClosure productTermAndPreClosure = DepositProductTermAndPreClosure.createNew(preClosureDetail,
depositTermDetail, depositProductAmountDetails, null);
// Savings product charges
final Set<Charge> charges = assembleListOfSavingsProductCharges(command, currencyCode);
// Interest rate charts
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
final Set<InterestRateChart> charts = assembleListOfCharts(command, currency.getCode(), baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
if (interestRate == null) {
interestRate = BigDecimal.ZERO;
}
boolean withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
final Long taxGroupId = command.longValueOfParameterNamed(taxGroupIdParamName);
TaxGroup taxGroup = null;
if (taxGroupId != null) {
taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId);
}
FixedDepositProduct fixedDepositProduct = FixedDepositProduct.createNew(name, shortName, description, currency, interestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRuleType, charges, productTermAndPreClosure, charts,
minBalanceForInterestCalculation, withHoldTax, taxGroup);
// update product reference
productTermAndPreClosure.updateProductReference(fixedDepositProduct);
fixedDepositProduct.validateDomainRules();
return fixedDepositProduct;
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
public RecurringDepositProduct assembleRecurringDepositProduct(final JsonCommand command) {
final String name = command.stringValueOfParameterNamed(nameParamName);
final String shortName = command.stringValueOfParameterNamed(shortNameParamName);
final String description = command.stringValueOfParameterNamed(descriptionParamName);
final String currencyCode = command.stringValueOfParameterNamed(currencyCodeParamName);
final Integer digitsAfterDecimal = command.integerValueOfParameterNamed(digitsAfterDecimalParamName);
final Integer inMultiplesOf = command.integerValueOfParameterNamed(inMultiplesOfParamName);
final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal, inMultiplesOf);
BigDecimal interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName);
SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = null;
final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName);
if (interestPeriodTypeValue != null) {
interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.fromInt(interestPeriodTypeValue);
}
SavingsPostingInterestPeriodType interestPostingPeriodType = null;
final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName);
if (interestPostingPeriodTypeValue != null) {
interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodTypeValue);
}
SavingsInterestCalculationType interestCalculationType = null;
final Integer interestCalculationTypeValue = command.integerValueOfParameterNamed(interestCalculationTypeParamName);
if (interestCalculationTypeValue != null) {
interestCalculationType = SavingsInterestCalculationType.fromInt(interestCalculationTypeValue);
}
SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType = null;
final Integer interestCalculationDaysInYearTypeValue = command
.integerValueOfParameterNamed(interestCalculationDaysInYearTypeParamName);
if (interestCalculationDaysInYearTypeValue != null) {
interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(interestCalculationDaysInYearTypeValue);
}
final Integer lockinPeriodFrequency = command.integerValueOfParameterNamedDefaultToNullIfZero(lockinPeriodFrequencyParamName);
SavingsPeriodFrequencyType lockinPeriodFrequencyType = null;
final Integer lockinPeriodFrequencyTypeValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName);
if (lockinPeriodFrequencyTypeValue != null) {
lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
}
final BigDecimal minBalanceForInterestCalculation = command
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minBalanceForInterestCalculationParamName);
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
final DepositPreClosureDetail preClosureDetail = this.assemblePreClosureDetail(command);
final DepositTermDetail depositTermDetail = this.assembleDepositTermDetail(command);
final DepositProductAmountDetails depositProductAmountDetails = this.assembleDepositAmountDetails(command);
final DepositProductTermAndPreClosure productTermAndPreClosure = DepositProductTermAndPreClosure.createNew(preClosureDetail,
depositTermDetail, depositProductAmountDetails, null);
final DepositRecurringDetail recurringDetail = this.assembleRecurringDetail(command);
final DepositProductRecurringDetail productRecurringDetail = DepositProductRecurringDetail.createNew(recurringDetail, null);
// Savings product charges
final Set<Charge> charges = assembleListOfSavingsProductCharges(command, currencyCode);
// Interest rate charts
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
final Set<InterestRateChart> charts = assembleListOfCharts(command, currency.getCode(), baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
if (interestRate == null) {
interestRate = BigDecimal.ZERO;
}
final boolean withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
final TaxGroup taxGroup = assembleTaxGroup(command);
RecurringDepositProduct recurringDepositProduct = RecurringDepositProduct.createNew(name, shortName, description, currency,
interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRuleType, charges,
productTermAndPreClosure, productRecurringDetail, charts, minBalanceForInterestCalculation, taxGroup, withHoldTax);
// update product reference
productTermAndPreClosure.updateProductReference(recurringDepositProduct);
productRecurringDetail.updateProductReference(recurringDepositProduct);
recurringDepositProduct.validateDomainRules();
return recurringDepositProduct;
}
public DepositPreClosureDetail assemblePreClosureDetail(final JsonCommand command) {
boolean preClosurePenalApplicable = false;
BigDecimal preClosurePenalInterest = null;
PreClosurePenalInterestOnType preClosurePenalInterestType = null;
if (command.parameterExists(preClosurePenalApplicableParamName)) {
preClosurePenalApplicable = command.booleanObjectValueOfParameterNamed(preClosurePenalApplicableParamName);
if (preClosurePenalApplicable) {
preClosurePenalInterest = command.bigDecimalValueOfParameterNamed(preClosurePenalInterestParamName);
final Integer preClosurePenalInterestOnTypeId = command
.integerValueOfParameterNamed(preClosurePenalInterestOnTypeIdParamName);
preClosurePenalInterestType = preClosurePenalInterestOnTypeId == null ? null
: PreClosurePenalInterestOnType.fromInt(preClosurePenalInterestOnTypeId);
}
}
DepositPreClosureDetail preClosureDetail = DepositPreClosureDetail.createFrom(preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestType);
return preClosureDetail;
}
public DepositPreClosureDetail assemblePreClosureDetail(final JsonCommand command, DepositPreClosureDetail produPreClosureDetail) {
boolean preClosurePenalApplicable = false;
BigDecimal preClosurePenalInterest = null;
PreClosurePenalInterestOnType preClosurePenalInterestType = null;
Integer preClosurePenalInterestOnTypeId = null;
if (command.parameterExists(preClosurePenalApplicableParamName)) {
preClosurePenalApplicable = command.booleanObjectValueOfParameterNamed(preClosurePenalApplicableParamName);
if (preClosurePenalApplicable) {
if (command.parameterExists(preClosurePenalInterestParamName)) {
preClosurePenalInterest = command.bigDecimalValueOfParameterNamed(preClosurePenalInterestParamName);
} else {
preClosurePenalInterest = produPreClosureDetail.preClosurePenalInterest();
}
if (command.parameterExists(preClosurePenalInterestParamName)) {
preClosurePenalInterestOnTypeId = command.integerValueOfParameterNamed(preClosurePenalInterestOnTypeIdParamName);
} else {
preClosurePenalInterestOnTypeId = produPreClosureDetail.preClosurePenalInterestOnTypeId();
}
}
} else {
preClosurePenalApplicable = produPreClosureDetail.preClosurePenalApplicable();
preClosurePenalInterest = produPreClosureDetail.preClosurePenalInterest();
preClosurePenalInterestOnTypeId = produPreClosureDetail.preClosurePenalInterestOnTypeId();
}
preClosurePenalInterestType = preClosurePenalInterestOnTypeId == null ? null
: PreClosurePenalInterestOnType.fromInt(preClosurePenalInterestOnTypeId);
DepositPreClosureDetail preClosureDetail1 = DepositPreClosureDetail.createFrom(preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestType);
return preClosureDetail1;
}
public DepositTermDetail assembleDepositTermDetail(final JsonCommand command) {
final Integer minDepositTerm = command.integerValueOfParameterNamed(minDepositTermParamName);
final Integer maxDepositTerm = command.integerValueOfParameterNamed(maxDepositTermParamName);
final Integer minDepositTermTypeId = command.integerValueOfParameterNamed(minDepositTermTypeIdParamName);
final SavingsPeriodFrequencyType minDepositTermType = (minDepositTermTypeId == null) ? null
: SavingsPeriodFrequencyType.fromInt(minDepositTermTypeId);
final Integer maxDepositTermTypeId = command.integerValueOfParameterNamed(maxDepositTermTypeIdParamName);
final SavingsPeriodFrequencyType maxDepositTermType = (maxDepositTermTypeId == null) ? null
: SavingsPeriodFrequencyType.fromInt(maxDepositTermTypeId);
final Integer inMultiplesOfDepositTerm = command.integerValueOfParameterNamed(inMultiplesOfDepositTermParamName);
final Integer inMultiplesOfDepositTermTypeId = command.integerValueOfParameterNamed(inMultiplesOfDepositTermTypeIdParamName);
final SavingsPeriodFrequencyType inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null
: SavingsPeriodFrequencyType.fromInt(inMultiplesOfDepositTermTypeId);
final DepositTermDetail depositTermDetail = DepositTermDetail.createFrom(minDepositTerm, maxDepositTerm, minDepositTermType,
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType);
return depositTermDetail;
}
public DepositTermDetail assembleDepositTermDetail(final JsonCommand command, final DepositTermDetail prodDepositTermDetail) {
Integer minDepositTerm = null;
Integer maxDepositTerm = null;
Integer minDepositTermTypeId = null;
Integer maxDepositTermTypeId = null;
Integer inMultiplesOfDepositTerm = null;
Integer inMultiplesOfDepositTermTypeId = null;
if (command.parameterExists(minDepositTermParamName)) {
minDepositTerm = command.integerValueOfParameterNamed(minDepositTermParamName);
} else if (prodDepositTermDetail != null) {
minDepositTerm = prodDepositTermDetail.minDepositTerm();
}
if (command.parameterExists(maxDepositTermParamName)) {
maxDepositTerm = command.integerValueOfParameterNamed(maxDepositTermParamName);
} else if (prodDepositTermDetail != null) {
maxDepositTerm = prodDepositTermDetail.maxDepositTerm();
}
if (command.parameterExists(minDepositTermTypeIdParamName)) {
minDepositTermTypeId = command.integerValueOfParameterNamed(minDepositTermTypeIdParamName);
} else if (prodDepositTermDetail != null) {
minDepositTermTypeId = prodDepositTermDetail.minDepositTermType();
}
if (command.parameterExists(maxDepositTermTypeIdParamName)) {
maxDepositTermTypeId = command.integerValueOfParameterNamed(maxDepositTermTypeIdParamName);
} else if (prodDepositTermDetail != null) {
maxDepositTermTypeId = prodDepositTermDetail.maxDepositTermType();
}
final SavingsPeriodFrequencyType minDepositTermType = (minDepositTermTypeId == null) ? null
: SavingsPeriodFrequencyType.fromInt(minDepositTermTypeId);
final SavingsPeriodFrequencyType maxDepositTermType = (maxDepositTermTypeId == null) ? null
: SavingsPeriodFrequencyType.fromInt(maxDepositTermTypeId);
if (command.parameterExists(inMultiplesOfDepositTermParamName)) {
inMultiplesOfDepositTerm = command.integerValueOfParameterNamed(inMultiplesOfDepositTermParamName);
} else if (prodDepositTermDetail != null) {
inMultiplesOfDepositTerm = prodDepositTermDetail.inMultiplesOfDepositTerm();
}
if (command.parameterExists(preClosurePenalApplicableParamName)) {
inMultiplesOfDepositTermTypeId = command.integerValueOfParameterNamed(inMultiplesOfDepositTermTypeIdParamName);
} else if (prodDepositTermDetail != null) {
inMultiplesOfDepositTermTypeId = prodDepositTermDetail.inMultiplesOfDepositTermType();
}
final SavingsPeriodFrequencyType inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null
: SavingsPeriodFrequencyType.fromInt(inMultiplesOfDepositTermTypeId);
final DepositTermDetail depositTermDetail = DepositTermDetail.createFrom(minDepositTerm, maxDepositTerm, minDepositTermType,
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType);
return depositTermDetail;
}
public DepositRecurringDetail assembleRecurringDetail(final JsonCommand command) {
Boolean isMandatoryDeposit = command.booleanObjectValueOfParameterNamed(isMandatoryDepositParamName);
Boolean allowWithdrawal = command.booleanObjectValueOfParameterNamed(allowWithdrawalParamName);
Boolean adjustAdvanceTowardsFuturePayments = command
.booleanObjectValueOfParameterNamed(adjustAdvanceTowardsFuturePaymentsParamName);
if (isMandatoryDeposit == null) {
isMandatoryDeposit = false;
}
if (allowWithdrawal == null) {
allowWithdrawal = false;
}
if (adjustAdvanceTowardsFuturePayments == null) {
adjustAdvanceTowardsFuturePayments = false;
}
final DepositRecurringDetail depositRecurringDetail = DepositRecurringDetail.createFrom(isMandatoryDeposit, allowWithdrawal,
adjustAdvanceTowardsFuturePayments);
return depositRecurringDetail;
}
public Set<Charge> assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) {
final Set<Charge> charges = new HashSet<>();
if (command.parameterExists(chargesParamName)) {
final JsonArray chargesArray = command.arrayOfParameterNamed(chargesParamName);
if (chargesArray != null) {
for (int i = 0; i < chargesArray.size(); i++) {
final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
if (jsonObject.has(idParamName)) {
final Long id = jsonObject.get(idParamName).getAsLong();
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id);
if (!charge.isSavingsCharge()) {
final String errorMessage = "Charge with identifier " + charge.getId()
+ " cannot be applied to Savings product.";
throw new ChargeCannotBeAppliedToException("savings.product", errorMessage, charge.getId());
}
if (!savingsProductCurrencyCode.equals(charge.getCurrencyCode())) {
final String errorMessage = "Charge and Savings Product must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.savings.product", errorMessage);
}
charges.add(charge);
}
}
}
}
return charges;
}
private Set<InterestRateChart> assembleListOfCharts(JsonCommand command, String currencyCode, DataValidatorBuilder baseDataValidator) {
final Set<InterestRateChart> charts = new HashSet<>();
if (command.parameterExists(chartsParamName)) {
final JsonArray chartsArray = command.arrayOfParameterNamed(chartsParamName);
if (chartsArray != null) {
for (int i = 0; i < chartsArray.size(); i++) {
final JsonObject interstRateChartElement = chartsArray.get(i).getAsJsonObject();
InterestRateChart chart = this.chartAssembler.assembleFrom(interstRateChartElement, currencyCode, baseDataValidator);
charts.add(chart);
}
}
}
return charts;
}
public DepositProductAmountDetails assembleDepositAmountDetails(final JsonCommand command) {
BigDecimal minDepositAmount = null;
if (command.parameterExists(depositMinAmountParamName)) {
minDepositAmount = command.bigDecimalValueOfParameterNamed(depositMinAmountParamName);
}
BigDecimal maxDepositAmount = null;
if (command.parameterExists(depositMaxAmountParamName)) {
maxDepositAmount = command.bigDecimalValueOfParameterNamed(depositMaxAmountParamName);
}
BigDecimal depositAmount = null;
if (command.parameterExists(depositAmountParamName)) {
depositAmount = command.bigDecimalValueOfParameterNamed(depositAmountParamName);
}
final DepositProductAmountDetails depositRecurringDetail = new DepositProductAmountDetails(minDepositAmount, depositAmount,
maxDepositAmount);
return depositRecurringDetail;
}
public TaxGroup assembleTaxGroup(final JsonCommand command) {
final Long taxGroupId = command.longValueOfParameterNamed(taxGroupIdParamName);
TaxGroup taxGroup = null;
if (taxGroupId != null) {
taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId);
}
return taxGroup;
}
}
@@ -0,0 +1,71 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_deposit_product_recurring_detail")
public class DepositProductRecurringDetail extends AbstractPersistableCustom {
@Embedded
private DepositRecurringDetail recurringDetail;
@OneToOne
@JoinColumn(name = "savings_product_id", nullable = false)
private RecurringDepositProduct product;
protected DepositProductRecurringDetail() {
}
public static DepositProductRecurringDetail createNew(DepositRecurringDetail recurringDetail, SavingsProduct product) {
return new DepositProductRecurringDetail(recurringDetail, product);
}
private DepositProductRecurringDetail(DepositRecurringDetail recurringDetail, SavingsProduct product) {
this.recurringDetail = recurringDetail;
this.product = (RecurringDepositProduct) product;
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
if (this.recurringDetail != null) {
actualChanges.putAll(this.recurringDetail.update(command));
}
return actualChanges;
}
public DepositRecurringDetail recurringDetail() {
return this.recurringDetail;
}
public void updateProductReference(final SavingsProduct product) {
this.product = (RecurringDepositProduct) product;
}
}
@@ -0,0 +1,98 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_deposit_product_term_and_preclosure")
public class DepositProductTermAndPreClosure extends AbstractPersistableCustom {
@Embedded
private DepositPreClosureDetail preClosureDetail;
@Embedded
protected DepositTermDetail depositTermDetail;
@OneToOne
@JoinColumn(name = "savings_product_id", nullable = false)
private FixedDepositProduct product;
@Embedded
private DepositProductAmountDetails depositProductAmountDetails;
protected DepositProductTermAndPreClosure() {
}
public static DepositProductTermAndPreClosure createNew(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
DepositProductAmountDetails depositProductMinMaxAmountDetails, SavingsProduct product) {
return new DepositProductTermAndPreClosure(preClosureDetail, depositTermDetail, depositProductMinMaxAmountDetails, product);
}
private DepositProductTermAndPreClosure(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
DepositProductAmountDetails depositProductMinMaxAmountDetails, SavingsProduct product) {
this.preClosureDetail = preClosureDetail;
this.depositTermDetail = depositTermDetail;
this.depositProductAmountDetails = depositProductMinMaxAmountDetails;
this.product = (FixedDepositProduct) product;
}
public Map<String, Object> update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
if (this.preClosureDetail != null) {
actualChanges.putAll(this.preClosureDetail.update(command, baseDataValidator));
}
if (this.depositTermDetail != null) {
actualChanges.putAll(this.depositTermDetail.update(command, baseDataValidator));
}
if (this.depositProductAmountDetails != null) {
actualChanges.putAll(this.depositProductAmountDetails.update(command));
}
return actualChanges;
}
public DepositPreClosureDetail depositPreClosureDetail() {
return this.preClosureDetail;
}
public DepositTermDetail depositTermDetail() {
return this.depositTermDetail;
}
public DepositProductAmountDetails depositProductAmountDetails() {
return this.depositProductAmountDetails;
}
public void updateProductReference(final SavingsProduct product) {
this.product = (FixedDepositProduct) product;
}
}
@@ -0,0 +1,102 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.adjustAdvanceTowardsFuturePaymentsParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.allowWithdrawalParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isMandatoryDepositParamName;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
/**
* DepositRecurringDetail encapsulates all the details of a {@link RecurringDepositProduct} that are also used and
* persisted by a {@link RecurringDepositAccount}.
*/
@Embeddable
public class DepositRecurringDetail {
@Column(name = "is_mandatory", nullable = true)
private boolean isMandatoryDeposit;
@Column(name = "allow_withdrawal", nullable = true)
private boolean allowWithdrawal;
@Column(name = "adjust_advance_towards_future_payments", nullable = true)
private boolean adjustAdvanceTowardsFuturePayments;
protected DepositRecurringDetail() {
//
}
public static DepositRecurringDetail createFrom(final boolean isMandatoryDeposit, boolean allowWithdrawal,
boolean adjustAdvanceTowardsFuturePayments) {
return new DepositRecurringDetail(isMandatoryDeposit, allowWithdrawal, adjustAdvanceTowardsFuturePayments);
}
private DepositRecurringDetail(final boolean isMandatoryDeposit, boolean allowWithdrawal, boolean adjustAdvanceTowardsFuturePayments) {
this.isMandatoryDeposit = isMandatoryDeposit;
this.allowWithdrawal = allowWithdrawal;
this.adjustAdvanceTowardsFuturePayments = adjustAdvanceTowardsFuturePayments;
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
if (command.isChangeInBooleanParameterNamed(isMandatoryDepositParamName, this.isMandatoryDeposit)) {
final boolean newValue = command.booleanObjectValueOfParameterNamed(isMandatoryDepositParamName);
actualChanges.put(isMandatoryDepositParamName, newValue);
this.isMandatoryDeposit = newValue;
}
if (command.isChangeInBooleanParameterNamed(allowWithdrawalParamName, this.allowWithdrawal)) {
final boolean newValue = command.booleanObjectValueOfParameterNamed(allowWithdrawalParamName);
actualChanges.put(allowWithdrawalParamName, newValue);
this.allowWithdrawal = newValue;
}
if (command.isChangeInBooleanParameterNamed(adjustAdvanceTowardsFuturePaymentsParamName, this.adjustAdvanceTowardsFuturePayments)) {
final boolean newValue = command.booleanObjectValueOfParameterNamed(adjustAdvanceTowardsFuturePaymentsParamName);
actualChanges.put(adjustAdvanceTowardsFuturePaymentsParamName, newValue);
this.adjustAdvanceTowardsFuturePayments = newValue;
}
return actualChanges;
}
public boolean isMandatoryDeposit() {
return this.isMandatoryDeposit;
}
public boolean allowWithdrawal() {
return this.allowWithdrawal;
}
public boolean adjustAdvanceTowardsFuturePayments() {
return this.adjustAdvanceTowardsFuturePayments;
}
public DepositRecurringDetail copy() {
return DepositRecurringDetail.createFrom(this.isMandatoryDeposit, this.allowWithdrawal, this.adjustAdvanceTowardsFuturePayments);
}
}
@@ -0,0 +1,266 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.inMultiplesOfDepositTermTypeIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maxDepositTermParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maxDepositTermTypeIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.minDepositTermParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.minDepositTermTypeIdParamName;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
/**
* DepositTermDetail encapsulates all the details of a {@link FixedDepositProduct} that are also used and persisted by a
* {@link FixedDepositAccount}.
*/
@Embeddable
public class DepositTermDetail {
@Column(name = "min_deposit_term", nullable = true)
private Integer minDepositTerm;
@Column(name = "max_deposit_term", nullable = true)
private Integer maxDepositTerm;
@Column(name = "min_deposit_term_type_enum", nullable = true)
private Integer minDepositTermType;
@Column(name = "max_deposit_term_type_enum", nullable = true)
private Integer maxDepositTermType;
@Column(name = "in_multiples_of_deposit_term", nullable = true)
private Integer inMultiplesOfDepositTerm;
@Column(name = "in_multiples_of_deposit_term_type_enum", nullable = true)
private Integer inMultiplesOfDepositTermType;
public static DepositTermDetail createFrom(final Integer minDepositTerm, final Integer maxDepositTerm,
final SavingsPeriodFrequencyType minDepositTermType, final SavingsPeriodFrequencyType maxDepositTermType,
final Integer inMultiplesOfDepositTerm, final SavingsPeriodFrequencyType inMultiplesOfDepositTermType) {
return new DepositTermDetail(minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm,
inMultiplesOfDepositTermType);
}
protected DepositTermDetail() {
//
}
private DepositTermDetail(final Integer minDepositTerm, final Integer maxDepositTerm,
final SavingsPeriodFrequencyType minDepositTermType, final SavingsPeriodFrequencyType maxDepositTermType,
final Integer inMultiplesOfDepositTerm, final SavingsPeriodFrequencyType inMultiplesOfDepositTermType) {
this.minDepositTerm = minDepositTerm;
this.maxDepositTerm = maxDepositTerm;
this.minDepositTermType = (minDepositTermType == null) ? null : minDepositTermType.getValue();
this.maxDepositTermType = (maxDepositTermType == null) ? null : maxDepositTermType.getValue();
this.inMultiplesOfDepositTerm = inMultiplesOfDepositTerm;
this.inMultiplesOfDepositTermType = (inMultiplesOfDepositTermType == null) ? null : inMultiplesOfDepositTermType.getValue();
}
public Map<String, Object> update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
if (command.isChangeInIntegerParameterNamed(minDepositTermParamName, this.minDepositTerm)) {
final Integer newValue = command.integerValueOfParameterNamed(minDepositTermParamName);
actualChanges.put(minDepositTermParamName, newValue);
this.minDepositTerm = newValue;
}
if (command.isChangeInIntegerParameterNamed(maxDepositTermParamName, this.maxDepositTerm)) {
final Integer newValue = command.integerValueOfParameterNamed(maxDepositTermParamName);
actualChanges.put(maxDepositTermParamName, newValue);
this.maxDepositTerm = newValue;
}
if (command.isChangeInIntegerParameterNamed(minDepositTermTypeIdParamName, this.minDepositTermType)) {
final Integer newValue = command.integerValueOfParameterNamed(minDepositTermTypeIdParamName);
actualChanges.put(minDepositTermTypeIdParamName, SavingsEnumerations.depositTermFrequencyType(newValue));
this.minDepositTermType = newValue;
}
if (command.isChangeInIntegerParameterNamed(maxDepositTermTypeIdParamName, this.maxDepositTermType)) {
final Integer newValue = command.integerValueOfParameterNamed(maxDepositTermTypeIdParamName);
actualChanges.put(maxDepositTermTypeIdParamName, SavingsEnumerations.depositTermFrequencyType(newValue));
this.maxDepositTermType = newValue;
}
if (command.isChangeInIntegerParameterNamed(inMultiplesOfDepositTermParamName, this.inMultiplesOfDepositTerm)) {
final Integer newValue = command.integerValueOfParameterNamed(inMultiplesOfDepositTermParamName);
actualChanges.put(inMultiplesOfDepositTermParamName, newValue);
this.inMultiplesOfDepositTerm = newValue;
}
if (command.isChangeInIntegerParameterNamed(inMultiplesOfDepositTermTypeIdParamName, this.inMultiplesOfDepositTermType)) {
final Integer newValue = command.integerValueOfParameterNamed(inMultiplesOfDepositTermTypeIdParamName);
actualChanges.put(inMultiplesOfDepositTermTypeIdParamName, SavingsEnumerations.inMultiplesOfDepositTermFrequencyType(newValue));
this.inMultiplesOfDepositTermType = newValue;
}
if (isMinDepositTermGreaterThanMaxDepositTerm()) {
baseDataValidator.parameter(minDepositTermParamName).value(this.minDepositTerm).failWithCode(".greater.than.maxDepositTerm",
this.minDepositTerm, this.maxDepositTerm);
}
return actualChanges;
}
public boolean isMinDepositTermGreaterThanMaxDepositTerm() {
if (this.minDepositTerm != null && this.maxDepositTerm != null) {
final Integer minDepositInDays = this.convertToSafeDays(minDepositTerm, SavingsPeriodFrequencyType.fromInt(minDepositTermType));
final Integer maxDepositInDays = this.convertToSafeDays(maxDepositTerm, SavingsPeriodFrequencyType.fromInt(maxDepositTermType));
if (minDepositInDays.compareTo(maxDepositInDays) > 0) {
return true;
}
}
return false;
}
public Integer minDepositTerm() {
return this.minDepositTerm;
}
public Integer maxDepositTerm() {
return this.maxDepositTerm;
}
public Integer minDepositTermType() {
return this.minDepositTermType;
}
public Integer maxDepositTermType() {
return this.maxDepositTermType;
}
public Integer inMultiplesOfDepositTerm() {
return this.inMultiplesOfDepositTerm;
}
public Integer inMultiplesOfDepositTermType() {
return this.inMultiplesOfDepositTermType;
}
public boolean isDepositBetweenMinAndMax(LocalDate depositStartDate, LocalDate depositEndDate) {
return isEqualOrGreaterThanMin(depositStartDate, depositEndDate) && isEqualOrLessThanMax(depositStartDate, depositEndDate);
}
public boolean isValidInMultiplesOfPeriod(final Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequencyType) {
boolean isValidInMultiplesOfPeriod = true;
final Integer depositPeriodInDays = this.convertToSafeDays(depositPeriod, depositPeriodFrequencyType);
if (this.inMultiplesOfDepositTerm() != null) {
final Integer inMultiplesOfInDays = this.convertToSafeDays(this.inMultiplesOfDepositTerm(),
SavingsPeriodFrequencyType.fromInt(this.inMultiplesOfDepositTermType()));
final Integer minDepositInDays = this.convertToSafeDays(minDepositTerm, SavingsPeriodFrequencyType.fromInt(minDepositTermType));
if (inMultiplesOfInDays != 0) {
isValidInMultiplesOfPeriod = ((depositPeriodInDays - minDepositInDays) % inMultiplesOfInDays == 0);
}
}
return isValidInMultiplesOfPeriod;
}
private boolean isEqualOrGreaterThanMin(LocalDate depositStartDate, LocalDate depositEndDate) {
if (minDepositTerm() == null) {
return true;
}
final SavingsPeriodFrequencyType periodFrequencyType = SavingsPeriodFrequencyType.fromInt(this.minDepositTermType());
final Integer depositPeriod = depositPeriod(depositStartDate, depositEndDate, periodFrequencyType);
return minDepositTerm() == null || depositPeriod.compareTo(minDepositTerm()) >= 0;
}
private boolean isEqualOrLessThanMax(LocalDate depositStartDate, LocalDate depositEndDate) {
if (maxDepositTerm() == null) {
return true;
}
final SavingsPeriodFrequencyType periodFrequencyType = SavingsPeriodFrequencyType.fromInt(this.maxDepositTermType());
final Integer depositPeriod = depositPeriod(depositStartDate, depositEndDate, periodFrequencyType);
return maxDepositTerm() == null || depositPeriod.compareTo(maxDepositTerm()) <= 0;
}
public Integer depositPeriod(final LocalDate periodStartDate, final LocalDate periodEndDate,
final SavingsPeriodFrequencyType periodFrequencyType) {
Integer actualDepositPeriod = 0;
switch (periodFrequencyType) {
case DAYS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.DAYS.between(periodStartDate, periodEndDate));
break;
case WEEKS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.WEEKS.between(periodStartDate, periodEndDate));
break;
case MONTHS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.MONTHS.between(periodStartDate, periodEndDate));
break;
case YEARS:
actualDepositPeriod = Math.toIntExact(ChronoUnit.YEARS.between(periodStartDate, periodEndDate));
break;
case INVALID:
actualDepositPeriod = 0;// default value
break;
}
return actualDepositPeriod;
}
private Integer convertToSafeDays(final Integer period, final SavingsPeriodFrequencyType periodFrequencyType) {
Integer toDays = 0;
switch (periodFrequencyType) {
case DAYS:
toDays = period;
break;
case WEEKS:
toDays = period * 7;
break;
case MONTHS:
toDays = period * 30;// converting to stard 30 days
break;
case YEARS:
toDays = period * 365;
break;
case INVALID:
toDays = 0;// default value
break;
}
return toDays;
}
public DepositTermDetail copy() {
final Integer minDepositTerm = this.minDepositTerm;
final Integer maxDepositTerm = this.maxDepositTerm;
final SavingsPeriodFrequencyType minDepositTermType = SavingsPeriodFrequencyType.fromInt(this.minDepositTermType);
final SavingsPeriodFrequencyType maxDepositTermType = SavingsPeriodFrequencyType.fromInt(this.maxDepositTermType);
final Integer inMultiplesOfDepositTerm = this.inMultiplesOfDepositTerm;
final SavingsPeriodFrequencyType inMultiplesOfDepositTermType = SavingsPeriodFrequencyType
.fromInt(this.inMultiplesOfDepositTermType());
return DepositTermDetail.createFrom(minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType);
}
}
@@ -0,0 +1,346 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.deleteParamName;
import static org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants.idParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maxDepositTermParamName;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import jakarta.persistence.CascadeType;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Transient;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChart;
import org.apache.fineract.portfolio.interestratechart.service.InterestRateChartAssembler;
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
@Entity
@DiscriminatorValue("200")
public class FixedDepositProduct extends SavingsProduct {
@OneToOne(mappedBy = "product", cascade = CascadeType.ALL)
private DepositProductTermAndPreClosure productTermAndPreClosure;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "m_deposit_product_interest_rate_chart", joinColumns = @JoinColumn(name = "deposit_product_id"), inverseJoinColumns = @JoinColumn(name = "interest_rate_chart_id", unique = true))
protected Set<InterestRateChart> charts;
@Transient
protected InterestRateChartAssembler chartAssembler;
protected FixedDepositProduct() {
}
public static FixedDepositProduct createNew(final String name, final String shortName, final String description,
final MonetaryCurrency currency, final BigDecimal interestRate,
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final Integer lockinPeriodFrequency,
final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final AccountingRuleType accountingRuleType,
final Set<Charge> charges, final DepositProductTermAndPreClosure productTermAndPreClosure, final Set<InterestRateChart> charts,
BigDecimal minBalanceForInterestCalculation, boolean withHoldTax, TaxGroup taxGroup) {
final BigDecimal minRequiredOpeningBalance = null;
final boolean withdrawalFeeApplicableForTransfer = false;
final boolean allowOverdraft = false;
final BigDecimal overdraftLimit = null;
return new FixedDepositProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges,
productTermAndPreClosure, charts, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation, withHoldTax, taxGroup);
}
protected FixedDepositProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency,
final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
final DepositProductTermAndPreClosure productTermAndPreClosure, final Set<InterestRateChart> charts,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final BigDecimal minBalanceForInterestCalculation,
boolean withHoldTax, TaxGroup taxGroup) {
super(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit,
minBalanceForInterestCalculation, withHoldTax, taxGroup);
if (charts != null) {
this.charts = charts;
}
this.productTermAndPreClosure = productTermAndPreClosure;
}
public void addCharts(final Set<InterestRateChart> newCharts) {
final Set<InterestRateChart> existingCharts = setOfCharts();
existingCharts.addAll(newCharts);
}
public void addChart(final InterestRateChart newChart) {
final Set<InterestRateChart> existingCharts = setOfCharts();
existingCharts.add(newChart);
}
public Set<InterestRateChart> setOfCharts() {
if (this.charts == null) {
this.charts = new HashSet<>();
}
return this.charts;
}
@Override
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
actualChanges.putAll(this.update(command, baseDataValidator));
validateDomainRules(baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
return actualChanges;
}
protected Map<String, Object> update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
actualChanges.putAll(super.update(command));
if (this.productTermAndPreClosure != null) {
actualChanges.putAll(this.productTermAndPreClosure.update(command, baseDataValidator));
}
// update chart Slabs
if (command.hasParameter(DepositsApiConstants.chartsParamName)) {
updateCharts(command, actualChanges, baseDataValidator);
}
return actualChanges;
}
private void updateCharts(JsonCommand command, Map<String, Object> actualChanges, final DataValidatorBuilder baseDataValidator) {
final Map<String, Object> deletedCharts = new HashMap<>();
final Map<String, Object> chartsChanges = new HashMap<>();
if (command.hasParameter(DepositsApiConstants.chartsParamName)) {
final JsonArray array = command.arrayOfParameterNamed(DepositsApiConstants.chartsParamName);
if (array != null) {
for (int i = 0; i < array.size(); i++) {
final JsonObject chartElement = array.get(i).getAsJsonObject();
JsonCommand chartCommand = JsonCommand.fromExistingCommand(command, chartElement);
if (chartCommand.parameterExists(idParamName)) {
final Long chartId = chartCommand.longValueOfParameterNamed(idParamName);
final InterestRateChart chart = this.findChart(chartId);
if (chart == null) {
baseDataValidator.parameter(idParamName).value(chartId).failWithCode("no.chart.associated.with.id");
} else if (chartCommand.parameterExists(deleteParamName)) {
if (this.removeChart(chart)) {
deletedCharts.put(idParamName, chartId);
}
} else {
chart.update(chartCommand, chartsChanges, baseDataValidator, this.setOfCharts(), this.currency().getCode());
}
} else {
// assemble chart
final InterestRateChart newChart = this.chartAssembler.assembleFrom(chartElement, this.currency().getCode(),
baseDataValidator);
this.addChart(newChart);
}
}
}
}
// this.validateCharts(baseDataValidator);
// add chart changes to actual changes list.
if (!chartsChanges.isEmpty()) {
actualChanges.put(InterestRateChartApiConstants.chartSlabs, chartsChanges);
}
// add deleted chart to actual changes
if (!deletedCharts.isEmpty()) {
actualChanges.put("deletedChartSlabs", deletedCharts);
}
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
@Override
public InterestRateChart findChart(Long chartId) {
final Set<InterestRateChart> charts = setOfCharts();
for (InterestRateChart chart : charts) {
if (chart.getId().equals(chartId)) {
return chart;
}
}
return null;
}
private boolean removeChart(InterestRateChart chart) {
Set<InterestRateChart> charts = setOfCharts();
return charts.remove(chart);
}
public void setHelpers(final InterestRateChartAssembler chartAssembler) {
this.chartAssembler = chartAssembler;
}
public void validateCharts(final DataValidatorBuilder baseDataValidator) {
final Set<InterestRateChart> charts = this.setOfCharts();
for (InterestRateChart existingChart : charts) {
this.validateChart(baseDataValidator, existingChart);
}
}
public void validateChart(final DataValidatorBuilder baseDataValidator, final InterestRateChart comparingChart) {
final Set<InterestRateChart> charts = this.setOfCharts();
for (InterestRateChart existingChart : charts) {
if (!existingChart.equals(comparingChart)) {
if (existingChart.chartFields().isOverlapping(comparingChart.chartFields())) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.overlapping.from.and.end.dates",
existingChart.getFromDate(), existingChart.getEndDate(), comparingChart.getFromDate(),
comparingChart.getEndDate());
}
}
}
}
@Override
public InterestRateChart applicableChart(final LocalDate target) {
InterestRateChart applicableChart = null;
if (this.charts != null) {
for (InterestRateChart chart : this.charts) {
if (chart.isApplicableChartFor(target)) {
applicableChart = chart;
break;
}
}
}
return applicableChart;
}
public DepositProductTermAndPreClosure depositProductTermAndPreClosure() {
return this.productTermAndPreClosure;
}
public void validateInterestPostingAndCompoundingPeriodTypes(final DataValidatorBuilder baseDataValidator) {
Map<SavingsPostingInterestPeriodType, List<SavingsCompoundingInterestPeriodType>> postingtoCompoundMap = new HashMap<>();
postingtoCompoundMap.put(SavingsPostingInterestPeriodType.MONTHLY, Arrays.asList(new SavingsCompoundingInterestPeriodType[] {
SavingsCompoundingInterestPeriodType.DAILY, SavingsCompoundingInterestPeriodType.MONTHLY }));
postingtoCompoundMap.put(SavingsPostingInterestPeriodType.QUATERLY,
Arrays.asList(new SavingsCompoundingInterestPeriodType[] { SavingsCompoundingInterestPeriodType.DAILY,
SavingsCompoundingInterestPeriodType.MONTHLY, SavingsCompoundingInterestPeriodType.QUATERLY }));
postingtoCompoundMap.put(SavingsPostingInterestPeriodType.BIANNUAL,
Arrays.asList(new SavingsCompoundingInterestPeriodType[] { SavingsCompoundingInterestPeriodType.DAILY,
SavingsCompoundingInterestPeriodType.MONTHLY, SavingsCompoundingInterestPeriodType.QUATERLY,
SavingsCompoundingInterestPeriodType.BI_ANNUAL }));
postingtoCompoundMap.put(SavingsPostingInterestPeriodType.ANNUAL,
Arrays.asList(new SavingsCompoundingInterestPeriodType[] { SavingsCompoundingInterestPeriodType.DAILY,
SavingsCompoundingInterestPeriodType.MONTHLY, SavingsCompoundingInterestPeriodType.QUATERLY,
SavingsCompoundingInterestPeriodType.BI_ANNUAL, SavingsCompoundingInterestPeriodType.ANNUAL }));
SavingsPostingInterestPeriodType savingsPostingInterestPeriodType = SavingsPostingInterestPeriodType
.fromInt(interestPostingPeriodType);
SavingsCompoundingInterestPeriodType savingsCompoundingInterestPeriodType = SavingsCompoundingInterestPeriodType
.fromInt(interestCompoundingPeriodType);
if (postingtoCompoundMap.get(savingsPostingInterestPeriodType) == null
|| !postingtoCompoundMap.get(savingsPostingInterestPeriodType).contains(savingsCompoundingInterestPeriodType)) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("posting.period.type.is.less.than.compound.period.type",
savingsPostingInterestPeriodType.name(), savingsCompoundingInterestPeriodType.name());
}
}
public void validateDomainRules() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
validateDomainRules(baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void validateDomainRules(final DataValidatorBuilder baseDataValidator) {
final DepositTermDetail termDetails = this.depositProductTermAndPreClosure().depositTermDetail();
final boolean isMinTermGreaterThanMax = termDetails.isMinDepositTermGreaterThanMaxDepositTerm();
if (isMinTermGreaterThanMax) {
final Integer maxTerm = termDetails.maxDepositTerm();
baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm)
.failWithCodeNoParameterAddedToErrorCode("max.term.lessthan.min.term");
}
if (this.charts != null) {
validateCharts(baseDataValidator);
} else if (this.nominalAnnualInterestRate == null || this.nominalAnnualInterestRate.compareTo(BigDecimal.ZERO) == 0) {
baseDataValidator.reset().parameter(DepositsApiConstants.nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate)
.failWithCodeNoParameterAddedToErrorCode("interest.chart.or.nominal.interest.rate.required");
}
this.validateInterestPostingAndCompoundingPeriodTypes(baseDataValidator);
}
}
@@ -0,0 +1,34 @@
/**
* 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.portfolio.savings.domain;
import java.math.BigDecimal;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface GSIMRepositoy
extends JpaRepository<GroupSavingsIndividualMonitoring, Long>, JpaSpecificationExecutor<GroupSavingsIndividualMonitoring> {
GroupSavingsIndividualMonitoring findOneByIsAcceptingChild(boolean acceptingChild);
GroupSavingsIndividualMonitoring findOneByIsAcceptingChildAndApplicationId(boolean acceptingChild, BigDecimal applicationId);
GroupSavingsIndividualMonitoring findOneByAccountNumber(String accountNumber);
}
@@ -0,0 +1,138 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import java.math.BigDecimal;
import java.util.Set;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.portfolio.group.domain.Group;
@Entity
@Table(name = "gsim_accounts", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_number" }, name = "gsim_id") })
public final class GroupSavingsIndividualMonitoring extends AbstractPersistableCustom {
@ManyToOne
@JoinColumn(name = "group_id", nullable = false)
private Group group;
@Column(name = "account_number", nullable = false)
private String accountNumber;
@Column(name = "parent_deposit")
private BigDecimal parentDeposit;
@Column(name = "child_accounts_count")
private Long childAccountsCount;
@Column(name = "accepting_child")
private Boolean isAcceptingChild;
@OneToMany
private Set<SavingsAccount> childSaving;
@Column(name = "savings_status_id", nullable = false)
private Integer savingsStatus;
@Column(name = "application_id", nullable = true)
private BigDecimal applicationId;
private GroupSavingsIndividualMonitoring() {}
private GroupSavingsIndividualMonitoring(String accountNumber, Group group, BigDecimal parentDeposit, Long childAccountsCount,
Boolean isAcceptingChild, Integer savingsStatus, BigDecimal applicationId) {
this.accountNumber = accountNumber;
this.group = group;
this.parentDeposit = parentDeposit;
this.childAccountsCount = childAccountsCount;
this.isAcceptingChild = isAcceptingChild;
this.savingsStatus = savingsStatus;
this.applicationId = applicationId;
}
public static GroupSavingsIndividualMonitoring getInstance(String accountNumber, Group group, BigDecimal parentDeposit,
Long childAccountsCount, Boolean isAcceptingChild, Integer savingsStatus, BigDecimal applicationId) {
return new GroupSavingsIndividualMonitoring(accountNumber, group, parentDeposit, childAccountsCount, isAcceptingChild,
savingsStatus, applicationId);
}
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public BigDecimal getParentDeposit() {
return parentDeposit;
}
public void setParentDeposit(BigDecimal parentDeposit) {
this.parentDeposit = parentDeposit;
}
public Long getChildAccountsCount() {
return childAccountsCount;
}
public void setChildAccountsCount(Long childAccountsCount) {
this.childAccountsCount = childAccountsCount;
}
public Boolean getIsAcceptingChild() {
return isAcceptingChild;
}
public void setIsAcceptingChild(Boolean isAcceptingChild) {
this.isAcceptingChild = isAcceptingChild;
}
public Set<SavingsAccount> getChildSaving() {
return childSaving;
}
public void setChildSaving(Set<SavingsAccount> childSaving) {
this.childSaving = childSaving;
}
public Integer getSavingsStatus() {
return savingsStatus;
}
public void setSavingsStatus(Integer savingsStatus) {
this.savingsStatus = savingsStatus;
}
}
@@ -0,0 +1,170 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maxDepositTermParamName;
import jakarta.persistence.CascadeType;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.OneToOne;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChart;
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
@Entity
@DiscriminatorValue("300")
public class RecurringDepositProduct extends FixedDepositProduct {
@OneToOne(mappedBy = "product", cascade = CascadeType.ALL)
private DepositProductRecurringDetail recurringDetail;
protected RecurringDepositProduct() {
}
public static RecurringDepositProduct createNew(final String name, final String shortName, final String description,
final MonetaryCurrency currency, final BigDecimal interestRate,
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final Integer lockinPeriodFrequency,
final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final AccountingRuleType accountingRuleType,
final Set<Charge> charges, final DepositProductTermAndPreClosure productTermAndPreClosure,
final DepositProductRecurringDetail recurringDetail, final Set<InterestRateChart> charts,
BigDecimal minBalanceForInterestCalculation, final TaxGroup taxGroup, final boolean withHoldTax) {
final BigDecimal minRequiredOpeningBalance = null;
final boolean withdrawalFeeApplicableForTransfer = false;
final boolean allowOverdraft = false;
final BigDecimal overdraftLimit = null;
return new RecurringDepositProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges,
productTermAndPreClosure, recurringDetail, charts, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation,
withHoldTax, taxGroup);
}
protected RecurringDepositProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency,
final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
final DepositProductTermAndPreClosure productTermAndPreClosure, final DepositProductRecurringDetail recurringDetail,
final Set<InterestRateChart> charts, final boolean allowOverdraft, final BigDecimal overdraftLimit,
final BigDecimal minBalanceForInterestCalculation, final boolean withHoldTax, final TaxGroup taxGroup) {
super(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, productTermAndPreClosure,
charts, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation, withHoldTax, taxGroup);
this.recurringDetail = recurringDetail;
}
@Override
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
actualChanges.putAll(this.update(command, baseDataValidator));
validateDomainRules(baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
return actualChanges;
}
@Override
protected Map<String, Object> update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
actualChanges.putAll(super.update(command, baseDataValidator));
if (this.recurringDetail != null) {
actualChanges.putAll(this.recurringDetail.update(command));
}
return actualChanges;
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
public DepositProductRecurringDetail depositRecurringDetail() {
return this.recurringDetail;
}
@Override
public void validateDomainRules() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
validateDomainRules(baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void validateDomainRules(final DataValidatorBuilder baseDataValidator) {
final DepositTermDetail termDetails = this.depositProductTermAndPreClosure().depositTermDetail();
final boolean isMinTermGreaterThanMax = termDetails.isMinDepositTermGreaterThanMaxDepositTerm();
if (isMinTermGreaterThanMax) {
final Integer maxTerm = termDetails.maxDepositTerm();
baseDataValidator.reset().parameter(maxDepositTermParamName).value(maxTerm)
.failWithCodeNoParameterAddedToErrorCode("max.term.lessthan.min.term");
}
if (this.charts != null) {
validateCharts(baseDataValidator);
} else if (this.nominalAnnualInterestRate == null || this.nominalAnnualInterestRate.compareTo(BigDecimal.ZERO) == 0) {
baseDataValidator.reset().parameter(DepositsApiConstants.nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate)
.failWithCodeNoParameterAddedToErrorCode("interest.chart.or.nominal.interest.rate.required");
}
this.validateInterestPostingAndCompoundingPeriodTypes(baseDataValidator);
}
}
@@ -0,0 +1,27 @@
/**
* 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.portfolio.savings.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface RecurringDepositProductRepository
extends JpaRepository<RecurringDepositProduct, Long>, JpaSpecificationExecutor<RecurringDepositProduct> {
//
}
@@ -0,0 +1,919 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.amountParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dateFormatParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeIntervalParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.localeParamName;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.time.MonthDay;
import java.time.temporal.ChronoField;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.charge.exception.SavingsAccountChargeWithoutMandatoryFieldException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author dv6
*
*/
@Entity
@Table(name = "m_savings_account_charge")
public class SavingsAccountCharge extends AbstractAuditableWithUTCDateTimeCustom {
private static final Logger LOG = LoggerFactory.getLogger(SavingsAccountCharge.class);
@ManyToOne(optional = false)
@JoinColumn(name = "savings_account_id", referencedColumnName = "id", nullable = false)
private SavingsAccount savingsAccount;
@ManyToOne(optional = false)
@JoinColumn(name = "charge_id", referencedColumnName = "id", nullable = false)
private Charge charge;
@Column(name = "charge_time_enum", nullable = false)
private Integer chargeTime;
@Column(name = "charge_due_date")
private LocalDate dueDate;
@Column(name = "fee_on_month", nullable = true)
private Integer feeOnMonth;
@Column(name = "fee_on_day", nullable = true)
private Integer feeOnDay;
@Column(name = "fee_interval", nullable = true)
private Integer feeInterval;
@Column(name = "charge_calculation_enum")
private Integer chargeCalculation;
@Column(name = "free_withdrawal_count", nullable = true)
private Integer freeWithdrawalCount;
@Column(name = "charge_reset_date", nullable = true)
private LocalDate chargeResetDate;
@Column(name = "calculation_percentage", scale = 6, precision = 19, nullable = true)
private BigDecimal percentage;
// TODO AA: This field may not require for savings charges
@Column(name = "calculation_on_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal amountPercentageAppliedTo;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@Column(name = "amount_paid_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal amountPaid;
@Column(name = "amount_waived_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal amountWaived;
@Column(name = "amount_writtenoff_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal amountWrittenOff;
@Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false)
private BigDecimal amountOutstanding;
@Column(name = "is_penalty", nullable = false)
private boolean penaltyCharge = false;
@Column(name = "is_paid_derived", nullable = false)
private boolean paid = false;
@Column(name = "waived", nullable = false)
private boolean waived = false;
@Column(name = "is_active", nullable = false)
private boolean status = true;
@Column(name = "inactivated_on_date")
private LocalDate inactivationDate;
public static SavingsAccountCharge createNewFromJson(final SavingsAccount savingsAccount, final Charge chargeDefinition,
final JsonCommand command) {
BigDecimal amount = command.bigDecimalValueOfParameterNamed(amountParamName);
final LocalDate dueDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
MonthDay feeOnMonthDay = command.extractMonthDayNamed(feeOnMonthDayParamName);
Integer feeInterval = command.integerValueOfParameterNamed(feeIntervalParamName);
final ChargeTimeType chargeTime = null;
final ChargeCalculationType chargeCalculation = null;
final boolean status = true;
// If these values is not sent as parameter, then derive from Charge
// definition
amount = (amount == null) ? chargeDefinition.getAmount() : amount;
feeOnMonthDay = (feeOnMonthDay == null) ? chargeDefinition.getFeeOnMonthDay() : feeOnMonthDay;
feeInterval = (feeInterval == null) ? chargeDefinition.getFeeInterval() : feeInterval;
return new SavingsAccountCharge(savingsAccount, chargeDefinition, amount, chargeTime, chargeCalculation, dueDate, status,
feeOnMonthDay, feeInterval);
}
public static SavingsAccountCharge createNewWithoutSavingsAccount(final Charge chargeDefinition, final BigDecimal amountPayable,
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, final boolean status,
final MonthDay feeOnMonthDay, final Integer feeInterval) {
return new SavingsAccountCharge(null, chargeDefinition, amountPayable, chargeTime, chargeCalculation, dueDate, status,
feeOnMonthDay, feeInterval);
}
protected SavingsAccountCharge() {
//
}
private SavingsAccountCharge(final SavingsAccount savingsAccount, final Charge chargeDefinition, final BigDecimal amount,
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, final boolean status,
MonthDay feeOnMonthDay, final Integer feeInterval) {
this.savingsAccount = savingsAccount;
this.charge = chargeDefinition;
this.penaltyCharge = chargeDefinition.isPenalty();
this.chargeTime = (chargeTime == null) ? chargeDefinition.getChargeTimeType() : chargeTime.getValue();
if (isOnSpecifiedDueDate()) {
if (dueDate == null) {
final String defaultUserMessage = "Savings Account charge is missing due date.";
throw new SavingsAccountChargeWithoutMandatoryFieldException("savingsaccount.charge", dueAsOfDateParamName,
defaultUserMessage, chargeDefinition.getId(), chargeDefinition.getName());
}
}
if (isAnnualFee() || isMonthlyFee()) {
feeOnMonthDay = (feeOnMonthDay == null) ? chargeDefinition.getFeeOnMonthDay() : feeOnMonthDay;
if (feeOnMonthDay == null) {
final String defaultUserMessage = "Savings Account charge is missing due date.";
throw new SavingsAccountChargeWithoutMandatoryFieldException("savingsaccount.charge", dueAsOfDateParamName,
defaultUserMessage, chargeDefinition.getId(), chargeDefinition.getName());
}
this.feeOnMonth = feeOnMonthDay.getMonthValue();
this.feeOnDay = feeOnMonthDay.getDayOfMonth();
} else if (isWeeklyFee()) {
if (dueDate == null) {
final String defaultUserMessage = "Savings Account charge is missing due date.";
throw new SavingsAccountChargeWithoutMandatoryFieldException("savingsaccount.charge", dueAsOfDateParamName,
defaultUserMessage, chargeDefinition.getId(), chargeDefinition.getName());
}
/**
* For Weekly fee feeOnDay is ISO standard day of the week. Monday=1, Tuesday=2
*/
this.feeOnDay = dueDate.get(ChronoField.DAY_OF_WEEK);
} else {
this.feeOnDay = null;
this.feeOnMonth = null;
this.feeInterval = null;
}
if (isMonthlyFee() || isWeeklyFee()) {
this.feeInterval = (feeInterval == null) ? chargeDefinition.feeInterval() : feeInterval;
}
this.dueDate = dueDate;
this.chargeCalculation = chargeDefinition.getChargeCalculation();
if (chargeCalculation != null) {
this.chargeCalculation = chargeCalculation.getValue();
}
BigDecimal chargeAmount = chargeDefinition.getAmount();
if (amount != null) {
chargeAmount = amount;
}
final BigDecimal transactionAmount = new BigDecimal(0);
populateDerivedFields(transactionAmount, chargeAmount);
if (this.isWithdrawalFee() || this.isSavingsNoActivity()) {
this.amountOutstanding = BigDecimal.ZERO;
}
this.paid = determineIfFullyPaid();
this.status = status;
}
public void resetPropertiesForRecurringFees() {
if (isMonthlyFee() || isAnnualFee() || isWeeklyFee()) {
// FIXME: AA: If charge is percentage of x amount then need to
// update amount outstanding accordingly.
// Right now annual and monthly charges supports charge calculation
// type flat.
this.amountOutstanding = this.amount;
this.paid = false;// reset to false for recurring fee.
this.waived = false;
}
}
private void populateDerivedFields(final BigDecimal transactionAmount, final BigDecimal chargeAmount) {
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
case INVALID:
this.percentage = null;
this.amount = null;
this.amountPercentageAppliedTo = null;
this.amountPaid = null;
this.amountOutstanding = BigDecimal.ZERO;
this.amountWaived = null;
this.amountWrittenOff = null;
break;
case FLAT:
this.percentage = null;
this.amount = chargeAmount;
this.amountPercentageAppliedTo = null;
this.amountPaid = null;
this.amountOutstanding = chargeAmount;
this.amountWaived = null;
this.amountWrittenOff = null;
break;
case PERCENT_OF_AMOUNT:
this.percentage = chargeAmount;
this.amountPercentageAppliedTo = transactionAmount;
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
this.amountPaid = null;
this.amountOutstanding = calculateOutstanding();
this.amountWaived = null;
this.amountWrittenOff = null;
break;
case PERCENT_OF_AMOUNT_AND_INTEREST:
this.percentage = null;
this.amount = null;
this.amountPercentageAppliedTo = null;
this.amountPaid = null;
this.amountOutstanding = BigDecimal.ZERO;
this.amountWaived = null;
this.amountWrittenOff = null;
break;
case PERCENT_OF_INTEREST:
this.percentage = null;
this.amount = null;
this.amountPercentageAppliedTo = null;
this.amountPaid = null;
this.amountOutstanding = BigDecimal.ZERO;
this.amountWaived = null;
this.amountWrittenOff = null;
break;
case PERCENT_OF_DISBURSEMENT_AMOUNT:
this.percentage = null;
this.amount = null;
this.amountPercentageAppliedTo = null;
this.amountPaid = null;
this.amountOutstanding = BigDecimal.ZERO;
this.amountWaived = null;
this.amountWrittenOff = null;
break;
}
}
public void markAsFullyPaid() {
this.amountPaid = this.amount;
this.amountOutstanding = BigDecimal.ZERO;
this.paid = true;
}
public void resetToOriginal(final MonetaryCurrency currency) {
this.amountPaid = BigDecimal.ZERO;
this.amountWaived = BigDecimal.ZERO;
this.amountWrittenOff = BigDecimal.ZERO;
this.amountOutstanding = calculateAmountOutstanding(currency);
this.paid = false;
this.waived = false;
}
public void undoPayment(final MonetaryCurrency currency, final Money transactionAmount) {
Money amountPaid = getAmountPaid(currency);
amountPaid = amountPaid.minus(transactionAmount);
this.amountPaid = amountPaid.getAmount();
this.amountOutstanding = calculateAmountOutstanding(currency);
if (this.isWithdrawalFee()) {
this.amountOutstanding = BigDecimal.ZERO;
}
// to reset amount outstanding for annual and monthly fee
resetPropertiesForRecurringFees();
updateToPreviousDueDate();// reset annual and monthly due date.
this.paid = false;
this.status = true;
}
public Money waive(final MonetaryCurrency currency) {
Money amountWaivedToDate = Money.of(currency, this.amountWaived);
Money amountOutstanding = Money.of(currency, this.amountOutstanding);
this.amountWaived = amountWaivedToDate.plus(amountOutstanding).getAmount();
this.amountOutstanding = BigDecimal.ZERO;
this.waived = true;
resetPropertiesForRecurringFees();
updateNextDueDateForRecurringFees();
return amountOutstanding;
}
public void undoWaiver(final MonetaryCurrency currency, final Money transactionAmount) {
Money amountWaived = getAmountWaived(currency);
amountWaived = amountWaived.minus(transactionAmount);
this.amountWaived = amountWaived.getAmount();
this.amountOutstanding = calculateAmountOutstanding(currency);
this.waived = false;
this.status = true;
resetPropertiesForRecurringFees();
updateToPreviousDueDate();
}
public Money pay(final MonetaryCurrency currency, final Money amountPaid) {
Money amountPaidToDate = Money.of(currency, this.amountPaid);
Money amountOutstanding = Money.of(currency, this.amountOutstanding);
amountPaidToDate = amountPaidToDate.plus(amountPaid);
amountOutstanding = amountOutstanding.minus(amountPaid);
this.amountPaid = amountPaidToDate.getAmount();
this.amountOutstanding = amountOutstanding.getAmount();
this.paid = determineIfFullyPaid();
if (BigDecimal.ZERO.compareTo(this.amountOutstanding) == 0) {
// full outstanding is paid, update to next due date
updateNextDueDateForRecurringFees();
resetPropertiesForRecurringFees();
}
return Money.of(currency, this.amountOutstanding);
}
private BigDecimal calculateAmountOutstanding(final MonetaryCurrency currency) {
return getAmount(currency).minus(getAmountWaived(currency)).minus(getAmountPaid(currency)).getAmount();
}
public void update(final SavingsAccount savingsAccount) {
this.savingsAccount = savingsAccount;
}
public void update(final BigDecimal amount, final LocalDate dueDate, final MonthDay feeOnMonthDay, final Integer feeInterval) {
final BigDecimal transactionAmount = BigDecimal.ZERO;
if (dueDate != null) {
this.dueDate = dueDate;
if (isWeeklyFee()) {
this.feeOnDay = dueDate.get(ChronoField.DAY_OF_WEEK);
}
}
if (feeOnMonthDay != null) {
this.feeOnMonth = feeOnMonthDay.getMonthValue();
this.feeOnDay = feeOnMonthDay.getDayOfMonth();
}
if (feeInterval != null) {
this.feeInterval = feeInterval;
}
if (amount != null) {
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
case INVALID:
break;
case FLAT:
this.amount = amount;
break;
case PERCENT_OF_AMOUNT:
this.percentage = amount;
this.amountPercentageAppliedTo = transactionAmount;
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
this.amountOutstanding = calculateOutstanding();
break;
case PERCENT_OF_AMOUNT_AND_INTEREST:
this.percentage = amount;
this.amount = null;
this.amountPercentageAppliedTo = null;
this.amountOutstanding = null;
break;
case PERCENT_OF_INTEREST:
this.percentage = amount;
this.amount = null;
this.amountPercentageAppliedTo = null;
this.amountOutstanding = null;
break;
case PERCENT_OF_DISBURSEMENT_AMOUNT:
LOG.error("TODO Implement update ChargeCalculationType for PERCENT_OF_DISBURSEMENT_AMOUNT");
break;
}
}
}
@SuppressFBWarnings(value = "NP_NULL_PARAM_DEREF_NONVIRTUAL") // https://issues.apache.org/jira/browse/FINERACT-987
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(7);
final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale();
if (command.isChangeInLocalDateParameterNamed(dueAsOfDateParamName, getDueDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(dueAsOfDateParamName);
actualChanges.put(dueAsOfDateParamName, valueAsInput);
actualChanges.put(dateFormatParamName, dateFormatAsInput);
actualChanges.put(localeParamName, localeAsInput);
this.dueDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
if (this.isWeeklyFee()) {
this.feeOnDay = this.dueDate.get(ChronoField.DAY_OF_WEEK);
}
}
if (command.hasParameter(feeOnMonthDayParamName)) {
final MonthDay monthDay = command.extractMonthDayNamed(feeOnMonthDayParamName);
final String actualValueEntered = command.stringValueOfParameterNamed(feeOnMonthDayParamName);
final Integer dayOfMonthValue = monthDay.getDayOfMonth();
if (!this.feeOnDay.equals(dayOfMonthValue)) {
actualChanges.put(feeOnMonthDayParamName, actualValueEntered);
actualChanges.put(localeParamName, localeAsInput);
this.feeOnDay = dayOfMonthValue;
}
final Integer monthOfYear = monthDay.getMonthValue();
if (!this.feeOnMonth.equals(monthOfYear)) {
actualChanges.put(feeOnMonthDayParamName, actualValueEntered);
actualChanges.put(localeParamName, localeAsInput);
this.feeOnMonth = monthOfYear;
}
}
if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName);
actualChanges.put(amountParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
case INVALID:
break;
case FLAT:
this.amount = newValue;
this.amountOutstanding = calculateOutstanding();
break;
case PERCENT_OF_AMOUNT:
this.percentage = newValue;
this.amountPercentageAppliedTo = null;
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
this.amountOutstanding = calculateOutstanding();
break;
case PERCENT_OF_AMOUNT_AND_INTEREST:
this.percentage = newValue;
this.amount = null;
this.amountPercentageAppliedTo = null;
this.amountOutstanding = null;
break;
case PERCENT_OF_INTEREST:
this.percentage = newValue;
this.amount = null;
this.amountPercentageAppliedTo = null;
this.amountOutstanding = null;
break;
case PERCENT_OF_DISBURSEMENT_AMOUNT:
LOG.error("TODO Implement update ChargeCalculationType for PERCENT_OF_DISBURSEMENT_AMOUNT");
break;
}
}
return actualChanges;
}
private boolean isGreaterThanZero(final BigDecimal value) {
return value.compareTo(BigDecimal.ZERO) > 0;
}
public LocalDate getDueDate() {
return this.dueDate;
}
private boolean determineIfFullyPaid() {
return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0;
}
private BigDecimal calculateOutstanding() {
BigDecimal amountPaidLocal = BigDecimal.ZERO;
if (this.amountPaid != null) {
amountPaidLocal = this.amountPaid;
}
BigDecimal amountWaivedLocal = BigDecimal.ZERO;
if (this.amountWaived != null) {
amountWaivedLocal = this.amountWaived;
}
BigDecimal amountWrittenOffLocal = BigDecimal.ZERO;
if (this.amountWrittenOff != null) {
amountWrittenOffLocal = this.amountWrittenOff;
}
final BigDecimal totalAccountedFor = amountPaidLocal.add(amountWaivedLocal).add(amountWrittenOffLocal);
return this.amount.subtract(totalAccountedFor);
}
private BigDecimal percentageOf(final BigDecimal value, final BigDecimal percentage) {
BigDecimal percentageOf = BigDecimal.ZERO;
if (isGreaterThanZero(value)) {
final MathContext mc = new MathContext(8, MoneyHelper.getRoundingMode());
final BigDecimal multiplicand = percentage.divide(BigDecimal.valueOf(100L), mc);
percentageOf = value.multiply(multiplicand, mc);
}
return percentageOf;
}
public BigDecimal amount() {
return this.amount;
}
public BigDecimal amoutOutstanding() {
return this.amountOutstanding;
}
public boolean isFeeCharge() {
return !this.penaltyCharge;
}
public boolean isPenaltyCharge() {
return this.penaltyCharge;
}
public boolean isNotFullyPaid() {
return !isPaid();
}
public boolean isPaid() {
return this.paid;
}
public boolean isWaived() {
return this.waived;
}
public boolean isPaidOrPartiallyPaid(final MonetaryCurrency currency) {
final Money amountWaivedOrWrittenOff = getAmountWaived(currency).plus(getAmountWrittenOff(currency));
return Money.of(currency, this.amountPaid).plus(amountWaivedOrWrittenOff).isGreaterThanZero();
}
public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount);
}
private Money getAmountPaid(final MonetaryCurrency currency) {
return Money.of(currency, this.amountPaid);
}
public Money getAmountWaived(final MonetaryCurrency currency) {
return Money.of(currency, this.amountWaived);
}
public Money getAmountWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.amountWrittenOff);
}
public Money getAmountOutstanding(final MonetaryCurrency currency) {
return Money.of(currency, this.amountOutstanding);
}
/**
* @param incrementBy
* Amount used to pay off this charge
* @return Actual amount paid on this charge
*/
public Money updatePaidAmountBy(final Money incrementBy) {
Money amountPaidToDate = Money.of(incrementBy.getCurrency(), this.amountPaid);
final Money amountOutstanding = Money.of(incrementBy.getCurrency(), this.amountOutstanding);
Money amountPaidOnThisCharge = Money.zero(incrementBy.getCurrency());
if (incrementBy.isGreaterThanOrEqualTo(amountOutstanding)) {
amountPaidOnThisCharge = amountOutstanding;
amountPaidToDate = amountPaidToDate.plus(amountOutstanding);
this.amountPaid = amountPaidToDate.getAmount();
this.amountOutstanding = BigDecimal.ZERO;
} else {
amountPaidOnThisCharge = incrementBy;
amountPaidToDate = amountPaidToDate.plus(incrementBy);
this.amountPaid = amountPaidToDate.getAmount();
final Money amountExpected = Money.of(incrementBy.getCurrency(), this.amount);
this.amountOutstanding = amountExpected.minus(amountPaidToDate).getAmount();
}
this.paid = determineIfFullyPaid();
return amountPaidOnThisCharge;
}
public String name() {
return this.charge.getName();
}
public String currencyCode() {
return this.charge.getCurrencyCode();
}
public Charge getCharge() {
return this.charge;
}
public boolean isEnableFreeWithdrawal() {
return charge.isEnableFreeWithdrawal();
}
public boolean isEnablePaymentType() {
return charge.isEnablePaymentType();
}
public Integer getFrequencyFreeWithdrawalCharge() { // number of times free withdrawal allowed
return charge.getFrequencyFreeWithdrawalCharge();
}
public Integer getRestartFrequency() { // numeric value of which numeric-period, count should restart
return charge.getRestartFrequency();
}
public Integer getRestartFrequencyEnum() { // enum day/week/month for restarting the count.
return charge.getRestartFrequencyEnum();
}
public Integer getFreeWithdrawalCount() {
return freeWithdrawalCount;
}
public void setFreeWithdrawalCount(Integer freeWithdrawalCount) {
this.freeWithdrawalCount = freeWithdrawalCount;
}
public LocalDate getResetChargeDate() {
return chargeResetDate;
}
public void setDiscountDueDate(final LocalDate date) {
this.chargeResetDate = date;
}
public SavingsAccount savingsAccount() {
return this.savingsAccount;
}
public boolean isOnSpecifiedDueDate() {
return ChargeTimeType.fromInt(this.chargeTime).isOnSpecifiedDueDate();
}
public boolean isSavingsActivation() {
return ChargeTimeType.fromInt(this.chargeTime).isSavingsActivation();
}
public boolean isSavingsNoActivity() {
return ChargeTimeType.fromInt(this.chargeTime).isSavingsNoActivityFee();
}
public boolean isSavingsClosure() {
return ChargeTimeType.fromInt(this.chargeTime).isSavingsClosure();
}
public boolean isWithdrawalFee() {
return ChargeTimeType.fromInt(this.chargeTime).isWithdrawalFee();
}
public boolean isOverdraftFee() {
return ChargeTimeType.fromInt(this.chargeTime).isOverdraftFee();
}
public boolean isAnnualFee() {
return ChargeTimeType.fromInt(this.chargeTime).isAnnualFee();
}
public boolean isMonthlyFee() {
return ChargeTimeType.fromInt(this.chargeTime).isMonthlyFee();
}
public boolean isWeeklyFee() {
return ChargeTimeType.fromInt(this.chargeTime).isWeeklyFee();
}
public boolean hasCurrencyCodeOf(final String matchingCurrencyCode) {
if (this.currencyCode() == null || matchingCurrencyCode == null) {
return false;
}
return this.currencyCode().equalsIgnoreCase(matchingCurrencyCode);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof SavingsAccountCharge)) {
return false;
}
SavingsAccountCharge that = (SavingsAccountCharge) o;
return (penaltyCharge == that.penaltyCharge) && (paid == that.paid) && (waived == that.waived) && (status == that.status)
&& Objects.equals(savingsAccount, that.savingsAccount) && Objects.equals(charge, that.charge)
&& Objects.equals(chargeTime, that.chargeTime) && DateUtils.isEqual(dueDate, that.dueDate)
&& Objects.equals(feeOnMonth, that.feeOnMonth) && Objects.equals(feeOnDay, that.feeOnDay)
&& Objects.equals(feeInterval, that.feeInterval) && Objects.equals(chargeCalculation, that.chargeCalculation)
&& Objects.equals(percentage, that.percentage) && Objects.equals(amountPercentageAppliedTo, that.amountPercentageAppliedTo)
&& Objects.equals(amount, that.amount) && Objects.equals(amountPaid, that.amountPaid)
&& Objects.equals(amountWaived, that.amountWaived) && Objects.equals(amountWrittenOff, that.amountWrittenOff)
&& Objects.equals(amountOutstanding, that.amountOutstanding) && DateUtils.isEqual(inactivationDate, that.inactivationDate);
}
@Override
public int hashCode() {
return Objects.hash(savingsAccount, charge, chargeTime, dueDate, feeOnMonth, feeOnDay, feeInterval, chargeCalculation, percentage,
amountPercentageAppliedTo, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, penaltyCharge, paid,
waived, status, inactivationDate);
}
public BigDecimal calculateWithdralFeeAmount(@NotNull BigDecimal transactionAmount) {
BigDecimal amountPaybale = BigDecimal.ZERO;
if (ChargeCalculationType.fromInt(this.chargeCalculation).isFlat()) {
amountPaybale = this.amount;
} else if (ChargeCalculationType.fromInt(this.chargeCalculation).isPercentageOfAmount()) {
amountPaybale = transactionAmount.multiply(this.percentage).divide(BigDecimal.valueOf(100L), MoneyHelper.getRoundingMode());
}
return amountPaybale;
}
public BigDecimal updateWithdralFeeAmount(final BigDecimal transactionAmount) {
return amountOutstanding = calculateWithdralFeeAmount(transactionAmount);
}
public BigDecimal updateNoWithdrawalFee() {
return amountOutstanding = BigDecimal.ZERO;
}
public void updateToNextDueDateFrom(final LocalDate startingDate) {
if (isAnnualFee() || isMonthlyFee() || isWeeklyFee()) {
this.dueDate = getNextDueDateFrom(startingDate);
}
}
public LocalDate getNextDueDateFrom(final LocalDate startingDate) {
LocalDate nextDueLocalDate = null;
if (isAnnualFee() || isMonthlyFee()) {
nextDueLocalDate = startingDate.withMonth(this.feeOnMonth);
nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
while (DateUtils.isBefore(nextDueLocalDate, startingDate)) {
nextDueLocalDate = calculateNextDueDate(nextDueLocalDate);
}
} else if (isWeeklyFee()) {
nextDueLocalDate = getDueDate();
while (DateUtils.isBefore(nextDueLocalDate, startingDate)) {
nextDueLocalDate = calculateNextDueDate(nextDueLocalDate);
}
} else {
nextDueLocalDate = calculateNextDueDate(startingDate);
}
return nextDueLocalDate;
}
private LocalDate calculateNextDueDate(final LocalDate date) {
LocalDate nextDueLocalDate = null;
if (isAnnualFee()) {
nextDueLocalDate = date.withMonth(this.feeOnMonth).plusYears(1);
nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
} else if (isMonthlyFee()) {
nextDueLocalDate = date.plusMonths(this.feeInterval);
nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
} else if (isWeeklyFee()) {
nextDueLocalDate = date.plusWeeks(this.feeInterval);
nextDueLocalDate = setDayOfWeek(nextDueLocalDate);
}
return nextDueLocalDate;
}
private LocalDate setDayOfMonth(LocalDate nextDueLocalDate) {
int maxDayOfMonth = nextDueLocalDate.lengthOfMonth();
int newDayOfMonth = (this.feeOnDay.intValue() < maxDayOfMonth) ? this.feeOnDay : maxDayOfMonth;
nextDueLocalDate = nextDueLocalDate.withDayOfMonth(newDayOfMonth);
return nextDueLocalDate;
}
private LocalDate setDayOfWeek(LocalDate nextDueLocalDate) {
if (this.feeOnDay != nextDueLocalDate.get(ChronoField.DAY_OF_WEEK)) {
nextDueLocalDate = nextDueLocalDate.with(ChronoField.DAY_OF_WEEK, this.feeOnDay);
}
return nextDueLocalDate;
}
public void updateNextDueDateForRecurringFees() {
if (isAnnualFee() || isMonthlyFee() || isWeeklyFee()) {
this.dueDate = calculateNextDueDate(this.dueDate);
}
}
public void updateToPreviousDueDate() {
if (isAnnualFee() || isMonthlyFee() || isWeeklyFee()) {
LocalDate nextDueLocalDate = dueDate;
if (isAnnualFee()) {
nextDueLocalDate = nextDueLocalDate.withMonth(this.feeOnMonth).minusYears(1);
nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
} else if (isMonthlyFee()) {
nextDueLocalDate = nextDueLocalDate.minusMonths(this.feeInterval);
nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
} else if (isWeeklyFee()) {
nextDueLocalDate = nextDueLocalDate.minusDays(7 * this.feeInterval);
nextDueLocalDate = setDayOfWeek(nextDueLocalDate);
}
this.dueDate = nextDueLocalDate;
}
}
public boolean feeSettingsNotSet() {
return !feeSettingsSet();
}
public boolean feeSettingsSet() {
return this.feeOnDay != null && this.feeOnMonth != null;
}
public boolean isRecurringFee() {
return isWeeklyFee() || isMonthlyFee() || isAnnualFee();
}
public boolean isChargeIsDue(final LocalDate nextDueDate) {
return DateUtils.isBefore(getDueDate(), nextDueDate);
}
public boolean isChargeIsOverPaid(final LocalDate nextDueDate) {
return DateUtils.isAfter(getDueDate(), nextDueDate) && MathUtil.isGreaterThanZero(amountPaid());
}
private BigDecimal amountPaid() {
return this.amountPaid;
}
public void inactiavateCharge(final LocalDate inactivationOnDate) {
this.inactivationDate = inactivationOnDate;
this.status = false;
this.amountOutstanding = BigDecimal.ZERO;
this.paid = true;
}
public boolean isActive() {
return this.status;
}
public boolean isNotActive() {
return !isActive();
}
/**
* This method is to identify the charges which can override the savings rules(for example if there is a minimum
* enforced balance of 1000 on savings account with account balance of 1000, still these charges can be collected as
* these charges are initiated by system and it can bring down the balance below the enforced minimum balance).
*
*/
public boolean canOverriteSavingAccountRules() {
return (!this.isSavingsActivation() && !this.isWithdrawalFee());
}
}
@@ -0,0 +1,190 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.amountParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeTimeTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeIntervalParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.idParamName;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.MonthDay;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.charge.exception.ChargeCannotBeAppliedToException;
import org.apache.fineract.portfolio.charge.exception.SavingsAccountChargeNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SavingsAccountChargeAssembler {
private final FromJsonHelper fromApiJsonHelper;
private final ChargeRepositoryWrapper chargeRepository;
private final SavingsAccountChargeRepository savingsAccountChargeRepository;
@Autowired
public SavingsAccountChargeAssembler(final FromJsonHelper fromApiJsonHelper, final ChargeRepositoryWrapper chargeRepository,
final SavingsAccountChargeRepository savingsAccountChargeRepository) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.chargeRepository = chargeRepository;
this.savingsAccountChargeRepository = savingsAccountChargeRepository;
}
public Set<SavingsAccountCharge> fromParsedJson(final JsonElement element, final String productCurrencyCode) {
final Set<SavingsAccountCharge> savingsAccountCharges = new HashSet<>();
if (element.isJsonObject()) {
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
final String monthDayFormat = this.fromApiJsonHelper.extractMonthDayFormatParameter(topLevelJsonElement);
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
if (topLevelJsonElement.has(chargesParamName) && topLevelJsonElement.get(chargesParamName).isJsonArray()) {
final JsonArray array = topLevelJsonElement.get(chargesParamName).getAsJsonArray();
for (int i = 0; i < array.size(); i++) {
final JsonObject savingsChargeElement = array.get(i).getAsJsonObject();
final Long id = this.fromApiJsonHelper.extractLongNamed(idParamName, savingsChargeElement);
final Long chargeId = this.fromApiJsonHelper.extractLongNamed(chargeIdParamName, savingsChargeElement);
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed(amountParamName, savingsChargeElement, locale);
final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerNamed(chargeTimeTypeParamName, savingsChargeElement,
locale);
final Integer chargeCalculationType = this.fromApiJsonHelper.extractIntegerNamed(chargeCalculationTypeParamName,
savingsChargeElement, locale);
final LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, savingsChargeElement,
dateFormat, locale);
final MonthDay feeOnMonthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, savingsChargeElement,
monthDayFormat, locale);
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed(feeIntervalParamName, savingsChargeElement,
locale);
if (id == null) {
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
if (!chargeDefinition.isSavingsCharge()) {
final String errorMessage = "Charge with identifier " + chargeDefinition.getId()
+ " cannot be applied to Savings product.";
throw new ChargeCannotBeAppliedToException("savings.product", errorMessage, chargeDefinition.getId());
}
ChargeTimeType chargeTime = null;
if (chargeTimeType != null) {
chargeTime = ChargeTimeType.fromInt(chargeTimeType);
}
ChargeCalculationType chargeCalculation = null;
if (chargeCalculationType != null) {
chargeCalculation = ChargeCalculationType.fromInt(chargeCalculationType);
}
final boolean status = true;
final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewWithoutSavingsAccount(
chargeDefinition, amount, chargeTime, chargeCalculation, dueDate, status, feeOnMonthDay, feeInterval);
savingsAccountCharges.add(savingsAccountCharge);
} else {
final Long savingsAccountChargeId = id;
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findById(savingsAccountChargeId)
.orElseThrow(() -> new SavingsAccountChargeNotFoundException(savingsAccountChargeId));
savingsAccountCharge.update(amount, dueDate, feeOnMonthDay, feeInterval);
savingsAccountCharges.add(savingsAccountCharge);
}
}
}
}
this.validateSavingsCharges(savingsAccountCharges, productCurrencyCode);
return savingsAccountCharges;
}
public Set<SavingsAccountCharge> fromSavingsProduct(final SavingsProduct savingsProduct) {
final Set<SavingsAccountCharge> savingsAccountCharges = new HashSet<>();
Set<Charge> productCharges = savingsProduct.charges();
for (Charge charge : productCharges) {
ChargeTimeType chargeTime = null;
if (charge.getChargeTimeType() != null) {
chargeTime = ChargeTimeType.fromInt(charge.getChargeTimeType());
}
if (chargeTime != null && chargeTime.isOnSpecifiedDueDate()) {
continue;
}
ChargeCalculationType chargeCalculation = null;
if (charge.getChargeCalculation() != null) {
chargeCalculation = ChargeCalculationType.fromInt(charge.getChargeCalculation());
}
final boolean status = true;
final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewWithoutSavingsAccount(charge,
charge.getAmount(), chargeTime, chargeCalculation, null, status, charge.getFeeOnMonthDay(), charge.feeInterval());
savingsAccountCharges.add(savingsAccountCharge);
}
return savingsAccountCharges;
}
private void validateSavingsCharges(final Set<SavingsAccountCharge> charges, final String productCurrencyCode) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
boolean isOneWithdrawalPresent = false;
boolean isOneAnnualPresent = false;
for (SavingsAccountCharge charge : charges) {
if (!charge.hasCurrencyCodeOf(productCurrencyCode)) {
baseDataValidator.reset().parameter("currency").value(charge.getCharge().getId())
.failWithCodeNoParameterAddedToErrorCode("currency.and.charge.currency.not.same");
}
if (charge.isAnnualFee()) {
if (isOneAnnualPresent) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("multiple.annual.fee.per.account.not.supported");
}
isOneAnnualPresent = true;
}
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -0,0 +1,95 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_savings_account_charge_paid_by")
public class SavingsAccountChargePaidBy extends AbstractPersistableCustom {
@ManyToOne
@JoinColumn(name = "savings_account_transaction_id", nullable = false)
private SavingsAccountTransaction savingsAccountTransaction;
@ManyToOne
@JoinColumn(name = "savings_account_charge_id", nullable = false)
private SavingsAccountCharge savingsAccountCharge;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
protected SavingsAccountChargePaidBy() {
}
public static SavingsAccountChargePaidBy instance(final SavingsAccountTransaction savingsAccountTransaction,
final SavingsAccountCharge savingsAccountCharge, final BigDecimal amount) {
return new SavingsAccountChargePaidBy(savingsAccountTransaction, savingsAccountCharge, amount);
}
private SavingsAccountChargePaidBy(final SavingsAccountTransaction savingsAccountTransaction,
final SavingsAccountCharge savingsAccountCharge, final BigDecimal amount) {
this.savingsAccountTransaction = savingsAccountTransaction;
this.savingsAccountCharge = savingsAccountCharge;
this.amount = amount;
}
public SavingsAccountTransaction getSavingsAccountTransaction() {
return this.savingsAccountTransaction;
}
public void setSavingsAccountTransaction(final SavingsAccountTransaction savingsAccountTransaction) {
this.savingsAccountTransaction = savingsAccountTransaction;
}
public SavingsAccountCharge getSavingsAccountCharge() {
return this.savingsAccountCharge;
}
public void setSavingsAccountCharge(final SavingsAccountCharge savingsAccountCharge) {
this.savingsAccountCharge = savingsAccountCharge;
}
public BigDecimal getAmount() {
return this.amount;
}
public void setAmount(final BigDecimal amount) {
this.amount = amount;
}
public boolean isFeeCharge() {
return (this.savingsAccountCharge == null) ? false : this.savingsAccountCharge.isFeeCharge();
}
public boolean isPenaltyCharge() {
return (this.savingsAccountCharge == null) ? false : this.savingsAccountCharge.isPenaltyCharge();
}
public boolean canOverriteSavingAccountRules() {
return (this.savingsAccountCharge == null) ? false : this.savingsAccountCharge.canOverriteSavingAccountRules();
}
}
@@ -0,0 +1,36 @@
/**
* 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.portfolio.savings.domain;
import java.time.LocalDate;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface SavingsAccountChargeRepository
extends JpaRepository<SavingsAccountCharge, Long>, JpaSpecificationExecutor<SavingsAccountCharge> {
@Query("select sac from SavingsAccountCharge sac where sac.id =:id and sac.savingsAccount.id = :savingsAccountId")
SavingsAccountCharge findByIdAndSavingsAccountId(@Param("id") Long id, @Param("savingsAccountId") Long savingsAccountId);
@Query("select sac from SavingsAccountCharge sac where sac.dueDate <=:transactionDate and sac.waived = false and sac.paid=false order by sac.dueDate")
List<SavingsAccountCharge> findPendingCharges(@Param("transactionDate") LocalDate transactionDate);
}
@@ -0,0 +1,73 @@
/**
* 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.portfolio.savings.domain;
import java.time.LocalDate;
import java.util.List;
import org.apache.fineract.portfolio.charge.exception.SavingsAccountChargeNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* Wrapper for {@link SavingsAccountChargeRepository} that adds NULL checking and Error handling capabilities
* </p>
*/
@Service
public class SavingsAccountChargeRepositoryWrapper {
private final SavingsAccountChargeRepository repository;
@Autowired
public SavingsAccountChargeRepositoryWrapper(final SavingsAccountChargeRepository repository) {
this.repository = repository;
}
public SavingsAccountCharge findOneWithNotFoundDetection(final Long id) {
return this.repository.findById(id).orElseThrow(() -> new SavingsAccountChargeNotFoundException(id));
}
public SavingsAccountCharge findOneWithNotFoundDetection(final Long id, final Long savingsAccountId) {
final SavingsAccountCharge savingsAccountCharge = this.repository.findByIdAndSavingsAccountId(id, savingsAccountId);
if (savingsAccountCharge == null) {
throw new SavingsAccountChargeNotFoundException(id);
}
return savingsAccountCharge;
}
public List<SavingsAccountCharge> findPendingCharges(final LocalDate transactionDate) {
return this.repository.findPendingCharges(transactionDate);
}
public void save(final SavingsAccountCharge savingsAccountCharge) {
this.repository.save(savingsAccountCharge);
}
public void save(final Iterable<SavingsAccountCharge> savingsAccountCharges) {
this.repository.saveAll(savingsAccountCharges);
}
public void saveAndFlush(final SavingsAccountCharge savingsAccountCharge) {
this.repository.saveAndFlush(savingsAccountCharge);
}
public void delete(final SavingsAccountCharge savingsAccountCharge) {
this.repository.delete(savingsAccountCharge);
}
}
@@ -0,0 +1,49 @@
/**
* 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.portfolio.savings.domain;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Set;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
public interface SavingsAccountDomainService {
SavingsAccountTransaction handleWithdrawal(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, SavingsTransactionBooleanValues transactionBooleanValues,
boolean backdatedTxnsAllowedTill);
SavingsAccountTransaction handleDeposit(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean isAccountTransfer, boolean isRegularTransaction,
boolean backdatedTxnsAllowedTill);
void postJournalEntries(SavingsAccount savingsAccount, Set<Long> existingTransactionIds, Set<Long> existingReversedTransactionIds,
boolean backdatedTxnsAllowedTill);
SavingsAccountTransaction handleDividendPayout(SavingsAccount account, LocalDate transactionDate, BigDecimal transactionAmount,
boolean backdatedTxnsAllowedTill);
SavingsAccountTransaction handleReversal(SavingsAccount account, List<SavingsAccountTransaction> savingsAccountTransactions,
boolean backdatedTxnsAllowedTill);
SavingsAccountTransaction handleHold(SavingsAccount account, BigDecimal amount, LocalDate transactionDate, Boolean lienAllowed);
}
@@ -0,0 +1,341 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Transient;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.savings.domain.interest.PostingPeriod;
/**
* {@link SavingsAccountSummary} encapsulates all the summary details of a {@link SavingsAccount}.
*/
@Embeddable
public final class SavingsAccountSummary {
@Column(name = "total_deposits_derived", scale = 6, precision = 19)
private BigDecimal totalDeposits;
@Column(name = "total_withdrawals_derived", scale = 6, precision = 19)
private BigDecimal totalWithdrawals;
@Column(name = "total_interest_earned_derived", scale = 6, precision = 19)
private BigDecimal totalInterestEarned;
@Column(name = "total_interest_posted_derived", scale = 6, precision = 19)
private BigDecimal totalInterestPosted;
@Column(name = "total_withdrawal_fees_derived", scale = 6, precision = 19)
private BigDecimal totalWithdrawalFees;
@Column(name = "total_fees_charge_derived", scale = 6, precision = 19)
private BigDecimal totalFeeCharge;
@Column(name = "total_penalty_charge_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyCharge;
@Column(name = "total_annual_fees_derived", scale = 6, precision = 19)
private BigDecimal totalAnnualFees;
@Column(name = "account_balance_derived", scale = 6, precision = 19)
private BigDecimal accountBalance = BigDecimal.ZERO;
// TODO: AA do we need this data to be persisted.
@Transient
private BigDecimal totalFeeChargesWaived = BigDecimal.ZERO;
@Transient
private BigDecimal totalPenaltyChargesWaived = BigDecimal.ZERO;
@Column(name = "total_overdraft_interest_derived", scale = 6, precision = 19)
private BigDecimal totalOverdraftInterestDerived;
@Column(name = "total_withhold_tax_derived", scale = 6, precision = 19)
private BigDecimal totalWithholdTax;
@Column(name = "last_interest_calculation_date")
private LocalDate lastInterestCalculationDate;
// Currently this represents the last interest posting date
@Column(name = "interest_posted_till_date")
private LocalDate interestPostedTillDate;
@Transient
private BigDecimal runningBalanceOnInterestPostingTillDate = BigDecimal.ZERO;
SavingsAccountSummary() {
//
}
public void updateSummary(final MonetaryCurrency currency, final SavingsAccountTransactionSummaryWrapper wrapper,
final List<SavingsAccountTransaction> transactions) {
this.totalDeposits = wrapper.calculateTotalDeposits(currency, transactions);
this.totalWithdrawals = wrapper.calculateTotalWithdrawals(currency, transactions);
this.totalInterestPosted = wrapper.calculateTotalInterestPosted(currency, transactions);
this.totalWithdrawalFees = wrapper.calculateTotalWithdrawalFees(currency, transactions);
this.totalAnnualFees = wrapper.calculateTotalAnnualFees(currency, transactions);
this.totalFeeCharge = wrapper.calculateTotalFeesCharge(currency, transactions);
this.totalPenaltyCharge = wrapper.calculateTotalPenaltyCharge(currency, transactions);
this.totalFeeChargesWaived = wrapper.calculateTotalFeesChargeWaived(currency, transactions);
this.totalPenaltyChargesWaived = wrapper.calculateTotalPenaltyChargeWaived(currency, transactions);
this.totalOverdraftInterestDerived = wrapper.calculateTotalOverdraftInterest(currency, transactions);
this.totalWithholdTax = wrapper.calculateTotalWithholdTaxWithdrawal(currency, transactions);
updateRunningBalanceAndPivotDate(false, transactions, null, null, null, currency);
this.accountBalance = Money.of(currency, this.totalDeposits).plus(this.totalInterestPosted).minus(this.totalWithdrawals)
.minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).minus(this.totalFeeCharge).minus(this.totalPenaltyCharge)
.minus(totalOverdraftInterestDerived).minus(totalWithholdTax).getAmount();
}
public void updateSummaryWithPivotConfig(final MonetaryCurrency currency, final SavingsAccountTransactionSummaryWrapper wrapper,
final SavingsAccountTransaction transaction, final List<SavingsAccountTransaction> savingsAccountTransactions) {
if (transaction != null) {
if (transaction.isReversalTransaction()) {
return;
}
Money transactionAmount = Money.of(currency, transaction.getAmount());
switch (transaction.getTransactionType()) {
case DEPOSIT:
if (transaction.isDepositAndNotReversed() || transaction.isDividendPayoutAndNotReversed()) {
this.totalDeposits = Money.of(currency, this.totalDeposits).plus(transactionAmount).getAmount();
this.accountBalance = Money.of(currency, this.accountBalance).plus(transactionAmount).getAmount();
}
break;
case WITHDRAWAL:
if (transaction.isWithdrawal() && transaction.isNotReversed()) {
this.totalWithdrawals = Money.of(currency, this.totalWithdrawals).plus(transactionAmount).getAmount();
this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
}
break;
case WITHDRAWAL_FEE:
if (transaction.isWithdrawalFeeAndNotReversed() && transaction.isNotReversed()) {
this.totalWithdrawalFees = Money.of(currency, this.totalWithdrawalFees).plus(transactionAmount).getAmount();
this.totalFeeCharge = Money.of(currency, this.totalFeeCharge).plus(transactionAmount).getAmount();
this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
}
break;
case ANNUAL_FEE:
if (transaction.isAnnualFeeAndNotReversed() && transaction.isNotReversed()) {
this.totalAnnualFees = Money.of(currency, this.totalAnnualFees).plus(transactionAmount).getAmount();
this.totalFeeCharge = Money.of(currency, this.totalFeeCharge).plus(transactionAmount).getAmount();
this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
}
break;
case WAIVE_CHARGES:
if (transaction.isWaiveFeeChargeAndNotReversed()) {
this.totalFeeChargesWaived = Money.of(currency, this.totalFeeChargesWaived).plus(transactionAmount.getAmount())
.getAmount();
} else if (transaction.isWaivePenaltyChargeAndNotReversed()) {
this.totalPenaltyChargesWaived = Money.of(currency, this.totalPenaltyChargesWaived)
.plus(transactionAmount.getAmount()).getAmount();
}
break;
case PAY_CHARGE:
if (transaction.isFeeChargeAndNotReversed()) {
this.totalFeeCharge = Money.of(currency, this.totalFeeCharge).plus(transactionAmount).getAmount();
} else if (transaction.isPenaltyChargeAndNotReversed()) {
this.totalPenaltyCharge = Money.of(currency, this.totalPenaltyCharge).plus(transactionAmount).getAmount();
}
if (transaction.isFeeChargeAndNotReversed() || transaction.isPenaltyChargeAndNotReversed()) {
this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
}
break;
case OVERDRAFT_INTEREST:
if (transaction.isOverdraftInterestAndNotReversed()) {
this.totalOverdraftInterestDerived = Money.of(currency, this.totalOverdraftInterestDerived).plus(transactionAmount)
.getAmount();
this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
}
break;
case WITHHOLD_TAX:
if (transaction.isWithHoldTaxAndNotReversed()) {
this.totalWithholdTax = Money.of(currency, this.totalWithholdTax).plus(transactionAmount).getAmount();
this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
}
break;
default:
break;
}
} else {
// INTEREST_POSTING
Money interestTotal = Money.zero(currency);
Money withHoldTaxTotal = Money.zero(currency);
Money overdraftInterestTotal = Money.zero(currency);
this.totalDeposits = wrapper.calculateTotalDeposits(currency, savingsAccountTransactions);
this.totalWithdrawals = wrapper.calculateTotalWithdrawals(currency, savingsAccountTransactions);
final HashMap<String, Money> map = updateRunningBalanceAndPivotDate(true, savingsAccountTransactions, interestTotal,
overdraftInterestTotal, withHoldTaxTotal, currency);
interestTotal = map.get("interestTotal");
withHoldTaxTotal = map.get("withHoldTax");
overdraftInterestTotal = map.get("overdraftInterestTotal");
this.totalInterestPosted = interestTotal.getAmountDefaultedToNullIfZero();
this.totalOverdraftInterestDerived = overdraftInterestTotal.getAmountDefaultedToNullIfZero();
this.totalWithholdTax = withHoldTaxTotal.getAmountDefaultedToNullIfZero();
this.accountBalance = getRunningBalanceOnPivotDate();
this.accountBalance = Money.of(currency, this.accountBalance).plus(Money.of(currency, this.totalDeposits))
.plus(this.totalInterestPosted).minus(this.totalWithdrawals).minus(this.totalWithholdTax)
.minus(this.totalOverdraftInterestDerived).getAmount();
}
}
@SuppressWarnings("unchecked")
private HashMap<String, Money> updateRunningBalanceAndPivotDate(final boolean backdatedTxnsAllowedTill,
final List<SavingsAccountTransaction> savingsAccountTransactions, Money interestTotal, Money overdraftInterestTotal,
Money withHoldTaxTotal, MonetaryCurrency currency) {
boolean isUpdated = false;
HashMap<String, Money> map = new HashMap<>();
for (int i = savingsAccountTransactions.size() - 1; i >= 0; i--) {
final SavingsAccountTransaction savingsAccountTransaction = savingsAccountTransactions.get(i);
if (savingsAccountTransaction.isInterestPostingAndNotReversed() && !savingsAccountTransaction.isReversalTransaction()
&& !isUpdated) {
setInterestPostedTillDate(savingsAccountTransaction.getTransactionDate());
isUpdated = true;
if (!backdatedTxnsAllowedTill) {
break;
}
}
if (savingsAccountTransaction.isOverdraftInterestAndNotReversed() && !savingsAccountTransaction.isReversalTransaction()
&& !isUpdated) {
setInterestPostedTillDate(savingsAccountTransaction.getTransactionDate());
isUpdated = true;
if (!backdatedTxnsAllowedTill) {
break;
}
}
if (backdatedTxnsAllowedTill) {
if (savingsAccountTransaction.isInterestPostingAndNotReversed() && savingsAccountTransaction.isNotReversed()
&& !savingsAccountTransaction.isReversalTransaction()) {
interestTotal = interestTotal.plus(savingsAccountTransaction.getAmount(currency));
}
if (savingsAccountTransaction.isOverdraftInterestAndNotReversed() && !savingsAccountTransaction.isReversalTransaction()) {
overdraftInterestTotal = overdraftInterestTotal.plus(savingsAccountTransaction.getAmount());
}
if (savingsAccountTransaction.isWithHoldTaxAndNotReversed() && !savingsAccountTransaction.isReversalTransaction()) {
withHoldTaxTotal = withHoldTaxTotal.plus(savingsAccountTransaction.getAmount(currency));
}
}
}
if (backdatedTxnsAllowedTill) {
map.put("interestTotal", interestTotal);
map.put("withHoldTax", withHoldTaxTotal);
map.put("overdraftInterestTotal", overdraftInterestTotal);
}
return map;
}
public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, final List<PostingPeriod> allPostingPeriods) {
Money totalEarned = Money.zero(currency);
for (final PostingPeriod period : allPostingPeriods) {
Money interestEarned = period.interest();
interestEarned = interestEarned == null ? Money.zero(currency) : interestEarned;
totalEarned = totalEarned.plus(interestEarned);
}
this.lastInterestCalculationDate = DateUtils.getBusinessLocalDate();
this.totalInterestEarned = totalEarned.getAmount();
}
public boolean isLessThanOrEqualToAccountBalance(final Money amount) {
final Money accountBalance = getAccountBalance(amount.getCurrency());
return accountBalance.isGreaterThanOrEqualTo(amount);
}
public Money getAccountBalance(final MonetaryCurrency currency) {
return Money.of(currency, this.accountBalance);
}
public BigDecimal getAccountBalance() {
return this.accountBalance;
}
public void setAccountBalance(BigDecimal accountBalance) {
this.accountBalance = accountBalance;
}
public BigDecimal getTotalInterestPosted() {
return this.totalInterestPosted;
}
public LocalDate getLastInterestCalculationDate() {
return this.lastInterestCalculationDate;
}
public void setInterestPostedTillDate(final LocalDate date) {
this.interestPostedTillDate = date;
}
public LocalDate getInterestPostedTillDate() {
return this.interestPostedTillDate;
}
public void setRunningBalanceOnPivotDate(final BigDecimal runningBalanceOnPivotDate) {
this.runningBalanceOnInterestPostingTillDate = runningBalanceOnPivotDate;
}
public BigDecimal getRunningBalanceOnPivotDate() {
return this.runningBalanceOnInterestPostingTillDate;
}
public BigDecimal getTotalWithdrawals() {
return this.totalWithdrawals;
}
public BigDecimal getTotalDeposits() {
return this.totalDeposits;
}
public BigDecimal getTotalWithdrawalFees() {
return this.totalWithdrawalFees;
}
public BigDecimal getTotalFeeCharge() {
return this.totalFeeCharge;
}
public BigDecimal getTotalPenaltyCharge() {
return this.totalPenaltyCharge;
}
public BigDecimal getTotalAnnualFees() {
return this.totalAnnualFees;
}
public BigDecimal getTotalInterestEarned() {
return this.totalInterestEarned;
}
public BigDecimal getTotalOverdraftInterestDerived() {
return this.totalOverdraftInterestDerived;
}
public BigDecimal getTotalWithholdTax() {
return this.totalWithholdTax;
}
}
@@ -0,0 +1,884 @@
/**
* 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.portfolio.savings.domain;
import static org.apache.fineract.infrastructure.core.service.DateUtils.getSystemZoneId;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
import org.apache.fineract.infrastructure.core.service.DateUtils;
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.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
import org.apache.fineract.portfolio.savings.domain.interest.EndOfDayBalance;
import org.apache.fineract.portfolio.savings.domain.interest.SavingsAccountTransactionDetailsForPostingPeriod;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
import org.springframework.util.CollectionUtils;
/**
* All monetary transactions against a savings account are modelled through this entity.
*/
@Entity
@Table(name = "m_savings_account_transaction")
public final class SavingsAccountTransaction extends AbstractAuditableWithUTCDateTimeCustom {
@ManyToOne(optional = false)
@JoinColumn(name = "savings_account_id", referencedColumnName = "id", nullable = false)
private SavingsAccount savingsAccount;
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@ManyToOne(cascade = CascadeType.ALL, optional = true)
@JoinColumn(name = "payment_detail_id", nullable = true)
private PaymentDetail paymentDetail;
@Column(name = "transaction_type_enum", nullable = false)
private Integer typeOf;
@Column(name = "transaction_date", nullable = false)
private LocalDate dateOf;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@Column(name = "is_reversed", nullable = false)
private boolean reversed;
@Column(name = "running_balance_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal runningBalance;
@Column(name = "cumulative_balance_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal cumulativeBalance;
@Column(name = "balance_end_date_derived", nullable = true)
private LocalDate balanceEndDate;
@Column(name = "balance_number_of_days_derived", nullable = true)
private Integer balanceNumberOfDays;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "savingsAccountTransaction", orphanRemoval = true, fetch = FetchType.EAGER)
private Set<SavingsAccountChargePaidBy> savingsAccountChargesPaid = new HashSet<>();
@Column(name = "overdraft_amount_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal overdraftAmount;
@Deprecated
@Column(name = "created_date", nullable = true)
private LocalDateTime createdDateToRemove;
@Column(name = "submitted_on_date", nullable = false)
private LocalDate submittedOnDate;
@Column(name = "is_manual", length = 1, nullable = true)
private boolean isManualTransaction;
@Column(name = "is_loan_disbursement", length = 1, nullable = true)
private boolean isLoanDisbursement;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "savingsAccountTransaction")
private List<SavingsAccountTransactionTaxDetails> taxDetails = new ArrayList<>();
@Column(name = "release_id_of_hold_amount", length = 20)
private Long releaseIdOfHoldAmountTransaction;
@Column(name = "reason_for_block", nullable = true)
private String reasonForBlock;
@Column(name = "is_reversal", nullable = false)
private boolean reversalTransaction;
@Column(name = "original_transaction_id")
private Long originalTxnId;
@Column(name = "is_lien_transaction")
private Boolean lienTransaction;
@Column(name = "ref_no", nullable = true)
private String refNo;
SavingsAccountTransaction() {}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final BigDecimal amount, final boolean isReversed,
final boolean isManualTransaction, final Boolean lienTransaction, final String refNo) {
this.savingsAccount = savingsAccount;
this.office = office;
this.typeOf = typeOf;
this.dateOf = transactionLocalDate;
this.amount = amount;
this.reversed = isReversed;
this.paymentDetail = paymentDetail;
this.createdDateToRemove = null; // #audit backward compatibility deprecated
this.submittedOnDate = DateUtils.getBusinessLocalDate();
this.isManualTransaction = isManualTransaction;
this.lienTransaction = lienTransaction;
this.refNo = refNo;
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final Integer typeOf,
final LocalDate transactionLocalDate, final Money amount, final boolean isReversed, final boolean isManualTransaction,
final Boolean lienTransaction, final String refNo) {
this(savingsAccount, office, null, typeOf, transactionLocalDate, amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Money amount, final boolean isReversed,
final boolean isManualTransaction, final Boolean lienTransaction, final String refNo) {
this(savingsAccount, office, paymentDetail, typeOf, transactionLocalDate, amount.getAmount(), isReversed, isManualTransaction,
lienTransaction, refNo);
}
public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, final String refNo) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.DEPOSIT.getValue(), date,
amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount,
final SavingsAccountTransactionType savingsAccountTransactionType, final String refNo) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, savingsAccountTransactionType.getValue(), date, amount,
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction withdrawal(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, final String refNo) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.WITHDRAWAL.getValue(),
date, amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction interestPosting(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.INTEREST_POSTING.getValue(), date,
amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction overdraftInterest(final SavingsAccount savingsAccount, final Office office,
final LocalDate date, final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date,
amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final String refNo) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount,
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction annualFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount,
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction charge(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.PAY_CHARGE.getValue(), date, amount,
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction waiver(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WAIVE_CHARGES.getValue(), date, amount,
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction initiateTransfer(final SavingsAccount savingsAccount, final Office office,
final LocalDate date) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.INITIATE_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction approveTransfer(final SavingsAccount savingsAccount, final Office office,
final LocalDate date) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.APPROVE_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction withdrawTransfer(final SavingsAccount savingsAccount, final Office office,
final LocalDate date) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction withHoldTax(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final Map<TaxComponent, BigDecimal> taxDetails) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
final String refNo = null;
SavingsAccountTransaction accountTransaction = new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date, amount, isReversed, isManualTransaction, lienTransaction,
refNo);
updateTaxDetails(taxDetails, accountTransaction);
return accountTransaction;
}
public static SavingsAccountTransaction escheat(final SavingsAccount savingsAccount, final LocalDate date,
final boolean accountTransaction) {
final boolean isReversed = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail,
SavingsAccountTransactionType.ESCHEAT.getValue(), date, savingsAccount.getSummary().getAccountBalance(), isReversed,
accountTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction copyTransaction(SavingsAccountTransaction accountTransaction) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail,
accountTransaction.typeOf, accountTransaction.getTransactionDate(), accountTransaction.amount, accountTransaction.reversed,
accountTransaction.isManualTransaction, accountTransaction.lienTransaction, accountTransaction.refNo);
}
public static SavingsAccountTransaction holdAmount(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, final Boolean lienTransaction) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.AMOUNT_HOLD.getValue(),
date, amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction releaseAmount(SavingsAccountTransaction accountTransaction, LocalDate transactionDate) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail,
SavingsAccountTransactionType.AMOUNT_RELEASE.getValue(), transactionDate, accountTransaction.amount,
accountTransaction.reversed, accountTransaction.isManualTransaction, accountTransaction.lienTransaction,
accountTransaction.refNo);
}
public static SavingsAccountTransaction reversal(SavingsAccountTransaction accountTransaction) {
SavingsAccountTransaction sat = copyTransaction(accountTransaction);
sat.reversed = false;
sat.setReversalTransaction(true);
sat.originalTxnId = accountTransaction.getId();
return sat;
}
public static void updateTaxDetails(final Map<TaxComponent, BigDecimal> taxDetails,
final SavingsAccountTransaction accountTransaction) {
if (taxDetails != null) {
for (Map.Entry<TaxComponent, BigDecimal> mapEntry : taxDetails.entrySet()) {
accountTransaction.getTaxDetails()
.add(new SavingsAccountTransactionTaxDetails(accountTransaction, mapEntry.getKey(), mapEntry.getValue()));
}
}
}
public SavingsAccount getSavingsAccount() {
return this.savingsAccount;
}
public LocalDate getTransactionDate() {
return this.dateOf;
}
public LocalDate getEndOfBalanceDate() {
return balanceEndDate;
}
public boolean isReversed() {
return this.reversed;
}
public void reverse() {
this.reversed = true;
}
public BigDecimal getAmount() {
return this.amount;
}
public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount);
}
public void setAmount(final Money amount) {
this.amount = amount == null ? null : amount.getAmount();
}
public BigDecimal getRunningBalance() {
return this.runningBalance;
}
public Money getRunningBalance(final MonetaryCurrency currency) {
return Money.of(currency, this.runningBalance);
}
public void setRunningBalance(Money balance) {
this.runningBalance = balance == null ? null : balance.getAmount();
}
public boolean isManualTransaction() {
return this.isManualTransaction;
}
public Set<SavingsAccountChargePaidBy> getSavingsAccountChargesPaid() {
return this.savingsAccountChargesPaid;
}
public BigDecimal getOverdraftAmount() {
return this.overdraftAmount;
}
public Money getOverdraftAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.overdraftAmount);
}
void setOverdraftAmount(Money overdraftAmount) {
this.overdraftAmount = overdraftAmount == null ? null : overdraftAmount.getAmount();
}
public List<SavingsAccountTransactionTaxDetails> getTaxDetails() {
return this.taxDetails;
}
public Integer getTypeOf() {
return this.typeOf;
}
public SavingsAccountTransactionType getTransactionType() {
return SavingsAccountTransactionType.fromInt(this.typeOf);
}
public LocalDate getDateOf() {
return this.dateOf;
}
public String getRefNo() {
return this.refNo;
}
public PaymentDetail getPaymentDetail() {
return this.paymentDetail;
}
public void updateReleaseId(Long releaseId) {
this.releaseIdOfHoldAmountTransaction = releaseId;
}
public void updateReason(String reasonForBlock) {
this.reasonForBlock = reasonForBlock;
}
public Long getReleaseIdOfHoldAmountTransaction() {
return this.releaseIdOfHoldAmountTransaction;
}
public MonetaryCurrency getCurrency() {
return savingsAccount == null ? null : savingsAccount.getCurrency();
}
public Long getOfficeId() {
return this.office.getId();
}
public LocalDate getBalanceEndDate() {
return this.balanceEndDate;
}
public BigDecimal getCumulativeBalance() {
return this.cumulativeBalance;
}
public Integer getBalanceNumberOfDays() {
return this.balanceNumberOfDays;
}
public LocalDate getSubmittedOnDate() {
return submittedOnDate;
}
public boolean isReversalTransaction() {
return reversalTransaction;
}
void setReversalTransaction(boolean reversalTransaction) {
this.reversalTransaction = reversalTransaction;
}
public boolean isDeposit() {
return getTransactionType().isDeposit();
}
public boolean isDepositAndNotReversed() {
return getTransactionType().isDeposit() && isNotReversed();
}
public boolean isDividendPayout() {
return getTransactionType().isDividendPayout();
}
public boolean isDividendPayoutAndNotReversed() {
return getTransactionType().isDividendPayout() && isNotReversed();
}
public boolean isWithdrawal() {
return getTransactionType().isWithdrawal();
}
public boolean isPostInterestCalculationRequired() {
return this.isDeposit() || this.isWithdrawal() || this.isChargeTransaction() || this.isDividendPayout() || this.isInterestPosting();
}
public boolean isInterestPostingAndNotReversed() {
return getTransactionType().isInterestPosting() && isNotReversed();
}
public boolean isInterestPosting() {
return getTransactionType().isInterestPosting() || getTransactionType().isOverDraftInterestPosting();
}
public boolean isWithdrawalFeeAndNotReversed() {
return getTransactionType().isWithdrawalFee() && isNotReversed();
}
public boolean isWithdrawalFee() {
return getTransactionType().isWithdrawalFee();
}
public boolean isAnnualFeeAndNotReversed() {
return isAnnualFee() && isNotReversed();
}
public boolean isAnnualFee() {
return getTransactionType().isAnnualFee();
}
public boolean isNotReversed() {
return !isReversed();
}
public boolean isTransferInitiation() {
return getTransactionType().isTransferInitiation();
}
public boolean isTransferApproval() {
return getTransactionType().isTransferApproval();
}
public boolean isTransferRejection() {
return getTransactionType().isTransferRejection();
}
public boolean isTransferWithdrawal() {
return getTransactionType().isTransferWithdrawal();
}
public boolean isTransferRelatedTransaction() {
return isTransferInitiation() || isTransferApproval() || isTransferRejection() || isTransferWithdrawal();
}
public void zeroBalanceFields() {
this.runningBalance = null;
this.cumulativeBalance = null;
this.balanceEndDate = null;
this.balanceNumberOfDays = null;
}
public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) {
// balance end date should not be before transaction date
if (endOfBalanceDate != null && DateUtils.isBefore(endOfBalanceDate, this.getTransactionDate())) {
this.balanceEndDate = this.getTransactionDate();
} else {
this.balanceEndDate = endOfBalanceDate;
}
this.balanceNumberOfDays = LocalDateInterval.create(getTransactionDate(), endOfBalanceDate).daysInPeriodInclusiveOfEndDate();
this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount();
}
public boolean isAcceptableForDailyBalance(final LocalDateInterval interestPeriodInterval) {
return isNotReversed() && interestPeriodInterval.contains(getTransactionDate()) && isABalanceForAtLeastOneDay();
}
private boolean isABalanceForAtLeastOneDay() {
return this.balanceNumberOfDays != null && this.balanceNumberOfDays > 0;
}
public boolean hasNotAmount(final Money amountToCheck) {
final Money transactionAmount = getAmount(amountToCheck.getCurrency());
return transactionAmount.isNotEqualTo(amountToCheck);
}
public Map<String, Object> toMapData(final String currencyCode) {
final Map<String, Object> thisTransactionData = new LinkedHashMap<>();
final SavingsAccountTransactionEnumData transactionType = SavingsEnumerations.transactionType(this.typeOf);
thisTransactionData.put("id", getId());
thisTransactionData.put("officeId", this.office.getId());
thisTransactionData.put("type", transactionType);
thisTransactionData.put("reversed", isReversed());
thisTransactionData.put("date", getTransactionDate());
thisTransactionData.put("currencyCode", currencyCode);
thisTransactionData.put("amount", this.amount);
thisTransactionData.put("overdraftAmount", this.overdraftAmount);
if (this.paymentDetail != null) {
thisTransactionData.put("paymentTypeId", this.paymentDetail.getPaymentType().getId());
}
/***
* Sending data in a map, though in savings we currently expect a transaction to always repay a single charge
* (or may repay a part of a single charge too)
***/
if (!this.savingsAccountChargesPaid.isEmpty()) {
final List<Map<String, Object>> savingsChargesPaidData = new ArrayList<>();
for (final SavingsAccountChargePaidBy chargePaidBy : this.savingsAccountChargesPaid) {
final Map<String, Object> savingChargePaidData = new LinkedHashMap<>();
savingChargePaidData.put("chargeId", chargePaidBy.getSavingsAccountCharge().getCharge().getId());
savingChargePaidData.put("isPenalty", chargePaidBy.getSavingsAccountCharge().getCharge().isPenalty());
savingChargePaidData.put("savingsChargeId", chargePaidBy.getSavingsAccountCharge().getId());
savingChargePaidData.put("amount", chargePaidBy.getAmount());
savingsChargesPaidData.add(savingChargePaidData);
}
thisTransactionData.put("savingsChargesPaid", savingsChargesPaidData);
}
if (!this.taxDetails.isEmpty()) {
final List<Map<String, Object>> taxData = new ArrayList<>();
for (final SavingsAccountTransactionTaxDetails taxDetails : this.taxDetails) {
final Map<String, Object> taxDetailsData = new HashMap<>();
taxDetailsData.put("amount", taxDetails.getAmount());
if (taxDetails.getTaxComponent().getCreditAcount() != null) {
taxDetailsData.put("creditAccountId", taxDetails.getTaxComponent().getCreditAcount().getId());
}
taxData.add(taxDetailsData);
}
thisTransactionData.put("taxDetails", taxData);
}
return thisTransactionData;
}
public boolean isBefore(final LocalDate date) {
return DateUtils.isBefore(getTransactionDate(), date);
}
public boolean isAfter(final LocalDate date) {
return DateUtils.isAfter(getTransactionDate(), date);
}
public boolean occursOn(final LocalDate date) {
return DateUtils.isEqual(getTransactionDate(), date);
}
public EndOfDayBalance toEndOfDayBalance(final LocalDateInterval periodInterval, final MonetaryCurrency currency) {
final Money endOfDayBalance = Money.of(currency, this.runningBalance);
final Money openingBalance = endOfDayBalance;
LocalDate balanceDate = periodInterval.startDate();
int numberOfDays = periodInterval.daysInPeriodInclusiveOfEndDate();
if (periodInterval.contains(getTransactionDate())) {
balanceDate = getTransactionDate();
final LocalDateInterval newInterval = LocalDateInterval.create(getTransactionDate(), periodInterval.endDate());
numberOfDays = newInterval.daysInPeriodInclusiveOfEndDate();
}
return EndOfDayBalance.from(balanceDate, openingBalance, endOfDayBalance, numberOfDays);
}
public EndOfDayBalance toEndOfDayBalance(final Money openingBalance, final LocalDate nextTransactionDate) {
final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
if (isDeposit() || isDividendPayoutAndNotReversed()) {
endOfDayBalance = openingBalance.plus(getAmount(currency));
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
endOfDayBalance = openingBalance.minus(getAmount(currency));
}
int numberOfDays = LocalDateInterval.create(getTransactionDate(), nextTransactionDate).daysInPeriodInclusiveOfEndDate();
if (!openingBalance.isEqualTo(endOfDayBalance) && numberOfDays > 1) {
numberOfDays = numberOfDays - 1;
}
return EndOfDayBalance.from(getTransactionDate(), openingBalance, endOfDayBalance, numberOfDays);
}
public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) {
final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
if (isDeposit() || isDividendPayoutAndNotReversed()) {
endOfDayBalance = openingBalance.plus(getAmount(currency));
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
if (openingBalance.isGreaterThanZero() || this.savingsAccount.allowOverdraft()) {
endOfDayBalance = openingBalance.minus(getAmount(currency));
} else {
endOfDayBalance = getRunningBalance(currency);
}
}
return EndOfDayBalance.from(getTransactionDate(), openingBalance, endOfDayBalance, this.balanceNumberOfDays);
}
public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy) {
final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
int numberOfDaysOfBalance = this.balanceNumberOfDays;
LocalDate balanceStartDate = getTransactionDate();
LocalDate balanceEndDate = getEndOfBalanceDate();
if (DateUtils.isBefore(balanceStartDate, boundedBy.startDate())) {
balanceStartDate = boundedBy.startDate();
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
} else {
if (isDeposit() || isDividendPayoutAndNotReversed()) {
// endOfDayBalance = openingBalance.plus(getAmount(currency));
// if (endOfDayBalance.isLessThanZero()) {
endOfDayBalance = endOfDayBalance.plus(getAmount(currency));
// }
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
// endOfDayBalance = openingBalance.minus(getAmount(currency));
if (endOfDayBalance.isGreaterThanZero() || this.savingsAccount.allowOverdraft()) {
endOfDayBalance = endOfDayBalance.minus(getAmount(currency));
} else {
endOfDayBalance = getRunningBalance(currency);
}
}
}
if (DateUtils.isAfter(balanceEndDate, boundedBy.endDate())) {
balanceEndDate = boundedBy.endDate();
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
}
return EndOfDayBalance.from(balanceStartDate, openingBalance, endOfDayBalance, numberOfDaysOfBalance);
}
public boolean isBalanceInExistencesForOneDayOrMore() {
return this.balanceNumberOfDays != null && this.balanceNumberOfDays >= 1;
}
public boolean fallsWithin(final LocalDateInterval periodInterval) {
final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionDate(), getEndOfBalanceDate());
return periodInterval.contains(balanceInterval);
}
public boolean spansAnyPortionOf(final LocalDateInterval periodInterval) {
final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionDate(), getEndOfBalanceDate());
return balanceInterval.containsPortionOf(periodInterval);
}
public boolean isIdentifiedBy(final Long transactionId) {
return getId().equals(transactionId);
}
public boolean isCredit() {
return isCreditType() && !isReversed() && !isReversalTransaction();
}
public boolean isCreditType() {
return getTransactionType().isCredit();
}
public boolean isDebit() {
return isDebitType() && !isReversed() && !isReversalTransaction();
}
public boolean isDebitType() {
return getTransactionType().isDebit();
}
public boolean isWithHoldTaxAndNotReversed() {
return getTransactionType().isWithHoldTax() && isNotReversed();
}
public boolean isOverdraftInterestAndNotReversed() {
return getTransactionType().isIncomeFromInterest() && isNotReversed();
}
public boolean isPayCharge() {
return getTransactionType().isPayCharge();
}
public boolean isChargeTransaction() {
return getTransactionType().isChargeTransaction();
}
public boolean isChargeTransactionAndNotReversed() {
return getTransactionType().isChargeTransaction() && isNotReversed();
}
public boolean isWaiveCharge() {
return getTransactionType().isWaiveCharge();
}
public boolean isAmountOnHold() {
return getTransactionType().isAmountOnHold();
}
public boolean isAmountRelease() {
return getTransactionType().isAmountRelease();
}
private boolean canOverriteSavingAccountRules() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return isChargeTransaction() && chargePaidBy != null && chargePaidBy.canOverriteSavingAccountRules();
}
public boolean canProcessBalanceCheck() {
return isDebit() && !canOverriteSavingAccountRules();
}
public boolean isFeeCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return isPayCharge() && chargePaidBy != null && chargePaidBy.isFeeCharge();
}
public boolean isPenaltyCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return isPayCharge() && chargePaidBy != null && chargePaidBy.isPenaltyCharge();
}
public boolean isFeeChargeAndNotReversed() {
return isFeeCharge() && isNotReversed();
}
public boolean isPenaltyChargeAndNotReversed() {
return isPenaltyCharge() && isNotReversed();
}
public boolean isWaiveFeeCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return isWaiveCharge() && chargePaidBy != null && chargePaidBy.isFeeCharge();
}
public boolean isWaivePenaltyCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return isWaiveCharge() && chargePaidBy != null && chargePaidBy.isPenaltyCharge();
}
public boolean isWaiveFeeChargeAndNotReversed() {
return isWaiveFeeCharge() && isNotReversed();
}
public boolean isWaivePenaltyChargeAndNotReversed() {
return isWaivePenaltyCharge() && isNotReversed();
}
private SavingsAccountChargePaidBy getSavingsAccountChargePaidBy() {
if (!CollectionUtils.isEmpty(this.savingsAccountChargesPaid)) {
return this.savingsAccountChargesPaid.iterator().next();
}
return null;
}
public boolean isPaymentForCurrentCharge(final SavingsAccountCharge savingsAccountCharge) {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return chargePaidBy != null && chargePaidBy.getSavingsAccountCharge().equals(savingsAccountCharge);
}
@Override
public Optional<OffsetDateTime> getCreatedDate() {
// #audit backward compatibility keep system datetime
return Optional.ofNullable(super.getCreatedDate()
.orElse(createdDateToRemove == null ? null : createdDateToRemove.atZone(getSystemZoneId()).toOffsetDateTime()));
}
public boolean isAmountOnHoldNotReleased() {
return (isAmountOnHold() && getReleaseIdOfHoldAmountTransaction() == null);
}
public SavingsAccountTransactionDetailsForPostingPeriod toSavingsAccountTransactionDetailsForPostingPeriod(MonetaryCurrency currency,
boolean isAllowOverDraft) {
return new SavingsAccountTransactionDetailsForPostingPeriod(getId(), this.dateOf, this.balanceEndDate, this.runningBalance,
this.amount, currency, this.balanceNumberOfDays, isDeposit(), isWithdrawal(), isAllowOverDraft,
isChargeTransactionAndNotReversed(), isDividendPayoutAndNotReversed());
}
}
@@ -0,0 +1,44 @@
/**
* 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.portfolio.savings.domain;
import java.util.Comparator;
import org.apache.fineract.infrastructure.core.service.DateUtils;
/**
* Sort savings account transactions by transaction date and transaction type placing
*/
public class SavingsAccountTransactionComparator implements Comparator<SavingsAccountTransaction> {
@Override
public int compare(final SavingsAccountTransaction o1, final SavingsAccountTransaction o2) {
int result = DateUtils.compare(o1.getTransactionDate(), o2.getTransactionDate());
if (result != 0) {
return result;
}
result = DateUtils.compare(o1.getCreatedDateTime(), o2.getCreatedDateTime());
if (result != 0) {
return result;
}
if (o1.getId() != null && o2.getId() != null) {
return o1.getId().compareTo(o2.getId());
}
return 0;
}
}
@@ -0,0 +1,43 @@
/**
* 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.portfolio.savings.domain;
import java.util.Comparator;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
public class SavingsAccountTransactionDataComparator implements Comparator<SavingsAccountTransactionData> {
@Override
public int compare(final SavingsAccountTransactionData o1, final SavingsAccountTransactionData o2) {
int result = DateUtils.compare(o1.getTransactionDate(), o2.getTransactionDate());
if (result != 0) {
return result;
}
result = DateUtils.compare(o1.getSubmittedOnDate(), o2.getSubmittedOnDate());
if (result != 0) {
return result;
}
if (o1.getId() != null && o2.getId() != null) {
return o1.getId().compareTo(o2.getId());
}
return 0;
}
}
@@ -0,0 +1,56 @@
/**
* 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.portfolio.savings.domain;
import jakarta.persistence.LockModeType;
import java.time.LocalDate;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface SavingsAccountTransactionRepository
extends JpaRepository<SavingsAccountTransaction, Long>, JpaSpecificationExecutor<SavingsAccountTransaction> {
@Query("select sat from SavingsAccountTransaction sat where sat.id = :transactionId and sat.savingsAccount.id = :savingsId")
SavingsAccountTransaction findOneByIdAndSavingsAccountId(@Param("transactionId") Long transactionId,
@Param("savingsId") Long savingsId);
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select st from SavingsAccountTransaction st where st.savingsAccount = :savingsAccount and st.dateOf >= :transactionDate order by st.dateOf,st.createdDate,st.id")
List<SavingsAccountTransaction> findTransactionsAfterPivotDate(@Param("savingsAccount") SavingsAccount savingsAccount,
@Param("transactionDate") LocalDate transactionDate);
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select st from SavingsAccountTransaction st where st.savingsAccount = :savingsAccount and st.dateOf = :date and st.reversalTransaction <> 1 and st.reversed <> 1 order by st.id")
List<SavingsAccountTransaction> findTransactionRunningBalanceBeforePivotDate(@Param("savingsAccount") SavingsAccount savingsAccount,
@Param("date") LocalDate date);
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<SavingsAccountTransaction> findBySavingsAccount(@Param("savingsAccount") SavingsAccount savingsAccount);
List<SavingsAccountTransaction> findByRefNo(@Param("refNo") String refNo);
@Query("select sat from SavingsAccountTransaction sat where sat.savingsAccount.id = :savingsId and sat.dateOf <= :transactionDate and sat.reversed=false")
List<SavingsAccountTransaction> findBySavingsAccountIdAndLessThanDateOfAndReversedIsFalse(@Param("savingsId") Long savingsId,
@Param("transactionDate") LocalDate transactionDate, Pageable pageable);
}

Some files were not shown because too many files have changed in this diff Show More