850 lines
29 KiB
Python
850 lines
29 KiB
Python
import logging
|
|
import json
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from vin import VIN
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.http import JsonResponse
|
|
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.db.models import Q
|
|
from django.views.generic import (
|
|
View,
|
|
ListView,
|
|
DetailView,
|
|
CreateView,
|
|
UpdateView,
|
|
DeleteView,
|
|
TemplateView,
|
|
)
|
|
from django.utils import timezone, translation
|
|
from django.conf import settings
|
|
from urllib.parse import urlparse, urlunparse
|
|
from django.forms import ChoiceField, ModelForm, RadioSelect
|
|
from django.urls import reverse, reverse_lazy
|
|
from django.contrib import messages
|
|
from django.db.models import Sum, F, Count
|
|
from .services import elm, fetch_colors, translate, decode_vin_pyvin, normalize_name
|
|
from . import models, tables, forms
|
|
from django_tables2.export.views import ExportMixin
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
|
|
def switch_language(request):
|
|
language = request.GET.get("language", "en")
|
|
referer = request.META.get("HTTP_REFERER", "/")
|
|
parsed_url = urlparse(referer)
|
|
path_parts = parsed_url.path.split("/")
|
|
|
|
if path_parts[1] in dict(settings.LANGUAGES):
|
|
path_parts.pop(1)
|
|
|
|
new_path = "/".join(path_parts)
|
|
new_url = urlunparse(
|
|
(
|
|
parsed_url.scheme,
|
|
parsed_url.netloc,
|
|
new_path,
|
|
parsed_url.params,
|
|
parsed_url.query,
|
|
parsed_url.fragment,
|
|
)
|
|
)
|
|
|
|
if language in dict(settings.LANGUAGES):
|
|
logger.debug(f"Switching language to: {language}")
|
|
response = redirect(new_url)
|
|
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language)
|
|
translation.activate(language)
|
|
request.session[settings.LANGUAGE_COOKIE_NAME] = language
|
|
logger.debug(
|
|
f"Language switched to: {language}, Session: {request.session[settings.LANGUAGE_COOKIE_NAME]}"
|
|
)
|
|
return response
|
|
else:
|
|
logger.warning(f"Invalid language code: {language}")
|
|
return redirect("/")
|
|
|
|
|
|
class HomeView(LoginRequiredMixin, TemplateView):
|
|
template_name = "index.html"
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
if not hasattr(request.user, "dealer") or not request.user.is_authenticated:
|
|
messages.error(request, _("You are not associated with any dealer."))
|
|
return redirect("welcome")
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
total_cars = models.Car.objects.count()
|
|
total_reservations = models.CarReservation.objects.filter(
|
|
reserved_until__gte=timezone.now()
|
|
).count()
|
|
stats = models.CarFinance.objects.aggregate(
|
|
total_cost_price=Sum("cost_price"),
|
|
total_selling_price=Sum("selling_price"),
|
|
)
|
|
total_cost_price = stats["total_cost_price"] or 0
|
|
total_selling_price = stats["total_selling_price"] or 0
|
|
total_profit = total_selling_price - total_cost_price
|
|
|
|
context["total_cars"] = total_cars
|
|
context["total_reservations"] = total_reservations
|
|
context["total_cost_price"] = total_cost_price
|
|
context["total_selling_price"] = total_selling_price
|
|
context["total_profit"] = total_profit
|
|
return context
|
|
|
|
|
|
class WelcomeView(TemplateView):
|
|
template_name = "welcome.html"
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
if hasattr(request.user, "dealer") and request.user.is_authenticated:
|
|
return redirect("landing_page")
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
|
|
class CarCreateView(LoginRequiredMixin, CreateView):
|
|
model = models.Car
|
|
form_class = forms.CarForm
|
|
template_name = "inventory/car_form.html"
|
|
success_url = reverse_lazy("inventory_stats")
|
|
|
|
def form_valid(self, form):
|
|
form.instance.dealer = self.request.user.dealer
|
|
form.save()
|
|
messages.success(self.request, "Car saved successfully.")
|
|
return super().form_valid(form)
|
|
|
|
|
|
class AjaxHandlerView(LoginRequiredMixin, View):
|
|
def get(self, request, *args, **kwargs):
|
|
action = request.GET.get("action")
|
|
handlers = {
|
|
"decode_vin": self.decode_vin,
|
|
"get_models": self.get_models,
|
|
"get_series": self.get_series,
|
|
"get_trims": self.get_trims,
|
|
"get_specifications": self.get_specifications,
|
|
}
|
|
handler = handlers.get(action)
|
|
if handler:
|
|
return handler(request)
|
|
else:
|
|
return JsonResponse({"error": "Invalid action"}, status=400)
|
|
|
|
def decode_vin(self, request):
|
|
vin_no = request.GET.get("vin_no")
|
|
if not vin_no or len(vin_no.strip()) != 17:
|
|
return JsonResponse(
|
|
{"success": False, "error": "Invalid VIN number provided."}, status=400
|
|
)
|
|
vin_no = vin_no.strip()
|
|
vin_data = {}
|
|
decoding_method = ""
|
|
|
|
decoding_methods = [("PYVIN", decode_vin_pyvin), ("VIN", VIN), ("ELM", elm)]
|
|
|
|
manufacturer_name = model_name_before = model_name = year_model = None
|
|
|
|
for method_name, decode_function in decoding_methods:
|
|
try:
|
|
vin_info = decode_function(vin_no)
|
|
if vin_info:
|
|
if method_name == "PYVIN":
|
|
manufacturer_name = vin_info.Make.strip()
|
|
model_name_before = vin_info.Model.strip()
|
|
year_model = vin_info.ModelYear
|
|
if not manufacturer_name or not year_model:
|
|
raise ValueError("PYVIN returned incomplete data.")
|
|
elif method_name == "VIN":
|
|
manufacturer_name = vin_info.make.strip()
|
|
model_name_before = vin_info.model.strip()
|
|
year_model = vin_info.model_year
|
|
if (
|
|
not manufacturer_name
|
|
or not model_name_before
|
|
or not year_model
|
|
):
|
|
raise ValueError("VIN returned incomplete data.")
|
|
elif method_name == "ELM":
|
|
elm_data = vin_info.get("data", {})
|
|
manufacturer_name = elm_data.get("maker", "").strip()
|
|
model_name_before = elm_data.get("model", "").strip()
|
|
year_model = elm_data.get("modelYear", "").strip()
|
|
if (
|
|
not manufacturer_name
|
|
or not model_name_before
|
|
or not year_model
|
|
):
|
|
raise ValueError("ELM returned incomplete data.")
|
|
model_name = normalize_name(model_name_before)
|
|
decoding_method = method_name
|
|
print(f"decoded by {method_name}")
|
|
break
|
|
else:
|
|
logger.warning(f"{method_name} returned no data for {vin_no}.")
|
|
except Exception as e:
|
|
logger.warning(
|
|
f"VIN decoding with {method_name} failed for {vin_no}: {e}"
|
|
)
|
|
|
|
if not manufacturer_name or not model_name or not year_model:
|
|
return JsonResponse(
|
|
{"success": False, "error": "VIN not found in all sources."}, status=404
|
|
)
|
|
|
|
logger.info(
|
|
f"VIN decoded using {decoding_method}: Make={manufacturer_name}, Model={model_name}, Year={year_model}"
|
|
)
|
|
|
|
car_make = models.CarMake.objects.filter(
|
|
name__icontains=manufacturer_name
|
|
).first()
|
|
if not car_make:
|
|
return JsonResponse(
|
|
{"success": False, "error": "Manufacturer not found in the database."},
|
|
status=404,
|
|
)
|
|
vin_data["make_id"] = car_make.id_car_make
|
|
vin_data["name"] = car_make.name
|
|
vin_data["arabic_name"] = car_make.arabic_name
|
|
|
|
car_model = models.CarModel.objects.filter(
|
|
id_car_make=car_make.id_car_make, name__icontains=model_name
|
|
).first()
|
|
|
|
if not car_model:
|
|
return JsonResponse(
|
|
{
|
|
"success": False,
|
|
"error": "Model not found for the given manufacturer.",
|
|
},
|
|
status=404,
|
|
)
|
|
|
|
vin_data["model_id"] = car_model.id_car_model
|
|
vin_data["year"] = year_model
|
|
return JsonResponse({"success": True, "data": vin_data})
|
|
|
|
def get_models(self, request):
|
|
make_id = request.GET.get("make_id")
|
|
car_models = models.CarModel.objects.filter(id_car_make=make_id).values(
|
|
"id_car_model", "name", "arabic_name"
|
|
)
|
|
return JsonResponse(list(car_models), safe=False)
|
|
|
|
def get_series(self, request):
|
|
model_id = request.GET.get("model_id")
|
|
series = models.CarSerie.objects.filter(
|
|
id_car_model=model_id,
|
|
).values("id_car_serie", "name", "arabic_name")
|
|
return JsonResponse(list(series), safe=False)
|
|
|
|
def get_trims(self, request):
|
|
serie_id = request.GET.get("serie_id")
|
|
trims = models.CarTrim.objects.filter(id_car_serie=serie_id).values(
|
|
"id_car_trim", "name", "arabic_name"
|
|
)
|
|
return JsonResponse(list(trims), safe=False)
|
|
|
|
def get_specifications(self, request):
|
|
trim_id = request.GET.get("trim_id")
|
|
car_spec_values = models.CarSpecificationValue.objects.filter(
|
|
id_car_trim=trim_id
|
|
)
|
|
lang = translation.get_language()
|
|
specs_by_parent = {}
|
|
for value in car_spec_values:
|
|
specification = value.id_car_specification
|
|
parent = specification.id_parent
|
|
parent_id = parent.id_car_specification if parent else 0
|
|
if lang == "ar":
|
|
parent_name = parent.arabic_name if parent else "Root"
|
|
else:
|
|
parent_name = parent.name if parent else "Root"
|
|
if parent_id not in specs_by_parent:
|
|
specs_by_parent[parent_id] = {
|
|
"parent_name": parent_name,
|
|
"specifications": [],
|
|
}
|
|
spec_data = {
|
|
"specification_id": specification.id_car_specification,
|
|
"s_name": specification.arabic_name
|
|
if lang == "ar"
|
|
else specification.name,
|
|
"s_value": value.value,
|
|
"s_unit": value.unit if value.unit else "",
|
|
"trim_name": value.id_car_trim.name,
|
|
}
|
|
specs_by_parent[parent_id]["specifications"].append(spec_data)
|
|
serialized_specs = [
|
|
{"parent_name": v["parent_name"], "specifications": v["specifications"]}
|
|
for v in specs_by_parent.values()
|
|
]
|
|
return JsonResponse(serialized_specs, safe=False)
|
|
|
|
|
|
class CarInventory(LoginRequiredMixin, ListView):
|
|
model = models.Car
|
|
home_label = _("inventory")
|
|
template_name = "inventory/car_inventory.html"
|
|
context_object_name = "cars"
|
|
paginate_by = 10
|
|
ordering = ["receiving_date"]
|
|
|
|
def get_queryset(self, *args, **kwargs):
|
|
query = self.request.GET.get("q")
|
|
make_id = self.kwargs["make_id"]
|
|
model_id = self.kwargs["model_id"]
|
|
trim_id = self.kwargs["trim_id"]
|
|
cars = models.Car.objects.filter(
|
|
dealer__user=self.request.user,
|
|
id_car_make=make_id,
|
|
id_car_model=model_id,
|
|
id_car_trim=trim_id,
|
|
).order_by("receiving_date")
|
|
|
|
if query:
|
|
cars = cars.filter(Q(vin__icontains=query))
|
|
return cars
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["query"] = self.request.GET.get("q", "")
|
|
context["make_id"] = self.kwargs["make_id"]
|
|
context["model_id"] = self.kwargs["model_id"]
|
|
context["trim_id"] = self.kwargs["trim_id"]
|
|
return context
|
|
|
|
|
|
@login_required
|
|
def inventory_stats_view(request):
|
|
dealer = request.user.dealer
|
|
|
|
# Annotate total cars by make, model, and trim
|
|
cars = (
|
|
models.Car.objects.filter(dealer=dealer)
|
|
.select_related("id_car_make", "id_car_model", "id_car_trim")
|
|
.annotate(
|
|
make_total=Count("id_car_make"),
|
|
model_total=Count("id_car_model"),
|
|
trim_total=Count("id_car_trim"),
|
|
)
|
|
)
|
|
|
|
# Prepare the nested structure
|
|
inventory = {}
|
|
for car in cars:
|
|
# Make Level
|
|
make = car.id_car_make
|
|
|
|
if make.id_car_make not in inventory:
|
|
inventory[make.id_car_make] = {
|
|
"make_id": make.id_car_make,
|
|
"make_name": make.get_local_name(),
|
|
"total_cars": 0,
|
|
"models": {},
|
|
}
|
|
inventory[make.id_car_make]["total_cars"] += 1
|
|
|
|
# Model Level
|
|
model = car.id_car_model
|
|
if model and model.id_car_model not in inventory[make.id_car_make]["models"]:
|
|
inventory[make.id_car_make]["models"][model.id_car_model] = {
|
|
"model_id": model.id_car_model,
|
|
"model_name": model.get_local_name(),
|
|
"total_cars": 0,
|
|
"trims": {},
|
|
}
|
|
inventory[make.id_car_make]["models"][model.id_car_model]["total_cars"] += 1
|
|
|
|
# Trim Level
|
|
trim = car.id_car_trim
|
|
if (
|
|
trim
|
|
and trim.id_car_trim
|
|
not in inventory[make.id_car_make]["models"][model.id_car_model]["trims"]
|
|
):
|
|
inventory[make.id_car_make]["models"][model.id_car_model]["trims"][
|
|
trim.id_car_trim
|
|
] = {"trim_id": trim.id_car_trim, "trim_name": trim.name, "total_cars": 0}
|
|
inventory[make.id_car_make]["models"][model.id_car_model]["trims"][
|
|
trim.id_car_trim
|
|
]["total_cars"] += 1
|
|
|
|
# Convert to a list for easier template rendering
|
|
result = {
|
|
"total_cars": cars.count(),
|
|
"makes": [
|
|
{
|
|
"make_id": make_data["make_id"],
|
|
"make_name": make_data["make_name"],
|
|
"total_cars": make_data["total_cars"],
|
|
"models": [
|
|
{
|
|
"model_id": model_data["model_id"],
|
|
"model_name": model_data["model_name"],
|
|
"total_cars": model_data["total_cars"],
|
|
"trims": list(model_data["trims"].values()),
|
|
}
|
|
for model_data in make_data["models"].values()
|
|
],
|
|
}
|
|
for make_data in inventory.values()
|
|
],
|
|
}
|
|
|
|
return render(request, "inventory/inventory_stats.html", {"inventory": result})
|
|
|
|
|
|
class CarDetailView(LoginRequiredMixin, DetailView):
|
|
model = models.Car
|
|
template_name = "inventory/car_detail.html"
|
|
context_object_name = "car"
|
|
|
|
|
|
class CarFinanceCreateView(LoginRequiredMixin, CreateView):
|
|
model = models.CarFinance
|
|
form_class = forms.CarFinanceForm
|
|
template_name = "inventory/car_finance_form.html"
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def form_valid(self, form):
|
|
form.instance.car = self.car
|
|
messages.success(self.request, _("Car finance details saved successfully."))
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self):
|
|
return reverse("car_detail", kwargs={"pk": self.car.pk})
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["car"] = self.car
|
|
return context
|
|
|
|
|
|
class CarFinanceUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = models.CarFinance
|
|
form_class = forms.CarFinanceForm
|
|
template_name = "inventory/car_finance_form.html"
|
|
|
|
def form_valid(self, form):
|
|
messages.success(self.request, _("Car finance updated successfully."))
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self):
|
|
return reverse("car_detail", kwargs={"pk": self.object.car.pk})
|
|
|
|
|
|
class CarUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = models.Car
|
|
form_class = forms.CarUpdateForm
|
|
template_name = "inventory/car_edit.html"
|
|
|
|
def form_valid(self, form):
|
|
messages.success(self.request, _("Car updated successfully."))
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self):
|
|
return reverse("car_detail", kwargs={"pk": self.object.pk})
|
|
|
|
|
|
class CarDeleteView(LoginRequiredMixin, DeleteView):
|
|
model = models.Car
|
|
template_name = "inventory/car_confirm_delete.html"
|
|
success_url = reverse_lazy("inventory_stats")
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
messages.success(request, _("Car deleted successfully."))
|
|
return super().delete(request, *args, **kwargs)
|
|
|
|
|
|
class CustomCardCreateView(LoginRequiredMixin, CreateView):
|
|
model = models.CustomCard
|
|
form_class = forms.CustomCardForm
|
|
template_name = "inventory/add_custom_card.html"
|
|
|
|
def form_valid(self, form):
|
|
car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
|
form.instance.car = car
|
|
return super().form_valid(form)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["car"] = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
|
return context
|
|
|
|
def get_success_url(self):
|
|
messages.success(self.request, _("Custom Card added successfully."))
|
|
return reverse_lazy("car_detail", kwargs={"pk": self.kwargs["car_pk"]})
|
|
|
|
|
|
class CarColorCreateView(LoginRequiredMixin, CreateView):
|
|
model = models.CarColors
|
|
template_name = "inventory/color_palette.html"
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
|
self.available_colors = self.fetch_available_colors()
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_form_class(self):
|
|
class ColorPickerForm(ModelForm):
|
|
color = ChoiceField(
|
|
choices=self.get_color_choices(),
|
|
widget=RadioSelect(attrs={"class": "color-picker"}),
|
|
label=_("Select a Color"),
|
|
)
|
|
color_type = ChoiceField(
|
|
choices=models.CarColors.ColorType.choices,
|
|
widget=RadioSelect(attrs={"class": "color-type-picker"}),
|
|
label=_("Select Color Type"),
|
|
)
|
|
|
|
class Meta:
|
|
model = models.CarColors
|
|
fields = ["color", "color_type"]
|
|
|
|
return ColorPickerForm
|
|
|
|
def fetch_available_colors(self):
|
|
car_data = {
|
|
"make": self.car.id_car_make.name,
|
|
"model": self.car.id_car_model.name,
|
|
"year": str(self.car.year),
|
|
}
|
|
return fetch_colors(car_data) or []
|
|
|
|
def get_color_choices(self):
|
|
return [(color["rgb"], color["name"]) for color in self.available_colors]
|
|
|
|
def form_valid(self, form):
|
|
selected_rgb = form.cleaned_data["color"]
|
|
selected_name = next(
|
|
(
|
|
color["name"]
|
|
for color in self.available_colors
|
|
if color["rgb"] == selected_rgb
|
|
),
|
|
None,
|
|
)
|
|
|
|
if not selected_name:
|
|
messages.error(self.request, _("Invalid color selection."))
|
|
return self.form_invalid(form)
|
|
|
|
# Assign the car and selected color details
|
|
form.instance.car = self.car
|
|
form.instance.rgb = selected_rgb
|
|
form.instance.name = selected_name
|
|
form.instance.arabic_name = translate(selected_name)
|
|
form.instance.color_type = form.cleaned_data["color_type"]
|
|
|
|
messages.success(self.request, _("Color added successfully."))
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self):
|
|
return reverse("car_detail", kwargs={"pk": self.car.pk})
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["car"] = self.car
|
|
return context
|
|
|
|
|
|
class CarColorUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = forms.CarColors
|
|
template_name = "inventory/color_palette.html"
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
|
self.available_colors = self.fetch_available_colors()
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_form_class(self):
|
|
class ColorPickerForm(ModelForm):
|
|
color = ChoiceField(
|
|
choices=self.get_color_choices(),
|
|
widget=RadioSelect(attrs={"class": "color-picker"}),
|
|
label=_("Select a Color"),
|
|
)
|
|
|
|
class Meta:
|
|
model = forms.CarColors
|
|
fields = ["color"]
|
|
|
|
return ColorPickerForm
|
|
|
|
def fetch_available_colors(self):
|
|
car_data = {
|
|
"make": self.car.id_car_make.name,
|
|
"model": self.car.id_car_model.name,
|
|
"year": str(self.car.year),
|
|
}
|
|
return fetch_colors(car_data) or []
|
|
|
|
def get_color_choices(self):
|
|
return [(color["rgb"], color["name"]) for color in self.available_colors]
|
|
|
|
def form_valid(self, form):
|
|
selected_rgb = form.cleaned_data["color"]
|
|
selected_name = next(
|
|
(
|
|
color["name"]
|
|
for color in self.available_colors
|
|
if color["rgb"] == selected_rgb
|
|
),
|
|
None,
|
|
)
|
|
|
|
if not selected_name:
|
|
messages.error(self.request, _("Invalid color selection."))
|
|
return self.form_invalid(form)
|
|
|
|
form.instance.rgb = selected_rgb
|
|
form.instance.name = selected_name
|
|
form.instance.arabic_name = translate(selected_name)
|
|
|
|
messages.success(self.request, _("Exterior color updated successfully."))
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self):
|
|
return reverse("car_detail", kwargs={"pk": self.car.pk})
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["car"] = self.car
|
|
return context
|
|
|
|
|
|
@login_required()
|
|
def reserve_car_view(request, car_id):
|
|
if request.method == "POST":
|
|
car = get_object_or_404(models.Car, pk=car_id)
|
|
if car.is_reserved():
|
|
messages.error(request, _("This car is already reserved."))
|
|
return redirect("car_detail", pk=car.pk)
|
|
|
|
try:
|
|
reserved_until = timezone.now() + timezone.timedelta(hours=24)
|
|
models.CarReservation.objects.create(
|
|
car=car, reserved_by=request.user, reserved_until=reserved_until
|
|
)
|
|
messages.success(request, _("Car reserved successfully."))
|
|
except Exception as e:
|
|
messages.error(request, f"Error reserving car: {e}")
|
|
|
|
return redirect("car_detail", pk=car.pk)
|
|
return JsonResponse(
|
|
{"success": False, "message": "Invalid request method."}, status=400
|
|
)
|
|
|
|
|
|
@login_required
|
|
def manage_reservation(request, reservation_id):
|
|
reservation = get_object_or_404(
|
|
models.CarReservation, pk=reservation_id, reserved_by=request.user
|
|
)
|
|
|
|
if request.method == "POST":
|
|
action = request.POST.get("action")
|
|
if action == "renew":
|
|
reservation.reserved_until = timezone.now() + timezone.timedelta(hours=24)
|
|
reservation.save()
|
|
messages.success(request, _("Reservation renewed successfully."))
|
|
return redirect("car_detail", pk=reservation.car.pk)
|
|
|
|
elif action == "cancel":
|
|
reservation.delete()
|
|
messages.success(request, _("Reservation canceled successfully."))
|
|
return redirect("car_detail", pk=reservation.car.pk)
|
|
|
|
else:
|
|
return JsonResponse(
|
|
{"success": False, "message": _("Invalid action.")}, status=400
|
|
)
|
|
|
|
return JsonResponse(
|
|
{"success": False, "message": _("Invalid request method.")}, status=400
|
|
)
|
|
|
|
|
|
class DealerListView(LoginRequiredMixin, ListView):
|
|
model = models.Dealer
|
|
template_name = "dealer_list.html"
|
|
context_object_name = "dealers"
|
|
|
|
|
|
class DealerDetailView(LoginRequiredMixin, DetailView):
|
|
model = models.Dealer
|
|
template_name = "dealers/dealer_detail.html"
|
|
context_object_name = "dealer"
|
|
|
|
|
|
class DealerCreateView(LoginRequiredMixin, CreateView):
|
|
model = models.Dealer
|
|
form_class = forms.DealerForm
|
|
template_name = "dealer_form.html"
|
|
success_url = reverse_lazy("dealer_list")
|
|
|
|
def form_valid(self, form):
|
|
messages.success(self.request, _("Dealer created successfully."))
|
|
return super().form_valid(form)
|
|
|
|
|
|
class DealerUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = models.Dealer
|
|
form_class = forms.DealerForm
|
|
template_name = "dealers/dealer_form.html"
|
|
success_url = reverse_lazy("dealer_detail")
|
|
|
|
def form_valid(self, form):
|
|
messages.success(self.request, _("Dealer updated successfully."))
|
|
return super().form_valid(form)
|
|
|
|
|
|
class DealerDeleteView(LoginRequiredMixin, DeleteView):
|
|
model = models.Dealer
|
|
template_name = "dealer_confirm_delete.html"
|
|
success_url = reverse_lazy("dealer_list")
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
messages.success(request, _("Dealer deleted successfully."))
|
|
return super().delete(request, *args, **kwargs)
|
|
|
|
|
|
class CustomerListView(LoginRequiredMixin, ListView):
|
|
model = models.Customer
|
|
home_label = _("customers")
|
|
context_object_name = "customers"
|
|
paginate_by = 10
|
|
template_name = "customers/customer_list.html"
|
|
|
|
def get_queryset(self):
|
|
query = self.request.GET.get("q")
|
|
customers = models.Customer.objects.filter(dealer__user=self.request.user)
|
|
|
|
if query:
|
|
customers = customers.filter(
|
|
Q(national_id__icontains=query)
|
|
| Q(first_name__icontains=query)
|
|
| Q(last_name__icontains=query)
|
|
)
|
|
return customers
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["query"] = self.request.GET.get("q", "")
|
|
return context
|
|
|
|
|
|
class CustomerDetailView(LoginRequiredMixin, DetailView):
|
|
model = models.Customer
|
|
template_name = "customers/view_customer.html"
|
|
context_object_name = "customer"
|
|
|
|
|
|
class CustomerCreateView(LoginRequiredMixin, CreateView):
|
|
model = models.Customer
|
|
form_class = forms.CustomerForm
|
|
template_name = "customers/customer_form.html"
|
|
success_url = reverse_lazy("customer_list")
|
|
|
|
def form_valid(self, form):
|
|
if form.is_valid():
|
|
form.instance.dealer = self.request.user.dealer
|
|
form.save()
|
|
messages.success(self.request, _("Customer created successfully."))
|
|
return super().form_valid(form)
|
|
else:
|
|
return form.errors
|
|
|
|
|
|
class CustomerUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = models.Customer
|
|
form_class = forms.CustomerForm
|
|
template_name = "customers/customer_form.html"
|
|
success_url = reverse_lazy("customer_list")
|
|
|
|
def form_valid(self, form):
|
|
if form.is_valid():
|
|
form.instance.dealer = self.request.user.dealer
|
|
form.save()
|
|
messages.success(self.request, _("Customer updated successfully."))
|
|
return super().form_valid(form)
|
|
else:
|
|
return form.errors
|
|
|
|
|
|
@login_required
|
|
def delete_customer(request, pk):
|
|
customer = get_object_or_404(models.Customer, pk=pk)
|
|
customer.delete()
|
|
messages.success(request, _('Customer deleted successfully.'))
|
|
return redirect('customer_list')
|
|
|
|
|
|
class VendorListView(LoginRequiredMixin, ListView):
|
|
model = models.Vendor
|
|
context_object_name = 'vendors'
|
|
paginate_by = 10
|
|
template_name = "vendors/vendors_list.html"
|
|
|
|
|
|
class VendorDetailView(LoginRequiredMixin, DetailView):
|
|
model = models.Vendor
|
|
template_name = "vendors/view_vendor.html"
|
|
|
|
|
|
class VendorCreateView(LoginRequiredMixin, CreateView):
|
|
model = models.Vendor
|
|
form_class = forms.VendorForm
|
|
template_name = 'vendors/vendor_form.html'
|
|
success_url = reverse_lazy('vendor_list')
|
|
|
|
def form_valid(self, form):
|
|
if form.is_valid():
|
|
form.instance.dealer = self.request.user.dealer
|
|
form.save()
|
|
messages.success(self.request, _('Vendor created successfully.'))
|
|
return super().form_valid(form)
|
|
else:
|
|
return form.errors
|
|
|
|
|
|
class VendorUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = models.Vendor
|
|
form_class = forms.VendorForm
|
|
template_name = 'vendors/vendor_form.html'
|
|
success_url = reverse_lazy('vendor_list')
|
|
|
|
def form_valid(self, form):
|
|
if form.is_valid():
|
|
form.instance.dealer = self.request.user.dealer
|
|
form.save()
|
|
messages.success(self.request, _('Vendor updated successfully.'))
|
|
return super().form_valid(form)
|
|
else:
|
|
return form.errors
|
|
|
|
|
|
@login_required
|
|
def delete_vendor(request, pk):
|
|
vendor = get_object_or_404(models.Vendor, pk=pk)
|
|
vendor.delete()
|
|
messages.success(request, _('Vendor deleted successfully.'))
|
|
return redirect('vendor_list')
|
|
|
|
|
|
|