Merge branch 'feature/entry'
This commit is contained in:
commit
37179e65a1
1
py/TODO.md
Normal file
1
py/TODO.md
Normal file
@ -0,0 +1 @@
|
||||
- [ ] Fix date handling in entry
|
@ -3,7 +3,13 @@ import json
|
||||
|
||||
from flask import Flask, render_template
|
||||
|
||||
from . import db, inventory, location, template_utils
|
||||
from . import (
|
||||
db,
|
||||
entry,
|
||||
inventory,
|
||||
location,
|
||||
template_utils
|
||||
)
|
||||
|
||||
|
||||
def create_app():
|
||||
@ -21,6 +27,7 @@ def create_app():
|
||||
|
||||
app.register_blueprint(location.bp)
|
||||
app.register_blueprint(inventory.bp)
|
||||
app.register_blueprint(entry.bp)
|
||||
@app.route("/")
|
||||
def index():
|
||||
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):
|
||||
# It would be nicer to format this using the German locale
|
||||
# Too lazy to bother tho.
|
||||
@ -10,3 +13,18 @@ def format_date(d):
|
||||
|
||||
def format_bool(x):
|
||||
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;
|
||||
}
|
||||
}
|
||||
.form-input > label {
|
||||
font-size: .8em;
|
||||
}
|
||||
.form-input > input:not([type=radio]),
|
||||
.form-input > select {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -71,6 +78,7 @@
|
||||
<ul>
|
||||
<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("/entry") else "" }}><a href="/entry">Eintragen</a></li>
|
||||
<li {{ "class=current-page" if request.path.startswith("/location") else "" }}>
|
||||
<a href="/location">
|
||||
{% 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