Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pytest = "*"
black = "*"
importlib-metadata = "*" # required on python-3.7
dataclasses = "*" # required by black on python-3.6
syrupy = "*"

[packages]
ofxstatement = "*"
Expand Down
19 changes: 14 additions & 5 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 29 additions & 36 deletions src/ofxstatement_wise/wise.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from typing import Set, List, Iterable, TextIO, Optional
import itertools
from csv import DictReader
from datetime import datetime
from typing import Dict, Iterable, Optional
from decimal import Decimal

from ofxstatement.plugin import Plugin
from ofxstatement.parser import CsvStatementParser
from ofxstatement.parser import StatementParser
from ofxstatement.statement import (
Statement,
StatementLine,
BankAccount,
generate_unique_transaction_id,
)


Expand All @@ -18,65 +18,58 @@ class TransferwisePlugin(Plugin):
def get_parser(self, filename: str) -> "TransferwiseParser":
default_ccy = self.settings.get("currency")
account_id = self.settings.get("account")
return TransferwiseParser(open(filename, "rt"), default_ccy, account_id)
return TransferwiseParser(filename, default_ccy, account_id)


class TransferwiseParser(CsvStatementParser):
date_format: str = "%d-%m-%Y"
mappings = {
"amount": 3,
"date": 1,
"memo": 5,
"refnum": 0,
}

class TransferwiseParser(StatementParser[Dict[str, str]]):
def __init__(
self, fin: TextIO, currency: str | None = None, account_id: str | None = None
self, filename: str, currency: str | None = None, account_id: str | None = None
) -> None:
super().__init__(fin)
super().__init__()
self.filename = filename
self.currency = currency
self.account_id = account_id
self._unique: Set[str] = set()

def parse(self) -> Statement:
stmt = super().parse()
stmt.currency = self.currency
stmt.account_id = self.account_id
return stmt

def split_records(self) -> Iterable[List[str]]:
items = super().split_records()
# Skip the header line
yield from itertools.islice(items, 1, None)
def split_records(self) -> Iterable[Dict[str, str]]:
with open(self.filename, "rt") as f:
yield from DictReader(f)

def parse_record(self, line: List[str]) -> Optional[StatementLine]:
def parse_record(self, line: Dict[str, str]) -> Optional[StatementLine]:
"""Parse given transaction line and return StatementLine object"""
sl = super().parse_record(line)
if sl is None:
return None
sl = StatementLine()

sl.id = line["TransferWise ID"]
sl.date = datetime.strptime(line["Date Time"], "%d-%m-%Y %H:%M:%S.%f")
sl.memo = line["Description"]
sl.amount = Decimal(line["Amount"])

ccy = line[4]
if ccy != self.currency:
currency = line["Currency"]
if currency != self.currency:
# Skip lines in some other currencies
return None

sl.memo = self._make_memo(line)

sl.id = generate_unique_transaction_id(sl, self._unique)
payee_acc_no = line[12]
payee_acc_no = line["Payee Account Number"]
if payee_acc_no:
sl.bank_account_to = BankAccount("", payee_acc_no)

assert sl.amount is not None
sl.trntype = "DEBIT" if sl.amount > Decimal(0) else "CREDIT"
sl.trntype = line["Transaction Type"]
return sl

def _make_memo(self, line: List[str]) -> str:
descr = line[4]
payref = line[5]
exc_from = line[7]
exc_to = line[8]
exc_rate = line[9]
def _make_memo(self, line: Dict[str, str]) -> str:
descr = line["Description"]
payref = line["Payment Reference"]
exc_from = line["Exchange From"]
exc_to = line["Exchange To"]
exc_rate = line["Exchange Rate"]

