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 @@ - - + + + + - - { + "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 +