From 63bfe2a6b5eb15dc8067efa878a13a5c4c791533 Mon Sep 17 00:00:00 2001 From: Achmad Setyabudi Susilo Date: Sun, 28 Jun 2026 16:52:42 +0700 Subject: [PATCH] =?UTF-8?q?fix(#2):=20address=20PR=20review=20=E2=80=94=20?= =?UTF-8?q?use=20row=20mapper,=20require=20id=3D=3D0,=20drop=20ignoreCase?= =?UTF-8?q?=20on=20amount,=20doc=20drift,=20orphan-category=20note?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/expense/interactor/GetExpenseSummary.kt | 3 +++ .../ledgerr/domain/expense/interactor/GetExpenses.kt | 11 ++++++----- .../domain/expense/interactor/InsertExpenses.kt | 3 +++ docs/03-function-todos.md | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/GetExpenseSummary.kt b/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/GetExpenseSummary.kt index fb22cd7..d502e3f 100644 --- a/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/GetExpenseSummary.kt +++ b/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/GetExpenseSummary.kt @@ -17,6 +17,9 @@ class GetExpenseSummary( ) val totalAmount = expenses.sumOf { it.amount } val categoryMap = categoryDao.getAll().associate { it.id to it.toModel() } + // ForeignKey.RESTRICT keeps categories intact, but be defensive: if a + // category was deleted out-of-band, drop its group from byCategory + // rather than crashing. totalAmount still includes the orphan rows. val byCategory = expenses .groupBy { it.categoryId } .mapNotNull { (categoryId, group) -> diff --git a/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/GetExpenses.kt b/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/GetExpenses.kt index 0781b5f..02a1a5e 100644 --- a/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/GetExpenses.kt +++ b/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/GetExpenses.kt @@ -2,6 +2,7 @@ package dev.achmad.ledgerr.domain.expense.interactor import dev.achmad.ledgerr.data.local.dao.CategoryDao import dev.achmad.ledgerr.data.local.dao.ExpenseDao +import dev.achmad.ledgerr.data.local.dao.ExpenseWithCategoryRow import dev.achmad.ledgerr.data.local.mapper.toModel import dev.achmad.ledgerr.domain.expense.model.DateRange import dev.achmad.ledgerr.domain.expense.model.ExpenseWithCategory @@ -24,10 +25,10 @@ class GetExpenses( suspend fun awaitOne(id: Long): ExpenseWithCategory? { val expense = dao.getById(id) ?: return null val category = categoryDao.getById(expense.categoryId) ?: return null - return ExpenseWithCategory( - expense = expense.toModel(), - category = category.toModel(), - ) + return ExpenseWithCategoryRow( + expense = expense, + category = category, + ).toModel() } suspend fun awaitAll( @@ -46,7 +47,7 @@ class GetExpenses( val amountStr = entity.amount.toString() val categoryName = categoryMap[entity.categoryId]?.name.orEmpty() note.contains(query, ignoreCase = true) || - amountStr.contains(query, ignoreCase = true) || + amountStr.contains(query) || categoryName.contains(query, ignoreCase = true) } .mapNotNull { entity -> diff --git a/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/InsertExpenses.kt b/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/InsertExpenses.kt index 3c82eaa..3f7a9c5 100644 --- a/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/InsertExpenses.kt +++ b/app/src/main/java/dev/achmad/ledgerr/domain/expense/interactor/InsertExpenses.kt @@ -9,6 +9,9 @@ class InsertExpenses( ) { suspend fun awaitAll(expenses: List) { if (expenses.isEmpty()) return + require(expenses.all { it.id == 0L }) { + "InsertExpenses.awaitAll requires all Expense.id == 0L (got ${expenses.filter { it.id != 0L }.map { it.id }})" + } dao.insertAll(expenses.map { it.toEntity() }) } } diff --git a/docs/03-function-todos.md b/docs/03-function-todos.md index 43c21a2..0ee9b98 100644 --- a/docs/03-function-todos.md +++ b/docs/03-function-todos.md @@ -23,7 +23,7 @@ One-shot search. If `query` is blank and `range` is null, returns all expenses ( ## expense / UpsertExpense ### `await(expense: Expense): Long` -If `expense.id == 0L`: insert and return generated id. Otherwise: update by id and return the existing id. Uses Room `@Upsert`. +If `expense.id == 0L`: insert and return generated id. Otherwise: update by id and return the existing id. Routes through separate `dao.insert` / `dao.update` (the DAO does not expose a `@Upsert` method). ---