Applied adjusted values from MT 940 to AccountTransaction

This commit is contained in:
dankito 2024-09-05 19:14:23 +02:00
parent 47e2b851b9
commit bf76de4f23
9 changed files with 158 additions and 82 deletions

View File

@ -41,7 +41,7 @@ open class CsvWriter {
protected open suspend fun writeToFile(stream: AsyncStream, valueSeparator: String, customer: CustomerAccount, account: BankAccount, transaction: AccountTransaction) { protected open suspend fun writeToFile(stream: AsyncStream, valueSeparator: String, customer: CustomerAccount, account: BankAccount, transaction: AccountTransaction) {
val amount = if (valueSeparator == ";") transaction.amount.amount.string.replace('.', ',') else transaction.amount.amount.string.replace(',', '.') val amount = if (valueSeparator == ";") transaction.amount.amount.string.replace('.', ',') else transaction.amount.amount.string.replace(',', '.')
stream.writeString(listOf(customer.bankName, account.identifier, transaction.valueDate, amount, transaction.amount.currency, ensureNotNull(transaction.bookingText), wrap(transaction.reference), stream.writeString(listOf(customer.bankName, account.identifier, transaction.valueDate, amount, transaction.amount.currency, ensureNotNull(transaction.postingText), wrap(transaction.reference),
ensureNotNull(transaction.otherPartyName), ensureNotNull(transaction.otherPartyBankCode), ensureNotNull(transaction.otherPartyAccountId)).joinToString(valueSeparator)) ensureNotNull(transaction.otherPartyName), ensureNotNull(transaction.otherPartyBankCode), ensureNotNull(transaction.otherPartyAccountId)).joinToString(valueSeparator))
stream.writeString(NewLine) stream.writeString(NewLine)

View File

@ -227,7 +227,7 @@ open class FinTsJobExecutor(
response.getFirstSegmentById<ReceivedCreditCardTransactionsAndBalance>(InstituteSegmentId.CreditCardTransactions)?.let { creditCardTransactionsSegment -> response.getFirstSegmentById<ReceivedCreditCardTransactionsAndBalance>(InstituteSegmentId.CreditCardTransactions)?.let { creditCardTransactionsSegment ->
balance = Money(creditCardTransactionsSegment.balance.amount, creditCardTransactionsSegment.balance.currency ?: "EUR") balance = Money(creditCardTransactionsSegment.balance.amount, creditCardTransactionsSegment.balance.currency ?: "EUR")
bookedTransactions.addAll(creditCardTransactionsSegment.transactions.map { AccountTransaction(parameter.account, it.amount, it.description, it.bookingDate, it.transactionDescriptionBase ?: "", null, null, "", it.valueDate) }) bookedTransactions.addAll(creditCardTransactionsSegment.transactions.map { AccountTransaction(parameter.account, it.amount, it.description, it.bookingDate, it.valueDate, it.transactionDescriptionBase ?: "", null, null) })
} }
} }

View File

@ -111,14 +111,23 @@ open class FinTsModelMapper {
} }
open fun map(transaction: net.codinux.banking.fints.model.AccountTransaction): AccountTransaction { open fun map(transaction: net.codinux.banking.fints.model.AccountTransaction): AccountTransaction {
return AccountTransaction(transaction.amount, transaction.unparsedReference, transaction.bookingDate, return AccountTransaction(
transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId, transaction.bookingText, transaction.valueDate, transaction.amount, transaction.unparsedReference,
transaction.statementNumber, transaction.sequenceNumber, transaction.openingBalance, transaction.closingBalance, transaction.bookingDate, transaction.valueDate,
transaction.endToEndReference, transaction.customerReference, transaction.mandateReference, transaction.creditorIdentifier, transaction.originatorsIdentificationCode, transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId,
transaction.postingText, transaction.statementNumber, transaction.sheetNumber,
transaction.openingBalance, transaction.closingBalance,
transaction.customerReference, transaction.bankReference, transaction.furtherInformation,
transaction.endToEndReference, transaction.mandateReference, transaction.creditorIdentifier, transaction.originatorsIdentificationCode,
transaction.compensationAmount, transaction.originalAmount, transaction.sepaReference, transaction.deviantOriginator, transaction.deviantRecipient, transaction.compensationAmount, transaction.originalAmount, transaction.sepaReference, transaction.deviantOriginator, transaction.deviantRecipient,
transaction.referenceWithNoSpecialType, transaction.primaNotaNumber, transaction.textKeySupplement, transaction.referenceWithNoSpecialType,
transaction.currencyType, transaction.bookingKey, transaction.referenceForTheAccountOwner, transaction.referenceOfTheAccountServicingInstitution, transaction.supplementaryDetails,
transaction.transactionReferenceNumber, transaction.relatedReferenceNumber) transaction.journalNumber, transaction.textKeyAddition,
transaction.orderReferenceNumber, transaction.referenceNumber
)
} }

View File

@ -7,51 +7,118 @@ import net.codinux.banking.fints.extensions.UnixEpochStart
open class AccountTransaction( open class AccountTransaction(
val account: AccountData, val account: AccountData,
val amount: Money, val amount: Money,
val isReversal: Boolean,
val unparsedReference: String, val unparsedReference: String,
val bookingDate: LocalDate, val bookingDate: LocalDate,
val otherPartyName: String?,
val otherPartyBankCode: String?,
val otherPartyAccountId: String?,
val bookingText: String?,
val valueDate: LocalDate, val valueDate: LocalDate,
/**
* Name des Überweisenden oder Zahlungsempfängers
*/
val otherPartyName: String?,
/**
* BIC des Überweisenden / Zahlungsempfängers
*/
val otherPartyBankCode: String?,
/**
* IBAN des Überweisenden oder Zahlungsempfängers
*/
val otherPartyAccountId: String?,
/**
* Buchungstext, z. B. DAUERAUFTRAG, BARGELDAUSZAHLUNG, ONLINE-UEBERWEISUNG, FOLGELASTSCHRIFT, ...
*/
val postingText: String?,
/**
* Auszugsnummer
*/
val statementNumber: Int, val statementNumber: Int,
val sequenceNumber: Int?, /**
* Blattnummer
*/
val sheetNumber: Int?,
val openingBalance: Money?, val openingBalance: Money?,
val closingBalance: Money?, val closingBalance: Money?,
val endToEndReference: String?, /**
* Kundenreferenz.
*/
val customerReference: String?, val customerReference: String?,
/**
* Bankreferenz
*/
val bankReference: String?,
/**
* Währungsart und Umsatzbetrag in Ursprungswährung
*/
val furtherInformation: String?,
/* Remittance information */
val endToEndReference: String?,
val mandateReference: String?, val mandateReference: String?,
val creditorIdentifier: String?, val creditorIdentifier: String?,
val originatorsIdentificationCode: String?, val originatorsIdentificationCode: String?,
/**
* Summe aus Auslagenersatz und Bearbeitungsprovision bei einer nationalen Rücklastschrift
* sowie optionalem Zinsausgleich.
*/
val compensationAmount: String?, val compensationAmount: String?,
/**
* Betrag der ursprünglichen Lastschrift
*/
val originalAmount: String?, val originalAmount: String?,
val sepaReference: String?, val sepaReference: String?,
/**
* Abweichender Überweisender oder Zahlungsempfänger
*/
val deviantOriginator: String?, val deviantOriginator: String?,
/**
* Abweichender Zahlungsempfänger oder Zahlungspflichtiger
*/
val deviantRecipient: String?, val deviantRecipient: String?,
val referenceWithNoSpecialType: String?, val referenceWithNoSpecialType: String?,
val primaNotaNumber: String?,
val textKeySupplement: String?,
val currencyType: String?, /**
val bookingKey: String, * Primanoten-Nr.
val referenceForTheAccountOwner: String, */
val referenceOfTheAccountServicingInstitution: String?, val journalNumber: String?,
val supplementaryDetails: String?, /**
* Bei R-Transaktionen siehe Tabelle der
* SEPA-Rückgabecodes, bei SEPALastschriften siehe optionale Belegung
* bei GVC 104 und GVC 105 (GVC = Geschäftsvorfallcode)
*/
val textKeyAddition: String?,
val transactionReferenceNumber: String, /**
val relatedReferenceNumber: String? * Referenznummer, die vom Sender als eindeutige Kennung für die Nachricht vergeben wurde
* (z.B. als Referenz auf stornierte Nachrichten).
*/
val orderReferenceNumber: String?,
/**
* Bezugsreferenz
*/
val referenceNumber: String?,
/**
* Storno, ob die Buchung storniert wurde(?).
* Aus:
* RC = Storno Haben
* RD = Storno Soll
*/
val isReversal: Boolean
) { ) {
// for object deserializers // for object deserializers
internal constructor() : this(AccountData(), Money(Amount.Zero, ""), "", UnixEpochStart, null, null, null, null, UnixEpochStart) internal constructor() : this(AccountData(), Money(Amount.Zero, ""), "", UnixEpochStart, UnixEpochStart, null, null, null, null)
constructor(account: AccountData, amount: Money, unparsedReference: String, bookingDate: LocalDate, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, bookingText: String?, valueDate: LocalDate) constructor(account: AccountData, amount: Money, unparsedReference: String, bookingDate: LocalDate, valueDate: LocalDate, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, postingText: String? = null)
: this(account, amount, false, unparsedReference, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, : this(account, amount, unparsedReference, bookingDate, valueDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, postingText,
0, null, null, null, 0, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, "", "", null, null, "", null) "", null, null, "", null, false)
open val showOtherPartyName: Boolean open val showOtherPartyName: Boolean
@ -72,7 +139,7 @@ open class AccountTransaction(
if (otherPartyName != other.otherPartyName) return false if (otherPartyName != other.otherPartyName) return false
if (otherPartyBankCode != other.otherPartyBankCode) return false if (otherPartyBankCode != other.otherPartyBankCode) return false
if (otherPartyAccountId != other.otherPartyAccountId) return false if (otherPartyAccountId != other.otherPartyAccountId) return false
if (bookingText != other.bookingText) return false if (postingText != other.postingText) return false
if (valueDate != other.valueDate) return false if (valueDate != other.valueDate) return false
return true return true
@ -86,7 +153,7 @@ open class AccountTransaction(
result = 31 * result + (otherPartyName?.hashCode() ?: 0) result = 31 * result + (otherPartyName?.hashCode() ?: 0)
result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0) result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0)
result = 31 * result + (otherPartyAccountId?.hashCode() ?: 0) result = 31 * result + (otherPartyAccountId?.hashCode() ?: 0)
result = 31 * result + (bookingText?.hashCode() ?: 0) result = 31 * result + (postingText?.hashCode() ?: 0)
result = 31 * result + valueDate.hashCode() result = 31 * result + valueDate.hashCode()
return result return result
} }

View File

@ -44,24 +44,33 @@ open class Mt940AccountTransactionsParser(
protected open fun mapToAccountTransaction(statement: AccountStatement, transaction: Transaction, account: AccountData): AccountTransaction { protected open fun mapToAccountTransaction(statement: AccountStatement, transaction: Transaction, account: AccountData): AccountTransaction {
val currency = statement.closingBalance.currency val currency = statement.closingBalance.currency
// may parse postingKey to postingText (Buchungstext) according to table in 8.2.3 Buchungsschlüssel (Feld 61), S. 654 ff.
return AccountTransaction( return AccountTransaction(
account, account,
Money(mapAmount(transaction.statementLine), currency), Money(mapAmount(transaction.statementLine), currency),
transaction.statementLine.isReversal,
transaction.information?.unparsedReference ?: "", transaction.information?.unparsedReference ?: "",
transaction.statementLine.bookingDate ?: statement.closingBalance.bookingDate, transaction.statementLine.bookingDate ?: statement.closingBalance.bookingDate,
transaction.statementLine.valueDate,
transaction.information?.otherPartyName, transaction.information?.otherPartyName,
transaction.information?.otherPartyBankCode, transaction.information?.otherPartyBankCode,
transaction.information?.otherPartyAccountId, transaction.information?.otherPartyAccountId,
transaction.information?.postingText, transaction.information?.postingText,
transaction.statementLine.valueDate,
statement.statementNumber, statement.statementNumber,
statement.sheetNumber, statement.sheetNumber,
Money(mapAmount(statement.openingBalance), currency), // TODO: that's not true, these are the opening and closing balance of
Money(mapAmount(statement.closingBalance), currency), // all transactions of this day, not this specific transaction's ones Money(mapAmount(statement.openingBalance), currency),
Money(mapAmount(statement.closingBalance), currency),
// :60: customer reference: Wenn „KREF+“ eingestellt ist, dann erfolgt die Angabe der Referenznummer in Tag :86: .
transaction.information?.customerReference ?: transaction.statementLine.customerReference,
transaction.statementLine.bankReference,
transaction.statementLine.furtherInformationOriginalAmountAndCharges,
transaction.information?.endToEndReference, transaction.information?.endToEndReference,
transaction.information?.customerReference,
transaction.information?.mandateReference, transaction.information?.mandateReference,
transaction.information?.creditorIdentifier, transaction.information?.creditorIdentifier,
transaction.information?.originatorsIdentificationCode, transaction.information?.originatorsIdentificationCode,
@ -74,14 +83,10 @@ open class Mt940AccountTransactionsParser(
transaction.information?.journalNumber, transaction.information?.journalNumber,
transaction.information?.textKeyAddition, transaction.information?.textKeyAddition,
transaction.statementLine.currencyType,
transaction.statementLine.postingKey,
transaction.statementLine.customerReference,
transaction.statementLine.bankReference,
transaction.statementLine.furtherInformationOriginalAmountAndCharges,
statement.orderReferenceNumber, statement.orderReferenceNumber,
statement.referenceNumber statement.referenceNumber,
transaction.statementLine.isReversal,
) )
} }

View File

@ -273,7 +273,7 @@ open class Mt940Parser(
val postingKey = fieldValue.substring(postingKeyStart, postingKeyStart + 3) // TODO: parse codes, p. 178 val postingKey = fieldValue.substring(postingKeyStart, postingKeyStart + 3) // TODO: parse codes, p. 178
val customerAndBankReference = fieldValue.substring(postingKeyStart + 3).split("//") val customerAndBankReference = fieldValue.substring(postingKeyStart + 3).split("//")
val customerReference = customerAndBankReference[0] val customerReference = customerAndBankReference[0].takeIf { it != "NONREF" }
/** /**
* The content of this subfield is the account servicing institution's own reference for the transaction. * The content of this subfield is the account servicing institution's own reference for the transaction.
@ -281,14 +281,14 @@ open class Mt940Parser(
* reference may be identical to subfield 7, Reference for the Account Owner. If this is * reference may be identical to subfield 7, Reference for the Account Owner. If this is
* the case, Reference of the Account Servicing Institution, subfield 8 may be omitted. * the case, Reference of the Account Servicing Institution, subfield 8 may be omitted.
*/ */
var bankReference = if (customerAndBankReference.size > 1) customerAndBankReference[1] else customerReference // TODO: or use null? var bankReference = if (customerAndBankReference.size > 1) customerAndBankReference[1] else null
var furtherInformation: String? = null var furtherInformation: String? = null
val bankReferenceAndSupplementaryDetails = bankReference.split("\n") if (bankReference != null && bankReference.contains('\n')) {
if (bankReferenceAndSupplementaryDetails.size > 1) { val bankReferenceAndFurtherInformation = bankReference.split("\n")
bankReference = bankReferenceAndSupplementaryDetails[0].trim() bankReference = bankReferenceAndFurtherInformation[0].trim()
// TODO: parse /OCMT/ and /CHGS/, see page 518 // TODO: parse /OCMT/ and /CHGS/, see page 518
furtherInformation = bankReferenceAndSupplementaryDetails[1].trim() furtherInformation = bankReferenceAndFurtherInformation[1].trim()
} }
return StatementLine(!!!isDebit, isCancellation, valueDate, bookingDate, null, amount, postingKey, return StatementLine(!!!isDebit, isCancellation, valueDate, bookingDate, null, amount, postingKey,

View File

@ -58,17 +58,11 @@ open class StatementLine(
val postingKey: String, val postingKey: String,
/** /**
* Kundenreferenz. Bei * Kundenreferenz.
* Nichtbelegung wird * Bei Nichtbelegung wird NONREF eingestellt, zum Beispiel bei Schecknummer
* NONREF eingestellt, * Wenn KREF+ eingestellt ist, dann erfolgt die Angabe der Referenznummer in Tag :86: .
* zum Beispiel bei Schecknummer
* Wenn KREF+ eingestellt
* ist, dann erfolgt die
* Angabe der
* Referenznummer in Tag
* :86: .
*/ */
val customerReference: String, val customerReference: String?,
/** /**
* Bankreferenz * Bankreferenz

View File

@ -11,19 +11,26 @@ import net.codinux.banking.fints.extensions.UnixEpochStart
open class AccountTransaction( open class AccountTransaction(
val amount: Money, // TODO: if we decide to stick with Money, create own type, don't use that one from fints.model (or move over from) val amount: Money, // TODO: if we decide to stick with Money, create own type, don't use that one from fints.model (or move over from)
val unparsedReference: String, // alternative names: purpose, reason val unparsedReference: String, // alternative names: purpose, reason
val bookingDate: LocalDate, val bookingDate: LocalDate,
val valueDate: LocalDate,
val otherPartyName: String?, val otherPartyName: String?,
val otherPartyBankCode: String?, val otherPartyBankCode: String?,
val otherPartyAccountId: String?, val otherPartyAccountId: String?,
val bookingText: String?,
val valueDate: LocalDate, val postingText: String?,
val statementNumber: Int, val statementNumber: Int,
val sequenceNumber: Int?, val sheetNumber: Int?,
val openingBalance: Money?, val openingBalance: Money?,
val closingBalance: Money?, val closingBalance: Money?,
val endToEndReference: String?,
val customerReference: String?, val customerReference: String?,
val bankReference: String?,
val furtherInformation: String?,
val endToEndReference: String?,
val mandateReference: String?, val mandateReference: String?,
val creditorIdentifier: String?, val creditorIdentifier: String?,
val originatorsIdentificationCode: String?, val originatorsIdentificationCode: String?,
@ -36,24 +43,18 @@ open class AccountTransaction(
val primaNotaNumber: String?, val primaNotaNumber: String?,
val textKeySupplement: String?, val textKeySupplement: String?,
val currencyType: String?, val orderReferenceNumber: String?,
val bookingKey: String, val referenceNumber: 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(Money(Amount.Zero, ""), "", UnixEpochStart, null, null, null, null, UnixEpochStart) internal constructor() : this(Money(Amount.Zero, ""), "", UnixEpochStart, UnixEpochStart, null, null, null, null)
constructor(amount: Money, unparsedReference: String, bookingDate: LocalDate, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, bookingText: String?, valueDate: LocalDate) constructor(amount: Money, unparsedReference: String, bookingDate: LocalDate, valueDate: LocalDate, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, postingText: String?)
: this(amount, unparsedReference, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, : this(amount, unparsedReference, bookingDate, valueDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, postingText,
0, null, null, null, 0, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, "", "", null, null, "", null) null, null, null, null)
open val showOtherPartyName: Boolean open val showOtherPartyName: Boolean
@ -73,7 +74,7 @@ open class AccountTransaction(
if (otherPartyName != other.otherPartyName) return false if (otherPartyName != other.otherPartyName) return false
if (otherPartyBankCode != other.otherPartyBankCode) return false if (otherPartyBankCode != other.otherPartyBankCode) return false
if (otherPartyAccountId != other.otherPartyAccountId) return false if (otherPartyAccountId != other.otherPartyAccountId) return false
if (bookingText != other.bookingText) return false if (postingText != other.postingText) return false
if (valueDate != other.valueDate) return false if (valueDate != other.valueDate) return false
return true return true
@ -86,7 +87,7 @@ open class AccountTransaction(
result = 31 * result + (otherPartyName?.hashCode() ?: 0) result = 31 * result + (otherPartyName?.hashCode() ?: 0)
result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0) result = 31 * result + (otherPartyBankCode?.hashCode() ?: 0)
result = 31 * result + (otherPartyAccountId?.hashCode() ?: 0) result = 31 * result + (otherPartyAccountId?.hashCode() ?: 0)
result = 31 * result + (bookingText?.hashCode() ?: 0) result = 31 * result + (postingText?.hashCode() ?: 0)
result = 31 * result + valueDate.hashCode() result = 31 * result + valueDate.hashCode()
return result return result
} }

View File

@ -3,7 +3,7 @@ package net.codinux.banking.fints.transactions
import net.codinux.banking.fints.FinTsTestBase import net.codinux.banking.fints.FinTsTestBase
import net.codinux.banking.fints.transactions.mt940.Mt940Parser import net.codinux.banking.fints.transactions.mt940.Mt940Parser
import net.codinux.banking.fints.transactions.mt940.model.Balance import net.codinux.banking.fints.transactions.mt940.model.Balance
import net.codinux.banking.fints.transactions.mt940.model.InformationToAccountOwner import net.codinux.banking.fints.transactions.mt940.model.RemittanceInformationField
import net.codinux.banking.fints.transactions.mt940.model.StatementLine import net.codinux.banking.fints.transactions.mt940.model.StatementLine
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import net.codinux.banking.fints.extensions.* import net.codinux.banking.fints.extensions.*
@ -90,7 +90,7 @@ class Mt940ParserTest : FinTsTestBase() {
assertEquals(BankCode, statement.bankCodeBicOrIban) assertEquals(BankCode, statement.bankCodeBicOrIban)
assertEquals(CustomerId, statement.accountIdentifier) assertEquals(CustomerId, statement.accountIdentifier)
assertEquals(0, statement.statementNumber) assertEquals(0, statement.statementNumber)
assertNull(statement.sequenceNumber) assertNull(statement.sheetNumber)
assertBalance(statement.openingBalance, true, bookingDate, Amount("0,00")) assertBalance(statement.openingBalance, true, bookingDate, Amount("0,00"))
assertBalance(statement.closingBalance, isCredit, bookingDate, amount) assertBalance(statement.closingBalance, isCredit, bookingDate, amount)
@ -306,7 +306,7 @@ class Mt940ParserTest : FinTsTestBase() {
assertSize(1, result.first().transactions) assertSize(1, result.first().transactions)
result.first().transactions[0].information?.apply { result.first().transactions[0].information?.apply {
assertEquals("BASISLASTSCHRIFT", bookingText) assertEquals("BASISLASTSCHRIFT", postingText)
assertEquals("TUBDDEDD", otherPartyBankCode) assertEquals("TUBDDEDD", otherPartyBankCode)
assertEquals("DE87300308801234567890", otherPartyAccountId) assertEquals("DE87300308801234567890", otherPartyAccountId)
assertEquals("6MKL2OT30QENNLIU", endToEndReference) assertEquals("6MKL2OT30QENNLIU", endToEndReference)
@ -362,7 +362,7 @@ class Mt940ParserTest : FinTsTestBase() {
assertEquals(amount, statementLine.amount) assertEquals(amount, statementLine.amount)
} }
private fun assertTransactionDetails(details: InformationToAccountOwner?, otherPartyName: String, private fun assertTransactionDetails(details: RemittanceInformationField?, otherPartyName: String,
otherPartyBankCode: String, otherPartyAccountId: String) { otherPartyBankCode: String, otherPartyAccountId: String) {
assertNotNull(details) assertNotNull(details)