Merge pull request 'Implement bankstatement, export, and data interactors (#4)' (#16) from feat/4-implement-bankstatement-export-data into main
Reviewed-on: #16
This commit was merged in pull request #16.
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
package dev.achmad.ledgerr.di
|
||||
|
||||
import dev.achmad.ledgerr.domain.bankstatement.interactor.BankStatementImporter
|
||||
import dev.achmad.ledgerr.domain.bankstatement.interactor.ImportBNIBankStatement
|
||||
import dev.achmad.ledgerr.domain.bankstatement.interactor.ImportBRIBankStatement
|
||||
import dev.achmad.ledgerr.domain.bankstatement.interactor.ImportJagoBankStatement
|
||||
import dev.achmad.ledgerr.domain.category.interactor.DeleteCategory
|
||||
import dev.achmad.ledgerr.domain.category.interactor.GetCategories
|
||||
import dev.achmad.ledgerr.domain.category.interactor.SeedDefaultCategories
|
||||
import dev.achmad.ledgerr.domain.category.interactor.UpsertCategory
|
||||
import dev.achmad.ledgerr.domain.data.interactor.ClearAllData
|
||||
import dev.achmad.ledgerr.domain.export.interactor.ExportExpensesToCsv
|
||||
import dev.achmad.ledgerr.domain.expense.interactor.DeleteExpense
|
||||
import dev.achmad.ledgerr.domain.expense.interactor.GetExpenseSummary
|
||||
import dev.achmad.ledgerr.domain.expense.interactor.GetExpenses
|
||||
@@ -14,6 +20,8 @@ import dev.achmad.ledgerr.domain.recurring.interactor.DeleteRecurringExpense
|
||||
import dev.achmad.ledgerr.domain.recurring.interactor.GetRecurringExpenses
|
||||
import dev.achmad.ledgerr.domain.recurring.interactor.ProcessDueRecurringExpenses
|
||||
import dev.achmad.ledgerr.domain.recurring.interactor.UpsertRecurringExpense
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
|
||||
val domainModule = module {
|
||||
@@ -33,4 +41,15 @@ val domainModule = module {
|
||||
factory { UpsertRecurringExpense(get()) }
|
||||
factory { DeleteRecurringExpense(get()) }
|
||||
factory { ProcessDueRecurringExpenses(get()) }
|
||||
|
||||
factory<BankStatementImporter>(named("bri")) { ImportBRIBankStatement(androidContext()) }
|
||||
factory<BankStatementImporter>(named("jago")) { ImportJagoBankStatement(androidContext()) }
|
||||
factory<BankStatementImporter>(named("bni")) { ImportBNIBankStatement(androidContext()) }
|
||||
factory<List<BankStatementImporter>> {
|
||||
listOf(get(named("bri")), get(named("jago")), get(named("bni")))
|
||||
}
|
||||
|
||||
factory { ExportExpensesToCsv(get(), get(), androidContext()) }
|
||||
|
||||
factory { ClearAllData(get()) }
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package dev.achmad.ledgerr.domain.bankstatement.interactor
|
||||
|
||||
import android.net.Uri
|
||||
import dev.achmad.ledgerr.domain.bankstatement.model.PendingImportExpense
|
||||
|
||||
interface BankStatementImporter {
|
||||
val bankName: String
|
||||
suspend fun await(pdfUri: Uri): Result<List<PendingImportExpense>>
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package dev.achmad.ledgerr.domain.bankstatement.interactor
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import dev.achmad.ledgerr.domain.bankstatement.model.PendingImportExpense
|
||||
|
||||
class ImportBNIBankStatement(
|
||||
@Suppress("unused") private val context: Context,
|
||||
) : BankStatementImporter {
|
||||
override val bankName: String = "BNI"
|
||||
override suspend fun await(pdfUri: Uri): Result<List<PendingImportExpense>> =
|
||||
Result.failure(NotImplementedError("BNI import not yet implemented"))
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package dev.achmad.ledgerr.domain.bankstatement.interactor
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import dev.achmad.ledgerr.domain.bankstatement.model.PendingImportExpense
|
||||
|
||||
class ImportBRIBankStatement(
|
||||
@Suppress("unused") private val context: Context,
|
||||
) : BankStatementImporter {
|
||||
override val bankName: String = "BRI"
|
||||
override suspend fun await(pdfUri: Uri): Result<List<PendingImportExpense>> =
|
||||
Result.failure(NotImplementedError("BRI import not yet implemented"))
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package dev.achmad.ledgerr.domain.bankstatement.interactor
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import dev.achmad.ledgerr.domain.bankstatement.model.PendingImportExpense
|
||||
|
||||
class ImportJagoBankStatement(
|
||||
@Suppress("unused") private val context: Context,
|
||||
) : BankStatementImporter {
|
||||
override val bankName: String = "Jago"
|
||||
override suspend fun await(pdfUri: Uri): Result<List<PendingImportExpense>> =
|
||||
Result.failure(NotImplementedError("Jago import not yet implemented"))
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.achmad.ledgerr.domain.data.interactor
|
||||
|
||||
import androidx.room.withTransaction
|
||||
import dev.achmad.ledgerr.data.local.AppDatabase
|
||||
|
||||
class ClearAllData(
|
||||
private val database: AppDatabase,
|
||||
) {
|
||||
suspend fun await() {
|
||||
database.withTransaction {
|
||||
database.clearAllTables()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package dev.achmad.ledgerr.domain.export.interactor
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import dev.achmad.ledgerr.data.local.dao.CategoryDao
|
||||
import dev.achmad.ledgerr.data.local.dao.ExpenseDao
|
||||
import dev.achmad.ledgerr.data.local.mapper.toModel
|
||||
import dev.achmad.ledgerr.domain.expense.model.DateRange
|
||||
import okio.buffer
|
||||
import okio.sink
|
||||
|
||||
class ExportExpensesToCsv(
|
||||
private val expenseDao: ExpenseDao,
|
||||
private val categoryDao: CategoryDao,
|
||||
private val context: Context,
|
||||
) {
|
||||
suspend fun await(range: DateRange, outputUri: Uri): Result<Unit> = runCatching {
|
||||
val expenses = expenseDao.getByDateRange(
|
||||
startDay = range.start.toEpochDay(),
|
||||
endDay = range.end.toEpochDay(),
|
||||
).map { it.toModel() }
|
||||
val categoryMap = categoryDao.getAll().associate { entity ->
|
||||
entity.id to entity.toModel()
|
||||
}
|
||||
val outputStream = context.contentResolver.openOutputStream(outputUri)
|
||||
?: error("Failed to open output stream for $outputUri")
|
||||
outputStream.sink().buffer().use { sink ->
|
||||
sink.writeUtf8("\uFEFF")
|
||||
sink.writeUtf8("Date,Category,Amount,Note\n")
|
||||
expenses.forEach { expense ->
|
||||
val categoryName = categoryMap[expense.categoryId]?.name.orEmpty()
|
||||
val note = expense.note.orEmpty()
|
||||
sink.writeUtf8("${expense.date},$categoryName,${expense.amount},\"$note\"\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.achmad.ledgerr.ui.base
|
||||
|
||||
import android.app.Application
|
||||
import com.tom_roush.pdfbox.android.PDFBoxResourceLoader
|
||||
import dev.achmad.ledgerr.di.coreModule
|
||||
import dev.achmad.ledgerr.di.dataModule
|
||||
import dev.achmad.ledgerr.di.domainModule
|
||||
@@ -17,6 +18,7 @@ class MainApplication : Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
PDFBoxResourceLoader.init(this)
|
||||
startKoin {
|
||||
androidLogger()
|
||||
androidContext(this@MainApplication)
|
||||
|
||||
Reference in New Issue
Block a user