Renamed usage to reference, remittee to recipient and instant payment to real-time transfer
This commit is contained in:
parent
66feba0614
commit
bf9d406053
|
@ -8,7 +8,7 @@ However it's not a full implementation of FinTS standard but implements all comm
|
|||
|
||||
## Features
|
||||
- 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.
|
||||
|
||||
## Setup
|
||||
|
|
|
@ -400,7 +400,7 @@ open class FinTsClient(
|
|||
|
||||
response.getFirstSegmentById<ReceivedCreditCardTransactionsAndBalance>(InstituteSegmentId.CreditCardTransactions)?.let { transactionsSegment ->
|
||||
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.RetrieveBalance, messageBuilder.supportsGetBalance(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> {
|
||||
|
|
|
@ -289,7 +289,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
|||
|
||||
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)
|
||||
|
||||
|
@ -309,8 +309,8 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
|||
return supportsBankTransferAndSepaVersion(bank, account, CustomerSegmentId.SepaBankTransfer).first.isJobVersionSupported
|
||||
}
|
||||
|
||||
open fun supportsSepaInstantPaymentBankTransfer(bank: BankData, account: AccountData): Boolean {
|
||||
return supportsBankTransferAndSepaVersion(bank, account, CustomerSegmentId.SepaInstantPaymentBankTransfer).first.isJobVersionSupported
|
||||
open fun supportsSepaRealTimeTransfer(bank: BankData, account: AccountData): Boolean {
|
||||
return supportsBankTransferAndSepaVersion(bank, account, CustomerSegmentId.SepaRealTimeTransfer).first.isJobVersionSupported
|
||||
}
|
||||
|
||||
protected open fun supportsBankTransferAndSepaVersion(bank: BankData, account: AccountData, segmentId: CustomerSegmentId): Pair<MessageBuilderResult, String?> {
|
||||
|
|
|
@ -25,7 +25,7 @@ enum class CustomerSegmentId(override val id: String) : ISegmentId {
|
|||
|
||||
SepaBankTransfer("HKCCS"),
|
||||
|
||||
SepaInstantPaymentBankTransfer("HKIPZ"),
|
||||
SepaRealTimeTransfer("HKIPZ"),
|
||||
|
||||
SepaAccountInfoParameters("HKSPA") // not implemented, retrieved automatically with UPD
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ package net.dankito.banking.fints.messages.segmente.implementierte.sepa
|
|||
|
||||
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>")
|
||||
|
||||
}
|
|
@ -28,11 +28,11 @@ open class SepaBankTransferBase(
|
|||
"DebitorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(debitorName),
|
||||
"DebitorIban" to account.iban!!,
|
||||
"DebitorBic" to debitorBic,
|
||||
"CreditorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(data.creditorName),
|
||||
"CreditorIban" to data.creditorIban.replace(" ", ""),
|
||||
"CreditorBic" to data.creditorBic.replace(" ", ""),
|
||||
"RecipientName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(data.recipientName),
|
||||
"RecipientIban" to data.recipientAccountId.replace(" ", ""),
|
||||
"RecipientBic" to data.recipientBankCode.replace(" ", ""),
|
||||
"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
|
||||
),
|
||||
messageCreator
|
||||
|
|
|
@ -44,8 +44,8 @@ open class AccountData(
|
|||
open val supportsTransferringMoney: Boolean
|
||||
get() = supportsFeature(AccountFeature.TransferMoney)
|
||||
|
||||
open val supportsInstantPaymentMoneyTransfer: Boolean
|
||||
get() = supportsFeature(AccountFeature.InstantPayment)
|
||||
open val supportsRealTimeTransfer: Boolean
|
||||
get() = supportsFeature(AccountFeature.RealTimeTransfer)
|
||||
|
||||
|
||||
open fun supportsFeature(feature: AccountFeature): Boolean {
|
||||
|
|
|
@ -9,6 +9,6 @@ enum class AccountFeature {
|
|||
|
||||
TransferMoney,
|
||||
|
||||
InstantPayment
|
||||
RealTimeTransfer
|
||||
|
||||
}
|
|
@ -7,7 +7,7 @@ open class AccountTransaction(
|
|||
val account: AccountData,
|
||||
val amount: Money,
|
||||
val isReversal: Boolean,
|
||||
val unparsedUsage: String,
|
||||
val unparsedReference: String,
|
||||
val bookingDate: Date,
|
||||
val otherPartyName: String?,
|
||||
val otherPartyBankCode: String?,
|
||||
|
@ -26,10 +26,10 @@ open class AccountTransaction(
|
|||
val originatorsIdentificationCode: String?,
|
||||
val compensationAmount: String?,
|
||||
val originalAmount: String?,
|
||||
val sepaUsage: String?,
|
||||
val sepaReference: String?,
|
||||
val deviantOriginator: String?,
|
||||
val deviantRecipient: String?,
|
||||
val usageWithNoSpecialType: String?,
|
||||
val referenceWithNoSpecialType: String?,
|
||||
val primaNotaNumber: String?,
|
||||
val textKeySupplement: String?,
|
||||
|
||||
|
@ -46,20 +46,24 @@ open class AccountTransaction(
|
|||
// for object deserializers
|
||||
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)
|
||||
: this(account, amount, false, unparsedUsage, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate,
|
||||
constructor(account: AccountData, amount: Money, unparsedReference: String, bookingDate: Date, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, bookingText: String?, valueDate: Date)
|
||||
: this(account, amount, false, unparsedReference, bookingDate, 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)
|
||||
|
||||
|
||||
val reference: String
|
||||
get() = sepaReference ?: unparsedReference
|
||||
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is AccountTransaction) return false
|
||||
|
||||
if (account != other.account) 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 (otherPartyName != other.otherPartyName) return false
|
||||
if (otherPartyBankCode != other.otherPartyBankCode) return false
|
||||
|
@ -73,7 +77,7 @@ open class AccountTransaction(
|
|||
override fun hashCode(): Int {
|
||||
var result = account.hashCode()
|
||||
result = 31 * result + amount.hashCode()
|
||||
result = 31 * result + unparsedUsage.hashCode()
|
||||
result = 31 * result + unparsedReference.hashCode()
|
||||
result = 31 * result + bookingDate.hashCode()
|
||||
result = 31 * result + (otherPartyName?.hashCode() ?: 0)
|
||||
result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0)
|
||||
|
@ -85,7 +89,7 @@ open class AccountTransaction(
|
|||
|
||||
|
||||
override fun toString(): String {
|
||||
return "$valueDate $amount $otherPartyName: $unparsedUsage"
|
||||
return "$valueDate $amount $otherPartyName: $unparsedReference"
|
||||
}
|
||||
|
||||
}
|
|
@ -2,10 +2,10 @@ package net.dankito.banking.fints.model
|
|||
|
||||
|
||||
open class BankTransferData(
|
||||
val creditorName: String,
|
||||
val creditorIban: String,
|
||||
val creditorBic: String,
|
||||
val recipientName: String,
|
||||
val recipientAccountId: String,
|
||||
val recipientBankCode: String,
|
||||
val amount: Money,
|
||||
val usage: String,
|
||||
val instantPayment: Boolean = false
|
||||
val reference: String,
|
||||
val realTimeTransfer: Boolean = false
|
||||
)
|
|
@ -306,7 +306,7 @@ open class ResponseParser(
|
|||
parseBoolean(parametersDataElements[1]),
|
||||
parseBoolean(parametersDataElements[2]),
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ open class SepaAccountInfoParameters(
|
|||
* Über diese Information legt das Kreditinstitut fest, ob bei SEPA-Zahlungsverkehrsinstrumenten die Verwendung
|
||||
* 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.
|
||||
|
@ -47,7 +47,7 @@ open class SepaAccountInfoParameters(
|
|||
* 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.
|
||||
*/
|
||||
val countReservedUsageLength: Int,
|
||||
val countReservedReferenceLength: Int,
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
const val CountReservedUsageLengthNotSet = 0
|
||||
const val CountReservedReferenceLengthNotSet = 0
|
||||
}
|
||||
|
||||
}
|
|
@ -51,7 +51,7 @@ open class Mt940AccountTransactionsParser(
|
|||
account,
|
||||
Money(mapAmount(transaction.statementLine), currency),
|
||||
transaction.statementLine.isReversal,
|
||||
transaction.information?.unparsedUsage ?: "",
|
||||
transaction.information?.unparsedReference ?: "",
|
||||
transaction.statementLine.bookingDate ?: statement.closingBalance.bookingDate,
|
||||
transaction.information?.otherPartyName,
|
||||
transaction.information?.otherPartyBankCode,
|
||||
|
@ -70,10 +70,10 @@ open class Mt940AccountTransactionsParser(
|
|||
transaction.information?.originatorsIdentificationCode,
|
||||
transaction.information?.compensationAmount,
|
||||
transaction.information?.originalAmount,
|
||||
transaction.information?.sepaUsage,
|
||||
transaction.information?.sepaReference,
|
||||
transaction.information?.deviantOriginator,
|
||||
transaction.information?.deviantRecipient,
|
||||
transaction.information?.usageWithNoSpecialType,
|
||||
transaction.information?.referenceWithNoSpecialType,
|
||||
transaction.information?.primaNotaNumber,
|
||||
transaction.information?.textKeySupplement,
|
||||
|
||||
|
|
|
@ -56,19 +56,19 @@ open class Mt940Parser : IMt940Parser {
|
|||
|
||||
val AmountRegex = Regex("\\d+,\\d*")
|
||||
|
||||
val UsageTypeRegex = Regex("[A-Z]{4}\\+")
|
||||
val ReferenceTypeRegex = Regex("[A-Z]{4}\\+")
|
||||
|
||||
|
||||
const val EndToEndReferenceUsageKey = "EREF+"
|
||||
const val CustomerReferenceUsageKey = "KREF+"
|
||||
const val MandateReferenceUsageKey = "MREF+"
|
||||
const val CreditorIdentifierUsageKey = "CRED+"
|
||||
const val OriginatorsIdentificationCodeUsageKey = "DEBT+"
|
||||
const val CompensationAmountUsageKey = "COAM+"
|
||||
const val OriginalAmountUsageKey = "OAMT+"
|
||||
const val SepaUsageUsageKey = "SVWZ+"
|
||||
const val DeviantOriginatorUsageKey = "ABWA+"
|
||||
const val DeviantRecipientUsageKey = "ABWE+"
|
||||
const val EndToEndReferenceKey = "EREF+"
|
||||
const val CustomerReferenceKey = "KREF+"
|
||||
const val MandateReferenceKey = "MREF+"
|
||||
const val CreditorIdentifierKey = "CRED+"
|
||||
const val OriginatorsIdentificationCodeKey = "DEBT+"
|
||||
const val CompensationAmountKey = "COAM+"
|
||||
const val OriginalAmountKey = "OAMT+"
|
||||
const val SepaReferenceKey = "SVWZ+"
|
||||
const val DeviantOriginatorKey = "ABWA+"
|
||||
const val DeviantRecipientKey = "ABWE+"
|
||||
|
||||
|
||||
private val log = LoggerFactory.getLogger(Mt940Parser::class)
|
||||
|
@ -295,7 +295,7 @@ open class Mt940Parser : IMt940Parser {
|
|||
try {
|
||||
val information = parseInformationToAccountOwner(informationToAccountOwnerString)
|
||||
|
||||
mapUsage(information)
|
||||
mapReference(information)
|
||||
|
||||
return information
|
||||
} catch (e: Exception) {
|
||||
|
@ -310,7 +310,7 @@ open class Mt940Parser : IMt940Parser {
|
|||
// see Finanzdatenformate p. 209 - 215
|
||||
val geschaeftsvorfallCode = informationToAccountOwnerString.substring(0, 2) // TODO: may map
|
||||
|
||||
val usageParts = mutableListOf<String>()
|
||||
val referenceParts = mutableListOf<String>()
|
||||
val otherPartyName = StringBuilder()
|
||||
var otherPartyBankCode: String? = null
|
||||
var otherPartyAccountId: String? = null
|
||||
|
@ -326,48 +326,48 @@ open class Mt940Parser : IMt940Parser {
|
|||
when (fieldCode) {
|
||||
0 -> bookingText = fieldValue
|
||||
10 -> primaNotaNumber = fieldValue
|
||||
in 20..29 -> usageParts.add(fieldValue)
|
||||
in 20..29 -> referenceParts.add(fieldValue)
|
||||
30 -> otherPartyBankCode = fieldValue
|
||||
31 -> otherPartyAccountId = fieldValue
|
||||
32, 33 -> otherPartyName.append(fieldValue)
|
||||
34 -> textKeySupplement = fieldValue
|
||||
in 60..63 -> usageParts.add(fieldValue)
|
||||
in 60..63 -> referenceParts.add(fieldValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val usage = if (isFormattedUsage(usageParts)) joinUsageParts(usageParts)
|
||||
else usageParts.joinToString(" ")
|
||||
val reference = if (isFormattedReference(referenceParts)) joinReferenceParts(referenceParts)
|
||||
else referenceParts.joinToString(" ")
|
||||
|
||||
val otherPartyNameString = if (otherPartyName.isEmpty()) null else otherPartyName.toString()
|
||||
|
||||
return InformationToAccountOwner(
|
||||
usage, otherPartyNameString, otherPartyBankCode, otherPartyAccountId,
|
||||
reference, otherPartyNameString, otherPartyBankCode, otherPartyAccountId,
|
||||
bookingText, primaNotaNumber, textKeySupplement
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun joinUsageParts(usageParts: List<String>): String {
|
||||
val usage = StringBuilder()
|
||||
protected open fun joinReferenceParts(referenceParts: List<String>): String {
|
||||
val reference = StringBuilder()
|
||||
|
||||
usageParts.firstOrNull()?.let {
|
||||
usage.append(it)
|
||||
referenceParts.firstOrNull()?.let {
|
||||
reference.append(it)
|
||||
}
|
||||
|
||||
for (i in 1..usageParts.size - 1) {
|
||||
val part = usageParts[i]
|
||||
if (part.isNotEmpty() && part.first().isUpperCase && usageParts[i - 1].last().isUpperCase == false) {
|
||||
usage.append(" ")
|
||||
for (i in 1..referenceParts.size - 1) {
|
||||
val part = referenceParts[i]
|
||||
if (part.isNotEmpty() && part.first().isUpperCase && referenceParts[i - 1].last().isUpperCase == false) {
|
||||
reference.append(" ")
|
||||
}
|
||||
|
||||
usage.append(part)
|
||||
reference.append(part)
|
||||
}
|
||||
|
||||
return usage.toString()
|
||||
return reference.toString()
|
||||
}
|
||||
|
||||
protected open fun isFormattedUsage(usageParts: List<String>): Boolean {
|
||||
return usageParts.any { UsageTypeRegex.find(it) != null }
|
||||
protected open fun isFormattedReference(referenceParts: List<String>): Boolean {
|
||||
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.
|
||||
*/
|
||||
protected open fun mapUsage(information: InformationToAccountOwner) {
|
||||
val usageParts = getUsageParts(information.unparsedUsage)
|
||||
protected open fun mapReference(information: InformationToAccountOwner) {
|
||||
val referenceParts = getReferenceParts(information.unparsedReference)
|
||||
|
||||
usageParts.forEach { entry ->
|
||||
setUsageLineValue(information, entry.key, entry.value)
|
||||
referenceParts.forEach { entry ->
|
||||
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 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) {
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
when (usageType) {
|
||||
EndToEndReferenceUsageKey -> information.endToEndReference = typeValue
|
||||
CustomerReferenceUsageKey -> information.customerReference = typeValue
|
||||
MandateReferenceUsageKey -> information.mandateReference = typeValue
|
||||
CreditorIdentifierUsageKey -> information.creditorIdentifier = typeValue
|
||||
OriginatorsIdentificationCodeUsageKey -> information.originatorsIdentificationCode = typeValue
|
||||
CompensationAmountUsageKey -> information.compensationAmount = typeValue
|
||||
OriginalAmountUsageKey -> information.originalAmount = typeValue
|
||||
SepaUsageUsageKey -> information.sepaUsage = typeValue
|
||||
DeviantOriginatorUsageKey -> information.deviantOriginator = typeValue
|
||||
DeviantRecipientUsageKey -> information.deviantRecipient = typeValue
|
||||
else -> information.usageWithNoSpecialType = typeValue
|
||||
// TODO: there are more. See .pdf from Deutsche Bank
|
||||
protected open fun setReferenceLineValue(information: InformationToAccountOwner, referenceType: String, typeValue: String) {
|
||||
when (referenceType) {
|
||||
EndToEndReferenceKey -> information.endToEndReference = typeValue
|
||||
CustomerReferenceKey -> information.customerReference = typeValue
|
||||
MandateReferenceKey -> information.mandateReference = typeValue
|
||||
CreditorIdentifierKey -> information.creditorIdentifier = typeValue
|
||||
OriginatorsIdentificationCodeKey -> information.originatorsIdentificationCode = typeValue
|
||||
CompensationAmountKey -> information.compensationAmount = typeValue
|
||||
OriginalAmountKey -> information.originalAmount = typeValue
|
||||
SepaReferenceKey -> information.sepaReference = typeValue
|
||||
DeviantOriginatorKey -> information.deviantOriginator = typeValue
|
||||
DeviantRecipientKey -> information.deviantRecipient = typeValue
|
||||
else -> information.referenceWithNoSpecialType = typeValue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package net.dankito.banking.fints.transactions.mt940.model
|
|||
|
||||
|
||||
open class InformationToAccountOwner(
|
||||
val unparsedUsage: String,
|
||||
val unparsedReference: String,
|
||||
val otherPartyName: String?,
|
||||
val otherPartyBankCode: String?,
|
||||
val otherPartyAccountId: String?,
|
||||
|
@ -25,17 +25,17 @@ open class InformationToAccountOwner(
|
|||
|
||||
var originalAmount: String? = null
|
||||
|
||||
var sepaUsage: String? = null
|
||||
var sepaReference: String? = null
|
||||
|
||||
var deviantOriginator: String? = null
|
||||
|
||||
var deviantRecipient: String? = null
|
||||
|
||||
var usageWithNoSpecialType: String? = null
|
||||
var referenceWithNoSpecialType: String? = null
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
return "$otherPartyName $unparsedUsage"
|
||||
return "$otherPartyName $unparsedReference"
|
||||
}
|
||||
|
||||
}
|
|
@ -15,11 +15,11 @@ class SepaBankTransferBaseTest {
|
|||
val debitorName = "Nelson Mandela"
|
||||
val debitorIban = "ZA123456780987654321"
|
||||
val debitorBic = "ABCDZAEFXXX"
|
||||
val creditorName = "Mahatma Gandhi"
|
||||
val creditorIban = "IN123456780987654321"
|
||||
val creditorBic = "ABCDINEFXXX"
|
||||
val recipientName = "Mahatma Gandhi"
|
||||
val recipientIban = "IN123456780987654321"
|
||||
val recipientBic = "ABCDINEFXXX"
|
||||
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,
|
||||
AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()),
|
||||
debitorBic,
|
||||
BankTransferData(creditorName, creditorIban, creditorBic, Money(amount, "EUR"), usage)
|
||||
BankTransferData(recipientName, recipientIban, recipientBic, Money(amount, "EUR"), reference)
|
||||
)
|
||||
|
||||
|
||||
|
@ -42,8 +42,8 @@ class SepaBankTransferBaseTest {
|
|||
|
||||
|
||||
// then
|
||||
expect(result).contains(debitorName, debitorIban, debitorBic, creditorName, creditorIban, creditorBic,
|
||||
amount.toString().replace(',', '.'), usage, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03")
|
||||
expect(result).contains(debitorName, debitorIban, debitorBic, recipientName, recipientIban, recipientBic,
|
||||
amount.toString().replace(',', '.'), reference, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -55,7 +55,7 @@ class SepaBankTransferBaseTest {
|
|||
debitorName,
|
||||
AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()),
|
||||
debitorBic,
|
||||
BankTransferData(creditorName, creditorIban, creditorBic, Money(amount, "EUR"), usage)
|
||||
BankTransferData(recipientName, recipientIban, recipientBic, Money(amount, "EUR"), reference)
|
||||
)
|
||||
|
||||
|
||||
|
@ -64,8 +64,8 @@ class SepaBankTransferBaseTest {
|
|||
|
||||
|
||||
// then
|
||||
expect(result).contains(debitorName, debitorIban, debitorBic, creditorName, creditorIban, creditorBic,
|
||||
amount.toString().replace(',', '.'), usage, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03")
|
||||
expect(result).contains(debitorName, debitorIban, debitorBic, recipientName, recipientIban, recipientBic,
|
||||
amount.toString().replace(',', '.'), reference, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03")
|
||||
}
|
||||
|
||||
}
|
|
@ -558,9 +558,9 @@ class ResponseParserTest : FinTsTestBase() {
|
|||
for (segment in sepaAccountInfoParameters) {
|
||||
expect(segment.retrieveSingleAccountAllowed).isTrue()
|
||||
expect(segment.nationalAccountRelationshipAllowed).isFalse()
|
||||
expect(segment.structuredUsageAllowed).isFalse()
|
||||
expect(segment.structuredReferenceAllowed).isFalse()
|
||||
expect(segment.settingMaxAllowedEntriesAllowed).isFalse()
|
||||
expect(segment.countReservedUsageLength).toBe(SepaAccountInfoParameters.CountReservedUsageLengthNotSet)
|
||||
expect(segment.countReservedReferenceLength).toBe(SepaAccountInfoParameters.CountReservedReferenceLengthNotSet)
|
||||
expect(segment.supportedSepaFormats).containsExactly(
|
||||
"sepade.pain.001.001.02.xsd",
|
||||
"sepade.pain.001.002.02.xsd",
|
||||
|
|
|
@ -269,9 +269,9 @@ class Mt940ParserTest : FinTsTestBase() {
|
|||
result.flatMap { it.transactions }.forEach { transaction ->
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ open class FinTsClientTestBase {
|
|||
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
|
||||
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
|
||||
|
|
|
@ -19,7 +19,7 @@ class LuceneConfig {
|
|||
const val BookingDateFieldName = "booking_date"
|
||||
const val DateSortFieldName = "value_date_sort"
|
||||
|
||||
const val UsageFieldName = "usage"
|
||||
const val ReferenceFieldName = "reference"
|
||||
|
||||
const val BookingTextFieldName = "booking_text"
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import net.dankito.banking.LuceneConfig.Companion.IdFieldName
|
|||
import net.dankito.banking.LuceneConfig.Companion.OtherPartyAccountIdFieldName
|
||||
import net.dankito.banking.LuceneConfig.Companion.OtherPartyBankCodeFieldName
|
||||
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.util.ISerializer
|
||||
import net.dankito.banking.util.JacksonJsonSerializer
|
||||
|
@ -62,7 +62,7 @@ open class LuceneBankingPersistence(
|
|||
return listOf(
|
||||
fields.keywordField(BankAccountIdFieldName, account.technicalId),
|
||||
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.nullableStoredField(OtherPartyBankCodeFieldName, transaction.otherPartyBankCode),
|
||||
|
|
|
@ -12,14 +12,14 @@ import net.dankito.utils.lucene.search.Searcher
|
|||
import java.io.File
|
||||
|
||||
|
||||
open class LuceneRemitteeSearcher(indexFolder: File) : IRemitteeSearcher {
|
||||
open class LuceneTransactionPartySearcher(indexFolder: File) : ITransactionPartySearcher {
|
||||
|
||||
companion object {
|
||||
|
||||
private val properties = listOf(
|
||||
PropertyDescription(PropertyType.NullableString, OtherPartyNameFieldName, Remittee::name),
|
||||
PropertyDescription(PropertyType.NullableString, OtherPartyBankCodeFieldName, Remittee::bic),
|
||||
PropertyDescription(PropertyType.NullableString, OtherPartyAccountIdFieldName, Remittee::iban)
|
||||
PropertyDescription(PropertyType.NullableString, OtherPartyNameFieldName, TransactionParty::name),
|
||||
PropertyDescription(PropertyType.NullableString, OtherPartyBankCodeFieldName, TransactionParty::bic),
|
||||
PropertyDescription(PropertyType.NullableString, OtherPartyAccountIdFieldName, TransactionParty::iban)
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -30,16 +30,16 @@ open class LuceneRemitteeSearcher(indexFolder: File) : IRemitteeSearcher {
|
|||
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 ->
|
||||
listOf(
|
||||
queries.fulltextQuery(OtherPartyNameFieldName, singleTerm)
|
||||
)
|
||||
}
|
||||
|
||||
return searcher.searchAndMap(MappedSearchConfig(luceneQuery, Remittee::class.java, properties))
|
||||
.toSet() // don't display same Remittee 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
|
||||
return searcher.searchAndMap(MappedSearchConfig(luceneQuery, TransactionParty::class.java, properties))
|
||||
.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 transaction party's IBAN and BIC
|
||||
}
|
||||
|
||||
}
|
|
@ -19,7 +19,7 @@ import java.util.*
|
|||
import java.util.concurrent.ThreadLocalRandom
|
||||
|
||||
|
||||
class LuceneRemitteeSearcherTest {
|
||||
class LuceneTransactionPartySearcherTest {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -49,7 +49,7 @@ class LuceneRemitteeSearcherTest {
|
|||
|
||||
private val bankingPersistence = LuceneBankingPersistence(indexFolder, databaseFolder)
|
||||
|
||||
private val underTest = LuceneRemitteeSearcher(indexFolder)
|
||||
private val underTest = LuceneTransactionPartySearcher(indexFolder)
|
||||
|
||||
|
||||
@Before
|
||||
|
@ -68,12 +68,12 @@ class LuceneRemitteeSearcherTest {
|
|||
|
||||
|
||||
@Test
|
||||
fun findRemittees_ByFullName() {
|
||||
fun findTransactionParty_ByFullName() {
|
||||
|
||||
// given
|
||||
val query = OtherPartyName
|
||||
|
||||
val before = underTest.findRemittees(query)
|
||||
val before = underTest.findTransactionParty(query)
|
||||
assertThat(before).isEmpty()
|
||||
|
||||
bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf(
|
||||
|
@ -84,7 +84,7 @@ class LuceneRemitteeSearcherTest {
|
|||
|
||||
|
||||
// when
|
||||
val result = underTest.findRemittees(query)
|
||||
val result = underTest.findTransactionParty(query)
|
||||
|
||||
|
||||
// then
|
||||
|
@ -95,12 +95,12 @@ class LuceneRemitteeSearcherTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun findRemittees_ByPartialName() {
|
||||
fun findTransactionParty_ByPartialName() {
|
||||
|
||||
// given
|
||||
val query = "gand"
|
||||
|
||||
val before = underTest.findRemittees(query)
|
||||
val before = underTest.findTransactionParty(query)
|
||||
assertThat(before).isEmpty()
|
||||
|
||||
bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf(
|
||||
|
@ -111,7 +111,7 @@ class LuceneRemitteeSearcherTest {
|
|||
|
||||
|
||||
// when
|
||||
val result = underTest.findRemittees(query)
|
||||
val result = underTest.findTransactionParty(query)
|
||||
|
||||
|
||||
// then
|
||||
|
@ -122,13 +122,13 @@ class LuceneRemitteeSearcherTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun findRemittees_SimilarNames() {
|
||||
fun findTransactionParty_SimilarNames() {
|
||||
|
||||
// given
|
||||
val query = "gand"
|
||||
val secondOtherPartyName = "Gandalf"
|
||||
|
||||
val before = underTest.findRemittees(query)
|
||||
val before = underTest.findTransactionParty(query)
|
||||
assertThat(before).isEmpty()
|
||||
|
||||
bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf(
|
||||
|
@ -139,7 +139,7 @@ class LuceneRemitteeSearcherTest {
|
|||
|
||||
|
||||
// when
|
||||
val result = underTest.findRemittees(query)
|
||||
val result = underTest.findTransactionParty(query)
|
||||
|
||||
|
||||
// then
|
||||
|
@ -148,12 +148,12 @@ class LuceneRemitteeSearcherTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun findRemittees_DuplicateEntries() {
|
||||
fun findTransactionParty_DuplicateEntries() {
|
||||
|
||||
// given
|
||||
val query = OtherPartyName
|
||||
|
||||
val before = underTest.findRemittees(query)
|
||||
val before = underTest.findTransactionParty(query)
|
||||
assertThat(before).isEmpty()
|
||||
|
||||
bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf(
|
||||
|
@ -166,7 +166,7 @@ class LuceneRemitteeSearcherTest {
|
|||
|
||||
|
||||
// when
|
||||
val result = underTest.findRemittees(query)
|
||||
val result = underTest.findTransactionParty(query)
|
||||
|
||||
|
||||
// then
|
||||
|
@ -177,12 +177,12 @@ class LuceneRemitteeSearcherTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun findRemittees_OtherName() {
|
||||
fun findTransactionParty_OtherName() {
|
||||
|
||||
// given
|
||||
val query = "Mandela"
|
||||
|
||||
val before = underTest.findRemittees(query)
|
||||
val before = underTest.findTransactionParty(query)
|
||||
assertThat(before).isEmpty()
|
||||
|
||||
bankingPersistence.saveOrUpdateAccountTransactions(bankAccountMock, listOf(
|
||||
|
@ -193,7 +193,7 @@ class LuceneRemitteeSearcherTest {
|
|||
|
||||
|
||||
// when
|
||||
val result = underTest.findRemittees(query)
|
||||
val result = underTest.findTransactionParty(query)
|
||||
|
||||
|
||||
// then
|
||||
|
@ -203,17 +203,17 @@ class LuceneRemitteeSearcherTest {
|
|||
|
||||
private fun createTransaction(bankAccount: BankAccount = bankAccountMock, bookingDate: String, amount: BigDecimal = randomBigDecimal(),
|
||||
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,
|
||||
otherPartyBankCode, otherPartyAccountId, usage)
|
||||
otherPartyBankCode, otherPartyAccountId, reference)
|
||||
}
|
||||
|
||||
private fun createTransaction(bankAccount: BankAccount = bankAccountMock, bookingDate: Date = randomDate(), amount: BigDecimal = randomBigDecimal(),
|
||||
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 {
|
|
@ -5,8 +5,8 @@ import androidx.room.Room
|
|||
import net.dankito.banking.persistence.dao.BaseDao
|
||||
import net.dankito.banking.persistence.dao.saveOrUpdate
|
||||
import net.dankito.banking.persistence.model.*
|
||||
import net.dankito.banking.search.IRemitteeSearcher
|
||||
import net.dankito.banking.search.Remittee
|
||||
import net.dankito.banking.search.ITransactionPartySearcher
|
||||
import net.dankito.banking.search.TransactionParty
|
||||
import net.dankito.banking.ui.model.IAccountTransaction
|
||||
import net.dankito.banking.ui.model.TypedBankAccount
|
||||
import net.dankito.banking.ui.model.TypedBankData
|
||||
|
@ -18,7 +18,7 @@ import net.sqlcipher.database.SQLiteDatabase
|
|||
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
|
||||
|
||||
|
@ -154,11 +154,11 @@ open class RoomBankingPersistence(applicationContext: Context, password: String?
|
|||
}
|
||||
|
||||
|
||||
override fun findRemittees(query: String): List<Remittee> {
|
||||
return db.accountTransactionDao().findRemittees(query)
|
||||
.toSet() // don't display same Remittee multiple times
|
||||
override fun findTransactionParty(query: String): List<TransactionParty> {
|
||||
return db.accountTransactionDao().findTransactionParty(query)
|
||||
.toSet() // don't display same transaction party multiple times
|
||||
.filterNot { it.bankCode.isNullOrBlank() || it.accountId.isNullOrBlank() }
|
||||
.map { Remittee(it.name, it.accountId, it.bankCode) }
|
||||
.map { TransactionParty(it.name, it.accountId, it.bankCode) }
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,7 @@ package net.dankito.banking.persistence.dao
|
|||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import net.dankito.banking.persistence.model.AccountTransaction
|
||||
import net.dankito.banking.persistence.model.Remittee
|
||||
import net.dankito.banking.persistence.model.TransactionParty
|
||||
|
||||
|
||||
@Dao
|
||||
|
@ -13,6 +13,6 @@ interface AccountTransactionDao : BaseDao<AccountTransaction> {
|
|||
fun getAll(): List<AccountTransaction>
|
||||
|
||||
@Query("SELECT otherPartyName, otherPartyBankCode, otherPartyAccountId FROM AccountTransaction WHERE otherPartyName LIKE '%' || :query || '%'")
|
||||
fun findRemittees(query: String): List<Remittee>
|
||||
fun findTransactionParty(query: String): List<TransactionParty>
|
||||
|
||||
}
|
|
@ -15,7 +15,7 @@ open class AccountTransaction(
|
|||
|
||||
override var amount: BigDecimal,
|
||||
override var currency: String,
|
||||
override var unparsedUsage: String,
|
||||
override var unparsedReference: String,
|
||||
override var bookingDate: Date,
|
||||
override var otherPartyName: String?,
|
||||
override var otherPartyBankCode: String?,
|
||||
|
@ -34,10 +34,10 @@ open class AccountTransaction(
|
|||
override var originatorsIdentificationCode: String?,
|
||||
override var compensationAmount: String?,
|
||||
override var originalAmount: String?,
|
||||
override var sepaUsage: String?,
|
||||
override var sepaReference: String?,
|
||||
override var deviantOriginator: String?,
|
||||
override var deviantRecipient: String?,
|
||||
override var usageWithNoSpecialType: String?,
|
||||
override var referenceWithNoSpecialType: String?,
|
||||
override var primaNotaNumber: String?,
|
||||
override var textKeySupplement: String?,
|
||||
|
||||
|
@ -56,15 +56,15 @@ open class AccountTransaction(
|
|||
|
||||
/* convenience constructors for languages not supporting default values */
|
||||
|
||||
constructor(account: BankAccount, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
|
||||
: this(account, amount, "EUR", unparsedUsage, valueDate,
|
||||
constructor(account: BankAccount, otherPartyName: String?, unparsedReference: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
|
||||
: this(account, amount, "EUR", unparsedReference, 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?,
|
||||
bookingText: String?, valueDate: Date)
|
||||
: this(account, amount, currency, unparsedUsage, bookingDate,
|
||||
: this(account, amount, currency, unparsedReference, bookingDate,
|
||||
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)
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ open class BankAccount(
|
|||
override var supportsRetrievingAccountTransactions: Boolean = false,
|
||||
override var supportsRetrievingBalance: Boolean = false,
|
||||
override var supportsTransferringMoney: Boolean = false,
|
||||
override var supportsInstantPaymentMoneyTransfer: Boolean = false,
|
||||
override var supportsRealTimeTransfer: Boolean = false,
|
||||
|
||||
@Ignore
|
||||
override var bookedTransactions: List<IAccountTransaction> = listOf(),
|
||||
|
|
|
@ -26,7 +26,7 @@ open class RoomModelCreator : IModelCreator {
|
|||
account: TypedBankAccount,
|
||||
amount: BigDecimal,
|
||||
currency: String,
|
||||
unparsedUsage: String,
|
||||
unparsedReference: String,
|
||||
bookingDate: Date,
|
||||
otherPartyName: String?,
|
||||
otherPartyBankCode: String?,
|
||||
|
@ -44,10 +44,10 @@ open class RoomModelCreator : IModelCreator {
|
|||
originatorsIdentificationCode: String?,
|
||||
compensationAmount: String?,
|
||||
originalAmount: String?,
|
||||
sepaUsage: String?,
|
||||
sepaReference: String?,
|
||||
deviantOriginator: String?,
|
||||
deviantRecipient: String?,
|
||||
usageWithNoSpecialType: String?,
|
||||
referenceWithNoSpecialType: String?,
|
||||
primaNotaNumber: String?,
|
||||
textKeySupplement: String?,
|
||||
currencyType: String?,
|
||||
|
@ -58,10 +58,10 @@ open class RoomModelCreator : IModelCreator {
|
|||
transactionReferenceNumber: String,
|
||||
relatedReferenceNumber: String?
|
||||
): 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,
|
||||
creditorIdentifier, originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient,
|
||||
usageWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,
|
||||
creditorIdentifier, originatorsIdentificationCode, compensationAmount, originalAmount, sepaReference, deviantOriginator, deviantRecipient,
|
||||
referenceWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,
|
||||
referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package net.dankito.banking.persistence.model
|
|||
import androidx.room.ColumnInfo
|
||||
|
||||
|
||||
data class Remittee(
|
||||
data class TransactionParty(
|
||||
@ColumnInfo(name = "otherPartyName") val name: String,
|
||||
|
||||
@ColumnInfo(name = "otherPartyBankCode") val bankCode: String?,
|
|
@ -28,7 +28,7 @@ open class EntitiesModelCreator : IModelCreator {
|
|||
account: TypedBankAccount,
|
||||
amount: BigDecimal,
|
||||
currency: String,
|
||||
unparsedUsage: String,
|
||||
unparsedReference: String,
|
||||
bookingDate: Date,
|
||||
otherPartyName: String?,
|
||||
otherPartyBankCode: String?,
|
||||
|
@ -46,10 +46,10 @@ open class EntitiesModelCreator : IModelCreator {
|
|||
originatorsIdentificationCode: String?,
|
||||
compensationAmount: String?,
|
||||
originalAmount: String?,
|
||||
sepaUsage: String?,
|
||||
sepaReference: String?,
|
||||
deviantOriginator: String?,
|
||||
deviantRecipient: String?,
|
||||
usageWithNoSpecialType: String?,
|
||||
referenceWithNoSpecialType: String?,
|
||||
primaNotaNumber: String?,
|
||||
textKeySupplement: String?,
|
||||
currencyType: String?,
|
||||
|
@ -61,11 +61,11 @@ open class EntitiesModelCreator : IModelCreator {
|
|||
relatedReferenceNumber: String?
|
||||
) : 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,
|
||||
openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier,
|
||||
originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient,
|
||||
usageWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,
|
||||
originatorsIdentificationCode, compensationAmount, originalAmount, sepaReference, deviantOriginator, deviantRecipient,
|
||||
referenceWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,
|
||||
referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber)
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ open class AccountTransactionEntity(
|
|||
override var account: BankAccountEntity,
|
||||
override var amount: BigDecimal,
|
||||
override var currency: String,
|
||||
override var unparsedUsage: String,
|
||||
override var unparsedReference: String,
|
||||
override var bookingDate: Date,
|
||||
override var otherPartyName: String?,
|
||||
override var otherPartyBankCode: String?,
|
||||
|
@ -33,10 +33,10 @@ open class AccountTransactionEntity(
|
|||
override var originatorsIdentificationCode: String?,
|
||||
override var compensationAmount: String?,
|
||||
override var originalAmount: String?,
|
||||
override var sepaUsage: String?,
|
||||
override var sepaReference: String?,
|
||||
override var deviantOriginator: String?,
|
||||
override var deviantRecipient: String?,
|
||||
override var usageWithNoSpecialType: String?,
|
||||
override var referenceWithNoSpecialType: String?,
|
||||
override var primaNotaNumber: 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,
|
||||
null, "", null)
|
||||
|
||||
constructor(account: BankAccountEntity, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
|
||||
: this(account, amount, "EUR", unparsedUsage, valueDate, otherPartyName, null, null, bookingText, valueDate, 0, null, null, null,
|
||||
constructor(account: BankAccountEntity, otherPartyName: String?, unparsedReference: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
|
||||
: 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)
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ open class BankAccountEntity(
|
|||
override var supportsRetrievingAccountTransactions: Boolean = false,
|
||||
override var supportsRetrievingBalance: 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 unbookedTransactions: List<Any> = listOf(),
|
||||
override var technicalId: String = UUID.random(),
|
||||
|
|
|
@ -147,7 +147,7 @@ class BankingPersistenceJsonTest {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -233,7 +233,7 @@ class BankingPersistenceJsonTest {
|
|||
assertThat(deserializedTransaction.account.technicalId).isEqualTo(transaction.account.technicalId)
|
||||
|
||||
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.valueDate).isEqualTo(transaction.valueDate)
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ open class AccountTransactionAdapter(protected val presenter: BankingPresenter)
|
|||
val label = if (item.showOtherPartyName) item.otherPartyName else item.bookingText
|
||||
viewHolder.txtvwTransactionLabel.text = label ?: item.bookingText ?: ""
|
||||
|
||||
viewHolder.txtvwUsage.text = item.usage
|
||||
viewHolder.txtvwReference.text = item.reference
|
||||
|
||||
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.mnitmNewTransferToSameRemittee)?.let { mnitmShowTransferMoneyDialog ->
|
||||
menu.findItem(R.id.mnitmNewTransferToSameTransactionParty)?.let { mnitmShowTransferMoneyDialog ->
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -13,7 +13,7 @@ open class AccountTransactionViewHolder(itemView: View) : RecyclerView.ViewHolde
|
|||
|
||||
val txtvwTransactionLabel: TextView = itemView.txtvwTransactionLabel
|
||||
|
||||
val txtvwUsage: TextView = itemView.txtvwUsage
|
||||
val txtvwReference: TextView = itemView.txtvwReference
|
||||
|
||||
val txtvwAmount: TextView = itemView.txtvwAmount
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -9,8 +9,7 @@ import net.dankito.banking.ui.android.RouterAndroid
|
|||
import net.dankito.banking.ui.android.util.CurrentActivityTracker
|
||||
import net.dankito.banking.fints4kBankingClientCreator
|
||||
import net.dankito.banking.persistence.IBankingPersistence
|
||||
import net.dankito.banking.search.IRemitteeSearcher
|
||||
import net.dankito.banking.search.LuceneRemitteeSearcher
|
||||
import net.dankito.banking.search.ITransactionPartySearcher
|
||||
import net.dankito.banking.ui.IBankingClientCreator
|
||||
import net.dankito.banking.ui.IRouter
|
||||
import net.dankito.banking.ui.presenter.BankingPresenter
|
||||
|
@ -88,11 +87,11 @@ class BankingModule(private val applicationContext: Context) {
|
|||
@Singleton
|
||||
fun provideBankingPresenter(bankingClientCreator: IBankingClientCreator, bankFinder: IBankFinder,
|
||||
@Named(DataFolderKey) dataFolder: File,
|
||||
persister: IBankingPersistence, remitteeSearcher: IRemitteeSearcher, bankIconFinder: IBankIconFinder,
|
||||
persister: IBankingPersistence, transactionPartySearcher: ITransactionPartySearcher, bankIconFinder: IBankIconFinder,
|
||||
textExtractorRegistry: ITextExtractorRegistry, router: IRouter, invoiceDataExtractor: IInvoiceDataExtractor,
|
||||
modelCreator: IModelCreator, serializer: ISerializer, asyncRunner: IAsyncRunner) : BankingPresenter {
|
||||
return BankingPresenter(bankingClientCreator, bankFinder, dataFolder, persister, router, modelCreator,
|
||||
remitteeSearcher, bankIconFinder, textExtractorRegistry, invoiceDataExtractor, serializer, asyncRunner)
|
||||
transactionPartySearcher, bankIconFinder, textExtractorRegistry, invoiceDataExtractor, serializer, asyncRunner)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
@ -121,7 +120,7 @@ class BankingModule(private val applicationContext: Context) {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRemitteeSearcher(bankingPersistence: IBankingPersistence) : IRemitteeSearcher {
|
||||
fun provideTransactionPartySearcher(bankingPersistence: IBankingPersistence) : ITransactionPartySearcher {
|
||||
return bankingPersistence as RoomBankingPersistence
|
||||
}
|
||||
|
||||
|
|
|
@ -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.di.BankingComponent
|
||||
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.closePopupOnBackButtonPress
|
||||
import net.dankito.banking.ui.android.listener.ListItemSelectedListener
|
||||
import net.dankito.banking.ui.android.util.StandardAutocompleteCallback
|
||||
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.parameters.TransferMoneyData
|
||||
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 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
|
||||
|
||||
|
@ -118,73 +118,73 @@ open class TransferMoneyDialog : DialogFragment() {
|
|||
rootView.spnBankAccounts.adapter = adapter
|
||||
rootView.spnBankAccounts.onItemSelectedListener = ListItemSelectedListener(adapter) { selectedBankAccount ->
|
||||
this.account = selectedBankAccount
|
||||
setInstantPaymentControlsVisibility(rootView)
|
||||
setRealTimeTransferControlsVisibility(rootView)
|
||||
}
|
||||
preselectedValues?.account?.let { rootView.spnBankAccounts.setSelection(adapter.getItems().indexOf(it)) }
|
||||
}
|
||||
|
||||
initRemitteeAutocompletion(rootView.edtxtRemitteeName)
|
||||
initRecipientAutocompletion(rootView.edtxtRecipientName)
|
||||
|
||||
rootView.edtxtRemitteeName.addTextChangedListener(checkRequiredDataWatcher {
|
||||
checkIfEnteredRemitteeNameIsValidWhileUserIsTyping()
|
||||
rootView.edtxtRecipientName.addTextChangedListener(checkRequiredDataWatcher {
|
||||
checkIfEnteredRecipientNameIsValidWhileUserIsTyping()
|
||||
})
|
||||
|
||||
rootView.edtxtRemitteeIban.addTextChangedListener(StandardTextWatcher {
|
||||
checkIfEnteredRemitteeIbanIsValidWhileUserIsTyping()
|
||||
rootView.edtxtRecipientIban.addTextChangedListener(StandardTextWatcher {
|
||||
checkIfEnteredRecipientIbanIsValidWhileUserIsTyping()
|
||||
tryToGetBicFromIban(it)
|
||||
})
|
||||
|
||||
rootView.edtxtAmount.addTextChangedListener(checkRequiredDataWatcher {
|
||||
checkIfEnteredAmountIsValid()
|
||||
})
|
||||
rootView.edtxtUsage.addTextChangedListener(checkRequiredDataWatcher {
|
||||
checkIfEnteredUsageTextIsValid()
|
||||
rootView.edtxtReference.addTextChangedListener(checkRequiredDataWatcher {
|
||||
checkIfEnteredReferenceTextIsValid()
|
||||
})
|
||||
|
||||
rootView.edtxtRemitteeName.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRemitteeNameIsValidAfterFocusLost() }
|
||||
rootView.edtxtRemitteeIban.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRemitteeIbanIsValidAfterFocusLost() }
|
||||
rootView.edtxtRecipientName.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRecipientNameIsValidAfterFocusLost() }
|
||||
rootView.edtxtRecipientIban.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRecipientIbanIsValidAfterFocusLost() }
|
||||
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.edtxtRemitteeIban)
|
||||
transferMoneyIfEnterPressed(rootView.edtxtRecipientName)
|
||||
transferMoneyIfEnterPressed(rootView.edtxtRecipientIban)
|
||||
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)
|
||||
val decimalSeparator = DecimalFormatSymbols.getInstance().getDecimalSeparator()
|
||||
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.btnTransferMoney.setOnClickListener { transferMoney() }
|
||||
|
||||
adjustCheckBoxInstantPaymentWidth()
|
||||
adjustCheckBoxRealTimeTransferWidth()
|
||||
}
|
||||
|
||||
|
||||
protected open fun adjustCheckBoxInstantPaymentWidth() {
|
||||
protected open fun adjustCheckBoxRealTimeTransferWidth() {
|
||||
// wait some time till CheckBox is layout and lineCount is set
|
||||
val timer = Timer()
|
||||
timer.schedule(10) { requireActivity().runOnUiThread { adjustCheckBoxInstantPaymentWidthOnUiThread() }}
|
||||
timer.schedule(2500) { requireActivity().runOnUiThread { adjustCheckBoxInstantPaymentWidthOnUiThread() }}
|
||||
timer.schedule(10) { requireActivity().runOnUiThread { adjustCheckBoxRealTimeTransferWidthOnUiThread() }}
|
||||
timer.schedule(2500) { requireActivity().runOnUiThread { adjustCheckBoxRealTimeTransferWidthOnUiThread() }}
|
||||
}
|
||||
|
||||
protected open fun adjustCheckBoxInstantPaymentWidthOnUiThread() {
|
||||
if (chkbxInstantPayment.isEllipsized == false) {
|
||||
// by default chkbxInstantPayment 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)
|
||||
chkbxInstantPayment.requestLayout()
|
||||
protected open fun adjustCheckBoxRealTimeTransferWidthOnUiThread() {
|
||||
if (chkbxRealTimeTransfer.isEllipsized == false) {
|
||||
// by default chkbxRealTimeTransfer uses full width, even though if its text doesn't need this space -> there
|
||||
chkbxRealTimeTransfer.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 0f)
|
||||
chkbxRealTimeTransfer.requestLayout()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun setInstantPaymentControlsVisibility(rootView: View) {
|
||||
rootView.lytInstantPayment.visibility =
|
||||
if (account.supportsInstantPaymentMoneyTransfer) {
|
||||
protected open fun setRealTimeTransferControlsVisibility(rootView: View) {
|
||||
rootView.lytRealTimeTransfer.visibility =
|
||||
if (account.supportsRealTimeTransfer) {
|
||||
View.VISIBLE
|
||||
}
|
||||
else {
|
||||
|
@ -192,9 +192,9 @@ open class TransferMoneyDialog : DialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
protected open fun showInstantPaymentInfo(btnShowInstantPaymentInfo: ImageButton, rootView: View) {
|
||||
requireActivity().layoutInflater.inflate(R.layout.view_instant_payment_info, null)?.let { contentView ->
|
||||
requireContext().hideKeyboard(lytInstantPayment)
|
||||
protected open fun showRealTimeTransferInfo(btnShowRealTimeTransferInfo: ImageButton, rootView: View) {
|
||||
requireActivity().layoutInflater.inflate(R.layout.view_real_time_transfer_info, null)?.let { contentView ->
|
||||
requireContext().hideKeyboard(lytRealTimeTransfer)
|
||||
|
||||
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() }
|
||||
|
||||
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 initRemitteeAutocompletion(edtxtRemitteeName: EditText) {
|
||||
val autocompleteCallback = StandardAutocompleteCallback<Remittee> { _, item ->
|
||||
remitteeSelected(item)
|
||||
private fun initRecipientAutocompletion(edtxtRecipientName: EditText) {
|
||||
val autocompleteCallback = StandardAutocompleteCallback<TransactionParty> { _, item ->
|
||||
recipientSelected(item)
|
||||
true
|
||||
}
|
||||
|
||||
Autocomplete.on<Remittee>(edtxtRemitteeName)
|
||||
Autocomplete.on<TransactionParty>(edtxtRecipientName)
|
||||
.with(6f)
|
||||
.with(ColorDrawable(Color.WHITE))
|
||||
.with(autocompleteCallback)
|
||||
.with(RemitteePresenter(presenter, edtxtRemitteeName.context))
|
||||
.with(RecipientPresenter(presenter, edtxtRecipientName.context))
|
||||
.build()
|
||||
.closePopupOnBackButtonPress(dialog)
|
||||
}
|
||||
|
@ -244,8 +244,8 @@ open class TransferMoneyDialog : DialogFragment() {
|
|||
|
||||
setPreselectedValues()
|
||||
|
||||
if (remitteeBic != null) {
|
||||
tryToGetBicFromIban(edtxtRemitteeIban.text.toString())
|
||||
if (recipientBic != null) {
|
||||
tryToGetBicFromIban(edtxtRecipientIban.text.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,20 +254,20 @@ open class TransferMoneyDialog : DialogFragment() {
|
|||
preselectedValues?.let { data ->
|
||||
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
|
||||
edtxtRemitteeIban.setText(data.creditorIban)
|
||||
if (data.recipientAccountId.isNotBlank()) { // set only if recipientAccountId has a value as otherwise recipientBankCode would be overridden by empty search result
|
||||
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
|
||||
remitteeBic = data.creditorBic
|
||||
recipientBic = data.recipientBankCode
|
||||
|
||||
if (data.amount > BigDecimal.ZERO) {
|
||||
edtxtAmount.setText(data.amount.toString())
|
||||
}
|
||||
|
||||
edtxtUsage.setText(data.usage)
|
||||
edtxtReference.setText(data.reference)
|
||||
|
||||
focusEditTextAccordingToPreselectedValues()
|
||||
}
|
||||
|
@ -275,19 +275,19 @@ open class TransferMoneyDialog : DialogFragment() {
|
|||
|
||||
protected open fun focusEditTextAccordingToPreselectedValues() {
|
||||
when {
|
||||
edtxtRemitteeName.text.toString().isBlank() -> edtxtRemitteeName.requestFocus()
|
||||
edtxtRemitteeIban.text.toString().isBlank() -> edtxtRemitteeIban.requestFocus()
|
||||
edtxtRecipientName.text.toString().isBlank() -> edtxtRecipientName.requestFocus()
|
||||
edtxtRecipientIban.text.toString().isBlank() -> edtxtRecipientIban.requestFocus()
|
||||
edtxtAmount.text.toString().isBlank() -> edtxtAmount.requestFocus()
|
||||
edtxtUsage.text.toString().isBlank() -> edtxtUsage.requestFocus()
|
||||
else -> edtxtUsage.requestFocus()
|
||||
edtxtReference.text.toString().isBlank() -> edtxtReference.requestFocus()
|
||||
else -> edtxtReference.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected open fun remitteeSelected(item: Remittee) {
|
||||
edtxtRemitteeName.setText(item.name)
|
||||
edtxtRemitteeIban.setText(item.iban)
|
||||
remitteeBic = item.bic
|
||||
protected open fun recipientSelected(item: TransactionParty) {
|
||||
edtxtRecipientName.setText(item.name)
|
||||
edtxtRecipientIban.setText(item.iban)
|
||||
recipientBic = item.bic
|
||||
|
||||
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
|
||||
val data = TransferMoneyData(
|
||||
account,
|
||||
inputValidator.convertToAllowedSepaCharacters(edtxtRemitteeName.text.toString()),
|
||||
edtxtRemitteeIban.text.toString().replace(" ", ""),
|
||||
remitteeBic?.replace(" ", "") ?: "", // should always be != null at this point
|
||||
inputValidator.convertToAllowedSepaCharacters(edtxtRecipientName.text.toString()),
|
||||
edtxtRecipientIban.text.toString().replace(" ", ""),
|
||||
recipientBic?.replace(" ", "") ?: "", // should always be != null at this point
|
||||
amount.toBigDecimal(),
|
||||
inputValidator.convertToAllowedSepaCharacters(edtxtUsage.text.toString()),
|
||||
chkbxInstantPayment.isChecked
|
||||
inputValidator.convertToAllowedSepaCharacters(edtxtReference.text.toString()),
|
||||
chkbxRealTimeTransfer.isChecked
|
||||
)
|
||||
|
||||
presenter.transferMoneyAsync(data) {
|
||||
|
@ -317,11 +317,11 @@ open class TransferMoneyDialog : DialogFragment() {
|
|||
if (response.userCancelledAction == false) {
|
||||
val message = if (response.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 {
|
||||
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
|
||||
)
|
||||
}
|
||||
|
@ -356,66 +356,66 @@ open class TransferMoneyDialog : DialogFragment() {
|
|||
}
|
||||
|
||||
private fun showValuesForFoundBankOnUiThread(enteredIban: CharSequence, foundBank: BankInfo?) {
|
||||
validRemitteeBicEntered = foundBank != null
|
||||
remitteeBic = foundBank?.bic
|
||||
validRecipientBicEntered = foundBank != null
|
||||
recipientBic = foundBank?.bic
|
||||
|
||||
if (foundBank != null) {
|
||||
txtRemitteeBankInfo.text = getString(R.string.dialog_transfer_money_bic_detected_from_iban, foundBank.bic, foundBank.name)
|
||||
txtRemitteeBankInfo.visibility = View.VISIBLE
|
||||
txtRecipientBankInfo.text = getString(R.string.dialog_transfer_money_bic_detected_from_iban, foundBank.bic, foundBank.name)
|
||||
txtRecipientBankInfo.visibility = View.VISIBLE
|
||||
}
|
||||
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))
|
||||
txtRemitteeBankInfo.visibility = View.VISIBLE
|
||||
txtRecipientBankInfo.text = getString(R.string.dialog_transfer_money_could_not_determine_bic_from_iban, enteredIban.substring(4, InputValidator.MinimumLengthToDetermineBicFromIban))
|
||||
txtRecipientBankInfo.visibility = View.VISIBLE
|
||||
}
|
||||
else {
|
||||
txtRemitteeBankInfo.visibility = View.GONE
|
||||
txtRecipientBankInfo.visibility = View.GONE
|
||||
}
|
||||
|
||||
checkIfRequiredDataEnteredOnUiThread()
|
||||
}
|
||||
|
||||
protected open fun checkIfRequiredDataEnteredOnUiThread() {
|
||||
btnTransferMoney.isEnabled = validRemitteeNameEntered && validRemitteeIbanEntered
|
||||
&& validRemitteeBicEntered
|
||||
&& validAmountEntered && validUsageEntered
|
||||
btnTransferMoney.isEnabled = validRecipientNameEntered && validRecipientIbanEntered
|
||||
&& validRecipientBicEntered
|
||||
&& validAmountEntered && validReferenceEntered
|
||||
}
|
||||
|
||||
protected open fun checkIfEnteredRemitteeNameIsValidWhileUserIsTyping() {
|
||||
val enteredRemitteeName = edtxtRemitteeName.text.toString()
|
||||
val validationResult = inputValidator.validateRemitteeNameWhileTyping(enteredRemitteeName)
|
||||
protected open fun checkIfEnteredRecipientNameIsValidWhileUserIsTyping() {
|
||||
val enteredRecipientName = edtxtRecipientName.text.toString()
|
||||
val validationResult = inputValidator.validateRecipientNameWhileTyping(enteredRecipientName)
|
||||
|
||||
this.validRemitteeNameEntered = validationResult.validationSuccessfulOrCouldCorrectString
|
||||
this.validRecipientNameEntered = validationResult.validationSuccessfulOrCouldCorrectString
|
||||
|
||||
showValidationResult(lytRemitteeName, validationResult)
|
||||
showValidationResult(lytRecipientName, validationResult)
|
||||
}
|
||||
|
||||
protected open fun checkIfEnteredRemitteeNameIsValidAfterFocusLost() {
|
||||
val enteredRemitteeName = edtxtRemitteeName.text.toString()
|
||||
val validationResult = inputValidator.validateRemitteeName(enteredRemitteeName)
|
||||
protected open fun checkIfEnteredRecipientNameIsValidAfterFocusLost() {
|
||||
val enteredRecipientName = edtxtRecipientName.text.toString()
|
||||
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
|
||||
showValidationResult(lytRemitteeName, validationResult)
|
||||
showValidationResult(lytRecipientName, validationResult)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun checkIfEnteredRemitteeIbanIsValidWhileUserIsTyping() {
|
||||
val enteredIban = edtxtRemitteeIban.text.toString()
|
||||
protected open fun checkIfEnteredRecipientIbanIsValidWhileUserIsTyping() {
|
||||
val enteredIban = edtxtRecipientIban.text.toString()
|
||||
val validationResult = inputValidator.validateIbanWhileTyping(enteredIban)
|
||||
|
||||
this.validRemitteeIbanEntered = validationResult.validationSuccessfulOrCouldCorrectString
|
||||
this.validRecipientIbanEntered = validationResult.validationSuccessfulOrCouldCorrectString
|
||||
|
||||
showValidationResult(lytRemitteeIban, validationResult)
|
||||
showValidationResult(lytRecipientIban, validationResult)
|
||||
}
|
||||
|
||||
protected open fun checkIfEnteredRemitteeIbanIsValidAfterFocusLost() {
|
||||
val validationResult = inputValidator.validateIban(edtxtRemitteeIban.text.toString())
|
||||
protected open fun checkIfEnteredRecipientIbanIsValidAfterFocusLost() {
|
||||
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
|
||||
showValidationResult(lytRemitteeIban, validationResult)
|
||||
showValidationResult(lytRecipientIban, validationResult)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -437,12 +437,12 @@ open class TransferMoneyDialog : DialogFragment() {
|
|||
return null
|
||||
}
|
||||
|
||||
protected open fun checkIfEnteredUsageTextIsValid() {
|
||||
val validationResult = inputValidator.validateUsage(edtxtUsage.text.toString())
|
||||
protected open fun checkIfEnteredReferenceTextIsValid() {
|
||||
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) {
|
||||
|
@ -475,7 +475,7 @@ open class TransferMoneyDialog : DialogFragment() {
|
|||
|
||||
(textInputLayout.layoutParams as? ViewGroup.MarginLayoutParams)?.let { params ->
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.presenter.BankingPresenter
|
||||
import net.dankito.utils.android.extensions.asActivity
|
||||
import net.dankito.utils.android.extensions.getDimension
|
||||
import net.dankito.utils.multiplatform.sum
|
||||
import java.text.DateFormat
|
||||
import javax.inject.Inject
|
||||
|
@ -148,8 +147,8 @@ class HomeFragment : Fragment() {
|
|||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.mnitmNewTransferToSameRemittee -> {
|
||||
newTransferToSameRemittee()
|
||||
R.id.mnitmNewTransferToSameTransactionParty -> {
|
||||
newTransferToSameTransactionParty()
|
||||
return true
|
||||
}
|
||||
R.id.mnitmNewTransferWithSameData -> {
|
||||
|
@ -211,9 +210,9 @@ class HomeFragment : Fragment() {
|
|||
}
|
||||
|
||||
|
||||
private fun newTransferToSameRemittee() {
|
||||
private fun newTransferToSameTransactionParty() {
|
||||
transactionAdapter.selectedTransaction?.let { selectedTransaction ->
|
||||
presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndUsage(selectedTransaction))
|
||||
presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndReference(selectedTransaction))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,14 +47,14 @@
|
|||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/lytRemitteeName"
|
||||
android:id="@+id/lytRecipientName"
|
||||
android:layout_width="match_parent"
|
||||
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
|
||||
android:id="@+id/edtxtRemitteeName"
|
||||
android:id="@+id/edtxtRecipientName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dialog_transfer_money_input_fields_height"
|
||||
android:inputType="textPersonName"
|
||||
|
@ -67,14 +67,14 @@
|
|||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/lytRemitteeIban"
|
||||
android:id="@+id/lytRecipientIban"
|
||||
android:layout_width="match_parent"
|
||||
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
|
||||
android:id="@+id/edtxtRemitteeIban"
|
||||
android:id="@+id/edtxtRecipientIban"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dialog_transfer_money_autocomplete_fields_height"
|
||||
android:inputType="textCapCharacters"
|
||||
|
@ -83,7 +83,7 @@
|
|||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtRemitteeBankInfo"
|
||||
android:id="@+id/txtRecipientBankInfo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.AppCompat.Small"
|
||||
|
@ -109,14 +109,14 @@
|
|||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/lytUsage"
|
||||
android:id="@+id/lytReference"
|
||||
android:layout_width="match_parent"
|
||||
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
|
||||
android:id="@+id/edtxtUsage"
|
||||
android:id="@+id/edtxtReference"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dialog_transfer_money_input_fields_height"
|
||||
android:inputType="text"
|
||||
|
@ -127,20 +127,20 @@
|
|||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:id="@+id/lytInstantPayment"
|
||||
android:id="@+id/lytRealTimeTransfer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="@dimen/dialog_transfer_money_instant_payment_margin_top"
|
||||
android:layout_marginBottom="@dimen/dialog_transfer_money_instant_payment_margin_bottom"
|
||||
android:layout_marginTop="@dimen/dialog_transfer_money_real_time_transfer_margin_top"
|
||||
android:layout_marginBottom="@dimen/dialog_transfer_money_real_time_transfer_margin_bottom"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:visibility="gone"
|
||||
android:layout_marginRight="@dimen/dialog_transfer_money_instant_payment_show_info_button_margin_end"
|
||||
android:layout_marginEnd="@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_real_time_transfer_show_info_button_margin_end"
|
||||
>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/chkbxInstantPayment"
|
||||
android:id="@+id/chkbxRealTimeTransfer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
|
@ -148,18 +148,18 @@
|
|||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:textAlignment="gravity"
|
||||
android:textSize="@dimen/view_instant_payment_info_text_size"
|
||||
android:text="@string/dialog_transfer_money_instant_payment"
|
||||
android:textSize="@dimen/view_real_time_transfer_info_text_size"
|
||||
android:text="@string/dialog_transfer_money_real_time_transfer"
|
||||
/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnShowInstantPaymentInfo"
|
||||
android:layout_width="@dimen/dialog_transfer_money_instant_payment_show_info_button_size"
|
||||
android:layout_height="@dimen/dialog_transfer_money_instant_payment_show_info_button_size"
|
||||
android:id="@+id/btnShowRealTimeTransferInfo"
|
||||
android:layout_width="@dimen/dialog_transfer_money_real_time_transfer_show_info_button_size"
|
||||
android:layout_height="@dimen/dialog_transfer_money_real_time_transfer_show_info_button_size"
|
||||
android:layout_weight="0"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginLeft="@dimen/dialog_transfer_money_instant_payment_show_info_button_margin_start"
|
||||
android:layout_marginStart="@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_real_time_transfer_show_info_button_margin_start"
|
||||
android:background="@null"
|
||||
app:srcCompat="@drawable/ic_baseline_info_24"
|
||||
android:tint="@color/infoIconColor"
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
android:id="@+id/txtvwTransactionLabel"
|
||||
android:layout_width="match_parent"
|
||||
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:ellipsize="end"
|
||||
android:textSize="@dimen/list_item_account_transaction_label_text_size"
|
||||
|
@ -34,14 +34,14 @@
|
|||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvwUsage"
|
||||
android:id="@+id/txtvwReference"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:textAlignment="gravity"
|
||||
android:lines="2"
|
||||
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"
|
||||
/>
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="@dimen/list_item_remittee_padding"
|
||||
android:padding="@dimen/list_item_recipient_padding"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvwRemitteeName"
|
||||
android:id="@+id/txtvwRecipientName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.AppCompat.Medium"
|
||||
|
@ -21,10 +21,10 @@
|
|||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvwRemitteeBankName"
|
||||
android:id="@+id/txtvwRecipientBankName"
|
||||
android:layout_width="match_parent"
|
||||
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:lines="1"
|
||||
android:ellipsize="end"
|
||||
|
@ -35,11 +35,11 @@
|
|||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
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
|
||||
android:id="@+id/txtvwRemitteeAccountId"
|
||||
android:id="@+id/txtvwRecipientAccountId"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
|
@ -50,7 +50,7 @@
|
|||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvwRemitteeBankCode"
|
||||
android:id="@+id/txtvwRecipientBankCode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
|
@ -18,8 +18,8 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textSize="@dimen/view_instant_payment_info_text_size"
|
||||
android:text="@string/dialog_transfer_money_instant_payment_info"
|
||||
android:textSize="@dimen/view_real_time_transfer_info_text_size"
|
||||
android:text="@string/dialog_transfer_money_real_time_transfer_info"
|
||||
/>
|
||||
|
||||
</ScrollView>
|
||||
|
@ -27,14 +27,14 @@
|
|||
<LinearLayout
|
||||
android:id="@+id/lytButtonBar"
|
||||
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:gravity="center"
|
||||
>
|
||||
|
||||
<Button
|
||||
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"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:text="@android:string/ok"
|
|
@ -2,7 +2,7 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/mnitmNewTransferToSameRemittee"
|
||||
android:id="@+id/mnitmNewTransferToSameTransactionParty"
|
||||
android:title="@string/fragment_home_transfer_money_to"
|
||||
/>
|
||||
|
||||
|
|
|
@ -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_transfer_money_account">Konto:</string>
|
||||
<string name="dialog_transfer_money_remittee_name">Name:</string>
|
||||
<string name="dialog_transfer_money_remittee_iban">IBAN:</string>
|
||||
<string name="dialog_transfer_money_recipient_name">Name:</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_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_usage">Verwendungszweck:</string>
|
||||
<string name="dialog_transfer_money_instant_payment">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_reference">Verwendungszweck:</string>
|
||||
<string name="dialog_transfer_money_real_time_transfer">Echtzeitüberweisung (evtl. kostenpflichtig)</string>
|
||||
<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\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>
|
||||
|
@ -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_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_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>
|
||||
|
|
|
@ -34,9 +34,9 @@
|
|||
|
||||
<dimen name="list_item_account_transaction_height">74dp</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_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_margin_top">0dp</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_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_instant_payment_text_size">14sp</dimen>
|
||||
<dimen name="dialog_transfer_money_instant_payment_margin_top">6dp</dimen>
|
||||
<dimen name="dialog_transfer_money_instant_payment_margin_bottom">6dp</dimen>
|
||||
<dimen name="dialog_transfer_money_instant_payment_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_instant_payment_show_info_button_margin_end">12dp</dimen>
|
||||
<dimen name="dialog_transfer_money_real_time_transfer_text_size">14sp</dimen>
|
||||
<dimen name="dialog_transfer_money_real_time_transfer_margin_top">6dp</dimen>
|
||||
<dimen name="dialog_transfer_money_real_time_transfer_margin_bottom">6dp</dimen>
|
||||
<dimen name="dialog_transfer_money_real_time_transfer_show_info_button_size">20dp</dimen>
|
||||
<dimen name="dialog_transfer_money_real_time_transfer_show_info_button_margin_start">6dp</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="list_item_bank_account_padding">2dp</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_remittee_padding">4dp</dimen>
|
||||
<dimen name="list_item_remittee_space_between_fields">4dp</dimen>
|
||||
<dimen name="list_item_recipient_padding">4dp</dimen>
|
||||
<dimen name="list_item_recipient_space_between_fields">4dp</dimen>
|
||||
|
||||
<dimen name="view_instant_payment_info_padding">8dp</dimen>
|
||||
<dimen name="view_instant_payment_info_text_size">15sp</dimen>
|
||||
<dimen name="view_instant_payment_info_dismiss_button_width">150dp</dimen>
|
||||
<dimen name="view_instant_payment_info_dismiss_button_height">40dp</dimen>
|
||||
<dimen name="view_real_time_transfer_info_padding">8dp</dimen>
|
||||
<dimen name="view_real_time_transfer_info_text_size">15sp</dimen>
|
||||
<dimen name="view_real_time_transfer_info_dismiss_button_width">150dp</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_width">40dp</dimen>
|
||||
|
|
|
@ -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_transfer_money_account">Account:</string>
|
||||
<string name="dialog_transfer_money_remittee_name">Name:</string>
|
||||
<string name="dialog_transfer_money_remittee_iban">IBAN:</string>
|
||||
<string name="dialog_transfer_money_recipient_name">Name:</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_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_usage">Usage:</string>
|
||||
<string name="dialog_transfer_money_instant_payment">Instant payment (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.
|
||||
\n\nInstant payment transfers on the other hand are transferred within a maximum of 10 seconds, regardless of the day and time of day.
|
||||
<string name="dialog_transfer_money_reference">Reference:</string>
|
||||
<string name="dialog_transfer_money_real_time_transfer">Real-time transfer (may with costs)</string>
|
||||
<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\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\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_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>
|
||||
|
@ -110,17 +110,17 @@
|
|||
\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_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_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_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_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>
|
||||
|
|
|
@ -12,7 +12,7 @@ import net.dankito.banking.util.BankIconFinder
|
|||
import net.dankito.banking.bankfinder.LuceneBankFinder
|
||||
import net.dankito.banking.persistence.LuceneBankingPersistence
|
||||
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.extraction.JavaTextExtractorRegistry
|
||||
import net.dankito.text.extraction.TextExtractorRegistry
|
||||
|
@ -47,9 +47,9 @@ class MainWindow : View(messages["application.title"]) {
|
|||
|
||||
private val presenter = BankingPresenter(fints4kBankingClientCreator(modelCreator, serializer),
|
||||
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),
|
||||
// dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), RouterJavaFx(), modelCreator, LuceneRemitteeSearcher(indexFolder),
|
||||
// dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), RouterJavaFx(), modelCreator, LuceneTransactionPartySearcher(indexFolder),
|
||||
// BankIconFinder(), textExtractorRegistry)
|
||||
|
||||
|
||||
|
|
|
@ -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.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.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.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.account.label=Account:
|
||||
transfer.money.dialog.remittee.name.label=Remittee:
|
||||
transfer.money.dialog.remittee.iban.label=IBAN:
|
||||
transfer.money.dialog.remittee.bank.label=Bank:
|
||||
transfer.money.dialog.remittee.bic.label=BIC:
|
||||
transfer.money.dialog.recipient.name.label=Recipient:
|
||||
transfer.money.dialog.recipient.iban.label=IBAN:
|
||||
transfer.money.dialog.recipient.bank.label=Bank:
|
||||
transfer.money.dialog.recipient.bic.label=BIC:
|
||||
transfer.money.dialog.amount.label=Amount:
|
||||
transfer.money.dialog.usage.label=Usage:
|
||||
transfer.money.dialog.instant.payment.label=Instant payment
|
||||
transfer.money.dialog.reference.label=Reference:
|
||||
transfer.money.dialog.real.time.transfer.label=Real-time 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.not.found.for.iban=No bank found for this IBAN
|
||||
|
|
|
@ -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.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.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.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.account.label=Konto:
|
||||
transfer.money.dialog.remittee.name.label=Begünstigter (Name oder Firma):
|
||||
transfer.money.dialog.remittee.iban.label=IBAN:
|
||||
transfer.money.dialog.remittee.bank.label=Bank:
|
||||
transfer.money.dialog.remittee.bic.label=BIC:
|
||||
transfer.money.dialog.recipient.name.label=Begünstigter (Name oder Firma):
|
||||
transfer.money.dialog.recipient.iban.label=IBAN:
|
||||
transfer.money.dialog.recipient.bank.label=Bank:
|
||||
transfer.money.dialog.recipient.bic.label=BIC:
|
||||
transfer.money.dialog.amount.label=Betrag:
|
||||
transfer.money.dialog.usage.label=Verwendungszweck:
|
||||
transfer.money.dialog.instant.payment.label=Echtzeitüberweisung
|
||||
transfer.money.dialog.reference.label=Verwendungszweck:
|
||||
transfer.money.dialog.real.time.transfer.label=Echtzeitüberweisung
|
||||
transfer.money.dialog.transfer.money.label=Überweisen
|
||||
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
|
||||
|
|
|
@ -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 {
|
||||
contentDisplay = ContentDisplay.GRAPHIC_ONLY
|
||||
|
@ -72,7 +72,7 @@ open class AccountTransactionsTable @JvmOverloads constructor(
|
|||
}
|
||||
}
|
||||
|
||||
label(it.usage) {
|
||||
label(it.reference) {
|
||||
vboxConstraints {
|
||||
margin = LabelMargin
|
||||
}
|
||||
|
|
|
@ -84,8 +84,8 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi
|
|||
|
||||
contextMenu.apply {
|
||||
if (selectedItem.canCreateMoneyTransferFrom) {
|
||||
item(String.format(FX.messages["account.transactions.table.context.menu.new.transfer.to.same.remittee"], selectedItem.otherPartyName)) {
|
||||
action { newTransferToSameRemittee(selectedItem) }
|
||||
item(String.format(FX.messages["account.transactions.table.context.menu.new.transfer.to.same.recipient"], selectedItem.otherPartyName)) {
|
||||
action { newTransferToSameTransactionParty(selectedItem) }
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
protected open fun newTransferToSameRemittee(transaction: IAccountTransaction) {
|
||||
presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndUsage(transaction))
|
||||
protected open fun newTransferToSameTransactionParty(transaction: IAccountTransaction) {
|
||||
presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndReference(transaction))
|
||||
}
|
||||
|
||||
protected open fun newTransferWithSameData(transaction: IAccountTransaction) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package net.dankito.banking.ui.javafx.dialogs.cashtransfer
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import net.dankito.banking.search.Remittee
|
||||
import net.dankito.banking.search.TransactionParty
|
||||
import tornadofx.ItemViewModel
|
||||
|
||||
|
||||
open class RemitteeViewModel : ItemViewModel<Remittee>() {
|
||||
open class RecipientViewModel : ItemViewModel<TransactionParty>() {
|
||||
|
||||
val name = bind { SimpleStringProperty(item?.name) }
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -18,7 +18,7 @@ import net.dankito.banking.ui.model.responses.BankingClientResponse
|
|||
import net.dankito.banking.ui.presenter.BankingPresenter
|
||||
import net.dankito.banking.util.InputValidator
|
||||
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.banking.ui.javafx.extensions.focusNextControl
|
||||
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 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 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 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()
|
||||
|
@ -87,11 +88,11 @@ open class TransferMoneyDialog @JvmOverloads constructor(
|
|||
init {
|
||||
selectedBankAccount.addListener { _, _, newValue -> selectedBankAccountChanged(newValue) }
|
||||
|
||||
remitteeName.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() }
|
||||
remitteeIban.addListener { _, _, newValue -> tryToGetBicFromIban(newValue) }
|
||||
remitteeBic.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() }
|
||||
recipientName.addListener { _, _, _ -> checkIfRequiredDataEnteredOnUiThread() }
|
||||
recipientIban.addListener { _, _, newValue -> tryToGetBicFromIban(newValue) }
|
||||
recipientBic.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
|
||||
|
||||
txtfldRemitteeName = autocompletionsearchtextfield(this@TransferMoneyDialog.remitteeName) {
|
||||
txtfldRecipientName = autocompletionsearchtextfield(this@TransferMoneyDialog.recipientName) {
|
||||
fixedHeight = TextFieldHeight
|
||||
|
||||
textProperty().addListener { _, _, newValue -> searchRemittees(newValue) }
|
||||
textProperty().addListener { _, _, newValue -> searchRecipients(newValue) }
|
||||
|
||||
onAutoCompletion = { remitteeSelected(it) }
|
||||
listCellFragment = RemitteeListCellFragment::class
|
||||
onAutoCompletion = { recipientSelected(it) }
|
||||
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
|
||||
|
||||
textfield(remitteeIban) {
|
||||
textfield(recipientIban) {
|
||||
fixedHeight = TextFieldHeight
|
||||
|
||||
paddingLeft = 8.0
|
||||
|
||||
if (this@TransferMoneyDialog.remitteeName.value.isNotBlank()) {
|
||||
if (this@TransferMoneyDialog.recipientName.value.isNotBlank()) {
|
||||
runLater {
|
||||
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
|
||||
|
||||
textfield(remitteeBankName) {
|
||||
textfield(recipientBankName) {
|
||||
fixedHeight = TextFieldHeight
|
||||
|
||||
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
|
||||
|
||||
textfield(remitteeBic) {
|
||||
textfield(recipientBic) {
|
||||
fixedHeight = TextFieldHeight
|
||||
|
||||
isDisable = true
|
||||
|
@ -197,7 +198,7 @@ open class TransferMoneyDialog @JvmOverloads constructor(
|
|||
fixedWidth = 100.0
|
||||
alignment = Pos.CENTER_RIGHT
|
||||
|
||||
if (this@TransferMoneyDialog.remitteeName.value.isNotBlank() && remitteeIban.value.isNotBlank()) {
|
||||
if (this@TransferMoneyDialog.recipientName.value.isNotBlank() && recipientIban.value.isNotBlank()) {
|
||||
runLater {
|
||||
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
|
||||
|
||||
textfield(usage) {
|
||||
textfield(reference) {
|
||||
fixedHeight = TextFieldHeight
|
||||
}
|
||||
}
|
||||
|
@ -223,10 +224,10 @@ open class TransferMoneyDialog @JvmOverloads constructor(
|
|||
field {
|
||||
fixedHeight = FieldHeight
|
||||
|
||||
checkbox(messages["transfer.money.dialog.instant.payment.label"], instantPayment) {
|
||||
checkbox(messages["transfer.money.dialog.real.time.transfer.label"], realTimeTransfer) {
|
||||
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?) {
|
||||
supportsInstantPayment.value = newValue?.supportsInstantPaymentMoneyTransfer ?: false
|
||||
supportsRealTimeTransfer.value = newValue?.supportsRealTimeTransfer ?: false
|
||||
|
||||
if (supportsInstantPayment.value == false) {
|
||||
instantPayment.value = false
|
||||
if (supportsRealTimeTransfer.value == false) {
|
||||
realTimeTransfer.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected open fun searchRemittees(query: String?) {
|
||||
lastSearchRemitteeJob?.cancel()
|
||||
protected open fun searchRecipients(query: String?) {
|
||||
lastSearchRecipientJob?.cancel()
|
||||
|
||||
lastSearchRemitteeJob = GlobalScope.launch(Dispatchers.IO) {
|
||||
val potentialRemittees = presenter.findRemitteesForName(query?.toString() ?: "")
|
||||
lastSearchRecipientJob = GlobalScope.launch(Dispatchers.IO) {
|
||||
val potentialRecipients = presenter.findRecipientsForName(query?.toString() ?: "")
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
txtfldRemitteeName.setAutoCompleteList(potentialRemittees)
|
||||
txtfldRecipientName.setAutoCompleteList(potentialRecipients)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun remitteeSelected(remittee: Remittee) {
|
||||
txtfldRemitteeName.focusNextControl()
|
||||
protected open fun recipientSelected(transactionParty: TransactionParty) {
|
||||
txtfldRecipientName.focusNextControl()
|
||||
|
||||
remitteeName.value = remittee.name
|
||||
remitteeBic.value = remittee.bic
|
||||
remitteeIban.value = remittee.iban
|
||||
recipientName.value = transactionParty.name
|
||||
recipientBic.value = transactionParty.bic
|
||||
recipientIban.value = transactionParty.iban
|
||||
}
|
||||
|
||||
|
||||
|
@ -307,11 +308,11 @@ open class TransferMoneyDialog @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
@ -331,12 +332,12 @@ open class TransferMoneyDialog @JvmOverloads constructor(
|
|||
|
||||
protected open fun checkIfRequiredDataEnteredOnUiThread() {
|
||||
requiredDataEntered.value =
|
||||
remitteeName.value.isNotBlank()
|
||||
&& inputValidator.isRemitteeNameValid(remitteeName.value) // TODO: show error message for illegal characters
|
||||
&& inputValidator.isValidIban(remitteeIban.value)
|
||||
&& inputValidator.isValidBic(remitteeBic.value)
|
||||
recipientName.value.isNotBlank()
|
||||
&& inputValidator.isRecipientNameValid(recipientName.value) // TODO: show error message for illegal characters
|
||||
&& inputValidator.isValidIban(recipientIban.value)
|
||||
&& inputValidator.isValidBic(recipientBic.value)
|
||||
&& 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() {
|
||||
remitteeBank.value?.let {
|
||||
recipientBank.value?.let {
|
||||
val account = selectedBankAccount.value
|
||||
|
||||
val data = TransferMoneyData(
|
||||
account,
|
||||
inputValidator.convertToAllowedSepaCharacters(remitteeName.value),
|
||||
remitteeIban.value.replace(" ", ""),
|
||||
remitteeBic.value.replace(" ", ""),
|
||||
inputValidator.convertToAllowedSepaCharacters(recipientName.value),
|
||||
recipientIban.value.replace(" ", ""),
|
||||
recipientBic.value.replace(" ", ""),
|
||||
amount.value.toBigDecimal().toBigDecimal(),
|
||||
inputValidator.convertToAllowedSepaCharacters(usage.value),
|
||||
instantPayment.value
|
||||
inputValidator.convertToAllowedSepaCharacters(reference.value),
|
||||
realTimeTransfer.value
|
||||
)
|
||||
|
||||
presenter.transferMoneyAsync(data) {
|
||||
|
@ -371,11 +372,11 @@ open class TransferMoneyDialog @JvmOverloads constructor(
|
|||
|
||||
if (response.successful) {
|
||||
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) {
|
||||
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
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package net.dankito.banking.search
|
||||
|
||||
|
||||
interface IRemitteeSearcher {
|
||||
|
||||
fun findRemittees(query: String): List<Remittee>
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package net.dankito.banking.search
|
||||
|
||||
|
||||
interface ITransactionPartySearcher {
|
||||
|
||||
fun findTransactionParty(query: String): List<TransactionParty>
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package net.dankito.banking.search
|
||||
|
||||
|
||||
open class NoOpRemitteeSearcher : IRemitteeSearcher {
|
||||
|
||||
override fun findRemittees(query: String): List<Remittee> {
|
||||
return listOf()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package net.dankito.banking.search
|
||||
|
||||
|
||||
open class NoOpTransactionPartySearcher : ITransactionPartySearcher {
|
||||
|
||||
override fun findTransactionParty(query: String): List<TransactionParty> {
|
||||
return listOf()
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package net.dankito.banking.search
|
||||
|
||||
|
||||
data class Remittee(
|
||||
data class TransactionParty(
|
||||
val name: String,
|
||||
val iban: String?,
|
||||
val bic: String?,
|
||||
|
@ -14,7 +14,7 @@ data class Remittee(
|
|||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
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 (iban != other.iban) return false
|
|
@ -8,7 +8,7 @@ open class AccountTransaction(
|
|||
override val account: TypedBankAccount,
|
||||
override val amount: BigDecimal,
|
||||
override val currency: String,
|
||||
override val unparsedUsage: String,
|
||||
override val unparsedReference: String,
|
||||
override val bookingDate: Date,
|
||||
override val otherPartyName: String?,
|
||||
override val otherPartyBankCode: String?,
|
||||
|
@ -27,10 +27,10 @@ open class AccountTransaction(
|
|||
override val originatorsIdentificationCode: String?,
|
||||
override val compensationAmount: String?,
|
||||
override val originalAmount: String?,
|
||||
override val sepaUsage: String?,
|
||||
override val sepaReference: String?,
|
||||
override val deviantOriginator: String?,
|
||||
override val deviantRecipient: String?,
|
||||
override val usageWithNoSpecialType: String?,
|
||||
override val referenceWithNoSpecialType: String?,
|
||||
override val primaNotaNumber: String?,
|
||||
override val textKeySupplement: String?,
|
||||
|
||||
|
@ -49,15 +49,15 @@ open class AccountTransaction(
|
|||
|
||||
/* convenience constructors for languages not supporting default values */
|
||||
|
||||
constructor(account: BankAccount, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
|
||||
: this(account, amount, "EUR", unparsedUsage, valueDate,
|
||||
constructor(account: BankAccount, otherPartyName: String?, unparsedReference: String, amount: BigDecimal, valueDate: Date, bookingText: String?)
|
||||
: this(account, amount, "EUR", unparsedReference, 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?,
|
||||
bookingText: String?, valueDate: Date)
|
||||
: this(account, amount, currency, unparsedUsage, bookingDate,
|
||||
: this(account, amount, currency, unparsedReference, bookingDate,
|
||||
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)
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ open class BankAccount @JvmOverloads constructor(
|
|||
override var supportsRetrievingAccountTransactions: Boolean = false,
|
||||
override var supportsRetrievingBalance: 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 unbookedTransactions: List<Any> = listOf()
|
||||
) : TypedBankAccount {
|
||||
|
|
|
@ -16,7 +16,7 @@ interface IAccountTransaction {
|
|||
val account: IBankAccount<*>
|
||||
val amount: BigDecimal
|
||||
val currency: String
|
||||
val unparsedUsage: String
|
||||
val unparsedReference: String
|
||||
val bookingDate: Date
|
||||
val otherPartyName: String?
|
||||
val otherPartyBankCode: String?
|
||||
|
@ -35,10 +35,10 @@ interface IAccountTransaction {
|
|||
val originatorsIdentificationCode: String?
|
||||
val compensationAmount: String?
|
||||
val originalAmount: String?
|
||||
val sepaUsage: String?
|
||||
val sepaReference: String?
|
||||
val deviantOriginator: String?
|
||||
val deviantRecipient: String?
|
||||
val usageWithNoSpecialType: String?
|
||||
val referenceWithNoSpecialType: String?
|
||||
val primaNotaNumber: String?
|
||||
val textKeySupplement: String?
|
||||
|
||||
|
@ -54,7 +54,7 @@ interface IAccountTransaction {
|
|||
|
||||
var technicalId: String
|
||||
|
||||
open val transactionIdentifier: String
|
||||
val transactionIdentifier: String
|
||||
get() = buildTransactionIdentifier()
|
||||
|
||||
|
||||
|
@ -64,16 +64,16 @@ interface IAccountTransaction {
|
|||
val canCreateMoneyTransferFrom: Boolean
|
||||
get() = otherPartyAccountId != null && account.supportsTransferringMoney
|
||||
|
||||
val usage: String
|
||||
get() = sepaUsage ?: unparsedUsage
|
||||
val reference: String
|
||||
get() = sepaReference ?: unparsedReference
|
||||
|
||||
|
||||
fun buildTransactionIdentifier() : String {
|
||||
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
|
||||
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 (amount != other.amount) 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 (otherPartyName != other.otherPartyName) return false
|
||||
if (otherPartyBankCode != other.otherPartyBankCode) return false
|
||||
|
@ -101,7 +101,7 @@ interface IAccountTransaction {
|
|||
var result = account.hashCode()
|
||||
result = 31 * result + amount.hashCode()
|
||||
result = 31 * result + currency.hashCode()
|
||||
result = 31 * result + unparsedUsage.hashCode()
|
||||
result = 31 * result + unparsedReference.hashCode()
|
||||
result = 31 * result + bookingDate.hashCode()
|
||||
result = 31 * result + (otherPartyName?.hashCode() ?: 0)
|
||||
result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0)
|
||||
|
@ -112,6 +112,6 @@ interface IAccountTransaction {
|
|||
}
|
||||
|
||||
val stringRepresentation: String
|
||||
get() = "${DateFormatter(DateFormatStyle.Medium).format(valueDate)} $amount $otherPartyName: $usage"
|
||||
get() = "${DateFormatter(DateFormatStyle.Medium).format(valueDate)} $amount $otherPartyName: $reference"
|
||||
|
||||
}
|
|
@ -24,7 +24,7 @@ interface IBankAccount<TTransaction: IAccountTransaction> : OrderedDisplayable {
|
|||
var supportsRetrievingAccountTransactions: Boolean
|
||||
var supportsRetrievingBalance: Boolean
|
||||
var supportsTransferringMoney: Boolean
|
||||
var supportsInstantPaymentMoneyTransfer: Boolean
|
||||
var supportsRealTimeTransfer: Boolean
|
||||
var bookedTransactions: List<TTransaction>
|
||||
var unbookedTransactions: List<Any>
|
||||
var technicalId: String
|
||||
|
|
|
@ -22,7 +22,7 @@ open class DefaultModelCreator : IModelCreator {
|
|||
account: TypedBankAccount,
|
||||
amount: BigDecimal,
|
||||
currency: String,
|
||||
unparsedUsage: String,
|
||||
unparsedReference: String,
|
||||
bookingDate: Date,
|
||||
otherPartyName: String?,
|
||||
otherPartyBankCode: String?,
|
||||
|
@ -40,10 +40,10 @@ open class DefaultModelCreator : IModelCreator {
|
|||
originatorsIdentificationCode: String?,
|
||||
compensationAmount: String?,
|
||||
originalAmount: String?,
|
||||
sepaUsage: String?,
|
||||
sepaReference: String?,
|
||||
deviantOriginator: String?,
|
||||
deviantRecipient: String?,
|
||||
usageWithNoSpecialType: String?,
|
||||
referenceWithNoSpecialType: String?,
|
||||
primaNotaNumber: String?,
|
||||
textKeySupplement: String?,
|
||||
currencyType: String?,
|
||||
|
@ -55,11 +55,11 @@ open class DefaultModelCreator : IModelCreator {
|
|||
relatedReferenceNumber: String?
|
||||
) : IAccountTransaction {
|
||||
|
||||
return AccountTransaction(account, amount, currency, unparsedUsage, bookingDate,
|
||||
return AccountTransaction(account, amount, currency, unparsedReference, bookingDate,
|
||||
otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, statementNumber, sequenceNumber,
|
||||
openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier,
|
||||
originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient,
|
||||
usageWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,
|
||||
originatorsIdentificationCode, compensationAmount, originalAmount, sepaReference, deviantOriginator, deviantRecipient,
|
||||
referenceWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner,
|
||||
referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ interface IModelCreator {
|
|||
account: TypedBankAccount,
|
||||
amount: BigDecimal,
|
||||
currency: String,
|
||||
unparsedUsage: String,
|
||||
unparsedReference: String,
|
||||
bookingDate: Date,
|
||||
otherPartyName: String?,
|
||||
otherPartyBankCode: String?,
|
||||
|
@ -38,10 +38,10 @@ interface IModelCreator {
|
|||
originatorsIdentificationCode: String?,
|
||||
compensationAmount: String?,
|
||||
originalAmount: String?,
|
||||
sepaUsage: String?,
|
||||
sepaReference: String?,
|
||||
deviantOriginator: String?,
|
||||
deviantRecipient: String?,
|
||||
usageWithNoSpecialType: String?,
|
||||
referenceWithNoSpecialType: String?,
|
||||
primaNotaNumber: String?,
|
||||
textKeySupplement: String?,
|
||||
|
||||
|
|
|
@ -6,17 +6,17 @@ import net.dankito.utils.multiplatform.BigDecimal
|
|||
|
||||
open class TransferMoneyData(
|
||||
val account: TypedBankAccount,
|
||||
val creditorName: String,
|
||||
val creditorIban: String,
|
||||
val creditorBic: String,
|
||||
val recipientName: String,
|
||||
val recipientAccountId: String,
|
||||
val recipientBankCode: String,
|
||||
val amount: BigDecimal,
|
||||
val usage: String,
|
||||
val instantPayment: Boolean = false
|
||||
val reference: String,
|
||||
val realTimeTransfer: Boolean = false
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
fun fromAccountTransactionWithoutAmountAndUsage(transaction: IAccountTransaction): TransferMoneyData {
|
||||
fun fromAccountTransactionWithoutAmountAndReference(transaction: IAccountTransaction): TransferMoneyData {
|
||||
return TransferMoneyData(
|
||||
transaction.account as TypedBankAccount,
|
||||
transaction.otherPartyName ?: "",
|
||||
|
@ -34,7 +34,7 @@ open class TransferMoneyData(
|
|||
transaction.otherPartyAccountId ?: "",
|
||||
transaction.otherPartyBankCode ?: "",
|
||||
if (transaction.amount.isPositive) transaction.amount else transaction.amount.negated(),
|
||||
transaction.usage
|
||||
transaction.reference
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ import net.dankito.banking.ui.model.responses.BankingClientResponse
|
|||
import net.dankito.banking.ui.model.responses.GetTransactionsResponse
|
||||
import net.dankito.banking.bankfinder.IBankFinder
|
||||
import net.dankito.banking.bankfinder.BankInfo
|
||||
import net.dankito.banking.search.IRemitteeSearcher
|
||||
import net.dankito.banking.search.NoOpRemitteeSearcher
|
||||
import net.dankito.banking.search.Remittee
|
||||
import net.dankito.banking.search.ITransactionPartySearcher
|
||||
import net.dankito.banking.search.NoOpTransactionPartySearcher
|
||||
import net.dankito.banking.search.TransactionParty
|
||||
import net.dankito.banking.ui.model.mapper.DefaultModelCreator
|
||||
import net.dankito.banking.ui.model.mapper.IModelCreator
|
||||
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult
|
||||
|
@ -39,7 +39,7 @@ open class BankingPresenter(
|
|||
protected val persister: IBankingPersistence,
|
||||
protected val router: IRouter,
|
||||
protected val modelCreator: IModelCreator = DefaultModelCreator(),
|
||||
protected val remitteeSearcher: IRemitteeSearcher = NoOpRemitteeSearcher(),
|
||||
protected val transactionPartySearcher: ITransactionPartySearcher = NoOpTransactionPartySearcher(),
|
||||
protected val bankIconFinder: IBankIconFinder = NoOpBankIconFinder(),
|
||||
protected val textExtractorRegistry: ITextExtractorRegistry = NoOpTextExtractorRegistry(),
|
||||
protected val invoiceDataExtractor: IInvoiceDataExtractor = NoOpInvoiceDataExtractor(),
|
||||
|
@ -553,16 +553,16 @@ open class BankingPresenter(
|
|||
return bankFinder.findBankByNameBankCodeOrCity(query)
|
||||
}
|
||||
|
||||
open fun findRemitteesForName(name: String): List<Remittee> {
|
||||
return remitteeSearcher.findRemittees(name).map { remittee ->
|
||||
remittee.bankName = tryToFindBankName(remittee)
|
||||
open fun findRecipientsForName(name: String): List<TransactionParty> {
|
||||
return transactionPartySearcher.findTransactionParty(name).map { recipient ->
|
||||
recipient.bankName = tryToFindBankName(recipient)
|
||||
|
||||
remittee
|
||||
recipient
|
||||
}.toSet().toList()
|
||||
}
|
||||
|
||||
protected open fun tryToFindBankName(remittee: Remittee): String? {
|
||||
remittee.bic?.let { bic ->
|
||||
protected open fun tryToFindBankName(transactionParty: TransactionParty): String? {
|
||||
transactionParty.bic?.let { bic ->
|
||||
bankFinder.findBankByBic(bic)?.name?.let {
|
||||
return it
|
||||
}
|
||||
|
@ -574,7 +574,7 @@ open class BankingPresenter(
|
|||
}
|
||||
}
|
||||
|
||||
remittee.iban?.let { iban ->
|
||||
transactionParty.iban?.let { iban ->
|
||||
if (iban.length > 12) {
|
||||
val bankCode = iban.substring(4, 12)
|
||||
return bankFinder.findBankByBankCode(bankCode).firstOrNull()?.name
|
||||
|
@ -598,7 +598,7 @@ open class BankingPresenter(
|
|||
|
||||
return transactions.filter {
|
||||
it.otherPartyName?.toLowerCase()?.contains(queryLowercase) == true
|
||||
|| it.usage.toLowerCase().contains(queryLowercase)
|
||||
|| it.reference.toLowerCase().contains(queryLowercase)
|
||||
|| it.bookingText?.toLowerCase()?.contains(queryLowercase) == true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ open class InputValidator {
|
|||
|
||||
companion object {
|
||||
|
||||
const val RemitteNameMaxLength = 70
|
||||
const val RecipientNameMaxLength = 70
|
||||
|
||||
const val IbanMaxLength = 34
|
||||
|
||||
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
|
||||
|
||||
|
@ -65,45 +65,45 @@ open class InputValidator {
|
|||
protected val sepaMessageCreator: ISepaMessageCreator = SepaMessageCreator()
|
||||
|
||||
|
||||
open fun validateRemitteeNameWhileTyping(remitteeNameToTest: String): ValidationResult {
|
||||
return validateRemitteeName(remitteeNameToTest, true)
|
||||
open fun validateRecipientNameWhileTyping(recipientNameToTest: String): ValidationResult {
|
||||
return validateRecipientName(recipientNameToTest, true)
|
||||
}
|
||||
|
||||
open fun validateRemitteeName(remitteeNameToTest: String): ValidationResult {
|
||||
return validateRemitteeName(remitteeNameToTest, false)
|
||||
open fun validateRecipientName(recipientNameToTest: String): ValidationResult {
|
||||
return validateRecipientName(recipientNameToTest, false)
|
||||
}
|
||||
|
||||
open fun validateRemitteeName(remitteeNameToTest: String, userIsStillTyping: Boolean = false): ValidationResult {
|
||||
if (isRemitteeNameValid(remitteeNameToTest)) {
|
||||
return ValidationResult(remitteeNameToTest, true)
|
||||
open fun validateRecipientName(recipientNameToTest: String, userIsStillTyping: Boolean = false): ValidationResult {
|
||||
if (isRecipientNameValid(recipientNameToTest)) {
|
||||
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
|
||||
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) {
|
||||
val correctedString = remitteeNameToTest.substring(0, RemitteNameMaxLength)
|
||||
return ValidationResult(remitteeNameToTest, isRemitteeNameValid(correctedString), true, correctedString, "Name darf maximal 70 Zeichen lang sein") // TODO: translate
|
||||
if (hasRecipientNameValidLength(recipientNameToTest) == false) {
|
||||
val correctedString = recipientNameToTest.substring(0, RecipientNameMaxLength)
|
||||
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)
|
||||
return ValidationResult(remitteeNameToTest, isRemitteeNameValid(correctedString), true, correctedString, null, "Unzulässige(s) Zeichen eingegeben: $invalidRemitteeNameCharacters") // TODO: translate
|
||||
val correctedString = getCorrectedString(recipientNameToTest, invalidRecipientNameCharacters, true)
|
||||
return ValidationResult(recipientNameToTest, isRecipientNameValid(correctedString), true, correctedString, null, "Unzulässige(s) Zeichen eingegeben: $invalidRecipientNameCharacters") // TODO: translate
|
||||
}
|
||||
|
||||
open fun isRemitteeNameValid(stringToTest: String): Boolean {
|
||||
return hasRemitteeNameValidLength(stringToTest)
|
||||
open fun isRecipientNameValid(stringToTest: String): Boolean {
|
||||
return hasRecipientNameValidLength(stringToTest)
|
||||
&& containsOnlyValidSepaCharacters(stringToTest)
|
||||
}
|
||||
|
||||
open fun hasRemitteeNameValidLength(stringToTest: String): Boolean {
|
||||
return stringToTest.length in 1..RemitteNameMaxLength
|
||||
open fun hasRecipientNameValidLength(stringToTest: String): Boolean {
|
||||
return stringToTest.length in 1..RecipientNameMaxLength
|
||||
}
|
||||
|
||||
|
||||
|
@ -221,29 +221,29 @@ open class InputValidator {
|
|||
}
|
||||
|
||||
|
||||
open fun validateUsage(usageToTest: String): ValidationResult {
|
||||
if (isUsageValid(usageToTest)) {
|
||||
return ValidationResult(usageToTest, true)
|
||||
open fun validateReference(referenceToTest: String): ValidationResult {
|
||||
if (isReferenceValid(referenceToTest)) {
|
||||
return ValidationResult(referenceToTest, true)
|
||||
}
|
||||
|
||||
if (hasUsageValidLength(usageToTest) == false) {
|
||||
val correctedString = usageToTest.substring(0, UsageMaxLength)
|
||||
return ValidationResult(usageToTest, isUsageValid(correctedString), true, correctedString, "Verwendungszweck darf nur 140 Zeichen lang sein") // TODO: translate
|
||||
if (hasReferenceValidLength(referenceToTest) == false) {
|
||||
val correctedString = referenceToTest.substring(0, ReferenceMaxLength)
|
||||
return ValidationResult(referenceToTest, isReferenceValid(correctedString), true, correctedString, "Verwendungszweck darf nur 140 Zeichen lang sein") // TODO: translate
|
||||
}
|
||||
|
||||
|
||||
val invalidUsageCharacters = getInvalidSepaCharacters(usageToTest)
|
||||
val correctedString = getCorrectedString(usageToTest, invalidUsageCharacters, 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
|
||||
val invalidReferenceCharacters = getInvalidSepaCharacters(referenceToTest)
|
||||
val correctedString = getCorrectedString(referenceToTest, invalidReferenceCharacters, true)
|
||||
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 {
|
||||
return hasUsageValidLength(stringToTest)
|
||||
open fun isReferenceValid(stringToTest: String): Boolean {
|
||||
return hasReferenceValidLength(stringToTest)
|
||||
&& containsOnlyValidSepaCharacters(stringToTest)
|
||||
}
|
||||
|
||||
open fun hasUsageValidLength(stringToTest: String): Boolean {
|
||||
return stringToTest.length in 0..UsageMaxLength // usage is not a required field -> may be empty
|
||||
open fun hasReferenceValidLength(stringToTest: String): Boolean {
|
||||
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};)').
|
||||
// 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 {
|
||||
var correctedString = if (convertToAllowedSepaCharacters) convertToAllowedSepaCharacters(inputString) else inputString
|
||||
|
||||
|
|
|
@ -10,13 +10,13 @@ class InputValidatorTest {
|
|||
|
||||
companion object {
|
||||
|
||||
const val ValidRemitteeName = "Marieke Musterfrau"
|
||||
const val ValidRecipientName = "Marieke Musterfrau"
|
||||
|
||||
const val ValidIban = "DE11123456780987654321"
|
||||
|
||||
const val ValidBic = "ABCDDEBBXXX"
|
||||
|
||||
const val ValidUsage = "Usage"
|
||||
const val ValidReference = "Reference"
|
||||
|
||||
const val InvalidSepaCharacter = "!"
|
||||
|
||||
|
@ -56,13 +56,13 @@ class InputValidatorTest {
|
|||
|
||||
|
||||
@Test
|
||||
fun validateRemitteeName_EmptyStringEntered() {
|
||||
fun validateRecipientName_EmptyStringEntered() {
|
||||
|
||||
// given
|
||||
val enteredName = ""
|
||||
|
||||
// when
|
||||
val result = underTest.validateRemitteeName(enteredName)
|
||||
val result = underTest.validateRecipientName(enteredName)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(false)
|
||||
|
@ -74,13 +74,13 @@ class InputValidatorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun validateRemitteeName_ValidNameEntered() {
|
||||
fun validateRecipientName_ValidNameEntered() {
|
||||
|
||||
// given
|
||||
val enteredName = ValidRemitteeName
|
||||
val enteredName = ValidRecipientName
|
||||
|
||||
// when
|
||||
val result = underTest.validateRemitteeName(enteredName)
|
||||
val result = underTest.validateRecipientName(enteredName)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
|
@ -92,50 +92,50 @@ class InputValidatorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun validateRemitteeName_UmlautGetsConverted() {
|
||||
fun validateRecipientName_UmlautGetsConverted() {
|
||||
|
||||
// given
|
||||
val enteredName = ValidRemitteeName + InvalidUmlaut
|
||||
val enteredName = ValidRecipientName + InvalidUmlaut
|
||||
|
||||
// when
|
||||
val result = underTest.validateRemitteeName(enteredName)
|
||||
val result = underTest.validateRecipientName(enteredName)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
expect(result.didCorrectString).toBe(true)
|
||||
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.validationError).toBe(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validateRemitteeName_InvalidCharacterGetsRemoved() {
|
||||
fun validateRecipientName_InvalidCharacterGetsRemoved() {
|
||||
|
||||
// given
|
||||
val enteredName = ValidRemitteeName + InvalidSepaCharacter
|
||||
val enteredName = ValidRecipientName + InvalidSepaCharacter
|
||||
|
||||
// when
|
||||
val result = underTest.validateRemitteeName(enteredName)
|
||||
val result = underTest.validateRecipientName(enteredName)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
expect(result.didCorrectString).toBe(true)
|
||||
expect(result.inputString).toBe(enteredName)
|
||||
expect(result.correctedInputString).toBe(ValidRemitteeName)
|
||||
expect(result.correctedInputString).toBe(ValidRecipientName)
|
||||
expect(result.validationHint?.contains(InvalidSepaCharacter)).toBe(true)
|
||||
expect(result.validationError).toBe(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validateRemitteeName_TooLong() {
|
||||
fun validateRecipientName_TooLong() {
|
||||
|
||||
// given
|
||||
val nameWithMaxLength = IntRange(0, InputValidator.RemitteNameMaxLength - 1).map { "a" }.joinToString("")
|
||||
val enteredName = nameWithMaxLength + "a"
|
||||
|
||||
// when
|
||||
val result = underTest.validateRemitteeName(enteredName)
|
||||
val result = underTest.validateRecipientName(enteredName)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
|
@ -480,133 +480,133 @@ class InputValidatorTest {
|
|||
|
||||
|
||||
@Test
|
||||
fun validateUsage_EmptyStringEntered() {
|
||||
fun validateReference_EmptyStringEntered() {
|
||||
|
||||
// given
|
||||
val enteredUsage = ""
|
||||
val enteredReference = ""
|
||||
|
||||
// when
|
||||
val result = underTest.validateUsage(enteredUsage)
|
||||
val result = underTest.validateReference(enteredReference)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
expect(result.didCorrectString).toBe(false)
|
||||
expect(result.inputString).toBe(enteredUsage)
|
||||
expect(result.correctedInputString).toBe(enteredUsage)
|
||||
expect(result.inputString).toBe(enteredReference)
|
||||
expect(result.correctedInputString).toBe(enteredReference)
|
||||
expect(result.validationHint).toBe(null)
|
||||
expect(result.validationError).toBe(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validateUsage_ValidUsageEntered() {
|
||||
fun validateReference_ValidReferenceEntered() {
|
||||
|
||||
// given
|
||||
val enteredUsage = ValidUsage
|
||||
val enteredReference = ValidReference
|
||||
|
||||
// when
|
||||
val result = underTest.validateUsage(enteredUsage)
|
||||
val result = underTest.validateReference(enteredReference)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
expect(result.didCorrectString).toBe(false)
|
||||
expect(result.inputString).toBe(enteredUsage)
|
||||
expect(result.correctedInputString).toBe(enteredUsage)
|
||||
expect(result.inputString).toBe(enteredReference)
|
||||
expect(result.correctedInputString).toBe(enteredReference)
|
||||
expect(result.validationHint).toBe(null)
|
||||
expect(result.validationError).toBe(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validateUsage_UmlautGetsConverted() {
|
||||
fun validateReference_UmlautGetsConverted() {
|
||||
|
||||
// given
|
||||
val enteredUsage = ValidUsage + InvalidUmlaut
|
||||
val enteredReference = ValidReference + InvalidUmlaut
|
||||
|
||||
// when
|
||||
val result = underTest.validateUsage(enteredUsage)
|
||||
val result = underTest.validateReference(enteredReference)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
expect(result.didCorrectString).toBe(true)
|
||||
expect(result.inputString).toBe(enteredUsage)
|
||||
expect(result.correctedInputString).toBe(ValidUsage + ConvertedInvalidUmlaut)
|
||||
expect(result.inputString).toBe(enteredReference)
|
||||
expect(result.correctedInputString).toBe(ValidReference + ConvertedInvalidUmlaut)
|
||||
expect(result.validationHint?.contains(InvalidUmlaut)).toBe(true)
|
||||
expect(result.validationError).toBe(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validateUsage_InvalidCharacterGetsRemoved() {
|
||||
fun validateReference_InvalidCharacterGetsRemoved() {
|
||||
|
||||
// given
|
||||
val enteredUsage = ValidUsage + InvalidSepaCharacter
|
||||
val enteredReference = ValidReference + InvalidSepaCharacter
|
||||
|
||||
// when
|
||||
val result = underTest.validateUsage(enteredUsage)
|
||||
val result = underTest.validateReference(enteredReference)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
expect(result.didCorrectString).toBe(true)
|
||||
expect(result.inputString).toBe(enteredUsage)
|
||||
expect(result.correctedInputString).toBe(ValidUsage)
|
||||
expect(result.inputString).toBe(enteredReference)
|
||||
expect(result.correctedInputString).toBe(ValidReference)
|
||||
expect(result.validationHint?.contains(InvalidSepaCharacter)).toBe(true)
|
||||
expect(result.validationError).toBe(null)
|
||||
}
|
||||
|
||||
// TODO: does not work yet
|
||||
@Test
|
||||
fun validateUsage_AmpersandGetsRemoved() {
|
||||
fun validateReference_AmpersandGetsRemoved() {
|
||||
|
||||
// given
|
||||
val invalidSepaCharacter = "&"
|
||||
val enteredUsage = ValidUsage + invalidSepaCharacter
|
||||
val enteredReference = ValidReference + invalidSepaCharacter
|
||||
|
||||
// when
|
||||
val result = underTest.validateUsage(enteredUsage)
|
||||
val result = underTest.validateReference(enteredReference)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
expect(result.didCorrectString).toBe(true)
|
||||
expect(result.inputString).toBe(enteredUsage)
|
||||
expect(result.correctedInputString).toBe(ValidUsage)
|
||||
expect(result.inputString).toBe(enteredReference)
|
||||
expect(result.correctedInputString).toBe(ValidReference)
|
||||
expect(result.validationHint?.contains(invalidSepaCharacter)).toBe(true)
|
||||
expect(result.validationError).toBe(null)
|
||||
}
|
||||
|
||||
// TODO: does not work yet
|
||||
@Test
|
||||
fun validateUsage_EnteringACharacterAfterConvertingAXmlEntityDoesNotFail() {
|
||||
fun validateReference_EnteringACharacterAfterConvertingAXmlEntityDoesNotFail() {
|
||||
|
||||
// given
|
||||
val convertedXmlEntity = "&"
|
||||
val validSepaCharacter = "h"
|
||||
val enteredUsage = ValidUsage + convertedXmlEntity + validSepaCharacter
|
||||
val enteredReference = ValidReference + convertedXmlEntity + validSepaCharacter
|
||||
|
||||
// when
|
||||
val result = underTest.validateUsage(enteredUsage)
|
||||
val result = underTest.validateReference(enteredReference)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
expect(result.didCorrectString).toBe(true)
|
||||
expect(result.inputString).toBe(enteredUsage)
|
||||
expect(result.correctedInputString).toBe(ValidUsage + convertedXmlEntity + validSepaCharacter)
|
||||
expect(result.inputString).toBe(enteredReference)
|
||||
expect(result.correctedInputString).toBe(ValidReference + convertedXmlEntity + validSepaCharacter)
|
||||
expect(result.validationHint).toBe(null)
|
||||
expect(result.validationError).toBe(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validateUsage_TooLong() {
|
||||
fun validateReference_TooLong() {
|
||||
|
||||
// given
|
||||
val usageWithMaxLength = IntRange(0, InputValidator.UsageMaxLength - 1).map { "a" }.joinToString("")
|
||||
val enteredUsage = usageWithMaxLength + "a"
|
||||
val referenceWithMaxLength = IntRange(0, InputValidator.ReferenceMaxLength - 1).map { "a" }.joinToString("")
|
||||
val enteredReference = referenceWithMaxLength + "a"
|
||||
|
||||
// when
|
||||
val result = underTest.validateUsage(enteredUsage)
|
||||
val result = underTest.validateReference(enteredReference)
|
||||
|
||||
// then
|
||||
expect(result.validationSuccessful).toBe(true)
|
||||
expect(result.didCorrectString).toBe(true)
|
||||
expect(result.inputString).toBe(enteredUsage)
|
||||
expect(result.correctedInputString).toBe(usageWithMaxLength)
|
||||
expect(result.inputString).toBe(enteredReference)
|
||||
expect(result.correctedInputString).toBe(referenceWithMaxLength)
|
||||
expect(result.validationHint).toBe(null)
|
||||
expect(result.validationError).notToBeNull()
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package net.dankito.banking
|
|||
import net.dankito.banking.bankfinder.InMemoryBankFinder
|
||||
import net.dankito.banking.fints.webclient.IWebClient
|
||||
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.model.mapper.DefaultModelCreator
|
||||
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,
|
||||
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(),
|
||||
remitteeSearcher, bankIconFinder, NoOpTextExtractorRegistry(), NoOpInvoiceDataExtractor(), serializer, asyncRunner) {
|
||||
transactionPartySearcher, bankIconFinder, NoOpTextExtractorRegistry(), NoOpInvoiceDataExtractor(), serializer, asyncRunner) {
|
||||
|
||||
}
|
|
@ -28,11 +28,11 @@
|
|||
3642F01425018BA9005186FE /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F01325018BA9005186FE /* TabBarController.swift */; };
|
||||
3642F01625018DA1005186FE /* InterceptTabClickViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F01525018DA1005186FE /* InterceptTabClickViewController.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 */; };
|
||||
366FA4DA24C472A90094F009 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4D924C472A90094F009 /* Extensions.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 */; };
|
||||
366FA4E624C6EBF40094F009 /* EnterTanState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E524C6EBF40094F009 /* EnterTanState.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -340,7 +340,7 @@
|
|||
36BCF88424C098C8005BEC29 /* BankAccountListItem.swift */,
|
||||
36BE066424CDE62800CBBB68 /* AccountTransactionListItem.swift */,
|
||||
366FA4DB24C479120094F009 /* BankInfoListItem.swift */,
|
||||
366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */,
|
||||
366FA4DF24C4924A0094F009 /* RecipientListItem.swift */,
|
||||
36E21EDE24DCCC2700649DC8 /* CheckmarkListItem.swift */,
|
||||
);
|
||||
path = listitems;
|
||||
|
@ -552,7 +552,7 @@
|
|||
36BE06B424CF85A300CBBB68 /* AmountLabel.swift */,
|
||||
360782CE24F3D6610098FEFE /* InfoLabel.swift */,
|
||||
360782C224E49FF70098FEFE /* ValidationLabel.swift */,
|
||||
3642F0192502931F005186FE /* InstantPaymentInfoView.swift */,
|
||||
3642F0192502931F005186FE /* RealTimeTransferInfoView.swift */,
|
||||
36BE065C24CB08FB00CBBB68 /* LazyView.swift */,
|
||||
36C4009A24D2F9E4005227AD /* IconedTitleView.swift */,
|
||||
36E21ECE24DA0EEE00649DC8 /* IconView.swift */,
|
||||
|
@ -756,7 +756,7 @@
|
|||
360782D124F3F4120098FEFE /* SelectorWrapper.swift in Sources */,
|
||||
36BE06BA24D0783900CBBB68 /* FaviconFinder.swift in Sources */,
|
||||
36BCF89524C31F02005BEC29 /* AppData.swift in Sources */,
|
||||
3642F01A2502931F005186FE /* InstantPaymentInfoView.swift in Sources */,
|
||||
3642F01A2502931F005186FE /* RealTimeTransferInfoView.swift in Sources */,
|
||||
36B8A44F2503D97D00C15359 /* AuthenticationType.swift in Sources */,
|
||||
36E21EDD24DCA89100649DC8 /* TanMethodPicker.swift in Sources */,
|
||||
3608D6C624FBAB41006C93A8 /* TanGeneratorPositionMarker.swift in Sources */,
|
||||
|
@ -771,7 +771,7 @@
|
|||
36E21EDF24DCCC2700649DC8 /* CheckmarkListItem.swift in Sources */,
|
||||
36E7BA1424B3D05C00757859 /* ViewExtensions.swift in Sources */,
|
||||
36BCF88924C0A7D7005BEC29 /* Message.swift in Sources */,
|
||||
366FA4E024C4924A0094F009 /* RemitteeListItem.swift in Sources */,
|
||||
366FA4E024C4924A0094F009 /* RecipientListItem.swift in Sources */,
|
||||
3608D6C224FBA9C6006C93A8 /* TrianglePointingDown.swift in Sources */,
|
||||
36B8A4582503EEB600C15359 /* ActionSheet.swift in Sources */,
|
||||
36BE068B24CE3B0400CBBB68 /* SwiftExtensions.swift in Sources */,
|
||||
|
|
|
@ -24,15 +24,15 @@
|
|||
<attribute name="primaNotaNumber" optional="YES" attributeType="String"/>
|
||||
<attribute name="referenceForTheAccountOwner" 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="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="statementNumber" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="supplementaryDetails" optional="YES" attributeType="String"/>
|
||||
<attribute name="textKeySupplement" optional="YES" attributeType="String"/>
|
||||
<attribute name="transactionReferenceNumber" attributeType="String"/>
|
||||
<attribute name="unparsedUsage" attributeType="String"/>
|
||||
<attribute name="usageWithNoSpecialType" optional="YES" attributeType="String"/>
|
||||
<attribute name="unparsedReference" attributeType="String"/>
|
||||
<attribute name="valueDate" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistedBankAccount" inverseName="transactions" inverseEntity="PersistedBankAccount"/>
|
||||
</entity>
|
||||
|
@ -51,7 +51,7 @@
|
|||
<attribute name="retrievedTransactionsFromOn" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="retrievedTransactionsUpTo" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<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="supportsRetrievingBalance" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="supportsTransferringMoney" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
|
|
|
@ -101,20 +101,20 @@
|
|||
/* TransferMoneyDialog */
|
||||
|
||||
"Transfer Money Dialog Title" = "Bank transfer";
|
||||
"Remittee Name" = "Name";
|
||||
"Remittee IBAN" = "IBAN";
|
||||
"Recipient Name" = "Name";
|
||||
"Recipient IBAN" = "IBAN";
|
||||
"BIC: %@, %@" = "BIC: %@, %@";
|
||||
"No BIC found for bank code %@" = "No BIC found for bank code %@";
|
||||
"Amount" = "Amount";
|
||||
"Usage" = "Usage";
|
||||
"Enter usage" = "Optionally enter usage";
|
||||
"Reference" = "Reference";
|
||||
"Enter reference" = "Optionally enter reference";
|
||||
|
||||
"Instant Payment" = "Instant Payment";
|
||||
"Real-time transfer" = "Real-time transfer";
|
||||
"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
|
||||
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 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
|
||||
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
|
||||
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";
|
||||
|
||||
|
@ -170,7 +170,7 @@ Unfortunately, Bankmeister cannot know whether a bank charges for instant paymen
|
|||
"Supports Retrieving Balance" = "Retrieve balance";
|
||||
"Supports Retrieving Account Transactions" = "Retrieve transactions";
|
||||
"Supports Transferring Money" = "Transfer money";
|
||||
"Supports Instant payment transfer" = "Instant payment transfer";
|
||||
"Supports Real-time transfer" = "Real-time transfer";
|
||||
|
||||
|
||||
/* ProtectAppSettingsDialog */
|
||||
|
|
|
@ -47,7 +47,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
|
||||
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: presenter)
|
||||
|
|
|
@ -101,17 +101,17 @@
|
|||
/* TransferMoneyDialog */
|
||||
|
||||
"Transfer Money Dialog Title" = "Überweisung";
|
||||
"Remittee Name" = "Name";
|
||||
"Remittee IBAN" = "IBAN";
|
||||
"Recipient Name" = "Name";
|
||||
"Recipient IBAN" = "IBAN";
|
||||
"BIC: %@, %@" = "BIC: %@, %@";
|
||||
"No BIC found for bank code %@" = "Keine BIC gefunden für BLZ %@";
|
||||
"Amount" = "Betrag";
|
||||
"Usage" = "Verwendungszweck";
|
||||
"Enter usage" = "Optional einen Verwendungszweck eingeben";
|
||||
"Reference" = "Verwendungszweck";
|
||||
"Enter reference" = "Optional einen Verwendungszweck eingeben";
|
||||
|
||||
"Instant Payment" = "Echtzeitüberweisung";
|
||||
"Real-time transfer" = "Echtzeitüberweisung";
|
||||
"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
|
||||
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.";
|
||||
|
@ -170,7 +170,7 @@ Ob eine Bank Gebühren für Echtzeitüberweisungen erhebt, kann Bankmeister leid
|
|||
"Supports Retrieving Balance" = "Kontostand abrufen";
|
||||
"Supports Retrieving Account Transactions" = "Kontoumsätze abrufen";
|
||||
"Supports Transferring Money" = "Überweisen";
|
||||
"Supports Instant payment transfer" = "Echtzeitüberweisung";
|
||||
"Supports Real-time transfer" = "Echtzeitüberweisung";
|
||||
|
||||
|
||||
/* ProtectAppSettingsDialog */
|
||||
|
|
|
@ -159,7 +159,7 @@ extension BankInfo : Identifiable {
|
|||
}
|
||||
|
||||
|
||||
extension Remittee : Identifiable {
|
||||
extension TransactionParty : Identifiable {
|
||||
|
||||
public var id: String { name.localizedLowercase + "_" + (iban ?? "") }
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import UIKit
|
|||
import BankingUiSwift
|
||||
|
||||
|
||||
class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
|
||||
class CoreDataBankingPersistence: IBankingPersistence, ITransactionPartySearcher {
|
||||
|
||||
private let mapper = Mapper()
|
||||
|
||||
|
@ -123,7 +123,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
|
|||
return UserDefaults.standard.data(forKey: filePath)
|
||||
}
|
||||
|
||||
func findRemittees(query: String) -> [Remittee] {
|
||||
func findTransactionParty(query: String) -> [TransactionParty] {
|
||||
var transactions: [PersistedAccountTransaction] = []
|
||||
|
||||
do {
|
||||
|
@ -140,13 +140,13 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
|
|||
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
|
||||
.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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ class Mapper {
|
|||
}
|
||||
|
||||
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.isAccountTypeSupported = account.isAccountTypeSupported
|
||||
|
@ -97,7 +97,7 @@ class Mapper {
|
|||
mapped.supportsRetrievingAccountTransactions = account.supportsRetrievingAccountTransactions
|
||||
mapped.supportsRetrievingBalance = account.supportsRetrievingBalance
|
||||
mapped.supportsTransferringMoney = account.supportsTransferringMoney
|
||||
mapped.supportsInstantPaymentMoneyTransfer = account.supportsInstantPaymentMoneyTransfer
|
||||
mapped.supportsRealTimeTransfer = account.supportsRealTimeTransfer
|
||||
|
||||
mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched
|
||||
|
||||
|
@ -147,7 +147,7 @@ class Mapper {
|
|||
}
|
||||
|
||||
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
|
||||
|
||||
|
@ -166,7 +166,7 @@ class Mapper {
|
|||
|
||||
mapped.amount = map(transaction.amount)
|
||||
mapped.currency = transaction.currency
|
||||
mapped.unparsedUsage = transaction.unparsedUsage
|
||||
mapped.unparsedReference = transaction.unparsedReference
|
||||
mapped.bookingDate = map(transaction.bookingDate)
|
||||
mapped.otherPartyName = transaction.otherPartyName
|
||||
mapped.otherPartyBankCode = transaction.otherPartyBankCode
|
||||
|
@ -186,10 +186,10 @@ class Mapper {
|
|||
mapped.originatorsIdentificationCode = transaction.originatorsIdentificationCode
|
||||
mapped.compensationAmount = transaction.compensationAmount
|
||||
mapped.originalAmount = transaction.originalAmount
|
||||
mapped.sepaUsage = transaction.sepaUsage
|
||||
mapped.sepaReference = transaction.sepaReference
|
||||
mapped.deviantOriginator = transaction.deviantOriginator
|
||||
mapped.deviantRecipient = transaction.deviantRecipient
|
||||
mapped.usageWithNoSpecialType = transaction.usageWithNoSpecialType
|
||||
mapped.referenceWithNoSpecialType = transaction.referenceWithNoSpecialType
|
||||
mapped.primaNotaNumber = transaction.primaNotaNumber
|
||||
mapped.textKeySupplement = transaction.textKeySupplement
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ struct BankAccountSettingsDialog: View {
|
|||
|
||||
CheckmarkListItem("Supports Transferring Money", account.supportsTransferringMoney)
|
||||
|
||||
CheckmarkListItem("Supports Instant payment transfer", account.supportsInstantPaymentMoneyTransfer)
|
||||
CheckmarkListItem("Supports Real-time transfer", account.supportsRealTimeTransfer)
|
||||
}
|
||||
}
|
||||
.alert(message: $unsavedChangesMessage)
|
||||
|
|
|
@ -14,34 +14,34 @@ struct TransferMoneyDialog: View {
|
|||
|
||||
@State private var selectedAccountIndex = 0
|
||||
|
||||
@State private var remitteeName: String = ""
|
||||
@State private var isValidRemitteeNameEntered = false
|
||||
@State private var remitteeNameValidationResult: ValidationResult? = nil
|
||||
@State private var recipientName: String = ""
|
||||
@State private var isValidRecipientNameEntered = false
|
||||
@State private var recipientNameValidationResult: ValidationResult? = nil
|
||||
|
||||
@State private var showRemitteeAutocompleteList = false
|
||||
@State private var remitteeSearchResults = [Remittee]()
|
||||
@State private var showRecipientAutocompleteList = false
|
||||
@State private var recipientSearchResults = [TransactionParty]()
|
||||
|
||||
@State private var remitteeIban: String = ""
|
||||
@State private var isValidRemitteeIbanEntered = false
|
||||
@State private var remitteeIbanValidationResult: ValidationResult? = nil
|
||||
@State private var recipientIban: String = ""
|
||||
@State private var isValidRecipientIbanEntered = false
|
||||
@State private var recipientIbanValidationResult: ValidationResult? = nil
|
||||
|
||||
@State private var remitteeBic: String = ""
|
||||
@State private var isValidRemitteeBicEntered = false
|
||||
@State private var remitteeBicValidationResult: ValidationResult? = nil
|
||||
@State private var recipientBic: String = ""
|
||||
@State private var isValidRecipientBicEntered = false
|
||||
@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 isValidAmountEntered = false
|
||||
@State private var amountValidationResult: ValidationResult? = nil
|
||||
|
||||
@State private var usage: String = ""
|
||||
@State private var isValidUsageEntered = true
|
||||
@State private var usageValidationResult: ValidationResult? = nil
|
||||
@State private var reference: String = ""
|
||||
@State private var isValidReferenceEntered = true
|
||||
@State private var referenceValidationResult: ValidationResult? = nil
|
||||
|
||||
@State private var validateDataWhenShowingDialog = false
|
||||
|
||||
@State private var instantPayment = false
|
||||
@State private var realTimeTransfer = false
|
||||
|
||||
@State private var isTransferringMoney = false
|
||||
|
||||
|
@ -60,8 +60,8 @@ struct TransferMoneyDialog: View {
|
|||
return self.accountsSupportingTransferringMoney.first
|
||||
}
|
||||
|
||||
private var supportsInstantPayment: Bool {
|
||||
return self.account?.supportsInstantPaymentMoneyTransfer ?? false
|
||||
private var supportsRealTimeTransfer: Bool {
|
||||
return self.account?.supportsRealTimeTransfer ?? false
|
||||
}
|
||||
|
||||
|
||||
|
@ -82,22 +82,22 @@ struct TransferMoneyDialog: View {
|
|||
let preselectedBankAccount = preselectedValues.account
|
||||
self._selectedAccountIndex = State(initialValue: accountsSupportingTransferringMoney.firstIndex(where: { account in account == preselectedBankAccount }) ?? 0)
|
||||
|
||||
self._remitteeName = State(initialValue: preselectedValues.creditorName)
|
||||
self._remitteeBic = State(initialValue: preselectedValues.creditorBic)
|
||||
self._remitteeIban = State(initialValue: preselectedValues.creditorIban)
|
||||
self._recipientName = State(initialValue: preselectedValues.recipientName)
|
||||
self._recipientBic = State(initialValue: preselectedValues.recipientBankCode)
|
||||
self._recipientIban = State(initialValue: preselectedValues.recipientAccountId)
|
||||
|
||||
if remitteeBic.isBlank && remitteeIban.isNotBlank {
|
||||
tryToGetBicFromIban(remitteeIban)
|
||||
if recipientBic.isBlank && recipientIban.isNotBlank {
|
||||
tryToGetBicFromIban(recipientIban)
|
||||
}
|
||||
|
||||
self._usage = State(initialValue: preselectedValues.usage)
|
||||
self._reference = State(initialValue: preselectedValues.reference)
|
||||
|
||||
if preselectedValues.amount.decimal != NSDecimalNumber.zero {
|
||||
self._amount = State(initialValue: preselectedValues.amount.format(countDecimalPlaces: 2))
|
||||
}
|
||||
|
||||
if preselectedBankAccount.supportsInstantPaymentMoneyTransfer {
|
||||
self._instantPayment = State(initialValue: preselectedValues.instantPayment)
|
||||
if preselectedBankAccount.supportsRealTimeTransfer {
|
||||
self._realTimeTransfer = State(initialValue: preselectedValues.realTimeTransfer)
|
||||
}
|
||||
|
||||
_validateDataWhenShowingDialog = State(initialValue: true)
|
||||
|
@ -117,29 +117,29 @@ struct TransferMoneyDialog: View {
|
|||
}
|
||||
|
||||
Section {
|
||||
LabelledUIKitTextField(label: "Remittee Name", text: $remitteeName, focusOnStart: true, focusNextTextFieldOnReturnKeyPress: true,
|
||||
isFocusedChanged: remitteeNameisFocusedChanged, actionOnReturnKeyPress: handleReturnKeyPress, textChanged: enteredRemitteeNameChanged)
|
||||
LabelledUIKitTextField(label: "Recipient Name", text: $recipientName, focusOnStart: true, focusNextTextFieldOnReturnKeyPress: true,
|
||||
isFocusedChanged: recipientNameisFocusedChanged, actionOnReturnKeyPress: handleReturnKeyPress, textChanged: enteredRecipientNameChanged)
|
||||
.padding(.bottom, 0)
|
||||
|
||||
remitteeNameValidationResult.map { validationError in
|
||||
recipientNameValidationResult.map { validationError in
|
||||
ValidationLabel(validationError)
|
||||
}
|
||||
|
||||
if self.showRemitteeAutocompleteList {
|
||||
List(self.remitteeSearchResults) { remittee in
|
||||
RemitteeListItem(remittee: remittee)
|
||||
.onTapGesture { self.remitteeSelected(remittee) }
|
||||
if self.showRecipientAutocompleteList {
|
||||
List(self.recipientSearchResults) { recipient in
|
||||
RecipientListItem(recipient: recipient)
|
||||
.onTapGesture { self.recipientSelected(recipient) }
|
||||
}
|
||||
}
|
||||
|
||||
LabelledUIKitTextField(label: "Remittee IBAN", text: $remitteeIban, autocapitalizationType: .allCharacters, focusNextTextFieldOnReturnKeyPress: true, isFocusedChanged: validateRemitteeIbanOnFocusLost,
|
||||
actionOnReturnKeyPress: handleReturnKeyPress, textChanged: remitteeIbanisFocusedChanged)
|
||||
LabelledUIKitTextField(label: "Recipient IBAN", text: $recipientIban, autocapitalizationType: .allCharacters, focusNextTextFieldOnReturnKeyPress: true, isFocusedChanged: validateRecipientIbanOnFocusLost,
|
||||
actionOnReturnKeyPress: handleReturnKeyPress, textChanged: recipientIbanisFocusedChanged)
|
||||
|
||||
remitteeIbanValidationResult.map { validationError in
|
||||
recipientIbanValidationResult.map { validationError in
|
||||
ValidationLabel(validationError)
|
||||
}
|
||||
|
||||
remitteeBankInfo.map {
|
||||
recipientBankInfo.map {
|
||||
InfoLabel($0)
|
||||
.font(.caption)
|
||||
}
|
||||
|
@ -157,29 +157,29 @@ struct TransferMoneyDialog: View {
|
|||
Section {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text("Usage")
|
||||
Text("Reference")
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if supportsInstantPayment {
|
||||
if supportsRealTimeTransfer {
|
||||
Section {
|
||||
VStack {
|
||||
Toggle(isOn: $instantPayment) {
|
||||
Toggle(isOn: $realTimeTransfer) {
|
||||
HStack {
|
||||
Text("Instant Payment")
|
||||
Text("Real-time transfer")
|
||||
.lineLimit(1)
|
||||
|
||||
if instantPayment {
|
||||
InstantPaymentInfoView()
|
||||
if realTimeTransfer {
|
||||
RealTimeTransferInfoView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -205,8 +205,8 @@ struct TransferMoneyDialog: View {
|
|||
self.validateDataWhenShowingDialog = false
|
||||
self.validateAllFields()
|
||||
|
||||
if self.remitteeBankInfo == nil {
|
||||
self.showRemitteeBankInfo(self.remitteeBic, "")
|
||||
if self.recipientBankInfo == nil {
|
||||
self.showRecipientBankInfo(self.recipientBic, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -228,104 +228,104 @@ struct TransferMoneyDialog: View {
|
|||
|
||||
|
||||
private func validateAllFields() {
|
||||
self.validateRemitteeNameOnFocusLost()
|
||||
self.validateRemitteeIbanOnFocusLost()
|
||||
self.validateRemitteeBic()
|
||||
self.validateRecipientNameOnFocusLost()
|
||||
self.validateRecipientIbanOnFocusLost()
|
||||
self.validateRecipientBic()
|
||||
self.validateAmount()
|
||||
self.validateUsage()
|
||||
self.validateReference()
|
||||
}
|
||||
|
||||
|
||||
private func remitteeNameisFocusedChanged(_ isFocused: Bool) {
|
||||
private func recipientNameisFocusedChanged(_ isFocused: Bool) {
|
||||
if isFocused == false {
|
||||
validateRemitteeNameOnFocusLost()
|
||||
validateRecipientNameOnFocusLost()
|
||||
|
||||
self.showRemitteeAutocompleteList = false
|
||||
self.showRecipientAutocompleteList = false
|
||||
}
|
||||
else {
|
||||
self.showRemitteeAutocompleteList = self.remitteeSearchResults.isNotEmpty
|
||||
self.showRecipientAutocompleteList = self.recipientSearchResults.isNotEmpty
|
||||
}
|
||||
}
|
||||
|
||||
private func validateRemitteeNameOnFocusLost() {
|
||||
validateField($remitteeName, $remitteeNameValidationResult, $isValidRemitteeNameEntered) {
|
||||
inputValidator.validateRemitteeName(remitteeNameToTest: remitteeName)
|
||||
private func validateRecipientNameOnFocusLost() {
|
||||
validateField($recipientName, $recipientNameValidationResult, $isValidRecipientNameEntered) {
|
||||
inputValidator.validateRecipientName(recipientNameToTest: recipientName)
|
||||
}
|
||||
}
|
||||
|
||||
private func enteredRemitteeNameChanged(enteredRemitteeName: String) {
|
||||
validateField($remitteeName, $remitteeNameValidationResult, $isValidRemitteeNameEntered) {
|
||||
inputValidator.validateRemitteeNameWhileTyping(remitteeNameToTest: remitteeName)
|
||||
private func enteredRecipientNameChanged(enteredRecipientName: String) {
|
||||
validateField($recipientName, $recipientNameValidationResult, $isValidRecipientNameEntered) {
|
||||
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?
|
||||
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) {
|
||||
self.remitteeName = remittee.name
|
||||
self.remitteeIban = remittee.iban ?? self.remitteeIban
|
||||
self.remitteeBic = remittee.bic ?? self.remitteeBic
|
||||
private func recipientSelected(_ recipient: TransactionParty) {
|
||||
self.recipientName = recipient.name
|
||||
self.recipientIban = recipient.iban ?? self.recipientIban
|
||||
self.recipientBic = recipient.bic ?? self.recipientBic
|
||||
|
||||
tryToGetBicFromIban(self.remitteeIban)
|
||||
tryToGetBicFromIban(self.recipientIban)
|
||||
|
||||
validateAllFields()
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
||||
self.showRemitteeAutocompleteList = false
|
||||
self.showRecipientAutocompleteList = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func remitteeIbanisFocusedChanged(_ enteredIban: String) {
|
||||
validateField($remitteeIban, $remitteeIbanValidationResult, $isValidRemitteeIbanEntered) { inputValidator.validateIbanWhileTyping(ibanToTest: enteredIban) }
|
||||
private func recipientIbanisFocusedChanged(_ enteredIban: String) {
|
||||
validateField($recipientIban, $recipientIbanValidationResult, $isValidRecipientIbanEntered) { inputValidator.validateIbanWhileTyping(ibanToTest: enteredIban) }
|
||||
|
||||
tryToGetBicFromIban(enteredIban)
|
||||
}
|
||||
|
||||
private func validateRemitteeIbanOnFocusLost(_ isFocused: Bool) {
|
||||
private func validateRecipientIbanOnFocusLost(_ isFocused: Bool) {
|
||||
if isFocused == false {
|
||||
validateRemitteeIbanOnFocusLost()
|
||||
validateRecipientIbanOnFocusLost()
|
||||
}
|
||||
}
|
||||
|
||||
private func validateRemitteeIbanOnFocusLost() {
|
||||
validateField($remitteeIban, $remitteeIbanValidationResult, $isValidRemitteeIbanEntered) { inputValidator.validateIban(ibanToTest: remitteeIban) }
|
||||
private func validateRecipientIbanOnFocusLost() {
|
||||
validateField($recipientIban, $recipientIbanValidationResult, $isValidRecipientIbanEntered) { inputValidator.validateIban(ibanToTest: recipientIban) }
|
||||
}
|
||||
|
||||
private func tryToGetBicFromIban(_ enteredIban: String) {
|
||||
let foundBank = presenter.findUniqueBankForIban(iban: enteredIban)
|
||||
|
||||
if let foundBank = foundBank {
|
||||
self.remitteeBic = foundBank.bic
|
||||
showRemitteeBankInfo(foundBank.bic, foundBank.name)
|
||||
self.recipientBic = foundBank.bic
|
||||
showRecipientBankInfo(foundBank.bic, foundBank.name)
|
||||
}
|
||||
else {
|
||||
self.remitteeBic = ""
|
||||
self.recipientBic = ""
|
||||
|
||||
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 {
|
||||
self.remitteeBankInfo = nil
|
||||
self.recipientBankInfo = nil
|
||||
}
|
||||
}
|
||||
|
||||
validateRemitteeBic()
|
||||
validateRecipientBic()
|
||||
}
|
||||
|
||||
private func validateRemitteeBic() {
|
||||
self.isValidRemitteeBicEntered = inputValidator.validateBic(bicToTest: remitteeBic).validationSuccessful
|
||||
private func validateRecipientBic() {
|
||||
self.isValidRecipientBicEntered = inputValidator.validateBic(bicToTest: recipientBic).validationSuccessful
|
||||
}
|
||||
|
||||
private func showRemitteeBankInfo(_ bic: String, _ bankName: String) {
|
||||
self.remitteeBankInfo = "BIC: \(bic), \(bankName)"
|
||||
private func showRecipientBankInfo(_ bic: String, _ bankName: String) {
|
||||
self.recipientBankInfo = "BIC: \(bic), \(bankName)"
|
||||
}
|
||||
|
||||
|
||||
|
@ -352,12 +352,12 @@ struct TransferMoneyDialog: View {
|
|||
}
|
||||
|
||||
|
||||
private func validateUsage(enteredUsage: String) {
|
||||
validateUsage()
|
||||
private func validateReference(enteredReference: String) {
|
||||
validateReference()
|
||||
}
|
||||
|
||||
private func validateUsage() {
|
||||
validateField($usage, $usageValidationResult, $isValidUsageEntered) { inputValidator.validateUsage(usageToTest: self.usage) }
|
||||
private func validateReference() {
|
||||
validateField($reference, $referenceValidationResult, $isValidReferenceEntered) { inputValidator.validateReference(referenceToTest: self.reference) }
|
||||
}
|
||||
|
||||
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 {
|
||||
return account != nil
|
||||
&& isValidRemitteeNameEntered
|
||||
&& isValidRemitteeIbanEntered
|
||||
&& isValidRemitteeBicEntered
|
||||
&& isValidRecipientNameEntered
|
||||
&& isValidRecipientIbanEntered
|
||||
&& isValidRecipientBicEntered
|
||||
&& isValidAmountEntered
|
||||
&& isValidUsageEntered
|
||||
&& isValidReferenceEntered
|
||||
}
|
||||
|
||||
private func transferMoney() {
|
||||
|
@ -394,7 +394,7 @@ struct TransferMoneyDialog: View {
|
|||
isTransferringMoney = true
|
||||
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
|
||||
self.handleTransferMoneyResponse(data, response)
|
||||
|
@ -406,12 +406,12 @@ struct TransferMoneyDialog: View {
|
|||
isTransferringMoney = false
|
||||
|
||||
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()
|
||||
})
|
||||
}
|
||||
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 ?? "")."))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import SwiftUI
|
||||
|
||||
|
||||
struct InstantPaymentInfoView: View {
|
||||
struct RealTimeTransferInfoView: View {
|
||||
|
||||
@State private var showInstantPaymentInfoPopover = false
|
||||
@State private var showRealTimeTransferInfoPopover = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
@ -15,26 +15,26 @@ struct InstantPaymentInfoView: View {
|
|||
.minimumScaleFactor(0.5)
|
||||
.padding(.horizontal, 0)
|
||||
|
||||
UIKitButton(.infoLight) { self.showInstantPaymentInfoPopover = true }
|
||||
UIKitButton(.infoLight) { self.showRealTimeTransferInfoPopover = true }
|
||||
.frame(width: 15, height: 15)
|
||||
.popover(isPresented: $showInstantPaymentInfoPopover, arrowEdge: .leading ) {
|
||||
.popover(isPresented: $showRealTimeTransferInfoPopover, arrowEdge: .leading ) {
|
||||
if UIDevice.isRunningOniPad {
|
||||
ScrollView {
|
||||
Text("Instant payment information")
|
||||
Text("Real-time transfer information")
|
||||
.padding()
|
||||
.detailForegroundColor()
|
||||
}
|
||||
}
|
||||
else {
|
||||
VStack {
|
||||
Text("Instant payment information")
|
||||
Text("Real-time transfer information")
|
||||
.padding()
|
||||
.detailForegroundColor()
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
Button("OK") { self.showInstantPaymentInfoPopover = false }
|
||||
Button("OK") { self.showRealTimeTransferInfoPopover = false }
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
@ -47,13 +47,13 @@ struct InstantPaymentInfoView: View {
|
|||
}
|
||||
|
||||
|
||||
struct InstantPaymentInfoView_Previews: PreviewProvider {
|
||||
struct RealTimeTransferInfoView_Previews: PreviewProvider {
|
||||
|
||||
static var previews: some View {
|
||||
Form {
|
||||
Section {
|
||||
Toggle(isOn: .constant(true)) {
|
||||
InstantPaymentInfoView()
|
||||
RealTimeTransferInfoView()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ struct AccountTransactionListItem: View {
|
|||
.lineLimit(1)
|
||||
.frame(height: 20)
|
||||
|
||||
Text(transaction.usage)
|
||||
Text(transaction.reference)
|
||||
.styleAsDetail()
|
||||
.padding(.top, 4)
|
||||
.lineLimit(2)
|
||||
|
@ -62,7 +62,7 @@ struct AccountTransactionListItem: View {
|
|||
}
|
||||
.contextMenu {
|
||||
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 {
|
||||
Text("Transfer money to \(transaction.otherPartyName ?? "")")
|
||||
|
||||
|
@ -98,6 +98,6 @@ struct AccountTransactionListItem: View {
|
|||
|
||||
struct AccountTransactionListItem_Previews: PreviewProvider {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,16 +2,16 @@ import SwiftUI
|
|||
import BankingUiSwift
|
||||
|
||||
|
||||
struct RemitteeListItem: View {
|
||||
struct RecipientListItem: View {
|
||||
|
||||
let remittee: Remittee
|
||||
let recipient: TransactionParty
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
||||
HStack {
|
||||
Text(remittee.name)
|
||||
Text(recipient.name)
|
||||
.bold()
|
||||
.lineLimit(1)
|
||||
|
||||
|
@ -19,7 +19,7 @@ struct RemitteeListItem: View {
|
|||
}
|
||||
.padding(.vertical, 6)
|
||||
|
||||
remittee.bankName.map { bankName in
|
||||
recipient.bankName.map { bankName in
|
||||
HStack {
|
||||
Text(bankName)
|
||||
.font(.footnote)
|
||||
|
@ -31,11 +31,11 @@ struct RemitteeListItem: View {
|
|||
}
|
||||
|
||||
HStack {
|
||||
Text(remittee.iban ?? "")
|
||||
Text(recipient.iban ?? "")
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(remittee.bic ?? "")
|
||||
Text(recipient.bic ?? "")
|
||||
}
|
||||
.font(.footnote)
|
||||
.lineLimit(1)
|
||||
|
@ -45,10 +45,10 @@ struct RemitteeListItem: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct RemitteeListItem_Previews: PreviewProvider {
|
||||
struct RecipientListItem_Previews: PreviewProvider {
|
||||
|
||||
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"))
|
||||
}
|
||||
|
||||
}
|
|
@ -117,7 +117,7 @@ open class fints4kBankingClient(
|
|||
callback(BankingClientResponse(false, errorMessage))
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
|
|||
account.supportsRetrievingBalance = accountData.supportsRetrievingBalance
|
||||
account.supportsRetrievingAccountTransactions = accountData.supportsRetrievingAccountTransactions
|
||||
account.supportsTransferringMoney = accountData.supportsTransferringMoney
|
||||
account.supportsInstantPaymentMoneyTransfer = accountData.supportsInstantPaymentMoneyTransfer
|
||||
account.supportsRealTimeTransfer = accountData.supportsRealTimeTransfer
|
||||
}
|
||||
|
||||
open fun mapBankAccountType(type: AccountType?): BankAccountType {
|
||||
|
@ -199,7 +199,7 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
|
|||
account,
|
||||
transaction.amount.bigDecimal,
|
||||
transaction.amount.currency.code,
|
||||
transaction.unparsedUsage,
|
||||
transaction.unparsedReference,
|
||||
transaction.bookingDate,
|
||||
transaction.otherPartyName,
|
||||
transaction.otherPartyBankCode,
|
||||
|
@ -218,10 +218,10 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
|
|||
transaction.originatorsIdentificationCode,
|
||||
transaction.compensationAmount,
|
||||
transaction.originalAmount,
|
||||
transaction.sepaUsage,
|
||||
transaction.sepaReference,
|
||||
transaction.deviantOriginator,
|
||||
transaction.deviantRecipient,
|
||||
transaction.usageWithNoSpecialType,
|
||||
transaction.referenceWithNoSpecialType,
|
||||
transaction.primaNotaNumber,
|
||||
transaction.textKeySupplement,
|
||||
|
||||
|
|
|
@ -256,7 +256,7 @@ open class hbci4jBankingClient(
|
|||
}
|
||||
|
||||
protected open fun createTransferCashJob(handle: HBCIHandler, data: TransferMoneyData) {
|
||||
// TODO: implement instant payment
|
||||
// TODO: implement real-time transfer
|
||||
val transferCashJob = handle.newJob("UebSEPA")
|
||||
|
||||
val source = mapper.mapToKonto(data.account)
|
||||
|
@ -266,7 +266,7 @@ open class hbci4jBankingClient(
|
|||
transferCashJob.setParam("src", source)
|
||||
transferCashJob.setParam("dst", destination)
|
||||
transferCashJob.setParam("btg", amount)
|
||||
transferCashJob.setParam("usage", data.usage)
|
||||
transferCashJob.setParam("usage", data.reference)
|
||||
|
||||
transferCashJob.addToQueue()
|
||||
}
|
||||
|
|
|
@ -43,12 +43,12 @@ open class AccountTransactionMapper(
|
|||
}
|
||||
|
||||
protected open fun mapTransaction(account: TypedBankAccount, btag: GVRKUms.BTag, transaction: GVRKUms.UmsLine): IAccountTransaction {
|
||||
val unparsedUsage = transaction.usage.joinToString("")
|
||||
val parsedUsage = Mt940Parser().getUsageParts(unparsedUsage)
|
||||
val unparsedReference = transaction.usage.joinToString("")
|
||||
val parsedReference = Mt940Parser().getReferenceParts(unparsedReference)
|
||||
val statementAndMaySequenceNumber = btag.counter.split('/')
|
||||
|
||||
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.bic ?: transaction.other.blz,
|
||||
transaction.other.iban ?: transaction.other.number,
|
||||
|
@ -57,17 +57,17 @@ open class AccountTransactionMapper(
|
|||
if (statementAndMaySequenceNumber.size > 1) statementAndMaySequenceNumber[1].toInt() else null,
|
||||
mapValue(btag.start.value), mapValue(btag.end.value),
|
||||
|
||||
parsedUsage[Mt940Parser.EndToEndReferenceUsageKey],
|
||||
parsedUsage[Mt940Parser.CustomerReferenceUsageKey],
|
||||
parsedUsage[Mt940Parser.MandateReferenceUsageKey],
|
||||
parsedUsage[Mt940Parser.CreditorIdentifierUsageKey],
|
||||
parsedUsage[Mt940Parser.OriginatorsIdentificationCodeUsageKey],
|
||||
parsedUsage[Mt940Parser.CompensationAmountUsageKey],
|
||||
parsedUsage[Mt940Parser.OriginalAmountUsageKey],
|
||||
parsedUsage[Mt940Parser.SepaUsageUsageKey],
|
||||
parsedUsage[Mt940Parser.DeviantOriginatorUsageKey],
|
||||
parsedUsage[Mt940Parser.DeviantRecipientUsageKey],
|
||||
parsedUsage[""],
|
||||
parsedReference[Mt940Parser.EndToEndReferenceKey],
|
||||
parsedReference[Mt940Parser.CustomerReferenceKey],
|
||||
parsedReference[Mt940Parser.MandateReferenceKey],
|
||||
parsedReference[Mt940Parser.CreditorIdentifierKey],
|
||||
parsedReference[Mt940Parser.OriginatorsIdentificationCodeKey],
|
||||
parsedReference[Mt940Parser.CompensationAmountKey],
|
||||
parsedReference[Mt940Parser.OriginalAmountKey],
|
||||
parsedReference[Mt940Parser.SepaReferenceKey],
|
||||
parsedReference[Mt940Parser.DeviantOriginatorKey],
|
||||
parsedReference[Mt940Parser.DeviantRecipientKey],
|
||||
parsedReference[""],
|
||||
transaction.primanota,
|
||||
transaction.addkey,
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ open class hbci4jModelMapper(
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
Loading…
Reference in New Issue