From a3696a4716178cc4cb0d6170c19088eaf76f8c26 Mon Sep 17 00:00:00 2001 From: dankito Date: Thu, 6 Aug 2020 01:30:15 +0200 Subject: [PATCH] Fixed JSON serialization (as cannot add Jackson annotations to model classes in common project) --- .../json/BankingPersistenceJson/build.gradle | 8 + .../persistence/BankingPersistenceJson.kt | 21 +- .../persistence/mapper/EntitiesMapper.kt | 132 ++++++++++ .../model/AccountTransactionEntity.kt | 58 ++++ .../persistence/model/BankAccountEntity.kt | 38 +++ .../persistence/model/CustomerEntity.kt | 30 +++ .../persistence/BankingPersistenceJsonTest.kt | 247 ++++++++++++++++++ .../banking/ui/model/AccountTransaction.kt | 3 - .../dankito/banking/ui/model/BankAccount.kt | 3 - .../net/dankito/banking/ui/model/Customer.kt | 3 - 10 files changed, 531 insertions(+), 12 deletions(-) create mode 100644 persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesMapper.kt create mode 100644 persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt create mode 100644 persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt create mode 100644 persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt create mode 100644 persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt diff --git a/persistence/json/BankingPersistenceJson/build.gradle b/persistence/json/BankingPersistenceJson/build.gradle index 702f2fb8..67ec3f72 100644 --- a/persistence/json/BankingPersistenceJson/build.gradle +++ b/persistence/json/BankingPersistenceJson/build.gradle @@ -15,4 +15,12 @@ compileTestKotlin { dependencies { implementation project(':BankingUiCommon') + + + testImplementation "junit:junit:$junitVersion" + + testImplementation "org.assertj:assertj-core:$assertJVersion" + testImplementation "org.mockito:mockito-core:$mockitoVersion" + + testImplementation "org.slf4j:slf4j-simple:$slf4jVersion" } \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt index 111d4086..1b977ebe 100644 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt @@ -1,10 +1,13 @@ package net.dankito.banking.persistence +import net.dankito.banking.persistence.mapper.EntitiesMapper +import net.dankito.banking.persistence.model.CustomerEntity import net.dankito.utils.multiplatform.File import net.dankito.banking.ui.model.Customer import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.util.ISerializer +import net.dankito.utils.multiplatform.Month import java.io.FileOutputStream import java.net.URL @@ -14,6 +17,8 @@ open class BankingPersistenceJson( protected val serializer: ISerializer ) : IBankingPersistence { + protected val mapper = EntitiesMapper() + init { jsonFile.absoluteFile.parentFile.mkdirs() @@ -21,20 +26,30 @@ open class BankingPersistenceJson( override fun saveOrUpdateAccount(customer: Customer, allCustomers: List) { - serializer.serializeObject(allCustomers, jsonFile) + saveAllCustomers(allCustomers) } override fun deleteAccount(customer: Customer, allCustomers: List) { - serializer.serializeObject(allCustomers, jsonFile) + saveAllCustomers(allCustomers) } override fun readPersistedAccounts(): List { - return serializer.deserializeListOr(jsonFile, Customer::class, listOf()) + val deserializedCustomers = serializer.deserializeListOr(jsonFile, CustomerEntity::class) + + return mapper.mapCustomerEntities(deserializedCustomers) } override fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List) { // done when called saveOrUpdateAccount() + // TODO: or also call saveAllCustomers()? + } + + + protected open fun saveAllCustomers(allCustomers: List) { + val mappedCustomers = mapper.mapCustomers(allCustomers) + + serializer.serializeObject(mappedCustomers, jsonFile) } diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesMapper.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesMapper.kt new file mode 100644 index 00000000..827e51f5 --- /dev/null +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesMapper.kt @@ -0,0 +1,132 @@ +package net.dankito.banking.persistence.mapper + +import net.dankito.banking.persistence.model.AccountTransactionEntity +import net.dankito.banking.persistence.model.BankAccountEntity +import net.dankito.banking.persistence.model.CustomerEntity +import net.dankito.banking.ui.model.AccountTransaction +import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.Customer + + +open class EntitiesMapper { + + open fun mapCustomers(customers: List): List { + return customers.map { mapCustomer(it) } + } + + open fun mapCustomer(customer: Customer): CustomerEntity { + val mappedCustomer = CustomerEntity( + customer.bankCode, customer.customerId, customer.password, customer.finTsServerAddress, + customer.bankName, customer.bic, customer.customerName, customer.userId, customer.iconUrl, + listOf(), customer.supportedTanProcedures, customer.selectedTanProcedure, customer.tanMedia + ) + + mappedCustomer.id = customer.technicalId + + mappedCustomer.accounts = mapBankAccounts(customer.accounts, mappedCustomer) + + return mappedCustomer + } + + + open fun mapCustomerEntities(customers: List): List { + return customers.map { mapCustomer(it) } + } + + open fun mapCustomer(customer: CustomerEntity): Customer { + val mappedCustomer = Customer( + customer.bankCode, customer.customerId, customer.password, customer.finTsServerAddress, + customer.bankName, customer.bic, customer.customerName, customer.userId, customer.iconUrl + ) + + mappedCustomer.technicalId = customer.id + + mappedCustomer.accounts = mapBankAccounts(customer.accounts, mappedCustomer) + + + mappedCustomer.supportedTanProcedures = customer.supportedTanProcedures + mappedCustomer.selectedTanProcedure = customer.selectedTanProcedure + mappedCustomer.tanMedia = customer.tanMedia + + return mappedCustomer + } + + + open fun mapBankAccounts(transactions: List, customer: CustomerEntity): List { + return transactions.map { mapBankAccount(it, customer) } + } + + open fun mapBankAccount(account: BankAccount, customer: CustomerEntity): BankAccountEntity { + val mappedAccount = BankAccountEntity( + customer, account.identifier, account.accountHolderName, account.iban, account.subAccountNumber, + account.customerId, account.balance, account.currency, account.type, account.productName, + account.accountLimit, account.lastRetrievedTransactionsTimestamp, + account.supportsRetrievingAccountTransactions, account.supportsRetrievingBalance, + account.supportsTransferringMoney, account.supportsInstantPaymentMoneyTransfer + ) + + mappedAccount.id = account.technicalId + + mappedAccount.bookedTransactions = mapTransactions(account.bookedTransactions, mappedAccount) + + return mappedAccount + } + + + open fun mapBankAccounts(transactions: List, customer: Customer): List { + return transactions.map { mapBankAccount(it, customer) } + } + + open fun mapBankAccount(account: BankAccountEntity, customer: Customer): BankAccount { + val mappedAccount = BankAccount( + customer, account.identifier, account.accountHolderName, account.iban, account.subAccountNumber, + account.customerId, account.balance, account.currency, account.type, account.productName, + account.accountLimit, account.lastRetrievedTransactionsTimestamp, + account.supportsRetrievingAccountTransactions, account.supportsRetrievingBalance, + account.supportsTransferringMoney, account.supportsInstantPaymentMoneyTransfer + ) + + mappedAccount.technicalId = account.id + + mappedAccount.bookedTransactions = mapTransactions(account.bookedTransactions, mappedAccount) + + return mappedAccount + } + + + open fun mapTransactions(transactions: List, account: BankAccountEntity): List { + return transactions.map { mapTransaction(it, account) } + } + + open fun mapTransaction(transaction: AccountTransaction, account: BankAccountEntity): AccountTransactionEntity { + return AccountTransactionEntity(account, transaction.amount, transaction.currency, transaction.unparsedUsage, transaction.bookingDate, + transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId, transaction.bookingText, + transaction.valueDate, transaction.statementNumber, transaction.sequenceNumber, transaction.openingBalance, transaction.closingBalance, + 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, transaction.technicalId) + } + + + open fun mapTransactions(transactions: List, account: BankAccount): List { + return transactions.map { mapTransaction(it, account) } + } + + open fun mapTransaction(transaction: AccountTransactionEntity, account: BankAccount): AccountTransaction { + val mappedTransaction = AccountTransaction(account, transaction.amount, transaction.currency, transaction.unparsedUsage, transaction.bookingDate, + transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId, transaction.bookingText, + transaction.valueDate, transaction.statementNumber, transaction.sequenceNumber, transaction.openingBalance, transaction.closingBalance, + 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) + + mappedTransaction.technicalId = transaction.id + + return mappedTransaction + } + +} \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt new file mode 100644 index 00000000..2d0039d9 --- /dev/null +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt @@ -0,0 +1,58 @@ +package net.dankito.banking.persistence.model + +import com.fasterxml.jackson.annotation.JsonIdentityInfo +import com.fasterxml.jackson.annotation.ObjectIdGenerators +import net.dankito.utils.multiplatform.BigDecimal +import net.dankito.utils.multiplatform.Date +import net.dankito.utils.multiplatform.UUID + + +@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references +// had to define all properties as 'var' 'cause MapStruct cannot handle vals +open class AccountTransactionEntity( + open var bankAccount: BankAccountEntity, + open var amount: BigDecimal, + open var currency: String, + open var unparsedUsage: String, + open var bookingDate: Date, + open var otherPartyName: String?, + open var otherPartyBankCode: String?, + open var otherPartyAccountId: String?, + open var bookingText: String?, + open var valueDate: Date, + open var statementNumber: Int, + open var sequenceNumber: Int?, + open var openingBalance: BigDecimal?, + open var closingBalance: BigDecimal?, + + open var endToEndReference: String?, + open var customerReference: String?, + open var mandateReference: String?, + open var creditorIdentifier: String?, + open var originatorsIdentificationCode: String?, + open var compensationAmount: String?, + open var originalAmount: String?, + open var sepaUsage: String?, + open var deviantOriginator: String?, + open var deviantRecipient: String?, + open var usageWithNoSpecialType: String?, + open var primaNotaNumber: String?, + open var textKeySupplement: String?, + + open var currencyType: String?, + open var bookingKey: String, + open var referenceForTheAccountOwner: String, + open var referenceOfTheAccountServicingInstitution: String?, + open var supplementaryDetails: String?, + + open var transactionReferenceNumber: String, + open var relatedReferenceNumber: String?, + var id: String = UUID.random().toString() +) { + + // for object deserializers + internal constructor() : this(BankAccountEntity(), BigDecimal.Zero, "", "", Date(), null, null, null, null, Date(), + -1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, + null, "", null) + +} \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt new file mode 100644 index 00000000..47a632c4 --- /dev/null +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt @@ -0,0 +1,38 @@ +package net.dankito.banking.persistence.model + +import com.fasterxml.jackson.annotation.JsonIdentityInfo +import com.fasterxml.jackson.annotation.ObjectIdGenerators +import net.dankito.banking.ui.model.BankAccountType +import net.dankito.utils.multiplatform.BigDecimal +import net.dankito.utils.multiplatform.Date +import net.dankito.utils.multiplatform.UUID + + +@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references +// had to define all properties as 'var' 'cause MapStruct cannot handle vals (and cannot use Pozo's mapstruct-kotlin as SerializableBankAccountBuilder would fail with @Context) +open class BankAccountEntity( + open var customer: CustomerEntity, + open var identifier: String, + open var accountHolderName: String, + open var iban: String?, + open var subAccountNumber: String?, + open var customerId: String, + open var balance: BigDecimal = BigDecimal.Zero, + open var currency: String = "EUR", + open var type: BankAccountType = BankAccountType.Girokonto, + open var productName: String? = null, + open var accountLimit: String? = null, + open var lastRetrievedTransactionsTimestamp: Date? = null, + open var supportsRetrievingAccountTransactions: Boolean = false, + open var supportsRetrievingBalance: Boolean = false, + open var supportsTransferringMoney: Boolean = false, + open var supportsInstantPaymentMoneyTransfer: Boolean = false, + open var bookedTransactions: List = listOf(), + open var unbookedTransactions: List = listOf(), + open var id: String = UUID.random().toString() + +) { + + internal constructor() : this(CustomerEntity(), "", "", null, null, "") // for object deserializers + +} \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt new file mode 100644 index 00000000..92c78047 --- /dev/null +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt @@ -0,0 +1,30 @@ +package net.dankito.banking.persistence.model + +import com.fasterxml.jackson.annotation.* +import net.dankito.banking.ui.model.tan.TanMedium +import net.dankito.banking.ui.model.tan.TanProcedure +import java.util.* + + +@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references +// had to define all properties as 'var' 'cause MapStruct cannot handle vals (and cannot use Pozo's mapstruct-kotlin as SerializableCustomerBuilder would fail with @Context) +open class CustomerEntity( + var bankCode: String, + var customerId: String, + var password: String, + var finTsServerAddress: String, + var bankName: String, + var bic: String, + var customerName: String, + var userId: String = customerId, + var iconUrl: String? = null, + var accounts: List = listOf(), + var supportedTanProcedures: List = listOf(), + var selectedTanProcedure: TanProcedure? = null, + var tanMedia: List = listOf(), + var id: String = UUID.randomUUID().toString() +) { + + internal constructor() : this("", "", "", "", "", "", "") // for object deserializers + +} \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt b/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt new file mode 100644 index 00000000..9720aab9 --- /dev/null +++ b/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt @@ -0,0 +1,247 @@ +package net.dankito.banking.persistence + +import net.dankito.banking.persistence.mapper.CustomerConverter +import net.dankito.banking.persistence.mapper.CycleAvoidingMappingContext +import net.dankito.banking.persistence.model.AccountTransactionEntity +import net.dankito.banking.persistence.model.BankAccountEntity +import net.dankito.banking.persistence.model.CustomerEntity +import net.dankito.banking.ui.model.AccountTransaction +import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.Customer +import net.dankito.banking.util.JacksonJsonSerializer +import net.dankito.utils.multiplatform.BigDecimal +import net.dankito.utils.multiplatform.Date +import net.dankito.utils.multiplatform.File +import org.assertj.core.api.Assertions.assertThat +import org.junit.Assert +import org.junit.Test +import org.mapstruct.factory.Mappers +import kotlin.random.Random + + +class BankingPersistenceJsonTest { + + companion object { + + const val BankCode = "12345678" + + const val CustomerId = "0987654321" + + const val Password = "12345" + + const val FinTsServerAddress = "http://i-do-not-exist.fail/givemeyourmoney" + + const val BankName = "Abzock GmbH" + + const val Bic = "ABCDDEBB123" + + const val CustomerName = "Hans Dampf" + + const val UserId = CustomerId + + const val IconUrl = "http://i-do-not-exist.fail/favicon.ico" + + val NowMillis = System.currentTimeMillis() + + val TwoYearsAgoMillis = NowMillis - (2 * 365 * 24 * 60 * 60 * 1000L) + + + val TestDataFolder = File("testData") + + init { + TestDataFolder.mkdirs() + } + } + + + private val file = File(TestDataFolder, "test_accounts.json") + + private val serializer = JacksonJsonSerializer() + + private val underTest = BankingPersistenceJson(file, serializer) + + + @Test + fun saveOrUpdateAccount() { + + // given + val customers = listOf( + createCustomer(2), + createCustomer(3) + ) + + + // when + underTest.saveOrUpdateAccount(customers.first(), customers) + + + // then + val result = serializer.deserializeListOr(file, CustomerEntity::class) + + assertCustomersEqual(result, customers) + } + + @Test + fun saveOrUpdateAccountWithBankAccountsAndTransactions() { + + // given + val customer = createCustomer(2) + + + // when + underTest.saveOrUpdateAccount(customer, listOf(customer)) + + + // then + val result = serializer.deserializeListOr(file, CustomerEntity::class) + + assertCustomersEqual(result, listOf(customer)) + } + + + @Test + fun readPersistedAccounts() { + + // given + val customers = listOf( + createCustomer(2), + createCustomer(3) + ) + val serializableCustomers = Mappers.getMapper(CustomerConverter::class.java).mapToEntities(customers, CycleAvoidingMappingContext()) + + serializer.serializeObject(serializableCustomers, file) + + + // when + val result = underTest.readPersistedAccounts() + + + // then + assertCustomersEqual(serializableCustomers, result) + } + + + private fun createCustomer(countBankAccounts: Int = 0, customerId: String = CustomerId): Customer { + val result = Customer(BankCode, customerId, Password, FinTsServerAddress, BankName, Bic, CustomerName, UserId, IconUrl) + + result.accounts = createBankAccounts(countBankAccounts, result) + + return result + } + + private fun createBankAccounts(count: Int, customer: Customer): List { + val random = Random(System.nanoTime()) + + return IntRange(1, count).map { accountIndex -> + createBankAccount("Account_$accountIndex", customer, random.nextInt(2, 50)) + } + } + + private fun createBankAccount(productName: String, customer: Customer, countTransactions: Int = 0): BankAccount { + val result = BankAccount(customer, customer.customerId, "AccountHolder", "DE00" + customer.bankCode + customer.customerId, null, + customer.customerId, BigDecimal(84.25), productName = productName) + + result.bookedTransactions = createAccountTransactions(countTransactions, result) + + return result + } + + private fun createAccountTransactions(countTransactions: Int, account: BankAccount): List { + return IntRange(1, countTransactions).map { transactionIndex -> + createAccountTransaction(transactionIndex, account) + } + } + + private fun createAccountTransaction(transactionIndex: Int, account: BankAccount): AccountTransaction { + return AccountTransaction(account, "OtherParty_$transactionIndex", "Usage_$transactionIndex", BigDecimal(transactionIndex.toDouble()), createDate(), null) + } + + private fun createDate(): Date { + return Date(Random(System.nanoTime()).nextLong(TwoYearsAgoMillis, NowMillis)) + } + + + private fun assertCustomersEqual(deserializedCustomers: List, customers: List) { + assertThat(deserializedCustomers.size).isEqualTo(customers.size) + + deserializedCustomers.forEach { deserializedCustomer -> + val customer = customers.firstOrNull { it.technicalId == deserializedCustomer.id } + + if (customer == null) { + Assert.fail("Could not find matching customer for deserialized customer $deserializedCustomer. customers = $customers") + } + else { + assertCustomersEqual(deserializedCustomer, customer) + } + } + } + + private fun assertCustomersEqual(deserializedCustomer: CustomerEntity, customer: Customer) { + assertThat(deserializedCustomer.bankCode).isEqualTo(customer.bankCode) + assertThat(deserializedCustomer.customerId).isEqualTo(customer.customerId) + assertThat(deserializedCustomer.password).isEqualTo(customer.password) + assertThat(deserializedCustomer.finTsServerAddress).isEqualTo(customer.finTsServerAddress) + + assertThat(deserializedCustomer.bankName).isEqualTo(customer.bankName) + assertThat(deserializedCustomer.bic).isEqualTo(customer.bic) + assertThat(deserializedCustomer.customerName).isEqualTo(customer.customerName) + assertThat(deserializedCustomer.userId).isEqualTo(customer.userId) + assertThat(deserializedCustomer.iconUrl).isEqualTo(customer.iconUrl) + + assertBankAccountsEqual(deserializedCustomer.accounts, customer.accounts) + } + + private fun assertBankAccountsEqual(deserializedAccounts: List, accounts: List) { + assertThat(deserializedAccounts.size).isEqualTo(accounts.size) + + deserializedAccounts.forEach { deserializedAccount -> + val account = accounts.firstOrNull { it.technicalId == deserializedAccount.id } + + if (account == null) { + Assert.fail("Could not find matching account for deserialized account $deserializedAccount. accounts = $accounts") + } + else { + assertBankAccountsEqual(deserializedAccount, account) + } + } + } + + private fun assertBankAccountsEqual(deserializedAccount: BankAccountEntity, account: BankAccount) { + // to check if MapStruct created reference correctly + assertThat(deserializedAccount.customer.id).isEqualTo(account.customer.technicalId) + + assertThat(deserializedAccount.identifier).isEqualTo(account.identifier) + assertThat(deserializedAccount.iban).isEqualTo(account.iban) + assertThat(deserializedAccount.customerId).isEqualTo(account.customerId) + assertThat(deserializedAccount.balance).isEqualTo(account.balance) + assertThat(deserializedAccount.productName).isEqualTo(account.productName) + + assertAccountTransactionsEqual(deserializedAccount.bookedTransactions, account.bookedTransactions) + } + + private fun assertAccountTransactionsEqual(deserializedTransactions: List, transactions: List) { + assertThat(deserializedTransactions.size).isEqualTo(transactions.size) + + deserializedTransactions.forEach { deserializedTransaction -> + val transaction = transactions.firstOrNull { it.technicalId == deserializedTransaction.id } + + if (transaction == null) { + Assert.fail("Could not find matching transaction for deserialized transaction $deserializedTransaction. transactions = $transactions") + } + else { + assertAccountTransactionsEqual(deserializedTransaction, transaction) + } + } + } + + private fun assertAccountTransactionsEqual(deserializedTransaction: AccountTransactionEntity, transaction: AccountTransaction) { + // to check if MapStruct created reference correctly + assertThat(deserializedTransaction.bankAccount.id).isEqualTo(transaction.bankAccount.technicalId) + + assertThat(deserializedTransaction.otherPartyName).isEqualTo(transaction.otherPartyName) + assertThat(deserializedTransaction.unparsedUsage).isEqualTo(transaction.unparsedUsage) + assertThat(deserializedTransaction.amount).isEqualTo(transaction.amount) + assertThat(deserializedTransaction.valueDate).isEqualTo(transaction.valueDate) + } + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt index 80bf2a18..5fe89ad3 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt @@ -1,14 +1,11 @@ package net.dankito.banking.ui.model -//import com.fasterxml.jackson.annotation.JsonIdentityInfo -//import com.fasterxml.jackson.annotation.ObjectIdGenerators import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.Date import net.dankito.utils.multiplatform.DateFormatStyle import net.dankito.utils.multiplatform.DateFormatter -//@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references open class AccountTransaction( open val bankAccount: BankAccount, open val amount: BigDecimal, diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt index c2551c03..5c64df4c 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt @@ -1,14 +1,11 @@ package net.dankito.banking.ui.model -//import com.fasterxml.jackson.annotation.JsonIdentityInfo -//import com.fasterxml.jackson.annotation.ObjectIdGenerators import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.Date import net.dankito.utils.multiplatform.UUID import kotlin.jvm.JvmOverloads -//@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references open class BankAccount @JvmOverloads constructor( open val customer: Customer, open val identifier: String, diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt index 264073d8..00f1df96 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt @@ -1,7 +1,5 @@ package net.dankito.banking.ui.model -//import com.fasterxml.jackson.annotation.JsonIdentityInfo -//import com.fasterxml.jackson.annotation.ObjectIdGenerators import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.sum import net.dankito.banking.ui.model.tan.TanMedium @@ -10,7 +8,6 @@ import net.dankito.banking.ui.model.tan.TanProcedure import net.dankito.utils.multiplatform.UUID -//@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references open class Customer( open var bankCode: String, open var customerId: String,