Added transaction IDs and metadata
This commit is contained in:
		
							parent
							
								
									266aa455f9
								
							
						
					
					
						commit
						032175cd26
					
				
					 5 changed files with 94 additions and 29 deletions
				
			
		|  | @ -119,23 +119,37 @@ class Ledger: | |||
|         writing a ledger transaction based on the Transaction instance in | ||||
|         ``transaction``. | ||||
|         ''' | ||||
|         if not transaction.metadata.get('Id'): | ||||
|             transaction.generate_id() | ||||
| 
 | ||||
|         transaction_template = ('\n{date} {t.payee}\n' | ||||
|                                 '{tags}' | ||||
|                                 '{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_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') | ||||
|                 )) for p in transaction.postings | ||||
|             ]) | ||||
|         ).encode('utf8') | ||||
| 
 | ||||
|         with open(self.ledger_file, 'ab') as f: | ||||
|             f.write(output) | ||||
|  | @ -209,12 +223,41 @@ class Ledger: | |||
|                 symbol = posting.find( | ||||
|                     './post-amount/amount/commodity/symbol').text | ||||
| 
 | ||||
|                 # Get the posting metadata | ||||
|                 metadata = {} | ||||
| 
 | ||||
|                 values = posting.findall('./metadata/value') | ||||
|                 if values: | ||||
|                     for value in values: | ||||
|                         key = value.get('key') | ||||
|                         value = value.find('./string').text | ||||
| 
 | ||||
|                         _log.debug('metadata: %s: %s', key, value) | ||||
| 
 | ||||
|                         metadata.update({key: value}) | ||||
| 
 | ||||
|                 postings.append( | ||||
|                     Posting(account=account, | ||||
|                             metadata=metadata, | ||||
|                             amount=Amount(amount=amount, symbol=symbol))) | ||||
| 
 | ||||
|             # Get the transaction metadata | ||||
|             metadata = {} | ||||
| 
 | ||||
|             values = transaction.findall('./metadata/value') | ||||
|             if values: | ||||
|                 for value in values: | ||||
|                     key = value.get('key') | ||||
|                     value = value.find('./string').text | ||||
| 
 | ||||
|                     _log.debug('metadata: %s: %s', key, value) | ||||
| 
 | ||||
|                     metadata.update({key: value}) | ||||
| 
 | ||||
|             # Add a Transaction instance to the list | ||||
|             entries.append( | ||||
|                 Transaction(date=date, payee=payee, postings=postings)) | ||||
|                 Transaction(date=date, payee=payee, postings=postings, | ||||
|                             metadata=metadata)) | ||||
| 
 | ||||
|         return entries | ||||
| 
 | ||||
|  |  | |||
|  | @ -54,6 +54,23 @@ def _recurse_accounts(accounts, level=0): | |||
|         _recurse_accounts(account.accounts, level+1) | ||||
| 
 | ||||
| 
 | ||||
| def get_register(): | ||||
|     response = requests.get(HOST + '/register') | ||||
| 
 | ||||
|     register = response.json(cls=AccountingDecoder) | ||||
| 
 | ||||
|     for transaction in register['register_report']: | ||||
|         print('{date} {t.payee:.<69}'.format( | ||||
|             date=transaction.date.strftime('%Y-%m-%d'), | ||||
|             t=transaction)) | ||||
| 
 | ||||
|         for posting in transaction.postings: | ||||
|             print(' ' + posting.account + | ||||
|                   ' ' * (80 - len(posting.account) - len(posting.amount.symbol) - | ||||
|                          len(str(posting.amount.amount)) - 1 - 1) + | ||||
|                   posting.amount.symbol + ' ' + str(posting.amount.amount)) | ||||
| 
 | ||||
| 
 | ||||
| def main(argv=None, prog=None): | ||||
|     global HOST | ||||
|     if argv is None: | ||||
|  | @ -62,11 +79,12 @@ def main(argv=None, prog=None): | |||
| 
 | ||||
|     parser = argparse.ArgumentParser(prog=prog) | ||||
|     parser.add_argument('-p', '--paypal', type=Decimal) | ||||
|     parser.add_argument('-b', '--balance', action='store_true') | ||||
|     parser.add_argument('-r', '--register', action='store_true') | ||||
|     parser.add_argument('-v', '--verbosity', | ||||
|                         default='WARNING', | ||||
|                         help=('Filter logging output. Possible values:' + | ||||
|                         ' CRITICAL, ERROR, WARNING, INFO, DEBUG')) | ||||
|     parser.add_argument('-b', '--balance', action='store_true') | ||||
|     parser.add_argument('--host', default='http://localhost:5000') | ||||
|     args = parser.parse_args(argv) | ||||
| 
 | ||||
|  | @ -78,6 +96,8 @@ def main(argv=None, prog=None): | |||
|         insert_paypal_transaction(args.paypal) | ||||
|     elif args.balance: | ||||
|         get_balance() | ||||
|     elif args.register: | ||||
|         get_register() | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     sys.exit(main()) | ||||
|  |  | |||
|  | @ -1,11 +1,20 @@ | |||
| import uuid | ||||
| from decimal import Decimal | ||||
| 
 | ||||
| 
 | ||||
| class Transaction: | ||||
|     def __init__(self, date=None, payee=None, postings=None): | ||||
|     def __init__(self, date=None, payee=None, postings=None, metadata=None, | ||||
|                  _generate_id=False): | ||||
|         self.date = date | ||||
|         self.payee = payee | ||||
|         self.postings = postings | ||||
|         self.metadata = metadata if metadata is not None else {} | ||||
| 
 | ||||
|         if _generate_id: | ||||
|             self.generate_id() | ||||
| 
 | ||||
|     def generate_id(self): | ||||
|         self.metadata.update({'Id': uuid.uuid4()}) | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return ('<{self.__class__.__name__} {date}' + | ||||
|  | @ -15,9 +24,10 @@ class Transaction: | |||
| 
 | ||||
| 
 | ||||
| class Posting: | ||||
|     def __init__(self, account=None, amount=None): | ||||
|     def __init__(self, account=None, amount=None, metadata=None): | ||||
|         self.account = account | ||||
|         self.amount = amount | ||||
|         self.metadata = metadata if metadata is not None else {} | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return ('<{self.__class__.__name__} "{self.account}"' + | ||||
|  |  | |||
|  | @ -18,13 +18,15 @@ class AccountingEncoder(json.JSONEncoder): | |||
|                 __type__=o.__class__.__name__, | ||||
|                 date=o.date.strftime('%Y-%m-%d'), | ||||
|                 payee=o.payee, | ||||
|                 postings=o.postings | ||||
|                 postings=o.postings, | ||||
|                 metadata=o.metadata | ||||
|             ) | ||||
|         elif isinstance(o, Posting): | ||||
|             return dict( | ||||
|                 __type__=o.__class__.__name__, | ||||
|                 account=o.account, | ||||
|                 amount=o.amount, | ||||
|                 metadata=o.metadata | ||||
|             ) | ||||
|         elif isinstance(o, Amount): | ||||
|             return dict( | ||||
|  |  | |||
|  | @ -1,26 +1,29 @@ | |||
| 
 | ||||
| 2010/01/01 Kindly T. Donor | ||||
|     ;Id: Ids can be anything | ||||
|    Income:Foo:Donation   $-100.00 | ||||
|       ;Invoice: Projects/Foo/Invoices/Invoice20100101.pdf | ||||
|    Assets:Checking       $100.00 | ||||
| 
 | ||||
| 
 | ||||
| 2011/03/15 Another J. Donor | ||||
|     ;Id: but mind you if they collide. | ||||
|    Income:Foo:Donation   $-400.00 | ||||
|       ;Approval: Projects/Foo/earmark-record.txt | ||||
|    Assets:Checking       $400.00 | ||||
| 
 | ||||
| 2011/04/20 (1) Baz Hosting Services, LLC | ||||
|     ;Id: always make sure your IDs are unique | ||||
|    Expenses:Foo:Hosting   $250.00 | ||||
|       ;Receipt: Projects/Foo/Expenses/hosting/AprilHostingReceipt.pdf | ||||
|    Assets:Checking       $-250.00 | ||||
| 
 | ||||
| 2011/05/10 Donation to General Fund | ||||
|     ;Id: if you have two transactions with the same ID, bad things may happen | ||||
|    Income:Donation   $-50.00 | ||||
|       ;Invoice: Financial/Invoices/Invoice20110510.pdf | ||||
|    Assets:Checking   $50.00 | ||||
| 
 | ||||
| 2011/04/20 (2) Baz Hosting Services, LLC | ||||
|     ;Id: this is probably unique | ||||
|    Expenses:Blah:Hosting   $250.00 | ||||
|       ;Receipt: Projects/Blah/Expenses/hosting/AprilHostingReceipt.pdf | ||||
|       ;Invoice: Projects/Blah/Expenses/hosting/april-invoice.pdf | ||||
|  | @ -28,29 +31,16 @@ | |||
|       ;Statement: Financial/BankStuff/bank-statement.pdf | ||||
| 
 | ||||
| 2011-04-25 A transaction with ISO date | ||||
|     ;Id: I'm a snowflake! | ||||
|    Income:Karma              KARMA-10 | ||||
|    Assets:Karma Account      KARMA 10 | ||||
| 
 | ||||
| 2013-01-01 Kindly T. Donor | ||||
|   Income:Foo:Donation                                                      $ -100 | ||||
|   Assets:Checking                                                           $ 100 | ||||
| 
 | ||||
| 2013-03-15 Another J. Donor | ||||
|   Income:Foo:Donation                                                      $ -400 | ||||
|   Assets:Checking                                                           $ 400 | ||||
| 2013-12-11 PayPal donation | ||||
|    ;Id: bd7f6781-fdc6-4111-b3ad-bee2247e426d | ||||
|   Income:Donations:PayPal                                                $ -20.17 | ||||
|   Assets:Checking                                                         $ 20.17 | ||||
| 
 | ||||
| 2013-12-11 PayPal donation | ||||
|   Income:Donations:PayPal                                                  $ -100 | ||||
|   Assets:Checking                                                           $ 100 | ||||
| 
 | ||||
| 2013-12-11 PayPal donation | ||||
|   Income:Donations:PayPal                                                 $ -1000 | ||||
|   Assets:Checking                                                          $ 1000 | ||||
| 
 | ||||
| 2013-12-11 PayPal donation | ||||
|   Income:Donations:PayPal                                                 $ -0.25 | ||||
|   Assets:Checking                                                          $ 0.25 | ||||
| 
 | ||||
| 2013-12-11 PayPal donation | ||||
|   Income:Donations:PayPal                                                $ -0.252 | ||||
|   Assets:Checking                                                         $ 0.252 | ||||
|    ;Id: 31048b9d-a5b6-41d7-951a-e7128e7c53c0 | ||||
|   Income:Donations:PayPal                                                $ -20.18 | ||||
|   Assets:Checking                                                         $ 20.18 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Joar Wandborg
						Joar Wandborg