Re: [fava] Display budgets (#165)

920 views
Skip to first unread message

Martin Blais

unread,
Feb 25, 2016, 9:57:55 PM2/25/16
to aumayr/fava, Beancount
Lots to say about budgeting but I don't have that much to add than what I already said on the group. Search the group for previous discussions. IMHO budget is best implemented as a set of constraints (assertions).

I think it's awesome you built a prototype! :-)

(more below)


On Thu, Feb 25, 2016 at 3:15 PM, aumayr <notifi...@github.com> wrote:

Beancount itself is a double-book accounting tool, and does not know of the concept of "budgets".

But the more I use fava I wish there would be a way to display information if a certain account is over budget. I do practice setting budgets for various "accounts" myself to enforce certain spending behavior, and it would be nice to integrate this into the system.

In the "budgeting"-branch I drafted a first version of how this might work and look:

  • There is a new file which contains the budget-rules. It is a simple plain-text-file and has content like this:

    Expenses:Food:Groceries
        2013-W52       100.00 USD
        2016-W01       120.00 USD
        2016-W02       110.00 USD
        2016-W06       150.00 USD
    
    Expenses:Food:Alcohol
        2016-W01        20.00 USD
        2016-W02        10.00 USD
        2016-W06         5.00 USD
    
    ...
    

    The format is very simple: First, the account name (Expenses:Food:Groceries). Then, lines with the week of the year where this budget applies to (and starts) (2016-W01), and finally the amount and currency (20.00 USD). I will call these lines "Budget entries".

  • These budget entries represent the budget for the specified week of the year, and all weeks thereafter, until a new rule is set for a week after that. So you can set a budget of 10.00 USD at the very first week of 2015 (2015-W01), and this budget applies to all weeks until a new entry starting in week 5 of that same year (2015-W05) appears and sets the new budget to 20.00 USD.

  • A budget for a specific account, currency, start-date and end-date is then calculated by stepping through each day in that period, and adding the budget for the week of that day (divided by 7) to the total sum, until the end-date is reached.

I included a test-file in fava/util/test-budget.budget, which contains the rules above for Groceries and Alcohol (accounts in the file generated by bean-example).

You can test it by starting fava with a bean-example-generated file, and navigating to http://localhost:5000/account/Expenses:Food/monthly_changes/ (for example). It should look like thisscreenshot

Things to do:

  • Discussing if this is a good idea or not
It's certainly a good idea! :-)
This is an unoccupied spot in the space of implemented features for Beancount at the moment.

  • Discussing if it is to be included in fava or not
  • Discussing if the core functionality of this should be factored out into it's own package or should stay within fava
IMHO that feature is closer to the language than to the reporting. It's something you can likely implement "in-between" in a dependency tree, or if we can hash out a nice prototype I'm open to integrating assertions on deltas in Beancount syntax itself. A syntax for assertions, and some error reporting on a failing assertion would in theory be best defined closer to the language, and generating particular reports could live in the reporting UI.

Then again, this can always be done later if you wanted to do that. Maybe it's better to keep the velocity going now and let it bloom while the inspiration is flowing :-)
 
  • Writing tests for the "custom language parser"
Idea: If you want to integrate those rules in the same input file, you could use syntax that parses as comments in Beancount. It's a bit of a lo-fi solution but it could work, e.g. parsing only lines which begin with ;! or whatever.

  • The budget is currently "generated" and displayed in the account.html-template, but should ultimately be either part of the API or in a second API (BudgetAPI?).
 

What do you think @yagebu @corani @redstreet @jbms @xentac @blais


Reply to this email directly or view it on GitHub.


Martin Blais

unread,
Feb 28, 2016, 2:22:55 PM2/28/16
to aumayr/fava, Beancount, aumayr/fava, Martin Blais
+beancount


On Fri, Feb 26, 2016 at 3:06 AM, aumayr <notifi...@github.com> wrote:

I also had the feeling that this should be a feature of beancount rather than just fava.

Where I am pretty sure now is how it should work:

Set the budget for a specific period (day, week, month, quarter, year), and be able to change it with a new entry with a date after the previous entry, as in my example above.

I can't come up with a situation that this does not cover, from in-period budget changes to periods without a budget (setting it to None after a specific date, until the next budget entry comes along).

What I am still struggling with is how/where it should be implemented. That it should live in/near beancount is clear for me now, but exactly how is open. From your suggestions, and some new ideas of my own:

  1. In a separate file, like my example at the top.

  2. In the beancount-file, as metadata:

    2009-05-05 open Expenses:Food:Groceries
       budget: "2015-01-01 monthly  150.00 USD"
       budget: "2016-01-01 daily     25.00 USD"
    

    Pro: No change necessary to beancount itself. Accessable by plugins.
    Con: No real integration with beancount. Errors not along with beancount-errors. Hack-ish.

  3. In the beancount-file, as a new type of directive:

    2015-01-01 budget Expenses:Food:Groceries weekly  150.00 USD
    

    Pro: Official support, part of the language, "strongly typed", errors along existing errors.
    Con: Large change to beancount.

Eventually that's nicest IMO. For now, perhaps a bit too early. I'd start out with the one you have below and evolve that a bit before committing to creating a new entry.

(Note: Eventually I want to let plugins able to define their own directives like this. A plugin could declare a new directive type, and a lax parser would produce them and validate against a list of accepted tokens that comes with the new directive definition. This has only been an idea so far, but I think much of the existing directives I have could be converted to that. Anyway, it's a distant idea for now.)


 
  1. In the beancount-file, as comments:

    ;budget Expenses:Food:Groceries weekly  150.00 USD
    

    Pro: Quick prototype for beancount possible.
    Con: Ugly, hack-ish.

If this is to be included in a beancount-file, then the question of organizing is also relevant. I personally like having all budget-related infos next to the Account in one spot, like my example at the top and (1) and like the metadata-solution (2).

Martin Blais

unread,
Feb 28, 2016, 2:25:53 PM2/28/16
to aumayr/fava, Beancount, aumayr/fava, Martin Blais
+beancount


On Sun, Feb 28, 2016 at 3:16 AM, aumayr <notifi...@github.com> wrote:

I made some progress with the prototype, and one key learning is that (2) "In the beancount-file, as metadata" works well, but there are two drawbacks:

  • The syntax suggested is not working, because metadata-keys have to be unique (duh, that was obvious).

  • There is no "open"-directive for the root-level-accounts, eg. 2016-01-01 open Expenses. So with this method it is not possible to set metadata for the root-level-accounts, and therefore no budget info.

    (@blais is there a way around this? As far as I see when I do create an open entry for Expenses, it will be stripped.)

So I came up with a new "syntax", looking like this:

2015-10-01 open Expenses:Books
  budget-2015-10:       "50.00 EUR"
  budget-2015-11-01:    "45.00 EUR"
  budget-2015-W50:      "60.00 EUR"
  budget-2016:          "30.00 EUR"
  budget-2016-Q2:       "20.00 EUR"

I think it looks nice, and it is integrated in the beancount-file, where I think it belongs to. And it would be accessible to beancount-plugins, so it could be better integrated into beancount.

"Dated" metadata like this looks a little abusive. What does that look like with years worth of data on that account?
That would be a lot of meta-data...
Personally I think this would be better achieved with a dedicated directive.

 

On integrating this directly into beancount:

  1. I try to interate quickly on how the "Interface" (way of storing budget data) should look like.
  2. When this is settled, moving this from fava/the 'budgeting'-branch to beancount should be the next step. Checking the input for errors can be done by a plugin, and the reporting-part has to be integrated into the beancount-code.
  3. In beancount, there are two things that would make sense IMHO: Integration with bean-report, and integration with bean-query.

Martin Blais

unread,
Feb 28, 2016, 3:14:05 PM2/28/16
to Martin Blais, Beancount, aumayr/fava, aumayr/fava
I just created you a "custom" custom directive to muck around with.
This should parse, for example:

2015-10-01 open Expenses:Books
2016-10-01 custom "budget" 50.00 EUR
2016-11-01 custom "budget" 45.00 EUR
2016-11-10 custom "budget" 60.00 EUR
2016-12-31 custom "budget" 30.00 EUR

You can put a list of strings, dates, numbers, amounts, booleans after "budget".
The parser creates beancount.core.data.Custom directives but does nothing else with them.
It doesn't check that they're consistent.
You can write a plugin that picks these up and does something with them.
Later on I'll provide some sort of registration call one can add to have the parser check at least the data types for directives of a particular "type".

Finally, ideally, it would have been preferred to make this syntax just create an unknown or custom directive:

  2016-11-01 budget ...

but it's a bit sensitive to add such an unknown id to the tokenizer at the moment, as it would interfere with some of the rest of the syntax, like key-values. I'd need more time and a lot of testing to put that in (though it's possible).

I hope this helps prototyping stuff.

More details here:
Reply all
Reply to author
Forward
0 new messages