diff --git a/accountant/api/model/accounts.py b/accountant/api/model/accounts.py
index 5d79d68..1bb7d6c 100644
--- a/accountant/api/model/accounts.py
+++ b/accountant/api/model/accounts.py
@@ -14,8 +14,12 @@
You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see .
"""
+from sqlalchemy import func, case, cast
+
from accountant import db
+from .entries import Entry
+
class Account(db.Model):
id = db.Column(db.Integer, primary_key=True)
@@ -25,3 +29,18 @@ class Account(db.Model):
def __init__(self, name, authorized_overdraft):
self.name = name
self.authorized_overdraft = authorized_overdraft
+
+ @classmethod
+ def get_accounts(cls, session):
+ query = session.query(
+ cls.id.label("id"),
+ cls.name.label("name"),
+ cls.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(cls.id).order_by(cls.id)
+
+ return query
diff --git a/accountant/api/model/entries.py b/accountant/api/model/entries.py
index a459903..df64e8d 100644
--- a/accountant/api/model/entries.py
+++ b/accountant/api/model/entries.py
@@ -14,10 +14,9 @@
You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see .
"""
-from accountant import db
-from .accounts import Account
-from .scheduled_operations import ScheduledOperation
+from sqlalchemy import distinct, func, cast, extract
+from accountant import db
class Entry(db.Model):
@@ -30,9 +29,9 @@ class Entry(db.Model):
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,
+ account = db.relationship("Account", backref=db.backref('entry',
+ lazy="dynamic"))
+ scheduled_operation = db.relationship("ScheduledOperation",
backref=db.backref('entry',
lazy="dynamic"))
@@ -47,3 +46,19 @@ class Entry(db.Model):
self.account_id = account_id
self.category = category
self.scheduled_operation_id = scheduled_operation_id
+
+ @classmethod
+ def get_months_for_account(cls, session, account):
+ if isinstance(account, int) or isinstance(account, str):
+ account_id = account
+ else:
+ account_id = account.id
+
+ query = session.query(
+ distinct(func.lpad(cast(extract("year", cls.operation_date),
+ db.String), 4, '0')).label("year"),
+ func.lpad(cast(extract("month", cls.operation_date),
+ db.String), 2, '0').label("month")
+ ).filter(cls.account_id == account_id).order_by("year", "month")
+
+ return query
diff --git a/accountant/api/model/operations.py b/accountant/api/model/operations.py
index a92b482..3b212fd 100644
--- a/accountant/api/model/operations.py
+++ b/accountant/api/model/operations.py
@@ -14,6 +14,8 @@
You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see .
"""
+from sqlalchemy import func, case, desc
+
from accountant import db
from .accounts import Account
@@ -44,3 +46,52 @@ class Operation(db.Model):
self.value = value
self.account_id = account_id
self.category = category
+
+ @classmethod
+ def get_for_account_and_month(cls, session, account, year, month):
+ if isinstance(account, int) or isinstance(account, str):
+ account_id = account
+ else:
+ account_id = account.id
+
+ base_query = session.query(
+ cls,
+ case(
+ whens={cls.canceled: None},
+ else_=func.sum(cls.value).over(
+ partition_by="canceled",
+ order_by=["operation_date", desc("value"), desc("label")])
+ ).label("sold")
+ ).filter(cls.account_id == account_id).order_by(
+ desc(cls.operation_date),
+ cls.value,
+ cls.label,
+ ).subquery()
+
+ query = session.query(base_query).select_from(base_query)
+ query = query.filter(func.date_trunc(
+ 'month', base_query.c.operation_date) == "%s-%s-01" % (year, month))
+
+ return query
+
+ @classmethod
+ def get_account_status(cls, session, account, year, month):
+ if isinstance(account, int) or isinstance(account, str):
+ account_id = account
+ else:
+ account_id = account.id
+
+ query = session.query(
+ func.sum(case([(func.sign(cls.value) == -1, cls.value)],
+ else_=0)).label("expenses"),
+ func.sum(case([(func.sign(cls.value) == 1, cls.value)],
+ else_=0)).label("revenues"),
+ func.sum(cls.value).label("balance")
+ ).filter(
+ cls.account_id == account_id
+ ).filter(
+ func.date_trunc('month',
+ cls.operation_date) == "%s-%s-01" % (year, month)
+ ).group_by(cls.account_id)
+
+ return query
diff --git a/accountant/api/model/scheduled_operations.py b/accountant/api/model/scheduled_operations.py
index b0a837d..4eaad97 100644
--- a/accountant/api/model/scheduled_operations.py
+++ b/accountant/api/model/scheduled_operations.py
@@ -14,6 +14,8 @@
You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see .
"""
+from sqlalchemy import desc
+
from accountant import db
from .accounts import Account
@@ -44,3 +46,24 @@ class ScheduledOperation(db.Model):
self.value = value
self.account_id = account_id
self.category = category
+
+ @classmethod
+ def get_scheduled_operations_for_account(cls, session, account):
+ if isinstance(account, int) or isinstance(account, str):
+ account_id = account
+ else:
+ account_id = account.id
+
+ query = session.query(
+ cls
+ ).select_from(
+ session.query(cls)
+ .filter(cls.account_id == account_id)
+ .order_by(
+ desc(cls.day),
+ cls.value,
+ cls.label,
+ ).subquery()
+ )
+
+ return query
diff --git a/accountant/api/views/accounts.py b/accountant/api/views/accounts.py
index 4c6951f..46dde6f 100644
--- a/accountant/api/views/accounts.py
+++ b/accountant/api/views/accounts.py
@@ -16,11 +16,9 @@
"""
from flask import json, request
-from sqlalchemy import func, case, cast, extract, distinct
-
from forms.accounts import AccountForm
-from accountant import db, session_scope
+from accountant import session_scope
from . import auth
from .. import api
@@ -37,16 +35,7 @@ def get_accounts():
Returns accounts with their solds.
"""
with session_scope() as 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)
+ query = Account.get_accounts(session)
return json.dumps([{
"id": i.id,
@@ -62,19 +51,7 @@ def get_accounts():
@auth.login_required
def get_account_status(account_id, year, month):
with session_scope() as 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)
+ query = Operation.get_account_status(session, account_id, year, month)
if query.count() == 1:
result = query.one()
@@ -97,12 +74,7 @@ def get_account_status(account_id, year, month):
@auth.login_required
def get_months(account_id):
with session_scope() as 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")
+ query = Entry.get_months_for_account(session, account_id)
return json.dumps([{
"year": i.year,
@@ -129,10 +101,7 @@ def update_account(account_id):
if account_form.validate():
with session_scope() as session:
- query = session.query(Account)
- query = query.filter(Account.id == account_id)
-
- account = query.first()
+ account = session.query(Account).get(account_id)
account.name = request.json['name']
account.authorized_overdraft = request.json['authorized_overdraft']
diff --git a/accountant/api/views/entries.py b/accountant/api/views/entries.py
index 370ce2f..994a793 100644
--- a/accountant/api/views/entries.py
+++ b/accountant/api/views/entries.py
@@ -16,8 +16,6 @@
"""
from flask import json, request
-from sqlalchemy import func, case, desc
-
from accountant import session_scope
from .. import api
@@ -32,23 +30,8 @@ def get_entries(account_id, year, month):
Return entries for an account, year, and month.
"""
with session_scope() as session:
- base_query = session.query(
- Operation,
- case(
- whens={Operation.canceled: None},
- else_=func.sum(Operation.value).over(
- partition_by="canceled",
- order_by=["operation_date", desc("value"), desc("label")])
- ).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)
- query = query.filter(func.date_trunc(
- 'month', base_query.c.operation_date) == "%s-%s-01" % (year, month))
+ query = Operation.get_for_account_and_month(session, account_id, year,
+ month)
return json.dumps([{
"id": i.id,
@@ -85,7 +68,7 @@ def add_entry():
@api.route("/entries/", methods=["PUT"])
def update_entry(entry_id):
with session_scope() as session:
- entry = session.query(Entry).filter(Entry.id == entry_id).first()
+ entry = session.query(Entry).get(entry_id)
entry.id = entry_id
entry.operation_date = request.json['operation_date']
diff --git a/accountant/api/views/scheduled_operations.py b/accountant/api/views/scheduled_operations.py
index 0df8042..26bde80 100644
--- a/accountant/api/views/scheduled_operations.py
+++ b/accountant/api/views/scheduled_operations.py
@@ -16,8 +16,6 @@
"""
from flask import json, request
-from sqlalchemy import desc
-
from accountant import session_scope
from .. import api
@@ -30,17 +28,8 @@ def get_scheduled_operations(account_id):
Return entries for an account, year, and month.
"""
with session_scope() as 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()
- )
+ query = ScheduledOperation.get_scheduled_operations_for_account(
+ session, account_id)
return json.dumps([{
"id": i.id,