Implemented serializing transactions to JSON

This commit is contained in:
dankito 2022-02-24 02:13:37 +01:00
parent 85d6b079d6
commit ed66168c0b
17 changed files with 166 additions and 22 deletions

View File

@ -1,11 +1,13 @@
package net.dankito.banking.client.model
import kotlinx.datetime.LocalDate
import kotlinx.serialization.Serializable
import net.dankito.banking.fints.model.Amount
import net.dankito.banking.fints.model.Money
import net.dankito.utils.multiplatform.extensions.atUnixEpochStart
@Serializable
open class AccountTransaction(
val amount: Money, // TODO: if we decide to stick with Money, create own type, don't use that one from fints.model (or move over from)
val unparsedReference: String,

View File

@ -1,25 +1,27 @@
package net.dankito.banking.client.model
import kotlinx.datetime.LocalDate
import kotlinx.serialization.Serializable
import net.dankito.banking.fints.model.Currency
import net.dankito.banking.fints.model.Money
@Serializable
open class BankAccount(
identifier: String,
subAccountNumber: String?,
iban: String?,
val accountHolderName: String,
val type: BankAccountType = BankAccountType.CheckingAccount,
val productName: String? = null,
val currency: String = Currency.DefaultCurrencyCode, // TODO: may parse to a value object
val accountLimit: String? = null,
override val identifier: String,
override val subAccountNumber: String?,
override val iban: String?,
open val accountHolderName: String,
open val type: BankAccountType = BankAccountType.CheckingAccount,
open val productName: String? = null,
open val currency: String = Currency.DefaultCurrencyCode, // TODO: may parse to a value object
open val accountLimit: String? = null,
// TODO: create an enum AccountCapabilities [ RetrieveBalance, RetrieveTransactions, TransferMoney / MoneyTransfer(?), InstantPayment ]
val supportsRetrievingTransactions: Boolean = false,
val supportsRetrievingBalance: Boolean = false,
val supportsTransferringMoney: Boolean = false,
val supportsInstantPayment: Boolean = false
) : BankAccountIdentifier(identifier, subAccountNumber, iban) {
open val supportsRetrievingTransactions: Boolean = false,
open val supportsRetrievingBalance: Boolean = false,
open val supportsTransferringMoney: Boolean = false,
open val supportsInstantPayment: Boolean = false
) : BankAccountIdentifier {
internal constructor() : this("", null, null, "") // for object deserializers

View File

@ -1,8 +1,11 @@
package net.dankito.banking.client.model
interface BankAccountIdentifier {
open class BankAccountIdentifier(
open val identifier: String,
open val subAccountNumber: String?,
open val iban: String?,
)
val identifier: String
val subAccountNumber: String?
val iban: String?
}

View File

@ -0,0 +1,11 @@
package net.dankito.banking.client.model
import kotlinx.serialization.Serializable
@Serializable
open class BankAccountIdentifierImpl(
override val identifier: String,
override val subAccountNumber: String?,
override val iban: String?,
) : BankAccountIdentifier

View File

@ -1,5 +1,6 @@
package net.dankito.banking.client.model
import kotlinx.serialization.Serializable
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
import net.dankito.banking.fints.model.TanMethod
@ -7,6 +8,7 @@ import net.dankito.banking.fints.model.TanMethod
//import net.dankito.banking.client.model.tan.TanMethod
@Serializable
open class CustomerAccount(
override var bankCode: String,
override var loginName: String,

View File

@ -0,0 +1,24 @@
package net.dankito.banking.client.model.serializer
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import net.dankito.banking.fints.model.Amount
class AmountSerializer : KSerializer<Amount> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Amount", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Amount) {
encoder.encodeString(value.string)
}
override fun deserialize(decoder: Decoder): Amount {
return Amount(decoder.decodeString())
}
}

View File

@ -0,0 +1,24 @@
package net.dankito.banking.client.model.serializer
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import net.dankito.banking.fints.model.Currency
class CurrencySerializer : KSerializer<Currency> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Currency", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Currency) {
encoder.encodeString(value.code)
}
override fun deserialize(decoder: Decoder): Currency {
return Currency(decoder.decodeString())
}
}

View File

@ -0,0 +1,33 @@
package net.dankito.banking.client.model.serializer
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import net.dankito.banking.fints.model.Amount
import net.dankito.banking.fints.model.Currency
import net.dankito.banking.fints.model.Money
class MoneySerializer : KSerializer<Money> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Money", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Money) {
encoder.encodeString(value.amount.string + " " + value.currency.code)
}
override fun deserialize(decoder: Decoder): Money {
val value = decoder.decodeString()
val parts = value.split(" ")
if (parts.size > 1) {
return Money(Amount(parts[0]), parts[1])
}
return Money(Amount(value), Currency.DefaultCurrencyCode)
}
}

View File

@ -3,7 +3,6 @@ package net.dankito.banking.fints.mapper
import net.dankito.banking.client.model.*
import net.dankito.banking.client.model.AccountTransaction
import net.dankito.banking.client.model.parameter.FinTsClientParameter
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
import net.dankito.banking.client.model.response.ErrorCode
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
import net.dankito.banking.fints.model.*

View File

