wersja 0.0.4 #7

Merged
gru merged 47 commits from zliczanie_wydatkow_i_poprawki_w_js into master 2025-07-28 22:17:13 +02:00
2 changed files with 65 additions and 5 deletions
Showing only changes of commit 5c6e2f6540 - Show all commits

45
app.py
View File

@@ -1526,10 +1526,8 @@ def admin_panel():
purchased_count = l.purchased_count
percent = (purchased_count / total_count * 100) if total_count > 0 else 0
comments_count = len([i for i in items if i.note and i.note.strip() != ""])
receipt_pattern = f"list_{l.id}"
receipt_files = [f for f in all_files if receipt_pattern in f]
receipts_count = Receipt.query.filter_by(list_id=l.id).count()
# obliczenie czy wygasła
if l.is_temporary and l.expires_at:
expires_at = l.expires_at
if expires_at.tzinfo is None:
@@ -1545,7 +1543,7 @@ def admin_panel():
"purchased_count": purchased_count,
"percent": round(percent),
"comments_count": comments_count,
"receipts_count": len(receipt_files),
"receipts_count": receipts_count,
"total_expense": l.total_expense,
"expired": is_expired,
}
@@ -1743,7 +1741,44 @@ def admin_receipts(id):
flash("Nieprawidłowe ID listy.", "danger")
return redirect(url_for("admin_panel"))
return render_template("admin/receipts.html", receipts=receipts)
# Przeszukaj folder upload pod kątem „sierot”
upload_folder = app.config["UPLOAD_FOLDER"]
db_filenames = set(r.filename for r in receipts)
all_db_filenames = set(r.filename for r in Receipt.query.all()) # Wszystko z bazy
files_on_disk = set(os.listdir(upload_folder))
stale_files = [
f for f in files_on_disk
if f.endswith(".webp") and f not in all_db_filenames and f.startswith("list_")
]
# Przekaż do template: receipts (z bazy) i orphan_files (sieroty)
return render_template(
"admin/receipts.html",
receipts=receipts,
orphan_files=stale_files,
orphan_files_count=len(stale_files)
)
@app.route("/admin/delete_orphan_receipt_file/<filename>")
@login_required
@admin_required
def delete_orphan_receipt_file(filename):
upload_folder = app.config["UPLOAD_FOLDER"]
safe_filename = os.path.basename(filename)
file_path = os.path.join(upload_folder, safe_filename)
# Dowolnego pliku nie kasujemy jeśli jest w bazie (Receipt.filename)
if Receipt.query.filter_by(filename=safe_filename).first():
flash("Nie możesz usunąć pliku powiązanego z bazą!", "danger")
return redirect(url_for("admin_receipts", id="all"))
if not os.path.exists(file_path):
flash("Plik już nie istnieje.", "warning")
else:
try:
os.remove(file_path)
flash(f"Usunięto plik: {safe_filename}", "success")
except Exception as e:
flash(f"Błąd przy usuwaniu pliku: {e}", "danger")
return redirect(url_for("admin_receipts", id="all"))
@app.route("/admin/rotate_receipt/<int:receipt_id>")

View File

@@ -64,6 +64,31 @@
</div>
</div>
{% if orphan_files %}
<hr class="my-4">
<h4 class="mt-3 mb-2 text-warning">🧐 Znalezione nieprzypisane pliki ({{ orphan_files_count }})</h4>
<div class="row g-3">
{% for f in orphan_files %}
<div class="col-6 col-md-4 col-lg-3">
<div class="card bg-dark border-warning text-warning h-100">
<img src="{{ url_for('uploaded_file', filename=f) }}" class="card-img-top" style="object-fit: cover; height: 200px;">
<div class="card-body text-center">
<p class="small mb-1 fw-bold">{{ f }}</p>
<div class="alert alert-warning small py-1 mb-2">Brak powiązania z listą!</div>
<a href="{{ url_for('delete_orphan_receipt_file', filename=f) }}"
class="btn btn-sm btn-outline-danger w-100 mb-2"
onclick="return confirm('Na pewno usunąć WYŁĄCZNIE plik {{ f }} z dysku?');">
🗑 Usuń plik z serwera
</a>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<div class="modal fade" id="cropModal" tabindex="-1" aria-labelledby="cropModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content bg-dark text-white">