edit styles
This commit is contained in:
parent
9e0824de6d
commit
b227d9ff5b
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,7 +5,7 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
media
|
media
|
||||||
|
./car_inventorysettings.py
|
||||||
# Backup files #
|
# Backup files #
|
||||||
*.bak
|
*.bak
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -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')
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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):
|
||||||
|
|||||||
@ -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
|
||||||
@ -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(
|
||||||
|
|||||||
@ -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"))
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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'),
|
||||||
|
|||||||
@ -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
BIN
static/images/logos/.DS_Store
vendored
BIN
static/images/logos/.DS_Store
vendored
Binary file not shown.
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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"
|
««
|
||||||
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 -->
|
«
|
||||||
{% 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">««</span>
|
||||||
{% endif %}
|
</li>
|
||||||
|
<li class="page-item disabled">
|
||||||
|
<span class="page-link">«</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">
|
||||||
|
»
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}" aria-label="Last">
|
||||||
|
»»
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li class="page-item disabled">
|
||||||
|
<span class="page-link">»</span>
|
||||||
|
</li>
|
||||||
|
<li class="page-item disabled">
|
||||||
|
<span class="page-link">»»</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">«</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li class="page-item disabled">
|
|
||||||
<a class="page-link" href="#" aria-label="Previous">
|
|
||||||
<span aria-hidden="true">«</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">»</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li class="page-item disabled">
|
|
||||||
<a class="page-link" href="#" aria-label="Next">
|
|
||||||
<span aria-hidden="true">»</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -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 %}
|
||||||
Loading…
x
Reference in New Issue
Block a user