diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index ddb8237..1ca7312 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -6,11 +6,12 @@
-
-
+
+
+
+
-
@@ -42,26 +43,26 @@
- {
+ "keyToString": {
+ "Django Server.crmApi.executor": "Run",
+ "RunOnceActivity.OpenDjangoStructureViewOnStart": "true",
+ "RunOnceActivity.OpenProjectViewOnStart": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "SHARE_PROJECT_CONFIGURATION_FILES": "true",
+ "WebServerToolWindowFactoryState": "false",
+ "git-widget-placeholder": "dev",
+ "ignore.virus.scanning.warn.message": "true",
+ "last_opened_file_path": "C:/Users/mjavad/Projects/crmProject/Project/api-Project/crmApi",
+ "node.js.detected.package.eslint": "true",
+ "node.js.detected.package.tslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "settings.editor.selected.configurable": "preferences.pluginManager",
+ "vue.rearranger.settings.migration": "true"
}
-}]]>
+}
@@ -154,7 +155,13 @@
-
+
+
+
+
+
+
+
diff --git a/apps/jwt_auth/migrations/0089_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0089_alter_cuserroleloginopt_uuid.py
new file mode 100644
index 0000000..8ed8286
--- /dev/null
+++ b/apps/jwt_auth/migrations/0089_alter_cuserroleloginopt_uuid.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.2.1 on 2024-08-24 13:14
+
+from django.db import migrations, models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('jwt_auth', '0088_alter_cuserroleloginopt_uuid'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='cuserroleloginopt',
+ name='uuid',
+ field=models.UUIDField(default=uuid.UUID('70141bbf-e757-4eae-a6a4-7d430084c8a6'), unique=True),
+ ),
+ ]
diff --git a/apps/jwt_auth/migrations/0090_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0090_alter_cuserroleloginopt_uuid.py
new file mode 100644
index 0000000..4d30ffe
--- /dev/null
+++ b/apps/jwt_auth/migrations/0090_alter_cuserroleloginopt_uuid.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.2.1 on 2024-08-24 13:21
+
+from django.db import migrations, models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('jwt_auth', '0089_alter_cuserroleloginopt_uuid'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='cuserroleloginopt',
+ name='uuid',
+ field=models.UUIDField(default=uuid.UUID('48476cd6-6133-4355-a10b-319b7de479ee'), unique=True),
+ ),
+ ]
diff --git a/apps/jwt_auth/migrations/0091_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0091_alter_cuserroleloginopt_uuid.py
new file mode 100644
index 0000000..cb8a45c
--- /dev/null
+++ b/apps/jwt_auth/migrations/0091_alter_cuserroleloginopt_uuid.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.2.1 on 2024-08-27 06:14
+
+from django.db import migrations, models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('jwt_auth', '0090_alter_cuserroleloginopt_uuid'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='cuserroleloginopt',
+ name='uuid',
+ field=models.UUIDField(default=uuid.UUID('2072fa9d-9601-4c50-b1ee-bd2b437bee2c'), unique=True),
+ ),
+ ]
diff --git a/apps/jwt_auth/migrations/0092_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0092_alter_cuserroleloginopt_uuid.py
new file mode 100644
index 0000000..899f995
--- /dev/null
+++ b/apps/jwt_auth/migrations/0092_alter_cuserroleloginopt_uuid.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.2.1 on 2024-08-27 06:38
+
+from django.db import migrations, models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('jwt_auth', '0091_alter_cuserroleloginopt_uuid'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='cuserroleloginopt',
+ name='uuid',
+ field=models.UUIDField(default=uuid.UUID('52471ac1-6299-4ffa-b56f-8b1e8a343d18'), unique=True),
+ ),
+ ]
diff --git a/apps/lead/admin.py b/apps/lead/admin.py
index 68ff7d3..ffdccbe 100644
--- a/apps/lead/admin.py
+++ b/apps/lead/admin.py
@@ -33,4 +33,8 @@ class LeadReasonAdmin(admin.ModelAdmin):
pass
+@admin.register(LeadEvent)
+class LeadEventAdmin(admin.ModelAdmin):
+ pass
+
diff --git a/apps/lead/migrations/0043_lead_mobile1_lead_mobile2_lead_phone_and_more.py b/apps/lead/migrations/0043_lead_mobile1_lead_mobile2_lead_phone_and_more.py
new file mode 100644
index 0000000..ba25803
--- /dev/null
+++ b/apps/lead/migrations/0043_lead_mobile1_lead_mobile2_lead_phone_and_more.py
@@ -0,0 +1,33 @@
+# Generated by Django 4.2.1 on 2024-08-24 13:14
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('lead', '0042_alter_lead_desc_alter_lead_status_alter_lead_title_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='lead',
+ name='mobile1',
+ field=models.PositiveBigIntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='lead',
+ name='mobile2',
+ field=models.PositiveBigIntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='lead',
+ name='phone',
+ field=models.PositiveBigIntegerField(blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='lead',
+ name='status',
+ field=models.CharField(blank=True, choices=[('UN', 'unknown'), ('SU', 'OK'), ('FA', 'FAIL')], default='UN', max_length=2, null=True),
+ ),
+ ]
diff --git a/apps/lead/migrations/0044_leadevent.py b/apps/lead/migrations/0044_leadevent.py
new file mode 100644
index 0000000..0fd52b8
--- /dev/null
+++ b/apps/lead/migrations/0044_leadevent.py
@@ -0,0 +1,28 @@
+# Generated by Django 4.2.1 on 2024-08-24 13:21
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('workspace', '0016_remove_workspace_cuser_owner'),
+ ('lead', '0043_lead_mobile1_lead_mobile2_lead_phone_and_more'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='LeadEvent',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('updated_at', models.DateTimeField(auto_now=True)),
+ ('name', models.CharField(max_length=50)),
+ ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/apps/lead/migrations/0045_lead_lead_event_alter_lead_status.py b/apps/lead/migrations/0045_lead_lead_event_alter_lead_status.py
new file mode 100644
index 0000000..93cd0f4
--- /dev/null
+++ b/apps/lead/migrations/0045_lead_lead_event_alter_lead_status.py
@@ -0,0 +1,24 @@
+# Generated by Django 4.2.1 on 2024-08-27 06:14
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('lead', '0044_leadevent'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='lead',
+ name='lead_event',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='lead.leadevent'),
+ ),
+ migrations.AlterField(
+ model_name='lead',
+ name='status',
+ field=models.CharField(blank=True, choices=[('FA', 'FAIL'), ('UN', 'unknown'), ('SU', 'OK')], default='UN', max_length=2, null=True),
+ ),
+ ]
diff --git a/apps/lead/migrations/0046_leadevent_message_leadevent_send_sms.py b/apps/lead/migrations/0046_leadevent_message_leadevent_send_sms.py
new file mode 100644
index 0000000..7da39a3
--- /dev/null
+++ b/apps/lead/migrations/0046_leadevent_message_leadevent_send_sms.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.1 on 2024-08-27 06:38
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('lead', '0045_lead_lead_event_alter_lead_status'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='leadevent',
+ name='message',
+ field=models.TextField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='leadevent',
+ name='send_sms',
+ field=models.BooleanField(blank=True, default=False, null=True),
+ ),
+ ]
diff --git a/apps/lead/models.py b/apps/lead/models.py
index ce438b7..fdc8a94 100644
--- a/apps/lead/models.py
+++ b/apps/lead/models.py
@@ -30,6 +30,14 @@ class LeadStep(BaseModel):
def __str__(self):
return self.name
+class LeadEvent(BaseModel):
+ name = models.CharField(max_length=50)
+ message = models.TextField(null=True, blank=True)
+ send_sms = models.BooleanField(default=False, null=True, blank=True)
+ sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False)
+
+ def __str__(self):
+ return self.name
class LeadReason(BaseModel):
name = models.CharField(max_length=50)
@@ -50,7 +58,11 @@ class Lead(BaseModel):
title = models.CharField(max_length=300)
desc = models.TextField()
+ mobile1 = models.PositiveBigIntegerField(null=True, blank=True)
+ mobile2 = models.PositiveBigIntegerField(null=True, blank=True)
+ phone = models.PositiveBigIntegerField(null=True, blank=True)
customer = models.ForeignKey(CustomerModel, on_delete=models.CASCADE, null=False, blank=False)
+ lead_event = models.ForeignKey(LeadEvent, on_delete=models.SET_NULL, null=True, blank=True)
product_type = models.ForeignKey(ProductTypeModel, on_delete=models.SET_NULL, null=True, blank=True)
type = models.ForeignKey(LeadType, on_delete=models.SET_NULL, null=True, blank=True)
source = models.ForeignKey(LeadSource, on_delete=models.SET_NULL, null=True, blank=True)
diff --git a/apps/lead/selectors.py b/apps/lead/selectors.py
index 4e594da..90dc334 100644
--- a/apps/lead/selectors.py
+++ b/apps/lead/selectors.py
@@ -1,5 +1,5 @@
from apps.lead.models import Lead as LeadModel, LeadType as LeadTypeModel,\
- LeadSource as LeadSourceModel, LeadStep as LeadStepModel
+ LeadSource as LeadSourceModel, LeadStep as LeadStepModel, LeadEvent as LeadEventModel
from apps.shop.models import ProductType as ProductTypeModel
from apps.workspace.models import City as CityModel
from utils.get_user_workspace import get_user_workspace_uuid
@@ -84,6 +84,7 @@ def fields_new_lead_form(request):
product_types = ProductTypeModel.objects.filter(sys_ws__uuid=workspace_uuid)
lead_source = LeadSourceModel.objects.filter(sys_ws__uuid=workspace_uuid)
lead_step = LeadStepModel.objects.filter(sys_ws__uuid=workspace_uuid)
+ lead_event = LeadEventModel.objects.filter(sys_ws__uuid=workspace_uuid)
shahr = CityModel.objects.filter()
data = {
@@ -91,6 +92,7 @@ def fields_new_lead_form(request):
'lead_types': lead_type,
'lead_source': lead_source,
'lead_steps': lead_step,
+ 'lead_event': lead_event,
'shahr': shahr,
}
return data
diff --git a/apps/lead/serializers.py b/apps/lead/serializers.py
index 281839e..40fd47f 100644
--- a/apps/lead/serializers.py
+++ b/apps/lead/serializers.py
@@ -1,7 +1,8 @@
from rest_framework import serializers
from apps.lead.models import (
LeadType as LeadTypeModel, LeadSource as LeadSourceModel,
- LeadStep as LeadStepModel, LeadReason as LeadReasonModel
+ LeadStep as LeadStepModel, LeadReason as LeadReasonModel,
+ LeadEvent as LeadEventModel
)
from apps.shop.models import ProductType as ProductTypeModel
from apps.workspace.models import City as CityModel
@@ -39,6 +40,12 @@ class LeadReasonSerializers(serializers.ModelSerializer):
exclude = ['sys_ws', 'created_at', 'updated_at']
+class LeadEventSerializers(serializers.ModelSerializer):
+ class Meta:
+ model = LeadEventModel
+ exclude = ['sys_ws', 'created_at', 'updated_at']
+
+
class ShahrSerializers(serializers.ModelSerializer):
class Meta:
model = CityModel
diff --git a/apps/lead/services.py b/apps/lead/services.py
index e00cdf0..28f8540 100644
--- a/apps/lead/services.py
+++ b/apps/lead/services.py
@@ -1,16 +1,17 @@
from apps.lead.models import Lead as LeadModel,\
- LeadType as LeadTypeModel, LeadStep as LeadStepModel, LeadSource as LeadSourceModel, LeadComment as LeadCommentModel
+ LeadType as LeadTypeModel, LeadStep as LeadStepModel, LeadSource as LeadSourceModel, LeadComment as LeadCommentModel, LeadEvent as LeadEventModel
from apps.shop.models import ProductType as ProductTypeModel
from utils.get_user_workspace import get_user_workspace
from utils.get_cuser_role import get_cuser_role
from utils.validations import ObjectValidationById
from apps.customer.models import Customer as CustomerModel
from apps.workspace.models import City as CityModel
+from utils.services.sms import DefaultSmsProvider
from rest_framework import exceptions
-def create_new_lead(request, title: str, customer_id: str, desc: str, product_type_id: int,
- lead_type_id: int, lead_source_id: int, city_id: int, lead_step_id: int) -> LeadModel:
+def create_new_lead(request, title: str, customer_id: str, mobile1: int, desc: str, product_type_id: int,
+ lead_type_id: int, lead_source_id: int, city_id: int, lead_step_id: int, mobile2: int = None, phone: int = None, lead_event_id: int = None) -> LeadModel:
try:
sys_ws = get_user_workspace(request=request)
cuser_role = get_cuser_role(request=request)
@@ -22,9 +23,20 @@ def create_new_lead(request, title: str, customer_id: str, desc: str, product_ty
product_type = ObjectValidationById(object_model=ProductTypeModel, request=request).is_valid(product_type_id)
city = ObjectValidationById(object_model=CityModel, request=request).is_valid(city_id)
+ lead_event_obj = None
+
new_lead = LeadModel()
new_lead.title = title
new_lead.customer = customer
+ if mobile1:
+ new_lead.mobile1 = mobile1
+ if mobile2:
+ new_lead.mobile2 = mobile2
+ if phone:
+ new_lead.phone = phone
+ if lead_event_id and (lead_event_id != '0' or lead_event_id != 0):
+ lead_event_obj = LeadEventModel.objects.get(id=lead_event_id, sys_ws=sys_ws)
+ new_lead.lead_event = lead_event_obj
new_lead.desc = desc
new_lead.type = lead_type
new_lead.step = lead_step
@@ -36,6 +48,14 @@ def create_new_lead(request, title: str, customer_id: str, desc: str, product_ty
new_lead.save()
+ # send sms
+ if lead_event_obj and lead_event_obj.send_sms:
+ sms_provider = DefaultSmsProvider()
+ if mobile1:
+ sms_provider.send_sms(str(mobile1), lead_event_obj.message)
+ if mobile2:
+ sms_provider.send_sms(str(mobile2), lead_event_obj.message)
+
return new_lead
except Exception as exp:
@@ -45,7 +65,7 @@ def create_new_lead(request, title: str, customer_id: str, desc: str, product_ty
def update_lead(request, lead_id: int, title: str, customer_id: str, desc: str, product_type_id: int,
- lead_type_id: int, lead_source_id: int, city_id: int, lead_step_id: int) -> LeadModel:
+ lead_type_id: int, lead_source_id: int, city_id: int, lead_step_id: int, mobile1: int = None, mobile2: int = None, phone: int = None) -> LeadModel:
try:
sys_ws = get_user_workspace(request=request)
cuser_role = get_cuser_role(request=request)
@@ -60,6 +80,12 @@ def update_lead(request, lead_id: int, title: str, customer_id: str, desc: str,
lead = LeadModel.objects.get(id=lead_id, sys_ws=sys_ws, cuser_role=cuser_role)
lead.title = title
lead.customer = customer
+ if mobile1:
+ lead.mobile1 = mobile1
+ if mobile2:
+ lead.mobile2 = mobile2
+ if phone:
+ lead.phone = phone
lead.desc = desc
lead.type = lead_type
lead.step = lead_step
diff --git a/apps/lead/views.py b/apps/lead/views.py
index 6e8e231..da8825e 100644
--- a/apps/lead/views.py
+++ b/apps/lead/views.py
@@ -4,7 +4,7 @@ from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from rest_framework import serializers
from apps.lead.serializers import LeadTypeSerializers, ProductTypeSerializers,\
- LeadSourceSerializers, LeadStepSerializers, ShahrSerializers
+ LeadSourceSerializers, LeadStepSerializers, ShahrSerializers, LeadEventSerializers
from apps.lead.selectors import fields_new_lead_form
from apps.lead.services import create_new_lead, update_lead, update_lead_status,\
update_lead_comment, delete_lead_comment, create_lead_comment
@@ -25,6 +25,7 @@ class LeadFormItemsApi(APIView):
lead_types = LeadTypeSerializers(many=True)
product_types = ProductTypeSerializers(many=True)
lead_source = LeadSourceSerializers(many=True)
+ lead_event = LeadEventSerializers(many=True)
lead_steps = LeadStepSerializers(many=True)
shahr = ShahrSerializers(many=True)
@@ -41,7 +42,11 @@ class LeadCreateApi(APIView):
class InputSerializer(serializers.Serializer):
title = serializers.CharField(max_length=100, required=True)
customer_id = serializers.IntegerField(required=True)
- desc = serializers.CharField(max_length=200, required=True)
+ desc = serializers.CharField(required=True)
+ mobile1 = serializers.IntegerField(required=True)
+ mobile2 = serializers.IntegerField(required=False)
+ lead_event_id = serializers.IntegerField(required=False)
+ phone = serializers.IntegerField(required=False)
lead_type_id = serializers.IntegerField(required=True)
product_type_id = serializers.IntegerField(required=True)
lead_source_id = serializers.IntegerField(required=True)
diff --git a/apps/reminder/migrations/0037_alter_reminder_type.py b/apps/reminder/migrations/0037_alter_reminder_type.py
new file mode 100644
index 0000000..39396fc
--- /dev/null
+++ b/apps/reminder/migrations/0037_alter_reminder_type.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.1 on 2024-08-24 13:14
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('reminder', '0036_alter_reminder_type'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='reminder',
+ name='type',
+ field=models.CharField(choices=[('LE', 'LEAD'), ('CU', 'CUSTOMER'), ('UN', 'UNKNOWN'), ('NO', 'NOTE')], default='UN', max_length=2),
+ ),
+ ]
diff --git a/apps/reminder/migrations/0038_alter_reminder_type.py b/apps/reminder/migrations/0038_alter_reminder_type.py
new file mode 100644
index 0000000..19dd8c9
--- /dev/null
+++ b/apps/reminder/migrations/0038_alter_reminder_type.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.1 on 2024-08-24 13:21
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('reminder', '0037_alter_reminder_type'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='reminder',
+ name='type',
+ field=models.CharField(choices=[('LE', 'LEAD'), ('UN', 'UNKNOWN'), ('CU', 'CUSTOMER'), ('NO', 'NOTE')], default='UN', max_length=2),
+ ),
+ ]
diff --git a/apps/reminder/migrations/0039_alter_reminder_type.py b/apps/reminder/migrations/0039_alter_reminder_type.py
new file mode 100644
index 0000000..e7e0e32
--- /dev/null
+++ b/apps/reminder/migrations/0039_alter_reminder_type.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.1 on 2024-08-27 06:38
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('reminder', '0038_alter_reminder_type'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='reminder',
+ name='type',
+ field=models.CharField(choices=[('CU', 'CUSTOMER'), ('UN', 'UNKNOWN'), ('LE', 'LEAD'), ('NO', 'NOTE')], default='UN', max_length=2),
+ ),
+ ]
diff --git a/utils/services/__init__.py b/utils/services/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/utils/services/sms.py b/utils/services/sms.py
new file mode 100644
index 0000000..dad2a58
--- /dev/null
+++ b/utils/services/sms.py
@@ -0,0 +1,32 @@
+from abc import ABC, abstractmethod
+import requests
+
+
+class SmsProviderABC(ABC):
+ @abstractmethod
+ def send_sms(self, mobile: str, message: str) -> bool:
+ pass
+
+
+class DefaultSmsProvider(SmsProviderABC):
+ def send_sms(self, mobile: str, message: str) -> bool:
+ default_provider = DosefrNavadHashtSMS()
+ return default_provider.send_sms(mobile, message)
+
+
+class DosefrNavadHashtSMS(SmsProviderABC):
+ def send_sms(self, mobile: str, message: str) -> bool:
+ payload = {
+ 'USERNAME': 'vsms7937',
+ 'PASSWORD': '335577775533',
+ 'FROM': '30005367',
+ 'TO': int(mobile),
+ 'TEXT': f'{message}'
+ }
+ r = requests.get('http://www.0098sms.com/sendsmslink.aspx', params=payload)
+
+ if r.status_code == 200:
+ return True
+ else:
+ return False
+