Create null statistic entries for accounts not used in the period to close

This commit is contained in:
2019-11-14 21:57:34 +01:00
parent 278bd41f0f
commit 8cec43ee91
6 changed files with 82 additions and 9 deletions

View File

@@ -2,6 +2,7 @@ package de.financer.dba;
import de.financer.model.Account;
import de.financer.model.AccountStatistic;
import de.financer.model.AccountStatus;
import de.financer.model.Period;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
@@ -12,4 +13,7 @@ import org.springframework.transaction.annotation.Transactional;
public interface AccountStatisticRepository extends CrudRepository<AccountStatistic, Long> {
@Query("SELECT accStat FROM AccountStatistic accStat WHERE accStat.account = :account AND accStat.period = :period")
AccountStatistic findForAccountAndPeriod(Account account, Period period);
@Query("SELECT a FROM Account a WHERE a NOT IN (SELECT accStat.account FROM AccountStatistic accStat WHERE accStat.period = :period) AND a.status = :accountStatus")
Iterable<Account> getAccountsWithoutStatisticInPeriod(Period period, AccountStatus accountStatus);
}

View File

@@ -81,6 +81,16 @@ public class AccountGroupService {
return ResponseReason.OK;
}
/**
* This method gets the expenses for all account groups that were used between the given from and to
* dates. The result list contains one entry per account group that holds the sum of bookings.
*
* @param periodStart the start of the period as date without time
* @param periodEnd the end of the period as date without time
* @return a list containing the sum of bookings per account group in the given period, an empty list if
* either no bookings have been done yet or if the given period start and end dates are not valid, but never
* <code>null</code>
*/
public Iterable<AccountGroupExpense> getAccountGroupExpenses(String periodStart, String periodEnd) {
LocalDate startDate;
LocalDate endDate;
@@ -96,12 +106,21 @@ public class AccountGroupService {
return Collections.emptyList();
}
// Actual calculation done via SQL
return this.accountGroupRepository.getAccountGroupExpenses(startDate, endDate, AccountType.LIABILITY, AccountType.EXPENSE);
}
/**
* This method gets the expenses for all account groups that were used in the current expense period.
* The result list contains one entry per account group that holds the sum of bookings.
*
* @return a list containing the sum of bookings per account group in the current expense period, an empty list if
* no bookings have been done yet, but never <code>null</code>
*/
public Iterable<AccountGroupExpense> getAccountGroupExpensesCurrentExpensePeriod() {
final Period period = this.periodService.getCurrentExpensePeriod();
// Actual calculation done via SQL
return this.accountGroupRepository.getAccountGroupExpensesCurrentExpensePeriod(period, AccountType.LIABILITY, AccountType.EXPENSE);
}
}

View File

@@ -166,7 +166,8 @@ public class AccountService {
*
* @param periodStart the start of the arbitrary period
* @param periodEnd the end of the arbitrary period
* @return a mapping of {@link Account}<->its expenses in the given period
* @return a mapping of {@link Account}<->its expenses in the given period, an empty list of no
* ookings have been done yet or the given start and end dates are invalid, but never <code>null</code>
*/
public Iterable<AccountExpense> getAccountExpenses(String periodStart, String periodEnd) {
LocalDate startDate;
@@ -183,17 +184,20 @@ public class AccountService {
return Collections.emptyList();
}
// Actual calculation done in SQL
return this.accountRepository.getAccountExpenses(startDate, endDate, AccountType.LIABILITY, AccountType.EXPENSE);
}
/**
* This method calculates the expenses per account in the current expense period.
* This method gets the expenses per account in the current expense period.
*
* @return a mapping of {@link Account}<->its expenses in the current expense period
* @return a mapping of {@link Account}<->its expenses in the current expense period, an empty list of no
* bookings have been done yet, but never <code>null</code>
*/
public Iterable<AccountExpense> getAccountExpensesCurrentExpensePeriod() {
final Period period = this.periodService.getCurrentExpensePeriod();
// Actual calculation done in SQL
return this.accountRepository.getAccountExpenses(period, AccountType.LIABILITY, AccountType.EXPENSE);
}
}

