* Martin Blais <[email protected]> [2019-10-09 22:24 -0400]:
> On Tue, Oct 8, 2019 at 11:29 AM Phil! Gold <[email protected]> wrote:
> > The specific problem is that I have a few different funds I need to
> > keep separate from each other.  We periodically have projects in
> > collaboration with other organizations where the funding for those
> > projects has to remain separate from our general funds.  I manage this
> > in Ledger by having separate income and expense accounts for these
> > projects.
> 
> There's no such rule system (I don't recall coming across it in Ledger
> either, how does it work?),

Well, for Ledger I wrote my own scripts that process the Income and
Expense accounts and sort them into appropriate Net Asset accounts.  For
year-to-date reports, the sums just get printed in the reports, but at the
end of the year I write the sums into that year's journal file as new
Ledger transactions.

The net result is that I can use Ledger's built-in querying tools to get
either an income statement or a complete balance sheet for a fiscal year
by querying either the last day of the FY or the first day of the next FY,
respectively.  For year-to-date stuff (which is almost always current
year-to-date), if I use the built-in querying tools I have to mentally
combine the income and expense accounts myself.

Since beancount has a richer query language, I'd hoped to be able to do
all of the income/expense -> net asset stuff dynamically without fixing
the account closing stuff at the end of the fiscal year.  I guess that's
not possible.

> but what I'd recommend you do is write a little script that creates the
> EOY transactions and insert those into your file.

That's what I ended up doing.  In case it's useful to anyone else, I'm
attaching it here.  (But its rules--encoded in the get_account_target()
function--depend heavily on the particular account structure I've set up.)

-- 
...computer contrarian of the first order... / http://aperiodic.net/phil/
PGP: 026A27F2  print: D200 5BDB FC4B B24A 9248  9F7A 4322 2D22 026A 27F2
--- --
do {
    :
} until (HELL_FREEZES_OVER);
---- --- --

-- 
You received this message because you are subscribed to the Google Groups 
"Beancount" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/beancount/20191010170525.GM19546%40elros.aperiodic.net.
#!/usr/bin/env python3

__plugins__ = ['add_clear_txns']


import datetime
from decimal import Decimal

from beancount.core import amount, data, flags


FY_START_MONTH = 8


def get_account_target(account):
    pieces = account.split(':')
    if pieces[1] == 'Projects':
        return 'Net-Assets:{}:{}:{}'.format(pieces[1], pieces[2], pieces[3])
    elif pieces[1] == 'Restricted-Funds':
        return 'Net-Assets:{}:{}'.format(pieces[1], pieces[2])
    else:
        return 'Net-Assets:{}'.format(pieces[1])

def generate_close_transactions(entries, opened, account_totals, effective_date):
    new_entries = []
    for account, units in account_totals.items():
        p1 = data.Posting(
            account=account,
            units=amount.mul(units, Decimal(-1)),
            cost=None,
            price=None,
            flag=None,
            meta=None)
        p2 = data.Posting(
            account=get_account_target(account),
            units=units,
            cost=None,
            price=None,
            flag=None,
            meta=None)
        txn = data.Transaction(
            date=effective_date,
            meta=data.new_metadata('', 0),
            flag=flags.FLAG_SUMMARIZE,
            payee=None,
            narration='Close {}'.format(account),
            tags=set(),
            links=set(),
            postings=[p1, p2])
        new_entries.append(txn)

        if not p2.account in opened:
            opn = data.Open(
                date=txn.date,
                meta=data.new_metadata('', 0),
                account=p2.account,
                currencies=None,
                booking=None)
            new_entries.append(opn)
            opened.add(p2.account)

    return entries + new_entries, opened

def add_clear_txns(entries, options_map, config_str=None):
    config = eval(config_str)
    errors = []
    new_entries = []
    account_totals = {}
    opened = set(['Net-Assets:General'])
    current_fy_end = None
    for entry in entries:
        if isinstance(entry, data.Transaction):
            if current_fy_end is not None and current_fy_end < entry.date:
                new_entries, opened = generate_close_transactions(new_entries, opened, account_totals, current_fy_end)
                account_totals = {}
                current_fy_end = None
            if current_fy_end is None:
                current_fy_end = datetime.date(
                    year=entry.date.year,
                    month=FY_START_MONTH,
                    day=1) + datetime.timedelta(days=-1)
                if current_fy_end < entry.date:
                    current_fy_end = current_fy_end.replace(year=entry.date.year + 1)
            for posting in entry.postings:
                if posting.account.startswith('Income:') or posting.account.startswith('Expenses:'):
                    if posting.account not in account_totals:
                        account_totals[posting.account] = posting.units
                    else:
                        account_totals[posting.account] = amount.add(account_totals[posting.account], posting.units)
    new_entries, opened = generate_close_transactions(new_entries, opened, account_totals, current_fy_end)
    
    return entries + new_entries, errors

Reply via email to