From 780681dfd5d9d02cce5e03eeaf80edfccd7c6aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Mon, 13 Jul 2026 16:20:36 +0200 Subject: [PATCH] uprawnienia do stacji i usuwanie firm --- app/api.py | 33 +++++++++++++++++++ app/main.py | 9 +++-- app/templates/admin_companies.html | 2 +- tests/test_app.py | 53 ++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/app/api.py b/app/api.py index 9b079dc..0c2ec48 100644 --- a/app/api.py +++ b/app/api.py @@ -303,6 +303,39 @@ def create_fuel_card(): def update_fuel_card(card_id): card=FuelCard.query.get_or_404(card_id);d=payload();card.name=d.get('name',card.name).strip();card.provider=d.get('provider',card.provider).strip();card.description=(d.get('description') or '').strip() or None;card.active=boolv(d,'active');db.session.commit();return response(message='Zapisano kartę') +@api.delete('/fuel-cards/') +@role_required('boss','admin') +def delete_fuel_card(card_id): + card = FuelCard.query.get_or_404(card_id) + if current_user.role == 'boss' and card.company_id != current_user.company_id: + return fail('Brak uprawnień', 403) + + users_count = User.query.filter_by(fuel_card_id=card.id).count() + vehicles_count = Vehicle.query.filter_by(fuel_card_id=card.id).count() + entries_count = FuelEntry.query.filter_by(fuel_card_id=card.id).count() + blockers = [] + if users_count: + blockers.append(f'{users_count} użytkowników') + if vehicles_count: + blockers.append(f'{vehicles_count} pojazdów') + if entries_count: + blockers.append(f'{entries_count} tankowań') + if blockers: + return fail( + 'Nie można usunąć karty, ponieważ jest przypisana do: ' + ', '.join(blockers) + + '. Najpierw odłącz kartę od użytkowników i pojazdów. Karty użytej w tankowaniach nie można usunąć; możesz ją wyłączyć.', + 409, + ) + + company_id = card.company_id + db.session.delete(card) + db.session.commit() + socketio.emit('app_changed', {'resource':'fuel_cards', 'deleted_id':card_id}) + return response( + {'redirect':url_for('main.admin_companies', company_id=company_id)}, + 'Usunięto kartę flotową', + ) + @api.put('/fuel-cards//stations/') @role_required('boss','admin') def update_card_station_rule(card_id,station_id): diff --git a/app/main.py b/app/main.py index ad9fd28..f93270f 100644 --- a/app/main.py +++ b/app/main.py @@ -146,6 +146,12 @@ def update_card_policy(vehicle_id): @login_required def fuel(): vehicles = accessible_vehicles() + settings = current_user.company or CompanySettings.query.first() + if settings is None: + flash("Najpierw utwórz firmę i przypisz do niej użytkownika, aby korzystać z tankowań.", "warning") + if current_user.role in ("admin", "boss"): + return redirect(url_for("main.admin_companies")) + return redirect(url_for("main.dashboard")) if request.method == "POST": vehicle = next((v for v in vehicles if v.id == int(request.form["vehicle_id"])), None) if not vehicle: @@ -156,7 +162,7 @@ def fuel(): fueled_at = datetime.fromisoformat(request.form["fueled_at"]); wholesale = None; source = None station_company_id = request.form.get("station_company_id", type=int) station_company = db.session.get(FuelStationCompany, station_company_id) if station_company_id else None - settings = vehicle.company or current_user.company or CompanySettings.query.first() + settings = vehicle.company or settings access_mode = settings.station_access_mode or "all_prefer_favorites" if station_company and access_mode == "allowed_only" and station_company not in settings.allowed_stations: response = error_response("Ta stacja nie jest dozwolona przez politykę firmy", 403); return response or redirect(url_for("main.fuel")) @@ -169,7 +175,6 @@ def fuel(): station_label = station_company.company_name if station_company else request.form.get("station") e = FuelEntry(vehicle_id=vehicle.id, user_id=current_user.id, fueled_at=fueled_at, liters=Decimal(request.form["liters"]), price_per_liter=Decimal(request.form["price_per_liter"]), fuel_type=request.form["fuel_type"], odometer=odometer, station=station_label, station_company_id=station_company.id if station_company else None, invoice_number=request.form.get("invoice_number"), used_fuel_card="used_fuel_card" in request.form, wholesale_price=wholesale, wholesale_source=source) vehicle.current_odometer = odometer; db.session.add(e); db.session.commit(); socketio.emit("fuel_added", {"vehicle": vehicle.name, "gross": round(e.gross,2)}); response = ok_response("Zapisano tankowanie", redirect=url_for("main.dashboard")); return response or redirect(url_for("main.dashboard")) - settings=current_user.company or CompanySettings.query.first() allowed=list(settings.allowed_stations) company_favorites=list(settings.favorite_stations) access_mode=settings.station_access_mode or "all_prefer_favorites" diff --git a/app/templates/admin_companies.html b/app/templates/admin_companies.html index 0da0c2f..72063b5 100644 --- a/app/templates/admin_companies.html +++ b/app/templates/admin_companies.html @@ -65,7 +65,7 @@
Karty paliwowe
-
{% for card in fuel_cards %}{% else %}{% endfor %}
KartaOperator kartyOpisStatus
{{card.name}}{{card.provider}}{{card.description or '—'}}{{'aktywna' if card.active else 'wyłączona'}}
Brak kart paliwowych.
+
{% for card in fuel_cards %}{% else %}{% endfor %}
KartaOperator kartyOpisStatusAkcje
{{card.name}}{{card.provider}}{{card.description or '—'}}{{'aktywna' if card.active else 'wyłączona'}}
Brak kart paliwowych.

