update and fixes

This commit is contained in:
ismail 2025-09-04 19:25:36 +03:00
parent 50c1d82e4c
commit 8399554b48
18 changed files with 1327 additions and 1047 deletions

View File

@ -2,7 +2,7 @@
from django.contrib import admin from django.contrib import admin
from . import models from . import models
from django_ledger import models as ledger_models from django_ledger import models as ledger_models
from django.contrib import messages
# from django_pdf_actions.actions import export_to_pdf_landscape, export_to_pdf_portrait # from django_pdf_actions.actions import export_to_pdf_landscape, export_to_pdf_portrait
# from appointment import models as appointment_models # from appointment import models as appointment_models
from import_export.admin import ExportMixin from import_export.admin import ExportMixin
@ -72,7 +72,7 @@ admin.site.register(models.UserActivityLog)
admin.site.register(models.DealersMake) admin.site.register(models.DealersMake)
admin.site.register(models.ExtraInfo) admin.site.register(models.ExtraInfo)
admin.site.register(models.Ticket) admin.site.register(models.Ticket)
admin.site.register(models.UserRegistration) # admin.site.register(models.UserRegistration)
@admin.register(models.Car) @admin.register(models.Car)
@ -175,3 +175,93 @@ class CarOptionAdmin(admin.ModelAdmin):
# class ItemTransactionModelAdmin(admin.ModelAdmin): # class ItemTransactionModelAdmin(admin.ModelAdmin):
# actions = [export_to_pdf_landscape, export_to_pdf_portrait] # actions = [export_to_pdf_landscape, export_to_pdf_portrait]
@admin.register(models.UserRegistration)
class UserRegistrationAdmin(admin.ModelAdmin):
# Fields to display in the list view
list_display = [
'name',
'arabic_name',
'email',
'crn',
'vrn',
'phone_number',
'is_created',
'created_at',
]
# Filters in the right sidebar
list_filter = [
'is_created',
'created_at',
]
# Searchable fields
search_fields = [
'name', 'arabic_name', 'email', 'crn', 'vrn', 'phone_number'
]
# Read-only fields in detail view
readonly_fields = [
'created_at', 'updated_at', 'is_created', 'password'
]
# Organize form layout
fieldsets = [
('Account Information', {
'fields': ('name', 'arabic_name', 'email', 'phone_number')
}),
('Business Details', {
'fields': ('crn', 'vrn', 'address')
}),
('Status', {
'fields': ('is_created', 'password', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
]
# Custom action to create accounts
actions = ['create_dealer_accounts']
@admin.action(description='Create dealer account(s) for selected registrations')
def create_dealer_accounts(self, request, queryset):
created_count = 0
already_created_count = 0
failed_count = 0
for registration in queryset:
try:
if not registration.is_created:
registration.create_account() # Your existing method
created_count += 1
else:
already_created_count += 1
except Exception as e:
self.message_user(
request,
f"Error creating account for {registration.name}: {str(e)}",
level=messages.ERROR
)
failed_count += 1
# Show summary message
if created_count > 0:
self.message_user(
request,
f"Successfully created {created_count} account(s).",
level=messages.SUCCESS
)
if already_created_count > 0:
self.message_user(
request,
f"{already_created_count} registration(s) were already created.",
level=messages.INFO
)
if failed_count > 0:
self.message_user(
request,
f"Failed to create {failed_count} account(s). Check logs.",
level=messages.ERROR
)

View File

@ -9,7 +9,7 @@ from plans.models import PlanPricing
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from inventory.validators import SaudiPhoneNumberValidator from inventory.validators import SaudiPhoneNumberValidator
from .models import CustomGroup, Status, Stage, UserRegistration from .models import CustomGroup, Status, Stage
from .mixins import AddClassMixin from .mixins import AddClassMixin
from django_ledger.forms.invoice import ( from django_ledger.forms.invoice import (
InvoiceModelCreateForm as InvoiceModelCreateFormBase, InvoiceModelCreateForm as InvoiceModelCreateFormBase,
@ -57,6 +57,7 @@ from .models import (
Tasks, Tasks,
Recall, Recall,
Ticket, Ticket,
UserRegistration
) )
from django_ledger import models as ledger_models from django_ledger import models as ledger_models
from django.forms import ( from django.forms import (
@ -363,6 +364,7 @@ class CarForm(
"receiving_date", "receiving_date",
"vendor", "vendor",
] ]
required_fields = ["vin","id_car_make", "id_car_model", "id_car_serie", "id_car_trim", "vendor"]
widgets = { widgets = {
"id_car_make": forms.Select(attrs={"class": "form-select form-select-sm"}), "id_car_make": forms.Select(attrs={"class": "form-select form-select-sm"}),
"receiving_date": forms.DateTimeInput(attrs={"type": "datetime-local"}), "receiving_date": forms.DateTimeInput(attrs={"type": "datetime-local"}),
@ -1259,19 +1261,8 @@ class OpportunityForm(forms.ModelForm):
label=_("Expected Closing Date"), widget=forms.DateInput(attrs={"type": "date"}) label=_("Expected Closing Date"), widget=forms.DateInput(attrs={"type": "date"})
) )
probability = forms.IntegerField( car = forms.ModelChoiceField(
label=_("Probability (%)"), queryset=Car.objects.all(), label=_("Car"), required=True
widget=forms.NumberInput(
attrs={
"type": "range",
"min": "0",
"max": "100",
"step": "1",
"class": "form-range",
"oninput": "this.nextElementSibling.value = this.value",
}
),
initial=50, # Default value
) )
class Meta: class Meta:
@ -1280,23 +1271,17 @@ class OpportunityForm(forms.ModelForm):
"lead", "lead",
"car", "car",
"stage", "stage",
"probability",
"amount",
"expected_revenue",
"expected_close_date", "expected_close_date",
] ]
widgets = {
"expected_revenue": forms.NumberInput(attrs={"readonly": "readonly"}),
}
def __init__(self, *args, **kwargs): # def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # super().__init__(*args, **kwargs)
# Add a visible number input to display the current value # # Add a visible number input to display the current value
self.fields["probability"].widget.attrs["class"] = ( # self.fields["probability"].widget.attrs["class"] = (
"d-none" # Hide the default input # "d-none" # Hide the default input
) # )
if self.instance and self.instance.pk: # if self.instance and self.instance.pk:
self.fields["probability"].initial = self.instance.probability # self.fields["probability"].initial = self.instance.probability
class OpportunityStageForm(forms.ModelForm): class OpportunityStageForm(forms.ModelForm):
@ -2259,6 +2244,8 @@ class TicketResolutionForm(forms.ModelForm):
class CarDealershipRegistrationForm(forms.ModelForm): class CarDealershipRegistrationForm(forms.ModelForm):
# Add additional fields for the registration form
class Meta: class Meta:
model = UserRegistration model = UserRegistration
fields = "__all__" fields = ("name","arabic_name", "email","phone_number", "crn", "vrn", "address")

View File

@ -49,7 +49,18 @@ from plans.models import UserPlan
from django.db.models import Q from django.db.models import Q
from imagekit.models import ImageSpecField from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill from imagekit.processors import ResizeToFill
from encrypted_model_fields.fields import (
EncryptedCharField,
EncryptedTextField,
EncryptedEmailField,
EncryptedIntegerField,
EncryptedDateField,
EncryptedDateTimeField,
EncryptedBooleanField,
EncryptedJSONField,
SearchableEncryptedCharField,
SearchableEncryptedEmailField,
)
# from plans.models import AbstractPlan # from plans.models import AbstractPlan
# from simple_history.models import HistoricalRecords # from simple_history.models import HistoricalRecords
from plans.models import Invoice from plans.models import Invoice
@ -1339,7 +1350,7 @@ class Dealer(models.Model, LocalizedNameMixin):
) )
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
name = models.CharField(max_length=255, verbose_name=_("English Name")) name = models.CharField(max_length=255, verbose_name=_("English Name"))
phone_number = models.CharField( phone_number = EncryptedCharField(
max_length=255, max_length=255,
verbose_name=_("Phone Number"), verbose_name=_("Phone Number"),
validators=[SaudiPhoneNumberValidator()], validators=[SaudiPhoneNumberValidator()],
@ -1707,18 +1718,18 @@ class Customer(models.Model):
max_length=1, max_length=1,
verbose_name=_("Gender"), verbose_name=_("Gender"),
) )
dob = models.DateField(verbose_name=_("Date of Birth"), null=True, blank=True) dob = EncryptedDateField(verbose_name=_("Date of Birth"), null=True, blank=True)
email = models.EmailField(verbose_name=_("Email")) email = SearchableEncryptedEmailField(verbose_name=_("Email"))
national_id = models.CharField( national_id = EncryptedCharField(
max_length=10, unique=True, verbose_name=_("National ID"), null=True, blank=True max_length=10, unique=True, verbose_name=_("National ID"), null=True, blank=True
) )
phone_number = models.CharField( phone_number = EncryptedCharField(
max_length=255, max_length=255,
verbose_name=_("Phone Number"), verbose_name=_("Phone Number"),
validators=[SaudiPhoneNumberValidator()], validators=[SaudiPhoneNumberValidator()],
) )
address = models.CharField( address = EncryptedCharField(
max_length=200, blank=True, null=True, verbose_name=_("Address") max_length=200, blank=True, null=True, verbose_name=_("Address")
) )
active = models.BooleanField(default=True, verbose_name=_("Active")) active = models.BooleanField(default=True, verbose_name=_("Active"))
@ -2294,22 +2305,48 @@ class Schedule(models.Model):
related_name="schedules", related_name="schedules",
null=True, null=True,
blank=True, blank=True,
verbose_name=_("Customer"),
) )
scheduled_by = models.ForeignKey(User, on_delete=models.CASCADE) scheduled_by = models.ForeignKey(User, on_delete=models.CASCADE)
purpose = models.CharField(max_length=200, choices=PURPOSE_CHOICES) purpose = models.CharField(
scheduled_at = models.DateTimeField() max_length=200,
start_time = models.TimeField(verbose_name=_("Start Time"), null=True, blank=True) choices=PURPOSE_CHOICES,
end_time = models.TimeField(verbose_name=_("End Time"), null=True, blank=True) verbose_name=_("Purpose"),
help_text=_("What is the purpose of this schedule?"),
)
scheduled_at = models.DateTimeField(verbose_name=_("Scheduled Date"))
start_time = models.TimeField(
verbose_name=_("Start Time"), null=True, blank=True, help_text=_("HH:MM")
)
end_time = models.TimeField(
verbose_name=_("End Time"), null=True, blank=True, help_text=_("HH:MM")
)
scheduled_type = models.CharField( scheduled_type = models.CharField(
max_length=200, choices=ScheduledType, default="Call" max_length=200,
choices=ScheduledType,
default="Call",
verbose_name=_("Scheduled Type"),
help_text=_("What type of schedule is this?"),
) )
completed = models.BooleanField(default=False, verbose_name=_("Completed")) completed = models.BooleanField(
notes = models.TextField(blank=True, null=True) default=False,
verbose_name=_("Completed"),
help_text=_("Has this schedule been completed?"),
)
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
status = models.CharField( status = models.CharField(
max_length=200, choices=ScheduleStatusChoices, default="Scheduled" max_length=200,
choices=ScheduleStatusChoices,
default="Scheduled",
verbose_name=_("Status"),
help_text=_("What is the status of this schedule?"),
)
created_at = models.DateTimeField(
auto_now_add=True, verbose_name=_("Created Date"), help_text=_("When was this schedule created?")
)
updated_at = models.DateTimeField(
auto_now=True, verbose_name=_("Updated Date"), help_text=_("When was this schedule last updated?")
) )
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self): def __str__(self):
return f"Scheduled {self.purpose} on {self.scheduled_at}" return f"Scheduled {self.purpose} on {self.scheduled_at}"
@ -2414,10 +2451,11 @@ class Opportunity(models.Model):
"Lead", "Lead",
related_name="opportunity", related_name="opportunity",
on_delete=models.CASCADE, on_delete=models.CASCADE,
verbose_name=_("Lead"),
null=True, null=True,
blank=True, blank=True,
) )
probability = models.PositiveIntegerField(validators=[validate_probability]) probability = models.PositiveIntegerField(validators=[validate_probability],null=True, blank=True)
amount = models.DecimalField( amount = models.DecimalField(
max_digits=10, max_digits=10,
decimal_places=2, decimal_places=2,
@ -3873,7 +3911,7 @@ class UserRegistration(models.Model):
crn = models.CharField(_("Commercial Registration Number"), max_length=10, unique=True) crn = models.CharField(_("Commercial Registration Number"), max_length=10, unique=True)
vrn = models.CharField(_("Vehicle Registration Number"), max_length=15, unique=True) vrn = models.CharField(_("Vehicle Registration Number"), max_length=15, unique=True)
address = models.TextField(_("Address")) address = models.TextField(_("Address"))
password = models.CharField(_("Password"), max_length=255) password = models.CharField(_("Password"), max_length=255,null=True,blank=True)
is_created = models.BooleanField(default=False) is_created = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
@ -3884,16 +3922,37 @@ class UserRegistration(models.Model):
return self.email return self.email
def create_account(self): def create_account(self):
from django_q.tasks import async_task
from .tasks import create_user_dealer from .tasks import create_user_dealer
password = User.objects.make_random_password() if self.is_created or User.objects.filter(email=self.email).exists():
logger.info(f"Account already created or exists: {self.email}")
return False
if self.is_created: password = make_random_password()
return
dealer = create_user_dealer(self.email,password,self.name,self.arabic_name,self.phone,self.crn,self.vrn,self.address)
if dealer: try:
self.is_created = True logger.info(f"Creating user account {self.email}")
self.password = password dealer = create_user_dealer(
self.save() email=self.email,
password=password,
name=self.name,
arabic_name=self.arabic_name,
phone=self.phone_number,
crn=self.crn,
vrn=self.vrn,
address=self.address
)
if dealer:
self.is_created = True
self.password = password
self.save()
logger.info(f"User account created successfully: {self.email}")
return True
else:
logger.error(f"Failed to create dealer account: {self.email}")
return False
except Exception as e:
logger.error(f"Error creating account for {self.email}: {e}")
return False

View File

@ -310,20 +310,22 @@ def create_item_model(sender, instance, created, **kwargs):
""" """
entity = instance.dealer.entity entity = instance.dealer.entity
if created: coa = entity.get_default_coa()
coa = entity.get_default_coa() uom = entity.get_uom_all().filter(name="Unit").first()
uom = entity.get_uom_all().get(name="Unit") if not uom:
uom = entity.create_uom(name="Unit", unit_abbr="unit")
if not instance.item_model: if not instance.item_model:
inventory = entity.create_item_product( inventory = entity.create_item_product(
name=instance.vin, name=instance.vin,
item_type=ItemModel.ITEM_TYPE_MATERIAL, item_type=ItemModel.ITEM_TYPE_MATERIAL,
uom_model=uom, uom_model=uom,
coa_model=coa, coa_model=coa,
) )
instance.item_model = inventory instance.item_model = inventory
inventory.save() instance.save()
else:
if instance.marked_price:
instance.item_model.default_amount = instance.marked_price instance.item_model.default_amount = instance.marked_price
instance.item_model.save() instance.item_model.save()
@ -1391,11 +1393,35 @@ def handle_car_image(sender, instance, created, **kwargs):
@receiver(post_save, sender=models.UserRegistration) @receiver(post_save, sender=models.UserRegistration)
def handle_user_registration(sender, instance, created, **kwargs): def handle_user_registration(sender, instance, created, **kwargs):
if instance.is_created: if created:
send_email( send_email(
"Account Created", settings.DEFAULT_FROM_EMAIL,
f"Your account has been created and you can login with your email and password: {instance.password}", instance.email,
settings.DEFAULT_FROM_EMAIL, "Account Registration",
[instance.email], """
fail_silently=False, Thank you for registering with us. We will contact you shortly to complete your application.
) شكرا لمراسلتنا. سوف نتصل بك قريبا لاستكمال طلبك.
"""
)
if instance.is_created:
logger.info(f"User account created: {instance.email}, sending email")
send_email(
settings.DEFAULT_FROM_EMAIL,
instance.email,
"Account Created",
f"""
Dear {instance.name},
Your account has been created and you can login with your email {instance.email} and password: {instance.password}.
Please login to the website to complete your profile and start using our services.
Thank you for choosing us.
عزيزي {instance.name},
لقد تم إنشاء حسابك والآن يمكنك تسجيل الدخول باستخدام بريدك الإلكتروني {instance.email} وكلمة المرور: {instance.password}.
يرجى تسجيل الدخول إلى الموقع لاستكمال الملف الشخصي والبدء في استخدام خدماتنا.
شكرا لاختيارك لنا.
""")

View File

@ -1440,13 +1440,13 @@ class CarListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
qs = super().get_queryset() qs = super().get_queryset()
qs = qs.filter(dealer=dealer) qs = qs.filter(dealer=dealer)
# status = self.request.GET.get("status") # status = self.request.GET.get("status")
# status = self.request.GET.get("status")
search = self.request.GET.get("search") search = self.request.GET.get("search")
make = self.request.GET.get("make", None) make = self.request.GET.get("make", None)
model = self.request.GET.get("model", None) model = self.request.GET.get("model", None)
year = self.request.GET.get("year", None) year = self.request.GET.get("year", None)
car_status = self.request.GET.get("car_status", None) car_status = self.request.GET.get("car_status", None)
print("ALLLLLLLLL:::",make,model) print(car_status)
if car_status: if car_status:
qs = qs.filter(status=car_status) qs = qs.filter(status=car_status)
if search: if search:
@ -6331,9 +6331,14 @@ def lead_create(request, dealer_slug):
form.filter_qs(dealer=dealer) form.filter_qs(dealer=dealer)
if make := request.GET.get("id_car_make", None): if make := request.GET.get("id_car_make", None):
form.fields["id_car_model"].queryset = models.CarModel.objects.filter( qs = models.CarModel.objects.filter(
id_car_make=int(make) id_car_make=int(make)
) )
form.fields["id_car_model"].queryset = qs
form.fields["id_car_model"].choices = [
(obj.id_car_model, obj.get_local_name()) for obj in qs
]
else: else:
dealer_make_list = models.DealersMake.objects.filter(dealer=dealer).values_list( dealer_make_list = models.DealersMake.objects.filter(dealer=dealer).values_list(
"car_make", flat=True "car_make", flat=True
@ -6367,7 +6372,11 @@ def lead_create(request, dealer_slug):
] ]
if first_make := qs.first(): if first_make := qs.first():
form.fields["id_car_model"].queryset = first_make.carmodel_set.all() qs = first_make.carmodel_set.all()
form.fields["id_car_model"].queryset = qs
form.fields["id_car_model"].choices = [
(obj.id_car_model, obj.get_local_name()) for obj in qs
]
return render(request, "crm/leads/lead_form.html", {"form": form}) return render(request, "crm/leads/lead_form.html", {"form": form})

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

View File

@ -282,7 +282,7 @@
type="button" type="button"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#exampleModal"> data-bs-target="#exampleModal">
<i class="fa-solid fa-user-plus me-2"></i> Reassign Lead <i class="fa-solid fa-user-plus me-2"></i> {%trans "Reassign Lead"%}
</button> </button>
{% endif %} {% endif %}
<button class="btn btn-phoenix-primary btn-sm" <button class="btn btn-phoenix-primary btn-sm"
@ -301,7 +301,7 @@
method="post"> method="post">
{% csrf_token %} {% csrf_token %}
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Reassign Lead To Another Employee</h5> <h5 class="modal-title" id="exampleModalLabel">{%trans "Reassign Lead To Another Employee"%}</h5>
<button class="btn btn-close p-1" <button class="btn btn-close p-1"
type="button" type="button"
data-bs-dismiss="modal" data-bs-dismiss="modal"
@ -581,25 +581,25 @@
scope="col" scope="col"
data-sort="subject" data-sort="subject"
style="width:31%; style="width:31%;
min-width:350px">Subject</th> min-width:350px">{{ _("Subject") }}</th>
<th class="sort align-middle pe-3 text-uppercase" <th class="sort align-middle pe-3 text-uppercase"
scope="col" scope="col"
data-sort="sent" data-sort="sent"
style="width:15%; style="width:15%;
min-width:130px">Sent by</th> min-width:130px">{{ _("Sent by") }}</th>
<th class="sort align-middle text-start text-uppercase" <th class="sort align-middle text-start text-uppercase"
scope="col" scope="col"
data-sort="date" data-sort="date"
style="min-width:165px">Date</th> style="min-width:165px">{{ _("Date") }}</th>
<th class="sort align-middle pe-0 text-uppercase" <th class="sort align-middle pe-0 text-uppercase"
scope="col" scope="col"
style="width:15%; style="width:15%;
min-width:100px">Action</th> min-width:100px">{{ _("Action") }}</th>
<th class="sort align-middle text-end text-uppercase" <th class="sort align-middle text-end text-uppercase"
scope="col" scope="col"
data-sort="status" data-sort="status"
style="width:15%; style="width:15%;
min-width:100px">Status</th> min-width:100px">{{ _("Status") }}</th>
</tr> </tr>
</thead> </thead>
<tbody class="list" id="all-email-table-body"> <tbody class="list" id="all-email-table-body">
@ -619,10 +619,10 @@
<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|naturalday }}</td> <td class="date align-middle white-space-nowrap text-body py-2">{{ email.created|naturalday }}</td>
<td class="align-middle white-space-nowrap ps-3"> <td class="align-middle white-space-nowrap ps-3">
<a class="text-body" href=""><span class="fa-solid fa-phone text-primary me-2"></span>Call</a> <a class="text-body" href=""><span class="fa-solid fa-phone text-primary me-2"></span>{%trans "Call"%</a>
</td> </td>
<td class="status align-middle fw-semibold text-end py-2"> <td class="status align-middle fw-semibold text-end py-2">
<span class="badge badge-phoenix fs-10 badge-phoenix-success">sent</span> <span class="badge badge-phoenix fs-10 badge-phoenix-success">{%trans "sent"%}</span>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -670,25 +670,25 @@
scope="col" scope="col"
data-sort="subject" data-sort="subject"
style="width:31%; style="width:31%;
min-width:350px">Subject</th> min-width:350px">{% trans "Subject" %}</th>
<th class="sort align-middle pe-3 text-uppercase" <th class="sort align-middle pe-3 text-uppercase"
scope="col" scope="col"
data-sort="sent" data-sort="sent"
style="width:15%; style="width:15%;
min-width:130px">Sent by</th> min-width:130px">{% trans "Sent by" %}</th>
<th class="sort align-middle text-start text-uppercase" <th class="sort align-middle text-start text-uppercase"
scope="col" scope="col"
data-sort="date" data-sort="date"
style="min-width:165px">Date</th> style="min-width:165px">{% trans "Date" %}</th>
<th class="sort align-middle pe-0 text-uppercase" <th class="sort align-middle pe-0 text-uppercase"
scope="col" scope="col"
style="width:15%; style="width:15%;
min-width:100px">Action</th> min-width:100px">{% trans "Action" %}</th>
<th class="sort align-middle text-end text-uppercase" <th class="sort align-middle text-end text-uppercase"
scope="col" scope="col"
data-sort="status" data-sort="status"
style="width:15%; style="width:15%;
min-width:100px">Status</th> min-width:100px">{% trans "Status" %}</th>
</tr> </tr>
</thead> </thead>
<tbody class="list" id="drafts-email-table-body"> <tbody class="list" id="drafts-email-table-body">
@ -777,20 +777,20 @@
scope="col" scope="col"
data-sort="subject" data-sort="subject"
style="width:31%; style="width:31%;
min-width:100px">Name</th> min-width:100px">{%trans "Name"%}</th>
<th class="sort align-middle pe-3 text" <th class="sort align-middle pe-3 text"
scope="col" scope="col"
data-sort="sent" data-sort="sent"
style="width:15%; style="width:15%;
min-width:400px">Note</th> min-width:400px">{%trans "Note"%}</th>
<th class="sort align-middle text-start" <th class="sort align-middle text-start"
scope="col" scope="col"
data-sort="date" data-sort="date"
style="min-width:100px">Due Date</th> style="min-width:100px">{%trans "Due Date"%}</th>
<th class="sort align-middle text-start" <th class="sort align-middle text-start"
scope="col" scope="col"
data-sort="date" data-sort="date"
style="min-width:100px">Completed</th> style="min-width:100px">{%trans "Completed"%}</th>
</tr> </tr>
</thead> </thead>
<tbody class="list taskTable" id="all-tasks-table-body"> <tbody class="list taskTable" id="all-tasks-table-body">
@ -802,9 +802,7 @@
</div> </div>
<div class="row align-items-center justify-content-between py-2 pe-0 fs-9 mt-3"> <div class="row align-items-center justify-content-between py-2 pe-0 fs-9 mt-3">
<div class="col-auto d-flex"> <div class="col-auto d-flex">
<a class="nav-link px-3 d-block"
href="{% url 'appointment:get_user_appointments' %}"> <span class="me-2 text-body align-bottom" data-feather="calendar"></span>{{ _("View in Calendar") }}
</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -686,7 +686,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="tab-pane fade" <div class="tab-pane fade"
id="tab-emails" id="tab-emails"
role="tabpanel" role="tabpanel"
@ -694,7 +694,7 @@
<h2 class="mb-4">Emails</h2> <h2 class="mb-4">Emails</h2>
{% if perms.inventory.change_opportunity %} {% if perms.inventory.change_opportunity %}
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
{% if opportunity.lead %} {% if opportunity.lead %}
<button class="btn btn-phoenix-primary btn-sm" <button class="btn btn-phoenix-primary btn-sm"
type="button" type="button"

View File

@ -62,7 +62,7 @@
{% if form.stage.errors %}<div class="invalid-feedback d-block">{{ form.stage.errors }}</div>{% endif %} {% if form.stage.errors %}<div class="invalid-feedback d-block">{{ form.stage.errors }}</div>{% endif %}
</div> </div>
<!-- Amount Field --> <!-- Amount Field -->
<div class="mb-4"> {% comment %} <div class="mb-4">
<label class="form-label" for="{{ form.amount.id_for_label }}"> <label class="form-label" for="{{ form.amount.id_for_label }}">
{{ form.amount.label }} {{ form.amount.label }}
<span class="text-danger">*</span> <span class="text-danger">*</span>
@ -72,9 +72,9 @@
{{ form.amount|add_class:"form-control" }} {{ form.amount|add_class:"form-control" }}
</div> </div>
{% if form.amount.errors %}<div class="invalid-feedback d-block">{{ form.amount.errors }}</div>{% endif %} {% if form.amount.errors %}<div class="invalid-feedback d-block">{{ form.amount.errors }}</div>{% endif %}
</div> </div> {% endcomment %}
<!-- Probability Field --> <!-- Probability Field -->
<div class="mb-4"> {% comment %} <div class="mb-4">
<label class="form-label" for="{{ form.probability.id_for_label }}"> <label class="form-label" for="{{ form.probability.id_for_label }}">
{{ form.probability.label }} {{ form.probability.label }}
<span class="text-danger">*</span> <span class="text-danger">*</span>
@ -106,10 +106,10 @@
{% if form.expected_revenue.errors %} {% if form.expected_revenue.errors %}
<div class="invalid-feedback d-block">{{ form.expected_revenue.errors }}</div> <div class="invalid-feedback d-block">{{ form.expected_revenue.errors }}</div>
{% endif %} {% endif %}
</div> </div> {% endcomment %}
<!-- Closing Date --> <!-- Closing Date -->
<div class="mb-5"> <div class="mb-5">
<label class="form-label" for="{{ form.closing_date.id_for_label }}">{{ form.closing_date.label }}</label> <label class="form-label" for="{{ form.expected_close_date.id_for_label }}">{{ form.expected_close_date.label }}</label>
<div class="input-group"> <div class="input-group">
{{ form.expected_close_date|add_class:"form-control" }} {{ form.expected_close_date|add_class:"form-control" }}
<span class="input-group-text"><span class="far fa-calendar"></span></span> <span class="input-group-text"><span class="far fa-calendar"></span></span>

View File

@ -75,7 +75,7 @@
{% block content %} {% block content %}
<div class="container mt-4"> <div class="container mt-4">
<h2> <h2>
Upload Cars CSV <i class="fa-solid fa-file-csv text-primary"></i> {% trans 'Upload Cars CSV' %} <i class="fa-solid fa-file-csv text-primary"></i>
</h2> </h2>
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<a href="{% static 'sample/cars_sample.csv' %}" <a href="{% static 'sample/cars_sample.csv' %}"

View File

@ -194,11 +194,12 @@
{% if perms.inventory.add_carregistration %} {% if perms.inventory.add_carregistration %}
<button type="button" <button type="button"
class="btn btn-sm btn-phoenix-success" class="btn btn-sm btn-phoenix-success"
data-bs-toggle="modal"
data-bs-target="#mainModal"
hx-get="{% url 'add_registration' request.dealer.slug car.slug %}" hx-get="{% url 'add_registration' request.dealer.slug car.slug %}"
hx-target=".main-modal-body" hx-target=".main-modal-body"
hx-swap="innerHTML">{% trans 'Add' %}</button> hx-swap="innerHTML"
data-bs-toggle="modal"
data-bs-target="#mainModal"
>{% trans 'Add' %}</button>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
@ -214,10 +215,10 @@
{{ car.location.showroom.get_local_name }} {{ car.location.showroom.get_local_name }}
{% endif %} {% endif %}
{% if perms.inventory.add_cartransfer %} {% if perms.inventory.add_cartransfer %}
<a href="{% url 'update_car_location' car.slug car.location.pk %}" {% comment %} <a href="{% url 'update_car_location' car.slug car.location.pk %}"
class="btn btn-phoenix-danger btn-sm"> class="btn btn-phoenix-danger btn-sm">
{% trans "transfer"|capfirst %} {% trans "transfer"|capfirst %}
</a> </a> {% endcomment %}
{% endif %} {% endif %}
{% else %} {% else %}
{% trans "No location available." %} {% trans "No location available." %}

View File

@ -25,7 +25,7 @@
</style> </style>
{% endblock customCSS %} {% endblock customCSS %}
{% block content %} {% block content %}
{% if cars or request.GET.q %}
<div class="container-fluid" id="projectSummary"> <div class="container-fluid" id="projectSummary">
<div class="row g-3 justify-content-between align-items-end mb-4"> <div class="row g-3 justify-content-between align-items-end mb-4">
<div class="col-12 col-sm-auto"> <div class="col-12 col-sm-auto">
@ -105,7 +105,7 @@
<input class="form-control search-input search" <input class="form-control search-input search"
name="search" name="search"
type="search" type="search"
placeholder="Search" placeholder="{% trans 'Search' %}"
aria-label="Search" aria-label="Search"
hx-get="{% url 'car_list' request.dealer.slug %}" hx-get="{% url 'car_list' request.dealer.slug %}"
hx-trigger="keyup changed delay:500ms" hx-trigger="keyup changed delay:500ms"
@ -169,17 +169,17 @@
hx-get="{% url 'car_list' request.dealer.slug %}" hx-get="{% url 'car_list' request.dealer.slug %}"
hx-include=".make,.model,.year,.car_status" hx-include=".make,.model,.year,.car_status"
hx-indicator=".htmx-indicator" hx-indicator=".htmx-indicator"
hx-target=".table-responsive" hx-target=".table-responsive1"
hx-select=".table-responsive" hx-select=".table-responsive1"
hx-swap="outerHTML show:window:top" hx-swap="outerHTML show:window:top"
class="btn btn-sm btn-phoenix-primary ms-1" class="btn btn-sm btn-phoenix-primary ms-1"
hx-on::before-request="filter_before_request()" hx-on::before-request="filter_before_request()"
hx-on::after-request="filter_after_request()">{{ _("Search") }}</button> hx-on::after-request="filter_after_request()">{{ _("Search") }}</button>
</div> </div>
<div class="table-responsive scrollbar"> <div class="table-responsive1 scrollbar">
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9"> <div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9">
<div class="d-flex" <div class="d-flex"
hx-boost="true" hx-boost="false"
hx-push-url="false" hx-push-url="false"
hx-include=".make,.model,.year,.car_status" hx-include=".make,.model,.year,.car_status"
hx-target=".table-responsive" hx-target=".table-responsive"
@ -301,10 +301,6 @@
</div> </div>
</div> </div>
</div> </div>
{% else %}
{% url "car_add" request.dealer.slug as create_car_url %}
{% include "empty-illustration-page.html" with value="car" url=create_car_url %}
{% endif %}
{% endblock %} {% endblock %}
{% block customJS%} {% block customJS%}
<script> <script>

View File

@ -169,7 +169,7 @@
class="form-control form-control-sm" class="form-control form-control-sm"
required required
placeholder="email@example.com" placeholder="email@example.com"
value="{{ request.dealer.email }}"> value="{{ request.dealer.user.email }}">
</div> </div>
<label class="form-label" for="phone">{{ _("Phone Number") }}</label> <label class="form-label" for="phone">{{ _("Phone Number") }}</label>
<div class="input-group"> <div class="input-group">

View File

@ -1,5 +1,15 @@
{% load i18n %}
<div class="form-group" id="form-{{ name }}"> <div class="form-group" id="form-{{ name }}">
<label for="{{ name }}">{{ name|capfirst }}</label> {% if name == "make" %}
<label for="{{ name }}">{% trans "Make" %}</label>
{% elif name == "model" %}
<label for="{{ name }}">{% trans "Model" %}</label>
{% elif name == "serie" %}
<label for="{{ name }}">{% trans "Serie" %}</label>
{% elif name == "trim" %}
<label for="{{ name }}">{% trans "Trim" %}</label>
{% endif %}
<select class="form-control" <select class="form-control"
name="{{ name }}" name="{{ name }}"
id="{{ name }}" id="{{ name }}"

View File

@ -31,7 +31,7 @@
<thead> <thead>
<tr class="bg-body-highlight"> <tr class="bg-body-highlight">
<th scope="col">{% trans "Name" %}</th> <th scope="col">{% trans "Name" %}</th>
<th scope="col">{% trans "Quatnity" %}</th> <th scope="col">{% trans "Quantity" %}</th>
<th scope="col">{% trans "Unit Cost" %}</th> <th scope="col">{% trans "Unit Cost" %}</th>
<th scope="col">{% trans "Is Data Uploaded ?" %}</th> <th scope="col">{% trans "Is Data Uploaded ?" %}</th>
</tr> </tr>

View File

@ -128,9 +128,9 @@
<h2 class="mb-7">{{ _("Pricing") }}</h2> <h2 class="mb-7">{{ _("Pricing") }}</h2>
<div class="row g-3 mb-7 mb-lg-11"> <div class="row g-3 mb-7 mb-lg-11">
{% for plan in plan_list %} {% for plan in plan_list %}
<div class="col-lg-4" onclick="window.location='{% url "account_signup" %}';"> <div class="col-lg-4" onclick="window.location='{% url "account_signup" %}';">
<input type="radio" <input type="radio"
class="btn-check" class="btn-check"
name="selected_plan" name="selected_plan"
@ -144,7 +144,7 @@
<h3 class="h6 mb-3"> <h3 class="h6 mb-3">
{{ plan.planpricing_set.first.price }} {{ plan.planpricing_set.first.price }}
<span class="icon-saudi_riyal"></span> <span class="icon-saudi_riyal"></span>
<span class="fs-8 fw-normal">/ {{ _("Per month") }}</span> <span class="fs-8 fw-normal">/{{plan.planpricing_set.first.pricing.period}} {{ _("month") }}</span>
</h3> </h3>
<h5 class="mb-3 h6">{{ _("Included") }}</h5> <h5 class="mb-3 h6">{{ _("Included") }}</h5>
<ul class="fa-ul ps-3 m-0"> <ul class="fa-ul ps-3 m-0">
@ -158,9 +158,9 @@
</div> </div>
</div> </div>
</label> </label>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>