edit styles

This commit is contained in:
Marwan Alwali 2024-12-12 11:00:42 +03:00
parent 9e0824de6d
commit b227d9ff5b
23 changed files with 917 additions and 510 deletions

2
.gitignore vendored
View File

@ -5,7 +5,7 @@
__pycache__ __pycache__
db.sqlite3 db.sqlite3
media media
./car_inventorysettings.py
# Backup files # # Backup files #
*.bak *.bak

View File

@ -9,7 +9,7 @@ https://docs.djangoproject.com/en/5.0/topics/settings/
For the full list of settings and their values, see For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/ https://docs.djangoproject.com/en/5.0/ref/settings/
""" """
from decimal import Decimal
from pathlib import Path from pathlib import Path
import os import os
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -26,7 +26,7 @@ SECRET_KEY = 'django-insecure-gc9bh4*3=b6hihdnaom0edjsbxh$5t)aap@e8p&340r7)*)qb8
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
ALLOWED_HOSTS = ['10.10.1.109',"10.10.1.120", 'localhost', '127.0.0.1', '192.168.1.135', '172.20.10.4'] ALLOWED_HOSTS = ['10.10.1.109', 'localhost', '127.0.0.1', '192.168.1.135', '172.20.10.4']
# Application definition # Application definition
@ -110,9 +110,9 @@ WSGI_APPLICATION = 'car_inventory.wsgi.application'
DATABASES = { DATABASES = {
"default": { "default": {
"ENGINE": "django_prometheus.db.backends.postgresql", "ENGINE": "django_prometheus.db.backends.postgresql",
"NAME": "haikal", "NAME": "haikal_app",
"USER": "haikal", "USER": "f95166",
"PASSWORD": "haikal", "PASSWORD": "Kfsh&rc9788",
"HOST": "localhost", "HOST": "localhost",
"PORT": 5432, "PORT": 5432,
} }
@ -255,3 +255,7 @@ LOGGING = {
}, },
}, },
} }
# Global Settings
CURRENCY = _('SAR')
VAT_RATE = Decimal('0.15')

View File

@ -15,6 +15,7 @@ admin.site.register(models.CustomCard)
admin.site.register(models.CarSpecificationValue) admin.site.register(models.CarSpecificationValue)
admin.site.register(models.ExteriorColors) admin.site.register(models.ExteriorColors)
admin.site.register(models.InteriorColors) admin.site.register(models.InteriorColors)
admin.site.register(models.CarLocation)
@admin.register(models.CarMake) @admin.register(models.CarMake)
class CarMakeAdmin(admin.ModelAdmin): class CarMakeAdmin(admin.ModelAdmin):

View File

@ -4,3 +4,6 @@ from django.apps import AppConfig
class InventoryConfig(AppConfig): class InventoryConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'inventory' name = 'inventory'
def ready(self):
import inventory.signals

View File

