Implemented updating AccountTransaction properties
This commit is contained in:
parent
bbfc591e5b
commit
18ea0e35f1
|
@ -51,4 +51,6 @@ interface BankingRepository {
|
||||||
|
|
||||||
fun getTransactionById(transactionId: Long): AccountTransactionEntity?
|
fun getTransactionById(transactionId: Long): AccountTransactionEntity?
|
||||||
|
|
||||||
|
suspend fun updateTransaction(transaction: AccountTransactionEntity, userSetOtherPartyName: String?, userSetReference: String?, notes: String?)
|
||||||
|
|
||||||
}
|
}
|
|
@ -88,6 +88,10 @@ class InMemoryBankingRepository(
|
||||||
override fun getTransactionById(transactionId: Long): AccountTransactionEntity? =
|
override fun getTransactionById(transactionId: Long): AccountTransactionEntity? =
|
||||||
getAllAccountTransactions().firstOrNull { it.id == transactionId }
|
getAllAccountTransactions().firstOrNull { it.id == transactionId }
|
||||||
|
|
||||||
|
override suspend fun updateTransaction(transaction: AccountTransactionEntity, userSetOtherPartyName: String?, userSetReference: String?, notes: String?) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun map(bank: BankAccess) = BankAccessEntity(
|
private fun map(bank: BankAccess) = BankAccessEntity(
|
||||||
nextId++,
|
nextId++,
|
||||||
|
|
|
@ -441,6 +441,22 @@ open class SqliteBankingRepository : BankingRepository {
|
||||||
return AccountTransactionEntity(getLastInsertedId(), bankId, accountId, transaction)
|
return AccountTransactionEntity(getLastInsertedId(), bankId, accountId, transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun updateTransaction(transaction: AccountTransactionEntity, userSetOtherPartyName: String?, userSetReference: String?, notes: String?) {
|
||||||
|
accountTransactionQueries.transaction {
|
||||||
|
if (transaction.userSetOtherPartyName != userSetOtherPartyName) {
|
||||||
|
accountTransactionQueries.updateAccountTransactionUserSetOtherPartyName(userSetOtherPartyName, transaction.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction.userSetReference != userSetReference) {
|
||||||
|
accountTransactionQueries.updateAccountTransactionUserSetOReference(userSetReference, transaction.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction.notes != notes) {
|
||||||
|
accountTransactionQueries.updateAccountTransactionNotes(notes, transaction.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun getLastInsertedId(): Long =
|
private fun getLastInsertedId(): Long =
|
||||||
bankQueries.getLastInsertedId().executeAsOne()
|
bankQueries.getLastInsertedId().executeAsOne()
|
||||||
|
|
|
@ -17,8 +17,8 @@ data class AccountTransactionViewModel(
|
||||||
val otherPartyName: String? = null,
|
val otherPartyName: String? = null,
|
||||||
|
|
||||||
val postingText: String? = null,
|
val postingText: String? = null,
|
||||||
val userSetReference: String? = null,
|
var userSetReference: String? = null,
|
||||||
val userSetOtherPartyName: String? = null
|
var userSetOtherPartyName: String? = null
|
||||||
) {
|
) {
|
||||||
constructor(entity: AccountTransactionEntity) : this(entity.id, entity.bankId, entity.accountId, entity)
|
constructor(entity: AccountTransactionEntity) : this(entity.id, entity.bankId, entity.accountId, entity)
|
||||||
|
|
||||||
|
|
|
@ -145,6 +145,22 @@ SELECT AccountTransaction.*
|
||||||
FROM AccountTransaction WHERE id = ?;
|
FROM AccountTransaction WHERE id = ?;
|
||||||
|
|
||||||
|
|
||||||
|
updateAccountTransactionUserSetOtherPartyName:
|
||||||
|
UPDATE AccountTransaction
|
||||||
|
SET userSetOtherPartyName = ?
|
||||||
|
WHERE id = ?;
|
||||||
|
|
||||||
|
updateAccountTransactionUserSetOReference:
|
||||||
|
UPDATE AccountTransaction
|
||||||
|
SET userSetReference = ?
|
||||||
|
WHERE id = ?;
|
||||||
|
|
||||||
|
updateAccountTransactionNotes:
|
||||||
|
UPDATE AccountTransaction
|
||||||
|
SET notes = ?
|
||||||
|
WHERE id = ?;
|
||||||
|
|
||||||
|
|
||||||
deleteTransactionsByBankId {
|
deleteTransactionsByBankId {
|
||||||
DELETE FROM BankAccount
|
DELETE FROM BankAccount
|
||||||
WHERE bankId = :bankId;
|
WHERE bankId = :bankId;
|
||||||
|
|
|
@ -51,11 +51,11 @@ fun TransactionListItem(bank: BankAccess?, transaction: AccountTransactionViewMo
|
||||||
|
|
||||||
DI.uiState.showTransferMoneyDialogData.value = ShowTransferMoneyDialogData(
|
DI.uiState.showTransferMoneyDialogData.value = ShowTransferMoneyDialogData(
|
||||||
DI.uiState.banks.value.firstNotNullOf { it.accounts.firstOrNull { it.id == transaction.accountId } },
|
DI.uiState.banks.value.firstNotNullOf { it.accounts.firstOrNull { it.id == transaction.accountId } },
|
||||||
transaction.otherPartyName,
|
transaction.otherPartyName, // we don't use userSetOtherPartyName here on purpose
|
||||||
transactionEntity?.otherPartyBankId,
|
transactionEntity?.otherPartyBankId,
|
||||||
transactionEntity?.otherPartyAccountId,
|
transactionEntity?.otherPartyAccountId,
|
||||||
if (withSameData) transaction.amount else null,
|
if (withSameData) transaction.amount else null,
|
||||||
if (withSameData) transaction.reference else null
|
if (withSameData) transaction.reference else null // we don't use userSetReference here on purpose
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ fun TransactionListItem(bank: BankAccess?, transaction: AccountTransactionViewMo
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = transaction.otherPartyName ?: transaction.postingText ?: "",
|
text = transaction.userSetOtherPartyName ?: transaction.otherPartyName ?: transaction.postingText ?: "",
|
||||||
Modifier.fillMaxWidth(),
|
Modifier.fillMaxWidth(),
|
||||||
color = Style.ListItemHeaderTextColor,
|
color = Style.ListItemHeaderTextColor,
|
||||||
fontWeight = Style.ListItemHeaderWeight,
|
fontWeight = Style.ListItemHeaderWeight,
|
||||||
|
@ -95,7 +95,7 @@ fun TransactionListItem(bank: BankAccess?, transaction: AccountTransactionViewMo
|
||||||
Spacer(modifier = Modifier.height(6.dp))
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = transaction.reference ?: "",
|
text = transaction.userSetReference ?: transaction.reference ?: "",
|
||||||
Modifier.fillMaxWidth(),
|
Modifier.fillMaxWidth(),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
|
@ -121,7 +121,7 @@ fun TransactionListItem(bank: BankAccess?, transaction: AccountTransactionViewMo
|
||||||
offset = showMenuAt ?: DpOffset.Zero,
|
offset = showMenuAt ?: DpOffset.Zero,
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem({ newMoneyTransferToOtherParty(false) }) {
|
DropdownMenuItem({ newMoneyTransferToOtherParty(false) }) {
|
||||||
Text("Neue Überweisung an ${transaction.otherPartyName} ...")
|
Text("Neue Überweisung an ${transaction.userSetOtherPartyName ?: transaction.otherPartyName} ...") // really use userSetOtherPartyName here as we don't use it in ShowTransferMoneyDialogData
|
||||||
}
|
}
|
||||||
|
|
||||||
DropdownMenuItem({ newMoneyTransferToOtherParty(true) }) {
|
DropdownMenuItem({ newMoneyTransferToOtherParty(true) }) {
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
package net.codinux.banking.ui.screens
|
package net.codinux.banking.ui.screens
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import net.codinux.banking.client.model.isNegative
|
import net.codinux.banking.client.model.isNegative
|
||||||
import net.codinux.banking.persistence.entities.AccountTransactionEntity
|
import net.codinux.banking.persistence.entities.AccountTransactionEntity
|
||||||
import net.codinux.banking.ui.config.DI
|
import net.codinux.banking.ui.config.DI
|
||||||
import net.codinux.banking.ui.extensions.verticalScroll
|
import net.codinux.banking.ui.extensions.verticalScroll
|
||||||
import net.codinux.banking.ui.forms.LabelledValue
|
import net.codinux.banking.ui.forms.LabelledValue
|
||||||
|
import net.codinux.banking.ui.forms.OutlinedTextField
|
||||||
import net.codinux.banking.ui.forms.SectionHeader
|
import net.codinux.banking.ui.forms.SectionHeader
|
||||||
|
|
||||||
private val formatUtil = DI.formatUtil
|
private val formatUtil = DI.formatUtil
|
||||||
|
@ -35,14 +39,50 @@ fun AccountTransactionDetailsScreen(transaction: AccountTransactionEntity, onClo
|
||||||
|| transaction.journalNumber != null || transaction.textKeyAddition != null
|
|| transaction.journalNumber != null || transaction.textKeyAddition != null
|
||||||
|
|
||||||
|
|
||||||
FullscreenViewBase("Umsatzdetails", onClosed = onClosed) {
|
var enteredOtherPartyName by remember { mutableStateOf(transaction.userSetOtherPartyName ?: transaction.otherPartyName ?: "") }
|
||||||
|
|
||||||
|
var enteredReference by remember { mutableStateOf(transaction.userSetReference ?: transaction.reference ?: "") }
|
||||||
|
|
||||||
|
var enteredNotes by remember { mutableStateOf(transaction.notes ?: "") }
|
||||||
|
|
||||||
|
val hasDataChanged by remember(enteredOtherPartyName, enteredReference, enteredNotes) {
|
||||||
|
mutableStateOf(
|
||||||
|
(enteredOtherPartyName != transaction.userSetOtherPartyName && (transaction.userSetOtherPartyName?.isNotBlank() == true || enteredOtherPartyName.isNotBlank()))
|
||||||
|
|| (enteredReference != transaction.userSetReference && (transaction.userSetReference?.isNotBlank() == true || enteredReference.isNotBlank()))
|
||||||
|
|| (enteredNotes != transaction.notes && enteredNotes.isNotBlank())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
|
||||||
|
fun saveChanges() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
DI.bankingService.updateAccountTransactionEntity(transaction, enteredOtherPartyName.takeUnless { it.isBlank() }, enteredReference.takeUnless { it.isBlank() }, enteredNotes.takeUnless { it.isBlank() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FullscreenViewBase(
|
||||||
|
"Umsatzdetails",
|
||||||
|
confirmButtonTitle = "Speichern",
|
||||||
|
confirmButtonEnabled = hasDataChanged,
|
||||||
|
showDismissButton = true,
|
||||||
|
onConfirm = { saveChanges() },
|
||||||
|
onClosed = onClosed
|
||||||
|
) {
|
||||||
SelectionContainer {
|
SelectionContainer {
|
||||||
Column(Modifier.fillMaxSize().verticalScroll().padding(8.dp)) {
|
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(8.dp)) {
|
||||||
|
|
||||||
Column(Modifier.fillMaxWidth()) {
|
Column(Modifier.fillMaxWidth()) {
|
||||||
SectionHeader(if (isExpense) "Empfänger*in" else "Zahlende*r", false)
|
SectionHeader(if (isExpense) "Empfänger*in" else "Zahlende*r", false)
|
||||||
|
|
||||||
LabelledValue("Name", transaction.otherPartyName ?: "")
|
OutlinedTextField(
|
||||||
|
label = { Text("Name") },
|
||||||
|
value = enteredOtherPartyName,
|
||||||
|
onValueChange = { enteredOtherPartyName = it },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
LabelledValue("BIC", transaction.otherPartyBankId ?: "")
|
LabelledValue("BIC", transaction.otherPartyBankId ?: "")
|
||||||
|
|
||||||
|
@ -57,7 +97,12 @@ fun AccountTransactionDetailsScreen(transaction: AccountTransactionEntity, onClo
|
||||||
|
|
||||||
LabelledValue("Buchungstext", transaction.postingText ?: "")
|
LabelledValue("Buchungstext", transaction.postingText ?: "")
|
||||||
|
|
||||||
LabelledValue("Verwendungszweck", transaction.reference ?: "")
|
OutlinedTextField(
|
||||||
|
label = { Text("Verwendungszweck") },
|
||||||
|
value = enteredReference,
|
||||||
|
onValueChange = { enteredReference = it },
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
|
||||||
|
)
|
||||||
|
|
||||||
LabelledValue("Buchungsdatum", formatUtil.formatDate(transaction.bookingDate))
|
LabelledValue("Buchungsdatum", formatUtil.formatDate(transaction.bookingDate))
|
||||||
|
|
||||||
|
@ -70,6 +115,16 @@ fun AccountTransactionDetailsScreen(transaction: AccountTransactionEntity, onClo
|
||||||
transaction.closingBalance?.let {
|
transaction.closingBalance?.let {
|
||||||
LabelledValue("Tagesendsaldo", formatUtil.formatAmount(it, accountCurrency))
|
LabelledValue("Tagesendsaldo", formatUtil.formatAmount(it, accountCurrency))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text("Notizen") },
|
||||||
|
value = enteredNotes,
|
||||||
|
onValueChange = { enteredNotes = it },
|
||||||
|
singleLine = false,
|
||||||
|
minLines = 2,
|
||||||
|
maxLines = 3,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasDetailedValues) {
|
if (hasDetailedValues) {
|
||||||
|
|
|
@ -46,7 +46,10 @@ class AccountTransactionsFilterService {
|
||||||
|
|
||||||
private fun matchesSearchTerm(transaction: AccountTransactionViewModel, searchTerm: String): Boolean =
|
private fun matchesSearchTerm(transaction: AccountTransactionViewModel, searchTerm: String): Boolean =
|
||||||
transaction.reference?.contains(searchTerm, true) == true
|
transaction.reference?.contains(searchTerm, true) == true
|
||||||
|
|| transaction.userSetReference?.contains(searchTerm, true) == true
|
||||||
|| transaction.otherPartyName?.contains(searchTerm, true) == true
|
|| transaction.otherPartyName?.contains(searchTerm, true) == true
|
||||||
|
|| transaction.userSetOtherPartyName?.contains(searchTerm, true) == true
|
||||||
|
|| transaction.postingText?.contains(searchTerm, true) == true
|
||||||
|
|
||||||
|
|
||||||
fun filterHoldings(holdings: List<HoldingEntity>, filter: AccountTransactionsFilter): List<HoldingEntity> {
|
fun filterHoldings(holdings: List<HoldingEntity>, filter: AccountTransactionsFilter): List<HoldingEntity> {
|
||||||
|
|
|
@ -19,6 +19,7 @@ class BankDataImporterAndExporter {
|
||||||
transactions.forEach { transaction ->
|
transactions.forEach { transaction ->
|
||||||
writer.writeRow(
|
writer.writeRow(
|
||||||
// TODO: add bank and bank account
|
// TODO: add bank and bank account
|
||||||
|
// TODO: also regard userSetOtherPartyName and userSetReference?
|
||||||
formatAmount(transaction.amount, decimalSeparator), transaction.currency,
|
formatAmount(transaction.amount, decimalSeparator), transaction.currency,
|
||||||
transaction.valueDate.toString(), transaction.bookingDate.toString(),
|
transaction.valueDate.toString(), transaction.bookingDate.toString(),
|
||||||
transaction.reference,
|
transaction.reference,
|
||||||
|
|
|
@ -303,6 +303,31 @@ class BankingService(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun updateAccountTransactionEntity(transaction: AccountTransactionEntity, userSetOtherPartyName: String?, userSetReference: String?, notes: String?) {
|
||||||
|
try {
|
||||||
|
bankingRepository.updateTransaction(transaction, userSetOtherPartyName, userSetReference, notes)
|
||||||
|
|
||||||
|
val transactionViewModel = uiState.transactions.value.firstOrNull { it.id == transaction.id } // should actually never be null
|
||||||
|
|
||||||
|
if (transaction.userSetOtherPartyName != userSetOtherPartyName) {
|
||||||
|
transaction.userSetOtherPartyName = userSetOtherPartyName
|
||||||
|
transactionViewModel?.userSetOtherPartyName = userSetOtherPartyName // also update displayed AccountTransactionViewModel
|
||||||
|
}
|
||||||
|
if (transaction.userSetReference != userSetReference) {
|
||||||
|
transaction.userSetReference = userSetReference
|
||||||
|
transactionViewModel?.userSetReference = userSetReference
|
||||||
|
}
|
||||||
|
if (transaction.notes != notes) {
|
||||||
|
transaction.notes = notes
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyAccountTransactionListUpdated()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
showAndLogError(ErroneousAction.SaveToDatabase, "Could not update account transaction $transaction", "Kontoumsatz konnten nicht aktualisisert werden", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun getCurrentUiBanksList() = uiState.banks.value
|
private fun getCurrentUiBanksList() = uiState.banks.value
|
||||||
|
|
||||||
private suspend fun notifyBanksListUpdated() {
|
private suspend fun notifyBanksListUpdated() {
|
||||||
|
@ -321,6 +346,18 @@ class BankingService(
|
||||||
uiState.banks.emit(banks)
|
uiState.banks.emit(banks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun notifyAccountTransactionListUpdated() {
|
||||||
|
val currentTransactionsList = uiState.transactions.value
|
||||||
|
|
||||||
|
if (currentTransactionsList.isNotEmpty()) {
|
||||||
|
// if we only would call uiState.banks.emit(banks) with the same banks list as currently, nothing would change ->
|
||||||
|
// update does not get triggered -> for a short time display a different banks list and then return to actual banks list
|
||||||
|
uiState.transactions.emit(currentTransactionsList.toMutableList().also { it.add(it.last()) })
|
||||||
|
try { delay(10) } catch (e: Throwable) { }
|
||||||
|
uiState.transactions.emit(currentTransactionsList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateTransactionsInUi(addedTransactions: List<AccountTransactionEntity>): List<AccountTransactionViewModel> {
|
private fun updateTransactionsInUi(addedTransactions: List<AccountTransactionEntity>): List<AccountTransactionViewModel> {
|
||||||
val transactionsViewModel = addedTransactions.map { AccountTransactionViewModel(it) }
|
val transactionsViewModel = addedTransactions.map { AccountTransactionViewModel(it) }
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ class RecipientFinder(private val bankFinder: BankFinder) {
|
||||||
|
|
||||||
suspend fun updateData(transactions: List<AccountTransaction>) {
|
suspend fun updateData(transactions: List<AccountTransaction>) {
|
||||||
availableRecipients = transactions.mapNotNull {
|
availableRecipients = transactions.mapNotNull {
|
||||||
|
// TODO: also regard userSetOtherPartyName?
|
||||||
if (it.otherPartyName != null && it.otherPartyAccountId != null) {
|
if (it.otherPartyName != null && it.otherPartyAccountId != null) {
|
||||||
RecipientSuggestion(it.otherPartyName!!, it.otherPartyBankId, it.otherPartyAccountId!!)
|
RecipientSuggestion(it.otherPartyName!!, it.otherPartyBankId, it.otherPartyAccountId!!)
|
||||||
} else {
|
} else {
|
||||||
|
@ -45,6 +46,7 @@ class RecipientFinder(private val bankFinder: BankFinder) {
|
||||||
|
|
||||||
transactionsByIban = transactions.filter { it.otherPartyAccountId != null }.groupBy { it.otherPartyAccountId!! }
|
transactionsByIban = transactions.filter { it.otherPartyAccountId != null }.groupBy { it.otherPartyAccountId!! }
|
||||||
.mapValues { it.value.map {
|
.mapValues { it.value.map {
|
||||||
|
// TODO: also regard userSetReference?
|
||||||
PaymentDataSuggestion(it.reference ?: "", Amount(it.amount.toString().replace("-", "")), it.currency, it.valueDate)
|
PaymentDataSuggestion(it.reference ?: "", Amount(it.amount.toString().replace("-", "")), it.currency, it.valueDate)
|
||||||
}.toSet().sortedByDescending { it.valueDate } }
|
}.toSet().sortedByDescending { it.valueDate } }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue