hsoft / moneyguru (http://hardcoded.net/moneyguru)

Future-aware personal finance application for Mac OS X and Windows.

Clone this repository (size: 8.3 MB): HTTPS / SSH
$ hg clone http://hg.hardcoded.net/moneyguru
commit 986: 096c21184984
parent 985: a00d52a70d9b
branch: default
Extracted entry-based functionalities of Account into a new EntryList class.
Virgil Dupras / hsoft
6 weeks ago

Changed (Δ13.5 KB):

raw changeset »

core/document.py (1 lines added, 1 lines removed)

core/gui/account_balance_graph.py (1 lines added, 1 lines removed)

core/gui/account_flow_graph.py (1 lines added, 1 lines removed)

core/gui/account_pie_chart.py (2 lines added, 2 lines removed)

core/gui/balance_sheet.py (4 lines added, 4 lines removed)

core/gui/entry_table.py (3 lines added, 2 lines removed)

core/gui/income_statement.py (4 lines added, 4 lines removed)

core/gui/net_worth_graph.py (1 lines added, 1 lines removed)

core/gui/profit_graph.py (1 lines added, 1 lines removed)

core/model/account.py (5 lines added, 100 lines removed)

core/model/budget.py (1 lines added, 1 lines removed)

core/model/entry.py (200 lines added, 0 lines removed)

core/model/oven.py (7 lines added, 6 lines removed)

core/model/transaction.py (0 lines added, 80 lines removed)

core/saver/qif.py (1 lines added, 1 lines removed)

core/tests/gui/balance_sheet_test.py (0 lines added, 1 lines removed)

core/tests/model/account_test.py (5 lines added, 7 lines removed)

Up to file-list core/document.py:

@@ -529,7 +529,7 @@ class Document(Repeater):
529
529
            return 0
530
530
        budgeted_amount = sum(-b.amount_for_date_range(date_range, currency=currency) for b in budgets)
531
531
        if target is not None:
532
            budgeted_amount = target._normalize_amount(budgeted_amount)
532
            budgeted_amount = target.normalize_amount(budgeted_amount)
533
533
        return budgeted_amount
534
534
    
535
535
    def change_budget(self, original, new):

Up to file-list core/gui/account_balance_graph.py:

@@ -20,7 +20,7 @@ class AccountBalanceGraph(BalanceGraph):
20
20
    def _balance_for_date(self, date):
21
21
        if self._account is None:
22
22
            return 0
23
        entry = self._account.last_entry(date=date)
23
        entry = self._account.entries.last_entry(date=date)
24
24
        return entry.normal_balance() if entry else 0
25
25
    
26
26
    def _budget_for_date(self, date):

Up to file-list core/gui/account_flow_graph.py:

@@ -24,7 +24,7 @@ class AccountFlowGraph(BarGraph):
24
24
        self.document.oven.continue_cooking(date_range.end) # it's possible that the overflow is not cooked
25
25
        account = self.mainwindow.shown_account
26
26
        currency = self._currency()
27
        cash_flow = account.normal_cash_flow(date_range, currency=currency)
27
        cash_flow = account.entries.normal_cash_flow(date_range, currency=currency)
28
28
        budgeted = self.document.budgets.normal_amount_for_account(account, date_range, currency=currency)
29
29
        return cash_flow + budgeted
30
30
    

Up to file-list core/gui/account_pie_chart.py:

@@ -65,7 +65,7 @@ class _BalancePieChart(_AccountPieChart)
65
65
        date = self.document.date_range.end
66
66
        currency = self.app.default_currency
67
67
        def get_value(account):
68
            balance = account.normal_balance(date=date, currency=currency)
68
            balance = account.entries.normal_balance(date=date, currency=currency)
69
69
            budget_date_range = DateRange(date.min, self.document.date_range.end)
70
70
            budgeted = self.document.budgeted_amount_for_target(account, budget_date_range)
71
71
            budgeted = convert_amount(budgeted, currency, date)
@@ -90,7 +90,7 @@ class _CashFlowPieChart(_AccountPieChart
90
90
        date_range = self.document.date_range
91
91
        currency = self.app.default_currency
92
92
        def get_value(account):
93
            cash_flow = account.normal_cash_flow(date_range, currency=currency)
93
            cash_flow = account.entries.normal_cash_flow(date_range, currency=currency)
94
94
            budgeted = self.document.budgets.normal_amount_for_account(account, date_range, currency=currency)
95
95
            return cash_flow + budgeted
96
96
        

Up to file-list core/gui/balance_sheet.py:

@@ -36,10 +36,10 @@ class BalanceSheet(Report):
36
36
        start_date = date_range.start
37
37
        end_date = date_range.end
38
38
        currency = self.app.default_currency
39
        start_amount = account.normal_balance(start_date - timedelta(1))
40
        start_amount_native = account.normal_balance(start_date - timedelta(1), currency=currency)
41
        end_amount = account.normal_balance(end_date)
42
        end_amount_native = account.normal_balance(end_date, currency=currency)
39
        start_amount = account.entries.normal_balance(start_date - timedelta(1))
40
        start_amount_native = account.entries.normal_balance(start_date - timedelta(1), currency=currency)
41
        end_amount = account.entries.normal_balance(end_date)
42
        end_amount_native = account.entries.normal_balance(end_date, currency=currency)
43
43
        budget_date_range = DateRange(date.today(), end_date)
44
44
        budgeted_amount = self.document.budgeted_amount_for_target(account, budget_date_range)
45
45
        budgeted_amount_native = convert_amount(budgeted_amount, currency, date_range.end)

Up to file-list core/gui/entry_table.py:

@@ -10,8 +10,9 @@ import datetime
10
10
from operator import attrgetter
11
11
12
12
from ..model.amount import convert_amount
13
from ..model.entry import Entry
13
14
from ..model.recurrence import Spawn
14
from ..model.transaction import Transaction, Entry
15
from ..model.transaction import Transaction
15
16
from ..trans import tr
16
17
from .column import Column
17
18
from .table import RowWithDebitAndCredit, RowWithDate, rowattr
@@ -99,7 +100,7 @@ class EntryTable(TransactionTableBase):
99
100
        balance = 0
100
101
        reconciled_balance = 0
101
102
        balance_with_budget = 0
102
        previous_entry = account.last_entry(date=date)
103
        previous_entry = account.entries.last_entry(date=date)
103
104
        if previous_entry:
104
105
            balance = previous_entry.balance
105
106
            reconciled_balance = previous_entry.reconciled_balance

Up to file-list core/gui/income_statement.py:

@@ -29,10 +29,10 @@ class IncomeStatement(Report):
29
29
        account = node.account
30
30
        date_range = self.document.date_range
31
31
        currency = self.app.default_currency
32
        cash_flow = account.normal_cash_flow(date_range)
33
        cash_flow_native = account.normal_cash_flow(date_range, currency)
34
        last_cash_flow = account.normal_cash_flow(date_range.prev())
35
        last_cash_flow_native = account.normal_cash_flow(date_range.prev(), currency)
32
        cash_flow = account.entries.normal_cash_flow(date_range)
33
        cash_flow_native = account.entries.normal_cash_flow(date_range, currency)
34
        last_cash_flow = account.entries.normal_cash_flow(date_range.prev())
35
        last_cash_flow_native = account.entries.normal_cash_flow(date_range.prev(), currency)
36
36
        remaining = self.document.budgets.normal_amount_for_account(account, date_range)
37
37
        remaining_native = self.document.budgets.normal_amount_for_account(account, date_range, currency)
38
38
        delta = cash_flow - last_cash_flow

Up to file-list core/gui/net_worth_graph.py:

@@ -16,7 +16,7 @@ class NetWorthGraph(BalanceGraph, SheetV
16
16
        BalanceGraph.__init__(self, view, networth_view)
17
17
    
18
18
    def _balance_for_date(self, date):
19
        balances = (a.balance(date=date, currency=self._currency) for a in self._accounts)
19
        balances = (a.entries.balance(date=date, currency=self._currency) for a in self._accounts)
20
20
        return sum(balances)
21
21
    
22
22
    def _budget_for_date(self, date):

Up to file-list core/gui/profit_graph.py:

@@ -24,7 +24,7 @@ class ProfitGraph(BarGraph, SheetViewNot
24
24
        self.document.oven.continue_cooking(date_range.end) # it's possible that the overflow is not cooked
25
25
        accounts = set(a for a in self.document.accounts if a.is_income_statement_account())
26
26
        accounts = accounts - self.document.excluded_accounts
27
        cash_flow = -sum(a.cash_flow(date_range, currency=self.app.default_currency) for a in accounts)
27
        cash_flow = -sum(a.entries.cash_flow(date_range, currency=self.app.default_currency) for a in accounts)
28
28
        budgeted_amount = self.document.budgeted_amount_for_target(None, date_range)
29
29
        return cash_flow + budgeted_amount
30
30
    

Up to file-list core/model/account.py:

8
8
9
9
from __future__ import division, unicode_literals
10
10
11
import bisect
12
11
from functools import partial
13
from itertools import takewhile
14
12
15
from hsutil.misc import flatten
16
17
from .amount import convert_amount
13
from .entry import EntryList
18
14
from .sort import sort_string
19
15
from ..exception import DuplicateAccountNameError
20
16
@@ -38,15 +34,12 @@ class Account(object):
38
34
    def __init__(self, name, currency, type):
39
35
        self.name = name
40
36
        self.currency = currency
41
        self.entries = []
42
37
        self.type = type
43
38
        self.reference = None
44
39
        self.group = None
45
40
        self.account_number = ''
46
        self._date2entries = {}
47
        self._sorted_entry_dates = []
48
        # the key for this dict is (date_range, currency)
49
        self._daterange2cashflow = {}
41
        # entries are filled by the Oven
42
        self.entries = EntryList(self)
50
43
        
51
44
    def __repr__(self):
52
45
        return '<Account %r>' % self.name
@@ -62,73 +55,10 @@ class Account(object):
62
55
        # __ne__() are defined for this class too.
63
56
        return cmp(sort_string(self.name), sort_string(other.name))
64
57
    
65
    #--- Private
66
    def _balance(self, balance_attr, date=None, currency=None):
67
        entry = self.last_entry(date) if date else self.last_entry()
68
        if entry:
69
            balance = getattr(entry, balance_attr)
70
            if currency:
71
                return convert_amount(balance, currency, date)
72
            else:
73
                return balance
74
        else:
75
            return 0
76
    
77
    def _cash_flow(self, date_range, currency):
78
        cache = self._date2entries
79
        entries = flatten(cache[date] for date in date_range if date in cache)
80
        entries = (e for e in entries if not getattr(e.transaction, 'is_budget', False))
81
        amounts = (convert_amount(e.amount, currency, e.date) for e in entries)
82
        return sum(amounts)
83
    
84
    def _normalize_amount(self, amount):
58
    #--- Public
59
    def normalize_amount(self, amount):
85
60
        return -amount if self.is_credit_account() else amount
86
61
    
87
    #--- Public
88
    def add_entry(self, entry):
89
        # add_entry() calls must *always* be made in order
90
        self.entries.append(entry)
91
        date = entry.date
92
        try:
93
            self._date2entries[date].append(entry)
94
        except KeyError:
95
            self._date2entries[date] = [entry]
96
        if not self._sorted_entry_dates or self._sorted_entry_dates[-1] < date:
97
            self._sorted_entry_dates.append(date)
98
    
99
    def balance(self, date=None, currency=None):
100
        return self._balance('balance', date, currency=currency)
101
    
102
    def balance_of_reconciled(self, date=None, currency=None):
103
        return self._balance('reconciled_balance', date, currency=currency)
104
    
105
    def balance_with_budget(self, date=None, currency=None):
106
        return self._balance('balance_with_budget', date, currency=currency)
107
    
108
    def cash_flow(self, date_range, currency=None):
109
        currency = currency or self.currency
110
        cache_key = (date_range, currency)
111
        if cache_key not in self._daterange2cashflow:
112
            cash_flow = self._cash_flow(date_range, currency)
113
            self._daterange2cashflow[cache_key] = cash_flow
114
        return self._daterange2cashflow[cache_key]
115
    
116
    def clear(self, from_date):
117
        if from_date is None:
118
            self.entries = []
119
            self._date2entries = {}
120
            self._daterange2cashflow = {}
121
            self._sorted_entry_dates = []
122
        else:
123
            self.entries = list(takewhile(lambda e: e.date < from_date, self.entries))
124
            index = bisect.bisect_left(self._sorted_entry_dates, from_date)
125
            for date in self._sorted_entry_dates[index:]:
126
                del self._date2entries[date]
127
            for date_range, currency in self._daterange2cashflow.keys():
128
                if date_range.end >= from_date:
129
                    del self._daterange2cashflow[(date_range, currency)]
130
            del self._sorted_entry_dates[index:]
131
    
132
62
    def is_balance_sheet_account(self):
133
63
        return self.type in (AccountType.Asset, AccountType.Liability)
134
64
    
@@ -141,31 +71,6 @@ class Account(object):
141
71
    def is_income_statement_account(self):
142
72
        return self.type in (AccountType.Income, AccountType.Expense)
143
73
    
144
    def last_entry(self, date=None):
145
        if self.entries:
146
            if date is None:
147
                return self.entries[-1]
148
            else:
149
                if date not in self._date2entries: # find the nearest smaller date
150
                    index = bisect.bisect_right(self._sorted_entry_dates, date) - 1
151
                    if index < 0:
152
                        return None
153
                    date = self._sorted_entry_dates[index]
154
                return self._date2entries[date][-1]
155
        return None
156
    
157
    def normal_balance(self, date=None, currency=None):
158
        balance = self.balance(date=date, currency=currency)
159
        return self._normalize_amount(balance)
160
    
161
    def normal_balance_of_reconciled(self, date=None, currency=None):
162
        balance = self.balance_of_reconciled(date=date, currency=currency)
163
        return self._normalize_amount(balance)
164
    
165
    def normal_cash_flow(self, date_range, currency=None):
166
        cash_flow = self.cash_flow(date_range, currency)
167
        return self._normalize_amount(cash_flow)
168
    
169
74
    #--- Properties
170
75
    @property
171
76
    def combined_display(self):

Up to file-list core/model/budget.py:

@@ -106,5 +106,5 @@ class BudgetList(list):
106
106
    
107
107
    def normal_amount_for_account(self, account, date_range, currency=None):
108
108
        budgeted_amount = self.amount_for_account(account, date_range, currency)
109
        return account._normalize_amount(budgeted_amount)
109
        return account.normalize_amount(budgeted_amount)
110
110
    

Up to file-list core/model/entry.py:

1
# -*- coding: utf-8 -*-
2
# Created By: Virgil Dupras
3
# Created On: 2010-07-29
4
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
5
# 
6
# This software is licensed under the "HS" License as described in the "LICENSE" file, 
7
# which should be included with this package. The terms are also available at 
8
# http://www.hardcoded.net/licenses/hs_license
9
10
import bisect
11
from collections import Sequence
12
from itertools import takewhile
13
14
from hsutil.misc import flatten
15
from .amount import convert_amount, same_currency
16
17
class Entry(object):
18
    def __init__(self, split, amount, balance, reconciled_balance, balance_with_budget):
19
        self.split = split
20
        self.amount = amount
21
        self.balance = balance
22
        self.reconciled_balance = reconciled_balance
23
        self.balance_with_budget = balance_with_budget
24
    
25
    def __repr__(self):
26
        return '<Entry %r %r>' % (self.date, self.description)
27
    
28
    def change_amount(self, amount):
29
        assert len(self.splits) == 1
30
        self.split.amount = amount
31
        other_split = self.splits[0]
32
        is_mct = False
33
        if not same_currency(amount, other_split.amount):
34
            is_asset = self.account is not None and self.account.is_balance_sheet_account()
35
            other_is_asset = other_split.account is not None and other_split.account.is_balance_sheet_account()
36
            if is_asset and other_is_asset:
37
                is_mct = True
38
        if is_mct: # don't touch other side unless we have a logical imbalance
39
            if self.split.is_on_same_side(other_split):
40
                other_split.amount *= -1
41
        else:
42
            other_split.amount = -amount
43
    
44
    def normal_balance(self):
45
        is_credit = self.account is not None and self.account.is_credit_account()
46
        return -self.balance if is_credit else self.balance
47
    
48
    @property
49
    def account(self):
50
        return self.split.account
51
    
52
    @property
53
    def checkno(self):
54
        return self.transaction.checkno
55
    
56
    @property
57
    def date(self):
58
        return self.transaction.date
59
    
60
    @property
61
    def description(self):
62
        return self.transaction.description
63
    
64
    @property
65
    def payee(self):
66
        return self.transaction.payee
67
    
68
    @property
69
    def mtime(self):
70
        return self.transaction.mtime
71
    
72
    @property
73
    def splits(self):
74
        return [x for x in self.split.transaction.splits if x is not self.split]
75
    
76
    @property
77
    def transaction(self):
78
        return self.split.transaction
79
    
80
    @property
81
    def transfer(self):
82
        return [split.account for split in self.splits if split.account is not None]
83
    
84
    @property
85
    def reconciled(self):
86
        return self.split.reconciled
87
    
88
    @property
89
    def reconciliation_date(self):
90
        return self.split.reconciliation_date
91
    
92
    @property
93
    def reference(self):
94
        return self.split.reference
95
    
96
97
class EntryList(Sequence):
98
    def __init__(self, account):
99
        self.account = account
100
        self._entries = []
101
        self._date2entries = {}
102
        self._sorted_entry_dates = []
103
        # the key for this dict is (date_range, currency)
104
        self._daterange2cashflow = {}
105
    
106
    def __getitem__(self, key):
107
        return self._entries.__getitem__(key)
108
    
109
    def __len__(self):
110
        return len(self._entries)
111
    
112
    #--- Private
113
    def _balance(self, balance_attr, date=None, currency=None):
114
        entry = self.last_entry(date) if date else self.last_entry()
115
        if entry:
116
            balance = getattr(entry, balance_attr)
117
            if currency:
118
                return convert_amount(balance, currency, date)
119
            else:
120
                return balance
121
        else:
122
            return 0
123
    
124
    def _cash_flow(self, date_range, currency):
125
        cache = self._date2entries
126
        entries = flatten(cache[date] for date in date_range if date in cache)
127
        entries = (e for e in entries if not getattr(e.transaction, 'is_budget', False))
128
        amounts = (convert_amount(e.amount, currency, e.date) for e in entries)
129
        return sum(amounts)
130
    
131
    #--- Public
132
    def add_entry(self, entry):
133
        # add_entry() calls must *always* be made in order
134
        self._entries.append(entry)
135
        date = entry.date
136
        try:
137
            self._date2entries[date].append(entry)
138
        except KeyError:
139
            self._date2entries[date] = [entry]
140
        if not self._sorted_entry_dates or self._sorted_entry_dates[-1] < date:
141
            self._sorted_entry_dates.append(date)
142
    
143
    def balance(self, date=None, currency=None):
144
        return self._balance('balance', date, currency=currency)
145
    
146
    def balance_of_reconciled(self, date=None, currency=None):
147
        return self._balance('reconciled_balance', date, currency=currency)
148
    
149
    def balance_with_budget(self, date=None, currency=None):
150
        return self._balance('balance_with_budget', date, currency=currency)
151
    
152
    def cash_flow(self, date_range, currency=None):
153
        currency = currency or self.account.currency
154
        cache_key = (date_range, currency)
155
        if cache_key not in self._daterange2cashflow:
156
            cash_flow = self._cash_flow(date_range, currency)
157
            self._daterange2cashflow[cache_key] = cash_flow
158
        return self._daterange2cashflow[cache_key]
159
    
160
    def clear(self, from_date):
161
        if from_date is None:
162
            self._entries = []
163
            self._date2entries = {}
164
            self._daterange2cashflow = {}
165
            self._sorted_entry_dates = []
166
        else:
167
            self._entries = list(takewhile(lambda e: e.date < from_date, self._entries))
168
            index = bisect.bisect_left(self._sorted_entry_dates, from_date)
169
            for date in self._sorted_entry_dates[index:]:
170
                del self._date2entries[date]
171
            for date_range, currency in self._daterange2cashflow.keys():
172
                if date_range.end >= from_date:
173
                    del self._daterange2cashflow[(date_range, currency)]
174
            del self._sorted_entry_dates[index:]
175
    
176
    def last_entry(self, date=None):
177
        if self._entries:
178
            if date is None:
179
                return self._entries[-1]
180
            else:
181
                if date not in self._date2entries: # find the nearest smaller date
182
                    index = bisect.bisect_right(self._sorted_entry_dates, date) - 1
183
                    if index < 0:
184
                        return None
185
                    date = self._sorted_entry_dates[index]
186
                return self._date2entries[date][-1]
187
        return None
188
    
189
    def normal_balance(self, date=None, currency=None):
190
        balance = self.balance(date=date, currency=currency)
191
        return self.account.normalize_amount(balance)
192
    
193
    def normal_balance_of_reconciled(self, date=None, currency=None):
194
        balance = self.balance_of_reconciled(date=date, currency=currency)
195
        return self.account.normalize_amount(balance)
196
    
197
    def normal_cash_flow(self, date_range, currency=None):
198
        cash_flow = self.cash_flow(date_range, currency)
199
        return self.account.normalize_amount(cash_flow)
200
    

Up to file-list core/model/oven.py:

@@ -14,9 +14,9 @@ from operator import attrgetter
14
14
from hsutil.misc import flatten
15
15
16
16
from .amount import convert_amount
17
from .entry import Entry, EntryList
17
18
from .budget import Budget, BudgetSpawn
18
19
from .date import inc_month
19
from .transaction import Entry
20
20
21
21
class Oven(object):
22
22
    """The Oven takes "raw data" from accounts and transactions and "cooks" calculated data out of
@@ -52,17 +52,18 @@ class Oven(object):
52
52
        account = split.account
53
53
        if account is None:
54
54
            return
55
        entries = account.entries
55
56
        amount = split.amount
56
57
        converted_amount = convert_amount(amount, account.currency, split.transaction.date)
57
        balance = account.balance()
58
        reconciled_balance = account.balance_of_reconciled()
59
        balance_with_budget = account.balance_with_budget()
58
        balance = entries.balance()
59
        reconciled_balance = entries.balance_of_reconciled()
60
        balance_with_budget = entries.balance_with_budget()
60
61
        balance_with_budget += converted_amount
61
62
        if not isinstance(split.transaction, BudgetSpawn):
62
63
            balance += converted_amount
63
64
            if split.reconciled:
64
65
                reconciled_balance += split.amount
65
        account.add_entry(Entry(split, amount, balance, reconciled_balance, balance_with_budget))
66
        entries.add_entry(Entry(split, amount, balance, reconciled_balance, balance_with_budget))
66
67
    
67
68
    def _cook_transaction(self, txn):
68
69
        for split in txn.splits:
@@ -80,7 +81,7 @@ class Oven(object):
80
81
                    If we don't, we might end up in an infinite loop.
81
82
        """
82
83
        for account in self._accounts:
83
            account.clear(from_date)
84
            account.entries.clear(from_date)
84
85
        if from_date is None:
85
86
            self.transactions = []
86
87
        else:

Up to file-list core/model/transaction.py:

@@ -309,83 +309,3 @@ class Split(object):
309
309
    def reconciled(self):
310
310
        return self.reconciliation_date is not None
311
311
    
312
313
class Entry(object):
314
    def __init__(self, split, amount, balance, reconciled_balance, balance_with_budget):
315
        self.split = split
316
        self.amount = amount
317
        self.balance = balance
318
        self.reconciled_balance = reconciled_balance
319
        self.balance_with_budget = balance_with_budget
320
    
321
    def __repr__(self):
322
        return '<Entry %r %r>' % (self.date, self.description)
323
    
324
    def change_amount(self, amount):
325
        assert len(self.splits) == 1
326
        self.split.amount = amount
327
        other_split = self.splits[0]
328
        is_mct = False
329
        if not same_currency(amount, other_split.amount):
330
            is_asset = self.account is not None and self.account.is_balance_sheet_account()
331
            other_is_asset = other_split.account is not None and other_split.account.is_balance_sheet_account()
332
            if is_asset and other_is_asset:
333
                is_mct = True
334
        if is_mct: # don't touch other side unless we have a logical imbalance
335
            if self.split.is_on_same_side(other_split):
336
                other_split.amount *= -1
337
        else:
338
            other_split.amount = -amount
339
    
340
    def normal_balance(self):
341
        is_credit = self.account is not None and self.account.is_credit_account()
342
        return -self.balance if is_credit else self.balance
343
    
344
    @property
345
    def account(self):
346
        return self.split.account
347
    
348
    @property
349
    def checkno(self):
350
        return self.transaction.checkno
351
    
352
    @property
353
    def date(self):
354
        return self.transaction.date
355
    
356
    @property
357
    def description(self):
358
        return self.transaction.description
359
    
360
    @property
361
    def payee(self):
362
        return self.transaction.payee
363
    
364
    @property
365
    def mtime(self):
366
        return self.transaction.mtime
367
    
368
    @property
369
    def splits(self):
370
        return [x for x in self.split.transaction.splits if x is not self.split]
371
    
372
    @property
373
    def transaction(self):
374
        return self.split.transaction
375
    
376
    @property
377
    def transfer(self):
378
        return [split.account for split in self.splits if split.account is not None]
379
    
380
    @property
381
    def reconciled(self):
382
        return self.split.reconciled
383
    
384
    @property
385
    def reconciliation_date(self):
386
        return self.split.reconciliation_date
387
    
388
    @property
389
    def reference(self):
390
        return self.split.reference
391
    

Up to file-list core/saver/qif.py:

@@ -21,7 +21,7 @@ def save(filename, accounts):
21
21
        qif_account_type = 'Oth L' if account.type == AccountType.Liability else 'Bank'
22
22
        lines.append('!Account')
23
23
        lines.append('N%s' % account.name)
24
        lines.append('B%s' % format_amount_for_qif(account.balance()))
24
        lines.append('B%s' % format_amount_for_qif(account.entries.balance()))
25
25
        lines.append('T%s' % qif_account_type)
26
26
        lines.append('^')
27
27
        lines.append('!Type:%s' % qif_account_type)

Up to file-list core/tests/gui/balance_sheet_test.py:

@@ -491,7 +491,6 @@ class MultipleCurrencies(TestCase):
491
491
        self.add_entry('1/1/2007', 'USD entry', increase='50.00')
492
492
        self.add_entry('1/1/2008', 'USD entry', increase='80.00')
493
493
        self.add_entry('31/1/2008', 'USD entry', increase='20.00')
494
        eq_(self.mainwindow.shown_account.balance(), Amount(150, USD))
495
494
        self.add_account('CAD account', currency=CAD, group_name='Group')
496
495
        self.mainwindow.show_account()
497
496
        self.add_entry('1/1/2008', 'USD entry', increase='100.00')

Up to file-list core/tests/model/account_test.py:

5
5
# which should be included with this package. The terms are also available at 
6
6
# http://www.hardcoded.net/licenses/hs_license
7
7
8
import xmlrpclib
9
8
from datetime import date
10
9
11
10
from hscommon.currency import USD, CAD
12
11
13
12
from ..base import TestCase
14
from ...model import currency
15
13
from ...model.account import Account, Group, AccountList, AccountType
16
14
from ...model.amount import Amount
17
15
from ...model.date import MonthRange
18
16
from ...model.oven import Oven
19
from ...model.transaction import Transaction, Entry
17
from ...model.transaction import Transaction
20
18
from ...model.transaction_list import TransactionList
21
19
22
20
class AccountComparison(TestCase):
@@ -76,16 +74,16 @@ class OneAccount(TestCase):
76
74
        oven.cook(date.min, date.max)
77
75
    
78
76
    def test_balance(self):
79
        self.assertEqual(self.account.balance(date(2007, 12, 31)), Amount(20, USD))
77
        self.assertEqual(self.account.entries.balance(date(2007, 12, 31)), Amount(20, USD))
80
78
        
81
79
        # The balance is converted using the rate on the day the balance is
82
80
        # requested.
83
        self.assertEqual(self.account.balance(date(2007, 12, 31), currency=CAD), Amount(20 * 1.1, CAD))
81
        self.assertEqual(self.account.entries.balance(date(2007, 12, 31), currency=CAD), Amount(20 * 1.1, CAD))
84
82
85
83
    def test_cash_flow(self):
86
84
        range = MonthRange(date(2008, 1, 1))
87
        self.assertEqual(self.account.cash_flow(range), Amount(252, USD))
85
        self.assertEqual(self.account.entries.cash_flow(range), Amount(252, USD))
88
86
89
87
        # Each entry is converted using the entry's day rate.
90
        self.assertEqual(self.account.cash_flow(range, CAD), Amount(201.40, CAD))
88
        self.assertEqual(self.account.entries.cash_flow(range, CAD), Amount(201.40, CAD))
91
89