@ -13,7 +13,8 @@ from .models import (
CarColors, CarColors,
ExteriorColors, ExteriorColors,
InteriorColors, InteriorColors,
SaleQuotation SaleQuotation,
CarLocation
) )
from django.forms import ModelMultipleChoiceField from django.forms import ModelMultipleChoiceField
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -97,6 +98,15 @@ class CarFinanceForm(AddClassMixin, forms.ModelForm):
exclude = ['car', 'profit_margin', 'vat_amount', 'total', 'vat_rate'] exclude = ['car', 'profit_margin', 'vat_amount', 'total', 'vat_rate']
class CarLocationForm(forms.ModelForm):
class Meta:
model = CarLocation
fields = ['showroom', 'description']
widgets = {
'description': forms.Textarea(attrs={'rows': 2, 'class': 'form-control'}),
}
# Custom Card Form # Custom Card Form
class CustomCardForm(forms.ModelForm): class CustomCardForm(forms.ModelForm):
custom_date = forms.DateTimeField( custom_date = forms.DateTimeField(

View File

@ -1,4 +1,5 @@
from uuid import uuid4 from uuid import uuid4
from django.conf import settings
from django.db import models, transaction from django.db import models, transaction
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.models.signals import pre_save, post_save from django.db.models.signals import pre_save, post_save
@ -21,6 +22,7 @@ from django.core.exceptions import ValidationError
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.timezone import now from django.utils.timezone import now
from .mixins import LocalizedNameMixin from .mixins import LocalizedNameMixin
@ -222,10 +224,10 @@ class Car(models.Model):
class CarReservation(models.Model): class CarReservation(models.Model):
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations') car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Car"))
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE) reserved_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Reserved By"))
reserved_at = models.DateTimeField(auto_now_add=True) reserved_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Reserved At"))
reserved_until = models.DateTimeField() reserved_until = models.DateTimeField(verbose_name=_("Reserved Until"))
def is_active(self): def is_active(self):
return self.reserved_until > now() return self.reserved_until > now()
@ -233,11 +235,13 @@ class CarReservation(models.Model):
class Meta: class Meta:
unique_together = ('car', 'reserved_until') unique_together = ('car', 'reserved_until')
ordering = ['-reserved_at'] ordering = ['-reserved_at']
verbose_name = _("Car Reservation")
verbose_name_plural = _("Car Reservations")
# Car Finance Model # Car Finance Model
class CarFinance(models.Model): class CarFinance(models.Model):
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='finances') car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='finances')
cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price")) cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price"))
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price")) selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"))
profit_margin = models.DecimalField(max_digits=14, profit_margin = models.DecimalField(max_digits=14,
@ -258,35 +262,35 @@ class CarFinance(models.Model):
default=Decimal('0.00')) default=Decimal('0.00'))
custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"), custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"),
default=Decimal('0.00')) default=Decimal('0.00'))
vat_rate = models.DecimalField(max_digits=14, decimal_places=2, default=Decimal('0.15'), verbose_name=_("VAT Rate"),)
total = models.DecimalField(max_digits=14, decimal_places=2, default=Decimal('0.00'), null=True, blank=True) total = models.DecimalField(max_digits=14, decimal_places=2, default=Decimal('0.00'), null=True, blank=True)
def __str__(self): def __str__(self):
return f"{self.selling_price}" return f"Car: {self.car}, Selling Price: {self.selling_price}"
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
vat_rate = settings.VAT_RATE
self.full_clean() self.full_clean()
try: try:
services = self.administration_fee + self.transportation_fee + self.custom_card_fee services = self.administration_fee + self.transportation_fee + self.custom_card_fee
price_after_discount = self.selling_price - self.discount_amount price_after_discount = self.selling_price - self.discount_amount
total_vat_amount = (price_after_discount + services) * self.vat_rate total_vat_amount = (price_after_discount + services) * vat_rate
self.vat_amount = self.selling_price * self.vat_rate self.vat_amount = price_after_discount * vat_rate
self.profit_margin = self.selling_price - self.cost_price - self.discount_amount - self.registration_fee self.profit_margin = self.selling_price - self.cost_price - self.discount_amount - self.registration_fee
self.total = price_after_discount + services + total_vat_amount + self.registration_fee self.total = price_after_discount + services + total_vat_amount + self.registration_fee
except InvalidOperation as e: except InvalidOperation as e:
raise ValidationError(_("Invalid decimal operation: %s") % str(e)) raise ValidationError(_("Invalid decimal operation: %s") % str(e))
super().save(*args, **kwargs) super().save(*args, **kwargs)
class Meta:
verbose_name = _("Car Financial Details")
@property @property
def total_vat_amount(self): def total_vat_amount(self):
vat_rate = settings.VAT_RATE
services = self.administration_fee + self.transportation_fee + self.custom_card_fee services = self.administration_fee + self.transportation_fee + self.custom_card_fee
price_after_discount = self.selling_price - self.discount_amount price_after_discount = self.selling_price - self.discount_amount
return (price_after_discount + services) * self.vat_rate return (price_after_discount + services) * vat_rate
class Meta:
verbose_name = _("Car Financial Details")
verbose_name_plural = _("Car Financial Details")
class ExteriorColors(models.Model, LocalizedNameMixin): class ExteriorColors(models.Model, LocalizedNameMixin):
@ -332,7 +336,7 @@ class CarColors(models.Model):
# Custom Card Model # Custom Card Model
class CustomCard(models.Model): class CustomCard(models.Model):
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='custom_cards', verbose_name=_("Car")) car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='custom_cards', verbose_name=_("Car"))
custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number")) custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number"))
custom_date = models.DateField(verbose_name=_("Custom Date")) custom_date = models.DateField(verbose_name=_("Custom Date"))
@ -344,6 +348,55 @@ class CustomCard(models.Model):
return f"{self.car} - {self.custom_number}" return f"{self.car} - {self.custom_number}"
class CarLocation(models.Model):
car = models.OneToOneField(
Car,
on_delete=models.CASCADE,
related_name='location',
verbose_name=_("Car")
)
owner = models.ForeignKey(
'Dealer',
on_delete=models.CASCADE,
related_name='owned_cars',
verbose_name=_("Owner"),
help_text=_("Dealer who owns the car.")
)
showroom = models.ForeignKey(
'Dealer',
on_delete=models.CASCADE,
related_name='showroom_cars',
verbose_name=_("Showroom"),
help_text=_("Dealer where the car is displayed (can be the owner).")
)
description = models.TextField(
blank=True,
null=True,
verbose_name=_("Description"),
help_text=_("Optional description about the showroom placement.")
)
created_at = models.DateTimeField(
auto_now_add=True,
verbose_name=_("Created At")
)
updated_at = models.DateTimeField(
auto_now=True,
verbose_name=_("Last Updated")
)
class Meta:
verbose_name = _("Car Location")
verbose_name_plural = _("Car Locations")
def __str__(self):
return f"Car: {self.car}, Showroom: {self.showroom}, Owner: {self.owner}"
def is_owner_showroom(self):
"""
Returns True if the showroom is the same as the owner.
"""
return self.owner == self.showroom
# Car Registration Model # Car Registration Model
class CarRegistration(models.Model): class CarRegistration(models.Model):
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='registrations', verbose_name=_("Car")) car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='registrations', verbose_name=_("Car"))

View File

@ -2,11 +2,27 @@ from random import randint
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from django_ledger.models import EntityModel, VendorModel, CustomerModel, UnitOfMeasureModel from django_ledger.models import EntityModel, VendorModel, CustomerModel, UnitOfMeasureModel, AccountModel, \
ItemModelAbstract
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from . import models from . import models
@receiver(post_save, sender=models.Car)
def create_car_location(sender, instance, created, **kwargs):
"""
Signal to create or update the car's location when a car instance is saved.
"""
if created:
models.CarLocation.objects.create(
car=instance,
owner=instance.dealer,
showroom=instance.dealer,
description=f"Initial location set for car {instance.vin}."
)
print("Car Location created")
@receiver(post_save, sender=models.CarReservation) @receiver(post_save, sender=models.CarReservation)
def update_car_status_on_reservation(sender, instance, created, **kwargs): def update_car_status_on_reservation(sender, instance, created, **kwargs):
if created: if created:
@ -24,21 +40,21 @@ def update_car_status_on_reservation_delete(sender, instance, **kwargs):
# Create Entity # Create Entity
@receiver(post_save, sender=models.Dealer) # @receiver(post_save, sender=models.Dealer)
def create_ledger_entity(sender, instance, created, **kwargs): # def create_ledger_entity(sender, instance, created, **kwargs):
if created: # if created:
entity = EntityModel.objects.create( # entity = EntityModel.objects.create(
name=instance.name, # name=instance.name,
admin=instance.user, # admin=instance.user,
address_1=instance.address, # address_1=instance.address,
fy_start_month=1, # fy_start_month=1,
accrual_method=True, # accrual_method=True,
depth=0, # depth=0,
) # )
#
default_coa = entity.create_chart_of_accounts(assign_as_default=True, # default_coa = entity.create_chart_of_accounts(assign_as_default=True,
commit=True, # commit=True,
coa_name=_("Chart of Accounts")) # coa_name=_("Chart of Accounts"))
# entity.create_account( # entity.create_account(
# coa_model=coa, # coa_model=coa,
# code=1010, # code=1010,
@ -48,25 +64,56 @@ def create_ledger_entity(sender, instance, created, **kwargs):
# ) # )
# entity.create_account( # entity.create_account(
# coa_model=coa, # coa_model=coa,
# code=1100,
# role='asset_ca_recv',
# name=_('Accounts Receivable'),
# balance_type="debit",
# )
# entity.create_account(
# coa_model=coa,
# code=1200, # code=1200,
# role='asset_ca_inv', # role='asset_ca_inv',
# name=_('Inventory'), # name=_('Inventory'),
# balance_type="debit", # balance_type="debit",
# active=True) # active=True)
#
# entity.create_account(
# coa_model=coa,
# code=2010,
# role='lia_cl_acc_payable',
# name=_('Accounts Payable'),
# balance_type="credit",
# active=True)
#
# entity.create_account(
# coa_model=coa,
# code=4010,
# role='in_operational',
# name=_('Sales Income'),
# balance_type="credit",
# active=True)
#
# entity.create_account(
# coa_model=coa,
# code=5010,
# role='cogs_regular',
# name=_('Cost of Goods Sold'),
# balance_type="debit",
# active=True)
if default_coa: # if default_coa:
entity.populate_default_coa(activate_accounts=True, coa_model=default_coa) # entity.populate_default_coa(activate_accounts=True, coa_model=default_coa)
#
uom_name = _("Unit") # uom_name = _("Unit")
unit_abbr = _("U") # unit_abbr = _("U")
#
entity.create_uom(uom_name, unit_abbr) # entity.create_uom(uom_name, unit_abbr)
#
print(f"Ledger entity created for Dealer: {instance.name}") # print(f"Ledger entity created for Dealer: {instance.name}")
# # Create Vendor # Create Vendor
@receiver(post_save, sender=models.Vendor) @receiver(post_save, sender=models.Vendor)
def create_ledger_vendor(sender, instance, created, **kwargs): def create_ledger_vendor(sender, instance, created, **kwargs):
@ -113,55 +160,64 @@ def create_customer(sender, instance, created, **kwargs):
# Create Item # Create Item
@receiver(post_save, sender=models.Car) # @receiver(post_save, sender=models.Car)
def create_item_model(sender, instance, created, **kwargs): # def create_item_model(sender, instance, created, **kwargs):
item_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model} - {instance.id_car_trim}" # item_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model} - {instance.id_car_trim}"
uom_name = _("Car") # uom_name = _("Car")
unit_abbr = _("C") # unit_abbr = _("C")
#
uom, uom_created = UnitOfMeasureModel.objects.get_or_create( # uom, uom_created = UnitOfMeasureModel.objects.get_or_create(
name=uom_name, # name=uom_name,
unit_abbr=unit_abbr # unit_abbr=unit_abbr
) # )
#
if uom_created: # if uom_created:
print(f"UOM created: {uom_name}") # print(f"UOM created: {uom_name}")
else: # else:
print(f"Using existing UOM: {uom_name}") # print(f"Using existing UOM: {uom_name}")
#
entity = EntityModel.objects.filter(name=instance.dealer.name).first() # entity = EntityModel.objects.filter(name=instance.dealer.name).first()
#
inventory_account = AccountModel.objects.first() # inventory_account = AccountModel.objects.first()
cogs_account = AccountModel.objects.first() # cogs_account = AccountModel.objects.first()
earnings_account = AccountModel.objects.first() # earnings_account = AccountModel.objects.first()
#
entity.create_i # entity.create_item_product(
# item_name=item_name,
item = ItemModel.objects.create( # item_role=ItemModelAbstract.ITEM_ROLE_PRODUCT,
entity=entity, # item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL,
uom=uom, # item_id=instance.vin,
name=item_name, # sold_as_unit=True,
item_role=ItemModelAbstract.ITEM_ROLE_INVENTORY, # inventory_received=1.00,
item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL, # inventory_received_value=0.00,
item_id=instance.vin, # inventory_account=inventory_account,
sold_as_unit=True, # for_inventory=True,)
inventory_received=1.00, #
inventory_received_value=0.00, # item = ItemModel.objects.create(
inventory_account=inventory_account, # entity=entity,
for_inventory=True, # uom=uom,
is_product_or_service=True, # name=item_name,
cogs_account=cogs_account, # item_role=ItemModelAbstract.ITEM_ROLE_INVENTORY,
earnings_account=earnings_account, # item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL,
is_active=True, # item_id=instance.vin,
additional_info={ # sold_as_unit=True,
"remarks": instance.remarks, # inventory_received=1.00,
"status": instance.status, # inventory_received_value=0.00,
"stock_type": instance.stock_type, # inventory_account=inventory_account,
"mileage": instance.mileage, # for_inventory=True,
}, # is_product_or_service=True,
) # cogs_account=cogs_account,
# earnings_account=earnings_account,
print(f"ItemModel {'created' if created else 'updated'} for Car: {item.name}") # is_active=True,
# additional_info={
# "remarks": instance.remarks,
# "status": instance.status,
# "stock_type": instance.stock_type,
# "mileage": instance.mileage,
# },
# )
#
# print(f"ItemModel {'created' if created else 'updated'} for Car: {item.name}")
# #
# #
# # update price - CarFinance # # update price - CarFinance

