Esempio n. 1
0
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))
Esempio n. 2
0
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
Esempio n. 3
0
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
Esempio n. 4
0
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
            ]
Esempio n. 5
0
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
Esempio n. 6
0
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
    }