ukrywanie akcji

This commit is contained in:
Mateusz Gruszczyński
2025-10-10 13:58:36 +02:00
parent f9406a46cf
commit 35a8d5dd8e
4 changed files with 165 additions and 41 deletions

View File

@@ -1,2 +1,17 @@
ALTER TABLE zbiorka ALTER COLUMN numer_konta DROP NOT NULL; ALTER TABLE zbiorka ALTER COLUMN numer_konta DROP NOT NULL;
ALTER TABLE zbiorka ALTER COLUMN numer_telefonu_blik DROP NOT NULL; ALTER TABLE zbiorka ALTER COLUMN numer_telefonu_blik DROP NOT NULL;
_______________________________
PGSQL
ALTER TABLE wplata ADD COLUMN ukryta boolean NOT NULL DEFAULT false;
ALTER TABLE wydatek ADD COLUMN ukryta boolean NOT NULL DEFAULT false;
-- po migracji można zdjąć DEFAULT (opcjonalnie)
ALTER TABLE wplata ALTER COLUMN ukryta DROP DEFAULT;
ALTER TABLE wydatek ALTER COLUMN ukryta DROP DEFAULT;
SQLite
ALTER TABLE wplata ADD COLUMN ukryta INTEGER NOT NULL DEFAULT 0;
ALTER TABLE wydatek ADD COLUMN ukryta INTEGER NOT NULL DEFAULT 0;

97
app.py
View File