View File

@ -56,6 +56,8 @@ urlpatterns = [
path('cars/add/', views.CarCreateView.as_view(), name='car_add'), path('cars/add/', views.CarCreateView.as_view(), name='car_add'),
path('ajax/', views.AjaxHandlerView.as_view(), name='ajax_handler'), path('ajax/', views.AjaxHandlerView.as_view(), name='ajax_handler'),
path('cars/<int:car_pk>/add-color/', views.CarColorCreate.as_view(), name='add_color'), path('cars/<int:car_pk>/add-color/', views.CarColorCreate.as_view(), name='add_color'),
path('car/<int:car_pk>/location/add/', views.CarLocationCreateView.as_view(), name='add_car_location'),
path('car/<int:pk>/location/update/', views.CarLocationUpdateView.as_view(), name='transfer'),
# path('cars/<int:car_pk>/colors/<int:pk>/update/',views.CarColorUpdateView.as_view(),name='color_update'), # path('cars/<int:car_pk>/colors/<int:pk>/update/',views.CarColorUpdateView.as_view(),name='color_update'),
path('cars/reserve/<int:car_id>/', views.reserve_car_view, name='reserve_car'), path('cars/reserve/<int:car_id>/', views.reserve_car_view, name='reserve_car'),

View File

@ -167,7 +167,9 @@ class AjaxHandlerView(LoginRequiredMixin, View):
def get_models(self, request): def get_models(self, request):
make_id = request.GET.get('make_id') 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') car_models = (models.CarModel.objects.filter(id_car_make=make_id)
.values('id_car_model', 'name', 'arabic_name')
.order_by('name'))
return JsonResponse(list(car_models), safe=False) return JsonResponse(list(car_models), safe=False)
def get_series(self, request): def get_series(self, request):
@ -405,6 +407,31 @@ class CarDeleteView(LoginRequiredMixin, DeleteView):
return super().delete(request, *args, **kwargs) return super().delete(request, *args, **kwargs)
class CarLocationCreateView(CreateView):
model = models.CarLocation
form_class = forms.CarLocationForm
template_name = 'inventory/car_location_form.html'
def get_success_url(self):
return reverse_lazy('car_detail', kwargs={'pk': self.object.car.pk})
def form_valid(self, form):
form.instance.car = get_object_or_404(models.Car, pk=self.kwargs['car_pk'])
form.instance.owner = self.request.user.dealer
form.save()
messages.success(self.request, 'Car saved successfully.')
return super().form_valid(form)
class CarLocationUpdateView(UpdateView):
model = models.CarLocation
form_class = forms.CarLocationForm
template_name = 'inventory/car_location_form.html'
def get_success_url(self):
return reverse_lazy('car_detail', kwargs={'pk': self.object.car.pk})
class CustomCardCreateView(LoginRequiredMixin, CreateView): class CustomCardCreateView(LoginRequiredMixin, CreateView):
model = models.CustomCard model = models.CustomCard
form_class = forms.CustomCardForm form_class = forms.CustomCardForm

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -149,6 +149,27 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
<tr>
<th>{% trans 'Showroom Location' %}</th>
<td>
{% if car.location %}
{% if car.location.is_owner_showroom %}
{% trans 'Our Showroom' %}
{% else %}
{{ car.location.showroom.get_local_name }}
{% endif %}
<a href="{% url 'transfer' car.location.pk %}" class="btn btn-danger btn-sm">{% trans "transfer" %}</a>
{% else %}
{% trans "No location available." %}
<a href="{% url 'add_car_location' car.pk %}"
class="btn btn-success btn-sm mb-3 ms-2">
{% trans "Add" %}
</a>
</td>
{% endif %}
</tr>
</table> </table>
</div> </div>
<div class="col-lg-6 col-xl-6"> <div class="col-lg-6 col-xl-6">
@ -304,7 +325,7 @@
<div class="row g-4"> <div class="row g-4">
<div class=""> <div class="">
<!-- Actions --> <!-- Actions -->
<a href="#" class="btn btn-danger btn-sm">{% trans "transfer" %}</a>
<a href="{% url 'car_update' car.pk %}" class="btn btn-warning btn-sm">{% trans "Edit" %}</a> <a href="{% url 'car_update' car.pk %}" class="btn btn-warning btn-sm">{% trans "Edit" %}</a>

