from collections import defaultdict from datetime import date, datetime from decimal import Decimal from flask import current_app, jsonify, request, url_for from flask_login import current_user, login_required, login_user from sqlalchemy import extract from sqlalchemy.exc import IntegrityError from . import api from .common import accessible_vehicles, boolv, fail, payload, response, role_required, station_dict, user_dict, vehicle_dict from .. import THEMES from ..api_tokens import create_access_token from ..extensions import db from ..models import AppSetting, CompanySettings, FuelCard, FuelCardPolicy, FuelCardStationRule, FuelEntry, FuelStationCompany, FuelStationPoint, OrlenPrice, User, Vehicle from ..services import POLISH_REGIONS, aggregate_ure_companies, calculate_costs, fetch_orlen_price, fetch_orlen_range, fetch_ure_stations, invoice_period from ..station_catalog import sync_station_catalog FUEL_TYPES = ("PB95", "PB98", "DIESEL", "LPG") @api.get('/stations') @login_required def stations(): q=request.args.get('q','').strip();page=request.args.get('page',1,type=int);per=min(request.args.get('per_page',30,type=int),100);query=FuelStationCompany.query.filter_by(active=True) if q: for term in [x for x in q.split() if x]: needle=f'%{term}%' query=query.filter(db.or_(FuelStationCompany.brand_name.ilike(needle),FuelStationCompany.company_name.ilike(needle),FuelStationCompany.nip.ilike(needle),FuelStationCompany.regon.ilike(needle),FuelStationCompany.station_names.ilike(needle))) sort=request.args.get('sort','company_name');direction=request.args.get('direction','asc') allowed={'company_name':FuelStationCompany.brand_name,'station_count':FuelStationCompany.station_count,'nip':FuelStationCompany.nip,'regon':FuelStationCompany.regon,'selected_region':FuelStationCompany.selected_region} col=allowed.get(sort,FuelStationCompany.company_name);query=query.order_by(col.desc() if direction=='desc' else col.asc(),FuelStationCompany.brand_name.asc()) p=query.paginate(page=page,per_page=per,error_out=False);return response({'items':[station_dict(x) for x in p.items],'page':p.page,'pages':p.pages,'total':p.total,'sort':sort,'direction':direction}) @api.get('/stations//points') @login_required def station_points(station_id): station=FuelStationCompany.query.get_or_404(station_id) page=max(request.args.get('page',1,type=int),1) per_page=min(max(request.args.get('per_page',50,type=int),1),100) q=(request.args.get('q') or '').strip() query=FuelStationPoint.query.filter_by(station_company_id=station.id) if q: needle=f'%{q}%' query=query.filter(db.or_(FuelStationPoint.station_name.ilike(needle),FuelStationPoint.street.ilike(needle),FuelStationPoint.city.ilike(needle),FuelStationPoint.postal_code.ilike(needle))) query=query.order_by(FuelStationPoint.city.asc(),FuelStationPoint.street.asc(),FuelStationPoint.street_number.asc()) result=query.paginate(page=page,per_page=per_page,error_out=False) items=[{'id':x.id,'dkn':x.ure_dkn.split(':',1)[0] if x.ure_dkn else '','name':x.station_name,'address':x.address,'city':x.city,'region':x.region,'coordinates':x.coordinates,'has_petrol':x.has_petrol,'has_diesel':x.has_diesel,'has_lpg':x.has_lpg} for x in result.items] return response({'station':station_dict(station),'items':items,'page':result.page,'pages':result.pages,'total':result.total}) @api.put('/me/favorite-stations') @login_required def favorite_stations(): d=payload();ids=request.form.getlist('station_ids') if request.form else d.get('station_ids',[]);ids=[ids] if isinstance(ids,(str,int)) else ids try: ids=[int(x) for x in ids] except Exception: return fail('Nieprawidłowa lista stacji') if len(ids)>10:return fail('Użytkownik może mieć maksymalnie 10 ulubionych stacji') current_user.favorite_stations=FuelStationCompany.query.filter(FuelStationCompany.id.in_(ids),FuelStationCompany.active.is_(True)).all() if ids else [] db.session.commit();return response([station_dict(x) for x in current_user.favorite_stations],'Zapisano ulubione stacje') @api.put('/settings/allowed-stations') @role_required('boss','admin') def allowed_stations(): d=payload();ids=request.form.getlist('station_ids') if request.form else d.get('station_ids',[]);ids=[ids] if isinstance(ids,(str,int)) else ids try: ids=[int(x) for x in ids] except Exception: return fail('Nieprawidłowa lista stacji') settings=CompanySettings.query.first();settings.allowed_stations=FuelStationCompany.query.filter(FuelStationCompany.id.in_(ids),FuelStationCompany.active.is_(True)).all() if ids else [] db.session.commit();return response([station_dict(x) for x in settings.allowed_stations],'Zapisano dozwolone stacje') @api.post('/stations/sync') @role_required('boss','admin') def stations_sync(): try: companies=aggregate_ure_companies(fetch_ure_stations()) added,updated=sync_station_catalog(companies) db.session.commit();return response({'added':added,'updated':updated},f'Katalog URE: dodano {added}, zaktualizowano {updated}') except Exception as exc:db.session.rollback();return fail(f'Nie udało się pobrać danych URE: {exc}',502) @api.put('/stations/') @role_required('boss','admin') def update_station(station_id): s=FuelStationCompany.query.get_or_404(station_id);d=payload() region=None if 'selected_region' in d: region=(d.get('selected_region') or '').strip().lower() if region and region not in s.region_list:return fail('Wybrane województwo nie występuje w danych tej firmy') try: company_id=int(d.get('company_id') or current_user.company_id or 0) except (TypeError,ValueError): return fail('Nieprawidłowa firma') settings=CompanySettings.query.get_or_404(company_id) if current_user.role=='boss' and current_user.company_id!=settings.id:return fail('Brak uprawnień',403) if region is not None: s.selected_region=region s.active=boolv(d,'active');allowed=boolv(d,'allowed') if allowed and s not in settings.allowed_stations: settings.allowed_stations.append(s) if not allowed and s in settings.allowed_stations: settings.allowed_stations.remove(s) db.session.commit();return response(station_dict(s),f'Zapisano ustawienia stacji dla firmy {settings.name}')