Merge branch 'main' of http://10.10.1.136:3000/ismail/haikal
This commit is contained in:
commit
c427a8d4db
120
inventory/management/commands/generate_slugs.py
Normal file
120
inventory/management/commands/generate_slugs.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
from inventory.models import *
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.db import transaction, models
|
||||||
|
from django.utils.text import slugify
|
||||||
|
from django.db.models import Case, When, Value
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Generate slugs for model instances with proper empty value handling'
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--model',
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
help='Model name (format: "app_label.ModelName")'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--field',
|
||||||
|
type=str,
|
||||||
|
default='name',
|
||||||
|
help='Field to use as slug source (default: "name")'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--batch-size',
|
||||||
|
type=int,
|
||||||
|
default=1000,
|
||||||
|
help='Number of records to process at once (default: 1000)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--dry-run',
|
||||||
|
action='store_true',
|
||||||
|
help='Test without actually saving changes'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--fill-empty',
|
||||||
|
action='store_true',
|
||||||
|
help='Fill empty slugs with model-ID when source field is empty'
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
model = self.get_model(options['model'])
|
||||||
|
source_field = options['field']
|
||||||
|
batch_size = options['batch_size']
|
||||||
|
dry_run = options['dry_run']
|
||||||
|
fill_empty = options['fill_empty']
|
||||||
|
|
||||||
|
queryset = model.objects.filter(models.Q(slug__isnull=True) | models.Q(slug=''))
|
||||||
|
total_count = queryset.count()
|
||||||
|
processed = 0
|
||||||
|
empty_source = 0
|
||||||
|
|
||||||
|
self.stdout.write(
|
||||||
|
self.style.SUCCESS(
|
||||||
|
f'Generating slugs for {total_count} {model._meta.model_name} records '
|
||||||
|
f'using field "{source_field}" (batch size: {batch_size})'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
if dry_run:
|
||||||
|
self.stdout.write(self.style.WARNING('DRY RUN - No changes will be saved'))
|
||||||
|
transaction.set_rollback(True)
|
||||||
|
|
||||||
|
for offset in range(0, total_count, batch_size):
|
||||||
|
batch = queryset[offset:offset + batch_size]
|
||||||
|
updates = []
|
||||||
|
|
||||||
|
for obj in batch:
|
||||||
|
source_value = getattr(obj, source_field, '')
|
||||||
|
|
||||||
|
if not source_value:
|
||||||
|
if fill_empty:
|
||||||
|
# Fallback to model-ID when source field is empty
|
||||||
|
new_slug = f"{model._meta.model_name.lower()}-{obj.pk}"
|
||||||
|
empty_source += 1
|
||||||
|
else:
|
||||||
|
self.stdout.write(
|
||||||
|
self.style.WARNING(
|
||||||
|
f'Skipping {obj} (empty {source_field})'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
slug_base = slugify(str(source_value))[:50] # Ensure string and truncate
|
||||||
|
new_slug = f"{slug_base}-{obj.pk}" # Guaranteed unique
|
||||||
|
|
||||||
|
updates.append((obj.pk, new_slug))
|
||||||
|
processed += 1
|
||||||
|
|
||||||
|
if updates and not dry_run:
|
||||||
|
cases = [When(pk=pk, then=Value(slug)) for pk, slug in updates]
|
||||||
|
model.objects.filter(pk__in=[u[0] for u in updates]).update(
|
||||||
|
slug=Case(*cases, output_field=models.CharField())
|
||||||
|
)
|
||||||
|
|
||||||
|
self.stdout.write(
|
||||||
|
f'Processed batch {offset//batch_size + 1}: '
|
||||||
|
f'{min(offset + batch_size, total_count)}/{total_count}'
|
||||||
|
)
|
||||||
|
|
||||||
|
stats = [
|
||||||
|
f"Total processed: {processed}",
|
||||||
|
f"Records with empty source field: {empty_source}",
|
||||||
|
f"Skipped records: {total_count - processed - empty_source}"
|
||||||
|
]
|
||||||
|
|
||||||
|
self.stdout.write(
|
||||||
|
self.style.SUCCESS('\n'.join(stats))
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_model(self, model_path):
|
||||||
|
"""Get model class from 'app_label.ModelName' string"""
|
||||||
|
from django.apps import apps
|
||||||
|
try:
|
||||||
|
app_label, model_name = model_path.split('.')
|
||||||
|
return apps.get_model(app_label, model_name)
|
||||||
|
except ValueError:
|
||||||
|
raise self.style.ERROR('Model must be specified as "app_label.ModelName"')
|
||||||
|
except LookupError as e:
|
||||||
|
raise self.style.ERROR(f'Model not found: {e}')
|
||||||
@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 5.1.7 on 2025-05-18 11:18
|
# Generated by Django 5.1.7 on 2025-05-18 15:52
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
@ -7,6 +7,7 @@ import django.utils.timezone
|
|||||||
import inventory.mixins
|
import inventory.mixins
|
||||||
import inventory.models
|
import inventory.models
|
||||||
import phonenumber_field.modelfields
|
import phonenumber_field.modelfields
|
||||||
|
import uuid
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
@ -28,7 +29,10 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Car',
|
name='Car',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='Primary Key')),
|
||||||
|
('slug', models.SlugField(blank=True, help_text='Slug for the object. If not provided, it will be generated automatically.', null=True, unique=True, verbose_name='Slug')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
|
||||||
('vin', models.CharField(max_length=17, unique=True, verbose_name='VIN')),
|
('vin', models.CharField(max_length=17, unique=True, verbose_name='VIN')),
|
||||||
('year', models.IntegerField(verbose_name='Year')),
|
('year', models.IntegerField(verbose_name='Year')),
|
||||||
('status', models.CharField(choices=[('available', 'Available'), ('sold', 'Sold'), ('hold', 'Hold'), ('damaged', 'Damaged'), ('reserved', 'Reserved'), ('transfer', 'Transfer')], default='available', max_length=10, verbose_name='Status')),
|
('status', models.CharField(choices=[('available', 'Available'), ('sold', 'Sold'), ('hold', 'Hold'), ('damaged', 'Damaged'), ('reserved', 'Reserved'), ('transfer', 'Transfer')], default='available', max_length=10, verbose_name='Status')),
|
||||||
@ -50,6 +54,7 @@ class Migration(migrations.Migration):
|
|||||||
('name', models.CharField(blank=True, max_length=255, null=True)),
|
('name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
('year_begin', models.IntegerField(blank=True, null=True)),
|
('year_begin', models.IntegerField(blank=True, null=True)),
|
||||||
|
('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Equipment',
|
'verbose_name': 'Equipment',
|
||||||
@ -61,6 +66,7 @@ class Migration(migrations.Migration):
|
|||||||
fields=[
|
fields=[
|
||||||
('id_car_make', models.AutoField(primary_key=True, serialize=False)),
|
('id_car_make', models.AutoField(primary_key=True, serialize=False)),
|
||||||
('name', models.CharField(blank=True, max_length=255, null=True)),
|
('name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)),
|
||||||
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
('logo', models.ImageField(blank=True, null=True, upload_to='car_make', verbose_name='logo')),
|
('logo', models.ImageField(blank=True, null=True, upload_to='car_make', verbose_name='logo')),
|
||||||
('is_sa_import', models.BooleanField(default=False)),
|
('is_sa_import', models.BooleanField(default=False)),
|
||||||
@ -166,6 +172,7 @@ class Migration(migrations.Migration):
|
|||||||
('id_car_model', models.AutoField(primary_key=True, serialize=False)),
|
('id_car_model', models.AutoField(primary_key=True, serialize=False)),
|
||||||
('name', models.CharField(blank=True, max_length=255, null=True)),
|
('name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)),
|
||||||
('id_car_make', models.ForeignKey(db_column='id_car_make', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake')),
|
('id_car_make', models.ForeignKey(db_column='id_car_make', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
@ -184,6 +191,7 @@ class Migration(migrations.Migration):
|
|||||||
('id_car_option', models.AutoField(primary_key=True, serialize=False)),
|
('id_car_option', models.AutoField(primary_key=True, serialize=False)),
|
||||||
('name', models.CharField(blank=True, max_length=255, null=True)),
|
('name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)),
|
||||||
('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.caroption')),
|
('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.caroption')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
@ -230,6 +238,7 @@ class Migration(migrations.Migration):
|
|||||||
('year_begin', models.IntegerField(blank=True, null=True)),
|
('year_begin', models.IntegerField(blank=True, null=True)),
|
||||||
('year_end', models.IntegerField(blank=True, null=True)),
|
('year_end', models.IntegerField(blank=True, null=True)),
|
||||||
('generation_name', models.CharField(blank=True, max_length=255, null=True)),
|
('generation_name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)),
|
||||||
('id_car_model', models.ForeignKey(db_column='id_car_model', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel')),
|
('id_car_model', models.ForeignKey(db_column='id_car_model', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
@ -248,6 +257,7 @@ class Migration(migrations.Migration):
|
|||||||
('id_car_specification', models.AutoField(primary_key=True, serialize=False)),
|
('id_car_specification', models.AutoField(primary_key=True, serialize=False)),
|
||||||
('name', models.CharField(max_length=255)),
|
('name', models.CharField(max_length=255)),
|
||||||
('arabic_name', models.CharField(max_length=255)),
|
('arabic_name', models.CharField(max_length=255)),
|
||||||
|
('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)),
|
||||||
('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')),
|
('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
@ -263,6 +273,7 @@ class Migration(migrations.Migration):
|
|||||||
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
('start_production_year', models.IntegerField(blank=True, null=True)),
|
('start_production_year', models.IntegerField(blank=True, null=True)),
|
||||||
('end_production_year', models.IntegerField(blank=True, null=True)),
|
('end_production_year', models.IntegerField(blank=True, null=True)),
|
||||||
|
('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)),
|
||||||
('id_car_serie', models.ForeignKey(db_column='id_car_serie', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie')),
|
('id_car_serie', models.ForeignKey(db_column='id_car_serie', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
|
|||||||
18
inventory/migrations/0002_dealer_slug.py
Normal file
18
inventory/migrations/0002_dealer_slug.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.7 on 2025-05-18 16:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='dealer',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(blank=True, max_length=255, null=True, unique=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
inventory/migrations/0003_customer_slug.py
Normal file
18
inventory/migrations/0003_customer_slug.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.7 on 2025-05-18 16:37
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0002_dealer_slug'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customer',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(blank=True, editable=False, max_length=255, null=True, unique=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
inventory/migrations/0004_vendor_slug.py
Normal file
18
inventory/migrations/0004_vendor_slug.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.7 on 2025-05-18 16:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0003_customer_slug'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='vendor',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(blank=True, max_length=255, null=True, unique=True, verbose_name='Slug'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
inventory/migrations/0005_lead_slug.py
Normal file
18
inventory/migrations/0005_lead_slug.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.7 on 2025-05-18 16:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0004_vendor_slug'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='lead',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(blank=True, null=True, unique=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
inventory/migrations/0006_alter_activity_activity_type.py
Normal file
18
inventory/migrations/0006_alter_activity_activity_type.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.7 on 2025-05-18 16:58
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0005_lead_slug'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='activity',
|
||||||
|
name='activity_type',
|
||||||
|
field=models.CharField(choices=[('call', 'Call'), ('sms', 'SMS'), ('email', 'Email'), ('meeting', 'Meeting'), ('whatsapp', 'WhatsApp'), ('visit', 'Visit'), ('negotiation', 'Negotiation'), ('follow_up', 'Follow Up'), ('won', 'Won'), ('lost', 'Lost'), ('closed', 'Closed'), ('converted', 'Converted'), ('transfer', 'Transfer'), ('add_car', 'Add Car'), ('sale_car', 'Sale Car'), ('reserve_car', 'Reserve Car'), ('transfer_car', 'Transfer Car'), ('remove_car', 'Remove Car'), ('create_quotation', 'Create Quotation'), ('cancel_quotation', 'Cancel Quotation'), ('create_order', 'Create Order'), ('cancel_order', 'Cancel Order'), ('create_invoice', 'Create Invoice'), ('cancel_invoice', 'Cancel Invoice')], max_length=50, verbose_name='Activity Type'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -1,5 +1,7 @@
|
|||||||
|
import uuid
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from django.utils.text import slugify
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
import hashlib
|
import hashlib
|
||||||
@ -30,6 +32,22 @@ from plans.models import UserPlan,Quota,PlanQuota
|
|||||||
# from plans.models import AbstractPlan
|
# from plans.models import AbstractPlan
|
||||||
# from simple_history.models import HistoricalRecords
|
# from simple_history.models import HistoricalRecords
|
||||||
|
|
||||||
|
class Base(models.Model):
|
||||||
|
id = models.UUIDField(unique=True, editable=False, default=uuid.uuid4, primary_key=True,verbose_name=_("Primary Key"))
|
||||||
|
slug = models.SlugField(null=True, blank=True, unique=True,verbose_name=_("Slug"),
|
||||||
|
help_text=_("Slug for the object. If not provided, it will be generated automatically."))
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
|
||||||
|
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
|
||||||
|
def clean(self):
|
||||||
|
if isinstance(self.id, str):
|
||||||
|
try:
|
||||||
|
uuid.UUID(self.id)
|
||||||
|
except ValueError:
|
||||||
|
raise ValidationError({'id': 'Invalid UUID format'})
|
||||||
|
super().clean()
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
class DealerUserManager(UserManager):
|
class DealerUserManager(UserManager):
|
||||||
def create_user_with_dealer(
|
def create_user_with_dealer(
|
||||||
@ -162,11 +180,16 @@ class CarType(models.IntegerChoices):
|
|||||||
class CarMake(models.Model, LocalizedNameMixin):
|
class CarMake(models.Model, LocalizedNameMixin):
|
||||||
id_car_make = models.AutoField(primary_key=True)
|
id_car_make = models.AutoField(primary_key=True)
|
||||||
name = models.CharField(max_length=255, blank=True, null=True)
|
name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||||
arabic_name = models.CharField(max_length=255, blank=True, null=True)
|
arabic_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
logo = models.ImageField(_("logo"), upload_to="car_make", blank=True, null=True)
|
logo = models.ImageField(_("logo"), upload_to="car_make", blank=True, null=True)
|
||||||
is_sa_import = models.BooleanField(default=False)
|
is_sa_import = models.BooleanField(default=False)
|
||||||
car_type = models.SmallIntegerField(choices=CarType.choices, blank=True, null=True)
|
car_type = models.SmallIntegerField(choices=CarType.choices, blank=True, null=True)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(self.name)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@ -178,6 +201,11 @@ class CarModel(models.Model, LocalizedNameMixin):
|
|||||||
id_car_make = models.ForeignKey(CarMake, models.DO_NOTHING, db_column="id_car_make")
|
id_car_make = models.ForeignKey(CarMake, models.DO_NOTHING, db_column="id_car_make")
|
||||||
name = models.CharField(max_length=255, blank=True, null=True)
|
name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
arabic_name = models.CharField(max_length=255, blank=True, null=True)
|
arabic_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(self.name)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -196,6 +224,11 @@ class CarSerie(models.Model, LocalizedNameMixin):
|
|||||||
year_begin = models.IntegerField(blank=True, null=True)
|
year_begin = models.IntegerField(blank=True, null=True)
|
||||||
year_end = models.IntegerField(blank=True, null=True)
|
year_end = models.IntegerField(blank=True, null=True)
|
||||||
generation_name = models.CharField(max_length=255, blank=True, null=True)
|
generation_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(self.name)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -213,6 +246,11 @@ class CarTrim(models.Model, LocalizedNameMixin):
|
|||||||
arabic_name = models.CharField(max_length=255, blank=True, null=True)
|
arabic_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
start_production_year = models.IntegerField(blank=True, null=True)
|
start_production_year = models.IntegerField(blank=True, null=True)
|
||||||
end_production_year = models.IntegerField(blank=True, null=True)
|
end_production_year = models.IntegerField(blank=True, null=True)
|
||||||
|
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(self.name)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -227,6 +265,11 @@ class CarEquipment(models.Model, LocalizedNameMixin):
|
|||||||
name = models.CharField(max_length=255, blank=True, null=True)
|
name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
arabic_name = models.CharField(max_length=255, blank=True, null=True)
|
arabic_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
year_begin = models.IntegerField(blank=True, null=True)
|
year_begin = models.IntegerField(blank=True, null=True)
|
||||||
|
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(self.name)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -242,6 +285,11 @@ class CarSpecification(models.Model, LocalizedNameMixin):
|
|||||||
id_parent = models.ForeignKey(
|
id_parent = models.ForeignKey(
|
||||||
"self", models.DO_NOTHING, db_column="id_parent", blank=True, null=True
|
"self", models.DO_NOTHING, db_column="id_parent", blank=True, null=True
|
||||||
)
|
)
|
||||||
|
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(self.name)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -273,6 +321,11 @@ class CarOption(models.Model, LocalizedNameMixin):
|
|||||||
id_parent = models.ForeignKey(
|
id_parent = models.ForeignKey(
|
||||||
"self", models.DO_NOTHING, db_column="id_parent", blank=True, null=True
|
"self", models.DO_NOTHING, db_column="id_parent", blank=True, null=True
|
||||||
)
|
)
|
||||||
|
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(self.name)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -368,7 +421,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin):
|
|||||||
return self.name + " - " + str(self.price)
|
return self.name + " - " + str(self.price)
|
||||||
|
|
||||||
|
|
||||||
class Car(models.Model):
|
class Car(Base):
|
||||||
vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
|
vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
|
||||||
dealer = models.ForeignKey(
|
dealer = models.ForeignKey(
|
||||||
"Dealer", models.DO_NOTHING, related_name="cars", verbose_name=_("Dealer")
|
"Dealer", models.DO_NOTHING, related_name="cars", verbose_name=_("Dealer")
|
||||||
@ -432,6 +485,7 @@ class Car(models.Model):
|
|||||||
# history = HistoricalRecords()
|
# history = HistoricalRecords()
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
self.slug = slugify(self.vin)
|
||||||
self.hash = self.get_hash
|
self.hash = self.get_hash
|
||||||
super(Car, self).save(*args, **kwargs)
|
super(Car, self).save(*args, **kwargs)
|
||||||
|
|
||||||
@ -445,6 +499,9 @@ class Car(models.Model):
|
|||||||
trim = self.id_car_trim.name if self.id_car_trim else "Unknown Trim"
|
trim = self.id_car_trim.name if self.id_car_trim else "Unknown Trim"
|
||||||
return f"{self.year} - {make} - {model} - {trim}"
|
return f"{self.year} - {make} - {model} - {trim}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def product(self):
|
||||||
|
return self.dealer.entity.get_items_all().filter(name=self.vin).first()
|
||||||
def get_reservation(self):
|
def get_reservation(self):
|
||||||
return self.reservations.filter(reserved_until__gt=now()).first()
|
return self.reservations.filter(reserved_until__gt=now()).first()
|
||||||
def is_reserved(self):
|
def is_reserved(self):
|
||||||
@ -507,7 +564,7 @@ class Car(models.Model):
|
|||||||
"mileage": self.mileage,
|
"mileage": self.mileage,
|
||||||
"receiving_date": self.receiving_date.strftime('%Y-%m-%d %H:%M:%S'),
|
"receiving_date": self.receiving_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
'hash': self.get_hash,
|
'hash': self.get_hash,
|
||||||
"id": self.id,
|
"id": str(self.id),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_specifications(self):
|
def get_specifications(self):
|
||||||
@ -830,9 +887,14 @@ class Dealer(models.Model, LocalizedNameMixin):
|
|||||||
)
|
)
|
||||||
joined_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Joined At"))
|
joined_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Joined At"))
|
||||||
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
|
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
|
||||||
|
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||||
|
|
||||||
objects = DealerUserManager()
|
objects = DealerUserManager()
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(self.name)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active_plan(self):
|
def active_plan(self):
|
||||||
@ -997,6 +1059,7 @@ class ActionChoices(models.TextChoices):
|
|||||||
CALL = "call", _("Call")
|
CALL = "call", _("Call")
|
||||||
SMS = "sms", _("SMS")
|
SMS = "sms", _("SMS")
|
||||||
EMAIL = "email", _("Email")
|
EMAIL = "email", _("Email")
|
||||||
|
MEETING = "meeting", _("Meeting")
|
||||||
WHATSAPP = "whatsapp", _("WhatsApp")
|
WHATSAPP = "whatsapp", _("WhatsApp")
|
||||||
VISIT = "visit", _("Visit")
|
VISIT = "visit", _("Visit")
|
||||||
LEAD_NEGOTIATION = "negotiation", _("Negotiation")
|
LEAD_NEGOTIATION = "negotiation", _("Negotiation")
|
||||||
@ -1072,6 +1135,12 @@ class Customer(models.Model):
|
|||||||
)
|
)
|
||||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
|
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
|
||||||
updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
|
updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
|
||||||
|
slug = models.SlugField(max_length=255, unique=True, editable=False, null=True, blank=True)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(f"{self.first_name} {self.last_name}")
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Customer")
|
verbose_name = _("Customer")
|
||||||
@ -1348,7 +1417,12 @@ class Lead(models.Model):
|
|||||||
auto_now_add=True, verbose_name=_("Created"), db_index=True
|
auto_now_add=True, verbose_name=_("Created"), db_index=True
|
||||||
)
|
)
|
||||||
updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
|
updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
|
||||||
|
slug = models.SlugField(unique=True, blank=True, null=True)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(f"{self.first_name} {self.last_name}")
|
||||||
|
super(Lead, self).save(*args, **kwargs)
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Lead")
|
verbose_name = _("Lead")
|
||||||
verbose_name_plural = _("Leads")
|
verbose_name_plural = _("Leads")
|
||||||
@ -1684,7 +1758,12 @@ class Vendor(models.Model, LocalizedNameMixin):
|
|||||||
upload_to="logos/vendors", blank=True, null=True, verbose_name=_("Logo")
|
upload_to="logos/vendors", blank=True, null=True, verbose_name=_("Logo")
|
||||||
)
|
)
|
||||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
|
||||||
|
slug = models.SlugField(max_length=255, unique=True, verbose_name=_("Slug"), null=True,blank=True)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(self.name)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Vendor")
|
verbose_name = _("Vendor")
|
||||||
verbose_name_plural = _("Vendors")
|
verbose_name_plural = _("Vendors")
|
||||||
|
|||||||
@ -621,7 +621,7 @@ def create_make_ledger_accounts(sender, instance, created, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
# @receiver(post_save, sender=VendorModel)
|
# @receiver(post_save, sender=VendorModel)
|
||||||
# def create_vendor_accounts(sender, instance, created, **kwargs):
|
# def create_vendor_accounts(sender, instance, created, **kwargs):Dealer)
|
||||||
# if created:
|
# if created:
|
||||||
# entity = instance.entity_model
|
# entity = instance.entity_model
|
||||||
# coa = entity.get_default_coa()
|
# coa = entity.get_default_coa()
|
||||||
|
|||||||
@ -53,7 +53,7 @@ urlpatterns = [
|
|||||||
path("submit_plan/", views.submit_plan, name="submit_plan"),
|
path("submit_plan/", views.submit_plan, name="submit_plan"),
|
||||||
path('payment-callback/', views.payment_callback, name='payment_callback'),
|
path('payment-callback/', views.payment_callback, name='payment_callback'),
|
||||||
#
|
#
|
||||||
path("dealers/<int:pk>/settings/", views.DealerSettingsView, name="dealer_settings"),
|
path("dealers/<slug:slug>/settings/", views.DealerSettingsView, name="dealer_settings"),
|
||||||
path("dealers/assign-car-makes/", views.assign_car_makes, name="assign_car_makes"),
|
path("dealers/assign-car-makes/", views.assign_car_makes, name="assign_car_makes"),
|
||||||
path("dashboards/manager/", views.ManagerDashboard.as_view(), name="manager_dashboard"),
|
path("dashboards/manager/", views.ManagerDashboard.as_view(), name="manager_dashboard"),
|
||||||
path("dashboards/sales/", views.SalesDashboard.as_view(), name="sales_dashboard"),
|
path("dashboards/sales/", views.SalesDashboard.as_view(), name="sales_dashboard"),
|
||||||
@ -61,9 +61,9 @@ urlpatterns = [
|
|||||||
path('cars/inventory/table/', views.CarListViewTable.as_view(), name="car_table"),
|
path('cars/inventory/table/', views.CarListViewTable.as_view(), name="car_table"),
|
||||||
path("export/format/", TableExport, name="export"),
|
path("export/format/", TableExport, name="export"),
|
||||||
# Dealer URLs
|
# Dealer URLs
|
||||||
path("dealers/<int:pk>/", views.DealerDetailView.as_view(), name="dealer_detail"),
|
path("dealers/<slug:slug>/", views.DealerDetailView.as_view(), name="dealer_detail"),
|
||||||
path(
|
path(
|
||||||
"dealers/<int:pk>/update/",
|
"dealers/<slug:slug>/update/",
|
||||||
views.DealerUpdateView.as_view(),
|
views.DealerUpdateView.as_view(),
|
||||||
name="dealer_update",
|
name="dealer_update",
|
||||||
),
|
),
|
||||||
@ -76,7 +76,7 @@ urlpatterns = [
|
|||||||
# CRM URLs
|
# CRM URLs
|
||||||
path("customers/", views.CustomerListView.as_view(), name="customer_list"),
|
path("customers/", views.CustomerListView.as_view(), name="customer_list"),
|
||||||
path(
|
path(
|
||||||
"customers/<int:pk>/",
|
"customers/<slug:slug>/",
|
||||||
views.CustomerDetailView.as_view(),
|
views.CustomerDetailView.as_view(),
|
||||||
name="customer_detail",
|
name="customer_detail",
|
||||||
),
|
),
|
||||||
@ -84,18 +84,18 @@ urlpatterns = [
|
|||||||
"customers/create/", views.CustomerCreateView.as_view(), name="customer_create"
|
"customers/create/", views.CustomerCreateView.as_view(), name="customer_create"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"customers/<int:pk>/update/",
|
"customers/<slug:slug>/update/",
|
||||||
views.CustomerUpdateView.as_view(),
|
views.CustomerUpdateView.as_view(),
|
||||||
name="customer_update",
|
name="customer_update",
|
||||||
),
|
),
|
||||||
path("customers/<int:pk>/delete/", views.delete_customer, name="customer_delete"),
|
path("customers/<slug:slug>/delete/", views.delete_customer, name="customer_delete"),
|
||||||
path(
|
path(
|
||||||
"customers/<str:customer_id>/opportunities/create/",
|
"customers/<slug:slug>/opportunities/create/",
|
||||||
views.OpportunityCreateView.as_view(),
|
views.OpportunityCreateView.as_view(),
|
||||||
name="create_opportunity",
|
name="create_opportunity",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"customers/<int:pk>/add-note/",
|
"customers/<slug:slug>/add-note/",
|
||||||
views.add_note_to_customer,
|
views.add_note_to_customer,
|
||||||
name="add_note_to_customer",
|
name="add_note_to_customer",
|
||||||
),
|
),
|
||||||
@ -105,13 +105,13 @@ urlpatterns = [
|
|||||||
|
|
||||||
path("crm/leads/", views.LeadListView.as_view(), name="lead_list"),
|
path("crm/leads/", views.LeadListView.as_view(), name="lead_list"),
|
||||||
path(
|
path(
|
||||||
"crm/leads/<int:pk>/view/", views.LeadDetailView.as_view(), name="lead_detail"
|
"crm/leads/<slug:slug>/view/", views.LeadDetailView.as_view(), name="lead_detail"
|
||||||
),
|
),
|
||||||
path("crm/leads/create/", views.lead_create, name="lead_create"),
|
path("crm/leads/create/", views.lead_create, name="lead_create"),
|
||||||
path(
|
path(
|
||||||
"crm/leads/<int:pk>/update/", views.LeadUpdateView.as_view(), name="lead_update"
|
"crm/leads/<int:pk>/update/", views.LeadUpdateView.as_view(), name="lead_update"
|
||||||
),
|
),
|
||||||
path("crm/leads/<int:pk>/delete/", views.LeadDeleteView, name="lead_delete"),
|
path("crm/leads/<slug:slug>/delete/", views.LeadDeleteView, name="lead_delete"),
|
||||||
path("crm/leads/<int:pk>/lead-convert/", views.lead_convert, name="lead_convert"),
|
path("crm/leads/<int:pk>/lead-convert/", views.lead_convert, name="lead_convert"),
|
||||||
path("crm/leads/<int:pk>/add-note/", views.add_note_to_lead, name="add_note_to_lead"),
|
path("crm/leads/<int:pk>/add-note/", views.add_note_to_lead, name="add_note_to_lead"),
|
||||||
path('crm/leads/<int:pk>/update-note/', views.update_note, name='update_note_to_lead'),
|
path('crm/leads/<int:pk>/update-note/', views.update_note, name='update_note_to_lead'),
|
||||||
@ -122,27 +122,27 @@ urlpatterns = [
|
|||||||
name="update_task",
|
name="update_task",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"crm/<str:content_type>/<int:pk>/add-task/",
|
"crm/<str:content_type>/<slug:slug>/add-task/",
|
||||||
views.add_task,
|
views.add_task,
|
||||||
name="add_task",
|
name="add_task",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"crm/<str:content_type>/<int:pk>/add-activity/",
|
"crm/<str:content_type>/<slug:slug>/add-activity/",
|
||||||
views.add_activity,
|
views.add_activity,
|
||||||
name="add_activity",
|
name="add_activity",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"crm/leads/<int:pk>/send_lead_email/",
|
"crm/leads/<slug:slug>/send_lead_email/",
|
||||||
views.send_lead_email,
|
views.send_lead_email,
|
||||||
name="send_lead_email",
|
name="send_lead_email",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"crm/leads/<int:pk>/send_lead_email/<int:email_pk>",
|
"crm/leads/<slug:slug>/send_lead_email/<int:email_pk>",
|
||||||
views.send_lead_email,
|
views.send_lead_email,
|
||||||
name="send_lead_email_with_template",
|
name="send_lead_email_with_template",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"crm/leads/<int:pk>/schedule/",
|
"crm/leads/<slug:slug>/schedule/",
|
||||||
views.schedule_lead,
|
views.schedule_lead,
|
||||||
name="schedule_lead",
|
name="schedule_lead",
|
||||||
),
|
),
|
||||||
@ -229,33 +229,34 @@ urlpatterns = [
|
|||||||
path('crm/calender/', views.EmployeeCalendarView.as_view(), name='calendar_list'),
|
path('crm/calender/', views.EmployeeCalendarView.as_view(), name='calendar_list'),
|
||||||
# Vendor URLs
|
# Vendor URLs
|
||||||
path("vendors", views.VendorListView.as_view(), name="vendor_list"),
|
path("vendors", views.VendorListView.as_view(), name="vendor_list"),
|
||||||
path("vendors/<int:pk>/", views.vendorDetailView, name="vendor_detail"),
|
path("vendors/<slug:slug>/", views.vendorDetailView, name="vendor_detail"),
|
||||||
path("vendors/create/", views.VendorCreateView.as_view(), name="vendor_create"),
|
path("vendors/create/", views.VendorCreateView.as_view(), name="vendor_create"),
|
||||||
path(
|
path(
|
||||||
"vendors/<int:pk>/update/",
|
"vendors/<slug:slug>/update/",
|
||||||
views.VendorUpdateView.as_view(),
|
views.VendorUpdateView.as_view(),
|
||||||
name="vendor_update",
|
name="vendor_update",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"vendors/<int:pk>/delete/",
|
"vendors/<slug:slug>/delete/",
|
||||||
views.delete_vendor,
|
views.delete_vendor,
|
||||||
name="vendor_delete",
|
name="vendor_delete",
|
||||||
),
|
),
|
||||||
# Car URLs
|
# Car URLs
|
||||||
|
path("cars/add/", views.CarCreateView.as_view(), name="car_add"),
|
||||||
path("cars/inventory/", views.CarInventory.as_view(), name="car_inventory_all"),
|
path("cars/inventory/", views.CarInventory.as_view(), name="car_inventory_all"),
|
||||||
path(
|
path(
|
||||||
"cars/inventory/<int:make_id>/<int:model_id>/<int:trim_id>/",
|
"cars/inventory/<slug:make_id>/<slug:model_id>/<slug:trim_id>/",
|
||||||
views.CarInventory.as_view(),
|
views.CarInventory.as_view(),
|
||||||
name="car_inventory",
|
name="car_inventory",
|
||||||
),
|
),
|
||||||
path("cars/inventory/stats", views.inventory_stats_view, name="inventory_stats"),
|
path("cars/inventory/stats", views.inventory_stats_view, name="inventory_stats"),
|
||||||
path("cars/inventory/list", views.CarListView.as_view(), name="car_list"),
|
path("cars/inventory/list", views.CarListView.as_view(), name="car_list"),
|
||||||
path("cars/<int:pk>/", views.CarDetailView.as_view(), name="car_detail"),
|
path("cars/<slug:slug>/", views.CarDetailView.as_view(), name="car_detail"),
|
||||||
path("cars/<int:pk>/history/", views.car_history, name="car_history"),
|
path("cars/<slug:slug>/history/", views.car_history, name="car_history"),
|
||||||
path("cars/<int:pk>/update/", views.CarUpdateView.as_view(), name="car_update"),
|
path("cars/<slug:slug>/update/", views.CarUpdateView.as_view(), name="car_update"),
|
||||||
path("cars/<int:pk>/delete/", views.CarDeleteView.as_view(), name="car_delete"),
|
path("cars/<slug:slug>/delete/", views.CarDeleteView.as_view(), name="car_delete"),
|
||||||
path(
|
path(
|
||||||
"cars/<int:car_pk>/finance/create/",
|
"cars/<slug:slug>/finance/create/",
|
||||||
views.CarFinanceCreateView.as_view(),
|
views.CarFinanceCreateView.as_view(),
|
||||||
name="car_finance_create",
|
name="car_finance_create",
|
||||||
),
|
),
|
||||||
@ -264,43 +265,42 @@ urlpatterns = [
|
|||||||
views.CarFinanceUpdateView.as_view(),
|
views.CarFinanceUpdateView.as_view(),
|
||||||
name="car_finance_update",
|
name="car_finance_update",
|
||||||
),
|
),
|
||||||
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(
|
path(
|
||||||
"cars/<int:car_pk>/add-color/", views.CarColorCreate.as_view(), name="add_color"
|
"cars/<slug:slug>/add-color/", views.CarColorCreate.as_view(), name="add_color"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cars/<int:car_pk>/location/add/",
|
"cars/<slug:slug>/location/add/",
|
||||||
views.CarLocationCreateView.as_view(),
|
views.CarLocationCreateView.as_view(),
|
||||||
name="add_car_location",
|
name="add_car_location",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cars/<int:car_pk>/location/<int:pk>/update",
|
"cars/<slug:car_pk>/location/<int:pk>/update",
|
||||||
views.CarLocationUpdateView.as_view(),
|
views.CarLocationUpdateView.as_view(),
|
||||||
name="update_car_location",
|
name="update_car_location",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cars/<int:pk>/location/update/",
|
"cars/<slug:slug>/location/update/",
|
||||||
views.CarTransferCreateView.as_view(),
|
views.CarTransferCreateView.as_view(),
|
||||||
name="transfer",
|
name="transfer",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cars/<int:pk>/location/detail/",
|
"cars/<slug:slug>/location/detail/",
|
||||||
views.CarTransferDetailView.as_view(),
|
views.CarTransferDetailView.as_view(),
|
||||||
name="transfer_detail",
|
name="transfer_detail",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cars/<int:car_pk>/location/<int:transfer_pk>/transfer_approve/",
|
"cars/<slug:slug>/location/<int:transfer_pk>/transfer_approve/",
|
||||||
views.car_transfer_approve,
|
views.car_transfer_approve,
|
||||||
name="transfer_confirm",
|
name="transfer_confirm",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cars/<int:car_pk>/location/<int:transfer_pk>/transfer_accept_reject/",
|
"cars/<slug:slug>/location/<int:transfer_pk>/transfer_accept_reject/",
|
||||||
views.car_transfer_accept_reject,
|
views.car_transfer_accept_reject,
|
||||||
name="transfer_accept_reject",
|
name="transfer_accept_reject",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cars/<int:car_pk>/location/<int:transfer_pk>/preview/",
|
"cars/<slug:slug>/location/<int:transfer_pk>/preview/",
|
||||||
views.CarTransferPreviewView,
|
views.CarTransferPreviewView,
|
||||||
name="transfer_preview",
|
name="transfer_preview",
|
||||||
),
|
),
|
||||||
@ -308,7 +308,7 @@ path(
|
|||||||
views.SearchCodeView.as_view(),
|
views.SearchCodeView.as_view(),
|
||||||
name="car_search"),
|
name="car_search"),
|
||||||
# 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>/",
|
path("cars/reserve/<slug:slug>/",
|
||||||
views.reserve_car_view,
|
views.reserve_car_view,
|
||||||
name="reserve_car"),
|
name="reserve_car"),
|
||||||
path(
|
path(
|
||||||
@ -317,11 +317,11 @@ path(
|
|||||||
name="reservations",
|
name="reservations",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cars/<int:car_pk>/add-custom-card/",
|
"cars/<slug:slug>/add-custom-card/",
|
||||||
views.CustomCardCreateView.as_view(),
|
views.CustomCardCreateView.as_view(),
|
||||||
name="add_custom_card",
|
name="add_custom_card",
|
||||||
),
|
),
|
||||||
path('cars/<int:car_pk>/add-registration/',
|
path('cars/<slug:slug>/add-registration/',
|
||||||
views.CarRegistrationCreateView.as_view(),
|
views.CarRegistrationCreateView.as_view(),
|
||||||
name='add_registration'),
|
name='add_registration'),
|
||||||
|
|
||||||
|
|||||||
@ -213,7 +213,7 @@ def reserve_car(car, request):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
messages.error(request, f"Error reserving car: {e}")
|
messages.error(request, f"Error reserving car: {e}")
|
||||||
|
|
||||||
return redirect("car_detail", pk=car.pk)
|
return redirect("car_detail", slug=car.slug)
|
||||||
|
|
||||||
|
|
||||||
def calculate_vat_amount(amount):
|
def calculate_vat_amount(amount):
|
||||||
|
|||||||
@ -604,7 +604,7 @@ class CarCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def car_history(request, pk):
|
def car_history(request, slug):
|
||||||
"""
|
"""
|
||||||
Fetch and display the history of activities related to a specific car.
|
Fetch and display the history of activities related to a specific car.
|
||||||
|
|
||||||
@ -622,7 +622,7 @@ def car_history(request, pk):
|
|||||||
including the car and its associated activities as context data.
|
including the car and its associated activities as context data.
|
||||||
:rtype: HttpResponse
|
:rtype: HttpResponse
|
||||||
"""
|
"""
|
||||||
car = get_object_or_404(models.Car, pk=pk)
|
car = get_object_or_404(models.Car, slug=slug)
|
||||||
activities = models.Activity.objects.filter(
|
activities = models.Activity.objects.filter(
|
||||||
content_type__model="car", object_id=car.id
|
content_type__model="car", object_id=car.id
|
||||||
)
|
)
|
||||||
@ -859,7 +859,7 @@ class SearchCodeView(LoginRequiredMixin, View):
|
|||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
"success": True,
|
"success": True,
|
||||||
"code": code,
|
"code": code,
|
||||||
"redirect_url": reverse("car_detail", args=[car.pk])
|
"redirect_url": reverse("car_detail", args=[car.slug])
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -903,16 +903,16 @@ class CarInventory(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
|||||||
|
|
||||||
def get_queryset(self, *args, **kwargs):
|
def get_queryset(self, *args, **kwargs):
|
||||||
query = self.request.GET.get("q")
|
query = self.request.GET.get("q")
|
||||||
make_id = self.kwargs["make_id"]
|
make = models.CarMake.objects.get(slug=self.kwargs["make_id"])
|
||||||
model_id = self.kwargs["model_id"]
|
model = models.CarModel.objects.get(slug=self.kwargs["model_id"])
|
||||||
trim_id = self.kwargs["trim_id"]
|
trim = models.CarTrim.objects.get(slug=self.kwargs["trim_id"])
|
||||||
|
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
cars = models.Car.objects.filter(
|
cars = models.Car.objects.filter(
|
||||||
dealer=dealer,
|
dealer=dealer,
|
||||||
id_car_make=make_id,
|
id_car_make=make,
|
||||||
id_car_model=model_id,
|
id_car_model=model,
|
||||||
id_car_trim=trim_id,
|
id_car_trim=trim,
|
||||||
).order_by("receiving_date")
|
).order_by("receiving_date")
|
||||||
|
|
||||||
return apply_search_filters(cars, query)
|
return apply_search_filters(cars, query)
|
||||||
@ -951,16 +951,16 @@ class CarColorCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|||||||
permission_required = ["inventory.view_car"]
|
permission_required = ["inventory.view_car"]
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
car = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
form.instance.car = car
|
form.instance.car = car
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy("car_detail", kwargs={"pk": self.kwargs["car_pk"]})
|
return reverse_lazy("car_detail", kwargs={"slug": self.kwargs["slug"]})
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["car"] = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
context["car"] = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@ -1103,6 +1103,7 @@ def inventory_stats_view(request):
|
|||||||
if make.id_car_make not in inventory:
|
if make.id_car_make not in inventory:
|
||||||
inventory[make.id_car_make] = {
|
inventory[make.id_car_make] = {
|
||||||
"make_id": make.id_car_make,
|
"make_id": make.id_car_make,
|
||||||
|
"slug": make.slug,
|
||||||
"make_name": make.get_local_name(),
|
"make_name": make.get_local_name(),
|
||||||
"total_cars": 0,
|
"total_cars": 0,
|
||||||
"models": {},
|
"models": {},
|
||||||
@ -1113,6 +1114,7 @@ def inventory_stats_view(request):
|
|||||||
if model and model.id_car_model not in inventory[make.id_car_make]["models"]:
|
if model and model.id_car_model not in inventory[make.id_car_make]["models"]:
|
||||||
inventory[make.id_car_make]["models"][model.id_car_model] = {
|
inventory[make.id_car_make]["models"][model.id_car_model] = {
|
||||||
"model_id": model.id_car_model,
|
"model_id": model.id_car_model,
|
||||||
|
"slug": model.slug,
|
||||||
"model_name": model.get_local_name(),
|
"model_name": model.get_local_name(),
|
||||||
"total_cars": 0,
|
"total_cars": 0,
|
||||||
"trims": {},
|
"trims": {},
|
||||||
@ -1132,6 +1134,7 @@ def inventory_stats_view(request):
|
|||||||
trim.id_car_trim
|
trim.id_car_trim
|
||||||
] = {
|
] = {
|
||||||
"trim_id": trim.id_car_trim,
|
"trim_id": trim.id_car_trim,
|
||||||
|
"slug": trim.slug,
|
||||||
"trim_name": trim.name,
|
"trim_name": trim.name,
|
||||||
"total_cars": 0,
|
"total_cars": 0,
|
||||||
}
|
}
|
||||||
@ -1146,11 +1149,13 @@ def inventory_stats_view(request):
|
|||||||
"makes": [
|
"makes": [
|
||||||
{
|
{
|
||||||
"make_id": make_data["make_id"],
|
"make_id": make_data["make_id"],
|
||||||
|
"slug": make_data["slug"],
|
||||||
"make_name": make_data["make_name"],
|
"make_name": make_data["make_name"],
|
||||||
"total_cars": make_data["total_cars"],
|
"total_cars": make_data["total_cars"],
|
||||||
"models": [
|
"models": [
|
||||||
{
|
{
|
||||||
"model_id": model_data["model_id"],
|
"model_id": model_data["model_id"],
|
||||||
|
"slug": model_data["slug"],
|
||||||
"model_name": model_data["model_name"],
|
"model_name": model_data["model_name"],
|
||||||
"total_cars": model_data["total_cars"],
|
"total_cars": model_data["total_cars"],
|
||||||
"trims": list(model_data["trims"].values()),
|
"trims": list(model_data["trims"].values()),
|
||||||
@ -1161,7 +1166,7 @@ def inventory_stats_view(request):
|
|||||||
for make_data in inventory.values()
|
for make_data in inventory.values()
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
print(result['makes'])
|
||||||
return render(request, "inventory/inventory_stats.html", {"inventory": result})
|
return render(request, "inventory/inventory_stats.html", {"inventory": result})
|
||||||
|
|
||||||
|
|
||||||
@ -1218,7 +1223,7 @@ class CarFinanceCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVi
|
|||||||
permission_required = ["inventory.add_carfinance"]
|
permission_required = ["inventory.add_carfinance"]
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
self.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
self.car = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
@ -1227,7 +1232,7 @@ class CarFinanceCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVi
|
|||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse("car_detail", kwargs={"pk": self.car.pk})
|
return reverse("car_detail", kwargs={"slug": self.car.slug})
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
@ -1273,7 +1278,7 @@ class CarFinanceUpdateView(
|
|||||||
permission_required = ["inventory.change_carfinance"]
|
permission_required = ["inventory.change_carfinance"]
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse("car_detail", kwargs={"pk": self.object.car.pk})
|
return reverse("car_detail", kwargs={"slug": self.object.car.slug})
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
@ -1325,7 +1330,7 @@ class CarUpdateView(
|
|||||||
permission_required = ["inventory.change_car"]
|
permission_required = ["inventory.change_car"]
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse("car_detail", kwargs={"pk": self.object.pk})
|
return reverse("car_detail", kwargs={"slug": self.object.slug})
|
||||||
|
|
||||||
def get_form(self, form_class=None):
|
def get_form(self, form_class=None):
|
||||||
form = super().get_form(form_class)
|
form = super().get_form(form_class)
|
||||||
@ -1388,10 +1393,10 @@ class CarLocationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateV
|
|||||||
permission_required = ["inventory.add_carlocation"]
|
permission_required = ["inventory.add_carlocation"]
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy("car_detail", kwargs={"pk": self.object.car.pk})
|
return reverse_lazy("car_detail", kwargs={"slug": self.object.car.slug})
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
form.instance.car = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
form.instance.owner = dealer
|
form.instance.owner = dealer
|
||||||
form.save()
|
form.save()
|
||||||
@ -1425,11 +1430,11 @@ class CarLocationUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateV
|
|||||||
|
|
||||||
# def get_initial(self):
|
# def get_initial(self):
|
||||||
# initial = super().get_initial()
|
# initial = super().get_initial()
|
||||||
# initial["car"] = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
# initial["car"] = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
# return initial
|
# return initial
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
form.instance.car = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
form.instance.owner = dealer
|
form.instance.owner = dealer
|
||||||
form.save()
|
form.save()
|
||||||
@ -1437,7 +1442,7 @@ class CarLocationUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateV
|
|||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy("car_detail", kwargs={"pk": self.object.car.pk})
|
return reverse_lazy("car_detail", kwargs={"slug": self.object.car.slug})
|
||||||
|
|
||||||
|
|
||||||
class CarTransferCreateView(LoginRequiredMixin, CreateView):
|
class CarTransferCreateView(LoginRequiredMixin, CreateView):
|
||||||
@ -1465,12 +1470,12 @@ class CarTransferCreateView(LoginRequiredMixin, CreateView):
|
|||||||
form.fields["to_dealer"].queryset = models.Dealer.objects.exclude(
|
form.fields["to_dealer"].queryset = models.Dealer.objects.exclude(
|
||||||
pk=get_user_type(self.request).pk
|
pk=get_user_type(self.request).pk
|
||||||
).all()
|
).all()
|
||||||
form.fields["car"].queryset = models.Car.objects.filter(pk=self.kwargs["pk"])
|
form.fields["car"].queryset = models.Car.objects.filter(slug=self.kwargs["slug"])
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
initial = super().get_initial()
|
initial = super().get_initial()
|
||||||
initial["car"] = get_object_or_404(models.Car, pk=self.kwargs["pk"])
|
initial["car"] = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
@ -1480,7 +1485,7 @@ class CarTransferCreateView(LoginRequiredMixin, CreateView):
|
|||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy("car_detail", kwargs={"pk": self.object.car.pk})
|
return reverse_lazy("car_detail", kwargs={"slug": self.object.car.slug})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1516,7 +1521,7 @@ class CarTransferDetailView(LoginRequiredMixin, SuccessMessageMixin, DetailView)
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def car_transfer_approve(request, car_pk, transfer_pk):
|
def car_transfer_approve(request, slug, transfer_pk):
|
||||||
"""
|
"""
|
||||||
Approves or cancels a car transfer request based on the action parameter. This view
|
Approves or cancels a car transfer request based on the action parameter. This view
|
||||||
handles the workflow of updating the transfer status and notifying the involved parties
|
handles the workflow of updating the transfer status and notifying the involved parties
|
||||||
@ -1529,7 +1534,7 @@ def car_transfer_approve(request, car_pk, transfer_pk):
|
|||||||
:param transfer_pk: Primary key of the transfer request to approve or cancel.
|
:param transfer_pk: Primary key of the transfer request to approve or cancel.
|
||||||
:return: An HTTP response redirecting to the car detail page of the specified car.
|
:return: An HTTP response redirecting to the car detail page of the specified car.
|
||||||
"""
|
"""
|
||||||
car = get_object_or_404(models.Car, pk=car_pk)
|
car = get_object_or_404(models.Car, slug=slug)
|
||||||
transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk)
|
transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk)
|
||||||
action = request.GET.get("action")
|
action = request.GET.get("action")
|
||||||
if action == "cancel":
|
if action == "cancel":
|
||||||
@ -1543,12 +1548,12 @@ def car_transfer_approve(request, car_pk, transfer_pk):
|
|||||||
user=transfer.from_dealer.user,
|
user=transfer.from_dealer.user,
|
||||||
message=f"Car transfer request from {transfer.to_dealer} is canceled.",
|
message=f"Car transfer request from {transfer.to_dealer} is canceled.",
|
||||||
)
|
)
|
||||||
return redirect("car_detail", pk=car.pk)
|
return redirect("car_detail", slug=car.slug)
|
||||||
transfer.status = "approved"
|
transfer.status = "approved"
|
||||||
transfer.save()
|
transfer.save()
|
||||||
url = request.build_absolute_uri(
|
url = request.build_absolute_uri(
|
||||||
reverse(
|
reverse(
|
||||||
"transfer_preview", kwargs={"car_pk": car.pk, "transfer_pk": transfer.pk}
|
"transfer_preview", kwargs={"slug": car.slug, "transfer_pk": transfer.pk}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
models.Notification.objects.create(
|
models.Notification.objects.create(
|
||||||
@ -1556,11 +1561,11 @@ def car_transfer_approve(request, car_pk, transfer_pk):
|
|||||||
message=f"Car transfer request from {transfer.from_dealer} is waiting for your acceptance. <a href='{url}'> Accept</a>",
|
message=f"Car transfer request from {transfer.from_dealer} is waiting for your acceptance. <a href='{url}'> Accept</a>",
|
||||||
)
|
)
|
||||||
messages.success(request, _("Car transfer approved successfully"))
|
messages.success(request, _("Car transfer approved successfully"))
|
||||||
return redirect("car_detail", pk=car.pk)
|
return redirect("car_detail", slug=car.slug)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def car_transfer_accept_reject(request, car_pk, transfer_pk):
|
def car_transfer_accept_reject(request, slug, transfer_pk):
|
||||||
"""
|
"""
|
||||||
Handles the acceptance or rejection of a car transfer request. Based on the
|
Handles the acceptance or rejection of a car transfer request. Based on the
|
||||||
`status` parameter obtained from the query string, the function updates the
|
`status` parameter obtained from the query string, the function updates the
|
||||||
@ -1574,7 +1579,7 @@ def car_transfer_accept_reject(request, car_pk, transfer_pk):
|
|||||||
:param transfer_pk: The primary key of the car transfer request to be processed.
|
:param transfer_pk: The primary key of the car transfer request to be processed.
|
||||||
:return: An HTTP redirect response to the 'inventory_stats' view.
|
:return: An HTTP redirect response to the 'inventory_stats' view.
|
||||||
"""
|
"""
|
||||||
car = get_object_or_404(models.Car, pk=car_pk)
|
car = get_object_or_404(models.Car, slug=slug)
|
||||||
transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk)
|
transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk)
|
||||||
status = request.GET.get("status")
|
status = request.GET.get("status")
|
||||||
if status == "rejected":
|
if status == "rejected":
|
||||||
@ -1606,7 +1611,7 @@ def car_transfer_accept_reject(request, car_pk, transfer_pk):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def CarTransferPreviewView(request, car_pk, transfer_pk):
|
def CarTransferPreviewView(request, slug, transfer_pk):
|
||||||
"""
|
"""
|
||||||
Handles the preview of car transfer details and ensures that a user has appropriate
|
Handles the preview of car transfer details and ensures that a user has appropriate
|
||||||
permissions to view the transfer based on their associated dealer.
|
permissions to view the transfer based on their associated dealer.
|
||||||
@ -1627,7 +1632,7 @@ def CarTransferPreviewView(request, car_pk, transfer_pk):
|
|||||||
"""
|
"""
|
||||||
transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk)
|
transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk)
|
||||||
if transfer.to_dealer != get_user_type(request):
|
if transfer.to_dealer != get_user_type(request):
|
||||||
return redirect("car_detail", pk=car_pk)
|
return redirect("car_detail", slug=slug)
|
||||||
return render(request, "inventory/transfer_preview.html", {"transfer": transfer})
|
return render(request, "inventory/transfer_preview.html", {"transfer": transfer})
|
||||||
|
|
||||||
|
|
||||||
@ -1651,18 +1656,18 @@ class CustomCardCreateView(LoginRequiredMixin, CreateView):
|
|||||||
template_name = "inventory/add_custom_card.html"
|
template_name = "inventory/add_custom_card.html"
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
car = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
form.instance.car = car
|
form.instance.car = car
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["car"] = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
context["car"] = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
messages.success(self.request, _("Custom Card added successfully"))
|
messages.success(self.request, _("Custom Card added successfully"))
|
||||||
return reverse_lazy("car_detail", kwargs={"pk": self.kwargs["car_pk"]})
|
return reverse_lazy("car_detail", kwargs={"slug": self.kwargs["slug"]})
|
||||||
|
|
||||||
|
|
||||||
class CarRegistrationCreateView(LoginRequiredMixin, CreateView):
|
class CarRegistrationCreateView(LoginRequiredMixin, CreateView):
|
||||||
@ -1692,22 +1697,22 @@ class CarRegistrationCreateView(LoginRequiredMixin, CreateView):
|
|||||||
template_name = "inventory/car_registration_form.html"
|
template_name = "inventory/car_registration_form.html"
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
car = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
form.instance.car = car
|
form.instance.car = car
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["car"] = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
context["car"] = get_object_or_404(models.Car, slug=self.kwargs["slug"])
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
messages.success(self.request, _("Registration added successfully"))
|
messages.success(self.request, _("Registration added successfully"))
|
||||||
return reverse_lazy("car_detail", kwargs={"pk": self.kwargs["car_pk"]})
|
return reverse_lazy("car_detail", kwargs={"slug": self.kwargs["slug"]})
|
||||||
|
|
||||||
|
|
||||||
@login_required()
|
@login_required()
|
||||||
def reserve_car_view(request, car_id):
|
def reserve_car_view(request, slug):
|
||||||
"""
|
"""
|
||||||
Handles car reservation requests. This view requires the user to be logged in
|
Handles car reservation requests. This view requires the user to be logged in
|
||||||
and processes only POST requests. When invoked, it checks if the specified car
|
and processes only POST requests. When invoked, it checks if the specified car
|
||||||
@ -1723,10 +1728,10 @@ def reserve_car_view(request, car_id):
|
|||||||
:rtype: HttpResponse or JsonResponse
|
:rtype: HttpResponse or JsonResponse
|
||||||
"""
|
"""
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
car = get_object_or_404(models.Car, pk=car_id)
|
car = get_object_or_404(models.Car, slug=slug)
|
||||||
if car.is_reserved():
|
if car.is_reserved():
|
||||||
messages.error(request, _("This car is already reserved"))
|
messages.error(request, _("This car is already reserved"))
|
||||||
return redirect("car_detail", pk=car.pk)
|
return redirect("car_detail", slug=car.slug)
|
||||||
response = reserve_car(car, request)
|
response = reserve_car(car, request)
|
||||||
return response
|
return response
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
@ -1764,7 +1769,7 @@ def manage_reservation(request, reservation_id):
|
|||||||
reservation.reserved_until = timezone.now() + timezone.timedelta(hours=24)
|
reservation.reserved_until = timezone.now() + timezone.timedelta(hours=24)
|
||||||
reservation.save()
|
reservation.save()
|
||||||
messages.success(request, _("Reservation renewed successfully"))
|
messages.success(request, _("Reservation renewed successfully"))
|
||||||
return redirect("car_detail", pk=reservation.car.pk)
|
return redirect("car_detail", slug=reservation.car.slug)
|
||||||
|
|
||||||
elif action == "cancel":
|
elif action == "cancel":
|
||||||
car = reservation.car
|
car = reservation.car
|
||||||
@ -1772,7 +1777,7 @@ def manage_reservation(request, reservation_id):
|
|||||||
car.status = models.CarStatusChoices.AVAILABLE
|
car.status = models.CarStatusChoices.AVAILABLE
|
||||||
car.save()
|
car.save()
|
||||||
messages.success(request, _("Reservation canceled successfully"))
|
messages.success(request, _("Reservation canceled successfully"))
|
||||||
return redirect("car_detail", pk=reservation.car.pk)
|
return redirect("car_detail", slug=reservation.car.slug)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
@ -1857,7 +1862,7 @@ class DealerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|||||||
success_message = _("Dealer updated successfully")
|
success_message = _("Dealer updated successfully")
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse("dealer_detail", kwargs={"pk": self.object.pk})
|
return reverse("dealer_detail", kwargs={"slug": self.object.slug})
|
||||||
|
|
||||||
|
|
||||||
class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||||
@ -2148,7 +2153,7 @@ class VendorListView(LoginRequiredMixin, ListView):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def vendorDetailView(request, pk):
|
def vendorDetailView(request, slug):
|
||||||
"""
|
"""
|
||||||
Fetches and renders the detail view for a specific vendor.
|
Fetches and renders the detail view for a specific vendor.
|
||||||
|
|
||||||
@ -2163,7 +2168,7 @@ def vendorDetailView(request, pk):
|
|||||||
:return: An HttpResponse object containing the rendered vendor detail page.
|
:return: An HttpResponse object containing the rendered vendor detail page.
|
||||||
:rtype: HttpResponse
|
:rtype: HttpResponse
|
||||||
"""
|
"""
|
||||||
vendor = get_object_or_404(models.Vendor, pk=pk)
|
vendor = get_object_or_404(models.Vendor, slug=slug)
|
||||||
return render(
|
return render(
|
||||||
request, template_name="vendors/view_vendor.html", context={"vendor": vendor}
|
request, template_name="vendors/view_vendor.html", context={"vendor": vendor}
|
||||||
)
|
)
|
||||||
@ -2258,7 +2263,7 @@ class VendorUpdateView(
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_vendor(request, pk):
|
def delete_vendor(request, slug):
|
||||||
"""
|
"""
|
||||||
Deletes an existing vendor record from the database.
|
Deletes an existing vendor record from the database.
|
||||||
|
|
||||||
@ -2273,7 +2278,7 @@ def delete_vendor(request, pk):
|
|||||||
:return: HttpResponseRedirect object for redirecting to the vendor list page.
|
:return: HttpResponseRedirect object for redirecting to the vendor list page.
|
||||||
:rtype: HttpResponseRedirect
|
:rtype: HttpResponseRedirect
|
||||||
"""
|
"""
|
||||||
vendor = get_object_or_404(models.Vendor, pk=pk)
|
vendor = get_object_or_404(models.Vendor, slug=slug)
|
||||||
vendor.active = False
|
vendor.active = False
|
||||||
vendor.vendor_model.active = False
|
vendor.vendor_model.active = False
|
||||||
vendor.save()
|
vendor.save()
|
||||||
@ -2367,7 +2372,7 @@ class GroupCreateView(
|
|||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
instance = form.save(commit=False)
|
instance = form.save(commit=False)
|
||||||
group = Group.objects.create(name=f"{dealer.pk}_{instance.name}")
|
group = Group.objects.create(name=f"{dealer.slug}_{instance.name}")
|
||||||
instance.dealer = dealer
|
instance.dealer = dealer
|
||||||
instance.group = group
|
instance.group = group
|
||||||
instance.save()
|
instance.save()
|
||||||
@ -2410,7 +2415,7 @@ class GroupUpdateView(
|
|||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
instance = form.save(commit=False)
|
instance = form.save(commit=False)
|
||||||
instance.set_defualt_permissions()
|
instance.set_defualt_permissions()
|
||||||
instance.group.name = f"{dealer.pk}_{instance.name}"
|
instance.group.name = f"{dealer.slug}_{instance.name}"
|
||||||
instance.save()
|
instance.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
@ -4812,7 +4817,7 @@ def add_note_to_lead(request, pk):
|
|||||||
note.created_by = request.user
|
note.created_by = request.user
|
||||||
note.save()
|
note.save()
|
||||||
messages.success(request, _("Note added successfully"))
|
messages.success(request, _("Note added successfully"))
|
||||||
return redirect("lead_detail", pk=lead.pk)
|
return redirect("lead_detail", slug=lead.slug)
|
||||||
else:
|
else:
|
||||||
form = forms.NoteForm()
|
form = forms.NoteForm()
|
||||||
return render(request, "crm/note_form.html", {"form": form, "lead": lead})
|
return render(request, "crm/note_form.html", {"form": form, "lead": lead})
|
||||||
@ -4865,6 +4870,7 @@ def update_note(request, pk):
|
|||||||
"""
|
"""
|
||||||
note = get_object_or_404(models.Notes, pk=pk, created_by=request.user)
|
note = get_object_or_404(models.Notes, pk=pk, created_by=request.user)
|
||||||
lead_pk = note.content_object.pk
|
lead_pk = note.content_object.pk
|
||||||
|
lead = models.Lead.objects.get(pk=lead_pk)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = forms.NoteForm(request.POST, instance=note)
|
form = forms.NoteForm(request.POST, instance=note)
|
||||||
@ -4874,7 +4880,7 @@ def update_note(request, pk):
|
|||||||
updated_note.created_by = request.user
|
updated_note.created_by = request.user
|
||||||
updated_note.save()
|
updated_note.save()
|
||||||
messages.success(request, _("Note updated successfully"))
|
messages.success(request, _("Note updated successfully"))
|
||||||
return redirect("lead_detail", pk=lead_pk)
|
return redirect("lead_detail", slug=lead.slug)
|
||||||
else:
|
else:
|
||||||
form = forms.NoteForm(instance=note)
|
form = forms.NoteForm(instance=note)
|
||||||
|
|
||||||
@ -5035,7 +5041,7 @@ def lead_transfer(request, pk):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def send_lead_email(request, pk, email_pk=None):
|
def send_lead_email(request, slug, email_pk=None):
|
||||||
"""
|
"""
|
||||||
Handles sending emails related to a lead. Supports creating drafts, sending emails, and rendering
|
Handles sending emails related to a lead. Supports creating drafts, sending emails, and rendering
|
||||||
an email composition page. Changes on the lead or email objects, such as marking a lead as contacted
|
an email composition page. Changes on the lead or email objects, such as marking a lead as contacted
|
||||||
@ -5055,15 +5061,15 @@ def send_lead_email(request, pk, email_pk=None):
|
|||||||
or email composition rendering, a response object is returned to render the respective page.
|
or email composition rendering, a response object is returned to render the respective page.
|
||||||
Type: HttpResponse
|
Type: HttpResponse
|
||||||
"""
|
"""
|
||||||
lead = get_object_or_404(models.Lead, pk=pk)
|
lead = get_object_or_404(models.Lead, slug=slug)
|
||||||
status = request.GET.get("status")
|
status = request.GET.get("status")
|
||||||
dealer = get_user_type(request)
|
dealer = get_user_type(request)
|
||||||
if status == 'draft':
|
if status == 'draft':
|
||||||
models.Email.objects.create(content_object=lead, created_by=request.user,from_email="manager@tenhal.com", to_email=request.GET.get("to"), subject=request.GET.get("subject"), message=request.GET.get("message"),status=models.EmailStatus.DRAFT)
|
models.Email.objects.create(content_object=lead, created_by=request.user,from_email="manager@tenhal.com", to_email=request.GET.get("to"), subject=request.GET.get("subject"), message=request.GET.get("message"),status=models.EmailStatus.DRAFT)
|
||||||
models.Activity.objects.create(dealer=dealer,content_object=lead, notes="Email Draft",created_by=request.user,activity_type=models.ActionChoices.EMAIL)
|
models.Activity.objects.create(dealer=dealer,content_object=lead, notes="Email Draft",created_by=request.user,activity_type=models.ActionChoices.EMAIL)
|
||||||
messages.success(request, _("Email Draft successfully"))
|
messages.success(request, _("Email Draft successfully"))
|
||||||
response = HttpResponse(redirect("lead_detail", pk=lead.pk))
|
response = HttpResponse(redirect("lead_detail", slug=lead.slug))
|
||||||
response["HX-Redirect"] = reverse("lead_detail", args=[lead.pk])
|
response["HX-Redirect"] = reverse("lead_detail", args=[lead.slug])
|
||||||
return response
|
return response
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
@ -7028,7 +7034,7 @@ class CarListViewTable(LoginRequiredMixin, ExportMixin, SingleTableView):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def DealerSettingsView(request, pk):
|
def DealerSettingsView(request, slug):
|
||||||
"""
|
"""
|
||||||
Handles dealer settings view where dealers can update their financial and
|
Handles dealer settings view where dealers can update their financial and
|
||||||
payment account settings. This view ensures validation and reassigns form
|
payment account settings. This view ensures validation and reassigns form
|
||||||
@ -7045,7 +7051,7 @@ def DealerSettingsView(request, pk):
|
|||||||
to the dealer detail view after successful form submission.
|
to the dealer detail view after successful form submission.
|
||||||
:rtype: HttpResponse
|
:rtype: HttpResponse
|
||||||
"""
|
"""
|
||||||
dealer_setting = get_object_or_404(models.DealerSettings, dealer__pk=pk)
|
dealer_setting = get_object_or_404(models.DealerSettings, dealer__slug=slug)
|
||||||
dealer = get_user_type(request)
|
dealer = get_user_type(request)
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = forms.DealerSettingsForm(request.POST, instance=dealer_setting)
|
form = forms.DealerSettingsForm(request.POST, instance=dealer_setting)
|
||||||
@ -7054,7 +7060,7 @@ def DealerSettingsView(request, pk):
|
|||||||
instance.dealer = dealer
|
instance.dealer = dealer
|
||||||
instance.save()
|
instance.save()
|
||||||
messages.success(request, _('Settings updated'))
|
messages.success(request, _('Settings updated'))
|
||||||
return redirect('dealer_detail', pk=dealer.pk)
|
return redirect('dealer_detail', slug=dealer.slug)
|
||||||
else:
|
else:
|
||||||
print(form.errors)
|
print(form.errors)
|
||||||
form = forms.DealerSettingsForm(instance=dealer_setting, initial={"dealer": dealer})
|
form = forms.DealerSettingsForm(instance=dealer_setting, initial={"dealer": dealer})
|
||||||
@ -7134,7 +7140,7 @@ def assign_car_makes(request):
|
|||||||
makes = form.cleaned_data["car_makes"]
|
makes = form.cleaned_data["car_makes"]
|
||||||
create_accounts_for_make(dealer, makes)
|
create_accounts_for_make(dealer, makes)
|
||||||
form.save()
|
form.save()
|
||||||
return redirect("dealer_detail", pk=dealer.pk)
|
return redirect("dealer_detail", slug=dealer.slug)
|
||||||
else:
|
else:
|
||||||
print(form.errors)
|
print(form.errors)
|
||||||
else:
|
else:
|
||||||
@ -7743,13 +7749,13 @@ def notifications_history(request):
|
|||||||
# )
|
# )
|
||||||
# return render(request, 'activity_history.html')
|
# return render(request, 'activity_history.html')
|
||||||
|
|
||||||
def add_activity(request,content_type,pk):
|
def add_activity(request,content_type,slug):
|
||||||
try:
|
try:
|
||||||
model = apps.get_model(f'inventory.{content_type}')
|
model = apps.get_model(f'inventory.{content_type}')
|
||||||
except LookupError:
|
except LookupError:
|
||||||
raise Http404("Model not found")
|
raise Http404("Model not found")
|
||||||
|
|
||||||
obj = get_object_or_404(model, pk=pk)
|
obj = get_object_or_404(model, slug=slug)
|
||||||
dealer = get_user_type(request)
|
dealer = get_user_type(request)
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = forms.ActivityForm(request.POST)
|
form = forms.ActivityForm(request.POST)
|
||||||
@ -7765,14 +7771,14 @@ def add_activity(request,content_type,pk):
|
|||||||
messages.success(request, _("Activity added successfully"))
|
messages.success(request, _("Activity added successfully"))
|
||||||
else:
|
else:
|
||||||
messages.error(request, _("Activity form is not valid"))
|
messages.error(request, _("Activity form is not valid"))
|
||||||
return redirect(f"{content_type}_detail", pk=pk)
|
return redirect(f"{content_type}_detail", slug=slug)
|
||||||
def add_task(request,content_type,pk):
|
def add_task(request,content_type,slug):
|
||||||
try:
|
try:
|
||||||
model = apps.get_model(f'inventory.{content_type}')
|
model = apps.get_model(f'inventory.{content_type}')
|
||||||
except LookupError:
|
except LookupError:
|
||||||
raise Http404("Model not found")
|
raise Http404("Model not found")
|
||||||
|
|
||||||
obj = get_object_or_404(model, pk=pk)
|
obj = get_object_or_404(model, slug=slug)
|
||||||
dealer = get_user_type(request)
|
dealer = get_user_type(request)
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = forms.StaffTaskForm(request.POST)
|
form = forms.StaffTaskForm(request.POST)
|
||||||
@ -7788,16 +7794,22 @@ def add_task(request,content_type,pk):
|
|||||||
else:
|
else:
|
||||||
print(form.errors)
|
print(form.errors)
|
||||||
messages.error(request, _("Task form is not valid"))
|
messages.error(request, _("Task form is not valid"))
|
||||||
return redirect(f"{content_type}_detail", pk=pk)
|
return redirect(f"{content_type}_detail", slug=slug)
|
||||||
|
|
||||||
def update_task(request,pk):
|
def update_task(request,pk):
|
||||||
task = get_object_or_404(models.Tasks, pk=pk)
|
task = get_object_or_404(models.Tasks, pk=pk)
|
||||||
|
lead = get_object_or_404(models.Lead, pk=task.content_object.id)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
task.completed = False if task.completed else True
|
task.completed = False if task.completed else True
|
||||||
task.save()
|
task.save()
|
||||||
messages.success(request, _("Task updated successfully"))
|
messages.success(request, _("Task updated successfully"))
|
||||||
else:
|
else:
|
||||||
messages.error(request, _("Task form is not valid"))
|
messages.error(request, _("Task form is not valid"))
|
||||||
response = HttpResponse()
|
# response = HttpResponse()
|
||||||
response['HX-Refresh'] = 'true'
|
# response['HX-Refresh'] = 'true'
|
||||||
return response
|
# return response
|
||||||
|
tasks = models.Tasks.objects.filter(
|
||||||
|
content_type__model="lead", object_id=lead.id
|
||||||
|
)
|
||||||
|
return render(request,'crm/leads/lead_detail.html',{'lead':lead,'tasks':tasks})
|
||||||
@ -2,22 +2,22 @@
|
|||||||
echo "Loading initial data"
|
echo "Loading initial data"
|
||||||
|
|
||||||
echo "Loading carmake"
|
echo "Loading carmake"
|
||||||
python3 manage.py loaddata --app carmake carmake_backup.json
|
python3 manage.py loaddata --app carmake carmake_backup_output.json
|
||||||
|
|
||||||
echo "Loading carmodel"
|
echo "Loading carmodel"
|
||||||
python3 manage.py loaddata --app carmodel carmodel_backup.json
|
python3 manage.py loaddata --app carmodel carmodel_backup_output.json
|
||||||
|
|
||||||
echo "Loading carserie"
|
echo "Loading carserie"
|
||||||
python3 manage.py loaddata --app carserie carserie_backup.json
|
python3 manage.py loaddata --app carserie carserie_backup_output.json
|
||||||
|
|
||||||
echo "Loading cartrim"
|
echo "Loading cartrim"
|
||||||
python3 manage.py loaddata --app cartrim cartrim_backup.json
|
python3 manage.py loaddata --app cartrim cartrim_backup_output.json
|
||||||
|
|
||||||
echo "Loading caroption"
|
echo "Loading caroption"
|
||||||
python3 manage.py loaddata --app caroption caroption_backup.json
|
python3 manage.py loaddata --app caroption caroption_backup_output.json
|
||||||
|
|
||||||
echo "Loading carequipment"
|
echo "Loading carequipment"
|
||||||
python3 manage.py loaddata --app carequipment carequipment_backup.json
|
python3 manage.py loaddata --app carequipment carequipment_backup_output.json
|
||||||
|
|
||||||
echo "Populating colors"
|
echo "Populating colors"
|
||||||
python3 manage.py populate_colors
|
python3 manage.py populate_colors
|
||||||
@ -26,4 +26,6 @@ python3 manage.py tenhal_plan
|
|||||||
|
|
||||||
python3 manage.py set_vat
|
python3 manage.py set_vat
|
||||||
|
|
||||||
|
python3 manage.py initial_services_offered
|
||||||
|
|
||||||
echo "Done"
|
echo "Done"
|
||||||
56
slug_data.py
Normal file
56
slug_data.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import json
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
from slugify import slugify
|
||||||
|
|
||||||
|
def process_json_file(input_file, output_file=None, batch_size=10000):
|
||||||
|
"""Add slugs to JSON data file with optimal performance"""
|
||||||
|
if output_file is None:
|
||||||
|
output_file = f"{Path(input_file).stem}_with_slugs.json"
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
with open(input_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
total = len(data)
|
||||||
|
processed = 0
|
||||||
|
|
||||||
|
print(f"Processing {total} records...")
|
||||||
|
|
||||||
|
for item in data:
|
||||||
|
# Generate slug from name field
|
||||||
|
name = item['fields'].get('name', '')
|
||||||
|
pk = item['pk']
|
||||||
|
|
||||||
|
if name:
|
||||||
|
slug = slugify(name)[:50] # Truncate to 50 chars
|
||||||
|
# Append PK to ensure uniqueness
|
||||||
|
item['fields']['slug'] = f"{slug}"
|
||||||
|
else:
|
||||||
|
# Fallback to model-pk if name is empty
|
||||||
|
model_name = item['model'].split('.')[-1]
|
||||||
|
item['fields']['slug'] = f"{model_name}-{pk}"
|
||||||
|
|
||||||
|
processed += 1
|
||||||
|
if processed % batch_size == 0:
|
||||||
|
print(f"Processed {processed}/{total} records...")
|
||||||
|
|
||||||
|
# Save the modified data
|
||||||
|
with open(output_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
print(f"Completed in {time.time() - start_time:.2f} seconds")
|
||||||
|
print(f"Output saved to {output_file}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('input_file', help='Path to input JSON file')
|
||||||
|
parser.add_argument('-o', '--output', help='Output file path')
|
||||||
|
parser.add_argument('-b', '--batch', type=int, default=10000,
|
||||||
|
help='Progress reporting batch size')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
process_json_file(args.input_file, args.output, args.batch)
|
||||||
BIN
static/images/customers/image_UGmtPMg.png
Normal file
BIN
static/images/customers/image_UGmtPMg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
BIN
static/images/logos/vendors/logo-for-the-word-daju-48a980_Bw4t8ED.jpg
vendored
Normal file
BIN
static/images/logos/vendors/logo-for-the-word-daju-48a980_Bw4t8ED.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
static/images/logos/vendors/logo-for-the-word-daju-48a980_CfNtYr7.jpg
vendored
Normal file
BIN
static/images/logos/vendors/logo-for-the-word-daju-48a980_CfNtYr7.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@ -50,3 +50,15 @@
|
|||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-select select {
|
||||||
|
padding: 0 16px 0 48px;
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select:after {
|
||||||
|
left: 16px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
|||||||
@ -3780,8 +3780,9 @@ textarea.form-control-lg {
|
|||||||
|
|
||||||
.form-select {
|
.form-select {
|
||||||
--phoenix-form-select-bg-img: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDE1MCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03NS4zNDggMTI3LjE5MkM3Mi40MzgxIDEyNy4xOTIgNjkuODUxNCAxMjYuMjIyIDY3LjkxMTUgMTI0LjI4Mkw1LjgzMjE1IDYyLjIwMjNDMS42Mjg4NyA1OC4zMjIzIDEuNjI4ODcgNTEuNTMyNCA1LjgzMjE1IDQ3LjY1MjVDOS43MTIxMSA0My40NDkyIDE2LjUwMiA0My40NDkyIDIwLjM4MiA0Ny42NTI1TDc1LjM0OCAxMDIuMjk1TDEyOS45OTEgNDcuNjUyNUMxMzMuODcxIDQzLjQ0OTIgMTQwLjY2MSA0My40NDkyIDE0NC41NDEgNDcuNjUyNUMxNDguNzQ0IDUxLjUzMjQgMTQ4Ljc0NCA1OC4zMjIzIDE0NC41NDEgNjIuMjAyM0w4Mi40NjEzIDEyNC4yODJDODAuNTIxMyAxMjYuMjIyIDc3LjkzNDcgMTI3LjE5MiA3NS4zNDggMTI3LjE5MloiIGZpbGw9IiMzMTM3NEEiLz4KPC9zdmc+Cg==");
|
--phoenix-form-select-bg-img: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDE1MCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03NS4zNDggMTI3LjE5MkM3Mi40MzgxIDEyNy4xOTIgNjkuODUxNCAxMjYuMjIyIDY3LjkxMTUgMTI0LjI4Mkw1LjgzMjE1IDYyLjIwMjNDMS42Mjg4NyA1OC4zMjIzIDEuNjI4ODcgNTEuNTMyNCA1LjgzMjE1IDQ3LjY1MjVDOS43MTIxMSA0My40NDkyIDE2LjUwMiA0My40NDkyIDIwLjM4MiA0Ny42NTI1TDc1LjM0OCAxMDIuMjk1TDEyOS45OTEgNDcuNjUyNUMxMzMuODcxIDQzLjQ0OTIgMTQwLjY2MSA0My40NDkyIDE0NC41NDEgNDcuNjUyNUMxNDguNzQ0IDUxLjUzMjQgMTQ4Ljc0NCA1OC4zMjIzIDE0NC41NDEgNjIuMjAyM0w4Mi40NjEzIDEyNC4yODJDODAuNTIxMyAxMjYuMjIyIDc3LjkzNDcgMTI3LjE5MiA3NS4zNDggMTI3LjE5MloiIGZpbGw9IiMzMTM3NEEiLz4KPC9zdmc+Cg==");
|
||||||
|
align-content: start;
|
||||||
display: block;
|
display: block;
|
||||||
text-align: start;
|
text-align: right;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.5rem 1rem 0.5rem 2.5rem;
|
padding: 0.5rem 1rem 0.5rem 2.5rem;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
@ -3808,9 +3809,12 @@ textarea.form-control-lg {
|
|||||||
}
|
}
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.form-select {
|
.form-select {
|
||||||
-webkit-transition: none;
|
-webkit-transition:right;
|
||||||
-o-transition: none;
|
-o-transition: none;
|
||||||
transition: none;
|
transition: none;
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
direction: rtl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.form-select:focus {
|
.form-select:focus {
|
||||||
|
|||||||
@ -1138,6 +1138,9 @@ select {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
|
padding: 0 16px 0 48px;
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
}
|
}
|
||||||
select:disabled {
|
select:disabled {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -3784,8 +3787,9 @@ textarea.form-control-lg {
|
|||||||
.form-select {
|
.form-select {
|
||||||
--phoenix-form-select-bg-img: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDE1MCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03NS4zNDggMTI3LjE5MkM3Mi40MzgxIDEyNy4xOTIgNjkuODUxNCAxMjYuMjIyIDY3LjkxMTUgMTI0LjI4Mkw1LjgzMjE1IDYyLjIwMjNDMS42Mjg4NyA1OC4zMjIzIDEuNjI4ODcgNTEuNTMyNCA1LjgzMjE1IDQ3LjY1MjVDOS43MTIxMSA0My40NDkyIDE2LjUwMiA0My40NDkyIDIwLjM4MiA0Ny42NTI1TDc1LjM0OCAxMDIuMjk1TDEyOS45OTEgNDcuNjUyNUMxMzMuODcxIDQzLjQ0OTIgMTQwLjY2MSA0My40NDkyIDE0NC41NDEgNDcuNjUyNUMxNDguNzQ0IDUxLjUzMjQgMTQ4Ljc0NCA1OC4zMjIzIDE0NC41NDEgNjIuMjAyM0w4Mi40NjEzIDEyNC4yODJDODAuNTIxMyAxMjYuMjIyIDc3LjkzNDcgMTI3LjE5MiA3NS4zNDggMTI3LjE5MloiIGZpbGw9IiMzMTM3NEEiLz4KPC9zdmc+Cg==");
|
--phoenix-form-select-bg-img: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDE1MCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03NS4zNDggMTI3LjE5MkM3Mi40MzgxIDEyNy4xOTIgNjkuODUxNCAxMjYuMjIyIDY3LjkxMTUgMTI0LjI4Mkw1LjgzMjE1IDYyLjIwMjNDMS42Mjg4NyA1OC4zMjIzIDEuNjI4ODcgNTEuNTMyNCA1LjgzMjE1IDQ3LjY1MjVDOS43MTIxMSA0My40NDkyIDE2LjUwMiA0My40NDkyIDIwLjM4MiA0Ny42NTI1TDc1LjM0OCAxMDIuMjk1TDEyOS45OTEgNDcuNjUyNUMxMzMuODcxIDQzLjQ0OTIgMTQwLjY2MSA0My40NDkyIDE0NC41NDEgNDcuNjUyNUMxNDguNzQ0IDUxLjUzMjQgMTQ4Ljc0NCA1OC4zMjIzIDE0NC41NDEgNjIuMjAyM0w4Mi40NjEzIDEyNC4yODJDODAuNTIxMyAxMjYuMjIyIDc3LjkzNDcgMTI3LjE5MiA3NS4zNDggMTI3LjE5MloiIGZpbGw9IiMzMTM3NEEiLz4KPC9zdmc+Cg==");
|
||||||
display: block;
|
display: block;
|
||||||
|
align-content: start;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: start;
|
text-align: right;
|
||||||
padding: 0.5rem 2.5rem 0.5rem 1rem;
|
padding: 0.5rem 2.5rem 0.5rem 1rem;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
BIN
staticfiles/images/logos/vendors/logo-for-the-word-daju-48a980_Bw4t8ED.jpg
vendored
Normal file
BIN
staticfiles/images/logos/vendors/logo-for-the-word-daju-48a980_Bw4t8ED.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@ -10,7 +10,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form action="{% url 'add_activity' content_type=content_type pk=pk %}" method="post" class="add_activity_form">
|
<form action="{% url 'add_activity' content_type=content_type slug=slug %}" method="post" class="add_activity_form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="mb-2 form-group">
|
<div class="mb-2 form-group">
|
||||||
<select class="form-select" name="activity_type" id="activity_type">
|
<select class="form-select" name="activity_type" id="activity_type">
|
||||||
|
|||||||
@ -352,7 +352,7 @@
|
|||||||
<div class="tab-pane fade" id="tab-emails" role="tabpanel" aria-labelledby="emails-tab">
|
<div class="tab-pane fade" id="tab-emails" role="tabpanel" aria-labelledby="emails-tab">
|
||||||
<div class="mb-1 d-flex justify-content-between align-items-center">
|
<div class="mb-1 d-flex justify-content-between align-items-center">
|
||||||
<h3 class="mb-0" id="scrollspyEmails">{{ _("Emails") }}</h3>
|
<h3 class="mb-0" id="scrollspyEmails">{{ _("Emails") }}</h3>
|
||||||
<a href="{% url 'send_lead_email' lead.pk %}">
|
<a href="{% url 'send_lead_email' lead.slug %}">
|
||||||
<button type="button" class="btn btn-sm btn-phoenix-primary">
|
<button type="button" class="btn btn-sm btn-phoenix-primary">
|
||||||
<span class="fas fa-plus me-1"></span>
|
<span class="fas fa-plus me-1"></span>
|
||||||
{% trans 'Send Email' %}
|
{% trans 'Send Email' %}
|
||||||
@ -448,7 +448,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="sent align-middle white-space-nowrap text-start fw-bold text-body-tertiary py-2">{{email.from_email}}</td>
|
<td class="sent align-middle white-space-nowrap text-start fw-bold text-body-tertiary py-2">{{email.from_email}}</td>
|
||||||
<td class="date align-middle white-space-nowrap text-body py-2">{{email.created}}</td>
|
<td class="date align-middle white-space-nowrap text-body py-2">{{email.created}}</td>
|
||||||
<td class="align-middle white-space-nowrap ps-3"><a class="text-body" href="{% url 'send_lead_email_with_template' lead.pk email.pk %}"><span class="fa-solid fa-email text-primary me-2"></span>Send</a></td>
|
<td class="align-middle white-space-nowrap ps-3"><a class="text-body" href="{% url 'send_lead_email_with_template' lead.slug email.pk %}"><span class="fa-solid fa-email text-primary me-2"></span>Send</a></td>
|
||||||
<td class="status align-middle fw-semibold text-end py-2"><span class="badge badge-phoenix fs-10 badge-phoenix-warning">draft</span></td>
|
<td class="status align-middle fw-semibold text-end py-2"><span class="badge badge-phoenix fs-10 badge-phoenix-warning">draft</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -496,10 +496,10 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody class="list" id="all-email-table-body">
|
<tbody class="list" id="all-email-table-body">
|
||||||
{% for task in tasks %}
|
{% for task in tasks %}
|
||||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static {% if task.completed %}completed-task{% endif %}">
|
<tr class="task-{{task.pk}} hover-actions-trigger btn-reveal-trigger position-static {% if task.completed %}completed-task{% endif %}">
|
||||||
<td class="fs-9 align-middle px-0 py-3">
|
<td class="fs-9 align-middle px-0 py-3">
|
||||||
<div class="form-check mb-0 fs-8">
|
<div class="form-check mb-0 fs-8">
|
||||||
<input class="form-check-input" type="checkbox" hx-post="{% url 'update_task' task.pk %}" hx-trigger="change" hx-swap="none" />
|
<input class="form-check-input" type="checkbox" hx-post="{% url 'update_task' task.pk %}" hx-trigger="change" hx-swap="outerHTML" hx-select=".task-{{task.pk}}" hx-target=".task-{{task.pk}}" />
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="subject order align-middle white-space-nowrap py-2 ps-0"><a class="fw-semibold text-primary" href="">{{task.title}}</a>
|
<td class="subject order align-middle white-space-nowrap py-2 ps-0"><a class="fw-semibold text-primary" href="">{{task.title}}</a>
|
||||||
@ -552,7 +552,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- activity Modal -->
|
<!-- activity Modal -->
|
||||||
{% include "components/activity_modal.html" with content_type="lead" pk=lead.pk %}
|
{% include "components/activity_modal.html" with content_type="lead" slug=lead.slug %}
|
||||||
<!-- task Modal -->
|
<!-- task Modal -->
|
||||||
<div class="modal fade" id="taskModal" tabindex="-1" aria-labelledby="taskModalLabel" aria-hidden="true">
|
<div class="modal fade" id="taskModal" tabindex="-1" aria-labelledby="taskModalLabel" aria-hidden="true">
|
||||||
|
|
||||||
@ -565,7 +565,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form action="{% url 'add_task' 'lead' lead.pk %}" method="post" class="add_task_form">
|
<form action="{% url 'add_task' 'lead' lead.slug %}" method="post" class="add_task_form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ staff_task_form|crispy }}
|
{{ staff_task_form|crispy }}
|
||||||
<button type="submit" class="btn btn-success w-100">{% trans 'Save' %}</button>
|
<button type="submit" class="btn btn-success w-100">{% trans 'Save' %}</button>
|
||||||
|
|||||||
@ -148,7 +148,7 @@
|
|||||||
<p>{% trans "Are you sure you want to delete this lead?" %}</p>
|
<p>{% trans "Are you sure you want to delete this lead?" %}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer flex justify-content-center border-top-0">
|
<div class="modal-footer flex justify-content-center border-top-0">
|
||||||
<a type="button" class="btn btn-sm btn-danger w-100" href="{% url 'lead_delete' lead.pk %}">
|
<a type="button" class="btn btn-sm btn-danger w-100" href="{% url 'lead_delete' lead.slug %}">
|
||||||
{% trans "Yes" %}
|
{% trans "Yes" %}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -159,7 +159,7 @@
|
|||||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||||
<td class="name align-middle white-space-nowrap ps-0">
|
<td class="name align-middle white-space-nowrap ps-0">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' lead.pk %}">{{lead.full_name}}</a>
|
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' lead.slug %}">{{lead.full_name}}</a>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2"></p>
|
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2"></p>
|
||||||
{% if lead.status == "new" %}
|
{% if lead.status == "new" %}
|
||||||
@ -187,11 +187,11 @@
|
|||||||
<div class="accordion" id="accordionExample">
|
<div class="accordion" id="accordionExample">
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header" id="headingTwo">
|
<h2 class="accordion-header" id="headingTwo">
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{lead.pk}}" aria-expanded="false" aria-controls="collapseTwo">
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{lead.slug}}" aria-expanded="false" aria-controls="collapseTwo">
|
||||||
View Schedules ({{lead.get_latest_schedules.count}})
|
View Schedules ({{lead.get_latest_schedules.count}})
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="accordion-collapse collapse" id="collapse{{lead.pk}}" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
|
<div class="accordion-collapse collapse" id="collapse{{lead.slug}}" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
|
||||||
<div class="accordion-body pt-0">
|
<div class="accordion-body pt-0">
|
||||||
<div class="d-flex flex-column gap-2">
|
<div class="d-flex flex-column gap-2">
|
||||||
<table><tbody>
|
<table><tbody>
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
<div class="card email-content">
|
<div class="card email-content">
|
||||||
<h5 class="card-header">Send Mail</h5>
|
<h5 class="card-header">Send Mail</h5>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form class="d-flex flex-column h-100" action="{% url 'send_lead_email' lead.pk %}" method="post">
|
<form class="d-flex flex-column h-100" action="{% url 'send_lead_email' lead.slug %}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="row g-3 mb-2">
|
<div class="row g-3 mb-2">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
@ -26,8 +26,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<a href="{% url 'lead_detail' lead.pk %}" class="btn btn-link text-body fs-10 text-decoration-none">Discard</a>
|
<a href="{% url 'lead_detail' lead.slug %}" class="btn btn-link text-body fs-10 text-decoration-none">Discard</a>
|
||||||
<a hx-boost="true" hx-push-url='false' hx-include="#message,#subject,#to" href="{% url 'send_lead_email' lead.pk %}?status=draft" class="btn btn-secondary text-white fs-10 text-decoration-none">Save as Draft</a>
|
<a hx-boost="true" hx-push-url='false' hx-include="#message,#subject,#to" href="{% url 'send_lead_email' lead.slug %}?status=draft" class="btn btn-secondary text-white fs-10 text-decoration-none">Save as Draft</a>
|
||||||
<button class="btn btn-primary fs-10" type="submit">Send<span class="fa-solid fa-paper-plane ms-1"></span></button>
|
<button class="btn btn-primary fs-10" type="submit">Send<span class="fa-solid fa-paper-plane ms-1"></span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -72,7 +72,7 @@
|
|||||||
<div class="kanban-column">
|
<div class="kanban-column">
|
||||||
<div class="kanban-header">New Leads ({{new|length}})</div>
|
<div class="kanban-header">New Leads ({{new|length}})</div>
|
||||||
{% for lead in new %}
|
{% for lead in new %}
|
||||||
<a href="{% url 'lead_detail' lead.id %}">
|
<a href="{% url 'lead_detail' lead.slug %}">
|
||||||
<div class="lead-card">
|
<div class="lead-card">
|
||||||
<strong>{{lead.full_name|capfirst}}</strong><br>
|
<strong>{{lead.full_name|capfirst}}</strong><br>
|
||||||
<small>{{lead.email}}</small><br>
|
<small>{{lead.email}}</small><br>
|
||||||
@ -88,7 +88,7 @@
|
|||||||
<div class="kanban-column">
|
<div class="kanban-column">
|
||||||
<div class="kanban-header">Follow Ups ({{follow_up|length}})</div>
|
<div class="kanban-header">Follow Ups ({{follow_up|length}})</div>
|
||||||
{% for lead in follow_up %}
|
{% for lead in follow_up %}
|
||||||
<a href="{% url 'lead_detail' lead.id %}">
|
<a href="{% url 'lead_detail' lead.slug %}">
|
||||||
<div class="lead-card">
|
<div class="lead-card">
|
||||||
<strong>{{lead.full_name|capfirst}}</strong><br>
|
<strong>{{lead.full_name|capfirst}}</strong><br>
|
||||||
<small>{{lead.email}}</small><br>
|
<small>{{lead.email}}</small><br>
|
||||||
@ -104,7 +104,7 @@
|
|||||||
<div class="kanban-column">
|
<div class="kanban-column">
|
||||||
<div class="kanban-header">Negotiation ({{negotiation|length}})</div>
|
<div class="kanban-header">Negotiation ({{negotiation|length}})</div>
|
||||||
{% for lead in negotiation %}
|
{% for lead in negotiation %}
|
||||||
<a href="{% url 'lead_detail' lead.id %}">
|
<a href="{% url 'lead_detail' lead.slug %}">
|
||||||
<div class="lead-card">
|
<div class="lead-card">
|
||||||
<strong>{{lead.full_name|capfirst}}</strong><br>
|
<strong>{{lead.full_name|capfirst}}</strong><br>
|
||||||
<small>{{lead.email}}</small><br>
|
<small>{{lead.email}}</small><br>
|
||||||
@ -120,7 +120,7 @@
|
|||||||
<div class="kanban-column">
|
<div class="kanban-column">
|
||||||
<div class="kanban-header">Won ({{won|length}})</div>
|
<div class="kanban-header">Won ({{won|length}})</div>
|
||||||
{% for lead in won %}
|
{% for lead in won %}
|
||||||
<a href="{% url 'lead_detail' lead.id %}">
|
<a href="{% url 'lead_detail' lead.slug %}">
|
||||||
<div class="lead-card">
|
<div class="lead-card">
|
||||||
<strong>{{lead.full_name|capfirst}}</strong><br>
|
<strong>{{lead.full_name|capfirst}}</strong><br>
|
||||||
<small>{{lead.email}}</small><br>
|
<small>{{lead.email}}</small><br>
|
||||||
@ -136,7 +136,7 @@
|
|||||||
<div class="kanban-column">
|
<div class="kanban-column">
|
||||||
<div class="kanban-header">Lose ({{lose|length}})</div>
|
<div class="kanban-header">Lose ({{lose|length}})</div>
|
||||||
{% for lead in lose %}
|
{% for lead in lose %}
|
||||||
<a href="{% url 'lead_detail' lead.id %}">
|
<a href="{% url 'lead_detail' lead.slug %}">
|
||||||
<div class="lead-card">
|
<div class="lead-card">
|
||||||
<strong>{{lead.full_name|capfirst}}</strong><br>
|
<strong>{{lead.full_name|capfirst}}</strong><br>
|
||||||
<small>{{lead.email}}</small><br>
|
<small>{{lead.email}}</small><br>
|
||||||
|
|||||||
@ -70,7 +70,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="name align-middle white-space-nowrap ps-0">
|
<td class="name align-middle white-space-nowrap ps-0">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div><a class="fs-8 fw-bold" href="{% url 'customer_detail' customer.pk %}">{{ customer.full_name }}</a>
|
<div><a class="fs-8 fw-bold" href="{% url 'customer_detail' customer.slug %}">{{ customer.full_name }}</a>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -91,13 +91,13 @@
|
|||||||
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ customer.created|date }}</td>
|
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ customer.created|date }}</td>
|
||||||
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
|
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
|
||||||
{% if perms.django_ledger.change_customermodel %}
|
{% if perms.django_ledger.change_customermodel %}
|
||||||
<a href="{% url 'customer_update' customer.pk %}" class="btn btn-sm btn-phoenix-primary me-2" data-url="{% url 'customer_update' customer.pk %}">
|
<a href="{% url 'customer_update' customer.slug %}" class="btn btn-sm btn-phoenix-primary me-2" data-url="{% url 'customer_update' customer.slug %}">
|
||||||
<i class="fas fa-pen"></i>
|
<i class="fas fa-pen"></i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.django_ledger.delete_customermodel %}
|
{% if perms.django_ledger.delete_customermodel %}
|
||||||
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
||||||
data-url="{% url 'customer_delete' customer.pk %}"
|
data-url="{% url 'customer_delete' customer.slug %}"
|
||||||
data-message="{{ _("Are you sure you want to delete this customer")}}"
|
data-message="{{ _("Are you sure you want to delete this customer")}}"
|
||||||
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
{% if perms.django_ledger.delete_customermodel %}
|
{% if perms.django_ledger.delete_customermodel %}
|
||||||
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
||||||
data-url="{% url 'customer_delete' customer.pk %}"
|
data-url="{% url 'customer_delete' customer.slug %}"
|
||||||
data-message="Are you sure you want to delete this customer?"
|
data-message="Are you sure you want to delete this customer?"
|
||||||
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||||
<i class="fas fa-trash me-1"> </i>{{ _("Delete") }}
|
<i class="fas fa-trash me-1"> </i>{{ _("Delete") }}
|
||||||
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
{% if perms.django_ledger.change_customermodel %}
|
{% if perms.django_ledger.change_customermodel %}
|
||||||
<a href="{% url 'customer_update' customer.pk %}" class="btn btn-sm btn-phoenix-warning"><span class="fa-solid fa-pen-to-square me-2"></span>{{_("Update")}}</a>
|
<a href="{% url 'customer_update' customer.slug %}" class="btn btn-sm btn-phoenix-warning"><span class="fa-solid fa-pen-to-square me-2"></span>{{_("Update")}}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -81,7 +81,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="d-flex align-items-center justify-content-end">
|
<div class="d-flex align-items-center justify-content-end">
|
||||||
<a id="addBtn" href="#" class="btn btn-sm btn-phoenix-primary mb-3" data-url="{% url 'add_note_to_customer' customer.pk %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Add") }}<i class='fa fa-plus-circle text-success ms-2'></i>">
|
<a id="addBtn" href="#" class="btn btn-sm btn-phoenix-primary mb-3" data-url="{% url 'add_note_to_customer' customer.slug %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Add") }}<i class='fa fa-plus-circle text-success ms-2'></i>">
|
||||||
<span class="fas fa-plus me-1"></span>
|
<span class="fas fa-plus me-1"></span>
|
||||||
{% trans 'Add Note' %}
|
{% trans 'Add Note' %}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
<a href="{% url 'account_change_password' %}" class="btn btn-phoenix-danger"><span class="fas fa-key me-2"></span>{{ _("Change Password") }}</a>
|
<a href="{% url 'account_change_password' %}" class="btn btn-phoenix-danger"><span class="fas fa-key me-2"></span>{{ _("Change Password") }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<a class="btn btn-phoenix-secondary" href="{% url 'dealer_update' dealer.pk %}"><span class="fas fa-edit me-2 text-body-quaternary"></span>{{ _("Edit") }} </a>
|
<a class="btn btn-phoenix-secondary" href="{% url 'dealer_update' dealer.slug %}"><span class="fas fa-edit me-2 text-body-quaternary"></span>{{ _("Edit") }} </a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -415,7 +415,7 @@
|
|||||||
<ul class="nav d-flex flex-column mb-2 pb-1">
|
<ul class="nav d-flex flex-column mb-2 pb-1">
|
||||||
{% if request.is_dealer %}
|
{% if request.is_dealer %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link px-3 d-block" href="{% url 'dealer_detail' request.user.dealer.pk %}"> <span class="me-2 text-body align-bottom" data-feather="user"></span><span>{% translate 'profile'|capfirst %}</span></a>
|
<a class="nav-link px-3 d-block" href="{% url 'dealer_detail' request.user.dealer.slug %}"> <span class="me-2 text-body align-bottom" data-feather="user"></span><span>{% translate 'profile'|capfirst %}</span></a>
|
||||||
</li>
|
</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
@ -432,7 +432,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
{% if request.is_dealer %}
|
{% if request.is_dealer %}
|
||||||
<a class="nav-link px-3 d-block" href="{% url 'dealer_settings' request.user.dealer.pk %}"> <span class="me-2 text-body align-bottom" data-feather="settings"></span>{{ _("Settings") }}</a>
|
<a class="nav-link px-3 d-block" href="{% url 'dealer_settings' request.user.dealer.slug %}"> <span class="me-2 text-body align-bottom" data-feather="settings"></span>{{ _("Settings") }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load crispy_forms_filters %}
|
{% load crispy_forms_filters %}
|
||||||
|
|
||||||
<form method="post" id="customCardForm" action="{% url 'add_custom_card' car.pk %}">
|
<form method="post" id="customCardForm" action="{% url 'add_custom_card' car.slug %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
<div class="d-flex gap-1">
|
<div class="d-flex gap-1">
|
||||||
|
|||||||
@ -155,16 +155,15 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|
||||||
<th>{% trans 'Location'|capfirst %}</th>
|
<th>{% trans 'Location'|capfirst %}</th>
|
||||||
<td>
|
<td>
|
||||||
{% if car.finances and not car.get_transfer %}
|
{% if car.finances and not car.get_transfer %}
|
||||||
{% if car.location %} {% if car.location.is_owner_showroom %} {% trans 'Our Showroom' %} {% else %} {{ car.location.showroom.get_local_name }} {% endif %}
|
{% if car.location %} {% if car.location.is_owner_showroom %} {% trans 'Our Showroom' %} {% else %} {{ car.location.showroom.get_local_name }} {% endif %}
|
||||||
<a href="{% url 'update_car_location' car.pk car.location.pk%}" class="btn btn-phoenix-danger btn-sm">
|
<a href="{% url 'update_car_location' car.slug car.location.pk%}" class="btn btn-phoenix-danger btn-sm">
|
||||||
{% trans "transfer"|capfirst %}
|
{% trans "transfer"|capfirst %}
|
||||||
</a>
|
</a>
|
||||||
{% else %} {% trans "No location available." %}
|
{% else %} {% trans "No location available." %}
|
||||||
<a href="{% url 'add_car_location' car.pk %}" class="btn btn-phoenix-success btn-sm ms-2">
|
<a href="{% url 'add_car_location' car.slug %}" class="btn btn-phoenix-success btn-sm ms-2">
|
||||||
{% trans "Add" %}
|
{% trans "Add" %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -176,8 +175,8 @@
|
|||||||
<div>
|
<div>
|
||||||
{% if not car.get_transfer %}
|
{% if not car.get_transfer %}
|
||||||
{% if perms.inventory.change_car %}
|
{% if perms.inventory.change_car %}
|
||||||
<a href="{% url 'car_update' car.pk %}" class="btn btn-phoenix-warning btn-sm mt-1">{% trans "Edit" %}</a>
|
<a href="{% url 'car_update' car.slug %}" class="btn btn-phoenix-warning btn-sm mt-1">{% trans "Edit" %}</a>
|
||||||
<a href="{% url 'transfer' car.pk %}" class="btn btn-phoenix-danger btn-sm">
|
<a href="{% url 'transfer' car.slug %}" class="btn btn-phoenix-danger btn-sm">
|
||||||
{% trans "Sell to another dealer"|capfirst %}
|
{% trans "Sell to another dealer"|capfirst %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -242,7 +241,7 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<p>{% trans "No finance details available." %}</p>
|
<p>{% trans "No finance details available." %}</p>
|
||||||
{% if perms.inventory.add_carfinance %}
|
{% if perms.inventory.add_carfinance %}
|
||||||
<a href="{% url 'car_finance_create' car.pk %}" class="btn btn-phoenix-success btn-sm mb-3">
|
<a href="{% url 'car_finance_create' car.slug %}" class="btn btn-phoenix-success btn-sm mb-3">
|
||||||
{% trans "Add" %}
|
{% trans "Add" %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -288,7 +287,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
{% if perms.inventory.change_carcolors %}
|
{% if perms.inventory.change_carcolors %}
|
||||||
<a href="{% url 'add_color' car.pk %}" class="btn btn-phoenix-success btn-sm">
|
<a href="{% url 'add_color' car.slug %}" class="btn btn-phoenix-success btn-sm">
|
||||||
{% trans "Add" %}
|
{% trans "Add" %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -454,9 +453,7 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{% trans 'Are you sure you want to reserve this car?' %}
|
{% trans 'Are you sure you want to reserve this car?' %}
|
||||||
</div>
|
</div>
|
||||||
<form method="POST" action="{% url 'reserve_car' car.id %}" class="form ">
|
<form method="POST" action="{% url 'reserve_car' car.slug %}" class="form ">
|
||||||
|
|
||||||
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="p-1">
|
<div class="p-1">
|
||||||
<div class="d-flex gap-1">
|
<div class="d-flex gap-1">
|
||||||
@ -493,8 +490,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
|
||||||
const csrftoken = getCookie("csrftoken");
|
const csrftoken = getCookie("csrftoken");
|
||||||
const ajaxUrl = "{% url 'ajax_handler' %}";
|
const ajaxUrl = "{% url 'ajax_handler' %}";
|
||||||
|
|
||||||
@ -503,7 +498,7 @@
|
|||||||
|
|
||||||
// When the modal is triggered, load the form
|
// When the modal is triggered, load the form
|
||||||
customCardModal.addEventListener("show.bs.modal", function () {
|
customCardModal.addEventListener("show.bs.modal", function () {
|
||||||
const url = "{% url 'add_custom_card' car.pk %}";
|
const url = "{% url 'add_custom_card' car.slug %}";
|
||||||
|
|
||||||
fetch(url)
|
fetch(url)
|
||||||
.then((response) => response.text())
|
.then((response) => response.text())
|
||||||
@ -525,7 +520,7 @@
|
|||||||
|
|
||||||
// When the modal is triggered, load the form
|
// When the modal is triggered, load the form
|
||||||
registrationModal.addEventListener("show.bs.modal", function () {
|
registrationModal.addEventListener("show.bs.modal", function () {
|
||||||
const url = "{% url 'add_registration' car.pk %}";
|
const url = "{% url 'add_registration' car.slug %}";
|
||||||
|
|
||||||
fetch(url)
|
fetch(url)
|
||||||
.then((response) => response.text())
|
.then((response) => response.text())
|
||||||
@ -608,7 +603,7 @@
|
|||||||
document.querySelectorAll(".reserve-btn").forEach((button) => {
|
document.querySelectorAll(".reserve-btn").forEach((button) => {
|
||||||
button.addEventListener("click", async function () {
|
button.addEventListener("click", async function () {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`{% url 'reserve_car' car.pk %}`, {
|
const response = await fetch(`{% url 'reserve_car' car.slug %}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"X-CSRFToken": csrfToken,
|
"X-CSRFToken": csrfToken,
|
||||||
|
|||||||
@ -70,7 +70,7 @@
|
|||||||
<span class="badge badge-phoenix badge-phoenix-info"><span class="badge-label">{{_("Used")}}</span></span>
|
<span class="badge badge-phoenix badge-phoenix-info"><span class="badge-label">{{_("Used")}}</span></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle white-space-nowrap text-start"><a class="fs-9 fw-bold" href="{% url 'car_detail' car.pk %}">{{ car.vin }}</a></td>
|
<td class="align-middle white-space-nowrap text-start"><a class="fs-9 fw-bold" href="{% url 'car_detail' car.slug %}">{{ car.vin }}</a></td>
|
||||||
<td class="align-middle white-space-nowrap text-center fw-bold">{{ car.year }}</td>
|
<td class="align-middle white-space-nowrap text-center fw-bold">{{ car.year }}</td>
|
||||||
{% if car.colors %}
|
{% if car.colors %}
|
||||||
<td class="align-middle white-space-nowrap text-body fs-9 text-start">
|
<td class="align-middle white-space-nowrap text-body fs-9 text-start">
|
||||||
@ -111,7 +111,7 @@
|
|||||||
<span class="fw-light">{{ car.receiving_date|timesince }}</span>
|
<span class="fw-light">{{ car.receiving_date|timesince }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
|
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
|
||||||
<a class="btn btn-sm btn-phoenix-success" href="{% url 'car_detail' car.pk %}">{% trans "view"|capfirst %}</a>
|
<a class="btn btn-sm btn-phoenix-success" href="{% url 'car_detail' car.slug %}">{% trans "view"|capfirst %}</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|||||||
@ -117,10 +117,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="list" id="project-list-table-body">
|
<tbody class="list" id="project-list-table-body">
|
||||||
{% for car in page_obj %}
|
{% for car in cars %}
|
||||||
<tr class="position-static">
|
<tr class="position-static">
|
||||||
<td class="align-middle white-space-nowrap ps-1">
|
<td class="align-middle white-space-nowrap ps-1">
|
||||||
<a class="fw-bold" href="{% url 'car_detail' car.pk %}">{{car.vin}}</a>
|
<a class="fw-bold" href="{% url 'car_detail' car.slug %}">{{car.vin}}</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle white-space-nowrap">
|
<td class="align-middle white-space-nowrap">
|
||||||
<p class="text-body mb-0">{{car.id_car_make.get_local_name|default:car.id_car_make.name}}</p>
|
<p class="text-body mb-0">{{car.id_car_make.get_local_name|default:car.id_car_make.name}}</p>
|
||||||
@ -164,7 +164,7 @@
|
|||||||
<div class="btn-reveal-trigger position-static">
|
<div class="btn-reveal-trigger position-static">
|
||||||
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
|
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
|
||||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||||
<a class="dropdown-item" href="{% url 'car_detail' car.pk %}">{{ _("View") }}</a>
|
<a class="dropdown-item" href="{% url 'car_detail' car.slug %}">{{ _("View") }}</a>
|
||||||
<a class="dropdown-item" href="#!">{{ _("Export") }}</a>
|
<a class="dropdown-item" href="#!">{{ _("Export") }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
{% load crispy_forms_filters %}
|
{% load crispy_forms_filters %}
|
||||||
|
|
||||||
<div class="w-100 g-3">
|
<div class="w-100 g-3">
|
||||||
<form method="post" id="registrationForm" action="{% url 'add_registration' car.pk %}">
|
<form method="post" id="registrationForm" action="{% url 'add_registration' car.slug %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
<div class="d-flex gap-1">
|
<div class="d-flex gap-1">
|
||||||
|
|||||||
@ -67,7 +67,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
{% for trim in model.trims %}
|
{% for trim in model.trims %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'car_inventory' make_id=make.make_id model_id=model.model_id trim_id=trim.trim_id %}">
|
<a href="{% url 'car_inventory' make_id=make.slug model_id=model.slug trim_id=trim.slug %}">
|
||||||
{{ trim.trim_name }}
|
{{ trim.trim_name }}
|
||||||
</a> - {% trans "Total" %}:
|
</a> - {% trans "Total" %}:
|
||||||
<strong>{{ trim.total_cars }}</strong></li>
|
<strong>{{ trim.total_cars }}</strong></li>
|
||||||
|
|||||||
6
templates/vendors/vendors_list.html
vendored
6
templates/vendors/vendors_list.html
vendored
@ -84,7 +84,7 @@
|
|||||||
<div class="avatar avatar-xl me-3"><img class="rounded-circle" src="{% static 'images/icons/picture.svg' %}" alt="" />
|
<div class="avatar avatar-xl me-3"><img class="rounded-circle" src="{% static 'images/icons/picture.svg' %}" alt="" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div><a class="fs-8 fw-bold" href="{% url 'vendor_detail' vendor.pk%}">{{ vendor.arabic_name }}</a>
|
<div><a class="fs-8 fw-bold" href="{% url 'vendor_detail' vendor.slug%}">{{ vendor.arabic_name }}</a>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.name}}</p><!--<span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.vendor_model.uuid }}</span>-->
|
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.name}}</p><!--<span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.vendor_model.uuid }}</span>-->
|
||||||
</div>
|
</div>
|
||||||
@ -101,12 +101,12 @@
|
|||||||
<div class="btn-reveal-trigger position-static">
|
<div class="btn-reveal-trigger position-static">
|
||||||
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
|
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
|
||||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||||
<a href="{% url 'vendor_update' vendor.pk %}" class="dropdown-item text-success-dark">
|
<a href="{% url 'vendor_update' vendor.slug %}" class="dropdown-item text-success-dark">
|
||||||
{% trans "Edit" %}
|
{% trans "Edit" %}
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<button class="delete-btn dropdown-item text-danger"
|
<button class="delete-btn dropdown-item text-danger"
|
||||||
data-url="{% url 'vendor_delete' vendor.pk %}"
|
data-url="{% url 'vendor_delete' vendor.slug %}"
|
||||||
data-message="{{ _("Are you sure you want to delete this vendor")}}?"
|
data-message="{{ _("Are you sure you want to delete this vendor")}}?"
|
||||||
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||||
{{ _("Delete") }}
|
{{ _("Delete") }}
|
||||||
|
|||||||
4
templates/vendors/view_vendor.html
vendored
4
templates/vendors/view_vendor.html
vendored
@ -28,12 +28,12 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer d-flex">
|
<div class="card-footer d-flex">
|
||||||
<a class="btn btn-sm btn-phoenix-primary me-1" href="{% url 'vendor_update' vendor.id %}">
|
<a class="btn btn-sm btn-phoenix-primary me-1" href="{% url 'vendor_update' vendor.slug %}">
|
||||||
{% trans "Edit" %}
|
{% trans "Edit" %}
|
||||||
<i class="fa fa-pencil"></i>
|
<i class="fa fa-pencil"></i>
|
||||||
</a>
|
</a>
|
||||||
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
||||||
data-url="{% url 'vendor_delete' vendor.pk %}"
|
data-url="{% url 'vendor_delete' vendor.slug %}"
|
||||||
data-message="{{ _("Are you sure you want to delete this vendor")}}?"
|
data-message="{{ _("Are you sure you want to delete this vendor")}}?"
|
||||||
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||||
{{ _("Delete") }}
|
{{ _("Delete") }}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user