def get_postings_table( entries: data.Entries, options_map: Dict, accounts_map: Dict[str, data.Open], threshold: Decimal = D('0.01')) -> Table: """Enumerate all the postings.""" header = [ 'account', 'account_abbrev', 'number', 'currency', 'cost_number', 'cost_currency', 'cost_date' ] balances, _ = summarize.balance_by_account(entries, compress_unbooked=True) acctypes = options.get_account_types(options_map) rows = [] for acc, balance in sorted(balances.items()): # Keep only the balance sheet accounts. acctype = account_types.get_account_type(acc) if not acctype in (acctypes.assets, acctypes.liabilities): continue # Create a posting for each of the positions. for pos in balance: acc_abbrev = abbreviate_account(acc, accounts_map) row = [ acc, acc_abbrev, pos.units.number, pos.units.currency, pos.cost.number if pos.cost else ONE, pos.cost.currency if pos.cost else pos.units.currency, pos.cost.date if pos.cost else None ] rows.append(row) return Table(header, rows)
def test_change_commodity_subaccount(self, entries, _, options_map): """ 2014-01-01 open Assets:Account1 2014-01-01 open Income:Misc 2014-01-01 open Income:Pnl 2014-01-15 * Income:Misc -1000 USD Assets:Account1 10 HOUSE {100 USD} 2014-01-15 price HOUSE 100 USD 2014-01-20 price HOUSE 125 USD 2014-02-05 price HOUSE 150 USD 2014-02-15 * Assets:Account1 -10 HOUSE {100 USD} Assets:Account1 30 CAR {50 USD} Income:Pnl -500 USD 2014-02-15 price CAR 50 USD 2014-02-20 price CAR 100 USD """ new_entries, _ = unrealized_periodic.add_unrealized_gains( entries, options_map, subaccount='Unrealized') unreal_entries = unrealized_periodic.get_unrealized_entries( new_entries) self.assertEqual(3, len(unreal_entries)) self.assertEqual(2, len(unreal_entries[-2].postings) ) # The second to last transaction zeroed the account self.assertEqual( 2, len(unreal_entries[-1].postings )) # The last transaction added the new unrealized gain summary = summarize.balance_by_account(unreal_entries) self.assertEqual(summary[0]['Assets:Account1:Unrealized'], inventory.Inventory.from_string("1500 USD"))
def test_balance_by_account__middle(self): # Test in the middle. balances, index = summarize.balance_by_account( self.entries, datetime.date(2014, 2, 10)) self.assertEqual(4, index) self.assertEqual( { 'Assets:AccountA': inventory.from_string('10 USD'), 'Equity:Opening-Balances': inventory.from_string('-10 USD'), }, balances)
def test_balance_by_account__no_end_date(self): # Test with no end date. balances, index = summarize.balance_by_account(self.entries) self.assertEqual(len(self.entries), index) self.assertEqual( { 'Assets:AccountA': inventory.from_string('11 USD'), 'Equity:Opening-Balances': inventory.from_string('-23 USD'), 'Assets:AccountB': inventory.from_string('12 USD') }, balances)
def get_postings_table(entries: data.Entries, options_map: Dict, accounts_map: Dict[str, data.Open], threshold: Decimal = D('0.01')) -> Table: """Enumerate all the postings.""" header = ['account', 'account_abbrev', 'number', 'currency', 'cost_number', 'cost_currency', 'cost_date'] balances, _ = summarize.balance_by_account(entries) acctypes = options.get_account_types(options_map) rows = [] for acc, balance in sorted(balances.items()): # Keep only the balance sheet accounts. acctype = account_types.get_account_type(acc) if not acctype in (acctypes.assets, acctypes.liabilities): continue # If the account has "NONE" booking method, merge all its postings # together in order to obtain an accurate cost basis and balance of # units. # # (This is a complex issue.) If you accrued positions without having them # booked properly against existing cost bases, you have not properly accounted # for the profit/loss to other postings. This means that the resulting # profit/loss is merged in the cost basis of the positive and negative # postings. dopen = accounts_map.get(acc, None) if dopen is not None and dopen.booking is data.Booking.NONE: average_balance = balance.average() balance = inventory.Inventory(pos for pos in average_balance if pos.units.number >= threshold) # Create a posting for each of the positions. for pos in balance: acc_abbrev = abbreviate_account(acc, accounts_map) row = [acc, acc_abbrev, pos.units.number, pos.units.currency, pos.cost.number if pos.cost else ONE, pos.cost.currency if pos.cost else pos.units.currency, pos.cost.date if pos.cost else None] rows.append(row) return Table(header, rows)
def find_balance_currencies(entries, date=None): """Return currencies relevant for the given date. This computes the account balances as of the date, and returns the union of: a) The currencies held at cost, and b) Currency pairs from previous conversions, but only for currencies with non-zero balances. This is intended to produce the list of currencies whose prices are relevant at a particular date, based on previous history. Args: entries: A list of directives. date: A datetime.date instance. Returns: A set of (base, quote) currencies. """ # Compute the balances. currencies = set() currencies_on_books = set() balances, _ = summarize.balance_by_account(entries, date) for _, balance in balances.items(): for pos in balance: if pos.cost is not None: # Add currencies held at cost. currencies.add((pos.units.currency, pos.cost.currency)) else: # Add regular currencies. currencies_on_books.add(pos.units.currency) # Create currency pairs from the currencies which are on account balances. # In order to figure out the quote currencies, we use the list of price # conversions until this date. converted = (find_currencies_converted(entries, date) | find_currencies_priced(entries, date)) for cbase in currencies_on_books: for base_quote in converted: base, quote = base_quote if base == cbase: currencies.add(base_quote) return currencies
def get_balance_sheet_balances(entries, options_map): """Enumerate all the assets and liabilities. Args: entries: A list of directives, as per the loader. options_map: An options map, as per the parser. Yields: Instances of Posting. """ balances, _ = summarize.balance_by_account(entries) date = entries[-1].date acctypes = options.get_account_types(options_map) for account, balance in sorted(balances.items()): # Keep only the balance sheet accounts. acctype = account_types.get_account_type(account) if not acctype in (acctypes.assets, acctypes.liabilities): continue # Create a posting for each of the positions. for position in balance: yield data.Posting(account, position.units, position.cost, None, None, None)
def main(): logging.basicConfig(level=logging.INFO, format='%(levelname)-8s: %(message)s') parser = argparse.ArgumentParser(description=__doc__.strip()) parser.add_argument('filename', help='Beancount input filename') args = parser.parse_args() entries, errors, options_map = loader.load_file(args.filename) root_account = realization.realize(entries) balances, _ = summarize.balance_by_account(entries, compress_unbooked=True) realization_account_names = { real_account.account for real_account in realization.iter_children(root_account) if not real_account.balance.is_empty()} summarize_account_names = { account for account, balance in balances.items() if not balance.is_empty()} if (realization_account_names - summarize_account_names): pprint.pprint(realization_account_names - summarize_account_names) if (summarize_account_names - realization_account_names): pprint.pprint(summarize_account_names - realization_account_names) for real_account in sorted(list(realization.iter_children(root_account)), key=lambda ra: ra.account): summarize_balance = balances.get(real_account.account, inventory.Inventory()) if summarize_balance == real_account.balance: continue print(real_account.account) print(" realization") for pos in real_account.balance: print(" {}".format(pos)) print(" summarization") for pos in summarize_balance: print(" {}".format(pos)) print()
def test_balance_by_account__first_date(self): # Test on the first date (should be empty). balances, index = summarize.balance_by_account( self.entries, datetime.date(2014, 2, 1)) self.assertEqual(3, index) self.assertEqual({}, balances)