Skip to content

Commit

Permalink
Handle Fund Transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
Nef10 committed Jan 13, 2024
1 parent eace81f commit 140f40a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SwiftBeanCountParserUtils
import Wealthsimple

/// Functions to transform downloaded Wealthsimple data into SwiftBeanCountModel types
public struct WealthsimpleLedgerMapper {
public struct WealthsimpleLedgerMapper { // swiftlint:disable:this type_body_length

private typealias WTransaction = Wealthsimple.Transaction
private typealias STransaction = SwiftBeanCountModel.Transaction
Expand Down Expand Up @@ -284,7 +284,7 @@ public struct WealthsimpleLedgerMapper {
}

private func mapStockSplits(_ transactions: [WTransaction], in account: WAccount) throws -> [STransaction] {
var splitPairs = [String: [WTransaction]]()
var splitPairs = [String: [WTransaction]](), splitNonPairs = [WTransaction]()
for transaction in transactions {
if splitPairs["\(transaction.symbol)"] == nil {
splitPairs["\(transaction.symbol)"] = []
Expand All @@ -293,15 +293,24 @@ public struct WealthsimpleLedgerMapper {
}
var transactions = [STransaction]()
for (_, transactionPair) in splitPairs {
transactions.append(try mapStockSplit(transactionPair, in: account))
if transactionPair.count != 2 {
splitNonPairs.append(contentsOf: transactionPair)
} else {
transactions.append(try mapStockSplit(transactionPair, in: account))
}
}
if !splitNonPairs.isEmpty {
if splitNonPairs.count == 2 && (splitNonPairs[0].marketValueAmount == "-\(splitNonPairs[1].marketValueAmount)"
|| "-\(splitNonPairs[0].marketValueAmount)" == splitNonPairs[1].marketValueAmount) {
transactions.append(try mapStockSplit(splitNonPairs, in: account))
} else {
throw WealthsimpleConversionError.unexpectedStockSplit(splitNonPairs.first!.description)
}
}
return transactions
}

private func mapStockSplit(_ transactions: [WTransaction], in account: WAccount) throws -> STransaction {
guard transactions.count == 2 else {
throw WealthsimpleConversionError.unexpectedStockSplit(transactions.first!.description)
}
guard let buyTransaction = transactions.first(where: { !$0.quantity.starts(with: "-") }),
let sellTransaction = transactions.first(where: { $0.quantity.starts(with: "-") }) else {
throw WealthsimpleConversionError.unexpectedStockSplit(transactions.first!.description)
Expand All @@ -313,7 +322,7 @@ public struct WealthsimpleLedgerMapper {
cost: try Cost(amount: nil, date: nil, label: nil)),
Posting(accountName: try lookup.ledgerAccountName(of: account, symbol: buyTransaction.symbol),
amount: Amount(for: buyTransaction.quantity, in: try lookup.commoditySymbol(for: buyTransaction.symbol)),
cost: try Cost(amount: nil, date: nil, label: nil))
cost: try Cost(amount: buyTransaction.symbol != sellTransaction.symbol ? buyTransaction.marketPrice : nil, date: nil, label: nil))
])
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ final class WealthsimpleLedgerMapperTests: XCTestCase {
var transaction2 = testTransaction
transaction2.transactionType = .stockDistribution
transaction2.quantity = "-\(transaction2.quantity)"
transaction2.marketValueAmount = "-\(transaction2.marketValueAmount)"

let emptyCost = try Cost(amount: nil, date: nil, label: nil)
let resultTransaction = Transaction(metaData: TransactionMetaData(date: transaction1.processDate, metaData: [MetaDataKeys.id: transaction1.id]),
Expand All @@ -404,6 +405,30 @@ final class WealthsimpleLedgerMapperTests: XCTestCase {
XCTAssertEqual(transactions, [resultTransaction])
}

func testSplitTransactionsDifferentCommodities() throws {
try? ledger.add(Commodity(symbol: "ETF2"))

var transaction1 = testTransaction
transaction1.transactionType = .stockDistribution
transaction1.symbol = "ETF2"
var transaction2 = testTransaction
transaction2.transactionType = .stockDistribution
transaction2.quantity = "-\(transaction2.quantity)"
transaction2.marketValueAmount = "-\(transaction2.marketValueAmount)"

let emptyCost = try Cost(amount: nil, date: nil, label: nil)
let cost = try Cost(amount: transaction2.marketPrice, date: nil, label: nil)
let resultTransaction = Transaction(metaData: TransactionMetaData(date: transaction1.processDate, metaData: [MetaDataKeys.id: transaction1.id]),
postings: [
try posting(account: "Assets:W:ETF", number: transaction2.quantity, commodity: transaction2.symbol, cost: emptyCost),
try posting(account: "Assets:W:ETF2", number: transaction1.quantity, commodity: transaction1.symbol, cost: cost),
])

let (prices, transactions) = try mapper.mapTransactionsToPriceAndTransactions([transaction1, transaction2])
XCTAssert(prices.isEmpty)
XCTAssertEqual(transactions, [resultTransaction])
}

// swiftlint:disable line_length
private func posting(account: String = "Assets:W:Cash", number: String = "-11.76", commodity: String = "CAD", decimals: Int = 2, price: Amount? = nil, cost: Cost? = nil) throws -> Posting {
Posting(accountName: try AccountName(account), amount: Amount(number: Decimal(string: number)!, commoditySymbol: commodity, decimalDigits: decimals), price: price, cost: cost)
Expand Down

0 comments on commit 140f40a

Please sign in to comment.