def category_key_fetcher(data_key): """ Find the category that the account name belongs to. :param data_key: transaction split :return: string """ return get_category_for_account(clean_account_name(data_key.account.fullname))
def parse_walker_parameters(definition): """ convert the incoming definition into a kwargs that can be used for the account walker. :param definition: dictionary, list, or string containing account definitions. :return: dictionary containing: accounts - list of accounts to walk through ignores - list of accounts to ignore while walking through the accounts place_holders - should place holder accounts be processed recursive - should the children accounts be processed """ return_value = { 'ignores': None, 'place_holders': False, 'recursive': True, 'name': None } # Allow for a none definition to be provided and overwrite to an empty list if definition is None: definition = [] if isinstance(definition, dict): # If the definition has a defined category, then this will override all of the other parameters. Category # definitions have already been processed account walkers, and therefore should not contain place_holders # or recursive values. if 'category' in definition: definition.update({ 'ignores': None, 'place_holders': False, 'recursive': False, 'accounts': get_accounts_for_category(definition['category']), }) return_value.update(definition) elif isinstance(definition, list) or isinstance(definition, set): return_value.update(accounts=list(definition)) else: return_value.update(accounts=[definition]) # Set a name value for the account walker parameters to a default which is the leaf name of the first account if return_value['name'] is None: if return_value['accounts']: return_value['name'] = clean_account_name( return_value['accounts'][0]).split('.')[-1] else: return_value['name'] = 'None' return return_value
def get_category_for_account(account_name): """ Look up the category for a given account. :param account_name: full account name string. :return: category name. """ # Translate the account name to use the common formatting. account_name = clean_account_name(account_name) value = _reverse.get(account_name, _default_category) if value == _default_category: print 'Returning: %s -> %s' % (account_name, _default_category) return value
def account_walker(accounts, ignores=None, place_holders=False, recursive=True, **kwargs): """ Generator method that will recursively walk the list of accounts provided, ignoring the accounts that are in the ignore list. :param accounts: list of accounts to start processing :param ignores: any accounts that should be ignored :param recursive: walk through the children of the accounts in the list :param place_holders: return place holder accounts """ if not ignores: ignores = [] # Allow for a none account list to be provided if accounts is None: accounts = [] _account_list = [a for a in accounts] ignores = [clean_account_name(account_name) for account_name in ignores] while _account_list: account_name = _account_list.pop() if account_name in ignores: continue account = get_account(account_name) if not account.placeholder or place_holders: yield account if recursive: _account_list += [ clean_account_name(a.fullname) for a in account.children ]
def configure(configuration): """ Load a list of all the expense categories and the accounts that implement that category. The account definition is a dictionary that will be passed to the account walker method to find accounts (so can ignore and recursively search an account tree). :param configuration: configuration object. """ global _expense_categories, _reverse from gnucash_reports.wrapper import account_walker, parse_walker_parameters for definition in configuration.get('expenses_categories', []): category = definition['name'] accounts = parse_walker_parameters(definition.get('definition', [])) all_accounts = set() for account in account_walker(**accounts): # print 'loading account: %s' % account.fullname all_accounts.add(clean_account_name(account.fullname)) for account in all_accounts: _reverse[account] = category _expense_categories[category] = all_accounts
def income_tax(income=None, tax=None, start=PeriodStart.this_year, end=PeriodEnd.this_year, tax_name='federal', tax_status='single', deductions=None, deduction=None): """ Walk through all of the income accounts provided to determine tax owed based on tax tables. Then use deductions, deduction_accounts to reduce income, and then calculate a simple tax owed based on the tax_name and tax_status information. Will only use the account data between the start and end values. :param income: account walker parameters for the income accounts :param tax: account walker parameters for the tax paid accounts :param start: Period start date :param end: Period end date, :param tax_name: name of tax table to use :param tax_status: name of tax sub-table to use :param deductions: list of decimal definitions that should be deducted from the taxable income :param deduction: account walker parameters that should be used to reduce the taxable income as well :return: dictionary containing income - total taxable income made during the period tax_value - total calculated tax based on income value taxes_paid - total taxes paid into the tax accounts """ income_accounts = income or [] tax_accounts = tax or [] deductions = deductions or [] deduction_accounts = deduction or [] income_accounts = parse_walker_parameters(income_accounts) tax_accounts = parse_walker_parameters(tax_accounts) period_start = PeriodStart(start) period_end = PeriodEnd(end) deduction_accounts = parse_walker_parameters(deduction_accounts) total_income = Decimal(0.0) total_taxes = Decimal(0.0) pre_tax_deductions = Decimal(0.0) # Find all of the deduction accounts, and store them in a list so that they can be walked when handling the # income from the income accounts deduction_account_names = set() for account in account_walker(**deduction_accounts): deduction_account_names.add(clean_account_name(account.fullname)) # Calculate all of the income that has been received, and calculate all of the contributions to the deduction # accounts that will reduce tax burden. for account in account_walker(**income_accounts): for split in get_splits(account, period_start.date, period_end.date): value = split.value * -1 # negate the value because income is leaving these accounts total_income += value # Go through the split's parent and find all of the values that are in the deduction accounts as well transaction = split.transaction for t_split in transaction.splits: if clean_account_name( t_split.account.fullname) in deduction_account_names: pre_tax_deductions += t_split.value # Calculate all of the taxes that have been currently paid for account in account_walker(**tax_accounts): for split in get_splits(account, period_start.date, period_end.date): value = split.value total_taxes += value # Remove all of the deductions from the total income value for deduction in deductions: pre_tax_deductions += Decimal(deduction) # Remove all of the contributions from the income accounts that went into pre-tax accounts and any standard # deductions total_income -= pre_tax_deductions tax_value = calculate_tax(tax_name, tax_status, total_income) return { 'income': total_income, 'tax_value': tax_value, 'taxes_paid': total_taxes }