Renamed Customer to BankData

This commit is contained in:
dankito 2020-09-22 06:06:11 +02:00
parent df1ce4fd3a
commit 60c93dedfb
84 changed files with 810 additions and 811 deletions

View File

@ -45,22 +45,22 @@ open class LuceneBankingPersistence(
protected val fields = FieldBuilder()
override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List<IAccountTransaction>) {
override fun saveOrUpdateAccountTransactions(account: TypedBankAccount, transactions: List<IAccountTransaction>) {
val writer = getWriter()
transactions.forEach { transaction ->
writer.updateDocumentForNonNullFields(
IdFieldName, transaction.technicalId,
*createFieldsForAccountTransaction(bankAccount, transaction).toTypedArray()
*createFieldsForAccountTransaction(account, transaction).toTypedArray()
)
}
writer.flushChangesToDisk()
}
protected open fun createFieldsForAccountTransaction(bankAccount: TypedBankAccount, transaction: IAccountTransaction): List<IndexableField?> {
protected open fun createFieldsForAccountTransaction(account: TypedBankAccount, transaction: IAccountTransaction): List<IndexableField?> {
return listOf(
fields.keywordField(BankAccountIdFieldName, bankAccount.technicalId),
fields.keywordField(BankAccountIdFieldName, account.technicalId),
fields.nullableFullTextSearchField(OtherPartyNameFieldName, transaction.otherPartyName, true),
fields.fullTextSearchField(UsageFieldName, transaction.usage, true),
fields.nullableFullTextSearchField(BookingTextFieldName, transaction.bookingText, true),
@ -77,21 +77,21 @@ open class LuceneBankingPersistence(
}
override fun deleteAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
override fun deleteBank(bank: TypedBankData, allBanks: List<TypedBankData>) {
try {
deleteAccountTransactions(customer.accounts)
deleteAccountTransactions(bank.accounts)
} catch (e: Exception) {
log.error("Could not delete account transactions of account $customer", e)
log.error("Could not delete account transactions of account $bank", e)
}
super.deleteAccount(customer, allCustomers)
super.deleteBank(bank, allBanks)
}
protected open fun deleteAccountTransactions(bankAccounts: List<TypedBankAccount>) {
protected open fun deleteAccountTransactions(accounts: List<TypedBankAccount>) {
val writer = getWriter()
val bankAccountIds = bankAccounts.map { it.technicalId }
writer.deleteDocumentsAndFlushChangesToDisk(BankAccountIdFieldName, *bankAccountIds.toTypedArray())
val accountIds = accounts.map { it.technicalId }
writer.deleteDocumentsAndFlushChangesToDisk(BankAccountIdFieldName, *accountIds.toTypedArray())
}

View File

@ -1,7 +1,7 @@
package net.dankito.banking.search
import net.dankito.banking.persistence.LuceneBankingPersistence
import net.dankito.banking.ui.model.Customer
import net.dankito.banking.ui.model.BankData
import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount
import net.dankito.utils.io.FileUtils
@ -37,7 +37,7 @@ class LuceneRemitteeSearcherTest {
private val Amount = BigDecimal.valueOf(123.45)
private val bankAccountMock = BankAccount(mock(Customer::class.java), "", "", null, null, "")
private val bankAccountMock = BankAccount(mock(BankData::class.java), "", "", null, null, "")
private val dateFormat = SimpleDateFormat("dd.MM.yyyy")

View File

@ -9,7 +9,7 @@ import net.dankito.banking.search.IRemitteeSearcher
import net.dankito.banking.search.Remittee
import net.dankito.banking.ui.model.IAccountTransaction
import net.dankito.banking.ui.model.TypedBankAccount
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.tan.MobilePhoneTanMedium
import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
import net.dankito.banking.util.persistence.doSaveUrlToFile
@ -32,8 +32,8 @@ open class RoomBankingPersistence(applicationContext: Context, password: String?
}
override fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
(customer as? Bank)?.let { bank ->
override fun saveOrUpdateBank(bank: TypedBankData, allBanks: List<TypedBankData>) {
(bank as? Bank)?.let { bank ->
bank.selectedTanMethodId = bank.selectedTanMethod?.technicalId
db.bankDao().saveOrUpdate(bank)
@ -64,8 +64,8 @@ open class RoomBankingPersistence(applicationContext: Context, password: String?
}
}
override fun deleteAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
(customer as? Bank)?.let { bank ->
override fun deleteBank(bank: TypedBankData, allBanks: List<TypedBankData>) {
(bank as? Bank)?.let { bank ->
db.accountTransactionDao().delete(bank.accounts.flatMap { it.bookedTransactions }.filterIsInstance<AccountTransaction>())
db.bankAccountDao().delete(bank.accounts.filterIsInstance<BankAccount>())
@ -77,7 +77,7 @@ open class RoomBankingPersistence(applicationContext: Context, password: String?
}
}
override fun readPersistedAccounts(): List<TypedCustomer> {
override fun readPersistedBanks(): List<TypedBankData> {
val banks = db.bankDao().getAll()
val accounts = db.bankAccountDao().getAll()
@ -92,12 +92,12 @@ open class RoomBankingPersistence(applicationContext: Context, password: String?
bank.accounts = accounts.filter { it.bankId == bank.id }
bank.accounts.filterIsInstance<BankAccount>().forEach { account ->
account.customer = bank
account.bank = bank
account.bookedTransactions = transactions.filter { it.bankAccountId == account.id }
account.bookedTransactions = transactions.filter { it.accountId == account.id }
account.bookedTransactions.filterIsInstance<AccountTransaction>().forEach { transaction ->
transaction.bankAccount = account
transaction.account = account
}
}
@ -111,12 +111,12 @@ open class RoomBankingPersistence(applicationContext: Context, password: String?
return banks
}
override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List<IAccountTransaction>) {
val accountId = (bankAccount as? BankAccount)?.id ?: bankAccount.technicalId.toLong()
override fun saveOrUpdateAccountTransactions(account: TypedBankAccount, transactions: List<IAccountTransaction>) {
val accountId = (account as? BankAccount)?.id ?: account.technicalId.toLong()
val mappedTransactions = transactions.filterIsInstance<AccountTransaction>()
mappedTransactions.forEach { it.bankAccountId = accountId }
mappedTransactions.forEach { it.accountId = accountId }
db.accountTransactionDao().saveOrUpdate(mappedTransactions)
}

View File

@ -11,7 +11,7 @@ import net.dankito.utils.multiplatform.*
@Entity
open class AccountTransaction(
@Ignore
override var bankAccount: BankAccount,
override var account: BankAccount,
override var amount: BigDecimal,
override var currency: String,
@ -56,15 +56,15 @@ open class AccountTransaction(
/* convenience constructors for languages not supporting default values */
constructor(bankAccount: BankAccount, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
: this(bankAccount, amount, "EUR", unparsedUsage, valueDate,
constructor(account: BankAccount, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
: this(account, amount, "EUR", unparsedUsage, valueDate,
otherPartyName, null, null, bookingText, valueDate)
constructor(bankAccount: BankAccount, amount: BigDecimal, currency: String, unparsedUsage: String, bookingDate: Date,
constructor(account: BankAccount, amount: BigDecimal, currency: String, unparsedUsage: String, bookingDate: Date,
otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?,
bookingText: String?, valueDate: Date)
: this(bankAccount, amount, currency, unparsedUsage, bookingDate,
: this(account, amount, currency, unparsedUsage, bookingDate,
otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate,
0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null)
@ -75,7 +75,7 @@ open class AccountTransaction(
override var technicalId: String = buildTransactionIdentifier()
// Room doesn't allow me to add getters and setters -> have to map it manually
open var bankAccountId: Long = BaseDao.ObjectNotInsertedId
open var accountId: Long = BaseDao.ObjectNotInsertedId
override fun equals(other: Any?): Boolean {

View File

@ -5,7 +5,7 @@ import androidx.room.Ignore
import androidx.room.PrimaryKey
import net.dankito.banking.persistence.dao.BaseDao
import net.dankito.banking.ui.model.TypedBankAccount
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.tan.TanMedium
import net.dankito.banking.ui.model.tan.TanMethod
@ -44,7 +44,7 @@ open class Bank(
override var userSetDisplayName: String? = null,
override var displayIndex: Int = 0
) : TypedCustomer {
) : TypedBankData {
internal constructor() : this("", "", "", "", "", "", "") // for object deserializers

View File

@ -13,7 +13,7 @@ import net.dankito.utils.multiplatform.UUID
@Entity
open class BankAccount(
@Ignore
override var customer: TypedCustomer,
override var bank: TypedBankData,
override var identifier: String,
override var accountHolderName: String,
override var iban: String?,
@ -42,10 +42,10 @@ open class BankAccount(
/* convenience constructors for languages not supporting default values */
constructor(customer: TypedCustomer, productName: String?, identifier: String) : this(customer, productName, identifier, BankAccountType.Girokonto)
constructor(bank: TypedBankData, productName: String?, identifier: String) : this(bank, productName, identifier, BankAccountType.Girokonto)
constructor(customer: TypedCustomer, productName: String?, identifier: String, type: BankAccountType = BankAccountType.Girokonto, balance: BigDecimal = BigDecimal.Zero)
: this(customer, identifier, "", null, null, "", balance, "EUR", type, productName)
constructor(bank: TypedBankData, productName: String?, identifier: String, type: BankAccountType = BankAccountType.Girokonto, balance: BigDecimal = BigDecimal.Zero)
: this(bank, identifier, "", null, null, "", balance, "EUR", type, productName)
@PrimaryKey(autoGenerate = true)

View File

@ -2,7 +2,7 @@ package net.dankito.banking.persistence.model
import net.dankito.banking.ui.model.IAccountTransaction
import net.dankito.banking.ui.model.TypedBankAccount
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.mapper.IModelCreator
import net.dankito.banking.ui.model.tan.AllowedTanFormat
import net.dankito.banking.ui.model.tan.TanMethodType
@ -12,18 +12,18 @@ import net.dankito.utils.multiplatform.Date
open class RoomModelCreator : IModelCreator {
override fun createCustomer(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String,
bic: String, customerName: String, userId: String, iconUrl: String?): TypedCustomer {
override fun createBank(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String,
bic: String, customerName: String, userId: String, iconUrl: String?): TypedBankData {
return Bank(bankCode, customerId, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl)
}
override fun createBankAccount(customer: TypedCustomer, productName: String?, identifier: String): TypedBankAccount {
return BankAccount(customer, productName, identifier)
override fun createAccount(bank: TypedBankData, productName: String?, identifier: String): TypedBankAccount {
return BankAccount(bank, productName, identifier)
}
override fun createTransaction(
bankAccount: TypedBankAccount,
account: TypedBankAccount,
amount: BigDecimal,
currency: String,
unparsedUsage: String,
@ -58,7 +58,7 @@ open class RoomModelCreator : IModelCreator {
transactionReferenceNumber: String,
relatedReferenceNumber: String?
): IAccountTransaction {
return AccountTransaction(bankAccount as BankAccount, amount, currency, unparsedUsage, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId,
return AccountTransaction(account as BankAccount, amount, currency, unparsedUsage, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId,
bookingText, valueDate, statementNumber, sequenceNumber, openingBalance, closingBalance, endToEndReference, customerReference, mandateReference,
creditorIdentifier, originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient,
usageWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,

View File

@ -1,6 +1,6 @@
package net.dankito.banking.persistence
import net.dankito.banking.persistence.model.CustomerEntity
import net.dankito.banking.persistence.model.BankDataEntity
import net.dankito.banking.ui.model.*
import net.dankito.utils.multiplatform.File
import net.dankito.banking.util.ISerializer
@ -17,27 +17,27 @@ open class BankingPersistenceJson(
}
override fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
saveAllCustomers(allCustomers)
override fun saveOrUpdateBank(bank: TypedBankData, allBanks: List<TypedBankData>) {
saveAllBanks(allBanks)
}
override fun deleteAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
saveAllCustomers(allCustomers)
override fun deleteBank(bank: TypedBankData, allBanks: List<TypedBankData>) {
saveAllBanks(allBanks)
}
override fun readPersistedAccounts(): List<TypedCustomer> {
return serializer.deserializeListOr(jsonFile, CustomerEntity::class).map { it as TypedCustomer }
override fun readPersistedBanks(): List<TypedBankData> {
return serializer.deserializeListOr(jsonFile, BankDataEntity::class).map { it as TypedBankData }
}
override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List<IAccountTransaction>) {
override fun saveOrUpdateAccountTransactions(account: TypedBankAccount, transactions: List<IAccountTransaction>) {
// done when called saveOrUpdateAccount()
// TODO: or also call saveAllCustomers()?
// TODO: or also call saveAllBanks()?
}
protected open fun saveAllCustomers(allCustomers: List<TypedCustomer>) {
serializer.serializeObject(allCustomers, jsonFile)
protected open fun saveAllBanks(allBanks: List<TypedBankData>) {
serializer.serializeObject(allBanks, jsonFile)
}

View File

@ -2,10 +2,10 @@ package net.dankito.banking.persistence.mapper
import net.dankito.banking.persistence.model.AccountTransactionEntity
import net.dankito.banking.persistence.model.BankAccountEntity
import net.dankito.banking.persistence.model.CustomerEntity
import net.dankito.banking.persistence.model.BankDataEntity
import net.dankito.banking.ui.model.IAccountTransaction
import net.dankito.banking.ui.model.TypedBankAccount
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.mapper.IModelCreator
import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.utils.multiplatform.Date
@ -13,19 +13,19 @@ import net.dankito.utils.multiplatform.Date
open class EntitiesModelCreator : IModelCreator {
override fun createCustomer(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String, bic: String,
customerName: String, userId: String, iconUrl: String?): TypedCustomer {
override fun createBank(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String, bic: String,
customerName: String, userId: String, iconUrl: String?): TypedBankData {
return CustomerEntity(bankCode, customerId, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl) as TypedCustomer
return BankDataEntity(bankCode, customerId, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl) as TypedBankData
}
override fun createBankAccount(customer: TypedCustomer, productName: String?, identifier: String): TypedBankAccount {
return BankAccountEntity(customer as CustomerEntity, identifier, "", null, null, "", productName = productName) as TypedBankAccount
override fun createAccount(bank: TypedBankData, productName: String?, identifier: String): TypedBankAccount {
return BankAccountEntity(bank as BankDataEntity, identifier, "", null, null, "", productName = productName) as TypedBankAccount
}
override fun createTransaction(
bankAccount: TypedBankAccount,
account: TypedBankAccount,
amount: BigDecimal,
currency: String,
unparsedUsage: String,
@ -61,7 +61,7 @@ open class EntitiesModelCreator : IModelCreator {
relatedReferenceNumber: String?
) : IAccountTransaction {
return AccountTransactionEntity(bankAccount as BankAccountEntity, amount, currency, unparsedUsage, bookingDate,
return AccountTransactionEntity(account as BankAccountEntity, amount, currency, unparsedUsage, bookingDate,
otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, statementNumber, sequenceNumber,
openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier,
originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient,

View File

@ -11,7 +11,7 @@ import net.dankito.utils.multiplatform.UUID
@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references
// had to define all properties as 'var' 'cause MapStruct cannot handle vals
open class AccountTransactionEntity(
override var bankAccount: BankAccountEntity,
override var account: BankAccountEntity,
override var amount: BigDecimal,
override var currency: String,
override var unparsedUsage: String,
@ -56,8 +56,8 @@ open class AccountTransactionEntity(
-1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null,
null, "", null)
constructor(bankAccount: BankAccountEntity, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
: this(bankAccount, amount, "EUR", unparsedUsage, valueDate, otherPartyName, null, null, bookingText, valueDate, 0, null, null, null,
constructor(account: BankAccountEntity, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
: this(account, amount, "EUR", unparsedUsage, valueDate, otherPartyName, null, null, bookingText, valueDate, 0, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null,
null, "", "", null, null, "", null)

View File

@ -11,7 +11,7 @@ import net.dankito.utils.multiplatform.UUID
@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references
// had to define all properties as 'var' 'cause MapStruct cannot handle vals (and cannot use Pozo's mapstruct-kotlin as SerializableBankAccountBuilder would fail with @Context)
open class BankAccountEntity(
override var customer: CustomerEntity,
override var bank: BankDataEntity,
override var identifier: String,
override var accountHolderName: String,
override var iban: String?,
@ -38,7 +38,7 @@ open class BankAccountEntity(
) : IBankAccount<AccountTransactionEntity> {
internal constructor() : this(CustomerEntity(), "", "", null, null, "") // for object deserializers
internal constructor() : this(BankDataEntity(), "", "", null, null, "") // for object deserializers
override fun toString(): String {

View File

@ -1,7 +1,7 @@
package net.dankito.banking.persistence.model
import com.fasterxml.jackson.annotation.*
import net.dankito.banking.ui.model.ICustomer
import net.dankito.banking.ui.model.IBankData
import net.dankito.banking.ui.model.tan.TanMedium
import net.dankito.banking.ui.model.tan.TanMethod
import java.util.*
@ -9,7 +9,7 @@ import java.util.*
@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references
// had to define all properties as 'var' 'cause MapStruct cannot handle vals (and cannot use Pozo's mapstruct-kotlin as SerializableCustomerBuilder would fail with @Context)
open class CustomerEntity(
open class BankDataEntity(
override var bankCode: String,
override var customerId: String,
override var password: String,
@ -27,7 +27,7 @@ open class CustomerEntity(
override var technicalId: String = UUID.randomUUID().toString(),
override var userSetDisplayName: String? = null,
override var displayIndex: Int = 0
) : ICustomer<BankAccountEntity, AccountTransactionEntity> {
) : IBankData<BankAccountEntity, AccountTransactionEntity> {
internal constructor() : this("", "", "", "", "", "", "") // for object deserializers

View File

@ -2,7 +2,7 @@ package net.dankito.banking.persistence
import net.dankito.banking.persistence.model.AccountTransactionEntity
import net.dankito.banking.persistence.model.BankAccountEntity
import net.dankito.banking.persistence.model.CustomerEntity
import net.dankito.banking.persistence.model.BankDataEntity
import net.dankito.banking.ui.model.*
import net.dankito.banking.util.JacksonJsonSerializer
import net.dankito.utils.multiplatform.BigDecimal
@ -57,96 +57,96 @@ class BankingPersistenceJsonTest {
@Test
fun saveOrUpdateAccount() {
fun saveOrUpdateBank() {
// given
val customers = listOf(
createCustomer(2),
createCustomer(3)
val banks = listOf(
createBank(2),
createBank(3)
)
// when
underTest.saveOrUpdateAccount(customers.first() as TypedCustomer, customers.map { it as TypedCustomer })
underTest.saveOrUpdateBank(banks.first() as TypedBankData, banks.map { it as TypedBankData })
// then
val result = serializer.deserializeListOr(file, CustomerEntity::class)
val result = serializer.deserializeListOr(file, BankDataEntity::class)
assertCustomersEqual(result, customers)
assertBanksEqual(result, banks)
}
@Test
fun saveOrUpdateAccountWithBankAccountsAndTransactions() {
fun saveOrUpdateBankWithAccountsAndTransactions() {
// given
val customer = createCustomer(2)
val bank = createBank(2)
// when
underTest.saveOrUpdateAccount(customer as TypedCustomer, listOf(customer).map { it as TypedCustomer })
underTest.saveOrUpdateBank(bank as TypedBankData, listOf(bank).map { it as TypedBankData })
// then
val result = serializer.deserializeListOr(file, CustomerEntity::class)
val result = serializer.deserializeListOr(file, BankDataEntity::class)
assertCustomersEqual(result, listOf(customer) as List<CustomerEntity>)
assertBanksEqual(result, listOf(bank) as List<BankDataEntity>)
}
@Test
fun readPersistedAccounts() {
fun readPersistedBanks() {
// given
val customers = listOf(
createCustomer(2),
createCustomer(3)
val banks = listOf(
createBank(2),
createBank(3)
)
serializer.serializeObject(customers, file)
serializer.serializeObject(banks, file)
// when
val result = underTest.readPersistedAccounts()
val result = underTest.readPersistedBanks()
// then
assertCustomersEqual(customers, result as List<CustomerEntity>)
assertBanksEqual(banks, result as List<BankDataEntity>)
}
private fun createCustomer(countBankAccounts: Int = 0, customerId: String = CustomerId): CustomerEntity {
val result = CustomerEntity(BankCode, customerId, Password, FinTsServerAddress, BankName, Bic, CustomerName, UserId, IconUrl)
private fun createBank(countAccounts: Int = 0, customerId: String = CustomerId): BankDataEntity {
val result = BankDataEntity(BankCode, customerId, Password, FinTsServerAddress, BankName, Bic, CustomerName, UserId, IconUrl)
result.accounts = createBankAccounts(countBankAccounts, result)
result.accounts = createAccounts(countAccounts, result)
return result
}
private fun createBankAccounts(count: Int, customer: CustomerEntity): List<BankAccountEntity> {
private fun createAccounts(count: Int, customer: BankDataEntity): List<BankAccountEntity> {
val random = Random(System.nanoTime())
return IntRange(1, count).map { accountIndex ->
createBankAccount("Account_$accountIndex", customer, random.nextInt(2, 50))
createAccount("Account_$accountIndex", customer, random.nextInt(2, 50))
}
}
private fun createBankAccount(productName: String, customer: CustomerEntity, countTransactions: Int = 0): BankAccountEntity {
private fun createAccount(productName: String, customer: BankDataEntity, countTransactions: Int = 0): BankAccountEntity {
val result = BankAccountEntity(customer, customer.customerId, "AccountHolder", "DE00" + customer.bankCode + customer.customerId, null,
customer.customerId, BigDecimal(84.25), productName = productName)
result.bookedTransactions = createAccountTransactions(countTransactions, result)
result.bookedTransactions = createTransactions(countTransactions, result)
return result
}
private fun createAccountTransactions(countTransactions: Int, account: BankAccountEntity): List<AccountTransactionEntity> {
private fun createTransactions(countTransactions: Int, account: BankAccountEntity): List<AccountTransactionEntity> {
return IntRange(1, countTransactions).map { transactionIndex ->
createAccountTransaction(transactionIndex, account)
createTransaction(transactionIndex, account)
}
}
private fun createAccountTransaction(transactionIndex: Int, account: BankAccountEntity): AccountTransactionEntity {
private fun createTransaction(transactionIndex: Int, account: BankAccountEntity): AccountTransactionEntity {
return AccountTransactionEntity(account, "OtherParty_$transactionIndex", "Usage_$transactionIndex", BigDecimal(transactionIndex.toDouble()), createDate(), null)
}
@ -155,37 +155,37 @@ class BankingPersistenceJsonTest {
}
private fun assertCustomersEqual(deserializedCustomers: List<CustomerEntity>, customers: List<CustomerEntity>) {
assertThat(deserializedCustomers.size).isEqualTo(customers.size)
private fun assertBanksEqual(deserializedBanks: List<BankDataEntity>, banks: List<BankDataEntity>) {
assertThat(deserializedBanks.size).isEqualTo(banks.size)
deserializedCustomers.forEach { deserializedCustomer ->
val customer = customers.firstOrNull { it.technicalId == deserializedCustomer.technicalId }
deserializedBanks.forEach { deserializedBanks ->
val bank = banks.firstOrNull { it.technicalId == deserializedBanks.technicalId }
if (customer == null) {
Assert.fail("Could not find matching customer for deserialized customer $deserializedCustomer. customers = $customers")
if (bank == null) {
Assert.fail("Could not find matching bank for deserialized bank $deserializedBanks. banks = $banks")
}
else {
assertCustomersEqual(deserializedCustomer, customer)
assertBanksEqual(deserializedBanks, bank)
}
}
}
private fun assertCustomersEqual(deserializedCustomer: CustomerEntity, customer: CustomerEntity) {
assertThat(deserializedCustomer.bankCode).isEqualTo(customer.bankCode)
assertThat(deserializedCustomer.customerId).isEqualTo(customer.customerId)
assertThat(deserializedCustomer.password).isEqualTo(customer.password)
assertThat(deserializedCustomer.finTsServerAddress).isEqualTo(customer.finTsServerAddress)
private fun assertBanksEqual(deserializedBank: BankDataEntity, bank: BankDataEntity) {
assertThat(deserializedBank.bankCode).isEqualTo(bank.bankCode)
assertThat(deserializedBank.customerId).isEqualTo(bank.customerId)
assertThat(deserializedBank.password).isEqualTo(bank.password)
assertThat(deserializedBank.finTsServerAddress).isEqualTo(bank.finTsServerAddress)
assertThat(deserializedCustomer.bankName).isEqualTo(customer.bankName)
assertThat(deserializedCustomer.bic).isEqualTo(customer.bic)
assertThat(deserializedCustomer.customerName).isEqualTo(customer.customerName)
assertThat(deserializedCustomer.userId).isEqualTo(customer.userId)
assertThat(deserializedCustomer.iconUrl).isEqualTo(customer.iconUrl)
assertThat(deserializedBank.bankName).isEqualTo(bank.bankName)
assertThat(deserializedBank.bic).isEqualTo(bank.bic)
assertThat(deserializedBank.customerName).isEqualTo(bank.customerName)
assertThat(deserializedBank.userId).isEqualTo(bank.userId)
assertThat(deserializedBank.iconUrl).isEqualTo(bank.iconUrl)
assertBankAccountsEqual(deserializedCustomer.accounts, customer.accounts)
assertAccountsEqual(deserializedBank.accounts, bank.accounts)
}
private fun assertBankAccountsEqual(deserializedAccounts: List<BankAccountEntity>, accounts: List<BankAccountEntity>) {
private fun assertAccountsEqual(deserializedAccounts: List<BankAccountEntity>, accounts: List<BankAccountEntity>) {
assertThat(deserializedAccounts.size).isEqualTo(accounts.size)
deserializedAccounts.forEach { deserializedAccount ->
@ -195,14 +195,14 @@ class BankingPersistenceJsonTest {
Assert.fail("Could not find matching account for deserialized account $deserializedAccount. accounts = $accounts")
}
else {
assertBankAccountsEqual(deserializedAccount, account)
assertAccountsEqual(deserializedAccount, account)
}
}
}
private fun assertBankAccountsEqual(deserializedAccount: BankAccountEntity, account: BankAccountEntity) {
private fun assertAccountsEqual(deserializedAccount: BankAccountEntity, account: BankAccountEntity) {
// to check if MapStruct created reference correctly
assertThat(deserializedAccount.customer.technicalId).isEqualTo(account.customer.technicalId)
assertThat(deserializedAccount.bank.technicalId).isEqualTo(account.bank.technicalId)
assertThat(deserializedAccount.identifier).isEqualTo(account.identifier)
assertThat(deserializedAccount.iban).isEqualTo(account.iban)
@ -210,10 +210,10 @@ class BankingPersistenceJsonTest {
assertThat(deserializedAccount.balance).isEqualTo(account.balance)
assertThat(deserializedAccount.productName).isEqualTo(account.productName)
assertAccountTransactionsEqual(deserializedAccount.bookedTransactions, account.bookedTransactions)
assertTransactionsEqual(deserializedAccount.bookedTransactions, account.bookedTransactions)
}
private fun assertAccountTransactionsEqual(deserializedTransactions: List<AccountTransactionEntity>, transactions: List<AccountTransactionEntity>) {
private fun assertTransactionsEqual(deserializedTransactions: List<AccountTransactionEntity>, transactions: List<AccountTransactionEntity>) {
assertThat(deserializedTransactions.size).isEqualTo(transactions.size)
deserializedTransactions.forEach { deserializedTransaction ->
@ -223,14 +223,14 @@ class BankingPersistenceJsonTest {
Assert.fail("Could not find matching transaction for deserialized transaction $deserializedTransaction. transactions = $transactions")
}
else {
assertAccountTransactionsEqual(deserializedTransaction, transaction)
assertTransactionsEqual(deserializedTransaction, transaction)
}
}
}
private fun assertAccountTransactionsEqual(deserializedTransaction: AccountTransactionEntity, transaction: AccountTransactionEntity) {
private fun assertTransactionsEqual(deserializedTransaction: AccountTransactionEntity, transaction: AccountTransactionEntity) {
// to check if MapStruct created reference correctly
assertThat(deserializedTransaction.bankAccount.technicalId).isEqualTo(transaction.bankAccount.technicalId)
assertThat(deserializedTransaction.account.technicalId).isEqualTo(transaction.account.technicalId)
assertThat(deserializedTransaction.otherPartyName).isEqualTo(transaction.otherPartyName)
assertThat(deserializedTransaction.unparsedUsage).isEqualTo(transaction.unparsedUsage)

View File

@ -3,7 +3,7 @@ package net.dankito.banking.ui.android
import net.dankito.banking.ui.android.util.CurrentActivityTracker
import net.dankito.banking.ui.IRouter
import net.dankito.banking.ui.android.dialogs.*
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanResult
@ -20,10 +20,10 @@ open class RouterAndroid(protected val activityTracker: CurrentActivityTracker)
}
}
override fun getTanFromUserFromNonUiThread(customer: TypedCustomer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) {
override fun getTanFromUserFromNonUiThread(bank: TypedBankData, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) {
activityTracker.currentOrNextActivity { activity ->
activity.runOnUiThread {
EnterTanDialog().show(customer, tanChallenge, activity, false) { result ->
EnterTanDialog().show(bank, tanChallenge, activity, false) { result ->
callback(result)
}
}

View File

@ -44,7 +44,7 @@ open class AccountTransactionAdapter(protected val presenter: BankingPresenter)
viewHolder.txtvwAmount.showAmount(presenter, item.amount)
val iconUrl = item.bankAccount.customer.iconUrl
val iconUrl = item.account.bank.iconUrl
if (iconUrl != null && presenter.areAllAccountSelected) {
viewHolder.imgvwBankIcon.visibility = View.VISIBLE
viewHolder.imgvwBankIcon.setImageURI(Uri.parse(iconUrl))

View File

@ -12,7 +12,7 @@ import net.dankito.banking.ui.model.TypedBankAccount
import net.dankito.utils.android.ui.adapter.ListAdapter
open class BankAccountsAdapter(bankAccounts: List<TypedBankAccount>) : ListAdapter<TypedBankAccount>(bankAccounts) {
open class BankAccountsAdapter(accounts: List<TypedBankAccount>) : ListAdapter<TypedBankAccount>(accounts) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
@ -30,9 +30,9 @@ open class BankAccountsAdapter(bankAccounts: List<TypedBankAccount>) : ListAdapt
return view
}
protected open fun setIcon(bankAccount: TypedBankAccount, imgBankIcon: ImageView) {
protected open fun setIcon(account: TypedBankAccount, imgBankIcon: ImageView) {
try {
val iconUrl = bankAccount.customer.iconUrl
val iconUrl = account.bank.iconUrl
imgBankIcon.visibility = if (iconUrl == null) View.GONE else View.VISIBLE
imgBankIcon.setImageURI(Uri.parse(iconUrl))
} catch (e: Exception) {

View File

@ -3,13 +3,13 @@ package net.dankito.banking.ui.android.alerts
import android.content.Context
import androidx.appcompat.app.AlertDialog
import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.presenter.BankingPresenter
open class AskDeleteAccountAlert {
open fun show(bank: TypedCustomer, presenter: BankingPresenter, context: Context, accountDeleted: (() -> Unit)? = null) {
open fun show(bank: TypedBankData, presenter: BankingPresenter, context: Context, accountDeleted: (() -> Unit)? = null) {
AlertDialog.Builder(context)
.setTitle(context.getString(R.string.alert_ask_delete_account_title, bank.displayName))
.setMessage(context.getString(R.string.alert_ask_delete_account_message))

View File

@ -21,7 +21,7 @@ import net.dankito.banking.ui.android.adapter.TanMediumAdapter
import net.dankito.banking.ui.android.adapter.TanMethodsAdapter
import net.dankito.banking.ui.android.di.BankingComponent
import net.dankito.banking.ui.android.listener.ListItemSelectedListener
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.tan.*
import net.dankito.banking.ui.presenter.BankingPresenter
@ -41,7 +41,7 @@ open class EnterTanDialog : DialogFragment() {
}
protected lateinit var customer: TypedCustomer
protected lateinit var bank: TypedBankData
protected lateinit var tanChallenge: TanChallenge
@ -59,10 +59,10 @@ open class EnterTanDialog : DialogFragment() {
}
open fun show(customer: TypedCustomer, tanChallenge: TanChallenge, activity: AppCompatActivity,
open fun show(bank: TypedBankData, tanChallenge: TanChallenge, activity: AppCompatActivity,
fullscreen: Boolean = false, tanEnteredCallback: (EnterTanResult) -> Unit) {
this.customer = customer
this.bank = bank
this.tanChallenge = tanChallenge
this.tanEnteredCallback = tanEnteredCallback
@ -97,13 +97,13 @@ open class EnterTanDialog : DialogFragment() {
protected open fun setupSelectTanMethodView(rootView: View) {
val adapter = TanMethodsAdapter()
val tanMethodsWithoutUnsupported = customer.supportedTanMethods.filterNot { it.type == TanMethodType.ChipTanUsb } // USB tan generators are not supported on Android
val tanMethodsWithoutUnsupported = bank.supportedTanMethods.filterNot { it.type == TanMethodType.ChipTanUsb } // USB tan generators are not supported on Android
adapter.setItems(tanMethodsWithoutUnsupported)
rootView.findViewById<Spinner>(R.id.spnTanMethods)?.let { spinner ->
spinner.adapter = adapter
val selectedTanMethod = customer.selectedTanMethod
val selectedTanMethod = bank.selectedTanMethod
?: tanMethodsWithoutUnsupported.firstOrNull { it.type != TanMethodType.ChipTanManuell && it.type != TanMethodType.ChipTanUsb }
?: tanMethodsWithoutUnsupported.firstOrNull()
selectedTanMethod?.let { spinner.setSelection(adapter.getItems().indexOf(selectedTanMethod)) }
@ -120,12 +120,12 @@ open class EnterTanDialog : DialogFragment() {
}
protected open fun setupSelectTanMediumView(rootView: View) {
val tanMediaForTanMethod = presenter.getTanMediaForTanMethod(customer, tanChallenge.tanMethod)
val tanMediaForTanMethod = presenter.getTanMediaForTanMethod(bank, tanChallenge.tanMethod)
if (tanMediaForTanMethod.size > 1) {
rootView.lytTanMedium.visibility = View.VISIBLE
tanMediumAdapter.setItems(customer.tanMediaSorted)
tanMediumAdapter.setItems(bank.tanMediaSorted)
rootView.spnTanMedium.adapter = tanMediumAdapter
rootView.spnTanMedium.onItemSelectedListener = ListItemSelectedListener(tanMediumAdapter) { selectedTanMedium ->

View File

@ -51,7 +51,7 @@ open class SendMessageLogDialog : DialogFragment() {
}
protected open fun setupUI(rootView: View) {
val messageLog = presenter.getMessageLogForAccounts(presenter.customers).joinToString("\r\n\r\n")
val messageLog = presenter.getMessageLogForAccounts(presenter.allBanks).joinToString("\r\n\r\n")
if (messageLog.isBlank()) {
rootView.txtvwInfoNoMessageLogEntriesYet.visibility = View.VISIBLE

View File

@ -53,7 +53,7 @@ open class TransferMoneyDialog : DialogFragment() {
}
protected lateinit var bankAccount: TypedBankAccount
protected lateinit var account: TypedBankAccount
protected var preselectedValues: TransferMoneyData? = null
@ -108,16 +108,16 @@ open class TransferMoneyDialog : DialogFragment() {
}
protected open fun setupUI(rootView: View) {
val allBankAccountsSupportingTransferringMoney = presenter.bankAccountsSupportingTransferringMoney
bankAccount = preselectedValues?.account ?: allBankAccountsSupportingTransferringMoney.first()
val accountsSupportingTransferringMoney = presenter.accountsSupportingTransferringMoney
account = preselectedValues?.account ?: accountsSupportingTransferringMoney.first()
if (allBankAccountsSupportingTransferringMoney.size > 1) {
if (accountsSupportingTransferringMoney.size > 1) {
rootView.lytSelectBankAccount.visibility = View.VISIBLE
val adapter = BankAccountsAdapter(allBankAccountsSupportingTransferringMoney)
val adapter = BankAccountsAdapter(accountsSupportingTransferringMoney)
rootView.spnBankAccounts.adapter = adapter
rootView.spnBankAccounts.onItemSelectedListener = ListItemSelectedListener(adapter) { selectedBankAccount ->
this.bankAccount = selectedBankAccount
this.account = selectedBankAccount
setInstantPaymentControlsVisibility(rootView)
}
preselectedValues?.account?.let { rootView.spnBankAccounts.setSelection(adapter.getItems().indexOf(it)) }
@ -184,7 +184,7 @@ open class TransferMoneyDialog : DialogFragment() {
protected open fun setInstantPaymentControlsVisibility(rootView: View) {
rootView.lytInstantPayment.visibility =
if (bankAccount.supportsInstantPaymentMoneyTransfer) {
if (account.supportsInstantPaymentMoneyTransfer) {
View.VISIBLE
}
else {
@ -295,7 +295,7 @@ open class TransferMoneyDialog : DialogFragment() {
protected open fun transferMoney() {
getEnteredAmount()?.let { amount -> // should only come at this stage when a valid amount has been entered
val data = TransferMoneyData(
bankAccount,
account,
inputValidator.convertToAllowedSepaCharacters(edtxtRemitteeName.text.toString()),
edtxtRemitteeIban.text.toString().replace(" ", ""),
remitteeBic?.replace(" ", "") ?: "", // should always be != null at this point

View File

@ -13,7 +13,7 @@ import net.dankito.banking.ui.android.alerts.AskDeleteAccountAlert
import net.dankito.banking.ui.android.alerts.AskDismissChangesAlert
import net.dankito.banking.ui.android.di.BankingComponent
import net.dankito.banking.ui.android.views.FormEditText
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.presenter.BankingPresenter
import javax.inject.Inject
@ -25,7 +25,7 @@ open class BankSettingsDialog : DialogFragment() {
}
protected lateinit var bank: TypedCustomer
protected lateinit var bank: TypedBankData
@Inject
@ -38,7 +38,7 @@ open class BankSettingsDialog : DialogFragment() {
fun show(bank: TypedCustomer, activity: AppCompatActivity, fullscreen: Boolean = false) {
fun show(bank: TypedBankData, activity: AppCompatActivity, fullscreen: Boolean = false) {
this.bank = bank
val style = if (fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.FloatingDialog
@ -108,7 +108,7 @@ open class BankSettingsDialog : DialogFragment() {
bank.customerId = edtxtCustomerId.text
bank.password = edtxtPassword.text
presenter.accountUpdated(bank)
presenter.bankUpdated(bank)
}
protected open fun askToDismissChanges() {

View File

@ -170,8 +170,8 @@ class HomeFragment : Fragment() {
private fun initLogicAfterUiInitialized() {
presenter.addAccountsChangedListener { updateMenuItemsStateAndTransactionsToDisplay() } // on account addition or deletion may menu items' state changes
presenter.addSelectedBankAccountsChangedListener { updateMenuItemsStateAndTransactionsToDisplay() }
presenter.addBanksChangedListener { updateMenuItemsStateAndTransactionsToDisplay() } // on account addition or deletion may menu items' state changes
presenter.addSelectedAccountsChangedListener { updateMenuItemsStateAndTransactionsToDisplay() }
presenter.addRetrievedAccountTransactionsResponseListener { response ->
handleGetTransactionsResponseOffUiThread(response)
@ -183,15 +183,15 @@ class HomeFragment : Fragment() {
private fun updateMenuItemsStateAndTransactionsToDisplay() {
context?.asActivity()?.runOnUiThread {
mnitmSearchTransactions.isVisible = presenter.doSelectedBankAccountsSupportRetrievingAccountTransactions
mnitmUpdateTransactions.isVisible = presenter.doSelectedBankAccountsSupportRetrievingAccountTransactions
mnitmSearchTransactions.isVisible = presenter.doSelectedAccountsSupportRetrievingTransactions
mnitmUpdateTransactions.isVisible = presenter.doSelectedAccountsSupportRetrievingTransactions
updateTransactionsToDisplayOnUiThread()
}
}
private fun updateAccountsTransactions() {
presenter.updateSelectedBankAccountTransactionsAsync { }
presenter.updateSelectedAccountsTransactionsAsync { }
}
private fun handleGetTransactionsResponseOffUiThread(response: GetTransactionsResponse) {
@ -249,14 +249,14 @@ class HomeFragment : Fragment() {
private fun updateTransactionsToDisplayOnUiThread() {
transactionAdapter.items = presenter.searchSelectedAccountTransactions(appliedTransactionsFilter)
mnitmBalance.title = presenter.formatAmount(presenter.balanceOfSelectedBankAccounts)
mnitmBalance.isVisible = presenter.doSelectedBankAccountsSupportRetrievingBalance
mnitmBalance.title = presenter.formatAmount(presenter.balanceOfSelectedAccounts)
mnitmBalance.isVisible = presenter.doSelectedAccountsSupportRetrievingBalance
lytTransactionsSummary.visibility = if (presenter.doSelectedBankAccountsSupportRetrievingBalance) View.VISIBLE else View.GONE
lytTransactionsSummary.visibility = if (presenter.doSelectedAccountsSupportRetrievingBalance) View.VISIBLE else View.GONE
txtCountTransactions.text = context?.getString(R.string.fragment_home_count_transactions, transactionAdapter.items.size)
val sumOfDisplayedTransactions = if (appliedTransactionsFilter.isBlank()) presenter.balanceOfSelectedBankAccounts
val sumOfDisplayedTransactions = if (appliedTransactionsFilter.isBlank()) presenter.balanceOfSelectedAccounts
else transactionAdapter.items.map { it.amount }.sum()
txtTransactionsBalance.showAmount(presenter, sumOfDisplayedTransactions)
@ -266,9 +266,9 @@ class HomeFragment : Fragment() {
}
private fun setRecyclerViewAndNoTransactionsFetchedView() {
val transactionsRetrievalState = presenter.selectedBankAccountsTransactionRetrievalState
val transactionsRetrievalState = presenter.selectedAccountsTransactionRetrievalState
val haveTransactionsBeenRetrieved = transactionsRetrievalState == TransactionsRetrievalState.RetrievedTransactions
val noAccountsAddedYet = presenter.customers.isEmpty()
val noAccountsAddedYet = presenter.allBanks.isEmpty()
rcyvwAccountTransactions.visibility = if (haveTransactionsBeenRetrieved) View.VISIBLE else View.GONE
lytNoTransactionsFetched.visibility = if (haveTransactionsBeenRetrieved || noAccountsAddedYet) View.GONE else View.VISIBLE
@ -280,7 +280,7 @@ class HomeFragment : Fragment() {
TransactionsRetrievalState.AccountTypeNotSupported -> R.string.fragment_home_transactions_retrieval_state_account_type_not_supported
TransactionsRetrievalState.AccountDoesNotSupportFetchingTransactions -> R.string.fragment_home_transactions_retrieval_state_account_does_not_support_retrieving_transactions
TransactionsRetrievalState.NoTransactionsInRetrievedPeriod -> {
val account = presenter.selectedBankAccounts.first()
val account = presenter.selectedAccounts.first()
account.retrievedTransactionsFromOn?.let { messageArgs.add(RetrievedTransactionsPeriodDateFormat.format(it)) }
account.retrievedTransactionsUpTo?.let { messageArgs.add(RetrievedTransactionsPeriodDateFormat.format(it)) }
R.string.fragment_home_transactions_retrieval_state_no_transactions_in_retrieved_period
@ -292,12 +292,12 @@ class HomeFragment : Fragment() {
}
private fun setFetchAllTransactionsView() {
accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedBankAccountsForWhichNotAllTransactionsHaveBeenFetched
accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedAccountsForWhichNotAllTransactionsHaveBeenFetched
var floatingActionMenuBottomMarginResourceId = R.dimen.fab_margin_bottom_without_toolbar
if (doNotShowFetchAllTransactionsOverlay || accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty()
|| presenter.selectedBankAccountsTransactionRetrievalState != TransactionsRetrievalState.RetrievedTransactions) {
|| presenter.selectedAccountsTransactionRetrievalState != TransactionsRetrievalState.RetrievedTransactions) {
lytFetchAllTransactionsOverlay.visibility = View.GONE
}
else {
@ -312,9 +312,9 @@ class HomeFragment : Fragment() {
private fun fetchTransactions() {
presenter.selectedBankAccounts.forEach { account ->
presenter.selectedAccounts.forEach { account ->
if (account.haveAllTransactionsBeenFetched) {
presenter.updateBankAccountTransactionsAsync(account)
presenter.updateAccountTransactionsAsync(account)
}
else {
presenter.fetchAllAccountTransactionsAsync(account)

View File

@ -20,7 +20,7 @@ import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.android.dialogs.settings.BankSettingsDialog
import net.dankito.banking.ui.android.extensions.withIcon
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.presenter.BankingPresenter
import org.slf4j.LoggerFactory
@ -32,9 +32,9 @@ open class DrawerView(
) {
companion object {
private const val AccountLevel = 2
private const val BankLevel = 2
private const val BankAccountLevel = 7
private const val AccountLevel = 7
private const val AccountsSectionHeaderId = 1000L
private const val AllAccountsId = 1001L
@ -60,11 +60,11 @@ open class DrawerView(
setDefaultDrawerItems()
presenter.addAccountsChangedListener {
presenter.addBanksChangedListener {
activity.runOnUiThread { updateDrawerItems() }
}
presenter.addSelectedBankAccountsChangedListener {
presenter.addSelectedAccountsChangedListener {
activity.runOnUiThread { updateDrawerItems() }
}
@ -83,16 +83,16 @@ open class DrawerView(
PrimaryDrawerItem()
.withName(R.string.drawer_menu_all_bank_accounts_title)
.withIdentifier(AllAccountsId)
.withLevel(AccountLevel)
.withLevel(BankLevel)
.withSelected(true)
.withIcon(activity, GoogleMaterial.Icon.gmd_account_balance, R.color.primaryTextColor_Dark)
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAllBankAccounts() } }
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAllAccounts() } }
,
PrimaryDrawerItem()
.withName(R.string.add_account)
.withIdentifier(AddAccountId)
.withLevel(AccountLevel)
.withLevel(BankLevel)
.withIcon(activity, GoogleMaterial.Icon.gmd_add, R.color.primaryTextColor_Dark)
.withSelectable(false)
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.showAddAccountDialog() } }
@ -129,29 +129,29 @@ open class DrawerView(
}
private fun createAccountsDrawerItems(): List<IDrawerItem<*>> {
return presenter.customers.map { account ->
return presenter.allBanks.map { account ->
val accountItem = createAccountDrawerItem(account)
val bankAccountsItems = createBankAccountsDrawerItems(account).toMutableList()
bankAccountsItems.add(0, accountItem)
val accountsItems = createBankAccountsDrawerItems(account).toMutableList()
accountsItems.add(0, accountItem)
return@map bankAccountsItems
return@map accountsItems
}.flatten()
}
private fun createAccountDrawerItem(customer: TypedCustomer): IDrawerItem<*> {
private fun createAccountDrawerItem(bank: TypedBankData): IDrawerItem<*> {
val accountItem = AccountDrawerItem()
.withName(customer.displayName)
.withLevel(AccountLevel)
.withName(bank.displayName)
.withLevel(BankLevel)
.withSecondaryIcon(R.drawable.ic_baseline_settings_24)
.withSecondaryIconColor(activity, R.color.primaryTextColor_Dark)
.withOnSecondaryIconClickedListener { closeDrawerAndEditAccount(customer) }
.withIcon(customer.iconUrl ?: "")
.withSelected(presenter.isSingleSelectedAccount(customer))
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAccount(customer) } }
.withOnSecondaryIconClickedListener { closeDrawerAndEditAccount(bank) }
.withIcon(bank.iconUrl ?: "")
.withSelected(presenter.isSingleSelectedBank(bank))
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedBank(bank) } }
if (customer.iconUrl == null) {
if (bank.iconUrl == null) {
accountItem.withIcon(activity, FontAwesome.Icon.faw_piggy_bank, R.color.primaryTextColor_Dark)
}
@ -159,13 +159,13 @@ open class DrawerView(
return accountItem
}
private fun createBankAccountsDrawerItems(customer: TypedCustomer): List<IDrawerItem<*>> {
return customer.accounts.map { bankAccount ->
private fun createBankAccountsDrawerItems(bank: TypedBankData): List<IDrawerItem<*>> {
return bank.accounts.map { account ->
SecondaryDrawerItem()
.withName(bankAccount.displayName)
.withLevel(BankAccountLevel)
.withSelected(presenter.isSingleSelectedBankAccount(bankAccount))
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedBankAccount(bankAccount) } }
.withName(account.displayName)
.withLevel(AccountLevel)
.withSelected(presenter.isSingleSelectedAccount(account))
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAccount(account) } }
}
}
@ -175,14 +175,14 @@ open class DrawerView(
return false
}
private fun closeDrawerAndEditAccount(customer: TypedCustomer) {
private fun closeDrawerAndEditAccount(bank: TypedBankData) {
closeDrawer()
editAccount(customer)
editAccount(bank)
}
private fun editAccount(customer: TypedCustomer) {
BankSettingsDialog().show(customer, activity, true)
private fun editAccount(bank: TypedBankData) {
BankSettingsDialog().show(bank, activity, true)
}
private fun showAppVersion(navigationHeaderView: View?) {

View File

@ -35,7 +35,7 @@ open class MainActivityFloatingActionMenuButton(
init {
setupButtons(floatingActionMenu)
presenter.addAccountsChangedListener {
presenter.addBanksChangedListener {
fabTransferMoney.context.asActivity()?.runOnUiThread {
checkIfThereAreAccountsThatCanTransferMoney()
}
@ -65,9 +65,9 @@ open class MainActivityFloatingActionMenuButton(
protected open fun checkIfThereAreAccountsThatCanTransferMoney() {
fabTransferMoney.isEnabled = presenter.hasBankAccountsSupportTransferringMoney
fabTransferMoney.isEnabled = presenter.hasAccountsSupportTransferringMoney
fabTransferMoneyFromPdf.isEnabled = presenter.hasBankAccountsSupportTransferringMoney
fabTransferMoneyFromPdf.isEnabled = presenter.hasAccountsSupportTransferringMoney
}

View File

@ -23,7 +23,7 @@ open class MainMenuBar(protected val presenter: BankingPresenter) : View() {
init {
presenter.addAccountsChangedListener {
presenter.addBanksChangedListener {
runLater {
checkIfThereAreAccountsThatCanTransferMoney()
}
@ -68,7 +68,7 @@ open class MainMenuBar(protected val presenter: BankingPresenter) : View() {
protected open fun checkIfThereAreAccountsThatCanTransferMoney() {
areAccountsThatCanTransferMoneyAdded.value = presenter.hasBankAccountsSupportTransferringMoney
areAccountsThatCanTransferMoneyAdded.value = presenter.hasAccountsSupportTransferringMoney
}
protected open fun transferMoneyWithDataFromPdf() {

View File

@ -4,7 +4,7 @@ import net.dankito.banking.ui.IRouter
import net.dankito.banking.ui.javafx.dialogs.AddAccountDialog
import net.dankito.banking.ui.javafx.dialogs.cashtransfer.TransferMoneyDialog
import net.dankito.banking.ui.javafx.dialogs.tan.EnterTanDialog
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanResult
@ -22,9 +22,9 @@ open class RouterJavaFx : IRouter {
AddAccountDialog(presenter).show(messages["add.account.dialog.title"])
}
override fun getTanFromUserFromNonUiThread(customer: TypedCustomer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) {
override fun getTanFromUserFromNonUiThread(bank: TypedBankData, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) {
FX.runAndWait {
EnterTanDialog(customer, tanChallenge, presenter) { result ->
EnterTanDialog(bank, tanChallenge, presenter) { result ->
callback(result)
}.show(messages["enter.tan.dialog.title"])
}

View File

@ -109,8 +109,8 @@ open class AccountTransactionsControlView(
protected open fun initLogic() {
presenter.addAccountsChangedListener { runLater { accountsChanged() } }
presenter.addSelectedBankAccountsChangedListener { selectedBankAccountsChanged() }
presenter.addBanksChangedListener { runLater { accountsChanged() } }
presenter.addSelectedAccountsChangedListener { selectedBankAccountsChanged() }
checkIfSupportsTransferringMoneyOnUiThread()
checkIfSupportsRetrievingAccountTransactionsOnUiThread()
@ -130,13 +130,13 @@ open class AccountTransactionsControlView(
protected open fun checkIfSupportsTransferringMoneyOnUiThread() {
supportsTransferringMoney.value = presenter.hasBankAccountsSupportTransferringMoney
supportsTransferringMoney.value = presenter.hasAccountsSupportTransferringMoney
}
protected open fun checkIfSupportsRetrievingAccountTransactionsOnUiThread() {
supportsRetrievingBalance.value = presenter.doSelectedBankAccountsSupportRetrievingBalance
supportsRetrievingBalance.value = presenter.doSelectedAccountsSupportRetrievingBalance
supportsRetrievingAccountTransactions.value = presenter.doSelectedBankAccountsSupportRetrievingAccountTransactions
supportsRetrievingAccountTransactions.value = presenter.doSelectedAccountsSupportRetrievingTransactions
}
protected open fun updateAccountTransactions(processingIndicatorButton: ProcessingIndicatorButton) {

View File

@ -31,7 +31,7 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi
init {
presenter.addSelectedBankAccountsChangedListener { handleSelectedBankAccountsChanged(it) }
presenter.addSelectedAccountsChangedListener { handleSelectedBankAccountsChanged(it) }
presenter.addRetrievedAccountTransactionsResponseListener { response ->
handleGetTransactionsResponseOffUiThread(response)
@ -39,7 +39,7 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi
transactionsFilter.addListener { _, _, newValue -> updateTransactionsToDisplay(newValue) }
handleSelectedBankAccountsChanged(presenter.selectedBankAccounts) // so that isAccountSelected and transactionsToDisplay get set
handleSelectedBankAccountsChanged(presenter.selectedAccounts) // so that isAccountSelected and transactionsToDisplay get set
}
@ -135,7 +135,7 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi
transactionsToDisplay.setAll(presenter.searchSelectedAccountTransactions(filter))
// TODO: if transactions are filtered calculate and show balance of displayed transactions?
balance.value = presenter.formatAmount(presenter.balanceOfSelectedBankAccounts)
balance.value = presenter.formatAmount(presenter.balanceOfSelectedAccounts)
}
protected open fun handleGetTransactionsResponseOffUiThread(response: GetTransactionsResponse) {

View File

@ -10,14 +10,14 @@ import javafx.scene.input.KeyCode
import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService
import net.dankito.banking.ui.javafx.model.AccountsAccountTreeItem
import net.dankito.banking.ui.javafx.model.AccountsRootTreeItem
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.presenter.BankingPresenter
import tornadofx.*
import tornadofx.FX.Companion.messages
open class AccountsTreeView(customers: ObservableList<TypedCustomer>, protected val presenter: BankingPresenter)
: TreeView<String>(AccountsRootTreeItem(customers)) {
open class AccountsTreeView(banks: ObservableList<TypedBankData>, protected val presenter: BankingPresenter)
: TreeView<String>(AccountsRootTreeItem(banks)) {
protected var currentMenu: ContextMenu? = null
@ -64,7 +64,7 @@ open class AccountsTreeView(customers: ObservableList<TypedCustomer>, protected
}
protected open fun askIfAccountShouldBeDeleted(treeItem: AccountsAccountTreeItem) {
val account = treeItem.customer
val account = treeItem.bank
val selectedButton = JavaFxDialogService().showDialog(
Alert.AlertType.WARNING,

View File

@ -15,11 +15,11 @@ import tornadofx.*
open class AccountsView(protected val presenter: BankingPresenter) : View() {
protected val accounts = FXCollections.observableArrayList(presenter.customers)
protected val accounts = FXCollections.observableArrayList(presenter.allBanks)
init {
presenter.addAccountsChangedListener {
presenter.addBanksChangedListener {
runLater {
accounts.setAll(it)
}
@ -69,9 +69,9 @@ open class AccountsView(protected val presenter: BankingPresenter) : View() {
protected open fun selectedBankAccountChanged(accountTreeItem: TreeItem<String>?) {
accountTreeItem?.let {
when (accountTreeItem) {
is AccountsBankAccountTreeItem -> presenter.selectedBankAccount(accountTreeItem.bankAccount)
is AccountsAccountTreeItem -> presenter.selectedAccount(accountTreeItem.customer)
else -> presenter.selectedAllBankAccounts()
is AccountsBankAccountTreeItem -> presenter.selectedAccount(accountTreeItem.account)
is AccountsAccountTreeItem -> presenter.selectedBank(accountTreeItem.bank)
else -> presenter.selectedAllAccounts()
}
}
}

View File

@ -256,7 +256,7 @@ open class AddAccountDialog(protected val presenter: BankingPresenter) : Window(
handleSuccessfullyAddedAccountResultOnUiThread(response)
}
else {
val account = response.customer
val account = response.bank
checkEnteredCredentialsResult.value = String.format(messages["add.account.dialog.error.could.not.add.account"],
account.bankCode, account.customerId, response.errorToShowToUser)
@ -273,7 +273,7 @@ open class AddAccountDialog(protected val presenter: BankingPresenter) : Window(
val userSelection = dialogService.showDialog(Alert.AlertType.CONFIRMATION, message, null, currentStage, ButtonType.YES, ButtonType.NO)
when (userSelection) {
ButtonType.YES -> presenter.fetchAllAccountTransactionsAsync(response.customer) { }
ButtonType.YES -> presenter.fetchAllAccountTransactionsAsync(response.bank) { }
else -> { } // nothing to do then, simply close dialog
}

View File

@ -47,7 +47,7 @@ open class TransferMoneyDialog @JvmOverloads constructor(
}
protected val bankAccountsSupportingTransferringMoney = FXCollections.observableArrayList(presenter.bankAccounts.filter { it.supportsTransferringMoney })
protected val bankAccountsSupportingTransferringMoney = FXCollections.observableArrayList(presenter.allAccounts.filter { it.supportsTransferringMoney })
protected val selectedBankAccount = SimpleObjectProperty<TypedBankAccount>(preselectedValues?.account ?: bankAccountsSupportingTransferringMoney.firstOrNull())
@ -114,7 +114,7 @@ open class TransferMoneyDialog @JvmOverloads constructor(
cellFormat {
text = it.displayName
it.customer.iconUrl?.let { iconUrl ->
it.bank.iconUrl?.let { iconUrl ->
graphic = ImageView(iconUrl)?.apply {
this.fitHeight = BankIconSize
this.fitWidth = BankIconSize
@ -346,10 +346,10 @@ open class TransferMoneyDialog @JvmOverloads constructor(
protected open fun transferMoney() {
remitteeBank.value?.let {
val bankAccount = selectedBankAccount.value
val account = selectedBankAccount.value
val data = TransferMoneyData(
bankAccount,
account,
inputValidator.convertToAllowedSepaCharacters(remitteeName.value),
remitteeIban.value.replace(" ", ""),
remitteeBic.value.replace(" ", ""),
@ -360,14 +360,14 @@ open class TransferMoneyDialog @JvmOverloads constructor(
presenter.transferMoneyAsync(data) {
runLater {
handleTransferMoneyResultOnUiThread(bankAccount, data, it)
handleTransferMoneyResultOnUiThread(account, data, it)
}
}
}
}
protected open fun handleTransferMoneyResultOnUiThread(bankAccount: TypedBankAccount, transferData: TransferMoneyData, response: BankingClientResponse) {
val currency = bankAccount.currency
protected open fun handleTransferMoneyResultOnUiThread(account: TypedBankAccount, transferData: TransferMoneyData, response: BankingClientResponse) {
val currency = account.currency
if (response.successful) {
dialogService.showInfoMessage(String.format(messages["transfer.money.dialog.message.transfer.cash.success"],

View File

@ -10,7 +10,7 @@ import javafx.scene.text.FontWeight
import net.dankito.banking.ui.javafx.dialogs.tan.controls.ChipTanFlickerCodeView
import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService
import net.dankito.banking.ui.javafx.dialogs.tan.controls.TanImageView
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.tan.*
import net.dankito.banking.ui.presenter.BankingPresenter
@ -20,7 +20,7 @@ import tornadofx.*
open class EnterTanDialog(
protected val customer: TypedCustomer,
protected val bank: TypedBankData,
protected val challenge: TanChallenge,
protected val presenter: BankingPresenter,
protected val tanEnteredCallback: (EnterTanResult) -> Unit
@ -41,11 +41,11 @@ open class EnterTanDialog(
protected var tanImageView: TanImageView? = null
protected val tanMethodsWithoutUnsupported = customer.supportedTanMethods.filterNot { it.type == TanMethodType.ChipTanUsb } // USB tan generators are not supported
protected val tanMethodsWithoutUnsupported = bank.supportedTanMethods.filterNot { it.type == TanMethodType.ChipTanUsb } // USB tan generators are not supported
protected val selectedTanMethod = SimpleObjectProperty<TanMethod>(customer.selectedTanMethod ?: tanMethodsWithoutUnsupported.firstOrNull { it.displayName.contains("manuell", true) == false } ?: tanMethodsWithoutUnsupported.firstOrNull())
protected val selectedTanMethod = SimpleObjectProperty<TanMethod>(bank.selectedTanMethod ?: tanMethodsWithoutUnsupported.firstOrNull { it.displayName.contains("manuell", true) == false } ?: tanMethodsWithoutUnsupported.firstOrNull())
protected val selectedTanMedium = SimpleObjectProperty<TanMedium>(customer.tanMediaSorted.firstOrNull())
protected val selectedTanMedium = SimpleObjectProperty<TanMedium>(bank.tanMediaSorted.firstOrNull())
protected val enteredTan = SimpleStringProperty("")
@ -84,13 +84,13 @@ open class EnterTanDialog(
}
}
if (customer.tanMediaSorted.isNotEmpty()) {
if (bank.tanMediaSorted.isNotEmpty()) {
field(messages["enter.tan.dialog.select.tan.medium"]) {
label.apply {
font = Font.font(font.family, FontWeight.BLACK, font.size)
}
combobox(selectedTanMedium, customer.tanMediaSorted) {
combobox(selectedTanMedium, bank.tanMediaSorted) {
cellFormat {
text = it.displayName
}

View File

@ -2,10 +2,10 @@ package net.dankito.banking.ui.javafx.model
import javafx.scene.Node
import javafx.scene.image.ImageView
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
open class AccountsAccountTreeItem(val customer: TypedCustomer) : AccountsTreeItemBase(customer.displayName) {
open class AccountsAccountTreeItem(val bank: TypedBankData) : AccountsTreeItemBase(bank.displayName) {
companion object {
private const val IconSize = 16.0
@ -17,13 +17,13 @@ open class AccountsAccountTreeItem(val customer: TypedCustomer) : AccountsTreeIt
graphic = createIconImageView()
customer.accounts.forEach { bankAccount ->
children.add(AccountsBankAccountTreeItem(bankAccount))
bank.accounts.forEach { account ->
children.add(AccountsBankAccountTreeItem(account))
}
}
protected open fun createIconImageView(): Node? {
customer.iconUrl?.let {
bank.iconUrl?.let {
val iconImageView = ImageView(it)
iconImageView.fitHeight = IconSize

View File

@ -3,4 +3,4 @@ package net.dankito.banking.ui.javafx.model
import net.dankito.banking.ui.model.TypedBankAccount
open class AccountsBankAccountTreeItem(val bankAccount: TypedBankAccount) : AccountsTreeItemBase(bankAccount.displayName)
open class AccountsBankAccountTreeItem(val account: TypedBankAccount) : AccountsTreeItemBase(account.displayName)

View File

@ -2,26 +2,26 @@ package net.dankito.banking.ui.javafx.model
import javafx.collections.ListChangeListener
import javafx.collections.ObservableList
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import tornadofx.FX.Companion.messages
import tornadofx.get
import tornadofx.runLater
open class AccountsRootTreeItem(customers: ObservableList<TypedCustomer>) : AccountsTreeItemBase(messages["accounts.view.all.accounts"]) {
open class AccountsRootTreeItem(banks: ObservableList<TypedBankData>) : AccountsTreeItemBase(messages["accounts.view.all.accounts"]) {
init {
setAccounts(customers)
setBanks(banks)
customers.addListener(ListChangeListener {
runLater { setAccounts(customers) }
banks.addListener(ListChangeListener {
runLater { setBanks(banks) }
})
}
protected open fun setAccounts(customers: List<TypedCustomer>) {
isExpanded = customers.isNotEmpty()
protected open fun setBanks(banks: List<TypedBankData>) {
isExpanded = banks.isNotEmpty()
children.setAll(customers.map { AccountsAccountTreeItem(it) })
children.setAll(banks.map { AccountsAccountTreeItem(it) })
}
}

View File

@ -6,14 +6,14 @@ import net.dankito.utils.multiplatform.File
interface IBankingPersistence {
fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>)
fun saveOrUpdateBank(bank: TypedBankData, allBanks: List<TypedBankData>)
fun deleteAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>)
fun deleteBank(bank: TypedBankData, allBanks: List<TypedBankData>)
fun readPersistedAccounts(): List<TypedCustomer>
fun readPersistedBanks(): List<TypedBankData>
fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List<IAccountTransaction>)
fun saveOrUpdateAccountTransactions(account: TypedBankAccount, transactions: List<IAccountTransaction>)
fun saveUrlToFile(url: String, file: File)

View File

@ -6,20 +6,20 @@ import net.dankito.utils.multiplatform.File
open class NoOpBankingPersistence : IBankingPersistence {
override fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
override fun saveOrUpdateBank(bank: TypedBankData, allBanks: List<TypedBankData>) {
}
override fun deleteAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
override fun deleteBank(bank: TypedBankData, allBanks: List<TypedBankData>) {
}
override fun readPersistedAccounts(): List<TypedCustomer> {
override fun readPersistedBanks(): List<TypedBankData> {
return listOf()
}
override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List<IAccountTransaction>) {
override fun saveOrUpdateAccountTransactions(account: TypedBankAccount, transactions: List<IAccountTransaction>) {
}

View File

@ -1,6 +1,6 @@
package net.dankito.banking.ui
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanResult
import net.dankito.banking.ui.model.tan.TanChallenge
@ -9,7 +9,7 @@ import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
interface BankingClientCallback {
fun enterTan(customer: TypedCustomer, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit)
fun enterTan(bank: TypedBankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit)
/**
* This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator.

View File

@ -23,8 +23,8 @@ interface IBankingClient {
fun transferMoneyAsync(data: TransferMoneyData, callback: (BankingClientResponse) -> Unit)
fun dataChanged(customer: TypedCustomer)
fun dataChanged(bank: TypedBankData)
fun deletedAccount(customer: TypedCustomer, wasLastAccountWithThisCredentials: Boolean)
fun deletedBank(bank: TypedBankData, wasLastAccountWithThisCredentials: Boolean)
}

View File

@ -1,14 +1,14 @@
package net.dankito.banking.ui
import net.dankito.utils.multiplatform.File
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.util.IAsyncRunner
interface IBankingClientCreator {
fun createClient(
customer: TypedCustomer,
bank: TypedBankData,
dataFolder: File,
asyncRunner: IAsyncRunner,
callback: BankingClientCallback

View File

@ -1,6 +1,6 @@
package net.dankito.banking.ui
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanResult
@ -13,7 +13,7 @@ interface IRouter {
fun showAddAccountDialog(presenter: BankingPresenter)
fun getTanFromUserFromNonUiThread(customer: TypedCustomer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit)
fun getTanFromUserFromNonUiThread(bank: TypedBankData, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit)
fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit)

View File

@ -2,12 +2,10 @@ package net.dankito.banking.ui.model
import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.utils.multiplatform.Date
import net.dankito.utils.multiplatform.DateFormatStyle
import net.dankito.utils.multiplatform.DateFormatter
open class AccountTransaction(
override val bankAccount: TypedBankAccount,
override val account: TypedBankAccount,
override val amount: BigDecimal,
override val currency: String,
override val unparsedUsage: String,
@ -51,15 +49,15 @@ open class AccountTransaction(
/* convenience constructors for languages not supporting default values */
constructor(bankAccount: BankAccount, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
: this(bankAccount, amount, "EUR", unparsedUsage, valueDate,
constructor(account: BankAccount, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
: this(account, amount, "EUR", unparsedUsage, valueDate,
otherPartyName, null, null, bookingText, valueDate)
constructor(bankAccount: BankAccount, amount: BigDecimal, currency: String, unparsedUsage: String, bookingDate: Date,
constructor(account: BankAccount, amount: BigDecimal, currency: String, unparsedUsage: String, bookingDate: Date,
otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?,
bookingText: String?, valueDate: Date)
: this(bankAccount, amount, currency, unparsedUsage, bookingDate,
: this(account, amount, currency, unparsedUsage, bookingDate,
otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate,
0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null)

View File

@ -7,7 +7,7 @@ import kotlin.jvm.JvmOverloads
open class BankAccount @JvmOverloads constructor(
override val customer: TypedCustomer,
override val bank: TypedBankData,
override var identifier: String,
override var accountHolderName: String,
override var iban: String?,
@ -28,14 +28,14 @@ open class BankAccount @JvmOverloads constructor(
override var unbookedTransactions: List<Any> = listOf()
) : TypedBankAccount {
internal constructor() : this(Customer(), null, "") // for object deserializers
internal constructor() : this(BankData(), null, "") // for object deserializers
/* convenience constructors for languages not supporting default values */
constructor(customer: TypedCustomer, productName: String?, identifier: String) : this(customer, productName, identifier, BankAccountType.Girokonto)
constructor(bank: TypedBankData, productName: String?, identifier: String) : this(bank, productName, identifier, BankAccountType.Girokonto)
constructor(customer: TypedCustomer, productName: String?, identifier: String, type: BankAccountType = BankAccountType.Girokonto, balance: BigDecimal = BigDecimal.Zero)
: this(customer, identifier, "", null, null, "", balance, "EUR", type, productName)
constructor(bank: TypedBankData, productName: String?, identifier: String, type: BankAccountType = BankAccountType.Girokonto, balance: BigDecimal = BigDecimal.Zero)
: this(bank, identifier, "", null, null, "", balance, "EUR", type, productName)
override var technicalId: String = UUID.random()

View File

@ -5,7 +5,7 @@ import net.dankito.banking.ui.model.tan.TanMethod
import net.dankito.utils.multiplatform.UUID
open class Customer(
open class BankData(
override var bankCode: String,
override var customerId: String,
override var password: String,
@ -16,7 +16,7 @@ open class Customer(
override var userId: String = customerId,
override var iconUrl: String? = null,
override var accounts: List<TypedBankAccount> = listOf()
) : TypedCustomer {
) : TypedBankData {
internal constructor() : this("", "", "", "", "", "", "") // for object deserializers

View File

@ -13,7 +13,7 @@ interface IAccountTransaction {
}
val bankAccount: IBankAccount<*>
val account: IBankAccount<*>
val amount: BigDecimal
val currency: String
val unparsedUsage: String
@ -62,15 +62,15 @@ interface IAccountTransaction {
get() = otherPartyName.isNullOrBlank() == false /* && type != "ENTGELTABSCHLUSS" && type != "AUSZAHLUNG" */ // TODO
val canCreateMoneyTransferFrom: Boolean
get() = otherPartyAccountId != null && bankAccount.supportsTransferringMoney
get() = otherPartyAccountId != null && account.supportsTransferringMoney
val usage: String
get() = sepaUsage ?: unparsedUsage
fun buildTransactionIdentifier() : String {
if (bankAccount != null) {
return "${bankAccount.technicalId} ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedUsage $otherPartyName $otherPartyBankCode $otherPartyAccountId"
if (account != null) {
return "${account.technicalId} ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedUsage $otherPartyName $otherPartyBankCode $otherPartyAccountId"
}
else { // happens for derived classes during initialization. These have to set technicalId after initialization by themselves
return "<uninitialized_bank_acccount> ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedUsage $otherPartyName $otherPartyBankCode $otherPartyAccountId"
@ -83,7 +83,7 @@ interface IAccountTransaction {
if (this === other) return true
if (other !is IAccountTransaction) return false
if (bankAccount != other.bankAccount) return false
if (account != other.account) return false
if (amount != other.amount) return false
if (currency != other.currency) return false
if (unparsedUsage != other.unparsedUsage) return false
@ -98,7 +98,7 @@ interface IAccountTransaction {
}
fun calculateHashCode(): Int {
var result = bankAccount.hashCode()
var result = account.hashCode()
result = 31 * result + amount.hashCode()
result = 31 * result + currency.hashCode()
result = 31 * result + unparsedUsage.hashCode()

View File

@ -8,7 +8,7 @@ typealias TypedBankAccount = IBankAccount<IAccountTransaction>
interface IBankAccount<TTransaction: IAccountTransaction> : OrderedDisplayable {
val customer: ICustomer<*, *>
val bank: IBankData<*, *>
var identifier: String
var accountHolderName: String
var iban: String?

View File

@ -8,10 +8,10 @@ import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.utils.multiplatform.sum
typealias TypedCustomer = ICustomer<IBankAccount<IAccountTransaction>, IAccountTransaction>
typealias TypedBankData = IBankData<IBankAccount<IAccountTransaction>, IAccountTransaction>
interface ICustomer<TAccount: IBankAccount<TAccountTransaction>, TAccountTransaction: IAccountTransaction> : OrderedDisplayable {
interface IBankData<TAccount: IBankAccount<TAccountTransaction>, TAccountTransaction: IAccountTransaction> : OrderedDisplayable {
var bankCode: String
var customerId: String

View File

@ -6,7 +6,7 @@ import net.dankito.utils.multiplatform.Date
open class MessageLogEntry(
val message: String,
val time: Date,
val customer: TypedCustomer
val bank: TypedBankData
) {
override fun toString(): String {

View File

@ -5,8 +5,8 @@ enum class SelectedAccountType {
AllAccounts,
SingleAccount,
SingleBank,
SingleBankAccount
SingleAccount
}

View File

@ -7,19 +7,19 @@ import net.dankito.utils.multiplatform.Date
open class DefaultModelCreator : IModelCreator {
override fun createCustomer(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String, bic: String,
customerName: String, userId: String, iconUrl: String?): TypedCustomer {
override fun createBank(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String, bic: String,
customerName: String, userId: String, iconUrl: String?): TypedBankData {
return Customer(bankCode, customerId, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl)
return BankData(bankCode, customerId, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl)
}
override fun createBankAccount(customer: TypedCustomer, productName: String?, identifier: String): TypedBankAccount {
return BankAccount(customer, productName, identifier)
override fun createAccount(bank: TypedBankData, productName: String?, identifier: String): TypedBankAccount {
return BankAccount(bank, productName, identifier)
}
override fun createTransaction(
bankAccount: TypedBankAccount,
account: TypedBankAccount,
amount: BigDecimal,
currency: String,
unparsedUsage: String,
@ -55,7 +55,7 @@ open class DefaultModelCreator : IModelCreator {
relatedReferenceNumber: String?
) : IAccountTransaction {
return AccountTransaction(bankAccount, amount, currency, unparsedUsage, bookingDate,
return AccountTransaction(account, amount, currency, unparsedUsage, bookingDate,
otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, statementNumber, sequenceNumber,
openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier,
originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient,

View File

@ -8,15 +8,15 @@ import net.dankito.utils.multiplatform.Date
interface IModelCreator {
fun createCustomer(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String, bic: String,
customerName: String = "", userId: String = customerId, iconUrl: String? = null): TypedCustomer
fun createBank(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String, bic: String,
customerName: String = "", userId: String = customerId, iconUrl: String? = null): TypedBankData
fun createBankAccount(customer: TypedCustomer, productName: String?, identifier: String) : TypedBankAccount
fun createAccount(bank: TypedBankData, productName: String?, identifier: String) : TypedBankAccount
fun createTransaction(
bankAccount: TypedBankAccount,
account: TypedBankAccount,
amount: BigDecimal,
currency: String,
unparsedUsage: String,

View File

@ -18,7 +18,7 @@ open class TransferMoneyData(
fun fromAccountTransactionWithoutAmountAndUsage(transaction: IAccountTransaction): TransferMoneyData {
return TransferMoneyData(
transaction.bankAccount as TypedBankAccount,
transaction.account as TypedBankAccount,
transaction.otherPartyName ?: "",
transaction.otherPartyAccountId ?: "",
transaction.otherPartyBankCode ?: "",
@ -29,7 +29,7 @@ open class TransferMoneyData(
fun fromAccountTransaction(transaction: IAccountTransaction): TransferMoneyData {
return TransferMoneyData(
transaction.bankAccount as TypedBankAccount,
transaction.account as TypedBankAccount,
transaction.otherPartyName ?: "",
transaction.otherPartyAccountId ?: "",
transaction.otherPartyBankCode ?: "",

View File

@ -4,24 +4,24 @@ import net.dankito.banking.ui.model.*
open class AddAccountResponse(
open val customer: TypedCustomer,
open val bank: TypedBankData,
retrievedData: List<RetrievedAccountData> = listOf(),
errorToShowToUser: String?,
userCancelledAction: Boolean = false
) : GetTransactionsResponse(retrievedData, errorToShowToUser, userCancelledAction) {
constructor(customer: TypedCustomer, errorToShowToUser: String?) : this(customer, listOf(), errorToShowToUser)
constructor(bank: TypedBankData, errorToShowToUser: String?) : this(bank, listOf(), errorToShowToUser)
override val successful: Boolean
get() = super.successful && customer.accounts.isNotEmpty()
get() = super.successful && bank.accounts.isNotEmpty()
open val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean
get() = retrievedData.isNotEmpty() && retrievedData.any { it.successfullyRetrievedData }
override fun toString(): String {
return customer.toString() + " " + super.toString()
return bank.toString() + " " + super.toString()
}
}

View File

@ -59,31 +59,31 @@ open class BankingPresenter(
}
protected val bankingClientsForAccounts = mutableMapOf<TypedCustomer, IBankingClient>()
protected val bankingClientsForBanks = mutableMapOf<TypedBankData, IBankingClient>()
protected var selectedBankAccountsField = mutableListOf<TypedBankAccount>()
protected var _selectedAccounts = mutableListOf<TypedBankAccount>()
protected var selectedAccountType = SelectedAccountType.AllAccounts
protected var saveAccountOnNextEnterTanInvocation = false
protected val accountsChangedListeners = mutableListOf<(List<TypedCustomer>) -> Unit>()
protected val banksChangedListeners = mutableListOf<(List<TypedBankData>) -> Unit>()
protected val retrievedAccountTransactionsResponseListeners = mutableListOf<(GetTransactionsResponse) -> Unit>()
protected val selectedBankAccountsChangedListeners = mutableListOf<(List<TypedBankAccount>) -> Unit>()
protected val selectedAccountsChangedListeners = mutableListOf<(List<TypedBankAccount>) -> Unit>()
protected val callback: BankingClientCallback = object : BankingClientCallback {
override fun enterTan(customer: TypedCustomer, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
override fun enterTan(bank: TypedBankData, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) {
if (saveAccountOnNextEnterTanInvocation) {
persistAccountOffUiThread(customer)
persistBankOffUiThread(bank)
saveAccountOnNextEnterTanInvocation = false
}
router.getTanFromUserFromNonUiThread(customer, tanChallenge, this@BankingPresenter) { result ->
router.getTanFromUserFromNonUiThread(bank, tanChallenge, this@BankingPresenter) { result ->
if (result.changeTanMethodTo != null || result.changeTanMediumTo != null) { // then either selected TAN medium or method will change -> save account on next call to enterTan() as then changes will be visible
saveAccountOnNextEnterTanInvocation = true
}
@ -104,7 +104,7 @@ open class BankingPresenter(
init {
asyncRunner.runAsync {
readAppSettings()
readPersistedAccounts()
readPersistedBanks()
updateAccountsTransactionsIfNoTanIsRequiredAsync()
}
@ -116,81 +116,81 @@ open class BankingPresenter(
}
protected open fun readPersistedAccounts() {
protected open fun readPersistedBanks() {
try {
val deserializedAccounts = persister.readPersistedAccounts()
val deserializedBanks = persister.readPersistedBanks()
deserializedAccounts.forEach { customer ->
val newClient = bankingClientCreator.createClient(customer, dataFolder, asyncRunner, callback)
deserializedBanks.forEach { bank ->
val newClient = bankingClientCreator.createClient(bank, dataFolder, asyncRunner, callback)
addClientForAccount(customer, newClient)
addClientForBank(bank, newClient)
customer.accounts.forEach { account ->
bank.accounts.forEach { account ->
if (account.haveAllTransactionsBeenFetched == false && didFetchAllTransactionsStoredOnBankServer(account, listOf())) {
account.haveAllTransactionsBeenFetched = true // no need to save account, just delays app start-up, as even if account doesn't get saved during app run, haveAllTransactionsBeenFetched gets restored on next app run
}
}
}
callAccountsChangedListeners()
callBanksChangedListeners()
selectedAllBankAccounts() // TODO: save last selected bank account(s)
selectedAllAccounts() // TODO: save last selected bank account(s)
} catch (e: Exception) {
log.error(e) { "Could not deserialize persisted accounts with persister $persister" }
log.error(e) { "Could not deserialize persisted banks with persister $persister" }
}
}
protected open fun addClientForAccount(customer: TypedCustomer, client: IBankingClient) {
bankingClientsForAccounts.put(customer, client)
protected open fun addClientForBank(bank: TypedBankData, client: IBankingClient) {
bankingClientsForBanks.put(bank, client)
}
// TODO: move BankInfo out of fints4k
open fun addAccountAsync(bankInfo: BankInfo, customerId: String, password: String, callback: (AddAccountResponse) -> Unit) {
val customer = modelCreator.createCustomer(bankInfo.bankCode, customerId, password, bankInfo.pinTanAddress ?: "", bankInfo.name, bankInfo.bic, "")
val bank = modelCreator.createBank(bankInfo.bankCode, customerId, password, bankInfo.pinTanAddress ?: "", bankInfo.name, bankInfo.bic, "")
val newClient = bankingClientCreator.createClient(customer, dataFolder, asyncRunner, this.callback)
val newClient = bankingClientCreator.createClient(bank, dataFolder, asyncRunner, this.callback)
val startDate = Date()
newClient.addAccountAsync { response ->
if (response.successful) {
val account = response.customer
account.displayIndex = customers.size
val bank = response.bank
bank.displayIndex = allBanks.size
addClientForAccount(account, newClient)
addClientForBank(bank, newClient)
selectedAccount(account)
selectedBank(bank)
callAccountsChangedListeners()
callBanksChangedListeners()
persistAccountOffUiThread(account)
persistBankOffUiThread(bank)
response.retrievedData.forEach { retrievedData ->
retrievedAccountTransactions(GetTransactionsResponse(retrievedData), startDate, false)
}
findIconForBankAsync(account)
findIconForBankAsync(bank)
}
callback(response)
}
}
protected open fun findIconForBankAsync(customer: TypedCustomer) {
bankIconFinder.findIconForBankAsync(customer.bankName) { bankIconUrl ->
protected open fun findIconForBankAsync(bank: TypedBankData) {
bankIconFinder.findIconForBankAsync(bank.bankName) { bankIconUrl ->
bankIconUrl?.let {
try {
handleFindIconForBankResult(customer, bankIconUrl)
handleFindIconForBankResult(bank, bankIconUrl)
} catch (e: Exception) {
log.error(e) { "Could not get icon for bank ${customer.bankName}" }
log.error(e) { "Could not get icon for bank ${bank.bankName}" }
}
}
}
}
protected open fun handleFindIconForBankResult(customer: TypedCustomer, bankIconUrl: String) {
val bankIconFile = saveBankIconToDisk(customer, bankIconUrl)
protected open fun handleFindIconForBankResult(bank: TypedBankData, bankIconUrl: String) {
val bankIconFile = saveBankIconToDisk(bank, bankIconUrl)
var iconFilePath = bankIconFile.getAbsolutePath()
@ -198,19 +198,19 @@ open class BankingPresenter(
iconFilePath = "file://" + iconFilePath // without 'file://' Android will not find it
}
customer.iconUrl = iconFilePath
bank.iconUrl = iconFilePath
persistAccountOffUiThread(customer)
persistBankOffUiThread(bank)
callAccountsChangedListeners()
callBanksChangedListeners()
}
protected open fun saveBankIconToDisk(customer: TypedCustomer, bankIconUrl: String): File {
protected open fun saveBankIconToDisk(bank: TypedBankData, bankIconUrl: String): File {
val bankIconsDir = File(dataFolder, "bank_icons")
bankIconsDir.mkdirs()
val extension = getIconFileExtension(bankIconUrl)
val bankIconFile = File(bankIconsDir, customer.bankCode + if (extension != null) (".$extension") else "")
val bankIconFile = File(bankIconsDir, bank.bankCode + if (extension != null) (".$extension") else "")
persister.saveUrlToFile(bankIconUrl, bankIconFile)
@ -236,45 +236,45 @@ open class BankingPresenter(
}
open fun deleteAccount(customer: TypedCustomer) {
open fun deleteAccount(bank: TypedBankData) {
asyncRunner.runAsync {
deleteAccountOffUiThread(customer)
deleteAccountOffUiThread(bank)
}
}
protected open fun deleteAccountOffUiThread(customer: TypedCustomer) {
val wasSelected = isSingleSelectedAccount(customer) or // either account or one of its bank accounts is currently selected
(customer.accounts.firstOrNull { isSingleSelectedBankAccount(it) } != null)
protected open fun deleteAccountOffUiThread(bank: TypedBankData) {
val wasSelected = isSingleSelectedBank(bank) or // either account or one of its bank accounts is currently selected
(bank.accounts.firstOrNull { isSingleSelectedAccount(it) } != null)
val client = bankingClientsForAccounts.remove(customer)
val client = bankingClientsForBanks.remove(bank)
val displayIndex = customer.displayIndex
val displayIndex = bank.displayIndex
persister.deleteAccount(customer, customers)
persister.deleteBank(bank, allBanks)
val sortedBanks = customers.sortedByDisplayIndex()
val sortedBanks = allBanks.sortedByDisplayIndex()
for (i in IntRange(displayIndex, sortedBanks.size - 1)) {
val bank = sortedBanks[i]
bank.displayIndex = i
accountDisplayIndexUpdated(bank)
bankDisplayIndexUpdated(bank)
}
client?.deletedAccount(customer, customers.firstOrNull { it.customerId == customer.customerId && it.bankCode == customer.bankCode} == null)
client?.deletedBank(bank, allBanks.firstOrNull { it.customerId == bank.customerId && it.bankCode == bank.bankCode} == null)
callAccountsChangedListeners()
callBanksChangedListeners()
if (wasSelected || areAllAccountSelected) { // to update displayed account transactions as transactions of yet deleted accounts have to be removed
selectedAllBankAccounts()
selectedAllAccounts()
}
}
open fun fetchAllAccountTransactionsAsync(customer: TypedCustomer,
open fun fetchAllAccountTransactionsAsync(bank: TypedBankData,
callback: ((GetTransactionsResponse) -> Unit)? = null) {
customer.accounts.forEach { bankAccount ->
if (bankAccount.supportsRetrievingAccountTransactions) {
fetchAllAccountTransactionsAsync(bankAccount, callback) // TODO: use a synchronous version of fetchAccountTransactions() so that all bank accounts get handled serially
bank.accounts.forEach { account ->
if (account.supportsRetrievingAccountTransactions) {
fetchAllAccountTransactionsAsync(account, callback) // TODO: use a synchronous version of fetchAccountTransactions() so that all bank accounts get handled serially
}
}
}
@ -288,10 +288,10 @@ open class BankingPresenter(
open fun fetchAccountTransactionsAsync(account: TypedBankAccount, fromDate: Date?, abortIfTanIsRequired: Boolean = false,
callback: ((GetTransactionsResponse) -> Unit)? = null) {
getBankingClientForAccount(account.customer)?.let { client ->
getBankingClientForBank(account.bank)?.let { client ->
val startDate = Date()
client.getTransactionsAsync(GetTransactionsParameter(account,true, fromDate, null, abortIfTanIsRequired, { receivedAccountsTransactionChunk(account, it) } )) { response ->
client.getTransactionsAsync(GetTransactionsParameter(account,true, fromDate, null, abortIfTanIsRequired, { receivedAccountTransactionChunk(account, it) } )) { response ->
if (response.tanRequiredButWeWereToldToAbortIfSo == false) { // don't call retrievedAccountTransactions() if aborted due to TAN required but we told client to abort if so
retrievedAccountTransactions(response, startDate, fromDate == null)
@ -310,32 +310,32 @@ open class BankingPresenter(
updateAccountsTransactionsAsync(true) { }
}
open fun updateSelectedBankAccountTransactionsAsync(callback: (GetTransactionsResponse) -> Unit) {
updateBanksAccountsTransactionsAsync(selectedBankAccounts, false, callback)
open fun updateSelectedAccountsTransactionsAsync(callback: (GetTransactionsResponse) -> Unit) {
updateAccountsTransactionsAsync(selectedAccounts, false, callback)
}
protected open fun updateAccountsTransactionsAsync(abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) {
bankingClientsForAccounts.keys.forEach { account ->
account.accounts.forEach { bankAccount ->
if (bankAccount.supportsRetrievingAccountTransactions) {
updateBankAccountTransactionsAsync(bankAccount, abortIfTanIsRequired, callback)
bankingClientsForBanks.keys.forEach { account ->
account.accounts.forEach { account ->
if (account.supportsRetrievingAccountTransactions) {
updateAccountTransactionsAsync(account, abortIfTanIsRequired, callback)
}
}
}
}
protected open fun updateBanksAccountsTransactionsAsync(accounts: List<TypedBankAccount>, abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) {
accounts.forEach { bankAccount ->
if (bankAccount.supportsRetrievingAccountTransactions) {
updateBankAccountTransactionsAsync(bankAccount, abortIfTanIsRequired, callback)
protected open fun updateAccountsTransactionsAsync(accounts: List<TypedBankAccount>, abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) {
accounts.forEach { account ->
if (account.supportsRetrievingAccountTransactions) {
updateAccountTransactionsAsync(account, abortIfTanIsRequired, callback)
}
}
}
open fun updateBankAccountTransactionsAsync(bankAccount: TypedBankAccount, abortIfTanIsRequired: Boolean = false, callback: ((GetTransactionsResponse) -> Unit)? = null) {
val fromDate = bankAccount.retrievedTransactionsUpTo?.let { Date(it.millisSinceEpoch - OneDayMillis) } // one day before last received transactions
open fun updateAccountTransactionsAsync(account: TypedBankAccount, abortIfTanIsRequired: Boolean = false, callback: ((GetTransactionsResponse) -> Unit)? = null) {
val fromDate = account.retrievedTransactionsUpTo?.let { Date(it.millisSinceEpoch - OneDayMillis) } // one day before last received transactions
fetchAccountTransactionsAsync(bankAccount, fromDate, abortIfTanIsRequired, callback)
fetchAccountTransactionsAsync(account, fromDate, abortIfTanIsRequired, callback)
}
protected open fun retrievedAccountTransactions(response: GetTransactionsResponse, startDate: Date, didFetchAllTransactions: Boolean) {
@ -359,7 +359,7 @@ open class BankingPresenter(
}
protected open fun didFetchAllTransactionsStoredOnBankServer(account: IBankAccount<IAccountTransaction>, fetchedTransactions: Collection<IAccountTransaction>): Boolean {
account.customer.countDaysForWhichTransactionsAreKept?.let { countDaysForWhichTransactionsAreKept ->
account.bank.countDaysForWhichTransactionsAreKept?.let { countDaysForWhichTransactionsAreKept ->
(account.retrievedTransactionsFromOn ?: getDateOfFirstRetrievedTransaction(account.bookedTransactions) ?: getDateOfFirstRetrievedTransaction(fetchedTransactions))?.let { retrievedTransactionsFromOn ->
val dayOfFirstTransactionStoredOnBankServer = Date(Date.today.millisSinceEpoch - countDaysForWhichTransactionsAreKept * OneDayMillis)
@ -374,12 +374,12 @@ open class BankingPresenter(
return transactions.map { it.valueDate }.minBy { it.millisSinceEpoch }
}
protected open fun receivedAccountsTransactionChunk(bankAccount: TypedBankAccount, accountTransactionsChunk: List<IAccountTransaction>) {
if (accountTransactionsChunk.isNotEmpty()) {
protected open fun receivedAccountTransactionChunk(account: TypedBankAccount, transactionsChunk: List<IAccountTransaction>) {
if (transactionsChunk.isNotEmpty()) {
asyncRunner.runAsync { // don't block retrieving next chunk by blocked saving to db / json
updateAccountTransactions(bankAccount, accountTransactionsChunk)
updateAccountTransactions(account, transactionsChunk)
callRetrievedAccountTransactionsResponseListener(GetTransactionsResponse(RetrievedAccountData(bankAccount, true, null, accountTransactionsChunk, listOf(), null, null)))
callRetrievedAccountTransactionsResponseListener(GetTransactionsResponse(RetrievedAccountData(account, true, null, transactionsChunk, listOf(), null, null)))
}
}
}
@ -405,10 +405,10 @@ open class BankingPresenter(
persistAccountTransactionsOffUiThread(account, newBookedTransactions)
}
protected open fun updateBalance(bankAccount: TypedBankAccount, balance: BigDecimal) {
bankAccount.balance = balance
protected open fun updateBalance(account: TypedBankAccount, balance: BigDecimal) {
account.balance = balance
persistAccountOffUiThread(bankAccount.customer)
persistBankOffUiThread(account.bank)
}
@ -417,60 +417,60 @@ open class BankingPresenter(
}
open fun allAccountsUpdated() {
customers.forEach { account ->
accountDisplayIndexUpdated(account)
open fun allBanksUpdated() {
allBanks.forEach { bank ->
bankDisplayIndexUpdated(bank)
}
}
open fun accountDisplayIndexUpdated(account: TypedCustomer) {
persistAccountAsync(account)
open fun bankDisplayIndexUpdated(bank: TypedBankData) {
persistBankAsync(bank)
callAccountsChangedListeners()
callBanksChangedListeners()
}
open fun accountUpdated(bank: TypedCustomer) {
persistAccountAsync(bank)
open fun bankUpdated(bank: TypedBankData) {
persistBankAsync(bank)
callAccountsChangedListeners()
callBanksChangedListeners()
getBankingClientForAccount(bank)?.dataChanged(bank)
getBankingClientForBank(bank)?.dataChanged(bank)
}
open fun accountUpdated(account: TypedBankAccount) {
persistAccountAsync(account.customer)
persistBankAsync(account.bank)
callAccountsChangedListeners()
callBanksChangedListeners()
}
protected open fun persistAccountAsync(customer: ICustomer<*, *>) {
protected open fun persistBankAsync(bank: IBankData<*, *>) {
asyncRunner.runAsync {
persistAccountOffUiThread(customer)
persistBankOffUiThread(bank)
}
}
/**
* Ensure that this method only gets called off UI thread (at least for Android Room db) as otherwise it may blocks UI thread.
*/
protected open fun persistAccountOffUiThread(customer: ICustomer<*, *>) {
persister.saveOrUpdateAccount(customer as TypedCustomer, customers)
protected open fun persistBankOffUiThread(bank: IBankData<*, *>) {
persister.saveOrUpdateBank(bank as TypedBankData, allBanks)
}
/**
* Ensure that this method only gets called off UI thread (at least for Android Room db) as otherwise it may blocks UI thread.
*/
protected open fun persistAccountTransactionsOffUiThread(bankAccount: TypedBankAccount, bookedTransactions: List<IAccountTransaction>) {
persister.saveOrUpdateAccountTransactions(bankAccount, bookedTransactions)
protected open fun persistAccountTransactionsOffUiThread(account: TypedBankAccount, bookedTransactions: List<IAccountTransaction>) {
persister.saveOrUpdateAccountTransactions(account, bookedTransactions)
}
open fun transferMoneyAsync(data: TransferMoneyData, callback: (BankingClientResponse) -> Unit) {
val account = data.account
getBankingClientForAccount(account.customer)?.let { client ->
getBankingClientForBank(account.bank)?.let { client ->
client.transferMoneyAsync(data) { response ->
if (response.successful) {
updateBankAccountTransactionsAsync(account, true)
updateAccountTransactionsAsync(account, true)
}
callback(response)
@ -492,7 +492,7 @@ open class BankingPresenter(
if (invoiceData.potentialTotalAmount != null || invoiceData.potentialIban != null) { // if at least an amount or IBAN could get extracted
val transferMoneyData = TransferMoneyData(
bankAccounts.first(), "",
allAccounts.first(), "",
invoiceData.potentialIban ?: "",
invoiceData.potentialBic ?: "",
invoiceData.potentialTotalAmount ?: BigDecimal.Zero, "")
@ -586,7 +586,7 @@ open class BankingPresenter(
open fun searchSelectedAccountTransactions(query: String): List<IAccountTransaction> {
return searchAccountTransactions(query, selectedBankAccountsAccountTransactions)
return searchAccountTransactions(query, selectedAccountsTransactions)
}
open fun searchAccountTransactions(query: String, transactions: List<IAccountTransaction>): List<IAccountTransaction> {
@ -604,13 +604,13 @@ open class BankingPresenter(
}
open fun getMessageLogForAccounts(customers: List<TypedCustomer>): List<String> {
val logEntries = customers.flatMap {
getBankingClientForAccount(it)?.messageLogWithoutSensitiveData ?: listOf()
open fun getMessageLogForAccounts(banks: List<TypedBankData>): List<String> {
val logEntries = banks.flatMap {
getBankingClientForBank(it)?.messageLogWithoutSensitiveData ?: listOf()
}
return logEntries.map { entry ->
MessageLogEntryDateFormatter.format(entry.time) + " " + entry.customer.bankCode + " " + entry.message
MessageLogEntryDateFormatter.format(entry.time) + " " + entry.bank.bankCode + " " + entry.message
}
}
@ -628,122 +628,122 @@ open class BankingPresenter(
}
protected open fun getBankingClientForAccount(customer: ICustomer<*, *>): IBankingClient? {
return bankingClientsForAccounts.get(customer as TypedCustomer)
protected open fun getBankingClientForBank(bank: IBankData<*, *>): IBankingClient? {
return bankingClientsForBanks.get(bank as TypedBankData)
}
open val selectedBankAccounts: List<TypedBankAccount>
get() = ArrayList(selectedBankAccountsField)
open val selectedAccounts: List<TypedBankAccount>
get() = ArrayList(_selectedAccounts)
open val selectedBankAccountsAccountTransactions: List<IAccountTransaction>
get() = getAccountTransactionsForBankAccounts(selectedBankAccounts)
open val selectedAccountsTransactions: List<IAccountTransaction>
get() = getTransactionsForAccounts(selectedAccounts)
open val balanceOfSelectedBankAccounts: BigDecimal
get() = sumBalance(selectedBankAccounts.map { it.balance })
open val balanceOfSelectedAccounts: BigDecimal
get() = sumBalance(selectedAccounts.map { it.balance })
open val selectedBankAccountsForWhichNotAllTransactionsHaveBeenFetched: List<TypedBankAccount>
get() = selectedBankAccounts.filter { it.haveAllTransactionsBeenFetched == false }
open val selectedAccountsForWhichNotAllTransactionsHaveBeenFetched: List<TypedBankAccount>
get() = selectedAccounts.filter { it.haveAllTransactionsBeenFetched == false }
open val selectedBankAccountsTransactionRetrievalState: TransactionsRetrievalState
get() = getAccountsTransactionRetrievalState(selectedBankAccounts)
open val selectedAccountsTransactionRetrievalState: TransactionsRetrievalState
get() = getAccountsTransactionRetrievalState(selectedAccounts)
open val areAllAccountSelected: Boolean
get() = selectedAccountType == SelectedAccountType.AllAccounts
open fun isSingleSelectedAccount(customer: TypedCustomer): Boolean {
open fun isSingleSelectedBank(bank: TypedBankData): Boolean {
return selectedAccountType == SelectedAccountType.SingleBank
&& _selectedAccounts.map { it.bank }.toSet().containsExactly(bank)
}
open fun isSingleSelectedAccount(account: TypedBankAccount): Boolean {
return selectedAccountType == SelectedAccountType.SingleAccount
&& selectedBankAccountsField.map { it.customer }.toSet().containsExactly(customer)
&& _selectedAccounts.containsExactly(account)
}
open fun isSingleSelectedBankAccount(bankAccount: TypedBankAccount): Boolean {
return selectedAccountType == SelectedAccountType.SingleBankAccount
&& selectedBankAccountsField.containsExactly(bankAccount)
}
open fun selectedAllBankAccounts() {
open fun selectedAllAccounts() {
selectedAccountType = SelectedAccountType.AllAccounts
setSelectedBankAccounts(bankAccounts)
setSelectedAccounts(allAccounts)
}
open fun selectedAccount(customer: TypedCustomer) {
open fun selectedBank(bank: TypedBankData) {
selectedAccountType = SelectedAccountType.SingleBank
setSelectedAccounts(bank.accounts)
}
open fun selectedAccount(account: TypedBankAccount) {
selectedAccountType = SelectedAccountType.SingleAccount
setSelectedBankAccounts(customer.accounts)
setSelectedAccounts(listOf(account))
}
open fun selectedBankAccount(bankAccount: TypedBankAccount) {
selectedAccountType = SelectedAccountType.SingleBankAccount
protected open fun setSelectedAccounts(accounts: List<TypedBankAccount>) {
this._selectedAccounts = ArrayList(accounts) // make a copy
setSelectedBankAccounts(listOf(bankAccount))
}
protected open fun setSelectedBankAccounts(bankAccounts: List<TypedBankAccount>) {
this.selectedBankAccountsField = ArrayList(bankAccounts) // make a copy
callSelectedBankAccountsChangedListeners(selectedBankAccountsField)
callSelectedAccountsChangedListeners(_selectedAccounts)
}
open val customers: List<TypedCustomer>
get() = bankingClientsForAccounts.keys.toList()
open val allBanks: List<TypedBankData>
get() = bankingClientsForBanks.keys.toList()
open val bankAccounts: List<TypedBankAccount>
get() = customers.flatMap { it.accounts }
open val allAccounts: List<TypedBankAccount>
get() = allBanks.flatMap { it.accounts }
open val allTransactions: List<IAccountTransaction>
get() = getAccountTransactionsForBankAccounts(bankAccounts)
get() = getTransactionsForAccounts(allAccounts)
open val balanceOfAllAccounts: BigDecimal
get() = getBalanceForAccounts(customers)
get() = getBalanceForBanks(allBanks)
open val bankAccountsSupportingRetrievingAccountTransactions: List<TypedBankAccount>
get() = bankAccounts.filter { it.supportsRetrievingAccountTransactions }
open val accountsSupportingRetrievingAccountTransactions: List<TypedBankAccount>
get() = allAccounts.filter { it.supportsRetrievingAccountTransactions }
open val hasBankAccountsSupportingRetrievingAccountTransactions: Boolean
get() = doBankAccountsSupportRetrievingAccountTransactions(bankAccounts)
open val hasAccountsSupportingRetrievingTransactions: Boolean
get() = doAccountsSupportRetrievingTransactions(allAccounts)
open val doSelectedBankAccountsSupportRetrievingAccountTransactions: Boolean
get() = doBankAccountsSupportRetrievingAccountTransactions(selectedBankAccounts)
open val doSelectedAccountsSupportRetrievingTransactions: Boolean
get() = doAccountsSupportRetrievingTransactions(selectedAccounts)
open fun doBankAccountsSupportRetrievingAccountTransactions(bankAccounts: List<TypedBankAccount>): Boolean {
return bankAccounts.firstOrNull { it.supportsRetrievingAccountTransactions } != null
open fun doAccountsSupportRetrievingTransactions(accounts: List<TypedBankAccount>): Boolean {
return accounts.firstOrNull { it.supportsRetrievingAccountTransactions } != null
}
open val bankAccountsSupportingRetrievingBalance: List<TypedBankAccount>
get() = bankAccounts.filter { it.supportsRetrievingBalance }
open val accountsSupportingRetrievingBalance: List<TypedBankAccount>
get() = allAccounts.filter { it.supportsRetrievingBalance }
open val hasBankAccountsSupportingRetrievingBalance: Boolean
get() = doBankAccountsSupportRetrievingBalance(bankAccounts)
open val hasAccountsSupportingRetrievingBalance: Boolean
get() = doAccountsSupportRetrievingBalance(allAccounts)
open val doSelectedBankAccountsSupportRetrievingBalance: Boolean
get() = doBankAccountsSupportRetrievingBalance(selectedBankAccounts)
open val doSelectedAccountsSupportRetrievingBalance: Boolean
get() = doAccountsSupportRetrievingBalance(selectedAccounts)
open fun doBankAccountsSupportRetrievingBalance(bankAccounts: List<TypedBankAccount>): Boolean {
return bankAccounts.firstOrNull { it.supportsRetrievingBalance } != null
open fun doAccountsSupportRetrievingBalance(accounts: List<TypedBankAccount>): Boolean {
return accounts.firstOrNull { it.supportsRetrievingBalance } != null
}
open val bankAccountsSupportingTransferringMoney: List<TypedBankAccount>
get() = bankAccounts.filter { it.supportsTransferringMoney }
open val accountsSupportingTransferringMoney: List<TypedBankAccount>
get() = allAccounts.filter { it.supportsTransferringMoney }
open val hasBankAccountsSupportTransferringMoney: Boolean
get() = doBankAccountsSupportTransferringMoney(bankAccounts)
open val hasAccountsSupportTransferringMoney: Boolean
get() = doAccountsSupportTransferringMoney(allAccounts)
open val doSelectedBankAccountsSupportTransferringMoney: Boolean
get() = doBankAccountsSupportTransferringMoney(selectedBankAccounts)
open val doSelectedAccountsSupportTransferringMoney: Boolean
get() = doAccountsSupportTransferringMoney(selectedAccounts)
open fun doBankAccountsSupportTransferringMoney(bankAccounts: List<TypedBankAccount>): Boolean {
return bankAccounts.firstOrNull { it.supportsTransferringMoney } != null
open fun doAccountsSupportTransferringMoney(account: List<TypedBankAccount>): Boolean {
return account.firstOrNull { it.supportsTransferringMoney } != null
}
protected open fun getAccountTransactionsForBankAccounts(bankAccounts: Collection<TypedBankAccount>): List<IAccountTransaction> {
return bankAccounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate.millisSinceEpoch } // TODO: someday add unbooked transactions
protected open fun getTransactionsForAccounts(accounts: Collection<TypedBankAccount>): List<IAccountTransaction> {
return accounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate.millisSinceEpoch } // TODO: someday add unbooked transactions
}
protected open fun getAccountsTransactionRetrievalState(accounts: List<TypedBankAccount>): TransactionsRetrievalState {
@ -788,8 +788,8 @@ open class BankingPresenter(
return TransactionsRetrievalState.NeverRetrievedTransactions
}
protected open fun getBalanceForAccounts(customers: Collection<TypedCustomer>): BigDecimal {
return customers.map { it.balance }.sum()
protected open fun getBalanceForBanks(banks: Collection<TypedBankData>): BigDecimal {
return banks.map { it.balance }.sum()
}
protected open fun sumBalance(singleBalances: Collection<BigDecimal>): BigDecimal {
@ -797,7 +797,7 @@ open class BankingPresenter(
}
open fun getTanMediaForTanMethod(bank: TypedCustomer, tanMethod: TanMethod): List<TanMedium> {
open fun getTanMediaForTanMethod(bank: TypedBankData, tanMethod: TanMethod): List<TanMedium> {
if (ChipTanTanMethods.contains(tanMethod.type)) {
return bank.tanMediaSorted.filterIsInstance<TanGeneratorTanMedium>()
}
@ -839,19 +839,19 @@ open class BankingPresenter(
}
open fun addAccountsChangedListener(listener: (List<TypedCustomer>) -> Unit): Boolean {
return accountsChangedListeners.add(listener)
open fun addBanksChangedListener(listener: (List<TypedBankData>) -> Unit): Boolean {
return banksChangedListeners.add(listener)
}
open fun removeAccountsChangedListener(listener: (List<TypedCustomer>) -> Unit): Boolean {
return accountsChangedListeners.add(listener)
open fun removeBanksChangedListener(listener: (List<TypedBankData>) -> Unit): Boolean {
return banksChangedListeners.add(listener)
}
protected open fun callAccountsChangedListeners() {
val accounts = ArrayList(this.customers)
protected open fun callBanksChangedListeners() {
val banks = ArrayList(this.allBanks)
ArrayList(accountsChangedListeners).forEach {
it(accounts) // TODO: use RxJava for this
ArrayList(banksChangedListeners).forEach {
it(banks) // TODO: use RxJava for this
}
}
@ -871,19 +871,19 @@ open class BankingPresenter(
}
open fun addSelectedBankAccountsChangedListener(listener: (List<TypedBankAccount>) -> Unit): Boolean {
return selectedBankAccountsChangedListeners.add(listener)
open fun addSelectedAccountsChangedListener(listener: (List<TypedBankAccount>) -> Unit): Boolean {
return selectedAccountsChangedListeners.add(listener)
}
open fun removeSelectedBankAccountsChangedListener(listener: (List<TypedBankAccount>) -> Unit): Boolean {
return selectedBankAccountsChangedListeners.add(listener)
open fun removeSelectedAccountsChangedListener(listener: (List<TypedBankAccount>) -> Unit): Boolean {
return selectedAccountsChangedListeners.add(listener)
}
protected open fun callSelectedBankAccountsChangedListeners(selectedBankAccounts: List<TypedBankAccount>) {
val selectedBankAccounts = this.selectedBankAccounts
protected open fun callSelectedAccountsChangedListeners(selectedAccounts: List<TypedBankAccount>) {
val accounts = ArrayList(selectedAccounts)
ArrayList(selectedBankAccountsChangedListeners).forEach {
it(selectedBankAccounts) // TODO: use RxJava for this
ArrayList(selectedAccountsChangedListeners).forEach {
it(accounts) // TODO: use RxJava for this
}
}

View File

@ -57,10 +57,10 @@
<attribute name="supportsTransferringMoney" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="type" attributeType="String"/>
<attribute name="userSetDisplayName" optional="YES" attributeType="String"/>
<relationship name="customer" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistedCustomer" inverseName="accounts" inverseEntity="PersistedCustomer"/>
<relationship name="bank" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistedBankData" inverseName="accounts" inverseEntity="PersistedBankData"/>
<relationship name="transactions" toMany="YES" deletionRule="Cascade" destinationEntity="PersistedAccountTransaction" inverseName="account" inverseEntity="PersistedAccountTransaction"/>
</entity>
<entity name="PersistedCustomer" representedClassName="PersistedCustomer" syncable="YES" codeGenerationType="class">
<entity name="PersistedBankData" representedClassName="PersistedBankData" syncable="YES" codeGenerationType="class">
<attribute name="bankCode" attributeType="String"/>
<attribute name="bankName" attributeType="String"/>
<attribute name="bic" attributeType="String"/>
@ -74,7 +74,7 @@
<attribute name="selectedTanMethodCode" optional="YES" attributeType="String"/>
<attribute name="userId" attributeType="String"/>
<attribute name="userSetDisplayName" optional="YES" attributeType="String"/>
<relationship name="accounts" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="PersistedBankAccount" inverseName="customer" inverseEntity="PersistedBankAccount"/>
<relationship name="accounts" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="PersistedBankAccount" inverseName="bank" inverseEntity="PersistedBankAccount"/>
<relationship name="supportedTanMethods" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="PersistedTanMethod"/>
<relationship name="tanMedia" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="PersistedTanMedium"/>
</entity>
@ -92,7 +92,7 @@
<elements>
<element name="PersistedAccountTransaction" positionX="-36" positionY="45" width="128" height="553"/>
<element name="PersistedBankAccount" positionX="-54" positionY="63" width="128" height="373"/>
<element name="PersistedCustomer" positionX="-63" positionY="-18" width="128" height="283"/>
<element name="PersistedBankData" positionX="-63" positionY="-18" width="128" height="283"/>
<element name="PersistedTanMedium" positionX="-45" positionY="144" width="128" height="28"/>
<element name="PersistedTanMethod" positionX="-54" positionY="135" width="128" height="118"/>
</elements>

View File

@ -15,20 +15,20 @@ let previewImageTanChallenge = ImageTanChallenge(image: TanImage(mimeType: "imag
let previewFlickerCodeTanChallenge = FlickerCodeTanChallenge(flickerCode: FlickerCode(challengeHHD_UC: "", parsedDataSet: "", decodingError: nil), messageToShowToUser: "", tanMethod: previewTanMethods[0])
func createPreviewBanks() -> [ICustomer] {
let bank1 = Customer(bankCode: "", customerId: "", password: "", finTsServerAddress: "", bankName: "Abzockbank", bic: "", customerName: "Marieke Musterfrau", userId: "", iconUrl: "", accounts: [])
func createPreviewBanks() -> [IBankData] {
let bank1 = BankData(bankCode: "", customerId: "", password: "", finTsServerAddress: "", bankName: "Abzockbank", bic: "", customerName: "Marieke Musterfrau", userId: "", iconUrl: "", accounts: [])
bank1.accounts = [
BankAccount(customer: bank1, productName: "Girokonto", identifier: "1234567890"),
BankAccount(bank: bank1, productName: "Girokonto", identifier: "1234567890"),
BankAccount(customer: bank1, productName: "Tagesgeld Minus", identifier: "0987654321")
BankAccount(bank: bank1, productName: "Tagesgeld Minus", identifier: "0987654321")
]
let bank2 = Customer(bankCode: "", customerId: "", password: "", finTsServerAddress: "", bankName: "Kundenverarschebank", bic: "", customerName: "Marieke Musterfrau", userId: "", iconUrl: "", accounts: [])
let bank2 = BankData(bankCode: "", customerId: "", password: "", finTsServerAddress: "", bankName: "Kundenverarschebank", bic: "", customerName: "Marieke Musterfrau", userId: "", iconUrl: "", accounts: [])
bank2.accounts = [
BankAccount(customer: bank2, productName: "Girokonto", identifier: "1234567890")
BankAccount(bank: bank2, productName: "Girokonto", identifier: "1234567890")
]
return [ bank1, bank2 ]

View File

@ -70,7 +70,7 @@ extension KeychainPasswordItem {
}
extension Customer : Identifiable {
extension BankData : Identifiable {
public var id: String { technicalId }
@ -89,11 +89,11 @@ extension AccountTransaction : Identifiable {
}
func ==(lhs: ICustomer, rhs: ICustomer) -> Bool {
func ==(lhs: IBankData, rhs: IBankData) -> Bool {
return lhs.technicalId == rhs.technicalId
}
func !=(lhs: ICustomer, rhs: ICustomer) -> Bool {
func !=(lhs: IBankData, rhs: IBankData) -> Bool {
return lhs.technicalId != rhs.technicalId
}
@ -114,7 +114,7 @@ func !=(lhs: IAccountTransaction, rhs: IAccountTransaction) -> Bool {
}
extension Array where Element == ICustomer {
extension Array where Element == IBankData {
func sumBalances() -> CommonBigDecimal {
return CommonBigDecimal(decimal_: self.map { $0.balance.decimal }.sum())

View File

@ -6,8 +6,8 @@ class AppData : ObservableObject {
@Inject private var presenter: BankingPresenterSwift
@Published var banks: [ICustomer] = []
@Published var banksSorted: [ICustomer] = []
@Published var banks: [IBankData] = []
@Published var banksSorted: [IBankData] = []
@Published var hasAtLeastOneAccountBeenAdded: Bool = false
@ -17,14 +17,14 @@ class AppData : ObservableObject {
init() {
setFieldsForBanks()
presenter.addAccountsChangedListener { banks in
presenter.addBanksChangedListener { banks in
self.setFieldsForBanks()
}
}
private func setFieldsForBanks() {
self.banks = presenter.customers
self.banks = presenter.allBanks
self.banksSorted = banks.sortedByDisplayIndex()
hasAtLeastOneAccountBeenAdded = banks.isNotEmpty

View File

@ -19,74 +19,74 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
}
func saveOrUpdateAccount(customer: ICustomer, allCustomers: [ICustomer]) {
func saveOrUpdateBank(bank: IBankData, allBanks: [IBankData]) {
do {
let mapped = mapper.map(customer, context)
let mapped = mapper.map(bank, context)
if customer.technicalId.isCoreDataId == false { // an unpersisted bank (but check should not be necessary)
if bank.technicalId.isCoreDataId == false { // an unpersisted bank (but check should not be necessary)
context.insert(mapped)
}
try context.save()
setIds(customer, mapped)
setIds(bank, mapped)
} catch {
NSLog("Could not save Customer \(customer): \(error)")
NSLog("Could not save bank \(bank): \(error)")
}
}
private func setIds(_ customer: ICustomer, _ mappedCustomer: PersistedCustomer) {
customer.technicalId = mappedCustomer.objectIDAsString
private func setIds(_ bank: IBankData, _ mappedBank: PersistedBankData) {
bank.technicalId = mappedBank.objectIDAsString
for account in customer.accounts {
if let mappedAccount = mappedCustomer.accounts?.first { ($0 as! PersistedBankAccount).identifier == account.identifier} as? PersistedBankAccount {
for account in bank.accounts {
if let mappedAccount = mappedBank.accounts?.first { ($0 as! PersistedBankAccount).identifier == account.identifier} as? PersistedBankAccount {
account.technicalId = mappedAccount.objectIDAsString
}
}
for tanMethod in customer.supportedTanMethods {
if let mappedTanMethod = mappedCustomer.supportedTanMethods?.first { ($0 as! PersistedTanMethod).bankInternalMethodCode == tanMethod.bankInternalMethodCode } as? PersistedTanMethod {
for tanMethod in bank.supportedTanMethods {
if let mappedTanMethod = mappedBank.supportedTanMethods?.first { ($0 as! PersistedTanMethod).bankInternalMethodCode == tanMethod.bankInternalMethodCode } as? PersistedTanMethod {
tanMethod.technicalId = mappedTanMethod.objectIDAsString
}
}
for tanMedium in customer.tanMedia {
if let mappedTanMedium = mappedCustomer.tanMedia?.first { ($0 as! PersistedTanMedium).displayName == tanMedium.displayName } as? PersistedTanMedium {
for tanMedium in bank.tanMedia {
if let mappedTanMedium = mappedBank.tanMedia?.first { ($0 as! PersistedTanMedium).displayName == tanMedium.displayName } as? PersistedTanMedium {
tanMedium.technicalId = mappedTanMedium.objectIDAsString
}
}
}
func readPersistedAccounts_() -> [ICustomer] {
var customers: [PersistedCustomer] = []
func readPersistedBanks_() -> [IBankData] {
var banks: [PersistedBankData] = []
do {
let request: NSFetchRequest<PersistedCustomer> = PersistedCustomer.fetchRequest()
let request: NSFetchRequest<PersistedBankData> = PersistedBankData.fetchRequest()
request.returnsObjectsAsFaults = false
try customers = context.fetch(request)
try banks = context.fetch(request)
} catch {
NSLog("Could not request Customers: \(error)")
NSLog("Could not request banks: \(error)")
}
return customers.map( { mapper.map($0) } )
return banks.map( { mapper.map($0) } )
}
func deleteAccount(customer: ICustomer, allCustomers: [ICustomer]) {
func deleteBank(bank: IBankData, allBanks: [IBankData]) {
do {
let mapped = mapper.map(customer, context)
let mapped = mapper.map(bank, context)
context.delete(mapped)
try context.save()
} catch {
NSLog("Could not delete Customer \(customer): \(error)")
NSLog("Could not delete Bank \(bank): \(error)")
}
}
func saveOrUpdateAccountTransactions(bankAccount: IBankAccount, transactions: [IAccountTransaction]) {
if let persistedAccount = context.objectByID(bankAccount.technicalId) as? PersistedBankAccount {
func saveOrUpdateAccountTransactions(account: IBankAccount, transactions: [IAccountTransaction]) {
if let persistedAccount = context.objectByID(account.technicalId) as? PersistedBankAccount {
for transaction in transactions {
if transaction.technicalId.isCoreDataId == false { // TODO: or also update already persisted transactions?
do {
@ -96,7 +96,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
transaction.technicalId = mappedTransaction.objectIDAsString
} catch {
NSLog("Could not save transaction \(transaction.buildTransactionIdentifier()) of account \(bankAccount.displayName): \(error)")
NSLog("Could not save transaction \(transaction.buildTransactionIdentifier()) of account \(account.displayName): \(error)")
}
}
}
@ -137,7 +137,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
try transactions = context.fetch(request)
} catch {
NSLog("Could not request Customers: \(error)")
NSLog("Could not request banks: \(error)")
}
let remittees = transactions
@ -152,13 +152,13 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
func deleteAll() {
do {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "PersistedCustomer")
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "PersistedBankData")
let deleteAll = NSBatchDeleteRequest(fetchRequest: request)
try context.execute(deleteAll)
} catch {
NSLog("Could not delete all Customers: \(error)")
NSLog("Could not delete all banks: \(error)")
}
}

View File

@ -6,15 +6,15 @@ class EnterTanState : Identifiable {
let id: Foundation.UUID = UUID()
let customer: ICustomer
let bank: IBankData
let tanChallenge: TanChallenge
let callback: (EnterTanResult) -> Void
init(_ customer: ICustomer, _ tanChallenge: TanChallenge, _ callback: @escaping (EnterTanResult) -> Void) {
self.customer = customer
init(_ bank: IBankData, _ tanChallenge: TanChallenge, _ callback: @escaping (EnterTanResult) -> Void) {
self.bank = bank
self.tanChallenge = tanChallenge
self.callback = callback
}

View File

@ -5,60 +5,60 @@ import BankingUiSwift
class Mapper {
func map(_ customer: PersistedCustomer) -> ICustomer {
let mapped = Customer(bankCode: map(customer.bankCode), customerId: map(customer.customerId), password: map(customer.password), finTsServerAddress: map(customer.finTsServerAddress), bankName: map(customer.bankName), bic: map(customer.bic), customerName: map(customer.customerName), userId: map(customer.userId), iconUrl: customer.iconUrl, accounts: [])
func map(_ bank: PersistedBankData) -> IBankData {
let mapped = BankData(bankCode: map(bank.bankCode), customerId: map(bank.customerId), password: map(bank.password), finTsServerAddress: map(bank.finTsServerAddress), bankName: map(bank.bankName), bic: map(bank.bic), customerName: map(bank.customerName), userId: map(bank.userId), iconUrl: bank.iconUrl, accounts: [])
mapped.countDaysForWhichTransactionsAreKept = mapToInt(customer.countDaysForWhichTransactionsAreKept)
mapped.countDaysForWhichTransactionsAreKept = mapToInt(bank.countDaysForWhichTransactionsAreKept)
mapped.userSetDisplayName = customer.userSetDisplayName
mapped.displayIndex = customer.displayIndex
mapped.userSetDisplayName = bank.userSetDisplayName
mapped.displayIndex = bank.displayIndex
mapped.accounts = map(mapped, customer.accounts?.array as? [PersistedBankAccount])
mapped.accounts = map(mapped, bank.accounts?.array as? [PersistedBankAccount])
mapped.supportedTanMethods = map(customer.supportedTanMethods?.array as? [PersistedTanMethod])
mapped.selectedTanMethod = mapped.supportedTanMethods.first(where: { $0.bankInternalMethodCode == customer.selectedTanMethodCode })
mapped.supportedTanMethods = map(bank.supportedTanMethods?.array as? [PersistedTanMethod])
mapped.selectedTanMethod = mapped.supportedTanMethods.first(where: { $0.bankInternalMethodCode == bank.selectedTanMethodCode })
mapped.tanMedia = map(customer.tanMedia?.array as? [PersistedTanMedium])
mapped.tanMedia = map(bank.tanMedia?.array as? [PersistedTanMedium])
mapped.technicalId = customer.objectIDAsString
mapped.technicalId = bank.objectIDAsString
return mapped
}
func map(_ customer: ICustomer, _ context: NSManagedObjectContext) -> PersistedCustomer {
let mapped = context.objectByID(customer.technicalId) ?? PersistedCustomer(context: context)
func map(_ bank: IBankData, _ context: NSManagedObjectContext) -> PersistedBankData {
let mapped = context.objectByID(bank.technicalId) ?? PersistedBankData(context: context)
mapped.bankCode = customer.bankCode
mapped.customerId = customer.customerId
mapped.password = customer.password
mapped.finTsServerAddress = customer.finTsServerAddress
mapped.bankName = customer.bankName
mapped.bic = customer.bic
mapped.customerName = customer.customerName
mapped.userId = customer.userId
mapped.iconUrl = customer.iconUrl
mapped.countDaysForWhichTransactionsAreKept = mapFromInt(customer.countDaysForWhichTransactionsAreKept)
mapped.bankCode = bank.bankCode
mapped.customerId = bank.customerId
mapped.password = bank.password
mapped.finTsServerAddress = bank.finTsServerAddress
mapped.bankName = bank.bankName
mapped.bic = bank.bic
mapped.customerName = bank.customerName
mapped.userId = bank.userId
mapped.iconUrl = bank.iconUrl
mapped.countDaysForWhichTransactionsAreKept = mapFromInt(bank.countDaysForWhichTransactionsAreKept)
mapped.userSetDisplayName = customer.userSetDisplayName
mapped.displayIndex = customer.displayIndex
mapped.userSetDisplayName = bank.userSetDisplayName
mapped.displayIndex = bank.displayIndex
mapped.accounts = NSOrderedSet(array: map(mapped, customer.accounts, context))
mapped.accounts = NSOrderedSet(array: map(mapped, bank.accounts, context))
mapped.supportedTanMethods = NSOrderedSet(array: map(customer.supportedTanMethods, context))
mapped.selectedTanMethodCode = customer.selectedTanMethod?.bankInternalMethodCode
mapped.supportedTanMethods = NSOrderedSet(array: map(bank.supportedTanMethods, context))
mapped.selectedTanMethodCode = bank.selectedTanMethod?.bankInternalMethodCode
mapped.tanMedia = NSOrderedSet(array: map(customer.tanMedia, context))
mapped.tanMedia = NSOrderedSet(array: map(bank.tanMedia, context))
return mapped
}
func map(_ customer: ICustomer, _ accounts: [PersistedBankAccount]?) -> [IBankAccount] {
return accounts?.map( { map(customer, $0) } ) ?? []
func map(_ bank: IBankData, _ accounts: [PersistedBankAccount]?) -> [IBankAccount] {
return accounts?.map( { map(bank, $0) } ) ?? []
}
func map(_ customer: ICustomer, _ account: PersistedBankAccount) -> IBankAccount {
let mapped = BankAccount(customer: customer, identifier: map(account.identifier), accountHolderName: map(account.accountHolderName), iban: account.iban, subAccountNumber: account.subAccountNumber, customerId: map(account.customerId), balance: map(account.balance), currency: map(account.currency), type: map(account.type), productName: account.productName, accountLimit: account.accountLimit, retrievedTransactionsFromOn: map(account.retrievedTransactionsFromOn), retrievedTransactionsUpTo: map(account.retrievedTransactionsUpTo), supportsRetrievingAccountTransactions: account.supportsRetrievingAccountTransactions, supportsRetrievingBalance: account.supportsRetrievingBalance, supportsTransferringMoney: account.supportsTransferringMoney, supportsInstantPaymentMoneyTransfer: account.supportsInstantPaymentMoneyTransfer, bookedTransactions: [], unbookedTransactions: [])
func map(_ bank: IBankData, _ account: PersistedBankAccount) -> IBankAccount {
let mapped = BankAccount(bank: bank, identifier: map(account.identifier), accountHolderName: map(account.accountHolderName), iban: account.iban, subAccountNumber: account.subAccountNumber, customerId: map(account.customerId), balance: map(account.balance), currency: map(account.currency), type: map(account.type), productName: account.productName, accountLimit: account.accountLimit, retrievedTransactionsFromOn: map(account.retrievedTransactionsFromOn), retrievedTransactionsUpTo: map(account.retrievedTransactionsUpTo), supportsRetrievingAccountTransactions: account.supportsRetrievingAccountTransactions, supportsRetrievingBalance: account.supportsRetrievingBalance, supportsTransferringMoney: account.supportsTransferringMoney, supportsInstantPaymentMoneyTransfer: account.supportsInstantPaymentMoneyTransfer, bookedTransactions: [], unbookedTransactions: [])
mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched
mapped.isAccountTypeSupported = account.isAccountTypeSupported
@ -73,14 +73,14 @@ class Mapper {
return mapped
}
func map(_ customer: PersistedCustomer, _ accounts: [IBankAccount], _ context: NSManagedObjectContext) -> [PersistedBankAccount] {
return accounts.map( { map(customer, $0, context) } )
func map(_ bank: PersistedBankData, _ accounts: [IBankAccount], _ context: NSManagedObjectContext) -> [PersistedBankAccount] {
return accounts.map( { map(bank, $0, context) } )
}
func map(_ customer: PersistedCustomer, _ account: IBankAccount, _ context: NSManagedObjectContext) -> PersistedBankAccount {
func map(_ bank: PersistedBankData, _ account: IBankAccount, _ context: NSManagedObjectContext) -> PersistedBankAccount {
let mapped = context.objectByID(account.technicalId) ?? PersistedBankAccount(context: context)
mapped.customer = customer
mapped.bank = bank
mapped.identifier = account.identifier
mapped.accountHolderName = account.accountHolderName
mapped.iban = account.iban
@ -147,7 +147,7 @@ class Mapper {
}
func map(_ account: IBankAccount, _ transaction: PersistedAccountTransaction) -> IAccountTransaction {
let mapped = AccountTransaction(bankAccount: account, amount: map(transaction.amount), currency: map(transaction.currency), unparsedUsage: map(transaction.unparsedUsage), bookingDate: map(transaction.bookingDate), otherPartyName: transaction.otherPartyName, otherPartyBankCode: transaction.otherPartyBankCode, otherPartyAccountId: transaction.otherPartyAccountId, bookingText: transaction.bookingText, valueDate: map(transaction.valueDate), statementNumber: Int32(transaction.statementNumber), sequenceNumber: map(transaction.sequenceNumber), openingBalance: map(transaction.openingBalance), closingBalance: map(transaction.closingBalance), endToEndReference: transaction.endToEndReference, customerReference: transaction.customerReference, mandateReference: transaction.mandateReference, creditorIdentifier: transaction.creditorIdentifier, originatorsIdentificationCode: transaction.originatorsIdentificationCode, compensationAmount: transaction.compensationAmount, originalAmount: transaction.originalAmount, sepaUsage: transaction.sepaUsage, deviantOriginator: transaction.deviantOriginator, deviantRecipient: transaction.deviantRecipient, usageWithNoSpecialType: transaction.usageWithNoSpecialType, primaNotaNumber: transaction.primaNotaNumber, textKeySupplement: transaction.textKeySupplement, currencyType: transaction.currencyType, bookingKey: map(transaction.bookingKey), referenceForTheAccountOwner: map(transaction.referenceForTheAccountOwner), referenceOfTheAccountServicingInstitution: transaction.referenceOfTheAccountServicingInstitution, supplementaryDetails: transaction.supplementaryDetails, transactionReferenceNumber: map(transaction.transactionReferenceNumber), relatedReferenceNumber: transaction.relatedReferenceNumber)
let mapped = AccountTransaction(account: account, amount: map(transaction.amount), currency: map(transaction.currency), unparsedUsage: map(transaction.unparsedUsage), bookingDate: map(transaction.bookingDate), otherPartyName: transaction.otherPartyName, otherPartyBankCode: transaction.otherPartyBankCode, otherPartyAccountId: transaction.otherPartyAccountId, bookingText: transaction.bookingText, valueDate: map(transaction.valueDate), statementNumber: Int32(transaction.statementNumber), sequenceNumber: map(transaction.sequenceNumber), openingBalance: map(transaction.openingBalance), closingBalance: map(transaction.closingBalance), endToEndReference: transaction.endToEndReference, customerReference: transaction.customerReference, mandateReference: transaction.mandateReference, creditorIdentifier: transaction.creditorIdentifier, originatorsIdentificationCode: transaction.originatorsIdentificationCode, compensationAmount: transaction.compensationAmount, originalAmount: transaction.originalAmount, sepaUsage: transaction.sepaUsage, deviantOriginator: transaction.deviantOriginator, deviantRecipient: transaction.deviantRecipient, usageWithNoSpecialType: transaction.usageWithNoSpecialType, primaNotaNumber: transaction.primaNotaNumber, textKeySupplement: transaction.textKeySupplement, currencyType: transaction.currencyType, bookingKey: map(transaction.bookingKey), referenceForTheAccountOwner: map(transaction.referenceForTheAccountOwner), referenceOfTheAccountServicingInstitution: transaction.referenceOfTheAccountServicingInstitution, supplementaryDetails: transaction.supplementaryDetails, transactionReferenceNumber: map(transaction.transactionReferenceNumber), relatedReferenceNumber: transaction.relatedReferenceNumber)
mapped.technicalId = transaction.objectIDAsString

View File

@ -11,7 +11,7 @@ extension Message {
secondaryButton: .cancel())
}
static func createAskUserToDeleteAccountMessage(_ bank: ICustomer, _ deleteAccount: @escaping (ICustomer) -> Void) -> Message {
static func createAskUserToDeleteAccountMessage(_ bank: IBankData, _ deleteAccount: @escaping (IBankData) -> Void) -> Message {
return Message(title: Text("Really delete account '\(bank.displayName)'?"),
message: Text("All data for this account will be permanently deleted locally."),
primaryButton: .destructive(Text("Delete"), action: { deleteAccount(bank) } ),

View File

@ -9,8 +9,8 @@ class SwiftUiRouter : IRouter {
}
func getTanFromUserFromNonUiThread(customer: ICustomer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: @escaping (EnterTanResult) -> Void) {
let enterTanState = EnterTanState(customer, tanChallenge, callback)
func getTanFromUserFromNonUiThread(bank: IBankData, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: @escaping (EnterTanResult) -> Void) {
let enterTanState = EnterTanState(bank, tanChallenge, callback)
SceneDelegate.navigateToView(EnterTanDialog(enterTanState))
}

View File

@ -53,22 +53,22 @@ struct AccountTransactionsDialog: View {
@Inject private var presenter: BankingPresenterSwift
init(allBanks: [ICustomer]) {
init(allBanks: [IBankData]) {
self.init("All accounts", true)
presenter.selectedAllBankAccounts()
presenter.selectedAllAccounts()
}
init(bank: ICustomer) {
init(bank: IBankData) {
self.init(bank.displayName, false)
presenter.selectedAccount(customer: bank)
presenter.selectedBank(bank: bank)
}
init(account: IBankAccount) {
self.init(account.displayName, false)
presenter.selectedBankAccount(bankAccount: account)
presenter.selectedAccount(account: account)
}
fileprivate init(_ title: String, _ showBankIcons: Bool) {
@ -168,7 +168,7 @@ struct AccountTransactionsDialog: View {
private func setInitialValues() {
self.balanceOfAllTransactions = self.presenter.balanceOfSelectedBankAccounts
self.balanceOfAllTransactions = self.presenter.balanceOfSelectedAccounts
self.filterTransactions("")
@ -176,10 +176,10 @@ struct AccountTransactionsDialog: View {
}
private func setTransactionsView() {
let transactionsRetrievalState = presenter.selectedBankAccountsTransactionRetrievalState
let transactionsRetrievalState = presenter.selectedAccountsTransactionRetrievalState
self.haveTransactionsBeenRetrievedForSelectedAccounts = transactionsRetrievalState == .retrievedtransactions
self.accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedBankAccountsForWhichNotAllTransactionsHaveBeenFetched
self.accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedAccountsForWhichNotAllTransactionsHaveBeenFetched
self.haveAllTransactionsBeenFetched = self.accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty
self.showFetchAllTransactionsOverlay = shouldShowFetchAllTransactionsOverlay && haveTransactionsBeenRetrievedForSelectedAccounts
@ -199,10 +199,10 @@ struct AccountTransactionsDialog: View {
private func updateTransactions(_ executingDone: @escaping () -> Void) {
presenter.updateSelectedBankAccountTransactionsAsync { response in
presenter.updateSelectedAccountsTransactionsAsync { response in
executingDone()
self.balanceOfAllTransactions = self.presenter.balanceOfSelectedBankAccounts
self.balanceOfAllTransactions = self.presenter.balanceOfSelectedAccounts
if response.successful {
self.filterTransactions(self.searchText)
@ -216,9 +216,9 @@ struct AccountTransactionsDialog: View {
}
private func fetchTransactions() {
for account in presenter.selectedBankAccounts {
for account in presenter.selectedAccounts {
if account.haveAllTransactionsBeenFetched {
presenter.updateBankAccountTransactionsAsync(bankAccount: account, abortIfTanIsRequired: false, callback: self.handleGetTransactionsResult)
presenter.updateAccountTransactionsAsync(account: account, abortIfTanIsRequired: false, callback: self.handleGetTransactionsResult)
}
else {
presenter.fetchAllAccountTransactionsAsync(account: account, callback: self.handleGetTransactionsResult)
@ -250,7 +250,7 @@ struct AccountTransactionsDialog: View {
return "No transactions fetched yet"
}
else if state == .notransactionsinretrievedperiod {
let account = presenter.selectedBankAccounts.first!
let account = presenter.selectedAccounts.first!
return "There haven't been any transactions in retrieved period from \(mapDate(account.retrievedTransactionsFromOn)) - \(mapDate(account.retrievedTransactionsUpTo))"
}
else if state == .accountdoesnotsupportfetchingtransactions {

View File

@ -99,7 +99,7 @@ struct AddAccountDialog: View {
self.closeDialog()
let authenticationService = AuthenticationService()
if self.presenter.customers.count == 1 && authenticationService.authenticationType == .unset {
if self.presenter.allBanks.count == 1 && authenticationService.authenticationType == .unset {
authenticationService.setAuthenticationType(.none)
UIAlert("Secure data?", "Secure data with?",

View File

@ -10,7 +10,7 @@ struct BankSettingsDialog: View {
@Inject private var presenter: BankingPresenterSwift
private let bank: ICustomer
private let bank: IBankData
@State private var displayName: String
@ -32,7 +32,7 @@ struct BankSettingsDialog: View {
}
init(_ bank: ICustomer) {
init(_ bank: IBankData) {
self.bank = bank
_displayName = State(initialValue: bank.displayName)
@ -102,7 +102,7 @@ struct BankSettingsDialog: View {
func reorderAccounts(from source: IndexSet, to destination: Int) {
accountsSorted = accountsSorted.reorder(from: source, to: destination)
presenter.accountDisplayIndexUpdated(account: bank)
presenter.bankDisplayIndexUpdated(bank: bank)
}
@ -110,8 +110,8 @@ struct BankSettingsDialog: View {
self.askUserToDeleteAccountOrSaveChangesMessage = Message.createAskUserToDeleteAccountMessage(bank, self.deleteAccount)
}
func deleteAccount(bank: ICustomer) {
presenter.deleteAccount(customer: bank)
func deleteAccount(bank: IBankData) {
presenter.deleteAccount(bank: bank)
closeDialog()
}
@ -135,7 +135,7 @@ struct BankSettingsDialog: View {
bank.selectedTanMethod = selectedTanMethod
presenter.accountUpdated(bank: bank)
presenter.bankUpdated(bank: bank)
}
closeDialog()

View File

@ -11,7 +11,7 @@ struct EnterTanDialog: View {
private var tanChallenge: TanChallenge
private var customer: ICustomer
private var bank: IBankData
private var customersTanMedia: [TanMedium] = []
@ -50,9 +50,9 @@ struct EnterTanDialog: View {
self.state = state
self.tanChallenge = state.tanChallenge
self.customer = state.customer
self.bank = state.bank
self.customersTanMedia = customer.tanMediaSorted
self.customersTanMedia = bank.tanMediaSorted
self.showSelectTanMediumView = self.customersTanMedia.count > 1 // TODO: use isOpticalTanMethod && tanMedia.count > 1
@ -73,7 +73,7 @@ struct EnterTanDialog: View {
var body: some View {
Form {
Section {
TanMethodPicker(customer, state.tanChallenge.tanMethod) { selectedTanMethod in
TanMethodPicker(bank, state.tanChallenge.tanMethod) { selectedTanMethod in
self.selectedTanMethodChanged(selectedTanMethod)
}
@ -215,7 +215,7 @@ struct EnterTanDialog: View {
struct EnterTanDialog_Previews: PreviewProvider {
static var previews: some View {
let customer = Customer(bankCode: "", customerId: "", password: "", finTsServerAddress: "")
let customer = BankData(bankCode: "", customerId: "", password: "", finTsServerAddress: "")
customer.supportedTanMethods = previewTanMethods
customer.tanMedia = previewTanMedia

View File

@ -58,7 +58,7 @@ struct SettingsDialog: View {
data.banksDisplayIndexChanged()
presenter.allAccountsUpdated()
presenter.allBanksUpdated()
}
func deleteBanks(at offsets: IndexSet) {
@ -68,14 +68,14 @@ struct SettingsDialog: View {
}
}
func askUserToDeleteAccount(_ bankToDelete: ICustomer) {
func askUserToDeleteAccount(_ bankToDelete: IBankData) {
self.askToDeleteAccountMessage = Message.createAskUserToDeleteAccountMessage(bankToDelete, self.deleteAccountWithSecurityChecks)
}
func deleteAccountWithSecurityChecks(_ bankToDelete: ICustomer) {
func deleteAccountWithSecurityChecks(_ bankToDelete: IBankData) {
// don't know why but when deleting last bank application crashes if we don't delete bank async
DispatchQueue.main.async {
if self.presenter.customers.count == 1 {
if self.presenter.allBanks.count == 1 {
self.editMode?.wrappedValue = .inactive
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
@ -88,8 +88,8 @@ struct SettingsDialog: View {
}
}
private func deleteAccount(_ bankToDelete: ICustomer) {
self.presenter.deleteAccount(customer: bankToDelete)
private func deleteAccount(_ bankToDelete: IBankData) {
self.presenter.deleteAccount(bank: bankToDelete)
}

View File

@ -71,7 +71,7 @@ struct TransferMoneyDialog: View {
init() {
self.accountsSupportingTransferringMoney = self.presenter.bankAccounts.filter({ $0.supportsTransferringMoney })
self.accountsSupportingTransferringMoney = self.presenter.accountsSupportingTransferringMoney
self.showAccounts = self.accountsSupportingTransferringMoney.count > 1
}

View File

@ -13,12 +13,12 @@ struct IconedTitleView: View {
private var titleFont: Font?
init(_ bank: ICustomer, titleFont: Font? = nil) {
init(_ bank: IBankData, titleFont: Font? = nil) {
self.init(accountTitle: bank.displayName, iconUrl: bank.iconUrl, defaultIconName: Styles.AccountFallbackIcon, titleFont: titleFont)
}
init(_ account: IBankAccount, titleFont: Font? = nil) {
self.init(accountTitle: account.displayName, iconUrl: account.customer.iconUrl, defaultIconName: Styles.AccountFallbackIcon, titleFont: titleFont)
self.init(accountTitle: account.displayName, iconUrl: account.bank.iconUrl, defaultIconName: Styles.AccountFallbackIcon, titleFont: titleFont)
}
init(accountTitle: String, iconUrl: String?, defaultIconName: String, titleFont: Font? = nil) {

View File

@ -47,7 +47,7 @@ struct AccountTransactionListItem: View {
VStack(alignment: .trailing) {
if areMoreThanOneBanksTransactionsDisplayed {
IconView(iconUrl: transaction.bankAccount.customer.iconUrl, defaultIconName: Styles.AccountFallbackIcon)
IconView(iconUrl: transaction.account.bank.iconUrl, defaultIconName: Styles.AccountFallbackIcon)
Spacer()
}
@ -98,6 +98,6 @@ struct AccountTransactionListItem: View {
struct AccountTransactionListItem_Previews: PreviewProvider {
static var previews: some View {
AccountTransactionListItem(AccountTransaction(bankAccount: previewBanks[0].accounts[0] as! BankAccount, otherPartyName: "Marieke Musterfrau", unparsedUsage: "Vielen Dank für Ihre Mühen", amount: CommonBigDecimal(double: 1234.56), valueDate: CommonDate(year: 2020, month: .march, day_: 27), bookingText: "SEPA Überweisung"), false)
AccountTransactionListItem(AccountTransaction(account: previewBanks[0].accounts[0] as! BankAccount, otherPartyName: "Marieke Musterfrau", unparsedUsage: "Vielen Dank für Ihre Mühen", amount: CommonBigDecimal(double: 1234.56), valueDate: CommonDate(year: 2020, month: .march, day_: 27), bookingText: "SEPA Überweisung"), false)
}
}

View File

@ -4,7 +4,7 @@ import BankingUiSwift
struct AllBanksListItem: View {
let banks: [ICustomer]
let banks: [IBankData]
@State private var navigateToAccountTransactionsDialog = false

View File

@ -4,7 +4,7 @@ import BankingUiSwift
struct BankListItem : View {
let bank: ICustomer
let bank: IBankData
@State private var navigateToAccountTransactionsDialog = false
@ -70,8 +70,8 @@ struct BankListItem : View {
).show()
}
private func deleteAccount(_ bank: ICustomer) {
presenter.deleteAccount(customer: bank)
private func deleteAccount(_ bank: IBankData) {
presenter.deleteAccount(bank: bank)
}
}

View File

@ -4,7 +4,7 @@ import BankingUiSwift
struct TanMethodPicker: View {
private let bank: ICustomer
private let bank: IBankData
private let selectedTanMethodChanged: (TanMethod) -> Void
@ -27,7 +27,7 @@ struct TanMethodPicker: View {
}
init(_ bank: ICustomer, _ initiallySelectedTanMethod: TanMethod? = nil, selectedTanMethodChanged: @escaping (TanMethod) -> Void) {
init(_ bank: IBankData, _ initiallySelectedTanMethod: TanMethod? = nil, selectedTanMethodChanged: @escaping (TanMethod) -> Void) {
self.bank = bank
self.selectedTanMethodChanged = selectedTanMethodChanged

View File

@ -16,6 +16,7 @@ import net.dankito.banking.fints.util.PureKotlinBase64Service
import net.dankito.banking.fints.webclient.IWebClient
import net.dankito.banking.fints.webclient.KtorWebClient
import net.dankito.banking.extensions.toMoney
import net.dankito.banking.fints.model.BankData
import net.dankito.banking.fints.response.client.FinTsClientResponse
import net.dankito.banking.ui.model.*
import net.dankito.banking.ui.model.MessageLogEntry
@ -26,7 +27,7 @@ import net.dankito.utils.multiplatform.log.LoggerFactory
open class fints4kBankingClient(
protected val customer: TypedCustomer,
protected val bank: TypedBankData,
protected val modelCreator: IModelCreator,
protected val dataFolder: File,
protected val serializer: ISerializer,
@ -48,14 +49,14 @@ open class fints4kBankingClient(
protected var didTryToGetAccountDataFromBank = false
protected val bank = restoreDataOrMapFromUiModel(customer)
protected val fintsBank = restoreDataOrMapFromUiModel(bank)
protected open val client = FinTsClientForCustomer(bank, createFinTsClientCallback(callback), webClient, base64Service)
protected open val client = FinTsClientForCustomer(fintsBank, createFinTsClientCallback(callback), webClient, base64Service)
override val messageLogWithoutSensitiveData: List<MessageLogEntry>
get() = client.messageLogWithoutSensitiveData.map { MessageLogEntry(it.message, it.time, customer) }
get() = client.messageLogWithoutSensitiveData.map { MessageLogEntry(it.message, it.time, bank) }
override fun addAccountAsync(callback: (AddAccountResponse) -> Unit) {
@ -66,8 +67,8 @@ open class fints4kBankingClient(
protected open fun handleAddAccountResponse(response: net.dankito.banking.fints.response.client.AddAccountResponse,
callback: (AddAccountResponse) -> Unit) {
mapper.mapBank(customer, bank)
val mappedResponse = mapper.mapResponse(customer, response)
mapper.mapBank(bank, fintsBank)
val mappedResponse = mapper.mapResponse(bank, response)
saveData()
@ -76,33 +77,33 @@ open class fints4kBankingClient(
override fun getTransactionsAsync(parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) {
val bankAccount = parameter.account
val account = parameter.account
findAccountForBankAccount(bankAccount) { account, errorMessage ->
if (account == null) {
callback(GetTransactionsResponse(bankAccount, errorMessage ?: ""))
findAccountForAccount(account) { accountData, errorMessage ->
if (accountData == null) {
callback(GetTransactionsResponse(account, errorMessage ?: ""))
}
else {
val mappedParameter = GetTransactionsParameter(account, parameter.alsoRetrieveBalance, parameter.fromDate,
val mappedParameter = GetTransactionsParameter(accountData, parameter.alsoRetrieveBalance, parameter.fromDate,
parameter.toDate, null, parameter.abortIfTanIsRequired) {
parameter.retrievedChunkListener?.invoke(mapper.mapTransactions(bankAccount, it))
parameter.retrievedChunkListener?.invoke(mapper.mapTransactions(account, it))
}
doGetTransactionsAsync(mappedParameter, bankAccount, callback)
doGetTransactionsAsync(mappedParameter, account, callback)
}
}
}
protected open fun doGetTransactionsAsync(parameter: net.dankito.banking.fints.model.GetTransactionsParameter,
bankAccount: TypedBankAccount, callback: (GetTransactionsResponse) -> Unit) {
account: TypedBankAccount, callback: (GetTransactionsResponse) -> Unit) {
client.getTransactionsAsync(parameter) { response ->
handleGetTransactionsResponse(bankAccount, response, callback)
handleGetTransactionsResponse(account, response, callback)
}
}
protected open fun handleGetTransactionsResponse(bankAccount: TypedBankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse,
protected open fun handleGetTransactionsResponse(account: TypedBankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse,
callback: (GetTransactionsResponse) -> Unit) {
val mappedResponse = mapper.mapResponse(bankAccount, response)
val mappedResponse = mapper.mapResponse(account, response)
saveData()
@ -111,7 +112,7 @@ open class fints4kBankingClient(
override fun transferMoneyAsync(data: TransferMoneyData, callback: (BankingClientResponse) -> Unit) {
findAccountForBankAccount(data.account) { account, errorMessage ->
findAccountForAccount(data.account) { account, errorMessage ->
if (account == null) {
callback(BankingClientResponse(false, errorMessage))
}
@ -136,19 +137,19 @@ open class fints4kBankingClient(
}
override fun dataChanged(customer: TypedCustomer) {
mapper.mapChangesFromUiToClientModel(customer, bank)
override fun dataChanged(bank: TypedBankData) {
mapper.mapChangesFromUiToClientModel(bank, this.fintsBank)
}
override fun deletedAccount(customer: TypedCustomer, wasLastAccountWithThisCredentials: Boolean) {
override fun deletedBank(bank: TypedBankData, wasLastAccountWithThisCredentials: Boolean) {
if (wasLastAccountWithThisCredentials) {
getFints4kClientDataFile(customer).delete()
getFints4kClientDataFile(bank).delete()
}
}
protected open fun findAccountForBankAccount(bankAccount: TypedBankAccount, findAccountResult: (AccountData?, error: String?) -> Unit) {
val mappedAccount = mapper.findAccountForBankAccount(bank, bankAccount)
protected open fun findAccountForAccount(account: TypedBankAccount, findAccountResult: (AccountData?, error: String?) -> Unit) {
val mappedAccount = mapper.findMatchingAccount(fintsBank, account)
if (mappedAccount != null) {
findAccountResult(mappedAccount, null)
@ -157,37 +158,37 @@ open class fints4kBankingClient(
addAccountAsync { response ->
didTryToGetAccountDataFromBank = !!! response.successful
findAccountResult(mapper.findAccountForBankAccount(bank, bankAccount),
findAccountResult(mapper.findMatchingAccount(fintsBank, account),
response.errorToShowToUser)
}
}
else {
findAccountResult(null, "Cannot find account for ${bankAccount.identifier}") // TODO: translate
findAccountResult(null, "Cannot find account for ${account.identifier}") // TODO: translate
}
}
protected open fun restoreDataOrMapFromUiModel(customer: TypedCustomer): BankData {
if (isNewAccount(customer)) {
return mapToBankData(customer)
protected open fun restoreDataOrMapFromUiModel(bank: TypedBankData): BankData {
if (isNewAccount(bank)) {
return mapToBankData(bank)
}
return restoreData(customer) ?: mapToBankData(customer)
return restoreData(bank) ?: mapToBankData(bank)
}
protected open fun isNewAccount(customer: TypedCustomer): Boolean {
return customer.accounts.isEmpty()
protected open fun isNewAccount(bank: TypedBankData): Boolean {
return bank.accounts.isEmpty()
}
protected open fun mapToBankData(customer: TypedCustomer): BankData {
return BankData(customer.bankCode, customer.customerId, customer.password, customer.finTsServerAddress, customer.bic, customer.bankName)
protected open fun mapToBankData(bank: TypedBankData): BankData {
return BankData(bank.bankCode, bank.customerId, bank.password, bank.finTsServerAddress, bank.bic, bank.bankName)
}
protected open fun restoreData(customer: TypedCustomer): BankData? {
protected open fun restoreData(bank: TypedBankData): BankData? {
try {
return serializer.deserializeObject(getFints4kClientDataFile(customer), BankData::class)
return serializer.deserializeObject(getFints4kClientDataFile(bank), BankData::class)
} catch (e: Exception) {
log.warn(e) { "Could not deserialize bank data of $customer" }
log.warn(e) { "Could not deserialize bank data of $bank" }
}
return null
@ -195,16 +196,16 @@ open class fints4kBankingClient(
protected open fun saveData() {
try {
val clientDataFile = getFints4kClientDataFile(bank.bankCode, bank.customerId)
val clientDataFile = getFints4kClientDataFile(fintsBank.bankCode, fintsBank.customerId)
serializer.serializeObject(bank, clientDataFile)
serializer.serializeObject(fintsBank, clientDataFile)
} catch (e: Exception) {
log.error("Could not save customer data for $bank", e)
log.error("Could not save bank data for $fintsBank", e)
}
}
protected open fun getFints4kClientDataFile(customer: TypedCustomer): File {
return getFints4kClientDataFile(customer.bankCode, customer.customerId)
protected open fun getFints4kClientDataFile(bank: TypedBankData): File {
return getFints4kClientDataFile(bank.bankCode, bank.customerId)
}
protected open fun getFints4kClientDataFile(bankCode: String, customerId: String): File {
@ -240,15 +241,15 @@ open class fints4kBankingClient(
}
protected open fun handleEnterTan(bank: BankData, tanChallenge: TanChallenge, enterTanCallback: (EnterTanResult) -> Unit, clientCallback: BankingClientCallback) {
mapper.updateTanMediaAndMethods(this@fints4kBankingClient.customer, bank)
mapper.updateTanMediaAndMethods(this@fints4kBankingClient.bank, bank)
clientCallback.enterTan(this@fints4kBankingClient.customer, mapper.mapTanChallenge(tanChallenge)) { result ->
clientCallback.enterTan(this@fints4kBankingClient.bank, mapper.mapTanChallenge(tanChallenge)) { result ->
enterTanCallback(mapper.mapEnterTanResult(result, bank))
}
}
protected open fun handleEnterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium, enterAtcCallback: (EnterTanGeneratorAtcResult) -> Unit, clientCallback: BankingClientCallback) {
mapper.updateTanMediaAndMethods(this@fints4kBankingClient.customer, bank)
mapper.updateTanMediaAndMethods(this@fints4kBankingClient.bank, bank)
clientCallback.enterTanGeneratorAtc(mapper.mapTanMedium(tanMedium)) { result ->
enterAtcCallback(mapper.mapEnterTanGeneratorAtcResult(result))

View File

@ -5,7 +5,7 @@ import net.dankito.banking.ui.IBankingClient
import net.dankito.banking.ui.IBankingClientCreator
import net.dankito.banking.fints.webclient.IWebClient
import net.dankito.banking.fints.webclient.KtorWebClient
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.mapper.IModelCreator
import net.dankito.banking.util.IAsyncRunner
import net.dankito.banking.util.ISerializer
@ -19,13 +19,13 @@ open class fints4kBankingClientCreator(
) : IBankingClientCreator {
override fun createClient(
customer: TypedCustomer,
bank: TypedBankData,
dataFolder: File,
asyncRunner: IAsyncRunner,
callback: BankingClientCallback
): IBankingClient {
return fints4kBankingClient(customer, modelCreator, dataFolder, serializer, webClient, callback = callback)
return fints4kBankingClient(bank, modelCreator, dataFolder, serializer, webClient, callback = callback)
}
}

View File

@ -28,23 +28,23 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
return BankingClientResponse(response.successful, mapErrorToShowToUser(response), response.userCancelledAction)
}
open fun mapResponse(customer: TypedCustomer, response: net.dankito.banking.fints.response.client.AddAccountResponse): AddAccountResponse {
open fun mapResponse(bank: TypedBankData, response: net.dankito.banking.fints.response.client.AddAccountResponse): AddAccountResponse {
return AddAccountResponse(customer, map(customer, response.retrievedData), mapErrorToShowToUser(response), response.userCancelledAction)
return AddAccountResponse(bank, map(bank, response.retrievedData), mapErrorToShowToUser(response), response.userCancelledAction)
}
open fun mapResponse(bankAccount: TypedBankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse): GetTransactionsResponse {
open fun mapResponse(account: TypedBankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse): GetTransactionsResponse {
return GetTransactionsResponse(map(bankAccount.customer as TypedCustomer, response.retrievedData),
return GetTransactionsResponse(map(account.bank as TypedBankData, response.retrievedData),
mapErrorToShowToUser(response), response.userCancelledAction, response.tanRequiredButWeWereToldToAbortIfSo)
}
open fun map(customer: TypedCustomer, retrievedData: List<net.dankito.banking.fints.model.RetrievedAccountData>): List<RetrievedAccountData> {
return retrievedData.mapNotNull { map(customer, it) }
open fun map(bank: TypedBankData, retrievedData: List<net.dankito.banking.fints.model.RetrievedAccountData>): List<RetrievedAccountData> {
return retrievedData.mapNotNull { map(bank, it) }
}
open fun map(customer: TypedCustomer, retrievedData: net.dankito.banking.fints.model.RetrievedAccountData): RetrievedAccountData? {
val account = findMatchingBankAccount(customer, retrievedData.accountData)
open fun map(bank: TypedBankData, retrievedData: net.dankito.banking.fints.model.RetrievedAccountData): RetrievedAccountData? {
val account = findMatchingAccount(bank, retrievedData.accountData)
if (account == null) {
log.error("No matching account found for ${retrievedData.accountData}. Has there an account been added we didn't map yet?")
@ -70,41 +70,41 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
open fun mapBank(customer: TypedCustomer, bank: BankData) {
customer.bankCode = bank.bankCode
customer.customerId = bank.customerId
customer.password = bank.pin
customer.finTsServerAddress = bank.finTs3ServerAddress
customer.bankName = bank.bankName
customer.bic = bank.bic
customer.customerName = bank.customerName
customer.countDaysForWhichTransactionsAreKept = bank.countDaysForWhichTransactionsAreKept
customer.userId = bank.userId
open fun mapBank(bank: TypedBankData, fintsBank: BankData) {
bank.bankCode = fintsBank.bankCode
bank.customerId = fintsBank.customerId
bank.password = fintsBank.pin
bank.finTsServerAddress = fintsBank.finTs3ServerAddress
bank.bankName = fintsBank.bankName
bank.bic = fintsBank.bic
bank.customerName = fintsBank.customerName
bank.countDaysForWhichTransactionsAreKept = fintsBank.countDaysForWhichTransactionsAreKept
bank.userId = fintsBank.userId
customer.accounts = mapBankAccounts(customer, bank.accounts)
bank.accounts = mapAccounts(bank, fintsBank.accounts)
updateTanMediaAndMethods(customer, bank)
updateTanMediaAndMethods(bank, fintsBank)
}
/**
* In UI only customerId, password, (bankCode,) and selected TAN method can be set
*/
open fun mapChangesFromUiToClientModel(customer: TypedCustomer, bank: BankData) {
bank.customerId = customer.customerId
bank.pin = customer.password
open fun mapChangesFromUiToClientModel(bank: TypedBankData, fintsBank: BankData) {
fintsBank.customerId = bank.customerId
fintsBank.pin = bank.password
bank.bankCode = customer.bankCode
fintsBank.bankCode = bank.bankCode
bank.selectedTanMethod = findTanMethod(bank, customer.selectedTanMethod) ?: bank.selectedTanMethod
fintsBank.selectedTanMethod = findTanMethod(fintsBank, bank.selectedTanMethod) ?: fintsBank.selectedTanMethod
}
open fun mapBankAccounts(customer: TypedCustomer, accountData: List<AccountData>): List<TypedBankAccount> {
open fun mapAccounts(bank: TypedBankData, accountData: List<AccountData>): List<TypedBankAccount> {
return accountData.mapIndexed { index, account ->
val mappedAccount = customer.accounts.firstOrNull { it.identifier == account.accountIdentifier }
?: modelCreator.createBankAccount(customer, account.productName, account.accountIdentifier)
val mappedAccount = bank.accounts.firstOrNull { it.identifier == account.accountIdentifier }
?: modelCreator.createAccount(bank, account.productName, account.accountIdentifier)
mapBankAccount(mappedAccount, account)
mapAccount(mappedAccount, account)
mappedAccount.displayIndex = index
@ -112,7 +112,7 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
}
open fun mapBankAccount(account: TypedBankAccount, accountData: AccountData) {
open fun mapAccount(account: TypedBankAccount, accountData: AccountData) {
account.identifier = accountData.accountIdentifier
account.accountHolderName = accountData.accountHolderName
account.iban = accountData.iban
@ -147,22 +147,22 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
// TODO: move to a fints4k internal mapper
open fun updateBankAccounts(bank: BankData, updatedAccounts: List<AccountData>) {
open fun updateAccounts(bank: BankData, updatedAccounts: List<AccountData>) {
val accounts = bank.accounts
updatedAccounts.forEach { updatedAccount ->
val matchingExistingAccount = findMatchingBankAccount(accounts, updatedAccount)
val matchingExistingAccount = findMatchingAccount(accounts, updatedAccount)
if (matchingExistingAccount == null) {
bank.addAccount(updatedAccount)
}
else {
updateBankAccount(matchingExistingAccount, updatedAccount)
updateAccount(matchingExistingAccount, updatedAccount)
}
}
bank.accounts.forEach { account ->
val updatedAccount = findMatchingBankAccount(updatedAccounts, account)
val updatedAccount = findMatchingAccount(updatedAccounts, account)
if (updatedAccount == null) {
bank.removeAccount(account)
@ -170,7 +170,7 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
}
open fun updateBankAccount(account: AccountData, updatedAccount: AccountData) {
open fun updateAccount(account: AccountData, updatedAccount: AccountData) {
account.allowedJobs = updatedAccount.allowedJobs
AccountFeature.values().forEach { feature ->
@ -178,26 +178,26 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
}
open fun findAccountForBankAccount(bank: BankData, bankAccount: TypedBankAccount): AccountData? {
return bank.accounts.firstOrNull { bankAccount.identifier == it.accountIdentifier }
open fun findMatchingAccount(bank: BankData, account: TypedBankAccount): AccountData? {
return bank.accounts.firstOrNull { account.identifier == it.accountIdentifier }
}
open fun findMatchingBankAccount(customer: TypedCustomer, accountData: AccountData): TypedBankAccount? {
return customer.accounts.firstOrNull { it.identifier == accountData.accountIdentifier }
open fun findMatchingAccount(bank: TypedBankData, accountData: AccountData): TypedBankAccount? {
return bank.accounts.firstOrNull { it.identifier == accountData.accountIdentifier }
}
open fun findMatchingBankAccount(accounts: List<AccountData>, accountData: AccountData): AccountData? {
open fun findMatchingAccount(accounts: List<AccountData>, accountData: AccountData): AccountData? {
return accounts.firstOrNull { it.accountIdentifier == accountData.accountIdentifier }
}
open fun mapTransactions(bankAccount: TypedBankAccount, transactions: Collection<net.dankito.banking.fints.model.AccountTransaction>): List<IAccountTransaction> {
return transactions.map { mapTransaction(bankAccount, it) }
open fun mapTransactions(account: TypedBankAccount, transactions: Collection<net.dankito.banking.fints.model.AccountTransaction>): List<IAccountTransaction> {
return transactions.map { mapTransaction(account, it) }
}
open fun mapTransaction(bankAccount: TypedBankAccount, transaction: net.dankito.banking.fints.model.AccountTransaction): IAccountTransaction {
open fun mapTransaction(account: TypedBankAccount, transaction: net.dankito.banking.fints.model.AccountTransaction): IAccountTransaction {
return modelCreator.createTransaction(
bankAccount,
account,
transaction.amount.toBigDecimal(),
transaction.amount.currency.code,
transaction.unparsedUsage,
@ -238,20 +238,20 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
open fun updateTanMediaAndMethods(account: TypedCustomer, bank: BankData) {
account.supportedTanMethods = bank.tanMethodsAvailableForUser.map { tanMethod ->
findMappedTanMethod(account, tanMethod) ?: mapTanMethod(tanMethod)
open fun updateTanMediaAndMethods(bank: TypedBankData, fintsBank: BankData) {
bank.supportedTanMethods = fintsBank.tanMethodsAvailableForUser.map { tanMethod ->
findMappedTanMethod(bank, tanMethod) ?: mapTanMethod(tanMethod)
}
if (bank.isTanMethodSelected) {
account.selectedTanMethod = findMappedTanMethod(account, bank.selectedTanMethod)
if (fintsBank.isTanMethodSelected) {
bank.selectedTanMethod = findMappedTanMethod(bank, fintsBank.selectedTanMethod)
}
else {
account.selectedTanMethod = null
bank.selectedTanMethod = null
}
account.tanMedia = bank.tanMedia.map { tanMedium ->
findMappedTanMedium(account, tanMedium) ?: mapTanMedium(tanMedium)
bank.tanMedia = fintsBank.tanMedia.map { tanMedium ->
findMappedTanMedium(bank, tanMedium) ?: mapTanMedium(tanMedium)
}
}
@ -292,8 +292,8 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
}
}
protected open fun findMappedTanMethod(customer: TypedCustomer, tanMethod: net.dankito.banking.fints.model.TanMethod): TanMethod? {
return customer.supportedTanMethods.firstOrNull { it.bankInternalMethodCode == tanMethod.securityFunction.code }
protected open fun findMappedTanMethod(bank: TypedBankData, tanMethod: net.dankito.banking.fints.model.TanMethod): TanMethod? {
return bank.supportedTanMethods.firstOrNull { it.bankInternalMethodCode == tanMethod.securityFunction.code }
}
protected open fun findTanMethod(bank: BankData, tanMethod: TanMethod?): net.dankito.banking.fints.model.TanMethod? {
@ -304,8 +304,8 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
return bank.tanMethodsAvailableForUser.firstOrNull { it.securityFunction.code == tanMethod.bankInternalMethodCode }
}
protected open fun findMappedTanMedium(customer: TypedCustomer, tanMedium: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium): TanMedium? {
return customer.tanMedia.firstOrNull { doesMatchTanMedium(tanMedium, it) }
protected open fun findMappedTanMedium(bank: TypedBankData, tanMedium: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium): TanMedium? {
return bank.tanMedia.firstOrNull { doesMatchTanMedium(tanMedium, it) }
}
protected open fun doesMatchTanMedium(fintsTanMedium: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium, tanMedium: TanMedium): Boolean {

View File

@ -2,7 +2,7 @@ package net.dankito.banking
import net.dankito.banking.model.AccountCredentials
import net.dankito.banking.ui.BankingClientCallback
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.tan.FlickerCodeTanChallenge
import net.dankito.banking.ui.model.tan.ImageTanChallenge
import net.dankito.banking.ui.model.tan.TanChallenge
@ -23,7 +23,7 @@ import org.slf4j.LoggerFactory
*/
open class HbciCallback(
protected val credentials: AccountCredentials,
protected val customer: TypedCustomer,
protected val bank: TypedBankData,
protected val mapper: hbci4jModelMapper,
protected val callback: BankingClientCallback
) : AbstractHBCICallback() {
@ -84,13 +84,13 @@ open class HbciCallback(
// ADDED: Auswaehlen welches PinTan Verfahren verwendet werden soll
HBCICallback.NEED_PT_SECMECH -> selectTanMethod(retData.toString())?.let { selectedTanMethod ->
customer.selectedTanMethod = selectedTanMethod
bank.selectedTanMethod = selectedTanMethod
retData.replace(0, retData.length, selectedTanMethod.bankInternalMethodCode)
}
// chipTan or simple TAN request (iTAN, smsTAN, ...)
HBCICallback.NEED_PT_TAN -> {
getTanFromUser(customer, msg, retData.toString())?.let { enteredTan ->
getTanFromUser(bank, msg, retData.toString())?.let { enteredTan ->
retData.replace(0, retData.length, enteredTan)
}
}
@ -99,7 +99,7 @@ open class HbciCallback(
HBCICallback.NEED_PT_QRTAN -> { // use class QRCode to display QR code
val qrData = retData.toString()
val qrCode = QRCode(qrData, msg)
val enterTanResult = callback.enterTan(customer, ImageTanChallenge(TanImage(qrCode.mimetype, qrCode.image), msg, customer.selectedTanMethod!!))
val enterTanResult = callback.enterTan(bank, ImageTanChallenge(TanImage(qrCode.mimetype, qrCode.image), msg, bank.selectedTanMethod!!))
enterTanResult.enteredTan?.let { enteredTan ->
retData.replace(0, retData.length, enteredTan)
}
@ -108,7 +108,7 @@ open class HbciCallback(
// photoTan
HBCICallback.NEED_PT_PHOTOTAN -> { // use class MatrixCode to display photo
val matrixCode = MatrixCode(retData.toString())
val enterTanResult = callback.enterTan(customer, ImageTanChallenge(TanImage(matrixCode.mimetype, matrixCode.image), msg, customer.selectedTanMethod!!))
val enterTanResult = callback.enterTan(bank, ImageTanChallenge(TanImage(matrixCode.mimetype, matrixCode.image), msg, bank.selectedTanMethod!!))
enterTanResult.enteredTan?.let { enteredTan ->
retData.replace(0, retData.length, enteredTan)
}
@ -171,7 +171,7 @@ open class HbciCallback(
}
open fun getTanFromUser(customer: TypedCustomer, messageToShowToUser: String, challengeHHD_UC: String): String? {
open fun getTanFromUser(bank: TypedBankData, messageToShowToUser: String, challengeHHD_UC: String): String? {
// Wenn per "retData" Daten uebergeben wurden, dann enthalten diese
// den fuer chipTAN optisch zu verwendenden Flickercode.
// Falls nicht, ist es eine TAN-Abfrage, fuer die keine weiteren
@ -183,14 +183,14 @@ open class HbciCallback(
// werden.
val enterTanResult = if (challengeHHD_UC.isNullOrEmpty()) {
callback.enterTan(customer, TanChallenge(messageToShowToUser, customer.selectedTanMethod!!))
callback.enterTan(bank, TanChallenge(messageToShowToUser, bank.selectedTanMethod!!))
}
else {
// for Sparkasse messageToShowToUser started with "chipTAN optisch\nTAN-Nummer\n\n"
val usefulMessage = messageToShowToUser.split("\n").last().trim()
// val parsedDataSet = FlickerCode(challengeHHD_UC).render()
callback.enterTan(customer, FlickerCodeTanChallenge(net.dankito.banking.ui.model.tan.FlickerCode("", challengeHHD_UC), usefulMessage, customer.selectedTanMethod!!))
callback.enterTan(bank, FlickerCodeTanChallenge(net.dankito.banking.ui.model.tan.FlickerCode("", challengeHHD_UC), usefulMessage, bank.selectedTanMethod!!))
}
return enterTanResult.enteredTan
@ -201,7 +201,7 @@ open class HbciCallback(
open fun selectTanMethod(supportedTanMethodsString: String): net.dankito.banking.ui.model.tan.TanMethod? {
val supportedTanMethods = mapper.mapTanMethods(supportedTanMethodsString)
customer.supportedTanMethods = supportedTanMethods
bank.supportedTanMethods = supportedTanMethods
if (supportedTanMethods.isNotEmpty()) {
// select any method, user then can select her preferred one in EnterTanDialog; try not to select 'chipTAN manuell'

View File

@ -33,7 +33,7 @@ import java.util.*
open class hbci4jBankingClient(
protected val customer: TypedCustomer,
protected val bank: TypedBankData,
modelCreator: IModelCreator,
protected val dataFolder: File,
protected val asyncRunner: IAsyncRunner = ThreadPoolAsyncRunner(ThreadPool()),
@ -50,7 +50,7 @@ open class hbci4jBankingClient(
}
protected val credentials = AccountCredentials(customer)
protected val credentials = AccountCredentials(bank)
protected val mapper = hbci4jModelMapper(modelCreator)
@ -76,22 +76,22 @@ open class hbci4jBankingClient(
val accounts = passport.accounts
if (accounts == null || accounts.size == 0) {
log.error("Keine Konten ermittelbar")
return AddAccountResponse(customer, "Keine Konten ermittelbar") // TODO: translate
return AddAccountResponse(bank, "Keine Konten ermittelbar") // TODO: translate
}
this.customer.accounts = mapper.mapBankAccounts(customer, accounts, passport)
this.bank.accounts = mapper.mapAccounts(bank, accounts, passport)
return tryToRetrieveAccountTransactionsForAddedAccounts(customer)
return tryToRetrieveAccountTransactionsForAddedAccounts(bank)
}
}
return AddAccountResponse(customer, connection.error?.getInnerExceptionMessage() ?: "Could not connect")
return AddAccountResponse(bank, connection.error?.getInnerExceptionMessage() ?: "Could not connect")
}
protected open fun tryToRetrieveAccountTransactionsForAddedAccounts(customer: TypedCustomer): AddAccountResponse {
protected open fun tryToRetrieveAccountTransactionsForAddedAccounts(bank: TypedBankData): AddAccountResponse {
var userCancelledAction = false
val retrievedData = customer.accounts.map { account ->
val retrievedData = bank.accounts.map { account ->
if (account.supportsRetrievingAccountTransactions) {
val response = getTransactionsOfLast90Days(account)
@ -106,34 +106,34 @@ open class hbci4jBankingClient(
}
}
return AddAccountResponse(customer, retrievedData, null, userCancelledAction)
return AddAccountResponse(bank, retrievedData, null, userCancelledAction)
}
/**
* According to PSD2 for the accounting entries of the last 90 days the two-factor authorization does not have to
* According to PSD2 for the account transactions of the last 90 days the two-factor authorization does not have to
* be applied. It depends on the bank if they request a second factor or not.
*
* So we simply try to retrieve at accounting entries of the last 90 days and see if a second factor is required
* So we simply try to retrieve at account transactions of the last 90 days and see if a second factor is required
* or not.
*/
open fun getTransactionsOfLast90DaysAsync(bankAccount: TypedBankAccount, callback: (GetTransactionsResponse) -> Unit) {
open fun getTransactionsOfLast90DaysAsync(account: TypedBankAccount, callback: (GetTransactionsResponse) -> Unit) {
asyncRunner.runAsync {
callback(getTransactionsOfLast90Days(bankAccount))
callback(getTransactionsOfLast90Days(account))
}
}
/**
* According to PSD2 for the accounting entries of the last 90 days the two-factor authorization does not have to
* According to PSD2 for the account transactions of the last 90 days the two-factor authorization does not have to
* be applied. It depends on the bank if they request a second factor or not.
*
* So we simply try to retrieve at accounting entries of the last 90 days and see if a second factor is required
* So we simply try to retrieve at account transactions of the last 90 days and see if a second factor is required
* or not.
*/
open fun getTransactionsOfLast90Days(bankAccount: TypedBankAccount): GetTransactionsResponse {
open fun getTransactionsOfLast90Days(account: TypedBankAccount): GetTransactionsResponse {
val ninetyDaysAgo = Date(Date.today.time - NinetyDaysInMilliseconds)
return getTransactions(GetTransactionsParameter(bankAccount, bankAccount.supportsRetrievingBalance, ninetyDaysAgo)) // TODO: implement abortIfTanIsRequired
return getTransactions(GetTransactionsParameter(account, account.supportsRetrievingBalance, ninetyDaysAgo)) // TODO: implement abortIfTanIsRequired
}
override fun getTransactionsAsync(parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) {
@ -148,7 +148,7 @@ open class hbci4jBankingClient(
connection.handle?.let { handle ->
try {
val (nullableBalanceJob, accountTransactionsJob, status) = executeJobsForGetAccountingEntries(handle, parameter)
val (nullableBalanceJob, accountTransactionsJob, status) = executeJobsForGetAccountTransactions(handle, parameter)
// Pruefen, ob die Kommunikation mit der Bank grundsaetzlich geklappt hat
if (!status.isOK) {
@ -180,10 +180,10 @@ open class hbci4jBankingClient(
}
return GetTransactionsResponse(RetrievedAccountData(account, true, balance.toBigDecimal(),
accountTransactionMapper.mapAccountTransactions(account, result), listOf(), parameter.fromDate, parameter.toDate))
accountTransactionMapper.mapTransactions(account, result), listOf(), parameter.fromDate, parameter.toDate))
}
catch(e: Exception) {
log.error("Could not get accounting details for bank ${credentials.bankCode}", e)
log.error("Could not get account transactions for bank ${credentials.bankCode}", e)
return GetTransactionsResponse(account, e.getInnerExceptionMessage())
}
finally {
@ -196,7 +196,7 @@ open class hbci4jBankingClient(
return GetTransactionsResponse(account, connection.error?.getInnerExceptionMessage() ?: "Could not connect")
}
protected open fun executeJobsForGetAccountingEntries(handle: HBCIHandler, parameter: GetTransactionsParameter): Triple<HBCIJob?, HBCIJob, HBCIExecStatus> {
protected open fun executeJobsForGetAccountTransactions(handle: HBCIHandler, parameter: GetTransactionsParameter): Triple<HBCIJob?, HBCIJob, HBCIExecStatus> {
val konto = mapper.mapToKonto(parameter.account)
// 1. Auftrag fuer das Abrufen des Saldos erzeugen
@ -272,17 +272,17 @@ open class hbci4jBankingClient(
}
override fun dataChanged(customer: TypedCustomer) {
if (customer.bankCode != credentials.bankCode || customer.customerId != credentials.customerId || customer.password != credentials.password) {
override fun dataChanged(bank: TypedBankData) {
if (bank.bankCode != credentials.bankCode || bank.customerId != credentials.customerId || bank.password != credentials.password) {
getPassportFile(credentials).delete()
}
credentials.bankCode = customer.bankCode
credentials.customerId = customer.customerId
credentials.password = customer.password
credentials.bankCode = bank.bankCode
credentials.customerId = bank.customerId
credentials.password = bank.password
}
override fun deletedAccount(customer: TypedCustomer, wasLastAccountWithThisCredentials: Boolean) {
override fun deletedBank(bank: TypedBankData, wasLastAccountWithThisCredentials: Boolean) {
getPassportFile(credentials).delete()
}
@ -296,7 +296,7 @@ open class hbci4jBankingClient(
// In "props" koennen optional Kernel-Parameter abgelegt werden, die in der Klasse
// org.kapott.hbci.manager.HBCIUtils (oben im Javadoc) beschrieben sind.
val props = Properties()
HBCIUtils.init(props, HbciCallback(credentials, customer, mapper, callback))
HBCIUtils.init(props, HbciCallback(credentials, bank, mapper, callback))
// In der Passport-Datei speichert HBCI4Java die Daten des Bankzugangs (Bankparameterdaten, Benutzer-Parameter, etc.).
// Die Datei kann problemlos geloescht werden. Sie wird beim naechsten mal automatisch neu erzeugt,

View File

@ -3,7 +3,7 @@ package net.dankito.banking
import net.dankito.banking.ui.BankingClientCallback
import net.dankito.banking.ui.IBankingClient
import net.dankito.banking.ui.IBankingClientCreator
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
import net.dankito.banking.ui.model.mapper.IModelCreator
import net.dankito.banking.util.IAsyncRunner
import net.dankito.utils.multiplatform.File
@ -14,13 +14,13 @@ open class hbci4jBankingClientCreator(
) : IBankingClientCreator {
override fun createClient(
customer: TypedCustomer,
bank: TypedBankData,
dataFolder: File,
asyncRunner: IAsyncRunner,
callback: BankingClientCallback
): IBankingClient {
return hbci4jBankingClient(customer, modelCreator, dataFolder, asyncRunner, callback)
return hbci4jBankingClient(bank, modelCreator, dataFolder, asyncRunner, callback)
}
}

View File

@ -1,6 +1,6 @@
package net.dankito.banking.model
import net.dankito.banking.ui.model.TypedCustomer
import net.dankito.banking.ui.model.TypedBankData
open class AccountCredentials(
@ -9,6 +9,6 @@ open class AccountCredentials(
var password: String
) {
constructor(bank: TypedCustomer) : this(bank.bankCode, bank.customerId, bank.password)
constructor(bank: TypedBankData) : this(bank.bankCode, bank.customerId, bank.password)
}

View File

@ -28,26 +28,26 @@ open class AccountTransactionMapper(
}
open fun mapAccountTransactions(bankAccount: TypedBankAccount, result: GVRKUms): List<IAccountTransaction> {
open fun mapTransactions(account: TypedBankAccount, result: GVRKUms): List<IAccountTransaction> {
val entries = mutableListOf<IAccountTransaction>()
result.dataPerDay.forEach { btag ->
btag.lines.forEach { transaction ->
entries.add(mapAccountingEntry(bankAccount, btag, transaction))
entries.add(mapTransaction(account, btag, transaction))
}
}
log.debug("Retrieved ${result.flatData.size} accounting entries")
log.debug("Retrieved ${result.flatData.size} account transactions")
return entries
}
protected open fun mapAccountingEntry(bankAccount: TypedBankAccount, btag: GVRKUms.BTag, transaction: GVRKUms.UmsLine): IAccountTransaction {
protected open fun mapTransaction(account: TypedBankAccount, btag: GVRKUms.BTag, transaction: GVRKUms.UmsLine): IAccountTransaction {
val unparsedUsage = transaction.usage.joinToString("")
val parsedUsage = Mt940Parser().getUsageParts(unparsedUsage)
val statementAndMaySequenceNumber = btag.counter.split('/')
return modelCreator.createTransaction(bankAccount,
return modelCreator.createTransaction(account,
mapValue(transaction.value), transaction.value.curr, unparsedUsage, transaction.bdate.toDate(),
transaction.other.name + (transaction.other.name2 ?: ""),
transaction.other.bic ?: transaction.other.blz,

View File

@ -15,14 +15,14 @@ open class hbci4jModelMapper(
protected val modelCreator: IModelCreator
) {
open fun mapToKonto(bankAccount: TypedBankAccount): Konto {
val customer = bankAccount.customer
open fun mapToKonto(account: TypedBankAccount): Konto {
val bank = account.bank
val konto = Konto("DE", customer.bankCode, bankAccount.identifier, bankAccount.subAccountNumber)
val konto = Konto("DE", bank.bankCode, account.identifier, account.subAccountNumber)
konto.name = customer.bankName
konto.iban = bankAccount.iban
konto.bic = customer.bic
konto.name = bank.bankName
konto.iban = account.iban
konto.bic = bank.bic
return konto
}
@ -42,32 +42,32 @@ open class hbci4jModelMapper(
}
open fun mapBankAccounts(customer: TypedCustomer, bankAccounts: Array<out Konto>, passport: HBCIPassport): List<TypedBankAccount> {
return bankAccounts.map { bankAccount ->
val iban = if (bankAccount.iban.isNullOrBlank() == false) bankAccount.iban else passport.upd.getProperty("KInfo.iban") ?: ""
open fun mapAccounts(bank: TypedBankData, accounts: Array<out Konto>, passport: HBCIPassport): List<TypedBankAccount> {
return accounts.map { account ->
val iban = if (account.iban.isNullOrBlank() == false) account.iban else passport.upd.getProperty("KInfo.iban") ?: ""
val result = modelCreator.createBankAccount(customer, bankAccount.number,
if (bankAccount.name2.isNullOrBlank() == false) bankAccount.name + " " + bankAccount.name2 else bankAccount.name)
val result = modelCreator.createAccount(bank, account.number,
if (account.name2.isNullOrBlank() == false) account.name + " " + account.name2 else account.name)
result.iban = iban
result.subAccountNumber = bankAccount.subnumber
result.customerId = bankAccount.customerid
result.subAccountNumber = account.subnumber
result.customerId = account.customerid
result.currency = bankAccount.curr
result.type = mapBankAccountType(bankAccount)
result.currency = account.curr
result.type = mapBankAccountType(account)
result.isAccountTypeSupported = result.type == BankAccountType.Girokonto || result.type == BankAccountType.Festgeldkonto
result.accountLimit = bankAccount.limit?.value?.let { mapValue(it).toString() }
result.accountLimit = account.limit?.value?.let { mapValue(it).toString() }
result.supportsRetrievingBalance = bankAccount.allowedGVs.contains("HKSAL")
result.supportsRetrievingAccountTransactions = bankAccount.allowedGVs.contains("HKKAZ")
result.supportsRetrievingBalance = bankAccount.allowedGVs.contains("HKCCS")
result.supportsRetrievingBalance = account.allowedGVs.contains("HKSAL")
result.supportsRetrievingAccountTransactions = account.allowedGVs.contains("HKKAZ")
result.supportsRetrievingBalance = account.allowedGVs.contains("HKCCS")
result
}
}
open fun mapBankAccountType(bankAccount: Konto): BankAccountType {
val type = bankAccount.acctype
open fun mapBankAccountType(account: Konto): BankAccountType {
val type = account.acctype
return when {
type.length == 1 -> BankAccountType.Girokonto