diff --git a/inventory/forms.py b/inventory/forms.py index ceb30e33..8b5c12c8 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -1014,10 +1014,9 @@ class LeadForm(forms.ModelForm): id_car_make = forms.ModelChoiceField( label=_("Make"), - queryset=CarMake.objects.filter(is_sa_import=True), + queryset=CarMake.objects.all(), widget=forms.Select( attrs={ - "hx-get": "", "hx-include": "#id_id_car_make", "hx-select": "#div_id_id_car_model", @@ -1059,13 +1058,15 @@ class LeadForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + def filter_qs(self,**kwargs): + dealer = kwargs['dealer'] + dealer_make_list = DealersMake.objects.filter(dealer=dealer).values_list("car_make",flat=True) if "id_car_make" in self.fields: - queryset = self.fields["id_car_make"].queryset.filter(is_sa_import=True) + queryset = self.fields["id_car_make"].queryset.filter(is_sa_import=True,pk__in=dealer_make_list) self.fields["id_car_make"].choices = [ (obj.id_car_make, obj.get_local_name()) for obj in queryset ] - class ScheduleForm(forms.ModelForm): """ Represents a form for scheduling events, extending ModelForm to bind to the diff --git a/inventory/management/commands/initial_services_offered.py b/inventory/management/commands/initial_services_offered.py index 09a19235..1838b082 100644 --- a/inventory/management/commands/initial_services_offered.py +++ b/inventory/management/commands/initial_services_offered.py @@ -7,6 +7,6 @@ class Command(BaseCommand): def handle(self, *args, **options): Service.objects.all().delete() - Service.objects.create(name='Call', price=0,duration=datetime.timedelta(minutes=10),currency='SAR',description='15 min call') - Service.objects.create(name='Meeting', price=0,duration=datetime.timedelta(minutes=30),currency='SAR',description='30 min meeting') - Service.objects.create(name='Visit', price=0,duration=datetime.timedelta(minutes=30),currency='SAR',description='30 min visit') \ No newline at end of file + Service.objects.create(name='call', price=0,duration=datetime.timedelta(minutes=10),currency='SAR',description='15 min call') + Service.objects.create(name='meeting', price=0,duration=datetime.timedelta(minutes=30),currency='SAR',description='30 min meeting') + Service.objects.create(name='email', price=0,duration=datetime.timedelta(minutes=30),currency='SAR',description='30 min visit') \ No newline at end of file diff --git a/inventory/migrations/0011_lead_organization_alter_lead_customer.py b/inventory/migrations/0011_lead_organization_alter_lead_customer.py new file mode 100644 index 00000000..7432315a --- /dev/null +++ b/inventory/migrations/0011_lead_organization_alter_lead_customer.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.7 on 2025-05-07 14:32 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0010_organization_active'), + ] + + operations = [ + migrations.AddField( + model_name='lead', + name='organization', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='organization_leads', to='inventory.organization'), + ), + migrations.AlterField( + model_name='lead', + name='customer', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='customer_leads', to='inventory.customer'), + ), + ] diff --git a/inventory/migrations/0012_alter_customer_dob_alter_customer_national_id.py b/inventory/migrations/0012_alter_customer_dob_alter_customer_national_id.py new file mode 100644 index 00000000..82f162e2 --- /dev/null +++ b/inventory/migrations/0012_alter_customer_dob_alter_customer_national_id.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.7 on 2025-05-08 10:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0011_lead_organization_alter_lead_customer'), + ] + + operations = [ + migrations.AlterField( + model_name='customer', + name='dob', + field=models.DateField(blank=True, null=True, verbose_name='Date of Birth'), + ), + migrations.AlterField( + model_name='customer', + name='national_id', + field=models.CharField(blank=True, max_length=10, null=True, unique=True, verbose_name='National ID'), + ), + ] diff --git a/inventory/models.py b/inventory/models.py index c0f1327e..5ec5c4ea 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -173,7 +173,6 @@ class CarMake(models.Model, LocalizedNameMixin): class Meta: verbose_name = _("Make") - class CarModel(models.Model, LocalizedNameMixin): id_car_model = models.AutoField(primary_key=True) id_car_make = models.ForeignKey(CarMake, models.DO_NOTHING, db_column="id_car_make") @@ -927,9 +926,10 @@ class Staff(models.Model, LocalizedNameMixin): def add_group(self,group): try: self.user.groups.add(group) - self.add_as_manager() + if self.staff_type in ["accountant","manager"]: + self.add_as_manager() except Exception as e: - pass + print(e) def add_as_manager(self): if self.staff_type in ["accountant","manager"]: EntityManagementModel.objects.get_or_create( @@ -1049,10 +1049,10 @@ class Customer(models.Model): max_length=1, verbose_name=_("Gender"), ) - dob = models.DateField(verbose_name=_("Date of Birth")) + dob = models.DateField(verbose_name=_("Date of Birth"), null=True, blank=True) email = models.EmailField(unique=True, verbose_name=_("Email")) national_id = models.CharField( - max_length=10, unique=True, verbose_name=_("National ID") + max_length=10, unique=True, verbose_name=_("National ID"), null=True,blank=True ) phone_number = PhoneNumberField( region="SA", unique=True, verbose_name=_("Phone Number") @@ -1095,6 +1095,8 @@ class Customer(models.Model): except Exception: pass customer.save() + self.customer_model = customer + self.save() return customer def update_user_model(self): @@ -1127,6 +1129,8 @@ class Customer(models.Model): ) user.set_password("Tenhal@123") user.save() + self.user = user + self.save() return user def deactivate_account(self): self.active = False @@ -1184,6 +1188,8 @@ class Organization(models.Model, LocalizedNameMixin): except Exception: pass customer.save() + self.customer_model = customer + self.save() return customer def update_user_model(self): @@ -1214,6 +1220,8 @@ class Organization(models.Model, LocalizedNameMixin): ) user.set_password("Tenhal@123") user.save() + self.user = user + self.save() return user def deactivate_account(self): @@ -1258,12 +1266,13 @@ class Lead(models.Model): max_length=50, choices=[("customer", _("Customer")), ("organization", _("Organization"))], verbose_name=_("Lead Type") ,default="customer") customer = models.ForeignKey( - CustomerModel, on_delete=models.CASCADE, related_name="leads", + Customer, on_delete=models.CASCADE, related_name="customer_leads", + null=True,blank=True + ) + organization = models.ForeignKey( + Organization, on_delete=models.CASCADE, related_name="organization_leads", null=True,blank=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, @@ -1347,30 +1356,16 @@ class Lead(models.Model): @property def full_name(self): return f"{self.first_name} {self.last_name}" - def convert_to_customer(self,entity,lead): - customer = entity.get_customers().filter(email=self.email).first() - if entity and not customer: - customer = entity.create_customer( - commit=False, - customer_model_kwargs={ - "customer_name": self.full_name, - "address_1": self.address, - "phone": self.phone_number, - "email": self.email, - } - ) - - customer.additional_info.update({"info":self.to_dict()}) - if lead.lead_type == "organization": - customer.additional_info.update({"type":"organization"}) - else: - customer.additional_info.update({"type":"customer"}) - customer.save() - self.customer = customer + def convert_to_customer(self): self.status = Status.QUALIFIED self.save() - return customer + return self.get_customer_model() + def get_customer_model(self): + if self.customer: + return self.customer.customer_model + if self.organization: + return self.organization.customer_model def get_latest_schedule(self): return self.schedules.order_by('-scheduled_at').first() def get_latest_schedules(self): @@ -1378,15 +1373,15 @@ class Lead(models.Model): def get_all_schedules(self): return self.schedules.all().order_by('-scheduled_at') def get_calls(self): - return self.get_all_schedules().filter(scheduled_type='Call') + return self.get_all_schedules().filter(scheduled_type='call') def get_meetings(self): - return self.get_all_schedules().filter(scheduled_type='Meeting') + return self.get_all_schedules().filter(scheduled_type='meeting') def get_emails(self): - return Email.objects.filter(content_type__model="lead", object_id=self.id) + return Email.objects.filter(content_type__model="lead", object_id=self.pk) def get_notes(self): - return Notes.objects.filter(content_type__model="lead", object_id=self.id) + return Notes.objects.filter(content_type__model="lead", object_id=self.pk) def get_activities(self): - return Activity.objects.filter(content_type__model="lead", object_id=self.id) + return Activity.objects.filter(content_type__model="lead", object_id=self.pk) class Schedule(models.Model): PURPOSE_CHOICES = [ diff --git a/inventory/views.py b/inventory/views.py index 270e83af..288d4dca 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -150,7 +150,7 @@ from plans.quota import get_user_quota from django_tables2 import SingleTableView from django_tables2.export.views import ExportMixin from appointment.models import Appointment, AppointmentRequest, Service, StaffMember - +from django.db.models.functions import Lower from .models import SaleOrder from .services import ( decodevin, @@ -2494,6 +2494,7 @@ def UserGroupView(request, pk): if request.method == "POST": form = forms.UserGroupForm(request.POST) groups = request.POST.getlist("name") + staff.clear_groups() for i in groups: cg = models.CustomGroup.objects.get(id=int(i)) @@ -4601,13 +4602,7 @@ def lead_create(request): :return: An HTTP response object rendering the lead creation form or redirecting to the lead list page upon success. :rtype: HttpResponse """ - 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) - ) + dealer = get_user_type(request) if request.method == "POST": form = forms.LeadForm(request.POST) @@ -4620,51 +4615,57 @@ def lead_create(request): try: if form.is_valid(): instance = form.save(commit=False) - dealer = get_user_type(request) instance.dealer = dealer instance.staff = form.cleaned_data.get("staff") - # creating customer in ledger - customer = ( - dealer.entity.get_customers().filter(email=instance.email).first() - ) - if not customer: - customer = dealer.entity.create_customer( - commit=False, - customer_model_kwargs={ - "customer_name": instance.full_name, - "address_1": instance.address, - "phone": instance.phone_number, - "email": instance.email, - "sales_tax_rate": 0.15, - }, - ) - customer_info = { - "first_name": instance.first_name, - "last_name": instance.last_name, - "address": instance.address, - "phone_number": str(instance.phone_number), - "email": instance.email, - "crn": form.cleaned_data["crn"], - "vrn": form.cleaned_data["vrn"], - } - customer.additional_info.update({"customer_info": customer_info}) - customer.additional_info.update({"type": "lead"}) - customer.save() - instance.customer = customer - # try: - # user = User.objects.get(email=customer.email) - # user.first_name = instance.first_name - # user.last_name = instance.last_name - # user.save() - # except Exception as e: - # print(e) + if instance.lead_type == "customer": + customer = models.Customer.objects.filter(email=instance.email) + if not customer: + customer = models.Customer( + dealer=dealer, + address=instance.address, + phone_number=instance.phone_number, + email=instance.email, + first_name=instance.first_name, + last_name=instance.last_name, + ) + customer.create_user_model() + customer.create_customer_model() + customer.save() + instance.customer = customer + + + if instance.lead_type == "organization": + organization = models.Organization.objects.filter(email=instance.email) + if not organization: + organization = models.Organization( + dealer=dealer, + address=instance.address, + phone_number=instance.phone_number, + email=instance.email, + name=instance.first_name + " " + instance.last_name, + ) + organization.create_user_model() + organization.create_customer_model() + organization.save() + instance.organization = organization + instance.save() messages.success(request, _("Lead created successfully")) return redirect("lead_list") + else: + messages.error(request, f"Lead was not created ... : {str(form.errors)}") except Exception as e: messages.error(request, f"Lead was not created ... : {str(e)}") + form = forms.LeadForm() + form.filter_qs(dealer=dealer) + + 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) + ) return render(request, "crm/leads/lead_form.html", {"form": form}) @@ -4870,7 +4871,7 @@ def lead_convert(request, pk): if hasattr(lead, "opportunity"): messages.error(request, _("Lead is already converted to customer")) else: - customer = lead.convert_to_customer(dealer.entity,lead) + customer = lead.convert_to_customer() models.Opportunity.objects.create(dealer=dealer,customer=customer,lead=lead,probability=50,stage=models.Stage.PROSPECT,staff=lead.staff,status=models.Status.QUALIFIED) messages.success(request, _("Lead converted to customer successfully")) return redirect("lead_list") @@ -4896,6 +4897,7 @@ def schedule_lead(request, pk): method and validity of the form submission. :rtype: HttpResponse """ + if not request.is_staff: messages.error(request, _("You do not have permission to schedule lead")) return redirect("lead_list") @@ -4907,10 +4909,12 @@ def schedule_lead(request, pk): instance = form.save(commit=False) instance.lead = lead instance.scheduled_by = request.user - instance.customer = lead.customer + + instance.customer = lead.get_customer_model() # Create AppointmentRequest - service,_ = Service.objects.get_or_create(name=instance.scheduled_type,duration=datetime.timedelta(minutes=10),price=0) + # service,_ = Service.objects.get_or_create(name=instance.scheduled_type,duration=datetime.timedelta(minutes=10),price=0) + service = Service.objects.get(name=instance.scheduled_type) # service = Service.objects.filter(name=instance.scheduled_type).first() # if not service: # messages.error(request, "Service not found!") @@ -5008,7 +5012,7 @@ def send_lead_email(request, pk, email_pk=None): lead.status = models.Status.CONTACTED lead.save() - # lead.convert_to_customer(dealer.entity) + if request.method == "POST": email_pk = request.POST.get("email_pk") diff --git a/templates/crm/leads/lead_list.html b/templates/crm/leads/lead_list.html index 1b00c7c7..34cd1475 100644 --- a/templates/crm/leads/lead_list.html +++ b/templates/crm/leads/lead_list.html @@ -153,15 +153,15 @@ {% for schedule in lead.get_latest_schedules %}