Implemented making data element's value optional

This commit is contained in:
dankl 2019-10-13 14:07:28 +02:00 committed by dankito
parent 9a094ed3fc
commit 4dfc53994c
19 changed files with 101 additions and 86 deletions

View File

@ -6,6 +6,13 @@ import net.dankito.fints.messages.Existenzstatus
abstract class Datenelement(existenzstatus: Existenzstatus): DatenelementBase(existenzstatus) {
abstract val isValueSet: Boolean
open val writeToOutput: Boolean
get() = existenzstatus == Existenzstatus.Mandatory
|| existenzstatus == Existenzstatus.Optional && isValueSet
@Throws(IllegalArgumentException::class)
abstract fun validate()

View File

@ -7,14 +7,14 @@ import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDat
/**
* Es sind nur die jeweils aufgeführten Werte zulässig.
*/
abstract class Code(code: String, val allowedValues: List<String>, existenzstatus: Existenzstatus)
abstract class Code(code: String?, val allowedValues: List<String>, existenzstatus: Existenzstatus)
: AlphanumerischesDatenelement(code, existenzstatus) {
override fun validate() {
super.validate()
if (allowedValues.contains(text) == false) {
throwValidationException("'$text' ist kein Wert aus der Liste der zulässigen Werte: " +
if (allowedValues.contains(value) == false) {
throwValidationException("'$value' ist kein Wert aus der Liste der zulässigen Werte: " +
allowedValues.joinToString(", ")
)
}

View File

@ -11,27 +11,16 @@ import java.util.*
*
* Erlaubt sind alle existenten Datumsangaben.
*/
open class Datum(date: Int, existenzstatus: Existenzstatus) : NumerischesDatenelement(date, 8, existenzstatus) {
open class Datum(date: Int?, existenzstatus: Existenzstatus) : NumerischesDatenelement(date, 8, existenzstatus) {
companion object {
const val HbciDateFormatString = "yyyyMMdd"
val HbciDateFormat = SimpleDateFormat(HbciDateFormatString)
const val DateNotSet = Int.MIN_VALUE
}
constructor(date: Date?, existenzstatus: Existenzstatus)
: this(date?.let { HbciDateFormat.format(it).toInt() } ?: DateNotSet, existenzstatus)
override fun format(): String {
if (value == DateNotSet) {
return "" // optional element and value not set -> write nothing to output
}
return super.format()
}
: this(date?.let { HbciDateFormat.format(it).toInt() }, existenzstatus)
}

View File

@ -9,5 +9,5 @@ import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDat
*
* Maximal 30 Zeichen
*/
abstract class Identifikation(identification: String, existenzstatus: Existenzstatus)
abstract class Identifikation(identification: String?, existenzstatus: Existenzstatus)
: AlphanumerischesDatenelement(identification, existenzstatus, 30)

View File

@ -10,4 +10,4 @@ import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDat
* Hat das DE den Status Kann, so gilt bei Auslassung der Standardwert N.
*/
open class JaNein(yes: Boolean?, existenzstatus: Existenzstatus)
: AlphanumerischesDatenelement(if (yes == true) "J" else "N", existenzstatus, 1)
: AlphanumerischesDatenelement(yes?.let { if (yes == true) "J" else "N" }, existenzstatus, 1)

View File

@ -9,7 +9,7 @@ import net.dankito.fints.messages.datenelemente.basisformate.ZiffernDatenelement
*
* (Siehe z.B. PDF Messages_Geschaeftsvorfaelle Kapitel E.4 (S. 813) oder https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes .)
*/
open class Laenderkennzeichen(countryCode: Int, existenzstatus: Existenzstatus)
open class Laenderkennzeichen(countryCode: Int?, existenzstatus: Existenzstatus)
: ZiffernDatenelement(countryCode, 3, existenzstatus) {
companion object {

View File

@ -12,27 +12,16 @@ import java.util.*
* Gültige Uhrzeit. Es ist immer Ortszeit des sendenden Systems einzustellen.
* Unterschiedliche Zeitzonen werden nicht unterstützt
*/
open class Uhrzeit(time: Int, existenzstatus: Existenzstatus) : ZiffernDatenelement(time, 6, existenzstatus) {
open class Uhrzeit(time: Int?, existenzstatus: Existenzstatus) : ZiffernDatenelement(time, 6, existenzstatus) {
companion object {
const val HbciTimeFormatString = "HHmmss"
val HbciTimeFormat = SimpleDateFormat(HbciTimeFormatString)
const val TimeNotSet = Int.MIN_VALUE
}
constructor(time: Date?, existenzstatus: Existenzstatus)
: this(time?.let { HbciTimeFormat.format(it).toInt() } ?: 0, existenzstatus)
override fun format(): String {
if (value == TimeNotSet) {
return "" // optional element and value not set -> write nothing to output
}
return super.format()
}
: this(time?.let { HbciTimeFormat.format(it).toInt() }, existenzstatus)
}

View File

@ -7,22 +7,24 @@ import net.dankito.fints.messages.Existenzstatus
* Es gilt der FinTS-Basiszeichensatz ohne die Zeichen CR und LF.
*/
abstract class AlphanumerischesDatenelement @JvmOverloads constructor(
alphanumericValue: String, existenzstatus: Existenzstatus, val maxLength: Int? = null
alphanumericValue: String?, existenzstatus: Existenzstatus, val maxLength: Int? = null
) : TextDatenelement(alphanumericValue, existenzstatus) {
override fun validate() {
super.validate()
if (text.contains("\r") || text.contains("\n")) {
throwValidationException("Alphanumerischer Wert '$text' darf kein Carriage Return (\r) oder " +
if (writeToOutput && value != null) { // if value is null and value has to be written to output then validation already fails above
if (value.contains("\r") || value.contains("\n")) {
throwValidationException("Alphanumerischer Wert '$value' darf kein Carriage Return (\r) oder " +
"Line Feed (\n) enthalten.")
}
maxLength?.let {
if (text.length > maxLength) {
throwValidationException("Wert '$text' darf maximal $maxLength Zeichen lang sein, " +
"hat aber ${text.length} Zeichen.")
if (value.length > maxLength) {
throwValidationException("Wert '$value' darf maximal $maxLength Zeichen lang sein, " +
"hat aber ${value.length} Zeichen.")
}
}
}
}

View File

@ -1,7 +1,6 @@
package net.dankito.fints.messages.datenelemente.basisformate
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.Datenelement
/**
@ -10,12 +9,13 @@ import net.dankito.fints.messages.datenelemente.Datenelement
* für binäre Daten keine Gültigkeit besitzt. Ferner gelten die speziellen Syntaxregeln für
* binäre Daten (s. Kap. H.1.3).
*/
open class BinaerDatenelement @JvmOverloads constructor(val data: String, existenzstatus: Existenzstatus, val maxLength: Int? = null)
: Datenelement(existenzstatus) {
open class BinaerDatenelement @JvmOverloads constructor(data: String?, existenzstatus: Existenzstatus, val maxLength: Int? = null)
: TextDatenelement(data, existenzstatus) {
@JvmOverloads constructor(data: ByteArray, existenzstatus: Existenzstatus, maxLength: Int? = null) :
this(String(data), existenzstatus, maxLength)
/**
* Für binäre Daten gilt eine besondere Syntaxregelung: Das Auftreten dieser Daten wird eingeleitet mit dem
* Binärdatenkennzeichen (@). Anschließend folgt die Längenangabe zu den binären Daten und der binäre Wert selbst,
@ -27,21 +27,23 @@ open class BinaerDatenelement @JvmOverloads constructor(val data: String, existe
* Bei Elementen, die entsprechende Zeichen enthalten können (z. B. DE SEPAName) ist eine base64-Kodierung in der
* Spezifikation vorzusehen.
*/
override fun format(): String {
if (data.length > 0) {
return "@${data.length}@" + data
}
return ""
override fun formatValue(value: String): String {
return "@${value.length}@" + value
}
override fun validate() {
// binary data aren't checked, so they are always valid
if (writeToOutput) {
checkIfMandatoryValueIsSet()
value?.let { // if value is null and value has to be written to output then validation already fails above
maxLength?.let {
if (data.length > maxLength) {
throwValidationException("Binäre Daten dürfen nur eine maximale Größe von $maxLength Bytes haben, " +
"haben aber ${data.length} Bytes.")
if (value.length > maxLength) {
throwValidationException("Binäre Daten dürfen nur eine maximale Größe von $maxLength Bytes " +
"haben, haben aber ${value.length} Bytes.")
}
}
}
}
}

View File

@ -1,26 +1,24 @@
package net.dankito.fints.messages.datenelemente.basisformate
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.Datenelement
/**
* Zulässig sind lediglich die Ziffern 0 bis 9. Führende Nullen sind nicht zugelassen.
*/
abstract class NumerischesDatenelement(val value: Int, val numberOfDigits: Int, existenzstatus: Existenzstatus)
: Datenelement(existenzstatus) {
override fun format(): String {
return value.toString()
}
abstract class NumerischesDatenelement(val number: Int?, val numberOfDigits: Int, existenzstatus: Existenzstatus)
: TextDatenelement(number?.toString(), existenzstatus) {
override fun validate() {
super.validate()
if (writeToOutput && number != null) { // if number is null and number has to be written to output then validation already fails above
val maxValue = Math.pow(10.0, numberOfDigits.toDouble()) - 1
if (value < 0 || value > maxValue) {
throwValidationException("Wert '$value' muss im Wertebereich von 0 - $maxValue liegen.")
if (number < 0 || number > maxValue) {
throwValidationException("Wert '$number' muss im Wertebereich von 0 - $maxValue liegen.")
}
}
}

View File

@ -8,27 +8,49 @@ import net.dankito.fints.messages.datenelemente.Datenelement
/**
* Es gilt der vollständige FinTS-Basiszeichensatz.
*/
abstract class TextDatenelement(val text: String, existenzstatus: Existenzstatus) : Datenelement(existenzstatus) {
abstract class TextDatenelement(val value: String?, existenzstatus: Existenzstatus) : Datenelement(existenzstatus) {
override val isValueSet = value != null
override fun format(): String {
return text
if (writeToOutput) {
value?.let {
return formatValue(it)
}
}
return ""
}
protected open fun formatValue(value: String): String {
return value // may overwritten in sub classes
}
override fun validate() {
if (writeToOutput) {
checkIfMandatoryValueIsSet()
try {
if (HbciCharset.DefaultCharset.newEncoder().canEncode(text) == false) {
if (HbciCharset.DefaultCharset.newEncoder().canEncode(value) == false) {
throwInvalidCharacterException()
}
} catch (e: Exception) {
throwInvalidCharacterException()
}
}
}
protected open fun checkIfMandatoryValueIsSet() {
if (existenzstatus == Existenzstatus.Mandatory && value == null) {
throwValidationException("Wert ist auf dem Pflichtfeld ${javaClass.simpleName} not set")
}
}
protected open fun throwInvalidCharacterException() {
throwValidationException(
"Wert '$text' enthält Zeichen die gemäß des Zeichensatzes " +
"Wert '$value' enthält Zeichen die gemäß des Zeichensatzes " +
"${HbciCharset.DefaultCharset.displayName()} nicht erlaubt sind."
)
}

View File

@ -6,12 +6,12 @@ import net.dankito.fints.messages.Existenzstatus
/**
* Zulässig sind die Ziffern 0 bis 9. Führende Nullen sind zugelassen.
*/
abstract class ZiffernDatenelement(value: Int, numberOfDigits: Int, existenzstatus: Existenzstatus)
abstract class ZiffernDatenelement(value: Int?, numberOfDigits: Int, existenzstatus: Existenzstatus)
: NumerischesDatenelement(value, numberOfDigits, existenzstatus) {
override fun format(): String {
return String.format("%0${numberOfDigits}d", value)
override fun formatValue(value: String): String {
return String.format("%0${numberOfDigits}d", number)
}
}

View File

@ -18,11 +18,14 @@ import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenele
abstract class Bezugssegment(segmentNumber: Int, existenzstatus: Existenzstatus)
: NumerischesDatenelement(segmentNumber, 3, existenzstatus) {
override fun validate() {
super.validate()
if (value < 1) {
throwValidationException("Bezug Segmentnummer '$value' muss größer oder gleich 1 sein.")
number?.let { // if number is null and number has to be written to output then validation already fails above
if (number < 1) {
throwValidationException("Bezug Segmentnummer '$number' muss größer oder gleich 1 sein.")
}
}
}

View File

@ -11,11 +11,14 @@ import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenele
*/
open class Segmentnummer(number: Int) : NumerischesDatenelement(number, 3, Existenzstatus.Mandatory) {
override fun validate() {
super.validate()
if (value < 1) {
throwValidationException("Segmentnummer '$value' muss größer oder gleich 1 sein.")
number?.let { // if number is null and number has to be written to output then validation already fails above
if (number < 1) {
throwValidationException("Segmentnummer '$number' muss größer oder gleich 1 sein.")
}
}
}

View File

@ -12,4 +12,4 @@ import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Identifikatio
* Es ist zu beachten, dass Kontonummern auch führende Nullen beinhalten können, die
* bankfachlich relevant sind und nicht abgeschnitten werden dürfen.
*/
open class KontoDepotnummer(number: String, existenzstatus: Existenzstatus) : Identifikation(number, existenzstatus)
open class KontoDepotnummer(number: String?, existenzstatus: Existenzstatus) : Identifikation(number, existenzstatus)

View File

@ -9,4 +9,4 @@ import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Identifikatio
* geführt werden. Anstatt einer Kontonummer kann auch ein anderes identifizierendes Merkmal
* angegeben werden (z.B. der entsprechende ISO-Währungscode bei Währungskonten).
*/
open class Unterkontomerkmal(attribute: String, existenzstatus: Existenzstatus) : Identifikation(attribute, existenzstatus)
open class Unterkontomerkmal(attribute: String?, existenzstatus: Existenzstatus) : Identifikation(attribute, existenzstatus)

View File

@ -9,5 +9,5 @@ import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDat
* kann in Verwaltungs-Geschäftsvorfällen benutzt werden, wenn z. B. die Angabe der echten Handynummer
* aus Datenschutzgründen nicht möglich ist oder auch um die Benutzerfreundlichkeit zu erhöhen.
*/
open class BezeichnungDesTANMediums(identifier: String, existenzstatus: Existenzstatus)
open class BezeichnungDesTANMediums(identifier: String?, existenzstatus: Existenzstatus)
: AlphanumerischesDatenelement(identifier, existenzstatus, 32)

View File

@ -27,9 +27,9 @@ open class KontoverbindungInternational(
) : Datenelementgruppe(listOf(
IBAN(iban ?: "", Existenzstatus.Optional),
BIC(bic ?: "", Existenzstatus.Optional),
KontoDepotnummer(accountNumber ?: "", Existenzstatus.Optional),
Unterkontomerkmal(subAccountAttribute ?: "", Existenzstatus.Optional),
Kreditinstitutskennung(bankCountryCode ?: 0, bankCode ?: "", Existenzstatus.Optional)
KontoDepotnummer(accountNumber, Existenzstatus.Optional),
Unterkontomerkmal(subAccountAttribute, Existenzstatus.Optional),
Kreditinstitutskennung(bankCountryCode ?: 0, bankCode ?: "", if (bankCountryCode != null && bankCode != null) Existenzstatus.Optional else Existenzstatus.NotAllowed)
), Existenzstatus.Mandatory) {
constructor(bank: BankData, customer: CustomerData, subAccountAttribute: String?)

View File

@ -33,6 +33,6 @@ open class ZweiSchrittTanEinreichung(
NotAllowedDatenelement(), // TODO: SMS-Abbuchungskonto // M: Bei TAN-Process=1, 3, 4 und „SMS-Abbuchungskonto erforderlich“=2. O: sonst
NotAllowedDatenelement(), // TODO: Challenge-Klasse // M: bei TAN-Prozess=1 und „Challenge-Klasse erforderlich“=J. N: sonst
NotAllowedDatenelement(), // TODO: Parameter Challenge-Klasse // O: Bei TAN-Process=1 „Challenge-Klasse erforderlich“=J. N: sonst
BezeichnungDesTANMediums(tanMediaIdentifier ?: "", Existenzstatus.Optional), // M: bei TAN-Prozess=1, 3, 4 und „Anzahl unterstützter aktiver TAN-Medien“ nicht vorhanden. O: sonst
BezeichnungDesTANMediums(tanMediaIdentifier, Existenzstatus.Optional), // M: bei TAN-Prozess=1, 3, 4 und „Anzahl unterstützter aktiver TAN-Medien“ nicht vorhanden. O: sonst
NotAllowedDatenelement() // TODO: Antwort HHD_UC // M: bei TAN-Prozess=2 und „Antwort HHD_UC erforderlich“=“J“. O: sonst
), Existenzstatus.Mandatory)