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

This commit is contained in:
dankito 2020-09-18 18:54:09 +02:00
parent 4ed3d44b9e
commit b403557f2d
9 changed files with 111 additions and 23 deletions

View File

@ -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<Segment>(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<Segment>(transactionsJob)
addTanSegmentIfRequired(CustomerSegmentId.AccountTransactionsMt940, dialogContext, segments)
return createSignedMessageBuilderResult(dialogContext, segments)
}
protected open fun determineIsSettingMaxCountEntriesAllowed(bank: BankData, segmentId: ISegmentId, supportedJobVersions: List<Int>): Boolean {
return bank.supportedJobs.filterIsInstance<RetrieveAccountTransactionsInMt940Parameters>()
.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<Segment>(SepaBankTransferBase(segmentId, generator.resetSegmentNumber(2),

View File

@ -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
))

View File

@ -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<AccountTransaction>) -> Unit)? = null
)
) {
internal var isSettingMaxCountEntriesAllowedByBank = false
internal val maxCountEntriesIfSettingItIsAllowed: Int?
get() = if (isSettingMaxCountEntriesAllowedByBank) maxCountEntries else null
}

View File

@ -63,8 +63,10 @@ open class SepaAccountInfoParameters(
* zugelassenen SEPA pain messages.
*/
val supportedSepaFormats: List<String>
)
: JobParameters(parameters) {
) : JobParameters(parameters) {
internal constructor() : this(JobParameters(), false, false, false, false, -1, listOf()) // for object deserializers
companion object {
const val CountReservedUsageLengthNotSet = 0

View File

@ -4,5 +4,8 @@ package net.dankito.banking.fints.response.segments
open class TanInfo(
parameters: JobParameters,
val tanProcedureParameters: TwoStepTanProcedureParameters
)
: JobParameters(parameters)
) : JobParameters(parameters) {
internal constructor() : this(JobParameters(), TwoStepTanProcedureParameters()) // for object deserializers
}

View File

@ -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"
}

View File

@ -6,4 +6,8 @@ open class TwoStepTanProcedureParameters(
val moreThanOneTanDependentJobPerMessageAllowed: Boolean,
val jobHashValue: String, // not evaluated for PIN/TAN
val procedureParameters: List<TanProcedureParameters>
)
) {
internal constructor() : this(false, false, "", listOf()) // for object deserializers
}

View File

@ -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) {

View File

@ -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)
}
}
}