Started UI specific model to get independent of underlying FinTS- / banking client implementation
This commit is contained in:
parent
913d85d4a8
commit
f4194f832e
|
@ -0,0 +1,34 @@
|
||||||
|
package net.dankito.banking.ui.model
|
||||||
|
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
|
||||||
|
open class Account(
|
||||||
|
val bank: Bank,
|
||||||
|
val customerId: String,
|
||||||
|
var pin: String,
|
||||||
|
var name: String,
|
||||||
|
var userId: String = customerId,
|
||||||
|
var bankAccounts: List<BankAccount> = listOf()
|
||||||
|
) {
|
||||||
|
|
||||||
|
internal constructor() : this(Bank(), "", "", "") // for object deserializers
|
||||||
|
|
||||||
|
|
||||||
|
var supportedTanProcedures: List<TanProcedure> = listOf()
|
||||||
|
|
||||||
|
var selectedTanProcedure: TanProcedure? = null
|
||||||
|
|
||||||
|
|
||||||
|
val balance: BigDecimal
|
||||||
|
get() = bankAccounts.map { it.balance }.fold(BigDecimal.ZERO) { acc, e -> acc + e }
|
||||||
|
|
||||||
|
val transactions: List<AccountTransaction>
|
||||||
|
get() = bankAccounts.flatMap { it.transactions }
|
||||||
|
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$name ($customerId)"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package net.dankito.banking.ui.model
|
||||||
|
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
open class AccountTransaction(
|
||||||
|
val amount: BigDecimal,
|
||||||
|
val currency: String,
|
||||||
|
val usage: String,
|
||||||
|
val bookingDate: Date,
|
||||||
|
val otherPartyName: String?,
|
||||||
|
val otherPartyBankCode: String?,
|
||||||
|
val otherPartyAccountId: String?,
|
||||||
|
val bookingText: String?,
|
||||||
|
val bankAccount: BankAccount
|
||||||
|
) {
|
||||||
|
|
||||||
|
// for object deserializers
|
||||||
|
internal constructor() : this(0.toBigDecimal(),"", "", Date(), null, null, null, null, BankAccount())
|
||||||
|
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "${DateFormat.getDateInstance(DateFormat.MEDIUM).format(bookingDate)} $amount $otherPartyName: $usage"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package net.dankito.banking.ui.model
|
||||||
|
|
||||||
|
|
||||||
|
open class Bank(
|
||||||
|
val bankCode: String,
|
||||||
|
var finTsServerAddress: String,
|
||||||
|
var bic: String,
|
||||||
|
var name: String
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
internal constructor() : this("", "", "", "") // for object deserializers
|
||||||
|
|
||||||
|
|
||||||
|
var iconUrl: String? = null
|
||||||
|
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$name ($bankCode)"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package net.dankito.banking.ui.model
|
||||||
|
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
|
||||||
|
open class BankAccount @JvmOverloads constructor(
|
||||||
|
val account: Account,
|
||||||
|
val identifier: String,
|
||||||
|
var name: String,
|
||||||
|
var iban: String?,
|
||||||
|
var subAccountNumber: String?,
|
||||||
|
var balance: BigDecimal = BigDecimal.ZERO,
|
||||||
|
var currency: String = "EUR",
|
||||||
|
var type: BankAccountType = BankAccountType.Giro,
|
||||||
|
accountTransactions: List<AccountTransaction> = listOf()
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
internal constructor() : this(Account(), "", "", null, null) // for object deserializers
|
||||||
|
|
||||||
|
|
||||||
|
var transactions: List<AccountTransaction> = accountTransactions
|
||||||
|
protected set
|
||||||
|
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$name ($identifier)"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package net.dankito.banking.ui.model
|
||||||
|
|
||||||
|
|
||||||
|
enum class BankAccountType {
|
||||||
|
|
||||||
|
Giro
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package net.dankito.banking.ui.model
|
||||||
|
|
||||||
|
|
||||||
|
open class TanProcedure(
|
||||||
|
val displayName: String,
|
||||||
|
val type: TanProcedureType,
|
||||||
|
val bankInternalProcedureCode: String
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$displayName ($type, ${bankInternalProcedureCode})"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.dankito.banking.ui.model
|
||||||
|
|
||||||
|
|
||||||
|
enum class TanProcedureType {
|
||||||
|
|
||||||
|
EnterTan,
|
||||||
|
|
||||||
|
ChipTanManuell,
|
||||||
|
|
||||||
|
ChipTanOptisch,
|
||||||
|
|
||||||
|
ChipTanQrCode,
|
||||||
|
|
||||||
|
PhotoTan,
|
||||||
|
|
||||||
|
SmsTan,
|
||||||
|
|
||||||
|
PushTan
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package net.dankito.banking.ui.model.responses
|
||||||
|
|
||||||
|
import net.dankito.banking.ui.model.Account
|
||||||
|
import net.dankito.banking.ui.model.AccountTransaction
|
||||||
|
import net.dankito.banking.ui.model.BankAccount
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
|
||||||
|
open class AddAccountResponse(
|
||||||
|
isSuccessful: Boolean,
|
||||||
|
errorToShowToUser: String?,
|
||||||
|
val account: Account,
|
||||||
|
val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean = false,
|
||||||
|
bookedTransactionsOfLast90Days: Map<BankAccount, List<AccountTransaction>> = mapOf(),
|
||||||
|
unbookedTransactionsOfLast90Days: Map<BankAccount, List<Any>> = mapOf(),
|
||||||
|
balances: Map<BankAccount, BigDecimal> = mapOf()
|
||||||
|
)
|
||||||
|
: GetTransactionsResponse(isSuccessful, errorToShowToUser, bookedTransactionsOfLast90Days, unbookedTransactionsOfLast90Days, balances) {
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return account.toString() + " " + super.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.dankito.banking.ui.model.responses
|
||||||
|
|
||||||
|
|
||||||
|
open class BankingClientResponse(
|
||||||
|
val isSuccessful: Boolean,
|
||||||
|
val errorToShowToUser: String?
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return if (isSuccessful) {
|
||||||
|
"Successful"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Error: $errorToShowToUser"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package net.dankito.banking.ui.model.responses
|
||||||
|
|
||||||
|
import net.dankito.banking.ui.model.AccountTransaction
|
||||||
|
import net.dankito.banking.ui.model.BankAccount
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
|
||||||
|
open class GetTransactionsResponse(
|
||||||
|
isSuccessful: Boolean,
|
||||||
|
errorToShowToUser: String?,
|
||||||
|
val bookedTransactions: Map<BankAccount, List<AccountTransaction>> = mapOf(),
|
||||||
|
val unbookedTransactions: Map<BankAccount, List<Any>> = mapOf(),
|
||||||
|
val balances: Map<BankAccount, BigDecimal> = mapOf()
|
||||||
|
)
|
||||||
|
: BankingClientResponse(isSuccessful, errorToShowToUser)
|
|
@ -0,0 +1,141 @@
|
||||||
|
package net.dankito.banking.fints4java.android.mapper
|
||||||
|
|
||||||
|
import net.dankito.banking.ui.model.*
|
||||||
|
import net.dankito.banking.ui.model.responses.AddAccountResponse
|
||||||
|
import net.dankito.banking.ui.model.responses.GetTransactionsResponse
|
||||||
|
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
|
||||||
|
import net.dankito.fints.model.AccountData
|
||||||
|
import net.dankito.fints.model.BankData
|
||||||
|
import net.dankito.fints.model.CustomerData
|
||||||
|
import net.dankito.fints.response.client.FinTsClientResponse
|
||||||
|
import net.dankito.fints.response.segments.AccountType
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
|
||||||
|
open class fints4javaModelMapper {
|
||||||
|
|
||||||
|
open fun mapAccount(customer: CustomerData, bank: BankData): Account {
|
||||||
|
val mappedBank = mapBank(bank)
|
||||||
|
|
||||||
|
val account = Account(mappedBank, customer.customerId, customer.pin, customer.name, customer.userId)
|
||||||
|
|
||||||
|
account.bankAccounts = mapBankAccounts(account, customer.accounts)
|
||||||
|
account.supportedTanProcedures = mapTanProcedures(customer.supportedTanProcedures)
|
||||||
|
|
||||||
|
return account
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapBank(bank: BankData): Bank {
|
||||||
|
return Bank(bank.bankCode, bank.finTs3ServerAddress, bank.bic, bank.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapBankAccounts(account: Account, accountData: List<AccountData>): List<BankAccount> {
|
||||||
|
return accountData.map { mapBankAccount(account, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapBankAccount(account: Account, accountData: AccountData): BankAccount {
|
||||||
|
return BankAccount(account, accountData.accountIdentifier, accountData.accountHolderName, accountData.iban,
|
||||||
|
accountData.subAccountAttribute, BigDecimal.ZERO, accountData.currency ?: "EUR",
|
||||||
|
mapBankAccountType(accountData.accountType))
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapBankAccountType(type: AccountType?): BankAccountType {
|
||||||
|
return when (type) {
|
||||||
|
|
||||||
|
else -> BankAccountType.Giro
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapTransactions(bankAccount: BankAccount, transactions: List<net.dankito.fints.model.AccountTransaction>): List<AccountTransaction> {
|
||||||
|
return transactions.map { mapTransaction(bankAccount, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapTransaction(bankAccount: BankAccount, transaction: net.dankito.fints.model.AccountTransaction): AccountTransaction {
|
||||||
|
return AccountTransaction(
|
||||||
|
transaction.amount,
|
||||||
|
transaction.currency,
|
||||||
|
transaction.usage,
|
||||||
|
transaction.bookingDate,
|
||||||
|
transaction.otherPartyName,
|
||||||
|
transaction.otherPartyBankCode,
|
||||||
|
transaction.otherPartyAccountId,
|
||||||
|
transaction.bookingText,
|
||||||
|
bankAccount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapTanProcedures(tanProcedures: List<net.dankito.fints.model.TanProcedure>): List<TanProcedure> {
|
||||||
|
return tanProcedures.map { mapTanProcedure(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapTanProcedure(tanProcedure: net.dankito.fints.model.TanProcedure): TanProcedure {
|
||||||
|
return TanProcedure(
|
||||||
|
tanProcedure.displayName,
|
||||||
|
mapTanProcedureType(tanProcedure.type),
|
||||||
|
tanProcedure.securityFunction.code
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapTanProcedureType(type: net.dankito.fints.model.TanProcedureType): TanProcedureType {
|
||||||
|
return when (type) {
|
||||||
|
net.dankito.fints.model.TanProcedureType.EnterTan -> TanProcedureType.EnterTan // TODO: add ChipTanManuell
|
||||||
|
net.dankito.fints.model.TanProcedureType.ChipTan -> TanProcedureType.ChipTanOptisch
|
||||||
|
net.dankito.fints.model.TanProcedureType.ChipTanQrCode -> TanProcedureType.ChipTanQrCode
|
||||||
|
net.dankito.fints.model.TanProcedureType.PhotoTan -> TanProcedureType.PhotoTan
|
||||||
|
net.dankito.fints.model.TanProcedureType.SmsTan -> TanProcedureType.SmsTan
|
||||||
|
net.dankito.fints.model.TanProcedureType.PushTan -> TanProcedureType.PushTan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun mapResponse(account: Account, response: net.dankito.fints.response.client.AddAccountResponse): AddAccountResponse {
|
||||||
|
var bookedTransactions = mapOf<BankAccount, List<AccountTransaction>>()
|
||||||
|
var balances = mapOf<BankAccount, BigDecimal>()
|
||||||
|
|
||||||
|
account.bankAccounts.firstOrNull()?.let { bankAccount -> // TODO: set bank account also on net.dankito.fints.response.client.GetTransactionsResponse
|
||||||
|
bookedTransactions = mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions))
|
||||||
|
response.balance?.let { balances = mapOf(bankAccount to it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return AddAccountResponse(response.isSuccessful, mapErrorToShowToUser(response),
|
||||||
|
account, response.supportsRetrievingTransactionsOfLast90DaysWithoutTan,
|
||||||
|
bookedTransactions,
|
||||||
|
mapOf(), // TODO: map unbooked transactions
|
||||||
|
balances)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mapResponse(account: Account, response: net.dankito.fints.response.client.GetTransactionsResponse): GetTransactionsResponse {
|
||||||
|
val bankAccount = account.bankAccounts.first() // TODO: set bank account also on net.dankito.fints.response.client.GetTransactionsResponse
|
||||||
|
|
||||||
|
return GetTransactionsResponse(response.isSuccessful, mapErrorToShowToUser(response),
|
||||||
|
mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions)),
|
||||||
|
mapOf(), // TODO: map unbooked transactions
|
||||||
|
response.balance?.let { mapOf(bankAccount to it) } ?: mapOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapErrorToShowToUser(response: FinTsClientResponse): String? {
|
||||||
|
return response.exception?.localizedMessage ?: response.errorsToShowToUser.joinToString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open fun mapTanProcedureBack(tanProcedure: TanProcedure): net.dankito.fints.model.TanProcedure {
|
||||||
|
return net.dankito.fints.model.TanProcedure(
|
||||||
|
tanProcedure.displayName,
|
||||||
|
Sicherheitsfunktion.values().first { it.code == tanProcedure.bankInternalProcedureCode },
|
||||||
|
mapTanProcedureTypeBack(tanProcedure.type)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun mapTanProcedureTypeBack(type: TanProcedureType): net.dankito.fints.model.TanProcedureType {
|
||||||
|
return when (type) {
|
||||||
|
TanProcedureType.EnterTan -> net.dankito.fints.model.TanProcedureType.EnterTan
|
||||||
|
TanProcedureType.ChipTanManuell -> net.dankito.fints.model.TanProcedureType.EnterTan // TODO: add ChipTanManuell
|
||||||
|
TanProcedureType.ChipTanOptisch -> net.dankito.fints.model.TanProcedureType.ChipTan
|
||||||
|
TanProcedureType.ChipTanQrCode -> net.dankito.fints.model.TanProcedureType.ChipTanQrCode
|
||||||
|
TanProcedureType.PhotoTan -> net.dankito.fints.model.TanProcedureType.PhotoTan
|
||||||
|
TanProcedureType.SmsTan -> net.dankito.fints.model.TanProcedureType.SmsTan
|
||||||
|
TanProcedureType.PushTan -> net.dankito.fints.model.TanProcedureType.PushTan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,14 +1,17 @@
|
||||||
package net.dankito.banking.fints4java.android.ui
|
package net.dankito.banking.fints4java.android.ui
|
||||||
|
|
||||||
import net.dankito.banking.fints4java.android.Base64ServiceAndroid
|
import net.dankito.banking.fints4java.android.Base64ServiceAndroid
|
||||||
|
import net.dankito.banking.ui.model.Account
|
||||||
|
import net.dankito.banking.ui.model.AccountTransaction
|
||||||
|
import net.dankito.banking.ui.model.BankAccount
|
||||||
|
import net.dankito.banking.ui.model.responses.AddAccountResponse
|
||||||
|
import net.dankito.banking.ui.model.responses.GetTransactionsResponse
|
||||||
import net.dankito.fints.FinTsClient
|
import net.dankito.fints.FinTsClient
|
||||||
import net.dankito.fints.FinTsClientCallback
|
import net.dankito.fints.FinTsClientCallback
|
||||||
import net.dankito.fints.banks.BankFinder
|
import net.dankito.fints.banks.BankFinder
|
||||||
import net.dankito.fints.model.*
|
import net.dankito.fints.model.*
|
||||||
import net.dankito.fints.model.mapper.BankDataMapper
|
import net.dankito.fints.model.mapper.BankDataMapper
|
||||||
import net.dankito.fints.response.client.AddAccountResponse
|
|
||||||
import net.dankito.fints.response.client.FinTsClientResponse
|
import net.dankito.fints.response.client.FinTsClientResponse
|
||||||
import net.dankito.fints.response.client.GetTransactionsResponse
|
|
||||||
import net.dankito.utils.IThreadPool
|
import net.dankito.utils.IThreadPool
|
||||||
import net.dankito.utils.ThreadPool
|
import net.dankito.utils.ThreadPool
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
@ -26,18 +29,20 @@ open class MainWindowPresenter(callback: FinTsClientCallback) {
|
||||||
|
|
||||||
protected val bankDataMapper = BankDataMapper()
|
protected val bankDataMapper = BankDataMapper()
|
||||||
|
|
||||||
|
protected val fints4javaModelMapper = net.dankito.banking.fints4java.android.mapper.fints4javaModelMapper()
|
||||||
|
|
||||||
protected val accounts = mutableMapOf<CustomerData, BankData>()
|
|
||||||
|
|
||||||
protected val bookedTransactions = mutableMapOf<CustomerData, MutableSet<AccountTransaction>>()
|
protected val accounts = mutableMapOf<Account, Any>()
|
||||||
|
|
||||||
protected val unbookedTransactions = mutableMapOf<CustomerData, MutableSet<Any>>()
|
protected val bookedTransactions = mutableMapOf<BankAccount, MutableSet<AccountTransaction>>()
|
||||||
|
|
||||||
protected val balances = mutableMapOf<CustomerData, BigDecimal>()
|
protected val unbookedTransactions = mutableMapOf<BankAccount, MutableSet<Any>>()
|
||||||
|
|
||||||
protected val accountAddedListeners = mutableListOf<(BankData, CustomerData) -> Unit>()
|
protected val balances = mutableMapOf<BankAccount, BigDecimal>()
|
||||||
|
|
||||||
protected val retrievedAccountTransactionsResponseListeners = mutableListOf<(CustomerData, GetTransactionsResponse) -> Unit>()
|
protected val accountAddedListeners = mutableListOf<(Account) -> Unit>()
|
||||||
|
|
||||||
|
protected val retrievedAccountTransactionsResponseListeners = mutableListOf<(BankAccount, GetTransactionsResponse) -> Unit>()
|
||||||
|
|
||||||
|
|
||||||
open fun addAccountAsync(bankInfo: BankInfo, customerId: String, pin: String,
|
open fun addAccountAsync(bankInfo: BankInfo, customerId: String, pin: String,
|
||||||
|
@ -47,81 +52,106 @@ open class MainWindowPresenter(callback: FinTsClientCallback) {
|
||||||
val customer = CustomerData(customerId, pin)
|
val customer = CustomerData(customerId, pin)
|
||||||
|
|
||||||
finTsClient.addAccountAsync(bank, customer) { response ->
|
finTsClient.addAccountAsync(bank, customer) { response ->
|
||||||
if (response.isSuccessful) {
|
val account = fints4javaModelMapper.mapAccount(customer, bank)
|
||||||
accounts.put(customer, bank)
|
val mappedResponse = fints4javaModelMapper.mapResponse(account, response)
|
||||||
|
|
||||||
callAccountAddedListeners(bank, customer)
|
if (response.isSuccessful) {
|
||||||
|
accounts.put(account, Pair(customer, bank))
|
||||||
|
|
||||||
|
callAccountAddedListeners(account)
|
||||||
|
|
||||||
if (response.supportsRetrievingTransactionsOfLast90DaysWithoutTan) {
|
if (response.supportsRetrievingTransactionsOfLast90DaysWithoutTan) {
|
||||||
retrievedAccountTransactions(customer, response)
|
account.bankAccounts.forEach { bankAccount ->
|
||||||
|
retrievedAccountTransactions(bankAccount, mappedResponse)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(response)
|
callback(mappedResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun getAccountTransactionsAsync(bank: BankData, customer: CustomerData,
|
open fun getAccountTransactionsAsync(account: Account,
|
||||||
callback: (GetTransactionsResponse) -> Unit) {
|
callback: (GetTransactionsResponse) -> Unit) {
|
||||||
|
|
||||||
getAccountTransactionsAsync(bank, customer, null, callback)
|
account.bankAccounts.forEach { bankAccount ->
|
||||||
|
getAccountTransactionsAsync(bankAccount, callback) // TODO: use a synchronous version of getAccountTransactions() so that all bank accounts get handled serially
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getAccountTransactionsAsync(bank: BankData, customer: CustomerData, fromDate: Date?,
|
open fun getAccountTransactionsAsync(bankAccount: BankAccount,
|
||||||
callback: (GetTransactionsResponse) -> Unit) {
|
callback: (GetTransactionsResponse) -> Unit) {
|
||||||
|
|
||||||
finTsClient.getTransactionsAsync(GetTransactionsParameter(true, fromDate), bank, customer) { response ->
|
getAccountTransactionsAsync(bankAccount, null, callback)
|
||||||
retrievedAccountTransactions(customer, response)
|
}
|
||||||
|
|
||||||
callback(response)
|
open fun getAccountTransactionsAsync(bankAccount: BankAccount, fromDate: Date?,
|
||||||
|
callback: (GetTransactionsResponse) -> Unit) {
|
||||||
|
|
||||||
|
getCustomerAndBankForAccount(bankAccount.account)?.let { customerBankPair ->
|
||||||
|
finTsClient.getTransactionsAsync(GetTransactionsParameter(true, fromDate),
|
||||||
|
customerBankPair.second, customerBankPair.first) { response ->
|
||||||
|
|
||||||
|
val mappedResponse = fints4javaModelMapper.mapResponse(bankAccount.account, response)
|
||||||
|
|
||||||
|
retrievedAccountTransactions(bankAccount, mappedResponse)
|
||||||
|
|
||||||
|
callback(mappedResponse)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun updateAccountsTransactionsAsync(callback: (GetTransactionsResponse) -> Unit) {
|
open fun updateAccountsTransactionsAsync(callback: (GetTransactionsResponse) -> Unit) {
|
||||||
accounts.forEach { entry ->
|
accounts.keys.forEach { account ->
|
||||||
val customer = entry.key
|
account.bankAccounts.forEach { bankAccount ->
|
||||||
val today = Date() // TODO: still don't know where this bug is coming from that bank returns a transaction dated at end of year
|
val today = Date() // TODO: still don't know where this bug is coming from that bank returns a transaction dated at end of year
|
||||||
val lastRetrievedTransactionDate = bookedTransactions[customer]?.firstOrNull { it.bookingDate <= today }?.bookingDate
|
val lastRetrievedTransactionDate = bookedTransactions[bankAccount]?.firstOrNull { it.bookingDate <= today }?.bookingDate
|
||||||
val fromDate = lastRetrievedTransactionDate?.let { Date(it.time - 24 * 60 * 60 * 1000) } // on day before last received transaction
|
val fromDate = lastRetrievedTransactionDate?.let { Date(it.time - 24 * 60 * 60 * 1000) } // on day before last received transaction
|
||||||
|
|
||||||
getAccountTransactionsAsync(entry.value, customer, fromDate, callback)
|
getAccountTransactionsAsync(bankAccount, fromDate, callback)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun retrievedAccountTransactions(customer: CustomerData, response: GetTransactionsResponse) {
|
protected open fun retrievedAccountTransactions(bankAccount: BankAccount, response: GetTransactionsResponse) {
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
updateAccountTransactionsAndBalances(customer, response)
|
updateAccountTransactionsAndBalances(bankAccount, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
callRetrievedAccountTransactionsResponseListener(customer, response)
|
callRetrievedAccountTransactionsResponseListener(bankAccount, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun updateAccountTransactionsAndBalances(customer: CustomerData, response: GetTransactionsResponse) {
|
protected open fun updateAccountTransactionsAndBalances(bankAccount: BankAccount, response: GetTransactionsResponse) {
|
||||||
|
|
||||||
if (bookedTransactions.containsKey(customer) == false) {
|
response.bookedTransactions.forEach { entry ->
|
||||||
bookedTransactions.put(customer, response.bookedTransactions.toMutableSet())
|
if (bookedTransactions.containsKey(entry.key) == false) {
|
||||||
|
bookedTransactions.put(bankAccount, entry.value.toMutableSet())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bookedTransactions[customer]?.addAll(response.bookedTransactions) // TODO: does currently not work, overwrite equals()
|
bookedTransactions[bankAccount]?.addAll(entry.value) // TODO: does currently not work, overwrite equals()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unbookedTransactions.containsKey(customer) == false) {
|
response.unbookedTransactions.forEach { entry ->
|
||||||
unbookedTransactions.put(customer, response.unbookedTransactions.toMutableSet())
|
if (unbookedTransactions.containsKey(entry.key) == false) {
|
||||||
|
unbookedTransactions.put(bankAccount, entry.value.toMutableSet())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
unbookedTransactions[customer]?.addAll(response.unbookedTransactions)
|
unbookedTransactions[bankAccount]?.addAll(entry.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response.balance?.let {
|
response.balances.forEach { entry ->
|
||||||
balances[customer] = it
|
balances[entry.key] = entry.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun transferMoneyAsync(bankTransferData: BankTransferData, callback: (FinTsClientResponse) -> Unit) {
|
open fun transferMoneyAsync(bankAccount: BankAccount, bankTransferData: BankTransferData, callback: (FinTsClientResponse) -> Unit) {
|
||||||
accounts.entries.firstOrNull()?.let { // TODO: of course not correct, but i have to think of a multi account architecture and data model anyway
|
getCustomerAndBankForAccount(bankAccount.account)?.let { customerBankPair ->
|
||||||
finTsClient.doBankTransferAsync(bankTransferData, it.value, it.key, callback)
|
finTsClient.doBankTransferAsync(
|
||||||
|
bankTransferData, customerBankPair.second, customerBankPair.first, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +182,19 @@ open class MainWindowPresenter(callback: FinTsClientCallback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected open fun getCustomerAndBankForAccount(account: Account): Pair<CustomerData, BankData>? {
|
||||||
|
(accounts.get(account) as? Pair<CustomerData, BankData>)?.let { customerBankPair ->
|
||||||
|
account.selectedTanProcedure?.let { selectedTanProcedure ->
|
||||||
|
customerBankPair.first.selectedTanProcedure = fints4javaModelMapper.mapTanProcedureBack(selectedTanProcedure)
|
||||||
|
}
|
||||||
|
|
||||||
|
return customerBankPair // TODO: return IBankingClient
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
open val allTransactions: List<AccountTransaction>
|
open val allTransactions: List<AccountTransaction>
|
||||||
get() = bookedTransactions.values.flatten().toList() // TODO: someday add unbooked transactions
|
get() = bookedTransactions.values.flatten().toList() // TODO: someday add unbooked transactions
|
||||||
|
|
||||||
|
@ -159,23 +202,23 @@ open class MainWindowPresenter(callback: FinTsClientCallback) {
|
||||||
get() = balances.values.fold(BigDecimal.ZERO) { acc, e -> acc + e }
|
get() = balances.values.fold(BigDecimal.ZERO) { acc, e -> acc + e }
|
||||||
|
|
||||||
|
|
||||||
open fun addAccountAddedListener(listener: (BankData, CustomerData) -> Unit) {
|
open fun addAccountAddedListener(listener: (Account) -> Unit) {
|
||||||
accountAddedListeners.add(listener)
|
accountAddedListeners.add(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun callAccountAddedListeners(bank: BankData, customer: CustomerData) {
|
protected open fun callAccountAddedListeners(account: Account) {
|
||||||
ArrayList(accountAddedListeners).forEach {
|
ArrayList(accountAddedListeners).forEach {
|
||||||
it(bank, customer) // TODO: use RxJava for this
|
it(account) // TODO: use RxJava for this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun addRetrievedAccountTransactionsResponseListener(listener: (CustomerData, GetTransactionsResponse) -> Unit) {
|
open fun addRetrievedAccountTransactionsResponseListener(listener: (BankAccount, GetTransactionsResponse) -> Unit) {
|
||||||
retrievedAccountTransactionsResponseListeners.add(listener)
|
retrievedAccountTransactionsResponseListeners.add(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun callRetrievedAccountTransactionsResponseListener(customer: CustomerData, response: GetTransactionsResponse) {
|
protected open fun callRetrievedAccountTransactionsResponseListener(bankAccount: BankAccount, response: GetTransactionsResponse) {
|
||||||
ArrayList(retrievedAccountTransactionsResponseListeners).forEach {
|
ArrayList(retrievedAccountTransactionsResponseListeners).forEach {
|
||||||
it(customer, response) // TODO: use RxJava for this
|
it(bankAccount, response) // TODO: use RxJava for this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import android.view.ContextMenu
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import net.dankito.banking.fints4java.android.R
|
import net.dankito.banking.fints4java.android.R
|
||||||
import net.dankito.banking.fints4java.android.ui.adapter.viewholder.AccountTransactionViewHolder
|
import net.dankito.banking.fints4java.android.ui.adapter.viewholder.AccountTransactionViewHolder
|
||||||
import net.dankito.fints.model.AccountTransaction
|
import net.dankito.banking.ui.model.AccountTransaction
|
||||||
import net.dankito.utils.android.extensions.asActivity
|
import net.dankito.utils.android.extensions.asActivity
|
||||||
import net.dankito.utils.android.extensions.setTextColorToColorResource
|
import net.dankito.utils.android.extensions.setTextColorToColorResource
|
||||||
import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter
|
import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter
|
||||||
|
|
|
@ -4,7 +4,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import net.dankito.banking.fints4java.android.R
|
import net.dankito.banking.fints4java.android.R
|
||||||
import net.dankito.fints.model.TanProcedure
|
import net.dankito.banking.ui.model.TanProcedure
|
||||||
import net.dankito.utils.android.extensions.asActivity
|
import net.dankito.utils.android.extensions.asActivity
|
||||||
import net.dankito.utils.android.ui.adapter.ListAdapter
|
import net.dankito.utils.android.ui.adapter.ListAdapter
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ import net.dankito.banking.fints4java.android.R
|
||||||
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
|
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
|
||||||
import net.dankito.banking.fints4java.android.ui.adapter.BankListAdapter
|
import net.dankito.banking.fints4java.android.ui.adapter.BankListAdapter
|
||||||
import net.dankito.banking.fints4java.android.ui.adapter.TanProceduresAdapter
|
import net.dankito.banking.fints4java.android.ui.adapter.TanProceduresAdapter
|
||||||
|
import net.dankito.banking.ui.model.responses.AddAccountResponse
|
||||||
import net.dankito.fints.model.BankInfo
|
import net.dankito.fints.model.BankInfo
|
||||||
import net.dankito.fints.response.client.AddAccountResponse
|
|
||||||
import net.dankito.utils.android.extensions.asActivity
|
import net.dankito.utils.android.extensions.asActivity
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ open class AddAccountDialog : DialogFragment() {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
AlertDialog.Builder(context)
|
AlertDialog.Builder(context)
|
||||||
.setMessage(context.getString(R.string.dialog_add_account_message_could_not_add_account, (response.exception ?: response.errorsToShowToUser.joinToString("\n"))))
|
.setMessage(context.getString(R.string.dialog_add_account_message_could_not_add_account, response.errorToShowToUser))
|
||||||
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
|
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ open class AddAccountDialog : DialogFragment() {
|
||||||
val view = context.asActivity()?.layoutInflater?.inflate(R.layout.view_successfully_added_account, null)
|
val view = context.asActivity()?.layoutInflater?.inflate(R.layout.view_successfully_added_account, null)
|
||||||
|
|
||||||
val adapter = TanProceduresAdapter()
|
val adapter = TanProceduresAdapter()
|
||||||
adapter.setItems(response.customer.supportedTanProcedures)
|
adapter.setItems(response.account.supportedTanProcedures)
|
||||||
|
|
||||||
view?.findViewById<TextView>(R.id.txtSuccessfullyAddedAccountMessage)?.setText(messageId)
|
view?.findViewById<TextView>(R.id.txtSuccessfullyAddedAccountMessage)?.setText(messageId)
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ open class AddAccountDialog : DialogFragment() {
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||||
|
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||||
response.customer.selectedTanProcedure = adapter.getItem(position)
|
response.account.selectedTanProcedure = adapter.getItem(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ open class AddAccountDialog : DialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun retrieveAccountTransactionsAndDismiss(response: AddAccountResponse, messageDialog: DialogInterface) {
|
protected open fun retrieveAccountTransactionsAndDismiss(response: AddAccountResponse, messageDialog: DialogInterface) {
|
||||||
presenter.getAccountTransactionsAsync(response.bank, response.customer) { } // TODO: show error message if not successful. Here or in HomeFragment
|
presenter.getAccountTransactionsAsync(response.account) { }
|
||||||
|
|
||||||
messageDialog.dismiss()
|
messageDialog.dismiss()
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import kotlinx.android.synthetic.main.dialog_bank_transfer.*
|
||||||
import kotlinx.android.synthetic.main.dialog_bank_transfer.view.*
|
import kotlinx.android.synthetic.main.dialog_bank_transfer.view.*
|
||||||
import net.dankito.banking.fints4java.android.R
|
import net.dankito.banking.fints4java.android.R
|
||||||
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
|
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
|
||||||
|
import net.dankito.banking.ui.model.BankAccount
|
||||||
import net.dankito.fints.messages.segmente.implementierte.sepa.ISepaMessageCreator
|
import net.dankito.fints.messages.segmente.implementierte.sepa.ISepaMessageCreator
|
||||||
import net.dankito.fints.messages.segmente.implementierte.sepa.SepaMessageCreator
|
import net.dankito.fints.messages.segmente.implementierte.sepa.SepaMessageCreator
|
||||||
import net.dankito.fints.model.BankTransferData
|
import net.dankito.fints.model.BankTransferData
|
||||||
|
@ -30,17 +31,20 @@ open class BankTransferDialog : DialogFragment() {
|
||||||
|
|
||||||
protected lateinit var presenter: MainWindowPresenter
|
protected lateinit var presenter: MainWindowPresenter
|
||||||
|
|
||||||
|
protected lateinit var bankAccount: BankAccount
|
||||||
|
|
||||||
protected var preselectedValues: BankTransferData? = null
|
protected var preselectedValues: BankTransferData? = null
|
||||||
|
|
||||||
protected val sepaMessageCreator: ISepaMessageCreator = SepaMessageCreator()
|
protected val sepaMessageCreator: ISepaMessageCreator = SepaMessageCreator()
|
||||||
|
|
||||||
|
|
||||||
open fun show(activity: AppCompatActivity, presenter: MainWindowPresenter, fullscreen: Boolean = false) {
|
open fun show(activity: AppCompatActivity, presenter: MainWindowPresenter, bankAccount: BankAccount, fullscreen: Boolean = false) {
|
||||||
show(activity, presenter, null, fullscreen)
|
show(activity, presenter, bankAccount, null, fullscreen)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun show(activity: AppCompatActivity, presenter: MainWindowPresenter, preselectedValues: BankTransferData?, fullscreen: Boolean = false) {
|
open fun show(activity: AppCompatActivity, presenter: MainWindowPresenter, bankAccount: BankAccount, preselectedValues: BankTransferData?, fullscreen: Boolean = false) {
|
||||||
this.presenter = presenter
|
this.presenter = presenter
|
||||||
|
this.bankAccount = bankAccount
|
||||||
this.preselectedValues = preselectedValues
|
this.preselectedValues = preselectedValues
|
||||||
|
|
||||||
val style = if(fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.Dialog
|
val style = if(fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.Dialog
|
||||||
|
@ -113,7 +117,7 @@ open class BankTransferDialog : DialogFragment() {
|
||||||
edtxtUsage.text.toString()
|
edtxtUsage.text.toString()
|
||||||
)
|
)
|
||||||
|
|
||||||
presenter.transferMoneyAsync(transferData) {
|
presenter.transferMoneyAsync(bankAccount, transferData) {
|
||||||
context?.asActivity()?.runOnUiThread {
|
context?.asActivity()?.runOnUiThread {
|
||||||
handleTransferMoneyResultOnUiThread(it, transferData)
|
handleTransferMoneyResultOnUiThread(it, transferData)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,9 @@ import net.dankito.banking.fints4java.android.R
|
||||||
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
|
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
|
||||||
import net.dankito.banking.fints4java.android.ui.adapter.AccountTransactionAdapter
|
import net.dankito.banking.fints4java.android.ui.adapter.AccountTransactionAdapter
|
||||||
import net.dankito.banking.fints4java.android.ui.dialogs.BankTransferDialog
|
import net.dankito.banking.fints4java.android.ui.dialogs.BankTransferDialog
|
||||||
|
import net.dankito.banking.ui.model.AccountTransaction
|
||||||
|
import net.dankito.banking.ui.model.responses.GetTransactionsResponse
|
||||||
import net.dankito.fints.model.BankTransferData
|
import net.dankito.fints.model.BankTransferData
|
||||||
import net.dankito.fints.response.client.GetTransactionsResponse
|
|
||||||
import net.dankito.utils.android.extensions.asActivity
|
import net.dankito.utils.android.extensions.asActivity
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@ -159,7 +160,7 @@ class HomeFragment : Fragment() {
|
||||||
mnitmBalance.isVisible = true
|
mnitmBalance.isVisible = true
|
||||||
} else {
|
} else {
|
||||||
AlertDialog.Builder(activity) // TODO: may show account name in message
|
AlertDialog.Builder(activity) // TODO: may show account name in message
|
||||||
.setMessage(activity.getString(R.string.fragment_home_could_not_retrieve_account_transactions, response.exception ?: response.errorsToShowToUser.joinToString("\n")))
|
.setMessage(activity.getString(R.string.fragment_home_could_not_retrieve_account_transactions, response.errorToShowToUser))
|
||||||
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
|
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
@ -169,13 +170,15 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
|
|
||||||
private fun showBankTransferDialog() {
|
private fun showBankTransferDialog() {
|
||||||
|
transactionAdapter.selectedTransaction?.let { selectedTransaction ->
|
||||||
(context as? AppCompatActivity)?.let { activity ->
|
(context as? AppCompatActivity)?.let { activity ->
|
||||||
BankTransferDialog().show(activity, presenter, mapPreselectedValues())
|
BankTransferDialog().show(activity, presenter, selectedTransaction.bankAccount, mapPreselectedValues(selectedTransaction))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mapPreselectedValues(): BankTransferData? {
|
private fun mapPreselectedValues(selectedTransaction: AccountTransaction?): BankTransferData? {
|
||||||
transactionAdapter.selectedTransaction?.let { selectedTransaction ->
|
selectedTransaction?.let {
|
||||||
return BankTransferData(
|
return BankTransferData(
|
||||||
selectedTransaction.otherPartyName ?: "",
|
selectedTransaction.otherPartyName ?: "",
|
||||||
selectedTransaction.otherPartyAccountId ?: "",
|
selectedTransaction.otherPartyAccountId ?: "",
|
||||||
|
|
Loading…
Reference in New Issue