Implement cash basis accounting
This commit is contained in:
parent
e4c8d2f398
commit
63806da8dc
@ -32,9 +32,10 @@ def index():
|
|||||||
def trial():
|
def trial():
|
||||||
date = datetime.strptime(flask.request.args['date'], '%Y-%m-%d')
|
date = datetime.strptime(flask.request.args['date'], '%Y-%m-%d')
|
||||||
pstart = datetime.strptime(flask.request.args['pstart'], '%Y-%m-%d')
|
pstart = datetime.strptime(flask.request.args['pstart'], '%Y-%m-%d')
|
||||||
|
cash = flask.request.args.get('cash', False)
|
||||||
|
|
||||||
# Get trial balance
|
# Get trial balance
|
||||||
accounts = ledger.trial_balance(date, pstart)
|
accounts = ledger.trial_balance(date, pstart, cash)
|
||||||
|
|
||||||
total_dr = sum(a.balance for a in accounts if a.balance > 0)
|
total_dr = sum(a.balance for a in accounts if a.balance > 0)
|
||||||
total_cr = -sum(a.balance for a in accounts if a.balance < 0)
|
total_cr = -sum(a.balance for a in accounts if a.balance < 0)
|
||||||
@ -45,9 +46,10 @@ def trial():
|
|||||||
def balance():
|
def balance():
|
||||||
date = datetime.strptime(flask.request.args['date'], '%Y-%m-%d')
|
date = datetime.strptime(flask.request.args['date'], '%Y-%m-%d')
|
||||||
pstart = datetime.strptime(flask.request.args['pstart'], '%Y-%m-%d')
|
pstart = datetime.strptime(flask.request.args['pstart'], '%Y-%m-%d')
|
||||||
|
cash = flask.request.args.get('cash', False)
|
||||||
|
|
||||||
# Get trial balance
|
# Get trial balance
|
||||||
accounts = ledger.trial_balance(date, pstart)
|
accounts = ledger.trial_balance(date, pstart, cash)
|
||||||
accounts_map = ledger.make_account_tree(accounts)
|
accounts_map = ledger.make_account_tree(accounts)
|
||||||
|
|
||||||
# Calculate Profit/Loss
|
# Calculate Profit/Loss
|
||||||
@ -57,15 +59,16 @@ def balance():
|
|||||||
accounts.append(ledger.Account(ledger.config['current_year_earnings'], total_pandl))
|
accounts.append(ledger.Account(ledger.config['current_year_earnings'], total_pandl))
|
||||||
accounts_map = ledger.make_account_tree(accounts)
|
accounts_map = ledger.make_account_tree(accounts)
|
||||||
|
|
||||||
return flask.render_template('balance.html', date=date, assets=accounts_map[ledger.config['assets_account']], liabilities=accounts_map[ledger.config['liabilities_account']], equity=accounts_map[ledger.config['equity_account']])
|
return flask.render_template('balance.html', date=date, cash=cash, assets=accounts_map.get(ledger.config['assets_account'], ledger.Account('Assets')), liabilities=accounts_map.get(ledger.config['liabilities_account'], ledger.Account('Liabilities')), equity=accounts_map.get(ledger.config['equity_account'], ledger.Account('Equity')))
|
||||||
|
|
||||||
@app.route('/pandl')
|
@app.route('/pandl')
|
||||||
def pandl():
|
def pandl():
|
||||||
date_beg = datetime.strptime(flask.request.args['date_beg'], '%Y-%m-%d')
|
date_beg = datetime.strptime(flask.request.args['date_beg'], '%Y-%m-%d')
|
||||||
date_end = datetime.strptime(flask.request.args['date_end'], '%Y-%m-%d')
|
date_end = datetime.strptime(flask.request.args['date_end'], '%Y-%m-%d')
|
||||||
|
cash = flask.request.args.get('cash', False)
|
||||||
|
|
||||||
# Get P&L
|
# Get P&L
|
||||||
accounts = ledger.parse_balance(ledger.run_ledger('--begin', date_beg.strftime('%Y-%m-%d'), '--end', (date_end + timedelta(days=1)).strftime('%Y-%m-%d'), 'balance', '--balance-format', ledger.BALANCE_FORMAT, '--no-total', '--flat', '--cost', ledger.aregex(ledger.config['income_account']), ledger.aregex(ledger.config['expenses_account'])))
|
accounts = ledger.pandl(date_beg, date_end, cash)
|
||||||
accounts_map = ledger.make_account_tree(accounts)
|
accounts_map = ledger.make_account_tree(accounts)
|
||||||
|
|
||||||
if date_end == (date_beg.replace(year=date_beg.year + 1) - timedelta(days=1)):
|
if date_end == (date_beg.replace(year=date_beg.year + 1) - timedelta(days=1)):
|
||||||
|
@ -61,27 +61,29 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr><td colspan="2"> </td></tr>
|
<tr><td colspan="2"> </td></tr>
|
||||||
|
|
||||||
<tr><th class="h1" colspan="2">Liabilities</th></tr>
|
{% if not cash %}
|
||||||
|
<tr><th class="h1" colspan="2">Liabilities</th></tr>
|
||||||
{% for liability_class in liabilities.children %}
|
|
||||||
<tr><th class="h2" colspan="2">{{ liability_class.name_parts[-1] }} Liabilities</th></tr>
|
{% for liability_class in liabilities.children %}
|
||||||
{{ walk_children(liability_class.children, acc_level=1, invert=True) }}
|
<tr><th class="h2" colspan="2">{{ liability_class.name_parts[-1] }} Liabilities</th></tr>
|
||||||
|
{{ walk_children(liability_class.children, acc_level=1, invert=True) }}
|
||||||
|
<tr class="total">
|
||||||
|
<td>Total {{ liability_class.name_parts[-1] }} Liabilities</td>
|
||||||
|
<td>{{ -liability_class.total()|a }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr><td colspan="2"> </td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td>Total {{ liability_class.name_parts[-1] }} Liabilities</td>
|
<td>Total Liabilities</td>
|
||||||
<td>{{ -liability_class.total()|a }}</td>
|
<td>{{ -liabilities.total()|a }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="total">
|
||||||
|
<td>Net Assets</td>
|
||||||
|
<td>{{ (assets.total() + liabilities.total())|a }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr><td colspan="2"> </td></tr>
|
<tr><td colspan="2"> </td></tr>
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
|
|
||||||
<tr class="total">
|
|
||||||
<td>Total Liabilities</td>
|
|
||||||
<td>{{ -liabilities.total()|a }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="total">
|
|
||||||
<td>Net Assets</td>
|
|
||||||
<td>{{ (assets.total() + liabilities.total())|a }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td colspan="2"> </td></tr>
|
|
||||||
|
|
||||||
<tr><th class="h1" colspan="2">Equity</th></tr>
|
<tr><th class="h1" colspan="2">Equity</th></tr>
|
||||||
|
|
||||||
|
@ -26,20 +26,23 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><form action="{{ url_for('trial') }}">
|
<li><form action="{{ url_for('trial') }}">
|
||||||
<button type="submit">Trial balance</button>
|
<button type="submit">Trial balance</button>
|
||||||
Date: <input name="date" value="{{ date.strftime('%Y-%m-%d') }}" style="width: 6em;">
|
<label>Date: <input name="date" value="{{ date.strftime('%Y-%m-%d') }}" style="width: 6em;"></label>
|
||||||
Period start: <input name="pstart" value="{{ pstart.strftime('%Y-%m-%d') }}" style="width: 6em;">
|
<label>Period start: <input name="pstart" value="{{ pstart.strftime('%Y-%m-%d') }}" style="width: 6em;"></label>
|
||||||
|
<label><input name="cash" type="checkbox"> Cash basis</label>
|
||||||
</form></li>
|
</form></li>
|
||||||
|
|
||||||
<li><form action="{{ url_for('balance') }}">
|
<li><form action="{{ url_for('balance') }}">
|
||||||
<button type="submit">Balance sheet</button>
|
<button type="submit">Balance sheet</button>
|
||||||
Date: <input name="date" value="{{ date.strftime('%Y-%m-%d') }}" style="width: 6em;">
|
<label>Date: <input name="date" value="{{ date.strftime('%Y-%m-%d') }}" style="width: 6em;"></label>
|
||||||
Period start: <input name="pstart" value="{{ pstart.strftime('%Y-%m-%d') }}" style="width: 6em;">
|
<label>Period start: <input name="pstart" value="{{ pstart.strftime('%Y-%m-%d') }}" style="width: 6em;"></label>
|
||||||
|
<label><input name="cash" type="checkbox"> Cash basis</label>
|
||||||
</form></li>
|
</form></li>
|
||||||
|
|
||||||
<li><form action="{{ url_for('pandl') }}">
|
<li><form action="{{ url_for('pandl') }}">
|
||||||
<button type="submit">Income statement</button>
|
<button type="submit">Income statement</button>
|
||||||
Begin date: <input name="date_beg" value="{{ pstart.strftime('%Y-%m-%d') }}" style="width: 6em;">
|
<label>Begin date: <input name="date_beg" value="{{ pstart.strftime('%Y-%m-%d') }}" style="width: 6em;"></label>
|
||||||
End date: <input name="date_end" value="{{ date.strftime('%Y-%m-%d') }}" style="width: 6em;">
|
<label>End date: <input name="date_end" value="{{ date.strftime('%Y-%m-%d') }}" style="width: 6em;"></label>
|
||||||
|
<label><input name="cash" type="checkbox"> Cash basis</label>
|
||||||
</form></li>
|
</form></li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
|
@ -25,7 +25,10 @@ with open('config.yml', 'r') as f:
|
|||||||
config = yaml.safe_load(f)
|
config = yaml.safe_load(f)
|
||||||
|
|
||||||
class Account:
|
class Account:
|
||||||
def __init__(self, name, balance):
|
def __init__(self, name, balance=None):
|
||||||
|
if balance is None:
|
||||||
|
balance = Decimal(0)
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.balance = balance
|
self.balance = balance
|
||||||
|
|
||||||
@ -92,6 +95,11 @@ def make_account_tree(accounts):
|
|||||||
def aregex(account):
|
def aregex(account):
|
||||||
return '^{0}:|^{0}$'.format(account)
|
return '^{0}:|^{0}$'.format(account)
|
||||||
|
|
||||||
|
def amatch(needle, haystack):
|
||||||
|
if haystack == needle or haystack.startswith(needle + ':'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def financial_year(date):
|
def financial_year(date):
|
||||||
pstart = date.replace(day=1, month=7)
|
pstart = date.replace(day=1, month=7)
|
||||||
if pstart > date:
|
if pstart > date:
|
||||||
@ -109,7 +117,7 @@ def unrealized_gains(date):
|
|||||||
return unrealized_gains
|
return unrealized_gains
|
||||||
|
|
||||||
# Get account balances at date
|
# Get account balances at date
|
||||||
def get_accounts(date):
|
def get_accounts(date, cash=False):
|
||||||
# Calculate Unrealized Gains
|
# Calculate Unrealized Gains
|
||||||
unrealized_gains_amt = unrealized_gains(date)
|
unrealized_gains_amt = unrealized_gains(date)
|
||||||
|
|
||||||
@ -121,16 +129,30 @@ def get_accounts(date):
|
|||||||
accounts.append(Account(config['unrealized_gains'], -unrealized_gains_amt))
|
accounts.append(Account(config['unrealized_gains'], -unrealized_gains_amt))
|
||||||
accounts.sort(key=lambda a: a.name)
|
accounts.sort(key=lambda a: a.name)
|
||||||
|
|
||||||
|
# Convert to cash basis
|
||||||
|
if cash:
|
||||||
|
accounts_map = make_account_tree(accounts)
|
||||||
|
|
||||||
|
for account in accounts[:]:
|
||||||
|
if amatch(config['liabilities_account'], account.name) or (amatch(config['assets_account'], account.name) and not any(amatch(x, account.name) for x in config['cash_asset_accounts'])):
|
||||||
|
drcr = parse_balance(run_ledger_date(date, 'balance', '--related', '--balance-format', BALANCE_FORMAT, '--no-total', '--flat', '--cost' if amatch(config['income_account'], account.name) or amatch(config['expenses_account'], account.name) else '--market', aregex(account.name)))
|
||||||
|
|
||||||
|
for drcr_account in drcr:
|
||||||
|
accounts_map[drcr_account.name].balance -= drcr_account.balance
|
||||||
|
|
||||||
|
accounts.remove(account)
|
||||||
|
del accounts_map[account.name]
|
||||||
|
|
||||||
return accounts
|
return accounts
|
||||||
|
|
||||||
# Calculate trial balance
|
# Calculate trial balance
|
||||||
def trial_balance(date, pstart):
|
def trial_balance(date, pstart, cash=False):
|
||||||
# Get balances at period start
|
# Get balances at period start
|
||||||
accounts_pstart = get_accounts(pstart - timedelta(days=1))
|
accounts_pstart = get_accounts(pstart - timedelta(days=1), cash)
|
||||||
accounts_map_pstart = make_account_tree(accounts_pstart)
|
accounts_map_pstart = make_account_tree(accounts_pstart)
|
||||||
|
|
||||||
# Get balances at date
|
# Get balances at date
|
||||||
accounts = get_accounts(date)
|
accounts = get_accounts(date, cash)
|
||||||
|
|
||||||
# Adjust Retained Earnings
|
# Adjust Retained Earnings
|
||||||
total_pandl = Decimal(0)
|
total_pandl = Decimal(0)
|
||||||
@ -143,8 +165,18 @@ def trial_balance(date, pstart):
|
|||||||
|
|
||||||
# Adjust income/expense accounts
|
# Adjust income/expense accounts
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
if account.name == config['income_account'] or account.name.startswith(config['income_account'] + ':') or account.name == config['expenses_account'] or account.name.startswith(config['expenses_account'] + ':'):
|
if amatch(config['income_account'], account.name) or amatch(config['expenses_account'], account.name):
|
||||||
if account.name in accounts_map_pstart:
|
if account.name in accounts_map_pstart:
|
||||||
account.balance -= accounts_map_pstart[account.name].balance
|
account.balance -= accounts_map_pstart[account.name].balance
|
||||||
|
|
||||||
return accounts
|
return accounts
|
||||||
|
|
||||||
|
# Calculate profit and loss
|
||||||
|
def pandl(date_beg, date_end, cash=False):
|
||||||
|
accounts = trial_balance(date_end, date_beg, cash)
|
||||||
|
|
||||||
|
for account in accounts[:]:
|
||||||
|
if not (amatch(config['income_account'], account.name) or amatch(config['expenses_account'], account.name)):
|
||||||
|
accounts.remove(account)
|
||||||
|
|
||||||
|
return accounts
|
||||||
|
Reference in New Issue
Block a user