diff --git a/.DS_Store b/.DS_Store index 81c4ed63..8dcdf57e 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/db.sqlite b/db.sqlite index 7f709095..f0746d7c 100644 Binary files a/db.sqlite and b/db.sqlite differ diff --git a/inventory/__pycache__/__init__.cpython-311.pyc b/inventory/__pycache__/__init__.cpython-311.pyc index 4dd1f02a..f71fc08a 100644 Binary files a/inventory/__pycache__/__init__.cpython-311.pyc and b/inventory/__pycache__/__init__.cpython-311.pyc differ diff --git a/inventory/__pycache__/forms.cpython-311.pyc b/inventory/__pycache__/forms.cpython-311.pyc index 650be91c..a182052e 100644 Binary files a/inventory/__pycache__/forms.cpython-311.pyc and b/inventory/__pycache__/forms.cpython-311.pyc differ diff --git a/inventory/__pycache__/middleware.cpython-311.pyc b/inventory/__pycache__/middleware.cpython-311.pyc index 46734439..5e228927 100644 Binary files a/inventory/__pycache__/middleware.cpython-311.pyc and b/inventory/__pycache__/middleware.cpython-311.pyc differ diff --git a/inventory/__pycache__/models.cpython-311.pyc b/inventory/__pycache__/models.cpython-311.pyc index dd8f553c..515b703e 100644 Binary files a/inventory/__pycache__/models.cpython-311.pyc and b/inventory/__pycache__/models.cpython-311.pyc differ diff --git a/inventory/__pycache__/services.cpython-311.pyc b/inventory/__pycache__/services.cpython-311.pyc index 953e5eac..4263ba3d 100644 Binary files a/inventory/__pycache__/services.cpython-311.pyc and b/inventory/__pycache__/services.cpython-311.pyc differ diff --git a/inventory/__pycache__/urls.cpython-311.pyc b/inventory/__pycache__/urls.cpython-311.pyc index a85cbd34..558253c5 100644 Binary files a/inventory/__pycache__/urls.cpython-311.pyc and b/inventory/__pycache__/urls.cpython-311.pyc differ diff --git a/inventory/__pycache__/utils.cpython-311.pyc b/inventory/__pycache__/utils.cpython-311.pyc index aa75b3bc..a7051a50 100644 Binary files a/inventory/__pycache__/utils.cpython-311.pyc and b/inventory/__pycache__/utils.cpython-311.pyc differ diff --git a/inventory/__pycache__/views.cpython-311.pyc b/inventory/__pycache__/views.cpython-311.pyc index b68b7a79..bc437115 100644 Binary files a/inventory/__pycache__/views.cpython-311.pyc and b/inventory/__pycache__/views.cpython-311.pyc differ diff --git a/haikalna.py b/inventory/haikalna.py similarity index 98% rename from haikalna.py rename to inventory/haikalna.py index 49c4504a..21e82bcd 100644 --- a/haikalna.py +++ b/inventory/haikalna.py @@ -254,6 +254,7 @@ wmi_manufacturer_mapping = { "LND": "JMEV", "LNP": "NAC MG", "LNY": "Yuejin", + "LMX": "Dongfeng Forthing", "LPA": "Changan", "LPE": "BYD", "LPS": "Polestar", @@ -346,9 +347,7 @@ wmi_manufacturer_mapping = { "MCG": "Atul", "MC1": "Force", "MC2": "Eicher", - "MDE": "Kinetic Engineering Limited", "MDH": "Nissan", - "MDT": "Kerala Limited", "MD6": "TVS", "MD9": "Shuttles", "MEC": "Daimler", @@ -358,8 +357,6 @@ wmi_manufacturer_mapping = { "MET": "Piaggio", "MEX": "Škoda", "ME1": "Yamaha", - "ME3": "Royal Enfield", - "MYH": "Ather Energy", "MZB": "Kia", "MZZ": "Citroën", "MZ7": "MG", @@ -1501,7 +1498,7 @@ def decode_vds(manufacturer, vds): return "Unknown Model" -def decode_vin(vin): +def decode_vin_haikalna(vin): if len(vin) != 17: raise ValueError("Invalid VIN length. VIN must be 17 characters.") @@ -1518,21 +1515,22 @@ def decode_vin(vin): manufacturer = wmi_manufacturer_mapping.get(wmi, vds) year_code = vis[0] - year = vin_years(year_code) + year = vin_years(year_code)[0] model = decode_vds(manufacturer, vds) - return { - 'VIN': vin, - 'Manufacturer': manufacturer, - 'Year': year, - 'Model': model + data = { + 'maker': manufacturer, + 'model': model, + 'modelYear': year, + } + return data # JTDKB3FU4G3507653 # VR3USHNLWRJ521303 # KNARH81E8P5194005 # Example usage -vin_number = 'VYFED9HP0SJ519559' -decoded_vin = decode_vin(vin_number) -print(decoded_vin) \ No newline at end of file +# vin_number = 'LMXA14AF7PZ356070' +# decoded_vin = decode_vin_haikalna(vin_number) +# print(decoded_vin) \ No newline at end of file diff --git a/inventory/migrations/0022_alter_customer_is_lead.py b/inventory/migrations/0022_alter_customer_is_lead.py deleted file mode 100644 index 02c490b6..00000000 --- a/inventory/migrations/0022_alter_customer_is_lead.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-31 21:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0021_opportunitylog'), - ] - - operations = [ - migrations.AlterField( - model_name='customer', - name='is_lead', - field=models.BooleanField(default=False, verbose_name='Is Lead'), - ), - ] diff --git a/inventory/migrations/0022_remove_opportunitylog_user_opportunitylog_staff_and_more.py b/inventory/migrations/0022_remove_opportunitylog_user_opportunitylog_staff_and_more.py new file mode 100644 index 00000000..9c3caa2b --- /dev/null +++ b/inventory/migrations/0022_remove_opportunitylog_user_opportunitylog_staff_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 5.1.4 on 2025-01-01 16:25 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_ledger', '0017_alter_accountmodel_unique_together_and_more'), + ('inventory', '0021_opportunitylog'), + ] + + operations = [ + migrations.RemoveField( + model_name='opportunitylog', + name='user', + ), + migrations.AddField( + model_name='opportunitylog', + name='staff', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.staff', verbose_name='Staff'), + ), + migrations.AlterField( + model_name='carfinance', + name='additional_services', + field=models.ManyToManyField(blank=True, related_name='additional_finances', to='django_ledger.itemmodel'), + ), + migrations.AlterField( + model_name='customer', + name='is_lead', + field=models.BooleanField(default=False, verbose_name='Is Lead'), + ), + ] diff --git a/inventory/migrations/0023_remove_carfinance_additional_services_and_more.py b/inventory/migrations/0023_remove_carfinance_additional_services_and_more.py index c81d13d3..f844bf25 100644 --- a/inventory/migrations/0023_remove_carfinance_additional_services_and_more.py +++ b/inventory/migrations/0023_remove_carfinance_additional_services_and_more.py @@ -1,5 +1,6 @@ -# Generated by Django 4.2.17 on 2025-01-01 12:43 +# Generated by Django 5.1.4 on 2025-01-01 16:40 +import django.db.models.deletion from django.db import migrations, models @@ -7,7 +8,7 @@ class Migration(migrations.Migration): dependencies = [ ('django_ledger', '0017_alter_accountmodel_unique_together_and_more'), - ('inventory', '0022_rename_log_oportunitylog'), + ('inventory', '0022_remove_opportunitylog_user_opportunitylog_staff_and_more'), ] operations = [ @@ -17,7 +18,8 @@ class Migration(migrations.Migration): ), migrations.AddField( model_name='carfinance', - name='services', - field=models.ManyToManyField(blank=True, related_name='services', to='django_ledger.itemmodel'), + name='additional_services', + field=models.ForeignKey(blank=True, default=1, on_delete=django.db.models.deletion.CASCADE, related_name='additional_finances', to='django_ledger.itemmodel'), + preserve_default=False, ), ] diff --git a/inventory/migrations/0023_remove_opportunitylog_user_opportunitylog_staff.py b/inventory/migrations/0023_remove_opportunitylog_user_opportunitylog_staff.py deleted file mode 100644 index 2447e96a..00000000 --- a/inventory/migrations/0023_remove_opportunitylog_user_opportunitylog_staff.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.1.4 on 2025-01-01 01:24 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0022_alter_customer_is_lead'), - ] - - operations = [ - migrations.RemoveField( - model_name='opportunitylog', - name='user', - ), - migrations.AddField( - model_name='opportunitylog', - name='staff', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.staff', verbose_name='Staff'), - ), - ] diff --git a/inventory/migrations/0024_remove_carfinance_services_and_more.py b/inventory/migrations/0024_remove_carfinance_additional_services_and_more.py similarity index 87% rename from inventory/migrations/0024_remove_carfinance_services_and_more.py rename to inventory/migrations/0024_remove_carfinance_additional_services_and_more.py index a14fe9ee..85bfa2f4 100644 --- a/inventory/migrations/0024_remove_carfinance_services_and_more.py +++ b/inventory/migrations/0024_remove_carfinance_additional_services_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.17 on 2025-01-01 12:46 +# Generated by Django 5.1.4 on 2025-01-01 16:51 from django.db import migrations, models @@ -13,7 +13,7 @@ class Migration(migrations.Migration): operations = [ migrations.RemoveField( model_name='carfinance', - name='services', + name='additional_services', ), migrations.AddField( model_name='carfinance', diff --git a/inventory/models.py b/inventory/models.py index 5372c173..abd9dc10 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -154,7 +154,6 @@ class CarSpecificationValue(models.Model): verbose_name = "Specification Value" -# Car Model class CarStatusChoices(models.TextChoices): AVAILABLE = "available", _("Available") SOLD = "sold", _("Sold") @@ -184,6 +183,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin): def __str__(self): return self.name + " - " + str(self.price) + class Car(models.Model): vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN")) dealer = models.ForeignKey( @@ -308,6 +308,7 @@ class CarReservation(models.Model): verbose_name = _("Car Reservation") verbose_name_plural = _("Car Reservations") + # Car Finance Model class CarFinance(models.Model): additional_services = models.ManyToManyField(ItemModel, related_name="additional_finances",blank=True) @@ -389,7 +390,6 @@ class InteriorColors(models.Model, LocalizedNameMixin): return f"{self.name} ({self.rgb})" -# Colors Model class CarColors(models.Model): car = models.ForeignKey("Car", on_delete=models.CASCADE, related_name="colors") exterior = models.ForeignKey( @@ -408,7 +408,6 @@ class CarColors(models.Model): return f"{self.car} ({self.exterior.name}) ({self.interior.name})" -# Custom Card Model class CustomCard(models.Model): 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")) @@ -471,7 +470,7 @@ class CarLocation(models.Model): """ return self.owner == self.showroom -# Car Registration Model + class CarRegistration(models.Model): car = models.ForeignKey( Car, @@ -501,7 +500,7 @@ class TimestampedModel(models.Model): class Meta: abstract = True -#subscription + class Subscription(models.Model): plan = models.CharField(max_length=255) # e.g. "basic", "premium" start_date = models.DateField() @@ -529,7 +528,6 @@ class SubscriptionPlan(models.Model): return f"{self.name} - {self.price}" -# Dealer Model class Dealer(models.Model, LocalizedNameMixin): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="dealer") crn = models.CharField(max_length=10, @@ -608,18 +606,21 @@ class Dealer(models.Model, LocalizedNameMixin): # return self.parent_dealer if self.parent_dealer else self - -class STAFF_TYPES(models.TextChoices): +class StaffTypes(models.TextChoices): MANAGER = "manager", _("Manager") INVENTORY = "inventory", _("Inventory") ACCOUNTANT = "accountant", _("Accountant") SALES = "sales", _("Sales") +############################## +# Additional staff types for later + # COORDINATOR = "coordinator", _("Coordinator") # RECEPTIONIST = "receptionist", _("Receptionist") # AGENT = "agent", _("Agent") # TECHNICIAN = "technician", _("Technician") # DRIVER = "driver", _("Driver") +############################## class Staff(models.Model, LocalizedNameMixin): @@ -628,7 +629,7 @@ class Staff(models.Model, LocalizedNameMixin): name = models.CharField(max_length=255, verbose_name=_("Name")) arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number")) - staff_type = models.CharField(choices=STAFF_TYPES.choices, max_length=255, verbose_name=_("Staff Type")) + staff_type = models.CharField(choices=StaffTypes.choices, max_length=255, verbose_name=_("Staff Type")) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At")) @@ -641,7 +642,6 @@ class Staff(models.Model, LocalizedNameMixin): return f"{self.name} - {self.get_staff_type_display()}" -# Vendor Model class Vendor(models.Model, LocalizedNameMixin): dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="vendors") crn = models.CharField( @@ -671,7 +671,6 @@ class Vendor(models.Model, LocalizedNameMixin): return self.name -# Customer Model class Customer(models.Model): dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, @@ -932,6 +931,7 @@ class SaleQuotation(models.Model): last_quotation_number = 0 return itertools.count(last_quotation_number + 1) + class SaleQuotationCar(models.Model): quotation = models.ForeignKey( SaleQuotation, diff --git a/inventory/services.py b/inventory/services.py index f12183c9..df12bf59 100644 --- a/inventory/services.py +++ b/inventory/services.py @@ -12,6 +12,7 @@ from pyvin import VIN from django.conf import settings from openai import OpenAI from .models import Car,CarMake,CarModel +from inventory.haikalna import decode_vin_haikalna def get_make(item): @@ -37,6 +38,8 @@ def decodevin(vin): return result elif result:=elm(vin): return result + elif result:=decode_vin_haikalna(vin): + return result else: return None diff --git a/inventory/templatetags/__pycache__/custom_filters.cpython-311.pyc b/inventory/templatetags/__pycache__/custom_filters.cpython-311.pyc index 0e569b65..f421d566 100644 Binary files a/inventory/templatetags/__pycache__/custom_filters.cpython-311.pyc and b/inventory/templatetags/__pycache__/custom_filters.cpython-311.pyc differ diff --git a/inventory/templatetags/custom_filters.py b/inventory/templatetags/custom_filters.py index 6974aee8..c1f06443 100644 --- a/inventory/templatetags/custom_filters.py +++ b/inventory/templatetags/custom_filters.py @@ -29,4 +29,5 @@ def attr(field, args): attrs[key.strip()] = val.strip() else: attrs[definition.strip()] = True - return field.as_widget(attrs=attrs) \ No newline at end of file + return field.as_widget(attrs=attrs) + diff --git a/inventory/urls.py b/inventory/urls.py index 6453fa0b..23978c25 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -27,7 +27,7 @@ urlpatterns = [ path('login/code/', allauth_views.RequestLoginCodeView.as_view(template_name='account/request_login_code.html')), #Dashboards path('dashboards/accounting/', views.AccountingDashboard.as_view(), name='accounting'), - + path('test/', views.TestView.as_view(), name='test'), # Dealer URLs path('dealers//', views.DealerDetailView.as_view(), name='dealer_detail'), path('dealers//update/', views.DealerUpdateView.as_view(), name='dealer_update'), diff --git a/inventory/utils.py b/inventory/utils.py index 4a0ab507..84d5d4ba 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -57,4 +57,12 @@ def send_email(from_, to_, subject, message): message = message from_email = from_ recipient_list = [to_] - send_mail(subject, message, from_email, recipient_list) \ No newline at end of file + send_mail(subject, message, from_email, recipient_list) + + +def get_user_type(request): + if hasattr(request.user, 'dealer'): + dealer = request.user.dealer + elif hasattr(request.user, 'staff'): + dealer = request.user.staff.dealer + return dealer \ No newline at end of file diff --git a/inventory/views.py b/inventory/views.py index 7df69b17..109bd018 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -66,7 +66,7 @@ from . import models, forms from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.messages.views import SuccessMessageMixin from django.contrib.auth.models import Group -from .utils import get_calculations, send_email +from .utils import get_calculations, send_email, get_user_type from django.contrib.auth.models import User from allauth.account import views from django.db.models import Count, F, Value @@ -166,6 +166,10 @@ class HomeView(TemplateView): template_name = "index.html" +class TestView(TemplateView): + template_name = "test.html" + + class AccountingDashboard(LoginRequiredMixin, TemplateView): template_name = "dashboards/accounting.html" @@ -374,8 +378,9 @@ class CarInventory(LoginRequiredMixin, ListView): model_id = self.kwargs["model_id"] trim_id = self.kwargs["trim_id"] + dealer = get_user_type(self.request) cars = models.Car.objects.filter( - dealer=self.request.user.dealer, + dealer=dealer, id_car_make=make_id, id_car_model=model_id, id_car_trim=trim_id, @@ -415,7 +420,7 @@ class CarColorCreate(LoginRequiredMixin, CreateView): @login_required def inventory_stats_view(request): - dealer = request.user.dealer + dealer = get_user_type(request) # Annotate total cars by make, model, and trim cars = ( @@ -692,7 +697,6 @@ class DealerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): model = models.Customer - home_label = _("customers") context_object_name = "customers" paginate_by = 10 template_name = "customers/customer_list.html" @@ -700,17 +704,15 @@ class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): def get_queryset(self): query = self.request.GET.get("q") - if self.request.user.is_staff: - dealer = self.request.user.staff.dealer - customers = models.Customer.objects.filter( - dealer=dealer, - ) + dealer = get_user_type(self.request) + + customers = models.Customer.objects.filter(dealer=dealer) if query: customers = customers.filter( - Q(national_id__icontains=query) - | Q(first_name__icontains=query) - | Q(last_name__icontains=query) + Q(national_id__icontains=query) | + Q(first_name__icontains=query) | + Q(last_name__icontains=query) ) return customers @@ -879,7 +881,7 @@ class QuotationDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie @login_required def generate_invoice(request, pk): quotation = get_object_or_404(models.SaleQuotation, pk=pk) - dealer = request.user.dealer + dealer = get_user_type(request) entity = dealer.entity if not quotation.is_approved: messages.error( @@ -1731,7 +1733,8 @@ class EstimateListView(LoginRequiredMixin, ListView): # @csrf_exempt @login_required def create_estimate(request): - entity = request.entity + dealer = get_user_type(request) + entity = dealer.entity if request.method == "POST": try: data = json.loads(request.body) @@ -1932,7 +1935,8 @@ class InvoiceListView(LoginRequiredMixin, ListView): context_object_name = "invoices" def get_queryset(self): - entity = self.request.user.dealer.entity + dealer = get_user_type(self.request) + entity = dealer.entity return entity.get_invoices() @@ -2137,7 +2141,9 @@ def PaymentCreateView(request, pk=None): def PaymentListView(request): - entity = request.user.dealer.entity + dealer = get_user_type(request) + + entity = dealer.entity journals = JournalEntryModel.objects.filter(ledger__entity=entity).all() return render(request, "sales/payments/payment_list.html", {"journals": journals}) @@ -2314,10 +2320,10 @@ def fetch_notifications(request): class ItemServiceCreateView(CreateView): - model = ItemModel - form_class = ServiceCreateForm - template_name = "items/service/service_create.html" - success_url = reverse_lazy("item_service_list") + model = ItemModel + form_class = ServiceCreateForm + template_name = "items/service/service_create.html" + success_url = reverse_lazy("item_service_list") def get_form_kwargs(self): kwargs = super().get_form_kwargs() diff --git a/static/.DS_Store b/static/.DS_Store index 66355cb4..2b2bcc1b 100644 Binary files a/static/.DS_Store and b/static/.DS_Store differ diff --git a/static/css/custom.css b/static/css/custom.css index e69de29b..48e742af 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -0,0 +1,9 @@ + .color-div { + width: 22px; + height: 22px; + border-radius: 50%; + border: 1px solid #ccc; + text-align: center; + vertical-align: middle; + line-height: 22px; + } \ No newline at end of file diff --git a/static/images/.DS_Store b/static/images/.DS_Store index 2097c64d..e77671d4 100644 Binary files a/static/images/.DS_Store and b/static/images/.DS_Store differ diff --git a/static/images/logos/.DS_Store b/static/images/logos/.DS_Store index 56bc43f0..2524cbab 100644 Binary files a/static/images/logos/.DS_Store and b/static/images/logos/.DS_Store differ diff --git a/static/js/main.js b/static/js/main.js index 45f4e06e..bddab1a7 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -21,7 +21,7 @@ const getDataTableInit = () => { button.classList[disabled ? 'add' : 'remove']('disabled'); }; // Selectors - const table = document.getElementById('opportunityTable'); + const table = document.getElementById('inventoryTable'); if (table) { const options = { diff --git a/templates/base.html b/templates/base.html index aeafe268..2670e5e4 100644 --- a/templates/base.html +++ b/templates/base.html @@ -31,14 +31,15 @@ - + {% if LANGUAGE_CODE == 'en' %} {% else %} - {% endif %} + {% endif %} + {% block extra_css %}{% endblock extra_css %} diff --git a/templates/inventory/car_detail.html b/templates/inventory/car_detail.html index 2eb3b289..1ebc4f91 100644 --- a/templates/inventory/car_detail.html +++ b/templates/inventory/car_detail.html @@ -1,94 +1,28 @@ {% extends 'base.html' %} -{% load static %} -{% load i18n %} -{% load custom_filters %} +{% load i18n static custom_filters %} {% block title %}{{ _("Car Details") }}{% endblock %} + {% block content %} + -
-
- - - - - - - - - - - - -
-
-
- - -
-

{% trans 'Car Details' %}

-
-
- + +
+
+
+
+

{% trans 'Car Details' %}

+
+
+
@@ -129,7 +63,7 @@ - {% if car.vendor %} + {% if car.vendor %} @@ -142,11 +76,7 @@ @@ -164,10 +94,7 @@ @@ -176,42 +103,33 @@ {% endif %}
{% trans "VIN" %} {{ car.vin }}{% trans "Receiving Date"|capfirst %} {{ car.receiving_date|timesince }}
{% trans "Vendor"|capfirst %} {{ car.vendor.get_local_name }}
{% trans 'specifications'|capfirst %} -
{% trans "Custom Card" %} -
{% trans 'Location'|capfirst %} - {% if car.location %} - {% if car.location.is_owner_showroom %} - {% trans 'Our Showroom' %} - {% else %} - {{ car.location.showroom.get_local_name }} - {% endif %} - - {% trans "transfer"|capfirst %} + {% if car.location %} {% if car.location.is_owner_showroom %} {% trans 'Our Showroom' %} {% else %} {{ car.location.showroom.get_local_name }} {% endif %} + + {% trans "transfer"|capfirst %} {% else %} {% trans "No location available." %} - + {% trans "Add" %}
-
- -
-
+ -
-
-

{% trans 'Financial Details' %}

- -
+
+
+
+
+
+

{% trans 'Financial Details' %}

+
+
+ {% if car.finances %} -
-
{% if perms.inventory.view_carfinance %} @@ -231,12 +149,12 @@ {% if car.finances.additional_services.first.pk %} - {% for service in car.finances.additional_services.all %} - - - - - {% endfor %} + {% for service in car.finances.additional_services.all %} + + + + + {% endfor %} {% endif %} @@ -246,85 +164,74 @@ - + - + + {% endif %} +
{% trans "Cost Price"|capfirst %}
{{service.name}}{{ service.default_amount }}
{{service.name}}{{ service.default_amount }}
{% trans "VAT Amount"|capfirst %}{% trans "Total"|capfirst %} {{ car.finances.total }}
{% if perms.inventory.change_carfinance %} - + {% trans "Edit" %} - - {% endif %} - - {% else %} + + {% endif %} + {% else %}

{% trans "No finance details available." %}

- + {% trans "Add" %}
+
+
+
+
+

{% trans 'Colors Details' %}

+
+
+ + {% if car.colors.exists %} + {% for color in car.colors.all %} + + + + + + + + + + + {% endfor %} {% else %} + + + + + + {% endif %}
{% trans 'Exterior' %} + {{ color.exterior.get_local_name }} + +
+
{% trans 'Interior' %} + {{ color.interior.get_local_name }} + +
+
+ {% trans "No colors available for this car." %} +
+ + {% trans "Add" %} + +
-
-
-
-

{% trans 'Colors Details' %}

-
-
- - - {% if car.colors.exists %} - {% for color in car.colors.all %} - - - - - - - - - - - {% endfor %} - {% else %} - - - - - - - {% endif %} - -
{% trans 'Exterior' %} - {{ color.exterior.get_local_name }} - -
-
-
{% trans 'Interior' %} - {{ color.interior.get_local_name }} - -
-
-
- {% trans "No colors available for this car." %} -
- - {% trans "Add" %} - -
-
-
-
- -
-

{% trans 'Reservations Details' %}

-
-
+
+
+
+

{% trans 'Reservations Details' %}

+
+
+ {% if car.is_reserved %} -
@@ -341,50 +248,92 @@ {% if reservation.is_active %} {% csrf_token %} - - {% else %} - - {% trans "Expired" %} + + {% trans "Expired" %} {% endif %} - {% endfor %} - {% else %} - - - - + {% endfor %} {% else %} + + + + + {% endif %}
{% trans "Reserved By" %}
- - {% endif %} -
+ + +
-
-
-
+
+
+
+ + + + + + - + + \ No newline at end of file