You've already forked double-entry-accounting
161 lines
5.2 KiB
Python
161 lines
5.2 KiB
Python
import cherrypy
|
|
from sqlalchemy.orm import sessionmaker
|
|
from datetime import datetime
|
|
from accounting.models import Account, BankAccount, BankTransaction, JournalEntry, JournalEntryLine, ReconciliationReport, \
|
|
ReconciliationMatch
|
|
import simplejson as json
|
|
|
|
|
|
class AccountingAPI:
|
|
def __init__(self, db_engine):
|
|
self.db_engine = db_engine
|
|
Session = sessionmaker(bind=db_engine)
|
|
self.session = Session()
|
|
|
|
@cherrypy.expose
|
|
@cherrypy.tools.json_out()
|
|
def index(self):
|
|
return {"status": "success", "message": "Accounting API is running"}
|
|
|
|
# Bank Accounts Endpoints
|
|
@cherrypy.expose
|
|
@cherrypy.tools.json_out()
|
|
def bank_accounts(self):
|
|
if cherrypy.request.method != 'GET':
|
|
raise cherrypy.HTTPError(405)
|
|
|
|
accounts = self.session.query(BankAccount).all()
|
|
return [{
|
|
'id': a.id,
|
|
'name': a.name,
|
|
'bank_name': a.bank_name,
|
|
'account_number': a.account_number,
|
|
'currency': a.currency
|
|
} for a in accounts]
|
|
|
|
@cherrypy.expose
|
|
@cherrypy.tools.json_in()
|
|
@cherrypy.tools.json_out()
|
|
def add_bank_account(self):
|
|
if cherrypy.request.method != 'POST':
|
|
raise cherrypy.HTTPError(405)
|
|
|
|
data = cherrypy.request.json
|
|
account = BankAccount(
|
|
name=data['name'],
|
|
bank_name=data['bank_name'],
|
|
account_number=data['account_number'],
|
|
currency=data.get('currency', 'USD')
|
|
)
|
|
self.session.add(account)
|
|
self.session.commit()
|
|
return {'status': 'success', 'id': account.id}
|
|
|
|
# Add OPTIONS method handler for CORS preflight
|
|
@cherrypy.expose
|
|
def add_bank_account_options(self):
|
|
cherrypy.response.headers['Allow'] = 'POST, OPTIONS'
|
|
cherrypy.response.headers['Access-Control-Allow-Headers'] = 'Content-Type'
|
|
return ''
|
|
|
|
# Accounts Endpoints
|
|
@cherrypy.expose
|
|
@cherrypy.tools.json_out()
|
|
def accounts(self):
|
|
if cherrypy.request.method != 'GET':
|
|
raise cherrypy.HTTPError(405)
|
|
|
|
accounts = self.session.query(Account).all()
|
|
return [{
|
|
'id': a.id,
|
|
'name': a.name,
|
|
'code': a.code,
|
|
'type': a.account_type,
|
|
'balance': float(a.balance) if a.balance else 0
|
|
} for a in accounts]
|
|
|
|
@cherrypy.expose
|
|
@cherrypy.tools.json_in()
|
|
@cherrypy.tools.json_out()
|
|
def add_account(self):
|
|
if cherrypy.request.method != 'POST':
|
|
raise cherrypy.HTTPError(405)
|
|
|
|
data = cherrypy.request.json
|
|
account = Account(
|
|
name=data['name'],
|
|
account_type=data['type'],
|
|
code=data['code'],
|
|
parent_id=data.get('parent_id')
|
|
)
|
|
self.session.add(account)
|
|
self.session.commit()
|
|
return {'status': 'success', 'id': account.id}
|
|
|
|
# Journal Entries Endpoints
|
|
@cherrypy.expose
|
|
@cherrypy.tools.json_in()
|
|
@cherrypy.tools.json_out()
|
|
def add_journal_entry(self):
|
|
if cherrypy.request.method != 'POST':
|
|
raise cherrypy.HTTPError(405)
|
|
|
|
data = cherrypy.request.json
|
|
total_debit = sum(line['amount'] for line in data['lines'] if line['is_debit'])
|
|
total_credit = sum(line['amount'] for line in data['lines'] if not line['is_debit'])
|
|
|
|
if abs(total_debit - total_credit) > 0.01:
|
|
return {'status': 'error', 'message': 'Debits and credits must balance'}
|
|
|
|
entry = JournalEntry(
|
|
date=datetime.strptime(data['date'], '%Y-%m-%d').date(),
|
|
reference=data.get('reference', ''),
|
|
description=data.get('description', '')
|
|
)
|
|
self.session.add(entry)
|
|
|
|
for line_data in data['lines']:
|
|
line = JournalEntryLine(
|
|
journal_entry=entry,
|
|
account_id=line_data['account_id'],
|
|
amount=line_data['amount'],
|
|
is_debit=line_data['is_debit']
|
|
)
|
|
self.session.add(line)
|
|
|
|
account = self.session.query(Account).get(line_data['account_id'])
|
|
if line_data['is_debit']:
|
|
account.balance += line_data['amount']
|
|
else:
|
|
account.balance -= line_data['amount']
|
|
|
|
self.session.commit()
|
|
return {'status': 'success', 'id': entry.id}
|
|
|
|
@cherrypy.expose
|
|
@cherrypy.tools.json_out()
|
|
def journal_entries(self):
|
|
if cherrypy.request.method != 'GET':
|
|
raise cherrypy.HTTPError(405)
|
|
|
|
entries = self.session.query(JournalEntry).all()
|
|
return [{
|
|
'id': e.id,
|
|
'date': e.date.isoformat(),
|
|
'description': e.description,
|
|
'reference': e.reference,
|
|
'lines': [{
|
|
'account_id': l.account_id,
|
|
'amount': float(l.amount),
|
|
'is_debit': l.is_debit
|
|
} for l in e.lines]
|
|
} for e in entries]
|
|
|
|
# Add default OPTIONS handler for all endpoints
|
|
@cherrypy.expose
|
|
def default(self, *args, **kwargs):
|
|
if cherrypy.request.method == 'OPTIONS':
|
|
cherrypy.response.headers['Allow'] = 'GET, POST, OPTIONS'
|
|
cherrypy.response.headers['Access-Control-Allow-Headers'] = 'Content-Type'
|
|
return ''
|
|
raise cherrypy.HTTPError(404) |