FINERACT-1646-backdated-transaction-available-balance-fix

This commit is contained in:
Dhaval Maniyar
2022-08-20 12:49:20 +02:00
committed by Aleksandar Vidakovic
parent c9be0a2816
commit c21ea4e1d3
7 changed files with 54 additions and 16 deletions
@@ -116,6 +116,8 @@ public final class SavingsAccountData implements Serializable {
private final BigDecimal minOverdraftForInterestCalculation;
private transient List<SavingsAccountTransactionData> savingsAccountTransactionData = new ArrayList<>();
private transient SavingsAccountTransactionData lastSavingsAccountTransaction;
private List<DatatableData> datatables = null;
// import field
@@ -1104,4 +1106,12 @@ public final class SavingsAccountData implements Serializable {
public Set<Long> getExistingReversedTransactionIds() {
return this.existingReversedTransactionIds;
}
public SavingsAccountTransactionData getLastSavingsAccountTransaction() {
return lastSavingsAccountTransaction;
}
public void setLastSavingsAccountTransaction(SavingsAccountTransactionData lastSavingsAccountTransaction) {
this.lastSavingsAccountTransaction = lastSavingsAccountTransaction;
}
}
@@ -223,16 +223,23 @@ public class SavingsAccountSummaryData implements Serializable {
Money interestTotal = Money.of(currency, this.totalInterestPosted);
Money withHoldTaxTotal = Money.of(currency, this.totalWithholdTax);
Money overdraftInterestTotal = Money.of(currency, this.totalOverdraftInterestDerived);
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");
BigDecimal deltaInterest = interestTotal.minus(this.totalInterestPosted).getAmountDefaultedToNullIfZero();
BigDecimal deltaWithholdTax = withHoldTaxTotal.minus(this.totalWithholdTax).getAmountDefaultedToNullIfZero();
BigDecimal deltaOverdraftInterestDerived = overdraftInterestTotal.minus(this.totalOverdraftInterestDerived)
.getAmountDefaultedToNullIfZero();
this.totalInterestPosted = interestTotal.getAmountDefaultedToNullIfZero();
this.totalWithholdTax = withHoldTaxTotal.getAmountDefaultedToNullIfZero();
this.totalOverdraftInterestDerived = overdraftInterestTotal.getAmountDefaultedToNullIfZero();
this.accountBalance = Money.of(currency, this.accountBalance).plus(this.totalInterestPosted).minus(this.totalWithholdTax)
.minus(this.totalOverdraftInterestDerived).getAmount();
this.accountBalance = getRunningBalanceOnPivotDate();
this.accountBalance = Money.of(currency, this.accountBalance).plus(Money.of(currency, this.totalDeposits)).plus(deltaInterest)
.minus(this.totalWithdrawals).minus(deltaWithholdTax).minus(deltaOverdraftInterestDerived).getAmount();
}
}
@@ -48,6 +48,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdraw
import com.google.gson.JsonElement;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
@@ -74,6 +75,7 @@ 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.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
import org.apache.fineract.useradministration.domain.AppUser;
import org.slf4j.Logger;
@@ -381,12 +383,25 @@ public class SavingsAccountAssembler {
public SavingsAccountData assembleSavings(final SavingsAccountData account) {
// Update last running balance on account level
if (account.getTransactions() != null && account.getTransactions().size() != 0
&& account.getSummary().getInterestPostedTillDate() != null) {
account.getSummary().setRunningBalanceOnPivotDate(account.getTransactions().get(account.getTransactions().size() - 1)
.getRunningBalance(account.getCurrency()).getAmount());
}
final boolean backdatedTxnsAllowedTill = this.configurationDomainService.retrievePivotDateConfig();
if (backdatedTxnsAllowedTill && account.getTransactions() != null && account.getSummary().getInterestPostedTillDate() != null) {
List<SavingsAccountTransactionData> removalList = new ArrayList<>();
for (int i = 0; i < account.getTransactions().size(); i++) {
SavingsAccountTransactionData savingsAccountTransaction = account.getTransactions().get(i);
removalList.add(savingsAccountTransaction);
if ((savingsAccountTransaction.isInterestPostingAndNotReversed()
|| savingsAccountTransaction.isOverdraftInterestAndNotReversed())
&& !savingsAccountTransaction.isReversalTransaction()) {
account.getSummary().setRunningBalanceOnPivotDate(savingsAccountTransaction.getRunningBalance());
account.setLastSavingsAccountTransaction(savingsAccountTransaction);
break;
}
}
account.getTransactions().removeAll(removalList);
} else {
account.getSummary().setRunningBalanceOnPivotDate(BigDecimal.ZERO);
}
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
return account;
}
@@ -76,7 +76,7 @@ public class PostInterestForSavingTasklet implements Tasklet {
final int pageSize = batchSize * threadPoolSize;
Long maxSavingsIdInList = 0L;
final ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
final boolean backdatedTxnsAllowedTill = false;
final boolean backdatedTxnsAllowedTill = this.configurationDomainService.retrievePivotDateConfig();
long start = System.currentTimeMillis();
@@ -364,8 +364,12 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
}
final List<SavingsAccountTransactionData> listOfTransactionsSorted = new ArrayList<>();
listOfTransactionsSorted.addAll(savingsAccountData.getTransactions());
final SavingsAccountTransactionDataComparator transactionComparator = new SavingsAccountTransactionDataComparator();
Collections.sort(listOfTransactionsSorted, transactionComparator);
if (!listOfTransactionsSorted.isEmpty()) {
final SavingsAccountTransactionDataComparator transactionComparator = new SavingsAccountTransactionDataComparator();
Collections.sort(listOfTransactionsSorted, transactionComparator);
} else {
listOfTransactionsSorted.add(savingsAccountData.getLastSavingsAccountTransaction());
}
return listOfTransactionsSorted.get(0);
}
@@ -272,7 +272,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
+ "where (CASE WHEN sa.interest_posted_till_date is not null THEN tr.transaction_date >= sa.interest_posted_till_date ELSE tr.transaction_date >= sa.activatedon_date END) ";
}
sql = sql + " and (sa.interest_posted_till_date is null or sa.interest_posted_till_date < ? ) ";
sql = sql + " and (sa.interest_posted_till_date is null or sa.interest_posted_till_date <= ? ) ";
sql = sql + " order by sa.id, tr.transaction_date, tr.created_date, tr.id";
List<SavingsAccountData> savingsAccountDataList = this.jdbcTemplate.query(sql, this.savingAccountMapperForInterestPosting, // NOSONAR
@@ -2973,18 +2973,20 @@ public class ClientSavingsIntegrationTest {
configurationForBackdatedTransaction();
String transactionDate = "1 July 2022";
LocalDate transactionDate = LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(10);
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy");
String startDate = formatter.format(transactionDate);
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, transactionDate);
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, startDate);
Assertions.assertNotNull(clientID);
final Integer savingsId = createSavingsAccountDailyPostingOverdraft(clientID, transactionDate);
this.savingsAccountHelper.depositToSavingsAccount(savingsId, "200", transactionDate, CommonConstants.RESPONSE_RESOURCE_ID);
final Integer savingsId = createSavingsAccountDailyPostingOverdraft(clientID, startDate);
this.savingsAccountHelper.depositToSavingsAccount(savingsId, "200", startDate, CommonConstants.RESPONSE_RESOURCE_ID);
final String jobName = "Post Interest For Savings";
this.scheduleJobHelper.executeAndAwaitJob(jobName);
final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build();
final SavingsAccountHelper validationErrorHelper = new SavingsAccountHelper(this.requestSpec, errorResponse);
List<HashMap> error = (List<HashMap>) validationErrorHelper.depositToSavingsAccount(savingsId, "3000", transactionDate,
List<HashMap> error = (List<HashMap>) validationErrorHelper.depositToSavingsAccount(savingsId, "300", startDate,
CommonConstants.RESPONSE_ERROR);
assertEquals("error.msg.savings.transaction.is.not.allowed", error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));