update customer and organization & staffmember
This commit is contained in:
parent
21089f8995
commit
a96b1131d3
@ -1,5 +1,5 @@
|
||||
from django.core.cache import cache
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.auth.models import Group
|
||||
from appointment.models import Appointment, Service, StaffMember
|
||||
from django.urls import reverse
|
||||
@ -39,14 +39,14 @@ from .models import (
|
||||
InteriorColors,
|
||||
# SaleQuotation,
|
||||
CarLocation,
|
||||
|
||||
|
||||
Representative,
|
||||
|
||||
|
||||
# SaleQuotationCar,
|
||||
AdditionalServices,
|
||||
Staff,
|
||||
Opportunity,
|
||||
|
||||
|
||||
Lead,
|
||||
Activity,
|
||||
Notes,
|
||||
@ -56,7 +56,7 @@ from .models import (
|
||||
DealerSettings
|
||||
)
|
||||
from django_ledger import models as ledger_models
|
||||
from django.forms import (
|
||||
from django.forms import (
|
||||
DateInput,
|
||||
DateTimeInput,
|
||||
)
|
||||
@ -86,7 +86,7 @@ class StaffForm(forms.ModelForm):
|
||||
label="Email",
|
||||
widget=forms.EmailInput(attrs={"class": "form-control form-control-sm"}),
|
||||
)
|
||||
|
||||
|
||||
service_offered = forms.ModelMultipleChoiceField(
|
||||
label="Services Offered",
|
||||
widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
|
||||
@ -130,22 +130,17 @@ class DealerForm(forms.ModelForm):
|
||||
|
||||
class CustomerForm(forms.Form):
|
||||
first_name = forms.CharField()
|
||||
middle_name = forms.CharField()
|
||||
last_name = forms.CharField()
|
||||
national_id = forms.CharField(max_length=10)
|
||||
email = forms.EmailField()
|
||||
phone_number = PhoneNumberField(region="SA")
|
||||
address = forms.CharField()
|
||||
|
||||
|
||||
class OrganizationForm(forms.Form):
|
||||
name = forms.CharField()
|
||||
arabic_name = forms.CharField()
|
||||
email = forms.EmailField()
|
||||
phone_number = PhoneNumberField(region="SA")
|
||||
crn = forms.CharField()
|
||||
vrn = forms.CharField()
|
||||
national_id = forms.CharField(max_length=10,required=False)
|
||||
crn = forms.CharField(required=False)
|
||||
vrn = forms.CharField(required=False)
|
||||
address = forms.CharField()
|
||||
|
||||
|
||||
class OrganizationForm(CustomerForm):
|
||||
contact_person = forms.CharField(required=False)
|
||||
logo = forms.ImageField(required=False)
|
||||
|
||||
@ -448,7 +443,7 @@ class CarSelectionTable(tables.Table):
|
||||
|
||||
|
||||
class WizardForm1(forms.Form):
|
||||
hx_attrs = {
|
||||
hx_attrs = {
|
||||
"hx-post":"",
|
||||
"hx-target": "#wizardValidationForm1",
|
||||
"hx-select": "#wizardValidationForm1",
|
||||
@ -516,10 +511,10 @@ class WizardForm1(forms.Form):
|
||||
),
|
||||
error_messages={
|
||||
"required": _("You must accept the terms and privacy policy."),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data.get("email")
|
||||
if email:
|
||||
@ -528,7 +523,7 @@ class WizardForm1(forms.Form):
|
||||
_("An account with this email already exists.")
|
||||
)
|
||||
return email
|
||||
|
||||
|
||||
def clean_confirm_password(self):
|
||||
password = self.cleaned_data.get("password")
|
||||
confirm_password = self.cleaned_data.get("confirm_password")
|
||||
@ -569,7 +564,7 @@ class WizardForm2(forms.Form):
|
||||
phone_number = PhoneNumberField(
|
||||
label=_("Phone Number"),
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
attrs={
|
||||
"placeholder": _("Phone"),
|
||||
}
|
||||
),
|
||||
@ -578,7 +573,7 @@ class WizardForm2(forms.Form):
|
||||
"required": _("This field is required."),
|
||||
"invalid": _("Phone number must be in the format 05xxxxxxxx"),
|
||||
},
|
||||
required=True,
|
||||
required=True,
|
||||
)
|
||||
|
||||
|
||||
@ -741,6 +736,8 @@ class LeadForm(forms.ModelForm):
|
||||
"address",
|
||||
"id_car_make",
|
||||
"id_car_model",
|
||||
"crn",
|
||||
"vrn",
|
||||
"year",
|
||||
"source",
|
||||
"channel",
|
||||
@ -761,7 +758,7 @@ class LeadForm(forms.ModelForm):
|
||||
class ScheduleForm(forms.ModelForm):
|
||||
scheduled_at = forms.DateTimeField(
|
||||
widget=DateTimeInput(attrs={"type": "datetime-local"})
|
||||
)
|
||||
)
|
||||
class Meta:
|
||||
model = Schedule
|
||||
fields = ["purpose", "scheduled_type", "scheduled_at", "duration", "notes",]
|
||||
@ -788,7 +785,7 @@ class OpportunityForm(forms.ModelForm):
|
||||
class InvoiceModelCreateForm(InvoiceModelCreateFormBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
self.fields["cash_account"].widget = forms.HiddenInput()
|
||||
self.fields["prepaid_account"].widget = forms.HiddenInput()
|
||||
self.fields["unearned_account"].widget = forms.HiddenInput()
|
||||
@ -806,7 +803,7 @@ class InvoiceModelCreateForm(InvoiceModelCreateFormBase):
|
||||
class BillModelCreateForm(BillModelCreateFormBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
self.fields["cash_account"].widget = forms.HiddenInput()
|
||||
self.fields["prepaid_account"].widget = forms.HiddenInput()
|
||||
self.fields["unearned_account"].widget = forms.HiddenInput()
|
||||
@ -877,8 +874,8 @@ class OpportunityStatusForm(forms.Form):
|
||||
"hx-select": ".other-information",
|
||||
"hx-swap": "outerHTML",
|
||||
"hx-on::after-request": "this.setAttribute('disabled','true')",
|
||||
"disabled": "disabled",
|
||||
}
|
||||
"disabled": "disabled",
|
||||
}
|
||||
),
|
||||
required=True,
|
||||
)
|
||||
@ -887,7 +884,7 @@ class OpportunityStatusForm(forms.Form):
|
||||
choices=Stage.choices,
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
"class": "form-control form-control-sm",
|
||||
"class": "form-control form-control-sm",
|
||||
"hx-target": ".other-information",
|
||||
"hx-select": ".other-information",
|
||||
"hx-swap": "outerHTML",
|
||||
@ -895,9 +892,9 @@ class OpportunityStatusForm(forms.Form):
|
||||
"disabled": "disabled",
|
||||
}
|
||||
),
|
||||
required=True,
|
||||
)
|
||||
|
||||
required=True,
|
||||
)
|
||||
|
||||
class GroupForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CustomGroup
|
||||
@ -912,11 +909,11 @@ class PermissionForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
cache.set('permissions_queryset', Permission.objects.filter(content_type__app_label__in=["inventory","django_ledger"]), 60*60)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Permission
|
||||
fields = ["name"]
|
||||
|
||||
|
||||
class UserGroupForm(forms.ModelForm):
|
||||
name = forms.ModelMultipleChoiceField(
|
||||
queryset= CustomGroup.objects.all(),
|
||||
@ -927,10 +924,10 @@ class UserGroupForm(forms.ModelForm):
|
||||
model = CustomGroup
|
||||
fields = ["name"]
|
||||
|
||||
class DealerSettingsForm(forms.ModelForm):
|
||||
class DealerSettingsForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = DealerSettings
|
||||
fields = "__all__"
|
||||
|
||||
class LeadTransferForm(forms.Form):
|
||||
transfer_to = forms.ModelChoiceField(label="to",queryset=Staff.objects.all())
|
||||
transfer_to = forms.ModelChoiceField(label="to",queryset=Staff.objects.all())
|
||||
23
inventory/migrations/0053_lead_crn_lead_vrn.py
Normal file
23
inventory/migrations/0053_lead_crn_lead_vrn.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.17 on 2025-03-01 21:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0052_lead_lead_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='lead',
|
||||
name='crn',
|
||||
field=models.CharField(blank=True, max_length=10, null=True, unique=True, verbose_name='Commercial Registration Number'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='lead',
|
||||
name='vrn',
|
||||
field=models.CharField(blank=True, max_length=15, null=True, unique=True, verbose_name='VAT Registration Number'),
|
||||
),
|
||||
]
|
||||
@ -454,14 +454,14 @@ class Car(models.Model):
|
||||
self.status = CarStatusChoices.SOLD
|
||||
self.save()
|
||||
Activity.objects.create(dealer=dealer,content_object=self, notes="Car Sold",created_by=request.user,activity_type=ActionChoices.SALE_CAR)
|
||||
|
||||
def cancel_reservation(self):
|
||||
|
||||
def cancel_reservation(self):
|
||||
if self.reservations.exists():
|
||||
self.reservations.all().delete()
|
||||
def cancel_transfer(self):
|
||||
if self.transfer_logs.exists():
|
||||
self.transfer_logs.all().delete()
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"vin": self.vin,
|
||||
@ -580,9 +580,9 @@ class CarFinance(models.Model):
|
||||
verbose_name=_("Discount Amount"),
|
||||
default=Decimal("0.00"),
|
||||
)
|
||||
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
def total(self):
|
||||
return self.selling_price
|
||||
|
||||
@property
|
||||
@ -947,7 +947,7 @@ class StaffTypes(models.TextChoices):
|
||||
|
||||
class Staff(models.Model, LocalizedNameMixin):
|
||||
staff_member = models.OneToOneField(StaffMember, on_delete=models.CASCADE, related_name="staff")
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="staff")
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="staff")
|
||||
name = models.CharField(max_length=255, verbose_name=_("Name"))
|
||||
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"))
|
||||
@ -957,23 +957,26 @@ class Staff(models.Model, LocalizedNameMixin):
|
||||
|
||||
objects = StaffUserManager()
|
||||
|
||||
@property
|
||||
def email(self):
|
||||
return self.staff_member.user.email
|
||||
@property
|
||||
def user(self):
|
||||
return self.staff_member.user
|
||||
|
||||
|
||||
@property
|
||||
def groups(self):
|
||||
def groups(self):
|
||||
return [x.customgroup for x in self.user.groups.all()]
|
||||
|
||||
|
||||
|
||||
|
||||
def clear_groups(self):
|
||||
return self.user.groups.clear()
|
||||
|
||||
|
||||
def add_group(self,group):
|
||||
try:
|
||||
self.user.groups.add(group)
|
||||
except Exception as e:
|
||||
pass
|
||||
pass
|
||||
class Meta:
|
||||
verbose_name = _("Staff")
|
||||
verbose_name_plural = _("Staff")
|
||||
@ -1197,6 +1200,12 @@ class Lead(models.Model):
|
||||
channel = models.CharField(
|
||||
max_length=50, choices=Channel.choices, verbose_name=_("Channel")
|
||||
)
|
||||
crn = models.CharField(
|
||||
max_length=10, unique=True, verbose_name=_("Commercial Registration Number"), blank=True, null=True
|
||||
)
|
||||
vrn = models.CharField(
|
||||
max_length=15, unique=True, verbose_name=_("VAT Registration Number"), blank=True, null=True
|
||||
)
|
||||
address = models.CharField(max_length=50, verbose_name=_("address"))
|
||||
staff = models.ForeignKey(
|
||||
Staff,
|
||||
@ -1251,7 +1260,7 @@ class Lead(models.Model):
|
||||
@property
|
||||
def full_name(self):
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
def convert_to_customer(self,entity):
|
||||
def convert_to_customer(self,entity,lead):
|
||||
customer = entity.get_customers().filter(email=self.email).first()
|
||||
if entity and not customer:
|
||||
customer = entity.create_customer(
|
||||
@ -1262,10 +1271,13 @@ class Lead(models.Model):
|
||||
"phone": self.phone_number,
|
||||
"email": self.email,
|
||||
}
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
customer.additional_info.update({"info":self.to_dict()})
|
||||
customer.additional_info.update({"type":"customer"})
|
||||
if lead.lead_type == "organization":
|
||||
customer.additional_info.update({"type":"organization"})
|
||||
else:
|
||||
customer.additional_info.update({"type":"customer"})
|
||||
customer.save()
|
||||
self.customer = customer
|
||||
self.status = Status.QUALIFIED
|
||||
@ -1415,7 +1427,7 @@ class Email(models.Model):
|
||||
from_email = models.TextField(verbose_name=_("From Email"),null=True,blank=True)
|
||||
to_email = models.TextField(verbose_name=_("To Email"),null=True,blank=True)
|
||||
subject = models.TextField(verbose_name=_("Subject"),null=True,blank=True)
|
||||
message = models.TextField(verbose_name=_("Message"),null=True,blank=True)
|
||||
message = models.TextField(verbose_name=_("Message"),null=True,blank=True)
|
||||
status = models.CharField(max_length=20, choices=EmailStatus.choices, verbose_name=_("Status"),default=EmailStatus.OPEN)
|
||||
created_by = models.ForeignKey(
|
||||
User, on_delete=models.DO_NOTHING, related_name="emails_created"
|
||||
@ -1749,7 +1761,7 @@ class UserActivityLog(models.Model):
|
||||
def __str__(self):
|
||||
return f"{self.user.email} - {self.action} - {self.timestamp}"
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
class SaleOrder(models.Model):
|
||||
estimate = models.ForeignKey(
|
||||
EstimateModel,
|
||||
on_delete=models.CASCADE,
|
||||
@ -1772,12 +1784,12 @@ class SaleOrder(models.Model):
|
||||
comments = models.TextField(blank=True, null=True)
|
||||
formatted_order_id = models.CharField(max_length=10, unique=True, editable=False)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created']
|
||||
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.formatted_order_id:
|
||||
if not self.formatted_order_id:
|
||||
last_order = SaleOrder.objects.order_by('-id').first()
|
||||
if last_order:
|
||||
next_id = last_order.id + 1
|
||||
@ -1792,17 +1804,17 @@ class SaleOrder(models.Model):
|
||||
@property
|
||||
def full_name(self):
|
||||
return f"{self.customer.customer_name}"
|
||||
|
||||
|
||||
@property
|
||||
def price(self):
|
||||
return self.car.finances.selling_price
|
||||
|
||||
|
||||
@property
|
||||
def items(self):
|
||||
if self.estimate.get_itemtxs_data():
|
||||
return self.estimate.get_itemtxs_data()[0]
|
||||
return []
|
||||
|
||||
|
||||
@property
|
||||
def customer(self):
|
||||
return self.estimate.customer
|
||||
@ -1811,27 +1823,27 @@ class CustomGroup(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="groups")
|
||||
group = models.OneToOneField("auth.Group", verbose_name=_(""), on_delete=models.CASCADE)
|
||||
|
||||
|
||||
@property
|
||||
def users(self):
|
||||
return self.group.user_set.all()
|
||||
|
||||
|
||||
@property
|
||||
def permissions(self):
|
||||
return self.group.permissions.all()
|
||||
|
||||
|
||||
def clear_permissions(self):
|
||||
self.group.permissions.clear()
|
||||
|
||||
|
||||
def add_permission(self, permission):
|
||||
try:
|
||||
self.group.permissions.add(permission)
|
||||
except Permission.DoesNotExist:
|
||||
pass
|
||||
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
def set_default_manager_permissions(self):
|
||||
self.clear_permissions()
|
||||
try:
|
||||
@ -1839,7 +1851,7 @@ class CustomGroup(models.Model):
|
||||
self.add_permission(perm)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
# def set_default_inventory_permissions(self):
|
||||
# self.clear_permissions()
|
||||
# allowed_models = ["car","carequipment","interiorcolors","exteriorcolors","carcolors","carlocation","customcard"]
|
||||
@ -1849,14 +1861,14 @@ class CustomGroup(models.Model):
|
||||
# allowed_models = ["car","carfinance","carlocation","customcard"]
|
||||
# allowed_models_ledger = ["accountmodel","chartofaccountmodel","customcard","billmodel"]
|
||||
# self.set_permissions(allowed_models=allowed_models,other_perms=['view_carfinance'])
|
||||
# self.set_permissions(app="django_ledger",allowed_models=allowed_models_ledger)
|
||||
|
||||
# self.set_permissions(app="django_ledger",allowed_models=allowed_models_ledger)
|
||||
|
||||
def set_default_permissions(self):
|
||||
self.clear_permissions()
|
||||
if self.name == "Manager":
|
||||
self.set_permissions(app="inventory",allowed_models=["car","carfinance","carlocation","customcard"])
|
||||
self.set_permissions(app="django_ledger",allowed_models=["accountmodel","chartofaccountmodel","customcard","billmodel"])
|
||||
elif self.name == "Inventory":
|
||||
elif self.name == "Inventory":
|
||||
self.set_permissions(allowed_models=["car","carequipment","interiorcolors","exteriorcolors","carcolors","carlocation","customcard"]
|
||||
,other_perms=['view_carfinance'])
|
||||
elif self.name == "Sales":
|
||||
@ -1869,9 +1881,9 @@ class CustomGroup(models.Model):
|
||||
elif self.name == "Agent":
|
||||
# Todo : set permissions for agent
|
||||
pass
|
||||
|
||||
|
||||
def set_permissions(self,app="inventory", allowed_models=[],other_perms=[]):
|
||||
|
||||
def set_permissions(self,app="inventory", allowed_models=[],other_perms=[]):
|
||||
try:
|
||||
for perm in Permission.objects.filter(content_type__app_label=app,content_type__model__in=allowed_models):
|
||||
self.add_permission(perm)
|
||||
@ -1880,19 +1892,19 @@ class CustomGroup(models.Model):
|
||||
self.add_permission(perm)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class DealerSettings(models.Model):
|
||||
dealer = models.OneToOneField(Dealer, on_delete=models.CASCADE, related_name="settings",null=True, blank=True)
|
||||
|
||||
|
||||
class DealerSettings(models.Model):
|
||||
dealer = models.OneToOneField(Dealer, on_delete=models.CASCADE, related_name="settings",null=True, blank=True)
|
||||
invoice_cash_account = models.ForeignKey(AccountModel,related_name="invoice_cash", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
invoice_prepaid_account = models.ForeignKey(AccountModel,related_name="invoice_prepaid", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
invoice_unearned_account = models.ForeignKey(AccountModel,related_name="invoice_unearned", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
|
||||
|
||||
bill_cash_account = models.ForeignKey(AccountModel,related_name="bill_cash", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
bill_prepaid_account = models.ForeignKey(AccountModel,related_name="bill_prepaid", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
bill_unearned_account = models.ForeignKey(AccountModel,related_name="bill_unearned", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
additional_info = models.JSONField(default=dict,null=True,blank=True)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"Settings for {self.dealer}"
|
||||
@ -10,7 +10,7 @@ from calendar import month_name
|
||||
from pyzbar.pyzbar import decode
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
#####################################################################
|
||||
from django.db.models.deletion import RestrictedError
|
||||
from django.db.models.deletion import RestrictedError
|
||||
# Django
|
||||
from django.db.models import Q
|
||||
from django.conf import settings
|
||||
@ -28,7 +28,7 @@ from django.db.models import Count, F, Value
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone, translation
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.core.files.storage import default_storage
|
||||
@ -137,7 +137,7 @@ logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
class Hash(Func):
|
||||
function = 'get_hash'
|
||||
function = 'get_hash'
|
||||
|
||||
def switch_language(request):
|
||||
language = request.GET.get("language", "en")
|
||||
@ -179,12 +179,12 @@ def dealer_signup(request, *args, **kwargs):
|
||||
form1 = forms.WizardForm1()
|
||||
form2 = forms.WizardForm2()
|
||||
form3 = forms.WizardForm3()
|
||||
|
||||
|
||||
if request.method == "POST":
|
||||
if "Hx-Request" in request.headers:
|
||||
form1 = forms.WizardForm1(request.POST)
|
||||
if "Hx-Request" in request.headers:
|
||||
form1 = forms.WizardForm1(request.POST)
|
||||
return render(request,"account/signup-wizard.html",{"form1": form1, "form2": form2, "form3": form3})
|
||||
|
||||
|
||||
data = json.loads(request.body)
|
||||
wf1 = data.get("wizardValidationForm1")
|
||||
wf2 = data.get("wizardValidationForm2")
|
||||
@ -207,7 +207,7 @@ def dealer_signup(request, *args, **kwargs):
|
||||
user = User.objects.create(username=email, email=email)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
|
||||
StaffMember.objects.create(user=user)
|
||||
models.Dealer.objects.create(
|
||||
user=user,
|
||||
name=name,
|
||||
@ -219,7 +219,7 @@ def dealer_signup(request, *args, **kwargs):
|
||||
)
|
||||
return JsonResponse(
|
||||
{"message": "User created successfully."}, status=200
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
return JsonResponse({"error": str(e)}, status=400)
|
||||
return render(request,"account/signup-wizard.html",{"form1": form1, "form2": form2, "form3": form3})
|
||||
@ -309,7 +309,6 @@ class HomeView(TemplateView):
|
||||
"total_selling_price": 0,
|
||||
"total_profit": 0,
|
||||
})
|
||||
|
||||
return context
|
||||
|
||||
class TestView(TemplateView):
|
||||
@ -516,7 +515,7 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
||||
|
||||
logger.info(
|
||||
f"VIN decoded using {decoding_method}: Make={manufacturer_name}, Model={model_name}, Year={year_model}"
|
||||
)
|
||||
)
|
||||
|
||||
if not car_make:
|
||||
return JsonResponse(
|
||||
@ -724,7 +723,7 @@ class CarColorCreate(LoginRequiredMixin, CreateView):
|
||||
|
||||
def form_valid(self, form):
|
||||
car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
||||
form.instance.car = car
|
||||
form.instance.car = car
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
@ -744,7 +743,7 @@ class CarListView(LoginRequiredMixin, ListView):
|
||||
context = super().get_context_data(**kwargs)
|
||||
dealer = get_user_type(self.request)
|
||||
cars = models.Car.objects.filter(dealer=dealer).order_by("receiving_date")
|
||||
|
||||
|
||||
context["stats"] = {
|
||||
'all': cars.count(),
|
||||
'available':cars.filter(status='available').count(),
|
||||
@ -764,19 +763,19 @@ class CarListView(LoginRequiredMixin, ListView):
|
||||
if make and model:
|
||||
make_ = models.CarMake.objects.get(id_car_make=int(make))
|
||||
model_ = models.CarModel.objects.get(id_car_model=int(model))
|
||||
context['year'] = models.Car.objects.filter(id_car_make=make_,id_car_model=model_).values_list('year').distinct()
|
||||
context['year'] = models.Car.objects.filter(id_car_make=make_,id_car_model=model_).values_list('year').distinct()
|
||||
return context
|
||||
def get_queryset(self):
|
||||
dealer = get_user_type(self.request)
|
||||
qs = super().get_queryset()
|
||||
qs = super().get_queryset()
|
||||
qs = qs.filter(dealer=dealer)
|
||||
status = self.request.GET.get('status')
|
||||
status = self.request.GET.get('status')
|
||||
search = self.request.GET.get('search')
|
||||
make = self.request.GET.get('make',None)
|
||||
model = self.request.GET.get('model',None)
|
||||
year = self.request.GET.get('year',None)
|
||||
car_status = self.request.GET.get('car_status',None)
|
||||
|
||||
|
||||
if status:
|
||||
qs=qs.filter(status=status)
|
||||
if search:
|
||||
@ -791,7 +790,7 @@ class CarListView(LoginRequiredMixin, ListView):
|
||||
if year:
|
||||
query &= Q(year=year)
|
||||
if car_status:
|
||||
query &= Q(status=car_status)
|
||||
query &= Q(status=car_status)
|
||||
qs = qs.filter(query)
|
||||
return qs
|
||||
|
||||
@ -1247,14 +1246,14 @@ class CustomerDetailView(LoginRequiredMixin, DetailView):
|
||||
dealer = get_user_type(self.request)
|
||||
entity = dealer.entity
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
estimates = entity.get_estimates().filter(customer=self.object)
|
||||
invoices = entity.get_invoices().filter(customer=self.object)
|
||||
# txs = entity. transactions(customer=self.object)
|
||||
total = estimates.count() + invoices.count()
|
||||
context["estimates"] = estimates
|
||||
context["invoices"] = invoices
|
||||
context["total"] = total
|
||||
context["total"] = total
|
||||
return context
|
||||
|
||||
def add_note_to_customer(request, customer_id):
|
||||
@ -1296,14 +1295,13 @@ def CustomerCreateView(request):
|
||||
form = forms.CustomerForm(request.POST)
|
||||
dealer = get_user_type(request)
|
||||
|
||||
if form.is_valid():
|
||||
if form.is_valid():
|
||||
if dealer.entity.get_customers().filter(email=form.cleaned_data["email"]).exists():
|
||||
messages.error(request, _("Customer with this email already exists."))
|
||||
else:
|
||||
# Create customer name
|
||||
customer_name = (
|
||||
f"{form.cleaned_data['first_name']} "
|
||||
f"{form.cleaned_data['middle_name']} "
|
||||
f"{form.cleaned_data['last_name']}"
|
||||
)
|
||||
customer_dict = { x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"}
|
||||
@ -1346,8 +1344,6 @@ def CustomerUpdateView(request, pk):
|
||||
customer_name = (
|
||||
customer_dict["first_name"]
|
||||
+ " "
|
||||
+ customer_dict["middle_name"]
|
||||
+ " "
|
||||
+ customer_dict["last_name"]
|
||||
)
|
||||
|
||||
@ -1367,7 +1363,7 @@ def CustomerUpdateView(request, pk):
|
||||
user.save()
|
||||
except Exception as e:
|
||||
raise Exception(e)
|
||||
|
||||
|
||||
instance.save()
|
||||
messages.success(request, _("Customer updated successfully."))
|
||||
return redirect("customer_list")
|
||||
@ -1384,12 +1380,12 @@ def CustomerUpdateView(request, pk):
|
||||
@login_required
|
||||
def delete_customer(request, pk):
|
||||
customer = get_object_or_404(models.CustomerModel, pk=pk)
|
||||
user = User.objects.get(email=customer.email)
|
||||
user = User.objects.get(email=customer.email)
|
||||
customer.active = False
|
||||
user.is_active = False
|
||||
customer.save()
|
||||
user.save()
|
||||
|
||||
|
||||
messages.success(request, _("Customer deleted successfully."))
|
||||
return redirect("customer_list")
|
||||
|
||||
@ -1457,15 +1453,15 @@ class GroupListView(LoginRequiredMixin, ListView):
|
||||
paginate_by = 10
|
||||
template_name = "groups/group_list.html"
|
||||
|
||||
def get_queryset(self):
|
||||
def get_queryset(self):
|
||||
dealer = get_user_type(self.request)
|
||||
return dealer.groups.all()
|
||||
|
||||
class GroupDetailView(LoginRequiredMixin, DetailView):
|
||||
model = models.CustomGroup
|
||||
template_name = "groups/group_detail.html"
|
||||
context_object_name = "group"
|
||||
|
||||
context_object_name = "group"
|
||||
|
||||
|
||||
class GroupCreateView(
|
||||
LoginRequiredMixin,
|
||||
@ -1501,7 +1497,7 @@ class GroupUpdateView(
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = get_user_type(self.request)
|
||||
instance = form.save(commit=False)
|
||||
instance = form.save(commit=False)
|
||||
instance.group.name = f"{dealer.pk}_{instance.name}"
|
||||
instance.save()
|
||||
return super().form_valid(form)
|
||||
@ -1521,7 +1517,7 @@ def GroupPermissionView(request, pk):
|
||||
for i in permissions:
|
||||
group.add_permission(Permission.objects.get(id=int(i)))
|
||||
messages.success(request, _("Permission added successfully."))
|
||||
return redirect("group_detail", pk=group.pk)
|
||||
return redirect("group_detail", pk=group.pk)
|
||||
form = forms.PermissionForm(initial={"name": group.permissions})
|
||||
return render(request,"groups/group_permission_form.html",{"group": group, "form": form})
|
||||
|
||||
@ -1529,18 +1525,18 @@ def GroupPermissionView(request, pk):
|
||||
|
||||
def UserGroupView(request, pk):
|
||||
staff = get_object_or_404(models.Staff, pk=pk)
|
||||
|
||||
|
||||
if request.method == "POST":
|
||||
form = forms.UserGroupForm(request.POST)
|
||||
groups = request.POST.getlist("name")
|
||||
form = forms.UserGroupForm(request.POST)
|
||||
groups = request.POST.getlist("name")
|
||||
staff.clear_groups()
|
||||
for i in groups:
|
||||
for i in groups:
|
||||
cg = models.CustomGroup.objects.get(id=int(i))
|
||||
staff.add_group(cg.group)
|
||||
|
||||
|
||||
messages.success(request, _("Group added successfully."))
|
||||
return redirect("user_detail", pk=staff.pk)
|
||||
|
||||
|
||||
form = forms.UserGroupForm(initial={"name": staff.groups})
|
||||
form.fields['name'].queryset = models.CustomGroup.objects.filter(dealer=staff.dealer)
|
||||
return render(request,"users/user_group_form.html",{"staff": staff, "form": form})
|
||||
@ -1591,7 +1587,7 @@ class UserCreateView(
|
||||
|
||||
email = form.cleaned_data["email"]
|
||||
password = "Tenhal@123"
|
||||
|
||||
|
||||
user = User.objects.create_user(username=form.cleaned_data["name"], email=email, password=password)
|
||||
user.is_staff = True
|
||||
user.save()
|
||||
@ -1606,7 +1602,7 @@ class UserCreateView(
|
||||
staff.add_group(group)
|
||||
staff.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
|
||||
class UserUpdateView(
|
||||
LoginRequiredMixin,
|
||||
@ -1629,7 +1625,7 @@ class UserUpdateView(
|
||||
form.fields['email'].disabled = True
|
||||
return form
|
||||
def get_initial(self):
|
||||
initial = super().get_initial()
|
||||
initial = super().get_initial()
|
||||
initial['email'] = self.object.staff_member.user.email
|
||||
initial['service_offered'] = self.object.staff_member.services_offered.all()
|
||||
return initial
|
||||
@ -1639,16 +1635,16 @@ class UserUpdateView(
|
||||
self.object.staff_member.services_offered.clear()
|
||||
else:
|
||||
for service in services:
|
||||
self.object.staff_member.services_offered.add(service)
|
||||
|
||||
self.object.staff_member.services_offered.add(service)
|
||||
|
||||
staff = form.save(commit=False)
|
||||
staff.name = form.cleaned_data["name"]
|
||||
staff.arabic_name = form.cleaned_data["arabic_name"]
|
||||
staff.phone_number = form.cleaned_data["phone_number"]
|
||||
staff.staff_type = form.cleaned_data["staff_type"]
|
||||
staff.staff_type = form.cleaned_data["staff_type"]
|
||||
staff.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def UserDeleteview(request, pk):
|
||||
staff = get_object_or_404(models.Staff, pk=pk)
|
||||
staff.staff_member.delete()
|
||||
@ -1700,15 +1696,16 @@ def OrganizationCreateView(request):
|
||||
if CustomerModel.objects.filter(email=request.POST["email"]).exists():
|
||||
messages.error(request, _("An organization with this email already exists."))
|
||||
return redirect("organization_create")
|
||||
|
||||
|
||||
organization_dict = {
|
||||
x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
|
||||
}
|
||||
dealer = get_user_type(request)
|
||||
|
||||
instance = dealer.entity.create_customer(
|
||||
name = organization_dict["first_name"] + " " + organization_dict["last_name"]
|
||||
customer = dealer.entity.create_customer(
|
||||
commit=False,
|
||||
customer_model_kwargs={
|
||||
"customer_name": organization_dict["name"],
|
||||
"customer_name": name,
|
||||
"address_1": organization_dict["address"],
|
||||
"phone": organization_dict["phone_number"],
|
||||
"email": organization_dict["email"],
|
||||
@ -1718,12 +1715,12 @@ def OrganizationCreateView(request):
|
||||
if image:
|
||||
file_name = default_storage.save("images/{}".format(image.name), image)
|
||||
file_url = default_storage.url(file_name)
|
||||
|
||||
|
||||
organization_dict["logo"] = file_url
|
||||
organization_dict["pk"] = str(instance.pk)
|
||||
instance.additional_info["organization_info"] = organization_dict
|
||||
instance.additional_info["type"] = "organization"
|
||||
instance.save()
|
||||
organization_dict["pk"] = str(customer.pk)
|
||||
customer.additional_info.update({"customer_info": organization_dict})
|
||||
customer.additional_info.update({"type": "organization"})
|
||||
customer.save()
|
||||
messages.success(request, _("Organization created successfully."))
|
||||
return redirect("organization_list")
|
||||
else:
|
||||
@ -1732,44 +1729,45 @@ def OrganizationCreateView(request):
|
||||
|
||||
|
||||
def OrganizationUpdateView(request,pk):
|
||||
organization = get_object_or_404(CustomerModel, pk=pk)
|
||||
organization = get_object_or_404(CustomerModel, pk=pk)
|
||||
if request.method == "POST":
|
||||
form = forms.OrganizationForm(request.POST)
|
||||
|
||||
|
||||
organization_dict = {
|
||||
x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
|
||||
}
|
||||
}
|
||||
dealer = get_user_type(request)
|
||||
|
||||
instance = dealer.entity.get_customers().get(
|
||||
pk=organization.additional_info["organization_info"]["pk"]
|
||||
pk=organization.additional_info["customer_info"]["pk"]
|
||||
)
|
||||
instance.customer_name = organization_dict["name"]
|
||||
name = organization_dict["first_name"] + " " + organization_dict["last_name"]
|
||||
instance.customer_name = name
|
||||
instance.address_1 = organization_dict["address"]
|
||||
instance.phone = organization_dict["phone_number"]
|
||||
instance.email = organization_dict["email"]
|
||||
|
||||
|
||||
image = request.FILES.get("logo")
|
||||
if image:
|
||||
file_name = default_storage.save("images/{}".format(image.name), image)
|
||||
file_url = default_storage.url(file_name)
|
||||
file_url = default_storage.url(file_name)
|
||||
organization_dict["logo"] = file_url
|
||||
else:
|
||||
organization_dict["logo"] = organization.additional_info["organization_info"]["logo"]
|
||||
|
||||
organization_dict["logo"] = organization.additional_info["customer_info"]["logo"]
|
||||
|
||||
organization_dict["pk"] = str(instance.pk)
|
||||
instance.additional_info["organization_info"] = organization_dict
|
||||
instance.additional_info["customer_info"] = organization_dict
|
||||
instance.additional_info["type"] = "organization"
|
||||
instance.save()
|
||||
messages.success(request, _("Organization created successfully."))
|
||||
return redirect("organization_list")
|
||||
else:
|
||||
form = forms.OrganizationForm(
|
||||
initial=organization.additional_info["organization_info"] or {}
|
||||
form = forms.OrganizationForm(
|
||||
initial=organization.additional_info["customer_info"] or {}
|
||||
)
|
||||
# form.fields.pop("logo", None)
|
||||
# form.fields.pop("logo", None)
|
||||
return render(request, "organizations/organization_form.html", {"form": form})
|
||||
|
||||
|
||||
|
||||
# class OrganizationDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
# model = models.Organization
|
||||
@ -2047,7 +2045,7 @@ def create_estimate(request,pk=None):
|
||||
dealer = get_user_type(request)
|
||||
entity = dealer.entity
|
||||
|
||||
if request.method == "POST":
|
||||
if request.method == "POST":
|
||||
# try:
|
||||
data = json.loads(request.body)
|
||||
title = data.get("title")
|
||||
@ -2057,13 +2055,13 @@ def create_estimate(request,pk=None):
|
||||
|
||||
items = data.get("item", [])
|
||||
quantities = data.get("quantity", [])
|
||||
|
||||
|
||||
if not all([items, quantities]):
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "Items and Quantities are required"},
|
||||
status=400,
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
if isinstance(quantities, list):
|
||||
if "0" in quantities:
|
||||
return JsonResponse(
|
||||
@ -2084,7 +2082,7 @@ def create_estimate(request,pk=None):
|
||||
if int(quantities) > models.Car.objects.filter(hash=items,status='available').count():
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "Quantity must be less than or equal to the number of cars in stock"},
|
||||
)
|
||||
)
|
||||
estimate = entity.create_estimate(
|
||||
estimate_title=title, customer_model=customer, contract_terms=terms
|
||||
)
|
||||
@ -2105,7 +2103,7 @@ def create_estimate(request,pk=None):
|
||||
items_txs = []
|
||||
for item in items_list:
|
||||
car_instance = ItemModel.objects.filter(additional_info__car_info__hash=item.get("item_id")).all()
|
||||
|
||||
|
||||
for i in car_instance[:int(quantities[0])]:
|
||||
items_txs.append(
|
||||
{
|
||||
@ -2148,19 +2146,19 @@ def create_estimate(request,pk=None):
|
||||
for item in estimate_itemtxs.keys():
|
||||
item_instance = ItemModel.objects.filter(item_number=item).first()
|
||||
instance = models.Car.objects.get(vin=item_instance.name)
|
||||
reserve_car(instance, request)
|
||||
reserve_car(instance, request)
|
||||
|
||||
else:
|
||||
item_instance = ItemModel.objects.filter(additioinal_info__car_info__hash=items).first()
|
||||
instance = models.Car.objects.get(hash=item)
|
||||
response = reserve_car(instance, request)
|
||||
|
||||
|
||||
opportunity_id = data.get("opportunity_id")
|
||||
if opportunity_id != "None":
|
||||
opportunity = models.Opportunity.objects.get(pk=int(opportunity_id))
|
||||
opportunity.estimate = estimate
|
||||
opportunity.save()
|
||||
|
||||
opportunity.save()
|
||||
|
||||
url = reverse("estimate_detail", kwargs={"pk": estimate.pk})
|
||||
return JsonResponse(
|
||||
{
|
||||
@ -2174,14 +2172,14 @@ def create_estimate(request,pk=None):
|
||||
entity_slug=entity.slug, user_model=entity.admin
|
||||
)
|
||||
form.fields["customer"].queryset = entity.get_customers().filter(active=True,additional_info__type="customer")
|
||||
|
||||
|
||||
if pk:
|
||||
opportunity = models.Opportunity.objects.get(pk=pk)
|
||||
customer = opportunity.customer
|
||||
form.initial['customer'] = customer
|
||||
|
||||
|
||||
car_list = models.Car.objects.filter(dealer=dealer,colors__isnull=False,finances__isnull=False,status="available").annotate(color=F('colors__exterior__rgb'),color_name=F('colors__exterior__name')).values_list(
|
||||
'id_car_make__name', 'id_car_model__name','id_car_serie__name','id_car_trim__name','color','color_name','hash').annotate(hash_count=Count('hash')).distinct()
|
||||
'id_car_make__name', 'id_car_model__name','id_car_serie__name','id_car_trim__name','color','color_name','hash').annotate(hash_count=Count('hash')).distinct()
|
||||
context = {
|
||||
"form": form,
|
||||
"items": [
|
||||
@ -2200,7 +2198,7 @@ def create_estimate(request,pk=None):
|
||||
"opportunity_id": pk if pk else None,
|
||||
"customer_count": entity.get_customers().count()
|
||||
}
|
||||
|
||||
|
||||
return render(request, "sales/estimates/estimate_form.html", context)
|
||||
|
||||
|
||||
@ -2214,7 +2212,7 @@ class EstimateDetailView(LoginRequiredMixin, DetailView):
|
||||
if estimate.get_itemtxs_data():
|
||||
calculator = CarFinanceCalculator(estimate)
|
||||
finance_data = calculator.get_finance_data()
|
||||
|
||||
|
||||
kwargs["data"] = finance_data
|
||||
kwargs["invoice"] = (
|
||||
InvoiceModel.objects.all().filter(ce_model=estimate).first()
|
||||
@ -2238,10 +2236,10 @@ def create_sale_order(request, pk):
|
||||
item.item_model.additional_info['car_info']['status'] = 'sold'
|
||||
item.item_model.save()
|
||||
except KeyError:
|
||||
pass
|
||||
pass
|
||||
models.Car.objects.get(vin=item.item_model.name).mark_as_sold(request)
|
||||
|
||||
messages.success(request, "Sale Order created successfully")
|
||||
|
||||
messages.success(request, "Sale Order created successfully")
|
||||
return redirect("estimate_detail", pk=pk)
|
||||
|
||||
form = forms.SaleOrderForm()
|
||||
@ -2363,7 +2361,7 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
|
||||
|
||||
if invoice.get_itemtxs_data():
|
||||
calculator = CarFinanceCalculator(invoice)
|
||||
finance_data = calculator.get_finance_data()
|
||||
finance_data = calculator.get_finance_data()
|
||||
kwargs["data"] = finance_data
|
||||
kwargs["payments"] = JournalEntryModel.objects.filter(
|
||||
ledger=invoice.ledger
|
||||
@ -2464,10 +2462,10 @@ def invoice_create(request, pk):
|
||||
ledger.save()
|
||||
invoice.save()
|
||||
|
||||
|
||||
|
||||
calculator = CarFinanceCalculator(estimate)
|
||||
finance_data = calculator.get_finance_data()
|
||||
|
||||
|
||||
invoice_itemtxs = {
|
||||
i.get("item_number"): {
|
||||
"unit_cost": i.get("total_vat"),
|
||||
@ -2549,11 +2547,11 @@ def PaymentCreateView(request, pk):
|
||||
messages.error(request,"fully paid")
|
||||
return redirect(redirect_url, pk=model.pk)
|
||||
if model.amount_paid + amount > model.amount_due:
|
||||
messages.error(request,"Amount exceeds due amount")
|
||||
messages.error(request,"Amount exceeds due amount")
|
||||
return redirect(redirect_url, pk=model.pk)
|
||||
|
||||
|
||||
try:
|
||||
if invoice:
|
||||
if invoice:
|
||||
set_invoice_payment(dealer, entity, invoice, amount, payment_method)
|
||||
elif bill:
|
||||
set_bill_payment(dealer, entity, bill, amount, payment_method)
|
||||
@ -2703,9 +2701,9 @@ def lead_create(request):
|
||||
if form.is_valid():
|
||||
instance = form.save(commit=False)
|
||||
dealer = get_user_type(request)
|
||||
instance.dealer = dealer
|
||||
instance.dealer = dealer
|
||||
instance.staff = form.cleaned_data.get("staff")
|
||||
|
||||
|
||||
# creating customer in ledger
|
||||
customer = dealer.entity.get_customers().filter(email=instance.email).first()
|
||||
if not customer:
|
||||
@ -2716,7 +2714,7 @@ def lead_create(request):
|
||||
"address_1": instance.address,
|
||||
"phone": instance.phone_number,
|
||||
"email": instance.email,
|
||||
"sales_tax_rate": 0.15,
|
||||
"sales_tax_rate": 0.15,
|
||||
}
|
||||
)
|
||||
customer_info = {
|
||||
@ -2725,7 +2723,9 @@ def lead_create(request):
|
||||
"address": instance.address,
|
||||
"phone_number": str(instance.phone_number),
|
||||
"email": instance.email,
|
||||
}
|
||||
"crn": form.cleaned_data["crn"],
|
||||
"vrn": form.cleaned_data["vrn"],
|
||||
}
|
||||
customer.additional_info.update({"customer_info": customer_info })
|
||||
customer.additional_info.update({"type":"lead"})
|
||||
customer.save()
|
||||
@ -2753,7 +2753,7 @@ class LeadUpdateView(UpdateView):
|
||||
success_url = reverse_lazy("lead_list")
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
form = super().get_form(form_class)
|
||||
form.fields["id_car_model"].queryset = form.instance.id_car_make.carmodel_set.all()
|
||||
return form
|
||||
|
||||
@ -2761,7 +2761,7 @@ class LeadUpdateView(UpdateView):
|
||||
@login_required
|
||||
def LeadDeleteView(request,pk):
|
||||
lead = get_object_or_404(models.Lead, pk=pk)
|
||||
try:
|
||||
try:
|
||||
User.objects.get(email=lead.customer.email).delete()
|
||||
lead.customer.delete()
|
||||
except Exception as e:
|
||||
@ -2791,12 +2791,12 @@ def add_note_to_lead(request, pk):
|
||||
def add_note_to_opportunity(request, pk):
|
||||
opportunity = get_object_or_404(models.Opportunity, pk=pk)
|
||||
if request.method == "POST":
|
||||
notes = request.POST.get("notes")
|
||||
notes = request.POST.get("notes")
|
||||
if not notes:
|
||||
messages.error(request, "Notes field is required.")
|
||||
messages.error(request, "Notes field is required.")
|
||||
else:
|
||||
models.Notes.objects.create(content_object=opportunity, created_by=request.user,note=notes)
|
||||
messages.success(request, "Note added successfully!")
|
||||
messages.success(request, "Note added successfully!")
|
||||
return redirect("opportunity_detail", pk=opportunity.pk)
|
||||
|
||||
|
||||
@ -2835,8 +2835,8 @@ def lead_convert(request, pk):
|
||||
dealer = get_user_type(request)
|
||||
if hasattr(lead, "opportunity"):
|
||||
messages.error(request, "Lead is already converted to customer.")
|
||||
else:
|
||||
customer = lead.convert_to_customer(dealer.entity)
|
||||
else:
|
||||
customer = lead.convert_to_customer(dealer.entity,lead)
|
||||
models.Opportunity.objects.create(dealer=dealer,customer=customer,lead=lead,probability=50,stage=models.Stage.PROSPECT,staff=lead.staff,status=models.Status.QUALIFIED)
|
||||
messages.success(request, "Lead converted to customer successfully!")
|
||||
return redirect("lead_list")
|
||||
@ -2860,7 +2860,7 @@ def schedule_lead(request, pk):
|
||||
service = Service.objects.filter(name=instance.scheduled_type).first()
|
||||
if not service:
|
||||
messages.error(request, "Service not found!")
|
||||
return redirect("lead_list")
|
||||
return redirect("lead_list")
|
||||
|
||||
try:
|
||||
appointment_request = AppointmentRequest.objects.create(
|
||||
@ -2873,7 +2873,7 @@ def schedule_lead(request, pk):
|
||||
except ValidationError as e:
|
||||
messages.error(request, str(e))
|
||||
return redirect("schedule_lead", pk=lead.pk)
|
||||
|
||||
|
||||
client = get_object_or_404(User, email=lead.email)
|
||||
# Create Appointment
|
||||
Appointment.objects.create(
|
||||
@ -2902,7 +2902,7 @@ def lead_transfer(request,pk):
|
||||
if form.is_valid():
|
||||
lead.staff = form.cleaned_data["transfer_to"]
|
||||
lead.save()
|
||||
messages.success(request, "Lead transferred successfully!")
|
||||
messages.success(request, "Lead transferred successfully!")
|
||||
else:
|
||||
messages.error(request, f"Invalid form data: {str(form.errors)}")
|
||||
return redirect("lead_list")
|
||||
@ -2919,12 +2919,12 @@ def send_lead_email(request, pk,email_pk=None):
|
||||
response = HttpResponse(redirect("lead_detail", pk=lead.pk))
|
||||
response['HX-Redirect'] = reverse('lead_detail', args=[lead.pk])
|
||||
return response
|
||||
|
||||
|
||||
lead.status = models.Status.CONTACTED
|
||||
lead.save()
|
||||
# lead.convert_to_customer(dealer.entity)
|
||||
|
||||
if request.method == "POST":
|
||||
if request.method == "POST":
|
||||
email_pk = request.POST.get('email_pk')
|
||||
if email_pk not in [None,"None",""]:
|
||||
email = get_object_or_404(models.Email, pk=int(email_pk))
|
||||
@ -2940,7 +2940,7 @@ def send_lead_email(request, pk,email_pk=None):
|
||||
)
|
||||
dealer = get_user_type(request)
|
||||
models.Activity.objects.create(dealer=dealer,content_object=lead, notes="Email sent",created_by=request.user,activity_type=models.ActionChoices.EMAIL)
|
||||
messages.success(request, _("Email sent successfully!"))
|
||||
messages.success(request, _("Email sent successfully!"))
|
||||
return redirect("lead_list")
|
||||
msg = f"""
|
||||
السلام عليكم
|
||||
@ -2982,7 +2982,7 @@ def add_activity_to_lead(request, pk):
|
||||
if request.method == "POST":
|
||||
form = forms.ActivityForm(request.POST)
|
||||
if form.is_valid():
|
||||
activity = form.save(commit=False)
|
||||
activity = form.save(commit=False)
|
||||
activity.content_object = lead
|
||||
activity.created_by = request.user
|
||||
activity.save()
|
||||
@ -3005,8 +3005,8 @@ class OpportunityCreateView(CreateView, LoginRequiredMixin):
|
||||
def get_initial(self):
|
||||
initial = super().get_initial()
|
||||
if self.kwargs.get('pk',None):
|
||||
lead = models.Lead.objects.get(pk=self.kwargs.get('pk'))
|
||||
|
||||
lead = models.Lead.objects.get(pk=self.kwargs.get('pk'))
|
||||
|
||||
initial['customer'] = lead.customer
|
||||
return initial
|
||||
|
||||
@ -3037,11 +3037,11 @@ class OpportunityDetailView(DetailView):
|
||||
context = super().get_context_data(**kwargs)
|
||||
form = forms.OpportunityStatusForm()
|
||||
url = reverse("opportunity_update_status", args=[self.object.pk])
|
||||
form.fields["status"].widget.attrs["hx-get"] = url
|
||||
form.fields["status"].widget.attrs["hx-get"] = url
|
||||
form.fields["stage"].widget.attrs["hx-get"] = url
|
||||
form.fields["status"].initial = self.object.status
|
||||
form.fields["status"].initial = self.object.status
|
||||
form.fields["stage"].initial = self.object.stage
|
||||
context["status_form"] = form
|
||||
context["status_form"] = form
|
||||
context["notes"] = models.Notes.objects.filter(content_type__model="opportunity", object_id=self.object.id)
|
||||
context["activities"] = models.Activity.objects.filter(content_type__model="opportunity", object_id=self.object.id)
|
||||
email_qs = models.Email.objects.filter(content_type__model="opportunity", object_id=self.object.id)
|
||||
@ -3061,7 +3061,7 @@ class OpportunityListView(ListView):
|
||||
def get_queryset(self):
|
||||
dealer = get_user_type(self.request)
|
||||
return models.Opportunity.objects.filter(dealer=dealer).all()
|
||||
|
||||
|
||||
@login_required
|
||||
def delete_opportunity(request, pk):
|
||||
opportunity = get_object_or_404(models.Opportunity, pk=pk)
|
||||
@ -3227,7 +3227,7 @@ class BillDetailView(LoginRequiredMixin, DetailView):
|
||||
bill = kwargs.get("object")
|
||||
if bill.get_itemtxs_data():
|
||||
txs = bill.get_itemtxs_data()[0]
|
||||
|
||||
|
||||
transactions = [
|
||||
{
|
||||
"item": x,
|
||||
@ -3242,7 +3242,7 @@ class BillDetailView(LoginRequiredMixin, DetailView):
|
||||
|
||||
kwargs["transactions"] = transactions
|
||||
kwargs["grand_total"] = grand_total
|
||||
|
||||
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
@ -3323,7 +3323,7 @@ def bill_mark_as_paid(request, pk):
|
||||
bill.mark_as_paid(user_model=dealer.entity.admin)
|
||||
bill.save()
|
||||
bill.ledger.lock_journal_entries()
|
||||
bill.ledger.post_journal_entries()
|
||||
bill.ledger.post_journal_entries()
|
||||
bill.ledger.post()
|
||||
bill.ledger.save()
|
||||
messages.success(request, _("Bill marked as paid successfully."))
|
||||
@ -3461,7 +3461,7 @@ class OrderListView(ListView):
|
||||
|
||||
def get_queryset(self):
|
||||
dealer = get_user_type(self.request)
|
||||
qs = super().get_queryset()
|
||||
qs = super().get_queryset()
|
||||
return qs.filter(estimate__entity=dealer.entity)
|
||||
|
||||
# email
|
||||
@ -3861,7 +3861,7 @@ def DealerSettingsView(request,pk):
|
||||
dealer = get_user_type(request)
|
||||
if request.method == 'POST':
|
||||
form = forms.DealerSettingsForm(request.POST, instance=dealer_setting)
|
||||
if form.is_valid():
|
||||
if form.is_valid():
|
||||
instance = form.save(commit=False)
|
||||
instance.dealer = dealer
|
||||
instance.save()
|
||||
@ -3873,7 +3873,7 @@ def DealerSettingsView(request,pk):
|
||||
form.fields['invoice_cash_account'].queryset = dealer.entity.get_all_accounts().filter(role=roles.ASSET_CA_CASH)
|
||||
form.fields['invoice_prepaid_account'].queryset = dealer.entity.get_all_accounts().filter(role=roles.ASSET_CA_RECEIVABLES)
|
||||
form.fields['invoice_unearned_account'].queryset = dealer.entity.get_all_accounts().filter(role=roles.LIABILITY_CL_DEFERRED_REVENUE)
|
||||
|
||||
|
||||
form.fields['bill_cash_account'].queryset = dealer.entity.get_all_accounts().filter(role=roles.ASSET_CA_CASH)
|
||||
form.fields['bill_prepaid_account'].queryset = dealer.entity.get_all_accounts().filter(role=roles.ASSET_CA_PREPAID)
|
||||
form.fields['bill_unearned_account'].queryset = dealer.entity.get_all_accounts().filter(role=roles.LIABILITY_CL_ACC_PAYABLE)
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="align-middle white-space-nowrap text-uppercase" scope="col" style="width: 20%;">{{ _("Lead Name")|capfirst }}</th>
|
||||
<th class="align-middle white-space-nowrap text-uppercase" scope="col" style="width: 20%;">{{ _("Lead Name")|capfirst }}</th>
|
||||
<th class="align-middle white-space-nowrap text-uppercase" scope="col" style="width: 15%;">
|
||||
<div class="d-inline-flex flex-center">
|
||||
<div class="d-flex align-items-center px-1 py-1 bg-success-subtle rounded me-2"><i class="text-success-dark fas fa-car"></i></div>
|
||||
@ -109,7 +109,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<div class="d-flex align-items-center">
|
||||
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' lead.pk %}">{{lead.full_name}}</a>
|
||||
@ -132,12 +132,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
<td class="align-middle white-space-nowrap fw-semibold"><a class="text-body-highlight" href="">{{ lead.id_car_make.get_local_name }} - {{ lead.id_car_model.get_local_name }} {{ lead.year }}</a></td>
|
||||
<td class="align-middle white-space-nowrap fw-semibold"><a class="text-body-highlight" href="">{{ lead.email }}</a></td>
|
||||
<td class="align-middle white-space-nowrap fw-semibold"><a class="text-body-highlight" href="tel:{{ lead.phone_number }}">{{ lead.phone_number }}</a></td>
|
||||
<td class="align-middle white-space-nowrap fw-semibold">
|
||||
<div class="accordion" id="accordionExample">
|
||||
<td class="align-middle white-space-nowrap fw-semibold">
|
||||
<div class="accordion" id="accordionExample">
|
||||
<div class="accordion-item">
|
||||
<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">
|
||||
@ -148,7 +148,7 @@
|
||||
<div class="accordion-body pt-0">
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<table><tbody>
|
||||
{% for schedule in lead.get_latest_schedules %}
|
||||
{% for schedule in lead.get_latest_schedules %}
|
||||
<tr class="schedule-{{ schedule.pk }}">
|
||||
<td class="align-middle white-space-nowrap">
|
||||
{% if schedule.scheduled_type == "Call" %}
|
||||
@ -163,11 +163,11 @@
|
||||
<a href="{% url 'appointment:get_user_appointments' %}">
|
||||
<span class="badge badge-phoenix badge-phoenix-warning text-warning fw-semibold"><span class="text-warning" data-feather="email"></span>
|
||||
{{ schedule.scheduled_at }}</span></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a style="cursor: pointer;" hx-delete="{% url 'schedule_cancel' schedule.pk %}" hx-target=".schedule-{{ schedule.pk }}" hx-confirm="Are you sure you want to cancel this schedule?"><i class="fa-solid fa-ban text-danger"></i></a>
|
||||
</td>
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr><td><small><a href="{% url 'appointment:get_user_appointments' %}">View All ...</a></small></td></tr>
|
||||
@ -176,11 +176,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">{{ lead.staff|upper }}</td>
|
||||
<td class="align-middle white-space-nowrap fw-semibold text-body-highlight">{{ lead.source|upper }}</td>
|
||||
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">{{ lead.channel|upper }}</td>
|
||||
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">{{ lead.channel|upper }}</td>
|
||||
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">
|
||||
{% if lead.opportunity.stage == "prospect" %}
|
||||
<span class="badge text-bg-primary">{{ lead.opportunity.stage|upper }}</span>
|
||||
@ -192,7 +192,7 @@
|
||||
<span class="badge text-bg-success">{{ lead.opportunity.stage|upper }}</span>
|
||||
{% elif lead.opportunity.stage == "closed_lost" %}
|
||||
<span class="badge text-bg-danger">{{ lead.opportunity.stage|upper }}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">
|
||||
{% if lead.opportunity %}
|
||||
@ -203,7 +203,7 @@
|
||||
<span class="badge badge-phoenix badge-phoenix-danger">{{ _("No") }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-end">
|
||||
<td class="align-middle white-space-nowrap text-end">
|
||||
{% if user == lead.staff.user or request.is_dealer %}
|
||||
<div class="btn-reveal-trigger position-static">
|
||||
<button
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
<div class="row my-4">
|
||||
<h2>{{ organization.get_local_name }}</h2>
|
||||
<ul class="list-group mb-4">
|
||||
<li class="list-group-item"><strong>{% trans "CRN" %}:</strong> {{ organization.additional_info.organization_info.crn }}</li>
|
||||
<li class="list-group-item"><strong>{% trans "VRN" %}:</strong> {{ organization.additional_info.organization_info.vrn }}</li>
|
||||
<li class="list-group-item"><strong>{% trans "Phone" %}:</strong> {{ organization.additional_info.organization_info.phone_number }}</li>
|
||||
<li class="list-group-item"><strong>{% trans "Address" %}:</strong> {{ organization.additional_info.organization_info.address }}</li>
|
||||
<li class="list-group-item"><strong>{% trans "CRN" %}:</strong> {{ organization.additional_info.customer_info.crn }}</li>
|
||||
<li class="list-group-item"><strong>{% trans "VRN" %}:</strong> {{ organization.additional_info.customer_info.vrn }}</li>
|
||||
<li class="list-group-item"><strong>{% trans "Phone" %}:</strong> {{ organization.additional_info.customer_info.phone_number }}</li>
|
||||
<li class="list-group-item"><strong>{% trans "Address" %}:</strong> {{ organization.additional_info.customer_info.address }}</li>
|
||||
</ul>
|
||||
<div class="d-flex">
|
||||
<a href="{% url 'organization_update' organization.pk %}" class="btn btn-sm btn-warning me-2">{% trans "Edit" %}</a>
|
||||
|
||||
@ -107,8 +107,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.organization_info.crn }}</td>
|
||||
<td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.organization_info.vrn }}</td>
|
||||
<td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.customer_info.crn }}</td>
|
||||
<td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.customer_info.vrn }}</td>
|
||||
<td class="phone align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight">
|
||||
<a class="text-body-highlight" href="tel:{{ org.phone }}">{{ org.phone }}</a>
|
||||
</td>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user