def test_close(self): date = datetime.date(2012, 9, 1) closed_entries, index = self.do_close(self.entries, date, 'NOTHING', 'Equity:Conversions:Current') self.assertEqualEntries( """ 2012-01-01 open Income:Salary 2012-01-01 open Expenses:Taxes 2012-01-01 open Assets:US:Checking 2012-01-01 open Assets:CA:Checking 2012-03-01 * "Some income and expense to be summarized" Income:Salary 10000 USD Expenses:Taxes 3600 USD Assets:US:Checking -13600 USD 2012-03-02 * "Some conversion to be summarized" Assets:US:Checking -5000 USD @ 1.2 CAD Assets:CA:Checking 6000 CAD ;; 2012-06-01 BEGIN -------------------------------- 2012-08-01 * "Some income and expense to show" Income:Salary 11000 USD Expenses:Taxes 3200 USD Assets:US:Checking -14200 USD 2012-08-02 * "Some other conversion to be summarized" Assets:US:Checking -3000 USD @ 1.25 CAD Assets:CA:Checking 3750 CAD ;; 2012-09-01 END -------------------------------- 2012-08-31 C "Conversion for (-8000 USD, 9750 CAD)" Equity:Conversions:Current 8000 USD @ 0 NOTHING Equity:Conversions:Current -9750 CAD @ 0 NOTHING """, closed_entries) # Check the index is correctly beginning after the list of summarizing entries. self.assertEqual(8, index) # Check that our original example list of entries does not balance. input_balance = interpolate.compute_entries_balance(self.entries) self.assertFalse(input_balance.is_empty()) # Check that the truncated entries does not balance. balances = interpolate.compute_entries_balance(closed_entries[:index]) self.assertFalse(balances.is_empty()) # Check that the closed entries add up to precisely zero. balances = interpolate.compute_entries_balance(closed_entries) self.assertTrue(balances.is_empty())
def test_compute_entries_balance_currencies(self, entries, _, __): """ 2014-01-01 open Assets:Bank:Checking 2014-01-01 open Assets:Bank:Savings 2014-01-01 open Assets:Investing 2014-01-01 open Assets:Other 2014-06-01 * Assets:Bank:Checking 111.23 USD Assets:Other 2014-06-02 * Assets:Bank:Savings 222.74 USD Assets:Other 2014-06-03 * Assets:Bank:Savings 17.23 CAD Assets:Other 2014-06-04 * Assets:Investing 10000 EUR Assets:Other """ computed_balance = interpolate.compute_entries_balance(entries) expected_balance = inventory.Inventory() self.assertEqual(expected_balance, computed_balance)
def test_clear(self): date = datetime.date(2013, 1, 1) clear_entries, index = self.do_clear(self.entries, date, self.account_types, 'Equity:Earnings:Current') self.assertEqualEntries( """ 2012-01-01 open Income:Salary 2012-01-01 open Expenses:Taxes 2012-01-01 open Assets:US:Checking 2012-01-01 open Assets:CA:Checking 2012-03-01 * "Some income and expense to be summarized" Income:Salary 10000 USD Expenses:Taxes 3600 USD Assets:US:Checking -13600 USD 2012-03-02 * "Some conversion to be summarized" Assets:US:Checking -5000 USD @ 1.2 CAD Assets:CA:Checking 6000 CAD ;; 2012-06-01 BEGIN -------------------------------- 2012-08-01 * "Some income and expense to show" Income:Salary 11000 USD Expenses:Taxes 3200 USD Assets:US:Checking -14200 USD 2012-08-02 * "Some other conversion to be summarized" Assets:US:Checking -3000 USD @ 1.25 CAD Assets:CA:Checking 3750 CAD ;; 2012-09-01 END -------------------------------- 2012-11-01 * "Some income and expense to be truncated" Income:Salary 10000 USD Expenses:Taxes 3600 USD Assets:US:Checking -13600 USD 2012-12-31 T "Transfer balance for 'Expenses:Taxes' (Transfer balance)" Expenses:Taxes -10400 USD Equity:Earnings:Current 10400 USD 2012-12-31 T "Transfer balance for 'Income:Salary' (Transfer balance)" Income:Salary -31000 USD Equity:Earnings:Current 31000 USD """, clear_entries) # Check the index is correctly beginning after the list of summarizing entries. self.assertEqual(9, index) # Check that the cleared entries do not necessarily up to precisely zero # without closing. balances = interpolate.compute_entries_balance(clear_entries) self.assertFalse(balances.is_empty())
def test_conversions__not_needed(self): date = datetime.date(2012, 3, 2) conversion_entries = summarize.conversions(self.entries, self.ACCOUNT, 'NOTHING', date) self.assertEqualEntries(self.entries, conversion_entries) converted_balance = interpolate.compute_entries_balance( conversion_entries, date=date) self.assertTrue(converted_balance.cost().is_empty())
def conversions(entries, conversion_account, conversion_currency, date=None): """Insert a conversion entry at date 'date' at the given account. Args: entries: A list of entries. conversion_account: A string, the account to book against. conversion_currency: A string, the transfer currency to use for zero prices on the conversion entry. date: The date before which to insert the conversion entry. The new entry will be inserted as the last entry of the date just previous to this date. Returns: A modified list of entries. """ # Compute the balance at the given date. conversion_balance = interpolate.compute_entries_balance(entries, date=date) # Early exit if there is nothing to do. conversion_cost_balance = conversion_balance.reduce(convert.get_cost) if conversion_cost_balance.is_empty(): return entries # Calculate the index and the date for the new entry. We want to store it as # the last transaction of the day before. if date is not None: index = bisect_key.bisect_left_with_key(entries, date, key=lambda entry: entry.date) last_date = date - datetime.timedelta(days=1) else: index = len(entries) last_date = entries[-1].date meta = data.new_metadata('<conversions>', -1) narration = 'Conversion for {}'.format(conversion_balance) conversion_entry = Transaction(meta, last_date, flags.FLAG_CONVERSIONS, None, narration, data.EMPTY_SET, data.EMPTY_SET, []) for position in conversion_cost_balance.get_positions(): # Important note: Set the cost to zero here to maintain the balance # invariant. (This is the only single place we cheat on the balance rule # in the entire system and this is necessary; see documentation on # Conversions.) price = amount.Amount(ZERO, conversion_currency) neg_pos = -position conversion_entry.postings.append( data.Posting(conversion_account, neg_pos.units, neg_pos.cost, price, None, None)) # Make a copy of the list of entries and insert the new transaction into it. new_entries = list(entries) new_entries.insert(index, conversion_entry) return new_entries
def test_conversions__no_date(self): conversion_entries = summarize.conversions(self.entries, self.ACCOUNT, 'NOTHING') self.assertIncludesEntries(self.entries, conversion_entries) self.assertIncludesEntries( """ 2012-05-01 C "Conversion for (-700.00 USD, 100.00 CAD, 60 NT {10 CAD})" Equity:Conversions 700.00 USD @ 0 NOTHING Equity:Conversions -700.00 CAD @ 0 NOTHING """, conversion_entries) converted_balance = interpolate.compute_entries_balance( conversion_entries) self.assertTrue(converted_balance.cost().is_empty())
def test_conversions__with_transactions_at_cost(self): date = datetime.date(2012, 3, 10) conversion_entries = summarize.conversions(self.entries, self.ACCOUNT, 'XFER', date) self.assertIncludesEntries(self.entries, conversion_entries) self.assertIncludesEntries( """ 2012-03-09 C "Conversion for (-800.00 USD, 200.00 CAD, 60 NT {10 CAD})" Equity:Conversions 800.00 USD @ 0 XFER Equity:Conversions -800.00 CAD @ 0 XFER """, conversion_entries) converted_balance = interpolate.compute_entries_balance( conversion_entries, date=date) self.assertTrue(converted_balance.cost().is_empty())
def test_conversions__needed_middle(self): date = datetime.date(2012, 3, 3) conversion_entries = summarize.conversions(self.entries, self.ACCOUNT, 'NOTHING', date) self.assertIncludesEntries(self.entries, conversion_entries) self.assertIncludesEntries( """ 2012-03-02 C "Conversion for (-800.00 USD, 800.00 CAD)" Equity:Conversions 800.00 USD @ 0 NOTHING Equity:Conversions -800.00 CAD @ 0 NOTHING """, conversion_entries) converted_balance = interpolate.compute_entries_balance( conversion_entries, date=date) self.assertTrue(converted_balance.cost().is_empty())
def test_compute_entries_balance_conversions(self, entries, _, __): """ 2014-01-01 open Assets:Investing 2014-01-01 open Assets:Other 2014-06-06 * Assets:Investing 1000 EUR @ 1.78 GBP Assets:Other 2014-06-07 * Assets:Investing 1000 EUR @@ 1780 GBP Assets:Other """ computed_balance = interpolate.compute_entries_balance(entries) expected_balance = inventory.Inventory() expected_balance.add_amount(A('2000.00 EUR')) expected_balance.add_amount(A('-3560.00 GBP')) self.assertEqual(expected_balance, computed_balance)
def test_compute_entries_balance_at_cost(self, entries, _, __): """ 2014-01-01 open Assets:Investing 2014-01-01 open Assets:Other 2014-06-05 * Assets:Investing 30 HOOL {40 USD} Assets:Other 2014-06-05 * Assets:Investing -20 HOOL {40 USD} Assets:Other """ computed_balance = interpolate.compute_entries_balance(entries) expected_balance = inventory.Inventory() expected_balance.add_amount(A('-400 USD')) expected_balance.add_amount(A('10 HOOL'), A('40 USD')) self.assertEqual(expected_balance, computed_balance)
def test_open(self): date = datetime.date(2012, 6, 1) opened_entries, index = self.do_open(self.entries, date, self.account_types, 'NOTHING', 'Equity:Earnings:Previous', 'Equity:Opening-Balances', 'Equity:Conversions:Previous') self.assertEqualEntries( """ 2012-01-01 open Income:Salary 2012-01-01 open Expenses:Taxes 2012-01-01 open Assets:US:Checking 2012-01-01 open Assets:CA:Checking 2012-05-31 S "Opening balance for 'Assets:CA:Checking' (Summarization)" Assets:CA:Checking 6,000 CAD Equity:Opening-Balances -6,000 CAD 2012-05-31 S "Opening balance for 'Assets:US:Checking' (Summarization)" Assets:US:Checking -18,600 USD Equity:Opening-Balances 18,600 USD 2012-05-31 S "Opening balance for 'Equity:Earnings:Previous' (Summarization)" Equity:Earnings:Previous 13,600 USD Equity:Opening-Balances -13,600 USD 2012-05-31 S "Opening balance for 'Equity:Conversions:Previous' (Summarization)" Equity:Conversions:Previous 5,000 USD Equity:Opening-Balances -5,000 USD Equity:Conversions:Previous -6,000 CAD Equity:Opening-Balances 6,000 CAD ;; 2012-06-01 BEGIN -------------------------------- 2012-08-01 * "Some income and expense to show" Income:Salary 11,000 USD Expenses:Taxes 3,200 USD Assets:US:Checking -14,200 USD 2012-08-02 * "Some other conversion to be summarized" Assets:US:Checking -3,000 USD @ 1.25 CAD ; -3,750 CAD Assets:CA:Checking 3,750 CAD ; 3,750 CAD 2012-11-01 * "Some income and expense to be truncated" Income:Salary 10,000 USD Expenses:Taxes 3,600 USD Assets:US:Checking -13,600 USD """, opened_entries) # Check the index is correctly beginning after the list of summarizing entries. self.assertEqual(8, index) # Check that our original example list of entries does not balance. input_balance = interpolate.compute_entries_balance(self.entries) self.assertFalse(input_balance.is_empty()) # Check that the summarized entries add up to precisely zero. summarized_entries = opened_entries[:index] balances = interpolate.compute_entries_balance(summarized_entries) self.assertTrue(balances.is_empty()) # Check further conversions aren't accounted for (the close operation # takes care of this). opened_balance = interpolate.compute_entries_balance(opened_entries) self.assertFalse(opened_balance.is_empty())
def test_clamp(self, entries, errors, options_map): """ 2012-01-01 open Income:Salary 2012-01-01 open Expenses:Taxes 2012-01-01 open Assets:US:Checking 2012-01-01 open Assets:CA:Checking 2012-03-01 * "Some income and expense to be summarized" Income:Salary 10000.00 USD Expenses:Taxes 3600.00 USD Assets:US:Checking -13600.00 USD 2012-03-02 * "Some conversion to be summarized" Assets:US:Checking -5000.00 USD @ 1.2 CAD Assets:CA:Checking 6000.00 CAD ;; 2012-06-01 BEGIN -------------------------------- 2012-08-01 * "Some income and expense to show" Income:Salary 11000.00 USD Expenses:Taxes 3200.00 USD Assets:US:Checking -14200.00 USD 2012-08-02 * "Some other conversion to be summarized" Assets:US:Checking -3000.00 USD @ 1.25 CAD Assets:CA:Checking 3750.00 CAD ;; 2012-09-01 END -------------------------------- 2012-11-01 * "Some income and expense to be truncated" Income:Salary 10000.00 USD Expenses:Taxes 3600.00 USD Assets:US:Checking -13600.00 USD """ self.assertFalse(errors) begin_date = datetime.date(2012, 6, 1) end_date = datetime.date(2012, 9, 1) account_types = options.get_account_types(options_map) clamped_entries, index = summarize.clamp(entries, begin_date, end_date, account_types, 'NOTHING', 'Equity:Earnings', 'Equity:Opening-Balances', 'Equity:Conversions') self.assertEqualEntries( """ 2012-01-01 open Income:Salary 2012-01-01 open Expenses:Taxes 2012-01-01 open Assets:US:Checking 2012-01-01 open Assets:CA:Checking 2012-05-31 S "Opening balance for 'Assets:CA:Checking' (Summarization)" Assets:CA:Checking 6000.00 CAD Equity:Opening-Balances -6000.00 CAD 2012-05-31 S "Opening balance for 'Assets:US:Checking' (Summarization)" Assets:US:Checking -18600.00 USD Equity:Opening-Balances 18600.00 USD 2012-05-31 S "Opening balance for 'Equity:Earnings' (Summarization)" Equity:Earnings 13600.00 USD Equity:Opening-Balances -13600.00 USD ;; 2012-06-01 BEGIN -------------------------------- 2012-08-01 * "Some income and expense to show" Income:Salary 11000.00 USD Expenses:Taxes 3200.00 USD Assets:US:Checking -14200.00 USD 2012-08-02 * "Some other conversion to be summarized" Assets:US:Checking -3000.00 USD @ 1.25 CAD Assets:CA:Checking 3750.00 CAD ;; 2012-09-01 END -------------------------------- 2012-08-31 C "Conversion for (-3000.00 USD, 3750.00 CAD)" Equity:Conversions 3000.00 USD @ 0 NOTHING Equity:Conversions -3750.00 CAD @ 0 NOTHING """, clamped_entries) self.assertEqual(7, index) input_balance = interpolate.compute_entries_balance(entries) self.assertFalse(input_balance.is_empty()) clamped_balance = interpolate.compute_entries_balance(clamped_entries) self.assertTrue(clamped_balance.is_empty())
def inventory(self, account_name): return compute_entries_balance(self.entries, prefix=account_name)
def inventory(self, account_name): return compute_entries_balance(self.entries, prefix=account_name)