@ -1,5 +1,7 @@
package net.dankito.banking.fints.messages.datenelemente.implementierte.tan
import kotlinx.serialization.Serializable
/**
* Informationen zu Art und Parametrisierung von TAN-Medien. Als TAN-Medien werden sowohl
@ -8,6 +10,7 @@ package net.dankito.banking.fints.messages.datenelemente.implementierte.tan
*
* Wird das Datenelement TAN-Medium-Klasse mit B (bilateral vereinbart) belegt, so muss im Element Sicherheitsfunktion, kodiert die entsprechende Sicherheitsfunktion in der DEG Verfahrensparameter Zwei-Schritt-Verfahren referenziert werden.
*/
@Serializable
open class TanMedium(
open val mediumClass: TanMediumKlasse,
open val status: TanMediumStatus,

View File

@ -1,6 +1,10 @@
package net.dankito.banking.fints.model
import kotlinx.serialization.Serializable
import net.dankito.banking.client.model.serializer.AmountSerializer
@Serializable(with = AmountSerializer::class)
open class Amount(
val string: String
) {

View File

@ -1,6 +1,10 @@
package net.dankito.banking.fints.model
import kotlinx.serialization.Serializable
import net.dankito.banking.client.model.serializer.CurrencySerializer
@Serializable(with = CurrencySerializer::class)
open class Currency(
val code: String
) {

View File

@ -1,6 +1,9 @@
package net.dankito.banking.fints.model
import kotlinx.serialization.Serializable
@Serializable
open class DecoupledTanMethodParameters(
open val manualConfirmationAllowed: Boolean,
open val periodicStateRequestsAllowed: Boolean,

View File

@ -1,6 +1,10 @@
package net.dankito.banking.fints.model
import kotlinx.serialization.Serializable
import net.dankito.banking.client.model.serializer.MoneySerializer
@Serializable(with = MoneySerializer::class)
open class Money(
val amount: Amount,
val currency: Currency

View File

@ -1,9 +1,11 @@
package net.dankito.banking.fints.model
import kotlinx.serialization.Serializable
import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat
@Serializable
open class TanMethod(
open val displayName: String,
open val securityFunction: Sicherheitsfunktion,

View File

@ -1,4 +1,8 @@
import com.soywiz.korio.file.std.localCurrentDirVfs
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.LocalDate
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import net.dankito.banking.client.model.AccountTransaction
import net.dankito.banking.client.model.CustomerAccount
import net.dankito.banking.client.model.parameter.GetAccountDataParameter
@ -20,7 +24,7 @@ class NativeApp {
getAccountData(GetAccountDataParameter(bankCode, loginName, password))
}
fun getAccountData(param: GetAccountDataParameter) {
fun getAccountData(param: GetAccountDataParameter, outputFilePath: String? = null) {
val response = client.getAccountData(param)
if (response.error != null) {
@ -28,9 +32,13 @@ class NativeApp {
}
response.customerAccount?.let { account ->
println("Retrieved response from ${account.bankName} for ${account.customerName}")
if (outputFilePath != null) {
writeResponseToFile(outputFilePath, account)
} else {
println("Retrieved response from ${account.bankName} for ${account.customerName}")
displayRetrievedAccountData(account)
}
}
}
@ -100,4 +108,18 @@ class NativeApp {
return date.dayOfMonth.toStringWithTwoDigits() + "." + date.monthNumber.toStringWithTwoDigits() + "." + date.year
}
private fun writeResponseToFile(outputFilePath: String, customer: CustomerAccount) {
try {
val outputFile = localCurrentDirVfs.get(outputFilePath)
println("Writing file to ${outputFile.absolutePath}")
val json = Json.encodeToString(customer)
runBlocking { outputFile.writeString(json) }
} catch (e: Exception) {
println("Could not write file to $outputFilePath: $e")
}
}
}

View File

@ -38,6 +38,8 @@ class fints4kCommandLineInterface : CliktCommand(name = "fints", printHelpOnEmpt
val retrieveTransactionsForLastNDays by option("-l", "--last-n-days", help = "Retrieve transactions for last n days. If set 'retrieveTransactions' gets set to '${RetrieveTransactions.AccordingToRetrieveFromAndTo}' and 'retrieveTransactionsFrom' will be ignored.").int()
val outputFile by option("-o", help = "Write retrieved account transactions to file instead of stdout. Supported formats: JSON")
val preferredTanMethods by option("-m", "--tan-method", help = "Your preferred TAN methods to use if action affords a TAN. Can be repeated like '-m AppTan -m SmsTan'").enum<TanMethodType>().multiple()
val abortIfRequiresTan by option("-a", "--abort-if-requires-tan", help = "If actions should be aborted if it affords a TAN. Defaults to false").flag(default = false)
@ -71,7 +73,7 @@ class fints4kCommandLineInterface : CliktCommand(name = "fints", printHelpOnEmpt
app.getAccountData(GetAccountDataParameter(bankCode, loginName, password, null, retrieveBalance, effectiveRetrieveTransactions,
retrieveTransactionsFromDate, retrieveTransactionsToDate, preferredTanMethods, abortIfTanIsRequired = abortIfRequiresTan))
retrieveTransactionsFromDate, retrieveTransactionsToDate, preferredTanMethods, abortIfTanIsRequired = abortIfRequiresTan), outputFile)
}
}