Added /transaction endpoint
This commit is contained in:
parent
63c7b70000
commit
6f2c875c7b
5 changed files with 125 additions and 2 deletions
|
@ -111,6 +111,30 @@ class Ledger:
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def add_transaction(self, transaction):
|
||||||
|
transaction_template = ('\n{date} {t.payee}\n'
|
||||||
|
'{postings}')
|
||||||
|
|
||||||
|
posting_template = (' {account} {p.amount.symbol}'
|
||||||
|
' {p.amount.amount}\n')
|
||||||
|
|
||||||
|
output = b''
|
||||||
|
|
||||||
|
output += transaction_template.format(
|
||||||
|
date=transaction.date.strftime('%Y-%m-%d'),
|
||||||
|
t=transaction,
|
||||||
|
postings=''.join([posting_template.format(
|
||||||
|
p=p,
|
||||||
|
account=p.account + ' ' * (
|
||||||
|
80 - (len(p.account) + len(p.amount.symbol) +
|
||||||
|
len(p.amount.amount) + 1 + 2)
|
||||||
|
)) for p in transaction.postings])).encode('utf8')
|
||||||
|
|
||||||
|
with open(self.ledger_file, 'ab') as f:
|
||||||
|
f.write(output)
|
||||||
|
|
||||||
|
_log.debug('written to file: %s', output)
|
||||||
|
|
||||||
def bal(self):
|
def bal(self):
|
||||||
output = self.send_command('xml')
|
output = self.send_command('xml')
|
||||||
|
|
||||||
|
|
20
accounting/decorators.py
Normal file
20
accounting/decorators.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from flask import jsonify
|
||||||
|
|
||||||
|
from accounting.exceptions import AccountingException
|
||||||
|
|
||||||
|
|
||||||
|
def jsonify_exceptions(func):
|
||||||
|
'''
|
||||||
|
Wraps a Flask endpoint and catches any AccountingException-based
|
||||||
|
exceptions which are returned to the client as JSON.
|
||||||
|
'''
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kw):
|
||||||
|
try:
|
||||||
|
return func(*args, **kw)
|
||||||
|
except AccountingException as exc:
|
||||||
|
return jsonify(error=exc)
|
||||||
|
|
||||||
|
return wrapper
|
6
accounting/exceptions.py
Normal file
6
accounting/exceptions.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
class AccountingException(Exception):
|
||||||
|
'''
|
||||||
|
Used as a base for exceptions that are returned to the caller via the
|
||||||
|
jsonify_exceptions decorator
|
||||||
|
'''
|
||||||
|
pass
|
|
@ -1,3 +1,5 @@
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from flask import json
|
from flask import json
|
||||||
|
|
||||||
from accounting import Amount, Transaction, Posting, Account
|
from accounting import Amount, Transaction, Posting, Account
|
||||||
|
@ -30,6 +32,11 @@ class AccountingEncoder(json.JSONEncoder):
|
||||||
amount=o.amount,
|
amount=o.amount,
|
||||||
symbol=o.symbol
|
symbol=o.symbol
|
||||||
)
|
)
|
||||||
|
elif isinstance(o, Exception):
|
||||||
|
return dict(
|
||||||
|
__type__=o.__class__.__name__,
|
||||||
|
args=o.args
|
||||||
|
)
|
||||||
|
|
||||||
return json.JSONEncoder.default(self, o)
|
return json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
|
@ -46,4 +53,7 @@ class AccountingDecoder(json.JSONDecoder):
|
||||||
|
|
||||||
_type = d.pop('__type__')
|
_type = d.pop('__type__')
|
||||||
|
|
||||||
|
if _type == 'Transaction':
|
||||||
|
d['date'] = datetime.strptime(d['date'], '%Y-%m-%d')
|
||||||
|
|
||||||
return types[_type](**d)
|
return types[_type](**d)
|
||||||
|
|
|
@ -2,10 +2,12 @@ import sys
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from flask import Flask, g, jsonify, json, request
|
from flask import Flask, jsonify, request
|
||||||
|
|
||||||
from accounting import Ledger, Account, Posting, Transaction, Amount
|
from accounting import Ledger
|
||||||
from accounting.transport import AccountingEncoder, AccountingDecoder
|
from accounting.transport import AccountingEncoder, AccountingDecoder
|
||||||
|
from accounting.exceptions import AccountingException
|
||||||
|
from accounting.decorators import jsonify_exceptions
|
||||||
|
|
||||||
|
|
||||||
app = Flask('accounting')
|
app = Flask('accounting')
|
||||||
|
@ -32,6 +34,67 @@ def balance_report():
|
||||||
return jsonify(balance_report=report_data)
|
return jsonify(balance_report=report_data)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/transaction', methods=['POST'])
|
||||||
|
@jsonify_exceptions
|
||||||
|
def transaction():
|
||||||
|
'''
|
||||||
|
REST/JSON endpoint for transactions.
|
||||||
|
|
||||||
|
Current state:
|
||||||
|
|
||||||
|
Takes a POST request with a ``transactions`` JSON payload and writes it to
|
||||||
|
the ledger file.
|
||||||
|
|
||||||
|
Requires the ``transactions`` payload to be __type__-annotated:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"transactions": [
|
||||||
|
{
|
||||||
|
"__type__": "Transaction",
|
||||||
|
"date": "2013-01-01",
|
||||||
|
"payee": "Kindly T. Donor",
|
||||||
|
"postings": [
|
||||||
|
{
|
||||||
|
"__type__": "Posting",
|
||||||
|
"account": "Income:Foo:Donation",
|
||||||
|
"amount": {
|
||||||
|
"__type__": "Amount",
|
||||||
|
"amount": "-100",
|
||||||
|
"symbol": "$"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "Posting",
|
||||||
|
"account": "Assets:Checking",
|
||||||
|
"amount": {
|
||||||
|
"__type__": "Amount",
|
||||||
|
"amount": "100",
|
||||||
|
"symbol": "$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
becomes::
|
||||||
|
|
||||||
|
2013-01-01 Kindly T. Donor
|
||||||
|
Income:Foo:Donation $ -100
|
||||||
|
Assets:Checking $ 100
|
||||||
|
'''
|
||||||
|
transactions = request.json.get('transactions')
|
||||||
|
|
||||||
|
if not transactions:
|
||||||
|
raise AccountingException('No transaction data provided')
|
||||||
|
|
||||||
|
for transaction in transactions:
|
||||||
|
ledger.add_transaction(transaction)
|
||||||
|
|
||||||
|
return jsonify(foo='bar')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/parse-json', methods=['POST'])
|
@app.route('/parse-json', methods=['POST'])
|
||||||
def parse_json():
|
def parse_json():
|
||||||
r'''
|
r'''
|
||||||
|
|
Loading…
Reference in a new issue