@@ -145,6 +145,7 @@ class Wplata(db.Model):
data = db.Column(db.DateTime, default=datetime.utcnow) data = db.Column(db.DateTime, default=datetime.utcnow)
opis = db.Column(db.Text, nullable=True) opis = db.Column(db.Text, nullable=True)
zbiorka = db.relationship("Zbiorka", back_populates="wplaty") zbiorka = db.relationship("Zbiorka", back_populates="wplaty")
ukryta = db.Column(db.Boolean, nullable=False, default=False)
class Wydatek(db.Model): class Wydatek(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@@ -156,7 +157,7 @@ class Wydatek(db.Model):
kwota = db.Column(Numeric(12, 2), nullable=False) kwota = db.Column(Numeric(12, 2), nullable=False)
data = db.Column(db.DateTime, default=datetime.utcnow) data = db.Column(db.DateTime, default=datetime.utcnow)
opis = db.Column(db.Text, nullable=True) opis = db.Column(db.Text, nullable=True)
ukryta = db.Column(db.Boolean, nullable=False, default=False)
class UstawieniaGlobalne(db.Model): class UstawieniaGlobalne(db.Model):
__tablename__ = "ustawienia_globalne" __tablename__ = "ustawienia_globalne"
@@ -296,17 +297,25 @@ def zbiorka(zbiorka_id):
if zb.ukryta and (not current_user.is_authenticated or not current_user.czy_admin): if zb.ukryta and (not current_user.is_authenticated or not current_user.czy_admin):
abort(404) abort(404)
# scalona oś czasu: wpłaty + wydatki is_admin = current_user.is_authenticated and current_user.czy_admin
aktywnosci = [ show_hidden = is_admin and (request.args.get("show_hidden") in ("1", "true", "yes"))
{"typ": "wpłata", "kwota": w.kwota, "opis": w.opis, "data": w.data}
# wpłaty / wydatki z filtrem ukrycia
wplaty = [
{"typ": "wpłata", "kwota": w.kwota, "opis": w.opis, "data": w.data, "ukryta": getattr(w, "ukryta", False)}
for w in zb.wplaty for w in zb.wplaty
] + [ if show_hidden or not getattr(w, "ukryta", False)
{"typ": "wydatek", "kwota": x.kwota, "opis": x.opis, "data": x.data}
for x in zb.wydatki
] ]
wydatki = [
{"typ": "wydatek", "kwota": x.kwota, "opis": x.opis, "data": x.data, "ukryta": getattr(x, "ukryta", False)}
for x in zb.wydatki
if show_hidden or not getattr(x, "ukryta", False)
]
aktywnosci = wplaty + wydatki
aktywnosci.sort(key=lambda a: a["data"], reverse=True) aktywnosci.sort(key=lambda a: a["data"], reverse=True)
return render_template("zbiorka.html", zbiorka=zb, aktywnosci=aktywnosci) return render_template("zbiorka.html", zbiorka=zb, aktywnosci=aktywnosci, show_hidden=show_hidden)
# TRASY LOGOWANIA I REJESTRACJI # TRASY LOGOWANIA I REJESTRACJI
@@ -838,17 +847,41 @@ def robots():
def transakcje_zbiorki(zbiorka_id): def transakcje_zbiorki(zbiorka_id):
if not current_user.czy_admin: if not current_user.czy_admin:
flash("Brak uprawnień", "danger"); return redirect(url_for("index")) flash("Brak uprawnień", "danger"); return redirect(url_for("index"))
zb = db.session.get(Zbiorka, zbiorka_id) zb = db.session.get(Zbiorka, zbiorka_id)
if zb is None: if zb is None:
abort(404) abort(404)
aktywnosci = ( aktywnosci = (
[{"typ": "wpłata", "id": w.id, "kwota": w.kwota, "opis": w.opis, "data": w.data} for w in zb.wplaty] + [
[{"typ": "wydatek","id": x.id, "kwota": x.kwota,"opis": x.opis,"data": x.data} for x in zb.wydatki] {
"typ": "wpłata",
"id": w.id,
"kwota": w.kwota,
"opis": w.opis,
"data": w.data,
"ukryta": bool(w.ukryta),
}
for w in zb.wplaty
]
+
[
{
"typ": "wydatek",
"id": x.id,
"kwota": x.kwota,
"opis": x.opis,
"data": x.data,
"ukryta": bool(x.ukryta),
}
for x in zb.wydatki
]
) )
aktywnosci.sort(key=lambda a: a["data"], reverse=True) aktywnosci.sort(key=lambda a: a["data"], reverse=True)
return render_template("admin/transakcje.html", zbiorka=zb, aktywnosci=aktywnosci) return render_template("admin/transakcje.html", zbiorka=zb, aktywnosci=aktywnosci)
@app.route("/admin/wplata/<int:wplata_id>/zapisz", methods=["POST"]) @app.route("/admin/wplata/<int:wplata_id>/zapisz", methods=["POST"])
@login_required @login_required
def zapisz_wplate(wplata_id): def zapisz_wplate(wplata_id):
@@ -874,6 +907,50 @@ def zapisz_wplate(wplata_id):
flash("Wpłata zaktualizowana", "success") flash("Wpłata zaktualizowana", "success")
return redirect(url_for("transakcje_zbiorki", zbiorka_id=zb.id)) return redirect(url_for("transakcje_zbiorki", zbiorka_id=zb.id))
@app.post("/wplata/<int:wplata_id>/ukryj")
@login_required
def ukryj_wplate(wplata_id):
if not current_user.czy_admin: abort(403)
w = db.session.get(Wplata, wplata_id)
if not w: abort(404)
w.ukryta = True
db.session.commit()
flash("Wpłata ukryta.", "success")
return redirect(request.referrer or url_for("admin_dashboard"))
@app.post("/wplata/<int:wplata_id>/odkryj")
@login_required
def odkryj_wplate(wplata_id):
if not current_user.czy_admin: abort(403)
w = db.session.get(Wplata, wplata_id)
if not w: abort(404)
w.ukryta = False
db.session.commit()
flash("Wpłata odkryta.", "success")
return redirect(request.referrer or url_for("admin_dashboard"))
@app.post("/wydatek/<int:wydatek_id>/ukryj")
@login_required
def ukryj_wydatek(wydatek_id):
if not current_user.czy_admin: abort(403)
w = db.session.get(Wydatek, wydatek_id)
if not w: abort(404)
w.ukryta = True
db.session.commit()
flash("Wydatek ukryty.", "success")
return redirect(request.referrer or url_for("admin_dashboard"))
@app.post("/wydatek/<int:wydatek_id>/odkryj")
@login_required
def odkryj_wydatek(wydatek_id):
if not current_user.czy_admin: abort(403)
w = db.session.get(Wydatek, wydatek_id)
if not w: abort(404)
w.ukryta = False
db.session.commit()
flash("Wydatek odkryty.", "success")
return redirect(request.referrer or url_for("admin_dashboard"))
@app.route("/admin/wplata/<int:wplata_id>/usun", methods=["POST"]) @app.route("/admin/wplata/<int:wplata_id>/usun", methods=["POST"])
@login_required @login_required

View File

@@ -23,4 +23,5 @@ document.addEventListener('DOMContentLoaded', function () {
modalX.show(); modalX.show();
}); });
}); });
}); });

View File

