Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3901,4 +3901,13 @@ public CommandWrapperBuilder detachLoanOriginator(final Long loanId, final Long
this.href = "/loans/" + loanId + "/originators/" + originatorId;
return this;
}

public CommandWrapperBuilder savingsAccountForceWithdrawal(final Long accountId) {
this.actionName = "FORCE_WITHDRAWAL";
this.entityName = "SAVINGSACCOUNT";
this.entityId = accountId;
this.savingsId = accountId;
this.href = "/savingsaccounts/" + accountId;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public final class GlobalConfigurationConstants {
public static final String ALLOWED_LOAN_STATUSES_OF_DELAYED_SETTLEMENT_FOR_EXTERNAL_ASSET_TRANSFER = "allowed-loan-statuses-of-delayed-settlement-for-external-asset-transfer";
public static final String ENABLE_ORIGINATOR_CREATION_DURING_LOAN_APPLICATION = "enable-originator-creation-during-loan-application";
public static final String PASSWORD_REUSE_CHECK_HISTORY_COUNT = "password-reuse-check-history-count";
public static final String FORCE_WITHDRAWAL_ON_SAVINGS_ACCOUNT = "allow-force-withdrawal-on-savings-account";
public static final String FORCE_WITHDRAWAL_ON_SAVINGS_ACCOUNT_LIMIT = "force-withdrawal-on-savings-account-limit";
public static final String FORCE_PASSWORD_RESET_ON_FIRST_LOGIN = "force-password-reset-on-first-login";

private GlobalConfigurationConstants() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ public interface ConfigurationDomainService {

String getAssetOwnerTransferOustandingInterestStrategy();

boolean isForceWithdrawalOnSavingsAccountEnabled();

Long retrieveForceWithdrawalOnSavingsAccountLimit();

Integer getPasswordReuseRestrictionCount();

boolean isForcePasswordResetOnFirstLoginEnabled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ private List<String> validateRepaymentScheduleTotal(List<String> header, LoanSch
PostLoansLoanIdTransactionsRequest setReAgeingRequestProperties(PostLoansLoanIdTransactionsRequest request, List<String> headers,
List<String> values) {
for (int i = 0; i < headers.size(); i++) {
String header = headers.get(i).toLowerCase().trim().replaceAll(" ", "");
String header = headers.get(i).toLowerCase(java.util.Locale.ROOT).trim().replaceAll(" ", "");
switch (header) {
case "frequencynumber" -> request.setFrequencyNumber(Integer.parseInt(values.get(i)));
case "frequencytype" -> request.setFrequencyType(values.get(i));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,7 @@ public void initialize() throws Exception {
.recalculationRestFrequencyType(1)//
.recalculationRestFrequencyInterval(1)//
.repaymentEvery(1)//
.interestRatePerPeriod((double) 7.0)//
.interestRatePerPeriod(7.0)//
.interestRateFrequencyType(INTEREST_RATE_FREQUENCY_TYPE_MONTH)//
.enableDownPayment(false)//
.interestRecalculationCompoundingMethod(0)//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,16 @@ public String getAssetOwnerTransferOustandingInterestStrategy() {
GlobalConfigurationConstants.ASSET_OWNER_TRANSFER_OUTSTANDING_INTEREST_CALCULATION_STRATEGY).getStringValue();
}

@Override
public boolean isForceWithdrawalOnSavingsAccountEnabled() {
return getGlobalConfigurationPropertyData(GlobalConfigurationConstants.FORCE_WITHDRAWAL_ON_SAVINGS_ACCOUNT).isEnabled();
}

@Override
public Long retrieveForceWithdrawalOnSavingsAccountLimit() {
return getGlobalConfigurationPropertyData(GlobalConfigurationConstants.FORCE_WITHDRAWAL_ON_SAVINGS_ACCOUNT_LIMIT).getValue();
}

@Override
public Integer getPasswordReuseRestrictionCount() {
final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* 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.infrastructure.event.business.domain.savings.transaction;

import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;

public class SavingsAccountForceWithdrawalBusinessEvent extends SavingsAccountTransactionBusinessEvent {

private static final String TYPE = "SavingsAccountForceWithdrawalBusinessEvent";

public SavingsAccountForceWithdrawalBusinessEvent(SavingsAccountTransaction value) {
super(value);
}

@Override
public String getType() {
return TYPE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
Expand Down Expand Up @@ -134,6 +135,7 @@ public class InteropServiceImpl implements InteropService {
private final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper;

private final SavingsAccountDomainService savingsAccountService;
private final ConfigurationDomainService configurationDomainService;

private final JdbcTemplate jdbcTemplate;

Expand Down Expand Up @@ -566,7 +568,7 @@ private Loan validateAndGetLoan(String accountId) {
private SavingsAccount validateAndGetSavingAccount(@NonNull InteropRequestData request) {
// TODO: error handling
SavingsAccount savingsAccount = validateAndGetSavingAccount(request.getAccountId());
savingsAccount.setHelpers(savingsAccountTransactionSummaryWrapper, savingsHelper);
savingsAccount.setHelpers(savingsAccountTransactionSummaryWrapper, savingsHelper, configurationDomainService);

ApplicationCurrency requestCurrency = currencyRepository.findOneByCode(request.getAmount().getCurrency());
if (!savingsAccount.getCurrency().getCode().equals(requestCurrency.getCode())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.apache.fineract.interoperation.starter;

import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
Expand Down Expand Up @@ -52,12 +53,12 @@ public InteropService interopService(PlatformSecurityContext securityContext, In
PaymentTypeRepository paymentTypeRepository, InteropIdentifierRepository identifierRepository,
LoanRepositoryWrapper loanRepositoryWrapper, SavingsHelper savingsHelper,
SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper,
SavingsAccountDomainService savingsAccountService, JdbcTemplate jdbcTemplate,
PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
SavingsAccountDomainService savingsAccountService, ConfigurationDomainService configurationDomainService,
JdbcTemplate jdbcTemplate, PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer, DatabaseSpecificSQLGenerator sqlGenerator) {
return new InteropServiceImpl(securityContext, interopDataValidator, savingsAccountRepository, savingsAccountTransactionRepository,
applicationCurrencyRepository, noteRepository, paymentTypeRepository, identifierRepository, loanRepositoryWrapper,
savingsHelper, savingsAccountTransactionSummaryWrapper, savingsAccountService, jdbcTemplate,
savingsHelper, savingsAccountTransactionSummaryWrapper, savingsAccountService, configurationDomainService, jdbcTemplate,
commandsSourceWritePlatformService, toApiJsonSerializer, sqlGenerator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,10 @@ public String transaction(@PathParam("savingsId") final Long savingsId, @QueryPa
case "deposit" -> builder.savingsAccountDeposit(savingsId).build();
case "gsimDeposit" -> builder.gsimSavingsAccountDeposit(savingsId).build();
case "withdrawal" -> builder.savingsAccountWithdrawal(savingsId).build();
case "force-withdrawal" -> builder.savingsAccountForceWithdrawal(savingsId).build();
case "postInterestAsOn" -> builder.savingsAccountInterestPosting(savingsId).build();
case SavingsApiConstants.COMMAND_HOLD_AMOUNT -> builder.holdAmount(savingsId).build();
default -> throw new UnrecognizedQueryParamException("command", commandParam, "deposit", "withdrawal",
default -> throw new UnrecognizedQueryParamException("command", commandParam, "deposit", "withdrawal", "force-withdrawal",
SavingsApiConstants.COMMAND_HOLD_AMOUNT);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import java.util.Locale;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
Expand Down Expand Up @@ -120,6 +121,7 @@ public class DepositAccountAssembler {
private final PaymentDetailAssembler paymentDetailAssembler;

private final ExternalIdFactory externalIdFactory;
private final ConfigurationDomainService configurationDomainService;

@Autowired
public DepositAccountAssembler(final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper,
Expand All @@ -130,7 +132,8 @@ public DepositAccountAssembler(final SavingsAccountTransactionSummaryWrapper sav
final DepositProductAssembler depositProductAssembler,
final RecurringDepositProductRepository recurringDepositProductRepository,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService, final PlatformSecurityContext context,
final PaymentDetailAssembler paymentDetailAssembler, ExternalIdFactory externalIdFactory) {
final PaymentDetailAssembler paymentDetailAssembler, ExternalIdFactory externalIdFactory,
final ConfigurationDomainService configurationDomainService) {

this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper;
this.clientRepository = clientRepository;
Expand All @@ -146,6 +149,7 @@ public DepositAccountAssembler(final SavingsAccountTransactionSummaryWrapper sav
this.context = context;
this.paymentDetailAssembler = paymentDetailAssembler;
this.externalIdFactory = externalIdFactory;
this.configurationDomainService = configurationDomainService;
}

/**
Expand Down Expand Up @@ -356,7 +360,7 @@ public SavingsAccount assembleFrom(final JsonCommand command, final AppUser subm
}

if (account != null) {
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper, this.configurationDomainService);
account.validateNewApplicationState(depositAccountType.resourceName());
}

Expand All @@ -365,12 +369,12 @@ public SavingsAccount assembleFrom(final JsonCommand command, final AppUser subm

public SavingsAccount assembleFrom(final Long savingsId, DepositAccountType depositAccountType) {
final SavingsAccount account = this.savingsAccountRepository.findOneWithNotFoundDetection(savingsId, depositAccountType);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper, this.configurationDomainService);
return account;
}

public void assignSavingAccountHelpers(final SavingsAccount savingsAccount) {
savingsAccount.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
savingsAccount.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper, this.configurationDomainService);
}

public DepositAccountTermAndPreClosure assembleAccountTermAndPreClosure(final JsonCommand command,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ public SavingsAccount assembleFrom(final JsonCommand command, final AppUser subm
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, charges,
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, maxAllowedLienLimit, lienAllowed,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper, this.configurationDomainService);

account.validateNewApplicationState(SAVINGS_ACCOUNT_RESOURCE_NAME);

Expand Down Expand Up @@ -381,7 +381,7 @@ public SavingsAccount loadTransactionsToSavingsAccount(final SavingsAccount acco
}
}

account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper, this.configurationDomainService);
return account;
}

Expand Down Expand Up @@ -421,7 +421,7 @@ public boolean isRelaxingDaysConfigForPivotDateEnabled() {
}

public void setHelpers(final SavingsAccount account) {
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper, this.configurationDomainService);
}

/**
Expand Down Expand Up @@ -465,7 +465,7 @@ public SavingsAccount assembleFrom(final Client client, final Group group, final
product.isMinRequiredBalanceEnforced(), product.minRequiredBalance(), product.maxAllowedLienLimit(),
product.isLienAllowed(), product.nominalAnnualInterestRateOverdraft(), product.minOverdraftForInterestCalculation(),
product.withHoldTax());
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper, this.configurationDomainService);

account.validateNewApplicationState(SAVINGS_ACCOUNT_RESOURCE_NAME);

Expand All @@ -475,7 +475,7 @@ public SavingsAccount assembleFrom(final Client client, final Group group, final
}

public void assignSavingAccountHelpers(final SavingsAccount savingsAccount) {
savingsAccount.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
savingsAccount.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper, this.configurationDomainService);
}

public void assignSavingAccountHelpers(final SavingsAccountData savingsAccountData) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.event.business.domain.savings.transaction.SavingsAccountForceWithdrawalBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.savings.transaction.SavingsDepositBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.savings.transaction.SavingsWithdrawalBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
Expand Down Expand Up @@ -130,7 +131,7 @@ public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account,
}

account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount, transactionBooleanValues.isExceptionForBalanceCheck(),
depositAccountOnHoldTransactions, backdatedTxnsAllowedTill);
depositAccountOnHoldTransactions, backdatedTxnsAllowedTill, transactionBooleanValues.isForceWithdrawal());

saveTransactionToGenerateTransactionId(withdrawal);
if (backdatedTxnsAllowedTill) {
Expand All @@ -142,7 +143,11 @@ public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account,
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, transactionBooleanValues.isAccountTransfer(),
backdatedTxnsAllowedTill);

businessEventNotifierService.notifyPostBusinessEvent(new SavingsWithdrawalBusinessEvent(withdrawal));
if (transactionBooleanValues.isForceWithdrawal()) {
businessEventNotifierService.notifyPostBusinessEvent(new SavingsAccountForceWithdrawalBusinessEvent(withdrawal));
} else {
businessEventNotifierService.notifyPostBusinessEvent(new SavingsWithdrawalBusinessEvent(withdrawal));
}
return withdrawal;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* 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.handler;

import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@CommandType(entity = "SAVINGSACCOUNT", action = "FORCE_WITHDRAWAL")
public class ForceWithdrawalSavingsAccountCommandHandler implements NewCommandSourceHandler {

private final SavingsAccountWritePlatformService writePlatformService;

@Autowired
public ForceWithdrawalSavingsAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}

@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.forceWithdrawal(command.getSavingsId(), command);
}
}
Loading
Loading