This commit is contained in:
gitea 2025-02-09 16:19:52 +00:00
parent ffd72e2d93
commit f4111803bb
10 changed files with 210 additions and 83 deletions

View File

@ -686,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):

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 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,
}
@ -1176,7 +1171,8 @@ class Lead(models.Model):
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,
@ -1184,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()
@ -1219,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)

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.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()

View File

@ -1,4 +1,6 @@
from appointment.models import Appointment
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
@ -116,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 +2346,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 +2363,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 +2393,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 +2436,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 +2463,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)
@ -3031,12 +3054,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)
@ -3047,8 +3072,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)}")
@ -3131,9 +3181,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):

View File

@ -1,16 +1,17 @@
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,Lead,CarMake,CarModel,Schedule
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()
@ -24,7 +25,7 @@ def run():
# service="Haircut",
# date_time="2023-10-15 10:00:00",
# status="pending")
make = CarMake.objects.first()
# make = CarMake.objects.first()
# Lead.objects.create(
# first_name="John",
# last_name="Doe",
@ -53,19 +54,43 @@ def run():
# staff="John Doe",
# priority="high",
# )
service = Service.objects.first()
appointment_request = AppointmentRequest.objects.create(
date=get_localdate(),
start_time=datetime.now().strftime("%H:%M:%S"),
end_time=datetime.time(datetime.now() + timedelta(minutes=30)).strftime("%H:%M:%S"),
service=service,
staff_member=StaffMember.objects.first(),
)
# now = timezone.now()
# end_time = now + timedelta(minutes=10)
appointment = Appointment.objects.create(
client=User.objects.first(),
appointment_request=appointment_request,
phone="123-456-7890",
address="123 Main St",
)
# 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(),
# )
# 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)

View File

@ -140,6 +140,7 @@
<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 {% 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>
@ -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>

View File

@ -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>