def cap(entries, account_types, conversion_currency, account_earnings, account_conversions): """Transfer net income to equity and insert a final conversion entry. This is used to move and nullify balances from the income and expense accounts to an equity account in order to draw up a balance sheet with a balance of precisely zero. Args: entries: A list of directives. account_types: An instance of AccountTypes. conversion_currency: A string, the transfer currency to use for zero prices on the conversion entry. account_earnings: A string, the name of the equity account to transfer final balances of the income and expense accounts to. account_conversions: A string, the name of the equity account to use as the source for currency conversions. Returns: A modified list of entries, with the income and expense accounts transferred. """ # Transfer the balances of income and expense accounts as earnings / net # income. income_statement_account_pred = ( lambda account: is_income_statement_account(account, account_types)) entries = transfer_balances(entries, None, income_statement_account_pred, account_earnings) # Insert final conversion entries. entries = conversions(entries, account_conversions, conversion_currency, None) return entries
def clear(entries, date, account_types, account_earnings): """Transfer income and expenses balances at the given date to the equity accounts. This method insert entries to zero out balances on income and expenses accounts by transfering them to an equity account. Args: entries: A list of directive tuples. date: A datetime.date instance, one day beyond the end of the period. This date can be optionally left to None in order to close at the end of the list of entries. account_types: An instance of AccountTypes. account_earnings: A string, the name of the account to transfer previous earnings from the income statement accounts to the balance sheet. Returns: A new list of entries is returned, and the index that points to one before the last original transaction before the transfers. """ index = len(entries) # Transfer income and expenses before the period to equity. income_statement_account_pred = ( lambda account: is_income_statement_account(account, account_types)) new_entries = transfer_balances(entries, date, income_statement_account_pred, account_earnings) return new_entries, index
def clamp(entries, begin_date, end_date, account_types, conversion_currency, account_earnings, account_opening, account_conversions): """Filter entries to include only those during a specified time period. Firstly, this method will transfer all balances for the income and expense accounts occurring before the given period begin date to the 'account_earnings' account (earnings before the period, or "retained earnings") and summarize all of the transactions before that date against the 'account_opening' account (usually "opening balances"). The resulting income and expense accounts should have no transactions (since their balances have been transferred out and summarization of zero balances should not add any transactions). Secondly, all the entries after the period end date will be truncated and a conversion entry will be added for the resulting transactions that reflect changes occurring between the beginning and end of the exercise period. The resulting balance of all account should be empty. Args: entries: A list of directive tuples. begin_date: A datetime.date instance, the beginning of the period. end_date: A datetime.date instance, one day beyond the end of the period. account_types: An instance of AccountTypes. conversion_currency: A string, the transfer currency to use for zero prices on the conversion entry. account_earnings: A string, the name of the account to transfer previous earnings from the income statement accounts to the balance sheet. account_opening: A string, the name of the account in equity to transfer previous balances from, in order to initialize account balances at the beginning of the period. This is typically called an opening balances account. account_conversions: A string, tne name of the equity account to book currency conversions against. Returns: A new list of entries is returned, and the index that points to the first original transaction after the beginning date of the period. This index can be used to generate the opening balances report, which is a balance sheet fed with only the summarized entries. """ # Transfer income and expenses before the period to equity. income_statement_account_pred = ( lambda account: is_income_statement_account(account, account_types)) entries = transfer_balances(entries, begin_date, income_statement_account_pred, account_earnings) # Summarize all the previous balances, after transferring the income and # expense balances, so all entries for those accounts before the begin date # should now disappear. entries, index = summarize(entries, begin_date, account_opening) # Truncate the entries after this. entries = truncate(entries, end_date) # Insert conversion entries. entries = conversions(entries, account_conversions, conversion_currency, end_date) return entries, index
def render_mini_balances(entries, options_map, conversion=None, price_map=None): """Render a treeified list of the balances for the given transactions. Args: entries: A list of selected transactions to render. options_map: The parsed options. conversion: Conversion method string, None, 'value' or 'cost'. price_map: A price map from the original entries. If this isn't provided, the inventories are rendered directly. If it is, their contents are converted to market value. """ # Render linked entries (in date order) as errors (for Emacs). errors = [RenderError(entry.meta, '', entry) for entry in entries] printer.print_errors(errors) # Print out balances. real_root = realization.realize(entries) dformat = options_map['dcontext'].build(alignment=Align.DOT, reserved=2) # TODO(blais): I always want to be able to convert at cost. We need # arguments capability. # # TODO(blais): Ideally this conversion inserts a new transactions to # 'Unrealized' to account for the difference between cost and market value. # Insert one and update the realization. Add an update() method to the # realization, given a transaction. acctypes = options.get_account_types(options_map) if conversion == 'value': assert price_map is not None # Warning: Mutate the inventories in-place, converting them to market # value. balance_diff = inventory.Inventory() for real_account in realization.iter_children(real_root): balance_cost = real_account.balance.reduce(convert.get_cost) balance_value = real_account.balance.reduce(convert.get_value, price_map) real_account.balance = balance_value balance_diff.add_inventory(balance_cost) balance_diff.add_inventory(-balance_value) if not balance_diff.is_empty(): account_unrealized = account.join(acctypes.income, options_map["account_unrealized_gains"]) unrealized = realization.get_or_create(real_root, account_unrealized) unrealized.balance.add_inventory(balance_diff) elif conversion == 'cost': for real_account in realization.iter_children(real_root): real_account.balance = real_account.balance.reduce(convert.get_cost) realization.dump_balances(real_root, dformat, file=sys.stdout) # Print out net income change. net_income = inventory.Inventory() for real_node in realization.iter_children(real_root): if account_types.is_income_statement_account(real_node.account, acctypes): net_income.add_inventory(real_node.balance) print() print('Net Income: {}'.format(-net_income))
def do_linked(filename, args): """Print out a list of transactions linked to the one at the given line. Args: filename: A string, which consists in the filename. args: A tuple of the rest of arguments. We're expecting the first argument to be an integer as a string. """ from beancount.parser import options from beancount.parser import printer from beancount.core import account_types from beancount.core import inventory from beancount.core import data from beancount.core import realization from beancount import loader # Parse the arguments, get the line number. if len(args) != 1: raise SystemExit("Missing line number argument.") lineno = int(args[0]) # Load the input file. entries, errors, options_map = loader.load_file(filename) # Find the closest entry. closest_entry = data.find_closest(entries, options_map['filename'], lineno) # Find its links. links = closest_entry.links if closest_entry is None: raise SystemExit("No entry could be found before {}:{}".format( filename, lineno)) if not links: return # Find all linked entries. linked_entries = [ entry for entry in entries if (isinstance(entry, data.Transaction) and entry.links and entry.links & links) ] # Render linked entries (in date order) as errors (for Emacs). errors = [RenderError(entry.meta, '', entry) for entry in linked_entries] printer.print_errors(errors) # Print out balances. real_root = realization.realize(linked_entries) realization.dump_balances(real_root, file=sys.stdout) # Print out net income change. acctypes = options.get_account_types(options_map) net_income = inventory.Inventory() for real_node in realization.iter_children(real_root): if account_types.is_income_statement_account(real_node.account, acctypes): net_income.add_inventory(real_node.balance) print() print('Net Income: {}'.format(-net_income))
def test_is_account_categories(self): for account_name, expected in [ ("Assets:US:RBS:Savings", True), ("Liabilities:US:RBS:MortgageLoan", True), ("Equity:Opening-Balances", True), ("Income:US:ETrade:Dividends", False), ("Expenses:Toys:Computer", False), ]: self.assertEqual( expected, account_types.is_balance_sheet_account( account_name, account_types.DEFAULT_ACCOUNT_TYPES)) self.assertEqual( not expected, account_types.is_income_statement_account( account_name, account_types.DEFAULT_ACCOUNT_TYPES))
def do_linked(filename, args): """Print out a list of transactions linked to the one at the given line. Args: filename: A string, which consists in the filename. args: A tuple of the rest of arguments. We're expecting the first argument to be an integer as a string. """ from beancount.parser import options from beancount.parser import printer from beancount.core import account_types from beancount.core import inventory from beancount.core import data from beancount.core import realization from beancount import loader # Parse the arguments, get the line number. if len(args) != 1: raise SystemExit("Missing line number argument.") lineno = int(args[0]) # Load the input file. entries, errors, options_map = loader.load_file(filename) # Find the closest entry. closest_entry = data.find_closest(entries, options_map['filename'], lineno) # Find its links. if closest_entry is None: raise SystemExit("No entry could be found before {}:{}".format( filename, lineno)) links = (closest_entry.links if isinstance(closest_entry, data.Transaction) else data.EMPTY_SET) if not links: linked_entries = [closest_entry] else: # Find all linked entries. # # Note that there is an option here: You can either just look at the links # on the closest entry, or you can include the links of the linked # transactions as well. Whichever one you want depends on how you use your # links. Best would be to query the user (in Emacs) when there are many # links present. follow_links = True if not follow_links: linked_entries = [ entry for entry in entries if (isinstance(entry, data.Transaction) and entry.links and entry.links & links) ] else: links = set(links) linked_entries = [] while True: num_linked = len(linked_entries) linked_entries = [ entry for entry in entries if (isinstance(entry, data.Transaction) and entry.links and entry.links & links) ] if len(linked_entries) == num_linked: break for entry in linked_entries: if entry.links: links.update(entry.links) # Render linked entries (in date order) as errors (for Emacs). errors = [RenderError(entry.meta, '', entry) for entry in linked_entries] printer.print_errors(errors) # Print out balances. real_root = realization.realize(linked_entries) dformat = options_map['dcontext'].build( alignment=display_context.Align.DOT, reserved=2) realization.dump_balances(real_root, dformat, file=sys.stdout) # Print out net income change. acctypes = options.get_account_types(options_map) net_income = inventory.Inventory() for real_node in realization.iter_children(real_root): if account_types.is_income_statement_account(real_node.account, acctypes): net_income.add_inventory(real_node.balance) print() print('Net Income: {}'.format(-net_income))
def do_linked(filename, args): """Print out a list of transactions linked to the one at the given line. Args: filename: A string, which consists in the filename. args: A tuple of the rest of arguments. We're expecting the first argument to be a string which contains either a lineno integer or a filename:lineno combination (which can be used if the location is not in the top-level file). """ from beancount.parser import options from beancount.parser import printer from beancount.core import account_types from beancount.core import inventory from beancount.core import data from beancount.core import realization from beancount import loader # Parse the arguments, get the line number. if len(args) != 1: raise SystemExit("Missing line number or link argument.") location_spec = args[0] # Load the input file. entries, errors, options_map = loader.load_file(filename) # Accept an explicit link name as the location. Must include the '^' # character. if re.match(r"\^(.*)$", location_spec): search_filename = options_map['filename'] links = {location_spec[1:]} linked_entries = find_linked_entries(entries, links, False) else: # Parse the argument as a line number or a "<filename>:<lineno>" spec to # pull context from. match = re.match(r"(.+):(\d+)$", location_spec) if match: search_filename = path.abspath(match.group(1)) lineno = int(match.group(2)) elif re.match(r"(\d+)$", location_spec): # Parse the argument as just a line number to pull context from on # the main filename. search_filename = options_map['filename'] lineno = int(location_spec) else: raise SystemExit( "Invalid line number or link format for location.") # Find the closest entry. closest_entry = data.find_closest(entries, search_filename, lineno) # Find its links. if closest_entry is None: raise SystemExit("No entry could be found before {}:{}".format( search_filename, lineno)) links = (closest_entry.links if isinstance( closest_entry, data.Transaction) else data.EMPTY_SET) # Get the linked entries, or just the closest one, if no links. linked_entries = (find_linked_entries(entries, links, True) if links else [closest_entry]) # Render linked entries (in date order) as errors (for Emacs). errors = [RenderError(entry.meta, '', entry) for entry in linked_entries] printer.print_errors(errors) # Print out balances. real_root = realization.realize(linked_entries) dformat = options_map['dcontext'].build( alignment=display_context.Align.DOT, reserved=2) realization.dump_balances(real_root, dformat, file=sys.stdout) # Print out net income change. acctypes = options.get_account_types(options_map) net_income = inventory.Inventory() for real_node in realization.iter_children(real_root): if account_types.is_income_statement_account(real_node.account, acctypes): net_income.add_inventory(real_node.balance) print() print('Net Income: {}'.format(-net_income))