commit2 permissions
This commit is contained in:
47
app.py
47
app.py
@@ -1056,6 +1056,31 @@ def get_total_expenses_grouped_by_list_created_at(
|
||||
expenses = [round(grouped[l], 2) for l in labels]
|
||||
return {"labels": labels, "expenses": expenses}
|
||||
|
||||
def resolve_range(range_type: str):
|
||||
now = datetime.now(timezone.utc)
|
||||
sd = ed = None
|
||||
bucket = "monthly"
|
||||
|
||||
rt = (range_type or "").lower()
|
||||
if rt in ("last7days", "last_7_days"):
|
||||
sd = (now - timedelta(days=7)).date().strftime("%Y-%m-%d")
|
||||
ed = now.date().strftime("%Y-%m-%d")
|
||||
bucket = "daily"
|
||||
elif rt in ("last30days", "last_30_days"):
|
||||
sd = (now - timedelta(days=30)).date().strftime("%Y-%m-%d")
|
||||
ed = now.date().strftime("%Y-%m-%d")
|
||||
bucket = "monthly" elif rt in ("last90days", "last_90_days"):
|
||||
sd = (now - timedelta(days=90)).date().strftime("%Y-%m-%d")
|
||||
ed = now.date().strftime("%Y-%m-%d")
|
||||
bucket = "monthly"
|
||||
elif rt in ("thismonth", "this_month"):
|
||||
first = datetime(now.year, now.month, 1, tzinfo=timezone.utc)
|
||||
sd = first.date().strftime("%Y-%m-%d")
|
||||
ed = now.date().strftime("%Y-%m-%d")
|
||||
bucket = "monthly"
|
||||
|
||||
return sd, ed, bucket
|
||||
|
||||
|
||||
def save_pdf_as_webp(file, path):
|
||||
try:
|
||||
@@ -1960,24 +1985,19 @@ def view_list(list_id):
|
||||
@app.route("/expenses")
|
||||
@login_required
|
||||
def expenses():
|
||||
from sqlalchemy.orm import joinedload
|
||||
from types import SimpleNamespace
|
||||
|
||||
start_date_str = request.args.get("start_date")
|
||||
end_date_str = request.args.get("end_date")
|
||||
category_id = request.args.get("category_id", type=str) # może być "none"
|
||||
category_id = request.args.get("category_id", type=str)
|
||||
show_all = request.args.get("show_all", "true").lower() == "true"
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
# --- 1) Widoczne listy (właściciel/publiczne/udzielone), aktywne (niearch./niewygasłe) ---
|
||||
visible_clause = visible_lists_clause_for_expenses(
|
||||
user_id=current_user.id, include_shared=show_all, now_dt=now
|
||||
)
|
||||
|
||||
lists_q = ShoppingList.query.filter(*visible_clause)
|
||||
|
||||
# Filtr zakresu po CREATED_AT LISTY (nie po Expense)
|
||||
if start_date_str and end_date_str:
|
||||
try:
|
||||
start = datetime.strptime(start_date_str, "%Y-%m-%d")
|
||||
@@ -1989,7 +2009,6 @@ def expenses():
|
||||
except ValueError:
|
||||
flash("Błędny zakres dat", "danger")
|
||||
|
||||
# Filtr kategorii na LISTACH (bez joinów, żeby nie ucinać „none”)
|
||||
if category_id:
|
||||
if category_id == "none":
|
||||
lists_q = lists_q.filter(~ShoppingList.categories.any())
|
||||
@@ -2003,7 +2022,6 @@ def expenses():
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
# Materializacja list (to jest pełna, finalna lista do wyświetlenia)
|
||||
lists_filtered = (
|
||||
lists_q
|
||||
.options(joinedload(ShoppingList.owner), joinedload(ShoppingList.categories))
|
||||
@@ -2012,7 +2030,6 @@ def expenses():
|
||||
)
|
||||
list_ids = [l.id for l in lists_filtered] or [-1]
|
||||
|
||||
# --- 2) Wydatki: po wybranych listach (bez dodatkowego filtra dat, bo zakres dotyczy list) ---
|
||||
expenses = (
|
||||
Expense.query
|
||||
.options(
|
||||
@@ -2024,7 +2041,6 @@ def expenses():
|
||||
.all()
|
||||
)
|
||||
|
||||
# --- 3) Sumy per lista (LEFT OUTER JOIN + COALESCE; nie utnie list bez wydatków) ---
|
||||
totals_rows = (
|
||||
db.session.query(
|
||||
ShoppingList.id.label("lid"),
|
||||
@@ -2038,7 +2054,6 @@ def expenses():
|
||||
)
|
||||
totals_map = {row.lid: float(row.total_expense or 0) for row in totals_rows}
|
||||
|
||||
# --- 4) Kategorie do filtra (z samych widocznych list po filtrach) ---
|
||||
categories = (
|
||||
Category.query
|
||||
.join(shopping_list_category, shopping_list_category.c.category_id == Category.id)
|
||||
@@ -2050,7 +2065,6 @@ def expenses():
|
||||
)
|
||||
categories.append(SimpleNamespace(id="none", name="Bez kategorii"))
|
||||
|
||||
# --- 5) Dane do widoku (format bez zmian) ---
|
||||
expense_table = [
|
||||
{
|
||||
"title": (e.shopping_list.title if e.shopping_list else "Nieznana"),
|
||||
@@ -2065,7 +2079,7 @@ def expenses():
|
||||
"id": l.id,
|
||||
"title": l.title,
|
||||
"created_at": l.created_at,
|
||||
"total_expense": totals_map.get(l.id, 0.0), # 0.0 gdy brak wydatków
|
||||
"total_expense": totals_map.get(l.id, 0.0),
|
||||
"owner_username": l.owner.username if l.owner else "?",
|
||||
"categories": [c.id for c in l.categories],
|
||||
}
|
||||
@@ -2092,6 +2106,13 @@ def expenses_data():
|
||||
category_id = request.args.get("category_id")
|
||||
by_category = request.args.get("by_category", "false").lower() == "true"
|
||||
|
||||
if not start_date or not end_date:
|
||||
sd, ed, bucket = resolve_range(range_type)
|
||||
if sd and ed:
|
||||
start_date = sd
|
||||
end_date = ed
|
||||
range_type = bucket
|
||||
|
||||
if by_category:
|
||||
result = get_total_expenses_grouped_by_category(
|
||||
show_all=show_all,
|
||||
|
@@ -234,7 +234,7 @@ function toggleVisibility(listId) {
|
||||
toggleBtn.innerHTML = '🙈 Ukryj listę';
|
||||
} else {
|
||||
shareHeader.textContent = '🔗 Udostępnij link (widoczna tylko przez link / uprawnienia)';
|
||||
toggleBtn.innerHTML = '👁️ Uczyń publiczną';
|
||||
toggleBtn.innerHTML = '🐵 Uczyń publiczną';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="showAllLists" {% if show_all %}checked{% endif %}>
|
||||
<label class="form-check-label ms-2 text-white" for="showAllLists">
|
||||
Pokaż wszystkie publiczne listy innych
|
||||
Uwzględnij listy udostępnione dla mnie i publiczne
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -55,10 +55,9 @@
|
||||
{% if list.is_public %}
|
||||
🙈 Ustaw niepubliczną
|
||||
{% else %}
|
||||
👁️ Uczyń publiczną
|
||||
🐵 Uczyń publiczną
|
||||
{% endif %}
|
||||
</button>
|
||||
|
||||
<a href="{{ url_for('edit_my_list', list_id=list.id, next=url_for('view_list', list_id=list.id)) }}"
|
||||
class="btn btn-outline-info btn-sm flex-fill">
|
||||
➕ Nadaj dostęp
|
||||
|
@@ -63,7 +63,7 @@
|
||||
Twoje listy
|
||||
<button type="button" class="btn btn-sm btn-outline-light ms-2" data-bs-toggle="modal"
|
||||
data-bs-target="#archivedModal">
|
||||
📁 Zarchiwizowane
|
||||
🗄️ Zarchiwizowane
|
||||
</button>
|
||||
</h3>
|
||||
{% if user_lists %}
|
||||
@@ -87,15 +87,14 @@
|
||||
|
||||
<div class="btn-group mt-2 mt-md-0" role="group">
|
||||
<a href="{{ url_for('view_list', list_id=l.id) }}"
|
||||
class="btn btn-sm btn-outline-light d-flex align-items-center text-nowrap">📄 Otwórz</a>
|
||||
class="btn btn-sm btn-outline-light d-flex align-items-center text-nowrap">📂 Otwórz</a>
|
||||
<a href="{{ url_for('shared_list', token=l.share_token) }}"
|
||||
class="btn btn-sm btn-outline-light d-flex align-items-center text-nowrap">✏️ Odznaczaj</a>
|
||||
<a href="{{ url_for('copy_list', list_id=l.id) }}"
|
||||
class="btn btn-sm btn-outline-light d-flex align-items-center text-nowrap">📋 Kopiuj</a>
|
||||
|
||||
<a href="{{ url_for('toggle_visibility', list_id=l.id) }}"
|
||||
class="btn btn-sm btn-outline-light d-flex align-items-center text-nowrap">
|
||||
{% if l.is_public %}🙈 Niepubliczna{% else %}👁️ Publiczna{% endif %}
|
||||
{% if l.is_public %}🙈 Ukryj{% else %}🐵 Odkryj{% endif %}
|
||||
</a>
|
||||
<a href="{{ url_for('edit_my_list', list_id=l.id) }}"
|
||||
class="btn btn-sm btn-outline-light d-flex align-items-center text-nowrap">⚙️ Ustawienia</a>
|
||||
|
Reference in New Issue
Block a user