diff --git a/alters.txt b/alters.txt index 3de824c..e782235 100644 --- a/alters.txt +++ b/alters.txt @@ -1,2 +1,6 @@ ALTER TABLE zbiorka ADD COLUMN typ_zbiorki VARCHAR(20) NOT NULL DEFAULT 'standardowa'; -CREATE INDEX idx_zbiorka_typ ON zbiorka(typ_zbiorki); \ No newline at end of file +CREATE INDEX idx_zbiorka_typ ON zbiorka(typ_zbiorki); + + +ALTER TABLE ustawienia_globalne +ADD COLUMN kolejnosc_rezerwowych VARCHAR(20) NOT NULL DEFAULT 'id'; \ No newline at end of file diff --git a/app.py b/app.py index 3fe13c2..d72413b 100644 --- a/app.py +++ b/app.py @@ -201,6 +201,7 @@ class UstawieniaGlobalne(db.Model): typ_navbar = db.Column(db.String(10), default="text") typ_stopka = db.Column(db.String(10), default="text") stopka_text = db.Column(db.String(200), nullable=True) + kolejnosc_rezerwowych = db.Column(db.String(20), default="id", nullable=False) @login_manager.user_loader @@ -336,7 +337,23 @@ def inject_version(): # TRASY PUBLICZNE @app.route("/") def index(): - zbiorki = Zbiorka.query.filter_by(ukryta=False, zrealizowana=False).all() + settings = UstawieniaGlobalne.query.first() + kolejnosc = settings.kolejnosc_rezerwowych if settings else "id" + + standardowe = Zbiorka.query.filter_by(ukryta=False, zrealizowana=False).filter( + Zbiorka.typ_zbiorki != 'rezerwa' + ).all() + + rezerwowe = Zbiorka.query.filter_by(ukryta=False, zrealizowana=False, typ_zbiorki='rezerwa').all() + + # Sortuj według ustawienia + if kolejnosc == "first": + zbiorki = rezerwowe + standardowe + elif kolejnosc == "last": + zbiorki = standardowe + rezerwowe + else: # "id" + zbiorki = sorted(standardowe + rezerwowe, key=lambda z: z.id) + return render_template("index.html", zbiorki=zbiorki) @@ -351,12 +368,19 @@ def page_not_found(e): return redirect(url_for("index")) -@app.route("/zbiorka/") +@app.route("/zbiorka/", endpoint='zbiorka') +@app.route("/rezerwa/", endpoint='rezerwa') def zbiorka(zbiorka_id): zb = db.session.get(Zbiorka, zbiorka_id) if zb is None: abort(404) + # Zabezpieczenie: sprawdź czy URL pasuje do typu zbiórki + poprawny_endpoint = 'rezerwa' if zb.typ_zbiorki == 'rezerwa' else 'zbiorka' + + if request.endpoint != poprawny_endpoint: + return redirect(url_for(poprawny_endpoint, zbiorka_id=zbiorka_id), code=301) + if zb.ukryta and (not current_user.is_authenticated or not current_user.czy_admin): abort(404) @@ -945,9 +969,10 @@ def admin_ustawienia(): if not current_user.czy_admin: flash("Brak uprawnień do panelu administracyjnego", "danger") return redirect(url_for("index")) - + client_ip = get_real_ip() settings = UstawieniaGlobalne.query.first() + if request.method == "POST": numer_konta = request.form.get("numer_konta") numer_telefonu_blik = request.form.get("numer_telefonu_blik") @@ -958,7 +983,8 @@ def admin_ustawienia(): typ_stopka = request.form.get("typ_stopka", "text") stopka_text = request.form.get("stopka_text") or None pokaz_logo_w_navbar = (typ_navbar == "logo") - + kolejnosc_rezerwowych = request.form.get("kolejnosc_rezerwowych", "id") + if settings is None: settings = UstawieniaGlobalne( numer_konta=numer_konta, @@ -970,6 +996,7 @@ def admin_ustawienia(): typ_navbar=typ_navbar, typ_stopka=typ_stopka, stopka_text=stopka_text, + kolejnosc_rezerwowych=kolejnosc_rezerwowych, ) db.session.add(settings) else: @@ -982,11 +1009,12 @@ def admin_ustawienia(): settings.typ_navbar = typ_navbar settings.typ_stopka = typ_stopka settings.stopka_text = stopka_text - + settings.kolejnosc_rezerwowych = kolejnosc_rezerwowych + db.session.commit() flash("Ustawienia globalne zostały zaktualizowane", "success") return redirect(url_for("admin_dashboard")) - + return render_template("admin/ustawienia.html", settings=settings, client_ip=client_ip) @@ -1416,6 +1444,175 @@ def usun_rezerwe(rezerwa_id): return redirect(url_for("lista_rezerwowych")) +@app.route("/admin/statystyki") +@login_required +def admin_statystyki(): + if not current_user.czy_admin: + abort(403) + + from sqlalchemy import func, extract + from datetime import datetime, timedelta + + # ==================== PODSTAWOWE STATYSTYKI ==================== + total_wplaty = db.session.query(func.sum(Wplata.kwota)).filter(Wplata.ukryta == False).scalar() or 0 + total_wydatki = db.session.query(func.sum(Wydatek.kwota)).filter(Wydatek.ukryta == False).scalar() or 0 + bilans = total_wplaty - total_wydatki + + liczba_wplat = db.session.query(func.count(Wplata.id)).filter(Wplata.ukryta == False).scalar() or 0 + liczba_wydatkow = db.session.query(func.count(Wydatek.id)).filter(Wydatek.ukryta == False).scalar() or 0 + liczba_zbiorek = db.session.query(func.count(Zbiorka.id)).scalar() or 0 + + # Najwyższa wpłata + najwyzsza_wplata = db.session.query(Wplata).filter(Wplata.ukryta == False).order_by(Wplata.kwota.desc()).first() + + # Najwyższy wydatek + najwyzszy_wydatek = db.session.query(Wydatek).filter(Wydatek.ukryta == False).order_by(Wydatek.kwota.desc()).first() + + # Średnia wpłata i wydatek + srednia_wplata = total_wplaty / liczba_wplat if liczba_wplat > 0 else 0 + sredni_wydatek = total_wydatki / liczba_wydatkow if liczba_wydatkow > 0 else 0 + + # ==================== STATYSTYKI PRZESUNIĘĆ ==================== + total_przesuniec = db.session.query(func.sum(Przesuniecie.kwota)).filter(Przesuniecie.ukryta == False).scalar() or 0 + liczba_przesuniec = db.session.query(func.count(Przesuniecie.id)).filter(Przesuniecie.ukryta == False).scalar() or 0 + + # Top 5 źródeł przesunięć (zbiórki które najczęściej przekazują środki) + top_zrodla_przesuniec = db.session.query( + Zbiorka.nazwa, + func.count(Przesuniecie.id).label('liczba'), + func.sum(Przesuniecie.kwota).label('suma') + ).join(Przesuniecie, Przesuniecie.zbiorka_zrodlo_id == Zbiorka.id)\ + .filter(Przesuniecie.ukryta == False)\ + .group_by(Zbiorka.id, Zbiorka.nazwa)\ + .order_by(func.sum(Przesuniecie.kwota).desc())\ + .limit(5).all() + + # ==================== TOP 10 WPŁAT ==================== + top_10_wplat = db.session.query(Wplata)\ + .filter(Wplata.ukryta == False)\ + .order_by(Wplata.kwota.desc())\ + .limit(10).all() + + # ==================== AKTYWNOŚĆ CZASOWA ==================== + teraz = datetime.now() + rok_temu = teraz - timedelta(days=365) + miesiac_temu = teraz - timedelta(days=30) + tydzien_temu = teraz - timedelta(days=7) + + # Aktywność ostatnie 7 dni + wplaty_7dni = db.session.query( + func.count(Wplata.id).label('liczba'), + func.sum(Wplata.kwota).label('suma') + ).filter(Wplata.data >= tydzien_temu, Wplata.ukryta == False).first() + + wydatki_7dni = db.session.query( + func.count(Wydatek.id).label('liczba'), + func.sum(Wydatek.kwota).label('suma') + ).filter(Wydatek.data >= tydzien_temu, Wydatek.ukryta == False).first() + + # Aktywność ostatnie 30 dni + wplaty_30dni = db.session.query( + func.count(Wplata.id).label('liczba'), + func.sum(Wplata.kwota).label('suma') + ).filter(Wplata.data >= miesiac_temu, Wplata.ukryta == False).first() + + wydatki_30dni = db.session.query( + func.count(Wydatek.id).label('liczba'), + func.sum(Wydatek.kwota).label('suma') + ).filter(Wydatek.data >= miesiac_temu, Wydatek.ukryta == False).first() + + # ==================== STATYSTYKI MIESIĘCZNE (ostatnie 12 miesięcy) ==================== + wplaty_miesieczne = db.session.query( + extract('year', Wplata.data).label('rok'), + extract('month', Wplata.data).label('miesiac'), + func.sum(Wplata.kwota).label('suma'), + func.count(Wplata.id).label('liczba') + ).filter( + Wplata.data >= rok_temu, + Wplata.ukryta == False + ).group_by('rok', 'miesiac').order_by('rok', 'miesiac').all() + + wydatki_miesieczne = db.session.query( + extract('year', Wydatek.data).label('rok'), + extract('month', Wydatek.data).label('miesiac'), + func.sum(Wydatek.kwota).label('suma'), + func.count(Wydatek.id).label('liczba') + ).filter( + Wydatek.data >= rok_temu, + Wydatek.ukryta == False + ).group_by('rok', 'miesiac').order_by('rok', 'miesiac').all() + + przesuniecia_miesieczne = db.session.query( + extract('year', Przesuniecie.data).label('rok'), + extract('month', Przesuniecie.data).label('miesiac'), + func.sum(Przesuniecie.kwota).label('suma'), + func.count(Przesuniecie.id).label('liczba') + ).filter( + Przesuniecie.data >= rok_temu, + Przesuniecie.ukryta == False + ).group_by('rok', 'miesiac').order_by('rok', 'miesiac').all() + + # ==================== STATYSTYKI ROCZNE ==================== + wplaty_roczne = db.session.query( + extract('year', Wplata.data).label('rok'), + func.sum(Wplata.kwota).label('suma'), + func.count(Wplata.id).label('liczba') + ).filter( + Wplata.ukryta == False + ).group_by('rok').order_by('rok').all() + + wydatki_roczne = db.session.query( + extract('year', Wydatek.data).label('rok'), + func.sum(Wydatek.kwota).label('suma'), + func.count(Wydatek.id).label('liczba') + ).filter( + Wydatek.ukryta == False + ).group_by('rok').order_by('rok').all() + + # ==================== TOP 5 ZBIÓREK ==================== + top_zbiorki = db.session.query( + Zbiorka, + func.sum(Wplata.kwota).label('suma_wplat') + ).join(Wplata).filter( + Wplata.ukryta == False + ).group_by(Zbiorka.id).order_by(func.sum(Wplata.kwota).desc()).limit(5).all() + + return render_template( + "admin/statystyki.html", + # Podstawowe + total_wplaty=total_wplaty, + total_wydatki=total_wydatki, + bilans=bilans, + liczba_wplat=liczba_wplat, + liczba_wydatkow=liczba_wydatkow, + liczba_zbiorek=liczba_zbiorek, + najwyzsza_wplata=najwyzsza_wplata, + najwyzszy_wydatek=najwyzszy_wydatek, + srednia_wplata=srednia_wplata, + sredni_wydatek=sredni_wydatek, + # Przesunięcia + total_przesuniec=total_przesuniec, + liczba_przesuniec=liczba_przesuniec, + top_zrodla_przesuniec=top_zrodla_przesuniec, + # Top wpłaty + top_10_wplat=top_10_wplat, + # Aktywność czasowa + wplaty_7dni=wplaty_7dni, + wydatki_7dni=wydatki_7dni, + wplaty_30dni=wplaty_30dni, + wydatki_30dni=wydatki_30dni, + # Miesięczne + wplaty_miesieczne=wplaty_miesieczne, + wydatki_miesieczne=wydatki_miesieczne, + przesuniecia_miesieczne=przesuniecia_miesieczne, + # Roczne + wplaty_roczne=wplaty_roczne, + wydatki_roczne=wydatki_roczne, + # Top zbiórki + top_zbiorki=top_zbiorki + ) + + @app.route("/favicon.ico") def favicon(): return "", 204 diff --git a/templates/admin/dashboard.html b/templates/admin/dashboard.html index e80007b..33703cd 100644 --- a/templates/admin/dashboard.html +++ b/templates/admin/dashboard.html @@ -6,32 +6,80 @@ - + +
+
+
+
+ +

