Compare commits
36 Commits
43031b24ad
...
98260fbfbf
Author | SHA1 | Date |
---|---|---|
dankito | 98260fbfbf | |
dankito | 52db7be2f6 | |
dankito | 2b0f9fcc71 | |
dankito | 5d8079cfd4 | |
dankito | f8dd296b47 | |
dankito | d1cc7b0eb0 | |
dankito | 5d127828cb | |
dankito | 803e44118c | |
dankito | 12304684fc | |
dankito | 7448b7e94c | |
dankito | 0e26a19d13 | |
dankito | 647f848d5f | |
dankito | ce39c1cf7d | |
dankito | f1981dc5f0 | |
dankito | dbccc93981 | |
dankito | 2245806d12 | |
dankito | 7d90910ffd | |
dankito | a5e809ff68 | |
dankito | ef1177c76f | |
dankito | a33f31df02 | |
dankito | 3940b1e77f | |
dankito | 67ea188182 | |
dankito | c443656c03 | |
dankito | ee21f684eb | |
dankito | 933c761a0d | |
dankito | 0b6490f501 | |
dankito | e915b4479f | |
dankito | bbd40d7017 | |
dankito | 3f3c480e1c | |
dankito | c0e9db7234 | |
dankito | 786b849c8f | |
dankito | 7d715cc3b8 | |
dankito | 2164bc5a94 | |
dankito | 00b26e3bd0 | |
dankito | 0bf5b19f4a | |
dankito | cf06bffd1b |
|
@ -8,6 +8,7 @@ import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||||
import net.codinux.banking.client.model.response.GetTransactionsResponse
|
import net.codinux.banking.client.model.response.GetTransactionsResponse
|
||||||
import net.codinux.banking.client.model.response.Response
|
import net.codinux.banking.client.model.response.Response
|
||||||
import net.codinux.banking.client.model.response.TransferMoneyResponse
|
import net.codinux.banking.client.model.response.TransferMoneyResponse
|
||||||
|
import net.codinux.banking.client.model.tan.TanMethodType
|
||||||
|
|
||||||
interface BankingClient {
|
interface BankingClient {
|
||||||
|
|
||||||
|
@ -41,7 +42,10 @@ interface BankingClient {
|
||||||
*
|
*
|
||||||
* Optionally specify which [accounts] should be updated. If not specified all accounts will be updated.
|
* Optionally specify which [accounts] should be updated. If not specified all accounts will be updated.
|
||||||
*/
|
*/
|
||||||
suspend fun updateAccountTransactionsAsync(bank: BankAccess, accounts: List<BankAccount>? = null): Response<List<GetTransactionsResponse>>
|
suspend fun updateAccountTransactionsAsync(
|
||||||
|
bank: BankAccess, accounts: List<BankAccount>? = null,
|
||||||
|
preferredTanMethodsIfSelectedTanMethodIsNotAvailable: List<TanMethodType>? = TanMethodType.TanMethodsPreferredByMostApplications
|
||||||
|
): Response<List<GetTransactionsResponse>>
|
||||||
|
|
||||||
|
|
||||||
suspend fun transferMoneyAsync(bankCode: String, loginName: String, password: String, recipientName: String,
|
suspend fun transferMoneyAsync(bankCode: String, loginName: String, password: String, recipientName: String,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||||
import net.codinux.banking.client.model.response.GetTransactionsResponse
|
import net.codinux.banking.client.model.response.GetTransactionsResponse
|
||||||
import net.codinux.banking.client.model.response.Response
|
import net.codinux.banking.client.model.response.Response
|
||||||
import net.codinux.banking.client.model.response.TransferMoneyResponse
|
import net.codinux.banking.client.model.response.TransferMoneyResponse
|
||||||
|
import net.codinux.banking.client.model.tan.TanMethodType
|
||||||
|
|
||||||
interface BankingClientForUser {
|
interface BankingClientForUser {
|
||||||
|
|
||||||
|
@ -40,7 +41,10 @@ interface BankingClientForUser {
|
||||||
* Updates account's transactions beginning from [BankAccount.lastAccountUpdateTime].
|
* Updates account's transactions beginning from [BankAccount.lastAccountUpdateTime].
|
||||||
* This may requires TAN if [BankAccount.lastAccountUpdateTime] is older than 90 days.
|
* This may requires TAN if [BankAccount.lastAccountUpdateTime] is older than 90 days.
|
||||||
*/
|
*/
|
||||||
suspend fun updateAccountTransactionsAsync(accounts: List<BankAccount>? = null): Response<List<GetTransactionsResponse>>
|
suspend fun updateAccountTransactionsAsync(
|
||||||
|
accounts: List<BankAccount>? = null,
|
||||||
|
preferredTanMethodsIfSelectedTanMethodIsNotAvailable: List<TanMethodType>? = TanMethodType.TanMethodsPreferredByMostApplications
|
||||||
|
): Response<List<GetTransactionsResponse>>
|
||||||
|
|
||||||
|
|
||||||
suspend fun transferMoneyAsync(recipientName: String, recipientAccountIdentifier: String, amount: Amount, paymentReference: String? = null): Response<TransferMoneyResponse>
|
suspend fun transferMoneyAsync(recipientName: String, recipientAccountIdentifier: String, amount: Amount, paymentReference: String? = null): Response<TransferMoneyResponse>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import net.codinux.banking.client.model.request.TransferMoneyRequest
|
||||||
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
||||||
import net.codinux.banking.client.model.response.GetTransactionsResponse
|
import net.codinux.banking.client.model.response.GetTransactionsResponse
|
||||||
import net.codinux.banking.client.model.response.Response
|
import net.codinux.banking.client.model.response.Response
|
||||||
|
import net.codinux.banking.client.model.tan.TanMethodType
|
||||||
|
|
||||||
abstract class BankingClientForUserBase(
|
abstract class BankingClientForUserBase(
|
||||||
protected val credentials: AccountCredentials,
|
protected val credentials: AccountCredentials,
|
||||||
|
@ -25,7 +26,7 @@ abstract class BankingClientForUserBase(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun updateAccountTransactionsAsync(accounts: List<BankAccount>?): Response<List<GetTransactionsResponse>> =
|
override suspend fun updateAccountTransactionsAsync(accounts: List<BankAccount>?, preferredTanMethodsIfSelectedTanMethodIsNotAvailable: List<TanMethodType>?): Response<List<GetTransactionsResponse>> =
|
||||||
client.updateAccountTransactionsAsync(bank, accounts)
|
client.updateAccountTransactionsAsync(bank, accounts)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
||||||
import net.codinux.banking.client.model.request.TransferMoneyRequest
|
import net.codinux.banking.client.model.request.TransferMoneyRequest
|
||||||
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
||||||
|
import net.codinux.banking.client.model.tan.TanMethodType
|
||||||
|
|
||||||
/* BankingClient */
|
/* BankingClient */
|
||||||
|
|
||||||
|
@ -19,8 +20,8 @@ fun BankingClient.getAccountData(request: GetAccountDataRequest) = runBlocking {
|
||||||
getAccountDataAsync(request)
|
getAccountDataAsync(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun BankingClient.updateAccountTransactions(bank: BankAccess, accounts: List<BankAccount>? = null) = runBlocking {
|
fun BankingClient.updateAccountTransactions(bank: BankAccess, accounts: List<BankAccount>? = null, preferredTanMethodsIfSelectedTanMethodIsNotAvailable: List<TanMethodType>? = TanMethodType.TanMethodsPreferredByMostApplications) = runBlocking {
|
||||||
updateAccountTransactionsAsync(bank, accounts)
|
updateAccountTransactionsAsync(bank, accounts, preferredTanMethodsIfSelectedTanMethodIsNotAvailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,8 +45,8 @@ fun BankingClientForUser.getAccountData(options: GetAccountDataOptions) = runBlo
|
||||||
getAccountDataAsync(options)
|
getAccountDataAsync(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun BankingClientForUser.updateAccountTransactions() = runBlocking {
|
fun BankingClientForUser.updateAccountTransactions(accounts: List<BankAccount>? = null, preferredTanMethodsIfSelectedTanMethodIsNotAvailable: List<TanMethodType>? = TanMethodType.TanMethodsPreferredByMostApplications) = runBlocking {
|
||||||
updateAccountTransactionsAsync()
|
updateAccountTransactionsAsync(accounts, preferredTanMethodsIfSelectedTanMethodIsNotAvailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package net.codinux.banking.client.model
|
package net.codinux.banking.client.model
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
|
import net.codinux.banking.client.model.config.JsonIgnore
|
||||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
|
||||||
|
@Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED")
|
||||||
@NoArgConstructor
|
@NoArgConstructor
|
||||||
open class AccountTransaction(
|
open class AccountTransaction(
|
||||||
val amount: Amount = Amount.Zero, // TODO: a string is really bad in UI, find a better solution
|
val amount: Amount = Amount.Zero, // TODO: a string is really bad in UI, find a better solution
|
||||||
|
@ -134,5 +136,18 @@ open class AccountTransaction(
|
||||||
"$amount $currency $bookingDate $valueDate $reference $otherPartyName $otherPartyBankId $otherPartyAccountId"
|
"$amount $currency $bookingDate $valueDate $reference $otherPartyName $otherPartyBankId $otherPartyAccountId"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@get:JsonIgnore
|
||||||
|
open val displayedReference: String?
|
||||||
|
get() = userSetReference ?: referenceNumber
|
||||||
|
|
||||||
|
@get:JsonIgnore
|
||||||
|
open val displayedOtherPartyName: String?
|
||||||
|
get() = userSetOtherPartyName ?: otherPartyName
|
||||||
|
|
||||||
|
@get:JsonIgnore
|
||||||
|
open val displayedOtherPartyNameOrPostingText: String?
|
||||||
|
get() = displayedOtherPartyName ?: postingText
|
||||||
|
|
||||||
|
|
||||||
override fun toString() = "${valueDate.dayOfMonth}.${valueDate.monthNumber}.${valueDate.year} ${amount.toString().padStart(4, ' ')} ${if (currency == "EUR") "€" else currency} ${otherPartyName ?: ""} - $reference"
|
override fun toString() = "${valueDate.dayOfMonth}.${valueDate.monthNumber}.${valueDate.year} ${amount.toString().padStart(4, ' ')} ${if (currency == "EUR") "€" else currency} ${otherPartyName ?: ""} - $reference"
|
||||||
}
|
}
|
|
@ -70,6 +70,30 @@ open class BankAccess(
|
||||||
|
|
||||||
var wrongCredentialsEntered: Boolean = false
|
var wrongCredentialsEntered: Boolean = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BankingClient specific data of this account that the client needs to fulfill its job.
|
||||||
|
*
|
||||||
|
* You should treat it as opaque data, that only makes sense to the BankingClient, and pass it back to the client if set.
|
||||||
|
*
|
||||||
|
* For fints4k e.g. contains the FinTS jobs the bank supports, FinTS specific data like KundensystemID and so on.
|
||||||
|
*
|
||||||
|
* The deserialized in-memory only value of [serializedClientData] so that we don't have to deserialize [serializedClientData] each time.
|
||||||
|
*/
|
||||||
|
var clientData: Any? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized version of [clientData].
|
||||||
|
*
|
||||||
|
* The same as with [clientData] you should treat this value as opaque that only makes sense to the client implementation.
|
||||||
|
*
|
||||||
|
* [clientData] is the deserialized in-memory model of this value, so that we don't have to serialize this value each time.
|
||||||
|
* serializedClientData is the serialized version of clientData so that you can store (and restore) it e.g. to your
|
||||||
|
* database, so that on next application start client implementation doesn't have to fetch all these data again.
|
||||||
|
* Speeds up e.g. getting account transactions and transferring money with fints4k as FinTS requires quite a lot of
|
||||||
|
* data before account transactions can be retrieved.
|
||||||
|
*/
|
||||||
|
var serializedClientData: String? = null
|
||||||
|
|
||||||
|
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
open val displayName: String
|
open val displayName: String
|
||||||
|
|
|
@ -6,7 +6,18 @@ import kotlinx.datetime.Instant
|
||||||
open class MessageLogEntry(
|
open class MessageLogEntry(
|
||||||
open val type: MessageLogEntryType,
|
open val type: MessageLogEntryType,
|
||||||
open val message: String,
|
open val message: String,
|
||||||
open val messageTrace: String? = null,
|
open val messageWithoutSensitiveData: String? = null,
|
||||||
open val error: Throwable? = null,
|
open val error: Throwable? = null,
|
||||||
open val time: Instant = Clock.System.now()
|
open val time: Instant = Clock.System.now(),
|
||||||
)
|
|
||||||
|
val messageNumberString: String? = null,
|
||||||
|
val messageNumber: Int? = null,
|
||||||
|
|
||||||
|
val jobType: String? = null,
|
||||||
|
val messageCategory: String? = null,
|
||||||
|
|
||||||
|
val bank: BankAccess? = null, // TODO: make non-null
|
||||||
|
val account: BankAccount? = null
|
||||||
|
) {
|
||||||
|
override fun toString() = "$messageNumberString $jobType $messageCategory $type $message"
|
||||||
|
}
|
|
@ -27,14 +27,14 @@ open class GetAccountDataOptions(
|
||||||
* likes to use a different one, she can select another one in EnterTanDialog.
|
* likes to use a different one, she can select another one in EnterTanDialog.
|
||||||
*
|
*
|
||||||
* By default we prefer non visual TanMethods (like AppTan and SMS) over image based TanMethods (like QR-code and
|
* By default we prefer non visual TanMethods (like AppTan and SMS) over image based TanMethods (like QR-code and
|
||||||
* photoTan) and exclude ChipTanUsb, which is not supported by application, and Flickercode, which is hard to
|
* photoTan) and exclude ChipTanUsb, which is not supported by application, and FlickerCode, which is hard to
|
||||||
* implement and therefore most applications have not implemented.
|
* implement and therefore most applications have not implemented.
|
||||||
*
|
*
|
||||||
* Console apps can only handle non visual TanMethods.
|
* Console apps can only handle non visual TanMethods.
|
||||||
* But also graphical applications prefer non visual TanMethods as then they only have to display a text field to input
|
* But also graphical applications prefer non visual TanMethods as then they only have to display a text field to input
|
||||||
* TAN, and then image based TanMethods as then they additionally only have to display an image.
|
* TAN, and then image based TanMethods as then they additionally only have to display an image.
|
||||||
*/
|
*/
|
||||||
val preferredTanMethods: List<TanMethodType>? = TanMethodType.NonVisualOrImageBased,
|
val preferredTanMethods: List<TanMethodType>? = TanMethodType.TanMethodsPreferredByMostApplications,
|
||||||
|
|
||||||
val tanMethodsNotSupportedByApplication: List<TanMethodType> = TanMethodType.TanMethodsNotSupportedByMostApplications,
|
val tanMethodsNotSupportedByApplication: List<TanMethodType> = TanMethodType.TanMethodsNotSupportedByMostApplications,
|
||||||
|
|
||||||
|
|
|
@ -64,16 +64,19 @@ open class TransferMoneyRequest(
|
||||||
* likes to use a different one, she can select another one in EnterTanDialog.
|
* likes to use a different one, she can select another one in EnterTanDialog.
|
||||||
*
|
*
|
||||||
* By default we prefer non visual TanMethods (like AppTan and SMS) over image based TanMethods (like QR-code and
|
* By default we prefer non visual TanMethods (like AppTan and SMS) over image based TanMethods (like QR-code and
|
||||||
* photoTan) and exclude ChipTanUsb, which is not supported by application, and Flickercode, which is hard to
|
* photoTan) and exclude ChipTanUsb, which is not supported by application, and FlickerCode, which is hard to
|
||||||
* implement and therefore most applications have not implemented.
|
* implement and therefore most applications have not implemented.
|
||||||
*
|
*
|
||||||
* Console apps can only handle non visual TanMethods.
|
* Console apps can only handle non visual TanMethods.
|
||||||
* But also graphical applications prefer non visual TanMethods as then they only have to display a text field to input
|
* But also graphical applications prefer non visual TanMethods as then they only have to display a text field to input
|
||||||
* TAN, and then image based TanMethods as then they additionally only have to display an image.
|
* TAN, and then image based TanMethods as then they additionally only have to display an image.
|
||||||
*/
|
*/
|
||||||
val preferredTanMethods: List<TanMethodType>? = TanMethodType.NonVisualOrImageBased,
|
val preferredTanMethods: List<TanMethodType>? = TanMethodType.TanMethodsPreferredByMostApplications,
|
||||||
|
|
||||||
val tanMethodsNotSupportedByApplication: List<TanMethodType> = TanMethodType.TanMethodsNotSupportedByMostApplications
|
val tanMethodsNotSupportedByApplication: List<TanMethodType> = TanMethodType.TanMethodsNotSupportedByMostApplications,
|
||||||
|
|
||||||
|
val clientData: Any? = null,
|
||||||
|
var serializedClientData: String? = null
|
||||||
) {
|
) {
|
||||||
override fun toString() = "$amount to $recipientName - $paymentReference"
|
override fun toString() = "$amount to $recipientName - $paymentReference"
|
||||||
}
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
package net.codinux.banking.client.model.request
|
package net.codinux.banking.client.model.request
|
||||||
|
|
||||||
import net.codinux.banking.client.model.Amount
|
import net.codinux.banking.client.model.*
|
||||||
import net.codinux.banking.client.model.BankAccountIdentifier
|
|
||||||
import net.codinux.banking.client.model.DefaultValues
|
|
||||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
import net.codinux.banking.client.model.tan.TanMethodType
|
import net.codinux.banking.client.model.tan.TanMethodType
|
||||||
|
|
||||||
|
@ -37,13 +35,36 @@ open class TransferMoneyRequestForUser(
|
||||||
instantTransfer: Boolean = false,
|
instantTransfer: Boolean = false,
|
||||||
|
|
||||||
|
|
||||||
preferredTanMethods: List<TanMethodType>? = TanMethodType.NonVisualOrImageBased,
|
preferredTanMethods: List<TanMethodType>? = TanMethodType.TanMethodsPreferredByMostApplications,
|
||||||
tanMethodsNotSupportedByApplication: List<TanMethodType> = TanMethodType.TanMethodsNotSupportedByMostApplications,
|
tanMethodsNotSupportedByApplication: List<TanMethodType> = TanMethodType.TanMethodsNotSupportedByMostApplications,
|
||||||
) : TransferMoneyRequest(senderAccount, recipientName, recipientAccountIdentifier, recipientBankIdentifier, amount, currency, paymentReference, instantTransfer, preferredTanMethods, tanMethodsNotSupportedByApplication) {
|
|
||||||
|
clientData: Any? = null,
|
||||||
|
serializedClientData: String? = null
|
||||||
|
) : TransferMoneyRequest(senderAccount, recipientName, recipientAccountIdentifier, recipientBankIdentifier, amount, currency, paymentReference, instantTransfer, preferredTanMethods, tanMethodsNotSupportedByApplication, clientData, serializedClientData) {
|
||||||
|
|
||||||
constructor(bankCode: String, loginName: String, password: String, request: TransferMoneyRequest)
|
constructor(bankCode: String, loginName: String, password: String, request: TransferMoneyRequest)
|
||||||
: this(bankCode, loginName, password, request.senderAccount, request.recipientName, request.recipientAccountIdentifier, request.recipientBankIdentifier,
|
: this(bankCode, loginName, password, request.senderAccount, request.recipientName, request.recipientAccountIdentifier, request.recipientBankIdentifier,
|
||||||
request.amount, request.currency, request.paymentReference, request.instantTransfer, request.preferredTanMethods, request.tanMethodsNotSupportedByApplication)
|
request.amount, request.currency, request.paymentReference, request.instantTransfer, request.preferredTanMethods, request.tanMethodsNotSupportedByApplication)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
bank: BankAccess, account: BankAccount?,
|
||||||
|
recipientName: String, recipientAccountIdentifier: String, recipientBankIdentifier: String? = null,
|
||||||
|
amount: Amount, currency: String = DefaultValues.DefaultCurrency, paymentReference: String? = null, instantTransfer: Boolean = false,
|
||||||
|
preferredTanMethods: List<TanMethodType>? = TanMethodType.TanMethodsPreferredByMostApplications
|
||||||
|
) : this(bank.domesticBankCode, bank.loginName, bank.password!!, account?.let { BankAccountIdentifier(it.identifier, it.subAccountNumber, it.iban) },
|
||||||
|
recipientName, recipientAccountIdentifier, recipientBankIdentifier, amount, currency, paymentReference, instantTransfer,
|
||||||
|
listOf(bank.selectedTanMethod.type) + (preferredTanMethods ?: emptyList()), TanMethodType.TanMethodsNotSupportedByMostApplications,
|
||||||
|
bank.clientData, bank.serializedClientData
|
||||||
|
) {
|
||||||
|
this.bank = bank
|
||||||
|
this.account = account
|
||||||
|
}
|
||||||
|
|
||||||
|
open var bank: BankAccess? = null
|
||||||
|
protected set
|
||||||
|
|
||||||
|
open var account: BankAccount? = null
|
||||||
|
protected set
|
||||||
|
|
||||||
override fun toString() = "$bankCode $loginName ${super.toString()}"
|
override fun toString() = "$bankCode $loginName ${super.toString()}"
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package net.codinux.banking.client.model.response
|
||||||
|
|
||||||
import net.codinux.banking.client.model.AccountTransaction
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
import net.codinux.banking.client.model.BankAccess
|
import net.codinux.banking.client.model.BankAccess
|
||||||
|
import net.codinux.banking.client.model.MessageLogEntry
|
||||||
import net.codinux.banking.client.model.config.JsonIgnore
|
import net.codinux.banking.client.model.config.JsonIgnore
|
||||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package net.codinux.banking.client.model.response
|
package net.codinux.banking.client.model.response
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.MessageLogEntry
|
||||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
|
||||||
// TODO: may differentiate between ClientResponse, which is either Success or Error, and RestResponse, which can be Success, Error and TanRequired
|
// TODO: may differentiate between ClientResponse, which is either Success or Error, and RestResponse, which can be Success, Error and TanRequired
|
||||||
|
@ -8,21 +9,22 @@ open class Response<T> protected constructor(
|
||||||
val type: ResponseType,
|
val type: ResponseType,
|
||||||
val data: T? = null,
|
val data: T? = null,
|
||||||
val error: Error? = null,
|
val error: Error? = null,
|
||||||
val tanRequired: TanRequired? = null
|
val tanRequired: TanRequired? = null,
|
||||||
|
val messageLog: List<MessageLogEntry> = emptyList()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun <T> success(data: T): Response<T> =
|
fun <T> success(data: T, messageLog: List<MessageLogEntry> = emptyList()): Response<T> =
|
||||||
Response(ResponseType.Success, data)
|
Response(ResponseType.Success, data, messageLog = messageLog)
|
||||||
|
|
||||||
fun <T> error(errorType: ErrorType, internalError: String? = null, errorMessagesFromBank: List<String> = emptyList()): Response<T> =
|
fun <T> error(errorType: ErrorType, internalError: String? = null, errorMessagesFromBank: List<String> = emptyList(), messageLog: List<MessageLogEntry> = emptyList()): Response<T> =
|
||||||
Response(ResponseType.Error, null, Error(errorType, internalError, errorMessagesFromBank))
|
Response(ResponseType.Error, null, Error(errorType, internalError, errorMessagesFromBank), messageLog = messageLog)
|
||||||
|
|
||||||
fun <T> tanRequired(tanRequired: TanRequired): Response<T> =
|
fun <T> tanRequired(tanRequired: TanRequired, messageLog: List<MessageLogEntry> = emptyList()): Response<T> =
|
||||||
Response(ResponseType.TanRequired, null, null, tanRequired)
|
Response(ResponseType.TanRequired, null, null, tanRequired, messageLog)
|
||||||
|
|
||||||
fun <T> bankReturnedError(errorMessagesFromBank: List<String>): Response<T> =
|
fun <T> bankReturnedError(errorMessagesFromBank: List<String>, messageLog: List<MessageLogEntry> = emptyList()): Response<T> =
|
||||||
Response.error(ErrorType.BankReturnedError, null, errorMessagesFromBank)
|
Response.error(ErrorType.BankReturnedError, null, errorMessagesFromBank, messageLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package net.codinux.banking.client.model.response
|
package net.codinux.banking.client.model.response
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.MessageLogEntry
|
||||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,7 +12,7 @@ open class Holding(
|
||||||
open var isin: String? = null,
|
open var isin: String? = null,
|
||||||
open var wkn: String? = null,
|
open var wkn: String? = null,
|
||||||
|
|
||||||
open var quantity: Int? = null,
|
open var quantity: Double? = null,
|
||||||
open var currency: String? = null,
|
open var currency: String? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,13 +7,13 @@ import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
@NoArgConstructor
|
@NoArgConstructor
|
||||||
open class FlickerCode(
|
open class FlickerCode(
|
||||||
val challengeHHD_UC: String,
|
val challengeHHD_UC: String,
|
||||||
val parsedDataSet: String,
|
val parsedDataSet: String? = null,
|
||||||
val decodingError: String? = null
|
val decodingError: String? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val decodingSuccessful: Boolean
|
val decodingSuccessful: Boolean
|
||||||
get() = decodingError == null
|
get() = parsedDataSet != null
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
|
|
@ -88,7 +88,7 @@ open class TanChallenge(
|
||||||
return "$selectedTanMethod $forAction: $messageToShowToUser" + when (type) {
|
return "$selectedTanMethod $forAction: $messageToShowToUser" + when (type) {
|
||||||
TanChallengeType.EnterTan -> ""
|
TanChallengeType.EnterTan -> ""
|
||||||
TanChallengeType.Image -> ", Image: $tanImage"
|
TanChallengeType.Image -> ", Image: $tanImage"
|
||||||
TanChallengeType.Flickercode -> ", FlickerCode: $flickerCode"
|
TanChallengeType.FlickerCode -> ", FlickerCode: $flickerCode"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ package net.codinux.banking.client.model.tan
|
||||||
enum class TanChallengeType {
|
enum class TanChallengeType {
|
||||||
Image,
|
Image,
|
||||||
|
|
||||||
Flickercode,
|
FlickerCode,
|
||||||
|
|
||||||
EnterTan
|
EnterTan
|
||||||
}
|
}
|
|
@ -6,22 +6,22 @@ import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
@Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED")
|
@Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED")
|
||||||
@NoArgConstructor
|
@NoArgConstructor
|
||||||
open class TanImage(
|
open class TanImage(
|
||||||
val mimeType: String,
|
val mimeType: String? = null,
|
||||||
val imageBytesBase64: String,
|
val imageBytesBase64: String? = null,
|
||||||
val decodingError: String? = null
|
val decodingError: String? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val decodingSuccessful: Boolean
|
val decodingSuccessful: Boolean
|
||||||
get() = decodingError == null
|
get() = mimeType != null && imageBytesBase64 != null
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
if (decodingSuccessful == false) {
|
mimeType?.let {
|
||||||
return "Decoding error: $decodingError"
|
return mimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
return mimeType
|
return "Decoding error: $decodingError"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,9 +3,9 @@ package net.codinux.banking.client.model.tan
|
||||||
enum class TanMethodType {
|
enum class TanMethodType {
|
||||||
EnterTan,
|
EnterTan,
|
||||||
|
|
||||||
ChipTanManuell,
|
ChipTanManual,
|
||||||
|
|
||||||
ChipTanFlickercode,
|
ChipTanFlickerCode,
|
||||||
|
|
||||||
ChipTanUsb,
|
ChipTanUsb,
|
||||||
|
|
||||||
|
@ -33,16 +33,34 @@ enum class TanMethodType {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val NonVisual = listOf(TanMethodType.DecoupledTan, TanMethodType.DecoupledPushTan, TanMethodType.AppTan, TanMethodType.SmsTan, TanMethodType.ChipTanManuell, TanMethodType.EnterTan)
|
val NonVisual = listOf(TanMethodType.DecoupledTan, TanMethodType.DecoupledPushTan, TanMethodType.AppTan, TanMethodType.SmsTan, TanMethodType.ChipTanManual, TanMethodType.EnterTan)
|
||||||
|
|
||||||
val ImageBased = listOf(TanMethodType.QrCode, TanMethodType.ChipTanQrCode, TanMethodType.photoTan, TanMethodType.ChipTanPhotoTanMatrixCode)
|
val NonVisualWithoutChipTanManual = NonVisual.toMutableList().apply { remove(TanMethodType.ChipTanManual) }.toList()
|
||||||
|
|
||||||
|
val ImageBased = listOf(
|
||||||
|
TanMethodType.QrCode, TanMethodType.photoTan, // non ChipTan
|
||||||
|
TanMethodType.ChipTanQrCode, TanMethodType.ChipTanPhotoTanMatrixCode // ChipTan; QrCode (e.g. used by Sparkassen) is faster than MatrixCode (e.g. used by Volksbanken)
|
||||||
|
)
|
||||||
|
|
||||||
val NonVisualOrImageBased = buildList {
|
val NonVisualOrImageBased = buildList {
|
||||||
addAll(listOf(TanMethodType.DecoupledTan, TanMethodType.DecoupledPushTan, TanMethodType.AppTan, TanMethodType.SmsTan, TanMethodType.EnterTan))
|
addAll(NonVisualWithoutChipTanManual)
|
||||||
addAll(ImageBased)
|
addAll(ImageBased)
|
||||||
addAll(listOf(TanMethodType.ChipTanManuell)) // this is quite inconvenient for user, so i added it as last
|
addAll(listOf(TanMethodType.ChipTanManual)) // this is quite inconvenient for user, so i added it as last
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The same as [NonVisualOrImageBased] but including [ChipTanFlickerCode] - for applications supporting it - as
|
||||||
|
* FlickerCode is still the most used ChipTan procedure.
|
||||||
|
*/
|
||||||
|
val NonVisualOrImageBasedOrFlickerCode = NonVisualOrImageBased.toMutableList().apply {
|
||||||
|
val index = this.indexOf(ChipTanQrCode)
|
||||||
|
this.add(index, ChipTanFlickerCode)
|
||||||
|
}.toList()
|
||||||
|
|
||||||
|
|
||||||
|
val TanMethodsPreferredByMostApplications = NonVisualOrImageBased
|
||||||
|
|
||||||
|
|
||||||
val TanMethodsNotSupportedByMostApplications = listOf(TanMethodType.ChipTanUsb)
|
val TanMethodsNotSupportedByMostApplications = listOf(TanMethodType.ChipTanUsb)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform")
|
kotlin("multiplatform")
|
||||||
|
|
||||||
id("maven-publish")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +32,7 @@ kotlin {
|
||||||
browser {
|
browser {
|
||||||
testTask {
|
testTask {
|
||||||
useKarma {
|
useKarma {
|
||||||
useChromeHeadless()
|
// useChromeHeadless()
|
||||||
useFirefoxHeadless()
|
useFirefoxHeadless()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +75,7 @@ kotlin {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":BankingClient"))
|
api(project(":BankingClient"))
|
||||||
|
|
||||||
implementation("net.codinux.banking:fints4k:1.0.0-Alpha-14")
|
implementation("net.codinux.banking:fints4k:1.0.0-Alpha-15")
|
||||||
|
|
||||||
api("org.jetbrains.kotlinx:kotlinx-datetime:$kotlinxDateTimeVersion")
|
api("org.jetbrains.kotlinx:kotlinx-datetime:$kotlinxDateTimeVersion")
|
||||||
}
|
}
|
||||||
|
@ -115,21 +113,6 @@ kotlin {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//ext["customArtifactId"] = "fints4k-banking-client"
|
ext["customArtifactId"] = "fints4k-banking-client"
|
||||||
//
|
|
||||||
//apply(from = "../gradle/scripts/publish-codinux.gradle.kts")
|
|
||||||
|
|
||||||
|
apply(from = "../gradle/scripts/publish-codinux-repo.gradle.kts")
|
||||||
publishing {
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
name = "codinux"
|
|
||||||
url = uri("https://maven.dankito.net/api/packages/codinux/maven")
|
|
||||||
|
|
||||||
credentials(PasswordCredentials::class.java) {
|
|
||||||
username = project.property("codinuxRegistryWriterUsername") as String
|
|
||||||
password = project.property("codinuxRegistryWriterPassword") as String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ package net.codinux.banking.client.fints4k
|
||||||
import net.codinux.banking.client.BankingClientCallback
|
import net.codinux.banking.client.BankingClientCallback
|
||||||
import net.codinux.banking.client.model.MessageLogEntryType
|
import net.codinux.banking.client.model.MessageLogEntryType
|
||||||
import net.codinux.banking.fints.callback.FinTsClientCallback
|
import net.codinux.banking.fints.callback.FinTsClientCallback
|
||||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
||||||
import net.codinux.banking.fints.model.BankData
|
import net.codinux.banking.fints.model.BankData
|
||||||
import net.codinux.banking.fints.model.EnterTanGeneratorAtcResult
|
import net.codinux.banking.fints.model.EnterTanGeneratorAtcResult
|
||||||
import net.codinux.banking.fints.model.MessageLogEntry
|
import net.codinux.banking.fints.model.MessageLogEntry
|
||||||
|
@ -23,23 +23,20 @@ open class BridgeFintTsToBankingClientCallback(
|
||||||
if (enterTanResult.enteredTan != null) {
|
if (enterTanResult.enteredTan != null) {
|
||||||
tanChallenge.userEnteredTan(enterTanResult.enteredTan!!)
|
tanChallenge.userEnteredTan(enterTanResult.enteredTan!!)
|
||||||
} else if (enterTanResult.changeTanMethodTo != null) {
|
} else if (enterTanResult.changeTanMethodTo != null) {
|
||||||
tanChallenge.userAsksToChangeTanMethod(mapper.mapTanMethod(enterTanResult.changeTanMethodTo!!))
|
val fintsTanMethod = tanChallenge.bank.tanMethodsAvailableForUser.first { it.securityFunction.code == enterTanResult.changeTanMethodTo!!.identifier }
|
||||||
|
tanChallenge.userAsksToChangeTanMethod(fintsTanMethod)
|
||||||
} else {
|
} else {
|
||||||
tanChallenge.userDidNotEnterTan()
|
tanChallenge.userDidNotEnterTan()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanGeneratorTanMedium): EnterTanGeneratorAtcResult {
|
override suspend fun enterTanGeneratorAtc(bank: BankData, tanMedium: TanMedium): EnterTanGeneratorAtcResult {
|
||||||
return EnterTanGeneratorAtcResult.userDidNotEnterAtc()
|
return EnterTanGeneratorAtcResult.userDidNotEnterAtc()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun messageLogAdded(messageLogEntry: MessageLogEntry) {
|
override fun messageLogAdded(messageLogEntry: MessageLogEntry) {
|
||||||
val mapped = net.codinux.banking.client.model.MessageLogEntry(
|
val mapped = mapper.mapMessageLogEntry(messageLogEntry)
|
||||||
MessageLogEntryType.valueOf(messageLogEntry.type.name),
|
|
||||||
messageLogEntry.message, messageLogEntry.messageTrace,
|
|
||||||
messageLogEntry.error, messageLogEntry.time
|
|
||||||
)
|
|
||||||
|
|
||||||
bankingClientCallback.messageLogAdded(mapped)
|
bankingClientCallback.messageLogAdded(mapped)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
||||||
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
||||||
import net.codinux.banking.client.model.response.*
|
import net.codinux.banking.client.model.response.*
|
||||||
|
import net.codinux.banking.client.model.tan.TanMethodType
|
||||||
import net.codinux.banking.fints.FinTsClient
|
import net.codinux.banking.fints.FinTsClient
|
||||||
import net.codinux.banking.fints.config.FinTsClientConfiguration
|
import net.codinux.banking.fints.config.FinTsClientConfiguration
|
||||||
import net.codinux.banking.fints.model.BankData
|
|
||||||
|
|
||||||
open class FinTs4kBankingClient(
|
open class FinTs4kBankingClient(
|
||||||
config: FinTsClientConfiguration = FinTsClientConfiguration(),
|
config: FinTsClientConfiguration = FinTsClientConfiguration(),
|
||||||
|
@ -35,35 +35,33 @@ open class FinTs4kBankingClient(
|
||||||
return mapper.map(response, request.bankInfo)
|
return mapper.map(response, request.bankInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun updateAccountTransactionsAsync(bank: BankAccess, accounts: List<BankAccount>?): Response<List<GetTransactionsResponse>> {
|
override suspend fun updateAccountTransactionsAsync(bank: BankAccess, accounts: List<BankAccount>?, preferredTanMethodsIfSelectedTanMethodIsNotAvailable: List<TanMethodType>?): Response<List<GetTransactionsResponse>> {
|
||||||
val accountsToRequest = (accounts ?: bank.accounts).filter { it.supportsAnyFeature(BankAccountFeatures.RetrieveBalance, BankAccountFeatures.RetrieveBalance) }
|
val accountsToRequest = (accounts ?: bank.accounts).filter { it.supportsAnyFeature(BankAccountFeatures.RetrieveBalance, BankAccountFeatures.RetrieveTransactions) }
|
||||||
|
|
||||||
if (accountsToRequest.isNotEmpty()) {
|
if (accountsToRequest.isNotEmpty()) {
|
||||||
var finTsModel: BankData? = null
|
|
||||||
|
|
||||||
val responses = accountsToRequest.map { account ->
|
val responses = accountsToRequest.map { account ->
|
||||||
val parameter = mapper.mapToUpdateAccountTransactionsParameter(bank, account, finTsModel)
|
val preferredTanMethods = listOf(bank.selectedTanMethod.type) + (preferredTanMethodsIfSelectedTanMethodIsNotAvailable ?: emptyList())
|
||||||
|
|
||||||
|
val parameter = mapper.mapToUpdateAccountTransactionsParameter(bank, account, preferredTanMethods)
|
||||||
|
|
||||||
val response = client.getAccountDataAsync(parameter)
|
val response = client.getAccountDataAsync(parameter)
|
||||||
|
|
||||||
if (response.finTsModel != null) {
|
mapper.mapCommonResponseData(bank, response) // so that basic account data doesn't have to be retrieved another time if user has multiple accounts
|
||||||
finTsModel = response.finTsModel // so that basic account data doesn't have to be retrieved another time if user has multiple accounts
|
|
||||||
}
|
|
||||||
|
|
||||||
Triple(account, parameter, response)
|
Triple(account, parameter, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapper.map(responses)
|
return mapper.map(bank, responses)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.error(ErrorType.NoneOfTheAccountsSupportsRetrievingData, "Keiner der Konten unterstützt das Abholen der Umsätze oder des Kontostands") // TODO: translate
|
return Response.error(ErrorType.NoneOfTheAccountsSupportsRetrievingData, "Keines der Konten unterstützt das Abholen der Umsätze oder des Kontostands") // TODO: translate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override suspend fun transferMoneyAsync(request: TransferMoneyRequestForUser): Response<TransferMoneyResponse> {
|
override suspend fun transferMoneyAsync(request: TransferMoneyRequestForUser): Response<TransferMoneyResponse> {
|
||||||
val response = client.transferMoneyAsync(mapper.mapToTransferMoneyParameter(request))
|
val response = client.transferMoneyAsync(mapper.mapToTransferMoneyParameter(request))
|
||||||
|
|
||||||
return mapper.mapTransferMoneyResponse(response)
|
return mapper.mapTransferMoneyResponse(response, request.bank, request.account)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,6 +7,8 @@ import kotlinx.datetime.toLocalDateTime
|
||||||
import net.codinux.banking.client.model.*
|
import net.codinux.banking.client.model.*
|
||||||
import net.codinux.banking.client.model.AccountTransaction
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
import net.codinux.banking.client.model.Amount
|
import net.codinux.banking.client.model.Amount
|
||||||
|
import net.codinux.banking.client.model.MessageLogEntry
|
||||||
|
import net.codinux.banking.client.model.MessageLogEntryType
|
||||||
import net.codinux.banking.client.model.extensions.EuropeBerlin
|
import net.codinux.banking.client.model.extensions.EuropeBerlin
|
||||||
import net.codinux.banking.client.model.tan.*
|
import net.codinux.banking.client.model.tan.*
|
||||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
|
@ -14,6 +16,7 @@ import net.codinux.banking.client.model.request.GetAccountDataRequest
|
||||||
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
||||||
import net.codinux.banking.client.model.response.*
|
import net.codinux.banking.client.model.response.*
|
||||||
import net.codinux.banking.client.model.tan.ActionRequiringTan
|
import net.codinux.banking.client.model.tan.ActionRequiringTan
|
||||||
|
import net.codinux.banking.client.model.tan.AllowedTanFormat
|
||||||
import net.codinux.banking.client.model.tan.TanChallenge
|
import net.codinux.banking.client.model.tan.TanChallenge
|
||||||
import net.codinux.banking.client.model.tan.TanImage
|
import net.codinux.banking.client.model.tan.TanImage
|
||||||
import net.codinux.banking.client.model.tan.TanMethod
|
import net.codinux.banking.client.model.tan.TanMethod
|
||||||
|
@ -22,14 +25,15 @@ import net.dankito.banking.client.model.BankAccountIdentifierImpl
|
||||||
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
||||||
import net.dankito.banking.client.model.parameter.RetrieveTransactions
|
import net.dankito.banking.client.model.parameter.RetrieveTransactions
|
||||||
import net.dankito.banking.client.model.response.ErrorCode
|
import net.dankito.banking.client.model.response.ErrorCode
|
||||||
|
import net.dankito.banking.client.model.response.FinTsClientResponse
|
||||||
import net.codinux.banking.fints.mapper.FinTsModelMapper
|
import net.codinux.banking.fints.mapper.FinTsModelMapper
|
||||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.*
|
||||||
import net.codinux.banking.fints.model.*
|
|
||||||
import net.codinux.banking.fints.transactions.swift.model.Holding
|
|
||||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
|
||||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.MobilePhoneTanMedium
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.MobilePhoneTanMedium
|
||||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
|
||||||
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
||||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus
|
||||||
|
import net.codinux.banking.fints.model.*
|
||||||
|
import net.codinux.banking.fints.transactions.swift.model.Holding
|
||||||
import net.dankito.banking.banklistcreator.prettifier.BankingGroupMapper
|
import net.dankito.banking.banklistcreator.prettifier.BankingGroupMapper
|
||||||
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
||||||
import kotlin.io.encoding.Base64
|
import kotlin.io.encoding.Base64
|
||||||
|
@ -58,30 +62,25 @@ open class FinTs4kMapper {
|
||||||
bank.serverAddress, bank.bic, bank.name
|
bank.serverAddress, bank.bic, bank.name
|
||||||
)
|
)
|
||||||
|
|
||||||
open fun mapToUpdateAccountTransactionsParameter(bank: BankAccess, account: BankAccount, finTsModel: BankData?): GetAccountDataParameter {
|
open fun mapToUpdateAccountTransactionsParameter(bank: BankAccess, account: BankAccount, preferredTanMethods: List<TanMethodType>? = null): GetAccountDataParameter {
|
||||||
val defaults = GetAccountDataOptions()
|
val defaults = GetAccountDataOptions()
|
||||||
|
|
||||||
val accountIdentifier = BankAccountIdentifierImpl(account.identifier, account.subAccountNumber, account.iban)
|
val accountIdentifier = BankAccountIdentifierImpl(account.identifier, account.subAccountNumber, account.iban)
|
||||||
val from = account.lastAccountUpdateTime?.toLocalDateTime(TimeZone.EuropeBerlin)?.date // TODO: in case lastTransactionsUpdateTime is not set, this would retrieve all transactions (and require a TAN im most cases)
|
val from = account.lastAccountUpdateTime?.toLocalDateTime(TimeZone.EuropeBerlin)?.date // TODO: in case lastTransactionsUpdateTime is not set, this would retrieve all transactions (and require a TAN im most cases)
|
||||||
val retrieveTransactions = if (from != null) RetrieveTransactions.AccordingToRetrieveFromAndTo else RetrieveTransactions.valueOf(defaults.retrieveTransactions.name)
|
val retrieveTransactions = if (from != null) RetrieveTransactions.AccordingToRetrieveFromAndTo else RetrieveTransactions.valueOf(defaults.retrieveTransactions.name)
|
||||||
// val preferredTanMethods = listOf(mapTanMethodType(bank.selectedTanMethod.type)) // TODO: currently we aren't saving TanMethods in database, re-enable as soon as TanMethods get saved
|
|
||||||
val preferredTanMethods = defaults.preferredTanMethods?.map { mapTanMethodType(it) }
|
|
||||||
|
|
||||||
return GetAccountDataParameter(bank.domesticBankCode, bank.loginName, bank.password!!, listOf(accountIdentifier), true,
|
return GetAccountDataParameter(bank.domesticBankCode, bank.loginName, bank.password!!, listOf(accountIdentifier), true,
|
||||||
retrieveTransactions, from,
|
retrieveTransactions, from,
|
||||||
preferredTanMethods = preferredTanMethods,
|
preferredTanMethods = preferredTanMethods?.map { mapTanMethodType(it) },
|
||||||
preferredTanMedium = bank.selectedTanMediumIdentifier,
|
preferredTanMedium = bank.selectedTanMediumIdentifier,
|
||||||
finTsModel = finTsModel
|
finTsModel = bank.clientData as? BankData,
|
||||||
|
serializedFinTsModel = bank.serializedClientData
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun mapBankAccountIdentifier(account: BankAccountIdentifier): BankAccountIdentifierImpl =
|
open fun mapBankAccountIdentifier(account: BankAccountIdentifier): BankAccountIdentifierImpl =
|
||||||
BankAccountIdentifierImpl(account.identifier, account.subAccountNumber, account.iban)
|
BankAccountIdentifierImpl(account.identifier, account.subAccountNumber, account.iban)
|
||||||
|
|
||||||
open fun mapTanMethod(method: TanMethod) = net.codinux.banking.fints.model.TanMethod( // TODO: get instance from FinTsData, don't create manually
|
|
||||||
method.displayName, Sicherheitsfunktion.entries.first { it.code == method.identifier }, mapTanMethodType(method.type), null, method.maxTanInputLength, mapAllowedTanFormat(method.allowedTanFormat)
|
|
||||||
)
|
|
||||||
|
|
||||||
protected open fun mapTanMethodType(type: TanMethodType): net.codinux.banking.fints.model.TanMethodType =
|
protected open fun mapTanMethodType(type: TanMethodType): net.codinux.banking.fints.model.TanMethodType =
|
||||||
net.codinux.banking.fints.model.TanMethodType.valueOf(type.name)
|
net.codinux.banking.fints.model.TanMethodType.valueOf(type.name)
|
||||||
|
|
||||||
|
@ -89,41 +88,50 @@ open class FinTs4kMapper {
|
||||||
allowedTanFormat?.let { net.codinux.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.valueOf(it.name) } ?: net.codinux.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Alphanumeric
|
allowedTanFormat?.let { net.codinux.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.valueOf(it.name) } ?: net.codinux.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Alphanumeric
|
||||||
|
|
||||||
|
|
||||||
open fun map(response: net.dankito.banking.client.model.response.GetAccountDataResponse, bank: BankInfo? = null): Response<GetAccountDataResponse> =
|
open fun map(response: net.dankito.banking.client.model.response.GetAccountDataResponse, bankInfo: BankInfo? = null): Response<GetAccountDataResponse> =
|
||||||
if (response.successful && response.customerAccount != null) {
|
if (response.successful && response.customerAccount != null) {
|
||||||
Response.success(GetAccountDataResponse(mapBank(response.customerAccount!!, bank)))
|
val bank = mapBank(response.customerAccount!!, bankInfo, response)
|
||||||
|
Response.success(GetAccountDataResponse(bank), mapMessageLog(response, bank))
|
||||||
} else {
|
} else {
|
||||||
mapError(response)
|
mapError(response, mapMessageLog(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun map(responses: List<Triple<BankAccount, GetAccountDataParameter, net.dankito.banking.client.model.response.GetAccountDataResponse>>): Response<List<GetTransactionsResponse>> {
|
open fun map(bank: BankAccess, responses: List<Triple<BankAccount, GetAccountDataParameter, net.dankito.banking.client.model.response.GetAccountDataResponse>>): Response<List<GetTransactionsResponse>> {
|
||||||
val type = if (responses.all { it.third.successful }) ResponseType.Success else ResponseType.Error
|
val type = if (responses.all { it.third.successful }) ResponseType.Success else ResponseType.Error
|
||||||
|
|
||||||
// TODO: update BankAccess and BankAccount objects according to retrieved data
|
// TODO: update BankAccess and BankAccount objects according to retrieved data
|
||||||
val mappedResponses = responses.map { (account, param, getAccountDataResponse) ->
|
val mappedResponses = responses.map { (account, param, getAccountDataResponse) ->
|
||||||
val bank = getAccountDataResponse.customerAccount
|
val fintsBank = getAccountDataResponse.customerAccount
|
||||||
val finTsBankAccount = bank?.accounts?.firstOrNull { it.identifier == account.identifier && it.subAccountNumber == account.subAccountNumber }
|
val finTsBankAccount = fintsBank?.accounts?.firstOrNull { it.identifier == account.identifier && it.subAccountNumber == account.subAccountNumber }
|
||||||
|
|
||||||
if (getAccountDataResponse.successful && bank != null && finTsBankAccount != null) {
|
val messageLog = mapMessageLog(getAccountDataResponse, bank, account)
|
||||||
|
|
||||||
|
if (getAccountDataResponse.successful && fintsBank != null && finTsBankAccount != null) {
|
||||||
if (finTsBankAccount.lastAccountUpdateTime != null) {
|
if (finTsBankAccount.lastAccountUpdateTime != null) {
|
||||||
account.lastAccountUpdateTime = finTsBankAccount.lastAccountUpdateTime
|
account.lastAccountUpdateTime = finTsBankAccount.lastAccountUpdateTime
|
||||||
}
|
}
|
||||||
if (account.retrievedTransactionsFrom == null || (finTsBankAccount.retrievedTransactionsFrom != null
|
if (account.retrievedTransactionsFrom == null || (finTsBankAccount.retrievedTransactionsFrom != null
|
||||||
&& account.retrievedTransactionsFrom!! < finTsBankAccount.retrievedTransactionsFrom!!)) {
|
&& finTsBankAccount.retrievedTransactionsFrom!! < account.retrievedTransactionsFrom!!)) {
|
||||||
account.retrievedTransactionsFrom = finTsBankAccount.retrievedTransactionsFrom
|
account.retrievedTransactionsFrom = finTsBankAccount.retrievedTransactionsFrom
|
||||||
}
|
}
|
||||||
|
|
||||||
Response.success(GetTransactionsResponse(account, mapMoney(finTsBankAccount.balance), mapBookedTransactions(finTsBankAccount), emptyList(),
|
val balance = mapMoney(finTsBankAccount.balance)
|
||||||
|
account.balance = balance
|
||||||
|
|
||||||
|
mapCommonResponseData(bank, getAccountDataResponse)
|
||||||
|
|
||||||
|
Response.success(GetTransactionsResponse(account, balance, mapBookedTransactions(finTsBankAccount), emptyList(),
|
||||||
mapHoldings(finTsBankAccount.statementOfHoldings, finTsBankAccount.currency, finTsBankAccount.lastAccountUpdateTime),
|
mapHoldings(finTsBankAccount.statementOfHoldings, finTsBankAccount.currency, finTsBankAccount.lastAccountUpdateTime),
|
||||||
finTsBankAccount.lastAccountUpdateTime ?: Clock.System.now(), param.retrieveTransactionsFrom, param.retrieveTransactionsTo))
|
finTsBankAccount.lastAccountUpdateTime ?: Clock.System.now(), param.retrieveTransactionsFrom, param.retrieveTransactionsTo),
|
||||||
|
messageLog)
|
||||||
} else {
|
} else {
|
||||||
mapError(getAccountDataResponse)
|
mapError(getAccountDataResponse, messageLog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val data = mappedResponses.filter { it.type == ResponseType.Success }.mapNotNull { it.data }
|
val data = mappedResponses.filter { it.type == ResponseType.Success }.mapNotNull { it.data }
|
||||||
|
|
||||||
return (object : Response<List<GetTransactionsResponse>>(type, data, mappedResponses.firstNotNullOfOrNull { it.error }) { })
|
return (object : Response<List<GetTransactionsResponse>>(type, data, mappedResponses.firstNotNullOfOrNull { it.error }, messageLog = mappedResponses.flatMap { it.messageLog }) { })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,7 +146,7 @@ open class FinTs4kMapper {
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
protected open fun mapBank(bank: net.dankito.banking.client.model.CustomerAccount, info: BankInfo? = null) = BankAccess(
|
protected open fun mapBank(bank: net.dankito.banking.client.model.CustomerAccount, info: BankInfo? = null, response: FinTsClientResponse? = null) = BankAccess(
|
||||||
bank.bankCode, bank.loginName, bank.password,
|
bank.bankCode, bank.loginName, bank.password,
|
||||||
info?.name ?: bank.bankName, bank.bic, bank.customerName, bank.userId,
|
info?.name ?: bank.bankName, bank.bic, bank.customerName, bank.userId,
|
||||||
bank.accounts.map { mapAccount(it) }.sortedBy { it.type }
|
bank.accounts.map { mapAccount(it) }.sortedBy { it.type }
|
||||||
|
@ -150,7 +158,9 @@ open class FinTs4kMapper {
|
||||||
info?.bankingGroup ?: getBankingGroup(bank.bankName, bank.bic),
|
info?.bankingGroup ?: getBankingGroup(bank.bankName, bank.bic),
|
||||||
bank.finTsServerAddress,
|
bank.finTsServerAddress,
|
||||||
"de"
|
"de"
|
||||||
)
|
).apply {
|
||||||
|
response?.let { mapCommonResponseData(this, it) }
|
||||||
|
}
|
||||||
|
|
||||||
protected open fun getBankingGroup(bankName: String, bic: String): BankingGroup? =
|
protected open fun getBankingGroup(bankName: String, bic: String): BankingGroup? =
|
||||||
bankingGroupMapper.getBankingGroup(bankName, bic)
|
bankingGroupMapper.getBankingGroup(bankName, bic)
|
||||||
|
@ -317,7 +327,7 @@ open class FinTs4kMapper {
|
||||||
|
|
||||||
protected open fun mapTanChallengeType(challenge: net.codinux.banking.fints.model.TanChallenge): TanChallengeType = when {
|
protected open fun mapTanChallengeType(challenge: net.codinux.banking.fints.model.TanChallenge): TanChallengeType = when {
|
||||||
challenge is ImageTanChallenge -> TanChallengeType.Image
|
challenge is ImageTanChallenge -> TanChallengeType.Image
|
||||||
challenge is FlickerCodeTanChallenge -> TanChallengeType.Flickercode
|
challenge is FlickerCodeTanChallenge -> TanChallengeType.FlickerCode
|
||||||
else -> TanChallengeType.EnterTan
|
else -> TanChallengeType.EnterTan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,14 +348,18 @@ open class FinTs4kMapper {
|
||||||
TanImage(image.mimeType, mapToBase64(image.imageBytes), mapException(image.decodingError))
|
TanImage(image.mimeType, mapToBase64(image.imageBytes), mapException(image.decodingError))
|
||||||
|
|
||||||
@OptIn(ExperimentalEncodingApi::class)
|
@OptIn(ExperimentalEncodingApi::class)
|
||||||
protected open fun mapToBase64(bytes: ByteArray): String {
|
protected open fun mapToBase64(bytes: ByteArray?): String? {
|
||||||
|
if (bytes == null || bytes.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return Base64.Default.encode(bytes)
|
return Base64.Default.encode(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun mapTanMedium(tanMedium: TanMedium) = net.codinux.banking.client.model.tan.TanMedium(
|
protected open fun mapTanMedium(tanMedium: TanMedium) = net.codinux.banking.client.model.tan.TanMedium(
|
||||||
mapTanMediumType(tanMedium), tanMedium.mediumName, mapTanMediumStatus(tanMedium.status),
|
mapTanMediumType(tanMedium), tanMedium.mediumName, mapTanMediumStatus(tanMedium.status),
|
||||||
(tanMedium as? TanGeneratorTanMedium)?.let { mapTanGeneratorTanMedium(it) },
|
tanMedium.tanGenerator?.let { mapTanGeneratorTanMedium(it) },
|
||||||
(tanMedium as? MobilePhoneTanMedium)?.let { mapMobilePhoneTanMedium(it) }
|
tanMedium.mobilePhone?.let { mapMobilePhoneTanMedium(it) }
|
||||||
)
|
)
|
||||||
|
|
||||||
protected open fun mapTanMediumStatus(status: TanMediumStatus): net.codinux.banking.client.model.tan.TanMediumStatus = when (status) {
|
protected open fun mapTanMediumStatus(status: TanMediumStatus): net.codinux.banking.client.model.tan.TanMediumStatus = when (status) {
|
||||||
|
@ -364,9 +378,9 @@ open class FinTs4kMapper {
|
||||||
tanMedium.validFrom, tanMedium.validTo
|
tanMedium.validFrom, tanMedium.validTo
|
||||||
)
|
)
|
||||||
|
|
||||||
protected open fun mapTanMediumType(tanMedium: TanMedium): TanMediumType = when {
|
protected open fun mapTanMediumType(tanMedium: TanMedium): TanMediumType = when (tanMedium.mediumClass) {
|
||||||
tanMedium is MobilePhoneTanMedium -> TanMediumType.MobilePhone
|
TanMediumKlasse.MobiltelefonMitMobileTan -> TanMediumType.MobilePhone
|
||||||
tanMedium is TanGeneratorTanMedium -> TanMediumType.TanGenerator
|
TanMediumKlasse.TanGenerator -> TanMediumType.TanGenerator
|
||||||
else -> TanMediumType.Generic
|
else -> TanMediumType.Generic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,29 +395,74 @@ open class FinTs4kMapper {
|
||||||
request.recipientName, request.recipientAccountIdentifier, request.recipientBankIdentifier,
|
request.recipientName, request.recipientAccountIdentifier, request.recipientBankIdentifier,
|
||||||
mapToMoney(request.amount, request.currency), request.paymentReference, request.instantTransfer,
|
mapToMoney(request.amount, request.currency), request.paymentReference, request.instantTransfer,
|
||||||
request.preferredTanMethods?.map { mapTanMethodType(it) },
|
request.preferredTanMethods?.map { mapTanMethodType(it) },
|
||||||
request.tanMethodsNotSupportedByApplication.map { mapTanMethodType(it) }
|
request.tanMethodsNotSupportedByApplication.map { mapTanMethodType(it) },
|
||||||
|
finTsModel = request.clientData as? BankData,
|
||||||
|
serializedFinTsModel = request.serializedClientData
|
||||||
)
|
)
|
||||||
|
|
||||||
open fun mapTransferMoneyResponse(response: net.dankito.banking.client.model.response.TransferMoneyResponse): Response<TransferMoneyResponse> =
|
open fun mapTransferMoneyResponse(response: net.dankito.banking.client.model.response.TransferMoneyResponse, bank: BankAccess? = null, account: BankAccount? = null): Response<TransferMoneyResponse> =
|
||||||
if (response.successful) {
|
if (response.successful) {
|
||||||
Response.success(TransferMoneyResponse())
|
bank?.let { mapCommonResponseData(it, response) }
|
||||||
|
|
||||||
|
Response.success(TransferMoneyResponse(), mapMessageLog(response, bank, account))
|
||||||
} else {
|
} else {
|
||||||
mapError(response)
|
mapError(response, mapMessageLog(response, bank, account))
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun mapToMoney(amount: Amount, currency: String): Money = Money(amount.toString(), currency)
|
open fun mapToMoney(amount: Amount, currency: String): Money = Money(amount.toString(), currency)
|
||||||
|
|
||||||
|
|
||||||
protected open fun <T> mapError(response: net.dankito.banking.client.model.response.FinTsClientResponse): Response<T> {
|
open fun mapMessageLog(response: FinTsClientResponse, bank: BankAccess? = null, account: BankAccount? = null) =
|
||||||
return if (response.error != null) {
|
mapMessageLog(response.messageLog, bank, account)
|
||||||
Response.error(ErrorType.valueOf(response.error!!.name), if (response.error == ErrorCode.BankReturnedError) null else response.errorMessage,
|
|
||||||
if (response.error == ErrorCode.BankReturnedError && response.errorMessage !== null) listOf(response.errorMessage!!) else emptyList())
|
open fun mapMessageLog(messageLog: List<net.codinux.banking.fints.model.MessageLogEntry>, bank: BankAccess? = null, account: BankAccount? = null) =
|
||||||
} else {
|
messageLog.map { mapMessageLogEntry(it, bank, account) }
|
||||||
Response.error(ErrorType.UnknownError, response.errorMessage)
|
|
||||||
}
|
open fun mapMessageLogEntry(messageLogEntry: net.codinux.banking.fints.model.MessageLogEntry, bank: BankAccess? = null, account: BankAccount? = null): MessageLogEntry {
|
||||||
|
// TODO: may map messageLogEntry.context.BankData to BankAccess
|
||||||
|
val context = messageLogEntry.context
|
||||||
|
val fintsAccount = context.account
|
||||||
|
val effectiveAccount = account ?: bank?.accounts?.firstOrNull { it.identifier == fintsAccount?.accountIdentifier && it.subAccountNumber == fintsAccount.subAccountAttribute }
|
||||||
|
|
||||||
|
val messageNumberString = "${context.jobNumber.toString().padStart(2, '0')}_${context.dialogNumber.toString().padStart(2, '0')}_${context.messageNumber.toString().padStart(2, '0')}"
|
||||||
|
|
||||||
|
return MessageLogEntry(
|
||||||
|
MessageLogEntryType.valueOf(messageLogEntry.type.name),
|
||||||
|
messageLogEntry.message, messageLogEntry.messageWithoutSensitiveData,
|
||||||
|
messageLogEntry.error, messageLogEntry.time,
|
||||||
|
|
||||||
|
messageNumberString,
|
||||||
|
messageNumberString.replace("_", "").toIntOrNull(),
|
||||||
|
|
||||||
|
context.jobType.toString(),
|
||||||
|
context.messageType.toString(),
|
||||||
|
|
||||||
|
bank,
|
||||||
|
effectiveAccount
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected open fun <T> mapError(response: FinTsClientResponse, messageLog: List<MessageLogEntry>): Response<T> =
|
||||||
|
if (response.error != null) {
|
||||||
|
Response.error(ErrorType.valueOf(response.error!!.name), if (response.error == ErrorCode.BankReturnedError) null else response.errorMessage,
|
||||||
|
if (response.error == ErrorCode.BankReturnedError && response.errorMessage !== null) listOf(response.errorMessage!!) else emptyList(), messageLog)
|
||||||
|
} else {
|
||||||
|
Response.error(ErrorType.UnknownError, response.errorMessage, messageLog = messageLog)
|
||||||
|
}
|
||||||
|
|
||||||
protected open fun mapException(exception: Exception?): String? =
|
protected open fun mapException(exception: Exception?): String? =
|
||||||
exception?.stackTraceToString()
|
exception?.stackTraceToString()
|
||||||
|
|
||||||
|
|
||||||
|
open fun mapCommonResponseData(bank: BankAccess, response: FinTsClientResponse) {
|
||||||
|
response.finTsModel?.let {
|
||||||
|
bank.clientData = it
|
||||||
|
}
|
||||||
|
|
||||||
|
response.serializedFinTsModel?.let {
|
||||||
|
bank.serializedClientData = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ package net.codinux.banking.client.fints4k
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import net.codinux.banking.client.SimpleBankingClientCallback
|
import net.codinux.banking.client.SimpleBankingClientCallback
|
||||||
import net.codinux.banking.client.model.response.ResponseType
|
import net.codinux.banking.client.model.response.ResponseType
|
||||||
|
import net.codinux.banking.client.model.tan.EnterTanResult
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
|
@ -18,7 +19,10 @@ class FinTs4kBankingClientTest {
|
||||||
|
|
||||||
|
|
||||||
private val underTest = FinTs4kBankingClientForUser(bankCode, loginName, password, SimpleBankingClientCallback { tanChallenge, callback ->
|
private val underTest = FinTs4kBankingClientForUser(bankCode, loginName, password, SimpleBankingClientCallback { tanChallenge, callback ->
|
||||||
|
println("WARN: TAN is required to execute test: ${tanChallenge.messageToShowToUser}")
|
||||||
|
|
||||||
|
val enteredTan: String? = null
|
||||||
|
callback(EnterTanResult(enteredTan))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ class FinTs4kMapperTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getTotalBalance_TotalBalanceIsNull_CalculateByQuantityAndMarketValue() {
|
fun getTotalBalance_TotalBalanceIsNull_CalculateByQuantityAndMarketValue() {
|
||||||
val holding = Holding("", null, null, null, 4, null, null, null, fints4kAmount("13.33"))
|
val holding = Holding("", null, null, null, 4.0, null, null, null, fints4kAmount("13.33"))
|
||||||
|
|
||||||
val result = underTest.getTotalBalance(holding)
|
val result = underTest.getTotalBalance(holding)
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class FinTs4kMapperTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getTotalCostPrice_TotalCostPriceIsNull_CalculateByQuantityAndAverageCostPrice() {
|
fun getTotalCostPrice_TotalCostPriceIsNull_CalculateByQuantityAndAverageCostPrice() {
|
||||||
val holding = Holding("", null, null, null, 47, fints4kAmount("16.828"), null)
|
val holding = Holding("", null, null, null, 47.0, fints4kAmount("16.828"), null)
|
||||||
|
|
||||||
val result = underTest.getTotalCostPrice(holding)
|
val result = underTest.getTotalCostPrice(holding)
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,8 @@ not each project has the implement to model again.
|
||||||
### Gradle:
|
### Gradle:
|
||||||
|
|
||||||
```
|
```
|
||||||
plugins {
|
|
||||||
kotlin("jvm") version "2.0.10" // or kotlin("multiplatform"), depending on your requirements
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
// other repositories like mavenCentral(), ...
|
||||||
maven {
|
maven {
|
||||||
setUrl("https://maven.dankito.net/api/packages/codinux/maven")
|
setUrl("https://maven.dankito.net/api/packages/codinux/maven")
|
||||||
}
|
}
|
||||||
|
@ -26,7 +21,7 @@ repositories {
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("net.codinux.banking.client:fints4k-banking-client:0.6.1")
|
implementation("net.codinux.banking.client:fints4k-banking-client:0.7.1")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ buildscript {
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "net.codinux.banking.client"
|
group = "net.codinux.banking.client"
|
||||||
version = "0.6.1"
|
version = "0.7.1"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -23,7 +23,7 @@ allprojects {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ext["sourceCodeRepositoryBaseUrl"] = "github.com/codinux/BankingClient"
|
ext["sourceCodeRepositoryBaseUrl"] = "git.dankito.net/codinux/BankingClient"
|
||||||
|
|
||||||
ext["projectDescription"] = "Model and base definitions for Banking Client, a common abstraction for different implements of banking libraries"
|
ext["projectDescription"] = "Model and base definitions for Banking Client, a common abstraction for different implements of banking libraries"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit bdf8b14738c06016a48e1fc9781ad4d999e1219f
|
Subproject commit 88f1b01167e6a34b5b91f8797845bca0b7e4d3ab
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue