Merge remote-tracking branch 'origin/main'

This commit is contained in:
Marwan Alwali 2025-02-09 20:25:37 +03:00
commit 7379bb9e4d
14 changed files with 364 additions and 160 deletions

View File

@ -1,3 +1,4 @@
from django.urls import reverse
from django_countries.widgets import CountrySelectWidget from django_countries.widgets import CountrySelectWidget
from phonenumber_field.formfields import PhoneNumberField from phonenumber_field.formfields import PhoneNumberField
from django.core.validators import MinLengthValidator from django.core.validators import MinLengthValidator
@ -35,8 +36,7 @@ from .models import (
# SaleQuotationCar, # SaleQuotationCar,
AdditionalServices, AdditionalServices,
Staff, Staff,
Opportunity, Priority, Sources, Lead, Activity, Notes, CarModel, Opportunity, Priority, Sources, Lead, Activity, Notes, CarModel,SaleOrder,CarMake
SaleOrder
) )
from django_ledger import models as ledger_models from django_ledger import models as ledger_models
from django.forms import ModelMultipleChoiceField, ValidationError, DateInput,DateTimeInput from django.forms import ModelMultipleChoiceField, ValidationError, DateInput,DateTimeInput
@ -650,6 +650,12 @@ class EmailForm(forms.Form):
class LeadForm(forms.ModelForm): 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: class Meta:
model = Lead model = Lead
fields = [ fields = [
@ -658,7 +664,9 @@ class LeadForm(forms.ModelForm):
'email', 'email',
'phone_number', 'phone_number',
'address', 'address',
'car', 'id_car_make',
'id_car_model',
'year',
'source', 'source',
'channel', 'channel',
'staff', 'staff',
@ -678,7 +686,7 @@ class ScheduleForm(forms.ModelForm):
scheduled_at = forms.DateTimeField(widget=DateTimeInput(attrs={'type': 'datetime-local'})) scheduled_at = forms.DateTimeField(widget=DateTimeInput(attrs={'type': 'datetime-local'}))
class Meta: class Meta:
model = Schedule model = Schedule
fields = ['purpose','scheduled_type', 'scheduled_at', 'notes'] fields = ['purpose','scheduled_type', 'scheduled_at','duration', 'notes']
class NoteForm(forms.ModelForm): class NoteForm(forms.ModelForm):

View 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 = [
]

View File

@ -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'),
),
]

View 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 = [
]

View 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)),
),
]

View 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'),
),
]

View File

@ -1,38 +1,21 @@
# from datetime import timezone from decimal import Decimal
from django.utils import timezone import hashlib
import itertools from django.db import models
from uuid import uuid4 from datetime import timedelta
from django.conf import settings
from django.db import models, transaction
from django.db.models import Sum, F, Count
from django.contrib.auth.models import User, UserManager 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.utils.translation import gettext_lazy as _
from django_ledger.models import ( from django_ledger.models import (
VendorModel, VendorModel,
EntityModel, EntityModel,
EntityUnitModel,
ItemModel, ItemModel,
AccountModel,
ItemModelAbstract,
UnitOfMeasureModel,
CustomerModel, CustomerModel,
ItemModelQuerySet,
) )
from django_ledger.io.io_core import get_localdate 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 django.core.exceptions import ValidationError
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
from django.utils.timezone import now 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 .mixins import LocalizedNameMixin
from django_ledger.models import EntityModel, ItemModel,EstimateModel,InvoiceModel 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.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType 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")) remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage")) mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
receiving_date = models.DateTimeField(verbose_name=_("Receiving Date")) 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: class Meta:
verbose_name = _("Car") verbose_name = _("Car")
@ -424,6 +412,12 @@ class Car(models.Model):
def get_car_group(self): def get_car_group(self):
return f"{self.id_car_make.get_local_name} {self.id_car_model.get_local_name}" 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): def to_dict(self):
return { return {
"vin": self.vin, "vin": self.vin,
@ -437,6 +431,7 @@ class Car(models.Model):
"remarks": self.remarks, "remarks": self.remarks,
"mileage": self.mileage, "mileage": self.mileage,
"receiving_date": self.receiving_date.strftime('%Y-%m-%d %H:%M:%S'), "receiving_date": self.receiving_date.strftime('%Y-%m-%d %H:%M:%S'),
'hash': self.get_hash,
"id": self.id, "id": self.id,
} }
@ -1097,26 +1092,26 @@ class Lead(models.Model):
CustomerModel, on_delete=models.CASCADE, related_name="leads", CustomerModel, on_delete=models.CASCADE, related_name="leads",
null=True,blank=True null=True,blank=True
) )
car = models.ForeignKey( # car = models.ForeignKey(
Car, on_delete=models.DO_NOTHING, blank=True, null=True, verbose_name=_("Car") # 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( source = models.CharField(
max_length=50, choices=Sources.choices, verbose_name=_("Source") max_length=50, choices=Sources.choices, verbose_name=_("Source")
) )
@ -1168,14 +1163,16 @@ class Lead(models.Model):
"email": str(self.email), "email": str(self.email),
"address": str(self.address), "address": str(self.address),
"phone_number": str(self.phone_number), "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")), "created_at": str(self.created.strftime("%Y-%m-%d")),
} }
@property @property
def full_name(self): def full_name(self):
return f"{self.first_name} {self.last_name}" return f"{self.first_name} {self.last_name}"
def convert_to_customer(self,entity): 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 = entity.create_customer(
customer_model_kwargs={ customer_model_kwargs={
"customer_name": self.full_name, "customer_name": self.full_name,
@ -1184,11 +1181,12 @@ class Lead(models.Model):
"email": self.email, "email": self.email,
} }
) )
customer.additional_info = {}
customer.additional_info.update({"info":self.to_dict()}) customer.additional_info = {}
self.customer = customer customer.additional_info.update({"info":self.to_dict()})
customer.save() self.customer = customer
self.save() customer.save()
self.save()
def get_latest_schedule(self): def get_latest_schedule(self):
return self.schedules.order_by('-scheduled_at').first() 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) purpose = models.CharField(max_length=200, choices=PURPOSE_CHOICES)
scheduled_at = models.DateTimeField() scheduled_at = models.DateTimeField()
scheduled_type = models.CharField(max_length=200, choices=ScheduledType,default='Call') 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) notes = models.TextField(blank=True, null=True)
status = models.CharField(max_length=200, choices=ScheduleStatusChoices, default='Scheduled') status = models.CharField(max_length=200, choices=ScheduleStatusChoices, default='Scheduled')
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
@ -1226,8 +1225,9 @@ class Schedule(models.Model):
def __str__(self): def __str__(self):
return f"Scheduled {self.purpose} with {self.lead.full_name} on {self.scheduled_at}" return f"Scheduled {self.purpose} with {self.lead.full_name} on {self.scheduled_at}"
@property
def schedule_past_date(self): def schedule_past_date(self):
if self.scheduled_at < timezone.now(): if self.scheduled_at < now():
return True return True
return False return False

View File

@ -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 = entity.get_items_all().filter(name=instance.car.vin).first()
product.default_amount = instance.selling_price 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({"car_finance":instance.to_dict()})
product.additional_info.update({"additional_services": [service.to_dict() for service in instance.additional_services.all()]}) product.additional_info.update({"additional_services": [service.to_dict() for service in instance.additional_services.all()]})
product.save() product.save()

View File

@ -319,10 +319,10 @@ def number_to_words_arabic(number):
return ' '.join(words) return ' '.join(words)
@register.filter(name='num2words') # @register.filter(name='num2words')
def num2words(number, language='en'): # def num2words(number, language='en'):
"""Template filter to convert a number to words in the specified language.""" # """Template filter to convert a number to words in the specified language."""
if language == 'ar': # if language == 'ar':
return number_to_words_arabic(number) # return number_to_words_arabic(number)
else: # else:
return number_to_words_english(number) # return number_to_words_english(number)

View File

@ -88,7 +88,7 @@ urlpatterns = [
path( path(
"crm/leads/<int:pk>/view/", views.LeadDetailView.as_view(), name="lead_detail" "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( path(
"crm/leads/<int:pk>/update/", views.LeadUpdateView.as_view(), name="lead_update" "crm/leads/<int:pk>/update/", views.LeadUpdateView.as_view(), name="lead_update"
), ),
@ -196,7 +196,6 @@ urlpatterns = [
), ),
path("cars/add/", views.CarCreateView.as_view(), name="car_add"), path("cars/add/", views.CarCreateView.as_view(), name="car_add"),
path("ajax/", views.AjaxHandlerView.as_view(), name="ajax_handler"), path("ajax/", views.AjaxHandlerView.as_view(), name="ajax_handler"),
path("cars/get-car-models/", views.get_car_models, name="get_car_models"),
path( path(
"cars/<int:car_pk>/add-color/", views.CarColorCreate.as_view(), name="add_color" "cars/<int:car_pk>/add-color/", views.CarColorCreate.as_view(), name="add_color"
), ),

View File

