Add entry blueprint
This commit is contained in:
parent
c44f11a686
commit
9211d1f9b4
1
py/TODO.md
Normal file
1
py/TODO.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
- [ ] Fix date handling in entry
|
@ -2,7 +2,13 @@ import inspect
|
|||||||
|
|
||||||
from flask import Flask, render_template
|
from flask import Flask, render_template
|
||||||
|
|
||||||
from . import db, inventory, location, template_utils
|
from . import (
|
||||||
|
db,
|
||||||
|
entry,
|
||||||
|
inventory,
|
||||||
|
location,
|
||||||
|
template_utils
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
@ -19,6 +25,7 @@ def create_app():
|
|||||||
|
|
||||||
app.register_blueprint(location.bp)
|
app.register_blueprint(location.bp)
|
||||||
app.register_blueprint(inventory.bp)
|
app.register_blueprint(inventory.bp)
|
||||||
|
app.register_blueprint(entry.bp)
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
return render_template("index.html")
|
return render_template("index.html")
|
||||||
|
6
py/jon/db/get_groups.sql
Normal file
6
py/jon/db/get_groups.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
SELECT
|
||||||
|
group_id,
|
||||||
|
group_name
|
||||||
|
FROM garfield.inventory_item_groups
|
||||||
|
ORDER BY
|
||||||
|
group_name ASC
|
11
py/jon/db/get_snacks_by_barcode.sql
Normal file
11
py/jon/db/get_snacks_by_barcode.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM garfield.inventory_map
|
||||||
|
LEFT JOIN garfield.snacks USING (snack_id)
|
||||||
|
LEFT JOIN garfield.snacks_available USING (snack_id)
|
||||||
|
LEFT JOIN garfield.tax_groups USING (tax_group_id)
|
||||||
|
LEFT JOIN garfield.locations USING (location_id)
|
||||||
|
WHERE snack_barcode = %(snack_barcode)s
|
||||||
|
ORDER BY
|
||||||
|
snack_available DESC,
|
||||||
|
snack_timestamp DESC
|
59
py/jon/entry.py
Normal file
59
py/jon/entry.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import datetime
|
||||||
|
import zoneinfo
|
||||||
|
|
||||||
|
|
||||||
|
from flask import Blueprint, redirect, render_template, request, session
|
||||||
|
|
||||||
|
from . import db
|
||||||
|
|
||||||
|
|
||||||
|
bp = Blueprint("entry", __name__, url_prefix="/entry")
|
||||||
|
|
||||||
|
|
||||||
|
@bp.get("/")
|
||||||
|
def index():
|
||||||
|
return render_template("entry/index.html")
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/edit-item-data", methods=["GET", "POST"])
|
||||||
|
def edit_item_data():
|
||||||
|
if "entry" not in session:
|
||||||
|
session["entry"] = dict()
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
session["entry"] = {
|
||||||
|
"item_bought": datetime.datetime.strptime(request.form.get("item_bought"), "%Y-%m-%d"),
|
||||||
|
"item_barcode": request.form.get("item_barcode"),
|
||||||
|
"item_name": request.form.get("item_name"),
|
||||||
|
"item_group_id": int(request.form.get("item_group")),
|
||||||
|
"item_net_unit_price": float(request.form.get("item_net_unit_price")),
|
||||||
|
"item_tax_group_id": int(request.form.get("item_tax_group")),
|
||||||
|
"item_amount": int(request.form.get("item_amount")),
|
||||||
|
"item_location_id": int(request.form.get("item_location"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect("/entry/select-snack-entry")
|
||||||
|
|
||||||
|
groups = db.run_query("get_groups.sql").fetchall()
|
||||||
|
locations = db.run_query("get_locations.sql").fetchall()
|
||||||
|
|
||||||
|
return render_template("entry/edit-item-data.html", **{
|
||||||
|
"groups": groups,
|
||||||
|
"locations": locations,
|
||||||
|
"entry": session["entry"]
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/select-snack-entry", methods=["GET", "POST"])
|
||||||
|
def edit_snack_data():
|
||||||
|
if "entry" not in session:
|
||||||
|
return redirect("/entry/edit-item-data")
|
||||||
|
|
||||||
|
snacks = db.run_query("get_snacks_by_barcode.sql", {
|
||||||
|
"snack_barcode": session["entry"]["item_barcode"]
|
||||||
|
}).fetchall()
|
||||||
|
|
||||||
|
return render_template("entry/select-snack-entry.html", **{
|
||||||
|
"entry": session["entry"],
|
||||||
|
"snacks": snacks
|
||||||
|
})
|
@ -1,3 +1,6 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
def format_currency(x):
|
def format_currency(x):
|
||||||
# It would be nicer to format this using the German locale
|
# It would be nicer to format this using the German locale
|
||||||
# Too lazy to bother tho.
|
# Too lazy to bother tho.
|
||||||
@ -10,3 +13,18 @@ def format_date(d):
|
|||||||
|
|
||||||
def format_bool(x):
|
def format_bool(x):
|
||||||
return "✅" if x else "❌"
|
return "✅" if x else "❌"
|
||||||
|
|
||||||
|
|
||||||
|
def now():
|
||||||
|
return datetime.datetime.now()
|
||||||
|
|
||||||
|
|
||||||
|
def get_garfield_price(net_unit_price, tax_group_id):
|
||||||
|
if tax_group_id == 1:
|
||||||
|
tax_factor = 1.19
|
||||||
|
elif tax_group_id == 2:
|
||||||
|
tax_factor = 1.07
|
||||||
|
else:
|
||||||
|
raise Error("Unknown tax group ID")
|
||||||
|
|
||||||
|
return net_unit_price * tax_factor + 0.01
|
||||||
|
@ -62,6 +62,13 @@
|
|||||||
font-size: 8px;
|
font-size: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.form-input > label {
|
||||||
|
font-size: .8em;
|
||||||
|
}
|
||||||
|
.form-input > input:not([type=radio]),
|
||||||
|
.form-input > select {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -71,6 +78,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li {{ "class=current-page" if request.path == "/" else "" }}><a href="/">Home</a></li>
|
<li {{ "class=current-page" if request.path == "/" else "" }}><a href="/">Home</a></li>
|
||||||
<li {{ "class=current-page" if request.path.startswith("/inventory") else "" }}><a href="/inventory">Inventar</a></li>
|
<li {{ "class=current-page" if request.path.startswith("/inventory") else "" }}><a href="/inventory">Inventar</a></li>
|
||||||
|
<li {{ "class=current-page" if request.path.startswith("/entry") else "" }}><a href="/entry">Eintragen</a></li>
|
||||||
<li {{ "class=current-page" if request.path.startswith("/location") else "" }}>
|
<li {{ "class=current-page" if request.path.startswith("/location") else "" }}>
|
||||||
<a href="/location">
|
<a href="/location">
|
||||||
{% if "location" not in session %}
|
{% if "location" not in session %}
|
||||||
|
56
py/jon/templates/entry/edit-item-data.html
Normal file
56
py/jon/templates/entry/edit-item-data.html
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<pre>{{ entry }}</pre>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Neuer Inventareintrag</legend>
|
||||||
|
|
||||||
|
<form method="POST" action="/entry/edit-item-data">
|
||||||
|
<div class="form-input">
|
||||||
|
<label for="item_bought">Kaufdatum</label>
|
||||||
|
<input name="item_bought" id="item_bought" type="date" value="{{ (entry.item_bought or now()).strftime('%Y-%m-%d') }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-input">
|
||||||
|
<label for="item_barcode">Barcode</label>
|
||||||
|
<input name="item_barcode" id="item_barcode" type="text" value="{{ entry.item_barcode }}" placeholder="Barcode">
|
||||||
|
</div>
|
||||||
|
<div class="form-input">
|
||||||
|
<label for="item_name">Artikel</label>
|
||||||
|
<input name="item_name" id="item_name" type="text" value="{{ entry.item_name }}" placeholder="Artikel">
|
||||||
|
</div>
|
||||||
|
<div class="form-input">
|
||||||
|
<label for="item_group">Gruppe</label>
|
||||||
|
<select name="item_group" id="item_group">
|
||||||
|
{% for group in groups %}
|
||||||
|
<option value="{{ group.group_id }}"{% if entry.item_group_id == group.group_id %} selected{% endif %}>{{ group.group_name }} ({{ group.group_id }})</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-input">
|
||||||
|
<label for="item_net_unit_price">Stückpreis (Netto) in €</label>
|
||||||
|
<input name="item_net_unit_price" id="item_net_unit_price" type="number" step="0.01" value="{{ entry.item_net_unit_price }}" placeholder="Stückpreis (Netto) in €">
|
||||||
|
</div>
|
||||||
|
<div class="form-input">
|
||||||
|
<input name="item_tax_group" id="item_tax_group_1" type="radio" value="1"{% if entry.item_tax_group_id == 1 %} selected{% endif %}>
|
||||||
|
<label for="item_tax_group_1">Volle Umsatzsteuer (19%)</label>
|
||||||
|
|
||||||
|
<input name="item_tax_group" id="item_tax_group_2" type="radio" value="2"{% if entry.item_tax_group_id == 2 %} selected{% endif %}>
|
||||||
|
<label for="item_tax_group_2">Ermäßigte Umsatzsteuer (7%)</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-input">
|
||||||
|
<label for="item_amount">Anzahl</label>
|
||||||
|
<input name="item_amount" id="item_amount" type="number" value="{{ entry.item_amount }}" placeholder="Anzahl">
|
||||||
|
</div>
|
||||||
|
<div class="form-input">
|
||||||
|
<label for="item_group">Raum</label>
|
||||||
|
<select name="item_location" id="item_location">
|
||||||
|
{% for location in locations %}
|
||||||
|
<option value="{{ location.location_id }}"{% if entry.item_location_id == location.location_id or ("item_location" not in entry and (session.location.location_id == location.location_id)) %} selected{% endif %}>{{ location.location_name }} ({{ location.location_id }})</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button>Weiter zu den Snackeinträgen</button>
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
{% endblock %}
|
5
py/jon/templates/entry/index.html
Normal file
5
py/jon/templates/entry/index.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<a href="/entry/edit-item-data">Neuer Eintrag</a>
|
||||||
|
{% endblock %}
|
83
py/jon/templates/entry/select-snack-entry.html
Normal file
83
py/jon/templates/entry/select-snack-entry.html
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<pre>{{ entry }}</pre>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Neuer Inventareintrag</legend>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="--align-left">ID</th>
|
||||||
|
<td>{{ entry.item_id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="--align-left">Barcode</th>
|
||||||
|
<td><code>{{ entry.item_barcode }}</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="--align-left">Name</th>
|
||||||
|
<td>{{ entry.item_name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="--align-left">Einkaufspreis (Netto)</th>
|
||||||
|
<td>{{ format_currency(entry.item_net_unit_price) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="--align-left">Empfohlener Garfield-Verkaufspreis</th>
|
||||||
|
<td>{{ format_currency(get_garfield_price(entry.item_net_unit_price, entry.item_tax_group_id)) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="--align-left">Kaufdatum</th>
|
||||||
|
<td>{{ format_date(entry.item_bought) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="--align-left">Gruppe</th>
|
||||||
|
<td>{{ entry.item_group_name }} ({{ entry.item_group_id }})</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="--align-left">Anzahl</th>
|
||||||
|
<td>{{ entry.item_amount }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="--align-left">Raum</th>
|
||||||
|
<td>{{ entry.item_location_name }} ({{ entry.item_location_id }})</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Snackeinträge mit Barcode <code>{{ entry.item_barcode }}</code></legend>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Barcode</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Verkaufspreis (Brutto)</th>
|
||||||
|
<th>Eintragedatum</th>
|
||||||
|
<th>Steuersatz</th>
|
||||||
|
<th>Raum</th>
|
||||||
|
<th>Aktiv?</th>
|
||||||
|
<th>Aktionen</th>
|
||||||
|
</tr>
|
||||||
|
{% for snack in snacks %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ snack.snack_id }}</td>
|
||||||
|
<td><code>{{ snack.snack_barcode }}</code></td>
|
||||||
|
<td>{{ snack.snack_name }}</td>
|
||||||
|
<td class="--align-right">{{ format_currency(snack.snack_price) }}</td>
|
||||||
|
<td>{{ format_date(snack.snack_timestamp) }}</td>
|
||||||
|
<td>{{ snack.description }} ({{ snack.tax_group_id }})</td>
|
||||||
|
<td>{{ snack.location_name }} ({{ snack.location_id }})</td>
|
||||||
|
<td>{{ format_bool(snack.snack_available) }}</td>
|
||||||
|
<td>
|
||||||
|
<form method="POST" action="/entry/select-snack-entry">
|
||||||
|
<input type="hidden" name="snack_id" value="{{ snack.snack_id }}">
|
||||||
|
<button>Snackeintrag übernehmen</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user