edycja tankowań
This commit is contained in:
+134
-1
@@ -4,7 +4,7 @@ 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, or_
|
||||
from sqlalchemy import extract, or_, and_
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from .extensions import db
|
||||
from .models import User, Vehicle, FuelEntry, CompanySettings, AppSetting, OrlenPrice, FuelCardPolicy, FuelStationCompany, FuelStationPoint, FuelCard, FuelCardStationRule
|
||||
@@ -43,6 +43,63 @@ def roles(*allowed):
|
||||
return wrapped
|
||||
return deco
|
||||
|
||||
|
||||
|
||||
def fuel_entry_accessible(entry):
|
||||
if current_user.role == "admin":
|
||||
return True
|
||||
if current_user.role != "boss" or not current_user.company_id:
|
||||
return False
|
||||
company_id = entry.vehicle.company_id or (entry.vehicle.owner.company_id if entry.vehicle and entry.vehicle.owner else None)
|
||||
return company_id == current_user.company_id
|
||||
|
||||
def recalculate_vehicle_odometer(vehicle):
|
||||
latest = (db.session.query(db.func.max(FuelEntry.odometer))
|
||||
.filter(FuelEntry.vehicle_id == vehicle.id)
|
||||
.scalar())
|
||||
vehicle.current_odometer = int(latest or 0)
|
||||
|
||||
def parse_fuel_entry_form(entry):
|
||||
vehicle_id = request.form.get("vehicle_id", type=int)
|
||||
vehicle = db.session.get(Vehicle, vehicle_id)
|
||||
if not vehicle:
|
||||
raise ValueError("Nie znaleziono pojazdu")
|
||||
if current_user.role == "boss" and (vehicle.company_id or vehicle.owner.company_id) != current_user.company_id:
|
||||
abort(403)
|
||||
fuel_type = (request.form.get("fuel_type") or "").upper()
|
||||
if fuel_type not in FUEL_TYPES:
|
||||
raise ValueError("Nieprawidłowy rodzaj paliwa")
|
||||
fueled_at = datetime.fromisoformat(request.form.get("fueled_at", ""))
|
||||
liters = Decimal(request.form.get("liters", ""))
|
||||
price = Decimal(request.form.get("price_per_liter", ""))
|
||||
odometer = int(request.form.get("odometer", ""))
|
||||
if liters <= 0:
|
||||
raise ValueError("Liczba litrów musi być większa od zera")
|
||||
if price < 0 or odometer < 0:
|
||||
raise ValueError("Cena i stan licznika nie mogą być ujemne")
|
||||
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
|
||||
fuel_card_id = request.form.get("fuel_card_id", type=int)
|
||||
fuel_card = db.session.get(FuelCard, fuel_card_id) if fuel_card_id else None
|
||||
company_id = vehicle.company_id or vehicle.owner.company_id
|
||||
if fuel_card and fuel_card.company_id and fuel_card.company_id != company_id:
|
||||
raise ValueError("Wybrana karta nie należy do firmy pojazdu")
|
||||
entry.vehicle = vehicle
|
||||
entry.fueled_at = fueled_at
|
||||
entry.liters = liters
|
||||
entry.price_per_liter = price
|
||||
entry.fuel_type = fuel_type
|
||||
entry.odometer = odometer
|
||||
entry.station_company = station_company
|
||||
entry.station = station_company.company_name if station_company else (request.form.get("station") or "").strip() or None
|
||||
entry.invoice_number = (request.form.get("invoice_number") or "").strip() or None
|
||||
entry.used_fuel_card = "used_fuel_card" in request.form
|
||||
entry.fuel_card = fuel_card if entry.used_fuel_card else None
|
||||
wholesale_price = (request.form.get("wholesale_price") or "").strip()
|
||||
entry.wholesale_price = Decimal(wholesale_price) if wholesale_price else None
|
||||
entry.wholesale_source = (request.form.get("wholesale_source") or "").strip() or None
|
||||
return vehicle
|
||||
|
||||
def accessible_vehicles():
|
||||
if current_user.role == "admin":
|
||||
return Vehicle.query.order_by(Vehicle.name).all()
|
||||
@@ -374,6 +431,82 @@ def orlen_sync():
|
||||
except Exception as exc:
|
||||
db.session.rollback(); return jsonify({"ok": False, "error": str(exc)}), 502
|
||||
|
||||
|
||||
|
||||
@main.get("/admin/fuel-entries")
|
||||
@login_required
|
||||
@roles("boss", "admin")
|
||||
def admin_fuel_entries():
|
||||
q = request.args.get("q", "").strip()
|
||||
month = request.args.get("month", "").strip()
|
||||
company_id = request.args.get("company_id", type=int)
|
||||
page = request.args.get("page", 1, type=int)
|
||||
query = FuelEntry.query.join(Vehicle).join(User, Vehicle.owner_id == User.id)
|
||||
if current_user.role == "boss":
|
||||
query = query.filter(or_(Vehicle.company_id == current_user.company_id, and_(Vehicle.company_id.is_(None), User.company_id == current_user.company_id)))
|
||||
company_id = current_user.company_id
|
||||
elif company_id:
|
||||
query = query.filter(or_(Vehicle.company_id == company_id, and_(Vehicle.company_id.is_(None), User.company_id == company_id)))
|
||||
if month:
|
||||
try:
|
||||
year, mon = map(int, month.split("-"))
|
||||
query = query.filter(extract("year", FuelEntry.fueled_at) == year, extract("month", FuelEntry.fueled_at) == mon)
|
||||
except ValueError:
|
||||
month = ""
|
||||
if q:
|
||||
like = f"%{q}%"
|
||||
query = query.outerjoin(FuelStationCompany, FuelEntry.station_company_id == FuelStationCompany.id).filter(or_(
|
||||
Vehicle.name.ilike(like), Vehicle.registration.ilike(like), FuelEntry.station.ilike(like),
|
||||
FuelEntry.invoice_number.ilike(like), User.name.ilike(like), User.email.ilike(like),
|
||||
FuelStationCompany.company_name.ilike(like), FuelStationCompany.brand_name.ilike(like)
|
||||
))
|
||||
entries_page = query.order_by(FuelEntry.fueled_at.desc(), FuelEntry.id.desc()).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]
|
||||
vehicles_q = Vehicle.query.join(User, Vehicle.owner_id == User.id)
|
||||
cards_q = FuelCard.query.filter_by(active=True)
|
||||
if current_user.role == "boss":
|
||||
vehicles_q = vehicles_q.filter(or_(Vehicle.company_id == current_user.company_id, and_(Vehicle.company_id.is_(None), User.company_id == current_user.company_id)))
|
||||
cards_q = cards_q.filter_by(company_id=current_user.company_id)
|
||||
elif company_id:
|
||||
vehicles_q = vehicles_q.filter(or_(Vehicle.company_id == company_id, and_(Vehicle.company_id.is_(None), User.company_id == company_id)))
|
||||
cards_q = cards_q.filter_by(company_id=company_id)
|
||||
return render_template("admin_fuel_entries.html", entries_page=entries_page, q=q, month=month, company_id=company_id, companies=companies, vehicles=vehicles_q.order_by(Vehicle.name).all(), stations=FuelStationCompany.query.filter_by(active=True).order_by(FuelStationCompany.brand_name, FuelStationCompany.company_name).all(), fuel_cards=cards_q.order_by(FuelCard.name).all())
|
||||
|
||||
@main.put("/api/fuel-entries/<int:entry_id>")
|
||||
@login_required
|
||||
@roles("boss", "admin")
|
||||
def update_fuel_entry(entry_id):
|
||||
entry = FuelEntry.query.get_or_404(entry_id)
|
||||
if not fuel_entry_accessible(entry):
|
||||
abort(403)
|
||||
old_vehicle = entry.vehicle
|
||||
try:
|
||||
new_vehicle = parse_fuel_entry_form(entry)
|
||||
db.session.flush()
|
||||
recalculate_vehicle_odometer(old_vehicle)
|
||||
if new_vehicle.id != old_vehicle.id:
|
||||
recalculate_vehicle_odometer(new_vehicle)
|
||||
db.session.commit()
|
||||
except (ValueError, ArithmeticError) as exc:
|
||||
db.session.rollback()
|
||||
return jsonify({"ok": False, "error": str(exc)}), 400
|
||||
return jsonify({"ok": True, "message": "Zaktualizowano tankowanie"})
|
||||
|
||||
@main.delete("/api/fuel-entries/<int:entry_id>")
|
||||
@login_required
|
||||
@roles("boss", "admin")
|
||||
def delete_fuel_entry(entry_id):
|
||||
entry = FuelEntry.query.get_or_404(entry_id)
|
||||
if not fuel_entry_accessible(entry):
|
||||
abort(403)
|
||||
vehicle = entry.vehicle
|
||||
db.session.delete(entry)
|
||||
db.session.flush()
|
||||
recalculate_vehicle_odometer(vehicle)
|
||||
db.session.commit()
|
||||
return jsonify({"ok": True, "message": "Usunięto tankowanie"})
|
||||
|
||||
|
||||
@main.route("/admin", methods=["GET","POST"])
|
||||
@login_required
|
||||
@roles("boss", "admin")
|
||||
|
||||
Reference in New Issue
Block a user