{{ active_zbiorki|length }}

+ Aktywnych zbiórek +
+
+
+
+
+
+ +

{{ completed_zbiorki|length }}

+ Zrealizowanych +
+
+
+
+
+
+ +

{{ active_zbiorki|length + completed_zbiorki|length }}

+ Łącznie zbiórek +
+
+
+ +
+ + @@ -59,12 +107,20 @@ {{ z.id }}
- {{ z.nazwa }} - {# opcjonalnie: mini-meta z celem/stanem jeśli masz te pola #} + + {{ z.nazwa }} + {% if z.cel is defined or z.stan is defined %} - {% if z.cel is defined %} Cel: {{ z.cel|round(2) }} PLN {% endif %} - {% if z.stan is defined %} · Stan: {{ z.stan|round(2) }} PLN {% endif %} + {% if z.cel is defined and z.cel > 0 %} + Cel: {{ z.cel|round(2) }} PLN + {% endif %} + {% if z.stan is defined %} + · Stan: {{ z.stan|round(2) }} PLN + {% endif %} + {% if z.cel is defined and z.cel > 0 and z.stan is defined %} + · {{ ((z.stan / z.cel) * 100)|round(1) }}% + {% endif %} {% endif %}
@@ -72,65 +128,82 @@ {% if z.ukryta %} Ukryta + style="border-color: var(--border);"> Ukryta {% else %} - Widoczna + Widoczna {% endif %}
Edytuj + class="btn btn-sm btn-outline-light"> + Edytuj + @@ -145,9 +218,12 @@
+
Brak aktywnych zbiórek

Wygląda na to, że teraz nic nie zbieramy.

- Utwórz nową zbiórkę + + Utwórz nową zbiórkę +
{% endif %} @@ -174,7 +250,9 @@ {{ z.id }}
- {{ z.nazwa }} + + {{ z.nazwa }} + {% if z.cel is defined or z.stan is defined %} {% if z.cel is defined %} Cel: {{ z.cel|round(2) }} PLN {% endif %} @@ -186,68 +264,87 @@
Zrealizowana + style="background: var(--accent); color:#111;"> + Zrealizowana + {% if z.ukryta %} Ukryta + style="border-color: var(--border);"> Ukryta {% else %} - Widoczna + Widoczna {% endif %}
Edytuj + class="btn btn-sm btn-outline-light"> + Edytuj + @@ -261,14 +358,16 @@ {% else %}
+
Brak zbiórek zrealizowanych

Gdy jakaś zbiórka osiągnie 100%, pojawi się tutaj.

- Utwórz nową - zbiórkę + + Utwórz nową zbiórkę +
{% endif %}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/admin/lista_rezerwowych.html b/templates/admin/lista_rezerwowych.html index 9cf6caa..2023f58 100644 --- a/templates/admin/lista_rezerwowych.html +++ b/templates/admin/lista_rezerwowych.html @@ -3,141 +3,194 @@ {% block content %}
+
-

Listy rezerwowe

+
+

Listy rezerwowe

+

Zarządzaj środkami rezerwowymi i nadpłatami

+
+ + + {% if rezerwy %} +
+
+
+
+ +

{{ rezerwy|length }}

+ Aktywnych list +
+
+
+
+
+
+ +

{{ "%.2f"|format(rezerwy|sum(attribute='stan')) }} zł

+ Łączna rezerwa +
+
+
+
+
+
+ +

{{ rezerwy|selectattr('ukryta', 'equalto', False)|list|length }}

+ Widocznych publicznie +
+
+
+
+ {% endif %} {% if rezerwy %} -
- - - - - - - - - - - {% for r in rezerwy %} - - - + + {% endfor %} + +
IDNazwaWidocznośćOpcje
{{ r.id }} -
- - {{ r.nazwa }} - - - Stan: {{ r.stan|round(2) }} PLN - {% if r.opis %} - · {{ r.opis[:50] }}{% if r.opis|length > 50 %}...{% endif %} +
+
+ + + + + + + + + + + + {% for r in rezerwy %} + + + + + - - + - - {% endfor %} - -
IDNazwaStanWidocznośćOpcje
{{ r.id }} +
+ + {{ r.nazwa }} + + {% if r.opis %} + + {{ r.opis[:60] }}{% if r.opis|length > 60 %}...{% endif %} + + {% endif %} +
+
+ {{ "%.2f"|format(r.stan) }} zł + + {% if r.ukryta %} + + Ukryta + + {% else %} + + Widoczna + {% endif %} - - - - {% if r.ukryta %} - Ukryta - {% else %} - Widoczna - {% endif %} - - -
- Edytuj - -
+ +
+ + Edytuj - -
  • - -
  • -
  • - - Dodaj wpłatę - -
  • -
  • - - Dodaj wydatek - -
  • -
  • - - Przesuń środki - -
  • -
  • - - Transakcje - -
  • -
  • - - Edytuj stan - -
  • -
  • - -
  • -
  • -
    - -
    -
  • -
  • - -
  • -
  • -
    - -
    -
  • - -
    -
    -
    + + +
    +
    +
    +
    + {% else %}
    +
    Brak list rezerwowych
    -

    Nie masz jeszcze żadnych list rezerwowych. Utwórz pierwszą, aby zarządzać nadpłatami i środkami rezerwowymi.

    - Dodaj listę rezerwową +

    + Nie masz jeszcze żadnych list rezerwowych.
    + Utwórz pierwszą, aby zarządzać nadpłatami i środkami rezerwowymi. +

    + + Dodaj listę rezerwową +
    {% endif %} diff --git a/templates/admin/statystyki.html b/templates/admin/statystyki.html new file mode 100644 index 0000000..551b57f --- /dev/null +++ b/templates/admin/statystyki.html @@ -0,0 +1,461 @@ +{% extends 'base.html' %} +{% block title %}Statystyki - Panel Admina{% endblock %} + +{% block content %} +
    + + + + + +
    +
    +
    +
    +
    Suma wpłat
    +

    {{ "%.2f"|format(total_wplaty) }} zł

    + {{ liczba_wplat }} wpłat +
    +
    +
    + +
    +
    +
    +
    Suma wydatków
    +

    {{ "%.2f"|format(total_wydatki) }} zł

    + {{ liczba_wydatkow }} wydatków +
    +
    +
    + +
    +
    +
    +
    Suma przesunięć
    +

    {{ "%.2f"|format(total_przesuniec) }} zł

    + {{ liczba_przesuniec }} operacji +
    +
    +
    + +
    +
    +
    +
    Bilans
    +

    + {{ "%.2f"|format(bilans) }} zł +

    + {{ liczba_zbiorek }} zbiórek +
    +
    +
    +
    + + + + +
    + + +
    + + +
    +
    +
    +
    +
    Najwyższa wpłata
    +
    +
    + {% if najwyzsza_wplata %} +

    {{ "%.2f"|format(najwyzsza_wplata.kwota) }} zł

    +

    Opis: {{ najwyzsza_wplata.opis or "Brak opisu" }}

    +

    Data: {{ najwyzsza_wplata.data.strftime('%d.%m.%Y') }}

    +

    Zbiórka: + + {{ najwyzsza_wplata.zbiorka.nazwa }} + +

    + {% else %} +

    Brak wpłat

    + {% endif %} +
    +
    +
    + +
    +
    +
    +
    Najwyższy wydatek
    +
    +
    + {% if najwyzszy_wydatek %} +

    {{ "%.2f"|format(najwyzszy_wydatek.kwota) }} zł

    +

    Opis: {{ najwyzszy_wydatek.opis or "Brak opisu" }}

    +

    Data: {{ najwyzszy_wydatek.data.strftime('%d.%m.%Y') }}

    +

    Zbiórka: + + {{ najwyzszy_wydatek.zbiorka.nazwa }} + +

    + {% else %} +

    Brak wydatków

    + {% endif %} +
    +
    +
    + +
    +
    +
    +
    Średnie wartości
    +
    +
    +

    Średnia wpłata: {{ "%.2f"|format(srednia_wplata) }} zł

    +

    Średni wydatek: {{ "%.2f"|format(sredni_wydatek) }} zł

    +
    +
    +
    +
    + + +
    +
    +
    Top 10 najwyższych wpłat
    +
    +
    + {% if top_10_wplat %} +
    + + + + + + + + + + + + {% for wplata in top_10_wplat %} + + + + + + + + {% endfor %} + +
    #KwotaOpisDataZbiórka
    {{ loop.index }}{{ "%.2f"|format(wplata.kwota) }} zł{{ wplata.opis or "Brak opisu" }}{{ wplata.data.strftime('%d.%m.%Y') }} + + {{ wplata.zbiorka.nazwa }} + +
    +
    + {% else %} +
    +

    Brak danych

    +
    + {% endif %} +
    +
    + + +
    +
    +
    Top 5 zbiórek (największe wpłaty)
    +
    +
    + {% if top_zbiorki %} +
    + + + + + + + + + + {% for zbiorka, suma in top_zbiorki %} + + + + + + {% endfor %} + +
    #Nazwa zbiórkiSuma wpłat
    {{ loop.index }} + + {{ zbiorka.nazwa }} + + {{ "%.2f"|format(suma) }} zł
    +
    + {% else %} +
    +

    Brak danych

    +
    + {% endif %} +
    +
    + + +
    +
    +
    Top 5 źródeł przesunięć
    +
    +
    + {% if top_zrodla_przesuniec %} +
    + + + + + + + + + + + {% for nazwa, liczba, suma in top_zrodla_przesuniec %} + + + + + + + {% endfor %} + +
    #Zbiórka źródłowaLiczba przesunięćSuma
    {{ loop.index }}{{ nazwa }}{{ liczba }}{{ "%.2f"|format(suma) }} zł
    +
    + {% else %} +
    +

    Brak przesunięć

    +
    + {% endif %} +
    +
    + +
    + + +
    + +
    + +
    +
    +
    +
    Ostatnie 7 dni
    +
    +
    +
    Wpłaty
    +

    Liczba: {{ wplaty_7dni.liczba or 0 }}

    +

    Suma: {{ "%.2f"|format(wplaty_7dni.suma or 0) }} zł

    + +
    Wydatki
    +

    Liczba: {{ wydatki_7dni.liczba or 0 }}

    +

    Suma: {{ "%.2f"|format(wydatki_7dni.suma or 0) }} zł

    +
    +
    +
    + + +
    +
    +
    +
    Ostatnie 30 dni
    +
    +
    +
    Wpłaty
    +

    Liczba: {{ wplaty_30dni.liczba or 0 }}

    +

    Suma: {{ "%.2f"|format(wplaty_30dni.suma or 0) }} zł

    + +
    Wydatki
    +

    Liczba: {{ wydatki_30dni.liczba or 0 }}

    +

    Suma: {{ "%.2f"|format(wydatki_30dni.suma or 0) }} zł

    +
    +
    +
    +
    + +
    + + +
    + +
    +
    +
    Ostatnie 12 miesięcy
    +
    +
    + {% if wplaty_miesieczne or wydatki_miesieczne or przesuniecia_miesieczne %} +
    + + + + + + + + + + + + + + {% set wplaty_dict = {} %} + {% set wydatki_dict = {} %} + {% set przesuniecia_dict = {} %} + + {% for rok, miesiac, suma, liczba in wplaty_miesieczne %} + {% set klucz = "%d-%02d"|format(rok|int, miesiac|int) %} + {% set _ = wplaty_dict.update({klucz: {'suma': suma, 'liczba': liczba}}) %} + {% endfor %} + + {% for rok, miesiac, suma, liczba in wydatki_miesieczne %} + {% set klucz = "%d-%02d"|format(rok|int, miesiac|int) %} + {% set _ = wydatki_dict.update({klucz: {'suma': suma, 'liczba': liczba}}) %} + {% endfor %} + + {% for rok, miesiac, suma, liczba in przesuniecia_miesieczne %} + {% set klucz = "%d-%02d"|format(rok|int, miesiac|int) %} + {% set _ = przesuniecia_dict.update({klucz: {'suma': suma, 'liczba': liczba}}) %} + {% endfor %} + + {% set miesiace = (wplaty_dict.keys() | list + wydatki_dict.keys() | list + przesuniecia_dict.keys() | list) | unique | sort | reverse %} + + {% for miesiac_key in miesiace %} + {% set wp = wplaty_dict.get(miesiac_key, {'suma': 0, 'liczba': 0}) %} + {% set wy = wydatki_dict.get(miesiac_key, {'suma': 0, 'liczba': 0}) %} + {% set pr = przesuniecia_dict.get(miesiac_key, {'suma': 0, 'liczba': 0}) %} + {% set bilans_m = wp.suma - wy.suma %} + + + + + + + + + + {% endfor %} + +
    MiesiącWpłaty (suma)Wpłaty (liczba)Wydatki (suma)Wydatki (liczba)Przesunięcia (suma)Bilans
    {{ miesiac_key }}{{ "%.2f"|format(wp.suma) }} zł{{ wp.liczba }}{{ "%.2f"|format(wy.suma) }} zł{{ wy.liczba }}{{ "%.2f"|format(pr.suma) }} zł + {{ "%.2f"|format(bilans_m) }} zł +
    +
    + {% else %} +
    +

    Brak danych

    +
    + {% endif %} +
    +
    + +
    + + +
    + +
    +
    +
    Zestawienie roczne
    +
    +
    + {% if wplaty_roczne or wydatki_roczne %} +
    + + + + + + + + + + + + + {% set wplaty_dict = {} %} + {% set wydatki_dict = {} %} + + {% for rok, suma, liczba in wplaty_roczne %} + {% set _ = wplaty_dict.update({rok|int: {'suma': suma, 'liczba': liczba}}) %} + {% endfor %} + + {% for rok, suma, liczba in wydatki_roczne %} + {% set _ = wydatki_dict.update({rok|int: {'suma': suma, 'liczba': liczba}}) %} + {% endfor %} + + {% set lata = (wplaty_dict.keys() | list + wydatki_dict.keys() | list) | unique | sort | reverse %} + + {% for rok in lata %} + {% set wp = wplaty_dict.get(rok, {'suma': 0, 'liczba': 0}) %} + {% set wy = wydatki_dict.get(rok, {'suma': 0, 'liczba': 0}) %} + {% set bilans_rok = wp.suma - wy.suma %} + + + + + + + + + {% endfor %} + +
    RokWpłaty (suma)Wpłaty (liczba)Wydatki (suma)Wydatki (liczba)Bilans
    {{ rok|int }}{{ "%.2f"|format(wp.suma) }} zł{{ wp.liczba }}{{ "%.2f"|format(wy.suma) }} zł{{ wy.liczba }} + {{ "%.2f"|format(bilans_rok) }} zł +
    +
    + {% else %} +
    +

    Brak danych

    +
    + {% endif %} +
    +
    + +
    + +
    + +
    +{% endblock %} diff --git a/templates/admin/ustawienia.html b/templates/admin/ustawienia.html index 206818d..40974ba 100644 --- a/templates/admin/ustawienia.html +++ b/templates/admin/ustawienia.html @@ -3,14 +3,30 @@ {% block content %}
    + + +
    +
    +

    Ustawienia globalne

    +

    Konfiguracja systemu, płatności i wyglądu

    +
    + +
    +
    - {# {{ form.csrf_token }} jeśli używasz Flask-WTF #}

    Dane płatności

    - Używane jako wartości domyślne przy dodawaniu/edycji zbiórek + Wartości domyślne dla zbiórek
    @@ -23,7 +39,7 @@ value="{{ settings.numer_konta if settings else '' }}" inputmode="numeric" autocomplete="off" placeholder="12 3456 7890 1234 5678 9012 3456" required aria-describedby="ibanHelp">
    -
    Wpisz ciąg cyfr — spacje dodadzą się automatycznie co 4 znaki.
    +
    Wpisz ciąg cyfr — spacje dodadzą się automatycznie co 4 znaki
    @@ -34,7 +50,7 @@ value="{{ settings.numer_telefonu_blik if settings else '' }}" inputmode="tel" pattern="[0-9 ]{9,13}" placeholder="123 456 789" required aria-describedby="blikHelp">
    -
    9 cyfr. Spacje i format 3-3-3 dodajemy dla czytelności.
    +
    9 cyfr. Format 3-3-3 dla czytelności
    @@ -43,8 +59,8 @@
    -

    Dostęp — dozwolone adresy IP / hosty

    - Zależnie od konfiguracji logowanie może wymagać dopasowania do białej listy +

    Kontrola dostępu

    + Biała lista IP/hostów dla logowania
    @@ -59,16 +75,16 @@ ➕ Dodaj
    -
    Po wpisaniu kliknij „Dodaj”. Duplikaty są pomijane.
    +
    Po wpisaniu kliknij „Dodaj". Duplikaty są pomijane
    @@ -86,12 +102,37 @@ placeholder="Adresy IP lub nazwy domen — każdy w osobnej linii lub rozdzielony przecinkiem">{{ settings.dozwolone_hosty_logowania if settings and settings.dozwolone_hosty_logowania else '' }} - Akceptowane separatory: przecinek (`,`), średnik (`;`) i nowa linia. + Akceptowane separatory: przecinek (`,`), średnik (`;`) i nowa linia + +
    +
    +

    Kolejność wyświetlania

    + Pozycja list rezerwowych na stronie głównej +
    + +
    +
    + + + Określa, gdzie na stronie głównej będą wyświetlane listy rezerwowe względem standardowych zbiórek +
    +
    +
    @@ -107,7 +148,7 @@ -
    Transparentne, do ~60px wysokości.
    +
    Transparentne, do ~60px wysokości
    {% if settings and settings.logo_url %}
    Logo preview @@ -140,7 +181,7 @@ %}checked{% endif %}>
    -
    Jeśli wybierzesz logo, użyjemy adresu z pola "Tytuł serwisu".
    +
    Jeśli wybierzesz logo, użyjemy adresu z pola "Logo URL"
    @@ -157,17 +198,16 @@ - + -
    Pozostaw pusty, by użyć domyślnego.
    +
    Pozostaw pusty, by użyć domyślnego
    -
    Powrót @@ -180,4 +220,4 @@ {% block extra_scripts %} {{ super() }} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/index.html b/templates/index.html index 5aa50ce..f3e7f57 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,124 +1,143 @@ {% extends 'base.html' %} -{% block title %}{% if request.path == url_for('zbiorki_zrealizowane') %}Zrealizowane zbiórki{% else %}Aktualnie aktywne -zbiórki{% endif %}{% endblock %} +{% block title %}{% if request.path == url_for('zbiorki_zrealizowane') %}Zrealizowane zbiórki{% else %}Aktualnie aktywne zbiórki{% endif %}{% endblock %} {% block content %} {# Ustal kontekst listy #} {% set is_completed_view = (request.path == url_for('zbiorki_zrealizowane')) %} -
    -

    - {% if is_completed_view %}Zrealizowane zbiórki{% else %}Aktywne zbiórki{% endif %} -

    - -
    - -{% if zbiorki and zbiorki|length > 0 %} -
    - {% for z in zbiorki %} - {% set progress = (z.stan / z.cel * 100) if z.cel > 0 else 0 %} - {% set progress_clamped = 100 if progress > 100 else (0 if progress < 0 else progress) %} -
    -
    -
    -
    -
    {{ z.nazwa }}
    - - {% if z.typ_zbiorki == 'rezerwa' %} - Rezerwa - {% elif is_completed_view or progress_clamped >= 100 %} - Zrealizowana +
    + +
    +
    +

    + {% if is_completed_view %}Zrealizowane zbiórki{% else %}Aktywne zbiórki{% endif %} +

    +

    + {% if is_completed_view %} + Ukończone projekty i osiągnięte cele + {% else %} + Trwające zbiórki, które możesz wesprzeć + {% endif %} +

    +
    +
    -
    +
    + + + +
    -
    - {% if not z.ukryj_kwote %} - {% if z.cel > 0 and z.typ_zbiorki != 'rezerwa' %} - - Cel: {{ z.cel|round(2) }} PLN - - {% endif %} - - Stan: {{ z.stan|round(2) }} PLN - + {% if zbiorki and zbiorki|length > 0 %} +
    + {% for z in zbiorki %} + {% set progress = (z.stan / z.cel * 100) if z.cel > 0 else 0 %} + {% set progress_clamped = 100 if progress > 100 else (0 if progress < 0 else progress) %} +
    +
    +
    +
    +
    {{ z.nazwa }}
    - {% if z.cel > 0 and z.typ_zbiorki != 'rezerwa' %} - {% set delta = z.cel - z.stan %} - {% if delta > 0 %} - - Brakuje: {{ delta|round(2) }} PLN - - {% elif delta < 0 %} - - Nadwyżka: {{ (-delta)|round(2) }} PLN - - {% endif %} - {% endif %} - {% else %} - Kwoty niepubliczne - {% endif %} -
    + {% if z.typ_zbiorki == 'rezerwa' %} + Rezerwa + {% elif is_completed_view or progress_clamped >= 100 %} + Zrealizowana + {% endif %} +
    +
    - {# Progress bar TYLKO dla standardowych zbiórek (nie dla rezerwowych) #} - {% if z.typ_zbiorki != 'rezerwa' %} -
    -
    -
    +
    + {% if not z.ukryj_kwote %} + {% if z.cel > 0 and z.typ_zbiorki != 'rezerwa' %} + + Cel: {{ z.cel|round(2) }} PLN + + {% endif %} + + Stan: {{ z.stan|round(2) }} PLN + + + {% if z.cel > 0 and z.typ_zbiorki != 'rezerwa' %} + {% set delta = z.cel - z.stan %} + {% if delta > 0 %} + + Brakuje: {{ delta|round(2) }} PLN + + {% elif delta < 0 %} + + Nadwyżka: {{ (-delta)|round(2) }} PLN + + {% endif %} + {% endif %} + {% else %} + Kwoty niepubliczne + {% endif %}
    - {% if not z.ukryj_kwote %} - {{ progress_clamped|round(1) }}% - {% else %} - Postęp ukryty + {# Progress bar TYLKO dla standardowych zbiórek (nie dla rezerwowych) #} + {% if z.typ_zbiorki != 'rezerwa' %} +
    +
    +
    +
    + + {% if not z.ukryj_kwote %} + {{ progress_clamped|round(1) }}% + {% else %} + Postęp ukryty + {% endif %} +
    {% endif %} -
    - {% endif %} -
    - {# TO POWODUJE ZE BLOK JEST KLIKALNY #} - - -
    + {% endfor %}
    - {% endfor %} -
    -{% else %} -
    -
    - {% if is_completed_view %} -
    Brak zrealizowanych zbiórek
    -

    Gdy jakaś zbiórka osiągnie 100%, pojawi się tutaj.

    - Zobacz aktywne - {% else %} -
    Brak aktywnych zbiórek
    -

    Wygląda na to, że teraz nic nie zbieramy.

    - {% if current_user.is_authenticated and current_user.czy_admin %} - Utwórz nową zbiórkę - {% else %} - Zobacz zrealizowane - {% endif %} - {% endif %} + {% else %} + +
    +
    + {% if is_completed_view %} +
    Brak zrealizowanych zbiórek
    +

    Gdy jakaś zbiórka osiągnie 100%, pojawi się tutaj.

    + Zobacz aktywne + {% else %} +
    Brak aktywnych zbiórek
    +

    Wygląda na to, że teraz nic nie zbieramy.

    + {% if current_user.is_authenticated and current_user.czy_admin %} + Utwórz nową zbiórkę + {% else %} + Zobacz zrealizowane + {% endif %} + {% endif %} +
    + {% endif %}
    -{% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/zbiorka.html b/templates/zbiorka.html index c17bc51..8180ca7 100644 --- a/templates/zbiorka.html +++ b/templates/zbiorka.html @@ -12,12 +12,21 @@
    -

    +
    +

    + {% if zbiorka.typ_zbiorki == 'rezerwa' %} + + {% endif %} + {{ zbiorka.nazwa }} +

    {% if zbiorka.typ_zbiorki == 'rezerwa' %} - +

    Lista rezerwowa środków

    + {% elif is_done %} +

    Zbiórka zrealizowana

    + {% else %} +

    Aktywna zbiórka

    {% endif %} - {{ zbiorka.nazwa }} -

    +
    {% if zbiorka.typ_zbiorki == 'rezerwa' %} Lista rezerwowa @@ -33,15 +42,15 @@
    -
    +
    -
    +
    Opis
    -
    +
    {{ zbiorka.opis | markdown }}
    @@ -54,14 +63,17 @@ {% if has_items %} -
    +
    -
    Lista zakupów
    -
    +
    +
    Lista zakupów
    + {{ items|length }} pozycji +
    +
    {% set posortowane = items|sort(attribute='kupione') %}
      {% for it in posortowane %} -
    • +
    • {% if it.kupione %} Kupione @@ -70,8 +82,7 @@ {% endif %} {{ it.nazwa }} {% if it.link %} - Sklep - ↗ + Sklep ↗ {% endif %}
      @@ -131,11 +142,11 @@
    -
    +
    {# Pasek: Finanse #} {% if zbiorka.pokaz_postep_finanse %} -
    +
    Finanse
    @@ -149,7 +160,7 @@ {# Pasek: Zakupy sztukami #} {% if has_items and zbiorka.pokaz_postep_pozycje %} -
    +
    Zakupy (liczba pozycji)
    @@ -183,7 +194,7 @@
    -
    +
    @@ -208,12 +219,12 @@
    -
    +
    -
    @@ -223,12 +234,12 @@
    -
    +
    -
    @@ -240,23 +251,23 @@ {% endif %} {% if not zbiorka.ukryj_kwote %} -
      +
        {% if zbiorka.typ_zbiorki != 'rezerwa' %} {% if has_cel %} -
      • +
      • Cel {{ zbiorka.cel|round(2) }} PLN
      • {% endif %} {% endif %} -
      • +
      • Stan {{ zbiorka.stan|round(2) }} PLN
      • {% if zbiorka.typ_zbiorki != 'rezerwa' and has_cel %} -
      • +
      • {% if brak > 0 %}Brakuje{% elif brak == 0 %}Cel{% else %}Nadwyżka{% endif %} @@ -275,17 +286,14 @@ {% endif %} {% if current_user.is_authenticated and current_user.czy_admin %} -
        -
        - Dodaj - wpłatę - Dodaj - wydatek +
        +
        + Dodaj wpłatę + Dodaj wydatek Przesuń środki - Edytuj - stan + Edytuj stan {% if zbiorka.typ_zbiorki != 'rezerwa' %} Edytuj opis @@ -300,101 +308,102 @@
        - -
        -
        -
        Aktywność / Transakcje
        -
        - {% if aktywnosci and aktywnosci|length > 0 %} - Łącznie pozycji: {{ aktywnosci|length }} - {% endif %} - {% if current_user.is_authenticated and current_user.czy_admin %} - - Zarządzaj - - {% endif %} -
        -
        +
        -
        + +
        +
        +
        Aktywność / Transakcje
        +
        {% if aktywnosci and aktywnosci|length > 0 %} -
          - {% for a in aktywnosci %} -
        • -
          - {{ a.data|dt("%d.%m.%Y %H:%M") }} - - {% if a.typ == 'wpłata' %} - Wpłata - {% elif a.typ == 'wydatek' %} - Wydatek - {% elif a.typ == 'przesunięcie_przych' %} - Przesunięcie (↓ przychód) - {% elif a.typ == 'przesunięcie_wych' %} - Przesunięcie (↑ wychód) - {% endif %} - - {% if a.opis %} - — {{ a.opis }} - {% endif %} - - {# Informacja o przesunięciu wpłaty #} - {% if a.typ == 'wpłata' and a.przesuniecie_z %} -
          - Przesunięto - Źródło: - - {{ a.przesuniecie_z.zbiorka_zrodlo_nazwa }} - - {% if a.przesuniecie_z.opis %} -
          {{ a.przesuniecie_z.opis }} - {% endif %} -
          - {% endif %} - - {# Link do źródłowej/docelowej zbiórki dla przesunięć ogólnych #} - {% if a.typ in ['przesunięcie_przych', 'przesunięcie_wych'] and a.zbiorka_id %} - - - {% if a.typ == 'przesunięcie_przych' %} - z: {{ a.zbiorka_nazwa }} - {% else %} - do: {{ a.zbiorka_nazwa }} - {% endif %} - - {% endif %} -
          - - {% if not zbiorka.ukryj_kwote %} - - {% if a.typ == 'wpłata' or a.typ == 'przesunięcie_przych' %} - +{{ a.kwota|round(2) }} PLN - {% else %} - -{{ a.kwota|round(2) }} PLN - {% endif %} - - {% endif %} -
        • - {% endfor %} -
        - {% else %} -
        -
        Brak aktywności
        -

        Gdy pojawią się pierwsze wpłaty lub wydatki, zobaczysz je tutaj.

        -
        + Łącznie pozycji: {{ aktywnosci|length }} + {% endif %} + {% if current_user.is_authenticated and current_user.czy_admin %} + + Zarządzaj + {% endif %}
        - -
        -
        - Powrót do listy +
        + {% if aktywnosci and aktywnosci|length > 0 %} +
          + {% for a in aktywnosci %} +
        • +
          + {{ a.data|dt("%d.%m.%Y %H:%M") }} + + {% if a.typ == 'wpłata' %} + Wpłata + {% elif a.typ == 'wydatek' %} + Wydatek + {% elif a.typ == 'przesunięcie_przych' %} + Przesunięcie (↓ przychód) + {% elif a.typ == 'przesunięcie_wych' %} + Przesunięcie (↑ wychód) + {% endif %} + + {% if a.opis %} + — {{ a.opis }} + {% endif %} + + {# Informacja o przesunięciu wpłaty #} + {% if a.typ == 'wpłata' and a.przesuniecie_z %} +
          + Przesunięto + Źródło: + + {{ a.przesuniecie_z.zbiorka_zrodlo_nazwa }} + + {% if a.przesuniecie_z.opis %} +
          {{ a.przesuniecie_z.opis }} + {% endif %} +
          + {% endif %} + + {# Link do źródłowej/docelowej zbiórki dla przesunięć ogólnych #} + {% if a.typ in ['przesunięcie_przych', 'przesunięcie_wych'] and a.zbiorka_id %} + + + {% if a.typ == 'przesunięcie_przych' %} + z: {{ a.zbiorka_nazwa }} + {% else %} + do: {{ a.zbiorka_nazwa }} + {% endif %} + + {% endif %} +
          + + {% if not zbiorka.ukryj_kwote %} + + {% if a.typ == 'wpłata' or a.typ == 'przesunięcie_przych' %} + +{{ a.kwota|round(2) }} PLN + {% else %} + -{{ a.kwota|round(2) }} PLN + {% endif %} + + {% endif %} +
        • + {% endfor %} +
        + {% else %} +
        +
        Brak aktywności
        +

        Gdy pojawią się pierwsze wpłaty lub wydatki, zobaczysz je tutaj.

        +
        + {% endif %}
        -
        + + + +
        {% endblock %}