View File

@ -1,12 +1,13 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n %} {% load i18n %}
{% load static %}
{% block title %} {% block title %}
{% trans 'inventory'|capfirst %} {% trans 'inventory'|capfirst %}
{% endblock %} {% endblock %}
{% block inventory %}<a class="nav-link active fw-bold">{% trans "inventory" %}<span class="visually-hidden">(current)</span></a>{% endblock %} {% block inventory %}
<a class="nav-link active fw-bold">{% trans "inventory" %}<span class="visually-hidden">(current)</span></a>
{% endblock %}
{% block content %} {% block content %}
<style> <style>
.color-div { .color-div {
width: 22px; width: 22px;
height: 22px; height: 22px;
@ -15,115 +16,136 @@
margin: auto; margin: auto;
text-align: center; text-align: center;
} }
</style> </style>
<div class="d-flex flex-column min-vh-100"> <div class="d-flex flex-column min-vh-100">
<div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-1"> <div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-1">
<main class="d-grid gap-4 p-1"> <main class="d-grid gap-4 p-1">
<div class="row g-4"> <div class="row g-4">
<div class="col-lg-6 col-xl-12"> <div class="col-lg-6 col-xl-12">
<div class="container-fluid p-2">
<form method="get" class="mb-3">
<div class="input-group input-group-sm">
<button id="inputGroup-sizing-sm"
class="btn btn-sm btn-secondary rounded-start" type="submit">
{% trans 'search'|capfirst %}
</button>
<input type="text"
name="q"
class="form-control form-control-sm rounded-end"
value="{{ request.GET.q }}"
aria-describedby="inputGroup-sizing-sm" />
<!-- Clear Button -->
{% if request.GET.q %}
<a href="{% url 'inventory_list' %}"
class="btn btn-sm btn-outline-danger ms-1 rounded">
<i class="bi bi-x-lg"></i>
</a>
{% endif %}
</div>
</form>
</div>
<table class="table table-responsive table-sm align-middle">
<thead>
<tr>
<th>{% trans "VIN" %}</th>
<th>{% trans "Year" %}</th>
<th>{% trans 'Exterior Color' %}</th>
<th>{% trans 'Interior Color' %}</th>
<th>{% trans "Showroom Location" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for car in cars %}
<tr class="{% if car.is_reserved %}table-danger{% endif %}">
<td>{{ car.vin }}</td>
<td>{{ car.year }}</td>
{% if car.colors.exists %}
<td><span>{{ car.colors.first.exterior.get_local_name }}<div class="color-div" style="background-color: rgb({{ car.colors.first.exterior.rgb }});"></div></span></td>
<td>{{ car.colors.first.interior.get_local_name }}<div class="color-div" style="background-color: rgb({{ car.colors.first.interior.rgb }});"></div></td>
{% else %}
<td><div class="color-div"><i class="bi bi-x-lg fs-6"></i></div></td>
<td><div class="color-div"><i class="bi bi-x-lg fs-6"></i></div></td>
{% endif %}
{% if car.location.is_owner_showroom %}
<td> {% trans 'Our Showroom' %}</td>
{% else %}
<td>{{ car.location.showroom.get_local_name }}</td>
{% endif %}
<td>
<a href="{% url 'car_detail' car.pk %}" class="btn btn-sm btn-success">
<small>{% trans "view" %}</small>
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6">{% trans "No cars available." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="container-fluid p-2"> <!-- Pagination -->
<form method="get"> {% if is_paginated %}
<div class="input-group input-group-sm"> <nav aria-label="Page navigation">
<button id="inputGroup-sizing-sm" <ul class="pagination pagination-sm justify-content-center">
class="btn btn-sm btn-secondary rounded-start" type="submit"> {% if page_obj.has_previous %}
{% trans 'search'|capfirst %} <li class="page-item">
</button> <a class="page-link" href="?page=1" aria-label="First">
<input type="text" &laquo;&laquo;
name="q" </a>
class="form-control form-control-sm rounded-end" </li>
value="{{ request.GET.q }}" <li class="page-item">
aria-describedby="inputGroup-sizing-sm"/> <a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
<!-- Clear Button --> &laquo;
{% if request.GET.q %} </a>
<a href="{{request.META.HTTP_REFERER}}" </li>
class="btn btn-sm btn-outline-danger ms-1 rounded"> {% else %}
<i class="bi bi-x-lg"></i> <li class="page-item disabled">
</a> <span class="page-link">&laquo;&laquo;</span>
{% endif %} </li>
<li class="page-item disabled">
<span class="page-link">&laquo;</span>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
&raquo;
</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}" aria-label="Last">
&raquo;&raquo;
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">&raquo;</span>
</li>
<li class="page-item disabled">
<span class="page-link">&raquo;&raquo;</span>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div> </div>
</form> </div>
</div> </main>
<table class="table table-responsive table-sm align-middle">
<thead>
<tr>
<th></th>
<th>{% trans "VIN" %}</th>
<th>{% trans "Year" %}</th>
<th>{% trans "Make" %}</th>
<th>{% trans "Model" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for car in cars %}
<tr class="{% if car.is_reserved %}table-danger{% endif %}">
{% if car.colors.all %}
{% for color in car.colors.all %}
<td><div class="color-div" style="background-color: rgb({{ color.rgb }});"></div></td>
{% endfor %}
{% else %}
<td><div class="color-div"><i class="bi bi-x-lg fs-6"></i></div></td>
{% endif %}
<td>{{ car.vin }}</td>
<td>{{ car.year }}</td>
<td>{{ car.id_car_make.get_local_name }}</td>
<td>{{ car.id_car_model.get_local_name }}</td>
<td>
<a href="{% url 'car_detail' car.pk %}" class="btn btn-sm btn-success"><small>{% trans "view" %}</small></a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7">{% trans "No cars available." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- Optional: Pagination -->
{% if is_paginated %}
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item py-0">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %} {% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
</div>
</main>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,10 +1,22 @@
<!DOCTYPE html> {% extends 'base.html' %}
<html lang="en"> {% load i18n %}
<head> {% load crispy_forms_filters %}
<meta charset="UTF-8"> {% block title %}{% trans "Manage Car Location" %}{% endblock %}
<title>$Title$</title> {% block content %}
</head> {% if carlocation.exists %}
<body> <p>Transfer</p>
$END$ {% else %}
</body> <p>Add</p>
</html> {% endif %}
<div class="container mt-3">
<h3 class="mb-3">{% trans "Manage Car Location" %}</h3>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<div class="form-group">
<button type="submit" class="btn btn-primary">{% trans "Save" %}</button>
</div>
</form>
</div>
{% endblock %}