commit 89a5d70512b6db2b944b52ace962ddb2df45be3a Author: javad Date: Mon Jan 22 13:01:42 2024 +0330 first diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8dd262 --- /dev/null +++ b/.gitignore @@ -0,0 +1,161 @@ +media/* +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/.idea/crmApi.iml b/.idea/crmApi.iml new file mode 100644 index 0000000..bf29436 --- /dev/null +++ b/.idea/crmApi.iml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6b0bed8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..d7e5be2 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..561a452 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + { + "keyToString": { + "RunOnceActivity.OpenProjectViewOnStart": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "WebServerToolWindowFactoryState": "false", + "last_opened_file_path": "E:/Projects/crmProject/Project/api-Project/crmApi", + "node.js.detected.package.eslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" + } +} + + + + + + + + + + + + + + + + 1690021572968 + + + + + + \ No newline at end of file diff --git a/apps/__init__.py b/apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/cuser/__init__.py b/apps/cuser/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/cuser/admin.py b/apps/cuser/admin.py new file mode 100644 index 0000000..2d0b250 --- /dev/null +++ b/apps/cuser/admin.py @@ -0,0 +1,30 @@ +from django.contrib import admin +from apps.cuser.models import ( + CUser as CUserModel, + CuserRole as CuserRoleModel, + RoleType as RoleTypeModel, + SmsOpt as SmsOptModel +) +# Register your models here. + + +class CUserAdmin(admin.ModelAdmin): + pass + + +class CUserRoleAdmin(admin.ModelAdmin): + pass + + +class RoleTypeAdmin(admin.ModelAdmin): + pass + + +class SmsOptAdmin(admin.ModelAdmin): + pass + + +admin.site.register(CUserModel, CUserAdmin) +admin.site.register(CuserRoleModel, CUserRoleAdmin) +admin.site.register(RoleTypeModel, RoleTypeAdmin) +admin.site.register(SmsOptModel, SmsOptAdmin) \ No newline at end of file diff --git a/apps/cuser/apps.py b/apps/cuser/apps.py new file mode 100644 index 0000000..445a3c0 --- /dev/null +++ b/apps/cuser/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CuserConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.cuser' diff --git a/apps/cuser/migrations/0001_initial.py b/apps/cuser/migrations/0001_initial.py new file mode 100644 index 0000000..6121793 --- /dev/null +++ b/apps/cuser/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.1 on 2023-07-22 12:07 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='CUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('username', models.CharField(max_length=50, unique=True)), + ('name_family', models.CharField(blank=True, max_length=50, null=True)), + ('is_active', models.BooleanField(default=False)), + ('is_staff', models.BooleanField(default=False)), + ('is_superuser', models.BooleanField(default=False)), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/cuser/migrations/0002_roletype_cuserrole.py b/apps/cuser/migrations/0002_roletype_cuserrole.py new file mode 100644 index 0000000..87a09f1 --- /dev/null +++ b/apps/cuser/migrations/0002_roletype_cuserrole.py @@ -0,0 +1,47 @@ +# Generated by Django 4.2.1 on 2023-07-23 05:20 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='RoleType', + 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)), + ('role_name', models.CharField(max_length=20)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='CuserRole', + 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_family', models.CharField(max_length=30)), + ('title', models.CharField(blank=True, max_length=30, null=True)), + ('mobile', models.PositiveBigIntegerField()), + ('mobile_verify', models.BooleanField(default=False)), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), + ('is_active', models.BooleanField(default=False)), + ('cuser', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cuser_role', to=settings.AUTH_USER_MODEL)), + ('role_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_type', to='cuser.roletype')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/cuser/migrations/0003_roletype_role_name_en.py b/apps/cuser/migrations/0003_roletype_role_name_en.py new file mode 100644 index 0000000..a2ed46b --- /dev/null +++ b/apps/cuser/migrations/0003_roletype_role_name_en.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2023-08-08 08:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0002_roletype_cuserrole'), + ] + + operations = [ + migrations.AddField( + model_name='roletype', + name='role_name_en', + field=models.CharField(default='', max_length=20), + ), + ] diff --git a/apps/cuser/migrations/0004_alter_cuser_options.py b/apps/cuser/migrations/0004_alter_cuser_options.py new file mode 100644 index 0000000..ef749f1 --- /dev/null +++ b/apps/cuser/migrations/0004_alter_cuser_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.1 on 2023-08-10 05:19 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0003_roletype_role_name_en'), + ] + + operations = [ + migrations.AlterModelOptions( + name='cuser', + options={'permissions': [('user_is_workspace_owner', 'user is workspace owner'), ('user_is_workspace_admin', 'user is workspace admin'), ('user_is_workspace_operator', 'user is workspace operator'), ('user_is_workspace_customer', 'user is workspace customer')]}, + ), + ] diff --git a/apps/cuser/migrations/0005_smsopt.py b/apps/cuser/migrations/0005_smsopt.py new file mode 100644 index 0000000..d4feae7 --- /dev/null +++ b/apps/cuser/migrations/0005_smsopt.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.1 on 2023-08-20 08:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0004_alter_cuser_options'), + ] + + operations = [ + migrations.CreateModel( + name='SmsOpt', + 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)), + ('mobile', models.BigIntegerField()), + ('opt', models.IntegerField()), + ('expire', models.DateTimeField(blank=True, null=True)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/cuser/migrations/0006_cuser_mobile.py b/apps/cuser/migrations/0006_cuser_mobile.py new file mode 100644 index 0000000..6a14998 --- /dev/null +++ b/apps/cuser/migrations/0006_cuser_mobile.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0005_smsopt'), + ] + + operations = [ + migrations.AddField( + model_name='cuser', + name='mobile', + field=models.PositiveBigIntegerField(default=411), + ), + ] diff --git a/apps/cuser/migrations/0007_alter_cuser_mobile.py b/apps/cuser/migrations/0007_alter_cuser_mobile.py new file mode 100644 index 0000000..266d7e7 --- /dev/null +++ b/apps/cuser/migrations/0007_alter_cuser_mobile.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0006_cuser_mobile'), + ] + + operations = [ + migrations.AlterField( + model_name='cuser', + name='mobile', + field=models.PositiveBigIntegerField(unique=True), + ), + ] diff --git a/apps/cuser/migrations/__init__.py b/apps/cuser/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/cuser/models.py b/apps/cuser/models.py new file mode 100644 index 0000000..5e9f488 --- /dev/null +++ b/apps/cuser/models.py @@ -0,0 +1,82 @@ +from django.db import models +from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin +from utils.base_model import BaseModel +#from apps.workspace.models import Workspace as WorkspaceModel +import uuid + +# Create your models here. +import random + +class CUserManager(BaseUserManager): + def create_user(self, username, password, **extra_fields): + if not username: + raise ValueError("Please Enter username") + user = self.model(username=username, **extra_fields) + user.set_password(password) + user.save() + return user + + def create_superuser(self, username, password, **extra_fields): + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) + extra_fields.setdefault('is_active', True) + + return self.create_user(username, password, **extra_fields) + + +class CUser(BaseModel, AbstractBaseUser, PermissionsMixin): + username = models.CharField(max_length=50, null=False, blank=False, unique=True) + mobile = models.PositiveBigIntegerField(null=False, blank=False, unique=True) + name_family = models.CharField(max_length=50, null=True, blank=True) + is_active = models.BooleanField(default=False) + is_staff = models.BooleanField(default=False) + is_superuser = models.BooleanField(default=False) + uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) + + USERNAME_FIELD = 'username' + + objects = CUserManager() + + class Meta: + permissions = [ + ('user_is_workspace_owner', 'user is workspace owner'), + ('user_is_workspace_admin', 'user is workspace admin'), + ('user_is_workspace_operator', 'user is workspace operator'), + ('user_is_workspace_customer', 'user is workspace customer'), + ] + + def __str__(self): + return str(self.username) + + +class RoleType(BaseModel): + role_name = models.CharField(max_length=20, null=False, blank=False) + role_name_en = models.CharField(max_length=20, null=False, blank=False, default='') + + def __str__(self): + return str(self.role_name) + + +class CuserRole(BaseModel): + cuser = models.ForeignKey(CUser, on_delete=models.CASCADE, related_name='cuser_role') + role_type = models.ForeignKey(RoleType, on_delete=models.CASCADE, related_name='role_type') + name_family = models.CharField(max_length=30, null=False, blank=False) + title = models.CharField(max_length=30, null=True, blank=True) + mobile = models.PositiveBigIntegerField(null=False, blank=False) + mobile_verify = models.BooleanField(default=False) + #sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=True, blank=True, default=None) + + uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) + is_active = models.BooleanField(default=False) + + def __str__(self): + return f'{str(self.cuser) } - { str(self.id) }' + + +class SmsOpt(BaseModel): + mobile = models.BigIntegerField(null=False, blank=False) + opt = models.IntegerField(null=False, blank=False) + expire = models.DateTimeField(null=True, blank=True) + + def __str__(self): + return f'{self.mobile} {self.opt}' diff --git a/apps/cuser/tests.py b/apps/cuser/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/cuser/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/cuser/urls.py b/apps/cuser/urls.py new file mode 100644 index 0000000..73a719a --- /dev/null +++ b/apps/cuser/urls.py @@ -0,0 +1,6 @@ +from django.urls import path +from apps.cuser.views import * + +urlpatterns = [ + path('info/', CUserGetInfo.as_view(), name='cuser_info'), +] diff --git a/apps/cuser/views.py b/apps/cuser/views.py new file mode 100644 index 0000000..43cbe32 --- /dev/null +++ b/apps/cuser/views.py @@ -0,0 +1,17 @@ +from rest_framework import views +from apps.cuser.models import CuserRole as CuserRoleModel +from utils.get_cuser_role import get_cuser_role_uuid +from rest_framework.response import Response +from rest_framework import status +# Create your views here. + + +class CUserGetInfo(views.APIView): + def get(self, request): + + cuser_role_uuid = get_cuser_role_uuid(request) + cuser_obj = CuserRoleModel.objects.get(uuid=cuser_role_uuid) + + return Response(data={"name_family": cuser_obj.name_family}, status=status.HTTP_200_OK) + + diff --git a/apps/customer/__init__.py b/apps/customer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/customer/admin.py b/apps/customer/admin.py new file mode 100644 index 0000000..757acd3 --- /dev/null +++ b/apps/customer/admin.py @@ -0,0 +1,46 @@ +from django.contrib import admin +from apps.customer.models import ( + Customer as CustomerModel, + CustomerNote as CustomerNoteModel, + ShiveAshenaei as ShiveAshenaeiModel, + ResponsibleGroup as ResponsibleGroupModel, + CustomerResponsible as CustomerResponsibleModel, + CustomerGroupType as CustomerGroupTypeModel, + CustomerGroup as CustomerGroupModel, + ) +# Register your models here. + + +@admin.register(CustomerModel) +class CustomerAdmin(admin.ModelAdmin): + pass + + +@admin.register(CustomerNoteModel) +class CustomerNoteAdmin(admin.ModelAdmin): + pass + + +@admin.register(ShiveAshenaeiModel) +class ShiveAshenaeiAdmin(admin.ModelAdmin): + pass + + +@admin.register(ResponsibleGroupModel) +class ResponsibleGroupAdmin(admin.ModelAdmin): + pass + + +@admin.register(CustomerResponsibleModel) +class CustomerResponsibleAdmin(admin.ModelAdmin): + pass + + +@admin.register(CustomerGroupTypeModel) +class CustomerGroupTypeAdmin(admin.ModelAdmin): + pass + + +@admin.register(CustomerGroupModel) +class CustomerGroupAdmin(admin.ModelAdmin): + pass diff --git a/apps/customer/apps.py b/apps/customer/apps.py new file mode 100644 index 0000000..6510e1e --- /dev/null +++ b/apps/customer/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CustomerConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.customer' diff --git a/apps/customer/migrations/0001_initial.py b/apps/customer/migrations/0001_initial.py new file mode 100644 index 0000000..b7bb89f --- /dev/null +++ b/apps/customer/migrations/0001_initial.py @@ -0,0 +1,104 @@ +# Generated by Django 4.2.1 on 2023-08-08 08:37 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('workspace', '0002_city'), + ('cuser', '0002_roletype_cuserrole'), + ] + + operations = [ + migrations.CreateModel( + name='ShiveAshenaei', + 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=15)), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='ResponsibleGroup', + 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=15)), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='CustomerResponsible', + 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)), + ('cuser_role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cuser.cuserrole')), + ('responsible_group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='customer.responsiblegroup')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='CustomerGroupType', + 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=10)), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='CustomerGroup', + 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=15)), + ('group_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='customer.customergrouptype')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Customer', + 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_family', models.CharField(max_length=25)), + ('mobile', models.PositiveBigIntegerField(blank=True, null=True)), + ('phone', models.PositiveBigIntegerField(blank=True, null=True)), + ('address', models.CharField(max_length=100)), + ('city', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='workspace.city')), + ('cuser_role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cuser.cuserrole')), + ('responsible_group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='customer.responsiblegroup')), + ('shive_ashenaei', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='customer.shiveashenaei')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/customer/migrations/0002_alter_responsiblegroup_name.py b/apps/customer/migrations/0002_alter_responsiblegroup_name.py new file mode 100644 index 0000000..ce424a7 --- /dev/null +++ b/apps/customer/migrations/0002_alter_responsiblegroup_name.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2023-08-13 05:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('customer', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='responsiblegroup', + name='name', + field=models.CharField(max_length=20), + ), + ] diff --git a/apps/customer/migrations/0003_alter_customer_address.py b/apps/customer/migrations/0003_alter_customer_address.py new file mode 100644 index 0000000..178a20a --- /dev/null +++ b/apps/customer/migrations/0003_alter_customer_address.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2023-08-19 12:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('customer', '0002_alter_responsiblegroup_name'), + ] + + operations = [ + migrations.AlterField( + model_name='customer', + name='address', + field=models.CharField(blank=True, max_length=100, null=True), + ), + ] diff --git a/apps/customer/migrations/0004_alter_customerresponsible_responsible_group.py b/apps/customer/migrations/0004_alter_customerresponsible_responsible_group.py new file mode 100644 index 0000000..fcf9eea --- /dev/null +++ b/apps/customer/migrations/0004_alter_customerresponsible_responsible_group.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-09 10:05 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('customer', '0003_alter_customer_address'), + ] + + operations = [ + migrations.AlterField( + model_name='customerresponsible', + name='responsible_group', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customer_responsible', to='customer.responsiblegroup'), + ), + ] diff --git a/apps/customer/migrations/0005_alter_customer_options_customernote.py b/apps/customer/migrations/0005_alter_customer_options_customernote.py new file mode 100644 index 0000000..a575374 --- /dev/null +++ b/apps/customer/migrations/0005_alter_customer_options_customernote.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.1 on 2024-01-10 12:08 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0005_smsopt'), + ('workspace', '0003_alter_workspace_uuid'), + ('customer', '0004_alter_customerresponsible_responsible_group'), + ] + + operations = [ + migrations.AlterModelOptions( + name='customer', + options={'ordering': ['-created_at']}, + ), + migrations.CreateModel( + name='CustomerNote', + 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)), + ('desc', models.CharField(max_length=200)), + ('cuser_role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cuser.cuserrole')), + ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='customer.customer')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/customer/migrations/0006_alter_customernote_options.py b/apps/customer/migrations/0006_alter_customernote_options.py new file mode 100644 index 0000000..becf81f --- /dev/null +++ b/apps/customer/migrations/0006_alter_customernote_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.1 on 2024-01-13 10:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('customer', '0005_alter_customer_options_customernote'), + ] + + operations = [ + migrations.AlterModelOptions( + name='customernote', + options={'ordering': ['-created_at']}, + ), + ] diff --git a/apps/customer/migrations/__init__.py b/apps/customer/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/customer/models.py b/apps/customer/models.py new file mode 100644 index 0000000..e1d6d93 --- /dev/null +++ b/apps/customer/models.py @@ -0,0 +1,83 @@ +from django.db import models +from utils.base_model import BaseModel +from apps.workspace.models import City as CityModel, Workspace as WorkspaceModel +from apps.cuser.models import CuserRole as CuserRoleModel +from django.core.exceptions import ValidationError +# Create your models here. + + +class ShiveAshenaei(BaseModel): + name = models.CharField(max_length=15, null=False, blank=False) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return str(self.name) + + +class ResponsibleGroup(BaseModel): + name = models.CharField(max_length=20, null=False, blank=False) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return str(self.name) + + +class CustomerResponsible(BaseModel): + responsible_group = models.ForeignKey(ResponsibleGroup, on_delete=models.CASCADE, null=False, blank=False, related_name='customer_responsible') + cuser_role = models.ForeignKey(CuserRoleModel, on_delete=models.CASCADE, null=False, blank=False) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return str(self.responsible_group) + + +class CustomerGroupType(BaseModel): + name = models.CharField(max_length=10, null=False, blank=False) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return str(self.name) + + +class CustomerGroup(BaseModel): + name = models.CharField(max_length=15, null=False, blank=False) + group_type = models.ForeignKey(CustomerGroupType, on_delete=models.SET_NULL, null=True, blank=True) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return str(self.name) + + +class Customer(BaseModel): + name_family = models.CharField(max_length=25, null=False, blank=False) + mobile = models.PositiveBigIntegerField(null=True, blank=True) + phone = models.PositiveBigIntegerField(null=True, blank=True) + address = models.CharField(max_length=100, null=True, blank=True) + city = models.ForeignKey(CityModel, on_delete=models.SET_NULL, null=True, blank=True) + shive_ashenaei = models.ForeignKey(ShiveAshenaei, on_delete=models.SET_NULL, null=True, blank=True) + responsible_group = models.ForeignKey(ResponsibleGroup, on_delete=models.SET_NULL, null=True, blank=True) + cuser_role = models.ForeignKey(CuserRoleModel, on_delete=models.SET_NULL, null=True, blank=True) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + class Meta: + ordering = ['-created_at'] + + def __str__(self): + return str(self.name_family) + + def clean(self): + if self.cuser_role and not self.cuser_role.role_type.role_name_en == 'workspace_customer': + raise ValidationError("cuser_role type must be customer") + + +class CustomerNote(BaseModel): + customer = models.ForeignKey(Customer, on_delete=models.CASCADE, null=False, blank=False) + cuser_role = models.ForeignKey(CuserRoleModel, on_delete=models.CASCADE, null=False, blank=False) + desc = models.CharField(max_length=200, null=False, blank=False) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + class Meta: + ordering = ['-created_at'] + + def __str__(self): + return str(self.customer.name_family) diff --git a/apps/customer/selectors.py b/apps/customer/selectors.py new file mode 100644 index 0000000..7c3b2cd --- /dev/null +++ b/apps/customer/selectors.py @@ -0,0 +1,150 @@ +from apps.customer.models import ResponsibleGroup as ResponsibleGroupModel, CustomerResponsible as CustomerResponsibleModel +from apps.customer.models import ShiveAshenaei as ShiveAshenaeiModel +from apps.workspace.models import City as CityModel +from apps.customer.models import Customer as CustomerModel +from rest_framework import exceptions +from utils.get_user_workspace import get_user_workspace_uuid +from utils.get_cuser_role import get_cuser_role_uuid +from apps.lead.models import Lead as LeadModel +from apps.customer.models import CustomerNote as CustomerNoteModel +from apps.reminder.models import Reminder as ReminderModel + + + +def retrieve_responsible_group(responsible_group_id: int, workspace_uuid: str): + try: + responsible_group_obj = ResponsibleGroupModel.objects.get(id=responsible_group_id, sys_ws__uuid=workspace_uuid) + return responsible_group_obj + except Exception as exp: + message = f'Error Retrieve ResponsibleGroup: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def list_responsible_group(workspace_uuid: str) -> ResponsibleGroupModel: + try: + responsible_group_objs = ResponsibleGroupModel.objects.filter(sys_ws__uuid=workspace_uuid) + return responsible_group_objs + except Exception as exp: + message = f'Error Fetch ResponsibleGroup List: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def retrieve_shive_ashenaei(shive_id: int, workspace_uuid: str): + try: + responsible_group_obj = ShiveAshenaeiModel.objects.get(id=shive_id, sys_ws__uuid=workspace_uuid) + return responsible_group_obj + except Exception as exp: + message = f'Error Retrieve ShiveAshenaei: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def list_shive_ashenaei(workspace_uuid: str) -> ResponsibleGroupModel: + try: + shive_ashenaei_objs = ShiveAshenaeiModel.objects.filter(sys_ws__uuid=workspace_uuid) + return shive_ashenaei_objs + except Exception as exp: + message = f'Error Fetch ShiveAshenaei List: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def retrieve_customer(customer_id: int, workspace_uuid: str): + try: + customer_obj = CustomerModel.objects.get(id=customer_id, sys_ws__uuid=workspace_uuid) + return customer_obj + except Exception as exp: + message = f'Error Retrieve Customer: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def list_customer(workspace_uuid: str, name_family: str = None, mobile: int = None): + try: + customer_objs = CustomerModel.objects.filter(sys_ws__uuid=workspace_uuid) + + if name_family is not None: + customer_objs = customer_objs.filter(name_family__contains=name_family) + if mobile is not None: + customer_objs = customer_objs.filter(mobile__=mobile) + + return customer_objs[:3] + except Exception as exp: + message = f'Error Fetch Customer List: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def list_customer_v2(request): + try: + + user_workspace_uuid = get_user_workspace_uuid(request=request) + user_cuser_role_uuid = get_cuser_role_uuid(request=request) + + customer_responsible = CustomerResponsibleModel.objects.filter(cuser_role__uuid=user_cuser_role_uuid) + + customer_objs = CustomerModel.objects.filter(sys_ws__uuid=user_workspace_uuid, + responsible_group__customer_responsible__in=customer_responsible) + + return customer_objs + except Exception as exp: + message = f'Error Fetch Customer List: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def customer_form_item(request): + + workspace_uuid = get_user_workspace_uuid(request) + + shive_ashenaei = ShiveAshenaeiModel.objects.filter(sys_ws__uuid=workspace_uuid) + shahr = CityModel.objects.filter() + responsible_groups = ResponsibleGroupModel.objects.filter(sys_ws__uuid=workspace_uuid) + + data = { + 'shive_ashenaei': shive_ashenaei, + 'shahr': shahr, + 'responsible_groups': responsible_groups + } + + return data + + +def customer_summery(request, customer_id:int): + + workspace_uuid = get_user_workspace_uuid(request) + cuser_role_uuid = get_cuser_role_uuid(request) + + customer_notes = CustomerNoteModel.objects.filter(customer_id=customer_id, sys_ws__uuid=workspace_uuid)[:4] + customer_leads = LeadModel.objects.filter(customer_id=customer_id, sys_ws__uuid=workspace_uuid)[:4] + + data = { + 'customer_notes': customer_notes, + 'customer_leads': customer_leads, + } + + return data + + +def customer_note_list(request, customer_id: int) -> CustomerNoteModel: + + workspace_uuid = get_user_workspace_uuid(request) + + customer_notes = CustomerNoteModel.objects.filter(customer_id=customer_id, sys_ws__uuid=workspace_uuid)[:20] + + return customer_notes + + +def customer_note_retrieve(request, customer_id: int, note_id: int) -> CustomerNoteModel: + + workspace_uuid = get_user_workspace_uuid(request) + + customer_note = CustomerNoteModel.objects.get(id=note_id, customer_id=customer_id, sys_ws__uuid=workspace_uuid) + customer_note_reminder = None + try: + customer_note_reminder = ReminderModel.objects.get(type='NO', id_type=note_id, customer_id=customer_id, sys_ws__uuid=workspace_uuid) + except Exception as exp: + pass + + output = { + 'customer_note': customer_note, + 'customer_note_reminder': customer_note_reminder + } + return output + + diff --git a/apps/customer/services.py b/apps/customer/services.py new file mode 100644 index 0000000..6f43e0b --- /dev/null +++ b/apps/customer/services.py @@ -0,0 +1,235 @@ +from apps.customer.models import ( + ResponsibleGroup as ResponsibleGroupModel, + ShiveAshenaei as ShiveAshenaeiModel, + Customer as CustomerModel, + CustomerNote as CustomerNoteModel +) +from apps.reminder.models import Reminder as ReminderModel +from apps.workspace.models import Workspace as WorkspaceModel +from rest_framework import exceptions +from utils.get_user_workspace import get_user_workspace, get_user_workspace_uuid +from utils.get_cuser_role import get_cuser_role +from django.db import transaction + + +def create_responsible_group(name: str, workspace: WorkspaceModel): + try: + responsible_group_obj = ResponsibleGroupModel(name=name, sys_ws=workspace) + responsible_group_obj.full_clean() + responsible_group_obj.save() + + return responsible_group_obj + + except Exception as exp: + message = f'Error creating ResponsibleGroup: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def delete_responsible_group(responsible_group_id: int, workspace_uuid: str): + try: + ResponsibleGroupModel.objects.get(id=responsible_group_id, sys_ws__uuid=workspace_uuid).delete() + except Exception as exp: + message = f'Error Deleting ResponsibleGroup: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def create_shive_ashenaei(name: str, workspace: WorkspaceModel) -> ShiveAshenaeiModel: + try: + shive_obj = ShiveAshenaeiModel(name=name, sys_ws=workspace) + shive_obj.full_clean() + shive_obj.save() + return shive_obj + except Exception as exp: + message = f'Error creating ShiveAshenaei: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def delete_shive_ashenaei(shive_id: int, workspace_uuid: str) -> ShiveAshenaeiModel: + try: + ShiveAshenaeiModel.objects.get(id=shive_id, sys_ws__uuid=workspace_uuid).delete() + except Exception as exp: + message = f'Error Delete ShiveAshenaei: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def create_customer(name_family: str , mobile: int, phone: int, address: str, city: int, shive_ashenaei: int, + responsible_group: int, cuser_role: int = None, workspace: WorkspaceModel = None) -> CustomerModel: + try: + customer_obj = CustomerModel() + customer_obj.name_family = name_family + customer_obj.mobile = mobile + customer_obj.phone = phone + customer_obj.address = address + customer_obj.city = city + customer_obj.shive_ashenaei = shive_ashenaei + customer_obj.responsible_group = responsible_group + if cuser_role is not None: + customer_obj.cuser_role = cuser_role + customer_obj.sys_ws = workspace + + customer_obj.full_clean() + customer_obj.save() + return customer_obj + except Exception as exp: + message = f'Error Creating Customer: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def update_customer(customer_id: int, name_family: str = None , mobile: int = None, phone: int = None, address: str = None, + city: int = None, shive_ashenaei: int = None, responsible_group: int = None, cuser_role: int = None, workspace_uuid: str = None) -> CustomerModel: + try: + customer_obj = CustomerModel.objects.get(id=customer_id, sys_ws__uuid=workspace_uuid) + + if name_family is not None: + customer_obj.name_family = name_family + if mobile is not None: + customer_obj.mobile = mobile + if phone is not None: + customer_obj.phone = phone + if address is not None: + customer_obj.address = address + if city is not None: + customer_obj.city = city + if shive_ashenaei is not None: + customer_obj.shive_ashenaei = shive_ashenaei + if responsible_group is not None: + customer_obj.responsible_group = responsible_group + if cuser_role is not None: + customer_obj.cuser_role = cuser_role + + customer_obj.full_clean() + customer_obj.save() + return customer_obj + except Exception as exp: + message = f'Error Creating Customer: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def delete_customer(customer_id: int, workspace_uuid: str = None) -> CustomerModel: + try: + CustomerModel.objects.get(id=customer_id, sys_ws__uuid=workspace_uuid).delete() + except Exception as exp: + message = f'Error Deleting Customer: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def create_customer_v2(request, name_family: str , mobile: int, phone: int, address: str, city: int, shive_ashenaei: int, + responsible_group: int, cuser_role: int = None) -> CustomerModel: + try: + workspace = get_user_workspace(request=request) + + customer_obj = CustomerModel() + customer_obj.name_family = name_family + customer_obj.mobile = mobile + customer_obj.phone = phone + customer_obj.address = address + customer_obj.city_id = city + customer_obj.shive_ashenaei = ShiveAshenaeiModel.objects.get(id=shive_ashenaei, sys_ws=workspace) + + customer_obj.responsible_group = ResponsibleGroupModel.objects.get(id=responsible_group, sys_ws=workspace) + if cuser_role is not None: + customer_obj.cuser_role = cuser_role + customer_obj.sys_ws = workspace + + customer_obj.full_clean() + customer_obj.save() + return customer_obj + except Exception as exp: + message = f'Error Creating Customer: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def create_customer_note(request, customer_id: int, desc: str, + reminder_datetime: str = None, reminder_desc: str = None) -> CustomerNoteModel: + with transaction.atomic(): + try: + workspace = get_user_workspace(request=request) + cuser_role = get_cuser_role(request=request) + + customer_note = CustomerNoteModel() + customer_note.customer = CustomerModel.objects.get(id=customer_id, sys_ws=workspace) + customer_note.desc = desc + customer_note.sys_ws = workspace + customer_note.cuser_role = cuser_role + customer_note.save() + + if reminder_datetime: + try: + reminder = ReminderModel() + reminder.type = 'NO' + reminder.note = customer_note + reminder.id_type = customer_note.id + reminder.customer = customer_note.customer + reminder.datetime = reminder_datetime + if reminder_desc: + reminder.desc = reminder_desc + reminder.cuser_role = cuser_role + reminder.sys_ws = workspace + reminder.save() + + except Exception as exp: + transaction.set_rollback(True) + message = f'Error Creating Customer Note and Set Reminder: {exp}' + raise exceptions.ValidationError({'error': message}) + + return customer_note + + except Exception as exp: + transaction.set_rollback(True) + message = f'Error Creating Customer Note: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def customer_note_edit(request, customer_id: int, note_id: int, desc: str, + reminder_datetime: str = None, reminder_desc: str = None) -> CustomerNoteModel: + try: + workspace = get_user_workspace(request) + cuser_role = get_cuser_role(request) + + customer_note = CustomerNoteModel.objects.get(id=note_id, customer_id=customer_id, sys_ws=workspace) + customer_note.desc = desc + customer_note.save() + + if reminder_datetime: + try: + try: + reminder = ReminderModel.objects.get(type='NO', note=customer_note, id_type=note_id, customer_id=customer_id) + reminder.datetime = reminder_datetime + reminder.desc = reminder_desc + reminder.save() + except Exception as exp: + try: + reminder = ReminderModel() + reminder.type = 'NO' + reminder.note = customer_note + reminder.id_type = customer_note.id + reminder.customer = customer_note.customer + reminder.datetime = reminder_datetime + if reminder_desc: + reminder.desc = reminder_desc + reminder.cuser_role = cuser_role + reminder.sys_ws = workspace + reminder.save() + except Exception as exp: + print(exp) + except Exception as exp: + print(exp) + + return customer_note + + except Exception as exp: + message = f'Error Edit Customer Note: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def customer_note_delete(request, customer_id: int, note_id: int) -> CustomerNoteModel: + try: + workspace = get_user_workspace_uuid(request=request) + + customer_note = CustomerNoteModel.objects.get(id=note_id, customer_id=customer_id, sys_ws__uuid=workspace).delete() + + return customer_note + + except Exception as exp: + message = f'Error Delete Customer Note: {exp}' + raise exceptions.ValidationError({'error': message}) \ No newline at end of file diff --git a/apps/customer/tests.py b/apps/customer/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/customer/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/customer/urls.py b/apps/customer/urls.py new file mode 100644 index 0000000..01df296 --- /dev/null +++ b/apps/customer/urls.py @@ -0,0 +1,39 @@ +from django.urls import path +from apps.customer.views import * + +urlpatterns = [ + path('responsible-group/list/', ResponsibleGroupListApi.as_view(), name='list_responsible_group'), + path('responsible-group//', ResponsibleGroupRetrieveApi.as_view(), + name='retrieve_responsible_group'), + path('responsible-group/create/', ResponsibleGroupCreateApi.as_view(), name='create_responsible_group'), + path('responsible-group/delete//', ResponsibleGroupDeleteApi.as_view(), name='delete_responsible_group'), + + path('shive_ashenaei/create/', ShiveAshenaeiCreateApi.as_view(), name='create_shive_ashenaei'), + path('shive_ashenaei/delete//', ShiveAshenaeiDeleteApi.as_view(), name='delete_shive_ashenaei'), + path('shive_ashenaei/list/', ShiveAshenaeiListApi.as_view(), name='list_shive_ashenaei'), + path('shive_ashenaei//', ShiveAshenaeiRetrieveApi.as_view(), name='retrive_shive_ashenaei'), + + path('create/', CustomerCreateApi.as_view(), name='create_customer'), + path('update//', CustomerUpdateApi.as_view(), name='update_customer'), + path('delete//', CustomerDeleteApi.as_view(), name='delete_customer'), + path('list/', CustomerListApi.as_view(), name='list_customer'), + path('/', CustomerRetrieveApi.as_view(), name='retrive_customer'), + + path('/summary/', CustomerSummary.as_view(), name='customer_summary'), + + path('/note/create/', CustomerCreateNoteApi.as_view(), name='customer_note_create'), + + path('/note/list/', CustomerListNoteApi.as_view(), name='customer_note_list'), + + path('/note//', CustomerRetrieveNoteApi.as_view(), name='customer_note_retrieve'), + + path('/note//delete/', CustomerDeleteNoteApi.as_view(), name='customer_note_delete'), + + path('/note//edit/', CustomerEditNoteApi.as_view(), name='customer_note_edit'), + + path('v2/list/', CustomerListApiV2.as_view(), name='list_customer_v2'), + + path('v2/create/', CustomerCreateApiV2.as_view(), name='customer_create_v2'), + + path('form-item/', CustomerFormItemApi.as_view(), name='customer-form-item'), +] diff --git a/apps/customer/views/__init__.py b/apps/customer/views/__init__.py new file mode 100644 index 0000000..f0aa0eb --- /dev/null +++ b/apps/customer/views/__init__.py @@ -0,0 +1,9 @@ +from .responsible_group import ResponsibleGroupCreateApi, ResponsibleGroupDeleteApi,\ + ResponsibleGroupRetrieveApi, ResponsibleGroupListApi + +from .shive_ashenaei import ShiveAshenaeiCreateApi, ShiveAshenaeiDeleteApi, ShiveAshenaeiListApi, ShiveAshenaeiRetrieveApi + +from .customer import * +from .form_item import * +from .customer_summary import * +from .customer_note import * diff --git a/apps/customer/views/customer.py b/apps/customer/views/customer.py new file mode 100644 index 0000000..f9a20c4 --- /dev/null +++ b/apps/customer/views/customer.py @@ -0,0 +1,226 @@ +from rest_framework.views import APIView +from rest_framework import serializers +from apps.customer.models import Customer as CustomerModel +from apps.customer.selectors import retrieve_customer, list_customer, list_customer_v2 +from utils.get_user_workspace import get_user_workspace_uuid, get_user_workspace +from rest_framework.response import Response +from rest_framework import status +from rest_framework.permissions import IsAuthenticated +from utils import custom_permissions +from rest_framework import generics +from apps.customer.services import create_customer, update_customer, delete_customer, create_customer_v2 +from apps.workspace.models import City as CityModel +from utils.tools import inline_model_serializer, inline_serializer +from utils.pagination import CustomPagination +from django_filters import rest_framework as filters + + + + +class CustomerRetrieveApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = CustomerModel + fields = '__all__' + + def get(self, request, customer_id: int): + user_workspace_uuid = get_user_workspace_uuid(request=request) + + customer_obj = retrieve_customer(customer_id=customer_id, workspace_uuid=user_workspace_uuid) + + output = self.OutputSerializer(customer_obj) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class CustomerListApi(generics.ListAPIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class OutputSerializer(serializers.ModelSerializer): + + class CitySerializer(serializers.ModelSerializer): + + class OstanSerializer(serializers.ModelSerializer): + class Meta: + model = CityModel + fields = '__all__' + + ostan = OstanSerializer() + class Meta: + model = CityModel + fields = '__all__' + + city = CitySerializer() + + class Meta: + model = CustomerModel + fields = '__all__' + + serializer_class = OutputSerializer + + def get_queryset(self): + + user_workspace_uuid = get_user_workspace_uuid(request=self.request) + + name_family = self.request.query_params.get('name_family') + mobile = self.request.query_params.get('mobile') + + customer_objs = list_customer(workspace_uuid=user_workspace_uuid, name_family=name_family, mobile=mobile) + + return customer_objs + + +class CustomerCreateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class InputSerializer(serializers.ModelSerializer): + class Meta: + model = CustomerModel + fields = '__all__' + read_only_fields = ['sys_ws', ''] + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = CustomerModel + exclude = ('sys_ws', ) + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + user_workspace = get_user_workspace(request=request) + + customer_obj = create_customer(**serializer.validated_data, workspace=user_workspace) + output = self.OutputSerializer(customer_obj) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class CustomerUpdateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class InputSerializer(serializers.ModelSerializer): + class Meta: + model = CustomerModel + fields = '__all__' + read_only_fields = ['sys_ws', ''] + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = CustomerModel + exclude = ('sys_ws', ) + + def patch(self, request, customer_id: int): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + user_workspace_uuid = get_user_workspace_uuid(request=request) + + customer_obj = update_customer(**serializer.validated_data, customer_id=customer_id, workspace_uuid=user_workspace_uuid) + output = self.OutputSerializer(customer_obj) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class CustomerDeleteApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = CustomerModel + exclude = ('sys_ws', ) + + def delete(self, request, customer_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request=request) + + delete_customer(customer_id=customer_id, workspace_uuid=user_workspace_uuid) + + return Response(status=status.HTTP_200_OK) + + +class CustomerListApiV2(generics.ListAPIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class Pagination(CustomPagination): + page_size = 10 + page_size_query_param = 'page_size' + max_page_size = 20 + + class OutputSerializer(serializers.ModelSerializer): + + city = inline_serializer(fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField(), + 'ostan': inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField() + }) + }) + + shive_ashenaei = inline_serializer(fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField(), + }) + + responsible_group = inline_serializer(fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField(), + }) + + class Meta: + model = CustomerModel + fields = '__all__' + + class CustomerFilter(filters.FilterSet): + class Meta: + model = CustomerModel + fields = { + 'name_family': ['exact', 'contains'], + 'mobile': ['exact', 'contains'], + } + + serializer_class = OutputSerializer + + pagination_class = Pagination + + filter_backends = [filters.DjangoFilterBackend, ] + filterset_class = CustomerFilter + + def get_queryset(self): + customer_objs = list_customer_v2(request=self.request) + + return customer_objs + + +class CustomerCreateApiV2(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class InputSerializer(serializers.Serializer): + name_family = serializers.CharField(max_length=25, required=True) + mobile = serializers.IntegerField(required=True) + phone = serializers.IntegerField(required=True) + address = serializers.CharField(max_length=300, required=True) + city = serializers.IntegerField(required=True) + shive_ashenaei = serializers.IntegerField(required=True) + responsible_group = serializers.IntegerField(required=True) + + def post(self, request): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + customer = create_customer_v2(request=request, **serializer.validated_data) + + return Response(data={'id': customer.id}, status=status.HTTP_201_CREATED) + + diff --git a/apps/customer/views/customer_note.py b/apps/customer/views/customer_note.py new file mode 100644 index 0000000..f70c26c --- /dev/null +++ b/apps/customer/views/customer_note.py @@ -0,0 +1,94 @@ +from rest_framework.views import APIView +from rest_framework import serializers +from rest_framework.response import Response +from rest_framework import status +from apps.customer.services import create_customer_note, customer_note_edit, customer_note_delete +from apps.customer.selectors import customer_note_retrieve, customer_note_list +from apps.customer.models import CustomerNote as CustomerNoteModel +from apps.reminder.models import Reminder as ReminderModel +from utils.tools import inline_serializer + + + +class CustomerCreateNoteApi(APIView): + class InputSerializer(serializers.Serializer): + desc = serializers.CharField() + reminder_datetime = serializers.DateTimeField(required=False, allow_null=True) + reminder_desc = serializers.CharField(required=False, allow_null=True, max_length=200) + + def post(self, request, customer_id): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + output = create_customer_note(request=request, customer_id=customer_id, **serializer.validated_data) + + return Response(data={'id': output.id}, status=status.HTTP_201_CREATED) + + +class CustomerListNoteApi(APIView): + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = CustomerNoteModel + exclude = ['cuser_role', 'sys_ws'] + + def get(self, request, customer_id): + notes = customer_note_list(request=request, customer_id=customer_id) + output = self.OutputSerializer(notes, many=True) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class CustomerRetrieveNoteApi(APIView): + + class OutputSerializer(serializers.Serializer): + + class CustomerNoteSerializer(serializers.ModelSerializer): + class Meta: + model = CustomerNoteModel + fields = '__all__' + + class CustomerNoteReminderSerializer(serializers.ModelSerializer): + class Meta: + model = ReminderModel + fields = '__all__' + + customer_note = CustomerNoteSerializer(many=False, required=True) + customer_note_reminder = CustomerNoteReminderSerializer(many=False, required=False) + + def get(self, request, customer_id, note_id): + note = customer_note_retrieve(request=request, customer_id=customer_id, note_id=note_id) + output = self.OutputSerializer(note) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class CustomerEditNoteApi(APIView): + + class InputSerializer(serializers.Serializer): + desc = serializers.CharField(required=True) + reminder_datetime = serializers.DateTimeField(required=False, allow_null=True) + reminder_desc = serializers.CharField(required=False, allow_null=True, max_length=200) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = CustomerNoteModel + fields = '__all__' + + def patch(self, request, customer_id, note_id): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + note = customer_note_edit(request=request, customer_id=customer_id, note_id=note_id, **serializer.validated_data) + + return Response(data={'id': note.id}, status=status.HTTP_201_CREATED) + + +class CustomerDeleteNoteApi(APIView): + + def delete(self, request, customer_id, note_id): + note = customer_note_delete(request=request, customer_id=customer_id, note_id=note_id) + + return Response(data={'result': 'deleted'}, status=status.HTTP_200_OK) + diff --git a/apps/customer/views/customer_summary.py b/apps/customer/views/customer_summary.py new file mode 100644 index 0000000..2aab2fe --- /dev/null +++ b/apps/customer/views/customer_summary.py @@ -0,0 +1,30 @@ +from rest_framework import serializers +from rest_framework.response import Response +from rest_framework.views import APIView +from apps.customer.models import Customer as CustomerModel +from rest_framework import status +from apps.customer.selectors import customer_summery +from utils.tools import inline_serializer + + +class CustomerSummary(APIView): + + class OutputSerializer(serializers.Serializer): + customer_notes = inline_serializer(many=True, fields={ + 'id': serializers.IntegerField(required=True), + 'created_at': serializers.DateTimeField(required=True), + 'desc': serializers.CharField(required=True) + }) + customer_leads = inline_serializer(many=True, fields={ + 'id': serializers.IntegerField(required=True), + 'title': serializers.CharField(required=True), + 'created_at': serializers.DateTimeField(required=True), + }) + + def get(self, request, customer_id): + + summery = customer_summery(request=request, customer_id=customer_id) + output = self.OutputSerializer(summery) + + return Response(data=output.data, status=status.HTTP_200_OK) + diff --git a/apps/customer/views/form_item.py b/apps/customer/views/form_item.py new file mode 100644 index 0000000..95fa43a --- /dev/null +++ b/apps/customer/views/form_item.py @@ -0,0 +1,42 @@ +from rest_framework.views import APIView +from apps.customer.models import ShiveAshenaei as ShiveAshenaeiModel +from apps.workspace.models import City as CityModel +from rest_framework import serializers +from utils.tools import inline_serializer +from rest_framework import status +from apps.customer.selectors import customer_form_item +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated +from utils import custom_permissions + + +class CustomerFormItemApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class OutputSerializer(serializers.Serializer): + + shive_ashenaei = inline_serializer(many=True, fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField() + }) + + shahr = inline_serializer(many=True, fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField(), + 'ostan': inline_serializer(fields={'id': serializers.IntegerField(), }), + 'is_ostan': serializers.BooleanField(), + }) + + responsible_groups = inline_serializer(many=True, fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField() + }) + + def get(self, request): + + fields_value = customer_form_item(request) + serializer = self.OutputSerializer(fields_value) + + return Response(data=serializer.data, status=status.HTTP_200_OK) + diff --git a/apps/customer/views/responsible_group.py b/apps/customer/views/responsible_group.py new file mode 100644 index 0000000..cd8edc9 --- /dev/null +++ b/apps/customer/views/responsible_group.py @@ -0,0 +1,81 @@ +from rest_framework.views import APIView +from rest_framework import generics +from rest_framework import serializers +from rest_framework.response import Response +from apps.customer.services import create_responsible_group, delete_responsible_group +from apps.customer.selectors import retrieve_responsible_group, list_responsible_group +from utils.get_user_workspace import get_user_workspace_uuid, get_user_workspace +from rest_framework import status +from utils import custom_permissions +from apps.customer.models import ResponsibleGroup +from rest_framework.permissions import IsAuthenticated + + +class ResponsibleGroupRetrieveApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission, ) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ResponsibleGroup + fields = '__all__' + + def get(self, request, responsible_group_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request) + responsible_group_obj = retrieve_responsible_group(responsible_group_id=responsible_group_id, workspace_uuid=user_workspace_uuid) + + output = self.OutputSerializer(responsible_group_obj) + return Response(data=output.data, status=status.HTTP_200_OK) + + +class ResponsibleGroupListApi(generics.ListAPIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission, ) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ResponsibleGroup + fields = '__all__' + + serializer_class = OutputSerializer + + def get_queryset(self): + user_workspace_uuid = get_user_workspace_uuid(self.request) + return list_responsible_group(workspace_uuid=user_workspace_uuid) + + +class ResponsibleGroupCreateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission,) + + class InputSerializer(serializers.Serializer): + name = serializers.CharField(max_length=20) + + class OutputSerializer(serializers.Serializer): + name = serializers.CharField(max_length=20) + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + group_name = serializer.validated_data.get + user_workspace = get_user_workspace(request=request) + + responsible_group_obj = create_responsible_group(name=group_name, workspace=user_workspace) + output = self.OutputSerializer(responsible_group_obj) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class ResponsibleGroupDeleteApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission,) + + def delete(self, request, responsible_group_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request) + delete_responsible_group(responsible_group_id=responsible_group_id, workspace_uuid=user_workspace_uuid) + + return Response(status=status.HTTP_200_OK) + diff --git a/apps/customer/views/shive_ashenaei.py b/apps/customer/views/shive_ashenaei.py new file mode 100644 index 0000000..997135c --- /dev/null +++ b/apps/customer/views/shive_ashenaei.py @@ -0,0 +1,73 @@ +from apps.customer.models import ShiveAshenaei as ShiveAshenaeiModel +from rest_framework.views import APIView +from rest_framework import generics +from utils.get_user_workspace import get_user_workspace_uuid, get_user_workspace +from utils import custom_permissions +from rest_framework.permissions import IsAuthenticated +from rest_framework import serializers +from apps.customer.services import create_shive_ashenaei, delete_shive_ashenaei +from apps.customer.selectors import list_shive_ashenaei, retrieve_shive_ashenaei +from rest_framework.response import Response +from rest_framework import status + + +class ShiveAshenaeiCreateApi(APIView): + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission) + + class InputSerializer(serializers.Serializer): + name = serializers.CharField(max_length=20) + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + user_workspace = get_user_workspace(request=request) + name = serializer.validated_data.get + + create_shive_ashenaei(name=name, workspace=user_workspace) + + return Response(status=status.HTTP_201_CREATED) + + +class ShiveAshenaeiDeleteApi(APIView): + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission) + + def delete(self, request, shive_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request=request) + delete_shive_ashenaei(shive_id=shive_id, workspace_uuid=user_workspace_uuid) + + return Response(status=status.HTTP_200_OK) + + +class ShiveAshenaeiRetrieveApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission, ) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ShiveAshenaeiModel + fields = '__all__' + + def get(self, request, shive_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request) + responsible_group_obj = retrieve_shive_ashenaei(shive_id=shive_id, workspace_uuid=user_workspace_uuid) + + output = self.OutputSerializer(responsible_group_obj) + return Response(data=output.data, status=status.HTTP_200_OK) + + +class ShiveAshenaeiListApi(generics.ListAPIView): + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ShiveAshenaeiModel + fields = '__all__' + + serializer_class = OutputSerializer + + def get_queryset(self): + user_workspace_uuid = get_user_workspace_uuid(self.request) + return list_shive_ashenaei(workspace_uuid=user_workspace_uuid) \ No newline at end of file diff --git a/apps/customform/__init__.py b/apps/customform/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/customform/admin.py b/apps/customform/admin.py new file mode 100644 index 0000000..77cc13d --- /dev/null +++ b/apps/customform/admin.py @@ -0,0 +1,22 @@ +from django.contrib import admin +from apps.customform.models import ( + CustomForm as CustomFormModel, + CustomFormData as CustomFormDataModel, + CustomFormField as CustomFormFieldModel, +) +# Register your models here. + + +@admin.register(CustomFormModel) +class CustomFormAdmin(admin.ModelAdmin): + pass + + +@admin.register(CustomFormDataModel) +class CustomFormDataAdmin(admin.ModelAdmin): + pass + + +@admin.register(CustomFormFieldModel) +class CustomFormFieldAdmin(admin.ModelAdmin): + pass diff --git a/apps/customform/apps.py b/apps/customform/apps.py new file mode 100644 index 0000000..7db455d --- /dev/null +++ b/apps/customform/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CustomformConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.customform' diff --git a/apps/customform/migrations/0001_initial.py b/apps/customform/migrations/0001_initial.py new file mode 100644 index 0000000..1c12fa2 --- /dev/null +++ b/apps/customform/migrations/0001_initial.py @@ -0,0 +1,60 @@ +# Generated by Django 4.2.1 on 2023-07-23 06:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('cuser', '0002_roletype_cuserrole'), + ('workspace', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='CustomForm', + 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=20)), + ('desc', models.CharField(blank=True, max_length=30, null=True)), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='CustomFormField', + 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)), + ('field_name_fa', models.CharField(max_length=20)), + ('field_name_en', models.CharField(max_length=20)), + ('validator', models.CharField(blank=True, max_length=50, null=True)), + ('form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='field', to='customform.customform')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='CustomFormData', + 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)), + ('data', models.JSONField()), + ('form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='customform.customform')), + ('form_filler', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cuser.cuserrole')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/customform/migrations/0002_alter_customform_desc_alter_customform_name.py b/apps/customform/migrations/0002_alter_customform_desc_alter_customform_name.py new file mode 100644 index 0000000..dcd5e50 --- /dev/null +++ b/apps/customform/migrations/0002_alter_customform_desc_alter_customform_name.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.1 on 2023-07-23 06:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('customform', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='customform', + name='desc', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='customform', + name='name', + field=models.CharField(max_length=40), + ), + ] diff --git a/apps/customform/migrations/__init__.py b/apps/customform/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/customform/models.py b/apps/customform/models.py new file mode 100644 index 0000000..0c98c62 --- /dev/null +++ b/apps/customform/models.py @@ -0,0 +1,34 @@ +from django.db import models +from utils.base_model import BaseModel +import uuid +from apps.workspace.models import Workspace as WorkspaceModel +from apps.cuser.models import CuserRole as CuserRoleModel +# Create your models here. + + +class CustomForm(BaseModel): + name = models.CharField(max_length=40, null=False, blank=False) + desc = models.CharField(max_length=50, null=True, blank=True) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE) + + def __str__(self): + return f'{self.name}' + + +class CustomFormField(BaseModel): + form = models.ForeignKey(CustomForm, on_delete=models.CASCADE, related_name='field') + field_name_fa = models.CharField(max_length=20, null=False, blank=False) + field_name_en = models.CharField(max_length=20, null=False, blank=False) + validator = models.CharField(max_length=50, null=True, blank=True) + + def __str__(self): + return f'{self.form} -> {self.field_name_en}' + + +class CustomFormData(BaseModel): + form = models.ForeignKey(CustomForm, on_delete=models.CASCADE) + form_filler = models.ForeignKey(CuserRoleModel, on_delete=models.CASCADE) + data = models.JSONField(null=False, blank=False) + + def __str__(self): + return f'{self.form} -> {self.field_name_en}' diff --git a/apps/customform/tests.py b/apps/customform/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/customform/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/customform/views.py b/apps/customform/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/apps/customform/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/apps/jwt_auth/__init__.py b/apps/jwt_auth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/jwt_auth/admin.py b/apps/jwt_auth/admin.py new file mode 100644 index 0000000..f79b492 --- /dev/null +++ b/apps/jwt_auth/admin.py @@ -0,0 +1,17 @@ +from django.contrib import admin +from apps.jwt_auth.models import ( + JwtToken as JwtTokenModel, + CUserRoleLoginOpt as CUserRoleLoginOptModel, +) + +# Register your models here. + + +@admin.register(JwtTokenModel) +class JwtTokenAdmin(admin.ModelAdmin): + pass + + +@admin.register(CUserRoleLoginOptModel) +class CUserRoleLoginOptAdmin(admin.ModelAdmin): + pass diff --git a/apps/jwt_auth/apps.py b/apps/jwt_auth/apps.py new file mode 100644 index 0000000..b2f0951 --- /dev/null +++ b/apps/jwt_auth/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AuthConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.jwt_auth' diff --git a/apps/jwt_auth/migrations/0001_initial.py b/apps/jwt_auth/migrations/0001_initial.py new file mode 100644 index 0000000..ee35855 --- /dev/null +++ b/apps/jwt_auth/migrations/0001_initial.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.1 on 2023-07-23 11:23 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='JwtToken', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('refresh', models.CharField(max_length=350)), + ('access', models.CharField(blank=True, max_length=350, null=True)), + ('expire', models.DateTimeField(blank=True, null=True)), + ('is_blocked', models.BooleanField(default=False)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/apps/jwt_auth/migrations/0002_rename_user_jwttoken_cuser_jwttoken_cuser_role.py b/apps/jwt_auth/migrations/0002_rename_user_jwttoken_cuser_jwttoken_cuser_role.py new file mode 100644 index 0000000..0a3747c --- /dev/null +++ b/apps/jwt_auth/migrations/0002_rename_user_jwttoken_cuser_jwttoken_cuser_role.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.1 on 2023-07-23 11:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0002_roletype_cuserrole'), + ('jwt_auth', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='jwttoken', + old_name='user', + new_name='cuser', + ), + migrations.AddField( + model_name='jwttoken', + name='cuser_role', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='cuser.cuserrole'), + ), + ] diff --git a/apps/jwt_auth/migrations/0003_alter_jwttoken_cuser_role.py b/apps/jwt_auth/migrations/0003_alter_jwttoken_cuser_role.py new file mode 100644 index 0000000..dbe58f7 --- /dev/null +++ b/apps/jwt_auth/migrations/0003_alter_jwttoken_cuser_role.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.1 on 2023-07-23 11:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0002_roletype_cuserrole'), + ('jwt_auth', '0002_rename_user_jwttoken_cuser_jwttoken_cuser_role'), + ] + + operations = [ + migrations.AlterField( + model_name='jwttoken', + name='cuser_role', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cuser.cuserrole'), + ), + ] diff --git a/apps/jwt_auth/migrations/0004_cuserroleloginopt.py b/apps/jwt_auth/migrations/0004_cuserroleloginopt.py new file mode 100644 index 0000000..104d182 --- /dev/null +++ b/apps/jwt_auth/migrations/0004_cuserroleloginopt.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.1 on 2023-07-24 07:53 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0002_roletype_cuserrole'), + ('jwt_auth', '0003_alter_jwttoken_cuser_role'), + ] + + operations = [ + migrations.CreateModel( + name='CUserRoleLoginOpt', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('expire', models.DateTimeField()), + ('uuid', models.UUIDField(default=uuid.UUID('37c7f6dd-cba7-4960-ae50-3849df2f7403'), unique=True)), + ('cuser_role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cuser.cuserrole')), + ], + ), + ] diff --git a/apps/jwt_auth/migrations/0005_alter_cuserroleloginopt_uuid_alter_jwttoken_access_and_more.py b/apps/jwt_auth/migrations/0005_alter_cuserroleloginopt_uuid_alter_jwttoken_access_and_more.py new file mode 100644 index 0000000..b51fcb1 --- /dev/null +++ b/apps/jwt_auth/migrations/0005_alter_cuserroleloginopt_uuid_alter_jwttoken_access_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.1 on 2023-08-07 08:37 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0004_cuserroleloginopt'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('49367381-4117-42f8-a300-4ea7fdefe797'), unique=True), + ), + migrations.AlterField( + model_name='jwttoken', + name='access', + field=models.CharField(blank=True, max_length=500, null=True), + ), + migrations.AlterField( + model_name='jwttoken', + name='refresh', + field=models.CharField(max_length=500), + ), + ] diff --git a/apps/jwt_auth/migrations/0006_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0006_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..f6d719e --- /dev/null +++ b/apps/jwt_auth/migrations/0006_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-07 12:00 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0005_alter_cuserroleloginopt_uuid_alter_jwttoken_access_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('d747675e-0e3c-49d2-ac17-ee32b8669a56'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0007_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0007_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..7123bd9 --- /dev/null +++ b/apps/jwt_auth/migrations/0007_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-07 12:03 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0006_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('a688bf23-0a6c-4080-b31e-e67b96bae559'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0008_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0008_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..cfc8fcd --- /dev/null +++ b/apps/jwt_auth/migrations/0008_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-08 08:37 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0007_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('d78832c8-ecf7-43a9-b26f-76095fb09b69'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0009_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0009_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..114bcd2 --- /dev/null +++ b/apps/jwt_auth/migrations/0009_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-08 08:44 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0008_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('541dd0c2-e8e3-40fe-94e3-71c54f258e31'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0010_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0010_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..8c7ddaf --- /dev/null +++ b/apps/jwt_auth/migrations/0010_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-08 08:48 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0009_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('de316bc9-33a1-433f-a169-b033ceee4189'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0011_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0011_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..13b0cd0 --- /dev/null +++ b/apps/jwt_auth/migrations/0011_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-08 08:48 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0010_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('22233ca2-ac64-459d-acae-61ce323085e4'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0012_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0012_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..2266d50 --- /dev/null +++ b/apps/jwt_auth/migrations/0012_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-10 05:19 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0011_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('3de52e99-5b0d-4de3-853a-c5f356edcee2'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0013_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0013_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..5a07f41 --- /dev/null +++ b/apps/jwt_auth/migrations/0013_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-10 05:39 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0012_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('8b8db649-32ea-4d85-bd7f-70f37023dc83'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0014_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0014_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..a2e7c15 --- /dev/null +++ b/apps/jwt_auth/migrations/0014_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-13 05:45 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0013_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('d04a18c0-3728-45f5-a24d-368f35760491'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0015_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0015_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..fed5ec5 --- /dev/null +++ b/apps/jwt_auth/migrations/0015_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-13 05:51 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0014_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('e2754ac6-d826-412b-830c-36824044059a'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0016_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0016_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..44e6024 --- /dev/null +++ b/apps/jwt_auth/migrations/0016_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-13 05:52 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0015_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('0c143a3f-9352-4091-9166-3a36b259712d'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0017_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0017_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..b210b8f --- /dev/null +++ b/apps/jwt_auth/migrations/0017_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-13 05:55 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0016_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('e0cfd6f4-e278-4a1c-9d86-9d0ae5609feb'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0018_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0018_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..8a60dde --- /dev/null +++ b/apps/jwt_auth/migrations/0018_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-13 05:55 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0017_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('e78b913c-d449-4a86-bd1f-f62f14891c6e'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0019_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0019_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..995846b --- /dev/null +++ b/apps/jwt_auth/migrations/0019_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-14 05:48 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0018_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('fc09e8f3-8f33-408e-85c6-1f152d18d553'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0020_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0020_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..c74ec89 --- /dev/null +++ b/apps/jwt_auth/migrations/0020_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-14 05:49 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0019_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('762e49d2-f2e7-4fe1-a13b-22c5870b45ff'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0021_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0021_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..a7be374 --- /dev/null +++ b/apps/jwt_auth/migrations/0021_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-19 12:54 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0020_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('b3c6473e-841d-42f7-82bf-0f32ea31cdff'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0022_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0022_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..cded207 --- /dev/null +++ b/apps/jwt_auth/migrations/0022_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-20 08:10 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0021_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('d8115499-21f1-4bfd-a716-fd70fdb7e250'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0023_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0023_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..bca27af --- /dev/null +++ b/apps/jwt_auth/migrations/0023_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-10-22 06:55 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0022_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('7cc5a6b6-2729-4e2b-b03e-aa29cc66befe'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0024_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0024_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..b408f15 --- /dev/null +++ b/apps/jwt_auth/migrations/0024_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-10-22 07:00 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0023_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('f1b3355e-44b6-4fed-869d-1f264cf2888e'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0025_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0025_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..826fa4a --- /dev/null +++ b/apps/jwt_auth/migrations/0025_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-10-22 07:20 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0024_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('ac36e3e3-c59d-4db2-9fa9-3602dddb5a06'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0026_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0026_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..5f24d8e --- /dev/null +++ b/apps/jwt_auth/migrations/0026_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-20 10:41 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0025_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('d2983df9-8544-4517-9f87-6c5eac1ae08d'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0027_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0027_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..bd11b60 --- /dev/null +++ b/apps/jwt_auth/migrations/0027_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-20 11:32 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0026_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('1d347d2e-e288-4646-a03a-a8ac30f6d941'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0028_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0028_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..f6001f1 --- /dev/null +++ b/apps/jwt_auth/migrations/0028_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-24 06:43 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0027_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('fe63de9e-93c0-4138-87c3-da38fd98ce3d'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0029_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0029_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..f4dcbef --- /dev/null +++ b/apps/jwt_auth/migrations/0029_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-24 06:43 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0028_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('73ce073c-a35d-4517-a7eb-82fcfc46df02'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0030_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0030_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..e757799 --- /dev/null +++ b/apps/jwt_auth/migrations/0030_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-24 06:44 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0029_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('c038407d-b9a8-46bc-be64-110dce94ef24'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0031_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0031_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..fd007be --- /dev/null +++ b/apps/jwt_auth/migrations/0031_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-24 06:49 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0030_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('48aeb395-3ca3-4d86-8b4b-48e4ac33fdc0'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0032_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0032_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..eb0ae7b --- /dev/null +++ b/apps/jwt_auth/migrations/0032_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-24 06:54 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0031_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('a7685a15-7e73-4bd5-9a26-61f218c3fee8'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0033_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0033_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..9bf5da0 --- /dev/null +++ b/apps/jwt_auth/migrations/0033_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-24 06:55 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0032_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('4fae2729-617a-4b63-a8dd-f767d3ddca1d'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0034_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0034_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..a7ca77d --- /dev/null +++ b/apps/jwt_auth/migrations/0034_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-24 07:04 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0033_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('7b45d40b-1ae5-4ef6-9e96-c7d663590faa'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0035_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0035_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..6c5e5d2 --- /dev/null +++ b/apps/jwt_auth/migrations/0035_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-24 07:09 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0034_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('1775f974-b6fa-42f8-86e9-7e70488ff983'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0036_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0036_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..0785e3b --- /dev/null +++ b/apps/jwt_auth/migrations/0036_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-24 07:11 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0035_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('e0ab2d69-5a5d-497f-a1ef-35472005e935'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0037_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0037_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..eabe0f2 --- /dev/null +++ b/apps/jwt_auth/migrations/0037_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-25 08:53 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0036_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('ed6a4531-68e1-4c30-a2eb-07340ab502c3'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0038_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0038_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..dbeb6b8 --- /dev/null +++ b/apps/jwt_auth/migrations/0038_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-01 11:05 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0037_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('c9472e6e-8808-47a2-a156-c850c9a17b58'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0039_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0039_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..9ef2acb --- /dev/null +++ b/apps/jwt_auth/migrations/0039_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-01 11:07 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0038_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('536eb30f-f56d-4e5f-9c4a-b193418f9571'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0040_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0040_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..34095ae --- /dev/null +++ b/apps/jwt_auth/migrations/0040_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-01 11:08 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0039_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('8bf5e304-391f-4365-868b-cf852d8337b4'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0041_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0041_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..4cc8544 --- /dev/null +++ b/apps/jwt_auth/migrations/0041_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-05 07:11 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0040_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('c63107f7-41d6-41f9-95c7-3fbbcbde82ac'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0042_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0042_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..5d76826 --- /dev/null +++ b/apps/jwt_auth/migrations/0042_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-07 15:15 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0041_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('7ac2bbe1-3ce1-4853-b906-da5590eed9d4'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0043_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0043_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..42681be --- /dev/null +++ b/apps/jwt_auth/migrations/0043_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-07 15:15 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0042_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('407a4a55-76ba-4611-bf61-24f2f713595c'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0044_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0044_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..7dfe52c --- /dev/null +++ b/apps/jwt_auth/migrations/0044_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-08 11:53 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0043_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('b6c9dce8-5169-49f2-82c5-dc814655c94a'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0045_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0045_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..11b5af4 --- /dev/null +++ b/apps/jwt_auth/migrations/0045_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-09 10:05 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0044_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('6a39c5ae-4e05-4a55-a473-4a1387898c43'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0046_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0046_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..de62f11 --- /dev/null +++ b/apps/jwt_auth/migrations/0046_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-10 12:08 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0045_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('eb7ded6a-97d9-42ff-b10e-8106ccbf8916'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0047_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0047_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..abf2129 --- /dev/null +++ b/apps/jwt_auth/migrations/0047_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-13 10:29 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0046_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('922fd49d-4f1a-4523-aad4-9b562f2be187'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0048_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0048_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..c750fcc --- /dev/null +++ b/apps/jwt_auth/migrations/0048_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-13 10:31 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0047_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('ef5272f4-66af-4ceb-ace8-8b640e257d4b'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0049_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0049_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..071d818 --- /dev/null +++ b/apps/jwt_auth/migrations/0049_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-13 10:35 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0048_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('e92b9df9-508c-4cb1-bde0-d5f6bdb2edea'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0050_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0050_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..a2de5fa --- /dev/null +++ b/apps/jwt_auth/migrations/0050_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-13 15:42 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0049_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('411f5d2e-f1ab-4108-ad0f-dbe13334790a'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0051_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0051_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..679d68a --- /dev/null +++ b/apps/jwt_auth/migrations/0051_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-13 15:45 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0050_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('b4517f89-fc70-4ed3-b6f2-39fa57de3458'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0052_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0052_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..0de4532 --- /dev/null +++ b/apps/jwt_auth/migrations/0052_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-18 08:40 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0051_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('f8d12231-67f0-497a-82e6-27f89b0e563f'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0053_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0053_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..9fbee73 --- /dev/null +++ b/apps/jwt_auth/migrations/0053_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-20 07:49 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0052_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('36727b78-5687-4b52-9e16-203c91f2eefb'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0054_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0054_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..f070715 --- /dev/null +++ b/apps/jwt_auth/migrations/0054_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:06 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0053_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('993d0c0d-30c1-4a28-b254-b931ac01f37b'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0055_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0055_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..b6382a0 --- /dev/null +++ b/apps/jwt_auth/migrations/0055_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:07 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0054_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('3f2d5e83-6d3a-42d7-85c8-2ca542124ace'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0056_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0056_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..7a921a1 --- /dev/null +++ b/apps/jwt_auth/migrations/0056_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:07 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0055_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('b5b8ec0c-3cc7-40f9-876c-d7baa65c6603'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0057_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0057_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..5c8e2ea --- /dev/null +++ b/apps/jwt_auth/migrations/0057_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:09 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0056_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('c362cd5f-83e4-4f12-be55-14fbcd67b9bb'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0058_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0058_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..dfadd8c --- /dev/null +++ b/apps/jwt_auth/migrations/0058_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:10 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0057_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('6165ce5c-0732-481b-9a8b-10c6f2cebe4c'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0059_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0059_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..8f01863 --- /dev/null +++ b/apps/jwt_auth/migrations/0059_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 09:15 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0058_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('f461debd-f9bd-44c6-a5f0-757f82516af4'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0060_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0060_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..f17942a --- /dev/null +++ b/apps/jwt_auth/migrations/0060_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 09:16 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0059_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('6d83fcfc-f119-40d5-84e9-6df33c793575'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0061_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0061_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..8e21051 --- /dev/null +++ b/apps/jwt_auth/migrations/0061_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:43 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0060_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('d68a4b99-efd6-4faa-817a-07ccd11be546'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0062_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0062_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..7848b54 --- /dev/null +++ b/apps/jwt_auth/migrations/0062_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:44 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0061_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('8c4b6fce-b089-445d-96cf-613e7e55b24f'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0063_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0063_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..8519067 --- /dev/null +++ b/apps/jwt_auth/migrations/0063_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:45 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0062_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('22c19f40-7107-493b-b382-f5e4e001e598'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/0064_alter_cuserroleloginopt_uuid.py b/apps/jwt_auth/migrations/0064_alter_cuserroleloginopt_uuid.py new file mode 100644 index 0000000..ec71f5c --- /dev/null +++ b/apps/jwt_auth/migrations/0064_alter_cuserroleloginopt_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:59 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('jwt_auth', '0063_alter_cuserroleloginopt_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='cuserroleloginopt', + name='uuid', + field=models.UUIDField(default=uuid.UUID('1d75453f-f5e9-4d15-ad6b-a0efd104bcb6'), unique=True), + ), + ] diff --git a/apps/jwt_auth/migrations/__init__.py b/apps/jwt_auth/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/jwt_auth/models.py b/apps/jwt_auth/models.py new file mode 100644 index 0000000..bf8e6ba --- /dev/null +++ b/apps/jwt_auth/models.py @@ -0,0 +1,24 @@ +from django.db import models +from django.contrib.auth import get_user_model +from apps.cuser.models import CuserRole as CuserRoleModel +import uuid +CUser = get_user_model() +# Create your models here. + + +class JwtToken(models.Model): + refresh = models.CharField(max_length=500, null=False, blank=False) + access = models.CharField(max_length=500, null=True, blank=True) + expire = models.DateTimeField(null=True, blank=True) + cuser = models.ForeignKey(CUser, null=False, blank=False, on_delete=models.CASCADE) + cuser_role = models.ForeignKey(CuserRoleModel, null=False, blank=False, on_delete=models.CASCADE) + is_blocked = models.BooleanField(default=False) + + def __str__(self): + return f'{self.cuser}' + + +class CUserRoleLoginOpt(models.Model): + cuser_role = models.ForeignKey(CuserRoleModel, on_delete=models.CASCADE) + expire = models.DateTimeField(null=False, blank=False) + uuid = models.UUIDField(default=uuid.uuid4(), unique=True, null=False, blank=False) diff --git a/apps/jwt_auth/serializers.py b/apps/jwt_auth/serializers.py new file mode 100644 index 0000000..f0b3b87 --- /dev/null +++ b/apps/jwt_auth/serializers.py @@ -0,0 +1,15 @@ +from rest_framework_simplejwt.serializers import TokenObtainPairSerializer +from rest_framework_simplejwt.views import TokenObtainPairView + + +class CustomTokenObtainPairSerializer(TokenObtainPairSerializer): + @classmethod + def get_token(cls, user): + token = super().get_token(user) + + token['user_uuid'] = user.uuid + + if 'user_id' in token: + del token['user_id'] + + return token diff --git a/apps/jwt_auth/services.py b/apps/jwt_auth/services.py new file mode 100644 index 0000000..1c00f76 --- /dev/null +++ b/apps/jwt_auth/services.py @@ -0,0 +1,90 @@ +import uuid +import random +from django.contrib.auth import get_user_model +from apps.jwt_auth.models import CUserRoleLoginOpt +from apps.cuser.models import CuserRole as CuserRoleModel, SmsOpt as SmsOptModel +from django.utils import timezone +from rest_framework import exceptions +CUser = get_user_model() + + +def login_by_username(username: str, password: str) -> CUserRoleLoginOpt: + try: + print(username) + user = CUser.objects.get(username=username) + print(user) + if user and user.check_password(password): + + cuser_roles = CuserRoleModel.objects.filter(cuser=user) + CUserRoleLoginOpt.objects.filter(cuser_role__cuser=user).delete() + + if cuser_roles: + for user_role_obj in cuser_roles: + + if not (user_role_obj.is_active and user_role_obj.mobile_verify): + raise exceptions.ValidationError({"error": 'Not a valid user'}) + + expire_datetime = timezone.datetime.now() + timezone.timedelta(minutes=5) + + user_role_opt = CUserRoleLoginOpt() + user_role_opt.cuser_role = user_role_obj + user_role_opt.expire = expire_datetime + user_role_opt.uuid = uuid.uuid4() + user_role_opt.save() + + login_opts = CUserRoleLoginOpt.objects.filter(cuser_role__cuser=user) + return login_opts + + else: + raise exceptions.ValidationError({'error': "User not have role"}) + else: + raise exceptions.ValidationError({'error': "Username or password is not correct"}) + + except CUser.DoesNotExist: + raise exceptions.ValidationError({'error': "Username or password is not correct"}) + + +def login_by_mobile(mobile: int): + try: + + exists_cuser_roles = CuserRoleModel.objects.filter(mobile=mobile, mobile_verify=True) + if not len(exists_cuser_roles) >= 1: + raise exceptions.ValidationError({'error': "mobile not found"}) + + random_number = random.randint(100000, 999999) + + try: + SmsOptModel.objects.filter(mobile=mobile).delete() + except Exception as exp: + pass + + opt_code = SmsOptModel(mobile=mobile, opt=123456) + opt_code.save() + + except Exception as exp: + raise exceptions.ValidationError({'error': "mobile not found"}) + + +def verify_mobile_and_get_token(mobile: int, opt: int) -> CUserRoleLoginOpt: + try: + opt_obj = SmsOptModel.objects.filter(mobile=mobile, opt=opt)[0] + + if opt_obj: + cuser_roles = CuserRoleModel.objects.filter(mobile=mobile, mobile_verify=True, is_active=True) + CUserRoleLoginOpt.objects.filter(cuser_role__in=cuser_roles).delete() + + if cuser_roles: + for user_role_obj in cuser_roles: + expire_datetime = timezone.datetime.now() + timezone.timedelta(minutes=5) + + user_role_opt = CUserRoleLoginOpt() + user_role_opt.cuser_role = user_role_obj + user_role_opt.expire = expire_datetime + user_role_opt.uuid = uuid.uuid4() + user_role_opt.save() + + login_opts = CUserRoleLoginOpt.objects.filter(cuser_role__in=cuser_roles) + SmsOptModel.objects.filter(mobile=mobile).delete() + return login_opts + except Exception as exp: + raise exceptions.ValidationError({'error': "cannot verify this mobile and opt code:"+str(exp)}) diff --git a/apps/jwt_auth/tests.py b/apps/jwt_auth/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/jwt_auth/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/jwt_auth/urls.py b/apps/jwt_auth/urls.py new file mode 100644 index 0000000..87d7b64 --- /dev/null +++ b/apps/jwt_auth/urls.py @@ -0,0 +1,16 @@ +from django.urls import path +from apps.jwt_auth.views import ( + CustomTokenRefreshView, + CustomTokenObtainPairView, + LoginByUserView, + LoginByMobileView, + VerifyMobileAndGetTokenView +) + +urlpatterns = [ + path('login/username/', LoginByUserView.as_view(), name='login_by_username'), + path('login/mobile/', LoginByMobileView.as_view(), name='login_by_mobile'), + path('login/mobile/verify/', VerifyMobileAndGetTokenView.as_view(), name='verify_mobile'), + path('token/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'), + path('token/refresh/', CustomTokenRefreshView.as_view(), name='token_refresh'), +] \ No newline at end of file diff --git a/apps/jwt_auth/views.py b/apps/jwt_auth/views.py new file mode 100644 index 0000000..775d27f --- /dev/null +++ b/apps/jwt_auth/views.py @@ -0,0 +1,295 @@ +from django.shortcuts import render +from django.utils import timezone +from rest_framework.views import APIView +from rest_framework import serializers +from rest_framework.response import Response +from django.db import transaction +from apps.jwt_auth.models import JwtToken as JwtTokenModel, CUserRoleLoginOpt +from apps.cuser.models import CuserRole as CuserRoleModel, RoleType as RoleTypeModel +from apps.customer.models import Customer as CustomerModel, CustomerResponsible as CustomerResponsibleModel +from rest_framework_simplejwt.tokens import RefreshToken, AccessToken, Token +from rest_framework_simplejwt.authentication import JWTAuthentication +from apps.workspace.models import Workspace as WorkspaceModel +from apps.jwt_auth.services import login_by_username, login_by_mobile, verify_mobile_and_get_token +from django.contrib.auth import get_user_model +from rest_framework import status +CUser = get_user_model() + +# Create your views here. + + +class VerifyMobileAndGetTokenView(APIView): + + class InputSerializer(serializers.Serializer): + mobile = serializers.IntegerField(required=True) + opt = serializers.IntegerField(required=True) + + + class OutputSerializer(serializers.ModelSerializer): + + class CUserRoleSerializer(serializers.ModelSerializer): + + class RoleTypeSerializer(serializers.ModelSerializer): + class Meta: + model = RoleTypeModel + fields = ('role_name', ) + + role_type = RoleTypeSerializer() + + class Meta: + model = CuserRoleModel + fields = ('name_family', 'title', 'role_type') + + cuser_role = CUserRoleSerializer() + + class Meta: + model = CUserRoleLoginOpt + fields = ('cuser_role', 'expire', 'uuid') + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + print("ok") + + login_opts = verify_mobile_and_get_token(**serializer.validated_data) + + print("ok2") + + response_data = self.OutputSerializer(login_opts, many=True).data + + return Response(response_data) + + +class LoginByMobileView(APIView): + + class InputSerializer(serializers.Serializer): + mobile = serializers.IntegerField(required=True) + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + login_by_mobile(**serializer.validated_data) + + output = { + "result": { + "mobile": serializer.validated_data.get('mobile'), + "message": "opt code send" + } + } + + return Response(output) + + +class LoginByUserView(APIView): + + class InputSerializer(serializers.Serializer): + username = serializers.CharField(required=True) + password = serializers.CharField(required=True) + + class OutputSerializer(serializers.ModelSerializer): + + class CUserRoleSerializer(serializers.ModelSerializer): + + class RoleTypeSerializer(serializers.ModelSerializer): + class Meta: + model = RoleTypeModel + fields = ('role_name', ) + + role_type = RoleTypeSerializer() + + class Meta: + model = CuserRoleModel + fields = ('name_family', 'title', 'role_type') + + cuser_role = CUserRoleSerializer() + + class Meta: + model = CUserRoleLoginOpt + fields = ('cuser_role', 'expire', 'uuid') + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + username = serializer.validated_data.get('username') + password = serializer.validated_data.get('password') + + login_opts = login_by_username(username=username, password=password) + + response_data = self.OutputSerializer(login_opts, many=True).data + + return Response(response_data) + + +class CustomTokenObtainPairView(APIView): + + class InputSerializer(serializers.Serializer): + uuid_opt = serializers.CharField(required=True) + + class OutputSerializer(serializers.Serializer): + access = serializers.CharField(required=True) + refresh = serializers.CharField(required=True) + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + uuid_opt = serializer.validated_data.get('uuid_opt') + + try: + user_login_opt = CUserRoleLoginOpt.objects.get(uuid=uuid_opt) + user = user_login_opt.cuser_role.cuser + cuser_role = user_login_opt.cuser_role + + if user_login_opt: + refresh = RefreshToken.for_user(user) + + #if 'user_id' in refresh: + # del refresh['user_id'] + #print(cuser_role.uuid) + + + try: + customerObj = CustomerModel.objects.get(cuser_role__uuid=str(cuser_role.uuid)) + + #workspace = WorkspaceModel.objects.get(cuser_role_owner__uuid=str(cuser_role.uuid)) + + workspace = customerObj.sys_ws + except: + operator = CustomerResponsibleModel.objects.get(cuser_role=cuser_role) + workspace = operator.sys_ws + + refresh['workspace_uuid'] = str(workspace.uuid) + + refresh['cuser_role_uuid'] = str(cuser_role.uuid) + + refresh['user_uuid'] = str(user.uuid) + + #print(str(refresh)) + + try: + jwt_db = JwtTokenModel(refresh=str(refresh), access=str(refresh.access_token), + cuser=user, cuser_role=cuser_role) + jwt_db.save() + + CUserRoleLoginOpt.objects.filter(cuser_role__cuser=user).delete() + + except Exception as exp: + return Response({'error': str(exp)}) + + new_token_data = { + 'refresh': str(refresh), + 'access': str(refresh.access_token), + } + + out_data = self.OutputSerializer(data=new_token_data) + out_data.is_valid(raise_exception=True) + + return Response(out_data.data) + else: + return Response({"error": 'userpass is not True'}) + except CUserRoleLoginOpt.DoesNotExist: + return Response({"error": 'Token not valid'}) + except Exception as exp: + return Response({"error": 'user or pass is not true 2'}) + + +class CustomTokenRefreshView(APIView): + + class InputSerializer(serializers.Serializer): + refresh = serializers.CharField(required=True) + + class OutputSerializer(serializers.Serializer): + access = serializers.CharField(required=True) + refresh = serializers.CharField(required=True) + + def post(self, request): + with transaction.atomic(): + try: + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + refresh_token = serializer.validated_data.get('refresh') + + print(refresh_token) + + # check: token is valid? + ref_token = RefreshToken(token=refresh_token) + + print("ref") + + # first token must exists in whitelist and not blocked + token_exist = JwtTokenModel.objects.get(refresh=str(refresh_token)) + + print("exsist") + + token_is_blocked = JwtTokenModel.objects.get(refresh=str(refresh_token)).is_blocked + + print("yess") + + if token_exist and token_is_blocked is False: + JwtTokenModel.objects.get(refresh=str(refresh_token)).delete() + else: + raise ValueError('refresh token is not in whitelist or blocked!') + + user_uuid = ref_token.payload.get('user_uuid') + user_id = ref_token.payload.get('user_id') + cuser_role_uuid = ref_token.payload.get('cuser_role_uuid') + + token_exp_timestamp = ref_token.payload.get('exp') + token_exp = timezone.make_aware(timezone.datetime.fromtimestamp(token_exp_timestamp), timezone.utc) + user = CUser.objects.get(id=user_id) + cuser_role = CuserRoleModel.objects.get(uuid=cuser_role_uuid) + + + print("OKH###") + + if ref_token: + new_token = RefreshToken.for_user(user) + + #if 'user_id' in new_token: + #del new_token['user_id'] + + new_token['user_uuid'] = str(user.uuid) + new_token['cuser_role_uuid'] = str(cuser_role.uuid) + + print("OKOKKO 555") + + #workspace = WorkspaceModel.objects.get(cuser_role_owner__uuid=str(cuser_role.uuid)) + + try: + customerObj = CustomerModel.objects.get(cuser_role__uuid=str(cuser_role.uuid)) + new_token['workspace_uuid'] = str(customerObj.sys_ws.uuid) + except: + operator = CustomerResponsibleModel.objects.get(cuser_role__uuid=str(cuser_role.uuid)) + new_token['workspace_uuid'] = str(operator.sys_ws.uuid) + + + new_access_token = new_token.access_token + + new_token_data = { + 'refresh': str(new_token), + 'access': str(new_access_token), + } + + print("OKOKKO") + + new_token_obj = JwtTokenModel(refresh=str(new_token), + access=str(new_token_data.get('access')), + expire=token_exp, + cuser=user, + cuser_role=cuser_role + ) + + print("OKOKKO 222") + new_token_obj.save() + out_data = self.OutputSerializer(data=new_token_data) + out_data.is_valid(raise_exception=True) + return Response(out_data.data) + + except Exception as e: + transaction.set_rollback(True) + return Response({"error": str(e)}) diff --git a/apps/lead/__init__.py b/apps/lead/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/lead/admin.py b/apps/lead/admin.py new file mode 100644 index 0000000..68ff7d3 --- /dev/null +++ b/apps/lead/admin.py @@ -0,0 +1,36 @@ +from django.contrib import admin +from apps.lead.models import * +# Register your models here. + + +@admin.register(Lead) +class LeadAdmin(admin.ModelAdmin): + pass + + +@admin.register(LeadComment) +class LeadCommentAdmin(admin.ModelAdmin): + pass + + +@admin.register(LeadStep) +class LeadStepAdmin(admin.ModelAdmin): + pass + + +@admin.register(LeadType) +class LeadTypeAdmin(admin.ModelAdmin): + pass + + +@admin.register(LeadSource) +class LeadSourceAdmin(admin.ModelAdmin): + pass + + +@admin.register(LeadReason) +class LeadReasonAdmin(admin.ModelAdmin): + pass + + + diff --git a/apps/lead/apps.py b/apps/lead/apps.py new file mode 100644 index 0000000..370751a --- /dev/null +++ b/apps/lead/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class LeadConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.lead' diff --git a/apps/lead/migrations/0001_initial.py b/apps/lead/migrations/0001_initial.py new file mode 100644 index 0000000..0ab53df --- /dev/null +++ b/apps/lead/migrations/0001_initial.py @@ -0,0 +1,93 @@ +# Generated by Django 4.2.1 on 2024-01-01 11:07 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('shop', '0011_producttype'), + ('cuser', '0005_smsopt'), + ('customer', '0003_alter_customer_address'), + ('workspace', '0003_alter_workspace_uuid'), + ] + + operations = [ + migrations.CreateModel( + name='LeadType', + 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)), + ('title', models.CharField(max_length=50)), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='LeadStep', + 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, + }, + ), + migrations.CreateModel( + name='LeadSource', + 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)), + ('title', models.CharField(max_length=50)), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='LeadReason', + 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, + }, + ), + migrations.CreateModel( + name='Lead', + 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)), + ('title', models.CharField(max_length=100)), + ('status', models.CharField(blank=True, choices=[('SU', 'OK'), ('FA', 'OK')], max_length=2, null=True)), + ('city', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='workspace.city')), + ('cuser_role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cuser.cuserrole')), + ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='customer.customer')), + ('product_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.producttype')), + ('reason', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='lead.leadreason')), + ('source', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='lead.leadsource')), + ('step', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='lead.leadstep')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ('type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='lead.leadtype')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/lead/migrations/0002_leadreason_success.py b/apps/lead/migrations/0002_leadreason_success.py new file mode 100644 index 0000000..d4cc24c --- /dev/null +++ b/apps/lead/migrations/0002_leadreason_success.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-01 11:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='leadreason', + name='success', + field=models.BooleanField(blank=True, null=True), + ), + ] diff --git a/apps/lead/migrations/0003_leadproxy_lead_desc.py b/apps/lead/migrations/0003_leadproxy_lead_desc.py new file mode 100644 index 0000000..f05a862 --- /dev/null +++ b/apps/lead/migrations/0003_leadproxy_lead_desc.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.1 on 2024-01-05 07:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0002_leadreason_success'), + ] + + operations = [ + migrations.CreateModel( + name='LeadProxy', + fields=[ + ], + options={ + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('lead.lead',), + ), + migrations.AddField( + model_name='lead', + name='desc', + field=models.CharField(default='1', max_length=300), + preserve_default=False, + ), + ] diff --git a/apps/lead/migrations/0004_delete_leadproxy_alter_lead_status.py b/apps/lead/migrations/0004_delete_leadproxy_alter_lead_status.py new file mode 100644 index 0000000..27dada8 --- /dev/null +++ b/apps/lead/migrations/0004_delete_leadproxy_alter_lead_status.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.1 on 2024-01-07 15:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0003_leadproxy_lead_desc'), + ] + + operations = [ + migrations.DeleteModel( + name='LeadProxy', + ), + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(blank=True, choices=[('UN', 'unknown'), ('SU', 'OK'), ('FA', 'FAIL')], max_length=2, null=True), + ), + ] diff --git a/apps/lead/migrations/0005_alter_lead_status.py b/apps/lead/migrations/0005_alter_lead_status.py new file mode 100644 index 0000000..d888788 --- /dev/null +++ b/apps/lead/migrations/0005_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-07 15:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0004_delete_leadproxy_alter_lead_status'), + ] + + operations = [ + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(blank=True, choices=[('SU', 'OK'), ('FA', 'FAIL'), ('UN', 'unknown')], default='UN', max_length=2, null=True), + ), + ] diff --git a/apps/lead/migrations/0006_alter_lead_status_leadcomment.py b/apps/lead/migrations/0006_alter_lead_status_leadcomment.py new file mode 100644 index 0000000..93db011 --- /dev/null +++ b/apps/lead/migrations/0006_alter_lead_status_leadcomment.py @@ -0,0 +1,36 @@ +# Generated by Django 4.2.1 on 2024-01-08 11:53 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0005_smsopt'), + ('workspace', '0003_alter_workspace_uuid'), + ('lead', '0005_alter_lead_status'), + ] + + operations = [ + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(blank=True, choices=[('FA', 'FAIL'), ('SU', 'OK'), ('UN', 'unknown')], default='UN', max_length=2, null=True), + ), + migrations.CreateModel( + name='LeadComment', + 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)), + ('desc', models.CharField(max_length=300)), + ('cuser_role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cuser.cuserrole')), + ('lead', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lead_comment', to='lead.lead')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/lead/migrations/0007_alter_leadcomment_options.py b/apps/lead/migrations/0007_alter_leadcomment_options.py new file mode 100644 index 0000000..ee7ab07 --- /dev/null +++ b/apps/lead/migrations/0007_alter_leadcomment_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.1 on 2024-01-09 10:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0006_alter_lead_status_leadcomment'), + ] + + operations = [ + migrations.AlterModelOptions( + name='leadcomment', + options={'ordering': ['-created_at']}, + ), + ] diff --git a/apps/lead/migrations/0008_alter_lead_status.py b/apps/lead/migrations/0008_alter_lead_status.py new file mode 100644 index 0000000..daea1b6 --- /dev/null +++ b/apps/lead/migrations/0008_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-10 12:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0007_alter_leadcomment_options'), + ] + + operations = [ + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(blank=True, choices=[('SU', 'OK'), ('UN', 'unknown'), ('FA', 'FAIL')], default='UN', max_length=2, null=True), + ), + ] diff --git a/apps/lead/migrations/0009_alter_lead_status.py b/apps/lead/migrations/0009_alter_lead_status.py new file mode 100644 index 0000000..f7d46d3 --- /dev/null +++ b/apps/lead/migrations/0009_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-13 10:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0008_alter_lead_status'), + ] + + operations = [ + 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/0010_alter_lead_status.py b/apps/lead/migrations/0010_alter_lead_status.py new file mode 100644 index 0000000..d93af08 --- /dev/null +++ b/apps/lead/migrations/0010_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-13 10:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0009_alter_lead_status'), + ] + + operations = [ + 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/0011_alter_lead_status.py b/apps/lead/migrations/0011_alter_lead_status.py new file mode 100644 index 0000000..0e49faa --- /dev/null +++ b/apps/lead/migrations/0011_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-13 15:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0010_alter_lead_status'), + ] + + operations = [ + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(blank=True, choices=[('SU', 'OK'), ('FA', 'FAIL'), ('UN', 'unknown')], default='UN', max_length=2, null=True), + ), + ] diff --git a/apps/lead/migrations/0012_alter_lead_status.py b/apps/lead/migrations/0012_alter_lead_status.py new file mode 100644 index 0000000..78f2927 --- /dev/null +++ b/apps/lead/migrations/0012_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-18 08:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0011_alter_lead_status'), + ] + + operations = [ + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(blank=True, choices=[('FA', 'FAIL'), ('SU', 'OK'), ('UN', 'unknown')], default='UN', max_length=2, null=True), + ), + ] diff --git a/apps/lead/migrations/0013_alter_lead_status.py b/apps/lead/migrations/0013_alter_lead_status.py new file mode 100644 index 0000000..3156fbf --- /dev/null +++ b/apps/lead/migrations/0013_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-20 07:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0012_alter_lead_status'), + ] + + operations = [ + 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/0014_alter_lead_status.py b/apps/lead/migrations/0014_alter_lead_status.py new file mode 100644 index 0000000..04635a0 --- /dev/null +++ b/apps/lead/migrations/0014_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0013_alter_lead_status'), + ] + + operations = [ + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(blank=True, choices=[('FA', 'FAIL'), ('SU', 'OK'), ('UN', 'unknown')], default='UN', max_length=2, null=True), + ), + ] diff --git a/apps/lead/migrations/0015_alter_lead_status.py b/apps/lead/migrations/0015_alter_lead_status.py new file mode 100644 index 0000000..6a1529d --- /dev/null +++ b/apps/lead/migrations/0015_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0014_alter_lead_status'), + ] + + operations = [ + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(blank=True, choices=[('SU', 'OK'), ('FA', 'FAIL'), ('UN', 'unknown')], default='UN', max_length=2, null=True), + ), + ] diff --git a/apps/lead/migrations/0016_alter_lead_status.py b/apps/lead/migrations/0016_alter_lead_status.py new file mode 100644 index 0000000..f1ce797 --- /dev/null +++ b/apps/lead/migrations/0016_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 09:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0015_alter_lead_status'), + ] + + operations = [ + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(blank=True, choices=[('UN', 'unknown'), ('FA', 'FAIL'), ('SU', 'OK')], default='UN', max_length=2, null=True), + ), + ] diff --git a/apps/lead/migrations/0017_alter_lead_status.py b/apps/lead/migrations/0017_alter_lead_status.py new file mode 100644 index 0000000..12fea55 --- /dev/null +++ b/apps/lead/migrations/0017_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 09:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0016_alter_lead_status'), + ] + + operations = [ + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(blank=True, choices=[('SU', 'OK'), ('FA', 'FAIL'), ('UN', 'unknown')], default='UN', max_length=2, null=True), + ), + ] diff --git a/apps/lead/migrations/0018_alter_lead_status.py b/apps/lead/migrations/0018_alter_lead_status.py new file mode 100644 index 0000000..5303520 --- /dev/null +++ b/apps/lead/migrations/0018_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0017_alter_lead_status'), + ] + + operations = [ + 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/0019_alter_lead_status.py b/apps/lead/migrations/0019_alter_lead_status.py new file mode 100644 index 0000000..ad920eb --- /dev/null +++ b/apps/lead/migrations/0019_alter_lead_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0018_alter_lead_status'), + ] + + operations = [ + 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/__init__.py b/apps/lead/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/lead/models.py b/apps/lead/models.py new file mode 100644 index 0000000..11d28d6 --- /dev/null +++ b/apps/lead/models.py @@ -0,0 +1,80 @@ +from django.db import models +from utils.base_model import BaseModel +from apps.customer.models import Customer as CustomerModel +from apps.shop.models import ProductType as ProductTypeModel +from apps.workspace.models import City as CityModel, Workspace as WorkspaceModel +from apps.cuser.models import CuserRole as CuserRoleModel +# Create your models here. + + +class LeadType(BaseModel): + title = models.CharField(max_length=50) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return self.title + + +class LeadSource(BaseModel): + title = models.CharField(max_length=50) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return self.title + + +class LeadStep(BaseModel): + name = models.CharField(max_length=50) + 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) + success = models.BooleanField(null=True, blank=True) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return self.name + + +class Lead(BaseModel): + + STATUS_CHOICES = { + ("SU", "OK"), + ("FA", "FAIL"), + ("UN", "unknown"), + } + + title = models.CharField(max_length=100) + desc = models.CharField(max_length=300) + customer = models.ForeignKey(CustomerModel, on_delete=models.CASCADE, null=False, blank=False) + 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) + step = models.ForeignKey(LeadStep, on_delete=models.SET_NULL, null=True, blank=True) + city = models.ForeignKey(CityModel, on_delete=models.SET_NULL, null=True, blank=True) + status = models.CharField(max_length=2, choices=STATUS_CHOICES, null=True, blank=True, default='UN') + reason = models.ForeignKey(LeadReason, on_delete=models.SET_NULL, null=True, blank=True) + cuser_role = models.ForeignKey(CuserRoleModel, on_delete=models.SET_NULL, null=True, blank=True) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return self.title + + +class LeadComment(BaseModel): + + lead = models.ForeignKey(Lead, on_delete=models.CASCADE, null=False, blank=False, related_name='lead_comment') + desc = models.CharField(max_length=300) + cuser_role = models.ForeignKey(CuserRoleModel, on_delete=models.SET_NULL, null=True, blank=True) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + class Meta: + ordering = ['-created_at'] + + def __str__(self): + return self.lead.title + diff --git a/apps/lead/models_proxy.py b/apps/lead/models_proxy.py new file mode 100644 index 0000000..dd76f4f --- /dev/null +++ b/apps/lead/models_proxy.py @@ -0,0 +1,20 @@ +from apps.lead.models import Lead as LeadModel, LeadType as LeadTypeModel, LeadStep as LeadStepModel +from apps.workspace.models import City as CityModel +from apps.customer.models import Customer as CustomerModel +from apps.shop.models import ProductType as ProductTypeModel +from django.core.exceptions import ValidationError +from utils.validations import ObjectValidationById + + +class LeadProxy(LeadModel): + + def full_objects_validation_by_id(self, request): + customer = ObjectValidationById(object_model=CustomerModel, request=request).is_valid(self.customer_id) + customer = ObjectValidationById(object_model=LeadTypeModel, request=request).is_valid(lead_type_id) + + customer = ObjectValidationById(object_model=CustomerModel, request=request).is_valid(customer_id) + customer = ObjectValidationById(object_model=CustomerModel, request=request).is_valid(customer_id) + customer = ObjectValidationById(object_model=CustomerModel, request=request).is_valid(customer_id) + customer = ObjectValidationById(object_model=CustomerModel, request=request).is_valid(customer_id) + return True + diff --git a/apps/lead/selectors.py b/apps/lead/selectors.py new file mode 100644 index 0000000..36eba2b --- /dev/null +++ b/apps/lead/selectors.py @@ -0,0 +1,79 @@ +from apps.lead.models import Lead as LeadModel, LeadType as LeadTypeModel,\ + LeadSource as LeadSourceModel, LeadStep as LeadStepModel +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 +from utils.get_cuser_role import get_cuser_role_uuid +from rest_framework import exceptions + + +def list_leads(request) -> LeadModel: + + try: + workspace_uuid = get_user_workspace_uuid(request) + cuser_role_uuid = get_cuser_role_uuid(request) + leads = LeadModel.objects.filter(sys_ws__uuid=workspace_uuid, cuser_role__uuid=cuser_role_uuid).order_by('-created_at') + + return leads + except Exception as exp: + message = f'Error Fetching List Leads: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def retrieve_lead(request, id: int) -> LeadModel: + + try: + workspace_uuid = get_user_workspace_uuid(request) + cuser_role_uuid = get_cuser_role_uuid(request) + lead = LeadModel.objects.get(id=id, sys_ws__uuid=workspace_uuid, cuser_role__uuid=cuser_role_uuid) + return lead + except Exception as exp: + message = f'Error retrieve Leads: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def retrieve_summary_lead(request) -> LeadModel: + + try: + workspace_uuid = get_user_workspace_uuid(request) + cuser_role_uuid = get_cuser_role_uuid(request) + + mozakere = LeadModel.objects.filter(step_id=2, status__in=('UN', ''), + sys_ws__uuid=workspace_uuid, cuser_role__uuid=cuser_role_uuid)[:20] + pish = LeadModel.objects.filter(step_id=3, status__in=('UN', ''), + sys_ws__uuid=workspace_uuid, cuser_role__uuid=cuser_role_uuid)[:20] + kharid = LeadModel.objects.filter(step_id=4, status__in=('UN', ''), + sys_ws__uuid=workspace_uuid, cuser_role__uuid=cuser_role_uuid)[:20] + + leads = { + 'mozakere': mozakere, + 'pishfactor': pish, + 'kharid': kharid, + } + + return leads + + except Exception as exp: + message = f'Error retrieve summary Leads: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def fields_new_lead_form(request): + + workspace_uuid = get_user_workspace_uuid(request) + + lead_type = LeadTypeModel.objects.filter(sys_ws__uuid=workspace_uuid) + 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) + shahr = CityModel.objects.filter() + + data = { + 'product_types': product_types, + 'lead_types': lead_type, + 'lead_source': lead_source, + 'lead_steps': lead_step, + 'shahr': shahr, + } + return data + diff --git a/apps/lead/serializers.py b/apps/lead/serializers.py new file mode 100644 index 0000000..281839e --- /dev/null +++ b/apps/lead/serializers.py @@ -0,0 +1,45 @@ +from rest_framework import serializers +from apps.lead.models import ( + LeadType as LeadTypeModel, LeadSource as LeadSourceModel, + LeadStep as LeadStepModel, LeadReason as LeadReasonModel +) +from apps.shop.models import ProductType as ProductTypeModel +from apps.workspace.models import City as CityModel + + + + +class ProductTypeSerializers(serializers.ModelSerializer): + class Meta: + model = ProductTypeModel + exclude = ['sys_ws', 'created_at', 'updated_at'] + + +class LeadTypeSerializers(serializers.ModelSerializer): + class Meta: + model = LeadTypeModel + exclude = ['sys_ws', 'created_at', 'updated_at'] + + +class LeadSourceSerializers(serializers.ModelSerializer): + class Meta: + model = LeadSourceModel + exclude = ['sys_ws', 'created_at', 'updated_at'] + + +class LeadStepSerializers(serializers.ModelSerializer): + class Meta: + model = LeadStepModel + exclude = ['sys_ws', 'created_at', 'updated_at'] + + +class LeadReasonSerializers(serializers.ModelSerializer): + class Meta: + model = LeadReasonModel + exclude = ['sys_ws', 'created_at', 'updated_at'] + + +class ShahrSerializers(serializers.ModelSerializer): + class Meta: + model = CityModel + exclude = ['created_at', 'updated_at'] \ No newline at end of file diff --git a/apps/lead/services.py b/apps/lead/services.py new file mode 100644 index 0000000..e00cdf0 --- /dev/null +++ b/apps/lead/services.py @@ -0,0 +1,146 @@ +from apps.lead.models import Lead as LeadModel,\ + LeadType as LeadTypeModel, LeadStep as LeadStepModel, LeadSource as LeadSourceModel, LeadComment as LeadCommentModel +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 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: + try: + sys_ws = get_user_workspace(request=request) + cuser_role = get_cuser_role(request=request) + + customer = ObjectValidationById(object_model=CustomerModel, request=request).is_valid(int(customer_id)) + lead_type = ObjectValidationById(object_model=LeadTypeModel, request=request).is_valid(lead_type_id) + lead_step = ObjectValidationById(object_model=LeadStepModel, request=request).is_valid(lead_step_id) + lead_source = ObjectValidationById(object_model=LeadSourceModel, request=request).is_valid(lead_source_id) + product_type = ObjectValidationById(object_model=ProductTypeModel, request=request).is_valid(product_type_id) + city = ObjectValidationById(object_model=CityModel, request=request).is_valid(city_id) + + new_lead = LeadModel() + new_lead.title = title + new_lead.customer = customer + new_lead.desc = desc + new_lead.type = lead_type + new_lead.step = lead_step + new_lead.source = lead_source + new_lead.product_type = product_type + new_lead.city = city + new_lead.sys_ws = sys_ws + new_lead.cuser_role = cuser_role + + new_lead.save() + + return new_lead + + except Exception as exp: + print("error creae lead") + message = f'Error Create New Lead: {exp}' + raise exceptions.ValidationError({'error': message}) + + +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: + try: + sys_ws = get_user_workspace(request=request) + cuser_role = get_cuser_role(request=request) + + customer = ObjectValidationById(object_model=CustomerModel, request=request).is_valid(int(customer_id)) + lead_type = ObjectValidationById(object_model=LeadTypeModel, request=request).is_valid(lead_type_id) + lead_step = ObjectValidationById(object_model=LeadStepModel, request=request).is_valid(lead_step_id) + lead_source = ObjectValidationById(object_model=LeadSourceModel, request=request).is_valid(lead_source_id) + 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 = LeadModel.objects.get(id=lead_id, sys_ws=sys_ws, cuser_role=cuser_role) + lead.title = title + lead.customer = customer + lead.desc = desc + lead.type = lead_type + lead.step = lead_step + lead.source = lead_source + lead.product_type = product_type + lead.city = city + + lead.save() + + return lead + + except Exception as exp: + message = f'Error Update Lead: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def update_lead_status(request, lead_id: int, status: str) -> LeadModel: + try: + sys_ws = get_user_workspace(request=request) + cuser_role = get_cuser_role(request=request) + + lead = LeadModel.objects.get(id=lead_id, sys_ws=sys_ws, cuser_role=cuser_role) + lead.status = status + + lead.save() + + return lead + + except Exception as exp: + message = f'Error Update Lead Status: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def update_lead_comment(request, lead_comment_id: int, desc: str) -> LeadModel: + try: + sys_ws = get_user_workspace(request=request) + cuser_role = get_cuser_role(request=request) + + lead_comment = LeadCommentModel.objects.get(id=lead_comment_id, sys_ws=sys_ws, cuser_role=cuser_role) + lead_comment.desc = desc + + lead_comment.save() + + return lead_comment + + except Exception as exp: + message = f'Error Update Lead_comment: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def create_lead_comment(request, lead_id, desc: str) -> LeadCommentModel: + try: + sys_ws = get_user_workspace(request=request) + cuser_role = get_cuser_role(request=request) + + lead_comment = LeadCommentModel() + lead_comment.lead = LeadModel.objects.get(id=lead_id, sys_ws=sys_ws, cuser_role=cuser_role) + lead_comment.sys_ws = sys_ws + lead_comment.cuser_role = cuser_role + lead_comment.desc = desc + lead_comment.save() + + return lead_comment + + except Exception as exp: + message = f'Error Create Lead comment: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def delete_lead_comment(request, lead_comment_id: int) -> LeadModel: + try: + sys_ws = get_user_workspace(request=request) + cuser_role = get_cuser_role(request=request) + + lead_comment = LeadCommentModel.objects.get(id=lead_comment_id, sys_ws=sys_ws, cuser_role=cuser_role) + lead_comment.delete() + + return lead_comment + + except Exception as exp: + message = f'Error Delete Lead_comment: {exp}' + raise exceptions.ValidationError({'error': message}) + + diff --git a/apps/lead/tests.py b/apps/lead/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/lead/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/lead/urls.py b/apps/lead/urls.py new file mode 100644 index 0000000..391a10f --- /dev/null +++ b/apps/lead/urls.py @@ -0,0 +1,17 @@ +from django.urls import path, include +from apps.lead.views import * + +urlpatterns = [ + path('form-items/', LeadFormItemsApi.as_view(), name='lead-form-items'), + path('new/', LeadCreateApi.as_view(), name='lead-new'), + path('list/', LeadListApi.as_view(), name='lead-list'), + path('/', LeadRetrieveApi.as_view(), name='lead-id'), + path('/edit-status/', LeadUpdateStatusApi.as_view(), name='lead-edit-status'), + path('/edit/', LeadUpdateApi.as_view(), name='lead-edit'), + path('/comment/add/', LeadCommentCreateApi.as_view(), name='lead-comment-add'), + path('comment//edit/', LeadCommentUpdateApi.as_view(), name='lead-comment-edit'), + path('comment//delete/', LeadCommentDeleteApi.as_view(), name='lead-comment-delete'), + + path('summary/', LeadSummaryApi.as_view(), name='marketing-summary'), + +] \ No newline at end of file diff --git a/apps/lead/views.py b/apps/lead/views.py new file mode 100644 index 0000000..ec33d90 --- /dev/null +++ b/apps/lead/views.py @@ -0,0 +1,318 @@ +from django.shortcuts import render +from rest_framework.views import APIView +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 +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 +from apps.lead.models import Lead as LeadModel +from rest_framework import status +from apps.lead.selectors import list_leads, retrieve_lead, retrieve_summary_lead +from rest_framework.permissions import IsAuthenticated +from utils import custom_permissions +from utils.tools import inline_serializer +from utils.pagination import CustomPagination +from django_filters.rest_framework import DjangoFilterBackend +# Create your views here. + + +class LeadFormItemsApi(APIView): + + class OutputSerializer(serializers.Serializer): + lead_types = LeadTypeSerializers(many=True) + product_types = ProductTypeSerializers(many=True) + lead_source = LeadSourceSerializers(many=True) + lead_steps = LeadStepSerializers(many=True) + shahr = ShahrSerializers(many=True) + + def get(self, request): + + fields_value = fields_new_lead_form(request) + serializer = self.OutputSerializer(fields_value) + + return Response(data=serializer.data) + + +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) + lead_type_id = serializers.IntegerField(required=True) + product_type_id = serializers.IntegerField(required=True) + lead_source_id = serializers.IntegerField(required=True) + lead_step_id = serializers.IntegerField(required=True) + city_id = serializers.IntegerField(required=True) + + class OutputSerializer(serializers.Serializer): + id = serializers.IntegerField(required=True) + + def post(self, request): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + new_lead = create_new_lead(request=request, **serializer.validated_data) + + output = self.OutputSerializer(new_lead) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class LeadUpdateApi(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) + lead_type_id = serializers.IntegerField(required=True) + product_type_id = serializers.IntegerField(required=True) + lead_source_id = serializers.IntegerField(required=True) + lead_step_id = serializers.IntegerField(required=True) + city_id = serializers.IntegerField(required=True) + + class OutputSerializer(serializers.Serializer): + id = serializers.IntegerField(required=True) + + def patch(self, request, lead_id): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + lead = update_lead(request=request, lead_id=lead_id, **serializer.validated_data) + + output = self.OutputSerializer(lead) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class LeadUpdateStatusApi(APIView): + + class InputSerializer(serializers.Serializer): + status = serializers.CharField(max_length=2, required=True) + + class OutputSerializer(serializers.Serializer): + id = serializers.IntegerField(required=True) + + def patch(self, request, lead_id): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + lead = update_lead_status(request=request, lead_id=lead_id, **serializer.validated_data) + + output = self.OutputSerializer(lead) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class LeadListApi(ListAPIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class Pagination(CustomPagination): + page_size = 10 + page_size_query_param = 'page_size' + max_page_size = 20 + + class OutputSerializer(serializers.ModelSerializer): + + customer = inline_serializer(many=False, fields={'name_family': serializers.CharField(), }) + + product_type = inline_serializer(many=False, fields={'name': serializers.CharField(), }) + + type = inline_serializer(many=False, fields={'title': serializers.CharField(), }) + + source = inline_serializer(many=False, fields={'title': serializers.CharField(), }) + + step = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField(), + }) + + city = inline_serializer(many=False, fields={ + 'name': serializers.CharField(), + 'ostan': inline_serializer(many=False, fields={'name': serializers.CharField() }) + }) + + class Meta: + model = LeadModel + fields = '__all__' + + serializer_class = OutputSerializer + pagination_class = Pagination + + filter_backends = [DjangoFilterBackend] + filterset_fields = ['step', 'status', ] + + def get_queryset(self): + return list_leads(request=self.request) + + +class LeadRetrieveApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class OutputSerializer(serializers.ModelSerializer): + + customer = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'name_family': serializers.CharField(), + }) + + product_type = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField(), + }) + + type = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'title': serializers.CharField(), + }) + + source = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'title': serializers.CharField(), + }) + + step = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField(), + }) + + city = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField(), + 'ostan': inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'name': serializers.CharField() + }) + }) + + lead_comment = inline_serializer(many=True, fields={ + 'id': serializers.IntegerField(), + 'created_at': serializers.DateTimeField(), + 'desc': serializers.CharField() + }) + + lead_reminder = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'created_at': serializers.DateTimeField(), + 'datetime': serializers.DateTimeField(), + 'desc': serializers.CharField() + }) + + class Meta: + model = LeadModel + fields = '__all__' + + serializer_class = OutputSerializer + + def get(self, request, lead_id): + + lead = retrieve_lead(request=request, id=lead_id) + output = self.OutputSerializer(lead, many=False) + + return Response(data=output.data) + + +class LeadCommentCreateApi(APIView): + + class InputSerializer(serializers.Serializer): + desc = serializers.CharField(max_length=200, required=True) + + class OutputSerializer(serializers.Serializer): + id = serializers.IntegerField(required=True) + + def post(self, request, lead_id): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + lead = create_lead_comment(request=request, lead_id=lead_id, **serializer.validated_data) + + output = self.OutputSerializer(lead) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class LeadCommentUpdateApi(APIView): + + class InputSerializer(serializers.Serializer): + desc = serializers.CharField(max_length=200, required=True) + + class OutputSerializer(serializers.Serializer): + id = serializers.IntegerField(required=True) + + def patch(self, request, lead_comment_id): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + lead = update_lead_comment(request=request, lead_comment_id=lead_comment_id, **serializer.validated_data) + + output = self.OutputSerializer(lead) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class LeadCommentDeleteApi(APIView): + + class OutputSerializer(serializers.Serializer): + id = serializers.IntegerField(required=True) + + def delete(self, request, lead_comment_id): + + lead = delete_lead_comment(request=request, lead_comment_id=lead_comment_id) + + output = self.OutputSerializer(lead) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class LeadSummaryApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceOperatorPermission) + + class OutputSerializer(serializers.Serializer): + + mozakere = inline_serializer(many=True, fields={ + 'id': serializers.IntegerField(), + 'customer': inline_serializer(fields={ + 'id': serializers.IntegerField(), + 'name_family': serializers.CharField(), + }), + 'title': serializers.CharField(), + 'created_at': serializers.DateTimeField() + }) + + pishfactor = inline_serializer(many=True, fields={ + 'id': serializers.IntegerField(), + 'customer': inline_serializer(fields={ + 'id': serializers.IntegerField(), + 'name_family': serializers.CharField(), + }), + 'title': serializers.CharField(), + 'created_at': serializers.DateTimeField() + }) + + kharid = inline_serializer(many=True, fields={ + 'id': serializers.IntegerField(), + 'customer': inline_serializer(fields={ + 'id': serializers.IntegerField(), + 'name_family': serializers.CharField(), + }), + 'title': serializers.CharField(), + 'created_at': serializers.DateTimeField() + }) + + def get(self, request): + + leads = retrieve_summary_lead(request=request) + output = self.OutputSerializer(leads, many=False) + + return Response(data=output.data, status=status.HTTP_200_OK) diff --git a/apps/reminder/__init__.py b/apps/reminder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/reminder/admin.py b/apps/reminder/admin.py new file mode 100644 index 0000000..ce13ef7 --- /dev/null +++ b/apps/reminder/admin.py @@ -0,0 +1,11 @@ +from django.contrib import admin +from apps.reminder.models import * + +# Register your models here. + +# Register your models here. + + +@admin.register(Reminder) +class ReminderAdmin(admin.ModelAdmin): + pass diff --git a/apps/reminder/apps.py b/apps/reminder/apps.py new file mode 100644 index 0000000..758efdb --- /dev/null +++ b/apps/reminder/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ReminderConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.reminder' diff --git a/apps/reminder/migrations/0001_initial.py b/apps/reminder/migrations/0001_initial.py new file mode 100644 index 0000000..fef56ec --- /dev/null +++ b/apps/reminder/migrations/0001_initial.py @@ -0,0 +1,34 @@ +# Generated by Django 4.2.1 on 2024-01-13 10:31 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('workspace', '0003_alter_workspace_uuid'), + ('cuser', '0005_smsopt'), + ] + + operations = [ + migrations.CreateModel( + name='Reminder', + 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)), + ('type', models.CharField(choices=[('LE', 'LEAD'), ('UN', 'UNKNOWN'), ('NO', 'NOTE'), ('CU', 'CUSTOMER')], default='UN', max_length=2)), + ('id_type', models.PositiveBigIntegerField(blank=True, null=True)), + ('datetime', models.DateTimeField()), + ('desc', models.CharField(blank=True, max_length=200, null=True)), + ('cuser_role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cuser.cuserrole')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/reminder/migrations/0002_reminder_customer_alter_reminder_type.py b/apps/reminder/migrations/0002_reminder_customer_alter_reminder_type.py new file mode 100644 index 0000000..34e0a4c --- /dev/null +++ b/apps/reminder/migrations/0002_reminder_customer_alter_reminder_type.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.1 on 2024-01-13 10:35 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('customer', '0006_alter_customernote_options'), + ('reminder', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='reminder', + name='customer', + field=models.ForeignKey(default='', on_delete=django.db.models.deletion.CASCADE, related_name='customer_Reminder', to='customer.customer'), + preserve_default=False, + ), + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('UN', 'UNKNOWN'), ('LE', 'LEAD'), ('NO', 'NOTE'), ('CU', 'CUSTOMER')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0003_reminder_lead_reminder_note_alter_reminder_type.py b/apps/reminder/migrations/0003_reminder_lead_reminder_note_alter_reminder_type.py new file mode 100644 index 0000000..f887708 --- /dev/null +++ b/apps/reminder/migrations/0003_reminder_lead_reminder_note_alter_reminder_type.py @@ -0,0 +1,31 @@ +# Generated by Django 4.2.1 on 2024-01-13 15:42 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('customer', '0006_alter_customernote_options'), + ('lead', '0010_alter_lead_status'), + ('reminder', '0002_reminder_customer_alter_reminder_type'), + ] + + operations = [ + migrations.AddField( + model_name='reminder', + name='lead', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lead_reminder', to='lead.lead'), + ), + migrations.AddField( + model_name='reminder', + name='note', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='note_reminder', to='customer.customernote'), + ), + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('CU', 'CUSTOMER'), ('NO', 'NOTE'), ('LE', 'LEAD'), ('UN', 'UNKNOWN')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0004_alter_reminder_lead_alter_reminder_note_and_more.py b/apps/reminder/migrations/0004_alter_reminder_lead_alter_reminder_note_and_more.py new file mode 100644 index 0000000..c847f0c --- /dev/null +++ b/apps/reminder/migrations/0004_alter_reminder_lead_alter_reminder_note_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 4.2.1 on 2024-01-13 15:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('lead', '0011_alter_lead_status'), + ('customer', '0006_alter_customernote_options'), + ('reminder', '0003_reminder_lead_reminder_note_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='lead', + field=models.OneToOneField(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lead_reminder', to='lead.lead'), + ), + migrations.AlterField( + model_name='reminder', + name='note', + field=models.OneToOneField(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='note_reminder', to='customer.customernote'), + ), + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('CU', 'CUSTOMER'), ('LE', 'LEAD'), ('UN', 'UNKNOWN'), ('NO', 'NOTE')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0005_alter_reminder_options_alter_reminder_type.py b/apps/reminder/migrations/0005_alter_reminder_options_alter_reminder_type.py new file mode 100644 index 0000000..db912e4 --- /dev/null +++ b/apps/reminder/migrations/0005_alter_reminder_options_alter_reminder_type.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.1 on 2024-01-18 08:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0004_alter_reminder_lead_alter_reminder_note_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='reminder', + options={'ordering': ('-created_at',)}, + ), + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('LE', 'LEAD'), ('NO', 'NOTE'), ('CU', 'CUSTOMER'), ('UN', 'UNKNOWN')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0006_alter_reminder_type.py b/apps/reminder/migrations/0006_alter_reminder_type.py new file mode 100644 index 0000000..bb1dcb6 --- /dev/null +++ b/apps/reminder/migrations/0006_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-20 07:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0005_alter_reminder_options_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('NO', 'NOTE'), ('CU', 'CUSTOMER'), ('UN', 'UNKNOWN'), ('LE', 'LEAD')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0007_alter_reminder_type.py b/apps/reminder/migrations/0007_alter_reminder_type.py new file mode 100644 index 0000000..d861168 --- /dev/null +++ b/apps/reminder/migrations/0007_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0006_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('CU', 'CUSTOMER'), ('UN', 'UNKNOWN'), ('NO', 'NOTE'), ('LE', 'LEAD')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0008_alter_reminder_type.py b/apps/reminder/migrations/0008_alter_reminder_type.py new file mode 100644 index 0000000..7e1f124 --- /dev/null +++ b/apps/reminder/migrations/0008_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0007_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('LE', 'LEAD'), ('NO', 'NOTE'), ('CU', 'CUSTOMER'), ('UN', 'UNKNOWN')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0009_alter_reminder_type.py b/apps/reminder/migrations/0009_alter_reminder_type.py new file mode 100644 index 0000000..66826bb --- /dev/null +++ b/apps/reminder/migrations/0009_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0008_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('NO', 'NOTE'), ('UN', 'UNKNOWN'), ('CU', 'CUSTOMER'), ('LE', 'LEAD')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0010_alter_reminder_type.py b/apps/reminder/migrations/0010_alter_reminder_type.py new file mode 100644 index 0000000..f009dee --- /dev/null +++ b/apps/reminder/migrations/0010_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 08:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0009_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('CU', 'CUSTOMER'), ('UN', 'UNKNOWN'), ('NO', 'NOTE'), ('LE', 'LEAD')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0011_alter_reminder_type.py b/apps/reminder/migrations/0011_alter_reminder_type.py new file mode 100644 index 0000000..441d810 --- /dev/null +++ b/apps/reminder/migrations/0011_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 09:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0010_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('CU', 'CUSTOMER'), ('NO', 'NOTE'), ('LE', 'LEAD'), ('UN', 'UNKNOWN')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0012_alter_reminder_type.py b/apps/reminder/migrations/0012_alter_reminder_type.py new file mode 100644 index 0000000..904a100 --- /dev/null +++ b/apps/reminder/migrations/0012_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 09:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0011_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('CU', 'CUSTOMER'), ('LE', 'LEAD'), ('NO', 'NOTE'), ('UN', 'UNKNOWN')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0013_alter_reminder_type.py b/apps/reminder/migrations/0013_alter_reminder_type.py new file mode 100644 index 0000000..8b006b4 --- /dev/null +++ b/apps/reminder/migrations/0013_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0012_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('UN', 'UNKNOWN'), ('LE', 'LEAD'), ('NO', 'NOTE'), ('CU', 'CUSTOMER')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0014_alter_reminder_type.py b/apps/reminder/migrations/0014_alter_reminder_type.py new file mode 100644 index 0000000..b6c657b --- /dev/null +++ b/apps/reminder/migrations/0014_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0013_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('NO', 'NOTE'), ('LE', 'LEAD'), ('CU', 'CUSTOMER'), ('UN', 'UNKNOWN')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0015_alter_reminder_type.py b/apps/reminder/migrations/0015_alter_reminder_type.py new file mode 100644 index 0000000..fb7488c --- /dev/null +++ b/apps/reminder/migrations/0015_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0014_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('CU', 'CUSTOMER'), ('NO', 'NOTE'), ('UN', 'UNKNOWN'), ('LE', 'LEAD')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/0016_alter_reminder_type.py b/apps/reminder/migrations/0016_alter_reminder_type.py new file mode 100644 index 0000000..7f1b855 --- /dev/null +++ b/apps/reminder/migrations/0016_alter_reminder_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reminder', '0015_alter_reminder_type'), + ] + + operations = [ + migrations.AlterField( + model_name='reminder', + name='type', + field=models.CharField(choices=[('NO', 'NOTE'), ('LE', 'LEAD'), ('CU', 'CUSTOMER'), ('UN', 'UNKNOWN')], default='UN', max_length=2), + ), + ] diff --git a/apps/reminder/migrations/__init__.py b/apps/reminder/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/reminder/models.py b/apps/reminder/models.py new file mode 100644 index 0000000..b6c1894 --- /dev/null +++ b/apps/reminder/models.py @@ -0,0 +1,31 @@ +from django.db import models +from utils.base_model import BaseModel +from apps.workspace.models import Workspace as WorkspaceModel +from apps.cuser.models import CuserRole as CuserRoleModel +from apps.customer.models import Customer as CustomerModel +from apps.lead.models import Lead as LeadModel +from apps.customer.models import CustomerNote as CustomerNoteModel +# Create your models here. + + +class Reminder(BaseModel): + + TYPE_CHOICES = { + ("LE", "LEAD"), + ("CU", "CUSTOMER"), + ("NO", "NOTE"), + ("UN", "UNKNOWN"), + } + + type = models.CharField(max_length=2, choices=TYPE_CHOICES, null=False, blank=False, default='UN') + customer = models.ForeignKey(CustomerModel, on_delete=models.CASCADE, null=False, blank=False, related_name='customer_Reminder') + lead = models.OneToOneField(LeadModel, on_delete=models.CASCADE, null=True, blank=True, default=None, related_name='lead_reminder') + note = models.OneToOneField(CustomerNoteModel, on_delete=models.CASCADE, null=True, blank=True, default=None, related_name='note_reminder') + id_type = models.PositiveBigIntegerField(null=True, blank=True) + datetime = models.DateTimeField(null=False, blank=False) + desc = models.CharField(max_length=200, null=True, blank=True) + cuser_role = models.ForeignKey(CuserRoleModel, on_delete=models.SET_NULL, null=True, blank=True) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + class Meta: + ordering = ('-created_at', ) diff --git a/apps/reminder/selectors.py b/apps/reminder/selectors.py new file mode 100644 index 0000000..02783d3 --- /dev/null +++ b/apps/reminder/selectors.py @@ -0,0 +1,16 @@ +from apps.reminder.models import Reminder as ReminderModel +from utils.get_cuser_role import get_cuser_role_uuid +from utils.get_user_workspace import get_user_workspace_uuid +import datetime + + +def reminder_list(request) -> ReminderModel: + workspace_uuid = get_user_workspace_uuid(request) + cuser_role_uuid = get_cuser_role_uuid(request) + + nowdt = datetime.datetime.now() + + reminders = ReminderModel.objects.filter(datetime__lte=nowdt, + sys_ws__uuid=workspace_uuid, cuser_role__uuid=cuser_role_uuid).order_by('-datetime')[:30] + + return reminders diff --git a/apps/reminder/services.py b/apps/reminder/services.py new file mode 100644 index 0000000..cb3422b --- /dev/null +++ b/apps/reminder/services.py @@ -0,0 +1,48 @@ +from apps.reminder.models import Reminder as ReminderModel +from apps.lead.models import Lead as LeadModel +from rest_framework import exceptions +from utils.get_user_workspace import get_user_workspace +from utils.get_cuser_role import get_cuser_role + + +def create_or_edit_reminder_lead(request, lead_id: int, datetime: str, desc: str = None) -> ReminderModel: + try: + workspace = get_user_workspace(request) + cuser_role = get_cuser_role(request) + + lead = LeadModel.objects.get(id=lead_id) + customer = lead.customer + + try: + reminder = ReminderModel.objects.get(type='LE', lead=lead, customer=customer, sys_ws=workspace, cuser_role=cuser_role) + except Exception as exp: + reminder = ReminderModel(lead=lead) + + reminder.type = 'LE' + reminder.customer = customer + reminder.datetime = datetime + reminder.sys_ws = workspace + reminder.cuser_role = cuser_role + if desc: + reminder.desc = desc + reminder.save() + + return reminder + + except Exception as exp: + message = f'Error Creating or Update reminder for lead: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def delete_reminder_lead(request, lead_id: int) -> ReminderModel: + try: + workspace = get_user_workspace(request) + cuser_role = get_cuser_role(request) + + reminder = ReminderModel.objects.get(type='LE', lead_id=lead_id, sys_ws=workspace, cuser_role=cuser_role).delete() + + return reminder + + except Exception as exp: + message = f'Error Delete reminder for lead: {exp}' + raise exceptions.ValidationError({'error': message}) diff --git a/apps/reminder/tests.py b/apps/reminder/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/reminder/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/reminder/urls.py b/apps/reminder/urls.py new file mode 100644 index 0000000..210fe1b --- /dev/null +++ b/apps/reminder/urls.py @@ -0,0 +1,9 @@ +from django.urls import path, include +from apps.reminder.views import * + +urlpatterns = [ + path('list/', ReminderListApiView.as_view(), name='reminder-list'), + path('lead/create-update/', ReminderLeadCreateUpdateApiView.as_view(), name='reminder-lead-create-update'), + path('lead//delete/', ReminderLeadDeleteApiView.as_view(), name='reminder-lead-delete'), + +] \ No newline at end of file diff --git a/apps/reminder/views.py b/apps/reminder/views.py new file mode 100644 index 0000000..80a1a0b --- /dev/null +++ b/apps/reminder/views.py @@ -0,0 +1,66 @@ +from django.shortcuts import render +from rest_framework.views import APIView +from rest_framework import serializers +from rest_framework.response import Response +from rest_framework import status +from apps.reminder.models import Reminder as ReminderModel +from apps.reminder.services import create_or_edit_reminder_lead, delete_reminder_lead +from utils.tools import inline_serializer +from apps.reminder.selectors import reminder_list +# Create your views here. + + +class ReminderListApiView(APIView): + + class OutputSerializer(serializers.ModelSerializer): + + customer = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'name_family': serializers.CharField(), + }) + + lead = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'title': serializers.CharField(), + }) + + note = inline_serializer(many=False, fields={ + 'id': serializers.IntegerField(), + 'desc': serializers.CharField(), + }) + + class Meta: + model = ReminderModel + fields = '__all__' + + def get(self, request): + reminders = reminder_list(request=request) + output = self.OutputSerializer(reminders, many=True) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class ReminderLeadCreateUpdateApiView(APIView): + + class InputSerializers(serializers.Serializer): + lead_id = serializers.IntegerField(required=True) + datetime = serializers.DateTimeField(required=True) + desc = serializers.CharField(required=False, allow_null=True) + + def post(self, request): + serializer = self.InputSerializers(data=request.data) + serializer.is_valid(raise_exception=True) + + output = create_or_edit_reminder_lead(request, **serializer.validated_data) + + return Response(data={'id': output.id}, status=status.HTTP_201_CREATED) + + +class ReminderLeadDeleteApiView(APIView): + + def delete(self, request, lead_id): + output = delete_reminder_lead(request, lead_id=lead_id) + + return Response(data={'status': 'removed'}, status=status.HTTP_200_OK) + + diff --git a/apps/shop/__init__.py b/apps/shop/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/shop/admin.py b/apps/shop/admin.py new file mode 100644 index 0000000..0c8095c --- /dev/null +++ b/apps/shop/admin.py @@ -0,0 +1,40 @@ +from django.contrib import admin +from apps.shop.models import ( + ProductCategory as ProductCategoryModel, + Product as ProductModel, + Order as OrderModel, + OrderItem as OrderItemModel, + Cart as CartModel, + ProductType as ProductTypeModel +) +# Register your models here. + + +@admin.register(ProductTypeModel) +class ProductTypeAdmin(admin.ModelAdmin): + pass + + +@admin.register(ProductCategoryModel) +class ProductCategoryAdmin(admin.ModelAdmin): + pass + + +@admin.register(ProductModel) +class ProductAdmin(admin.ModelAdmin): + pass + + +@admin.register(OrderModel) +class OrderAdmin(admin.ModelAdmin): + pass + + +@admin.register(OrderItemModel) +class OrderItemAdmin(admin.ModelAdmin): + pass + + +@admin.register(CartModel) +class CartAdmin(admin.ModelAdmin): + pass diff --git a/apps/shop/apps.py b/apps/shop/apps.py new file mode 100644 index 0000000..3f70df4 --- /dev/null +++ b/apps/shop/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ShopConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.shop' diff --git a/apps/shop/migrations/0001_initial.py b/apps/shop/migrations/0001_initial.py new file mode 100644 index 0000000..2eb5e55 --- /dev/null +++ b/apps/shop/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.1 on 2023-08-07 12:00 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('workspace', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='ProductCategory', + 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=30)), + ('parent', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='shop.productcategory')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/shop/migrations/0002_alter_productcategory_parent.py b/apps/shop/migrations/0002_alter_productcategory_parent.py new file mode 100644 index 0000000..854a747 --- /dev/null +++ b/apps/shop/migrations/0002_alter_productcategory_parent.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-07 12:03 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='productcategory', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='shop.productcategory'), + ), + ] diff --git a/apps/shop/migrations/0003_order_product_orderitem.py b/apps/shop/migrations/0003_order_product_orderitem.py new file mode 100644 index 0000000..5d1e500 --- /dev/null +++ b/apps/shop/migrations/0003_order_product_orderitem.py @@ -0,0 +1,64 @@ +# Generated by Django 4.2.1 on 2023-08-08 08:37 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('customer', '0001_initial'), + ('workspace', '0002_city'), + ('shop', '0002_alter_productcategory_parent'), + ] + + operations = [ + migrations.CreateModel( + name='Order', + 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)), + ('total_amount', models.DecimalField(decimal_places=2, max_digits=12)), + ('factor_type', models.PositiveSmallIntegerField(choices=[(1, 'pish_factor'), (2, 'factor')])), + ('factor_status', models.PositiveSmallIntegerField(blank=True, choices=[(1, 'unknown'), (2, 'ok'), (3, 'cancel')], null=True)), + ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='customer.customer')), + ('responsible_group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='customer.responsiblegroup')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Product', + 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=150)), + ('price', models.DecimalField(decimal_places=2, max_digits=12)), + ('acc_code', models.PositiveBigIntegerField(blank=True, null=True)), + ('attr', models.JSONField(blank=True, null=True)), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.productcategory')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='OrderItem', + 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)), + ('price', models.DecimalField(decimal_places=2, max_digits=12)), + ('amount', models.PositiveIntegerField()), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.order')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.product')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/shop/migrations/0004_alter_product_acc_code.py b/apps/shop/migrations/0004_alter_product_acc_code.py new file mode 100644 index 0000000..cd91ea9 --- /dev/null +++ b/apps/shop/migrations/0004_alter_product_acc_code.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2023-08-13 05:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0003_order_product_orderitem'), + ] + + operations = [ + migrations.AlterField( + model_name='product', + name='acc_code', + field=models.CharField(blank=True, null=True), + ), + ] diff --git a/apps/shop/migrations/0005_product_sys_ws.py b/apps/shop/migrations/0005_product_sys_ws.py new file mode 100644 index 0000000..7be1078 --- /dev/null +++ b/apps/shop/migrations/0005_product_sys_ws.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.1 on 2023-08-13 05:52 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('workspace', '0002_city'), + ('shop', '0004_alter_product_acc_code'), + ] + + operations = [ + migrations.AddField( + model_name='product', + name='sys_ws', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace'), + ), + ] diff --git a/apps/shop/migrations/0006_alter_product_sys_ws.py b/apps/shop/migrations/0006_alter_product_sys_ws.py new file mode 100644 index 0000000..6560867 --- /dev/null +++ b/apps/shop/migrations/0006_alter_product_sys_ws.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.1 on 2023-08-13 05:55 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('workspace', '0002_city'), + ('shop', '0005_product_sys_ws'), + ] + + operations = [ + migrations.AlterField( + model_name='product', + name='sys_ws', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace'), + ), + ] diff --git a/apps/shop/migrations/0007_rename_total_amount_order_total_price_and_more.py b/apps/shop/migrations/0007_rename_total_amount_order_total_price_and_more.py new file mode 100644 index 0000000..40edd8f --- /dev/null +++ b/apps/shop/migrations/0007_rename_total_amount_order_total_price_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.1 on 2023-08-14 05:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0006_alter_product_sys_ws'), + ] + + operations = [ + migrations.RenameField( + model_name='order', + old_name='total_amount', + new_name='total_price', + ), + migrations.AddField( + model_name='orderitem', + name='product_name', + field=models.CharField(blank=True, max_length=150, null=True), + ), + ] diff --git a/apps/shop/migrations/0008_alter_orderitem_product_name.py b/apps/shop/migrations/0008_alter_orderitem_product_name.py new file mode 100644 index 0000000..1440745 --- /dev/null +++ b/apps/shop/migrations/0008_alter_orderitem_product_name.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2023-08-14 05:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0007_rename_total_amount_order_total_price_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='orderitem', + name='product_name', + field=models.CharField(max_length=150), + ), + ] diff --git a/apps/shop/migrations/0009_cart.py b/apps/shop/migrations/0009_cart.py new file mode 100644 index 0000000..8a0e88a --- /dev/null +++ b/apps/shop/migrations/0009_cart.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.1 on 2023-10-22 06:55 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0005_smsopt'), + ('shop', '0008_alter_orderitem_product_name'), + ] + + operations = [ + migrations.CreateModel( + name='Cart', + 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)), + ('quantity', models.IntegerField()), + ('cuser_role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cuser.cuserrole')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.product')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/shop/migrations/0010_cart_sys_ws.py b/apps/shop/migrations/0010_cart_sys_ws.py new file mode 100644 index 0000000..c332324 --- /dev/null +++ b/apps/shop/migrations/0010_cart_sys_ws.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.1 on 2023-10-22 07:00 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('workspace', '0003_alter_workspace_uuid'), + ('shop', '0009_cart'), + ] + + operations = [ + migrations.AddField( + model_name='cart', + name='sys_ws', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace'), + preserve_default=False, + ), + ] diff --git a/apps/shop/migrations/0011_producttype.py b/apps/shop/migrations/0011_producttype.py new file mode 100644 index 0000000..cdb41be --- /dev/null +++ b/apps/shop/migrations/0011_producttype.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.1 on 2024-01-01 11:05 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('workspace', '0003_alter_workspace_uuid'), + ('shop', '0010_cart_sys_ws'), + ] + + operations = [ + migrations.CreateModel( + name='ProductType', + 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=100)), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/shop/migrations/0012_productcategory_image.py b/apps/shop/migrations/0012_productcategory_image.py new file mode 100644 index 0000000..e85aeaf --- /dev/null +++ b/apps/shop/migrations/0012_productcategory_image.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-18 08:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0011_producttype'), + ] + + operations = [ + migrations.AddField( + model_name='productcategory', + name='image', + field=models.ImageField(blank=True, default=None, null=True, upload_to=''), + ), + ] diff --git a/apps/shop/migrations/0013_alter_productcategory_image.py b/apps/shop/migrations/0013_alter_productcategory_image.py new file mode 100644 index 0000000..a35b109 --- /dev/null +++ b/apps/shop/migrations/0013_alter_productcategory_image.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-20 07:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0012_productcategory_image'), + ] + + operations = [ + migrations.AlterField( + model_name='productcategory', + name='image', + field=models.ImageField(blank=True, default=None, null=True, upload_to='product-cat/'), + ), + ] diff --git a/apps/shop/migrations/__init__.py b/apps/shop/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/shop/models.py b/apps/shop/models.py new file mode 100644 index 0000000..cf90ff7 --- /dev/null +++ b/apps/shop/models.py @@ -0,0 +1,86 @@ +from django.db import models +from utils.base_model import BaseModel +from apps.workspace.models import Workspace as WorkspaceModel +from apps.cuser.models import CuserRole as CuserRoleModel +from apps.customer.models import ResponsibleGroup as ResponsibleGroupModel, Customer as CustomerModel +# Create your models here. + + +class ProductType(BaseModel): + name = models.CharField(max_length=100) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return f'{self.name}' + + +class ProductCategory(BaseModel): + name = models.CharField(max_length=30, null=False, blank=False) + parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True) + image = models.ImageField(null=True, blank=True, default=None, upload_to='product-cat/') + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return f'{self.name}' + + +class Product(BaseModel): + name = models.CharField(max_length=150, null=False, blank=False) + price = models.DecimalField(max_digits=12, decimal_places=2, null=False, blank=False) + category = models.ForeignKey(ProductCategory, on_delete=models.CASCADE, null=False, blank=False) + acc_code = models.CharField(null=True, blank=True) + attr = models.JSONField(null=True, blank=True) + + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return f'{self.name}' + + +class Cart(BaseModel): + cuser_role = models.ForeignKey(CuserRoleModel, on_delete=models.CASCADE, null=False, blank=False) + product = models.ForeignKey(Product, on_delete=models.CASCADE, null=False, blank=False) + quantity = models.IntegerField(null=False, blank=False) + + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return f'{self.cuser_role}' + + +class Order(BaseModel): + + FACTOR_TYPE_CHOICE = [ + (1, 'pish_factor'), + (2, 'factor'), + ] + + FACTOR_STATUS_CHOICE = [ + (1, 'unknown'), + (2, 'ok'), + (3, 'cancel'), + ] + + responsible_group = models.ForeignKey(ResponsibleGroupModel, on_delete=models.SET_NULL, null=True, blank=True) + customer = models.ForeignKey(CustomerModel, on_delete=models.CASCADE, null=False, blank=False) + total_price = models.DecimalField(max_digits=12, decimal_places=2, null=False, blank=False) + factor_type = models.PositiveSmallIntegerField(choices=FACTOR_TYPE_CHOICE, null=False, blank=False) + factor_status = models.PositiveSmallIntegerField(choices=FACTOR_STATUS_CHOICE, null=True, blank=True) + sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return f'{self.customer}' + + +class OrderItem(BaseModel): + order = models.ForeignKey(Order, on_delete=models.CASCADE, null=False, blank=False) + product = models.ForeignKey(Product, on_delete=models.CASCADE, null=False, blank=False) + product_name = models.CharField(max_length=150, null=False, blank=False) + price = models.DecimalField(max_digits=12, decimal_places=2, null=False, blank=False) + amount = models.PositiveIntegerField(null=False, blank=False) + + def __str__(self): + return f'{self.order} {self.order.id}' + + + diff --git a/apps/shop/selectors/__init__.py b/apps/shop/selectors/__init__.py new file mode 100644 index 0000000..d20c453 --- /dev/null +++ b/apps/shop/selectors/__init__.py @@ -0,0 +1,5 @@ +from .product_category import * +from .product import * +from .order import * +from .cart import * +from .order_item import * diff --git a/apps/shop/selectors/cart.py b/apps/shop/selectors/cart.py new file mode 100644 index 0000000..8fa69f8 --- /dev/null +++ b/apps/shop/selectors/cart.py @@ -0,0 +1,9 @@ +from apps.shop.models import Cart as CartModel + + +def list_cart(cuser_role_uuid: str, workspace_uuid: str) -> CartModel: + return CartModel.objects.filter(sys_ws__uuid=workspace_uuid, cuser_role__uuid=cuser_role_uuid) + + +def clean_cart(cuser_role_uuid: str, workspace_uuid: str) -> CartModel: + return CartModel.objects.filter(sys_ws__uuid=workspace_uuid, cuser_role__uuid=cuser_role_uuid).delete() diff --git a/apps/shop/selectors/order.py b/apps/shop/selectors/order.py new file mode 100644 index 0000000..26d5108 --- /dev/null +++ b/apps/shop/selectors/order.py @@ -0,0 +1,46 @@ +from apps.shop.models import Order as OrderModel +from apps.customer.models import CustomerResponsible as CustomerResponsibleModel +from apps.cuser.models import CuserRole as CuserRoleModel +from rest_framework import exceptions +from utils.custom_permissions import workspace_admin_up +from apps.customer.models import Customer as CustomerModel + + +def retrieve_order(cuser_role_uuid: str, order_id: int, workspace_uuid: str) -> OrderModel: + + try: + cuser_role = CuserRoleModel.objects.select_related('role_type').get + role_type = cuser_role.role_type + + if role_type.role_name_en == 'workspace_customer': + customer = CustomerModel.objects.get(cuser_role=cuser_role) + order_obj = OrderModel.objects.get(id=order_id, sys_ws__uuid=workspace_uuid, customer=customer) + return order_obj + + if role_type.role_name_en in ('workspace_owner', 'workspace_operator', 'workspace_admin'): + order_obj = OrderModel.objects.get(id=order_id, sys_ws__uuid=workspace_uuid) + return order_obj + + except Exception as exp: + message = f'Error Retrieve Order: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def list_order(cuser_role_uuid: str, workspace_uuid: str) -> OrderModel: + try: + + cuser_role = CuserRoleModel.objects.select_related('role_type').get(uuid=cuser_role_uuid) + role_type = cuser_role.role_type + + if role_type.role_name_en == 'workspace_customer': + customer = CustomerModel.objects.get(cuser_role=cuser_role) + orders_obj = OrderModel.objects.filter(sys_ws__uuid=workspace_uuid, customer=customer).order_by('-created_at') + return orders_obj + + if role_type.role_name_en in ('workspace_owner', 'workspace_operator', 'workspace_admin'): + orders_obj = OrderModel.objects.filter(sys_ws__uuid=workspace_uuid).order_by('-created_at') + return orders_obj + + except Exception as exp: + message = f'Error Fetching List Order: {exp}' + raise exceptions.ValidationError({'error': message}) diff --git a/apps/shop/selectors/order_item.py b/apps/shop/selectors/order_item.py new file mode 100644 index 0000000..27a23ea --- /dev/null +++ b/apps/shop/selectors/order_item.py @@ -0,0 +1,19 @@ +from apps.shop.models import OrderItem as OrderItemModel +from apps.cuser.models import CuserRole as CuserRoleModel +from rest_framework import exceptions + + +def list_order_item(order_id: int, cuser_role_uuid: str) -> OrderItemModel: + try: + + cuser_role_uuid_obj = CuserRoleModel.objects.get(uuid=cuser_role_uuid) + + order_items_obj = OrderItemModel.objects.filter( + order_id=order_id, + order__customer__cuser_role_id=cuser_role_uuid_obj.id).order_by('-created_at') + + return order_items_obj + + except Exception as exp: + message = f'Error Fetching List Order items: {exp}' + raise exceptions.ValidationError({'error': message}) diff --git a/apps/shop/selectors/product.py b/apps/shop/selectors/product.py new file mode 100644 index 0000000..87f442f --- /dev/null +++ b/apps/shop/selectors/product.py @@ -0,0 +1,38 @@ +from apps.shop.models import Product as ProductModel +from rest_framework import exceptions + + +def retrieve_product(product_id: int, workspace_uuid: str) -> ProductModel: + try: + product_obj = ProductModel.objects.get(id=product_id, sys_ws__uuid=workspace_uuid) + return product_obj + except Exception as exp: + message = f'Error Retrieve Product: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def list_product(workspace_uuid: str) -> ProductModel: + try: + product_category_obj = ProductModel.objects.filter(sys_ws__uuid=workspace_uuid) + return product_category_obj + except Exception as exp: + message = f'Error Fetching List Product: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def list_product_by_category(workspace_uuid: str, category_id: int) -> ProductModel: + try: + product_category_obj = ProductModel.objects.filter(sys_ws__uuid=workspace_uuid, category_id=category_id) + return product_category_obj + except Exception as exp: + message = f'Error Fetching List Product by category: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def count_product_in_category(workspace_uuid: str, category_id: int) -> int: + try: + count_product = ProductModel.objects.filter(sys_ws__uuid=workspace_uuid, category_id=category_id).count() + return count_product + except Exception as exp: + message = f'Error Couting Product in category: {exp}' + raise exceptions.ValidationError({'error': message}) \ No newline at end of file diff --git a/apps/shop/selectors/product_category.py b/apps/shop/selectors/product_category.py new file mode 100644 index 0000000..f78ecdc --- /dev/null +++ b/apps/shop/selectors/product_category.py @@ -0,0 +1,20 @@ +from apps.shop.models import ProductCategory as ProductCategoryModel +from rest_framework import exceptions + + +def retrieve_product_category(category_id: int, workspace_uuid: str) -> ProductCategoryModel: + try: + product_category_obj = ProductCategoryModel.objects.get(id=category_id, sys_ws__uuid=workspace_uuid) + return product_category_obj + except Exception as exp: + message = f'Error Retrieve ProductCategory: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def list_product_category(workspace_uuid: str) -> ProductCategoryModel: + try: + product_category_obj = ProductCategoryModel.objects.filter(sys_ws__uuid=workspace_uuid) + return product_category_obj + except Exception as exp: + message = f'Error Fetching List ProductCategory: {exp}' + raise exceptions.ValidationError({'error': message}) \ No newline at end of file diff --git a/apps/shop/services/__init__.py b/apps/shop/services/__init__.py new file mode 100644 index 0000000..4b326c5 --- /dev/null +++ b/apps/shop/services/__init__.py @@ -0,0 +1,5 @@ +from .product_category import * +from .product import * +from .order import * +from .order_item import * +from .cart import * diff --git a/apps/shop/services/cart.py b/apps/shop/services/cart.py new file mode 100644 index 0000000..274edbe --- /dev/null +++ b/apps/shop/services/cart.py @@ -0,0 +1,36 @@ +from rest_framework import exceptions +from decimal import Decimal +from django.db import transaction +from apps.shop.models import Cart as CartModel + + +def add_to_cart(product_id: int, quantity: int, workspace, cuser_role) -> CartModel: + try: + + try: + cart_obj = CartModel.objects.get(product_id=product_id, sys_ws=workspace, cuser_role=cuser_role) + except Exception as exp: + cart_obj = CartModel() + + cart_obj.product_id = product_id + cart_obj.quantity = quantity + cart_obj.sys_ws = workspace + cart_obj.cuser_role = cuser_role + + cart_obj.full_clean() + cart_obj.save() + + return cart_obj + + except Exception as exp: + message = f'Error Add to Cart: {exp}' + exceptions.ValidationError({'error': message}) + + +def delete_cart_iteam(cart_id: int, cuser_role_uuid, workspace_uuid: str) -> CartModel: + try: + cart_obj = CartModel.objects.get(id=cart_id, cuser_role__uuid=cuser_role_uuid, sys_ws__uuid=workspace_uuid).delete() + return cart_obj + except Exception as exp: + message = f'Error Deleting Cart iteam: {exp}' + raise exceptions.ValidationError({'error': message}) \ No newline at end of file diff --git a/apps/shop/services/order.py b/apps/shop/services/order.py new file mode 100644 index 0000000..359e0de --- /dev/null +++ b/apps/shop/services/order.py @@ -0,0 +1,111 @@ +import json +from apps.shop.models import ( + Order as OrderModel, + OrderItem as OrderItemModel, + Product as ProductModel, +) +from apps.customer.models import Customer as CustomerModel +from rest_framework import exceptions +from decimal import Decimal +from django.db import transaction + + +def create_order(responsible_group_id: int, customer_id: int, + total_amount: Decimal, factor_type: int, factor_status: int, workspace) -> OrderModel: + try: + order_obj = OrderModel() + order_obj.responsible_group_id = responsible_group_id + order_obj.customer_id = customer_id + order_obj.total_amount = total_amount + order_obj.factor_type = factor_type + order_obj.factor_status = factor_status + order_obj.sys_ws = workspace + + order_obj.full_clean() + order_obj.save() + + return order_obj + + except Exception as exp: + message = f'Error Create Order: {exp}' + exceptions.ValidationError({'error': message}) + + +def create_order_and_items(items: str, customer: CustomerModel, workspace) -> OrderModel: + with transaction.atomic(): + try: + sum_price = 0 + order_obj = OrderModel() + order_obj.responsible_group = customer.responsible_group + order_obj.customer = customer + order_obj.total_price = 0 + order_obj.factor_type = 1 + order_obj.factor_status = 1 + order_obj.sys_ws = workspace + + order_obj.full_clean() + order_obj.save() + + json_items = json.loads(items) + + for item in json_items: + product_obj = ProductModel.objects.get(id=int(item['product_id'])) + product_price = product_obj.price + product_name = product_obj.name + + order_item_obj = OrderItemModel() + order_item_obj.order = order_obj + order_item_obj.product_name = product_name + order_item_obj.price = product_price + order_item_obj.amount = int(item['amount']) + order_item_obj.product_id = int(item['product_id']) + + order_item_obj.full_clean() + order_item_obj.save() + + sum_price += product_price + + order_obj.total_price = sum_price + order_obj.full_clean() + order_obj.save() + + return order_obj + + except Exception as exp: + transaction.set_rollback(True) + message = f'Error Create Order: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def update_order(order_id: int, responsible_group_id: int = None, customer_id: int = None, + total_amount: Decimal = None, factor_type: int = None, factor_status: int = None, workspace_uuid = None) -> OrderModel: + try: + order_obj = OrderModel.objects.get(id=order_id, sys_ws__uuid=workspace_uuid) + + if responsible_group_id is not None: + order_obj.responsible_group_id = responsible_group_id + if customer_id is not None: + order_obj.customer_id = customer_id + if total_amount is not None: + order_obj.total_amount = total_amount + if factor_type is not None: + order_obj.factor_type = factor_type + if factor_status is not None: + order_obj.factor_status = factor_status + + order_obj.full_clean() + order_obj.save() + return order_obj + + except Exception as exp: + message = f'Error Updating Order: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def delete_order(order_id: int, workspace_uuid: str) -> OrderModel: + try: + order_obj = OrderModel.objects.get(id=order_id, sys_ws__uuid=workspace_uuid).delete() + return order_obj + except Exception as exp: + message = f'Error Deleting Order: {exp}' + raise exceptions.ValidationError({'error': message}) diff --git a/apps/shop/services/order_item.py b/apps/shop/services/order_item.py new file mode 100644 index 0000000..038bbde --- /dev/null +++ b/apps/shop/services/order_item.py @@ -0,0 +1,18 @@ +import json +from decimal import Decimal +from apps.shop.models import OrderItem as OrderItemModel + + +def create_order_items(items: str, order_id: int): + + json_items = json.loads(items) + + for item in json_items: + order_item_obj = OrderItemModel() + order_item_obj.order_id = order_id + order_item_obj.price = Decimal(item.price) + order_item_obj.amount = int(item.amount) + order_item_obj.product_id = int(item.product) + + order_item_obj.full_clean() + order_item_obj.save() diff --git a/apps/shop/services/product.py b/apps/shop/services/product.py new file mode 100644 index 0000000..107fefe --- /dev/null +++ b/apps/shop/services/product.py @@ -0,0 +1,55 @@ +from apps.shop.models import Product as ProductModel +from decimal import Decimal +from rest_framework import exceptions + + +def create_product(name: str, price: Decimal, category: int, acc_code: str = None, attr: str = None, workspace=None) -> ProductModel: + try: + product_obj = ProductModel() + product_obj.name = name + product_obj.price = price + product_obj.category_id = category + if acc_code is not None: + product_obj.acc_code = acc_code + if attr is not None: + product_obj.attr = attr + product_obj.sys_ws = workspace + + product_obj.full_clean() + product_obj.save() + + return product_obj + + except Exception as exp: + message = f'Error Create Product: {exp}' + exceptions.ValidationError({'error': message}) + + +def update_product(product_id: int, name: str, price: Decimal, category: int, acc_code: str = None, attr: str = None, workspace_uuid: str = None) -> ProductModel: + try: + + product_obj = ProductModel.objects.get(id=product_id, sys_ws__uuid=workspace_uuid) + + product_obj.name = name + product_obj.price = price + product_obj.category_id = category + if acc_code is not None: + product_obj.acc_code = acc_code + if attr is not None: + product_obj.attr = attr + product_obj.full_clean() + product_obj.save() + + return product_obj + except Exception as exp: + message = f'Error Updating Product: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def delete_product(product_id: int, workspace_uuid: str) -> ProductModel: + try: + product_obj = ProductModel.objects.get(id=product_id, sys_ws__uuid=workspace_uuid).delete() + return product_obj + except Exception as exp: + message = f'Error Deleting Product: {exp}' + raise exceptions.ValidationError({'error': message}) diff --git a/apps/shop/services/product_category.py b/apps/shop/services/product_category.py new file mode 100644 index 0000000..a261e38 --- /dev/null +++ b/apps/shop/services/product_category.py @@ -0,0 +1,40 @@ +from apps.shop.models import ProductCategory as ProductCategoryModel +from rest_framework import exceptions + + +def create_product_category(name: str, parent: int = None, workspace=None) -> ProductCategoryModel: + try: + product_category_obj = ProductCategoryModel(name=name, sys_ws=workspace) + + if parent is not None: + product_category_obj.parent_id = parent + + product_category_obj.full_clean() + product_category_obj.save() + + return product_category_obj + + except Exception as exp: + message = f'Error Create ProductCategory: {exp}' + exceptions.ValidationError({'error': message}) + + +def update_product_category(category_id: int, name: str, workspace_uuid: str) -> ProductCategoryModel: + try: + product_category_obj = ProductCategoryModel.objects.get(id=category_id, sys_ws__uuid=workspace_uuid) + product_category_obj.name = name + product_category_obj.full_clean() + product_category_obj.save() + return product_category_obj + except Exception as exp: + message = f'Error Updating ProductCategory: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def delete_product_category(category_id: int, workspace_uuid: str) -> ProductCategoryModel: + try: + product_category_obj = ProductCategoryModel.objects.get(id=category_id, sys_ws__uuid=workspace_uuid).delete() + return product_category_obj + except Exception as exp: + message = f'Error Deleting ProductCategory: {exp}' + raise exceptions.ValidationError({'error': message}) diff --git a/apps/shop/tests.py b/apps/shop/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/shop/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/shop/urls.py b/apps/shop/urls.py new file mode 100644 index 0000000..8e18d1b --- /dev/null +++ b/apps/shop/urls.py @@ -0,0 +1,34 @@ +from django.urls import path +from apps.shop.views import * + +urlpatterns = [ + path('product-category/create/', ProductCategoryCreateApi.as_view(), name='create_product_category'), + path('product-category//', ProductCategoryRetrieveApi.as_view(), name='retrieve_product_category'), + path('product-category/update//', ProductCategoryUpdateApi.as_view(), name='update_product_category'), + path('product-category/delete//', ProductCategoryDeleteApi.as_view(), name='delete_product_category'), + path('product-category/list/', ProductCategoryListApi.as_view(), name='list_product_category'), + path('v2/product-category/list/', ProductsCategoryApi.as_view(), name='v2_list_product_category'), + + + path('product/create/', ProductCreateApi.as_view(), name='create_product'), + path('product//', ProductRetrieveApi.as_view(), name='retrieve_product'), + path('product/update//', ProductUpdateApi.as_view(), name='update_product'), + path('product/delete//', ProductDeleteApi.as_view(), name='delete_product'), + path('product/list/', ProductListApi.as_view(), name='list_product'), + path('product/list//', ProductListCategoryApi.as_view(), name='list_product_by_category'), + + path('order/create/', OrderCreateApi.as_view(), name='create_order'), + path('order-and-item/create/', OrderAndItemCreateApi.as_view(), name='create_order_and_items'), + path('order//', OrderRetrieveApi.as_view(), name='retrieve_order'), + path('order/update//', OrderUpdateApi.as_view(), name='update_order'), + path('order/delete//', OrderDeleteApi.as_view(), name='delete_order'), + path('order/list/', OrderListApi.as_view(), name='list_order'), + + + path('order/iteams/list//', OrderItemsListApi.as_view(), name='list_order_items'), + + + path('cart/', CartListApi.as_view(), name='list_cart'), + path('cart/add/', CartCreateApi.as_view(), name='add_cart'), + path('cart/delete//', CartDeleteApi.as_view(), name='delete_cart'), +] diff --git a/apps/shop/views.py b/apps/shop/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/apps/shop/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/apps/shop/views/__init__.py b/apps/shop/views/__init__.py new file mode 100644 index 0000000..6662d7d --- /dev/null +++ b/apps/shop/views/__init__.py @@ -0,0 +1,16 @@ +from .product_category import ( + ProductCategoryCreateApi, ProductCategoryRetrieveApi, ProductCategoryDeleteApi, ProductCategoryListApi, + ProductCategoryUpdateApi, ProductsCategoryApi +) + +from .product import ( + ProductCreateApi, ProductRetrieveApi, ProductDeleteApi, ProductUpdateApi, ProductListApi, ProductListCategoryApi +) + +from .order import ( + OrderCreateApi, OrderUpdateApi, OrderDeleteApi, OrderRetrieveApi, OrderListApi, OrderAndItemCreateApi +) + +from .cart import (CartListApi, CartCreateApi, CartDeleteApi) + +from .order_item import (OrderItemsListApi, ) diff --git a/apps/shop/views/cart.py b/apps/shop/views/cart.py new file mode 100644 index 0000000..d407f85 --- /dev/null +++ b/apps/shop/views/cart.py @@ -0,0 +1,85 @@ +from rest_framework import generics, views +from apps.shop.models import Cart as CartModel, Product as ProductModel +from utils.get_user_workspace import get_user_workspace_uuid +from utils.get_cuser_role import get_cuser_role_uuid +from apps.shop.selectors import list_cart +from rest_framework import serializers +from rest_framework.permissions import IsAuthenticated +from utils import custom_permissions +from utils.get_user_workspace import get_user_workspace_uuid, get_user_workspace +from utils.get_cuser_role import get_cuser_role +from rest_framework.response import Response +from apps.shop.services import add_to_cart, delete_cart_iteam +from rest_framework import status + + +class CartListApi(generics.ListAPIView): + + class OutputSerializer(serializers.ModelSerializer): + + class ProductSerializer(serializers.ModelSerializer): + + class CategorySerializer(serializers.Serializer): + name = serializers.CharField() + + category = CategorySerializer() + + class Meta: + model = ProductModel + exclude = ('sys_ws', 'created_at', 'updated_at', 'acc_code', 'attr') + + product = ProductSerializer() + + class Meta: + model = CartModel + exclude = ('sys_ws', 'created_at', 'updated_at', 'cuser_role') + + serializer_class = OutputSerializer + + def get_queryset(self): + user_workspace_uuid = get_user_workspace_uuid(self.request) + cuser_role_uuid = get_cuser_role_uuid(self.request) + return list_cart(workspace_uuid=user_workspace_uuid, cuser_role_uuid=cuser_role_uuid) + + +class CartCreateApi(views.APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class InputSerializer(serializers.Serializer): + product_id = serializers.IntegerField(required=True) + quantity = serializers.IntegerField(required=True) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = CartModel + exclude = ('sys_ws', ) + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + user_workspace = get_user_workspace(request) + cuser_role = get_cuser_role(request) + + cart_obj = add_to_cart(**serializer.validated_data, workspace=user_workspace, cuser_role=cuser_role) + + output = self.OutputSerializer(cart_obj) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class CartDeleteApi(views.APIView): + + def delete(self, request, cart_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request=request) + cuser_role_uuid = get_cuser_role_uuid(request=request) + cart_obj = delete_cart_iteam(cart_id=cart_id, cuser_role_uuid=cuser_role_uuid, workspace_uuid=user_workspace_uuid) + + output = { + 'order_id': cart_obj, + 'message': 'deleted successfully' + } + + return Response(data=output, status=status.HTTP_200_OK) \ No newline at end of file diff --git a/apps/shop/views/order.py b/apps/shop/views/order.py new file mode 100644 index 0000000..25a55cd --- /dev/null +++ b/apps/shop/views/order.py @@ -0,0 +1,152 @@ +from apps.shop.models import Order as OrderModel +from rest_framework.views import APIView +from rest_framework import serializers +from utils.get_user_workspace import get_user_workspace_uuid, get_user_workspace +from utils.get_cuser_role import get_cuser_role_uuid +from rest_framework.response import Response +from rest_framework import status +from apps.shop.services import * +from apps.shop.selectors import * +from rest_framework import generics +from apps.customer.models import Customer as CustomerModel +from rest_framework.permissions import IsAuthenticated +from utils import custom_permissions + + +class OrderCreateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission) + + class InputSerializer(serializers.Serializer): + responsible_group_id = serializers.IntegerField(required=True) + customer_id = serializers.IntegerField(required=True) + total_amount = serializers.DecimalField(max_digits=20, decimal_places=2) + factor_type = serializers.IntegerField(required=True) + factor_status = serializers.IntegerField(required=True) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = OrderModel + exclude = ('sys_ws', ) + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_workspace = get_user_workspace(request) + + order_obj = create_order(**serializer.validated_data, workspace=user_workspace) + + output = self.OutputSerializer(order_obj) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class OrderAndItemCreateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class InputSerializer(serializers.Serializer): + items = serializers.CharField(required=True) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = OrderModel + exclude = ('sys_ws', ) + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_workspace = get_user_workspace(request) + + cuser_role_uuid = get_cuser_role_uuid(request) + + try: + customer_obj = CustomerModel.objects.get(cuser_role__uuid=cuser_role_uuid) + except Exception as exp: + raise exceptions.ValidationError({'error': f'customer obj not found: {exp}'}) + + order_obj = create_order_and_items(**serializer.validated_data, customer=customer_obj, workspace=user_workspace) + clean_cart(cuser_role_uuid=cuser_role_uuid,workspace_uuid=user_workspace.uuid) + + output = self.OutputSerializer(order_obj) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class OrderRetrieveApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = OrderModel + exclude = ('sys_ws', ) + + def get(self, request, order_id): + user_workspace_uuid = get_user_workspace_uuid(request=request) + cuser_role_uuid = get_cuser_role_uuid(request=request) + request_user = request.user + + order_obj = retrieve_order(cuser_role_uuid=cuser_role_uuid, order_id=order_id, workspace_uuid=user_workspace_uuid) + + output = self.OutputSerializer(order_obj) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class OrderUpdateApi(APIView): + + class InputSerializer(serializers.Serializer): + responsible_group_id = serializers.IntegerField(required=False) + customer_id = serializers.IntegerField(required=False) + total_amount = serializers.DecimalField(max_digits=20, decimal_places=2, required=False) + factor_type = serializers.IntegerField(required=False) + factor_status = serializers.IntegerField(required=False) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = OrderModel + exclude = ('sys_ws', ) + + def patch(self, request, order_id): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + user_workspace_uuid = get_user_workspace_uuid(request) + order_obj = update_order(order_id=order_id, **serializer.validated_data, workspace_uuid=user_workspace_uuid) + + output = self.OutputSerializer(order_obj) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class OrderDeleteApi(APIView): + + def delete(self, request, order_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request=request) + order_obj = delete_order(order_id=order_id, workspace_uuid=user_workspace_uuid) + + output = { + 'order_id': order_id, + 'message': 'deleted successfully' + } + + return Response(data=output, status=status.HTTP_200_OK) + + +class OrderListApi(generics.ListAPIView): + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = OrderModel + exclude = ('sys_ws', ) + + serializer_class = OutputSerializer + + def get_queryset(self): + cuser_role_uuid = get_cuser_role_uuid(request=self.request) + user_workspace_uuid = get_user_workspace_uuid(request=self.request) + + return list_order(cuser_role_uuid=cuser_role_uuid, workspace_uuid=user_workspace_uuid) + diff --git a/apps/shop/views/order_item.py b/apps/shop/views/order_item.py new file mode 100644 index 0000000..46cab4b --- /dev/null +++ b/apps/shop/views/order_item.py @@ -0,0 +1,30 @@ +from rest_framework.views import APIView +from rest_framework import generics +from rest_framework import serializers +from apps.shop.models import OrderItem as OrderItemModel +from utils.get_user_workspace import get_user_workspace_uuid, get_user_workspace +from utils.get_cuser_role import get_cuser_role_uuid +from apps.shop.selectors import list_order_item + + +class OrderItemsListApi(generics.ListAPIView): + + class OutputSerializer(serializers.ModelSerializer): + category_name = serializers.SerializerMethodField() + + class Meta: + model = OrderItemModel + fields = '__all__' + + def get_category_name(self, obj): + return obj.product.category.name + + + serializer_class = OutputSerializer + + def get_queryset(self): + cuser_role_uuid = get_cuser_role_uuid(request=self.request) + #user_workspace_uuid = get_user_workspace_uuid(request=self.request) + order_id = self.kwargs.get('order_id') + + return list_order_item(order_id=order_id, cuser_role_uuid=cuser_role_uuid) \ No newline at end of file diff --git a/apps/shop/views/product.py b/apps/shop/views/product.py new file mode 100644 index 0000000..0366e1f --- /dev/null +++ b/apps/shop/views/product.py @@ -0,0 +1,165 @@ +from apps.shop.models import Product as ProductModel +from rest_framework.views import APIView +from rest_framework import serializers +from utils.get_user_workspace import get_user_workspace_uuid, get_user_workspace +from rest_framework.response import Response +from rest_framework import status +from apps.shop.services import * +from apps.shop.selectors import * +from rest_framework import generics +from rest_framework.permissions import IsAuthenticated +from utils import custom_permissions + + +class ProductCreateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission) + + class InputSerializer(serializers.Serializer): + name = serializers.CharField(required=True, max_length=20) + price = serializers.DecimalField(required=True, max_digits=20, decimal_places=2) + category = serializers.IntegerField(required=True) + acc_code = serializers.CharField(required=False) + attr = serializers.CharField(required=False) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ProductModel + exclude = ('sys_ws', ) + + def post(self, request): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + user_workspace = get_user_workspace(request) + + product_obj = create_product(**serializer.validated_data, workspace=user_workspace) + + output = self.OutputSerializer(product_obj) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class ProductRetrieveApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ProductModel + exclude = ('sys_ws', ) + + def get(self, request, product_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request) + product_obj = retrieve_product(product_id, workspace_uuid=user_workspace_uuid) + output = self.OutputSerializer(product_obj) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class ProductUpdateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission) + + class InputSerializer(serializers.Serializer): + name = serializers.CharField(required=True, max_length=20) + price = serializers.DecimalField(required=True, max_digits=20, decimal_places=2) + category = serializers.IntegerField(required=True) + acc_code = serializers.CharField(required=False) + attr = serializers.CharField(required=False) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ProductModel + exclude = ('sys_ws', ) + + def put(self, request, product_id: int): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + user_workspace_uuid = get_user_workspace_uuid(request) + + product_obj = update_product(product_id=product_id, **serializer.validated_data, workspace_uuid=user_workspace_uuid) + output = self.OutputSerializer(product_obj) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class ProductDeleteApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ProductModel + exclude = ('sys_ws', ) + + def delete(self, request, product_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request=request) + product_obj = delete_product(product_id=product_id, workspace_uuid=user_workspace_uuid) + + output = { + 'id': product_id, + 'status': 'deleted successfully' + } + + return Response(data=output, status=status.HTTP_200_OK) + + +class ProductListApi(generics.ListAPIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ProductCategoryModel + exclude = ('sys_ws', ) + + def get_queryset(self): + user_workspace_uuid = get_user_workspace_uuid(self.request) + return list_product(workspace_uuid=user_workspace_uuid) + + serializer_class = OutputSerializer + + +class ProductListCategoryApi(generics.ListAPIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class OutputSerializer(serializers.Serializer): + id = serializers.IntegerField() + name = serializers.CharField() + price = serializers.FloatField() + + class Meta: + exclude = ('sys_ws', ) + + def list(self, request, category_id): + # Note the use of `get_queryset()` instead of `self.queryset` + queryset_data = self.get_queryset() + serializer = self.get_serializer(queryset_data, many=True) + workspace_uuid = get_user_workspace_uuid(request=request) + category = retrieve_product_category(category_id=category_id, workspace_uuid=workspace_uuid) + + if len(serializer.data) == 0: + return Response( + status=404, + ) + data = { + "category_name": category.name, + "products": serializer.data + } + + return Response(data) + + def get_queryset(self): + user_workspace_uuid = get_user_workspace_uuid(self.request) + category_id = self.kwargs.get('category_id') + + return list_product_by_category(workspace_uuid=user_workspace_uuid, category_id=category_id) + + serializer_class = OutputSerializer diff --git a/apps/shop/views/product_category.py b/apps/shop/views/product_category.py new file mode 100644 index 0000000..9812f46 --- /dev/null +++ b/apps/shop/views/product_category.py @@ -0,0 +1,162 @@ +from apps.shop.models import ProductCategory as ProductCategoryModel, Product as ProductModel +from rest_framework.views import APIView +from rest_framework import serializers +from utils.get_user_workspace import get_user_workspace_uuid, get_user_workspace +from rest_framework.response import Response +from rest_framework import status +from apps.shop.services import * +from apps.shop.selectors import * +from rest_framework import generics +from rest_framework.permissions import IsAuthenticated +from django.db.models import Count +from utils.tools import inline_serializer +from utils import custom_permissions + + +class ProductCategoryCreateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission) + + class InputSerializer(serializers.Serializer): + name = serializers.CharField(required=True, max_length=20) + parent = serializers.IntegerField(required=False) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ProductCategoryModel + exclude = ('sys_ws', ) + + def post(self, request): + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + user_workspace = get_user_workspace(request) + + product_category_obj = create_product_category(**serializer.validated_data, workspace=user_workspace) + + output = self.OutputSerializer(product_category_obj) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class ProductCategoryRetrieveApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ProductCategoryModel + exclude = ('sys_ws', ) + + def get(self, request, category_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request) + product_category_obj = retrieve_product_category(category_id=category_id, workspace_uuid=user_workspace_uuid) + output = self.OutputSerializer(product_category_obj) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class ProductCategoryUpdateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission) + + class InputSerializer(serializers.Serializer): + name = serializers.CharField(required=True) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ProductCategoryModel + exclude = ('sys_ws', ) + + def put(self, request, category_id: int): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + name = serializer.validated_data.get + + user_workspace_uuid = get_user_workspace_uuid(request) + + product_category_obj = update_product_category(name=name, category_id=category_id, workspace_uuid=user_workspace_uuid) + + output = self.OutputSerializer(product_category_obj) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class ProductCategoryDeleteApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceAdminPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ProductCategoryModel + exclude = ('sys_ws', ) + + def delete(self, request, category_id: int): + + user_workspace_uuid = get_user_workspace_uuid(request=request) + product_category_obj = delete_product_category(category_id=category_id, workspace_uuid=user_workspace_uuid) + + output = { + 'id': category_id, + 'status': 'deleted successfully' + } + + return Response(data=output, status=status.HTTP_200_OK) + + +class ProductCategoryListApi(generics.ListAPIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = ProductCategoryModel + exclude = ('sys_ws', ) + + def list(self, request): + # Note the use of `get_queryset()` instead of `self.queryset` + queryset_data = self.get_queryset() + serializer = self.get_serializer(queryset_data, many=True) + workspace_uuid = get_user_workspace_uuid(request=request) + print(workspace_uuid) + category = list_product_category(workspace_uuid=workspace_uuid) + + categorysList = [] + + for cate in category: + count_prd = count_product_in_category(category_id=cate.id, workspace_uuid=workspace_uuid) + if count_prd >= 1: + tmp_cat = { + "name": cate.name, + "count": count_prd, + "id": cate.id, + } + categorysList.append(tmp_cat) + + return Response(categorysList) + + def get_queryset(self): + user_workspace_uuid = get_user_workspace_uuid(self.request) + return list_product_category(workspace_uuid=user_workspace_uuid) + + serializer_class = OutputSerializer + + +class ProductsCategoryApi(APIView): + + class OutputSerializer(serializers.ModelSerializer): + product_count = serializers.IntegerField() + + class Meta: + model = ProductCategoryModel + fields = '__all__' + + def get(self, request): + + p_cate = ProductCategoryModel.objects.annotate(product_count=Count('product')).filter(product_count__gte=1) + output = self.OutputSerializer(p_cate, many=True) + + return Response(data=output.data, status=status.HTTP_200_OK) diff --git a/apps/ticket/__init__.py b/apps/ticket/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/ticket/admin.py b/apps/ticket/admin.py new file mode 100644 index 0000000..987afef --- /dev/null +++ b/apps/ticket/admin.py @@ -0,0 +1,19 @@ +from django.contrib import admin +from apps.ticket.models import Ticket, TicketComment, TicketCommentFile + +# Register your models here. + + +@admin.register(Ticket) +class TicketAdmin(admin.ModelAdmin): + pass + + +@admin.register(TicketComment) +class TicketCommentAdmin(admin.ModelAdmin): + pass + + +@admin.register(TicketCommentFile) +class TicketCommentFileAdmin(admin.ModelAdmin): + pass diff --git a/apps/ticket/apps.py b/apps/ticket/apps.py new file mode 100644 index 0000000..e61668b --- /dev/null +++ b/apps/ticket/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TicketConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.ticket' diff --git a/apps/ticket/migrations/0001_initial.py b/apps/ticket/migrations/0001_initial.py new file mode 100644 index 0000000..88e1921 --- /dev/null +++ b/apps/ticket/migrations/0001_initial.py @@ -0,0 +1,47 @@ +# Generated by Django 4.2.1 on 2023-12-20 10:41 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('workspace', '0003_alter_workspace_uuid'), + ('cuser', '0005_smsopt'), + ] + + operations = [ + migrations.CreateModel( + name='Ticket', + 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)), + ('title', models.CharField(max_length=100)), + ('status', models.PositiveSmallIntegerField(choices=[(0, 'wait'), (1, 'customer'), (2, 'operator')])), + ('cuser_role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cuser.cuserrole')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='TicketComment', + 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)), + ('desc', models.CharField(max_length=500)), + ('cuser_role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cuser.cuserrole')), + ('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')), + ('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ticket.ticket')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/ticket/migrations/0002_alter_ticket_status.py b/apps/ticket/migrations/0002_alter_ticket_status.py new file mode 100644 index 0000000..98e2e5a --- /dev/null +++ b/apps/ticket/migrations/0002_alter_ticket_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2023-12-20 11:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='ticket', + name='status', + field=models.PositiveSmallIntegerField(choices=[(0, 'wait'), (1, 'customer'), (2, 'operator')], default=0), + ), + ] diff --git a/apps/ticket/migrations/0003_ticketcomment_file.py b/apps/ticket/migrations/0003_ticketcomment_file.py new file mode 100644 index 0000000..11766e2 --- /dev/null +++ b/apps/ticket/migrations/0003_ticketcomment_file.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2023-12-24 06:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0002_alter_ticket_status'), + ] + + operations = [ + migrations.AddField( + model_name='ticketcomment', + name='file', + field=models.FileField(blank=True, null=True, upload_to=''), + ), + ] diff --git a/apps/ticket/migrations/0004_alter_ticketcomment_file.py b/apps/ticket/migrations/0004_alter_ticketcomment_file.py new file mode 100644 index 0000000..5f4591d --- /dev/null +++ b/apps/ticket/migrations/0004_alter_ticketcomment_file.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2023-12-24 06:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0003_ticketcomment_file'), + ] + + operations = [ + migrations.AlterField( + model_name='ticketcomment', + name='file', + field=models.FileField(blank=True, null=True, upload_to='fileha/'), + ), + ] diff --git a/apps/ticket/migrations/0005_remove_ticketcomment_file.py b/apps/ticket/migrations/0005_remove_ticketcomment_file.py new file mode 100644 index 0000000..fcf61be --- /dev/null +++ b/apps/ticket/migrations/0005_remove_ticketcomment_file.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.1 on 2023-12-24 07:04 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0004_alter_ticketcomment_file'), + ] + + operations = [ + migrations.RemoveField( + model_name='ticketcomment', + name='file', + ), + ] diff --git a/apps/ticket/migrations/0006_ticketcommentfiles.py b/apps/ticket/migrations/0006_ticketcommentfiles.py new file mode 100644 index 0000000..fdff95f --- /dev/null +++ b/apps/ticket/migrations/0006_ticketcommentfiles.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.1 on 2023-12-24 07:09 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0005_remove_ticketcomment_file'), + ] + + operations = [ + migrations.CreateModel( + name='TicketCommentFiles', + 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)), + ('file', models.FileField(blank=True, null=True, upload_to='ticketFiles/')), + ('ticket_comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ticket.ticketcomment')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/ticket/migrations/0007_rename_ticketcommentfiles_ticketcommentfile.py b/apps/ticket/migrations/0007_rename_ticketcommentfiles_ticketcommentfile.py new file mode 100644 index 0000000..4fec8b0 --- /dev/null +++ b/apps/ticket/migrations/0007_rename_ticketcommentfiles_ticketcommentfile.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.1 on 2023-12-24 07:11 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0006_ticketcommentfiles'), + ] + + operations = [ + migrations.RenameModel( + old_name='TicketCommentFiles', + new_name='TicketCommentFile', + ), + ] diff --git a/apps/ticket/migrations/0008_alter_ticketcommentfile_ticket_comment.py b/apps/ticket/migrations/0008_alter_ticketcommentfile_ticket_comment.py new file mode 100644 index 0000000..3115495 --- /dev/null +++ b/apps/ticket/migrations/0008_alter_ticketcommentfile_ticket_comment.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-12-25 08:53 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('ticket', '0007_rename_ticketcommentfiles_ticketcommentfile'), + ] + + operations = [ + migrations.AlterField( + model_name='ticketcommentfile', + name='ticket_comment', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ticketcommentfile_set', to='ticket.ticketcomment'), + ), + ] diff --git a/apps/ticket/migrations/__init__.py b/apps/ticket/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/ticket/models.py b/apps/ticket/models.py new file mode 100644 index 0000000..56ea21c --- /dev/null +++ b/apps/ticket/models.py @@ -0,0 +1,43 @@ +from django.db import models +from utils.base_model import BaseModel +from apps.workspace.models import Workspace +from apps.cuser.models import CuserRole +# Create your models here. + + +class Ticket(BaseModel): + + STATUS_CHOICES = [ + (0, "wait"), + (1, "customer"), + (2, "operator"), + ] + + title = models.CharField(max_length=100) + status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, default=0) + cuser_role = models.ForeignKey(CuserRole, on_delete=models.CASCADE, null=False, blank=False) + sys_ws = models.ForeignKey(Workspace, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return f'{self.title}' + + +class TicketComment(BaseModel): + + ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, null=False, blank=False) + desc = models.CharField(max_length=500) + cuser_role = models.ForeignKey(CuserRole, on_delete=models.CASCADE, null=False, blank=False) + sys_ws = models.ForeignKey(Workspace, on_delete=models.CASCADE, null=False, blank=False) + + def __str__(self): + return f'{self.ticket.title}' + + +class TicketCommentFile(BaseModel): + + ticket_comment = models.ForeignKey(TicketComment, on_delete=models.CASCADE, null=False, blank=False, related_name='ticketcommentfile_set') + file = models.FileField(null=True, blank=True, upload_to='ticketFiles/') + + def __str__(self): + return f'{self.ticket_comment.ticket.title}' + diff --git a/apps/ticket/selectors.py b/apps/ticket/selectors.py new file mode 100644 index 0000000..6687d2f --- /dev/null +++ b/apps/ticket/selectors.py @@ -0,0 +1,32 @@ +from apps.ticket.models import Ticket as TicketModel, TicketComment as TicketCommentModel +from apps.cuser.models import CuserRole as CuserRoleModel +from rest_framework import exceptions + + +def list_ticket(cuser_role_uuid: str, workspace_uuid: str) -> TicketModel: + try: + role_type_name = CuserRoleModel.objects.get(uuid=cuser_role_uuid).role_type.role_name_en + if role_type_name == 'workspace_operator': + tickets = TicketModel.objects.filter(sys_ws__uuid=workspace_uuid).order_by('-created_at') + else: + tickets = TicketModel.objects.filter(cuser_role__uuid=cuser_role_uuid, sys_ws__uuid=workspace_uuid).order_by('-created_at') + return tickets + except Exception as exp: + message = f'Error Fetching List tickets: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def retrieve_ticket_comments(ticket_id: int, cuser_role_uuid: str, workspace_uuid: str) -> TicketCommentModel: + try: + role_type_name = CuserRoleModel.objects.get(uuid=cuser_role_uuid).role_type.role_name_en + + if role_type_name == 'workspace_operator': + ticket = TicketModel.objects.get(id=ticket_id, sys_ws__uuid=workspace_uuid) + else: + ticket = TicketModel.objects.get(id=ticket_id, cuser_role__uuid=cuser_role_uuid, sys_ws__uuid=workspace_uuid) + + ticket_comments = TicketCommentModel.objects.filter(ticket=ticket, sys_ws__uuid=workspace_uuid) + return ticket_comments + except Exception as exp: + message = f'Error Fetching List ticket comments: {exp}' + raise exceptions.ValidationError({'error': message}) diff --git a/apps/ticket/services.py b/apps/ticket/services.py new file mode 100644 index 0000000..c501ad4 --- /dev/null +++ b/apps/ticket/services.py @@ -0,0 +1,85 @@ +from apps.ticket.models import Ticket, TicketComment, TicketCommentFile +from apps.cuser.models import CuserRole +from apps.workspace.models import Workspace +from apps.cuser.models import CuserRole as CuserRoleModel +from django.db import transaction +from rest_framework import exceptions +import base64 +from abc import ABC, abstractmethod + + +def create_ticket(title: str, desc: str, cuser_role: CuserRole, workspace: Workspace, files=None) -> Ticket: + + with transaction.atomic(): + + try: + + ticket = Ticket() + ticket.title = title + ticket.cuser_role = cuser_role + ticket.status = 0 + ticket.sys_ws = workspace + ticket.save() + + ticket_comment = TicketComment() + ticket_comment.ticket = ticket + ticket_comment.desc = desc + ticket_comment.cuser_role = cuser_role + ticket_comment.sys_ws = workspace + + ticket_comment.save() + + if files: + for file in files: + ticket_file = TicketCommentFile() + ticket_file.ticket_comment = ticket_comment + ticket_file.file = file + ticket_file.save() + + return ticket + + except Exception as exp: + transaction.set_rollback(True) + message = f'Error Create Ticket: {exp}' + raise exceptions.ValidationError({'error': message}) + + +def create_comment_ticket(ticket_id: int, desc: str, cuser_role: CuserRole, workspace: Workspace, files=None) -> TicketComment: + + with transaction.atomic(): + + try: + + role_type_name = cuser_role.role_type.role_name_en + ticket_status = None + if role_type_name == 'workspace_operator': + ticket = Ticket.objects.get(id=ticket_id) + if ticket.status == 0: + ticket.status = 2 + ticket.save() + else: + ticket = Ticket.objects.get(id=ticket_id, cuser_role=cuser_role, sys_ws=workspace) + if ticket.status == 0: + ticket.status = 1 + ticket.save() + + ticket_comment = TicketComment() + ticket_comment.ticket = ticket + ticket_comment.desc = desc + ticket_comment.cuser_role = cuser_role + ticket_comment.sys_ws = workspace + ticket_comment.save() + + if files: + for file in files: + ticket_file = TicketCommentFile() + ticket_file.ticket_comment = ticket_comment + ticket_file.file = file + ticket_file.save() + + return ticket_comment + + except Exception as exp: + transaction.set_rollback(True) + message = f'Error Create TicketComment: {exp}' + raise exceptions.ValidationError({'error': message}) diff --git a/apps/ticket/tests.py b/apps/ticket/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/ticket/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/ticket/urls.py b/apps/ticket/urls.py new file mode 100644 index 0000000..ec5e019 --- /dev/null +++ b/apps/ticket/urls.py @@ -0,0 +1,9 @@ +from django.urls import path +from apps.ticket.views import * + +urlpatterns = [ + path('create/', TicketCreateApi.as_view(), name='create_ticket'), + path('/', TicketCommentsListApi.as_view(), name='comments_ticket'), + path('/new/', TicketCommentCreateApi.as_view(), name='add_comment_ticket'), + path('list/', TicketListApi.as_view(), name='list_ticket'), +] diff --git a/apps/ticket/views.py b/apps/ticket/views.py new file mode 100644 index 0000000..f94b7ca --- /dev/null +++ b/apps/ticket/views.py @@ -0,0 +1,162 @@ +from django.shortcuts import render +from rest_framework.views import APIView +from rest_framework.generics import ListAPIView +from rest_framework.response import Response +from rest_framework import status +from rest_framework import serializers +from utils.get_user_workspace import get_user_workspace_uuid, get_user_workspace +from utils.get_cuser_role import get_cuser_role +from apps.ticket.services import create_ticket, create_comment_ticket +from apps.ticket.models import Ticket as TicketModel, TicketComment as TicketCommentModel, TicketCommentFile as TicketCommentFileModel +from apps.cuser.models import CuserRole as CuserRoleModel +from rest_framework.permissions import IsAuthenticated +from utils import custom_permissions +from utils.get_cuser_role import get_cuser_role_uuid +from utils.get_user_workspace import get_user_workspace_uuid +from apps.ticket.selectors import list_ticket, retrieve_ticket_comments +# Create your views here. + + +class TicketCreateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class InputSerializer(serializers.Serializer): + title = serializers.CharField(max_length=100) + desc = serializers.CharField(max_length=500) + + files = serializers.ListField( + child=serializers.FileField(required=False, max_length=10000000, allow_empty_file=False), + required=False + ) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = TicketModel + exclude = ('sys_ws', 'cuser_role') + + def post(self, request): + + serializer = self.InputSerializer(data=request.data) + try: + serializer.is_valid(raise_exception=True) + except Exception as exp: + print("exp...") + print(exp) + + user_workspace = get_user_workspace(request=request) + cuser_role_obj = get_cuser_role(request=request) + + print("3") + + ticket = create_ticket(**serializer.validated_data, cuser_role=cuser_role_obj, workspace=user_workspace) + + print("4") + output = self.OutputSerializer(ticket) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + + +class TicketListApi(ListAPIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = TicketModel + exclude = ('sys_ws', ) + + serializer_class = OutputSerializer + + def get_queryset(self): + + cuser_role_uuid = get_cuser_role_uuid(request=self.request) + user_workspace_uuid = get_user_workspace_uuid(request=self.request) + + return list_ticket(cuser_role_uuid=cuser_role_uuid, workspace_uuid=user_workspace_uuid) + + +class TicketCommentsListApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class OutputSerializer(serializers.ModelSerializer): + + class TicketSerializer(serializers.ModelSerializer): + class Meta: + model = TicketModel + exclude = ('sys_ws', 'cuser_role') + + class CuserRoleSerializer(serializers.ModelSerializer): + class Meta: + model = CuserRoleModel + fields = '__all__' + + class TicketCommentFileSerializer(serializers.ModelSerializer): + class Meta: + model = TicketCommentFileModel + fields = '__all__' + + ticket = TicketSerializer(many=False, read_only=True) + comment_files = TicketCommentFileSerializer(many=True, read_only=True, source='ticketcommentfile_set') + cuser_role = CuserRoleSerializer() + + + class Meta: + model = TicketCommentModel + exclude = ('sys_ws',) + + def get(self, request, ticket_id: int): + + print("ok22") + + cuser_role_uuid = get_cuser_role_uuid(request=request) + user_workspace_uuid = get_user_workspace_uuid(request=request) + + print("ok") + + #return Response({"javad":"salam"}) + + ticket_comments = retrieve_ticket_comments(ticket_id=ticket_id, cuser_role_uuid=cuser_role_uuid, + workspace_uuid=user_workspace_uuid) + + output = self.OutputSerializer(ticket_comments, many=True) + + return Response(data=output.data, status=status.HTTP_200_OK) + + +class TicketCommentCreateApi(APIView): + + permission_classes = (IsAuthenticated, custom_permissions.WorkspaceCustomerPermission) + + class InputSerializer(serializers.Serializer): + + files = serializers.ListField( + child=serializers.FileField( + max_length=100000, + allow_empty_file=False, + allow_null=True, + ), required=False + ) + + desc = serializers.CharField(max_length=500) + + class OutputSerializer(serializers.ModelSerializer): + class Meta: + model = TicketCommentModel + exclude = ('sys_ws', 'cuser_role') + + def post(self, request, ticket_id): + + serializer = self.InputSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + user_workspace = get_user_workspace(request=request) + cuser_role_obj = get_cuser_role(request=request) + + ticket_comment = create_comment_ticket(**serializer.validated_data, ticket_id=ticket_id, cuser_role=cuser_role_obj, workspace=user_workspace) + + output = self.OutputSerializer(ticket_comment) + + return Response(data=output.data, status=status.HTTP_201_CREATED) + diff --git a/apps/workspace/__init__.py b/apps/workspace/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/workspace/admin.py b/apps/workspace/admin.py new file mode 100644 index 0000000..205b30d --- /dev/null +++ b/apps/workspace/admin.py @@ -0,0 +1,19 @@ +from django.contrib import admin +from apps.workspace.models import ( + Workspace as WorkspaceModel, + City as CityModel, +) +# Register your models here. + + +class WorkspaceAdmin(admin.ModelAdmin): + pass + + +admin.site.register(WorkspaceModel, WorkspaceAdmin) + + +@admin.register(CityModel) +class CityAdmin(admin.ModelAdmin): + pass + diff --git a/apps/workspace/apps.py b/apps/workspace/apps.py new file mode 100644 index 0000000..5618a12 --- /dev/null +++ b/apps/workspace/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class WorkspaceConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.workspace' diff --git a/apps/workspace/migrations/0001_initial.py b/apps/workspace/migrations/0001_initial.py new file mode 100644 index 0000000..e0d16f1 --- /dev/null +++ b/apps/workspace/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# Generated by Django 4.2.1 on 2023-07-23 05:20 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('cuser', '0002_roletype_cuserrole'), + ] + + operations = [ + migrations.CreateModel( + name='Workspace', + 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=15)), + ('expire', models.DateTimeField(blank=True, null=True)), + ('desc', models.CharField(blank=True, max_length=30, null=True)), + ('online_order_active', models.BooleanField(default=False)), + ('online_order_open', models.BooleanField(default=True)), + ('is_active', models.BooleanField(default=True)), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), + ('cuser_role_owner', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='workspace', to='cuser.cuserrole')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/workspace/migrations/0002_city.py b/apps/workspace/migrations/0002_city.py new file mode 100644 index 0000000..37d3bc4 --- /dev/null +++ b/apps/workspace/migrations/0002_city.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.1 on 2023-08-08 08:37 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('workspace', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='City', + 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(blank=True, max_length=15, null=True)), + ('is_ostan', models.BooleanField(default=False)), + ('ostan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='workspace.city')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/workspace/migrations/0003_alter_workspace_uuid.py b/apps/workspace/migrations/0003_alter_workspace_uuid.py new file mode 100644 index 0000000..01bfc54 --- /dev/null +++ b/apps/workspace/migrations/0003_alter_workspace_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-08-19 12:54 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('workspace', '0002_city'), + ] + + operations = [ + migrations.AlterField( + model_name='workspace', + name='uuid', + field=models.UUIDField(default=uuid.uuid4, unique=True), + ), + ] diff --git a/apps/workspace/migrations/0004_alter_workspace_cuser_role_owner.py b/apps/workspace/migrations/0004_alter_workspace_cuser_role_owner.py new file mode 100644 index 0000000..7fcc659 --- /dev/null +++ b/apps/workspace/migrations/0004_alter_workspace_cuser_role_owner.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.1 on 2024-01-20 07:49 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cuser', '0005_smsopt'), + ('workspace', '0003_alter_workspace_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='workspace', + name='cuser_role_owner', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='workspace', to='cuser.cuserrole'), + ), + ] diff --git a/apps/workspace/migrations/0005_workspace_cuser.py b/apps/workspace/migrations/0005_workspace_cuser.py new file mode 100644 index 0000000..ca5b974 --- /dev/null +++ b/apps/workspace/migrations/0005_workspace_cuser.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.1 on 2024-01-21 09:15 + +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.AUTH_USER_MODEL), + ('workspace', '0004_alter_workspace_cuser_role_owner'), + ] + + operations = [ + migrations.AddField( + model_name='workspace', + name='cuser', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/apps/workspace/migrations/0006_rename_cuser_workspace_cuser_owner.py b/apps/workspace/migrations/0006_rename_cuser_workspace_cuser_owner.py new file mode 100644 index 0000000..dba9203 --- /dev/null +++ b/apps/workspace/migrations/0006_rename_cuser_workspace_cuser_owner.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 09:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('workspace', '0005_workspace_cuser'), + ] + + operations = [ + migrations.RenameField( + model_name='workspace', + old_name='cuser', + new_name='cuser_owner', + ), + ] diff --git a/apps/workspace/migrations/0007_workspace_username.py b/apps/workspace/migrations/0007_workspace_username.py new file mode 100644 index 0000000..704a126 --- /dev/null +++ b/apps/workspace/migrations/0007_workspace_username.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workspace', '0006_rename_cuser_workspace_cuser_owner'), + ] + + operations = [ + migrations.AddField( + model_name='workspace', + name='username', + field=models.CharField(blank=True, max_length=20, null=True, unique=True), + ), + ] diff --git a/apps/workspace/migrations/0008_alter_workspace_username.py b/apps/workspace/migrations/0008_alter_workspace_username.py new file mode 100644 index 0000000..43309f7 --- /dev/null +++ b/apps/workspace/migrations/0008_alter_workspace_username.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workspace', '0007_workspace_username'), + ] + + operations = [ + migrations.AlterField( + model_name='workspace', + name='username', + field=models.CharField(max_length=20, unique=True), + ), + ] diff --git a/apps/workspace/migrations/0009_alter_workspace_username.py b/apps/workspace/migrations/0009_alter_workspace_username.py new file mode 100644 index 0000000..7a8f437 --- /dev/null +++ b/apps/workspace/migrations/0009_alter_workspace_username.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.1 on 2024-01-21 12:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workspace', '0008_alter_workspace_username'), + ] + + operations = [ + migrations.AlterField( + model_name='workspace', + name='username', + field=models.CharField(max_length=30, unique=True), + ), + ] diff --git a/apps/workspace/migrations/__init__.py b/apps/workspace/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/workspace/models.py b/apps/workspace/models.py new file mode 100644 index 0000000..1ecbc09 --- /dev/null +++ b/apps/workspace/models.py @@ -0,0 +1,36 @@ +from django.db import models +from utils.base_model import BaseModel +import uuid +from apps.cuser.models import CuserRole as CuserRoleModel +from apps.cuser.models import CUser as CUserModel +# Create your models here. + + +class Workspace(BaseModel): + cuser_role_owner = models.OneToOneField(CuserRoleModel, on_delete=models.CASCADE, + related_name='workspace', null=True, blank=True) + cuser_owner = models.ForeignKey(CUserModel, on_delete=models.CASCADE, null=True, blank=True, default=None) + name = models.CharField(max_length=15, null=False, blank=False) + username = models.CharField(max_length=30, null=False, blank=False, unique=True) + expire = models.DateTimeField(null=True, blank=True) + desc = models.CharField(max_length=30, null=True, blank=True) + online_order_active = models.BooleanField(default=False) + online_order_open = models.BooleanField(default=True) + + is_active = models.BooleanField(default=True) + uuid = models.UUIDField(default=uuid.uuid4, editable=True, unique=True) + + def __str__(self): + return f'{self.name}' + + +class City(BaseModel): + name = models.CharField(max_length=15, null=True, blank=True) + is_ostan = models.BooleanField(default=False, null=False, blank=False) + ostan = models.ForeignKey('self', models.CASCADE, null=True, blank=True) + + def __str__(self): + return str(self.name) + + + diff --git a/apps/workspace/tests.py b/apps/workspace/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/workspace/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/workspace/views.py b/apps/workspace/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/apps/workspace/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/config/__init__.py b/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/asgi.py b/config/asgi.py new file mode 100644 index 0000000..072ae9e --- /dev/null +++ b/config/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for crmApi project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'crmApi.settings') + +application = get_asgi_application() diff --git a/config/settings/__init__.py b/config/settings/__init__.py new file mode 100644 index 0000000..1539980 --- /dev/null +++ b/config/settings/__init__.py @@ -0,0 +1,9 @@ +import os +django_env = os.getenv('DJANGO_ENV', 'local') +if django_env == 'local': + from .local import * +elif django_env == 'staging': + from .staging import * +elif django_env == 'production': + from .production import * + diff --git a/config/settings/base.py b/config/settings/base.py new file mode 100644 index 0000000..e33c440 --- /dev/null +++ b/config/settings/base.py @@ -0,0 +1,170 @@ +""" +Django settings for crmApi project. + +Generated by 'django-admin startproject' using Django 4.2.1. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.2/ref/settings/ +""" + +from pathlib import Path +from datetime import timedelta + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + +MEDIA_URL = '/media/' +MEDIA_ROOT = BASE_DIR.parent /'media' + +print(MEDIA_ROOT) + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-v#u07sqbnf(7yf58(6-%l8xx&4s-hba(!d&ww*49pk9bd$@w_n' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*'] + +CORS_ALLOWED_ORIGINS = [ + "http://localhost:3001", + "http://localhost:3000", +] + +AUTH_USER_MODEL = 'cuser.CUser' + +# Application definition +LOCAL_APPS = [ + 'apps.cuser', + 'apps.workspace', + 'apps.customform', + 'apps.jwt_auth', + 'apps.customer', + 'apps.shop', + 'apps.ticket', + 'apps.lead', + 'apps.reminder', +] + +THIRD_PARTY_APPS = [ + 'rest_framework', + 'drf_spectacular', + 'django_filters', + 'corsheaders', +] + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + *THIRD_PARTY_APPS, + *LOCAL_APPS, +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'corsheaders.middleware.CorsMiddleware', +] + +ROOT_URLCONF = 'config.urls' + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ), +} + +SIMPLE_JWT = { + "ACCESS_TOKEN_LIFETIME": timedelta(minutes=30), + "REFRESH_TOKEN_LIFETIME": timedelta(days=30), +} + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'config.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'crmAPI-dev2', + 'USER': 'postgres', + 'PASSWORD': '335577!@', + 'HOST': '127.0.0.1', + 'PORT': '5432' + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'Asia/Tehran' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.2/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/config/settings/local.py b/config/settings/local.py new file mode 100644 index 0000000..9b5ed21 --- /dev/null +++ b/config/settings/local.py @@ -0,0 +1 @@ +from .base import * diff --git a/config/settings/production.py b/config/settings/production.py new file mode 100644 index 0000000..9b5ed21 --- /dev/null +++ b/config/settings/production.py @@ -0,0 +1 @@ +from .base import * diff --git a/config/settings/staging.py b/config/settings/staging.py new file mode 100644 index 0000000..9b5ed21 --- /dev/null +++ b/config/settings/staging.py @@ -0,0 +1 @@ +from .base import * diff --git a/config/urls.py b/config/urls.py new file mode 100644 index 0000000..5401752 --- /dev/null +++ b/config/urls.py @@ -0,0 +1,34 @@ +""" +URL configuration for crmApi project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include +from django.conf import settings +from django.conf.urls.static import static + +urlpatterns = [ + path('admin/', admin.site.urls), + path('auth/', include('apps.jwt_auth.urls')), + path('cuser/', include('apps.cuser.urls')), + path('customer/', include('apps.customer.urls')), + path('shop/', include('apps.shop.urls')), + path('ticket/', include('apps.ticket.urls')), + path('lead/', include('apps.lead.urls')), + path('reminder/', include('apps.reminder.urls')), +] +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, + document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/config/wsgi.py b/config/wsgi.py new file mode 100644 index 0000000..de5557e --- /dev/null +++ b/config/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for crmApi project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'crmApi.settings') + +application = get_wsgi_application() diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..8e7ac79 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/requirements/base.txt b/requirements/base.txt new file mode 100644 index 0000000..45a7976 --- /dev/null +++ b/requirements/base.txt @@ -0,0 +1,9 @@ +Django==4.2.1 +djangorestframework==3.14.0 +Markdown==3.4.3 +django-filter==23.2 +drf-spectacular==0.26.2 +psycopg==3.1.9 +djangorestframework-simplejwt==5.2.2 +django-cors-headers==4.3.1 +pillow==10.2.0 \ No newline at end of file diff --git a/requirements/local.txt b/requirements/local.txt new file mode 100644 index 0000000..9c9dec9 --- /dev/null +++ b/requirements/local.txt @@ -0,0 +1 @@ +-r base.txt \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/base_model.py b/utils/base_model.py new file mode 100644 index 0000000..3e15729 --- /dev/null +++ b/utils/base_model.py @@ -0,0 +1,9 @@ +from django.db import models + + +class BaseModel(models.Model): + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + abstract = True diff --git a/utils/custom_permissions.py b/utils/custom_permissions.py new file mode 100644 index 0000000..427986e --- /dev/null +++ b/utils/custom_permissions.py @@ -0,0 +1,79 @@ +from rest_framework import permissions +from utils.get_user_workspace import * +from utils.get_cuser_role import * +from apps.shop.models import Order as OrderModel +from utils.get_cuser_role import get_cuser_role_uuid +from apps.cuser.models import CuserRole as CuserRoleModel + +workspace_group = [ + 'workspace_owner', +] + +workspace_admin_up = [ + 'workspace_owner', + 'workspace_admin', +] + +workspace_operator_up = [ + 'workspace_owner', + 'workspace_admin', + 'workspace_operator', +] + + +class WorkspaceOwnerPermission(permissions.BasePermission): + required_groups = workspace_group + + def has_permission(self, request, view): + + cuser_role_uuid = get_cuser_role_uuid(request) + cuser_role = CuserRoleModel.objects.select_related('role_type').get(uuid=cuser_role_uuid) + if cuser_role.role_type.role_name_en in self.required_groups: + return True + else: + return False + + +class WorkspaceAdminPermission(permissions.BasePermission): + required_groups = workspace_admin_up + + def has_permission(self, request, view): + + cuser_role_uuid = get_cuser_role_uuid(request) + cuser_role = CuserRoleModel.objects.select_related('role_type').get(uuid=cuser_role_uuid) + if cuser_role.role_type.role_name_en in self.required_groups: + return True + else: + return False + + +class WorkspaceOperatorPermission(permissions.BasePermission): + required_groups = workspace_operator_up + + def has_permission(self, request, view): + + cuser_role_uuid = get_cuser_role_uuid(request) + cuser_role = CuserRoleModel.objects.select_related('role_type').get(uuid=cuser_role_uuid) + if cuser_role.role_type.role_name_en in self.required_groups: + return True + else: + return False + + +class WorkspaceCustomerPermission(permissions.BasePermission): + required_groups = [ + 'workspace_owner', + 'workspace_admin', + 'workspace_operator', + 'workspace_customer', + ] + + def has_permission(self, request, view): + cuser_role_uuid = get_cuser_role_uuid(request) + cuser_role = CuserRoleModel.objects.select_related('role_type').get(uuid=cuser_role_uuid) + if cuser_role.role_type.role_name_en in self.required_groups: + return True + else: + return False + + diff --git a/utils/get_cuser_role.py b/utils/get_cuser_role.py new file mode 100644 index 0000000..51e27b6 --- /dev/null +++ b/utils/get_cuser_role.py @@ -0,0 +1,15 @@ +from apps.cuser.models import CuserRole as CuserRoleModel + + +def get_cuser_role(request) -> CuserRoleModel: + jwt_payload = request.auth.payload + cuser_role_uuid = jwt_payload.get('cuser_role_uuid') + # use_redis + cuser_role = CuserRoleModel.objects.get(uuid=cuser_role_uuid) + return cuser_role + + +def get_cuser_role_uuid(request) -> str: + jwt_payload = request.auth.payload + cuser_role_uuid = jwt_payload.get('cuser_role_uuid') + return str(cuser_role_uuid) diff --git a/utils/get_user_workspace.py b/utils/get_user_workspace.py new file mode 100644 index 0000000..3297b5e --- /dev/null +++ b/utils/get_user_workspace.py @@ -0,0 +1,24 @@ +from apps.workspace.models import Workspace as WorkspaceModel +from apps.cuser.models import CuserRole as CuserRoleModel +from apps.customer.models import CustomerResponsible as CustomerResponsibleModel, Customer as CustomerModel + +def get_user_workspace(request) -> WorkspaceModel: + jwt_payload = request.auth.payload + cuser_role_uuid = jwt_payload.get('cuser_role_uuid') + # use_redis + cuser_role = CuserRoleModel.objects.get(uuid=cuser_role_uuid) + role_type_name = cuser_role.role_type.role_name_en + + if role_type_name == 'workspace_operator': + operator = CustomerResponsibleModel.objects.get(cuser_role=cuser_role) + workspace = operator.sys_ws + return workspace + else: + customer = CustomerModel.objects.get(cuser_role=cuser_role) + return customer.sys_ws + + +def get_user_workspace_uuid(request) -> str: + jwt_payload = request.auth.payload + workspace_uuid = jwt_payload.get('workspace_uuid') + return str(workspace_uuid) diff --git a/utils/pagination/__init__.py b/utils/pagination/__init__.py new file mode 100644 index 0000000..cc5a5c7 --- /dev/null +++ b/utils/pagination/__init__.py @@ -0,0 +1 @@ +from .custom_pagination_style_1 import CustomPagination diff --git a/utils/pagination/custom_pagination_style_1.py b/utils/pagination/custom_pagination_style_1.py new file mode 100644 index 0000000..8fb271b --- /dev/null +++ b/utils/pagination/custom_pagination_style_1.py @@ -0,0 +1,16 @@ +from rest_framework.pagination import PageNumberPagination +from rest_framework.response import Response + + +class CustomPagination(PageNumberPagination): + def get_paginated_response(self, data): + return Response({ + 'links': { + 'next': self.get_next_link(), + 'next_page': self.page.next_page_number() if self.page.has_next() else None, + 'previous': self.get_previous_link(), + 'previous_page': self.page.previous_page_number() if self.page.has_previous() else None + }, + 'count': self.page.paginator.count, + 'results': data + }) \ No newline at end of file diff --git a/utils/permissions.py b/utils/permissions.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/tools/__init__.py b/utils/tools/__init__.py new file mode 100644 index 0000000..433f0d1 --- /dev/null +++ b/utils/tools/__init__.py @@ -0,0 +1,2 @@ +from .inline_serializer import inline_serializer +from .inline_model_serializer import inline_model_serializer diff --git a/utils/tools/inline_model_serializer.py b/utils/tools/inline_model_serializer.py new file mode 100644 index 0000000..eaa75c4 --- /dev/null +++ b/utils/tools/inline_model_serializer.py @@ -0,0 +1,20 @@ +from rest_framework import serializers + + +def create_model_serializer_class(name, meta_fields, fields=None): + if fields: + meta = type('Meta', (), {'fields': meta_fields} if meta_fields else {}) + return type(name, (serializers.ModelSerializer,), {'Meta': meta, **fields}) + else: + meta = type('Meta', (), {'fields': meta_fields} if meta_fields else {}) + return type(name, (serializers.ModelSerializer,), {'Meta': meta}) + + +def inline_model_serializer(*, meta_fields, fields=None, data=None, **kwargs): + + serializer_class = create_model_serializer_class(name="inline_model_serializer", fields=fields, meta_fields=meta_fields) + + if data is not None: + return serializer_class(data=data, **kwargs) + + return serializer_class(**kwargs) diff --git a/utils/tools/inline_serializer.py b/utils/tools/inline_serializer.py new file mode 100644 index 0000000..1c1eca6 --- /dev/null +++ b/utils/tools/inline_serializer.py @@ -0,0 +1,18 @@ +from rest_framework import serializers + + +def create_serializer_class(name, fields): + return type(name, (serializers.Serializer,), fields) + + +def inline_serializer(*, fields, data=None, **kwargs): + # Important note if you are using `drf-spectacular` + # Please refer to the following issue: + # https://github.com/HackSoftware/Django-Styleguide/issues/105#issuecomment-1669468898 + # Since you might need to use unique names (uuids) for each inline serializer + serializer_class = create_serializer_class(name="inline_serializer", fields=fields) + + if data is not None: + return serializer_class(data=data, **kwargs) + + return serializer_class(**kwargs) \ No newline at end of file diff --git a/utils/validations/__init__.py b/utils/validations/__init__.py new file mode 100644 index 0000000..58fd053 --- /dev/null +++ b/utils/validations/__init__.py @@ -0,0 +1 @@ +from .object_validation import * \ No newline at end of file diff --git a/utils/validations/object_validation.py b/utils/validations/object_validation.py new file mode 100644 index 0000000..c15350f --- /dev/null +++ b/utils/validations/object_validation.py @@ -0,0 +1,26 @@ +from abc import ABC, abstractmethod +from django.db.models import Model +from rest_framework import exceptions + +class ObjectValidationABC(ABC): + @abstractmethod + def is_valid(self, object_id) -> bool: + pass + + +class ObjectValidationById(ObjectValidationABC): + + def __init__(self, object_model, request): + self.object_model = object_model + self.request = request + + def is_valid(self, object_id) -> bool: + try: + result = self.object_model.objects.get(id=object_id) + return result + except Exception as exp: + message = f'Error Validation object: {exp}' + raise exceptions.ValidationError({'error': message}) + + +