Renamed usage to reference, remittee to recipient and instant payment to real-time transfer

This commit is contained in:
dankito 2020-09-24 02:53:09 +02:00
parent 66feba0614
commit bf9d406053
95 changed files with 921 additions and 917 deletions

View File

@ -8,7 +8,7 @@ However it's not a full implementation of FinTS standard but implements all comm
## Features ## Features
- Retrieving account information, balances and turnovers (Kontoumsätze und -saldo). - Retrieving account information, balances and turnovers (Kontoumsätze und -saldo).
- Transfer money and instant payments (SEPA Überweisungen und Echtzeitüberweisung). - Transfer money and real-time transfers (SEPA Überweisungen und Echtzeitüberweisung).
- Supports TAN methods chipTAN manual, Flickercode, QrCode and Photo (Matrix code), pushTAN, smsTAN and appTAN. - Supports TAN methods chipTAN manual, Flickercode, QrCode and Photo (Matrix code), pushTAN, smsTAN and appTAN.
## Setup ## Setup

View File

@ -400,7 +400,7 @@ open class FinTsClient(
response.getFirstSegmentById<ReceivedCreditCardTransactionsAndBalance>(InstituteSegmentId.CreditCardTransactions)?.let { transactionsSegment -> response.getFirstSegmentById<ReceivedCreditCardTransactionsAndBalance>(InstituteSegmentId.CreditCardTransactions)?.let { transactionsSegment ->
balance = Money(transactionsSegment.balance.amount, transactionsSegment.balance.currency ?: "EUR") balance = Money(transactionsSegment.balance.amount, transactionsSegment.balance.currency ?: "EUR")
bookedTransactions.addAll(transactionsSegment.transactions.map { AccountTransaction(parameter.account, it.amount, "", it.bookingDate, it.otherPartyName, null, null, "", it.valueDate) }) bookedTransactions.addAll(transactionsSegment.transactions.map { AccountTransaction(parameter.account, it.amount, it.otherPartyName, it.bookingDate, it.otherPartyName, null, null, "", it.valueDate) })
} }
} }
@ -1138,7 +1138,7 @@ open class FinTsClient(
account.setSupportsFeature(AccountFeature.RetrieveAccountTransactions, messageBuilder.supportsGetTransactions(account)) account.setSupportsFeature(AccountFeature.RetrieveAccountTransactions, messageBuilder.supportsGetTransactions(account))
account.setSupportsFeature(AccountFeature.RetrieveBalance, messageBuilder.supportsGetBalance(account)) account.setSupportsFeature(AccountFeature.RetrieveBalance, messageBuilder.supportsGetBalance(account))
account.setSupportsFeature(AccountFeature.TransferMoney, messageBuilder.supportsBankTransfer(bank, account)) account.setSupportsFeature(AccountFeature.TransferMoney, messageBuilder.supportsBankTransfer(bank, account))
account.setSupportsFeature(AccountFeature.InstantPayment, messageBuilder.supportsSepaInstantPaymentBankTransfer(bank, account)) account.setSupportsFeature(AccountFeature.RealTimeTransfer, messageBuilder.supportsSepaRealTimeTransfer(bank, account))
} }
protected open fun mapToTanMethods(tanInfo: TanInfo): List<TanMethod> { protected open fun mapToTanMethods(tanInfo: TanInfo): List<TanMethod> {

View File

@ -289,7 +289,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
open fun createBankTransferMessage(data: BankTransferData, account: AccountData, dialogContext: DialogContext): MessageBuilderResult { open fun createBankTransferMessage(data: BankTransferData, account: AccountData, dialogContext: DialogContext): MessageBuilderResult {
val segmentId = if (data.instantPayment) CustomerSegmentId.SepaInstantPaymentBankTransfer else CustomerSegmentId.SepaBankTransfer val segmentId = if (data.realTimeTransfer) CustomerSegmentId.SepaRealTimeTransfer else CustomerSegmentId.SepaBankTransfer
val (result, urn) = supportsBankTransferAndSepaVersion(dialogContext.bank, account, segmentId) val (result, urn) = supportsBankTransferAndSepaVersion(dialogContext.bank, account, segmentId)
@ -309,8 +309,8 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
return supportsBankTransferAndSepaVersion(bank, account, CustomerSegmentId.SepaBankTransfer).first.isJobVersionSupported return supportsBankTransferAndSepaVersion(bank, account, CustomerSegmentId.SepaBankTransfer).first.isJobVersionSupported
} }
open fun supportsSepaInstantPaymentBankTransfer(bank: BankData, account: AccountData): Boolean { open fun supportsSepaRealTimeTransfer(bank: BankData, account: AccountData): Boolean {
return supportsBankTransferAndSepaVersion(bank, account, CustomerSegmentId.SepaInstantPaymentBankTransfer).first.isJobVersionSupported return supportsBankTransferAndSepaVersion(bank, account, CustomerSegmentId.SepaRealTimeTransfer).first.isJobVersionSupported
} }
protected open fun supportsBankTransferAndSepaVersion(bank: BankData, account: AccountData, segmentId: CustomerSegmentId): Pair<MessageBuilderResult, String?> { protected open fun supportsBankTransferAndSepaVersion(bank: BankData, account: AccountData, segmentId: CustomerSegmentId): Pair<MessageBuilderResult, String?> {

View File

@ -25,7 +25,7 @@ enum class CustomerSegmentId(override val id: String) : ISegmentId {
SepaBankTransfer("HKCCS"), SepaBankTransfer("HKCCS"),
SepaInstantPaymentBankTransfer("HKIPZ"), SepaRealTimeTransfer("HKIPZ"),
SepaAccountInfoParameters("HKSPA") // not implemented, retrieved automatically with UPD SepaAccountInfoParameters("HKSPA") // not implemented, retrieved automatically with UPD

View File

@ -3,8 +3,8 @@ package net.dankito.banking.fints.messages.segmente.implementierte.sepa
enum class PaymentInformationMessages(val xmlTemplate: String) { enum class PaymentInformationMessages(val xmlTemplate: String) {
Pain_001_001_03("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Document xmlns=\"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03 pain.001.001.03.xsd\"><CstmrCdtTrfInitn><GrpHdr><MsgId>\$MessageId\$</MsgId><CreDtTm>\$CreationDateTime\$</CreDtTm><NbOfTxs>\$NumberOfTransactions\$</NbOfTxs><CtrlSum>\$Amount\$</CtrlSum><InitgPty><Nm>\$DebitorName\$</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>\$PaymentInformationId\$</PmtInfId><PmtMtd>TRF</PmtMtd><NbOfTxs>\$NumberOfTransactions\$</NbOfTxs><CtrlSum>\$Amount\$</CtrlSum><PmtTpInf><SvcLvl><Cd>SEPA</Cd></SvcLvl></PmtTpInf><ReqdExctnDt>\$RequestedExecutionDate\$</ReqdExctnDt><Dbtr><Nm>\$DebitorName\$</Nm></Dbtr><DbtrAcct><Id><IBAN>\$DebitorIban\$</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BIC>\$DebitorBic\$</BIC></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><EndToEndId>NOTPROVIDED</EndToEndId></PmtId><Amt><InstdAmt Ccy=\"EUR\">\$Amount\$</InstdAmt></Amt><CdtrAgt><FinInstnId><BIC>\$CreditorBic\$</BIC></FinInstnId></CdtrAgt><Cdtr><Nm>\$CreditorName\$</Nm></Cdtr><CdtrAcct><Id><IBAN>\$CreditorIban\$</IBAN></Id></CdtrAcct><RmtInf><Ustrd>\$Usage\$</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>"), Pain_001_001_03("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Document xmlns=\"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03 pain.001.001.03.xsd\"><CstmrCdtTrfInitn><GrpHdr><MsgId>\$MessageId\$</MsgId><CreDtTm>\$CreationDateTime\$</CreDtTm><NbOfTxs>\$NumberOfTransactions\$</NbOfTxs><CtrlSum>\$Amount\$</CtrlSum><InitgPty><Nm>\$DebitorName\$</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>\$PaymentInformationId\$</PmtInfId><PmtMtd>TRF</PmtMtd><NbOfTxs>\$NumberOfTransactions\$</NbOfTxs><CtrlSum>\$Amount\$</CtrlSum><PmtTpInf><SvcLvl><Cd>SEPA</Cd></SvcLvl></PmtTpInf><ReqdExctnDt>\$RequestedExecutionDate\$</ReqdExctnDt><Dbtr><Nm>\$DebitorName\$</Nm></Dbtr><DbtrAcct><Id><IBAN>\$DebitorIban\$</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BIC>\$DebitorBic\$</BIC></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><EndToEndId>NOTPROVIDED</EndToEndId></PmtId><Amt><InstdAmt Ccy=\"EUR\">\$Amount\$</InstdAmt></Amt><CdtrAgt><FinInstnId><BIC>\$RecipientBic\$</BIC></FinInstnId></CdtrAgt><Cdtr><Nm>\$RecipientName\$</Nm></Cdtr><CdtrAcct><Id><IBAN>\$RecipientIban\$</IBAN></Id></CdtrAcct><RmtInf><Ustrd>\$Reference\$</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>"),
Pain_001_003_03("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Document xmlns=\"urn:iso:std:iso:20022:tech:xsd:pain.001.003.03\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:iso:std:iso:20022:tech:xsd:pain.001.003.03 pain.001.003.03.xsd\"><CstmrCdtTrfInitn><GrpHdr><MsgId>\$MessageId\$</MsgId><CreDtTm>\$CreationDateTime\$</CreDtTm><NbOfTxs>\$NumberOfTransactions\$</NbOfTxs><InitgPty><Nm>\$DebitorName\$</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>\$PaymentInformationId\$</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>true</BtchBookg><NbOfTxs>\$NumberOfTransactions\$</NbOfTxs><CtrlSum>\$Amount\$</CtrlSum><PmtTpInf><SvcLvl><Cd>SEPA</Cd></SvcLvl></PmtTpInf><ReqdExctnDt>\$RequestedExecutionDate\$</ReqdExctnDt><Dbtr><Nm>\$DebitorName\$</Nm></Dbtr><DbtrAcct><Id><IBAN>\$DebitorIban\$</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BIC>\$DebitorBic\$</BIC></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><EndToEndId>NOTPROVIDED</EndToEndId></PmtId><Amt><InstdAmt Ccy=\"EUR\">\$Amount\$</InstdAmt></Amt><CdtrAgt><FinInstnId><BIC>\$CreditorBic\$</BIC></FinInstnId></CdtrAgt><Cdtr><Nm>\$CreditorName\$</Nm></Cdtr><CdtrAcct><Id><IBAN>\$CreditorIban\$</IBAN></Id></CdtrAcct><RmtInf><Ustrd>\$Usage\$</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>") Pain_001_003_03("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Document xmlns=\"urn:iso:std:iso:20022:tech:xsd:pain.001.003.03\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:iso:std:iso:20022:tech:xsd:pain.001.003.03 pain.001.003.03.xsd\"><CstmrCdtTrfInitn><GrpHdr><MsgId>\$MessageId\$</MsgId><CreDtTm>\$CreationDateTime\$</CreDtTm><NbOfTxs>\$NumberOfTransactions\$</NbOfTxs><InitgPty><Nm>\$DebitorName\$</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>\$PaymentInformationId\$</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>true</BtchBookg><NbOfTxs>\$NumberOfTransactions\$</NbOfTxs><CtrlSum>\$Amount\$</CtrlSum><PmtTpInf><SvcLvl><Cd>SEPA</Cd></SvcLvl></PmtTpInf><ReqdExctnDt>\$RequestedExecutionDate\$</ReqdExctnDt><Dbtr><Nm>\$DebitorName\$</Nm></Dbtr><DbtrAcct><Id><IBAN>\$DebitorIban\$</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BIC>\$DebitorBic\$</BIC></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><EndToEndId>NOTPROVIDED</EndToEndId></PmtId><Amt><InstdAmt Ccy=\"EUR\">\$Amount\$</InstdAmt></Amt><CdtrAgt><FinInstnId><BIC>\$RecipientBic\$</BIC></FinInstnId></CdtrAgt><Cdtr><Nm>\$RecipientName\$</Nm></Cdtr><CdtrAcct><Id><IBAN>\$RecipientIban\$</IBAN></Id></CdtrAcct><RmtInf><Ustrd>\$Reference\$</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>")
} }

View File

@ -28,11 +28,11 @@ open class SepaBankTransferBase(
"DebitorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(debitorName), "DebitorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(debitorName),
"DebitorIban" to account.iban!!, "DebitorIban" to account.iban!!,
"DebitorBic" to debitorBic, "DebitorBic" to debitorBic,
"CreditorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(data.creditorName), "RecipientName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(data.recipientName),
"CreditorIban" to data.creditorIban.replace(" ", ""), "RecipientIban" to data.recipientAccountId.replace(" ", ""),
"CreditorBic" to data.creditorBic.replace(" ", ""), "RecipientBic" to data.recipientBankCode.replace(" ", ""),
"Amount" to data.amount.amount.string.replace(',', '.'), // TODO: check if ',' or '.' should be used as decimal separator "Amount" to data.amount.amount.string.replace(',', '.'), // TODO: check if ',' or '.' should be used as decimal separator
"Usage" to if (data.usage.isEmpty()) " " else messageCreator.convertDiacriticsAndReservedXmlCharacters(data.usage), "Reference" to if (data.reference.isEmpty()) " " else messageCreator.convertDiacriticsAndReservedXmlCharacters(data.reference),
"RequestedExecutionDate" to RequestedExecutionDateValueForNotScheduledTransfers "RequestedExecutionDate" to RequestedExecutionDateValueForNotScheduledTransfers
), ),
messageCreator messageCreator

View File

@ -44,8 +44,8 @@ open class AccountData(
open val supportsTransferringMoney: Boolean open val supportsTransferringMoney: Boolean
get() = supportsFeature(AccountFeature.TransferMoney) get() = supportsFeature(AccountFeature.TransferMoney)
open val supportsInstantPaymentMoneyTransfer: Boolean open val supportsRealTimeTransfer: Boolean
get() = supportsFeature(AccountFeature.InstantPayment) get() = supportsFeature(AccountFeature.RealTimeTransfer)
open fun supportsFeature(feature: AccountFeature): Boolean { open fun supportsFeature(feature: AccountFeature): Boolean {

View File

@ -9,6 +9,6 @@ enum class AccountFeature {
TransferMoney, TransferMoney,
InstantPayment RealTimeTransfer
} }

View File

@ -7,7 +7,7 @@ open class AccountTransaction(
val account: AccountData, val account: AccountData,
val amount: Money, val amount: Money,
val isReversal: Boolean, val isReversal: Boolean,
val unparsedUsage: String, val unparsedReference: String,
val bookingDate: Date, val bookingDate: Date,
val otherPartyName: String?, val otherPartyName: String?,
val otherPartyBankCode: String?, val otherPartyBankCode: String?,
@ -26,10 +26,10 @@ open class AccountTransaction(
val originatorsIdentificationCode: String?, val originatorsIdentificationCode: String?,
val compensationAmount: String?, val compensationAmount: String?,
val originalAmount: String?, val originalAmount: String?,
val sepaUsage: String?, val sepaReference: String?,
val deviantOriginator: String?, val deviantOriginator: String?,
val deviantRecipient: String?, val deviantRecipient: String?,
val usageWithNoSpecialType: String?, val referenceWithNoSpecialType: String?,
val primaNotaNumber: String?, val primaNotaNumber: String?,
val textKeySupplement: String?, val textKeySupplement: String?,
@ -46,20 +46,24 @@ open class AccountTransaction(
// for object deserializers // for object deserializers
internal constructor() : this(AccountData(), Money(Amount.Zero, ""), "", Date(0), null, null, null, null, Date(0)) internal constructor() : this(AccountData(), Money(Amount.Zero, ""), "", Date(0), null, null, null, null, Date(0))
constructor(account: AccountData, amount: Money, unparsedUsage: String, bookingDate: Date, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, bookingText: String?, valueDate: Date) constructor(account: AccountData, amount: Money, unparsedReference: String, bookingDate: Date, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, bookingText: String?, valueDate: Date)
: this(account, amount, false, unparsedUsage, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, : this(account, amount, false, unparsedReference, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate,
0, null, null, null, 0, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, "", "", null, null, "", null) null, "", "", null, null, "", null)
val reference: String
get() = sepaReference ?: unparsedReference
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is AccountTransaction) return false if (other !is AccountTransaction) return false
if (account != other.account) return false if (account != other.account) return false
if (amount != other.amount) return false if (amount != other.amount) return false
if (unparsedUsage != other.unparsedUsage) return false if (unparsedReference != other.unparsedReference) return false
if (bookingDate != other.bookingDate) return false if (bookingDate != other.bookingDate) return false
if (otherPartyName != other.otherPartyName) return false if (otherPartyName != other.otherPartyName) return false
if (otherPartyBankCode != other.otherPartyBankCode) return false if (otherPartyBankCode != other.otherPartyBankCode) return false
@ -73,7 +77,7 @@ open class AccountTransaction(
override fun hashCode(): Int { override fun hashCode(): Int {
var result = account.hashCode() var result = account.hashCode()
result = 31 * result + amount.hashCode() result = 31 * result + amount.hashCode()
result = 31 * result + unparsedUsage.hashCode() result = 31 * result + unparsedReference.hashCode()
result = 31 * result + bookingDate.hashCode() result = 31 * result + bookingDate.hashCode()
result = 31 * result + (otherPartyName?.hashCode() ?: 0) result = 31 * result + (otherPartyName?.hashCode() ?: 0)
result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0) result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0)
@ -85,7 +89,7 @@ open class AccountTransaction(
override fun toString(): String { override fun toString(): String {
return "$valueDate $amount $otherPartyName: $unparsedUsage" return "$valueDate $amount $otherPartyName: $unparsedReference"
} }
} }

View File

@ -2,10 +2,10 @@ package net.dankito.banking.fints.model
open class BankTransferData( open class BankTransferData(
val creditorName: String, val recipientName: String,
val creditorIban: String, val recipientAccountId: String,
val creditorBic: String, val recipientBankCode: String,
val amount: Money, val amount: Money,
val usage: String, val reference: String,
val instantPayment: Boolean = false val realTimeTransfer: Boolean = false
) )

View File

@ -306,7 +306,7 @@ open class ResponseParser(
parseBoolean(parametersDataElements[1]), parseBoolean(parametersDataElements[1]),
parseBoolean(parametersDataElements[2]), parseBoolean(parametersDataElements[2]),
if (segmentVersion >= 2) parseBoolean(parametersDataElements[3]) else false, if (segmentVersion >= 2) parseBoolean(parametersDataElements[3]) else false,
if (segmentVersion >= 3) parseInt(parametersDataElements[4]) else SepaAccountInfoParameters.CountReservedUsageLengthNotSet, if (segmentVersion >= 3) parseInt(parametersDataElements[4]) else SepaAccountInfoParameters.CountReservedReferenceLengthNotSet,
parametersDataElements.subList(supportedSepaFormatsBeginIndex, parametersDataElements.size) parametersDataElements.subList(supportedSepaFormatsBeginIndex, parametersDataElements.size)
) )
} }

View File

@ -29,7 +29,7 @@ open class SepaAccountInfoParameters(
* Über diese Information legt das Kreditinstitut fest, ob bei SEPA-Zahlungsverkehrsinstrumenten die Verwendung * Über diese Information legt das Kreditinstitut fest, ob bei SEPA-Zahlungsverkehrsinstrumenten die Verwendung
* von strukturierten Verwendungszweckinformationen (StructuredRemittanceInformation) erlaubt ist oder nicht. * von strukturierten Verwendungszweckinformationen (StructuredRemittanceInformation) erlaubt ist oder nicht.
*/ */
val structuredUsageAllowed: Boolean, val structuredReferenceAllowed: Boolean,
/** /**
* Kennzeichen dafür, ob die Belegung des Feldes Maximale Anzahl Einträge im Kundenauftrag zugelassen ist. * Kennzeichen dafür, ob die Belegung des Feldes Maximale Anzahl Einträge im Kundenauftrag zugelassen ist.
@ -47,7 +47,7 @@ open class SepaAccountInfoParameters(
* reserviert sind. Diese Stellen dürfen vom Kundenprodukt nicht für andere Zwecke verwendet werden. Die Anzahl * reserviert sind. Diese Stellen dürfen vom Kundenprodukt nicht für andere Zwecke verwendet werden. Die Anzahl
* wird vom Ende des letzten SEPA-Elementes aus gezählt und darf den Wert 35 nicht überschreiten. * wird vom Ende des letzten SEPA-Elementes aus gezählt und darf den Wert 35 nicht überschreiten.
*/ */
val countReservedUsageLength: Int, val countReservedReferenceLength: Int,
/** /**
* Dieses DE beschreibt Ort, Name und Version einer SEPA pain message als URN. Die korrekte Bezeichnung des URN * Dieses DE beschreibt Ort, Name und Version einer SEPA pain message als URN. Die korrekte Bezeichnung des URN
@ -69,7 +69,7 @@ open class SepaAccountInfoParameters(
companion object { companion object {
const val CountReservedUsageLengthNotSet = 0 const val CountReservedReferenceLengthNotSet = 0
} }
} }

View File

@ -51,7 +51,7 @@ open class Mt940AccountTransactionsParser(
account, account,
Money(mapAmount(transaction.statementLine), currency), Money(mapAmount(transaction.statementLine), currency),
transaction.statementLine.isReversal, transaction.statementLine.isReversal,
transaction.information?.unparsedUsage ?: "", transaction.information?.unparsedReference ?: "",
transaction.statementLine.bookingDate ?: statement.closingBalance.bookingDate, transaction.statementLine.bookingDate ?: statement.closingBalance.bookingDate,
transaction.information?.otherPartyName, transaction.information?.otherPartyName,
transaction.information?.otherPartyBankCode, transaction.information?.otherPartyBankCode,
@ -70,10 +70,10 @@ open class Mt940AccountTransactionsParser(
transaction.information?.originatorsIdentificationCode, transaction.information?.originatorsIdentificationCode,
transaction.information?.compensationAmount, transaction.information?.compensationAmount,
transaction.information?.originalAmount, transaction.information?.originalAmount,
transaction.information?.sepaUsage, transaction.information?.sepaReference,
transaction.information?.deviantOriginator, transaction.information?.deviantOriginator,
transaction.information?.deviantRecipient, transaction.information?.deviantRecipient,
transaction.information?.usageWithNoSpecialType, transaction.information?.referenceWithNoSpecialType,
transaction.information?.primaNotaNumber, transaction.information?.primaNotaNumber,
transaction.information?.textKeySupplement, transaction.information?.textKeySupplement,

View File

@ -56,19 +56,19 @@ open class Mt940Parser : IMt940Parser {
val AmountRegex = Regex("\\d+,\\d*") val AmountRegex = Regex("\\d+,\\d*")
val UsageTypeRegex = Regex("[A-Z]{4}\\+") val ReferenceTypeRegex = Regex("[A-Z]{4}\\+")
const val EndToEndReferenceUsageKey = "EREF+" const val EndToEndReferenceKey = "EREF+"
const val CustomerReferenceUsageKey = "KREF+" const val CustomerReferenceKey = "KREF+"
const val MandateReferenceUsageKey = "MREF+" const val MandateReferenceKey = "MREF+"
const val CreditorIdentifierUsageKey = "CRED+" const val CreditorIdentifierKey = "CRED+"
const val OriginatorsIdentificationCodeUsageKey = "DEBT+" const val OriginatorsIdentificationCodeKey = "DEBT+"
const val CompensationAmountUsageKey = "COAM+" const val CompensationAmountKey = "COAM+"
const val OriginalAmountUsageKey = "OAMT+" const val OriginalAmountKey = "OAMT+"
const val SepaUsageUsageKey = "SVWZ+" const val SepaReferenceKey = "SVWZ+"
const val DeviantOriginatorUsageKey = "ABWA+" const val DeviantOriginatorKey = "ABWA+"
const val DeviantRecipientUsageKey = "ABWE+" const val DeviantRecipientKey = "ABWE+"
private val log = LoggerFactory.getLogger(Mt940Parser::class) private val log = LoggerFactory.getLogger(Mt940Parser::class)
@ -295,7 +295,7 @@ open class Mt940Parser : IMt940Parser {
try { try {
val information = parseInformationToAccountOwner(informationToAccountOwnerString) val information = parseInformationToAccountOwner(informationToAccountOwnerString)
mapUsage(information) mapReference(information)
return information return information
} catch (e: Exception) { } catch (e: Exception) {
@ -310,7 +310,7 @@ open class Mt940Parser : IMt940Parser {
// see Finanzdatenformate p. 209 - 215 // see Finanzdatenformate p. 209 - 215
val geschaeftsvorfallCode = informationToAccountOwnerString.substring(0, 2) // TODO: may map val geschaeftsvorfallCode = informationToAccountOwnerString.substring(0, 2) // TODO: may map
val usageParts = mutableListOf<String>() val referenceParts = mutableListOf<String>()
val otherPartyName = StringBuilder() val otherPartyName = StringBuilder()
var otherPartyBankCode: String? = null var otherPartyBankCode: String? = null
var otherPartyAccountId: String? = null var otherPartyAccountId: String? = null
@ -326,48 +326,48 @@ open class Mt940Parser : IMt940Parser {
when (fieldCode) { when (fieldCode) {
0 -> bookingText = fieldValue 0 -> bookingText = fieldValue
10 -> primaNotaNumber = fieldValue 10 -> primaNotaNumber = fieldValue
in 20..29 -> usageParts.add(fieldValue) in 20..29 -> referenceParts.add(fieldValue)
30 -> otherPartyBankCode = fieldValue 30 -> otherPartyBankCode = fieldValue
31 -> otherPartyAccountId = fieldValue 31 -> otherPartyAccountId = fieldValue
32, 33 -> otherPartyName.append(fieldValue) 32, 33 -> otherPartyName.append(fieldValue)
34 -> textKeySupplement = fieldValue 34 -> textKeySupplement = fieldValue
in 60..63 -> usageParts.add(fieldValue) in 60..63 -> referenceParts.add(fieldValue)
} }
} }
} }
val usage = if (isFormattedUsage(usageParts)) joinUsageParts(usageParts) val reference = if (isFormattedReference(referenceParts)) joinReferenceParts(referenceParts)
else usageParts.joinToString(" ") else referenceParts.joinToString(" ")
val otherPartyNameString = if (otherPartyName.isEmpty()) null else otherPartyName.toString() val otherPartyNameString = if (otherPartyName.isEmpty()) null else otherPartyName.toString()
return InformationToAccountOwner( return InformationToAccountOwner(
usage, otherPartyNameString, otherPartyBankCode, otherPartyAccountId, reference, otherPartyNameString, otherPartyBankCode, otherPartyAccountId,
bookingText, primaNotaNumber, textKeySupplement bookingText, primaNotaNumber, textKeySupplement
) )
} }
protected open fun joinUsageParts(usageParts: List<String>): String { protected open fun joinReferenceParts(referenceParts: List<String>): String {
val usage = StringBuilder() val reference = StringBuilder()
usageParts.firstOrNull()?.let { referenceParts.firstOrNull()?.let {
usage.append(it) reference.append(it)
} }
for (i in 1..usageParts.size - 1) { for (i in 1..referenceParts.size - 1) {
val part = usageParts[i] val part = referenceParts[i]
if (part.isNotEmpty() && part.first().isUpperCase && usageParts[i - 1].last().isUpperCase == false) { if (part.isNotEmpty() && part.first().isUpperCase && referenceParts[i - 1].last().isUpperCase == false) {
usage.append(" ") reference.append(" ")
} }
usage.append(part) reference.append(part)
} }
return usage.toString() return reference.toString()
} }
protected open fun isFormattedUsage(usageParts: List<String>): Boolean { protected open fun isFormattedReference(referenceParts: List<String>): Boolean {
return usageParts.any { UsageTypeRegex.find(it) != null } return referenceParts.any { ReferenceTypeRegex.find(it) != null }
} }
/** /**
@ -392,53 +392,54 @@ open class Mt940Parser : IMt940Parser {
* *
* Weitere 4 Verwendungszwecke können zu den Feldschlüsseln 60 bis 63 eingestellt werden. * Weitere 4 Verwendungszwecke können zu den Feldschlüsseln 60 bis 63 eingestellt werden.
*/ */
protected open fun mapUsage(information: InformationToAccountOwner) { protected open fun mapReference(information: InformationToAccountOwner) {
val usageParts = getUsageParts(information.unparsedUsage) val referenceParts = getReferenceParts(information.unparsedReference)
usageParts.forEach { entry -> referenceParts.forEach { entry ->
setUsageLineValue(information, entry.key, entry.value) setReferenceLineValue(information, entry.key, entry.value)
} }
} }
open fun getUsageParts(unparsedUsage: String): Map<String, String> { open fun getReferenceParts(unparsedReference: String): Map<String, String> {
var previousMatchType = "" var previousMatchType = ""
var previousMatchEnd = 0 var previousMatchEnd = 0
val usageParts = mutableMapOf<String, String>() val referenceParts = mutableMapOf<String, String>()
UsageTypeRegex.findAll(unparsedUsage).forEach { matchResult -> ReferenceTypeRegex.findAll(unparsedReference).forEach { matchResult ->
if (previousMatchEnd > 0) { if (previousMatchEnd > 0) {
val typeValue = unparsedUsage.substring(previousMatchEnd, matchResult.range.first) val typeValue = unparsedReference.substring(previousMatchEnd, matchResult.range.first)
usageParts[previousMatchType] = typeValue referenceParts[previousMatchType] = typeValue
} }
previousMatchType = unparsedUsage.substring(matchResult.range) previousMatchType = unparsedReference.substring(matchResult.range)
previousMatchEnd = matchResult.range.last + 1 previousMatchEnd = matchResult.range.last + 1
} }
if (previousMatchEnd > 0) { if (previousMatchEnd > 0) {
val typeValue = unparsedUsage.substring(previousMatchEnd, unparsedUsage.length) val typeValue = unparsedReference.substring(previousMatchEnd, unparsedReference.length)
usageParts[previousMatchType] = typeValue referenceParts[previousMatchType] = typeValue
} }
return usageParts return referenceParts
} }
protected open fun setUsageLineValue(information: InformationToAccountOwner, usageType: String, typeValue: String) { // TODO: there are more. See .pdf from Deutsche Bank
when (usageType) { protected open fun setReferenceLineValue(information: InformationToAccountOwner, referenceType: String, typeValue: String) {
EndToEndReferenceUsageKey -> information.endToEndReference = typeValue when (referenceType) {
CustomerReferenceUsageKey -> information.customerReference = typeValue EndToEndReferenceKey -> information.endToEndReference = typeValue
MandateReferenceUsageKey -> information.mandateReference = typeValue CustomerReferenceKey -> information.customerReference = typeValue
CreditorIdentifierUsageKey -> information.creditorIdentifier = typeValue MandateReferenceKey -> information.mandateReference = typeValue
OriginatorsIdentificationCodeUsageKey -> information.originatorsIdentificationCode = typeValue CreditorIdentifierKey -> information.creditorIdentifier = typeValue
CompensationAmountUsageKey -> information.compensationAmount = typeValue OriginatorsIdentificationCodeKey -> information.originatorsIdentificationCode = typeValue
OriginalAmountUsageKey -> information.originalAmount = typeValue CompensationAmountKey -> information.compensationAmount = typeValue
SepaUsageUsageKey -> information.sepaUsage = typeValue OriginalAmountKey -> information.originalAmount = typeValue
DeviantOriginatorUsageKey -> information.deviantOriginator = typeValue SepaReferenceKey -> information.sepaReference = typeValue
DeviantRecipientUsageKey -> information.deviantRecipient = typeValue DeviantOriginatorKey -> information.deviantOriginator = typeValue
else -> information.usageWithNoSpecialType = typeValue DeviantRecipientKey -> information.deviantRecipient = typeValue
else -> information.referenceWithNoSpecialType = typeValue
} }
} }

View File

@ -2,7 +2,7 @@ package net.dankito.banking.fints.transactions.mt940.model
open class InformationToAccountOwner( open class InformationToAccountOwner(
val unparsedUsage: String, val unparsedReference: String,
val otherPartyName: String?, val otherPartyName: String?,
val otherPartyBankCode: String?, val otherPartyBankCode: String?,
val otherPartyAccountId: String?, val otherPartyAccountId: String?,
@ -25,17 +25,17 @@ open class InformationToAccountOwner(
var originalAmount: String? = null var originalAmount: String? = null
var sepaUsage: String? = null var sepaReference: String? = null
var deviantOriginator: String? = null var deviantOriginator: String? = null
var deviantRecipient: String? = null var deviantRecipient: String? = null
var usageWithNoSpecialType: String? = null var referenceWithNoSpecialType: String? = null
override fun toString(): String { override fun toString(): String {
return "$otherPartyName $unparsedUsage" return "$otherPartyName $unparsedReference"
} }
} }

View File

@ -15,11 +15,11 @@ class SepaBankTransferBaseTest {
val debitorName = "Nelson Mandela" val debitorName = "Nelson Mandela"
val debitorIban = "ZA123456780987654321" val debitorIban = "ZA123456780987654321"
val debitorBic = "ABCDZAEFXXX" val debitorBic = "ABCDZAEFXXX"
val creditorName = "Mahatma Gandhi" val recipientName = "Mahatma Gandhi"
val creditorIban = "IN123456780987654321" val recipientIban = "IN123456780987654321"
val creditorBic = "ABCDINEFXXX" val recipientBic = "ABCDINEFXXX"
val amount = Amount("1234,56") val amount = Amount("1234,56")
val usage = "What should Mahatma Gandhi want with money?" val reference = "What should Mahatma Gandhi want with money?"
} }
@ -33,7 +33,7 @@ class SepaBankTransferBaseTest {
debitorName, debitorName,
AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()), AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()),
debitorBic, debitorBic,
BankTransferData(creditorName, creditorIban, creditorBic, Money(amount, "EUR"), usage) BankTransferData(recipientName, recipientIban, recipientBic, Money(amount, "EUR"), reference)
) )
@ -42,8 +42,8 @@ class SepaBankTransferBaseTest {
// then // then
expect(result).contains(debitorName, debitorIban, debitorBic, creditorName, creditorIban, creditorBic, expect(result).contains(debitorName, debitorIban, debitorBic, recipientName, recipientIban, recipientBic,
amount.toString().replace(',', '.'), usage, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03") amount.toString().replace(',', '.'), reference, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03")
} }
@Test @Test
@ -55,7 +55,7 @@ class SepaBankTransferBaseTest {
debitorName, debitorName,
AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()), AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()),
debitorBic, debitorBic,
BankTransferData(creditorName, creditorIban, creditorBic, Money(amount, "EUR"), usage) BankTransferData(recipientName, recipientIban, recipientBic, Money(amount, "EUR"), reference)
) )
@ -64,8 +64,8 @@ class SepaBankTransferBaseTest {
// then // then
expect(result).contains(debitorName, debitorIban, debitorBic, creditorName, creditorIban, creditorBic, expect(result).contains(debitorName, debitorIban, debitorBic, recipientName, recipientIban, recipientBic,
amount.toString().replace(',', '.'), usage, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03") amount.toString().replace(',', '.'), reference, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03")
} }
} }

