From 778a41aa479da8fb7327efb5014fdf49aa83efc3 Mon Sep 17 00:00:00 2001 From: gitea Date: Sun, 9 Feb 2025 07:36:28 +0000 Subject: [PATCH] changes --- inventory/forms.py | 18 +++- .../migrations/0012_merge_20250206_1308.py | 14 +++ ...13_alter_carregistration_text2_and_more.py | 23 +++++ ..._id_car_make_lead_id_car_model_and_more.py | 33 +++++++ inventory/models.py | 44 +++++---- inventory/templatetags/custom_filters.py | 14 +-- inventory/urls.py | 3 +- inventory/views.py | 44 +++++---- scripts/run.py | 97 +++++++++++-------- templates/crm/leads/lead_list.html | 6 +- 10 files changed, 198 insertions(+), 98 deletions(-) create mode 100644 inventory/migrations/0012_merge_20250206_1308.py create mode 100644 inventory/migrations/0013_alter_carregistration_text2_and_more.py create mode 100644 inventory/migrations/0014_remove_lead_car_lead_id_car_make_lead_id_car_model_and_more.py diff --git a/inventory/forms.py b/inventory/forms.py index 6a6c0ff3..c5ec2884 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -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.models import ItemModel, InvoiceModel, BillModel,VendorModel 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 = [ diff --git a/inventory/migrations/0012_merge_20250206_1308.py b/inventory/migrations/0012_merge_20250206_1308.py new file mode 100644 index 00000000..f185be66 --- /dev/null +++ b/inventory/migrations/0012_merge_20250206_1308.py @@ -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 = [ + ] diff --git a/inventory/migrations/0013_alter_carregistration_text2_and_more.py b/inventory/migrations/0013_alter_carregistration_text2_and_more.py new file mode 100644 index 00000000..335015a8 --- /dev/null +++ b/inventory/migrations/0013_alter_carregistration_text2_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.17 on 2025-02-06 11:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0012_merge_20250206_1308'), + ] + + operations = [ + migrations.AlterField( + model_name='carregistration', + name='text2', + field=models.CharField(blank=True, max_length=1, null=True, verbose_name='Text 2'), + ), + migrations.AlterField( + model_name='carregistration', + name='text3', + field=models.CharField(blank=True, max_length=1, null=True, verbose_name='Text 3'), + ), + ] diff --git a/inventory/migrations/0014_remove_lead_car_lead_id_car_make_lead_id_car_model_and_more.py b/inventory/migrations/0014_remove_lead_car_lead_id_car_make_lead_id_car_model_and_more.py new file mode 100644 index 00000000..fece8a8e --- /dev/null +++ b/inventory/migrations/0014_remove_lead_car_lead_id_car_make_lead_id_car_model_and_more.py @@ -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'), + ), + ] diff --git a/inventory/models.py b/inventory/models.py index 82143e22..a589c4b0 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -1096,26 +1096,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") ) @@ -1167,7 +1167,8 @@ 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 @@ -1225,8 +1226,9 @@ class Schedule(models.Model): def __str__(self): return f"Scheduled {self.purpose} with {self.customer.customer_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 diff --git a/inventory/templatetags/custom_filters.py b/inventory/templatetags/custom_filters.py index 9629cb29..aa4d1ea6 100644 --- a/inventory/templatetags/custom_filters.py +++ b/inventory/templatetags/custom_filters.py @@ -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) \ No newline at end of file +# @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) \ No newline at end of file diff --git a/inventory/urls.py b/inventory/urls.py index ca4c7280..7189d90d 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -88,7 +88,7 @@ urlpatterns = [ path( "crm/leads//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//update/", views.LeadUpdateView.as_view(), name="lead_update" ), @@ -194,7 +194,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//add-color/", views.CarColorCreate.as_view(), name="add_color" ), diff --git a/inventory/views.py b/inventory/views.py index 0015b501..8fa6f1a8 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -1,3 +1,4 @@ +from appointment.models import Appointment from calendar import month_name from random import randint from rich import print @@ -2944,28 +2945,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 @@ -2973,6 +2971,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) diff --git a/scripts/run.py b/scripts/run.py index fba9a050..e4cf4eb9 100644 --- a/scripts/run.py +++ b/scripts/run.py @@ -4,49 +4,68 @@ from decimal import Decimal from django_ledger.models import EstimateModel,EntityModel from rich import print from datetime import date -from inventory.models import VatRate +from inventory.models import 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 -def run(): - # estimate = EstimateModel.objects.first() - # calculator = CarFinanceCalculator(estimate) - # finance_data = calculator.get_finance_data() +User = get_user_model() + +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", + # ) + 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(), + ) - - # 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)) \ No newline at end of file + appointment = Appointment.objects.create( + client=User.objects.first(), + appointment_request=appointment_request, + phone="123-456-7890", + address="123 Main St", + ) + \ No newline at end of file diff --git a/templates/crm/leads/lead_list.html b/templates/crm/leads/lead_list.html index 43f2ec2c..c2e3f5b4 100644 --- a/templates/crm/leads/lead_list.html +++ b/templates/crm/leads/lead_list.html @@ -72,7 +72,7 @@
{{ _("Source")|capfirst }} - `` +
@@ -135,13 +135,13 @@
- {{ lead.car.id_car_make.get_local_name }} - {{ lead.car.id_car_model.get_local_name }} {{ lead.year }} + {{ lead.id_car_make.get_local_name }} - {{ lead.id_car_model.get_local_name }} {{ lead.year }} {{ lead.email }} {{ lead.phone_number }} {% if lead.get_latest_schedule %} {% if lead.get_latest_schedule.scheduled_type == "Call" %} - + {{ lead.get_latest_schedule.scheduled_at }} {% elif lead.get_latest_schedule.scheduled_type == "Meeting" %}