Added FinTs4kBankingClient
This commit is contained in:
parent
c581a9ebb1
commit
3e4dfee615
|
@ -0,0 +1,125 @@
|
||||||
|
@file:OptIn(ExperimentalWasmDsl::class)
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("multiplatform")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
// TODO: remove again as soon as kmp-web-client is released
|
||||||
|
maven {
|
||||||
|
setUrl("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(8)
|
||||||
|
|
||||||
|
jvm {
|
||||||
|
withJava()
|
||||||
|
|
||||||
|
testRuns["test"].executionTask.configure {
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
testLogging {
|
||||||
|
showExceptions = true
|
||||||
|
showStandardStreams = true
|
||||||
|
events("passed", "skipped", "failed")
|
||||||
|
// exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// js {
|
||||||
|
// moduleName = "fints4k-banking-client"
|
||||||
|
// binaries.executable()
|
||||||
|
//
|
||||||
|
// browser {
|
||||||
|
// testTask {
|
||||||
|
// useKarma {
|
||||||
|
// useChromeHeadless()
|
||||||
|
// useFirefoxHeadless()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// nodejs {
|
||||||
|
// testTask {
|
||||||
|
// useMocha {
|
||||||
|
// timeout = "20s" // Mocha times out after 2 s, which is too short for bufferExceeded() test
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// wasmJs()
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// linuxX64()
|
||||||
|
// mingwX64()
|
||||||
|
//
|
||||||
|
// iosArm64()
|
||||||
|
// iosSimulatorArm64()
|
||||||
|
// macosX64()
|
||||||
|
// macosArm64()
|
||||||
|
// watchosArm64()
|
||||||
|
// watchosSimulatorArm64()
|
||||||
|
// tvosArm64()
|
||||||
|
// tvosSimulatorArm64()
|
||||||
|
|
||||||
|
applyDefaultHierarchyTemplate()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val coroutinesVersion: String by project
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
api(project(":BankingClient"))
|
||||||
|
|
||||||
|
api("net.codinux.banking:fints4k:1.0.0-Alpha-10")
|
||||||
|
|
||||||
|
// implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commonTest {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test"))
|
||||||
|
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jvmMain {
|
||||||
|
dependencies {
|
||||||
|
// implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:$coroutinesVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jvmTest {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// jsMain {
|
||||||
|
// dependencies {
|
||||||
|
//// implementation(npm("@js-joda/timezone", "2.3.0"))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// jsTest { }
|
||||||
|
//
|
||||||
|
// nativeMain { }
|
||||||
|
// nativeTest { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ext["customArtifactId"] = "fints4k-banking-client"
|
||||||
|
|
||||||
|
apply(from = "../gradle/scripts/publish-codinux.gradle.kts")
|
|
@ -0,0 +1,26 @@
|
||||||
|
package net.codinux.banking.client.fints4k
|
||||||
|
|
||||||
|
import net.codinux.banking.client.BankingClient
|
||||||
|
import net.codinux.banking.client.model.AccountCredentials
|
||||||
|
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
|
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||||
|
import net.codinux.banking.client.model.response.Response
|
||||||
|
import net.dankito.banking.fints.FinTsClient
|
||||||
|
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
|
||||||
|
|
||||||
|
class FinTs4kBankingClient : BankingClient {
|
||||||
|
|
||||||
|
private val mapper = FinTs4kMapper()
|
||||||
|
|
||||||
|
private val client = FinTsClient(SimpleFinTsClientCallback { tanChallenge ->
|
||||||
|
// callback.enterTan()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun getAccountDataAsync(credentials: AccountCredentials, options: GetAccountDataOptions): Response<GetAccountDataResponse> {
|
||||||
|
val response = client.getAccountDataAsync(mapper.mapToGetAccountDataParameter(credentials, options))
|
||||||
|
|
||||||
|
return mapper.map(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package net.codinux.banking.client.fints4k
|
||||||
|
|
||||||
|
import net.codinux.banking.client.BankingClientForCustomerBase
|
||||||
|
import net.codinux.banking.client.model.AccountCredentials
|
||||||
|
|
||||||
|
class FinTs4kBankingClientForCustomer(credentials: AccountCredentials)
|
||||||
|
: BankingClientForCustomerBase(credentials, FinTs4kBankingClient()) {
|
||||||
|
|
||||||
|
constructor(bankCode: String, loginName: String, password: String)
|
||||||
|
: this(AccountCredentials(bankCode, loginName, password))
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
package net.codinux.banking.client.fints4k
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.*
|
||||||
|
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
|
import net.codinux.banking.client.model.response.*
|
||||||
|
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
|
||||||
|
import net.dankito.banking.client.model.parameter.RetrieveTransactions
|
||||||
|
import net.dankito.banking.client.model.response.ErrorCode
|
||||||
|
import net.dankito.banking.fints.model.Money
|
||||||
|
|
||||||
|
class FinTs4kMapper {
|
||||||
|
|
||||||
|
fun mapToGetAccountDataParameter(credentials: AccountCredentials, options: GetAccountDataOptions) = GetAccountDataParameter(
|
||||||
|
credentials.bankCode, credentials.loginName, credentials.password,
|
||||||
|
null,
|
||||||
|
options.retrieveBalance,
|
||||||
|
RetrieveTransactions.valueOf(options.retrieveTransactions.name), options.retrieveTransactionsFrom, options.retrieveTransactionsTo,
|
||||||
|
abortIfTanIsRequired = options.abortIfTanIsRequired
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
fun map(response: net.dankito.banking.client.model.response.GetAccountDataResponse): Response<GetAccountDataResponse> {
|
||||||
|
return if (response.successful && response.customerAccount != null) {
|
||||||
|
Response(ResponseType.Success, mapCustomer(response.customerAccount!!))
|
||||||
|
} else {
|
||||||
|
mapError(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun mapCustomer(customer: net.dankito.banking.client.model.CustomerAccount): GetAccountDataResponse {
|
||||||
|
val mapped = CustomerAccount(
|
||||||
|
customer.bankCode, customer.loginName, customer.password,
|
||||||
|
customer.bankName, customer.bic, customer.customerName, customer.userId,
|
||||||
|
customer.accounts.map { mapAccount(it) }
|
||||||
|
)
|
||||||
|
|
||||||
|
return GetAccountDataResponse(mapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun mapAccount(account: net.dankito.banking.client.model.BankAccount): BankAccount = BankAccount(
|
||||||
|
account.identifier, account.accountHolderName, mapAccountType(account.type), account.iban, account.subAccountNumber,
|
||||||
|
account.productName, account.currency, account.accountLimit, isAccountTypeSupported(account),
|
||||||
|
mapFeatures(account),
|
||||||
|
mapAmount(account.balance), account.retrievedTransactionsFrom, account.retrievedTransactionsTo,
|
||||||
|
// TODO: map haveAllTransactionsBeenRetrieved and countDaysForWhichTransactionsAreKept
|
||||||
|
bookedTransactions = account.bookedTransactions.map { mapTransaction(it) }.toMutableList()
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun mapAccountType(type: net.dankito.banking.client.model.BankAccountType): BankAccountType =
|
||||||
|
BankAccountType.valueOf(type.name)
|
||||||
|
|
||||||
|
private fun mapFeatures(account: net.dankito.banking.client.model.BankAccount): Set<BankAccountFeatures> = buildSet {
|
||||||
|
if (account.supportsRetrievingBalance) {
|
||||||
|
add(BankAccountFeatures.RetrieveBalance)
|
||||||
|
}
|
||||||
|
if (account.supportsRetrievingTransactions) {
|
||||||
|
add(BankAccountFeatures.RetrieveTransactions)
|
||||||
|
}
|
||||||
|
if (account.supportsTransferringMoney) {
|
||||||
|
add(BankAccountFeatures.TransferMoney)
|
||||||
|
}
|
||||||
|
if (account.supportsInstantPayment) {
|
||||||
|
add(BankAccountFeatures.InstantPayment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isAccountTypeSupported(account: net.dankito.banking.client.model.BankAccount): Boolean {
|
||||||
|
// TODO:
|
||||||
|
// open val isAccountTypeSupportedByApplication: Boolean
|
||||||
|
// get() = FinTsClient.SupportedAccountTypes.contains(accountType)
|
||||||
|
// || allowedJobNames.contains(CustomerSegmentId.Balance.id)
|
||||||
|
// || allowedJobNames.contains(CustomerSegmentId.AccountTransactionsMt940.id)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun mapTransaction(transaction: net.dankito.banking.client.model.AccountTransaction): AccountTransaction = AccountTransaction(
|
||||||
|
mapAmount(transaction.amount), transaction.amount.currency.code, transaction.unparsedReference,
|
||||||
|
transaction.bookingDate, transaction.valueDate,
|
||||||
|
transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId,
|
||||||
|
transaction.bookingText, null,
|
||||||
|
transaction.statementNumber, transaction.sequenceNumber,
|
||||||
|
mapNullableAmount(transaction.openingBalance), mapNullableAmount(transaction.closingBalance),
|
||||||
|
// TODO: map other properties
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun mapNullableAmount(amount: Money?) = amount?.let { mapAmount(it) }
|
||||||
|
|
||||||
|
private fun mapAmount(amount: Money) = Amount.fromString(amount.amount.string.replace(',', '.'))
|
||||||
|
|
||||||
|
|
||||||
|
private fun <T> mapError(response: net.dankito.banking.client.model.response.GetAccountDataResponse): Response<T> {
|
||||||
|
val error = if (response.error != null) {
|
||||||
|
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())
|
||||||
|
} else {
|
||||||
|
Error(ErrorType.UnknownError, response.errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response(ResponseType.Error, null, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package net.codinux.banking.client.fints4k
|
||||||
|
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import net.codinux.banking.client.model.response.ResponseType
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class FinTs4kBankingClientTest {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
// set your credentials here:
|
||||||
|
private const val bankCode = ""
|
||||||
|
private const val loginName = ""
|
||||||
|
private const val password = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val underTest = FinTs4kBankingClientForCustomer(bankCode, loginName, password)
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAccountDataAsync() = runTest {
|
||||||
|
val result = underTest.getAccountDataAsync()
|
||||||
|
|
||||||
|
assertEquals(ResponseType.Success, result.type)
|
||||||
|
assertNotNull(result.data)
|
||||||
|
assertTrue(result.data!!.bookedTransactions.isNotEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,13 +28,17 @@ allprojects {
|
||||||
tasks.register("publishAllToMavenLocal") {
|
tasks.register("publishAllToMavenLocal") {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
":BankingClientModel:publishToMavenLocal",
|
":BankingClientModel:publishToMavenLocal",
|
||||||
":BankingClient:publishToMavenLocal"
|
":BankingClient:publishToMavenLocal",
|
||||||
|
|
||||||
|
":FinTs4jBankingClient:publishToMavenLocal"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("publishAll") {
|
tasks.register("publishAll") {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
":BankingClientModel:publish",
|
":BankingClientModel:publish",
|
||||||
":BankingClient:publish"
|
":BankingClient:publish",
|
||||||
|
|
||||||
|
":FinTs4jBankingClient:publish"
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -26,3 +26,5 @@ rootProject.name = "BankingClientProject"
|
||||||
|
|
||||||
include("BankingClientModel")
|
include("BankingClientModel")
|
||||||
include("BankingClient")
|
include("BankingClient")
|
||||||
|
|
||||||
|
include("FinTs4jBankingClient")
|
||||||
|
|
Loading…
Reference in New Issue