diff --git a/inventory/forms.py b/inventory/forms.py index 7d78c4e7..fd26c519 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -653,9 +653,7 @@ class LeadForm(forms.ModelForm): 'email', 'phone_number', 'address', - 'id_car_make', - 'id_car_model', - 'year', + 'car', 'source', 'channel', 'staff', diff --git a/inventory/migrations/0008_schedule_status.py b/inventory/migrations/0008_schedule_status.py new file mode 100644 index 00000000..6d1d25ac --- /dev/null +++ b/inventory/migrations/0008_schedule_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.17 on 2025-02-05 09:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0007_schedule_scheduled_type'), + ] + + operations = [ + migrations.AddField( + model_name='schedule', + name='status', + field=models.CharField(choices=[('Scheduled', 'Scheduled'), ('Completed', 'Completed'), ('Canceled', 'Canceled')], default='Scheduled', max_length=200), + ), + ] diff --git a/inventory/migrations/0009_alter_opportunity_customer.py b/inventory/migrations/0009_alter_opportunity_customer.py new file mode 100644 index 00000000..a0497c71 --- /dev/null +++ b/inventory/migrations/0009_alter_opportunity_customer.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.17 on 2025-02-05 10:00 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.DJANGO_LEDGER_CUSTOMER_MODEL), + ('inventory', '0008_schedule_status'), + ] + + operations = [ + migrations.AlterField( + model_name='opportunity', + name='customer', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL), + ), + ] diff --git a/inventory/migrations/0010_remove_lead_id_car_make_remove_lead_id_car_model_and_more.py b/inventory/migrations/0010_remove_lead_id_car_make_remove_lead_id_car_model_and_more.py new file mode 100644 index 00000000..71bbe9b0 --- /dev/null +++ b/inventory/migrations/0010_remove_lead_id_car_make_remove_lead_id_car_model_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.17 on 2025-02-05 10:05 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0009_alter_opportunity_customer'), + ] + + operations = [ + migrations.RemoveField( + model_name='lead', + name='id_car_make', + ), + migrations.RemoveField( + model_name='lead', + name='id_car_model', + ), + migrations.AddField( + model_name='lead', + name='car', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.car', verbose_name='Car'), + ), + ] diff --git a/inventory/migrations/0011_remove_lead_year_alter_schedule_customer.py b/inventory/migrations/0011_remove_lead_year_alter_schedule_customer.py new file mode 100644 index 00000000..c317e165 --- /dev/null +++ b/inventory/migrations/0011_remove_lead_year_alter_schedule_customer.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.17 on 2025-02-05 10:17 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.DJANGO_LEDGER_CUSTOMER_MODEL), + ('inventory', '0010_remove_lead_id_car_make_remove_lead_id_car_model_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='lead', + name='year', + ), + migrations.AlterField( + model_name='schedule', + name='customer', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to=settings.DJANGO_LEDGER_CUSTOMER_MODEL), + ), + ] diff --git a/inventory/models.py b/inventory/models.py index 5e372430..55a59074 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -1096,23 +1096,26 @@ class Lead(models.Model): CustomerModel, on_delete=models.CASCADE, related_name="leads", null=True,blank=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 + 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 + # ) source = models.CharField( max_length=50, choices=Sources.choices, verbose_name=_("Source") ) @@ -1164,16 +1167,14 @@ class Lead(models.Model): "email": str(self.email), "address": str(self.address), "phone_number": str(self.phone_number), - "id_car_make": str(self.id_car_make.name), - "id_car_model": str(self.id_car_model.name), - "year": str(self.year), + "car": self.car.to_dict(), "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 CustomerModel.objects.filter(email=self.email).exists(): + if entity and not entity.get_customers().filter(email=self.email).exists(): customer = entity.create_customer( customer_model_kwargs={ "customer_name": self.full_name, @@ -1184,8 +1185,8 @@ class Lead(models.Model): ) customer.additional_info = {} customer.additional_info.update({"info":self.to_dict()}) + self.customer = customer customer.save() - self.customer = customer self.save() def get_latest_schedule(self): @@ -1205,19 +1206,25 @@ class Schedule(models.Model): ('Meeting', 'Meeting'), ('Email', 'Email'), ] + ScheduleStatusChoices = [ + ('Scheduled', 'Scheduled'), + ('Completed', 'Completed'), + ('Canceled', 'Canceled'), + ] lead = models.ForeignKey(Lead, on_delete=models.CASCADE, related_name='schedules') - customer = models.ForeignKey(CustomerModel, on_delete=models.CASCADE, related_name='schedules') + customer = models.ForeignKey(CustomerModel, on_delete=models.CASCADE, related_name='schedules',null=True,blank=True) scheduled_by = models.ForeignKey(Staff, on_delete=models.CASCADE) purpose = models.CharField(max_length=200, choices=PURPOSE_CHOICES) scheduled_at = models.DateTimeField() scheduled_type = models.CharField(max_length=200, choices=ScheduledType,default='Call') 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) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return f"Scheduled {self.purpose} with {self.customer.customer_name} on {self.scheduled_at}" - + def schedule_past_date(self): if self.scheduled_at < timezone.now(): return True @@ -1260,7 +1267,7 @@ class Opportunity(models.Model): Dealer, on_delete=models.CASCADE, related_name="opportunities" ) customer = models.ForeignKey( - Customer, on_delete=models.CASCADE, related_name="opportunities" + CustomerModel, on_delete=models.CASCADE, related_name="opportunities" ) car = models.ForeignKey( Car, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("Car") diff --git a/inventory/urls.py b/inventory/urls.py index f1352174..408ab020 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -116,6 +116,11 @@ urlpatterns = [ views.OpportunityCreateView.as_view(), name="opportunity_create", ), + path( + "crm/opportunities//create/", + views.OpportunityCreateView.as_view(), + name="opportunity_create", + ), path( "crm/opportunities//", views.OpportunityDetailView.as_view(), diff --git a/inventory/views.py b/inventory/views.py index 1e94bae2..f7665bbc 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -2978,9 +2978,13 @@ def add_note_to_lead(request, pk): def lead_convert(request, pk): lead = get_object_or_404(models.Lead, pk=pk) dealer = get_user_type(request) + if lead.is_converted: + messages.error(request, "Lead is already converted to customer.") + return redirect("opportunity_create",pk=lead.pk) + lead.convert_to_customer(dealer.entity) messages.success(request, "Lead converted to customer successfully!") - return redirect("lead_list") + return redirect("opportunity_create",pk=lead.pk) def schedule_lead(request, pk): lead = get_object_or_404(models.Lead, pk=pk) @@ -2988,8 +2992,7 @@ def schedule_lead(request, pk): form = forms.ScheduleForm(request.POST) if form.is_valid(): instance = form.save(commit=False) - instance.lead = lead - instance.customer = lead.customer + instance.lead = lead if hasattr(request.user, "staff"): instance.scheduled_by = request.user.staff instance.save() @@ -3067,10 +3070,16 @@ class OpportunityCreateView(CreateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) dealer = get_user_type(self.request) - context["customer"] = models.Customer.objects.filter(dealer=dealer) - context["cars"] = models.Car.objects.filter(dealer=dealer) return context - + + def get_initial(self): + initial = super().get_initial() + if self.kwargs.get('pk',None): + 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): dealer = get_user_type(self.request) form.instance.dealer = dealer diff --git a/templates/crm/leads/lead_list.html b/templates/crm/leads/lead_list.html index d2d21fd4..ed393ade 100644 --- a/templates/crm/leads/lead_list.html +++ b/templates/crm/leads/lead_list.html @@ -72,7 +72,7 @@
{{ _("Source")|capfirst }} - `` +
@@ -135,19 +135,26 @@
- {{ lead.id_car_make.get_local_name }} - {{ lead.id_car_model.get_local_name }} {{ lead.year }} + {{ lead.car.id_car_make.get_local_name }} - {{ lead.car.id_car_model.get_local_name }} {{ lead.year }} {{ lead.email }} {{ lead.phone_number }} {% if lead.get_latest_schedule %} - {{ lead.get_latest_schedule.scheduled_type }}
- {{ lead.get_latest_schedule.scheduled_at }} + {% if lead.get_latest_schedule.scheduled_type == "Call" %} + + {{ lead.get_latest_schedule.scheduled_at }} + {% elif lead.get_latest_schedule.scheduled_type == "Meeting" %} + + {{ lead.get_latest_schedule.scheduled_at }} + {% elif lead.get_latest_schedule.scheduled_type == "Email" %} + + {{ lead.get_latest_schedule.scheduled_at }} + {% endif %} {% endif %} {{ lead.staff|upper }} {{ lead.source|upper }} - {{ lead.channel|upper }} - {{ lead.created|date }} + {{ lead.channel|upper }}