Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
7379bb9e4d
@ -1,3 +1,4 @@
|
||||
from django.urls import reverse
|
||||
from django_countries.widgets import CountrySelectWidget
|
||||
from phonenumber_field.formfields import PhoneNumberField
|
||||
from django.core.validators import MinLengthValidator
|
||||
@ -35,8 +36,7 @@ from .models import (
|
||||
# SaleQuotationCar,
|
||||
AdditionalServices,
|
||||
Staff,
|
||||
Opportunity, Priority, Sources, Lead, Activity, Notes, CarModel,
|
||||
SaleOrder
|
||||
Opportunity, Priority, Sources, Lead, Activity, Notes, CarModel,SaleOrder,CarMake
|
||||
)
|
||||
from django_ledger import models as ledger_models
|
||||
from django.forms import ModelMultipleChoiceField, ValidationError, DateInput,DateTimeInput
|
||||
@ -650,6 +650,12 @@ class EmailForm(forms.Form):
|
||||
|
||||
|
||||
class LeadForm(forms.ModelForm):
|
||||
id_car_make = forms.ModelChoiceField(label="Make",
|
||||
queryset=CarMake.objects.filter(is_sa_import=True),
|
||||
widget=forms.Select(attrs={"class": "form-control form-control-sm","hx-get":"","hx-include":"#id_id_car_make","hx-select":"#div_id_id_car_model","hx-target":"#div_id_id_car_model","hx-swap":"outerHTML"}),
|
||||
required=True
|
||||
)
|
||||
id_car_model = forms.ModelChoiceField(label="Model", queryset=CarModel.objects.none(),widget=forms.Select(attrs={"class": "form-control form-control-sm"}),required=True)
|
||||
class Meta:
|
||||
model = Lead
|
||||
fields = [
|
||||
@ -658,7 +664,9 @@ class LeadForm(forms.ModelForm):
|
||||
'email',
|
||||
'phone_number',
|
||||
'address',
|
||||
'car',
|
||||
'id_car_make',
|
||||
'id_car_model',
|
||||
'year',
|
||||
'source',
|
||||
'channel',
|
||||
'staff',
|
||||
@ -666,8 +674,8 @@ class LeadForm(forms.ModelForm):
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if "id_car_make" in self.fields:
|
||||
queryset = self.fields["id_car_make"].queryset.filter(is_sa_import=True)
|
||||
self.fields["id_car_make"].choices = [
|
||||
@ -678,7 +686,7 @@ class ScheduleForm(forms.ModelForm):
|
||||
scheduled_at = forms.DateTimeField(widget=DateTimeInput(attrs={'type': 'datetime-local'}))
|
||||
class Meta:
|
||||
model = Schedule
|
||||
fields = ['purpose','scheduled_type', 'scheduled_at', 'notes']
|
||||
fields = ['purpose','scheduled_type', 'scheduled_at','duration', 'notes']
|
||||
|
||||
|
||||
class NoteForm(forms.ModelForm):
|
||||
|
||||
14
inventory/migrations/0012_merge_20250206_1308.py
Normal file
14
inventory/migrations/0012_merge_20250206_1308.py
Normal file
@ -0,0 +1,14 @@
|
||||
# Generated by Django 4.2.17 on 2025-02-06 10:08
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0002_alter_carregistration_car'),
|
||||
('inventory', '0011_remove_lead_year_alter_schedule_customer'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
@ -0,0 +1,33 @@
|
||||
# Generated by Django 4.2.17 on 2025-02-06 12:07
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0013_alter_carregistration_text2_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='lead',
|
||||
name='car',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='lead',
|
||||
name='id_car_make',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='lead',
|
||||
name='id_car_model',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='lead',
|
||||
name='year',
|
||||
field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Year'),
|
||||
),
|
||||
]
|
||||
14
inventory/migrations/0015_merge_20250209_1116.py
Normal file
14
inventory/migrations/0015_merge_20250209_1116.py
Normal file
@ -0,0 +1,14 @@
|
||||
# Generated by Django 4.2.17 on 2025-02-09 08:16
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0012_merge_20250206_1308'),
|
||||
('inventory', '0014_remove_lead_car_lead_id_car_make_lead_id_car_model_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
19
inventory/migrations/0016_schedule_duration.py
Normal file
19
inventory/migrations/0016_schedule_duration.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.17 on 2025-02-09 08:23
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0015_merge_20250209_1116'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='schedule',
|
||||
name='duration',
|
||||
field=models.DurationField(default=datetime.timedelta(seconds=300)),
|
||||
),
|
||||
]
|
||||
18
inventory/migrations/0017_car_hash.py
Normal file
18
inventory/migrations/0017_car_hash.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.17 on 2025-02-09 11:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0016_schedule_duration'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='hash',
|
||||
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='Hash'),
|
||||
),
|
||||
]
|
||||
@ -1,38 +1,21 @@
|
||||
# from datetime import timezone
|
||||
from django.utils import timezone
|
||||
import itertools
|
||||
from uuid import uuid4
|
||||
from django.conf import settings
|
||||
from django.db import models, transaction
|
||||
from django.db.models import Sum, F, Count
|
||||
from decimal import Decimal
|
||||
import hashlib
|
||||
from django.db import models
|
||||
from datetime import timedelta
|
||||
from django.contrib.auth.models import User, UserManager
|
||||
from django.db.models.signals import pre_save, post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_ledger.models import (
|
||||
VendorModel,
|
||||
EntityModel,
|
||||
EntityUnitModel,
|
||||
ItemModel,
|
||||
AccountModel,
|
||||
ItemModelAbstract,
|
||||
UnitOfMeasureModel,
|
||||
CustomerModel,
|
||||
ItemModelQuerySet,
|
||||
)
|
||||
from django_ledger.io.io_core import get_localdate
|
||||
from django.db.models import Sum
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from django.core.exceptions import ValidationError
|
||||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
from django.utils.timezone import now
|
||||
from sqlalchemy.orm.base import object_state
|
||||
|
||||
from .utilities.financials import get_financial_value, get_total, get_total_financials
|
||||
from django.db.models import FloatField
|
||||
from .mixins import LocalizedNameMixin
|
||||
from django_ledger.models import EntityModel, ItemModel,EstimateModel,InvoiceModel
|
||||
from django_countries.fields import CountryField
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
@ -403,6 +386,11 @@ class Car(models.Model):
|
||||
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
|
||||
mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
|
||||
receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
|
||||
hash = models.CharField(max_length=64, blank=True, null=True, verbose_name=_("Hash"))
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.hash = self.get_hash
|
||||
super(Car, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Car")
|
||||
@ -424,6 +412,12 @@ class Car(models.Model):
|
||||
def get_car_group(self):
|
||||
return f"{self.id_car_make.get_local_name} {self.id_car_model.get_local_name}"
|
||||
|
||||
@property
|
||||
def get_hash(self):
|
||||
hash_object = hashlib.sha256()
|
||||
hash_object.update(f"{self.id_car_make.name}{self.id_car_model.name}".encode('utf-8'))
|
||||
return hash_object.hexdigest()
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"vin": self.vin,
|
||||
@ -437,6 +431,7 @@ class Car(models.Model):
|
||||
"remarks": self.remarks,
|
||||
"mileage": self.mileage,
|
||||
"receiving_date": self.receiving_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'hash': self.get_hash,
|
||||
"id": self.id,
|
||||
}
|
||||
|
||||
@ -1097,26 +1092,26 @@ class Lead(models.Model):
|
||||
CustomerModel, on_delete=models.CASCADE, related_name="leads",
|
||||
null=True,blank=True
|
||||
)
|
||||
car = models.ForeignKey(
|
||||
Car, on_delete=models.DO_NOTHING, blank=True, null=True, verbose_name=_("Car")
|
||||
# car = models.ForeignKey(
|
||||
# Car, on_delete=models.DO_NOTHING, blank=True, null=True, verbose_name=_("Car")
|
||||
# )
|
||||
id_car_make = models.ForeignKey(
|
||||
CarMake,
|
||||
on_delete=models.DO_NOTHING,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("Make"),
|
||||
)
|
||||
id_car_model = models.ForeignKey(
|
||||
CarModel,
|
||||
on_delete=models.DO_NOTHING,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("Model"),
|
||||
)
|
||||
year = models.PositiveSmallIntegerField(
|
||||
verbose_name=_("Year"), blank=True, null=True
|
||||
)
|
||||
# id_car_make = models.ForeignKey(
|
||||
# CarMake,
|
||||
# on_delete=models.DO_NOTHING,
|
||||
# blank=True,
|
||||
# null=True,
|
||||
# verbose_name=_("Make"),
|
||||
# )
|
||||
# id_car_model = models.ForeignKey(
|
||||
# CarModel,
|
||||
# on_delete=models.DO_NOTHING,
|
||||
# blank=True,
|
||||
# null=True,
|
||||
# verbose_name=_("Model"),
|
||||
# )
|
||||
# year = models.PositiveSmallIntegerField(
|
||||
# verbose_name=_("Year"), blank=True, null=True
|
||||
# )
|
||||
source = models.CharField(
|
||||
max_length=50, choices=Sources.choices, verbose_name=_("Source")
|
||||
)
|
||||
@ -1168,14 +1163,16 @@ class Lead(models.Model):
|
||||
"email": str(self.email),
|
||||
"address": str(self.address),
|
||||
"phone_number": str(self.phone_number),
|
||||
"car": self.car.to_dict(),
|
||||
"make": str(self.id_car_make.name),
|
||||
"model": str(self.id_car_model.name),
|
||||
"created_at": str(self.created.strftime("%Y-%m-%d")),
|
||||
}
|
||||
@property
|
||||
def full_name(self):
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
def convert_to_customer(self,entity):
|
||||
if entity and not entity.get_customers().filter(email=self.email).exists():
|
||||
customer = entity.get_customers().filter(email=self.email).first()
|
||||
if entity and not customer:
|
||||
customer = entity.create_customer(
|
||||
customer_model_kwargs={
|
||||
"customer_name": self.full_name,
|
||||
@ -1183,12 +1180,13 @@ class Lead(models.Model):
|
||||
"phone": self.phone_number,
|
||||
"email": self.email,
|
||||
}
|
||||
)
|
||||
customer.additional_info = {}
|
||||
customer.additional_info.update({"info":self.to_dict()})
|
||||
self.customer = customer
|
||||
customer.save()
|
||||
self.save()
|
||||
)
|
||||
|
||||
customer.additional_info = {}
|
||||
customer.additional_info.update({"info":self.to_dict()})
|
||||
self.customer = customer
|
||||
customer.save()
|
||||
self.save()
|
||||
|
||||
def get_latest_schedule(self):
|
||||
return self.schedules.order_by('-scheduled_at').first()
|
||||
@ -1218,6 +1216,7 @@ class Schedule(models.Model):
|
||||
purpose = models.CharField(max_length=200, choices=PURPOSE_CHOICES)
|
||||
scheduled_at = models.DateTimeField()
|
||||
scheduled_type = models.CharField(max_length=200, choices=ScheduledType,default='Call')
|
||||
duration = models.DurationField(default=timedelta(minutes=5))
|
||||
notes = models.TextField(blank=True, null=True)
|
||||
status = models.CharField(max_length=200, choices=ScheduleStatusChoices, default='Scheduled')
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
@ -1226,8 +1225,9 @@ class Schedule(models.Model):
|
||||
def __str__(self):
|
||||
return f"Scheduled {self.purpose} with {self.lead.full_name} on {self.scheduled_at}"
|
||||
|
||||
@property
|
||||
def schedule_past_date(self):
|
||||
if self.scheduled_at < timezone.now():
|
||||
if self.scheduled_at < now():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@ -694,7 +694,8 @@ def update_item_model_cost(sender, instance, created, **kwargs):
|
||||
product = entity.get_items_all().filter(name=instance.car.vin).first()
|
||||
|
||||
product.default_amount = instance.selling_price
|
||||
product.additional_info = {}
|
||||
if not isinstance(product.additional_info, dict):
|
||||
product.additional_info = {}
|
||||
product.additional_info.update({"car_finance":instance.to_dict()})
|
||||
product.additional_info.update({"additional_services": [service.to_dict() for service in instance.additional_services.all()]})
|
||||
product.save()
|
||||
|
||||
@ -319,10 +319,10 @@ def number_to_words_arabic(number):
|
||||
|
||||
return ' '.join(words)
|
||||
|
||||
@register.filter(name='num2words')
|
||||
def num2words(number, language='en'):
|
||||
"""Template filter to convert a number to words in the specified language."""
|
||||
if language == 'ar':
|
||||
return number_to_words_arabic(number)
|
||||
else:
|
||||
return number_to_words_english(number)
|
||||
# @register.filter(name='num2words')
|
||||
# def num2words(number, language='en'):
|
||||
# """Template filter to convert a number to words in the specified language."""
|
||||
# if language == 'ar':
|
||||
# return number_to_words_arabic(number)
|
||||
# else:
|
||||
# return number_to_words_english(number)
|
||||
@ -88,7 +88,7 @@ urlpatterns = [
|
||||
path(
|
||||
"crm/leads/<int:pk>/view/", views.LeadDetailView.as_view(), name="lead_detail"
|
||||
),
|
||||
path("crm/leads/create/", views.LeadCreateView.as_view(), name="lead_create"),
|
||||
path("crm/leads/create/", views.lead_create, name="lead_create"),
|
||||
path(
|
||||
"crm/leads/<int:pk>/update/", views.LeadUpdateView.as_view(), name="lead_update"
|
||||
),
|
||||
@ -196,7 +196,6 @@ urlpatterns = [
|
||||
),
|
||||
path("cars/add/", views.CarCreateView.as_view(), name="car_add"),
|
||||
path("ajax/", views.AjaxHandlerView.as_view(), name="ajax_handler"),
|
||||
path("cars/get-car-models/", views.get_car_models, name="get_car_models"),
|
||||
path(
|
||||
"cars/<int:car_pk>/add-color/", views.CarColorCreate.as_view(), name="add_color"
|
||||
),
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
from django.db.models import Func
|
||||
from appointment.models import Appointment,AppointmentRequest,Service,StaffMember
|
||||
from datetime import timedelta
|
||||
from calendar import month_name
|
||||
from random import randint
|
||||
from rich import print
|
||||
@ -115,6 +118,11 @@ logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
|
||||
class Hash(Func):
|
||||
function = 'get_hash'
|
||||
|
||||
|
||||
def switch_language(request):
|
||||
language = request.GET.get("language", "en")
|
||||
referer = request.META.get("HTTP_REFERER", "/")
|
||||
@ -2339,12 +2347,13 @@ def create_estimate(request):
|
||||
|
||||
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(
|
||||
@ -2355,7 +2364,17 @@ def create_estimate(request):
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "Quantity must be greater than zero"}
|
||||
)
|
||||
|
||||
if isinstance(items, list):
|
||||
for item, quantity in zip(items, quantities):
|
||||
if int(quantity) > models.Car.objects.filter(hash=item).count():
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "Quantity must be less than or equal to the number of cars in stock"},
|
||||
)
|
||||
else:
|
||||
if int(quantities) > models.Car.objects.filter(hash=item).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
|
||||
)
|
||||
@ -2375,18 +2394,20 @@ def create_estimate(request):
|
||||
]
|
||||
items_txs = []
|
||||
for item in items_list:
|
||||
item_instance = ItemModel.objects.get(pk=item.get("item_id"))
|
||||
car_instance = models.Car.objects.get(vin=item_instance.name)
|
||||
items_txs.append(
|
||||
{
|
||||
"item_number": item_instance.item_number,
|
||||
"quantity": Decimal(item.get("quantity")),
|
||||
"unit_cost": car_instance.finances.selling_price,
|
||||
"unit_revenue": car_instance.finances.selling_price,
|
||||
"total_amount": (car_instance.finances.total_vat)
|
||||
* int(item.get("quantity")),
|
||||
}
|
||||
)
|
||||
# item_instance = ItemModel.objects.get(pk=item.get("item_id"))
|
||||
car_instance = ItemModel.objects.filter(additional_info__car_info__hash=item.get("item_id")).all()
|
||||
|
||||
# car_instance = models.Car.objects.get(vin=item_instance.name)
|
||||
for i in car_instance[:int(quantities[0])]:
|
||||
items_txs.append(
|
||||
{
|
||||
"item_number": i.item_number,
|
||||
"quantity": 1,
|
||||
"unit_cost": i.additional_info.get('car_finance').get("selling_price"),
|
||||
"unit_revenue": i.additional_info.get('car_finance').get("selling_price"),
|
||||
"total_amount": (i.additional_info.get('car_finance').get("total_vat"))
|
||||
}
|
||||
)
|
||||
|
||||
estimate_itemtxs = {
|
||||
item.get("item_number"): {
|
||||
@ -2416,14 +2437,18 @@ def create_estimate(request):
|
||||
)
|
||||
|
||||
if isinstance(items, list):
|
||||
for item in items:
|
||||
item_instance = ItemModel.objects.get(pk=item)
|
||||
instance = models.Car.objects.get(vin=item_instance.name)
|
||||
for item in estimate_itemtxs.keys():
|
||||
item_instance = ItemModel.objects.get(item_number=item)
|
||||
instance = models.Car.objects.get(name=item_instance.name)
|
||||
reserve_car(instance, request)
|
||||
# for item in items:
|
||||
# item_instance = ItemModel.objects.filter(additioinal_info__car_info__hash=item).first()
|
||||
# instance = models.Car.objects.get(hash=item)
|
||||
# reserve_car(instance, request)
|
||||
|
||||
else:
|
||||
item_instance = ItemModel.objects.get(pk=items)
|
||||
instance = models.Car.objects.get(vin=item_instance.name)
|
||||
item_instance = ItemModel.objects.get(additioinal_info__car_info__hash=items)
|
||||
instance = models.Car.objects.get(hash=item)
|
||||
response = reserve_car(instance, request)
|
||||
|
||||
url = reverse("estimate_detail", kwargs={"pk": estimate.pk})
|
||||
@ -2439,22 +2464,21 @@ def create_estimate(request):
|
||||
entity_slug=entity.slug, user_model=entity.admin
|
||||
)
|
||||
form.fields["customer"].queryset = entity.get_customers().filter(active=True)
|
||||
car_list = models.Car.objects.filter(
|
||||
dealer=dealer, finances__selling_price__gt=0
|
||||
).exclude(status="reserved")
|
||||
car_list = models.Car.objects.filter(dealer=dealer).exclude(status="reserved").values_list(
|
||||
'id_car_make__name', 'id_car_model__name','hash').distinct()
|
||||
|
||||
context = {
|
||||
"form": form,
|
||||
"items": [
|
||||
{
|
||||
"car": x,
|
||||
"product": entity.get_items_all()
|
||||
.filter(item_role=ItemModel.ITEM_ROLE_PRODUCT, name=x.vin)
|
||||
.first(),
|
||||
'make':x[0],
|
||||
'model':x[1],
|
||||
'hash': x[2]
|
||||
}
|
||||
for x in car_list
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
return render(request, "sales/estimates/estimate_form.html", context)
|
||||
|
||||
|
||||
@ -2940,28 +2964,25 @@ class LeadDetailView(DetailView):
|
||||
return context
|
||||
|
||||
|
||||
class LeadCreateView(CreateView, SuccessMessageMixin, LoginRequiredMixin):
|
||||
model = models.Lead
|
||||
form_class = forms.LeadForm
|
||||
template_name = "crm/leads/lead_form.html"
|
||||
success_message = "Lead created successfully!"
|
||||
success_url = reverse_lazy("lead_list")
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = get_user_type(self.request)
|
||||
form.instance.dealer = dealer
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def get_car_models(request):
|
||||
make_id = request.GET.get("id_car_make")
|
||||
if make_id:
|
||||
car_models = models.CarModel.objects.filter(id_car_make=make_id).values(
|
||||
"id_car_model", "name", "arabic_name"
|
||||
)
|
||||
return JsonResponse(list(car_models), safe=False)
|
||||
return JsonResponse([], safe=False)
|
||||
def lead_create(request):
|
||||
form = forms.LeadForm()
|
||||
make = request.GET.get("id_car_make",None)
|
||||
if make:
|
||||
form.fields['id_car_model'].queryset = models.CarModel.objects.filter(id_car_make=int(make))
|
||||
if request.method == "POST":
|
||||
form = forms.LeadForm(request.POST)
|
||||
form.fields['id_car_model'].queryset = models.CarModel.objects.filter(id_car_make=int(request.POST['id_car_make']))
|
||||
if form.is_valid():
|
||||
instance = form.save(commit=False)
|
||||
dealer = get_user_type(request)
|
||||
instance = form.save(commit=False)
|
||||
instance.dealer = dealer
|
||||
instance.save()
|
||||
|
||||
messages.success(request, "Lead created successfully!")
|
||||
return redirect("lead_list")
|
||||
|
||||
return render(request, "crm/leads/lead_form.html", {"form": form})
|
||||
|
||||
class LeadUpdateView(UpdateView):
|
||||
model = models.Lead
|
||||
@ -2969,6 +2990,10 @@ class LeadUpdateView(UpdateView):
|
||||
template_name = "crm/leads/lead_form.html"
|
||||
success_url = reverse_lazy("lead_list")
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
form.fields["id_car_model"].queryset = form.instance.id_car_make.carmodel_set.all()
|
||||
return form
|
||||
|
||||
def LeadDeleteView(request,pk):
|
||||
lead = get_object_or_404(models.Lead, pk=pk)
|
||||
@ -3030,12 +3055,14 @@ def lead_convert(request, pk):
|
||||
if lead.is_converted:
|
||||
messages.error(request, "Lead is already converted to customer.")
|
||||
return redirect("opportunity_create",pk=lead.pk)
|
||||
if not models.Car.objects.filter(id_car_make=lead.id_car_make,id_car_model=lead.id_car_model).first():
|
||||
messages.error(request, "Cannot convert lead to customer. Car model not found.")
|
||||
return redirect("lead_list")
|
||||
|
||||
lead.convert_to_customer(dealer.entity)
|
||||
messages.success(request, "Lead converted to customer successfully!")
|
||||
return redirect("opportunity_create",pk=lead.pk)
|
||||
|
||||
|
||||
@login_required
|
||||
def schedule_lead(request, pk):
|
||||
lead = get_object_or_404(models.Lead, pk=pk)
|
||||
@ -3046,8 +3073,33 @@ def schedule_lead(request, pk):
|
||||
instance.lead = lead
|
||||
if hasattr(request.user, "staff"):
|
||||
instance.scheduled_by = request.user.staff
|
||||
|
||||
# Save the Schedule instance
|
||||
instance.save()
|
||||
messages.success(request, "Lead scheduled successfully!")
|
||||
|
||||
# Create AppointmentRequest
|
||||
service = Service.objects.filter(name=instance.scheduled_type).first()
|
||||
if not service:
|
||||
messages.error(request, "Service not found!")
|
||||
return redirect("lead_list")
|
||||
|
||||
appointment_request = AppointmentRequest.objects.create(
|
||||
date=instance.scheduled_at.date(),
|
||||
start_time=instance.scheduled_at.time(),
|
||||
end_time=(instance.scheduled_at + instance.duration).time(),
|
||||
service=service,
|
||||
staff_member=StaffMember.objects.first()
|
||||
)
|
||||
|
||||
# Create Appointment
|
||||
Appointment.objects.create(
|
||||
client=request.user, # Replace with the appropriate client
|
||||
appointment_request=appointment_request,
|
||||
phone="123-456-7890", # Replace with actual phone number
|
||||
address="123 Main St", # Replace with actual address
|
||||
)
|
||||
|
||||
messages.success(request, "Lead scheduled and appointment created successfully!")
|
||||
return redirect("lead_list")
|
||||
else:
|
||||
messages.error(request, f"Invalid form data: {str(form.errors)}")
|
||||
@ -3130,9 +3182,9 @@ class OpportunityCreateView(CreateView):
|
||||
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
|
||||
initial['car'] = lead.car
|
||||
return initial
|
||||
|
||||
def form_valid(self, form):
|
||||
|
||||
122
scripts/run.py
122
scripts/run.py
@ -1,52 +1,96 @@
|
||||
from django_ledger.models.invoice import InvoiceModel
|
||||
from django_ledger.utils import accruable_net_summary
|
||||
from decimal import Decimal
|
||||
from django_ledger.models import EstimateModel,EntityModel
|
||||
from django_ledger.models import EstimateModel,EntityModel,ItemModel
|
||||
from rich import print
|
||||
from datetime import date
|
||||
from inventory.models import VatRate
|
||||
from inventory.models import Car, Dealer, VatRate,Lead,CarMake,CarModel,Schedule
|
||||
from inventory.utils import CarFinanceCalculator
|
||||
from appointment.models import Appointment,AppointmentRequest,Service,StaffMember
|
||||
from django.contrib.auth import get_user_model
|
||||
from django_ledger.io.io_core import get_localdate
|
||||
from datetime import datetime, timedelta
|
||||
from django.utils import timezone
|
||||
import hashlib
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
def run():
|
||||
# estimate = EstimateModel.objects.first()
|
||||
# calculator = CarFinanceCalculator(estimate)
|
||||
# finance_data = calculator.get_finance_data()
|
||||
def run():
|
||||
# print(Service.objects.first().pk)
|
||||
# print(Appointment.objects.first().client)
|
||||
|
||||
# print(finance_data)
|
||||
# entity = EntityModel.objects.get(name="ismail")
|
||||
# bs_report = entity.get_balance_sheet_statement(
|
||||
# to_date=date(2025, 1, 1),
|
||||
# save_pdf=False,
|
||||
# filepath='./'
|
||||
# )
|
||||
|
||||
# ic_report = entity.get_income_statement(
|
||||
# from_date=date(2022, 1, 1),
|
||||
# to_date=date(2022, 12, 31),
|
||||
# save_pdf=False,
|
||||
# filepath='./'
|
||||
# appointment = Appointment.objects.create(
|
||||
# client_name="John Doe",
|
||||
# client_email="john@example.com",
|
||||
# service="Haircut",
|
||||
# date_time="2023-10-15 10:00:00",
|
||||
# status="pending")
|
||||
# make = CarMake.objects.first()
|
||||
# Lead.objects.create(
|
||||
# first_name="John",
|
||||
# last_name="Doe",
|
||||
# email="john@example.com",
|
||||
# phone_number="123-456-7890",
|
||||
# address="123 Main St",
|
||||
# id_car_make=make,
|
||||
# id_car_model=make.carmodel_set.first(),
|
||||
# year="2022",
|
||||
# source="website",
|
||||
# channel="online",
|
||||
# staff="John Doe",
|
||||
# priority="high",
|
||||
# )
|
||||
|
||||
# # print(bs_report)
|
||||
# print(ic_report.get_report_data())
|
||||
# estimate = EstimateModel.objects.first()
|
||||
# calculator = CarFinanceCalculator(estimate)
|
||||
# finance_data = calculator.get_finance_data()
|
||||
# schedult = Schedule.objects.create(
|
||||
# name="John Doe",
|
||||
# email="john@example.com",
|
||||
# phone_number="123-456-7890",
|
||||
# address="123 Main St",
|
||||
# id_car_make=make,
|
||||
# id_car_model=make.carmodel_set.first(),
|
||||
# year="2022",
|
||||
# source="website",
|
||||
# channel="online",
|
||||
# staff="John Doe",
|
||||
# priority="high",
|
||||
# )
|
||||
# now = timezone.now()
|
||||
# end_time = now + timedelta(minutes=10)
|
||||
|
||||
# service = Service.objects.first()
|
||||
# appointment_request = AppointmentRequest.objects.create(
|
||||
# date=now.date(),
|
||||
# start_time=now.time(),
|
||||
# end_time=end_time.time(),
|
||||
# service=service,
|
||||
# staff_member=StaffMember.objects.first(),
|
||||
# )
|
||||
|
||||
# invoice_itemtxs = {
|
||||
# i.get("item_number"): {
|
||||
# "unit_cost": i.get("total_price"),
|
||||
# "quantity": i.get("quantity"),
|
||||
# "total_amount": i.get("total_vat"),
|
||||
# }
|
||||
# for i in finance_data.get("cars")
|
||||
# }
|
||||
# invoice = InvoiceModel.objects.first()
|
||||
entity = EntityModel.objects.filter(name="ismail").first()
|
||||
invoice_qs = InvoiceModel.objects.for_entity(
|
||||
entity_slug=entity.slug,
|
||||
user_model=entity.admin,
|
||||
).unpaid()
|
||||
print(accruable_net_summary(invoice_qs))
|
||||
# appointment = Appointment.objects.create(
|
||||
# client=User.objects.first(),
|
||||
# appointment_request=appointment_request,
|
||||
# phone="123-456-7890",
|
||||
# address="123 Main St",
|
||||
# )
|
||||
dealer = Dealer.objects.get(user__email="ismail.mosa.ibrahim@gmail.com")
|
||||
entity = dealer.entity
|
||||
|
||||
car_list = Car.objects.filter(dealer=dealer).all()
|
||||
# context = {
|
||||
# "items": [
|
||||
# {
|
||||
# "car": x,
|
||||
# "product": entity.get_items_all()
|
||||
# .filter(item_role=ItemModel.ITEM_ROLE_PRODUCT, name=x.vin)
|
||||
# .first(),
|
||||
# }
|
||||
# for x in car_list
|
||||
# ],
|
||||
# }
|
||||
|
||||
for i in car_list:
|
||||
hash_object = hashlib.sha256()
|
||||
hash_object.update(f"{i.id_car_make.name}{i.id_car_model.name}".encode('utf-8'))
|
||||
print(hash_object.hexdigest() , i.id_car_make.name, i.id_car_model.name)
|
||||
|
||||
|
||||
@ -135,13 +135,14 @@
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td class="align-middle white-space-nowrap fw-semibold"><a class="text-body-highlight" href="">{{ lead.car.id_car_make.get_local_name }} - {{ lead.car.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.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">
|
||||
{% if lead.get_latest_schedule %}
|
||||
<a href="{% url 'appointment:get_user_appointments' %}">
|
||||
{% if lead.get_latest_schedule.scheduled_type == "Call" %}
|
||||
<span class="badge badge-phoenix badge-phoenix-primary text-primary fw-semibold"><span class="text-primary" data-feather="phone"></span>
|
||||
<span class="badge badge-phoenix badge-phoenix-primary text-primary {% if lead.get_latest_schedule.schedule_past_date %}badge-phoenix-danger text-danger{% endif %} fw-semibold"><span class="text-primary {% if lead.get_latest_schedule.schedule_past_date %}text-danger{% endif %}" data-feather="phone"></span>
|
||||
{{ lead.get_latest_schedule.scheduled_at }}</span>
|
||||
{% elif lead.get_latest_schedule.scheduled_type == "Meeting" %}
|
||||
<span class="badge badge-phoenix badge-phoenix-success text-success fw-semibold"><span class="text-success" data-feather="calendar"></span>
|
||||
@ -150,6 +151,7 @@
|
||||
<span class="badge badge-phoenix badge-phoenix-warning text-warning fw-semibold"><span class="text-warning" data-feather="email"></span>
|
||||
{{ lead.get_latest_schedule.scheduled_at }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">{{ lead.staff|upper }}</td>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<div class="mb-2 col-sm-2">
|
||||
<select class="form-control item" name="item[]" required>
|
||||
{% for item in items %}
|
||||
<option value="{{ item.product.pk }}">{{ item.car.id_car_model }}</option>
|
||||
<option value="{{ item.hash }}">{{ item.make }} {{item.model}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user