Began project refactoring.
This commit is contained in:
0
api/__init__.py
Normal file
0
api/__init__.py
Normal file
0
api/controller/__init__.py
Normal file
0
api/controller/__init__.py
Normal file
145
api/controller/accounts.py
Normal file
145
api/controller/accounts.py
Normal file
@ -0,0 +1,145 @@
|
||||
"""
|
||||
This file is part of Accountant.
|
||||
|
||||
Accountant is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Foobar is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from app import app
|
||||
from app import db
|
||||
|
||||
from api.model.accounts import Account
|
||||
from api.model.entries import Entry
|
||||
from api.model.operations import Operation
|
||||
|
||||
from flask import json, request
|
||||
|
||||
from sqlalchemy import func, case, cast, extract, distinct
|
||||
|
||||
@app.route("/api/accounts", methods=["GET"])
|
||||
def get_accounts():
|
||||
"""
|
||||
Returns accounts with their solds.
|
||||
"""
|
||||
session = db.session
|
||||
|
||||
query = session.query(
|
||||
Account.id.label("id"),
|
||||
Account.name.label("name"),
|
||||
Account.authorized_overdraft.label("authorized_overdraft"),
|
||||
func.sum(Entry.value).label("future"),
|
||||
func.sum(case([(Entry.pointed, Entry.value,)], else_=cast(0, db.Numeric(15, 2)))).label("pointed"),
|
||||
func.sum(case([(Entry.operation_date < func.now(), Entry.value,)], else_=cast(0, db.Numeric(15, 2)))).label("current")
|
||||
).outerjoin(Entry).group_by(Account.id).order_by(Account.id)
|
||||
|
||||
return json.dumps([{
|
||||
"id": i.id,
|
||||
"name": i.name,
|
||||
"authorized_overdraft": i.authorized_overdraft,
|
||||
"current": str(i.current),
|
||||
"pointed": str(i.pointed),
|
||||
"future": str(i.future)
|
||||
} for i in query.all()])
|
||||
|
||||
@app.route("/api/accounts/<account_id>/<year>/<month>/")
|
||||
def get_account_status(account_id, year, month):
|
||||
session = db.session
|
||||
|
||||
query = session.query(
|
||||
func.sum(case([(func.sign(Operation.value) == -1, Operation.value)], else_=0)).label("expenses"),
|
||||
func.sum(case([(func.sign(Operation.value) == 1, Operation.value)], else_=0)).label("revenues"),
|
||||
func.sum(Operation.value).label("balance")
|
||||
).filter(
|
||||
Operation.account_id == account_id
|
||||
).filter(
|
||||
func.date_trunc('month', Operation.operation_date) == "%s-%s-01" % (year, month)
|
||||
).group_by(Operation.account_id)
|
||||
|
||||
if query.count() == 1:
|
||||
result = query.one()
|
||||
revenues = result.revenues
|
||||
expenses = result.expenses
|
||||
balance = result.balance
|
||||
else:
|
||||
revenues = 0.0
|
||||
expenses = 0.0
|
||||
balance = 0.0
|
||||
|
||||
return json.dumps({
|
||||
"expenses": str(expenses),
|
||||
"revenues": str(revenues),
|
||||
"balance": str(balance)
|
||||
})
|
||||
|
||||
@app.route("/api/accounts/<account_id>/months")
|
||||
def get_months(account_id):
|
||||
session = db.session
|
||||
|
||||
query = session.query(
|
||||
distinct(func.lpad(cast(extract("year", Entry.operation_date), db.String), 4, '0')).label("year"),
|
||||
func.lpad(cast(extract("month", Entry.operation_date), db.String), 2, '0').label("month")
|
||||
).filter(Entry.account_id == account_id).order_by("year", "month")
|
||||
|
||||
return json.dumps([{
|
||||
"year": i.year,
|
||||
"month": i.month.rjust(2, '0')
|
||||
} for i in query.all()])
|
||||
|
||||
@app.route("/api/accounts", methods=["PUT"])
|
||||
def add_account():
|
||||
session = db.session
|
||||
|
||||
try:
|
||||
account = Account(request.json['name'], request.json['authorized_overdraft'])
|
||||
|
||||
session.add(account)
|
||||
session.commit()
|
||||
|
||||
return json.dumps("Account added.")
|
||||
except:
|
||||
session.rollback()
|
||||
raise
|
||||
|
||||
|
||||
@app.route("/api/accounts/<account_id>", methods=["PUT"])
|
||||
def update_account(account_id):
|
||||
session = db.session
|
||||
|
||||
try:
|
||||
account = session.query(Account).filter(Account.id == account_id).first()
|
||||
|
||||
account.name = request.json['name']
|
||||
account.authorized_overdraft = request.json['authorized_overdraft']
|
||||
|
||||
session.merge(account)
|
||||
session.commit()
|
||||
|
||||
return json.dumps("Account #%s updated." % account_id)
|
||||
except:
|
||||
session.rollback()
|
||||
raise
|
||||
|
||||
@app.route("/api/accounts/<account_id>", methods=["DELETE"])
|
||||
def delete_account(account_id):
|
||||
session = db.session
|
||||
|
||||
try:
|
||||
account = session.query(Account).filter(Account.id == account_id).first()
|
||||
|
||||
session.delete(account)
|
||||
session.commit()
|
||||
|
||||
return json.dumps("Account #%s deleted." % account_id)
|
||||
except:
|
||||
session.rollback()
|
||||
raise
|
||||
|
125
api/controller/entries.py
Normal file
125
api/controller/entries.py
Normal file
@ -0,0 +1,125 @@
|
||||
"""
|
||||
This file is part of Accountant.
|
||||
|
||||
Accountant is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Foobar is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from app import app
|
||||
from app import db
|
||||
|
||||
from api.model.entries import Entry
|
||||
from api.model.operations import Operation
|
||||
from api.model.scheduled_operations import ScheduledOperation
|
||||
|
||||
from sqlalchemy import func, desc
|
||||
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
|
||||
from sqlalchemy.orm import sessionmaker, column_property, aliased
|
||||
from sqlalchemy.sql import func, select, case
|
||||
|
||||
#from sqlalchemy import *
|
||||
|
||||
from flask import json, request
|
||||
|
||||
@app.route("/api/entries/<account_id>/<year>/<month>")
|
||||
def get_entries(account_id, year, month):
|
||||
"""
|
||||
Return entries for an account, year, and month.
|
||||
"""
|
||||
session = db.session
|
||||
|
||||
base_query = session.query(
|
||||
Operation,
|
||||
case(whens={Operation.canceled: None}, else_=func.sum(Operation.value).over(partition_by="canceled", order_by="operation_date, value desc, label desc")).label("sold")
|
||||
).filter(Operation.account_id == account_id).order_by(
|
||||
desc(Operation.operation_date),
|
||||
Operation.value,
|
||||
Operation.label,
|
||||
).subquery()
|
||||
|
||||
query = session.query(base_query).select_from(base_query).filter(func.date_trunc('month', base_query.c.operation_date) == "%s-%s-01" % (year, month))
|
||||
|
||||
return json.dumps([{
|
||||
"id": i.id,
|
||||
"pointed": i.pointed,
|
||||
"operation_date": i.operation_date.strftime("%Y-%m-%d"),
|
||||
"label": i.label,
|
||||
"value": str(i.value),
|
||||
"category": i.category,
|
||||
"sold": str(i.sold) if not i.canceled else None,
|
||||
"account_id": i.account_id,
|
||||
"canceled": i.canceled,
|
||||
"scheduled_operation_id": i.scheduled_operation_id
|
||||
} for i in query.all()])
|
||||
|
||||
@app.route("/api/entries", methods=["PUT"])
|
||||
def add_entry():
|
||||
session = db.session
|
||||
|
||||
try:
|
||||
entry = Entry(
|
||||
operation_date = request.json['operation_date'],
|
||||
pointed = request.json['pointed'],
|
||||
label = request.json['label'],
|
||||
value = request.json['value'],
|
||||
category = request.json['category'],
|
||||
account_id = request.json['account_id'],
|
||||
scheduled_operation_id = request.json['scheduled_operation_id']
|
||||
)
|
||||
|
||||
session.add(entry)
|
||||
session.commit()
|
||||
|
||||
return json.dumps("Entry added.")
|
||||
except:
|
||||
session.rollback()
|
||||
raise
|
||||
|
||||
@app.route("/api/entries/<entry_id>", methods=["PUT"])
|
||||
def update_entry(entry_id):
|
||||
session = db.session
|
||||
|
||||
try:
|
||||
entry = session.query(Entry).filter(Entry.id == entry_id).first()
|
||||
|
||||
entry.id = entry_id
|
||||
entry.operation_date = request.json['operation_date']
|
||||
entry.pointed = request.json['pointed']
|
||||
entry.label = request.json['label']
|
||||
entry.value = request.json['value']
|
||||
entry.category = request.json['category']
|
||||
entry.account_id = request.json['account_id']
|
||||
entry.scheduled_operation_id = request.json['scheduled_operation_id']
|
||||
|
||||
session.merge(entry)
|
||||
session.commit()
|
||||
|
||||
return json.dumps("Entry #%s updated." % entry_id)
|
||||
except:
|
||||
session.rollback()
|
||||
raise
|
||||
|
||||
@app.route("/api/entries/<entry_id>", methods=["DELETE"])
|
||||
def delete_entry(entry_id):
|
||||
session = db.session
|
||||
|
||||
try:
|
||||
entry = session.query(Entry).filter(Entry.id == entry_id).first()
|
||||
|
||||
session.delete(entry)
|
||||
session.commit()
|
||||
|
||||
return json.dumps("Entry #%s deleted." % entry_id)
|
||||
except:
|
||||
session.rollback()
|
||||
raise
|
||||
|
107
api/controller/scheduled_operations.py
Normal file
107
api/controller/scheduled_operations.py
Normal file
@ -0,0 +1,107 @@
|
||||
from app import app
|
||||
from app import db
|
||||
|
||||
from api.model.scheduled_operations import ScheduledOperation
|
||||
|
||||
from sqlalchemy import func, desc
|
||||
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
|
||||
from sqlalchemy.orm import sessionmaker, column_property
|
||||
from sqlalchemy.sql import func, select
|
||||
|
||||
from flask import json, request
|
||||
|
||||
@app.route("/api/scheduled_operations/<account_id>")
|
||||
def get_scheduled_operations(account_id):
|
||||
"""
|
||||
Return entries for an account, year, and month.
|
||||
"""
|
||||
session = db.session
|
||||
|
||||
query = session.query(
|
||||
ScheduledOperation
|
||||
).select_from(
|
||||
session.query(ScheduledOperation)
|
||||
.filter(ScheduledOperation.account_id == account_id)
|
||||
.order_by(
|
||||
desc(ScheduledOperation.day),
|
||||
ScheduledOperation.value,
|
||||
ScheduledOperation.label,
|
||||
).subquery()
|
||||
)
|
||||
|
||||
return json.dumps([{
|
||||
"id": i.id,
|
||||
"start_date": i.start_date.strftime("%Y-%m-%d"),
|
||||
"stop_date": i.stop_date.strftime("%Y-%m-%d"),
|
||||
"day": str(i.day),
|
||||
"frequency": str(i.frequency),
|
||||
"label": i.label,
|
||||
"value": str(i.value),
|
||||
"category": i.category,
|
||||
"account_id": i.account_id
|
||||
} for i in query.all()])
|
||||
|
||||
@app.route("/api/scheduled_operations", methods=["PUT"])
|
||||
def add_scheduled_operation():
|
||||
session = db.session
|
||||
|
||||
try:
|
||||
scheduledOperation = ScheduledOperation(
|
||||
start_date = request.json['start_date'],
|
||||
stop_date = request.json['stop_date'],
|
||||
day = request.json['day'],
|
||||
frequency = request.json['frequency'],
|
||||
label = request.json['label'],
|
||||
value = request.json['value'],
|
||||
category = request.json['category'],
|
||||
account_id = request.json['account_id']
|
||||
)
|
||||
|
||||
session.add(scheduledOperation)
|
||||
session.commit()
|
||||
|
||||
return json.dumps("Scheduled operation added.")
|
||||
except:
|
||||
session.rollback()
|
||||
raise
|
||||
|
||||
@app.route("/api/scheduled_operations/<scheduled_operation_id>", methods=["PUT"])
|
||||
def update_scheduled_operation(scheduled_operation_id):
|
||||
session = db.session
|
||||
|
||||
try:
|
||||
scheduledOperation = session.query(ScheduledOperation).filter(ScheduledOperation.id == scheduled_operation_id).first()
|
||||
|
||||
scheduledOperation.id = scheduled_operation_id
|
||||
scheduledOperation.start_date = request.json['start_date'],
|
||||
scheduledOperation.stop_date = request.json['stop_date'],
|
||||
scheduledOperation.day = request.json['day'],
|
||||
scheduledOperation.frequency = request.json['frequency'],
|
||||
scheduledOperation.label = request.json['label']
|
||||
scheduledOperation.value = request.json['value']
|
||||
scheduledOperation.category = request.json['category']
|
||||
scheduledOperation.account_id = request.json['account_id']
|
||||
|
||||
session.merge(scheduledOperation)
|
||||
session.commit()
|
||||
|
||||
return json.dumps("Scheduled operation #%s updated." % scheduled_operation_id)
|
||||
except:
|
||||
session.rollback()
|
||||
raise
|
||||
|
||||
@app.route("/api/scheduled_operations/<scheduled_operation_id>", methods=["DELETE"])
|
||||
def delete_scheduled_operation(scheduled_operation_id):
|
||||
session = db.session
|
||||
|
||||
try:
|
||||
scheduledOperation = session.query(ScheduledOperation).filter(ScheduledOperation.id == scheduled_operation_id).first()
|
||||
|
||||
session.delete(scheduledOperation)
|
||||
session.commit()
|
||||
|
||||
return json.dumps("Scheduled operation #%s deleted." % scheduled_operation_id)
|
||||
except:
|
||||
session.rollback()
|
||||
raise
|
||||
|
0
api/model/__init__.py
Normal file
0
api/model/__init__.py
Normal file
28
api/model/accounts.py
Normal file
28
api/model/accounts.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""
|
||||
This file is part of Accountant.
|
||||
|
||||
Accountant is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Foobar is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from app import app
|
||||
from app import db
|
||||
|
||||
class Account(db.Model):
|
||||
id = db.Column(db.Integer, primary_key = True)
|
||||
name = db.Column(db.String(200), nullable = False)
|
||||
authorized_overdraft = db.Column(db.Integer, nullable = True, default = 0)
|
||||
|
||||
def __init__(self, name, authorized_overdraft):
|
||||
self.name = name
|
||||
self.authorized_overdraft = authorized_overdraft
|
||||
|
49
api/model/entries.py
Normal file
49
api/model/entries.py
Normal file
@ -0,0 +1,49 @@
|
||||
"""
|
||||
This file is part of Accountant.
|
||||
|
||||
Accountant is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Foobar is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from app import app
|
||||
from app import db
|
||||
|
||||
from api.model.accounts import Account
|
||||
from api.model.scheduled_operations import ScheduledOperation
|
||||
|
||||
from sqlalchemy import func, desc
|
||||
from sqlalchemy.orm import column_property
|
||||
from sqlalchemy.sql import func, select
|
||||
|
||||
class Entry(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
pointed = db.Column(db.Boolean, nullable = False, default = False)
|
||||
operation_date = db.Column(db.Date, nullable = False)
|
||||
label = db.Column(db.String(500), nullable = False)
|
||||
value = db.Column(db.Numeric(15, 2), nullable = False)
|
||||
account_id = db.Column(db.Integer, db.ForeignKey('account.id'))
|
||||
scheduled_operation_id = db.Column(db.Integer, db.ForeignKey('scheduled_operation.id'))
|
||||
|
||||
account = db.relationship(Account, backref = db.backref('entry', lazy="Dynamic"))
|
||||
scheduled_operation = db.relationship(ScheduledOperation, backref = db.backref('entry', lazy="Dynamic"))
|
||||
|
||||
category = db.Column(db.String(100), nullable = True)
|
||||
|
||||
def __init__(self, pointed, label, value, account_id, operation_date = None, category = None, scheduled_operation_id = None):
|
||||
self.pointed = pointed
|
||||
self.operation_date = operation_date
|
||||
self.label = label
|
||||
self.value = value
|
||||
self.account_id = account_id
|
||||
self.category = category
|
||||
self.scheduled_operation_id = scheduled_operation_id
|
||||
|
49
api/model/operations.py
Normal file
49
api/model/operations.py
Normal file
@ -0,0 +1,49 @@
|
||||
"""
|
||||
This file is part of Accountant.
|
||||
|
||||
Accountant is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Foobar is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from app import app
|
||||
from app import db
|
||||
|
||||
from api.model.accounts import Account
|
||||
from api.model.scheduled_operations import ScheduledOperation
|
||||
|
||||
from sqlalchemy import func, desc
|
||||
from sqlalchemy.orm import column_property
|
||||
from sqlalchemy.sql import func, select
|
||||
|
||||
class Operation(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
pointed = db.Column(db.Boolean, nullable = False, default = False)
|
||||
operation_date = db.Column(db.Date, nullable = False)
|
||||
label = db.Column(db.String(500), nullable = False)
|
||||
value = db.Column(db.Numeric(15, 2), nullable = False)
|
||||
account_id = db.Column(db.Integer, db.ForeignKey('account.id'))
|
||||
scheduled_operation_id = db.Column(db.Integer, db.ForeignKey('scheduled_operation.id'))
|
||||
|
||||
account = db.relationship(Account, backref = db.backref('operation', lazy="Dynamic"))
|
||||
|
||||
category = db.Column(db.String(100), nullable = True)
|
||||
|
||||
canceled = db.Column(db.Boolean, nullable = False)
|
||||
|
||||
def __init__(self, pointed, label, value, account_id, operation_date = None, category = None):
|
||||
self.pointed = pointed
|
||||
self.operation_date = operation_date
|
||||
self.label = label
|
||||
self.value = value
|
||||
self.account_id = account_id
|
||||
self.category = category
|
||||
|
33
api/model/scheduled_operations.py
Normal file
33
api/model/scheduled_operations.py
Normal file
@ -0,0 +1,33 @@
|
||||
from app import app
|
||||
from app import db
|
||||
|
||||
from api.model.accounts import Account
|
||||
|
||||
from sqlalchemy import func, desc
|
||||
from sqlalchemy.orm import column_property
|
||||
from sqlalchemy.sql import func, select
|
||||
|
||||
class ScheduledOperation(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
start_date = db.Column(db.Date, nullable = False)
|
||||
stop_date = db.Column(db.Date, nullable = False)
|
||||
day = db.Column(db.Integer, nullable = False)
|
||||
frequency = db.Column(db.Integer, nullable = False)
|
||||
label = db.Column(db.String(500), nullable = False)
|
||||
value = db.Column(db.Numeric(15, 2), nullable = False)
|
||||
account_id = db.Column(db.Integer, db.ForeignKey('account.id'))
|
||||
|
||||
account = db.relationship(Account, backref = db.backref('scheduled_operation', lazy="Dynamic"))
|
||||
|
||||
category = db.Column(db.String(100), nullable = True)
|
||||
|
||||
def __init__(self, start_date, stop_date, day, frequency, label, value, account_id, category = None):
|
||||
self.start_date = start_date
|
||||
self.stop_date = stop_date
|
||||
self.day = day
|
||||
self.frequency = frequency
|
||||
self.label = label
|
||||
self.value = value
|
||||
self.account_id = account_id
|
||||
self.category = category
|
||||
|
Reference in New Issue
Block a user