@@ -6,24 +6,21 @@
<div class="d-flex justify-content-between align-items-center mb-3"> <div class="d-flex justify-content-between align-items-center mb-3">
<h3 class="mb-0">Transakcje: {{ zbiorka.nazwa }}</h3> <h3 class="mb-0">Transakcje: {{ zbiorka.nazwa }}</h3>
<div class="btn-group" role="group" aria-label="Akcje zbiórki"> <div class="btn-group" role="group" aria-label="Akcje zbiórki">
<a class="btn btn-sm btn-outline-light" href="{{ url_for('dodaj_wplate', zbiorka_id=zbiorka.id) }}"> <a class="btn btn-sm btn-outline-light" href="{{ url_for('dodaj_wplate', zbiorka_id=zbiorka.id) }}">
<i class="fas fa-plus-circle"></i> Dodaj wpłatę <i class="fas fa-plus-circle"></i> Dodaj wpłatę
</a> </a>
<a class="btn btn-sm btn-outline-light" href="{{ url_for('dodaj_wydatek', zbiorka_id=zbiorka.id) }}"> <a class="btn btn-sm btn-outline-light" href="{{ url_for('dodaj_wydatek', zbiorka_id=zbiorka.id) }}">
Dodaj wydatek Dodaj wydatek
</a> </a>
<a class="btn btn-sm btn-outline-light" href="{{ url_for('edytuj_stan', zbiorka_id=zbiorka.id) }}"> <a class="btn btn-sm btn-outline-light" href="{{ url_for('edytuj_stan', zbiorka_id=zbiorka.id) }}">
Edytuj stan Edytuj stan
</a> </a>
<a class="btn btn-sm btn-outline-light" href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}"> <a class="btn btn-sm btn-outline-light" href="{{ url_for('zbiorka', zbiorka_id=zbiorka.id) }}">
Otwórz ↗ Otwórz ↗
</a> </a>
</div> </div>
</div> </div>
<div class="card shadow-sm"> <div class="card shadow-sm">
@@ -41,15 +38,22 @@
</thead> </thead>
<tbody> <tbody>
{% for a in aktywnosci %} {% for a in aktywnosci %}
<tr> <tr data-tx-id="{{ a.id }}" data-tx-typ="{{ a.typ }}">
<td>{{ a.data|dt("%d.%m.%Y %H:%M") }}</td> <td>{{ a.data|dt("%d.%m.%Y %H:%M") }}</td>
<td> <td>
<span class="badge {{ 'bg-success' if a.typ=='wpłata' else 'bg-danger' }}">{{ a.typ <span class="badge {{ 'bg-success' if a.typ=='wpłata' else 'bg-danger' }}">{{ a.typ
}}</span> }}</span>
</td> </td>
<td class="text-end">{{ '%.2f'|format(a.kwota) }} PLN</td> <td class="text-end">{{ '%.2f'|format(a.kwota) }} PLN</td>
<td class="text-muted">{{ a.opis or '—' }}</td> <td class="text-muted">{{ a.opis or '—' }}</td>
<td class="text-end"> <td class="text-end">
{% if a.ukryta %}<span class="badge bg-secondary ms-1">[ akcja ukryta ]</span>{% endif
%}
<div class="d-inline-flex flex-nowrap align-items-center gap-2">
{% if a.typ == 'wpłata' %} {% if a.typ == 'wpłata' %}
<button class="btn btn-sm btn-outline-light btn-edit-wplata" data-id="{{ a.id }}" <button class="btn btn-sm btn-outline-light btn-edit-wplata" data-id="{{ a.id }}"
data-kwota="{{ '%.2f'|format(a.kwota) }}" data-opis="{{ a.opis|e if a.opis }}" data-kwota="{{ '%.2f'|format(a.kwota) }}" data-opis="{{ a.opis|e if a.opis }}"
@@ -61,10 +65,22 @@
onsubmit="return confirm('Usunąć wpłatę? Cofnie to wpływ na stan.');"> onsubmit="return confirm('Usunąć wpłatę? Cofnie to wpływ na stan.');">
<button class="btn btn-sm btn-outline-danger">Usuń</button> <button class="btn btn-sm btn-outline-danger">Usuń</button>
</form> </form>
{% if a.ukryta %}
<form class="d-inline" method="post"
action="{{ url_for('odkryj_wplate', wplata_id=a.id) }}">
<button class="btn btn-sm btn-outline-secondary">Odkryj</button>
</form>
{% else %} {% else %}
<button class="btn btn-sm btn-outline-light btn-edit-wydatek" <form class="d-inline" method="post"
data-id="{{ a.id }}" data-kwota="{{ '%.2f'|format(a.kwota) }}" action="{{ url_for('ukryj_wplate', wplata_id=a.id) }}">
data-opis="{{ a.opis|e if a.opis }}" <button class="btn btn-sm btn-outline-secondary">Ukryj</button>
</form>
{% endif %}
{% else %}
<button class="btn btn-sm btn-outline-light btn-edit-wydatek" data-id="{{ a.id }}"
data-kwota="{{ '%.2f'|format(a.kwota) }}" data-opis="{{ a.opis|e if a.opis }}"
data-action="{{ url_for('zapisz_wydatek', wydatek_id=a.id) }}"> data-action="{{ url_for('zapisz_wydatek', wydatek_id=a.id) }}">
Edytuj Edytuj
</button> </button>
@@ -73,7 +89,22 @@
onsubmit="return confirm('Usunąć wydatek? Cofnie to wpływ na stan.');"> onsubmit="return confirm('Usunąć wydatek? Cofnie to wpływ na stan.');">
<button class="btn btn-sm btn-outline-danger">Usuń</button> <button class="btn btn-sm btn-outline-danger">Usuń</button>
</form> </form>
{% if a.ukryta %}
<span class="badge bg-secondary">Ukryta</span>
<form class="d-inline" method="post"
action="{{ url_for('odkryj_wydatek', wydatek_id=a.id) }}">
<button class="btn btn-sm btn-outline-secondary">Odkryj</button>
</form>
{% else %}
<form class="d-inline" method="post"
action="{{ url_for('ukryj_wydatek', wydatek_id=a.id) }}">
<button class="btn btn-sm btn-outline-secondary">Ukryj</button>
</form>
{% endif %} {% endif %}
{% endif %}
</div>
</td> </td>
</tr> </tr>
{% else %} {% else %}