Ulubione stacje firmy

Do 25 stacji. Pusta lista oznacza 10 największych sieci w Polsce.

diff --git a/tests/test_app.py b/tests/test_app.py index f4144b7..83747a5 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -263,3 +263,56 @@ def test_admin_stations_without_company_shows_company_picker(tmp_path): assert 'Wybierz firmę' in html assert 'name="company_id"' in html assert 'Przejdź do konfiguracji' in html + + +def test_fuel_without_company_redirects_instead_of_500(tmp_path): + from app.extensions import db + from app.models import CompanySettings, User + + app = make_app(tmp_path, "fuel-no-company.db") + client = app.test_client() + client.post('/login', data={'email':'admin@example.com','password':'admin123!'}) + with app.app_context(): + User.query.update({User.company_id: None}) + CompanySettings.query.delete() + db.session.commit() + response = client.get('/fuel', follow_redirects=False) + assert response.status_code == 302 + assert '/admin/companies' in response.headers['Location'] + + +def test_delete_unused_fuel_card(tmp_path): + app=make_app(tmp_path, "delete_card.db") + client=app.test_client() + client.post('/login',data={'email':'admin@example.com','password':'admin123!'}) + with app.app_context(): + from app.extensions import db + from app.models import CompanySettings, FuelCard + company=CompanySettings.query.first() + card=FuelCard(name='Karta do usunięcia',provider='Test',company_id=company.id) + db.session.add(card);db.session.commit();card_id=card.id + response=client.delete(f'/api/fuel-cards/{card_id}') + assert response.status_code == 200 + assert response.json['ok'] is True + with app.app_context(): + from app.models import FuelCard + assert FuelCard.query.get(card_id) is None + + +def test_cannot_delete_assigned_fuel_card(tmp_path): + app=make_app(tmp_path, "assigned_card.db") + client=app.test_client() + client.post('/login',data={'email':'admin@example.com','password':'admin123!'}) + with app.app_context(): + from app.extensions import db + from app.models import CompanySettings, FuelCard, User + company=CompanySettings.query.first() + card=FuelCard(name='Karta przypisana',provider='Test',company_id=company.id) + db.session.add(card);db.session.flush() + user=User.query.filter_by(email='admin@example.com').first() + user.fuel_card_id=card.id + db.session.commit();card_id=card.id + response=client.delete(f'/api/fuel-cards/{card_id}') + assert response.status_code == 409 + assert response.json['ok'] is False + assert 'użytkowników' in response.json['message']