summaryrefslogtreecommitdiff
path: root/kosten/app/views/consts.py
diff options
context:
space:
mode:
Diffstat (limited to 'kosten/app/views/consts.py')
-rw-r--r--kosten/app/views/consts.py174
1 files changed, 174 insertions, 0 deletions
diff --git a/kosten/app/views/consts.py b/kosten/app/views/consts.py
new file mode 100644
index 0000000..5d6598d
--- /dev/null
+++ b/kosten/app/views/consts.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+from . import Blueprint, flash, db, \
+ current_user, login_required, \
+ assert_authorisation, templated, redirect, request, \
+ today
+
+from ..model import Category, ConstExpense
+from .. import forms as F
+
+from sqlalchemy import sql
+from functools import partial
+
+mod = Blueprint('consts', __name__)
+assert_authorisation = partial(assert_authorisation, ConstExpense.get)
+
+def one_year(d):
+ """Returns the date d', such that [d, d'] spans exactly one year.
+ In effect, this is d + 11 months (NB: not 12!)"""
+ if d.month == 1:
+ return d.replace(month = 12)
+ else:
+ return d.replace(month = d.month - 1, year = d.year + 1)
+
+#
+# Form
+#
+class ConstForm(F.Form):
+ start = F.MonthField('Beginn', F.req,
+ default=lambda: today())
+
+ end = F.MonthField('Ende', F.req,
+ default=lambda: one_year(today()),
+ description='(einschließlich)')
+
+ months = F.IntegerField('Zahlungsrythmus', F.req,
+ description='Monate')
+
+ expense = F.DecimalField('Betrag', F.req,
+ description='EUR',
+ places=2)
+
+ description = F.StringField('Beschreibung', F.req)
+
+ category = F.QuerySelectField('Kategorie',
+ get_label='name',
+ get_pk=lambda c: c.id)
+
+ prev = F.QuerySelectField('Vorgänger',
+ get_label='description',
+ allow_blank=True,
+ get_pk=lambda p: p.id)
+
+ def __init__(self, cur=None, obj=None):
+ obj = cur if obj is None else obj
+ super().__init__(obj=obj)
+ self.category.query = Category.of(current_user).order_by(Category.name)
+
+ # init prev_list
+ CE = ConstExpense
+
+ filter = (CE.next == None)
+
+ if cur and cur.id is not None: # not empty
+ filter = sql.or_(CE.next == cur, filter)
+ filter = sql.and_(filter, CE.id != cur.id)
+
+ self.prev.query = CE.of(current_user).filter(filter).order_by(CE.description)
+
+#
+# Views
+#
+@mod.route('/')
+@login_required
+@templated
+def list ():
+ """List all constant expenses."""
+ d = today().replace(day = 1)
+
+ expenses = ConstExpense.of(current_user).order_by(ConstExpense.description).all()
+
+ current = []
+ old = []
+ future = []
+ last_month = []
+
+ for e in expenses:
+ if e.start <= d:
+ if e.end >= d:
+ current.append(e)
+ else:
+ if (d.month == 1 and e.end.month == 12 and e.end.year == d.year - 1) \
+ or (e.end.year == d.year and e.end.month == d.month - 1):
+ last_month.append(e)
+ else:
+ old.append(e)
+ else:
+ future.append(e)
+
+ return { 'current': current, 'old': old, 'future': future, 'last_month': last_month }
+
+
+@mod.route('/<int:id>')
+@login_required
+@assert_authorisation('id')
+@templated
+def show(id):
+ """Show a specific constant expense."""
+ return { 'exp': ConstExpense.get(id) }
+
+
+@mod.route('/edit/<int:id>', methods=('GET', 'POST'))
+@login_required
+@assert_authorisation('id')
+@templated
+def edit(id):
+ """Edit a specific constant expense. This includes deletion."""
+ exp = ConstExpense.get(id)
+ form = ConstForm(exp)
+
+ if form.is_submitted():
+ if 'deleteB' in request.form:
+ db.session.delete(exp)
+ db.session.commit()
+ return redirect('.list')
+
+ elif form.flash_validate(): # change
+ form.populate_obj(exp)
+ db.session.commit()
+ flash("Eintrag geändert.")
+ return redirect('.show', id = id)
+
+ return { 'form': form }
+
+
+@mod.route('/add/', methods=('GET', 'POST'))
+@login_required
+@templated
+def add():
+ """Add a new constant expense."""
+ exp = ConstExpense()
+
+ form = ConstForm()
+
+ if form.validate_on_submit():
+ form.populate_obj(exp)
+ exp.user = current_user
+ db.session.add(exp)
+ db.session.commit()
+ flash("Eintrag hinzugefügt.")
+ return redirect('.show', id = exp.id)
+
+ return { 'form': form }
+
+
+@mod.route('/add/from/<int:other>')
+@login_required
+@assert_authorisation('other')
+@templated('.add')
+def add_from(other):
+ """Copy `other` and create a new expense based on it."""
+ exp = ConstExpense() # needed to initialize 'CE.next'
+
+ other = ConstExpense.get(other)
+
+ # get form with data from other
+ form = ConstForm(obj = other)
+
+ # replace some fields to be more meaningful
+ start = max(form.end.data, today())
+ form.start.data = start
+ form.end.data = one_year(start)
+ if not other.next: form.prev.data = other
+
+ return { 'form': form }