39291-vm/core/views.py
Flatlogic Bot 46ee143ab1 1.1
2026-04-01 16:28:08 +00:00

147 lines
7.0 KiB
Python

from decimal import Decimal
from django.contrib import messages
from django.db.models import Sum
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.views.decorators.http import require_GET, require_http_methods, require_POST
from .forms import ReportFilterForm, TripFilterForm, TripForm
from .models import Trip
from .utils import apply_trip_filters, calculate_google_maps_distance, export_trips_csv, report_queryset, summarize_trips
DEFAULT_META_DESCRIPTION = "Mobile-friendly mileage logging with odometer history, Google Maps assist, and IRS-style reporting."
def latest_trip_with_odometer():
return Trip.objects.exclude(end_odometer__isnull=True).order_by("-date", "-end_time", "-created_at").first()
def home(request):
today = timezone.localdate()
current_month = Trip.objects.filter(date__year=today.year, date__month=today.month)
ytd = Trip.objects.filter(date__year=today.year)
recent_trips = Trip.objects.all()[:5]
month_business = current_month.filter(trip_type=Trip.TripType.BUSINESS).aggregate(total=Sum("distance_miles"))["total"] or Decimal("0.0")
ytd_business = ytd.filter(trip_type=Trip.TripType.BUSINESS).aggregate(total=Sum("distance_miles"))["total"] or Decimal("0.0")
business_trip_count = current_month.filter(trip_type=Trip.TripType.BUSINESS).count()
last_trip = Trip.objects.order_by("-date", "-end_time", "-created_at").first()
latest_odo = latest_trip_with_odometer()
context = {
"project_name": "MileLedger",
"meta_title": "MileLedger | IRS-friendly mileage tracking for mobile",
"meta_description": DEFAULT_META_DESCRIPTION,
"month_business": month_business,
"ytd_business": ytd_business,
"business_trip_count": business_trip_count,
"recent_trips": recent_trips,
"last_trip": last_trip,
"latest_end_odometer": latest_odo.end_odometer if latest_odo else None,
"report_month": today.month,
"report_year": today.year,
"empty_state": not Trip.objects.exists(),
}
return render(request, "core/index.html", context)
@require_http_methods(["GET", "POST"])
def trip_create(request):
latest_odo = latest_trip_with_odometer()
latest_end_odometer = latest_odo.end_odometer if latest_odo else None
if request.method == "POST":
form = TripForm(request.POST, latest_end_odometer=latest_end_odometer)
if form.is_valid():
trip = form.save()
messages.success(request, "Trip saved. Your mileage log is updated and ready for review.")
return redirect("trip_detail", pk=trip.pk)
else:
now = timezone.localtime(timezone.now())
form = TripForm(initial={"date": timezone.localdate(), "start_time": now.strftime("%H:%M"), "end_time": now.strftime("%H:%M"), "trip_type": Trip.TripType.BUSINESS}, latest_end_odometer=latest_end_odometer)
return render(request, "core/trip_form.html", {"form": form, "page_heading": "Add a mileage entry", "submit_label": "Save trip", "meta_title": "Log a trip | MileLedger", "meta_description": DEFAULT_META_DESCRIPTION, "latest_end_odometer": latest_end_odometer, "trip": None})
@require_http_methods(["GET", "POST"])
def trip_update(request, pk):
trip = get_object_or_404(Trip, pk=pk)
if request.method == "POST":
form = TripForm(request.POST, instance=trip)
if form.is_valid():
trip = form.save()
messages.success(request, "Trip updated. Your mileage history now reflects the latest details.")
return redirect("trip_detail", pk=trip.pk)
else:
form = TripForm(instance=trip)
return render(request, "core/trip_form.html", {"form": form, "page_heading": "Update trip details", "submit_label": "Save changes", "meta_title": "Edit trip | MileLedger", "meta_description": DEFAULT_META_DESCRIPTION, "trip": trip, "latest_end_odometer": None})
@require_GET
def trip_list(request):
form = TripFilterForm(request.GET or None)
trips = Trip.objects.all()
if form.is_valid():
trips = apply_trip_filters(trips, form.cleaned_data)
context = {"form": form, "trips": trips[:100], "summary": summarize_trips(trips), "meta_title": "Trip history | MileLedger", "meta_description": "Review, filter, and audit mileage entries."}
return render(request, "core/trip_list.html", context)
@require_GET
def trip_detail(request, pk):
trip = get_object_or_404(Trip, pk=pk)
return render(request, "core/trip_detail.html", {"trip": trip, "meta_title": f"Trip on {trip.date:%b %d, %Y} | MileLedger", "meta_description": "Trip detail and audit-ready timestamps."})
@require_http_methods(["GET", "POST"])
def trip_delete(request, pk):
trip = get_object_or_404(Trip, pk=pk)
if request.method == "POST":
trip.delete()
messages.success(request, "Trip deleted.")
return redirect("trip_list")
return render(request, "core/trip_detail.html", {"trip": trip, "confirm_delete": True, "meta_title": "Delete trip | MileLedger", "meta_description": "Confirm trip deletion."})
@require_GET
def report_view(request):
today = timezone.localdate()
initial = {"report_type": "month", "month": today.month, "year": today.year}
form = ReportFilterForm(request.GET or initial)
trips = Trip.objects.none()
summary = {"business_miles": Decimal("0.0"), "non_business_miles": Decimal("0.0"), "total_miles": Decimal("0.0"), "trip_count": 0}
if form.is_valid():
trips = report_queryset(form.cleaned_data)
summary = summarize_trips(trips)
return render(request, "core/report.html", {"form": form, "trips": trips, "summary": summary, "meta_title": "IRS-style mileage report | MileLedger", "meta_description": "Generate monthly, annual, or custom mileage reports and export them to CSV."})
@require_GET
def report_export_csv(request):
today = timezone.localdate()
initial = {"report_type": "month", "month": today.month, "year": today.year}
form = ReportFilterForm(request.GET or initial)
if not form.is_valid():
messages.error(request, "Choose a valid report filter before exporting.")
return redirect("report_view")
trips = report_queryset(form.cleaned_data)
response = HttpResponse(export_trips_csv(trips), content_type="text/csv")
response["Content-Disposition"] = 'attachment; filename="mileage-report.csv"'
return response
@require_POST
def distance_estimate(request):
start_location = (request.POST.get("start_location") or "").strip()
end_location = (request.POST.get("end_location") or "").strip()
if not start_location or not end_location:
return JsonResponse({"ok": False, "message": "Enter both a start and destination first."}, status=400)
result = calculate_google_maps_distance(start_location, end_location)
status = 200 if result.ok else 422
return JsonResponse({"ok": result.ok, "miles": float(result.miles) if result.miles is not None else None, "message": result.message}, status=status)