@@ -5,25 +5,28 @@ import time
from datetime import datetime
from xml.etree import ElementTree
from contextlib import contextmanager
from accounting.models import Account, Transaction, Posting, Amount
from accounting.storage import Storage
_log = logging.getLogger(__name__)
class Ledger(Storage):
def __init__(self, ledger_file=None, ledger_bin=None):
def __init__(self, app=None, ledger_file=None, ledger_bin=None):
if app:
ledger_file = app.config['LEDGER_FILE']
if ledger_file is None:
raise ValueError('ledger_file cannot be None')
self.ledger_bin = ledger_bin or 'ledger'
self.ledger_file = ledger_file
_log.info('ledger file: %s', ledger_file)
self.locked = False
self.ledger_process = None
@contextmanager
def locked_process(self):
@@ -149,45 +152,48 @@ class Ledger(Storage):
self.ledger_process.wait()
return output
def add_transaction(self, transaction):
'''
Writes a transaction to the ledger file by opening it in 'ab' mode and
writing a ledger transaction based on the
:class:`~accounting.models.Transaction` instance in
:data:`transaction`.
if not transaction.metadata.get('Id'):
if transaction.id is None:
_log.debug('No ID found. Generating an ID.')
transaction.generate_id()
transaction.metadata.update({'Id': transaction.id})
transaction_template = ('\n{date} {t.payee}\n'
'{tags}'
'{metadata}'
'{postings}')
metadata_template = ' ;{0}: {1}\n'
# TODO: Generate metadata for postings
posting_template = (' {account} {p.amount.symbol}'
' {p.amount.amount}\n')
output = b''
# XXX: Even I hardly understands what this does, however I indent it it
# stays unreadable.
output += transaction_template.format(
date=transaction.date.strftime('%Y-%m-%d'),
t=transaction,
tags=''.join([
metadata=''.join([
metadata_template.format(k, v)
for k, v in transaction.metadata.items()]),
postings=''.join([posting_template.format(
p=p,
account=p.account + ' ' * (
80 - (len(p.account) + len(p.amount.symbol) +
len(str(p.amount.amount)) + 1 + 2)
)) for p in transaction.postings
])
).encode('utf8')
with open(self.ledger_file, 'ab') as f:
@@ -230,24 +236,27 @@ class Ledger(Storage):
symbol = amount.find('./commodity/symbol').text
amounts.append(Amount(amount=quantity, symbol=symbol))
else:
_log.warning('Account %s does not have any amounts', name)
accounts.append(Account(name=name,
amounts=amounts,
accounts=self._recurse_accounts(account)))
return accounts
def get_transactions(self):
return self.reg()
def reg(self):
output = self.send_command('xml')
if output is None:
raise RuntimeError('reg call returned no output')
entries = []
reg_xml = ElementTree.fromstring(output.decode('utf8'))
for transaction in reg_xml.findall('./transactions/transaction'):
date = datetime.strptime(transaction.find('./date').text,
@@ -292,24 +301,27 @@ class Ledger(Storage):
_log.debug('metadata: %s: %s', key, value)
metadata.update({key: value})
# Add a Transaction instance to the list
id = metadata.pop('Id')
entries.append(
Transaction(id=id, date=date, payee=payee, postings=postings,
metadata=metadata))
return entries
def update_transaction(self, transaction):
_log.debug('DUMMY: Updated transaction: %s', transaction)
def main(argv=None):
import argparse
if argv is None:
argv = sys.argv
parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbosity',
default='INFO',
help=('Filter logging output. Possible values:' +
' CRITICAL, ERROR, WARNING, INFO, DEBUG'))