@ -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 calendar import month_name
from random import randint from random import randint
from rich import print from rich import print
@ -115,6 +118,11 @@ logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
class Hash(Func):
function = 'get_hash'
def switch_language(request): def switch_language(request):
language = request.GET.get("language", "en") language = request.GET.get("language", "en")
referer = request.META.get("HTTP_REFERER", "/") referer = request.META.get("HTTP_REFERER", "/")
@ -2345,6 +2353,7 @@ def create_estimate(request):
{"status": "error", "message": "Items and Quantities are required"}, {"status": "error", "message": "Items and Quantities are required"},
status=400, status=400,
) )
if isinstance(quantities, list): if isinstance(quantities, list):
if "0" in quantities: if "0" in quantities:
return JsonResponse( return JsonResponse(
@ -2355,7 +2364,17 @@ def create_estimate(request):
return JsonResponse( return JsonResponse(
{"status": "error", "message": "Quantity must be greater than zero"} {"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 = entity.create_estimate(
estimate_title=title, customer_model=customer, contract_terms=terms estimate_title=title, customer_model=customer, contract_terms=terms
) )
@ -2375,18 +2394,20 @@ def create_estimate(request):
] ]
items_txs = [] items_txs = []
for item in items_list: for item in items_list:
item_instance = ItemModel.objects.get(pk=item.get("item_id")) # item_instance = ItemModel.objects.get(pk=item.get("item_id"))
car_instance = models.Car.objects.get(vin=item_instance.name) car_instance = ItemModel.objects.filter(additional_info__car_info__hash=item.get("item_id")).all()
items_txs.append(
{ # car_instance = models.Car.objects.get(vin=item_instance.name)
"item_number": item_instance.item_number, for i in car_instance[:int(quantities[0])]:
"quantity": Decimal(item.get("quantity")), items_txs.append(
"unit_cost": car_instance.finances.selling_price, {
"unit_revenue": car_instance.finances.selling_price, "item_number": i.item_number,
"total_amount": (car_instance.finances.total_vat) "quantity": 1,
* int(item.get("quantity")), "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 = { estimate_itemtxs = {
item.get("item_number"): { item.get("item_number"): {
@ -2416,14 +2437,18 @@ def create_estimate(request):
) )
if isinstance(items, list): if isinstance(items, list):
for item in items: for item in estimate_itemtxs.keys():
item_instance = ItemModel.objects.get(pk=item) item_instance = ItemModel.objects.get(item_number=item)
instance = models.Car.objects.get(vin=item_instance.name) instance = models.Car.objects.get(name=item_instance.name)
reserve_car(instance, request) 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: else:
item_instance = ItemModel.objects.get(pk=items) item_instance = ItemModel.objects.get(additioinal_info__car_info__hash=items)
instance = models.Car.objects.get(vin=item_instance.name) instance = models.Car.objects.get(hash=item)
response = reserve_car(instance, request) response = reserve_car(instance, request)
url = reverse("estimate_detail", kwargs={"pk": estimate.pk}) url = reverse("estimate_detail", kwargs={"pk": estimate.pk})
@ -2439,17 +2464,16 @@ def create_estimate(request):
entity_slug=entity.slug, user_model=entity.admin entity_slug=entity.slug, user_model=entity.admin
) )
form.fields["customer"].queryset = entity.get_customers().filter(active=True) form.fields["customer"].queryset = entity.get_customers().filter(active=True)
car_list = models.Car.objects.filter( car_list = models.Car.objects.filter(dealer=dealer).exclude(status="reserved").values_list(
dealer=dealer, finances__selling_price__gt=0 'id_car_make__name', 'id_car_model__name','hash').distinct()
).exclude(status="reserved")
context = { context = {
"form": form, "form": form,
"items": [ "items": [
{ {
"car": x, 'make':x[0],
"product": entity.get_items_all() 'model':x[1],
.filter(item_role=ItemModel.ITEM_ROLE_PRODUCT, name=x.vin) 'hash': x[2]
.first(),
} }
for x in car_list for x in car_list
], ],
@ -2940,28 +2964,25 @@ class LeadDetailView(DetailView):
return context return context
class LeadCreateView(CreateView, SuccessMessageMixin, LoginRequiredMixin): def lead_create(request):
model = models.Lead form = forms.LeadForm()
form_class = forms.LeadForm make = request.GET.get("id_car_make",None)
template_name = "crm/leads/lead_form.html" if make:
success_message = "Lead created successfully!" form.fields['id_car_model'].queryset = models.CarModel.objects.filter(id_car_make=int(make))
success_url = reverse_lazy("lead_list") 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()
def form_valid(self, form): messages.success(request, "Lead created successfully!")
dealer = get_user_type(self.request) return redirect("lead_list")
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)
return render(request, "crm/leads/lead_form.html", {"form": form})
class LeadUpdateView(UpdateView): class LeadUpdateView(UpdateView):
model = models.Lead model = models.Lead
@ -2969,6 +2990,10 @@ class LeadUpdateView(UpdateView):
template_name = "crm/leads/lead_form.html" template_name = "crm/leads/lead_form.html"
success_url = reverse_lazy("lead_list") 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): def LeadDeleteView(request,pk):
lead = get_object_or_404(models.Lead, pk=pk) lead = get_object_or_404(models.Lead, pk=pk)
@ -3030,12 +3055,14 @@ def lead_convert(request, pk):
if lead.is_converted: if lead.is_converted:
messages.error(request, "Lead is already converted to customer.") messages.error(request, "Lead is already converted to customer.")
return redirect("opportunity_create",pk=lead.pk) 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) lead.convert_to_customer(dealer.entity)
messages.success(request, "Lead converted to customer successfully!") messages.success(request, "Lead converted to customer successfully!")
return redirect("opportunity_create",pk=lead.pk) return redirect("opportunity_create",pk=lead.pk)
@login_required @login_required
def schedule_lead(request, pk): def schedule_lead(request, pk):
lead = get_object_or_404(models.Lead, pk=pk) lead = get_object_or_404(models.Lead, pk=pk)
@ -3046,8 +3073,33 @@ def schedule_lead(request, pk):
instance.lead = lead instance.lead = lead
if hasattr(request.user, "staff"): if hasattr(request.user, "staff"):
instance.scheduled_by = request.user.staff instance.scheduled_by = request.user.staff
# Save the Schedule instance
instance.save() 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") return redirect("lead_list")
else: else:
messages.error(request, f"Invalid form data: {str(form.errors)}") messages.error(request, f"Invalid form data: {str(form.errors)}")
@ -3131,8 +3183,8 @@ class OpportunityCreateView(CreateView):
initial = super().get_initial() initial = super().get_initial()
if self.kwargs.get('pk',None): 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['customer'] = lead.customer
initial['car'] = lead.car
return initial return initial
def form_valid(self, form): def form_valid(self, form):

View File

@ -1,52 +1,96 @@
from django_ledger.models.invoice import InvoiceModel from django_ledger.models.invoice import InvoiceModel
from django_ledger.utils import accruable_net_summary from django_ledger.utils import accruable_net_summary
from decimal import Decimal from decimal import Decimal
from django_ledger.models import EstimateModel,EntityModel from django_ledger.models import EstimateModel,EntityModel,ItemModel
from rich import print from rich import print
from datetime import date 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 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(): def run():
# estimate = EstimateModel.objects.first() # print(Service.objects.first().pk)
# calculator = CarFinanceCalculator(estimate) # print(Appointment.objects.first().client)
# finance_data = calculator.get_finance_data()
# print(finance_data) # appointment = Appointment.objects.create(
# entity = EntityModel.objects.get(name="ismail") # client_name="John Doe",
# bs_report = entity.get_balance_sheet_statement( # client_email="john@example.com",
# to_date=date(2025, 1, 1), # service="Haircut",
# save_pdf=False, # date_time="2023-10-15 10:00:00",
# filepath='./' # 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",
# ) # )
# ic_report = entity.get_income_statement( # schedult = Schedule.objects.create(
# from_date=date(2022, 1, 1), # name="John Doe",
# to_date=date(2022, 12, 31), # email="john@example.com",
# save_pdf=False, # phone_number="123-456-7890",
# filepath='./' # 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(),
# ) # )
# # print(bs_report) # appointment = Appointment.objects.create(
# print(ic_report.get_report_data()) # client=User.objects.first(),
# estimate = EstimateModel.objects.first() # appointment_request=appointment_request,
# calculator = CarFinanceCalculator(estimate) # phone="123-456-7890",
# finance_data = calculator.get_finance_data() # 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)
# 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))

View File

@ -135,13 +135,14 @@
</div> </div>
</td> </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="">{{ 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"><a class="text-body-highlight" href="tel:{{ lead.phone_number }}">{{ lead.phone_number }}</a></td>
<td class="align-middle white-space-nowrap fw-semibold"> <td class="align-middle white-space-nowrap fw-semibold">
{% if lead.get_latest_schedule %} {% if lead.get_latest_schedule %}
<a href="{% url 'appointment:get_user_appointments' %}">
{% if lead.get_latest_schedule.scheduled_type == "Call" %} {% 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> {{ lead.get_latest_schedule.scheduled_at }}</span>
{% elif lead.get_latest_schedule.scheduled_type == "Meeting" %} {% 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> <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> <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> {{ lead.get_latest_schedule.scheduled_at }}</span>
{% endif %} {% endif %}
</a>
{% endif %} {% endif %}
</td> </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 text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">{{ lead.staff|upper }}</td>

View File

@ -18,7 +18,7 @@
<div class="mb-2 col-sm-2"> <div class="mb-2 col-sm-2">
<select class="form-control item" name="item[]" required> <select class="form-control item" name="item[]" required>
{% for item in items %} {% 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 %} {% endfor %}
</select> </select>
</div> </div>