Coverage Summary for Class: GetBudgetTransactionsUseCase (com.georgeci.moneysurfer.domain.usecase)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| GetBudgetTransactionsUseCase |
66.7%
(2/3)
|
|
83.3%
(5/6)
|
87%
(20/23)
|
| GetBudgetTransactionsUseCase$invoke$1 |
100%
(2/2)
|
50%
(1/2)
|
100%
(14/14)
|
97.3%
(71/73)
|
| GetBudgetTransactionsUseCase$invoke$1$invokeSuspend$$inlined$map$1 |
0%
(0/2)
|
|
| GetBudgetTransactionsUseCase$invoke$1$invokeSuspend$$inlined$map$1$1 |
|
| GetBudgetTransactionsUseCase$invoke$1$invokeSuspend$$inlined$map$1$2 |
0%
(0/1)
|
|
| GetBudgetTransactionsUseCase$invoke$1$invokeSuspend$$inlined$map$1$2$1 |
|
| Total |
50%
(4/8)
|
50%
(1/2)
|
95%
(19/20)
|
94.8%
(91/96)
|
package com.georgeci.moneysurfer.domain.usecase
import com.georgeci.moneysurfer.domain.model.Budget
import com.georgeci.moneysurfer.domain.model.CategorizedTransaction
import com.georgeci.moneysurfer.domain.model.transactionsIn
import com.georgeci.moneysurfer.domain.primitives.ClockUseCase
import com.georgeci.moneysurfer.domain.repositories.TransactionRepository
import com.georgeci.moneysurfer.domain.repositories.WorkspaceRepository
import com.georgeci.moneysurfer.domain.util.BudgetPeriodWindow
import com.georgeci.moneysurfer.domain.util.TransactionPeriodWindow
import com.georgeci.moneysurfer.domain.util.windowContaining
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.TimeZone
import kotlinx.datetime.minus
import kotlinx.datetime.toLocalDateTime
import org.koin.core.annotation.Single
/**
* A budget window holds one period of spend, so this bound is far above any realistic count —
* it exists only because the query is paged, and cuts off the oldest rows if it is ever hit.
*/
private const val WINDOW_TRANSACTION_LIMIT = 1000
/**
* The transactions that make up a budget's spend in its current window — the same filter
* [GetBudgetProgressUseCase] sums, so the list always adds up to the number above it.
*/
@Single
class GetBudgetTransactionsUseCase(
private val transactionRepository: TransactionRepository,
private val workspaceRepository: WorkspaceRepository,
private val clock: ClockUseCase,
) {
operator fun invoke(
budget: Budget,
timeZone: TimeZone = TimeZone.currentSystemDefault(),
): Flow<List<CategorizedTransaction>> = flow {
val baseCurrency = workspaceRepository.getById(budget.workspaceId)?.baseCurrency
val window = budget.windowContaining(clock.now().toLocalDateTime(timeZone).date)
emitAll(
transactionRepository.getCategorizedWindow(
accountId = null,
window = window.asTransactionPeriod(),
limit = WINDOW_TRANSACTION_LIMIT,
).map { categorized ->
// The date range is already applied by the query; the rest of the budget filter
// (workspace, type, status, transfers, categories, currency) is not.
val matching = budget.transactionsIn(
transactions = categorized.map { it.transaction },
window = window,
baseCurrency = baseCurrency,
).map { it.id }.toSet()
categorized.filter { it.transaction.id in matching }
},
)
}
}
/**
* The same span as a transaction-query window: a budget window is half-open, the query one is
* closed, so the last day moves in by one.
*/
private fun BudgetPeriodWindow.asTransactionPeriod(): TransactionPeriodWindow =
TransactionPeriodWindow(
from = startInclusive,
to = endExclusive.minus(1, DateTimeUnit.DAY),
)