memo = descr
if payref:
Expand Down
58 changes: 58 additions & 0 deletions tests/__snapshots__/test_transferwise.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# serializer version: 1
# name: test_transferwise
<Statement> { 'account_id': 'TW1',
'account_type': 'CHECKING',
'bank_id': None,
'currency': 'USD',
'invest_lines': [],
'lines': [ <StatementLine> { 'amount': Decimal('-357.38'),
'bank_account_to': <BankAccount> { 'acct_id': 'LT21 1111 2222 3333 4444',
'acct_key': None,
'acct_type': 'CHECKING',
'bank_id': '',
'branch_id': None},
'check_no': None,
'date': datetime.datetime(2020, 8, 24, 13, 43, 54, 496000),
'date_user': None,
'id': 'TRANSFER-175545673',
'memo': 'Sent money to John Doe (Moving to Revolut), 0.84840 USD/EUR',
'payee': None,
'refnum': None,
'trntype': 'DEBIT'},
<StatementLine> { 'amount': Decimal('-11.35'),
'check_no': None,
'date': datetime.datetime(2020, 8, 24, 8, 12, 37, 495000),
'date_user': None,
'id': 'TRANSFER-175545673',
'memo': 'TransferWise Charges for: TRANSFER-175545673 (Sending money)',
'payee': None,
'refnum': None,
'trntype': 'DEBIT'},
<StatementLine> { 'amount': Decimal('125.00'),
'check_no': None,
'date': datetime.datetime(2020, 8, 24, 19, 27, 5, 726000),
'date_user': None,
'id': 'TRANSFER-175522746',
'memo': 'Received money from the friend. with reference ',
'payee': None,
'refnum': None,
'trntype': 'CREDIT'},
<StatementLine> { 'amount': Decimal('20.17'),
'check_no': None,
'date': datetime.datetime(2020, 8, 20, 16, 1, 22, 725000),
'date_user': None,
'id': 'TRANSFER-174549828',
'memo': 'Topped up balance',
'payee': None,
'refnum': None,
'trntype': 'CREDIT'},
<StatementLine> { 'amount': Decimal('-0.17'),
'check_no': None,
'date': datetime.datetime(2020, 8, 20, 5, 55, 41, 960000),
'date_user': None,
'id': 'TRANSFER-174549828',
'memo': 'TransferWise Charges for: TRANSFER-174549828',
'payee': None,
'refnum': None,
'trntype': 'DEBIT'}]}
# ---
12 changes: 6 additions & 6 deletions tests/sample-statement.csv
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"TransferWise ID",Date,"Date Time",Amount,Currency,Description,"Payment Reference","Running Balance","Exchange From","Exchange To","Exchange Rate","Payer Name","Payee Name","Payee Account Number",Merchant,"Total fees"
TRANSFER-175545673,24-08-2020,"24-08-2020 13:43:54",-357.38,USD,"Sent money to John Doe","Moving to Revolut",787.62,USD,EUR,0.84840,,"John Doe","LT21 1111 2222 3333 4444",,11.35
TRANSFER-175545673,24-08-2020,"24-08-2020 08:12:37",-11.35,USD,"TransferWise Charges for: TRANSFER-175545673","Sending money",776.27,,,,,TransferWise,,,0
TRANSFER-175522746,24-08-2020,"24-08-2020 19:27:05",125.00,USD,"Received money from the friend. with reference ",,145.00,,,,"Friend.",,,,0.00
TRANSFER-174549828,20-08-2020,"20-08-2020 16:01:22",20.17,USD,"Topped up balance",,20.17,,,,,,,,0.17
TRANSFER-174549828,20-08-2020,"20-08-2020 05:55:41",-0.17,USD,"TransferWise Charges for: TRANSFER-174549828",,20.00,,,,,,,,0
"TransferWise ID",Date,"Date Time",Amount,Currency,Description,"Payment Reference","Running Balance","Exchange From","Exchange To","Exchange Rate","Payer Name","Payee Name","Payee Account Number",Merchant,"Card Last Four Digits","Card Holder Full Name",Attachment,"Total fees","Exchange To Amount","Transaction Type","Transaction Details Type"
TRANSFER-175545673,24-08-2020,"24-08-2020 13:43:54.496",-357.38,USD,"Sent money to John Doe","Moving to Revolut",787.62,USD,EUR,0.84840,,"John Doe","LT21 1111 2222 3333 4444",,,,,11.35,,DEBIT,
TRANSFER-175545673,24-08-2020,"24-08-2020 08:12:37.495",-11.35,USD,"TransferWise Charges for: TRANSFER-175545673","Sending money",776.27,,,,,TransferWise,,,,,,0,,DEBIT,
TRANSFER-175522746,24-08-2020,"24-08-2020 19:27:05.726",125.00,USD,"Received money from the friend. with reference ",,145.00,,,,"Friend.",,,,,,,0.00,,CREDIT,
TRANSFER-174549828,20-08-2020,"20-08-2020 16:01:22.725",20.17,USD,"Topped up balance",,20.17,,,,,,,,,,,0.17,,CREDIT,
TRANSFER-174549828,20-08-2020,"20-08-2020 05:55:41.960",-0.17,USD,"TransferWise Charges for: TRANSFER-174549828",,20.00,,,,,,,,,,,0,,DEBIT,
9 changes: 2 additions & 7 deletions tests/test_transferwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ofxstatement_wise.wise import TransferwisePlugin


def test_transferwise() -> None:
def test_transferwise(snapshot) -> None:
config = {"currency": "USD", "account": "TW1"}
plugin = TransferwisePlugin(UI(), config)
here = os.path.dirname(__file__)
Expand All @@ -14,9 +14,4 @@ def test_transferwise() -> None:
parser = plugin.get_parser(sample_filename)
statement = parser.parse()

assert statement is not None

assert len(statement.lines) == 5
# all ids are unique
assert len(set(ln.id for ln in statement.lines)) == 5
assert all(ln.amount for ln in statement.lines)
assert statement == snapshot