diff --git a/Sources/SwiftBeanCountWealthsimpleMapper/Extensions/Wealthsimple.Transaction+Helper.swift b/Sources/SwiftBeanCountWealthsimpleMapper/Extensions/Wealthsimple.Transaction+Helper.swift index 7e51164..ac6aa6e 100644 --- a/Sources/SwiftBeanCountWealthsimpleMapper/Extensions/Wealthsimple.Transaction+Helper.swift +++ b/Sources/SwiftBeanCountWealthsimpleMapper/Extensions/Wealthsimple.Transaction+Helper.swift @@ -14,6 +14,9 @@ extension Wealthsimple.Transaction { var marketPrice: Amount { Amount(for: marketPriceAmount, in: marketPriceCurrency) } + var negatedMarketValue: Amount { + Amount(for: marketValueAmount, in: marketValueCurrency, negate: true) + } var netCash: Amount { Amount(for: netCashAmount, in: netCashCurrency) } diff --git a/Sources/SwiftBeanCountWealthsimpleMapper/WealthsimpleLedgerMapper.swift b/Sources/SwiftBeanCountWealthsimpleMapper/WealthsimpleLedgerMapper.swift index 319ec7a..1de5335 100644 --- a/Sources/SwiftBeanCountWealthsimpleMapper/WealthsimpleLedgerMapper.swift +++ b/Sources/SwiftBeanCountWealthsimpleMapper/WealthsimpleLedgerMapper.swift @@ -191,6 +191,8 @@ public struct WealthsimpleLedgerMapper { result = try mapTransfer(transaction, in: account, accountTypes: [.expense]) case .fee, .reimbursement, .interest: result = try mapTransfer(transaction, in: account, accountTypes: [.expense, .income], payee: Self.payee) + case .stockDividend: + (price, result) = try mapStockDividend(transaction, in: account) default: throw WealthsimpleConversionError.unsupportedTransactionType(transaction.transactionType.rawValue) } @@ -263,6 +265,16 @@ public struct WealthsimpleLedgerMapper { return STransaction(metaData: TransactionMetaData(date: transaction.processDate, metaData: metaDataDict), postings: [posting1, posting2]) } + private func mapStockDividend(_ transaction: WTransaction, in account: WAccount) throws -> (Price, STransaction) { + let result = STransaction(metaData: TransactionMetaData(date: transaction.processDate, metaData: [MetaDataKeys.id: transaction.id]), postings: [ + Posting(accountName: try lookup.ledgerAccountName(for: .dividend(transaction.symbol), in: account, ofType: [.income]), amount: transaction.negatedMarketValue), + Posting(accountName: try lookup.ledgerAccountName(of: account, symbol: transaction.symbol), + amount: Amount(for: transaction.quantity, in: try lookup.commoditySymbol(for: transaction.symbol)), + cost: try Cost(amount: transaction.marketPrice, date: nil, label: nil)) + ]) + return (try Price(date: transaction.processDate, commoditySymbol: lookup.commoditySymbol(for: transaction.symbol), amount: transaction.marketPrice), result) + } + private func mapNonResidentWithholdingTax(_ transaction: WTransaction, in account: WAccount) throws -> STransaction { let amount = try parseNRWTDescription(transaction.description) let price = Amount(number: transaction.fxAmount.number, commoditySymbol: amount.commoditySymbol, decimalDigits: transaction.fxAmount.decimalDigits) diff --git a/Tests/SwiftBeanCountWealthsimpleMapperTests/Extensions/TransactionHelperTests.swift b/Tests/SwiftBeanCountWealthsimpleMapperTests/Extensions/TransactionHelperTests.swift index 7ae1ace..53dd5df 100644 --- a/Tests/SwiftBeanCountWealthsimpleMapperTests/Extensions/TransactionHelperTests.swift +++ b/Tests/SwiftBeanCountWealthsimpleMapperTests/Extensions/TransactionHelperTests.swift @@ -17,6 +17,10 @@ final class TransactionHelperTests: XCTestCase { XCTAssertEqual(transaction.netCash, Amount(number: Decimal(15.10), commoditySymbol: "CAD", decimalDigits: 2)) XCTAssertEqual(transaction.negatedNetCash, Amount(number: Decimal(-15.10), commoditySymbol: "CAD", decimalDigits: 2)) + transaction.marketValueAmount = "10.0110" + transaction.marketValueCurrency = "EUR" + XCTAssertEqual(transaction.negatedMarketValue, Amount(number: Decimal(-10.011_0), commoditySymbol: "EUR", decimalDigits: 4)) + transaction.marketValueCurrency = "CAD" XCTAssertFalse(transaction.useFx) transaction.marketValueCurrency = "EUR" diff --git a/Tests/SwiftBeanCountWealthsimpleMapperTests/WealthsimpleLedgerMapperTests.swift b/Tests/SwiftBeanCountWealthsimpleMapperTests/WealthsimpleLedgerMapperTests.swift index e87559c..2b669a8 100644 --- a/Tests/SwiftBeanCountWealthsimpleMapperTests/WealthsimpleLedgerMapperTests.swift +++ b/Tests/SwiftBeanCountWealthsimpleMapperTests/WealthsimpleLedgerMapperTests.swift @@ -34,6 +34,7 @@ final class WealthsimpleLedgerMapperTests: XCTestCase { quantity: "5.25", marketPriceAmount: "2.24", marketPriceCurrency: "CAD", + marketValueAmount: "11.76", marketValueCurrency: "CAD", netCashAmount: "-11.76", netCashCurrency: "CAD", @@ -305,6 +306,25 @@ final class WealthsimpleLedgerMapperTests: XCTestCase { XCTAssert(prices.isEmpty) } + func testMapTransactionsStockDividend() throws { + var transaction = testTransaction + transaction.transactionType = .stockDividend + + let accountName = try AccountName("Income:Dividend:ETF") + try ledger.add(SAccount(name: accountName, metaData: ["\(MetaDataKeys.dividendPrefix)ETF": accountNumber])) + + let (prices, transactions) = try mapper.mapTransactionsToPriceAndTransactions([transaction]) + let postings = [ + Posting(accountName: accountName, amount: Amount(number: -Decimal(string: transaction.marketValueAmount)!, commoditySymbol: "CAD", decimalDigits: 2)), + Posting(accountName: try AccountName("Assets:W:ETF"), + amount: Amount(number: Decimal(string: transaction.quantity)!, commoditySymbol: "ETF", decimalDigits: 2), + cost: try Cost(amount: testTransactionPrice.amount, date: nil, label: nil)) + ] + let resultTransaction = Transaction(metaData: TransactionMetaData(date: transaction.processDate, metaData: [MetaDataKeys.id: transactionId]), postings: postings) + XCTAssertEqual(prices, [testTransactionPrice]) + XCTAssertEqual(transactions, [resultTransaction]) + } + func testMapTransactionsTransfers() throws { var count = 1 let types: [SwiftBeanCountModel.AccountType: [Wealthsimple.TransactionType]] = [ @@ -395,3 +415,4 @@ final class WealthsimpleLedgerMapperTests: XCTestCase { } } +// swiftlint:disable:this file_length