Skip to content

Commit 933b3ea

Browse files
Merge pull request #36 from figo-connect/feature/add-categories-to-transactions
add categories to transactions
2 parents 0428f6a + 9c3a99c commit 933b3ea

6 files changed

Lines changed: 116 additions & 19 deletions

File tree

figo/figo.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ def credential_login(self, username, password, scope=None):
316316
Returns:
317317
Dictionary which contains an access token and a refresh token.
318318
"""
319+
319320
data = {"grant_type": "password",
320321
"username": username,
321322
"password": password}
@@ -327,9 +328,12 @@ def credential_login(self, username, password, scope=None):
327328
if 'error' in response:
328329
raise FigoException.from_dict(response)
329330

330-
return {'access_token': response['access_token'],
331-
'refresh_token': response['refresh_token'] if 'refresh_token' in response else None,
332-
'expires': datetime.now() + timedelta(seconds=response['expires_in'])}
331+
return {
332+
'access_token': response['access_token'],
333+
'refresh_token': response['refresh_token'] if 'refresh_token' in response else None,
334+
'expires': datetime.now() + timedelta(seconds=response['expires_in']),
335+
'scope': response['scope'],
336+
}
333337

334338
def convert_refresh_token(self, refresh_token):
335339
"""Convert a refresh token (granted for offline access and returned by
@@ -524,7 +528,8 @@ def add_account_and_sync(self, country, credentials, bank_code=None, iban=None,
524528
error=task_state.error['name'],
525529
error_description=task_state.error['description'],
526530
code=task_state.error['code'])
527-
raise FigoException("", task_state.message)
531+
raise FigoException("", error_description=task_state.error['message'],
532+
code=task_state.error['code'])
528533
return task_state
529534

530535
def add_account_and_sync_with_new_pin(self, pin_exception, new_pin):

figo/models.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ class Transaction(ModelBase):
363363
__dump_attributes__ = ["transaction_id", "account_id", "name",
364364
"account_number", "bank_code", "bank_name", "amount",
365365
"currency", "booking_date", "value_date", "purpose",
366-
"type", "booking_text", "booked", "creation_timestamp",
366+
"type", "booking_text", "booked", "categories", "creation_timestamp",
367367
"modification_timestamp", "visited", "additional_info"]
368368

369369
transaction_id = None
@@ -416,6 +416,9 @@ class Transaction(ModelBase):
416416
booked = None
417417
"""This flag indicates whether the transaction is booked or pending"""
418418

419+
categories = None
420+
"""List of categories assigned to this transaction, ordered from general to specific"""
421+
419422
creation_timestamp = None
420423
"""creation date"""
421424

@@ -443,12 +446,31 @@ def __init__(self, session, **kwargs):
443446
if self.value_date:
444447
self.value_date = dateutil.parser.parse(self.value_date)
445448

449+
if self.categories:
450+
self.categories = [Category.from_dict(session, c) for c in self.categories]
451+
446452
def __str__(self):
447453
"""Short String representation of a Transaction."""
448454
return "Transaction: %d %s to %s at %s" % (self.amount, self.currency,
449455
self.name, str(self.value_date))
450456

451457

458+
class Category(ModelBase):
459+
460+
"""Object representing a category for a transaction"""
461+
462+
__dump_attributes__ = ["id", "parent_id", "name"]
463+
464+
id = None
465+
466+
parent_id = None
467+
468+
name = None
469+
470+
def __str__(self):
471+
return self.name
472+
473+
452474
class Notification(ModelBase):
453475

454476
"""Object representing a configured notification, e.g a webhook or email hook."""

tests/conftest.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,22 @@ def new_user_id():
4949

5050
@pytest.yield_fixture
5151
def figo_session(figo_connection, new_user_id):
52-
if is_demo(CREDENTIALS):
53-
pytest.skip("The demo client has no write access to the servers.")
54-
5552
figo_connection.add_user("Test", new_user_id, PASSWORD)
5653
response = figo_connection.credential_login(new_user_id, PASSWORD)
54+
55+
scope = response['scope']
56+
57+
required_scopes = [
58+
'accounts=rw',
59+
'transactions=rw',
60+
'user=rw',
61+
'categorization=rw',
62+
'create_user',
63+
]
64+
65+
if any(s not in scope for s in required_scopes):
66+
pytest.skip("The client ID needs write access to the servers.")
67+
5768
session = FigoSession(response['access_token'])
5869

5970
yield session

tests/test_models.py

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,23 @@
1-
from figo.models import Account, BankContact, AccountBalance, Payment, \
2-
Transaction, Notification, SynchronizationStatus, User, Service, \
3-
LoginSettings, Credential, TaskToken, TaskState, Challenge, PaymentProposal, \
4-
Process, ProcessStep, ProcessOptions, Security
1+
from figo.models import Account
2+
from figo.models import AccountBalance
3+
from figo.models import BankContact
4+
from figo.models import Category
5+
from figo.models import Challenge
6+
from figo.models import Credential
7+
from figo.models import LoginSettings
8+
from figo.models import Notification
9+
from figo.models import Payment
10+
from figo.models import PaymentProposal
11+
from figo.models import Process
12+
from figo.models import ProcessOptions
13+
from figo.models import ProcessStep
14+
from figo.models import Security
15+
from figo.models import Service
16+
from figo.models import SynchronizationStatus
17+
from figo.models import TaskState
18+
from figo.models import TaskToken
19+
from figo.models import Transaction
20+
from figo.models import User
521

622

723
def test_create_account_from_dict(demo_session):
@@ -107,6 +123,45 @@ def test_create_transaction_from_dict(demo_session):
107123
assert isinstance(transaction, Transaction)
108124

109125

126+
def test_create_transaction_with_categories(demo_session):
127+
data = {
128+
"account_id": "A1.1",
129+
"account_number": "4711951501",
130+
"amount": -17.89,
131+
"bank_code": "90090042",
132+
"bank_name": "Demobank",
133+
"booked": False,
134+
"booking_date": "2013-04-11T12:00:00.000Z",
135+
"booking_text": "Ueberweisung",
136+
"creation_timestamp": "2013-04-11T13:54:02.000Z",
137+
"currency": "EUR",
138+
"modification_timestamp": "2013-04-11T13:54:02.000Z",
139+
"name": "Rogers Shipping, Inc.",
140+
"purpose": "Ihre Sendung 0815 vom 01.03.2012, Vielen Dank",
141+
"transaction_id": "T1.1.25",
142+
"type": "Transfer",
143+
"categories": [
144+
{
145+
"parent_id": None,
146+
"id": 150,
147+
"name": "Lebenshaltung"
148+
},
149+
{
150+
"parent_id": 150,
151+
"id": 162,
152+
"name": "Spende"
153+
}
154+
],
155+
"value_date": "2013-04-11T12:00:00.000Z",
156+
"visited": True
157+
}
158+
transaction = Transaction.from_dict(demo_session, data)
159+
assert hasattr(transaction, 'categories')
160+
for category in transaction.categories:
161+
assert isinstance(category, Category)
162+
assert hasattr(category, 'id')
163+
164+
110165
def test_create_notification_from_dict(demo_session):
111166
data = {
112167
"notification_id": "N1.7",

tests/test_writing_methods.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
# coding:utf-8
22

3+
import pytest
34
import time
45

5-
import pytest
66
from mock import patch
77

8-
from figo.figo import FigoException, FigoPinException
9-
from figo.models import TaskToken, TaskState, Service, LoginSettings
8+
from figo.figo import FigoException
9+
from figo.figo import FigoPinException
10+
from figo.models import LoginSettings
11+
from figo.models import Service
12+
from figo.models import TaskState
13+
from figo.models import TaskToken
14+
1015

1116
CREDENTIALS = ["figo", "figo"]
1217
BANK_CODE = "90090042"
@@ -98,8 +103,8 @@ def test_051_add_account_and_sync_wrong_and_correct_pin(figo_session):
98103
assert isinstance(task_state, TaskState)
99104
assert len(figo_session.accounts) == 3
100105
except FigoException as figo_exception:
101-
# BBB(Valentin): prevent demo account from complaining - it returns no code on error
102-
if "Please use demo account credentials" not in figo_exception.error_description:
106+
# XXXValentin): prevent demo account from complaining
107+
if figo_exception.code != 90000:
103108
raise
104109

105110

tox.ini

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,5 @@ commands = py.test --cov=figo tests/ --cov-report html --junit-xml test-results.
2121
#
2222
# For other warning/error codes, please see:
2323
# http://flake8.readthedocs.org/en/latest/warnings.html
24-
ignore = E123,E133,E226,E241,E242,F403
25-
exclude = tests/*
24+
ignore = E123,E133,E226,E241,E242,H404,H405
2625
max-line-length = 100

0 commit comments

Comments
 (0)