View File

@@ -1,10 +1,8 @@
package de.financer.service;
import de.financer.dba.AccountStatisticRepository;
import de.financer.model.Account;
import de.financer.model.AccountStatistic;
import de.financer.model.Period;
import de.financer.model.Transaction;
import de.financer.model.*;
import org.apache.commons.collections4.IterableUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -14,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class AccountStatisticService {
@@ -22,6 +21,17 @@ public class AccountStatisticService {
@Autowired
private AccountStatisticRepository accountStatisticRepository;
@Autowired
private AccountService accountService;
/**
* This method calculates the {@link AccountStatistic}s for the given transaction. The statistics are always
* symmetric, meaning that one will be calculated for the from account and one for the to account, with their
* respective from or to spending filled. As a transaction can be assigned to more than one period this method will
* calculate two statistics per assigned period.
*
* @param transaction to calculate the statistics for
*/
@Transactional(propagation = Propagation.REQUIRED)
public void calculateStatistics(Transaction transaction) {
final Account fromAccount = transaction.getFromAccount();
@@ -52,6 +62,34 @@ public class AccountStatisticService {
this.accountStatisticRepository.saveAll(resultList);
}
/**
* This method creates an {@link AccountStatistic}s entry with from and to spending <code>0</code>,
* for every account not used in a booking in the given period.
*
* @param period to generate the null statistic entries for
*/
@Transactional(propagation = Propagation.REQUIRED)
public void generateNullStatisticsForUnusedAccounts(Period period) {
final Iterable<Account> accountsWithoutStat = this.accountStatisticRepository
.getAccountsWithoutStatisticInPeriod(period, AccountStatus.OPEN);
this.accountStatisticRepository.saveAll(IterableUtils.toList(accountsWithoutStat)
.stream()
.map(a -> {
final AccountStatistic as = new AccountStatistic();
as.setTransactionCountTo(0);
as.setSpendingTotalTo(0);
as.setTransactionCountFrom(0);
as.setSpendingTotalFrom(0);
as.setAccount(a);
as.setPeriod(period);
return as;
})
.collect(Collectors.toList()));
}
private AccountStatistic calculateInternal(Account account, Period period, long amount, boolean from, int multiplier) {
AccountStatistic accountStatistic = this.accountStatisticRepository
.findForAccountAndPeriod(account, period);
@@ -66,8 +104,7 @@ public class AccountStatisticService {
if (from) {
accountStatistic.setSpendingTotalFrom(accountStatistic.getSpendingTotalFrom() + amount * multiplier);
accountStatistic.setTransactionCountFrom(accountStatistic.getTransactionCountFrom() + multiplier);
}
else {
} else {
accountStatistic.setSpendingTotalTo(accountStatistic.getSpendingTotalTo() + amount * multiplier);
accountStatistic.setTransactionCountTo(accountStatistic.getTransactionCountTo() + multiplier);
}

View File

@@ -20,6 +20,9 @@ public class PeriodService {
@Autowired
private PeriodRepository periodRepository;
@Autowired
private AccountStatisticService accountStatisticService;
/**
* @return the currently open expense period
*/
@@ -48,6 +51,8 @@ public class PeriodService {
this.periodRepository.save(currentPeriod);
this.periodRepository.save(nextPeriod);
this.accountStatisticService.generateNullStatisticsForUnusedAccounts(currentPeriod);
response = ResponseReason.OK;
} catch (Exception e) {
LOGGER.error("Could not close current expense period!", e);

View File

@@ -1,3 +1,7 @@
v25 -> v26:
- Close of the current expense period now creates null statistic entries for accounts that have not been used in
bookings in the period to close. This way the average spending better reflects the period average
v24 -> v25:
- Add color column in account overview
- Fix a bug that caused the chart generation to crash