From b403557f2dc01bf7635d3222591e7361012b1f8a Mon Sep 17 00:00:00 2001 From: dankito Date: Fri, 18 Sep 2020 18:54:09 +0200 Subject: [PATCH] Implemented checking if bank supports setting count max entries. Had to adjust Jackson serialization so that and not just JobParameters gets saved and restored and to implement a lot of default constructors for this --- .../banking/fints/messages/MessageBuilder.kt | 44 ++++++++++++------- .../KontoumsaetzeZeitraumMt940Base.kt | 2 +- .../fints/model/GetTransactionsParameter.kt | 17 ++++++- .../segments/SepaAccountInfoParameters.kt | 6 ++- .../fints/response/segments/TanInfo.kt | 7 ++- .../segments/TanProcedureParameters.kt | 6 +++ .../segments/TwoStepTanProcedureParameters.kt | 6 ++- .../banking/util/JacksonJsonSerializer.kt | 11 ++++- .../persistence/JacksonClassNameIdResolver.kt | 35 +++++++++++++++ 9 files changed, 111 insertions(+), 23 deletions(-) create mode 100644 ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/JacksonClassNameIdResolver.kt diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt index 88e1724d..53d2fd2b 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt @@ -12,15 +12,15 @@ import net.dankito.banking.fints.messages.segmente.Segment import net.dankito.banking.fints.messages.segmente.SegmentNumberGenerator import net.dankito.banking.fints.messages.segmente.Synchronisierung import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId +import net.dankito.banking.fints.messages.segmente.id.ISegmentId import net.dankito.banking.fints.messages.segmente.implementierte.* import net.dankito.banking.fints.messages.segmente.implementierte.sepa.SepaBankTransferBase import net.dankito.banking.fints.messages.segmente.implementierte.tan.TanGeneratorListeAnzeigen import net.dankito.banking.fints.messages.segmente.implementierte.tan.TanGeneratorTanMediumAnOderUmmelden import net.dankito.banking.fints.messages.segmente.implementierte.umsaetze.* import net.dankito.banking.fints.model.* -import net.dankito.banking.fints.response.segments.JobParameters -import net.dankito.banking.fints.response.segments.SepaAccountInfoParameters -import net.dankito.banking.fints.response.segments.TanResponse +import net.dankito.banking.fints.response.InstituteSegmentId +import net.dankito.banking.fints.response.segments.* import net.dankito.banking.fints.util.FinTsUtils import net.dankito.utils.multiplatform.Date import kotlin.math.absoluteValue @@ -152,20 +152,36 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg val result = supportsGetTransactionsMt940(account) if (result.isJobVersionSupported) { - val transactionsJob = if (result.isAllowed(7)) KontoumsaetzeZeitraumMt940Version7(generator.resetSegmentNumber(2), parameter, dialogContext.bank, account) - else if (result.isAllowed(6)) KontoumsaetzeZeitraumMt940Version6(generator.resetSegmentNumber(2), parameter, account) - else KontoumsaetzeZeitraumMt940Version5(generator.resetSegmentNumber(2), parameter, account) - - val segments = mutableListOf(transactionsJob) - - addTanSegmentIfRequired(CustomerSegmentId.AccountTransactionsMt940, dialogContext, segments) - - return createSignedMessageBuilderResult(dialogContext, segments) + return createGetTransactionsMessageMt940(result, parameter, dialogContext, account) } return result } + protected open fun createGetTransactionsMessageMt940(result: MessageBuilderResult, parameter: GetTransactionsParameter, + dialogContext: DialogContext, account: AccountData): MessageBuilderResult { + + if (parameter.maxCountEntries != null) { + parameter.isSettingMaxCountEntriesAllowedByBank = determineIsSettingMaxCountEntriesAllowed(dialogContext.bank, InstituteSegmentId.AccountTransactionsMt940Parameters, listOf(5, 6, 7)) + } + + val transactionsJob = if (result.isAllowed(7)) KontoumsaetzeZeitraumMt940Version7(generator.resetSegmentNumber(2), parameter, dialogContext.bank, account) + else if (result.isAllowed(6)) KontoumsaetzeZeitraumMt940Version6(generator.resetSegmentNumber(2), parameter, account) + else KontoumsaetzeZeitraumMt940Version5(generator.resetSegmentNumber(2), parameter, account) + + val segments = mutableListOf(transactionsJob) + + addTanSegmentIfRequired(CustomerSegmentId.AccountTransactionsMt940, dialogContext, segments) + + return createSignedMessageBuilderResult(dialogContext, segments) + } + + protected open fun determineIsSettingMaxCountEntriesAllowed(bank: BankData, segmentId: ISegmentId, supportedJobVersions: List): Boolean { + return bank.supportedJobs.filterIsInstance() + .filter { it.segmentId == segmentId.id && supportedJobVersions.contains(it.segmentVersion) } + .firstOrNull { it.settingCountEntriesAllowed } != null + } + open fun supportsGetTransactions(account: AccountData): Boolean { return supportsGetTransactionsMt940(account).isJobVersionSupported } @@ -255,9 +271,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg val segmentId = if (data.instantPayment) CustomerSegmentId.SepaInstantPaymentBankTransfer else CustomerSegmentId.SepaBankTransfer - val messageBuilderResultAndNullableUrn = supportsBankTransferAndSepaVersion(dialogContext.bank, account, segmentId) - val result = messageBuilderResultAndNullableUrn.first - val urn = messageBuilderResultAndNullableUrn.second + val (result, urn) = supportsBankTransferAndSepaVersion(dialogContext.bank, account, segmentId) if (result.isJobVersionSupported && urn != null) { val segments = mutableListOf(SepaBankTransferBase(segmentId, generator.resetSegmentNumber(2), diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KontoumsaetzeZeitraumMt940Base.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KontoumsaetzeZeitraumMt940Base.kt index 59e49cec..465c50af 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KontoumsaetzeZeitraumMt940Base.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KontoumsaetzeZeitraumMt940Base.kt @@ -34,6 +34,6 @@ abstract class KontoumsaetzeZeitraumMt940Base( AlleKonten(false, Existenzstatus.Mandatory), // currently no supported, we retrieve account transactions account by account (most banks don't support AlleKonten anyway) Datum(parameter.fromDate, Existenzstatus.Optional), Datum(parameter.toDate, Existenzstatus.Optional), - MaximaleAnzahlEintraege(parameter.maxCountEntries, Existenzstatus.Optional), // > 0. O: „Eingabe Anzahl Einträge erlaubt“ (BPD) = „J“. N: sonst + MaximaleAnzahlEintraege(parameter.maxCountEntriesIfSettingItIsAllowed, if (parameter.isSettingMaxCountEntriesAllowedByBank) Existenzstatus.Optional else Existenzstatus.NotAllowed), // > 0. O: „Eingabe Anzahl Einträge erlaubt“ (BPD) = „J“. N: sonst Aufsetzpunkt(null, Existenzstatus.Optional) // will be set dynamically, see MessageBuilder.rebuildMessageWithContinuationId(); M: vom Institut wurde ein Aufsetzpunkt rückgemeldet. N: sonst )) \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetTransactionsParameter.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetTransactionsParameter.kt index 5ed8ca79..3fad1a77 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetTransactionsParameter.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetTransactionsParameter.kt @@ -7,7 +7,22 @@ open class GetTransactionsParameter( val alsoRetrieveBalance: Boolean = true, val fromDate: Date? = null, val toDate: Date? = null, + + /** + * Be aware this is by far not supported by all banks. + * + * And it depends on the actual job if setting maxCountEntries is supported or not. + * + * // TODO: set a parameter in response if maxCountEntries is set but bank doesn't support it. + */ val maxCountEntries: Int? = null, val abortIfTanIsRequired: Boolean = false, val retrievedChunkListener: ((Collection) -> Unit)? = null -) \ No newline at end of file +) { + + internal var isSettingMaxCountEntriesAllowedByBank = false + + internal val maxCountEntriesIfSettingItIsAllowed: Int? + get() = if (isSettingMaxCountEntriesAllowedByBank) maxCountEntries else null + +} \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/SepaAccountInfoParameters.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/SepaAccountInfoParameters.kt index b7caa115..bcf07be2 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/SepaAccountInfoParameters.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/SepaAccountInfoParameters.kt @@ -63,8 +63,10 @@ open class SepaAccountInfoParameters( * zugelassenen SEPA pain messages. */ val supportedSepaFormats: List -) - : JobParameters(parameters) { +) : JobParameters(parameters) { + + internal constructor() : this(JobParameters(), false, false, false, false, -1, listOf()) // for object deserializers + companion object { const val CountReservedUsageLengthNotSet = 0 diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanInfo.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanInfo.kt index 9321433b..36d49340 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanInfo.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanInfo.kt @@ -4,5 +4,8 @@ package net.dankito.banking.fints.response.segments open class TanInfo( parameters: JobParameters, val tanProcedureParameters: TwoStepTanProcedureParameters -) - : JobParameters(parameters) \ No newline at end of file +) : JobParameters(parameters) { + + internal constructor() : this(JobParameters(), TwoStepTanProcedureParameters()) // for object deserializers + +} \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanProcedureParameters.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanProcedureParameters.kt index f3911110..a8a3dcf3 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanProcedureParameters.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanProcedureParameters.kt @@ -28,6 +28,12 @@ open class TanProcedureParameters( val countSupportedActiveTanMedia: Int? ) { + + internal constructor() : this(Sicherheitsfunktion.Klartext, TanProcess.TanProcess1, "", null, null, "", -1, + AllowedTanFormat.Alphanumeric, "", -1, false, TanZeitUndDialogbezug.NotSupported, false, SmsAbbuchungskontoErforderlich.SmsAbbuchungskontoDarfNichtAngegebenWerden, AuftraggeberkontoErforderlich.AuftraggeberkontoDarfNichtAngegebenWerden, + false, false, Initialisierungsmodus.InitialisierungsverfahrenMitKlartextPinOhneTan, BezeichnungDesTanMediumsErforderlich.BezeichnungDesTanMediumsDarfNichtAngegebenWerden, false, null) // for object deserializers + + override fun toString(): String { return "$procedureName $technicalTanProcedureIdentification" } diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TwoStepTanProcedureParameters.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TwoStepTanProcedureParameters.kt index 1c0ea16e..6268ad59 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TwoStepTanProcedureParameters.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TwoStepTanProcedureParameters.kt @@ -6,4 +6,8 @@ open class TwoStepTanProcedureParameters( val moreThanOneTanDependentJobPerMessageAllowed: Boolean, val jobHashValue: String, // not evaluated for PIN/TAN val procedureParameters: List -) \ No newline at end of file +) { + + internal constructor() : this(false, false, "", listOf()) // for object deserializers + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/JacksonJsonSerializer.kt b/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/JacksonJsonSerializer.kt index 8f83be81..40690cb6 100644 --- a/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/JacksonJsonSerializer.kt +++ b/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/JacksonJsonSerializer.kt @@ -1,11 +1,20 @@ package net.dankito.banking.util +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.databind.ObjectMapper +import net.dankito.banking.util.persistence.JacksonClassNameIdResolver import net.dankito.utils.multiplatform.File import kotlin.reflect.KClass open class JacksonJsonSerializer( - protected val serializer: net.dankito.utils.serialization.ISerializer = net.dankito.utils.serialization.JacksonJsonSerializer() + protected val serializer: net.dankito.utils.serialization.ISerializer = net.dankito.utils.serialization.JacksonJsonSerializer { objectMapper -> + val typeResolver = ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE) + typeResolver.init(JsonTypeInfo.Id.CLASS, JacksonClassNameIdResolver()) + typeResolver.inclusion(JsonTypeInfo.As.PROPERTY) + typeResolver.typeProperty("@CLASS") + objectMapper.setDefaultTyping(typeResolver) + } ) : ISerializer { override fun serializeObject(obj: Any, outputFile: File) { diff --git a/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/JacksonClassNameIdResolver.kt b/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/JacksonClassNameIdResolver.kt new file mode 100644 index 00000000..a2983c09 --- /dev/null +++ b/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/JacksonClassNameIdResolver.kt @@ -0,0 +1,35 @@ +package net.dankito.banking.util.persistence + +import com.fasterxml.jackson.databind.DatabindContext +import com.fasterxml.jackson.databind.JavaType +import com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver +import com.fasterxml.jackson.databind.type.SimpleType +import com.fasterxml.jackson.databind.type.TypeFactory +import net.dankito.banking.fints.response.segments.JobParameters +import net.dankito.banking.fints.response.segments.RetrieveAccountTransactionsInMt940Parameters +import net.dankito.banking.fints.response.segments.SepaAccountInfoParameters +import kotlin.reflect.jvm.jvmName + + +open class JacksonClassNameIdResolver : ClassNameIdResolver(SimpleType.construct(JobParameters::class.java), TypeFactory.defaultInstance()) { + + override fun idFromValue(value: Any?): String { + if (value is RetrieveAccountTransactionsInMt940Parameters) { + return RetrieveAccountTransactionsInMt940Parameters::class.jvmName + } + else if (value is SepaAccountInfoParameters) { + return SepaAccountInfoParameters::class.jvmName + } + + return super.idFromValue(value) + } + + override fun typeFromId(context: DatabindContext?, id: String?): JavaType { + return when (id) { + RetrieveAccountTransactionsInMt940Parameters::class.jvmName -> _typeFactory.constructSpecializedType(_baseType, RetrieveAccountTransactionsInMt940Parameters::class.java) + SepaAccountInfoParameters::class.jvmName -> _typeFactory.constructSpecializedType(_baseType, SepaAccountInfoParameters::class.java) + else -> super.typeFromId(context, id) + } + } + +} \ No newline at end of file