View File

@ -558,9 +558,9 @@ class ResponseParserTest : FinTsTestBase() {
for (segment in sepaAccountInfoParameters) { for (segment in sepaAccountInfoParameters) {
expect(segment.retrieveSingleAccountAllowed).isTrue() expect(segment.retrieveSingleAccountAllowed).isTrue()
expect(segment.nationalAccountRelationshipAllowed).isFalse() expect(segment.nationalAccountRelationshipAllowed).isFalse()
expect(segment.structuredUsageAllowed).isFalse() expect(segment.structuredReferenceAllowed).isFalse()
expect(segment.settingMaxAllowedEntriesAllowed).isFalse() expect(segment.settingMaxAllowedEntriesAllowed).isFalse()
expect(segment.countReservedUsageLength).toBe(SepaAccountInfoParameters.CountReservedUsageLengthNotSet) expect(segment.countReservedReferenceLength).toBe(SepaAccountInfoParameters.CountReservedReferenceLengthNotSet)
expect(segment.supportedSepaFormats).containsExactly( expect(segment.supportedSepaFormats).containsExactly(
"sepade.pain.001.001.02.xsd", "sepade.pain.001.001.02.xsd",
"sepade.pain.001.002.02.xsd", "sepade.pain.001.002.02.xsd",

View File

@ -269,9 +269,9 @@ class Mt940ParserTest : FinTsTestBase() {
result.flatMap { it.transactions }.forEach { transaction -> result.flatMap { it.transactions }.forEach { transaction ->
expect(transaction.information).notToBeNull() expect(transaction.information).notToBeNull()
expect(transaction.information?.sepaUsage).notToBeNull() expect(transaction.information?.sepaReference).notToBeNull()
if (transaction.information?.unparsedUsage?.contains("KREF+") == true) { if (transaction.information?.unparsedReference?.contains("KREF+") == true) {
expect(transaction.information?.customerReference).notToBeNull() expect(transaction.information?.customerReference).notToBeNull()
} }
} }

View File

@ -44,7 +44,7 @@ open class FinTsClientTestBase {
val Password = "<your PIN (Online-Banking Passwort) here>" val Password = "<your PIN (Online-Banking Passwort) here>"
val DateTimeFormatForUniqueBankTransferUsage = DateFormatter("yyyy-MM-dd'T'HH:mm:ss.SSS") val DateTimeFormatForUniqueBankTransferReference = DateFormatter("yyyy-MM-dd'T'HH:mm:ss.SSS")
} }
@ -234,7 +234,7 @@ open class FinTsClientTestBase {
// transfer 1 cent to yourself. Transferring money to oneself also doesn't require to enter a TAN according to PSD2 // transfer 1 cent to yourself. Transferring money to oneself also doesn't require to enter a TAN according to PSD2
val BankTransferData = BankTransferData(Bank.customerName, account?.iban!!, Bank.bic, Money(Amount("0,01"), "EUR"), val BankTransferData = BankTransferData(Bank.customerName, account?.iban!!, Bank.bic, Money(Amount("0,01"), "EUR"),
"${DateTimeFormatForUniqueBankTransferUsage.format(Date())} Test transaction ${UUID.random()}") "${DateTimeFormatForUniqueBankTransferReference.format(Date())} Test transaction ${UUID.random()}")
// when // when

View File

@ -19,7 +19,7 @@ class LuceneConfig {
const val BookingDateFieldName = "booking_date" const val BookingDateFieldName = "booking_date"
const val DateSortFieldName = "value_date_sort" const val DateSortFieldName = "value_date_sort"
const val UsageFieldName = "usage" const val ReferenceFieldName = "reference"
const val BookingTextFieldName = "booking_text" const val BookingTextFieldName = "booking_text"

View File

@ -13,7 +13,7 @@ import net.dankito.banking.LuceneConfig.Companion.IdFieldName
import net.dankito.banking.LuceneConfig.Companion.OtherPartyAccountIdFieldName import net.dankito.banking.LuceneConfig.Companion.OtherPartyAccountIdFieldName
import net.dankito.banking.LuceneConfig.Companion.OtherPartyBankCodeFieldName import net.dankito.banking.LuceneConfig.Companion.OtherPartyBankCodeFieldName
import net.dankito.banking.LuceneConfig.Companion.OtherPartyNameFieldName import net.dankito.banking.LuceneConfig.Companion.OtherPartyNameFieldName
import net.dankito.banking.LuceneConfig.Companion.UsageFieldName import net.dankito.banking.LuceneConfig.Companion.ReferenceFieldName
import net.dankito.banking.ui.model.* import net.dankito.banking.ui.model.*
import net.dankito.banking.util.ISerializer import net.dankito.banking.util.ISerializer
import net.dankito.banking.util.JacksonJsonSerializer import net.dankito.banking.util.JacksonJsonSerializer
@ -62,7 +62,7 @@ open class LuceneBankingPersistence(
return listOf( return listOf(
fields.keywordField(BankAccountIdFieldName, account.technicalId), fields.keywordField(BankAccountIdFieldName, account.technicalId),
fields.nullableFullTextSearchField(OtherPartyNameFieldName, transaction.otherPartyName, true), fields.nullableFullTextSearchField(OtherPartyNameFieldName, transaction.otherPartyName, true),
fields.fullTextSearchField(UsageFieldName, transaction.usage, true), fields.fullTextSearchField(ReferenceFieldName, transaction.reference, true),
fields.nullableFullTextSearchField(BookingTextFieldName, transaction.bookingText, true), fields.nullableFullTextSearchField(BookingTextFieldName, transaction.bookingText, true),
fields.nullableStoredField(OtherPartyBankCodeFieldName, transaction.otherPartyBankCode), fields.nullableStoredField(OtherPartyBankCodeFieldName, transaction.otherPartyBankCode),

View File

@ -12,14 +12,14 @@ import net.dankito.utils.lucene.search.Searcher
import java.io.File import java.io.File
open class LuceneRemitteeSearcher(indexFolder: File) : IRemitteeSearcher { open class LuceneTransactionPartySearcher(indexFolder: File) : ITransactionPartySearcher {
companion object { companion object {
private val properties = listOf( private val properties = listOf(
PropertyDescription(PropertyType.NullableString, OtherPartyNameFieldName, Remittee::name), PropertyDescription(PropertyType.NullableString, OtherPartyNameFieldName, TransactionParty::name),
PropertyDescription(PropertyType.NullableString, OtherPartyBankCodeFieldName, Remittee::bic), PropertyDescription(PropertyType.NullableString, OtherPartyBankCodeFieldName, TransactionParty::bic),
PropertyDescription(PropertyType.NullableString, OtherPartyAccountIdFieldName, Remittee::iban) PropertyDescription(PropertyType.NullableString, OtherPartyAccountIdFieldName, TransactionParty::iban)
) )
} }
@ -30,16 +30,16 @@ open class LuceneRemitteeSearcher(indexFolder: File) : IRemitteeSearcher {
protected val searcher = Searcher(LuceneConfig.getAccountTransactionsIndexFolder(indexFolder)) protected val searcher = Searcher(LuceneConfig.getAccountTransactionsIndexFolder(indexFolder))
override fun findRemittees(query: String): List<Remittee> { override fun findTransactionParty(query: String): List<TransactionParty> {
val luceneQuery = queries.createQueriesForSingleTerms(query.toLowerCase()) { singleTerm -> val luceneQuery = queries.createQueriesForSingleTerms(query.toLowerCase()) { singleTerm ->
listOf( listOf(
queries.fulltextQuery(OtherPartyNameFieldName, singleTerm) queries.fulltextQuery(OtherPartyNameFieldName, singleTerm)
) )
} }
return searcher.searchAndMap(MappedSearchConfig(luceneQuery, Remittee::class.java, properties)) return searcher.searchAndMap(MappedSearchConfig(luceneQuery, TransactionParty::class.java, properties))
.toSet() // don't display same Remittee multiple times .toSet() // don't display same transaction party multiple times
.filterNot { it.iban.isNullOrBlank() || it.bic.isNullOrBlank() } // e.g. comdirect doesn't supply other party's IBAN and BIC -> filter these as they have no value for auto-entering a remittee's IBAN and BIC .filterNot { it.iban.isNullOrBlank() || it.bic.isNullOrBlank() } // e.g. comdirect doesn't supply other party's IBAN and BIC -> filter these as they have no value for auto-entering a transaction party's IBAN and BIC
} }
} }

View File

@ -19,7 +19,7 @@ import java.util.*
import java.util.concurrent.ThreadLocalRandom import java.util.concurrent.ThreadLocalRandom
class LuceneRemitteeSearcherTest { class LuceneTransactionPartySearcherTest {
companion object { companion object {
@ -49,7 +49,7 @@ class LuceneRemitteeSearcherTest {
private val bankingPersistence = LuceneBankingPersistence(indexFolder, databaseFolder) private val bankingPersistence = LuceneBankingPersistence(indexFolder, databaseFolder)
private val underTest = LuceneRemitteeSearcher(indexFolder) private val underTest = LuceneTransactionPartySearcher(indexFolder)
@Before @Before
@ -68,12 +68,12 @@ class LuceneRemitteeSearcherTest {
@Test @Test
fun findRemittees_ByFullName() { fun findTransactionParty_ByFullName() {
// given // given
val query = OtherPartyName val query = OtherPartyName
val before = underTest.findRemittees(query) val before = underTest.findTransactionParty(query)
assertThat(before).isEmpty() assertThat(before).isEmpty()
bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf( bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf(
@ -84,7 +84,7 @@ class LuceneRemitteeSearcherTest {
// when // when
val result = underTest.findRemittees(query) val result = underTest.findTransactionParty(query)
// then // then
@ -95,12 +95,12 @@ class LuceneRemitteeSearcherTest {
} }
@Test @Test
fun findRemittees_ByPartialName() { fun findTransactionParty_ByPartialName() {
// given // given
val query = "gand" val query = "gand"
val before = underTest.findRemittees(query) val before = underTest.findTransactionParty(query)
assertThat(before).isEmpty() assertThat(before).isEmpty()
bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf( bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf(
@ -111,7 +111,7 @@ class LuceneRemitteeSearcherTest {
// when // when
val result = underTest.findRemittees(query) val result = underTest.findTransactionParty(query)
// then // then
@ -122,13 +122,13 @@ class LuceneRemitteeSearcherTest {
} }
@Test @Test
fun findRemittees_SimilarNames() { fun findTransactionParty_SimilarNames() {
// given // given
val query = "gand" val query = "gand"
val secondOtherPartyName = "Gandalf" val secondOtherPartyName = "Gandalf"
val before = underTest.findRemittees(query) val before = underTest.findTransactionParty(query)
assertThat(before).isEmpty() assertThat(before).isEmpty()
bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf( bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf(
@ -139,7 +139,7 @@ class LuceneRemitteeSearcherTest {
// when // when
val result = underTest.findRemittees(query) val result = underTest.findTransactionParty(query)
// then // then
@ -148,12 +148,12 @@ class LuceneRemitteeSearcherTest {
} }
@Test @Test
fun findRemittees_DuplicateEntries() { fun findTransactionParty_DuplicateEntries() {
// given // given
val query = OtherPartyName val query = OtherPartyName
val before = underTest.findRemittees(query) val before = underTest.findTransactionParty(query)
assertThat(before).isEmpty() assertThat(before).isEmpty()
bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf( bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf(
@ -166,7 +166,7 @@ class LuceneRemitteeSearcherTest {
// when // when
val result = underTest.findRemittees(query) val result = underTest.findTransactionParty(query)
// then // then
@ -177,12 +177,12 @@ class LuceneRemitteeSearcherTest {
} }
@Test @Test
fun findRemittees_OtherName() { fun findTransactionParty_OtherName() {
// given // given
val query = "Mandela" val query = "Mandela"
val before = underTest.findRemittees(query) val before = underTest.findTransactionParty(query)
assertThat(before).isEmpty() assertThat(before).isEmpty()
bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf( bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf(
@ -193,7 +193,7 @@ class LuceneRemitteeSearcherTest {
// when // when
val result = underTest.findRemittees(query) val result = underTest.findTransactionParty(query)
// then // then
@ -203,17 +203,17 @@ class LuceneRemitteeSearcherTest {
private fun createTransaction(bankAccount: BankAccount = bankAccountMock, bookingDate: String, amount: BigDecimal = randomBigDecimal(), private fun createTransaction(bankAccount: BankAccount = bankAccountMock, bookingDate: String, amount: BigDecimal = randomBigDecimal(),
otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(), otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(),
otherPartyAccountId: String = randomString(), usage: String = randomString()): AccountTransaction { otherPartyAccountId: String = randomString(), reference: String = randomString()): AccountTransaction {
return createTransaction(bankAccount, dateFormat.parse(bookingDate), amount, otherPartyName, return createTransaction(bankAccount, dateFormat.parse(bookingDate), amount, otherPartyName,
otherPartyBankCode, otherPartyAccountId, usage) otherPartyBankCode, otherPartyAccountId, reference)
} }
private fun createTransaction(bankAccount: BankAccount = bankAccountMock, bookingDate: Date = randomDate(), amount: BigDecimal = randomBigDecimal(), private fun createTransaction(bankAccount: BankAccount = bankAccountMock, bookingDate: Date = randomDate(), amount: BigDecimal = randomBigDecimal(),
otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(), otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(),
otherPartyAccountId: String = randomString(), usage: String = randomString()): AccountTransaction { otherPartyAccountId: String = randomString(), reference: String = randomString()): AccountTransaction {
return AccountTransaction(bankAccount, amount.toBigDecimal(), "EUR", usage, bookingDate.toDate(), otherPartyName, otherPartyBankCode, otherPartyAccountId, null, bookingDate.toDate()) return AccountTransaction(bankAccount, amount.toBigDecimal(), "EUR", reference, bookingDate.toDate(), otherPartyName, otherPartyBankCode, otherPartyAccountId, null, bookingDate.toDate())
} }
private fun randomString(): String { private fun randomString(): String {

View File

@ -5,8 +5,8 @@ import androidx.room.Room
import net.dankito.banking.persistence.dao.BaseDao import net.dankito.banking.persistence.dao.BaseDao
import net.dankito.banking.persistence.dao.saveOrUpdate import net.dankito.banking.persistence.dao.saveOrUpdate
import net.dankito.banking.persistence.model.* import net.dankito.banking.persistence.model.*
import net.dankito.banking.search.IRemitteeSearcher import net.dankito.banking.search.ITransactionPartySearcher
import net.dankito.banking.search.Remittee import net.dankito.banking.search.TransactionParty
import net.dankito.banking.ui.model.IAccountTransaction import net.dankito.banking.ui.model.IAccountTransaction
import net.dankito.banking.ui.model.TypedBankAccount import net.dankito.banking.ui.model.TypedBankAccount
import net.dankito.banking.ui.model.TypedBankData import net.dankito.banking.ui.model.TypedBankData
@ -18,7 +18,7 @@ import net.sqlcipher.database.SQLiteDatabase
import net.sqlcipher.database.SupportFactory import net.sqlcipher.database.SupportFactory
open class RoomBankingPersistence(applicationContext: Context, password: String? = null) : IBankingPersistence, IRemitteeSearcher { open class RoomBankingPersistence(applicationContext: Context, password: String? = null) : IBankingPersistence, ITransactionPartySearcher {
protected val db: BankingDatabase protected val db: BankingDatabase
@ -154,11 +154,11 @@ open class RoomBankingPersistence(applicationContext: Context, password: String?
} }
override fun findRemittees(query: String): List<Remittee> { override fun findTransactionParty(query: String): List<TransactionParty> {
return db.accountTransactionDao().findRemittees(query) return db.accountTransactionDao().findTransactionParty(query)
.toSet() // don't display same Remittee multiple times .toSet() // don't display same transaction party multiple times
.filterNot { it.bankCode.isNullOrBlank() || it.accountId.isNullOrBlank() } .filterNot { it.bankCode.isNullOrBlank() || it.accountId.isNullOrBlank() }
.map { Remittee(it.name, it.accountId, it.bankCode) } .map { TransactionParty(it.name, it.accountId, it.bankCode) }
} }
} }

View File

@ -3,7 +3,7 @@ package net.dankito.banking.persistence.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import net.dankito.banking.persistence.model.AccountTransaction import net.dankito.banking.persistence.model.AccountTransaction
import net.dankito.banking.persistence.model.Remittee import net.dankito.banking.persistence.model.TransactionParty
@Dao @Dao
@ -13,6 +13,6 @@ interface AccountTransactionDao : BaseDao<AccountTransaction> {
fun getAll(): List<AccountTransaction> fun getAll(): List<AccountTransaction>
@Query("SELECT otherPartyName, otherPartyBankCode, otherPartyAccountId FROM AccountTransaction WHERE otherPartyName LIKE '%' || :query || '%'") @Query("SELECT otherPartyName, otherPartyBankCode, otherPartyAccountId FROM AccountTransaction WHERE otherPartyName LIKE '%' || :query || '%'")
fun findRemittees(query: String): List<Remittee> fun findTransactionParty(query: String): List<TransactionParty>
} }

View File

@ -15,7 +15,7 @@ open class AccountTransaction(
override var amount: BigDecimal, override var amount: BigDecimal,
override var currency: String, override var currency: String,
override var unparsedUsage: String, override var unparsedReference: String,
override var bookingDate: Date, override var bookingDate: Date,
override var otherPartyName: String?, override var otherPartyName: String?,
override var otherPartyBankCode: String?, override var otherPartyBankCode: String?,
@ -34,10 +34,10 @@ open class AccountTransaction(
override var originatorsIdentificationCode: String?, override var originatorsIdentificationCode: String?,
override var compensationAmount: String?, override var compensationAmount: String?,
override var originalAmount: String?, override var originalAmount: String?,
override var sepaUsage: String?, override var sepaReference: String?,
override var deviantOriginator: String?, override var deviantOriginator: String?,
override var deviantRecipient: String?, override var deviantRecipient: String?,
override var usageWithNoSpecialType: String?, override var referenceWithNoSpecialType: String?,
override var primaNotaNumber: String?, override var primaNotaNumber: String?,
override var textKeySupplement: String?, override var textKeySupplement: String?,
@ -56,15 +56,15 @@ open class AccountTransaction(
/* convenience constructors for languages not supporting default values */ /* convenience constructors for languages not supporting default values */
constructor(account: BankAccount, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?) constructor(account: BankAccount, otherPartyName: String?, unparsedReference: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
: this(account, amount, "EUR", unparsedUsage, valueDate, : this(account, amount, "EUR", unparsedReference, valueDate,
otherPartyName, null, null, bookingText, valueDate) otherPartyName, null, null, bookingText, valueDate)
constructor(account: BankAccount, amount: BigDecimal, currency: String, unparsedUsage: String, bookingDate: Date, constructor(account: BankAccount, amount: BigDecimal, currency: String, unparsedReference: String, bookingDate: Date,
otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?,
bookingText: String?, valueDate: Date) bookingText: String?, valueDate: Date)
: this(account, amount, currency, unparsedUsage, bookingDate, : this(account, amount, currency, unparsedReference, bookingDate,
otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate,
0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null) 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null)

View File

@ -30,7 +30,7 @@ open class BankAccount(
override var supportsRetrievingAccountTransactions: Boolean = false, override var supportsRetrievingAccountTransactions: Boolean = false,
override var supportsRetrievingBalance: Boolean = false, override var supportsRetrievingBalance: Boolean = false,
override var supportsTransferringMoney: Boolean = false, override var supportsTransferringMoney: Boolean = false,
override var supportsInstantPaymentMoneyTransfer: Boolean = false, override var supportsRealTimeTransfer: Boolean = false,
@Ignore @Ignore
override var bookedTransactions: List<IAccountTransaction> = listOf(), override var bookedTransactions: List<IAccountTransaction> = listOf(),

View File

@ -26,7 +26,7 @@ open class RoomModelCreator : IModelCreator {
account: TypedBankAccount, account: TypedBankAccount,
amount: BigDecimal, amount: BigDecimal,
currency: String, currency: String,
unparsedUsage: String, unparsedReference: String,
bookingDate: Date, bookingDate: Date,
otherPartyName: String?, otherPartyName: String?,
otherPartyBankCode: String?, otherPartyBankCode: String?,
@ -44,10 +44,10 @@ open class RoomModelCreator : IModelCreator {
originatorsIdentificationCode: String?, originatorsIdentificationCode: String?,
compensationAmount: String?, compensationAmount: String?,
originalAmount: String?, originalAmount: String?,
sepaUsage: String?, sepaReference: String?,
deviantOriginator: String?, deviantOriginator: String?,
deviantRecipient: String?, deviantRecipient: String?,
usageWithNoSpecialType: String?, referenceWithNoSpecialType: String?,
primaNotaNumber: String?, primaNotaNumber: String?,
textKeySupplement: String?, textKeySupplement: String?,
currencyType: String?, currencyType: String?,
@ -58,10 +58,10 @@ open class RoomModelCreator : IModelCreator {
transactionReferenceNumber: String, transactionReferenceNumber: String,
relatedReferenceNumber: String? relatedReferenceNumber: String?
): IAccountTransaction { ): IAccountTransaction {
return AccountTransaction(account as BankAccount, amount, currency, unparsedUsage, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, return AccountTransaction(account as BankAccount, amount, currency, unparsedReference, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId,
bookingText, valueDate, statementNumber, sequenceNumber, openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, bookingText, valueDate, statementNumber, sequenceNumber, openingBalance, closingBalance, endToEndReference, customerReference, mandateReference,
creditorIdentifier, originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient, creditorIdentifier, originatorsIdentificationCode, compensationAmount, originalAmount, sepaReference, deviantOriginator, deviantRecipient,
usageWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner, referenceWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,
referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber) referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber)
} }

View File

@ -3,7 +3,7 @@ package net.dankito.banking.persistence.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
data class Remittee( data class TransactionParty(
@ColumnInfo(name = "otherPartyName") val name: String, @ColumnInfo(name = "otherPartyName") val name: String,
@ColumnInfo(name = "otherPartyBankCode") val bankCode: String?, @ColumnInfo(name = "otherPartyBankCode") val bankCode: String?,

View File

@ -28,7 +28,7 @@ open class EntitiesModelCreator : IModelCreator {
account: TypedBankAccount, account: TypedBankAccount,
amount: BigDecimal, amount: BigDecimal,
currency: String, currency: String,
unparsedUsage: String, unparsedReference: String,
bookingDate: Date, bookingDate: Date,
otherPartyName: String?, otherPartyName: String?,
otherPartyBankCode: String?, otherPartyBankCode: String?,
@ -46,10 +46,10 @@ open class EntitiesModelCreator : IModelCreator {
originatorsIdentificationCode: String?, originatorsIdentificationCode: String?,
compensationAmount: String?, compensationAmount: String?,
originalAmount: String?, originalAmount: String?,
sepaUsage: String?, sepaReference: String?,
deviantOriginator: String?, deviantOriginator: String?,
deviantRecipient: String?, deviantRecipient: String?,
usageWithNoSpecialType: String?, referenceWithNoSpecialType: String?,
primaNotaNumber: String?, primaNotaNumber: String?,
textKeySupplement: String?, textKeySupplement: String?,
currencyType: String?, currencyType: String?,
@ -61,11 +61,11 @@ open class EntitiesModelCreator : IModelCreator {
relatedReferenceNumber: String? relatedReferenceNumber: String?
) : IAccountTransaction { ) : IAccountTransaction {
return AccountTransactionEntity(account as BankAccountEntity, amount, currency, unparsedUsage, bookingDate, return AccountTransactionEntity(account as BankAccountEntity, amount, currency, unparsedReference, bookingDate,
otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, statementNumber, sequenceNumber, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, statementNumber, sequenceNumber,
openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier, openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier,
originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient, originatorsIdentificationCode, compensationAmount, originalAmount, sepaReference, deviantOriginator, deviantRecipient,
usageWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner, referenceWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,
referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber) referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber)
} }

View File

@ -14,7 +14,7 @@ open class AccountTransactionEntity(
override var account: BankAccountEntity, override var account: BankAccountEntity,
override var amount: BigDecimal, override var amount: BigDecimal,
override var currency: String, override var currency: String,
override var unparsedUsage: String, override var unparsedReference: String,
override var bookingDate: Date, override var bookingDate: Date,
override var otherPartyName: String?, override var otherPartyName: String?,
override var otherPartyBankCode: String?, override var otherPartyBankCode: String?,
@ -33,10 +33,10 @@ open class AccountTransactionEntity(
override var originatorsIdentificationCode: String?, override var originatorsIdentificationCode: String?,
override var compensationAmount: String?, override var compensationAmount: String?,
override var originalAmount: String?, override var originalAmount: String?,
override var sepaUsage: String?, override var sepaReference: String?,
override var deviantOriginator: String?, override var deviantOriginator: String?,
override var deviantRecipient: String?, override var deviantRecipient: String?,
override var usageWithNoSpecialType: String?, override var referenceWithNoSpecialType: String?,
override var primaNotaNumber: String?, override var primaNotaNumber: String?,
override var textKeySupplement: String?, override var textKeySupplement: String?,
@ -56,8 +56,8 @@ open class AccountTransactionEntity(
-1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, -1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null,
null, "", null) null, "", null)
constructor(account: BankAccountEntity, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?) constructor(account: BankAccountEntity, otherPartyName: String?, unparsedReference: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
: this(account, amount, "EUR", unparsedUsage, valueDate, otherPartyName, null, null, bookingText, valueDate, 0, null, null, null, : this(account, amount, "EUR", unparsedReference, valueDate, otherPartyName, null, null, bookingText, valueDate, 0, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, "", "", null, null, "", null) null, "", "", null, null, "", null)

View File

@ -27,7 +27,7 @@ open class BankAccountEntity(
override var supportsRetrievingAccountTransactions: Boolean = false, override var supportsRetrievingAccountTransactions: Boolean = false,
override var supportsRetrievingBalance: Boolean = false, override var supportsRetrievingBalance: Boolean = false,
override var supportsTransferringMoney: Boolean = false, override var supportsTransferringMoney: Boolean = false,
override var supportsInstantPaymentMoneyTransfer: Boolean = false, override var supportsRealTimeTransfer: Boolean = false,
override var bookedTransactions: List<AccountTransactionEntity> = listOf(), override var bookedTransactions: List<AccountTransactionEntity> = listOf(),
override var unbookedTransactions: List<Any> = listOf(), override var unbookedTransactions: List<Any> = listOf(),
override var technicalId: String = UUID.random(), override var technicalId: String = UUID.random(),

View File

@ -147,7 +147,7 @@ class BankingPersistenceJsonTest {
} }
private fun createTransaction(transactionIndex: Int, account: BankAccountEntity): AccountTransactionEntity { private fun createTransaction(transactionIndex: Int, account: BankAccountEntity): AccountTransactionEntity {
return AccountTransactionEntity(account, "OtherParty_$transactionIndex", "Usage_$transactionIndex", BigDecimal(transactionIndex.toDouble()), createDate(), null) return AccountTransactionEntity(account, "OtherParty_$transactionIndex", "Reference_$transactionIndex", BigDecimal(transactionIndex.toDouble()), createDate(), null)
} }
private fun createDate(): Date { private fun createDate(): Date {
@ -233,7 +233,7 @@ class BankingPersistenceJsonTest {
assertThat(deserializedTransaction.account.technicalId).isEqualTo(transaction.account.technicalId) assertThat(deserializedTransaction.account.technicalId).isEqualTo(transaction.account.technicalId)
assertThat(deserializedTransaction.otherPartyName).isEqualTo(transaction.otherPartyName) assertThat(deserializedTransaction.otherPartyName).isEqualTo(transaction.otherPartyName)
assertThat(deserializedTransaction.unparsedUsage).isEqualTo(transaction.unparsedUsage) assertThat(deserializedTransaction.unparsedReference).isEqualTo(transaction.unparsedReference)
assertThat(deserializedTransaction.amount).isEqualTo(transaction.amount) assertThat(deserializedTransaction.amount).isEqualTo(transaction.amount)
assertThat(deserializedTransaction.valueDate).isEqualTo(transaction.valueDate) assertThat(deserializedTransaction.valueDate).isEqualTo(transaction.valueDate)
} }

View File

@ -40,7 +40,7 @@ open class AccountTransactionAdapter(protected val presenter: BankingPresenter)
val label = if (item.showOtherPartyName) item.otherPartyName else item.bookingText val label = if (item.showOtherPartyName) item.otherPartyName else item.bookingText
viewHolder.txtvwTransactionLabel.text = label ?: item.bookingText ?: "" viewHolder.txtvwTransactionLabel.text = label ?: item.bookingText ?: ""
viewHolder.txtvwUsage.text = item.usage viewHolder.txtvwReference.text = item.reference
viewHolder.txtvwAmount.showAmount(presenter, item.amount) viewHolder.txtvwAmount.showAmount(presenter, item.amount)
@ -66,12 +66,12 @@ open class AccountTransactionAdapter(protected val presenter: BankingPresenter)
menu.findItem(R.id.mnitmNewTransferWithSameData)?.isVisible = canCreateMoneyTransferFrom menu.findItem(R.id.mnitmNewTransferWithSameData)?.isVisible = canCreateMoneyTransferFrom
menu.findItem(R.id.mnitmNewTransferToSameRemittee)?.let { mnitmShowTransferMoneyDialog -> menu.findItem(R.id.mnitmNewTransferToSameTransactionParty)?.let { mnitmShowTransferMoneyDialog ->
mnitmShowTransferMoneyDialog.isVisible = canCreateMoneyTransferFrom mnitmShowTransferMoneyDialog.isVisible = canCreateMoneyTransferFrom
val remitteeName = selectedTransaction?.otherPartyName ?: "" val recipientName = selectedTransaction?.otherPartyName ?: ""
mnitmShowTransferMoneyDialog.title = view.context.getString(R.string.fragment_home_transfer_money_to, remitteeName) mnitmShowTransferMoneyDialog.title = view.context.getString(R.string.fragment_home_transfer_money_to, recipientName)
} }
} }

View File

@ -0,0 +1,33 @@
package net.dankito.banking.ui.android.adapter
import android.view.View
import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.android.adapter.viewholder.RecipientViewHolder
import net.dankito.banking.search.TransactionParty
import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter
open class RecipientListAdapter(protected val itemClicked: ((TransactionParty) -> Unit)? = null) : ListRecyclerAdapter<TransactionParty, RecipientViewHolder>() {
override fun getListItemLayoutId() = R.layout.list_item_recipient
override fun createViewHolder(itemView: View): RecipientViewHolder {
return RecipientViewHolder(itemView)
}
override fun bindItemToView(viewHolder: RecipientViewHolder, item: TransactionParty) {
viewHolder.txtvwRecipientName.text = item.name
viewHolder.txtvwRecipientBankName.text = item.bankName
viewHolder.txtvwRecipientBankName.visibility = if (item.bankName.isNullOrBlank()) View.GONE else View.VISIBLE
viewHolder.txtvwRecipientAccountId.text = item.iban
viewHolder.txtvwRecipientBankCode.text = item.bic
viewHolder.itemView.setOnClickListener {
itemClicked?.invoke(item)
}
}
}

View File

@ -1,33 +0,0 @@
package net.dankito.banking.ui.android.adapter
import android.view.View
import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.android.adapter.viewholder.RemitteeViewHolder
import net.dankito.banking.search.Remittee
import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter
open class RemitteeListAdapter(protected val itemClicked: ((Remittee) -> Unit)? = null) : ListRecyclerAdapter<Remittee, RemitteeViewHolder>() {
override fun getListItemLayoutId() = R.layout.list_item_remittee
override fun createViewHolder(itemView: View): RemitteeViewHolder {
return RemitteeViewHolder(itemView)
}
override fun bindItemToView(viewHolder: RemitteeViewHolder, item: Remittee) {
viewHolder.txtvwRemitteeName.text = item.name
viewHolder.txtvwRemitteeBankName.text = item.bankName
viewHolder.txtvwRemitteeBankName.visibility = if (item.bankName.isNullOrBlank()) View.GONE else View.VISIBLE
viewHolder.txtvwRemitteeAccountId.text = item.iban
viewHolder.txtvwRemitteeBankCode.text = item.bic
viewHolder.itemView.setOnClickListener {
itemClicked?.invoke(item)
}
}
}

View File

@ -0,0 +1,38 @@
package net.dankito.banking.ui.android.adapter.presenter
import android.content.Context
import androidx.recyclerview.widget.RecyclerView
import com.otaliastudios.autocomplete.RecyclerViewPresenter
import kotlinx.coroutines.*
import net.dankito.banking.ui.android.adapter.RecipientListAdapter
import net.dankito.banking.search.TransactionParty
import net.dankito.banking.ui.android.extensions.addHorizontalItemDivider
import net.dankito.banking.ui.presenter.BankingPresenter
open class RecipientPresenter(protected val bankingPresenter: BankingPresenter, context: Context) : RecyclerViewPresenter<TransactionParty>(context) {
protected val adapter = RecipientListAdapter { dispatchClick(it) }
protected var lastSearchRecipientJob: Job? = null
override fun instantiateAdapter(): RecyclerView.Adapter<*> {
recyclerView?.addHorizontalItemDivider()
return adapter
}
override fun onQuery(query: CharSequence?) {
lastSearchRecipientJob?.cancel()
lastSearchRecipientJob = GlobalScope.launch(Dispatchers.IO) {
val potentialRecipients = bankingPresenter.findRecipientsForName(query?.toString() ?: "")
withContext(Dispatchers.Main) {
adapter.items = potentialRecipients
}
}
}
}

View File

@ -1,38 +0,0 @@
package net.dankito.banking.ui.android.adapter.presenter
import android.content.Context
import androidx.recyclerview.widget.RecyclerView
import com.otaliastudios.autocomplete.RecyclerViewPresenter
import kotlinx.coroutines.*
import net.dankito.banking.ui.android.adapter.RemitteeListAdapter
import net.dankito.banking.search.Remittee
import net.dankito.banking.ui.android.extensions.addHorizontalItemDivider
import net.dankito.banking.ui.presenter.BankingPresenter
open class RemitteePresenter(protected val bankingPresenter: BankingPresenter, context: Context) : RecyclerViewPresenter<Remittee>(context) {
protected val adapter = RemitteeListAdapter { dispatchClick(it) }
protected var lastSearchRemitteeJob: Job? = null
override fun instantiateAdapter(): RecyclerView.Adapter<*> {
recyclerView?.addHorizontalItemDivider()
return adapter
}
override fun onQuery(query: CharSequence?) {
lastSearchRemitteeJob?.cancel()
lastSearchRemitteeJob = GlobalScope.launch(Dispatchers.IO) {
val potentialRemittees = bankingPresenter.findRemitteesForName(query?.toString() ?: "")
withContext(Dispatchers.Main) {
adapter.items = potentialRemittees
}
}
}
}

View File

@ -13,7 +13,7 @@ open class AccountTransactionViewHolder(itemView: View) : RecyclerView.ViewHolde
val txtvwTransactionLabel: TextView = itemView.txtvwTransactionLabel val txtvwTransactionLabel: TextView = itemView.txtvwTransactionLabel
val txtvwUsage: TextView = itemView.txtvwUsage val txtvwReference: TextView = itemView.txtvwReference
val txtvwAmount: TextView = itemView.txtvwAmount val txtvwAmount: TextView = itemView.txtvwAmount

View File

@ -0,0 +1,19 @@
package net.dankito.banking.ui.android.adapter.viewholder
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.list_item_recipient.view.*
open class RecipientViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val txtvwRecipientName: TextView = itemView.txtvwRecipientName
val txtvwRecipientBankName: TextView = itemView.txtvwRecipientBankName
val txtvwRecipientAccountId: TextView = itemView.txtvwRecipientAccountId
val txtvwRecipientBankCode: TextView = itemView.txtvwRecipientBankCode
}

View File

@ -1,19 +0,0 @@
package net.dankito.banking.ui.android.adapter.viewholder
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.list_item_remittee.view.*
open class RemitteeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val txtvwRemitteeName: TextView = itemView.txtvwRemitteeName
val txtvwRemitteeBankName: TextView = itemView.txtvwRemitteeBankName
val txtvwRemitteeAccountId: TextView = itemView.txtvwRemitteeAccountId
val txtvwRemitteeBankCode: TextView = itemView.txtvwRemitteeBankCode
}

View File

@ -9,8 +9,7 @@ import net.dankito.banking.ui.android.RouterAndroid
import net.dankito.banking.ui.android.util.CurrentActivityTracker import net.dankito.banking.ui.android.util.CurrentActivityTracker
import net.dankito.banking.fints4kBankingClientCreator import net.dankito.banking.fints4kBankingClientCreator
import net.dankito.banking.persistence.IBankingPersistence import net.dankito.banking.persistence.IBankingPersistence
import net.dankito.banking.search.IRemitteeSearcher import net.dankito.banking.search.ITransactionPartySearcher
import net.dankito.banking.search.LuceneRemitteeSearcher
import net.dankito.banking.ui.IBankingClientCreator import net.dankito.banking.ui.IBankingClientCreator
import net.dankito.banking.ui.IRouter import net.dankito.banking.ui.IRouter
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
@ -88,11 +87,11 @@ class BankingModule(private val applicationContext: Context) {
@Singleton @Singleton
fun provideBankingPresenter(bankingClientCreator: IBankingClientCreator, bankFinder: IBankFinder, fun provideBankingPresenter(bankingClientCreator: IBankingClientCreator, bankFinder: IBankFinder,
@Named(DataFolderKey) dataFolder: File, @Named(DataFolderKey) dataFolder: File,
persister: IBankingPersistence, remitteeSearcher: IRemitteeSearcher, bankIconFinder: IBankIconFinder, persister: IBankingPersistence, transactionPartySearcher: ITransactionPartySearcher, bankIconFinder: IBankIconFinder,
textExtractorRegistry: ITextExtractorRegistry, router: IRouter, invoiceDataExtractor: IInvoiceDataExtractor, textExtractorRegistry: ITextExtractorRegistry, router: IRouter, invoiceDataExtractor: IInvoiceDataExtractor,
modelCreator: IModelCreator, serializer: ISerializer, asyncRunner: IAsyncRunner) : BankingPresenter { modelCreator: IModelCreator, serializer: ISerializer, asyncRunner: IAsyncRunner) : BankingPresenter {
return BankingPresenter(bankingClientCreator, bankFinder, dataFolder, persister, router, modelCreator, return BankingPresenter(bankingClientCreator, bankFinder, dataFolder, persister, router, modelCreator,
remitteeSearcher, bankIconFinder, textExtractorRegistry, invoiceDataExtractor, serializer, asyncRunner) transactionPartySearcher, bankIconFinder, textExtractorRegistry, invoiceDataExtractor, serializer, asyncRunner)
} }
@Provides @Provides
@ -121,7 +120,7 @@ class BankingModule(private val applicationContext: Context) {
@Provides @Provides
@Singleton @Singleton
fun provideRemitteeSearcher(bankingPersistence: IBankingPersistence) : IRemitteeSearcher { fun provideTransactionPartySearcher(bankingPersistence: IBankingPersistence) : ITransactionPartySearcher {
return bankingPersistence as RoomBankingPersistence return bankingPersistence as RoomBankingPersistence
} }

View File

@ -20,13 +20,13 @@ import kotlinx.android.synthetic.main.dialog_transfer_money.view.*
import net.dankito.banking.ui.android.R import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.android.di.BankingComponent import net.dankito.banking.ui.android.di.BankingComponent
import net.dankito.banking.ui.android.adapter.BankAccountsAdapter import net.dankito.banking.ui.android.adapter.BankAccountsAdapter
import net.dankito.banking.ui.android.adapter.presenter.RemitteePresenter import net.dankito.banking.ui.android.adapter.presenter.RecipientPresenter
import net.dankito.banking.ui.android.extensions.addEnterPressedListener import net.dankito.banking.ui.android.extensions.addEnterPressedListener
import net.dankito.banking.ui.android.extensions.closePopupOnBackButtonPress import net.dankito.banking.ui.android.extensions.closePopupOnBackButtonPress
import net.dankito.banking.ui.android.listener.ListItemSelectedListener import net.dankito.banking.ui.android.listener.ListItemSelectedListener
import net.dankito.banking.ui.android.util.StandardAutocompleteCallback import net.dankito.banking.ui.android.util.StandardAutocompleteCallback
import net.dankito.banking.ui.android.util.StandardTextWatcher import net.dankito.banking.ui.android.util.StandardTextWatcher
import net.dankito.banking.search.Remittee import net.dankito.banking.search.TransactionParty
import net.dankito.banking.ui.model.TypedBankAccount import net.dankito.banking.ui.model.TypedBankAccount
import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.BankingClientResponse import net.dankito.banking.ui.model.responses.BankingClientResponse
@ -60,16 +60,16 @@ open class TransferMoneyDialog : DialogFragment() {
protected val inputValidator = InputValidator() // TODO: move to presenter protected val inputValidator = InputValidator() // TODO: move to presenter
protected var remitteeBic: String? = null protected var recipientBic: String? = null
protected var validRemitteeNameEntered = false protected var validRecipientNameEntered = false
protected var validRemitteeIbanEntered = false protected var validRecipientIbanEntered = false
protected var validRemitteeBicEntered = false protected var validRecipientBicEntered = false
protected var validUsageEntered = true protected var validReferenceEntered = true
protected var validAmountEntered = false protected var validAmountEntered = false
@ -118,73 +118,73 @@ open class TransferMoneyDialog : DialogFragment() {
rootView.spnBankAccounts.adapter = adapter rootView.spnBankAccounts.adapter = adapter
rootView.spnBankAccounts.onItemSelectedListener = ListItemSelectedListener(adapter) { selectedBankAccount -> rootView.spnBankAccounts.onItemSelectedListener = ListItemSelectedListener(adapter) { selectedBankAccount ->
this.account = selectedBankAccount this.account = selectedBankAccount
setInstantPaymentControlsVisibility(rootView) setRealTimeTransferControlsVisibility(rootView)
} }
preselectedValues?.account?.let { rootView.spnBankAccounts.setSelection(adapter.getItems().indexOf(it)) } preselectedValues?.account?.let { rootView.spnBankAccounts.setSelection(adapter.getItems().indexOf(it)) }
} }
initRemitteeAutocompletion(rootView.edtxtRemitteeName) initRecipientAutocompletion(rootView.edtxtRecipientName)
rootView.edtxtRemitteeName.addTextChangedListener(checkRequiredDataWatcher { rootView.edtxtRecipientName.addTextChangedListener(checkRequiredDataWatcher {
checkIfEnteredRemitteeNameIsValidWhileUserIsTyping() checkIfEnteredRecipientNameIsValidWhileUserIsTyping()
}) })
rootView.edtxtRemitteeIban.addTextChangedListener(StandardTextWatcher { rootView.edtxtRecipientIban.addTextChangedListener(StandardTextWatcher {
checkIfEnteredRemitteeIbanIsValidWhileUserIsTyping() checkIfEnteredRecipientIbanIsValidWhileUserIsTyping()
tryToGetBicFromIban(it) tryToGetBicFromIban(it)
}) })
rootView.edtxtAmount.addTextChangedListener(checkRequiredDataWatcher { rootView.edtxtAmount.addTextChangedListener(checkRequiredDataWatcher {
checkIfEnteredAmountIsValid() checkIfEnteredAmountIsValid()
}) })
rootView.edtxtUsage.addTextChangedListener(checkRequiredDataWatcher { rootView.edtxtReference.addTextChangedListener(checkRequiredDataWatcher {
checkIfEnteredUsageTextIsValid() checkIfEnteredReferenceTextIsValid()
}) })
rootView.edtxtRemitteeName.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRemitteeNameIsValidAfterFocusLost() } rootView.edtxtRecipientName.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRecipientNameIsValidAfterFocusLost() }
rootView.edtxtRemitteeIban.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRemitteeIbanIsValidAfterFocusLost() } rootView.edtxtRecipientIban.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRecipientIbanIsValidAfterFocusLost() }
rootView.edtxtAmount.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredAmountIsValid() } rootView.edtxtAmount.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredAmountIsValid() }
rootView.edtxtUsage.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredUsageTextIsValid() } rootView.edtxtReference.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredReferenceTextIsValid() }
transferMoneyIfEnterPressed(rootView.edtxtRemitteeName) transferMoneyIfEnterPressed(rootView.edtxtRecipientName)
transferMoneyIfEnterPressed(rootView.edtxtRemitteeIban) transferMoneyIfEnterPressed(rootView.edtxtRecipientIban)
transferMoneyIfEnterPressed(rootView.edtxtAmount) transferMoneyIfEnterPressed(rootView.edtxtAmount)
transferMoneyIfEnterPressed(rootView.edtxtUsage) transferMoneyIfEnterPressed(rootView.edtxtReference)
// fix that even in Locales using ',' as decimal separator entering ',' is not allowed (thanks dstibbe! https://stackoverflow.com/a/34256139) // fix that even in Locales using ',' as decimal separator entering ',' is not allowed (thanks dstibbe! https://stackoverflow.com/a/34256139)
val decimalSeparator = DecimalFormatSymbols.getInstance().getDecimalSeparator() val decimalSeparator = DecimalFormatSymbols.getInstance().getDecimalSeparator()
rootView.edtxtAmount.keyListener = DigitsKeyListener.getInstance("0123456789$decimalSeparator") rootView.edtxtAmount.keyListener = DigitsKeyListener.getInstance("0123456789$decimalSeparator")
rootView.btnShowInstantPaymentInfo.setOnClickListener { showInstantPaymentInfo(rootView.btnShowInstantPaymentInfo, rootView) } rootView.btnShowRealTimeTransferInfo.setOnClickListener { showRealTimeTransferInfo(rootView.btnShowRealTimeTransferInfo, rootView) }
setInstantPaymentControlsVisibility(rootView) setRealTimeTransferControlsVisibility(rootView)
rootView.btnCancel.setOnClickListener { dismiss() } rootView.btnCancel.setOnClickListener { dismiss() }
rootView.btnTransferMoney.setOnClickListener { transferMoney() } rootView.btnTransferMoney.setOnClickListener { transferMoney() }
adjustCheckBoxInstantPaymentWidth() adjustCheckBoxRealTimeTransferWidth()
} }
protected open fun adjustCheckBoxInstantPaymentWidth() { protected open fun adjustCheckBoxRealTimeTransferWidth() {
// wait some time till CheckBox is layout and lineCount is set // wait some time till CheckBox is layout and lineCount is set
val timer = Timer() val timer = Timer()
timer.schedule(10) { requireActivity().runOnUiThread { adjustCheckBoxInstantPaymentWidthOnUiThread() }} timer.schedule(10) { requireActivity().runOnUiThread { adjustCheckBoxRealTimeTransferWidthOnUiThread() }}
timer.schedule(2500) { requireActivity().runOnUiThread { adjustCheckBoxInstantPaymentWidthOnUiThread() }} timer.schedule(2500) { requireActivity().runOnUiThread { adjustCheckBoxRealTimeTransferWidthOnUiThread() }}
} }
protected open fun adjustCheckBoxInstantPaymentWidthOnUiThread() { protected open fun adjustCheckBoxRealTimeTransferWidthOnUiThread() {
if (chkbxInstantPayment.isEllipsized == false) { if (chkbxRealTimeTransfer.isEllipsized == false) {
// by default chkbxInstantPayment uses full width, even though if its text doesn't need this space -> there // by default chkbxRealTimeTransfer uses full width, even though if its text doesn't need this space -> there
chkbxInstantPayment.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 0f) chkbxRealTimeTransfer.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 0f)
chkbxInstantPayment.requestLayout() chkbxRealTimeTransfer.requestLayout()
} }
} }
protected open fun setInstantPaymentControlsVisibility(rootView: View) { protected open fun setRealTimeTransferControlsVisibility(rootView: View) {
rootView.lytInstantPayment.visibility = rootView.lytRealTimeTransfer.visibility =
if (account.supportsInstantPaymentMoneyTransfer) { if (account.supportsRealTimeTransfer) {
View.VISIBLE View.VISIBLE
} }
else { else {
@ -192,9 +192,9 @@ open class TransferMoneyDialog : DialogFragment() {
} }
} }
protected open fun showInstantPaymentInfo(btnShowInstantPaymentInfo: ImageButton, rootView: View) { protected open fun showRealTimeTransferInfo(btnShowRealTimeTransferInfo: ImageButton, rootView: View) {
requireActivity().layoutInflater.inflate(R.layout.view_instant_payment_info, null)?.let { contentView -> requireActivity().layoutInflater.inflate(R.layout.view_real_time_transfer_info, null)?.let { contentView ->
requireContext().hideKeyboard(lytInstantPayment) requireContext().hideKeyboard(lytRealTimeTransfer)
val popupWindow = PopupWindow(contentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) val popupWindow = PopupWindow(contentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
@ -203,9 +203,9 @@ open class TransferMoneyDialog : DialogFragment() {
contentView.findViewById<Button>(R.id.btnDismissPopup)?.setOnClickListener { popupWindow.dismiss() } contentView.findViewById<Button>(R.id.btnDismissPopup)?.setOnClickListener { popupWindow.dismiss() }
popupWindow.showAtLocation(btnShowInstantPaymentInfo, Gravity.TOP, 0, 0) popupWindow.showAtLocation(btnShowRealTimeTransferInfo, Gravity.TOP, 0, 0)
popupWindow.showAsDropDown(btnShowInstantPaymentInfo) popupWindow.showAsDropDown(btnShowRealTimeTransferInfo)
} }
} }
@ -223,17 +223,17 @@ open class TransferMoneyDialog : DialogFragment() {
private fun isRequiredDataEntered() = btnTransferMoney.isEnabled private fun isRequiredDataEntered() = btnTransferMoney.isEnabled
private fun initRemitteeAutocompletion(edtxtRemitteeName: EditText) { private fun initRecipientAutocompletion(edtxtRecipientName: EditText) {
val autocompleteCallback = StandardAutocompleteCallback<Remittee> { _, item -> val autocompleteCallback = StandardAutocompleteCallback<TransactionParty> { _, item ->
remitteeSelected(item) recipientSelected(item)
true true
} }
Autocomplete.on<Remittee>(edtxtRemitteeName) Autocomplete.on<TransactionParty>(edtxtRecipientName)
.with(6f) .with(6f)
.with(ColorDrawable(Color.WHITE)) .with(ColorDrawable(Color.WHITE))
.with(autocompleteCallback) .with(autocompleteCallback)
.with(RemitteePresenter(presenter, edtxtRemitteeName.context)) .with(RecipientPresenter(presenter, edtxtRecipientName.context))
.build() .build()
.closePopupOnBackButtonPress(dialog) .closePopupOnBackButtonPress(dialog)
} }
@ -244,8 +244,8 @@ open class TransferMoneyDialog : DialogFragment() {
setPreselectedValues() setPreselectedValues()
if (remitteeBic != null) { if (recipientBic != null) {
tryToGetBicFromIban(edtxtRemitteeIban.text.toString()) tryToGetBicFromIban(edtxtRecipientIban.text.toString())
} }
} }
@ -254,20 +254,20 @@ open class TransferMoneyDialog : DialogFragment() {
preselectedValues?.let { data -> preselectedValues?.let { data ->
preselectedValues = null preselectedValues = null
edtxtRemitteeName.setText(data.creditorName) edtxtRecipientName.setText(data.recipientName)
if (data.creditorIban.isNotBlank()) { // set only if creditorIban has a value as otherwise creditorBic would be overridden by empty search result if (data.recipientAccountId.isNotBlank()) { // set only if recipientAccountId has a value as otherwise recipientBankCode would be overridden by empty search result
edtxtRemitteeIban.setText(data.creditorIban) edtxtRecipientIban.setText(data.recipientAccountId)
} }
// a little bit inconsistent as if IBAN is not set bank's name won't be displayed even though it can be retrieved by BIC // a little bit inconsistent as if IBAN is not set bank's name won't be displayed even though it can be retrieved by BIC
remitteeBic = data.creditorBic recipientBic = data.recipientBankCode
if (data.amount > BigDecimal.ZERO) { if (data.amount > BigDecimal.ZERO) {
edtxtAmount.setText(data.amount.toString()) edtxtAmount.setText(data.amount.toString())
} }
edtxtUsage.setText(data.usage) edtxtReference.setText(data.reference)
focusEditTextAccordingToPreselectedValues() focusEditTextAccordingToPreselectedValues()
} }
@ -275,19 +275,19 @@ open class TransferMoneyDialog : DialogFragment() {
protected open fun focusEditTextAccordingToPreselectedValues() { protected open fun focusEditTextAccordingToPreselectedValues() {
when { when {
edtxtRemitteeName.text.toString().isBlank() -> edtxtRemitteeName.requestFocus() edtxtRecipientName.text.toString().isBlank() -> edtxtRecipientName.requestFocus()
edtxtRemitteeIban.text.toString().isBlank() -> edtxtRemitteeIban.requestFocus() edtxtRecipientIban.text.toString().isBlank() -> edtxtRecipientIban.requestFocus()
edtxtAmount.text.toString().isBlank() -> edtxtAmount.requestFocus() edtxtAmount.text.toString().isBlank() -> edtxtAmount.requestFocus()
edtxtUsage.text.toString().isBlank() -> edtxtUsage.requestFocus() edtxtReference.text.toString().isBlank() -> edtxtReference.requestFocus()
else -> edtxtUsage.requestFocus() else -> edtxtReference.requestFocus()
} }
} }
protected open fun remitteeSelected(item: Remittee) { protected open fun recipientSelected(item: TransactionParty) {
edtxtRemitteeName.setText(item.name) edtxtRecipientName.setText(item.name)
edtxtRemitteeIban.setText(item.iban) edtxtRecipientIban.setText(item.iban)
remitteeBic = item.bic recipientBic = item.bic
focusEditTextAccordingToPreselectedValues() focusEditTextAccordingToPreselectedValues()
} }
@ -296,12 +296,12 @@ open class TransferMoneyDialog : DialogFragment() {
getEnteredAmount()?.let { amount -> // should only come at this stage when a valid amount has been entered getEnteredAmount()?.let { amount -> // should only come at this stage when a valid amount has been entered
val data = TransferMoneyData( val data = TransferMoneyData(
account, account,
inputValidator.convertToAllowedSepaCharacters(edtxtRemitteeName.text.toString()), inputValidator.convertToAllowedSepaCharacters(edtxtRecipientName.text.toString()),
edtxtRemitteeIban.text.toString().replace(" ", ""), edtxtRecipientIban.text.toString().replace(" ", ""),
remitteeBic?.replace(" ", "") ?: "", // should always be != null at this point recipientBic?.replace(" ", "") ?: "", // should always be != null at this point
amount.toBigDecimal(), amount.toBigDecimal(),
inputValidator.convertToAllowedSepaCharacters(edtxtUsage.text.toString()), inputValidator.convertToAllowedSepaCharacters(edtxtReference.text.toString()),
chkbxInstantPayment.isChecked chkbxRealTimeTransfer.isChecked
) )
presenter.transferMoneyAsync(data) { presenter.transferMoneyAsync(data) {
@ -317,11 +317,11 @@ open class TransferMoneyDialog : DialogFragment() {
if (response.userCancelledAction == false) { if (response.userCancelledAction == false) {
val message = if (response.successful) { val message = if (response.successful) {
context.getString(R.string.dialog_transfer_money_message_transfer_successful, context.getString(R.string.dialog_transfer_money_message_transfer_successful,
String.format("%.02f", transferData.amount), "", transferData.creditorName) // TODO: where to get currency from? String.format("%.02f", transferData.amount), "", transferData.recipientName) // TODO: where to get currency from?
} }
else { else {
context.getString(R.string.dialog_transfer_money_message_transfer_failed, context.getString(R.string.dialog_transfer_money_message_transfer_failed,
String.format("%.02f", transferData.amount), "", transferData.creditorName, // TODO: where to get currency from? String.format("%.02f", transferData.amount), "", transferData.recipientName, // TODO: where to get currency from?
response.errorToShowToUser response.errorToShowToUser
) )
} }
@ -356,66 +356,66 @@ open class TransferMoneyDialog : DialogFragment() {
} }
private fun showValuesForFoundBankOnUiThread(enteredIban: CharSequence, foundBank: BankInfo?) { private fun showValuesForFoundBankOnUiThread(enteredIban: CharSequence, foundBank: BankInfo?) {
validRemitteeBicEntered = foundBank != null validRecipientBicEntered = foundBank != null
remitteeBic = foundBank?.bic recipientBic = foundBank?.bic
if (foundBank != null) { if (foundBank != null) {
txtRemitteeBankInfo.text = getString(R.string.dialog_transfer_money_bic_detected_from_iban, foundBank.bic, foundBank.name) txtRecipientBankInfo.text = getString(R.string.dialog_transfer_money_bic_detected_from_iban, foundBank.bic, foundBank.name)
txtRemitteeBankInfo.visibility = View.VISIBLE txtRecipientBankInfo.visibility = View.VISIBLE
} }
else if (enteredIban.length >= InputValidator.MinimumLengthToDetermineBicFromIban) { else if (enteredIban.length >= InputValidator.MinimumLengthToDetermineBicFromIban) {
txtRemitteeBankInfo.text = getString(R.string.dialog_transfer_money_could_not_determine_bic_from_iban, enteredIban.substring(4, InputValidator.MinimumLengthToDetermineBicFromIban)) txtRecipientBankInfo.text = getString(R.string.dialog_transfer_money_could_not_determine_bic_from_iban, enteredIban.substring(4, InputValidator.MinimumLengthToDetermineBicFromIban))
txtRemitteeBankInfo.visibility = View.VISIBLE txtRecipientBankInfo.visibility = View.VISIBLE
} }
else { else {
txtRemitteeBankInfo.visibility = View.GONE txtRecipientBankInfo.visibility = View.GONE
} }
checkIfRequiredDataEnteredOnUiThread() checkIfRequiredDataEnteredOnUiThread()
} }
protected open fun checkIfRequiredDataEnteredOnUiThread() { protected open fun checkIfRequiredDataEnteredOnUiThread() {
btnTransferMoney.isEnabled = validRemitteeNameEntered && validRemitteeIbanEntered btnTransferMoney.isEnabled = validRecipientNameEntered && validRecipientIbanEntered
&& validRemitteeBicEntered && validRecipientBicEntered
&& validAmountEntered && validUsageEntered && validAmountEntered && validReferenceEntered
} }
protected open fun checkIfEnteredRemitteeNameIsValidWhileUserIsTyping() { protected open fun checkIfEnteredRecipientNameIsValidWhileUserIsTyping() {
val enteredRemitteeName = edtxtRemitteeName.text.toString() val enteredRecipientName = edtxtRecipientName.text.toString()
val validationResult = inputValidator.validateRemitteeNameWhileTyping(enteredRemitteeName) val validationResult = inputValidator.validateRecipientNameWhileTyping(enteredRecipientName)
this.validRemitteeNameEntered = validationResult.validationSuccessfulOrCouldCorrectString this.validRecipientNameEntered = validationResult.validationSuccessfulOrCouldCorrectString
showValidationResult(lytRemitteeName, validationResult) showValidationResult(lytRecipientName, validationResult)
} }
protected open fun checkIfEnteredRemitteeNameIsValidAfterFocusLost() { protected open fun checkIfEnteredRecipientNameIsValidAfterFocusLost() {
val enteredRemitteeName = edtxtRemitteeName.text.toString() val enteredRecipientName = edtxtRecipientName.text.toString()
val validationResult = inputValidator.validateRemitteeName(enteredRemitteeName) val validationResult = inputValidator.validateRecipientName(enteredRecipientName)
this.validRemitteeNameEntered = validationResult.validationSuccessfulOrCouldCorrectString this.validRecipientNameEntered = validationResult.validationSuccessfulOrCouldCorrectString
if (validationResult.validationSuccessful == false) { // only update hint / error if validation fails, don't hide previous hint / error otherwise if (validationResult.validationSuccessful == false) { // only update hint / error if validation fails, don't hide previous hint / error otherwise
showValidationResult(lytRemitteeName, validationResult) showValidationResult(lytRecipientName, validationResult)
} }
} }
protected open fun checkIfEnteredRemitteeIbanIsValidWhileUserIsTyping() { protected open fun checkIfEnteredRecipientIbanIsValidWhileUserIsTyping() {
val enteredIban = edtxtRemitteeIban.text.toString() val enteredIban = edtxtRecipientIban.text.toString()
val validationResult = inputValidator.validateIbanWhileTyping(enteredIban) val validationResult = inputValidator.validateIbanWhileTyping(enteredIban)
this.validRemitteeIbanEntered = validationResult.validationSuccessfulOrCouldCorrectString this.validRecipientIbanEntered = validationResult.validationSuccessfulOrCouldCorrectString
showValidationResult(lytRemitteeIban, validationResult) showValidationResult(lytRecipientIban, validationResult)
} }
protected open fun checkIfEnteredRemitteeIbanIsValidAfterFocusLost() { protected open fun checkIfEnteredRecipientIbanIsValidAfterFocusLost() {
val validationResult = inputValidator.validateIban(edtxtRemitteeIban.text.toString()) val validationResult = inputValidator.validateIban(edtxtRecipientIban.text.toString())
this.validRemitteeIbanEntered = validationResult.validationSuccessfulOrCouldCorrectString this.validRecipientIbanEntered = validationResult.validationSuccessfulOrCouldCorrectString
if (validationResult.validationSuccessful == false) { // only update hint / error if validation fails, don't hide previous hint / error otherwise if (validationResult.validationSuccessful == false) { // only update hint / error if validation fails, don't hide previous hint / error otherwise
showValidationResult(lytRemitteeIban, validationResult) showValidationResult(lytRecipientIban, validationResult)
} }
} }
@ -437,12 +437,12 @@ open class TransferMoneyDialog : DialogFragment() {
return null return null
} }
protected open fun checkIfEnteredUsageTextIsValid() { protected open fun checkIfEnteredReferenceTextIsValid() {
val validationResult = inputValidator.validateUsage(edtxtUsage.text.toString()) val validationResult = inputValidator.validateReference(edtxtReference.text.toString())
this.validUsageEntered = validationResult.validationSuccessfulOrCouldCorrectString this.validReferenceEntered = validationResult.validationSuccessfulOrCouldCorrectString
showValidationResult(lytUsage, validationResult) showValidationResult(lytReference, validationResult)
} }
protected open fun showValidationResult(textInputLayout: TextInputLayout, validationResult: ValidationResult) { protected open fun showValidationResult(textInputLayout: TextInputLayout, validationResult: ValidationResult) {
@ -475,7 +475,7 @@ open class TransferMoneyDialog : DialogFragment() {
(textInputLayout.layoutParams as? ViewGroup.MarginLayoutParams)?.let { params -> (textInputLayout.layoutParams as? ViewGroup.MarginLayoutParams)?.let { params ->
val isShowingHintOrError = validationResult.validationError != null || validationResult.validationHint != null val isShowingHintOrError = validationResult.validationError != null || validationResult.validationHint != null
params.bottomMargin = if (isShowingHintOrError == false || textInputLayout == lytUsage) 0 params.bottomMargin = if (isShowingHintOrError == false || textInputLayout == lytReference) 0
else context!!.getDimension(R.dimen.dialog_transfer_money_input_fields_bottom_margin_when_displaying_validation_label) else context!!.getDimension(R.dimen.dialog_transfer_money_input_fields_bottom_margin_when_displaying_validation_label)
} }
} }

View File

@ -25,7 +25,6 @@ import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.GetTransactionsResponse import net.dankito.banking.ui.model.responses.GetTransactionsResponse
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.utils.android.extensions.asActivity import net.dankito.utils.android.extensions.asActivity
import net.dankito.utils.android.extensions.getDimension
import net.dankito.utils.multiplatform.sum import net.dankito.utils.multiplatform.sum
import java.text.DateFormat import java.text.DateFormat
import javax.inject.Inject import javax.inject.Inject
@ -148,8 +147,8 @@ class HomeFragment : Fragment() {
override fun onContextItemSelected(item: MenuItem): Boolean { override fun onContextItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.mnitmNewTransferToSameRemittee -> { R.id.mnitmNewTransferToSameTransactionParty -> {
newTransferToSameRemittee() newTransferToSameTransactionParty()
return true return true
} }
R.id.mnitmNewTransferWithSameData -> { R.id.mnitmNewTransferWithSameData -> {
@ -211,9 +210,9 @@ class HomeFragment : Fragment() {
} }
private fun newTransferToSameRemittee() { private fun newTransferToSameTransactionParty() {
transactionAdapter.selectedTransaction?.let { selectedTransaction -> transactionAdapter.selectedTransaction?.let { selectedTransaction ->
presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndUsage(selectedTransaction)) presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndReference(selectedTransaction))
} }
} }

View File

@ -47,14 +47,14 @@
</LinearLayout> </LinearLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/lytRemitteeName" android:id="@+id/lytRecipientName"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/dialog_transfer_money_remittee_name" android:hint="@string/dialog_transfer_money_recipient_name"
> >
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/edtxtRemitteeName" android:id="@+id/edtxtRecipientName"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/dialog_transfer_money_input_fields_height" android:layout_height="@dimen/dialog_transfer_money_input_fields_height"
android:inputType="textPersonName" android:inputType="textPersonName"
@ -67,14 +67,14 @@
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/lytRemitteeIban" android:id="@+id/lytRecipientIban"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/dialog_transfer_money_remittee_iban" android:hint="@string/dialog_transfer_money_recipient_iban"
> >
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/edtxtRemitteeIban" android:id="@+id/edtxtRecipientIban"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/dialog_transfer_money_autocomplete_fields_height" android:layout_height="@dimen/dialog_transfer_money_autocomplete_fields_height"
android:inputType="textCapCharacters" android:inputType="textCapCharacters"
@ -83,7 +83,7 @@
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<TextView <TextView
android:id="@+id/txtRemitteeBankInfo" android:id="@+id/txtRecipientBankInfo"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Small" style="@style/TextAppearance.AppCompat.Small"
@ -109,14 +109,14 @@
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/lytUsage" android:id="@+id/lytReference"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/dialog_transfer_money_usage" android:hint="@string/dialog_transfer_money_reference"
> >
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/edtxtUsage" android:id="@+id/edtxtReference"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/dialog_transfer_money_input_fields_height" android:layout_height="@dimen/dialog_transfer_money_input_fields_height"
android:inputType="text" android:inputType="text"
@ -127,20 +127,20 @@
<LinearLayout <LinearLayout
android:orientation="horizontal" android:orientation="horizontal"
android:id="@+id/lytInstantPayment" android:id="@+id/lytRealTimeTransfer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" android:layout_height="30dp"
android:layout_marginTop="@dimen/dialog_transfer_money_instant_payment_margin_top" android:layout_marginTop="@dimen/dialog_transfer_money_real_time_transfer_margin_top"
android:layout_marginBottom="@dimen/dialog_transfer_money_instant_payment_margin_bottom" android:layout_marginBottom="@dimen/dialog_transfer_money_real_time_transfer_margin_bottom"
android:gravity="center_vertical" android:gravity="center_vertical"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:visibility="gone" android:visibility="gone"
android:layout_marginRight="@dimen/dialog_transfer_money_instant_payment_show_info_button_margin_end" android:layout_marginRight="@dimen/dialog_transfer_money_real_time_transfer_show_info_button_margin_end"
android:layout_marginEnd="@dimen/dialog_transfer_money_instant_payment_show_info_button_margin_end" android:layout_marginEnd="@dimen/dialog_transfer_money_real_time_transfer_show_info_button_margin_end"
> >
<androidx.appcompat.widget.AppCompatCheckBox <androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/chkbxInstantPayment" android:id="@+id/chkbxRealTimeTransfer"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
@ -148,18 +148,18 @@
android:ellipsize="end" android:ellipsize="end"
android:gravity="center_vertical" android:gravity="center_vertical"
android:textAlignment="gravity" android:textAlignment="gravity"
android:textSize="@dimen/view_instant_payment_info_text_size" android:textSize="@dimen/view_real_time_transfer_info_text_size"
android:text="@string/dialog_transfer_money_instant_payment" android:text="@string/dialog_transfer_money_real_time_transfer"
/> />
<ImageButton <ImageButton
android:id="@+id/btnShowInstantPaymentInfo" android:id="@+id/btnShowRealTimeTransferInfo"
android:layout_width="@dimen/dialog_transfer_money_instant_payment_show_info_button_size" android:layout_width="@dimen/dialog_transfer_money_real_time_transfer_show_info_button_size"
android:layout_height="@dimen/dialog_transfer_money_instant_payment_show_info_button_size" android:layout_height="@dimen/dialog_transfer_money_real_time_transfer_show_info_button_size"
android:layout_weight="0" android:layout_weight="0"
android:layout_gravity="start|center_vertical" android:layout_gravity="start|center_vertical"
android:layout_marginLeft="@dimen/dialog_transfer_money_instant_payment_show_info_button_margin_start" android:layout_marginLeft="@dimen/dialog_transfer_money_real_time_transfer_show_info_button_margin_start"
android:layout_marginStart="@dimen/dialog_transfer_money_instant_payment_show_info_button_margin_start" android:layout_marginStart="@dimen/dialog_transfer_money_real_time_transfer_show_info_button_margin_start"
android:background="@null" android:background="@null"
app:srcCompat="@drawable/ic_baseline_info_24" app:srcCompat="@drawable/ic_baseline_info_24"
android:tint="@color/infoIconColor" android:tint="@color/infoIconColor"

View File

@ -25,7 +25,7 @@
android:id="@+id/txtvwTransactionLabel" android:id="@+id/txtvwTransactionLabel"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/list_item_account_transaction_space_between_label_and_usage" android:layout_marginBottom="@dimen/list_item_account_transaction_space_between_label_and_reference"
android:lines="1" android:lines="1"
android:ellipsize="end" android:ellipsize="end"
android:textSize="@dimen/list_item_account_transaction_label_text_size" android:textSize="@dimen/list_item_account_transaction_label_text_size"
@ -34,14 +34,14 @@
/> />
<TextView <TextView
android:id="@+id/txtvwUsage" android:id="@+id/txtvwReference"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:gravity="center_vertical"
android:textAlignment="gravity" android:textAlignment="gravity"
android:lines="2" android:lines="2"
android:ellipsize="end" android:ellipsize="end"
android:textSize="@dimen/list_item_account_transaction_usage_text_size" android:textSize="@dimen/list_item_account_transaction_reference_text_size"
android:textColor="@color/textColorSecondary" android:textColor="@color/textColorSecondary"
/> />

View File

@ -5,11 +5,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:padding="@dimen/list_item_remittee_padding" android:padding="@dimen/list_item_recipient_padding"
> >
<TextView <TextView
android:id="@+id/txtvwRemitteeName" android:id="@+id/txtvwRecipientName"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Medium" style="@style/TextAppearance.AppCompat.Medium"
@ -21,10 +21,10 @@
/> />
<TextView <TextView
android:id="@+id/txtvwRemitteeBankName" android:id="@+id/txtvwRecipientBankName"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_item_remittee_space_between_fields" android:layout_marginTop="@dimen/list_item_recipient_space_between_fields"
android:gravity="center_vertical" android:gravity="center_vertical"
android:lines="1" android:lines="1"
android:ellipsize="end" android:ellipsize="end"
@ -35,11 +35,11 @@
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_item_remittee_space_between_fields" android:layout_marginTop="@dimen/list_item_recipient_space_between_fields"
> >
<TextView <TextView
android:id="@+id/txtvwRemitteeAccountId" android:id="@+id/txtvwRecipientAccountId"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
@ -50,7 +50,7 @@
/> />
<TextView <TextView
android:id="@+id/txtvwRemitteeBankCode" android:id="@+id/txtvwRecipientBankCode"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"

View File

@ -18,8 +18,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Medium" style="@style/TextAppearance.AppCompat.Medium"
android:textSize="@dimen/view_instant_payment_info_text_size" android:textSize="@dimen/view_real_time_transfer_info_text_size"
android:text="@string/dialog_transfer_money_instant_payment_info" android:text="@string/dialog_transfer_money_real_time_transfer_info"
/> />
</ScrollView> </ScrollView>
@ -27,14 +27,14 @@
<LinearLayout <LinearLayout
android:id="@+id/lytButtonBar" android:id="@+id/lytButtonBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/view_instant_payment_info_dismiss_button_height" android:layout_height="@dimen/view_real_time_info_dismiss_button_height"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:gravity="center" android:gravity="center"
> >
<Button <Button
android:id="@+id/btnDismissPopup" android:id="@+id/btnDismissPopup"
android:layout_width="@dimen/view_instant_payment_info_dismiss_button_width" android:layout_width="@dimen/view_real_time_transfer_info_dismiss_button_width"
android:layout_height="match_parent" android:layout_height="match_parent"
style="?android:attr/buttonBarButtonStyle" style="?android:attr/buttonBarButtonStyle"
android:text="@android:string/ok" android:text="@android:string/ok"

View File

@ -2,7 +2,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
android:id="@+id/mnitmNewTransferToSameRemittee" android:id="@+id/mnitmNewTransferToSameTransactionParty"
android:title="@string/fragment_home_transfer_money_to" android:title="@string/fragment_home_transfer_money_to"
/> />

View File

@ -53,14 +53,14 @@
<string name="dialog_add_account_message_could_not_add_account">Konto konnte nicht hinzugefügt werden.\n\nFehlermeldung Ihrer Bank:\n\n%s</string> <string name="dialog_add_account_message_could_not_add_account">Konto konnte nicht hinzugefügt werden.\n\nFehlermeldung Ihrer Bank:\n\n%s</string>
<string name="dialog_transfer_money_account">Konto:</string> <string name="dialog_transfer_money_account">Konto:</string>
<string name="dialog_transfer_money_remittee_name">Name:</string> <string name="dialog_transfer_money_recipient_name">Name:</string>
<string name="dialog_transfer_money_remittee_iban">IBAN:</string> <string name="dialog_transfer_money_recipient_iban">IBAN:</string>
<string name="dialog_transfer_money_bic_detected_from_iban">BIC: %1$s, %2$s</string> <string name="dialog_transfer_money_bic_detected_from_iban">BIC: %1$s, %2$s</string>
<string name="dialog_transfer_money_could_not_determine_bic_from_iban">Keine BIC gefunden für BLZ %1$s</string> <string name="dialog_transfer_money_could_not_determine_bic_from_iban">Keine BIC gefunden für BLZ %1$s</string>
<string name="dialog_transfer_money_amount">Betrag:</string> <string name="dialog_transfer_money_amount">Betrag:</string>
<string name="dialog_transfer_money_usage">Verwendungszweck:</string> <string name="dialog_transfer_money_reference">Verwendungszweck:</string>
<string name="dialog_transfer_money_instant_payment">Echtzeitüberweisung (evtl. kostenpflichtig)</string> <string name="dialog_transfer_money_real_time_transfer">Echtzeitüberweisung (evtl. kostenpflichtig)</string>
<string name="dialog_transfer_money_instant_payment_info">Normale Überweisungen werden in der Regel innerhalb eines Werktages gutgeschrieben. Dies gilt jedoch nur zu Geschäftszeiten der Banken, also schon mal nicht am Wochenende und an Feiertagen. Zudem unterscheiden sich die Geschäftszeiten von Bank zu Bank. Meistens gehen diese von 10 - 18 Uhr, manchmal auch bis 22 Uhr, manchmal (Freitags) aber auch nur bis 14 Uhr. <string name="dialog_transfer_money_real_time_transfer_info">Normale Überweisungen werden in der Regel innerhalb eines Werktages gutgeschrieben. Dies gilt jedoch nur zu Geschäftszeiten der Banken, also schon mal nicht am Wochenende und an Feiertagen. Zudem unterscheiden sich die Geschäftszeiten von Bank zu Bank. Meistens gehen diese von 10 - 18 Uhr, manchmal auch bis 22 Uhr, manchmal (Freitags) aber auch nur bis 14 Uhr.
\n\nEchtzeitüberweisungen werden hingegen innerhalb von maximal 10 Sekunden überwiesen, egal an welchem Tag und zu welcher Uhrzeit. \n\nEchtzeitüberweisungen werden hingegen innerhalb von maximal 10 Sekunden überwiesen, egal an welchem Tag und zu welcher Uhrzeit.
\n\nHäufig sind Echtzeitüberweisungen jedoch kostenpflichtig. \n\nHäufig sind Echtzeitüberweisungen jedoch kostenpflichtig.
\n\nOb eine Bank Gebühren für Echtzeitüberweisungen erhebt, kann Bankmeister leider nicht wissen. Dies entnehmen Sie bitte dem Preis- / Leistungsverzeichnis Ihrer Bank.</string> \n\nOb eine Bank Gebühren für Echtzeitüberweisungen erhebt, kann Bankmeister leider nicht wissen. Dies entnehmen Sie bitte dem Preis- / Leistungsverzeichnis Ihrer Bank.</string>
@ -120,7 +120,7 @@
<string name="error_invalid_bic_pattern_entered">Eine BIC besteht aus 8 oder 11 Zeichen und folgt dem Muster: ABCDED12(XYZ)</string> <string name="error_invalid_bic_pattern_entered">Eine BIC besteht aus 8 oder 11 Zeichen und folgt dem Muster: ABCDED12(XYZ)</string>
<string name="error_no_amount_entered">Bitte geben Sie den zu überweisenden Betrag ein</string> <string name="error_no_amount_entered">Bitte geben Sie den zu überweisenden Betrag ein</string>
<string name="error_invalid_amount_entered">Bitte geben Sie einen Betrag größer 0 ein.</string> <string name="error_invalid_amount_entered">Bitte geben Sie einen Betrag größer 0 ein.</string>
<string name="error_entered_usage_too_long">Verwendungszweck darf nur 140 Zeichen lang sein</string> <string name="error_entered_recipient_too_long">Verwendungszweck darf nur 140 Zeichen lang sein</string>
<string name="dialog_send_message_log_message_log_label">Message Log</string> <string name="dialog_send_message_log_message_log_label">Message Log</string>

View File

@ -34,9 +34,9 @@
<dimen name="list_item_account_transaction_height">74dp</dimen> <dimen name="list_item_account_transaction_height">74dp</dimen>
<dimen name="list_item_account_transaction_padding">0dp</dimen> <dimen name="list_item_account_transaction_padding">0dp</dimen>
<dimen name="list_item_account_transaction_space_between_label_and_usage">2dp</dimen> <dimen name="list_item_account_transaction_space_between_label_and_reference">2dp</dimen>
<dimen name="list_item_account_transaction_label_text_size">16sp</dimen> <dimen name="list_item_account_transaction_label_text_size">16sp</dimen>
<dimen name="list_item_account_transaction_usage_text_size">14sp</dimen> <dimen name="list_item_account_transaction_reference_text_size">14sp</dimen>
<dimen name="list_item_account_transaction_bank_icon_width_and_height">16dp</dimen> <dimen name="list_item_account_transaction_bank_icon_width_and_height">16dp</dimen>
<dimen name="list_item_account_transaction_bank_icon_margin_top">0dp</dimen> <dimen name="list_item_account_transaction_bank_icon_margin_top">0dp</dimen>
<dimen name="list_item_account_transaction_bank_icon_margin_bottom">2dp</dimen> <dimen name="list_item_account_transaction_bank_icon_margin_bottom">2dp</dimen>
@ -75,25 +75,25 @@
<dimen name="dialog_transfer_money_autocomplete_fields_height">50dp</dimen> <dimen name="dialog_transfer_money_autocomplete_fields_height">50dp</dimen>
<dimen name="dialog_transfer_money_input_fields_height">40dp</dimen> <dimen name="dialog_transfer_money_input_fields_height">40dp</dimen>
<dimen name="dialog_transfer_money_input_fields_bottom_margin_when_displaying_validation_label">8dp</dimen> <dimen name="dialog_transfer_money_input_fields_bottom_margin_when_displaying_validation_label">8dp</dimen>
<dimen name="dialog_transfer_money_instant_payment_text_size">14sp</dimen> <dimen name="dialog_transfer_money_real_time_transfer_text_size">14sp</dimen>
<dimen name="dialog_transfer_money_instant_payment_margin_top">6dp</dimen> <dimen name="dialog_transfer_money_real_time_transfer_margin_top">6dp</dimen>
<dimen name="dialog_transfer_money_instant_payment_margin_bottom">6dp</dimen> <dimen name="dialog_transfer_money_real_time_transfer_margin_bottom">6dp</dimen>
<dimen name="dialog_transfer_money_instant_payment_show_info_button_size">20dp</dimen> <dimen name="dialog_transfer_money_real_time_transfer_show_info_button_size">20dp</dimen>
<dimen name="dialog_transfer_money_instant_payment_show_info_button_margin_start">6dp</dimen> <dimen name="dialog_transfer_money_real_time_transfer_show_info_button_margin_start">6dp</dimen>
<dimen name="dialog_transfer_money_instant_payment_show_info_button_margin_end">12dp</dimen> <dimen name="dialog_transfer_money_real_time_transfer_show_info_button_margin_end">12dp</dimen>
<dimen name="dialog_transfer_money_buttons_width">120dp</dimen> <dimen name="dialog_transfer_money_buttons_width">120dp</dimen>
<dimen name="list_item_bank_account_padding">2dp</dimen> <dimen name="list_item_bank_account_padding">2dp</dimen>
<dimen name="list_item_bank_account_icon_size">20dp</dimen> <dimen name="list_item_bank_account_icon_size">20dp</dimen>
<dimen name="list_item_bank_account_icon_margin_right">8dp</dimen> <dimen name="list_item_bank_account_icon_margin_right">8dp</dimen>
<dimen name="list_item_remittee_padding">4dp</dimen> <dimen name="list_item_recipient_padding">4dp</dimen>
<dimen name="list_item_remittee_space_between_fields">4dp</dimen> <dimen name="list_item_recipient_space_between_fields">4dp</dimen>
<dimen name="view_instant_payment_info_padding">8dp</dimen> <dimen name="view_real_time_transfer_info_padding">8dp</dimen>
<dimen name="view_instant_payment_info_text_size">15sp</dimen> <dimen name="view_real_time_transfer_info_text_size">15sp</dimen>
<dimen name="view_instant_payment_info_dismiss_button_width">150dp</dimen> <dimen name="view_real_time_transfer_info_dismiss_button_width">150dp</dimen>
<dimen name="view_instant_payment_info_dismiss_button_height">40dp</dimen> <dimen name="view_real_time_info_dismiss_button_height">40dp</dimen>
<dimen name="view_tan_image_controls_buttons_height">40dp</dimen> <dimen name="view_tan_image_controls_buttons_height">40dp</dimen>
<dimen name="view_tan_image_controls_buttons_width">40dp</dimen> <dimen name="view_tan_image_controls_buttons_width">40dp</dimen>

View File

@ -53,17 +53,17 @@
<string name="dialog_add_account_message_could_not_add_account">Could not add account.\n\nError message from your bank:\n\n%s</string> <string name="dialog_add_account_message_could_not_add_account">Could not add account.\n\nError message from your bank:\n\n%s</string>
<string name="dialog_transfer_money_account">Account:</string> <string name="dialog_transfer_money_account">Account:</string>
<string name="dialog_transfer_money_remittee_name">Name:</string> <string name="dialog_transfer_money_recipient_name">Name:</string>
<string name="dialog_transfer_money_remittee_iban">IBAN:</string> <string name="dialog_transfer_money_recipient_iban">IBAN:</string>
<string name="dialog_transfer_money_bic_detected_from_iban">BIC: %1$s, %2$s</string> <string name="dialog_transfer_money_bic_detected_from_iban">BIC: %1$s, %2$s</string>
<string name="dialog_transfer_money_could_not_determine_bic_from_iban">No BIC found for bank code %1$s</string> <string name="dialog_transfer_money_could_not_determine_bic_from_iban">No BIC found for bank code %1$s</string>
<string name="dialog_transfer_money_amount">Amount:</string> <string name="dialog_transfer_money_amount">Amount:</string>
<string name="dialog_transfer_money_usage">Usage:</string> <string name="dialog_transfer_money_reference">Reference:</string>
<string name="dialog_transfer_money_instant_payment">Instant payment (may with costs)</string> <string name="dialog_transfer_money_real_time_transfer">Real-time transfer (may with costs)</string>
<string name="dialog_transfer_money_instant_payment_info">Bank transfers are usually credited within one business day. However, this only applies during bank business hours, and therefore not at weekends and on public holidays. In addition, business hours vary from bank to bank. Mostly they are from 10 - 18 o\'clock, sometimes also until 22 o\'clock, but sometimes (e. g. Fridays) only until 14 o\'clock. <string name="dialog_transfer_money_real_time_transfer_info">Bank transfers are usually credited within one business day. However, this only applies during bank business hours, and therefore not at weekends and on public holidays. In addition, business hours vary from bank to bank. Mostly they are from 10 - 18 o\'clock, sometimes also until 22 o\'clock, but sometimes (e. g. Fridays) only until 14 o\'clock.
\n\nInstant payment transfers on the other hand are transferred within a maximum of 10 seconds, regardless of the day and time of day. \n\nReal-time transfers on the other hand are transferred within a maximum of 10 seconds, regardless of the day and time of day.
\n\nHowever, real-time transfers are often subject to a fee. \n\nHowever, real-time transfers are often subject to a fee.
\n\nUnfortunately, Bankmeister cannot know whether a bank charges for instant payment transfers. Please refer to the list of prices and services of your bank.</string> \n\nUnfortunately, Bankmeister cannot know whether a bank charges for real-time transfers. Please refer to the list of prices and services of your bank.</string>
<string name="dialog_transfer_money_transfer">Transfer</string> <string name="dialog_transfer_money_transfer">Transfer</string>
<string name="dialog_transfer_money_message_transfer_successful">Successfully transferred %1$s %2$s to %3$s.</string> <string name="dialog_transfer_money_message_transfer_successful">Successfully transferred %1$s %2$s to %3$s.</string>
<string name="dialog_transfer_money_message_transfer_failed">Could not transfer %1$s %2$s to %3$s.\n\nError message from your bank:\n\n%4$s</string> <string name="dialog_transfer_money_message_transfer_failed">Could not transfer %1$s %2$s to %3$s.\n\nError message from your bank:\n\n%4$s</string>
@ -110,17 +110,17 @@
\n\nThey can only be restored, if then still possible, by retrieving it again from your bank server.</string> \n\nThey can only be restored, if then still possible, by retrieving it again from your bank server.</string>
<string name="error_no_name_entered">Please enter remittee\'s name</string> <string name="error_no_name_entered">Please enter recipient\'s name</string>
<string name="error_entered_name_too_long">Name may only have 70 characters</string> <string name="error_entered_name_too_long">Name may only have 70 characters</string>
<string name="error_invalid_sepa_characters_entered">Invalid character(s) entered: %s</string> <string name="error_invalid_sepa_characters_entered">Invalid character(s) entered: %s</string>
<string name="error_no_iban_entered">Please enter remittee\'s IBAN</string> <string name="error_no_iban_entered">Please enter recipient\'s IBAN</string>
<string name="error_invalid_iban_characters_entered">Invalid character(s) entered: %s</string> <string name="error_invalid_iban_characters_entered">Invalid character(s) entered: %s</string>
<string name="error_invalid_iban_pattern_entered">IBAN has to have pattern: EN12 1234 5678 9012 3456 78</string> <string name="error_invalid_iban_pattern_entered">IBAN has to have pattern: EN12 1234 5678 9012 3456 78</string>
<string name="error_invalid_bic_characters_entered">Invalid character(s) entered: %s</string> <string name="error_invalid_bic_characters_entered">Invalid character(s) entered: %s</string>
<string name="error_invalid_bic_pattern_entered">A BIC consists of 8 or 11 characters and has the pattern: ABCDED12(XYZ)</string> <string name="error_invalid_bic_pattern_entered">A BIC consists of 8 or 11 characters and has the pattern: ABCDED12(XYZ)</string>
<string name="error_no_amount_entered">Please enter the amount to be transferred</string> <string name="error_no_amount_entered">Please enter the amount to be transferred</string>
<string name="error_invalid_amount_entered">Please enter an amount greater zero.</string> <string name="error_invalid_amount_entered">Please enter an amount greater zero.</string>
<string name="error_entered_usage_too_long">Usage may only have 140 characters</string> <string name="error_entered_recipient_too_long">Reference may only have 140 characters</string>
<string name="dialog_send_message_log_message_log_label">Message log</string> <string name="dialog_send_message_log_message_log_label">Message log</string>

View File

@ -12,7 +12,7 @@ import net.dankito.banking.util.BankIconFinder
import net.dankito.banking.bankfinder.LuceneBankFinder import net.dankito.banking.bankfinder.LuceneBankFinder
import net.dankito.banking.persistence.LuceneBankingPersistence import net.dankito.banking.persistence.LuceneBankingPersistence
import net.dankito.banking.persistence.mapper.EntitiesModelCreator import net.dankito.banking.persistence.mapper.EntitiesModelCreator
import net.dankito.banking.search.LuceneRemitteeSearcher import net.dankito.banking.search.LuceneTransactionPartySearcher
import net.dankito.banking.util.JacksonJsonSerializer import net.dankito.banking.util.JacksonJsonSerializer
import net.dankito.banking.util.extraction.JavaTextExtractorRegistry import net.dankito.banking.util.extraction.JavaTextExtractorRegistry
import net.dankito.text.extraction.TextExtractorRegistry import net.dankito.text.extraction.TextExtractorRegistry
@ -47,9 +47,9 @@ class MainWindow : View(messages["application.title"]) {
private val presenter = BankingPresenter(fints4kBankingClientCreator(modelCreator, serializer), private val presenter = BankingPresenter(fints4kBankingClientCreator(modelCreator, serializer),
LuceneBankFinder(indexFolder), dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), LuceneBankFinder(indexFolder), dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder),
RouterJavaFx(), modelCreator, LuceneRemitteeSearcher(indexFolder), BankIconFinder(), textExtractorRegistry) RouterJavaFx(), modelCreator, LuceneTransactionPartySearcher(indexFolder), BankIconFinder(), textExtractorRegistry)
// private val presenter = BankingPresenter(hbci4jBankingClientCreator(), LuceneBankFinder(indexFolder), // private val presenter = BankingPresenter(hbci4jBankingClientCreator(), LuceneBankFinder(indexFolder),
// dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), RouterJavaFx(), modelCreator, LuceneRemitteeSearcher(indexFolder), // dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), RouterJavaFx(), modelCreator, LuceneTransactionPartySearcher(indexFolder),
// BankIconFinder(), textExtractorRegistry) // BankIconFinder(), textExtractorRegistry)

View File

@ -28,9 +28,9 @@ account.transactions.control.view.balance.label=Balance:
account.transactions.control.view.could.not.retrieve.account.transactions=Could not retrieve account transactions for \'%1$s\'.\n\nError message from your bank:\n\n%2$s account.transactions.control.view.could.not.retrieve.account.transactions=Could not retrieve account transactions for \'%1$s\'.\n\nError message from your bank:\n\n%2$s
account.transactions.table.column.header.value.date=Value date account.transactions.table.column.header.value.date=Value date
account.transactions.table.column.header.usage=Usage account.transactions.table.column.header.reference=Reference
account.transactions.table.column.header.amount=Amount account.transactions.table.column.header.amount=Amount
account.transactions.table.context.menu.new.transfer.to.same.remittee=Transfer money to %s account.transactions.table.context.menu.new.transfer.to.same.recipient=Transfer money to %s
account.transactions.table.context.menu.new.transfer.with.same.data=New transfer with same data account.transactions.table.context.menu.new.transfer.with.same.data=New transfer with same data
account.transactions.table.context.menu.show.transaction.details=Show details account.transactions.table.context.menu.show.transaction.details=Show details
@ -62,13 +62,13 @@ enter.tan.dialog.tan.error.changing.tan.medium=Could not change TAN medium to \'
transfer.money.dialog.title=Transfer money transfer.money.dialog.title=Transfer money
transfer.money.dialog.account.label=Account: transfer.money.dialog.account.label=Account:
transfer.money.dialog.remittee.name.label=Remittee: transfer.money.dialog.recipient.name.label=Recipient:
transfer.money.dialog.remittee.iban.label=IBAN: transfer.money.dialog.recipient.iban.label=IBAN:
transfer.money.dialog.remittee.bank.label=Bank: transfer.money.dialog.recipient.bank.label=Bank:
transfer.money.dialog.remittee.bic.label=BIC: transfer.money.dialog.recipient.bic.label=BIC:
transfer.money.dialog.amount.label=Amount: transfer.money.dialog.amount.label=Amount:
transfer.money.dialog.usage.label=Usage: transfer.money.dialog.reference.label=Reference:
transfer.money.dialog.instant.payment.label=Instant payment transfer.money.dialog.real.time.transfer.label=Real-time transfer
transfer.money.dialog.transfer.money.label=Transfer transfer.money.dialog.transfer.money.label=Transfer
transfer.money.dialog.bank.name.will.be.entered.automatically=Will be entered automatically transfer.money.dialog.bank.name.will.be.entered.automatically=Will be entered automatically
transfer.money.dialog.bank.not.found.for.iban=No bank found for this IBAN transfer.money.dialog.bank.not.found.for.iban=No bank found for this IBAN

View File

@ -28,9 +28,9 @@ account.transactions.control.view.balance.label=Saldo:
account.transactions.control.view.could.not.retrieve.account.transactions=Kontoumsätze für \'%1$s\' konnten nicht empfangen werden.\n\nFehlermeldung Ihrer Bank:\n\n%2$s account.transactions.control.view.could.not.retrieve.account.transactions=Kontoumsätze für \'%1$s\' konnten nicht empfangen werden.\n\nFehlermeldung Ihrer Bank:\n\n%2$s
account.transactions.table.column.header.value.date=Buchungstag account.transactions.table.column.header.value.date=Buchungstag
account.transactions.table.column.header.usage=Verwendungszweck account.transactions.table.column.header.reference=Verwendungszweck
account.transactions.table.column.header.amount=Betrag account.transactions.table.column.header.amount=Betrag
account.transactions.table.context.menu.new.transfer.to.same.remittee=Neue Überweisung an %s account.transactions.table.context.menu.new.transfer.to.same.recipient=Neue Überweisung an %s
account.transactions.table.context.menu.new.transfer.with.same.data=Neue Überweisung mit gleichen Daten account.transactions.table.context.menu.new.transfer.with.same.data=Neue Überweisung mit gleichen Daten
account.transactions.table.context.menu.show.transaction.details=Details anzeigen account.transactions.table.context.menu.show.transaction.details=Details anzeigen
@ -62,13 +62,13 @@ enter.tan.dialog.tan.error.changing.tan.medium=TAN Medium konnte nicht geändert
transfer.money.dialog.title=Neue Überweisung transfer.money.dialog.title=Neue Überweisung
transfer.money.dialog.account.label=Konto: transfer.money.dialog.account.label=Konto:
transfer.money.dialog.remittee.name.label=Begünstigter (Name oder Firma): transfer.money.dialog.recipient.name.label=Begünstigter (Name oder Firma):
transfer.money.dialog.remittee.iban.label=IBAN: transfer.money.dialog.recipient.iban.label=IBAN:
transfer.money.dialog.remittee.bank.label=Bank: transfer.money.dialog.recipient.bank.label=Bank:
transfer.money.dialog.remittee.bic.label=BIC: transfer.money.dialog.recipient.bic.label=BIC:
transfer.money.dialog.amount.label=Betrag: transfer.money.dialog.amount.label=Betrag:
transfer.money.dialog.usage.label=Verwendungszweck: transfer.money.dialog.reference.label=Verwendungszweck:
transfer.money.dialog.instant.payment.label=Echtzeitüberweisung transfer.money.dialog.real.time.transfer.label=Echtzeitüberweisung
transfer.money.dialog.transfer.money.label=Überweisen transfer.money.dialog.transfer.money.label=Überweisen
transfer.money.dialog.bank.name.will.be.entered.automatically=Wird automatisch eingetragen transfer.money.dialog.bank.name.will.be.entered.automatically=Wird automatisch eingetragen
transfer.money.dialog.bank.not.found.for.iban=Für diese IBAN wurde keine Bank gefunden transfer.money.dialog.bank.not.found.for.iban=Für diese IBAN wurde keine Bank gefunden

View File

@ -48,7 +48,7 @@ open class AccountTransactionsTable @JvmOverloads constructor(
} }
} }
columns.add(TableColumn<IAccountTransaction, IAccountTransaction>(messages["account.transactions.table.column.header.usage"]).apply { columns.add(TableColumn<IAccountTransaction, IAccountTransaction>(messages["account.transactions.table.column.header.reference"]).apply {
this.cellFormat { this.cellFormat {
contentDisplay = ContentDisplay.GRAPHIC_ONLY contentDisplay = ContentDisplay.GRAPHIC_ONLY
@ -72,7 +72,7 @@ open class AccountTransactionsTable @JvmOverloads constructor(
} }
} }
label(it.usage) { label(it.reference) {
vboxConstraints { vboxConstraints {
margin = LabelMargin margin = LabelMargin
} }

View File

@ -84,8 +84,8 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi
contextMenu.apply { contextMenu.apply {
if (selectedItem.canCreateMoneyTransferFrom) { if (selectedItem.canCreateMoneyTransferFrom) {
item(String.format(FX.messages["account.transactions.table.context.menu.new.transfer.to.same.remittee"], selectedItem.otherPartyName)) { item(String.format(FX.messages["account.transactions.table.context.menu.new.transfer.to.same.recipient"], selectedItem.otherPartyName)) {
action { newTransferToSameRemittee(selectedItem) } action { newTransferToSameTransactionParty(selectedItem) }
} }
item(String.format(FX.messages["account.transactions.table.context.menu.new.transfer.with.same.data"], selectedItem.otherPartyName)) { item(String.format(FX.messages["account.transactions.table.context.menu.new.transfer.with.same.data"], selectedItem.otherPartyName)) {
@ -110,8 +110,8 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi
// presenter.showTransactionDetailsWindow(transaction.item) // presenter.showTransactionDetailsWindow(transaction.item)
} }
protected open fun newTransferToSameRemittee(transaction: IAccountTransaction) { protected open fun newTransferToSameTransactionParty(transaction: IAccountTransaction) {
presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndUsage(transaction)) presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndReference(transaction))
} }
protected open fun newTransferWithSameData(transaction: IAccountTransaction) { protected open fun newTransferWithSameData(transaction: IAccountTransaction) {

View File

@ -0,0 +1,29 @@
package net.dankito.banking.ui.javafx.dialogs.cashtransfer
import net.dankito.banking.search.TransactionParty
import tornadofx.*
open class RecipientListCellFragment : ListCellFragment<TransactionParty>() {
companion object {
const val ItemHeight = 60.0
}
open val recipient = RecipientViewModel().bindTo(this)
override val root = vbox {
label(recipient.name)
label(recipient.bankName) {
vboxConstraints {
marginTopBottom(6.0)
}
}
label(recipient.iban)
}
}

View File

@ -1,11 +1,11 @@
package net.dankito.banking.ui.javafx.dialogs.cashtransfer package net.dankito.banking.ui.javafx.dialogs.cashtransfer
import javafx.beans.property.SimpleStringProperty import javafx.beans.property.SimpleStringProperty
import net.dankito.banking.search.Remittee import net.dankito.banking.search.TransactionParty
import tornadofx.ItemViewModel import tornadofx.ItemViewModel
open class RemitteeViewModel : ItemViewModel<Remittee>() { open class RecipientViewModel : ItemViewModel<TransactionParty>() {
val name = bind { SimpleStringProperty(item?.name) } val name = bind { SimpleStringProperty(item?.name) }

View File

@ -1,29 +0,0 @@
package net.dankito.banking.ui.javafx.dialogs.cashtransfer
import net.dankito.banking.search.Remittee
import tornadofx.*
open class RemitteeListCellFragment : ListCellFragment<Remittee>() {
companion object {
const val ItemHeight = 60.0
}
open val remittee = RemitteeViewModel().bindTo(this)
override val root = vbox {
label(remittee.name)
label(remittee.bankName) {
vboxConstraints {
marginTopBottom(6.0)
}
}
label(remittee.iban)
}
}

View File

@ -18,7 +18,7 @@ import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.util.InputValidator import net.dankito.banking.util.InputValidator
import net.dankito.banking.bankfinder.BankInfo import net.dankito.banking.bankfinder.BankInfo
import net.dankito.banking.search.Remittee import net.dankito.banking.search.TransactionParty
import net.dankito.utils.multiplatform.toBigDecimal import net.dankito.utils.multiplatform.toBigDecimal
import net.dankito.banking.ui.javafx.extensions.focusNextControl import net.dankito.banking.ui.javafx.extensions.focusNextControl
import net.dankito.utils.javafx.ui.controls.AutoCompletionSearchTextField import net.dankito.utils.javafx.ui.controls.AutoCompletionSearchTextField
@ -53,30 +53,31 @@ open class TransferMoneyDialog @JvmOverloads constructor(
protected val showBankAccounts = SimpleBooleanProperty(bankAccountsSupportingTransferringMoney.size > 1) protected val showBankAccounts = SimpleBooleanProperty(bankAccountsSupportingTransferringMoney.size > 1)
protected val remitteeName = SimpleStringProperty(preselectedValues?.creditorName ?: "") protected val recipientName = SimpleStringProperty(preselectedValues?.recipientName ?: "")
protected val remitteeIban = SimpleStringProperty(preselectedValues?.creditorIban ?: "") protected val recipientIban = SimpleStringProperty(preselectedValues?.recipientAccountId ?: "")
protected val remitteeBank = SimpleObjectProperty<BankInfo>() protected val recipientBank = SimpleObjectProperty<BankInfo>()
protected val remitteeBankName = SimpleStringProperty() protected val recipientBankName = SimpleStringProperty()
protected val remitteeBic = SimpleStringProperty(preselectedValues?.creditorBic ?: "") protected val recipientBic = SimpleStringProperty(preselectedValues?.recipientBankCode ?: "")
protected val amount = SimpleDoubleProperty(preselectedValues?.amount?.toDouble() ?: 0.0) protected val amount = SimpleDoubleProperty(preselectedValues?.amount?.toDouble() ?: 0.0)
protected val usage = SimpleStringProperty(preselectedValues?.usage ?: "") protected val reference = SimpleStringProperty(preselectedValues?.reference ?: "")
protected val instantPayment = SimpleBooleanProperty(false) protected val realTimeTransfer = SimpleBooleanProperty(false)
protected val supportsInstantPayment = SimpleBooleanProperty(selectedBankAccount.value?.supportsInstantPaymentMoneyTransfer ?: false) protected val supportsRealTimeTransfer
= SimpleBooleanProperty(selectedBankAccount.value?.supportsRealTimeTransfer ?: false)
protected val requiredDataEntered = SimpleBooleanProperty(false) protected val requiredDataEntered = SimpleBooleanProperty(false)
protected var txtfldRemitteeName: AutoCompletionSearchTextField<Remittee> by singleAssign() protected var txtfldRecipientName: AutoCompletionSearchTextField<TransactionParty> by singleAssign()
protected var lastSearchRemitteeJob: Job? = null protected var lastSearchRecipientJob: Job? = null
protected val inputValidator = InputValidator() protected val inputValidator = InputValidator()
@ -87,11 +88,11 @@ open class TransferMoneyDialog @JvmOverloads constructor(
init { init {
selectedBankAccount.addListener { _, _, newValue -> selectedBankAccountChanged(newValue) } selectedBankAccount.addListener { _, _, newValue -> selectedBankAccountChanged(newValue) }
remitteeName.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() } recipientName.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() }
remitteeIban.addListener { _, _, newValue -> tryToGetBicFromIban(newValue) } recipientIban.addListener { _, _, newValue -> tryToGetBicFromIban(newValue) }
remitteeBic.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() } recipientBic.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() }
amount.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() } amount.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() }
usage.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() } reference.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() }
} }
@ -131,30 +132,30 @@ open class TransferMoneyDialog @JvmOverloads constructor(
} }
} }
field(messages["transfer.money.dialog.remittee.name.label"]) { field(messages["transfer.money.dialog.recipient.name.label"]) {
fixedHeight = FieldHeight fixedHeight = FieldHeight
txtfldRemitteeName = autocompletionsearchtextfield(this@TransferMoneyDialog.remitteeName) { txtfldRecipientName = autocompletionsearchtextfield(this@TransferMoneyDialog.recipientName) {
fixedHeight = TextFieldHeight fixedHeight = TextFieldHeight
textProperty().addListener { _, _, newValue -> searchRemittees(newValue) } textProperty().addListener { _, _, newValue -> searchRecipients(newValue) }
onAutoCompletion = { remitteeSelected(it) } onAutoCompletion = { recipientSelected(it) }
listCellFragment = RemitteeListCellFragment::class listCellFragment = RecipientListCellFragment::class
setPrefItemHeight(RemitteeListCellFragment.ItemHeight) setPrefItemHeight(RecipientListCellFragment.ItemHeight)
} }
} }
field(messages["transfer.money.dialog.remittee.iban.label"]) { field(messages["transfer.money.dialog.recipient.iban.label"]) {
fixedHeight = FieldHeight fixedHeight = FieldHeight
textfield(remitteeIban) { textfield(recipientIban) {
fixedHeight = TextFieldHeight fixedHeight = TextFieldHeight
paddingLeft = 8.0 paddingLeft = 8.0
if (this@TransferMoneyDialog.remitteeName.value.isNotBlank()) { if (this@TransferMoneyDialog.recipientName.value.isNotBlank()) {
runLater { runLater {
requestFocus() requestFocus()
} }
@ -162,10 +163,10 @@ open class TransferMoneyDialog @JvmOverloads constructor(
} }
} }
field(messages["transfer.money.dialog.remittee.bank.label"]) { field(messages["transfer.money.dialog.recipient.bank.label"]) {
fixedHeight = FieldHeight fixedHeight = FieldHeight
textfield(remitteeBankName) { textfield(recipientBankName) {
fixedHeight = TextFieldHeight fixedHeight = TextFieldHeight
isDisable = true isDisable = true
@ -174,10 +175,10 @@ open class TransferMoneyDialog @JvmOverloads constructor(
} }
} }
field(messages["transfer.money.dialog.remittee.bic.label"]) { field(messages["transfer.money.dialog.recipient.bic.label"]) {
fixedHeight = FieldHeight fixedHeight = FieldHeight
textfield(remitteeBic) { textfield(recipientBic) {
fixedHeight = TextFieldHeight fixedHeight = TextFieldHeight
isDisable = true isDisable = true
@ -197,7 +198,7 @@ open class TransferMoneyDialog @JvmOverloads constructor(
fixedWidth = 100.0 fixedWidth = 100.0
alignment = Pos.CENTER_RIGHT alignment = Pos.CENTER_RIGHT
if (this@TransferMoneyDialog.remitteeName.value.isNotBlank() && remitteeIban.value.isNotBlank()) { if (this@TransferMoneyDialog.recipientName.value.isNotBlank() && recipientIban.value.isNotBlank()) {
runLater { runLater {
requestFocus() requestFocus()
} }
@ -212,10 +213,10 @@ open class TransferMoneyDialog @JvmOverloads constructor(
} }
} }
field(messages["transfer.money.dialog.usage.label"]) { field(messages["transfer.money.dialog.reference.label"]) {
fixedHeight = FieldHeight fixedHeight = FieldHeight
textfield(usage) { textfield(reference) {
fixedHeight = TextFieldHeight fixedHeight = TextFieldHeight
} }
} }
@ -223,10 +224,10 @@ open class TransferMoneyDialog @JvmOverloads constructor(
field { field {
fixedHeight = FieldHeight fixedHeight = FieldHeight
checkbox(messages["transfer.money.dialog.instant.payment.label"], instantPayment) { checkbox(messages["transfer.money.dialog.real.time.transfer.label"], realTimeTransfer) {
fixedHeight = TextFieldHeight fixedHeight = TextFieldHeight
enableWhen(supportsInstantPayment) enableWhen(supportsRealTimeTransfer)
} }
} }
} }
@ -264,37 +265,37 @@ open class TransferMoneyDialog @JvmOverloads constructor(
} }
} }
tryToGetBicFromIban(remitteeIban.value) tryToGetBicFromIban(recipientIban.value)
} }
private fun selectedBankAccountChanged(newValue: TypedBankAccount?) { private fun selectedBankAccountChanged(newValue: TypedBankAccount?) {
supportsInstantPayment.value = newValue?.supportsInstantPaymentMoneyTransfer ?: false supportsRealTimeTransfer.value = newValue?.supportsRealTimeTransfer ?: false
if (supportsInstantPayment.value == false) { if (supportsRealTimeTransfer.value == false) {
instantPayment.value = false realTimeTransfer.value = false
} }
} }
protected open fun searchRemittees(query: String?) { protected open fun searchRecipients(query: String?) {
lastSearchRemitteeJob?.cancel() lastSearchRecipientJob?.cancel()
lastSearchRemitteeJob = GlobalScope.launch(Dispatchers.IO) { lastSearchRecipientJob = GlobalScope.launch(Dispatchers.IO) {
val potentialRemittees = presenter.findRemitteesForName(query?.toString() ?: "") val potentialRecipients = presenter.findRecipientsForName(query?.toString() ?: "")
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
txtfldRemitteeName.setAutoCompleteList(potentialRemittees) txtfldRecipientName.setAutoCompleteList(potentialRecipients)
} }
} }
} }
protected open fun remitteeSelected(remittee: Remittee) { protected open fun recipientSelected(transactionParty: TransactionParty) {
txtfldRemitteeName.focusNextControl() txtfldRecipientName.focusNextControl()
remitteeName.value = remittee.name recipientName.value = transactionParty.name
remitteeBic.value = remittee.bic recipientBic.value = transactionParty.bic
remitteeIban.value = remittee.iban recipientIban.value = transactionParty.iban
} }
@ -307,11 +308,11 @@ open class TransferMoneyDialog @JvmOverloads constructor(
} }
protected open fun showValuesForFoundBankOnUiThread(firstFoundBank: BankInfo?, enteredIban: String) { protected open fun showValuesForFoundBankOnUiThread(firstFoundBank: BankInfo?, enteredIban: String) {
remitteeBank.value = firstFoundBank recipientBank.value = firstFoundBank
remitteeBankName.value = determineFoundBankLabel(enteredIban, firstFoundBank) recipientBankName.value = determineFoundBankLabel(enteredIban, firstFoundBank)
remitteeBic.value = firstFoundBank?.bic ?: messages["transfer.money.dialog.bank.name.will.be.entered.automatically"] recipientBic.value = firstFoundBank?.bic ?: messages["transfer.money.dialog.bank.name.will.be.entered.automatically"]
checkIfRequiredDataEnteredOnUiThread() checkIfRequiredDataEnteredOnUiThread()
} }
@ -331,12 +332,12 @@ open class TransferMoneyDialog @JvmOverloads constructor(
protected open fun checkIfRequiredDataEnteredOnUiThread() { protected open fun checkIfRequiredDataEnteredOnUiThread() {
requiredDataEntered.value = requiredDataEntered.value =
remitteeName.value.isNotBlank() recipientName.value.isNotBlank()
&& inputValidator.isRemitteeNameValid(remitteeName.value) // TODO: show error message for illegal characters && inputValidator.isRecipientNameValid(recipientName.value) // TODO: show error message for illegal characters
&& inputValidator.isValidIban(remitteeIban.value) && inputValidator.isValidIban(recipientIban.value)
&& inputValidator.isValidBic(remitteeBic.value) && inputValidator.isValidBic(recipientBic.value)
&& amount.value > 0 && amount.value > 0
&& inputValidator.isUsageValid(usage.value) // TODO: show error message for illegal characters && inputValidator.isReferenceValid(reference.value) // TODO: show error message for illegal characters
} }
@ -345,17 +346,17 @@ open class TransferMoneyDialog @JvmOverloads constructor(
} }
protected open fun transferMoney() { protected open fun transferMoney() {
remitteeBank.value?.let { recipientBank.value?.let {
val account = selectedBankAccount.value val account = selectedBankAccount.value
val data = TransferMoneyData( val data = TransferMoneyData(
account, account,
inputValidator.convertToAllowedSepaCharacters(remitteeName.value), inputValidator.convertToAllowedSepaCharacters(recipientName.value),
remitteeIban.value.replace(" ", ""), recipientIban.value.replace(" ", ""),
remitteeBic.value.replace(" ", ""), recipientBic.value.replace(" ", ""),
amount.value.toBigDecimal().toBigDecimal(), amount.value.toBigDecimal().toBigDecimal(),
inputValidator.convertToAllowedSepaCharacters(usage.value), inputValidator.convertToAllowedSepaCharacters(reference.value),
instantPayment.value realTimeTransfer.value
) )
presenter.transferMoneyAsync(data) { presenter.transferMoneyAsync(data) {
@ -371,11 +372,11 @@ open class TransferMoneyDialog @JvmOverloads constructor(
if (response.successful) { if (response.successful) {
dialogService.showInfoMessage(String.format(messages["transfer.money.dialog.message.transfer.cash.success"], dialogService.showInfoMessage(String.format(messages["transfer.money.dialog.message.transfer.cash.success"],
transferData.amount, currency, transferData.creditorName), null, currentStage) transferData.amount, currency, transferData.recipientName), null, currentStage)
} }
else if (response.userCancelledAction == false) { else if (response.userCancelledAction == false) {
dialogService.showErrorMessage(String.format(messages["transfer.money.dialog.message.transfer.cash.error"], dialogService.showErrorMessage(String.format(messages["transfer.money.dialog.message.transfer.cash.error"],
transferData.amount, currency, transferData.creditorName, response.errorToShowToUser), null, null, currentStage) transferData.amount, currency, transferData.recipientName, response.errorToShowToUser), null, null, currentStage)
} }
if (response.successful || response.userCancelledAction) { // do not close dialog if an error occurred if (response.successful || response.userCancelledAction) { // do not close dialog if an error occurred

View File

@ -1,8 +0,0 @@
package net.dankito.banking.search
interface IRemitteeSearcher {
fun findRemittees(query: String): List<Remittee>
}

View File

@ -0,0 +1,8 @@
package net.dankito.banking.search
interface ITransactionPartySearcher {
fun findTransactionParty(query: String): List<TransactionParty>
}

View File

@ -1,10 +0,0 @@
package net.dankito.banking.search
open class NoOpRemitteeSearcher : IRemitteeSearcher {
override fun findRemittees(query: String): List<Remittee> {
return listOf()
}
}

View File

@ -0,0 +1,10 @@
package net.dankito.banking.search
open class NoOpTransactionPartySearcher : ITransactionPartySearcher {
override fun findTransactionParty(query: String): List<TransactionParty> {
return listOf()
}
}

View File

@ -1,7 +1,7 @@
package net.dankito.banking.search package net.dankito.banking.search
data class Remittee( data class TransactionParty(
val name: String, val name: String,
val iban: String?, val iban: String?,
val bic: String?, val bic: String?,
@ -14,7 +14,7 @@ data class Remittee(
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is Remittee) return false if (other !is TransactionParty) return false
if (name.equals(other.name, true) == false) return false if (name.equals(other.name, true) == false) return false
if (iban != other.iban) return false if (iban != other.iban) return false

View File

@ -8,7 +8,7 @@ open class AccountTransaction(
override val account: TypedBankAccount, override val account: TypedBankAccount,
override val amount: BigDecimal, override val amount: BigDecimal,
override val currency: String, override val currency: String,
override val unparsedUsage: String, override val unparsedReference: String,
override val bookingDate: Date, override val bookingDate: Date,
override val otherPartyName: String?, override val otherPartyName: String?,
override val otherPartyBankCode: String?, override val otherPartyBankCode: String?,
@ -27,10 +27,10 @@ open class AccountTransaction(
override val originatorsIdentificationCode: String?, override val originatorsIdentificationCode: String?,
override val compensationAmount: String?, override val compensationAmount: String?,
override val originalAmount: String?, override val originalAmount: String?,
override val sepaUsage: String?, override val sepaReference: String?,
override val deviantOriginator: String?, override val deviantOriginator: String?,
override val deviantRecipient: String?, override val deviantRecipient: String?,
override val usageWithNoSpecialType: String?, override val referenceWithNoSpecialType: String?,
override val primaNotaNumber: String?, override val primaNotaNumber: String?,
override val textKeySupplement: String?, override val textKeySupplement: String?,
@ -49,15 +49,15 @@ open class AccountTransaction(
/* convenience constructors for languages not supporting default values */ /* convenience constructors for languages not supporting default values */
constructor(account: BankAccount, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?) constructor(account: BankAccount, otherPartyName: String?, unparsedReference: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
: this(account, amount, "EUR", unparsedUsage, valueDate, : this(account, amount, "EUR", unparsedReference, valueDate,
otherPartyName, null, null, bookingText, valueDate) otherPartyName, null, null, bookingText, valueDate)
constructor(account: BankAccount, amount: BigDecimal, currency: String, unparsedUsage: String, bookingDate: Date, constructor(account: BankAccount, amount: BigDecimal, currency: String, unparsedReference: String, bookingDate: Date,
otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?,
bookingText: String?, valueDate: Date) bookingText: String?, valueDate: Date)
: this(account, amount, currency, unparsedUsage, bookingDate, : this(account, amount, currency, unparsedReference, bookingDate,
otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate,
0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null) 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null)

View File

@ -23,7 +23,7 @@ open class BankAccount @JvmOverloads constructor(
override var supportsRetrievingAccountTransactions: Boolean = false, override var supportsRetrievingAccountTransactions: Boolean = false,
override var supportsRetrievingBalance: Boolean = false, override var supportsRetrievingBalance: Boolean = false,
override var supportsTransferringMoney: Boolean = false, override var supportsTransferringMoney: Boolean = false,
override var supportsInstantPaymentMoneyTransfer: Boolean = false, override var supportsRealTimeTransfer: Boolean = false,
override var bookedTransactions: List<IAccountTransaction> = listOf(), override var bookedTransactions: List<IAccountTransaction> = listOf(),
override var unbookedTransactions: List<Any> = listOf() override var unbookedTransactions: List<Any> = listOf()
) : TypedBankAccount { ) : TypedBankAccount {

View File

@ -16,7 +16,7 @@ interface IAccountTransaction {
val account: IBankAccount<*> val account: IBankAccount<*>
val amount: BigDecimal val amount: BigDecimal
val currency: String val currency: String
val unparsedUsage: String val unparsedReference: String
val bookingDate: Date val bookingDate: Date
val otherPartyName: String? val otherPartyName: String?
val otherPartyBankCode: String? val otherPartyBankCode: String?
@ -35,10 +35,10 @@ interface IAccountTransaction {
val originatorsIdentificationCode: String? val originatorsIdentificationCode: String?
val compensationAmount: String? val compensationAmount: String?
val originalAmount: String? val originalAmount: String?
val sepaUsage: String? val sepaReference: String?
val deviantOriginator: String? val deviantOriginator: String?
val deviantRecipient: String? val deviantRecipient: String?
val usageWithNoSpecialType: String? val referenceWithNoSpecialType: String?
val primaNotaNumber: String? val primaNotaNumber: String?
val textKeySupplement: String? val textKeySupplement: String?
@ -54,7 +54,7 @@ interface IAccountTransaction {
var technicalId: String var technicalId: String
open val transactionIdentifier: String val transactionIdentifier: String
get() = buildTransactionIdentifier() get() = buildTransactionIdentifier()
@ -64,16 +64,16 @@ interface IAccountTransaction {
val canCreateMoneyTransferFrom: Boolean val canCreateMoneyTransferFrom: Boolean
get() = otherPartyAccountId != null && account.supportsTransferringMoney get() = otherPartyAccountId != null && account.supportsTransferringMoney
val usage: String val reference: String
get() = sepaUsage ?: unparsedUsage get() = sepaReference ?: unparsedReference
fun buildTransactionIdentifier() : String { fun buildTransactionIdentifier() : String {
if (account != null) { if (account != null) {
return "${account.technicalId} ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedUsage $otherPartyName $otherPartyBankCode $otherPartyAccountId" return "${account.technicalId} ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedReference $otherPartyName $otherPartyBankCode $otherPartyAccountId"
} }
else { // happens for derived classes during initialization. These have to set technicalId after initialization by themselves else { // happens for derived classes during initialization. These have to set technicalId after initialization by themselves
return "<uninitialized_bank_acccount> ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedUsage $otherPartyName $otherPartyBankCode $otherPartyAccountId" return "<uninitialized_bank_acccount> ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedReference $otherPartyName $otherPartyBankCode $otherPartyAccountId"
} }
} }
@ -86,7 +86,7 @@ interface IAccountTransaction {
if (account != other.account) return false if (account != other.account) return false
if (amount != other.amount) return false if (amount != other.amount) return false
if (currency != other.currency) return false if (currency != other.currency) return false
if (unparsedUsage != other.unparsedUsage) return false if (unparsedReference != other.unparsedReference) return false
if (bookingDate != other.bookingDate) return false if (bookingDate != other.bookingDate) return false
if (otherPartyName != other.otherPartyName) return false if (otherPartyName != other.otherPartyName) return false
if (otherPartyBankCode != other.otherPartyBankCode) return false if (otherPartyBankCode != other.otherPartyBankCode) return false
@ -101,7 +101,7 @@ interface IAccountTransaction {
var result = account.hashCode() var result = account.hashCode()
result = 31 * result + amount.hashCode() result = 31 * result + amount.hashCode()
result = 31 * result + currency.hashCode() result = 31 * result + currency.hashCode()
result = 31 * result + unparsedUsage.hashCode() result = 31 * result + unparsedReference.hashCode()
result = 31 * result + bookingDate.hashCode() result = 31 * result + bookingDate.hashCode()
result = 31 * result + (otherPartyName?.hashCode() ?: 0) result = 31 * result + (otherPartyName?.hashCode() ?: 0)
result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0) result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0)
@ -112,6 +112,6 @@ interface IAccountTransaction {
} }
val stringRepresentation: String val stringRepresentation: String
get() = "${DateFormatter(DateFormatStyle.Medium).format(valueDate)} $amount $otherPartyName: $usage" get() = "${DateFormatter(DateFormatStyle.Medium).format(valueDate)} $amount $otherPartyName: $reference"
} }

View File

@ -24,7 +24,7 @@ interface IBankAccount<TTransaction: IAccountTransaction> : OrderedDisplayable {
var supportsRetrievingAccountTransactions: Boolean var supportsRetrievingAccountTransactions: Boolean
var supportsRetrievingBalance: Boolean var supportsRetrievingBalance: Boolean
var supportsTransferringMoney: Boolean var supportsTransferringMoney: Boolean
var supportsInstantPaymentMoneyTransfer: Boolean var supportsRealTimeTransfer: Boolean
var bookedTransactions: List<TTransaction> var bookedTransactions: List<TTransaction>
var unbookedTransactions: List<Any> var unbookedTransactions: List<Any>
var technicalId: String var technicalId: String

View File

@ -22,7 +22,7 @@ open class DefaultModelCreator : IModelCreator {
account: TypedBankAccount, account: TypedBankAccount,
amount: BigDecimal, amount: BigDecimal,
currency: String, currency: String,
unparsedUsage: String, unparsedReference: String,
bookingDate: Date, bookingDate: Date,
otherPartyName: String?, otherPartyName: String?,
otherPartyBankCode: String?, otherPartyBankCode: String?,
@ -40,10 +40,10 @@ open class DefaultModelCreator : IModelCreator {
originatorsIdentificationCode: String?, originatorsIdentificationCode: String?,
compensationAmount: String?, compensationAmount: String?,
originalAmount: String?, originalAmount: String?,
sepaUsage: String?, sepaReference: String?,
deviantOriginator: String?, deviantOriginator: String?,
deviantRecipient: String?, deviantRecipient: String?,
usageWithNoSpecialType: String?, referenceWithNoSpecialType: String?,
primaNotaNumber: String?, primaNotaNumber: String?,
textKeySupplement: String?, textKeySupplement: String?,
currencyType: String?, currencyType: String?,
@ -55,11 +55,11 @@ open class DefaultModelCreator : IModelCreator {
relatedReferenceNumber: String? relatedReferenceNumber: String?
) : IAccountTransaction { ) : IAccountTransaction {
return AccountTransaction(account, amount, currency, unparsedUsage, bookingDate, return AccountTransaction(account, amount, currency, unparsedReference, bookingDate,
otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, statementNumber, sequenceNumber, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, statementNumber, sequenceNumber,
openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier, openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier,
originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient, originatorsIdentificationCode, compensationAmount, originalAmount, sepaReference, deviantOriginator, deviantRecipient,
usageWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner, referenceWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,
referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber) referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber)
} }

View File

@ -19,7 +19,7 @@ interface IModelCreator {
account: TypedBankAccount, account: TypedBankAccount,
amount: BigDecimal, amount: BigDecimal,
currency: String, currency: String,
unparsedUsage: String, unparsedReference: String,
bookingDate: Date, bookingDate: Date,
otherPartyName: String?, otherPartyName: String?,
otherPartyBankCode: String?, otherPartyBankCode: String?,
@ -38,10 +38,10 @@ interface IModelCreator {
originatorsIdentificationCode: String?, originatorsIdentificationCode: String?,
compensationAmount: String?, compensationAmount: String?,
originalAmount: String?, originalAmount: String?,
sepaUsage: String?, sepaReference: String?,
deviantOriginator: String?, deviantOriginator: String?,
deviantRecipient: String?, deviantRecipient: String?,
usageWithNoSpecialType: String?, referenceWithNoSpecialType: String?,
primaNotaNumber: String?, primaNotaNumber: String?,
textKeySupplement: String?, textKeySupplement: String?,

View File

@ -6,17 +6,17 @@ import net.dankito.utils.multiplatform.BigDecimal
open class TransferMoneyData( open class TransferMoneyData(
val account: TypedBankAccount, val account: TypedBankAccount,
val creditorName: String, val recipientName: String,
val creditorIban: String, val recipientAccountId: String,
val creditorBic: String, val recipientBankCode: String,
val amount: BigDecimal, val amount: BigDecimal,
val usage: String, val reference: String,
val instantPayment: Boolean = false val realTimeTransfer: Boolean = false
) { ) {
companion object { companion object {
fun fromAccountTransactionWithoutAmountAndUsage(transaction: IAccountTransaction): TransferMoneyData { fun fromAccountTransactionWithoutAmountAndReference(transaction: IAccountTransaction): TransferMoneyData {
return TransferMoneyData( return TransferMoneyData(
transaction.account as TypedBankAccount, transaction.account as TypedBankAccount,
transaction.otherPartyName ?: "", transaction.otherPartyName ?: "",
@ -34,7 +34,7 @@ open class TransferMoneyData(
transaction.otherPartyAccountId ?: "", transaction.otherPartyAccountId ?: "",
transaction.otherPartyBankCode ?: "", transaction.otherPartyBankCode ?: "",
if (transaction.amount.isPositive) transaction.amount else transaction.amount.negated(), if (transaction.amount.isPositive) transaction.amount else transaction.amount.negated(),
transaction.usage transaction.reference
) )
} }

View File

@ -12,9 +12,9 @@ import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.model.responses.GetTransactionsResponse import net.dankito.banking.ui.model.responses.GetTransactionsResponse
import net.dankito.banking.bankfinder.IBankFinder import net.dankito.banking.bankfinder.IBankFinder
import net.dankito.banking.bankfinder.BankInfo import net.dankito.banking.bankfinder.BankInfo
import net.dankito.banking.search.IRemitteeSearcher import net.dankito.banking.search.ITransactionPartySearcher
import net.dankito.banking.search.NoOpRemitteeSearcher import net.dankito.banking.search.NoOpTransactionPartySearcher
import net.dankito.banking.search.Remittee import net.dankito.banking.search.TransactionParty
import net.dankito.banking.ui.model.mapper.DefaultModelCreator import net.dankito.banking.ui.model.mapper.DefaultModelCreator
import net.dankito.banking.ui.model.mapper.IModelCreator import net.dankito.banking.ui.model.mapper.IModelCreator
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult
@ -39,7 +39,7 @@ open class BankingPresenter(
protected val persister: IBankingPersistence, protected val persister: IBankingPersistence,
protected val router: IRouter, protected val router: IRouter,
protected val modelCreator: IModelCreator = DefaultModelCreator(), protected val modelCreator: IModelCreator = DefaultModelCreator(),
protected val remitteeSearcher: IRemitteeSearcher = NoOpRemitteeSearcher(), protected val transactionPartySearcher: ITransactionPartySearcher = NoOpTransactionPartySearcher(),
protected val bankIconFinder: IBankIconFinder = NoOpBankIconFinder(), protected val bankIconFinder: IBankIconFinder = NoOpBankIconFinder(),
protected val textExtractorRegistry: ITextExtractorRegistry = NoOpTextExtractorRegistry(), protected val textExtractorRegistry: ITextExtractorRegistry = NoOpTextExtractorRegistry(),
protected val invoiceDataExtractor: IInvoiceDataExtractor = NoOpInvoiceDataExtractor(), protected val invoiceDataExtractor: IInvoiceDataExtractor = NoOpInvoiceDataExtractor(),
@ -553,16 +553,16 @@ open class BankingPresenter(
return bankFinder.findBankByNameBankCodeOrCity(query) return bankFinder.findBankByNameBankCodeOrCity(query)
} }
open fun findRemitteesForName(name: String): List<Remittee> { open fun findRecipientsForName(name: String): List<TransactionParty> {
return remitteeSearcher.findRemittees(name).map { remittee -> return transactionPartySearcher.findTransactionParty(name).map { recipient ->
remittee.bankName = tryToFindBankName(remittee) recipient.bankName = tryToFindBankName(recipient)
remittee recipient
}.toSet().toList() }.toSet().toList()
} }
protected open fun tryToFindBankName(remittee: Remittee): String? { protected open fun tryToFindBankName(transactionParty: TransactionParty): String? {
remittee.bic?.let { bic -> transactionParty.bic?.let { bic ->
bankFinder.findBankByBic(bic)?.name?.let { bankFinder.findBankByBic(bic)?.name?.let {
return it return it
} }
@ -574,7 +574,7 @@ open class BankingPresenter(
} }
} }
remittee.iban?.let { iban -> transactionParty.iban?.let { iban ->
if (iban.length > 12) { if (iban.length > 12) {
val bankCode = iban.substring(4, 12) val bankCode = iban.substring(4, 12)
return bankFinder.findBankByBankCode(bankCode).firstOrNull()?.name return bankFinder.findBankByBankCode(bankCode).firstOrNull()?.name
@ -598,7 +598,7 @@ open class BankingPresenter(
return transactions.filter { return transactions.filter {
it.otherPartyName?.toLowerCase()?.contains(queryLowercase) == true it.otherPartyName?.toLowerCase()?.contains(queryLowercase) == true
|| it.usage.toLowerCase().contains(queryLowercase) || it.reference.toLowerCase().contains(queryLowercase)
|| it.bookingText?.toLowerCase()?.contains(queryLowercase) == true || it.bookingText?.toLowerCase()?.contains(queryLowercase) == true
} }
} }

View File

@ -9,13 +9,13 @@ open class InputValidator {
companion object { companion object {
const val RemitteNameMaxLength = 70 const val RecipientNameMaxLength = 70
const val IbanMaxLength = 34 const val IbanMaxLength = 34
const val BicMaxLength = 11 const val BicMaxLength = 11
const val UsageMaxLength = 140 const val ReferenceMaxLength = 140
const val MinimumLengthToDetermineBicFromIban = 12 // TODO: this is only true for German (and may some other) IBANs const val MinimumLengthToDetermineBicFromIban = 12 // TODO: this is only true for German (and may some other) IBANs
@ -65,45 +65,45 @@ open class InputValidator {
protected val sepaMessageCreator: ISepaMessageCreator = SepaMessageCreator() protected val sepaMessageCreator: ISepaMessageCreator = SepaMessageCreator()
open fun validateRemitteeNameWhileTyping(remitteeNameToTest: String): ValidationResult { open fun validateRecipientNameWhileTyping(recipientNameToTest: String): ValidationResult {
return validateRemitteeName(remitteeNameToTest, true) return validateRecipientName(recipientNameToTest, true)
} }
open fun validateRemitteeName(remitteeNameToTest: String): ValidationResult { open fun validateRecipientName(recipientNameToTest: String): ValidationResult {
return validateRemitteeName(remitteeNameToTest, false) return validateRecipientName(recipientNameToTest, false)
} }
open fun validateRemitteeName(remitteeNameToTest: String, userIsStillTyping: Boolean = false): ValidationResult { open fun validateRecipientName(recipientNameToTest: String, userIsStillTyping: Boolean = false): ValidationResult {
if (isRemitteeNameValid(remitteeNameToTest)) { if (isRecipientNameValid(recipientNameToTest)) {
return ValidationResult(remitteeNameToTest, true) return ValidationResult(recipientNameToTest, true)
} }
if (remitteeNameToTest.isEmpty()) { if (recipientNameToTest.isEmpty()) {
if (userIsStillTyping) { // if user is still typing, don't check if something has been entered yet if (userIsStillTyping) { // if user is still typing, don't check if something has been entered yet
return ValidationResult(remitteeNameToTest, true) return ValidationResult(recipientNameToTest, true)
} }
return ValidationResult(remitteeNameToTest, false, validationError = "Bitte geben Sie den Namen des Empfängers ein") // TODO: translate return ValidationResult(recipientNameToTest, false, validationError = "Bitte geben Sie den Namen des Empfängers ein") // TODO: translate
} }
if (hasRemitteeNameValidLength(remitteeNameToTest) == false) { if (hasRecipientNameValidLength(recipientNameToTest) == false) {
val correctedString = remitteeNameToTest.substring(0, RemitteNameMaxLength) val correctedString = recipientNameToTest.substring(0, RecipientNameMaxLength)
return ValidationResult(remitteeNameToTest, isRemitteeNameValid(correctedString), true, correctedString, "Name darf maximal 70 Zeichen lang sein") // TODO: translate return ValidationResult(recipientNameToTest, isRecipientNameValid(correctedString), true, correctedString, "Name darf maximal 70 Zeichen lang sein") // TODO: translate
} }
val invalidRemitteeNameCharacters = getInvalidSepaCharacters(remitteeNameToTest) val invalidRecipientNameCharacters = getInvalidSepaCharacters(recipientNameToTest)
val correctedString = getCorrectedString(remitteeNameToTest, invalidRemitteeNameCharacters, true) val correctedString = getCorrectedString(recipientNameToTest, invalidRecipientNameCharacters, true)
return ValidationResult(remitteeNameToTest, isRemitteeNameValid(correctedString), true, correctedString, null, "Unzulässige(s) Zeichen eingegeben: $invalidRemitteeNameCharacters") // TODO: translate return ValidationResult(recipientNameToTest, isRecipientNameValid(correctedString), true, correctedString, null, "Unzulässige(s) Zeichen eingegeben: $invalidRecipientNameCharacters") // TODO: translate
} }
open fun isRemitteeNameValid(stringToTest: String): Boolean { open fun isRecipientNameValid(stringToTest: String): Boolean {
return hasRemitteeNameValidLength(stringToTest) return hasRecipientNameValidLength(stringToTest)
&& containsOnlyValidSepaCharacters(stringToTest) && containsOnlyValidSepaCharacters(stringToTest)
} }
open fun hasRemitteeNameValidLength(stringToTest: String): Boolean { open fun hasRecipientNameValidLength(stringToTest: String): Boolean {
return stringToTest.length in 1..RemitteNameMaxLength return stringToTest.length in 1..RecipientNameMaxLength
} }
@ -221,29 +221,29 @@ open class InputValidator {
} }
open fun validateUsage(usageToTest: String): ValidationResult { open fun validateReference(referenceToTest: String): ValidationResult {
if (isUsageValid(usageToTest)) { if (isReferenceValid(referenceToTest)) {
return ValidationResult(usageToTest, true) return ValidationResult(referenceToTest, true)
} }
if (hasUsageValidLength(usageToTest) == false) { if (hasReferenceValidLength(referenceToTest) == false) {
val correctedString = usageToTest.substring(0, UsageMaxLength) val correctedString = referenceToTest.substring(0, ReferenceMaxLength)
return ValidationResult(usageToTest, isUsageValid(correctedString), true, correctedString, "Verwendungszweck darf nur 140 Zeichen lang sein") // TODO: translate return ValidationResult(referenceToTest, isReferenceValid(correctedString), true, correctedString, "Verwendungszweck darf nur 140 Zeichen lang sein") // TODO: translate
} }
val invalidUsageCharacters = getInvalidSepaCharacters(usageToTest) val invalidReferenceCharacters = getInvalidSepaCharacters(referenceToTest)
val correctedString = getCorrectedString(usageToTest, invalidUsageCharacters, true) val correctedString = getCorrectedString(referenceToTest, invalidReferenceCharacters, true)
return ValidationResult(usageToTest, isUsageValid(correctedString), true, correctedString, null, "Unzulässige(s) Zeichen eingegeben: $invalidUsageCharacters") // TODO: translate return ValidationResult(remitteeNameToTest, false, validationError = "Unzulässige(s) Zeichen eingegeben: ") // TODO: translate return ValidationResult(referenceToTest, isReferenceValid(correctedString), true, correctedString, null, "Unzulässige(s) Zeichen eingegeben: $invalidReferenceCharacters") // TODO: translate return ValidationResult(recipentNameToTest, false, validationError = "Unzulässige(s) Zeichen eingegeben: ") // TODO: translate
} }
open fun isUsageValid(stringToTest: String): Boolean { open fun isReferenceValid(stringToTest: String): Boolean {
return hasUsageValidLength(stringToTest) return hasReferenceValidLength(stringToTest)
&& containsOnlyValidSepaCharacters(stringToTest) && containsOnlyValidSepaCharacters(stringToTest)
} }
open fun hasUsageValidLength(stringToTest: String): Boolean { open fun hasReferenceValidLength(stringToTest: String): Boolean {
return stringToTest.length in 0..UsageMaxLength // usage is not a required field -> may be empty return stringToTest.length in 0..ReferenceMaxLength // reference is not a required field -> may be empty
} }
@ -265,7 +265,7 @@ open class InputValidator {
} }
// TODO: do not convert XML entities in user's. User will a) not understand what happened and b) afterwards auto correction will not work anymore (i think the issue lies in used Regex: '(&\w{2,4};)'). // TODO: do not convert XML entities in user's. User will a) not understand what happened and b) afterwards auto correction will not work anymore (i think the issue lies in used Regex: '(&\w{2,4};)').
// But take converted XML entities length into account when checking if remittee's name and usage length isn't too long // But take converted XML entities length into account when checking if recipient's name and reference length isn't too long
protected open fun getCorrectedString(inputString: String, invalidCharacters: String, convertToAllowedSepaCharacters: Boolean = false): String { protected open fun getCorrectedString(inputString: String, invalidCharacters: String, convertToAllowedSepaCharacters: Boolean = false): String {
var correctedString = if (convertToAllowedSepaCharacters) convertToAllowedSepaCharacters(inputString) else inputString var correctedString = if (convertToAllowedSepaCharacters) convertToAllowedSepaCharacters(inputString) else inputString

View File

@ -10,13 +10,13 @@ class InputValidatorTest {
companion object { companion object {
const val ValidRemitteeName = "Marieke Musterfrau" const val ValidRecipientName = "Marieke Musterfrau"
const val ValidIban = "DE11123456780987654321" const val ValidIban = "DE11123456780987654321"
const val ValidBic = "ABCDDEBBXXX" const val ValidBic = "ABCDDEBBXXX"
const val ValidUsage = "Usage" const val ValidReference = "Reference"
const val InvalidSepaCharacter = "!" const val InvalidSepaCharacter = "!"
@ -56,13 +56,13 @@ class InputValidatorTest {
@Test @Test
fun validateRemitteeName_EmptyStringEntered() { fun validateRecipientName_EmptyStringEntered() {
// given // given
val enteredName = "" val enteredName = ""
// when // when
val result = underTest.validateRemitteeName(enteredName) val result = underTest.validateRecipientName(enteredName)
// then // then
expect(result.validationSuccessful).toBe(false) expect(result.validationSuccessful).toBe(false)
@ -74,13 +74,13 @@ class InputValidatorTest {
} }
@Test @Test
fun validateRemitteeName_ValidNameEntered() { fun validateRecipientName_ValidNameEntered() {
// given // given
val enteredName = ValidRemitteeName val enteredName = ValidRecipientName
// when // when
val result = underTest.validateRemitteeName(enteredName) val result = underTest.validateRecipientName(enteredName)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
@ -92,50 +92,50 @@ class InputValidatorTest {
} }
@Test @Test
fun validateRemitteeName_UmlautGetsConverted() { fun validateRecipientName_UmlautGetsConverted() {
// given // given
val enteredName = ValidRemitteeName + InvalidUmlaut val enteredName = ValidRecipientName + InvalidUmlaut
// when // when
val result = underTest.validateRemitteeName(enteredName) val result = underTest.validateRecipientName(enteredName)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
expect(result.didCorrectString).toBe(true) expect(result.didCorrectString).toBe(true)
expect(result.inputString).toBe(enteredName) expect(result.inputString).toBe(enteredName)
expect(result.correctedInputString).toBe(ValidRemitteeName + ConvertedInvalidUmlaut) expect(result.correctedInputString).toBe(ValidRecipientName + ConvertedInvalidUmlaut)
expect(result.validationHint?.contains(InvalidUmlaut)).toBe(true) expect(result.validationHint?.contains(InvalidUmlaut)).toBe(true)
expect(result.validationError).toBe(null) expect(result.validationError).toBe(null)
} }
@Test @Test
fun validateRemitteeName_InvalidCharacterGetsRemoved() { fun validateRecipientName_InvalidCharacterGetsRemoved() {
// given // given
val enteredName = ValidRemitteeName + InvalidSepaCharacter val enteredName = ValidRecipientName + InvalidSepaCharacter
// when // when
val result = underTest.validateRemitteeName(enteredName) val result = underTest.validateRecipientName(enteredName)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
expect(result.didCorrectString).toBe(true) expect(result.didCorrectString).toBe(true)
expect(result.inputString).toBe(enteredName) expect(result.inputString).toBe(enteredName)
expect(result.correctedInputString).toBe(ValidRemitteeName) expect(result.correctedInputString).toBe(ValidRecipientName)
expect(result.validationHint?.contains(InvalidSepaCharacter)).toBe(true) expect(result.validationHint?.contains(InvalidSepaCharacter)).toBe(true)
expect(result.validationError).toBe(null) expect(result.validationError).toBe(null)
} }
@Test @Test
fun validateRemitteeName_TooLong() { fun validateRecipientName_TooLong() {
// given // given
val nameWithMaxLength = IntRange(0, InputValidator.RemitteNameMaxLength - 1).map { "a" }.joinToString("") val nameWithMaxLength = IntRange(0, InputValidator.RemitteNameMaxLength - 1).map { "a" }.joinToString("")
val enteredName = nameWithMaxLength + "a" val enteredName = nameWithMaxLength + "a"
// when // when
val result = underTest.validateRemitteeName(enteredName) val result = underTest.validateRecipientName(enteredName)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
@ -480,133 +480,133 @@ class InputValidatorTest {
@Test @Test
fun validateUsage_EmptyStringEntered() { fun validateReference_EmptyStringEntered() {
// given // given
val enteredUsage = "" val enteredReference = ""
// when // when
val result = underTest.validateUsage(enteredUsage) val result = underTest.validateReference(enteredReference)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
expect(result.didCorrectString).toBe(false) expect(result.didCorrectString).toBe(false)
expect(result.inputString).toBe(enteredUsage) expect(result.inputString).toBe(enteredReference)
expect(result.correctedInputString).toBe(enteredUsage) expect(result.correctedInputString).toBe(enteredReference)
expect(result.validationHint).toBe(null) expect(result.validationHint).toBe(null)
expect(result.validationError).toBe(null) expect(result.validationError).toBe(null)
} }
@Test @Test
fun validateUsage_ValidUsageEntered() { fun validateReference_ValidReferenceEntered() {
// given // given
val enteredUsage = ValidUsage val enteredReference = ValidReference
// when // when
val result = underTest.validateUsage(enteredUsage) val result = underTest.validateReference(enteredReference)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
expect(result.didCorrectString).toBe(false) expect(result.didCorrectString).toBe(false)
expect(result.inputString).toBe(enteredUsage) expect(result.inputString).toBe(enteredReference)
expect(result.correctedInputString).toBe(enteredUsage) expect(result.correctedInputString).toBe(enteredReference)
expect(result.validationHint).toBe(null) expect(result.validationHint).toBe(null)
expect(result.validationError).toBe(null) expect(result.validationError).toBe(null)
} }
@Test @Test
fun validateUsage_UmlautGetsConverted() { fun validateReference_UmlautGetsConverted() {
// given // given
val enteredUsage = ValidUsage + InvalidUmlaut val enteredReference = ValidReference + InvalidUmlaut
// when // when
val result = underTest.validateUsage(enteredUsage) val result = underTest.validateReference(enteredReference)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
expect(result.didCorrectString).toBe(true) expect(result.didCorrectString).toBe(true)
expect(result.inputString).toBe(enteredUsage) expect(result.inputString).toBe(enteredReference)
expect(result.correctedInputString).toBe(ValidUsage + ConvertedInvalidUmlaut) expect(result.correctedInputString).toBe(ValidReference + ConvertedInvalidUmlaut)
expect(result.validationHint?.contains(InvalidUmlaut)).toBe(true) expect(result.validationHint?.contains(InvalidUmlaut)).toBe(true)
expect(result.validationError).toBe(null) expect(result.validationError).toBe(null)
} }
@Test @Test
fun validateUsage_InvalidCharacterGetsRemoved() { fun validateReference_InvalidCharacterGetsRemoved() {
// given // given
val enteredUsage = ValidUsage + InvalidSepaCharacter val enteredReference = ValidReference + InvalidSepaCharacter
// when // when
val result = underTest.validateUsage(enteredUsage) val result = underTest.validateReference(enteredReference)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
expect(result.didCorrectString).toBe(true) expect(result.didCorrectString).toBe(true)
expect(result.inputString).toBe(enteredUsage) expect(result.inputString).toBe(enteredReference)
expect(result.correctedInputString).toBe(ValidUsage) expect(result.correctedInputString).toBe(ValidReference)
expect(result.validationHint?.contains(InvalidSepaCharacter)).toBe(true) expect(result.validationHint?.contains(InvalidSepaCharacter)).toBe(true)
expect(result.validationError).toBe(null) expect(result.validationError).toBe(null)
} }
// TODO: does not work yet // TODO: does not work yet
@Test @Test
fun validateUsage_AmpersandGetsRemoved() { fun validateReference_AmpersandGetsRemoved() {
// given // given
val invalidSepaCharacter = "&" val invalidSepaCharacter = "&"
val enteredUsage = ValidUsage + invalidSepaCharacter val enteredReference = ValidReference + invalidSepaCharacter
// when // when
val result = underTest.validateUsage(enteredUsage) val result = underTest.validateReference(enteredReference)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
expect(result.didCorrectString).toBe(true) expect(result.didCorrectString).toBe(true)
expect(result.inputString).toBe(enteredUsage) expect(result.inputString).toBe(enteredReference)
expect(result.correctedInputString).toBe(ValidUsage) expect(result.correctedInputString).toBe(ValidReference)
expect(result.validationHint?.contains(invalidSepaCharacter)).toBe(true) expect(result.validationHint?.contains(invalidSepaCharacter)).toBe(true)
expect(result.validationError).toBe(null) expect(result.validationError).toBe(null)
} }
// TODO: does not work yet // TODO: does not work yet
@Test @Test
fun validateUsage_EnteringACharacterAfterConvertingAXmlEntityDoesNotFail() { fun validateReference_EnteringACharacterAfterConvertingAXmlEntityDoesNotFail() {
// given // given
val convertedXmlEntity = "&amp;" val convertedXmlEntity = "&amp;"
val validSepaCharacter = "h" val validSepaCharacter = "h"
val enteredUsage = ValidUsage + convertedXmlEntity + validSepaCharacter val enteredReference = ValidReference + convertedXmlEntity + validSepaCharacter
// when // when
val result = underTest.validateUsage(enteredUsage) val result = underTest.validateReference(enteredReference)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
expect(result.didCorrectString).toBe(true) expect(result.didCorrectString).toBe(true)
expect(result.inputString).toBe(enteredUsage) expect(result.inputString).toBe(enteredReference)
expect(result.correctedInputString).toBe(ValidUsage + convertedXmlEntity + validSepaCharacter) expect(result.correctedInputString).toBe(ValidReference + convertedXmlEntity + validSepaCharacter)
expect(result.validationHint).toBe(null) expect(result.validationHint).toBe(null)
expect(result.validationError).toBe(null) expect(result.validationError).toBe(null)
} }
@Test @Test
fun validateUsage_TooLong() { fun validateReference_TooLong() {
// given // given
val usageWithMaxLength = IntRange(0, InputValidator.UsageMaxLength - 1).map { "a" }.joinToString("") val referenceWithMaxLength = IntRange(0, InputValidator.ReferenceMaxLength - 1).map { "a" }.joinToString("")
val enteredUsage = usageWithMaxLength + "a" val enteredReference = referenceWithMaxLength + "a"
// when // when
val result = underTest.validateUsage(enteredUsage) val result = underTest.validateReference(enteredReference)
// then // then
expect(result.validationSuccessful).toBe(true) expect(result.validationSuccessful).toBe(true)
expect(result.didCorrectString).toBe(true) expect(result.didCorrectString).toBe(true)
expect(result.inputString).toBe(enteredUsage) expect(result.inputString).toBe(enteredReference)
expect(result.correctedInputString).toBe(usageWithMaxLength) expect(result.correctedInputString).toBe(referenceWithMaxLength)
expect(result.validationHint).toBe(null) expect(result.validationHint).toBe(null)
expect(result.validationError).notToBeNull() expect(result.validationError).notToBeNull()
} }

View File

@ -3,7 +3,7 @@ package net.dankito.banking
import net.dankito.banking.bankfinder.InMemoryBankFinder import net.dankito.banking.bankfinder.InMemoryBankFinder
import net.dankito.banking.fints.webclient.IWebClient import net.dankito.banking.fints.webclient.IWebClient
import net.dankito.banking.persistence.IBankingPersistence import net.dankito.banking.persistence.IBankingPersistence
import net.dankito.banking.search.IRemitteeSearcher import net.dankito.banking.search.ITransactionPartySearcher
import net.dankito.banking.ui.IRouter import net.dankito.banking.ui.IRouter
import net.dankito.banking.ui.model.mapper.DefaultModelCreator import net.dankito.banking.ui.model.mapper.DefaultModelCreator
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
@ -14,8 +14,8 @@ import net.dankito.utils.multiplatform.File
class BankingPresenterSwift(dataFolder: File, router: IRouter, webClient: IWebClient, persistence: IBankingPersistence, class BankingPresenterSwift(dataFolder: File, router: IRouter, webClient: IWebClient, persistence: IBankingPersistence,
remitteeSearcher: IRemitteeSearcher, bankIconFinder: IBankIconFinder, serializer: ISerializer, asyncRunner: IAsyncRunner) transactionPartySearcher: ITransactionPartySearcher, bankIconFinder: IBankIconFinder, serializer: ISerializer, asyncRunner: IAsyncRunner)
: BankingPresenter(fints4kBankingClientCreator(DefaultModelCreator(), serializer, webClient), InMemoryBankFinder(), dataFolder, persistence, router, DefaultModelCreator(), : BankingPresenter(fints4kBankingClientCreator(DefaultModelCreator(), serializer, webClient), InMemoryBankFinder(), dataFolder, persistence, router, DefaultModelCreator(),
remitteeSearcher, bankIconFinder, NoOpTextExtractorRegistry(), NoOpInvoiceDataExtractor(), serializer, asyncRunner) { transactionPartySearcher, bankIconFinder, NoOpTextExtractorRegistry(), NoOpInvoiceDataExtractor(), serializer, asyncRunner) {
} }

View File

@ -28,11 +28,11 @@
3642F01425018BA9005186FE /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F01325018BA9005186FE /* TabBarController.swift */; }; 3642F01425018BA9005186FE /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F01325018BA9005186FE /* TabBarController.swift */; };
3642F01625018DA1005186FE /* InterceptTabClickViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F01525018DA1005186FE /* InterceptTabClickViewController.swift */; }; 3642F01625018DA1005186FE /* InterceptTabClickViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F01525018DA1005186FE /* InterceptTabClickViewController.swift */; };
3642F0182502723A005186FE /* UIKitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F0172502723A005186FE /* UIKitButton.swift */; }; 3642F0182502723A005186FE /* UIKitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F0172502723A005186FE /* UIKitButton.swift */; };
3642F01A2502931F005186FE /* InstantPaymentInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F0192502931F005186FE /* InstantPaymentInfoView.swift */; }; 3642F01A2502931F005186FE /* RealTimeTransferInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F0192502931F005186FE /* RealTimeTransferInfoView.swift */; };
3642F04B25031157005186FE /* SectionHeaderWithRightAlignedEditButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F04A25031157005186FE /* SectionHeaderWithRightAlignedEditButton.swift */; }; 3642F04B25031157005186FE /* SectionHeaderWithRightAlignedEditButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F04A25031157005186FE /* SectionHeaderWithRightAlignedEditButton.swift */; };
366FA4DA24C472A90094F009 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4D924C472A90094F009 /* Extensions.swift */; }; 366FA4DA24C472A90094F009 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4D924C472A90094F009 /* Extensions.swift */; };
366FA4DC24C479120094F009 /* BankInfoListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4DB24C479120094F009 /* BankInfoListItem.swift */; }; 366FA4DC24C479120094F009 /* BankInfoListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4DB24C479120094F009 /* BankInfoListItem.swift */; };
366FA4E024C4924A0094F009 /* RemitteeListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */; }; 366FA4E024C4924A0094F009 /* RecipientListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4DF24C4924A0094F009 /* RecipientListItem.swift */; };
366FA4E224C4ED6C0094F009 /* EnterTanDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */; }; 366FA4E224C4ED6C0094F009 /* EnterTanDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */; };
366FA4E624C6EBF40094F009 /* EnterTanState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E524C6EBF40094F009 /* EnterTanState.swift */; }; 366FA4E624C6EBF40094F009 /* EnterTanState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E524C6EBF40094F009 /* EnterTanState.swift */; };
3684EB8B2508F6F00001139E /* SearchBarWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3684EB8A2508F6F00001139E /* SearchBarWithLabel.swift */; }; 3684EB8B2508F6F00001139E /* SearchBarWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3684EB8A2508F6F00001139E /* SearchBarWithLabel.swift */; };
@ -175,11 +175,11 @@
3642F01325018BA9005186FE /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = "<group>"; }; 3642F01325018BA9005186FE /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = "<group>"; };
3642F01525018DA1005186FE /* InterceptTabClickViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterceptTabClickViewController.swift; sourceTree = "<group>"; }; 3642F01525018DA1005186FE /* InterceptTabClickViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterceptTabClickViewController.swift; sourceTree = "<group>"; };
3642F0172502723A005186FE /* UIKitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitButton.swift; sourceTree = "<group>"; }; 3642F0172502723A005186FE /* UIKitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitButton.swift; sourceTree = "<group>"; };
3642F0192502931F005186FE /* InstantPaymentInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPaymentInfoView.swift; sourceTree = "<group>"; }; 3642F0192502931F005186FE /* RealTimeTransferInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealTimeTransferInfoView.swift; sourceTree = "<group>"; };
3642F04A25031157005186FE /* SectionHeaderWithRightAlignedEditButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderWithRightAlignedEditButton.swift; sourceTree = "<group>"; }; 3642F04A25031157005186FE /* SectionHeaderWithRightAlignedEditButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderWithRightAlignedEditButton.swift; sourceTree = "<group>"; };
366FA4D924C472A90094F009 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; }; 366FA4D924C472A90094F009 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
366FA4DB24C479120094F009 /* BankInfoListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankInfoListItem.swift; sourceTree = "<group>"; }; 366FA4DB24C479120094F009 /* BankInfoListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankInfoListItem.swift; sourceTree = "<group>"; };
366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemitteeListItem.swift; sourceTree = "<group>"; }; 366FA4DF24C4924A0094F009 /* RecipientListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientListItem.swift; sourceTree = "<group>"; };
366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterTanDialog.swift; sourceTree = "<group>"; }; 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterTanDialog.swift; sourceTree = "<group>"; };
366FA4E524C6EBF40094F009 /* EnterTanState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterTanState.swift; sourceTree = "<group>"; }; 366FA4E524C6EBF40094F009 /* EnterTanState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterTanState.swift; sourceTree = "<group>"; };
3684EB8A2508F6F00001139E /* SearchBarWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarWithLabel.swift; sourceTree = "<group>"; }; 3684EB8A2508F6F00001139E /* SearchBarWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarWithLabel.swift; sourceTree = "<group>"; };
@ -340,7 +340,7 @@
36BCF88424C098C8005BEC29 /* BankAccountListItem.swift */, 36BCF88424C098C8005BEC29 /* BankAccountListItem.swift */,
36BE066424CDE62800CBBB68 /* AccountTransactionListItem.swift */, 36BE066424CDE62800CBBB68 /* AccountTransactionListItem.swift */,
366FA4DB24C479120094F009 /* BankInfoListItem.swift */, 366FA4DB24C479120094F009 /* BankInfoListItem.swift */,
366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */, 366FA4DF24C4924A0094F009 /* RecipientListItem.swift */,
36E21EDE24DCCC2700649DC8 /* CheckmarkListItem.swift */, 36E21EDE24DCCC2700649DC8 /* CheckmarkListItem.swift */,
); );
path = listitems; path = listitems;
@ -552,7 +552,7 @@
36BE06B424CF85A300CBBB68 /* AmountLabel.swift */, 36BE06B424CF85A300CBBB68 /* AmountLabel.swift */,
360782CE24F3D6610098FEFE /* InfoLabel.swift */, 360782CE24F3D6610098FEFE /* InfoLabel.swift */,
360782C224E49FF70098FEFE /* ValidationLabel.swift */, 360782C224E49FF70098FEFE /* ValidationLabel.swift */,
3642F0192502931F005186FE /* InstantPaymentInfoView.swift */, 3642F0192502931F005186FE /* RealTimeTransferInfoView.swift */,
36BE065C24CB08FB00CBBB68 /* LazyView.swift */, 36BE065C24CB08FB00CBBB68 /* LazyView.swift */,
36C4009A24D2F9E4005227AD /* IconedTitleView.swift */, 36C4009A24D2F9E4005227AD /* IconedTitleView.swift */,
36E21ECE24DA0EEE00649DC8 /* IconView.swift */, 36E21ECE24DA0EEE00649DC8 /* IconView.swift */,
@ -756,7 +756,7 @@
360782D124F3F4120098FEFE /* SelectorWrapper.swift in Sources */, 360782D124F3F4120098FEFE /* SelectorWrapper.swift in Sources */,
36BE06BA24D0783900CBBB68 /* FaviconFinder.swift in Sources */, 36BE06BA24D0783900CBBB68 /* FaviconFinder.swift in Sources */,
36BCF89524C31F02005BEC29 /* AppData.swift in Sources */, 36BCF89524C31F02005BEC29 /* AppData.swift in Sources */,
3642F01A2502931F005186FE /* InstantPaymentInfoView.swift in Sources */, 3642F01A2502931F005186FE /* RealTimeTransferInfoView.swift in Sources */,
36B8A44F2503D97D00C15359 /* AuthenticationType.swift in Sources */, 36B8A44F2503D97D00C15359 /* AuthenticationType.swift in Sources */,
36E21EDD24DCA89100649DC8 /* TanMethodPicker.swift in Sources */, 36E21EDD24DCA89100649DC8 /* TanMethodPicker.swift in Sources */,
3608D6C624FBAB41006C93A8 /* TanGeneratorPositionMarker.swift in Sources */, 3608D6C624FBAB41006C93A8 /* TanGeneratorPositionMarker.swift in Sources */,
@ -771,7 +771,7 @@
36E21EDF24DCCC2700649DC8 /* CheckmarkListItem.swift in Sources */, 36E21EDF24DCCC2700649DC8 /* CheckmarkListItem.swift in Sources */,
36E7BA1424B3D05C00757859 /* ViewExtensions.swift in Sources */, 36E7BA1424B3D05C00757859 /* ViewExtensions.swift in Sources */,
36BCF88924C0A7D7005BEC29 /* Message.swift in Sources */, 36BCF88924C0A7D7005BEC29 /* Message.swift in Sources */,
366FA4E024C4924A0094F009 /* RemitteeListItem.swift in Sources */, 366FA4E024C4924A0094F009 /* RecipientListItem.swift in Sources */,
3608D6C224FBA9C6006C93A8 /* TrianglePointingDown.swift in Sources */, 3608D6C224FBA9C6006C93A8 /* TrianglePointingDown.swift in Sources */,
36B8A4582503EEB600C15359 /* ActionSheet.swift in Sources */, 36B8A4582503EEB600C15359 /* ActionSheet.swift in Sources */,
36BE068B24CE3B0400CBBB68 /* SwiftExtensions.swift in Sources */, 36BE068B24CE3B0400CBBB68 /* SwiftExtensions.swift in Sources */,

View File

@ -24,15 +24,15 @@
<attribute name="primaNotaNumber" optional="YES" attributeType="String"/> <attribute name="primaNotaNumber" optional="YES" attributeType="String"/>
<attribute name="referenceForTheAccountOwner" attributeType="String"/> <attribute name="referenceForTheAccountOwner" attributeType="String"/>
<attribute name="referenceOfTheAccountServicingInstitution" optional="YES" attributeType="String"/> <attribute name="referenceOfTheAccountServicingInstitution" optional="YES" attributeType="String"/>
<attribute name="referenceWithNoSpecialType" optional="YES" attributeType="String"/>
<attribute name="relatedReferenceNumber" optional="YES" attributeType="String"/> <attribute name="relatedReferenceNumber" optional="YES" attributeType="String"/>
<attribute name="sepaUsage" optional="YES" attributeType="String"/> <attribute name="sepaReference" optional="YES" attributeType="String"/>
<attribute name="sequenceNumber" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="sequenceNumber" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="statementNumber" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="statementNumber" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="supplementaryDetails" optional="YES" attributeType="String"/> <attribute name="supplementaryDetails" optional="YES" attributeType="String"/>
<attribute name="textKeySupplement" optional="YES" attributeType="String"/> <attribute name="textKeySupplement" optional="YES" attributeType="String"/>
<attribute name="transactionReferenceNumber" attributeType="String"/> <attribute name="transactionReferenceNumber" attributeType="String"/>
<attribute name="unparsedUsage" attributeType="String"/> <attribute name="unparsedReference" attributeType="String"/>
<attribute name="usageWithNoSpecialType" optional="YES" attributeType="String"/>
<attribute name="valueDate" attributeType="Date" usesScalarValueType="NO"/> <attribute name="valueDate" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistedBankAccount" inverseName="transactions" inverseEntity="PersistedBankAccount"/> <relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistedBankAccount" inverseName="transactions" inverseEntity="PersistedBankAccount"/>
</entity> </entity>
@ -51,7 +51,7 @@
<attribute name="retrievedTransactionsFromOn" optional="YES" attributeType="Date" usesScalarValueType="NO"/> <attribute name="retrievedTransactionsFromOn" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="retrievedTransactionsUpTo" optional="YES" attributeType="Date" usesScalarValueType="NO"/> <attribute name="retrievedTransactionsUpTo" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="subAccountNumber" optional="YES" attributeType="String"/> <attribute name="subAccountNumber" optional="YES" attributeType="String"/>
<attribute name="supportsInstantPaymentMoneyTransfer" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="supportsRealTimeTransfer" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="supportsRetrievingAccountTransactions" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="supportsRetrievingAccountTransactions" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="supportsRetrievingBalance" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="supportsRetrievingBalance" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="supportsTransferringMoney" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="supportsTransferringMoney" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>

View File

@ -101,20 +101,20 @@
/* TransferMoneyDialog */ /* TransferMoneyDialog */
"Transfer Money Dialog Title" = "Bank transfer"; "Transfer Money Dialog Title" = "Bank transfer";
"Remittee Name" = "Name"; "Recipient Name" = "Name";
"Remittee IBAN" = "IBAN"; "Recipient IBAN" = "IBAN";
"BIC: %@, %@" = "BIC: %@, %@"; "BIC: %@, %@" = "BIC: %@, %@";
"No BIC found for bank code %@" = "No BIC found for bank code %@"; "No BIC found for bank code %@" = "No BIC found for bank code %@";
"Amount" = "Amount"; "Amount" = "Amount";
"Usage" = "Usage"; "Reference" = "Reference";
"Enter usage" = "Optionally enter usage"; "Enter reference" = "Optionally enter reference";
"Instant Payment" = "Instant Payment"; "Real-time transfer" = "Real-time transfer";
"may with costs" = "may with costs"; "may with costs" = "may with costs";
"Instant payment information" = "Bank transfers are usually credited within one business day. However, this only applies during bank business hours, and therefore not at weekends and on public holidays. In addition, business hours vary from bank to bank. Mostly they are from 10 - 18 o'clock, sometimes also until 22 o'clock, but sometimes (e. g. Fridays) only until 14 o'clock.\n "Real-time transfer information" = "Bank transfers are usually credited within one business day. However, this only applies during bank business hours, and therefore not at weekends and on public holidays. In addition, business hours vary from bank to bank. Mostly they are from 10 - 18 o'clock, sometimes also until 22 o'clock, but sometimes (e. g. Fridays) only until 14 o'clock.\n
Instant payment transfers on the other hand are transferred within a maximum of 10 seconds, regardless of the day and time of day.\n Real-time transfers on the other hand are transferred within a maximum of 10 seconds, regardless of the day and time of day.\n
However, real-time transfers are often subject to a fee.\n However, real-time transfers are often subject to a fee.\n
Unfortunately, Bankmeister cannot know whether a bank charges for instant payment transfers. Please refer to the list of prices and services of your bank."; Unfortunately, Bankmeister cannot know whether a bank charges for real-time transfers. Please refer to the list of prices and services of your bank.";
"Transfer Money" = "Transfer"; "Transfer Money" = "Transfer";
@ -170,7 +170,7 @@ Unfortunately, Bankmeister cannot know whether a bank charges for instant paymen
"Supports Retrieving Balance" = "Retrieve balance"; "Supports Retrieving Balance" = "Retrieve balance";
"Supports Retrieving Account Transactions" = "Retrieve transactions"; "Supports Retrieving Account Transactions" = "Retrieve transactions";
"Supports Transferring Money" = "Transfer money"; "Supports Transferring Money" = "Transfer money";
"Supports Instant payment transfer" = "Instant payment transfer"; "Supports Real-time transfer" = "Real-time transfer";
/* ProtectAppSettingsDialog */ /* ProtectAppSettingsDialog */

View File

@ -47,7 +47,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
let dataFolder = URL(fileURLWithPath: "data", isDirectory: true, relativeTo: URL(fileURLWithPath: appDataFolder)) let dataFolder = URL(fileURLWithPath: "data", isDirectory: true, relativeTo: URL(fileURLWithPath: appDataFolder))
let presenter = BankingPresenterSwift(dataFolder: dataFolder, router: SwiftUiRouter(), webClient: UrlSessionWebClient(), persistence: persistence, remitteeSearcher: persistence, bankIconFinder: SwiftBankIconFinder(), serializer: NoOpSerializer(), asyncRunner: DispatchQueueAsyncRunner()) let presenter = BankingPresenterSwift(dataFolder: dataFolder, router: SwiftUiRouter(), webClient: UrlSessionWebClient(), persistence: persistence, transactionPartySearcher: persistence, bankIconFinder: SwiftBankIconFinder(), serializer: NoOpSerializer(), asyncRunner: DispatchQueueAsyncRunner())
DependencyInjector.register(dependency: persistence) DependencyInjector.register(dependency: persistence)
DependencyInjector.register(dependency: presenter) DependencyInjector.register(dependency: presenter)

View File

@ -101,17 +101,17 @@
/* TransferMoneyDialog */ /* TransferMoneyDialog */
"Transfer Money Dialog Title" = "Überweisung"; "Transfer Money Dialog Title" = "Überweisung";
"Remittee Name" = "Name"; "Recipient Name" = "Name";
"Remittee IBAN" = "IBAN"; "Recipient IBAN" = "IBAN";
"BIC: %@, %@" = "BIC: %@, %@"; "BIC: %@, %@" = "BIC: %@, %@";
"No BIC found for bank code %@" = "Keine BIC gefunden für BLZ %@"; "No BIC found for bank code %@" = "Keine BIC gefunden für BLZ %@";
"Amount" = "Betrag"; "Amount" = "Betrag";
"Usage" = "Verwendungszweck"; "Reference" = "Verwendungszweck";
"Enter usage" = "Optional einen Verwendungszweck eingeben"; "Enter reference" = "Optional einen Verwendungszweck eingeben";
"Instant Payment" = "Echtzeitüberweisung"; "Real-time transfer" = "Echtzeitüberweisung";
"may with costs" = "evtl. kostenpflichtig"; "may with costs" = "evtl. kostenpflichtig";
"Instant payment information" = "Normale Überweisungen werden in der Regel innerhalb eines Werktages gutgeschrieben. Dies gilt jedoch nur zu Geschäftszeiten der Banken, also schon mal nicht am Wochenende und an Feiertagen. Zudem unterscheiden sich die Geschäftszeiten von Bank zu Bank. Meistens gehen diese von 10 - 18 Uhr, manchmal auch bis 22 Uhr, manchmal (Freitags) aber auch nur bis 14 Uhr.\n "Real-time transfer information" = "Normale Überweisungen werden in der Regel innerhalb eines Werktages gutgeschrieben. Dies gilt jedoch nur zu Geschäftszeiten der Banken, also schon mal nicht am Wochenende und an Feiertagen. Zudem unterscheiden sich die Geschäftszeiten von Bank zu Bank. Meistens gehen diese von 10 - 18 Uhr, manchmal auch bis 22 Uhr, manchmal (Freitags) aber auch nur bis 14 Uhr.\n
Echtzeitüberweisungen werden hingegen innerhalb von maximal 10 Sekunden überwiesen, egal an welchem Tag und zu welcher Uhrzeit.\n Echtzeitüberweisungen werden hingegen innerhalb von maximal 10 Sekunden überwiesen, egal an welchem Tag und zu welcher Uhrzeit.\n
Häufig sind Echtzeitüberweisungen jedoch kostenpflichtig.\n Häufig sind Echtzeitüberweisungen jedoch kostenpflichtig.\n
Ob eine Bank Gebühren für Echtzeitüberweisungen erhebt, kann Bankmeister leider nicht wissen. Dies entnehmen Sie bitte dem Preis- / Leistungsverzeichnis Ihrer Bank."; Ob eine Bank Gebühren für Echtzeitüberweisungen erhebt, kann Bankmeister leider nicht wissen. Dies entnehmen Sie bitte dem Preis- / Leistungsverzeichnis Ihrer Bank.";
@ -170,7 +170,7 @@ Ob eine Bank Gebühren für Echtzeitüberweisungen erhebt, kann Bankmeister leid
"Supports Retrieving Balance" = "Kontostand abrufen"; "Supports Retrieving Balance" = "Kontostand abrufen";
"Supports Retrieving Account Transactions" = "Kontoumsätze abrufen"; "Supports Retrieving Account Transactions" = "Kontoumsätze abrufen";
"Supports Transferring Money" = "Überweisen"; "Supports Transferring Money" = "Überweisen";
"Supports Instant payment transfer" = "Echtzeitüberweisung"; "Supports Real-time transfer" = "Echtzeitüberweisung";
/* ProtectAppSettingsDialog */ /* ProtectAppSettingsDialog */

View File

@ -159,7 +159,7 @@ extension BankInfo : Identifiable {
} }
extension Remittee : Identifiable { extension TransactionParty : Identifiable {
public var id: String { name.localizedLowercase + "_" + (iban ?? "") } public var id: String { name.localizedLowercase + "_" + (iban ?? "") }

View File

@ -4,7 +4,7 @@ import UIKit
import BankingUiSwift import BankingUiSwift
class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher { class CoreDataBankingPersistence: IBankingPersistence, ITransactionPartySearcher {
private let mapper = Mapper() private let mapper = Mapper()
@ -123,7 +123,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
return UserDefaults.standard.data(forKey: filePath) return UserDefaults.standard.data(forKey: filePath)
} }
func findRemittees(query: String) -> [Remittee] { func findTransactionParty(query: String) -> [TransactionParty] {
var transactions: [PersistedAccountTransaction] = [] var transactions: [PersistedAccountTransaction] = []
do { do {
@ -140,13 +140,13 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
NSLog("Could not request banks: \(error)") NSLog("Could not request banks: \(error)")
} }
let remittees = transactions let transactionParties = transactions
.filter { $0.otherPartyAccountId != nil } // if IBAN is not set we cannot make use of it .filter { $0.otherPartyAccountId != nil } // if IBAN is not set we cannot make use of it
.map( { Remittee(name: $0.otherPartyName ?? "", iban: $0.otherPartyAccountId, bic: $0.otherPartyBankCode, bankName: nil) } ) .map( { TransactionParty(name: $0.otherPartyName ?? "", iban: $0.otherPartyAccountId, bic: $0.otherPartyBankCode, bankName: nil) } )
let uniqueRemittees = Set<Remittee>(remittees) let uniqueTransactionParties = Set<TransactionParty>(transactionParties)
return Array(uniqueRemittees) return Array(uniqueTransactionParties)
} }

View File

@ -58,7 +58,7 @@ class Mapper {
} }
func map(_ bank: IBankData, _ account: PersistedBankAccount) -> IBankAccount { func map(_ bank: IBankData, _ account: PersistedBankAccount) -> IBankAccount {
let mapped = BankAccount(bank: bank, identifier: map(account.identifier), accountHolderName: map(account.accountHolderName), iban: account.iban, subAccountNumber: account.subAccountNumber, customerId: map(account.customerId), balance: map(account.balance), currency: map(account.currency), type: map(account.type), productName: account.productName, accountLimit: account.accountLimit, retrievedTransactionsFromOn: map(account.retrievedTransactionsFromOn), retrievedTransactionsUpTo: map(account.retrievedTransactionsUpTo), supportsRetrievingAccountTransactions: account.supportsRetrievingAccountTransactions, supportsRetrievingBalance: account.supportsRetrievingBalance, supportsTransferringMoney: account.supportsTransferringMoney, supportsInstantPaymentMoneyTransfer: account.supportsInstantPaymentMoneyTransfer, bookedTransactions: [], unbookedTransactions: []) let mapped = BankAccount(bank: bank, identifier: map(account.identifier), accountHolderName: map(account.accountHolderName), iban: account.iban, subAccountNumber: account.subAccountNumber, customerId: map(account.customerId), balance: map(account.balance), currency: map(account.currency), type: map(account.type), productName: account.productName, accountLimit: account.accountLimit, retrievedTransactionsFromOn: map(account.retrievedTransactionsFromOn), retrievedTransactionsUpTo: map(account.retrievedTransactionsUpTo), supportsRetrievingAccountTransactions: account.supportsRetrievingAccountTransactions, supportsRetrievingBalance: account.supportsRetrievingBalance, supportsTransferringMoney: account.supportsTransferringMoney, supportsRealTimeTransfer: account.supportsRealTimeTransfer, bookedTransactions: [], unbookedTransactions: [])
mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched
mapped.isAccountTypeSupported = account.isAccountTypeSupported mapped.isAccountTypeSupported = account.isAccountTypeSupported
@ -97,7 +97,7 @@ class Mapper {
mapped.supportsRetrievingAccountTransactions = account.supportsRetrievingAccountTransactions mapped.supportsRetrievingAccountTransactions = account.supportsRetrievingAccountTransactions
mapped.supportsRetrievingBalance = account.supportsRetrievingBalance mapped.supportsRetrievingBalance = account.supportsRetrievingBalance
mapped.supportsTransferringMoney = account.supportsTransferringMoney mapped.supportsTransferringMoney = account.supportsTransferringMoney
mapped.supportsInstantPaymentMoneyTransfer = account.supportsInstantPaymentMoneyTransfer mapped.supportsRealTimeTransfer = account.supportsRealTimeTransfer
mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched
@ -147,7 +147,7 @@ class Mapper {
} }
func map(_ account: IBankAccount, _ transaction: PersistedAccountTransaction) -> IAccountTransaction { func map(_ account: IBankAccount, _ transaction: PersistedAccountTransaction) -> IAccountTransaction {
let mapped = AccountTransaction(account: account, amount: map(transaction.amount), currency: map(transaction.currency), unparsedUsage: map(transaction.unparsedUsage), bookingDate: map(transaction.bookingDate), otherPartyName: transaction.otherPartyName, otherPartyBankCode: transaction.otherPartyBankCode, otherPartyAccountId: transaction.otherPartyAccountId, bookingText: transaction.bookingText, valueDate: map(transaction.valueDate), statementNumber: Int32(transaction.statementNumber), sequenceNumber: map(transaction.sequenceNumber), openingBalance: map(transaction.openingBalance), closingBalance: map(transaction.closingBalance), endToEndReference: transaction.endToEndReference, customerReference: transaction.customerReference, mandateReference: transaction.mandateReference, creditorIdentifier: transaction.creditorIdentifier, originatorsIdentificationCode: transaction.originatorsIdentificationCode, compensationAmount: transaction.compensationAmount, originalAmount: transaction.originalAmount, sepaUsage: transaction.sepaUsage, deviantOriginator: transaction.deviantOriginator, deviantRecipient: transaction.deviantRecipient, usageWithNoSpecialType: transaction.usageWithNoSpecialType, primaNotaNumber: transaction.primaNotaNumber, textKeySupplement: transaction.textKeySupplement, currencyType: transaction.currencyType, bookingKey: map(transaction.bookingKey), referenceForTheAccountOwner: map(transaction.referenceForTheAccountOwner), referenceOfTheAccountServicingInstitution: transaction.referenceOfTheAccountServicingInstitution, supplementaryDetails: transaction.supplementaryDetails, transactionReferenceNumber: map(transaction.transactionReferenceNumber), relatedReferenceNumber: transaction.relatedReferenceNumber) let mapped = AccountTransaction(account: account, amount: map(transaction.amount), currency: map(transaction.currency), unparsedReference: map(transaction.unparsedReference), bookingDate: map(transaction.bookingDate), otherPartyName: transaction.otherPartyName, otherPartyBankCode: transaction.otherPartyBankCode, otherPartyAccountId: transaction.otherPartyAccountId, bookingText: transaction.bookingText, valueDate: map(transaction.valueDate), statementNumber: Int32(transaction.statementNumber), sequenceNumber: map(transaction.sequenceNumber), openingBalance: map(transaction.openingBalance), closingBalance: map(transaction.closingBalance), endToEndReference: transaction.endToEndReference, customerReference: transaction.customerReference, mandateReference: transaction.mandateReference, creditorIdentifier: transaction.creditorIdentifier, originatorsIdentificationCode: transaction.originatorsIdentificationCode, compensationAmount: transaction.compensationAmount, originalAmount: transaction.originalAmount, sepaReference: transaction.sepaReference, deviantOriginator: transaction.deviantOriginator, deviantRecipient: transaction.deviantRecipient, referenceWithNoSpecialType: transaction.referenceWithNoSpecialType, primaNotaNumber: transaction.primaNotaNumber, textKeySupplement: transaction.textKeySupplement, currencyType: transaction.currencyType, bookingKey: map(transaction.bookingKey), referenceForTheAccountOwner: map(transaction.referenceForTheAccountOwner), referenceOfTheAccountServicingInstitution: transaction.referenceOfTheAccountServicingInstitution, supplementaryDetails: transaction.supplementaryDetails, transactionReferenceNumber: map(transaction.transactionReferenceNumber), relatedReferenceNumber: transaction.relatedReferenceNumber)
mapped.technicalId = transaction.objectIDAsString mapped.technicalId = transaction.objectIDAsString
@ -166,7 +166,7 @@ class Mapper {
mapped.amount = map(transaction.amount) mapped.amount = map(transaction.amount)
mapped.currency = transaction.currency mapped.currency = transaction.currency
mapped.unparsedUsage = transaction.unparsedUsage mapped.unparsedReference = transaction.unparsedReference
mapped.bookingDate = map(transaction.bookingDate) mapped.bookingDate = map(transaction.bookingDate)
mapped.otherPartyName = transaction.otherPartyName mapped.otherPartyName = transaction.otherPartyName
mapped.otherPartyBankCode = transaction.otherPartyBankCode mapped.otherPartyBankCode = transaction.otherPartyBankCode
@ -186,10 +186,10 @@ class Mapper {
mapped.originatorsIdentificationCode = transaction.originatorsIdentificationCode mapped.originatorsIdentificationCode = transaction.originatorsIdentificationCode
mapped.compensationAmount = transaction.compensationAmount mapped.compensationAmount = transaction.compensationAmount
mapped.originalAmount = transaction.originalAmount mapped.originalAmount = transaction.originalAmount
mapped.sepaUsage = transaction.sepaUsage mapped.sepaReference = transaction.sepaReference
mapped.deviantOriginator = transaction.deviantOriginator mapped.deviantOriginator = transaction.deviantOriginator
mapped.deviantRecipient = transaction.deviantRecipient mapped.deviantRecipient = transaction.deviantRecipient
mapped.usageWithNoSpecialType = transaction.usageWithNoSpecialType mapped.referenceWithNoSpecialType = transaction.referenceWithNoSpecialType
mapped.primaNotaNumber = transaction.primaNotaNumber mapped.primaNotaNumber = transaction.primaNotaNumber
mapped.textKeySupplement = transaction.textKeySupplement mapped.textKeySupplement = transaction.textKeySupplement

View File

@ -57,7 +57,7 @@ struct BankAccountSettingsDialog: View {
CheckmarkListItem("Supports Transferring Money", account.supportsTransferringMoney) CheckmarkListItem("Supports Transferring Money", account.supportsTransferringMoney)
CheckmarkListItem("Supports Instant payment transfer", account.supportsInstantPaymentMoneyTransfer) CheckmarkListItem("Supports Real-time transfer", account.supportsRealTimeTransfer)
} }
} }
.alert(message: $unsavedChangesMessage) .alert(message: $unsavedChangesMessage)

View File

@ -14,34 +14,34 @@ struct TransferMoneyDialog: View {
@State private var selectedAccountIndex = 0 @State private var selectedAccountIndex = 0
@State private var remitteeName: String = "" @State private var recipientName: String = ""
@State private var isValidRemitteeNameEntered = false @State private var isValidRecipientNameEntered = false
@State private var remitteeNameValidationResult: ValidationResult? = nil @State private var recipientNameValidationResult: ValidationResult? = nil
@State private var showRemitteeAutocompleteList = false @State private var showRecipientAutocompleteList = false
@State private var remitteeSearchResults = [Remittee]() @State private var recipientSearchResults = [TransactionParty]()
@State private var remitteeIban: String = "" @State private var recipientIban: String = ""
@State private var isValidRemitteeIbanEntered = false @State private var isValidRecipientIbanEntered = false
@State private var remitteeIbanValidationResult: ValidationResult? = nil @State private var recipientIbanValidationResult: ValidationResult? = nil
@State private var remitteeBic: String = "" @State private var recipientBic: String = ""
@State private var isValidRemitteeBicEntered = false @State private var isValidRecipientBicEntered = false
@State private var remitteeBicValidationResult: ValidationResult? = nil @State private var recipientBicValidationResult: ValidationResult? = nil
@State private var remitteeBankInfo: String? = nil @State private var recipientBankInfo: String? = nil
@State private var amount = "" @State private var amount = ""
@State private var isValidAmountEntered = false @State private var isValidAmountEntered = false
@State private var amountValidationResult: ValidationResult? = nil @State private var amountValidationResult: ValidationResult? = nil
@State private var usage: String = "" @State private var reference: String = ""
@State private var isValidUsageEntered = true @State private var isValidReferenceEntered = true
@State private var usageValidationResult: ValidationResult? = nil @State private var referenceValidationResult: ValidationResult? = nil
@State private var validateDataWhenShowingDialog = false @State private var validateDataWhenShowingDialog = false
@State private var instantPayment = false @State private var realTimeTransfer = false
@State private var isTransferringMoney = false @State private var isTransferringMoney = false
@ -60,8 +60,8 @@ struct TransferMoneyDialog: View {
return self.accountsSupportingTransferringMoney.first return self.accountsSupportingTransferringMoney.first
} }
private var supportsInstantPayment: Bool { private var supportsRealTimeTransfer: Bool {
return self.account?.supportsInstantPaymentMoneyTransfer ?? false return self.account?.supportsRealTimeTransfer ?? false
} }
@ -82,22 +82,22 @@ struct TransferMoneyDialog: View {
let preselectedBankAccount = preselectedValues.account let preselectedBankAccount = preselectedValues.account
self._selectedAccountIndex = State(initialValue: accountsSupportingTransferringMoney.firstIndex(where: { account in account == preselectedBankAccount }) ?? 0) self._selectedAccountIndex = State(initialValue: accountsSupportingTransferringMoney.firstIndex(where: { account in account == preselectedBankAccount }) ?? 0)
self._remitteeName = State(initialValue: preselectedValues.creditorName) self._recipientName = State(initialValue: preselectedValues.recipientName)
self._remitteeBic = State(initialValue: preselectedValues.creditorBic) self._recipientBic = State(initialValue: preselectedValues.recipientBankCode)
self._remitteeIban = State(initialValue: preselectedValues.creditorIban) self._recipientIban = State(initialValue: preselectedValues.recipientAccountId)
if remitteeBic.isBlank && remitteeIban.isNotBlank { if recipientBic.isBlank && recipientIban.isNotBlank {
tryToGetBicFromIban(remitteeIban) tryToGetBicFromIban(recipientIban)
} }
self._usage = State(initialValue: preselectedValues.usage) self._reference = State(initialValue: preselectedValues.reference)
if preselectedValues.amount.decimal != NSDecimalNumber.zero { if preselectedValues.amount.decimal != NSDecimalNumber.zero {
self._amount = State(initialValue: preselectedValues.amount.format(countDecimalPlaces: 2)) self._amount = State(initialValue: preselectedValues.amount.format(countDecimalPlaces: 2))
} }
if preselectedBankAccount.supportsInstantPaymentMoneyTransfer { if preselectedBankAccount.supportsRealTimeTransfer {
self._instantPayment = State(initialValue: preselectedValues.instantPayment) self._realTimeTransfer = State(initialValue: preselectedValues.realTimeTransfer)
} }
_validateDataWhenShowingDialog = State(initialValue: true) _validateDataWhenShowingDialog = State(initialValue: true)
@ -117,29 +117,29 @@ struct TransferMoneyDialog: View {
} }
Section { Section {
LabelledUIKitTextField(label: "Remittee Name", text: $remitteeName, focusOnStart: true, focusNextTextFieldOnReturnKeyPress: true, LabelledUIKitTextField(label: "Recipient Name", text: $recipientName, focusOnStart: true, focusNextTextFieldOnReturnKeyPress: true,
isFocusedChanged: remitteeNameisFocusedChanged, actionOnReturnKeyPress: handleReturnKeyPress, textChanged: enteredRemitteeNameChanged) isFocusedChanged: recipientNameisFocusedChanged, actionOnReturnKeyPress: handleReturnKeyPress, textChanged: enteredRecipientNameChanged)
.padding(.bottom, 0) .padding(.bottom, 0)
remitteeNameValidationResult.map { validationError in recipientNameValidationResult.map { validationError in
ValidationLabel(validationError) ValidationLabel(validationError)
} }
if self.showRemitteeAutocompleteList { if self.showRecipientAutocompleteList {
List(self.remitteeSearchResults) { remittee in List(self.recipientSearchResults) { recipient in
RemitteeListItem(remittee: remittee) RecipientListItem(recipient: recipient)
.onTapGesture { self.remitteeSelected(remittee) } .onTapGesture { self.recipientSelected(recipient) }
} }
} }
LabelledUIKitTextField(label: "Remittee IBAN", text: $remitteeIban, autocapitalizationType: .allCharacters, focusNextTextFieldOnReturnKeyPress: true, isFocusedChanged: validateRemitteeIbanOnFocusLost, LabelledUIKitTextField(label: "Recipient IBAN", text: $recipientIban, autocapitalizationType: .allCharacters, focusNextTextFieldOnReturnKeyPress: true, isFocusedChanged: validateRecipientIbanOnFocusLost,
actionOnReturnKeyPress: handleReturnKeyPress, textChanged: remitteeIbanisFocusedChanged) actionOnReturnKeyPress: handleReturnKeyPress, textChanged: recipientIbanisFocusedChanged)
remitteeIbanValidationResult.map { validationError in recipientIbanValidationResult.map { validationError in
ValidationLabel(validationError) ValidationLabel(validationError)
} }
remitteeBankInfo.map { recipientBankInfo.map {
InfoLabel($0) InfoLabel($0)
.font(.caption) .font(.caption)
} }
@ -157,29 +157,29 @@ struct TransferMoneyDialog: View {
Section { Section {
VStack(alignment: .leading) { VStack(alignment: .leading) {
HStack { HStack {
Text("Usage") Text("Reference")
Spacer() Spacer()
} }
UIKitTextField("Enter usage", text: $usage, actionOnReturnKeyPress: handleReturnKeyPress, textChanged: validateUsage) UIKitTextField("Enter reference", text: $reference, actionOnReturnKeyPress: handleReturnKeyPress, textChanged: validateReference)
} }
usageValidationResult.map { validationError in referenceValidationResult.map { validationError in
ValidationLabel(validationError) ValidationLabel(validationError)
} }
} }
if supportsInstantPayment { if supportsRealTimeTransfer {
Section { Section {
VStack { VStack {
Toggle(isOn: $instantPayment) { Toggle(isOn: $realTimeTransfer) {
HStack { HStack {
Text("Instant Payment") Text("Real-time transfer")
.lineLimit(1) .lineLimit(1)
if instantPayment { if realTimeTransfer {
InstantPaymentInfoView() RealTimeTransferInfoView()
} }
} }
} }
@ -205,8 +205,8 @@ struct TransferMoneyDialog: View {
self.validateDataWhenShowingDialog = false self.validateDataWhenShowingDialog = false
self.validateAllFields() self.validateAllFields()
if self.remitteeBankInfo == nil { if self.recipientBankInfo == nil {
self.showRemitteeBankInfo(self.remitteeBic, "") self.showRecipientBankInfo(self.recipientBic, "")
} }
} }
} }
@ -228,104 +228,104 @@ struct TransferMoneyDialog: View {
private func validateAllFields() { private func validateAllFields() {
self.validateRemitteeNameOnFocusLost() self.validateRecipientNameOnFocusLost()
self.validateRemitteeIbanOnFocusLost() self.validateRecipientIbanOnFocusLost()
self.validateRemitteeBic() self.validateRecipientBic()
self.validateAmount() self.validateAmount()
self.validateUsage() self.validateReference()
} }
private func remitteeNameisFocusedChanged(_ isFocused: Bool) { private func recipientNameisFocusedChanged(_ isFocused: Bool) {
if isFocused == false { if isFocused == false {
validateRemitteeNameOnFocusLost() validateRecipientNameOnFocusLost()
self.showRemitteeAutocompleteList = false self.showRecipientAutocompleteList = false
} }
else { else {
self.showRemitteeAutocompleteList = self.remitteeSearchResults.isNotEmpty self.showRecipientAutocompleteList = self.recipientSearchResults.isNotEmpty
} }
} }
private func validateRemitteeNameOnFocusLost() { private func validateRecipientNameOnFocusLost() {
validateField($remitteeName, $remitteeNameValidationResult, $isValidRemitteeNameEntered) { validateField($recipientName, $recipientNameValidationResult, $isValidRecipientNameEntered) {
inputValidator.validateRemitteeName(remitteeNameToTest: remitteeName) inputValidator.validateRecipientName(recipientNameToTest: recipientName)
} }
} }
private func enteredRemitteeNameChanged(enteredRemitteeName: String) { private func enteredRecipientNameChanged(enteredRecipientName: String) {
validateField($remitteeName, $remitteeNameValidationResult, $isValidRemitteeNameEntered) { validateField($recipientName, $recipientNameValidationResult, $isValidRecipientNameEntered) {
inputValidator.validateRemitteeNameWhileTyping(remitteeNameToTest: remitteeName) inputValidator.validateRecipientNameWhileTyping(recipientNameToTest: recipientName)
} }
searchRemittees(remitteeName) searchRecipients(recipientName)
} }
private func searchRemittees(_ searchText: String) { private func searchRecipients(_ searchText: String) {
// TODO: why doesn't it work to search on background thread? // TODO: why doesn't it work to search on background thread?
self.remitteeSearchResults = self.presenter.findRemitteesForName(name: searchText) self.recipientSearchResults = self.presenter.findRecipientsForName(name: searchText)
self.showRemitteeAutocompleteList = self.remitteeSearchResults.isNotEmpty self.showRecipientAutocompleteList = self.recipientSearchResults.isNotEmpty
} }
private func remitteeSelected(_ remittee: Remittee) { private func recipientSelected(_ recipient: TransactionParty) {
self.remitteeName = remittee.name self.recipientName = recipient.name
self.remitteeIban = remittee.iban ?? self.remitteeIban self.recipientIban = recipient.iban ?? self.recipientIban
self.remitteeBic = remittee.bic ?? self.remitteeBic self.recipientBic = recipient.bic ?? self.recipientBic
tryToGetBicFromIban(self.remitteeIban) tryToGetBicFromIban(self.recipientIban)
validateAllFields() validateAllFields()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
self.showRemitteeAutocompleteList = false self.showRecipientAutocompleteList = false
} }
} }
private func remitteeIbanisFocusedChanged(_ enteredIban: String) { private func recipientIbanisFocusedChanged(_ enteredIban: String) {
validateField($remitteeIban, $remitteeIbanValidationResult, $isValidRemitteeIbanEntered) { inputValidator.validateIbanWhileTyping(ibanToTest: enteredIban) } validateField($recipientIban, $recipientIbanValidationResult, $isValidRecipientIbanEntered) { inputValidator.validateIbanWhileTyping(ibanToTest: enteredIban) }
tryToGetBicFromIban(enteredIban) tryToGetBicFromIban(enteredIban)
} }
private func validateRemitteeIbanOnFocusLost(_ isFocused: Bool) { private func validateRecipientIbanOnFocusLost(_ isFocused: Bool) {
if isFocused == false { if isFocused == false {
validateRemitteeIbanOnFocusLost() validateRecipientIbanOnFocusLost()
} }
} }
private func validateRemitteeIbanOnFocusLost() { private func validateRecipientIbanOnFocusLost() {
validateField($remitteeIban, $remitteeIbanValidationResult, $isValidRemitteeIbanEntered) { inputValidator.validateIban(ibanToTest: remitteeIban) } validateField($recipientIban, $recipientIbanValidationResult, $isValidRecipientIbanEntered) { inputValidator.validateIban(ibanToTest: recipientIban) }
} }
private func tryToGetBicFromIban(_ enteredIban: String) { private func tryToGetBicFromIban(_ enteredIban: String) {
let foundBank = presenter.findUniqueBankForIban(iban: enteredIban) let foundBank = presenter.findUniqueBankForIban(iban: enteredIban)
if let foundBank = foundBank { if let foundBank = foundBank {
self.remitteeBic = foundBank.bic self.recipientBic = foundBank.bic
showRemitteeBankInfo(foundBank.bic, foundBank.name) showRecipientBankInfo(foundBank.bic, foundBank.name)
} }
else { else {
self.remitteeBic = "" self.recipientBic = ""
if enteredIban.count >= InputValidator.Companion().MinimumLengthToDetermineBicFromIban { if enteredIban.count >= InputValidator.Companion().MinimumLengthToDetermineBicFromIban {
self.remitteeBankInfo = "No BIC found for bank code \(enteredIban[4..<Int(InputValidator.Companion().MinimumLengthToDetermineBicFromIban)])" self.recipientBankInfo = "No BIC found for bank code \(enteredIban[4..<Int(InputValidator.Companion().MinimumLengthToDetermineBicFromIban)])"
} }
else { else {
self.remitteeBankInfo = nil self.recipientBankInfo = nil
} }
} }
validateRemitteeBic() validateRecipientBic()
} }
private func validateRemitteeBic() { private func validateRecipientBic() {
self.isValidRemitteeBicEntered = inputValidator.validateBic(bicToTest: remitteeBic).validationSuccessful self.isValidRecipientBicEntered = inputValidator.validateBic(bicToTest: recipientBic).validationSuccessful
} }
private func showRemitteeBankInfo(_ bic: String, _ bankName: String) { private func showRecipientBankInfo(_ bic: String, _ bankName: String) {
self.remitteeBankInfo = "BIC: \(bic), \(bankName)" self.recipientBankInfo = "BIC: \(bic), \(bankName)"
} }
@ -352,12 +352,12 @@ struct TransferMoneyDialog: View {
} }
private func validateUsage(enteredUsage: String) { private func validateReference(enteredReference: String) {
validateUsage() validateReference()
} }
private func validateUsage() { private func validateReference() {
validateField($usage, $usageValidationResult, $isValidUsageEntered) { inputValidator.validateUsage(usageToTest: self.usage) } validateField($reference, $referenceValidationResult, $isValidReferenceEntered) { inputValidator.validateReference(referenceToTest: self.reference) }
} }
private func validateField(_ newValue: Binding<String>, _ validationResult: Binding<ValidationResult?>, _ isValidValueEntered: Binding<Bool>, _ validateValue: () -> ValidationResult) { private func validateField(_ newValue: Binding<String>, _ validationResult: Binding<ValidationResult?>, _ isValidValueEntered: Binding<Bool>, _ validateValue: () -> ValidationResult) {
@ -382,11 +382,11 @@ struct TransferMoneyDialog: View {
private func isRequiredDataEntered() -> Bool { private func isRequiredDataEntered() -> Bool {
return account != nil return account != nil
&& isValidRemitteeNameEntered && isValidRecipientNameEntered
&& isValidRemitteeIbanEntered && isValidRecipientIbanEntered
&& isValidRemitteeBicEntered && isValidRecipientBicEntered
&& isValidAmountEntered && isValidAmountEntered
&& isValidUsageEntered && isValidReferenceEntered
} }
private func transferMoney() { private func transferMoney() {
@ -394,7 +394,7 @@ struct TransferMoneyDialog: View {
isTransferringMoney = true isTransferringMoney = true
UIApplication.hideKeyboard() UIApplication.hideKeyboard()
let data = TransferMoneyData(account: account!, creditorName: remitteeName, creditorIban: remitteeIban, creditorBic: remitteeBic, amount: amount, usage: usage, instantPayment: instantPayment) let data = TransferMoneyData(account: account!, recipientName: recipientName, recipientAccountId: recipientIban, recipientBankCode: recipientBic, amount: amount, reference: reference, realTimeTransfer: realTimeTransfer)
presenter.transferMoneyAsync(data: data) { response in presenter.transferMoneyAsync(data: data) { response in
self.handleTransferMoneyResponse(data, response) self.handleTransferMoneyResponse(data, response)
@ -406,12 +406,12 @@ struct TransferMoneyDialog: View {
isTransferringMoney = false isTransferringMoney = false
if (response.successful) { if (response.successful) {
self.transferMoneyResponseMessage = Message(message: Text("Successfully transferred \(data.amount) \("") to \(data.creditorName)."), primaryButton: .ok { self.transferMoneyResponseMessage = Message(message: Text("Successfully transferred \(data.amount) \("") to \(data.recipientName)."), primaryButton: .ok {
self.presentation.wrappedValue.dismiss() self.presentation.wrappedValue.dismiss()
}) })
} }
else if response.userCancelledAction == false { else if response.userCancelledAction == false {
self.transferMoneyResponseMessage = Message(message: Text("Could not transfer \(data.amount) \("") to \(data.creditorName). Error: \(response.errorToShowToUser ?? "").")) self.transferMoneyResponseMessage = Message(message: Text("Could not transfer \(data.amount) \("") to \(data.recipientName). Error: \(response.errorToShowToUser ?? "")."))
} }
} }

View File

@ -1,9 +1,9 @@
import SwiftUI import SwiftUI
struct InstantPaymentInfoView: View { struct RealTimeTransferInfoView: View {
@State private var showInstantPaymentInfoPopover = false @State private var showRealTimeTransferInfoPopover = false
var body: some View { var body: some View {
@ -15,26 +15,26 @@ struct InstantPaymentInfoView: View {
.minimumScaleFactor(0.5) .minimumScaleFactor(0.5)
.padding(.horizontal, 0) .padding(.horizontal, 0)
UIKitButton(.infoLight) { self.showInstantPaymentInfoPopover = true } UIKitButton(.infoLight) { self.showRealTimeTransferInfoPopover = true }
.frame(width: 15, height: 15) .frame(width: 15, height: 15)
.popover(isPresented: $showInstantPaymentInfoPopover, arrowEdge: .leading ) { .popover(isPresented: $showRealTimeTransferInfoPopover, arrowEdge: .leading ) {
if UIDevice.isRunningOniPad { if UIDevice.isRunningOniPad {
ScrollView { ScrollView {
Text("Instant payment information") Text("Real-time transfer information")
.padding() .padding()
.detailForegroundColor() .detailForegroundColor()
} }
} }
else { else {
VStack { VStack {
Text("Instant payment information") Text("Real-time transfer information")
.padding() .padding()
.detailForegroundColor() .detailForegroundColor()
HStack { HStack {
Spacer() Spacer()
Button("OK") { self.showInstantPaymentInfoPopover = false } Button("OK") { self.showRealTimeTransferInfoPopover = false }
Spacer() Spacer()
} }
@ -47,13 +47,13 @@ struct InstantPaymentInfoView: View {
} }
struct InstantPaymentInfoView_Previews: PreviewProvider { struct RealTimeTransferInfoView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
Form { Form {
Section { Section {
Toggle(isOn: .constant(true)) { Toggle(isOn: .constant(true)) {
InstantPaymentInfoView() RealTimeTransferInfoView()
} }
} }
} }

View File

@ -36,7 +36,7 @@ struct AccountTransactionListItem: View {
.lineLimit(1) .lineLimit(1)
.frame(height: 20) .frame(height: 20)
Text(transaction.usage) Text(transaction.reference)
.styleAsDetail() .styleAsDetail()
.padding(.top, 4) .padding(.top, 4)
.lineLimit(2) .lineLimit(2)
@ -62,7 +62,7 @@ struct AccountTransactionListItem: View {
} }
.contextMenu { .contextMenu {
if transaction.canCreateMoneyTransferFrom { if transaction.canCreateMoneyTransferFrom {
NavigationLink(destination: LazyView(TransferMoneyDialog(preselectedValues: TransferMoneyData.Companion().fromAccountTransactionWithoutAmountAndUsage(transaction: self.transaction)))) { NavigationLink(destination: LazyView(TransferMoneyDialog(preselectedValues: TransferMoneyData.Companion().fromAccountTransactionWithoutAmountAndReference(transaction: self.transaction)))) {
HStack { HStack {
Text("Transfer money to \(transaction.otherPartyName ?? "")") Text("Transfer money to \(transaction.otherPartyName ?? "")")
@ -98,6 +98,6 @@ struct AccountTransactionListItem: View {
struct AccountTransactionListItem_Previews: PreviewProvider { struct AccountTransactionListItem_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
AccountTransactionListItem(AccountTransaction(account: previewBanks[0].accounts[0] as! BankAccount, otherPartyName: "Marieke Musterfrau", unparsedUsage: "Vielen Dank für Ihre Mühen", amount: CommonBigDecimal(double: 1234.56), valueDate: CommonDate(year: 2020, month: .march, day_: 27), bookingText: "SEPA Überweisung"), false) AccountTransactionListItem(AccountTransaction(account: previewBanks[0].accounts[0] as! BankAccount, otherPartyName: "Marieke Musterfrau", unparsedReference: "Vielen Dank für Ihre Mühen", amount: CommonBigDecimal(double: 1234.56), valueDate: CommonDate(year: 2020, month: .march, day_: 27), bookingText: "SEPA Überweisung"), false)
} }
} }

View File

@ -2,16 +2,16 @@ import SwiftUI
import BankingUiSwift import BankingUiSwift
struct RemitteeListItem: View { struct RecipientListItem: View {
let remittee: Remittee let recipient: TransactionParty
var body: some View { var body: some View {
VStack { VStack {
HStack { HStack {
Text(remittee.name) Text(recipient.name)
.bold() .bold()
.lineLimit(1) .lineLimit(1)
@ -19,7 +19,7 @@ struct RemitteeListItem: View {
} }
.padding(.vertical, 6) .padding(.vertical, 6)
remittee.bankName.map { bankName in recipient.bankName.map { bankName in
HStack { HStack {
Text(bankName) Text(bankName)
.font(.footnote) .font(.footnote)
@ -31,11 +31,11 @@ struct RemitteeListItem: View {
} }
HStack { HStack {
Text(remittee.iban ?? "") Text(recipient.iban ?? "")
Spacer() Spacer()
Text(remittee.bic ?? "") Text(recipient.bic ?? "")
} }
.font(.footnote) .font(.footnote)
.lineLimit(1) .lineLimit(1)
@ -45,10 +45,10 @@ struct RemitteeListItem: View {
} }
} }
struct RemitteeListItem_Previews: PreviewProvider { struct RecipientListItem_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
RemitteeListItem(remittee: Remittee(name: "Marieke Musterfrau", iban: "DE12876543211234567890", bic: "ABZODEBBXX", bankName: "Abzockbank Berlin")) RecipientListItem(recipient: TransactionParty(name: "Marieke Musterfrau", iban: "DE12876543211234567890", bic: "ABZODEBBXX", bankName: "Abzockbank Berlin"))
} }
} }

View File

@ -117,7 +117,7 @@ open class fints4kBankingClient(
callback(BankingClientResponse(false, errorMessage)) callback(BankingClientResponse(false, errorMessage))
} }
else { else {
val mappedData = BankTransferData(data.creditorName, data.creditorIban, data.creditorBic, data.amount.toMoney(), data.usage, data.instantPayment) val mappedData = BankTransferData(data.recipientName, data.recipientAccountId, data.recipientBankCode, data.amount.toMoney(), data.reference, data.realTimeTransfer)
doBankTransferAsync(mappedData, account, callback) doBankTransferAsync(mappedData, account, callback)
} }

View File

@ -127,7 +127,7 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
account.supportsRetrievingBalance = accountData.supportsRetrievingBalance account.supportsRetrievingBalance = accountData.supportsRetrievingBalance
account.supportsRetrievingAccountTransactions = accountData.supportsRetrievingAccountTransactions account.supportsRetrievingAccountTransactions = accountData.supportsRetrievingAccountTransactions
account.supportsTransferringMoney = accountData.supportsTransferringMoney account.supportsTransferringMoney = accountData.supportsTransferringMoney
account.supportsInstantPaymentMoneyTransfer = accountData.supportsInstantPaymentMoneyTransfer account.supportsRealTimeTransfer = accountData.supportsRealTimeTransfer
} }
open fun mapBankAccountType(type: AccountType?): BankAccountType { open fun mapBankAccountType(type: AccountType?): BankAccountType {
@ -199,7 +199,7 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
account, account,
transaction.amount.bigDecimal, transaction.amount.bigDecimal,
transaction.amount.currency.code, transaction.amount.currency.code,
transaction.unparsedUsage, transaction.unparsedReference,
transaction.bookingDate, transaction.bookingDate,
transaction.otherPartyName, transaction.otherPartyName,
transaction.otherPartyBankCode, transaction.otherPartyBankCode,
@ -218,10 +218,10 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
transaction.originatorsIdentificationCode, transaction.originatorsIdentificationCode,
transaction.compensationAmount, transaction.compensationAmount,
transaction.originalAmount, transaction.originalAmount,
transaction.sepaUsage, transaction.sepaReference,
transaction.deviantOriginator, transaction.deviantOriginator,
transaction.deviantRecipient, transaction.deviantRecipient,
transaction.usageWithNoSpecialType, transaction.referenceWithNoSpecialType,
transaction.primaNotaNumber, transaction.primaNotaNumber,
transaction.textKeySupplement, transaction.textKeySupplement,

View File

@ -256,7 +256,7 @@ open class hbci4jBankingClient(
} }
protected open fun createTransferCashJob(handle: HBCIHandler, data: TransferMoneyData) { protected open fun createTransferCashJob(handle: HBCIHandler, data: TransferMoneyData) {
// TODO: implement instant payment // TODO: implement real-time transfer
val transferCashJob = handle.newJob("UebSEPA") val transferCashJob = handle.newJob("UebSEPA")
val source = mapper.mapToKonto(data.account) val source = mapper.mapToKonto(data.account)
@ -266,7 +266,7 @@ open class hbci4jBankingClient(
transferCashJob.setParam("src", source) transferCashJob.setParam("src", source)
transferCashJob.setParam("dst", destination) transferCashJob.setParam("dst", destination)
transferCashJob.setParam("btg", amount) transferCashJob.setParam("btg", amount)
transferCashJob.setParam("usage", data.usage) transferCashJob.setParam("usage", data.reference)
transferCashJob.addToQueue() transferCashJob.addToQueue()
} }

View File

@ -43,12 +43,12 @@ open class AccountTransactionMapper(
} }
protected open fun mapTransaction(account: TypedBankAccount, btag: GVRKUms.BTag, transaction: GVRKUms.UmsLine): IAccountTransaction { protected open fun mapTransaction(account: TypedBankAccount, btag: GVRKUms.BTag, transaction: GVRKUms.UmsLine): IAccountTransaction {
val unparsedUsage = transaction.usage.joinToString("") val unparsedReference = transaction.usage.joinToString("")
val parsedUsage = Mt940Parser().getUsageParts(unparsedUsage) val parsedReference = Mt940Parser().getReferenceParts(unparsedReference)
val statementAndMaySequenceNumber = btag.counter.split('/') val statementAndMaySequenceNumber = btag.counter.split('/')
return modelCreator.createTransaction(account, return modelCreator.createTransaction(account,
mapValue(transaction.value), transaction.value.curr, unparsedUsage, transaction.bdate.toDate(), mapValue(transaction.value), transaction.value.curr, unparsedReference, transaction.bdate.toDate(),
transaction.other.name + (transaction.other.name2 ?: ""), transaction.other.name + (transaction.other.name2 ?: ""),
transaction.other.bic ?: transaction.other.blz, transaction.other.bic ?: transaction.other.blz,
transaction.other.iban ?: transaction.other.number, transaction.other.iban ?: transaction.other.number,
@ -57,17 +57,17 @@ open class AccountTransactionMapper(
if (statementAndMaySequenceNumber.size > 1) statementAndMaySequenceNumber[1].toInt() else null, if (statementAndMaySequenceNumber.size > 1) statementAndMaySequenceNumber[1].toInt() else null,
mapValue(btag.start.value), mapValue(btag.end.value), mapValue(btag.start.value), mapValue(btag.end.value),
parsedUsage[Mt940Parser.EndToEndReferenceUsageKey], parsedReference[Mt940Parser.EndToEndReferenceKey],
parsedUsage[Mt940Parser.CustomerReferenceUsageKey], parsedReference[Mt940Parser.CustomerReferenceKey],
parsedUsage[Mt940Parser.MandateReferenceUsageKey], parsedReference[Mt940Parser.MandateReferenceKey],
parsedUsage[Mt940Parser.CreditorIdentifierUsageKey], parsedReference[Mt940Parser.CreditorIdentifierKey],
parsedUsage[Mt940Parser.OriginatorsIdentificationCodeUsageKey], parsedReference[Mt940Parser.OriginatorsIdentificationCodeKey],
parsedUsage[Mt940Parser.CompensationAmountUsageKey], parsedReference[Mt940Parser.CompensationAmountKey],
parsedUsage[Mt940Parser.OriginalAmountUsageKey], parsedReference[Mt940Parser.OriginalAmountKey],
parsedUsage[Mt940Parser.SepaUsageUsageKey], parsedReference[Mt940Parser.SepaReferenceKey],
parsedUsage[Mt940Parser.DeviantOriginatorUsageKey], parsedReference[Mt940Parser.DeviantOriginatorKey],
parsedUsage[Mt940Parser.DeviantRecipientUsageKey], parsedReference[Mt940Parser.DeviantRecipientKey],
parsedUsage[""], parsedReference[""],
transaction.primanota, transaction.primanota,
transaction.addkey, transaction.addkey,

View File

@ -28,7 +28,7 @@ open class hbci4jModelMapper(
} }
open fun mapToKonto(data: TransferMoneyData): Konto { open fun mapToKonto(data: TransferMoneyData): Konto {
return mapToKonto(data.creditorName, data.creditorIban, data.creditorBic) return mapToKonto(data.recipientName, data.recipientAccountId, data.recipientBankCode)
} }
open fun mapToKonto(accountHolderName: String, iban: String, bic: String): Konto { open fun mapToKonto(accountHolderName: String, iban: String, bic: String): Konto {