Implemented mapping all account transaction fields; Renamed usage to unparsedUsage, isCancellation to isReversal, customerReference to referenceForTheAccountOwner, bankReference to referenceOfTheAccountServicingInstitution and referenceReferenceNumber to relatedReferenceNumber
This commit is contained in:
parent
ac78758262
commit
a16cf630b4
|
@ -6,27 +6,54 @@ import java.util.*
|
||||||
|
|
||||||
|
|
||||||
open class AccountTransaction(
|
open class AccountTransaction(
|
||||||
|
val account: AccountData,
|
||||||
val amount: BigDecimal,
|
val amount: BigDecimal,
|
||||||
val currency: String,
|
val currency: String,
|
||||||
val usage: String,
|
val isReversal: Boolean,
|
||||||
|
val unparsedUsage: String,
|
||||||
val bookingDate: Date,
|
val bookingDate: Date,
|
||||||
val otherPartyName: String?,
|
val otherPartyName: String?,
|
||||||
val otherPartyBankCode: String?,
|
val otherPartyBankCode: String?,
|
||||||
val otherPartyAccountId: String?,
|
val otherPartyAccountId: String?,
|
||||||
val bookingText: String?,
|
val bookingText: String?,
|
||||||
val valueDate: Date?,
|
val valueDate: Date,
|
||||||
|
val statementNumber: Int,
|
||||||
|
val sequenceNumber: Int?,
|
||||||
val openingBalance: BigDecimal?,
|
val openingBalance: BigDecimal?,
|
||||||
val closingBalance: BigDecimal?,
|
val closingBalance: BigDecimal?,
|
||||||
val account: AccountData
|
|
||||||
// TODO: may also add other values from parsed usage lines
|
val endToEndReference: String?,
|
||||||
|
val customerReference: String?,
|
||||||
|
val mandateReference: String?,
|
||||||
|
val creditorIdentifier: String?,
|
||||||
|
val originatorsIdentificationCode: String?,
|
||||||
|
val compensationAmount: String?,
|
||||||
|
val originalAmount: String?,
|
||||||
|
val sepaUsage: String?,
|
||||||
|
val deviantOriginator: String?,
|
||||||
|
val deviantRecipient: String?,
|
||||||
|
val usageWithNoSpecialType: String?,
|
||||||
|
val primaNotaNumber: String?,
|
||||||
|
val textKeySupplement: String?,
|
||||||
|
|
||||||
|
val currencyType: String?,
|
||||||
|
val bookingKey: String,
|
||||||
|
val referenceForTheAccountOwner: String,
|
||||||
|
val referenceOfTheAccountServicingInstitution: String?,
|
||||||
|
val supplementaryDetails: String?,
|
||||||
|
|
||||||
|
val transactionReferenceNumber: String,
|
||||||
|
val relatedReferenceNumber: String?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// for object deserializers
|
// for object deserializers
|
||||||
internal constructor() : this(0.toBigDecimal(),"", "", Date(), null, null, null, null, null, null, null, AccountData())
|
internal constructor() : this(AccountData(), 0.toBigDecimal(),"", false, "", Date(), null, null, null, null, Date(), 0, null, null, null,
|
||||||
|
null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||||
|
null, "", "", null, null, "", null)
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "${DateFormat.getDateInstance(DateFormat.MEDIUM).format(bookingDate)} $amount $otherPartyName: $usage"
|
return "${DateFormat.getDateInstance(DateFormat.MEDIUM).format(bookingDate)} $amount $otherPartyName: $unparsedUsage"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -45,18 +45,44 @@ open class Mt940AccountTransactionsParser @JvmOverloads constructor(
|
||||||
|
|
||||||
protected open fun mapToAccountTransaction(statement: AccountStatement, transaction: Transaction, account: AccountData): AccountTransaction {
|
protected open fun mapToAccountTransaction(statement: AccountStatement, transaction: Transaction, account: AccountData): AccountTransaction {
|
||||||
return AccountTransaction(
|
return AccountTransaction(
|
||||||
|
account,
|
||||||
mapAmount(transaction.statementLine),
|
mapAmount(transaction.statementLine),
|
||||||
statement.closingBalance.currency,
|
statement.closingBalance.currency,
|
||||||
transaction.information?.sepaUsage ?: transaction.information?.usage ?: "",
|
transaction.statementLine.isReversal,
|
||||||
|
transaction.information?.unparsedUsage ?: "",
|
||||||
transaction.statementLine.bookingDate ?: statement.closingBalance.bookingDate,
|
transaction.statementLine.bookingDate ?: statement.closingBalance.bookingDate,
|
||||||
transaction.information?.otherPartyName,
|
transaction.information?.otherPartyName,
|
||||||
transaction.information?.otherPartyBankCode,
|
transaction.information?.otherPartyBankCode,
|
||||||
transaction.information?.otherPartyAccountId,
|
transaction.information?.otherPartyAccountId,
|
||||||
transaction.information?.bookingText,
|
transaction.information?.bookingText,
|
||||||
transaction.statementLine.valueDate,
|
transaction.statementLine.valueDate,
|
||||||
|
statement.statementNumber,
|
||||||
|
statement.sequenceNumber,
|
||||||
mapAmount(statement.openingBalance), // TODO: that's not true, these are the opening and closing balance of
|
mapAmount(statement.openingBalance), // TODO: that's not true, these are the opening and closing balance of
|
||||||
mapAmount(statement.closingBalance), // all transactions of this day, not this specific transaction's ones
|
mapAmount(statement.closingBalance), // all transactions of this day, not this specific transaction's ones
|
||||||
account
|
|
||||||
|
transaction.information?.endToEndReference,
|
||||||
|
transaction.information?.customerReference,
|
||||||
|
transaction.information?.mandateReference,
|
||||||
|
transaction.information?.creditorIdentifier,
|
||||||
|
transaction.information?.originatorsIdentificationCode,
|
||||||
|
transaction.information?.compensationAmount,
|
||||||
|
transaction.information?.originalAmount,
|
||||||
|
transaction.information?.sepaUsage,
|
||||||
|
transaction.information?.deviantOriginator,
|
||||||
|
transaction.information?.deviantRecipient,
|
||||||
|
transaction.information?.usageWithNoSpecialType,
|
||||||
|
transaction.information?.primaNotaNumber,
|
||||||
|
transaction.information?.textKeySupplement,
|
||||||
|
|
||||||
|
transaction.statementLine.currencyType,
|
||||||
|
transaction.statementLine.bookingKey,
|
||||||
|
transaction.statementLine.referenceForTheAccountOwner,
|
||||||
|
transaction.statementLine.referenceOfTheAccountServicingInstitution,
|
||||||
|
transaction.statementLine.supplementaryDetails,
|
||||||
|
|
||||||
|
statement.transactionReferenceNumber,
|
||||||
|
statement.relatedReferenceNumber
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,18 @@ open class Mt940Parser : IMt940Parser {
|
||||||
val UsageTypePattern = Pattern.compile("\\w{4}\\+")
|
val UsageTypePattern = Pattern.compile("\\w{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+"
|
||||||
|
|
||||||
|
|
||||||
private val log = LoggerFactory.getLogger(Mt940Parser::class.java)
|
private val log = LoggerFactory.getLogger(Mt940Parser::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,36 +374,35 @@ open class Mt940Parser : IMt940Parser {
|
||||||
* Weitere 4 Verwendungszwecke können zu den Feldschlüsseln 60 bis 63 eingestellt werden.
|
* Weitere 4 Verwendungszwecke können zu den Feldschlüsseln 60 bis 63 eingestellt werden.
|
||||||
*/
|
*/
|
||||||
protected open fun mapUsage(information: InformationToAccountOwner) {
|
protected open fun mapUsage(information: InformationToAccountOwner) {
|
||||||
val usageParts = getUsageParts(information)
|
val usageParts = getUsageParts(information.unparsedUsage)
|
||||||
|
|
||||||
usageParts.forEach { pair ->
|
usageParts.forEach { entry ->
|
||||||
setUsageLineValue(information, pair.first, pair.second)
|
setUsageLineValue(information, entry.key, entry.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun getUsageParts(information: InformationToAccountOwner): MutableList<Pair<String, String>> {
|
open fun getUsageParts(unparsedUsage: String): Map<String, String> {
|
||||||
val usage = information.usage
|
|
||||||
var previousMatchType = ""
|
var previousMatchType = ""
|
||||||
var previousMatchEnd = 0
|
var previousMatchEnd = 0
|
||||||
|
|
||||||
val usageParts = mutableListOf<Pair<String, String>>()
|
val usageParts = mutableMapOf<String, String>()
|
||||||
val matcher = UsageTypePattern.matcher(information.usage)
|
val matcher = UsageTypePattern.matcher(unparsedUsage)
|
||||||
|
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
if (previousMatchEnd > 0) {
|
if (previousMatchEnd > 0) {
|
||||||
val typeValue = usage.substring(previousMatchEnd, matcher.start())
|
val typeValue = unparsedUsage.substring(previousMatchEnd, matcher.start())
|
||||||
|
|
||||||
usageParts.add(Pair(previousMatchType, typeValue))
|
usageParts[previousMatchType] = typeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
previousMatchType = usage.substring(matcher.start(), matcher.end())
|
previousMatchType = unparsedUsage.substring(matcher.start(), matcher.end())
|
||||||
previousMatchEnd = matcher.end()
|
previousMatchEnd = matcher.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previousMatchEnd > 0) {
|
if (previousMatchEnd > 0) {
|
||||||
val typeValue = usage.substring(previousMatchEnd, usage.length)
|
val typeValue = unparsedUsage.substring(previousMatchEnd, unparsedUsage.length)
|
||||||
|
|
||||||
usageParts.add(Pair(previousMatchType, typeValue))
|
usageParts[previousMatchType] = typeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return usageParts
|
return usageParts
|
||||||
|
@ -399,16 +410,16 @@ open class Mt940Parser : IMt940Parser {
|
||||||
|
|
||||||
protected open fun setUsageLineValue(information: InformationToAccountOwner, usageType: String, typeValue: String) {
|
protected open fun setUsageLineValue(information: InformationToAccountOwner, usageType: String, typeValue: String) {
|
||||||
when (usageType) {
|
when (usageType) {
|
||||||
"EREF+" -> information.endToEndReference = typeValue
|
EndToEndReferenceUsageKey -> information.endToEndReference = typeValue
|
||||||
"KREF+" -> information.customerReference = typeValue
|
CustomerReferenceUsageKey -> information.customerReference = typeValue
|
||||||
"MREF+" -> information.mandateReference = typeValue
|
MandateReferenceUsageKey -> information.mandateReference = typeValue
|
||||||
"CRED+" -> information.creditorIdentifier = typeValue
|
CreditorIdentifierUsageKey -> information.creditorIdentifier = typeValue
|
||||||
"DEBT+" -> information.originatorsIdentificationCode = typeValue
|
OriginatorsIdentificationCodeUsageKey -> information.originatorsIdentificationCode = typeValue
|
||||||
"COAM+" -> information.compensationAmount = typeValue
|
CompensationAmountUsageKey -> information.compensationAmount = typeValue
|
||||||
"OAMT+" -> information.originalAmount = typeValue
|
OriginalAmountUsageKey -> information.originalAmount = typeValue
|
||||||
"SVWZ+" -> information.sepaUsage = typeValue
|
SepaUsageUsageKey -> information.sepaUsage = typeValue
|
||||||
"ABWA+" -> information.deviantOriginator = typeValue
|
DeviantOriginatorUsageKey -> information.deviantOriginator = typeValue
|
||||||
"ABWE+" -> information.deviantRecipient = typeValue
|
DeviantRecipientUsageKey -> information.deviantRecipient = typeValue
|
||||||
else -> information.usageWithNoSpecialType = typeValue
|
else -> information.usageWithNoSpecialType = typeValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ open class AccountStatement(
|
||||||
*
|
*
|
||||||
* Max length = 16
|
* Max length = 16
|
||||||
*/
|
*/
|
||||||
val referenceReferenceNumber: String?,
|
val relatedReferenceNumber: String?,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* xxxxxxxxxxx/Konto-Nr. oder yyyyyyyy/Konto-Nr.
|
* xxxxxxxxxxx/Konto-Nr. oder yyyyyyyy/Konto-Nr.
|
||||||
|
|
|
@ -2,7 +2,7 @@ package net.dankito.banking.fints.transactions.mt940.model
|
||||||
|
|
||||||
|
|
||||||
open class InformationToAccountOwner(
|
open class InformationToAccountOwner(
|
||||||
val usage: String,
|
val unparsedUsage: String,
|
||||||
val otherPartyName: String?,
|
val otherPartyName: String?,
|
||||||
val otherPartyBankCode: String?,
|
val otherPartyBankCode: String?,
|
||||||
val otherPartyAccountId: String?,
|
val otherPartyAccountId: String?,
|
||||||
|
@ -35,7 +35,7 @@ open class InformationToAccountOwner(
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$otherPartyName $usage"
|
return "$otherPartyName $unparsedUsage"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -19,7 +19,7 @@ open class StatementLine(
|
||||||
*/
|
*/
|
||||||
val isCredit: Boolean,
|
val isCredit: Boolean,
|
||||||
|
|
||||||
val isCancellation: Boolean,
|
val isReversal: Boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Valuta (JJMMTT)
|
* Valuta (JJMMTT)
|
||||||
|
@ -58,9 +58,9 @@ open class StatementLine(
|
||||||
*/
|
*/
|
||||||
val bookingKey: String,
|
val bookingKey: String,
|
||||||
|
|
||||||
val customerReference: String,
|
val referenceForTheAccountOwner: String,
|
||||||
|
|
||||||
val bankReference: String?,
|
val referenceOfTheAccountServicingInstitution: String?,
|
||||||
|
|
||||||
val supplementaryDetails: String? = null
|
val supplementaryDetails: String? = null
|
||||||
|
|
||||||
|
|
|
@ -249,7 +249,7 @@ class Mt940ParserTest : FinTsTestBase() {
|
||||||
bookingDate: Date? = valueDate) {
|
bookingDate: Date? = valueDate) {
|
||||||
|
|
||||||
assertThat(statementLine.isCredit).isEqualTo(isCredit)
|
assertThat(statementLine.isCredit).isEqualTo(isCredit)
|
||||||
assertThat(statementLine.isCancellation).isFalse()
|
assertThat(statementLine.isReversal).isFalse()
|
||||||
assertThat(statementLine.valueDate).isEqualTo(valueDate)
|
assertThat(statementLine.valueDate).isEqualTo(valueDate)
|
||||||
assertThat(statementLine.bookingDate).isEqualTo(bookingDate)
|
assertThat(statementLine.bookingDate).isEqualTo(bookingDate)
|
||||||
assertThat(statementLine.amount).isEqualTo(amount)
|
assertThat(statementLine.amount).isEqualTo(amount)
|
||||||
|
|
|
@ -72,9 +72,9 @@ open class LuceneBankingPersistence(
|
||||||
fields.storedField(BookingDateFieldName, transaction.bookingDate),
|
fields.storedField(BookingDateFieldName, transaction.bookingDate),
|
||||||
fields.storedField(AmountFieldName, transaction.amount),
|
fields.storedField(AmountFieldName, transaction.amount),
|
||||||
fields.storedField(CurrencyFieldName, transaction.currency),
|
fields.storedField(CurrencyFieldName, transaction.currency),
|
||||||
fields.nullableStoredField(BalanceFieldName, transaction.balance),
|
fields.nullableStoredField(BalanceFieldName, transaction.closingBalance), // TODO: remove
|
||||||
|
|
||||||
fields.sortField(BookingDateSortFieldName, transaction.bookingDate)
|
fields.sortField(BookingDateSortFieldName, transaction.bookingDate) // TODO: sort by valueDate
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -211,7 +211,7 @@ class LuceneRemitteeSearcherTest {
|
||||||
otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(),
|
otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(),
|
||||||
otherPartyAccountId: String = randomString(), usage: String = randomString()): AccountTransaction {
|
otherPartyAccountId: String = randomString(), usage: String = randomString()): AccountTransaction {
|
||||||
|
|
||||||
return AccountTransaction(amount, bookingDate, usage, otherPartyName, otherPartyBankCode, otherPartyAccountId, null, null, "EUR", bankAccount)
|
return AccountTransaction(bankAccount, amount, usage, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, null, bookingDate)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun randomString(): String {
|
private fun randomString(): String {
|
||||||
|
|
|
@ -72,7 +72,7 @@ open class AccountTransactionsTable @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
label(it.usage ?: "") {
|
label(it.usage) {
|
||||||
vboxConstraints {
|
vboxConstraints {
|
||||||
margin = LabelMargin
|
margin = LabelMargin
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,20 +9,57 @@ import java.util.*
|
||||||
|
|
||||||
@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references
|
@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references
|
||||||
open class AccountTransaction(
|
open class AccountTransaction(
|
||||||
|
val bankAccount: BankAccount,
|
||||||
val amount: BigDecimal,
|
val amount: BigDecimal,
|
||||||
|
val currency: String,
|
||||||
|
val isReversal: Boolean,
|
||||||
|
val unparsedUsage: String,
|
||||||
val bookingDate: Date,
|
val bookingDate: Date,
|
||||||
val usage: String,
|
|
||||||
val otherPartyName: String?,
|
val otherPartyName: String?,
|
||||||
val otherPartyBankCode: String?,
|
val otherPartyBankCode: String?,
|
||||||
val otherPartyAccountId: String?,
|
val otherPartyAccountId: String?,
|
||||||
val bookingText: String?,
|
val bookingText: String?,
|
||||||
val balance: BigDecimal?,
|
val valueDate: Date,
|
||||||
val currency: String,
|
val statementNumber: Int,
|
||||||
val bankAccount: BankAccount
|
val sequenceNumber: Int?,
|
||||||
|
val openingBalance: BigDecimal?,
|
||||||
|
val closingBalance: BigDecimal?,
|
||||||
|
|
||||||
|
val endToEndReference: String?,
|
||||||
|
val customerReference: String?,
|
||||||
|
val mandateReference: String?,
|
||||||
|
val creditorIdentifier: String?,
|
||||||
|
val originatorsIdentificationCode: String?,
|
||||||
|
val compensationAmount: String?,
|
||||||
|
val originalAmount: String?,
|
||||||
|
val sepaUsage: String?,
|
||||||
|
val deviantOriginator: String?,
|
||||||
|
val deviantRecipient: String?,
|
||||||
|
val usageWithNoSpecialType: String?,
|
||||||
|
val primaNotaNumber: String?,
|
||||||
|
val textKeySupplement: String?,
|
||||||
|
|
||||||
|
val currencyType: String?,
|
||||||
|
val bookingKey: String,
|
||||||
|
val referenceForTheAccountOwner: String,
|
||||||
|
val referenceOfTheAccountServicingInstitution: String?,
|
||||||
|
val supplementaryDetails: String?,
|
||||||
|
|
||||||
|
val transactionReferenceNumber: String,
|
||||||
|
val relatedReferenceNumber: String?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
constructor(bankAccount: BankAccount, amount: BigDecimal, unparsedUsage: String, bookingDate: Date,
|
||||||
|
otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?,
|
||||||
|
bookingText: String?, valueDate: Date)
|
||||||
|
: this(bankAccount, amount, "EUR", false, unparsedUsage, 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)
|
||||||
|
|
||||||
// for object deserializers
|
// for object deserializers
|
||||||
internal constructor() : this(BigDecimal.ZERO, Date(),"", null, null, null, null, BigDecimal.ZERO, "", BankAccount())
|
internal constructor() : this(BankAccount(), BigDecimal.ZERO, "", false,"", Date(), null, null, null, null, Date(), 0, null, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||||
|
null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||||
|
null, "", "", null, null, "", null)
|
||||||
|
|
||||||
|
|
||||||
var id: String = UUID.randomUUID().toString()
|
var id: String = UUID.randomUUID().toString()
|
||||||
|
@ -32,6 +69,9 @@ open class AccountTransaction(
|
||||||
val showOtherPartyName: Boolean
|
val showOtherPartyName: Boolean
|
||||||
get() = otherPartyName.isNullOrBlank() == false /* && type != "ENTGELTABSCHLUSS" && type != "AUSZAHLUNG" */ // TODO
|
get() = otherPartyName.isNullOrBlank() == false /* && type != "ENTGELTABSCHLUSS" && type != "AUSZAHLUNG" */ // TODO
|
||||||
|
|
||||||
|
val usage: String
|
||||||
|
get() = sepaUsage ?: unparsedUsage
|
||||||
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
|
@ -39,7 +79,7 @@ open class AccountTransaction(
|
||||||
|
|
||||||
if (amount.compareTo(other.amount) != 0) return false
|
if (amount.compareTo(other.amount) != 0) return false
|
||||||
if (currency != other.currency) return false
|
if (currency != other.currency) return false
|
||||||
if (usage != other.usage) return false
|
if (unparsedUsage != other.unparsedUsage) return false
|
||||||
if (bookingDate != other.bookingDate) return false
|
if (bookingDate != other.bookingDate) return false
|
||||||
if (otherPartyName != other.otherPartyName) return false
|
if (otherPartyName != other.otherPartyName) return false
|
||||||
if (otherPartyBankCode != other.otherPartyBankCode) return false
|
if (otherPartyBankCode != other.otherPartyBankCode) return false
|
||||||
|
@ -53,7 +93,7 @@ open class AccountTransaction(
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = amount.hashCode()
|
var result = amount.hashCode()
|
||||||
result = 31 * result + currency.hashCode()
|
result = 31 * result + currency.hashCode()
|
||||||
result = 31 * result + usage.hashCode()
|
result = 31 * result + unparsedUsage.hashCode()
|
||||||
result = 31 * result + bookingDate.hashCode()
|
result = 31 * result + bookingDate.hashCode()
|
||||||
result = 31 * result + (otherPartyName?.hashCode() ?: 0)
|
result = 31 * result + (otherPartyName?.hashCode() ?: 0)
|
||||||
result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0)
|
result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0)
|
||||||
|
|
|
@ -175,16 +175,44 @@ open class fints4kModelMapper {
|
||||||
|
|
||||||
open fun mapTransaction(bankAccount: BankAccount, transaction: net.dankito.banking.fints.model.AccountTransaction): AccountTransaction {
|
open fun mapTransaction(bankAccount: BankAccount, transaction: net.dankito.banking.fints.model.AccountTransaction): AccountTransaction {
|
||||||
return AccountTransaction(
|
return AccountTransaction(
|
||||||
|
bankAccount,
|
||||||
transaction.amount,
|
transaction.amount,
|
||||||
|
transaction.currency,
|
||||||
|
transaction.isReversal,
|
||||||
|
transaction.unparsedUsage,
|
||||||
transaction.bookingDate,
|
transaction.bookingDate,
|
||||||
transaction.usage,
|
|
||||||
transaction.otherPartyName,
|
transaction.otherPartyName,
|
||||||
transaction.otherPartyBankCode,
|
transaction.otherPartyBankCode,
|
||||||
transaction.otherPartyAccountId,
|
transaction.otherPartyAccountId,
|
||||||
transaction.bookingText,
|
transaction.bookingText,
|
||||||
|
transaction.valueDate,
|
||||||
|
transaction.statementNumber,
|
||||||
|
transaction.sequenceNumber,
|
||||||
|
transaction.openingBalance,
|
||||||
transaction.closingBalance,
|
transaction.closingBalance,
|
||||||
transaction.currency,
|
|
||||||
bankAccount
|
transaction.endToEndReference,
|
||||||
|
transaction.customerReference,
|
||||||
|
transaction.mandateReference,
|
||||||
|
transaction.creditorIdentifier,
|
||||||
|
transaction.originatorsIdentificationCode,
|
||||||
|
transaction.compensationAmount,
|
||||||
|
transaction.originalAmount,
|
||||||
|
transaction.sepaUsage,
|
||||||
|
transaction.deviantOriginator,
|
||||||
|
transaction.deviantRecipient,
|
||||||
|
transaction.usageWithNoSpecialType,
|
||||||
|
transaction.primaNotaNumber,
|
||||||
|
transaction.textKeySupplement,
|
||||||
|
|
||||||
|
transaction.currencyType,
|
||||||
|
transaction.bookingKey,
|
||||||
|
transaction.referenceForTheAccountOwner,
|
||||||
|
transaction.referenceOfTheAccountServicingInstitution,
|
||||||
|
transaction.supplementaryDetails,
|
||||||
|
|
||||||
|
transaction.transactionReferenceNumber,
|
||||||
|
transaction.relatedReferenceNumber
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package net.dankito.banking.util
|
package net.dankito.banking.util
|
||||||
|
|
||||||
|
import net.dankito.banking.fints.transactions.mt940.Mt940Parser
|
||||||
import net.dankito.banking.ui.model.AccountTransaction
|
import net.dankito.banking.ui.model.AccountTransaction
|
||||||
import net.dankito.banking.ui.model.BankAccount
|
import net.dankito.banking.ui.model.BankAccount
|
||||||
import org.kapott.hbci.GV_Result.GVRKUms
|
import org.kapott.hbci.GV_Result.GVRKUms
|
||||||
|
import org.kapott.hbci.structures.Value
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
@ -25,8 +27,10 @@ open class AccountTransactionMapper {
|
||||||
open fun mapAccountTransactions(bankAccount: BankAccount, result: GVRKUms): List<AccountTransaction> {
|
open fun mapAccountTransactions(bankAccount: BankAccount, result: GVRKUms): List<AccountTransaction> {
|
||||||
val entries = ArrayList<AccountTransaction>()
|
val entries = ArrayList<AccountTransaction>()
|
||||||
|
|
||||||
result.flatData.forEach { transaction ->
|
result.dataPerDay.forEach { btag ->
|
||||||
entries.add(mapAccountingEntry(bankAccount, transaction))
|
btag.lines.forEach { transaction ->
|
||||||
|
entries.add(mapAccountingEntry(bankAccount, btag, transaction))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Retrieved ${result.flatData.size} accounting entries")
|
log.debug("Retrieved ${result.flatData.size} accounting entries")
|
||||||
|
@ -34,129 +38,50 @@ open class AccountTransactionMapper {
|
||||||
return entries.sortedByDescending { it.bookingDate }
|
return entries.sortedByDescending { it.bookingDate }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun mapAccountingEntry(bankAccount: BankAccount, transaction: GVRKUms.UmsLine): AccountTransaction {
|
protected open fun mapAccountingEntry(bankAccount: BankAccount, btag: GVRKUms.BTag, transaction: GVRKUms.UmsLine): AccountTransaction {
|
||||||
|
val unparsedUsage = transaction.usage.joinToString("")
|
||||||
|
val parsedUsage = Mt940Parser().getUsageParts(unparsedUsage)
|
||||||
|
val statementAndMaySequenceNumber = btag.counter.split('/')
|
||||||
|
|
||||||
val result = AccountTransaction(BigDecimal.valueOf(transaction.value.longValue).divide(BigDecimal.valueOf(100)), transaction.bdate, transaction.usage.joinToString(""),
|
val result = AccountTransaction(bankAccount,
|
||||||
if (transaction.other.name2.isNullOrBlank() == false) transaction.other.name + " " + transaction.other.name2 else transaction.other.name,
|
mapValue(transaction.value), transaction.value.curr, transaction.isStorno, unparsedUsage, transaction.bdate,
|
||||||
if (transaction.other.bic != null) transaction.other.bic else transaction.other.blz,
|
transaction.other.name + (transaction.other.name2 ?: ""),
|
||||||
if (transaction.other.iban != null) transaction.other.iban else transaction.other.number,
|
transaction.other.bic ?: transaction.other.blz,
|
||||||
transaction.text, BigDecimal.valueOf(transaction.saldo.value.longValue), transaction.value.curr, bankAccount)
|
transaction.other.iban ?: transaction.other.number,
|
||||||
|
transaction.text, transaction.valuta,
|
||||||
|
statementAndMaySequenceNumber[0].toInt(),
|
||||||
|
if (statementAndMaySequenceNumber.size > 1) statementAndMaySequenceNumber[1].toInt() else null,
|
||||||
|
mapValue(btag.start.value), mapValue(btag.end.value),
|
||||||
|
|
||||||
// mapUsage(transaction, result)
|
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[""],
|
||||||
|
transaction.primanota,
|
||||||
|
transaction.addkey,
|
||||||
|
|
||||||
|
null,
|
||||||
|
"",
|
||||||
|
transaction.customerref,
|
||||||
|
transaction.instref,
|
||||||
|
transaction.additional,
|
||||||
|
|
||||||
|
"",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected open fun mapValue(value: Value): BigDecimal {
|
||||||
* From https://sites.google.com/a/crem-solutions.de/doku/version-2012-neu/buchhaltung/03-zahlungsverkehr/05-e-banking/technische-beschreibung-der-mt940-sta-datei:
|
return BigDecimal.valueOf(value.longValue).divide(BigDecimal.valueOf(100))
|
||||||
*
|
}
|
||||||
* Weitere 4 Verwendungszwecke können zu den Feldschlüsseln 60 bis 63 eingestellt werden.
|
|
||||||
* Jeder Bezeichner [z.B. EREF+] muss am Anfang eines Subfeldes [z. B. ?21] stehen.
|
|
||||||
* Bei Längenüberschreitung wird im nachfolgenden Subfeld ohne Wiederholung des Bezeichners fortgesetzt. Bei Wechsel des Bezeichners ist ein neues Subfeld zu beginnen.
|
|
||||||
* Belegung in der nachfolgenden Reihenfolge, wenn vorhanden:
|
|
||||||
* EREF+[ Ende-zu-Ende Referenz ] (DD-AT10; CT-AT41 - Angabe verpflichtend; NOTPROVIDED wird nicht eingestellt.)
|
|
||||||
* KREF+[Kundenreferenz]
|
|
||||||
* MREF+[Mandatsreferenz] (DD-AT01 - Angabe verpflichtend)
|
|
||||||
* CRED+[Creditor Identifier] (DD-AT02 - Angabe verpflichtend bei SEPA-Lastschriften, nicht jedoch bei SEPA-Rücklastschriften)
|
|
||||||
* DEBT+[Originators Identification Code](CT-AT10- Angabe verpflichtend,)
|
|
||||||
* Entweder CRED oder DEBT
|
|
||||||
*
|
|
||||||
* optional zusätzlich zur Einstellung in Feld 61, Subfeld 9:
|
|
||||||
*
|
|
||||||
* COAM+ [Compensation Amount / Summe aus Auslagenersatz und Bearbeitungsprovision bei einer nationalen Rücklastschrift sowie optionalem Zinsausgleich.]
|
|
||||||
* OAMT+[Original Amount] Betrag der ursprünglichen Lastschrift
|
|
||||||
*
|
|
||||||
* SVWZ+[SEPA-Verwendungszweck] (DD-AT22; CT-AT05 -Angabe verpflichtend, nicht jedoch bei R-Transaktionen)
|
|
||||||
* ABWA+[Abweichender Überweisender] (CT-AT08) / Abweichender Zahlungsempfänger (DD-AT38) ] (optional)
|
|
||||||
* ABWE+[Abweichender Zahlungsemp-fänger (CT-AT28) / Abweichender Zahlungspflichtiger ((DD-AT15)] (optional)
|
|
||||||
*/
|
|
||||||
// protected open fun mapUsage(buchung: GVRKUms.UmsLine, entry: AccountingEntry) {
|
|
||||||
// var lastUsageLineType = UsageLineType.ContinuationFromLastLine
|
|
||||||
// var typeValue = ""
|
|
||||||
//
|
|
||||||
// buchung.usage.forEach { line ->
|
|
||||||
// val (type, adjustedString) = getUsageLineType(line, entry)
|
|
||||||
//
|
|
||||||
// if (type == UsageLineType.ContinuationFromLastLine) {
|
|
||||||
// typeValue += (if(adjustedString[0].isUpperCase()) " " else "") + adjustedString
|
|
||||||
// }
|
|
||||||
// else if (lastUsageLineType != type) {
|
|
||||||
// if (lastUsageLineType != UsageLineType.ContinuationFromLastLine) {
|
|
||||||
// setUsageLineValue(entry, lastUsageLineType, typeValue)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// typeValue = adjustedString
|
|
||||||
// lastUsageLineType = type
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// tryToParseBookingDateFromUsageLine(entry, adjustedString, typeValue)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if(lastUsageLineType != UsageLineType.ContinuationFromLastLine) {
|
|
||||||
// setUsageLineValue(entry, lastUsageLineType, typeValue)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// protected open fun setUsageLineValue(entry: AccountingEntry, lastUsageLineType: UsageLineType, typeValue: String) {
|
|
||||||
// entry.parsedUsages.add(typeValue)
|
|
||||||
//
|
|
||||||
// when (lastUsageLineType) {
|
|
||||||
// UsageLineType.EREF -> entry.endToEndReference = typeValue
|
|
||||||
// UsageLineType.KREF -> entry.kundenreferenz = typeValue
|
|
||||||
// UsageLineType.MREF -> entry.mandatsreferenz = typeValue
|
|
||||||
// UsageLineType.CRED -> entry.creditorIdentifier = typeValue
|
|
||||||
// UsageLineType.DEBT -> entry.originatorsIdentificationCode = typeValue
|
|
||||||
// UsageLineType.COAM -> entry.compensationAmount = typeValue
|
|
||||||
// UsageLineType.OAMT -> entry.originalAmount = typeValue
|
|
||||||
// UsageLineType.SVWZ -> entry.sepaVerwendungszweck = typeValue
|
|
||||||
// UsageLineType.ABWA -> entry.abweichenderAuftraggeber = typeValue
|
|
||||||
// UsageLineType.ABWE -> entry.abweichenderZahlungsempfaenger = typeValue
|
|
||||||
// UsageLineType.NoSpecialType -> entry.usageWithNoSpecialType = typeValue
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// protected open fun getUsageLineType(line: String, entry: AccountingEntry): Pair<UsageLineType, String> {
|
|
||||||
// return when {
|
|
||||||
// line.startsWith("EREF+") -> Pair(UsageLineType.EREF, line.substring(5))
|
|
||||||
// line.startsWith("KREF+") -> Pair(UsageLineType.KREF, line.substring(5))
|
|
||||||
// line.startsWith("MREF+") -> Pair(UsageLineType.MREF, line.substring(5))
|
|
||||||
// line.startsWith("CRED+") -> Pair(UsageLineType.CRED, line.substring(5))
|
|
||||||
// line.startsWith("DEBT+") -> Pair(UsageLineType.DEBT, line.substring(5))
|
|
||||||
// line.startsWith("COAM+") -> Pair(UsageLineType.COAM, line.substring(5))
|
|
||||||
// line.startsWith("OAMT+") -> Pair(UsageLineType.OAMT, line.substring(5))
|
|
||||||
// line.startsWith("SVWZ+") -> Pair(UsageLineType.SVWZ, line.substring(5))
|
|
||||||
// line.startsWith("ABWA+") -> Pair(UsageLineType.ABWA, line.substring(5))
|
|
||||||
// line.startsWith("ABWE+") -> Pair(UsageLineType.ABWE, line.substring(5))
|
|
||||||
// entry.usage.startsWith(line) -> Pair(UsageLineType.NoSpecialType, line)
|
|
||||||
// else -> Pair(UsageLineType.ContinuationFromLastLine, line)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// protected open fun tryToParseBookingDateFromUsageLine(entry: AccountingEntry, currentLine: String, typeLine: String) {
|
|
||||||
// if (currentLine.startsWith(DateStartString)) {
|
|
||||||
// tryToParseBookingDateFromUsageLine(entry, currentLine)
|
|
||||||
// }
|
|
||||||
// else if (typeLine.startsWith(DateStartString)) {
|
|
||||||
// tryToParseBookingDateFromUsageLine(entry, typeLine)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// protected open fun tryToParseBookingDateFromUsageLine(entry: AccountingEntry, line: String) {
|
|
||||||
// var dateString = line.replace(DateStartString, "")
|
|
||||||
// val index = dateString.indexOf(DateEndString)
|
|
||||||
// if (index > 0) {
|
|
||||||
// dateString = dateString.substring(0, index)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// entry.bookingDate = DateTimeFormat.parse(dateString)
|
|
||||||
// } catch (e: Exception) {
|
|
||||||
// try {
|
|
||||||
// entry.bookingDate = DateFormat.parse(dateString)
|
|
||||||
// } catch (secondException: Exception) {
|
|
||||||
// log.debug("Could not parse '$dateString' from '$line' to a Date", e)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue