Coverage Summary for Class: CategorySpendRepositoryImpl (com.georgeci.moneysurfer.data.repository)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| CategorySpendRepositoryImpl |
100%
(3/3)
|
62.5%
(5/8)
|
100%
(17/17)
|
93.7%
(89/95)
|
| CategorySpendRepositoryImpl$monthlyTotals-Bjg8v0k$$inlined$map$1 |
0%
(0/2)
|
|
| CategorySpendRepositoryImpl$monthlyTotals-Bjg8v0k$$inlined$map$1$1 |
|
| CategorySpendRepositoryImpl$monthlyTotals-Bjg8v0k$$inlined$map$1$2 |
0%
(0/1)
|
|
| CategorySpendRepositoryImpl$monthlyTotals-Bjg8v0k$$inlined$map$1$2$1 |
|
| Total |
50%
(3/6)
|
62.5%
(5/8)
|
100%
(17/17)
|
93.7%
(89/95)
|
package com.georgeci.moneysurfer.data.repository
import com.georgeci.moneysurfer.data.db.dao.TransactionDao
import com.georgeci.moneysurfer.data.db.entity.CategoryMonthlyTotalEntity
import com.georgeci.moneysurfer.domain.model.CategoryMonthlyTotal
import com.georgeci.moneysurfer.domain.model.next
import com.georgeci.moneysurfer.domain.primitives.CategoryId
import com.georgeci.moneysurfer.domain.primitives.CurrencyCode
import com.georgeci.moneysurfer.domain.primitives.Money
import com.georgeci.moneysurfer.domain.primitives.TransactionType
import com.georgeci.moneysurfer.domain.primitives.WorkspaceId
import com.georgeci.moneysurfer.domain.repositories.CategorySpendRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.datetime.YearMonth
import org.koin.core.annotation.Single
@Single(binds = [CategorySpendRepository::class])
class CategorySpendRepositoryImpl(
private val dao: TransactionDao,
) : CategorySpendRepository {
override fun monthlyTotals(
workspaceId: WorkspaceId,
categoryIds: List<CategoryId>,
type: TransactionType,
baseCurrency: CurrencyCode?,
fromMonth: YearMonth,
toMonth: YearMonth,
): Flow<List<CategoryMonthlyTotal>> {
// `IN ()` is a syntax error in SQLite, and an empty subtree is a normal state — a
// category can be deleted out from under an open detail screen.
if (categoryIds.isEmpty()) return flowOf(emptyList())
return dao.getMonthlyTotalsByCategory(
workspaceId = workspaceId.value,
categoryIds = categoryIds.map { it.value },
type = type.name,
baseCurrency = baseCurrency?.value,
fromDate = fromMonth.firstDay.toString(),
toDateExclusive = toMonth.next().firstDay.toString(),
).map { rows -> rows.mapNotNull { it.toDomain() } }
}
/**
* Null for a row whose `month` will not parse. `substr` cannot produce one from a valid
* `operationDate`, and since the query joined the shared spend predicate it cannot see an
* invalid one either — `operationDate = date(operationDate)` admits only canonical dates. Kept
* as a second line because the column is a plain string one predicate change away from letting
* something else through, and dropping one unreadable month beats failing the whole screen.
*/
private fun CategoryMonthlyTotalEntity.toDomain(): CategoryMonthlyTotal? {
val parsed = runCatching { YearMonth.parse(month) }.getOrNull() ?: return null
return CategoryMonthlyTotal(
categoryId = CategoryId(categoryId),
month = parsed,
total = Money.fromMinor(totalMinor),
transactionCount = transactionCount,
)
}
}