from collections import defaultdict from datetime import datetime, date from decimal import Decimal from functools import wraps from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, abort from flask_login import login_required, current_user from sqlalchemy import extract from sqlalchemy.exc import IntegrityError from .extensions import db from .models import User, Vehicle, FuelEntry, CompanySettings, AppSetting, OrlenPrice, FuelCardPolicy, FuelStationCompany, FuelStationPoint, FuelCard, FuelCardStationRule from .services import calculate_costs, fetch_orlen_price, fetch_orlen_range, invoice_period, fetch_ure_stations, aggregate_ure_companies, POLISH_REGIONS from .station_catalog import sync_station_catalog from . import THEMES from .vat import effective_vat_rate, global_vat_override, save_global_vat_override main = Blueprint("main", __name__) FUEL_TYPES = ("PB95", "PB98", "DIESEL", "LPG") def wants_json(): return request.headers.get("X-Requested-With") == "XMLHttpRequest" or request.is_json def ok_response(message, **extra): if wants_json(): return jsonify({"ok": True, "message": message, **extra}) flash(message, "success") return None def error_response(message, status=400): if wants_json(): return jsonify({"ok": False, "error": message}), status flash(message, "danger") return None def roles(*allowed): def deco(fn): @wraps(fn) def wrapped(*a, **kw): if current_user.role not in allowed: flash("Brak uprawnień", "danger") return redirect(url_for("main.dashboard")) return fn(*a, **kw) return wrapped return deco def accessible_vehicles(): if current_user.role == "admin": return Vehicle.query.order_by(Vehicle.name).all() if current_user.role == "boss" and current_user.company_id: return Vehicle.query.filter_by(company_id=current_user.company_id).order_by(Vehicle.name).all() ids = {v.id for v in current_user.owned_vehicles + current_user.shared_vehicles} return Vehicle.query.filter(Vehicle.id.in_(ids)).order_by(Vehicle.name).all() if ids else [] @main.route("/") @login_required def dashboard(): settings = CompanySettings.query.first(); vehicles = accessible_vehicles(); ids = [v.id for v in vehicles] split_enabled = (db.session.get(AppSetting, "invoice_split_enabled") or AppSetting(key="invoice_split_enabled", value="1")).value == "1" month = request.args.get("month", datetime.now().strftime("%Y-%m")) try: year, mon = map(int, month.split("-")) except ValueError: year, mon = datetime.now().year, datetime.now().month; month=f"{year:04d}-{mon:02d}" entries = FuelEntry.query.filter(FuelEntry.vehicle_id.in_(ids), extract("year", FuelEntry.fueled_at)==year, extract("month", FuelEntry.fueled_at)==mon).order_by(FuelEntry.fueled_at.desc()).all() if ids else [] totals = {"gross": Decimal("0"), "deductible_vat": Decimal("0"), "final_cost": Decimal("0"), "liters": Decimal("0")} by_vehicle = defaultdict(lambda: {"gross": 0, "payable": 0, "liters": 0}); invoices = defaultdict(lambda: {"gross": 0, "payable": 0, "count": 0}); settlements = {} for e in entries: vat_rate = effective_vat_rate(settings.vat_rate, e.fueled_at) c = calculate_costs(e.gross, vat_rate, settings.vat_deduction_percent) totals["gross"] += c["gross"]; totals["deductible_vat"] += c["deductible_vat"]; totals["final_cost"] += c["final_cost"]; totals["liters"] += Decimal(e.liters) station_policy = e.station_company normal_price = float(e.price_per_liter) payable_price = normal_price rule = FuelCardStationRule.query.filter_by(fuel_card_id=e.fuel_card_id, station_company_id=e.station_company_id).first() if e.used_fuel_card and e.fuel_card_id and e.station_company_id else None uses_station_last_price = bool(rule and rule.use_orlen_last_price and e.wholesale_price is not None) if uses_station_last_price: payable_price = float(e.wholesale_price) * (1 + float(vat_rate) / 100) if rule: net = payable_price / (1 + float(vat_rate) / 100) net = net * (1 - float(rule.discount_net_percent or 0) / 100) + float(rule.surcharge_net_per_liter or 0) payable_price = net * (1 + float(vat_rate) / 100) payable_price = max(payable_price, 0) payable_gross = float(e.liters) * payable_price settlements[e.id] = {"normal_price": normal_price, "payable_price": payable_price, "normal_gross": e.gross, "payable_gross": payable_gross, "uses_last_price": uses_station_last_price, "vat_rate": float(vat_rate)} by_vehicle[e.vehicle.name]["gross"] += float(c["gross"]); by_vehicle[e.vehicle.name]["payable"] += payable_gross; by_vehicle[e.vehicle.name]["liters"] += float(e.liters) if e.used_fuel_card: key = invoice_period(e.fueled_at, settings.invoice_split_day) if split_enabled else "Cały miesiąc" else: key = "Poza kartą" invoices[key]["gross"] += float(c["gross"]); invoices[key]["payable"] += payable_gross; invoices[key]["count"] += 1 return render_template("dashboard.html", vehicles=vehicles, entries=entries, totals=totals, month=month, by_vehicle=dict(by_vehicle), invoices=dict(invoices), settings=settings, split_enabled=split_enabled, settlements=settlements, vat_override=global_vat_override()) @main.route("/vehicles", methods=["GET", "POST"]) @login_required def vehicles(): if request.method == "POST": v = Vehicle(name=request.form["name"], make=request.form["make"], model=request.form["model"], registration=request.form["registration"].upper(), fuel_type=request.form["fuel_type"], owner_id=current_user.id, has_fuel_card="has_fuel_card" in request.form, card_provider=request.form.get("card_provider"), current_odometer=int(request.form.get("current_odometer") or 0)) db.session.add(v); db.session.commit(); response = ok_response("Dodano pojazd", vehicle_id=v.id); return response or redirect(url_for("main.vehicles")) company_id=current_user.company_id users_q=User.query.filter_by(active=True) cards_q=FuelCard.query.filter_by(active=True) if current_user.role=='boss' and company_id: users_q=users_q.filter_by(company_id=company_id);cards_q=cards_q.filter_by(company_id=company_id) return render_template("vehicles.html", vehicles=accessible_vehicles(), fuel_cards=cards_q.order_by(FuelCard.name).all(), assignable_users=users_q.order_by(User.name).all()) @main.route("/vehicles//share", methods=["POST"]) @login_required def share_vehicle(vehicle_id): vehicle = Vehicle.query.get_or_404(vehicle_id) if current_user.role != "admin" and vehicle.owner_id != current_user.id: response = error_response("Tylko właściciel lub admin może udostępniać auto", 403); return response or redirect(url_for("main.vehicles")) user = User.query.filter_by(email=request.form["email"].strip().lower()).first() if not user: response = error_response("Nie znaleziono użytkownika", 404); return response or redirect(url_for("main.vehicles")) if user not in vehicle.drivers: vehicle.drivers.append(user); db.session.commit() response = ok_response("Udostępniono pojazd"); return response or redirect(url_for("main.vehicles")) @main.post("/vehicles//unshare/") @login_required def unshare_vehicle(vehicle_id, user_id): vehicle = Vehicle.query.get_or_404(vehicle_id) if current_user.role != "admin" and vehicle.owner_id != current_user.id: return error_response("Tylko właściciel lub admin może odebrać dostęp", 403) user = User.query.get_or_404(user_id) if user in vehicle.drivers: vehicle.drivers.remove(user); db.session.commit() response = ok_response("Odebrano dostęp do pojazdu") return response or redirect(url_for("main.vehicles")) @main.post("/vehicles//card-policy") @login_required def update_card_policy(vehicle_id): vehicle = Vehicle.query.get_or_404(vehicle_id) if current_user.role != "admin" and vehicle.owner_id != current_user.id: return error_response("Tylko właściciel lub admin może zmienić zasady rozliczeń", 403) policy = vehicle.fuel_card_policy or FuelCardPolicy(vehicle=vehicle) policy.use_orlen_last_price = "use_orlen_last_price" in request.form policy.discount_percent = Decimal(request.form.get("discount_percent") or 0) policy.surcharge_per_liter = Decimal(request.form.get("surcharge_per_liter") or 0) policy.description = request.form.get("description", "").strip() or None db.session.add(policy); db.session.commit() response = ok_response("Zapisano zasady rozliczeń karty") return response or redirect(url_for("main.vehicles")) @main.route("/fuel", methods=["GET", "POST"]) @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: response = error_response("Brak dostępu do pojazdu", 403); return response or redirect(url_for("main.fuel")) odometer = int(request.form["odometer"]) if odometer < vehicle.current_odometer: response = error_response("Licznik nie może być niższy od poprzedniego"); return response or redirect(url_for("main.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 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")) if station_company and access_mode == "favorites_allowed_only" and station_company not in settings.allowed_stations and station_company not in settings.favorite_stations: response = error_response("Ta stacja nie jest ulubiona ani dozwolona przez politykę firmy", 403); return response or redirect(url_for("main.fuel")) price_region = station_company.selected_region if station_company and station_company.selected_region else settings.region try: p = fetch_orlen_price(request.form["fuel_type"], fueled_at.date(), price_region); wholesale=p["price_per_liter"]; source=p["source"] except Exception: pass 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(); response = ok_response("Zapisano tankowanie", redirect=url_for("main.dashboard")); return response or redirect(url_for("main.dashboard")) allowed=list(settings.allowed_stations) company_favorites=list(settings.favorite_stations) access_mode=settings.station_access_mode or "all_prefer_favorites" base_query=FuelStationCompany.query.filter_by(active=True) if access_mode == "allowed_only": allowed_ids=[x.id for x in allowed] stations=base_query.filter(FuelStationCompany.id.in_(allowed_ids)).all() if allowed_ids else [] elif access_mode == "favorites_allowed_only": scope_ids={x.id for x in allowed}|{x.id for x in company_favorites} stations=base_query.filter(FuelStationCompany.id.in_(scope_ids)).all() if scope_ids else [] else: stations=base_query.all() stations=sorted(stations,key=lambda x:(-x.station_count,x.display_name.lower())) user_favorites=[x for x in current_user.favorite_stations if x in stations] user_favorite_ids={x.id for x in current_user.favorite_stations} favorites=list(user_favorites) favorite_scope="user" if not favorites and access_mode in ("all_prefer_favorites", "favorites_allowed_only") and company_favorites: favorites=[x for x in company_favorites if x in stations];favorite_scope="company" if not favorites and access_mode == "all_prefer_favorites": favorites=stations[:10];favorite_scope="default" favorites=sorted(favorites,key=lambda x:(-x.station_count,x.display_name.lower())) favorite_ids={x.id for x in favorites} top_stations=sorted((x for x in stations if x.id not in favorite_ids),key=lambda x:(-x.station_count,x.display_name.lower()))[:10] proposed_ids=favorite_ids|{x.id for x in top_stations} favorite_catalog=sorted(stations,key=lambda x:(0 if x.id in user_favorite_ids else 1,-x.station_count,x.display_name.lower())) other_stations=sorted((x for x in stations if x.id not in proposed_ids),key=lambda x:x.display_name.lower()) return render_template("fuel_form.html", vehicles=vehicles, stations=stations, favorite_stations=favorites, top_stations=top_stations, favorite_catalog=favorite_catalog, other_stations=other_stations, now=datetime.now().strftime("%Y-%m-%dT%H:%M"), restricted=access_mode in ("allowed_only", "favorites_allowed_only"), access_mode=access_mode, user_favorite_ids=user_favorite_ids, favorite_scope=favorite_scope, company=settings, fuel_cards=FuelCard.query.filter_by(active=True,company_id=settings.id).order_by(FuelCard.name).all()) @main.route("/api/orlen-price") @login_required def orlen_price(): try: return jsonify(fetch_orlen_price(request.args["fuel_type"], datetime.fromisoformat(request.args["date"]).date(), CompanySettings.query.first().region)) except Exception as exc: return jsonify({"error": str(exc)}), 502 @main.route("/orlen") @login_required def orlen_data(): settings = CompanySettings.query.first() selected_year = request.args.get("year", type=int) or date.today().year selected_fuels = [f.upper() for f in request.args.getlist("fuel") if f.upper() in FUEL_TYPES] or ["PB95"] include_vat = request.args.get("gross") == "1" show_lpg_regions = request.args.get('show_lpg_regions') == '1' selected_regions = [r.lower() for r in request.args.getlist("region") if r.lower() in POLISH_REGIONS] if not selected_regions: selected_regions=[settings.region.lower()] if not show_lpg_regions: selected_regions=[settings.region.lower()] query = OrlenPrice.query.filter(OrlenPrice.fuel_type.in_(selected_fuels), extract("year", OrlenPrice.effective_date)==selected_year) rows = query.order_by(OrlenPrice.effective_date, OrlenPrice.fuel_type).all() chart = {} for fuel in selected_fuels: fuel_rows = [r for r in rows if r.fuel_type == fuel and (fuel != "LPG" or r.region in selected_regions)] chart_key = fuel if fuel != "LPG" else None if fuel == "LPG": for region in selected_regions: chart[f"LPG · {region}"] = [{"date": r.effective_date.isoformat(), "value": round(float(r.price_per_liter) * (1 + float(effective_vat_rate(settings.vat_rate, r.effective_date)) / 100 if include_vat else 1), 4)} for r in fuel_rows if r.region == region] else: chart[fuel] = [{"date": r.effective_date.isoformat(), "value": round(float(r.price_per_liter) * (1 + float(effective_vat_rate(settings.vat_rate, r.effective_date)) / 100 if include_vat else 1), 4)} for r in fuel_rows] return render_template("orlen.html", rows=rows, chart=chart, selected_fuels=selected_fuels, selected_year=selected_year, settings=settings, fuel_types=FUEL_TYPES, include_vat=include_vat, regions=POLISH_REGIONS, selected_regions=selected_regions, show_lpg_regions=show_lpg_regions) @main.post("/orlen/sync") @login_required @roles("boss", "admin") def orlen_sync(): payload = request.get_json(silent=True) or request.form fuels = payload.get("fuels", []) if isinstance(fuels, str): fuels = [fuels] fuels = [f.upper() for f in fuels if f.upper() in FUEL_TYPES] year = int(payload.get("year", date.today().year)) if not fuels: return jsonify({"ok": False, "error": "Wybierz co najmniej jedno paliwo."}), 400 settings = CompanySettings.query.first(); start = date(year,1,1); end = min(date(year,12,31), date.today()) result = {}; total = 0 try: for fuel in fuels: imported = 0; updated = 0 for row in fetch_orlen_range(fuel, start, end, "all" if fuel == "LPG" else settings.region): existing = OrlenPrice.query.filter_by(fuel_type=row["fuel_type"], effective_date=row["effective_date"], region=row["region"]).first() if existing: existing.price_per_liter=row["price_per_liter"]; existing.raw_value=row["raw_value"]; existing.product_name=row["product_name"]; existing.source=row["source"]; existing.fetched_at=datetime.utcnow(); updated += 1 else: db.session.add(OrlenPrice(**row)); imported += 1 result[fuel] = {"added": imported, "updated": updated}; total += imported + updated db.session.commit() return jsonify({"ok": True, "message": f"Przetworzono {total} rekordów.", "result": result}) except Exception as exc: db.session.rollback(); return jsonify({"ok": False, "error": str(exc)}), 502 @main.route("/admin", methods=["GET","POST"]) @login_required @roles("boss", "admin") def admin(): if request.method=="POST": settings=current_user.company or CompanySettings.query.first() settings.name=request.form.get("name",settings.name);settings.entity_type=request.form.get("entity_type",settings.entity_type);settings.vat_rate=Decimal(request.form.get("vat_rate",settings.vat_rate));settings.vat_deduction_percent=Decimal(request.form.get("vat_deduction_percent",settings.vat_deduction_percent));settings.region=request.form.get("region",settings.region);settings.invoice_split_day=int(request.form.get("invoice_split_day",settings.invoice_split_day)) theme=request.form.get("theme") if theme in THEMES:db.session.merge(AppSetting(key="theme",value=theme)) db.session.commit();return ok_response("Zapisano") or redirect(url_for("main.admin_companies")) return redirect(url_for("main.admin_companies")) @main.get("/admin/companies") @login_required @roles("boss", "admin") def admin_companies(): q = request.args.get("q", "").strip() page = request.args.get("page", 1, type=int) selected_id = request.args.get("company_id", type=int) companies_query = CompanySettings.query if current_user.role == "boss": companies_query = companies_query.filter_by(id=current_user.company_id) elif q: companies_query = companies_query.filter(CompanySettings.name.ilike(f"%{q}%")) companies_page = companies_query.order_by(CompanySettings.name.asc()).paginate( page=page, per_page=20, error_out=False ) visible_companies = companies_page.items if current_user.role == "boss": selected_company = current_user.company else: selected_company = db.session.get(CompanySettings, selected_id) if selected_id else None if selected_company is None: selected_company = visible_companies[0] if visible_companies else CompanySettings.query.order_by(CompanySettings.name.asc()).first() if selected_company and current_user.role == "boss" and selected_company.id != current_user.company_id: abort(403) fuel_cards = FuelCard.query.filter_by(company_id=selected_company.id).order_by(FuelCard.name.asc()).all() if selected_company else [] stations = FuelStationCompany.query.filter_by(active=True).order_by( FuelStationCompany.station_count.desc(), FuelStationCompany.brand_name.asc() ).all() return render_template( "admin_companies.html", companies_page=companies_page, selected_company=selected_company, fuel_cards=fuel_cards, q=q, regions=POLISH_REGIONS, stations=stations, vat_override=global_vat_override(), ) @main.post("/admin/global-vat") @login_required @roles("admin") def update_global_vat(): try: save_global_vat_override(request.form) db.session.commit() response = ok_response("Zapisano globalne ustawienia VAT") return response or redirect(url_for("main.admin_companies")) except ValueError as exc: db.session.rollback() response = error_response(str(exc)) return response or redirect(url_for("main.admin_companies")) @main.get("/admin/application") @login_required @roles("admin") def admin_application(): return render_template("admin_application.html") @main.get("/admin/users") @login_required @roles("boss", "admin") def admin_users(): page=request.args.get("page",1,type=int);q=request.args.get("q","").strip();users_q=User.query if current_user.role=="boss": users_q=users_q.filter_by(company_id=current_user.company_id) if q: users_q=users_q.filter(db.or_(User.name.ilike(f"%{q}%"),User.email.ilike(f"%{q}%"))) users_page=users_q.order_by(User.name).paginate(page=page,per_page=25,error_out=False) companies=CompanySettings.query.order_by(CompanySettings.name).all() if current_user.role=="admin" else [current_user.company] cards=FuelCard.query.order_by(FuelCard.name).all() if current_user.role=="boss": cards=cards.filter_by(company_id=current_user.company_id).all() return render_template("admin_users.html",users_page=users_page,q=q,companies=[c for c in companies if c],fuel_cards=cards) @main.post("/admin/users/") @login_required @roles("admin") def edit_user(user_id): user = User.query.get_or_404(user_id) try: user.name = request.form["name"].strip(); user.email = request.form["email"].strip().lower(); user.role = request.form["role"] user.active = "active" in request.form password = request.form.get("password", "") if password: if len(password) < 8: raise ValueError("Nowe hasło musi mieć co najmniej 8 znaków.") user.set_password(password) db.session.commit(); response = ok_response(f"Zaktualizowano użytkownika {user.email}"); return response or redirect(url_for("main.admin")) except (ValueError, IntegrityError) as exc: db.session.rollback(); message = str(exc.orig) if isinstance(exc, IntegrityError) else str(exc); response = error_response(message); return response or redirect(url_for("main.admin", _anchor=f"user-{user_id}")) @main.route("/stations") @login_required @roles("boss", "admin") def stations(): q = request.args.get("q", "").strip() page = request.args.get("page", 1, type=int) sort = request.args.get("sort", "company_name") direction = request.args.get("direction", "asc") companies_q = CompanySettings.query.filter_by(active=True).order_by(CompanySettings.name.asc()) if current_user.role == "boss": companies_q = companies_q.filter_by(id=current_user.company_id) companies = companies_q.all() requested_company_id = request.args.get("company_id", type=int) if current_user.role == "boss": company = current_user.company elif requested_company_id is None: return render_template("stations_company_select.html", companies=companies) else: company = db.session.get(CompanySettings, requested_company_id) if company not in companies: abort(404, description="Nie znaleziono aktywnej firmy") if company is None: abort(404, description="Brak aktywnej firmy do konfiguracji") query = FuelStationCompany.query.filter_by(active=True) if q: terms = [term for term in q.split() if term] for term in terms: pattern = f"%{term}%" query = query.filter(db.or_(FuelStationCompany.brand_name.ilike(pattern), FuelStationCompany.company_name.ilike(pattern), FuelStationCompany.nip.ilike(pattern), FuelStationCompany.regon.ilike(pattern), FuelStationCompany.station_names.ilike(pattern))) columns = {"company_name": FuelStationCompany.brand_name, "station_count": FuelStationCompany.station_count, "nip": FuelStationCompany.nip, "regon": FuelStationCompany.regon, "selected_region": FuelStationCompany.selected_region} column = columns.get(sort, FuelStationCompany.company_name) query = query.order_by(column.desc() if direction == "desc" else column.asc(), FuelStationCompany.brand_name.asc()) station_page = query.paginate(page=page, per_page=30, error_out=False) cards_q=FuelCard.query.filter_by(active=True, company_id=company.id) cards=cards_q.order_by(FuelCard.name).all() rules={(r.fuel_card_id,r.station_company_id):r for r in FuelCardStationRule.query.filter(FuelCardStationRule.fuel_card_id.in_([c.id for c in cards] or [-1])).all()} return render_template("stations.html", station_page=station_page, q=q, regions=POLISH_REGIONS, sort=sort, direction=direction, allowed_ids={x.id for x in company.allowed_stations}, favorite_ids={x.id for x in company.favorite_stations}, company=company, companies=companies, fuel_cards=cards, card_rules=rules) @main.post("/stations/sync") @login_required @roles("boss", "admin") def stations_sync(): try: companies = aggregate_ure_companies(fetch_ure_stations()) added, updated = sync_station_catalog(companies) db.session.commit() response = ok_response(f"Katalog URE: dodano {added}, zaktualizowano {updated}.") return response or redirect(url_for("main.stations")) except Exception as exc: db.session.rollback(); response = error_response(f"Nie udało się pobrać danych URE: {exc}", 502); return response or redirect(url_for("main.stations")) @main.post("/stations/") @login_required @roles("boss", "admin") def update_station(station_id): station = FuelStationCompany.query.get_or_404(station_id) region = request.form.get("selected_region", "").strip().lower() if region and region not in station.region_list: return error_response("Wybrane województwo nie występuje w danych tej firmy.") station.selected_region = region station.active = "active" in request.form db.session.commit(); response = ok_response(f"Zapisano ustawienia firmy {station.company_name}"); return response or redirect(url_for("main.stations")) @main.get("/api/stations") @login_required def station_suggestions(): q = request.args.get("q", "").strip() query = FuelStationCompany.query.filter_by(active=True) if q: pattern = f"%{q}%" query = query.filter(db.or_(FuelStationCompany.brand_name.ilike(pattern), FuelStationCompany.company_name.ilike(pattern), FuelStationCompany.station_names.ilike(pattern), FuelStationCompany.nip.ilike(pattern))) rows = query.order_by(FuelStationCompany.company_name).limit(30).all() return jsonify([{"id": x.id, "name": x.company_name, "brands": x.station_name_list[:5], "region": x.selected_region, "last_price": x.use_orlen_last_price} for x in rows])