diff --git a/FY2013/2012-06.dat b/FY2013/2012-06.dat index fc3beb1..65f21c8 100644 --- a/FY2013/2012-06.dat +++ b/FY2013/2012-06.dat @@ -1,7 +1,3 @@ -2012-06-01 Opening Balance - Assets:Operations:New Alliance $ 102.45 - Equity:Owners:Chad Whitacre -$ 102.45 - ; Gittip Payday 0 2012-06-01 Users Assets:Escrow:Samurai $ 2.96 diff --git a/FY2013/FY2013.dat b/FY2013/FY2013.dat new file mode 100644 index 0000000..7469559 --- /dev/null +++ b/FY2013/FY2013.dat @@ -0,0 +1,3 @@ +2012-06-01 Opening Balance + Assets:Operations:New Alliance $ 102.45 + Equity:Owners:Chad Whitacre -$ 102.45 diff --git a/declarations.dat b/FY2013/declarations.dat similarity index 99% rename from declarations.dat rename to FY2013/declarations.dat index 33e5f96..37514c2 100644 --- a/declarations.dat +++ b/FY2013/declarations.dat @@ -1,4 +1,5 @@ commodity $ + account Assets:Escrow:Cash account Assets:Escrow:New Alliance account Assets:Escrow:Samurai @@ -10,14 +11,13 @@ account Assets:Fee Buffer:Stripe account Assets:Operations:New Alliance account Assets:Operations:Samurai account Assets:Operations:Stripe + +account Liabilities:Escrow +account Liabilities:Fee Buffer + account Equity:Owners:Chad Whitacre account Equity:Current Activity -account Expenses:Escrow:Cash -account Expenses:Fee Buffer:Cash -account Expenses:Fee Buffer:Samurai -account Expenses:Fee Buffer:Stripe -account Expenses:Operations:Errors:Samurai -account Expenses:Operations:Processing:Stripe + account Income:Escrow:Samurai account Income:Escrow:Stripe account Income:Fee Buffer:Cash @@ -26,5 +26,10 @@ account Income:Fee Buffer:Stripe account Income:Operations:Errors:Samurai account Income:Operations:IHasAMoney.com account Income:Operations:Verification & Testing -account Liabilities:Escrow -account Liabilities:Fee Buffer + +account Expenses:Escrow:Cash +account Expenses:Fee Buffer:Cash +account Expenses:Fee Buffer:Samurai +account Expenses:Fee Buffer:Stripe +account Expenses:Operations:Errors:Samurai +account Expenses:Operations:Processing:Stripe diff --git a/README.md b/README.md index 98a673d..df8f3c4 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,14 @@ # Gratipay Finances This is [Gratipay](https://gratipay.com/)'s financial accounting system, which -is based on [Ledger](http://ledger-cli.org/). We have a directory for each -year, and an `NN.dat` file for each month. Our wrapper scripts are in the -`bin/` directory; add it to your `PATH` for best results. Each month gets [a -PR](https://github.com/gratipay/finances/pulls). +is comprised of some wrapper scripts for [Ledger](http://ledger-cli.org/) and a +[workflow](#workflow) here on GitHub. While we [catch +up](https://github.com/gratipay/finances/issues/3) on our books, our budget and +old data are available in our +[old spreadsheet](https://docs.google.com/spreadsheet/pub?key=0AiDJ5uiG6Hp3dDJnVDNLMVk4NldhSy1JbFJ0aWRUYkE&output=html&widget=true). [![status](https://api.travis-ci.org/gratipay/finances.svg)](https://travis-ci.org/gratipay/finances) -While we [catch up](https://github.com/gratipay/finances/issues/3) on our -books, our budget and old data are only available in our old -[spreadsheet](https://docs.google.com/spreadsheet/pub?key=0AiDJ5uiG6Hp3dDJnVDNLMVk4NldhSy1JbFJ0aWRUYkE&output=html&widget=true). - ## How Our Books are Organized @@ -30,15 +27,17 @@ any other Gratipay Team). To deal with this dual reality, we use a **fee buffer**. Ideally the balance in the fee buffer is zero, though of course it fluctuates in practice. -You'll see, then, that the assets on our balance sheet, as well as our income -and expenses on our income statement, are broken down according to these three -second-level categories: escrow, fee buffer, and operations. The fee buffer and -operations on the income statement hit retained earnings on the balance sheet. -Escrow on the income statement hits escrow liability on the balance sheet. +You'll see, then, that the assets on our balance sheet are broken down +according to these three second-level categories: escrow, fee buffer, and +operations. Each also gets a separate income statement: + + - Net income on the income statement hits Current Activity on the balance sheet + - Escrow activity hits the Escrow liability account + - Fee Buffer activity hits the Fee Buffer liability account -Whereas the second-level categories are *logical*, our actual *physical* bank -and processor accounts end up as third-level categories. So, for example, our -actual balance at New Alliance Federal Credit Union is equal to the sum of +Whereas the second-level asset categories are *logical*, our actual *physical* +bank and processor accounts end up as third-level categories. So, for example, +our actual balance at New Alliance Federal Credit Union is equal to the sum of these three balance sheet accounts: - Assets:Escrow:New Alliance @@ -46,6 +45,23 @@ these three balance sheet accounts: - Assets:Operations:New Alliance +### Fiscal Year + +Our fiscal year runs from June 1 through May 31. + + +## How This Repo is Organized + +There is a directory for each fiscal year, named `FYNNNN`. Inside are three +kinds of files: + + - `FYNNNN.dat`—the opening and closing transactions for the fiscal year + - `NNNN-MM.dat`—a month's worth of transactions + - `declarations.dat`—the list of accounts in use during the fiscal year + +Our scripts and helpers are in the `bin/` directory. + + ## Working on the Finances First, you'll need [Ledger](http://ledger-cli.org/) (v3), @@ -57,22 +73,39 @@ and then, from the root of your clone of this repo, run (with [`bin`](https://github.com/gratipay/finances/blob/master/bin/) on your `PATH`): ```bash -clear && test.py && balance-sheet.py && income-statement.py +test.py && clear && balance-sheet.py && income-statement.py ``` That'll check for errors (we also have CI set up [at Travis](https://travis-ci.org/gratipay/finances)) and then show you a balance sheet and income statement. If you need to add accounts or currencies you can -do so in -[`declarations.dat`](https://github.com/gratipay/finances/blob/master/declarations.dat). -If you want to run arbitrary Ledger commands, we provide a wrapper that points -`ledger` to our `dat` files for your convenience: +do so in the `declarations.dat` file for the year you're working on. If you +want to run arbitrary Ledger +[commands](http://ledger-cli.org/3.0/doc/ledger3.html), we provide a wrapper +that points `ledger` to our `dat` files for your convenience: ```bash wledger.py register ``` +### Workflow + +Each month gets [a PR](https://github.com/gratipay/finances/pulls) entitled +`reconcile YYYY-MM`, with a branch named `YYYY-MM`. We close the month by +merging the PR for the month. Inside of an open month, we should overwrite +ledger transactions as needed (changes are tracked in Git commits and GitHub +comments). Outside of an open month, we must make any correcting transactions +in the current month, rather than overwriting transactions in an old dat file. + +Each fiscal year also gets a PR entitled `close FYNNNN` and/or `audit FYNNNN`. +We close the year by merging the PR(s). Inside of an open year, we may change +account names (this affects all month files for the year). Once a year is +closed, we mustn't edit it at all, apart from comments. + +It's always okay to add or clarify comments. + + ### Style Here are some style notes for the `dat` files: @@ -92,23 +125,6 @@ Here are some style notes for the `dat` files: 1. Use comments! Especially for weird stuff. -### Change Restrictions - -We start a PR for each month, named `YYYY-MM`, and we close the month by -merging the PR. - -Inside of an open month, we should overwrite ledger transactions as needed. -Changes are tracked in git commits and GitHub comments. Outside of an open -month, we must make correcting transactions in the current month, rather than -overwriting transactions in the old dat file. - -Inside of an open year, we may change account names (this affects all month -files for the year). Outside of an open year, we must archive accounts rather -than renaming them. - -It's always okay to add comments to a file. - - ### Access Many accounting tasks require access to Gratipay's bank and payment processor @@ -126,4 +142,7 @@ Radar](http://inside.gratipay.com/howto/sweep-the-radar). # Legal The scripts and data in this repo are released into the public domain to the -extent possible under [CC0](http://creativecommons.org/publicdomain/zero/1.0/). +extent possible under [CC0](http://creativecommons.org/publicdomain/zero/1.0/), +and if you don't accept that then you may also use them under the +[CC](https://creativecommons.org/licenses/) or [OSI +license](https://opensource.org/licenses) of your choosing. diff --git a/bin/reporting.py b/bin/reporting.py index 9a89b3d..c38d21a 100644 --- a/bin/reporting.py +++ b/bin/reporting.py @@ -54,7 +54,7 @@ def list_datfiles(start=None, end=None, root='.'): for dirname in sorted(listdir(root)): if not dirname.startswith('FY'): continue for filename in sorted(listdir(path.join(root, dirname))): - if not filename.endswith('.dat'): continue + if re.match(r'\d\d\d\d-\d\d\.dat', filename) is None: continue year, month = filename[:-len('.dat')].split('-') years.append(year) months_by_year[year].append(month) @@ -66,35 +66,40 @@ def list_datfiles(start=None, end=None, root='.'): if year not in years: sys.exit("Sorry, we don't have any data for {}.".format(year)) - filtered = [] + fiscal_years = set() + months_to_load = [] for year in years: if year < start[0]: continue elif year > end[0]: break - months = months_by_year[year] + months_for_year = months_by_year[year] def check(month): - if month[1] not in months: + if month[1] not in months_for_year: sys.exit("Sorry, we don't have any data for {}-{}.".format(*month)) if start[0] == year: - if start[1] is None: start[1] = months[0] + if start[1] is None: start[1] = months_for_year[0] check(start) if end[0] == year: - if end[1] is None: end[1] = months[-1] + if end[1] is None: end[1] = months_for_year[-1] check(end) - for month in months: + for month in months_for_year: if start[1] == year and month < start[1]: continue elif end[1] == year and month > end[1]: break fiscal_year = year if month < FISCAL_YEAR_STARTING_MONTH else unicode(int(year) + 1) - filtered.append('FY{}/{}-{}.dat'.format(fiscal_year, year, month)) + fiscal_years.add(fiscal_year) + months_to_load.append((fiscal_year, year, month)) if end < start: sys.exit('Error: {}-{} comes before {}-{}.'.format(*(end + start))) - return ['-f ' + f for f in filtered] + year_ends = ['-f ' + 'FY{}/FY{}.dat'.format(fy, fy) for fy in sorted(fiscal_years)] + declarations = ['-f ' + 'FY{}/declarations.dat'.format(fy) for fy in sorted(fiscal_years)] + transactions = ['-f ' + 'FY{}/{}-{}.dat'.format(fy, y, m) for fy, y, m in months_to_load] + return declarations + year_ends + transactions def income_statement(): @@ -154,7 +159,7 @@ def wrapped(*a, **kw): @in_root def report(cmd): - cmd = [cmd[0]] + ['-f', 'declarations.dat', '--pedantic'] + cmd[1:] + cmd.append('--pedantic') retcode = subprocess.call(' '.join(cmd), shell=True) if retcode != 0: raise SystemExit(retcode)