Compare commits

..
10 Commits
Author SHA1 Message Date
javad 8941fded53 dockerfile| 2026-06-20 18:17:43 +03:30
javad 0d0c2452c8 order to excel for all 2025-06-29 12:02:45 +03:30
javad 813e5055b2 order view for all 2025-06-28 15:12:07 +03:30
javad 076ba42ab2 orders for all 2025-06-28 15:04:27 +03:30
javad fc914d6f6d accsess all to list customer 2025-06-24 14:10:08 +03:30
javad db708a662e lead accsess for all operator 2024-09-30 09:04:15 +03:30
javad 5e396c0e32 Add Lead Event and Send SMS 2024-08-27 12:04:27 +03:30
mohammad javad fesahat 43f5dd4594 before tehran 2024-08-24 16:00:11 +03:30
mohammad javad fesahat ef18434b86 before change Ticket Desc to TextArea 2024-07-25 09:06:30 +03:30
javad f7dbd02a6c small changes 2024-02-07 10:40:45 +03:30
115 changed files with 2320 additions and 77 deletions

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
.git
.gitignore
__pycache__/
*.pyc
venv/
.venv/
+2
View File
@@ -7,6 +7,8 @@ __pycache__/
# C extensions
*.so
*.xlsx
# Distribution / packaging
.Python
build/
+10
View File
@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# GitHub Copilot persisted chat sessions
/copilot/chatSessions
Generated
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="accountSettings">
<option name="activeProfile" value="profile:default" />
<option name="activeRegion" value="us-east-1" />
<option name="recentlyUsedProfiles">
<list>
<option value="profile:default" />
</list>
</option>
<option name="recentlyUsedRegions">
<list>
<option value="us-east-1" />
</list>
</option>
</component>
<component name="connectionManager">
<option name="activeConnectionId" value="sso;us-east-1;https://view.awsapps.com/start" />
</component>
</project>
+5 -2
View File
@@ -13,8 +13,11 @@
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
+4 -1
View File
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="crmApiEnv" project-jdk-type="Python SDK" />
<component name="Black">
<option name="sdkName" value="Python 3.12 (venv)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
</project>
+166 -18
View File
@@ -1,14 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="87c5db51-3e89-4a98-9bbb-121c9c52ab3c" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/apps/cuser/cuser_model.py" beforeDir="false" afterPath="$PROJECT_DIR$/apps/cuser/cuser_model.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/apps/cuser/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/apps/cuser/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/apps/customer/services.py" beforeDir="false" afterPath="$PROJECT_DIR$/apps/customer/services.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/apps/customer/urls.py" beforeDir="false" afterPath="$PROJECT_DIR$/apps/customer/urls.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/apps/customer/views/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/apps/customer/views/__init__.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/apps/customer/views/customer.py" beforeDir="false" afterPath="$PROJECT_DIR$/apps/customer/views/customer.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -31,23 +28,35 @@
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 3
}</component>
<component name="ProjectId" id="2SvKNIPBcTXVjDKPSdS8NHW9I0w" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;WebServerToolWindowFactoryState&quot;: &quot;false&quot;,
&quot;last_opened_file_path&quot;: &quot;E:/Projects/crmProject/Project/api-Project/crmApi&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Django Server.crmApi.executor": "Run",
"RunOnceActivity.OpenDjangoStructureViewOnStart": "true",
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
"WebServerToolWindowFactoryState": "false",
"git-widget-placeholder": "main",
"ignore.virus.scanning.warn.message": "true",
"last_opened_file_path": "C:/Users/mjavad/Projects/crmProject/Project/api-Project/crmApi",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "preferences.pluginManager",
"vue.rearranger.settings.migration": "true"
}
}</component>
}]]></component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="E:\Projects\crmProject\Project\api-Project\crmApi\utils" />
@@ -57,13 +66,14 @@
<component name="RunManager">
<configuration name="crmApi" type="Python.DjangoServer" factoryName="Django server">
<module name="crmApi" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="WORKING_DIRECTORY" value="C:\Users\mjavad\Projects\crmProject\Project\api-Project\crmApi" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
@@ -79,6 +89,14 @@
<method v="2" />
</configuration>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-js-predefined-1d06a55b98c1-74d2a5396914-JavaScript-PY-241.14494.241" />
<option value="bundled-python-sdk-0509580d9d50-28c9f5db9ffe-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-241.14494.241" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
@@ -96,7 +114,49 @@
<workItem from="1697269439761" duration="66295000" />
<workItem from="1702900258020" duration="63755000" />
<workItem from="1704088148329" duration="105568000" />
<workItem from="1704951046597" duration="99812000" />
<workItem from="1704951046597" duration="135372000" />
<workItem from="1706950546279" duration="21524000" />
<workItem from="1707974605580" duration="43207000" />
<workItem from="1710141658112" duration="240000" />
<workItem from="1710142130443" duration="1078000" />
<workItem from="1710150399673" duration="961000" />
<workItem from="1710419485683" duration="68525000" />
<workItem from="1710488609275" duration="177000" />
<workItem from="1710488798818" duration="2376000" />
<workItem from="1710491283739" duration="274000" />
<workItem from="1710491571470" duration="2407000" />
<workItem from="1710859420770" duration="5021000" />
<workItem from="1711518197148" duration="371000" />
<workItem from="1711518633367" duration="2351000" />
<workItem from="1711525074800" duration="90000" />
<workItem from="1711525175741" duration="553000" />
<workItem from="1711525746440" duration="138000" />
<workItem from="1711525904502" duration="187000" />
<workItem from="1711526396806" duration="5937000" />
<workItem from="1711602888442" duration="717000" />
<workItem from="1711606336704" duration="5357000" />
<workItem from="1711869672173" duration="628000" />
<workItem from="1711878111438" duration="700000" />
<workItem from="1711880635851" duration="981000" />
<workItem from="1712055178474" duration="84000" />
<workItem from="1713166182071" duration="22113000" />
<workItem from="1714581267648" duration="6000" />
<workItem from="1714884894390" duration="1477000" />
<workItem from="1715065792878" duration="1238000" />
<workItem from="1715426896536" duration="10640000" />
<workItem from="1719038043515" duration="2436000" />
<workItem from="1720441183640" duration="1046000" />
<workItem from="1720442591368" duration="302000" />
<workItem from="1721885637579" duration="1931000" />
<workItem from="1722924760522" duration="1012000" />
<workItem from="1724485712064" duration="3533000" />
<workItem from="1724504428300" duration="205000" />
<workItem from="1724504644170" duration="64000" />
<workItem from="1724504724350" duration="43000" />
<workItem from="1724504786406" duration="1429000" />
<workItem from="1724678669565" duration="55000" />
<workItem from="1724678749300" duration="2110000" />
<workItem from="1727674001765" duration="421000" />
</task>
<servers />
</component>
@@ -114,4 +174,92 @@
</map>
</option>
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/shop/views/excel_import.py</url>
<line>16</line>
<option name="timeStamp" value="2" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/shop/views/excel_import.py</url>
<line>19</line>
<option name="timeStamp" value="3" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/shop/views/excel_import.py</url>
<line>21</line>
<option name="timeStamp" value="4" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/shop/views/excel_import.py</url>
<line>23</line>
<option name="timeStamp" value="5" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/shop/views/excel_import.py</url>
<line>29</line>
<option name="timeStamp" value="6" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/shop/views/excel_import.py</url>
<line>30</line>
<option name="timeStamp" value="7" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/shop/views/excel_import.py</url>
<line>39</line>
<option name="timeStamp" value="8" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/jwt_auth/services.py</url>
<line>14</line>
<option name="timeStamp" value="13" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/jwt_auth/services.py</url>
<line>17</line>
<option name="timeStamp" value="14" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/jwt_auth/services.py</url>
<line>19</line>
<option name="timeStamp" value="15" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/jwt_auth/services.py</url>
<line>20</line>
<option name="timeStamp" value="16" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/jwt_auth/services.py</url>
<line>37</line>
<option name="timeStamp" value="17" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/jwt_auth/services.py</url>
<line>38</line>
<option name="timeStamp" value="18" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/jwt_auth/views.py</url>
<line>116</line>
<option name="timeStamp" value="19" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/apps/jwt_auth/views.py</url>
<line>118</line>
<option name="timeStamp" value="20" />
</line-breakpoint>
</breakpoints>
<default-breakpoints>
<breakpoint type="python-exception">
<properties notifyOnTerminate="true" exception="BaseException">
<option name="notifyOnTerminate" value="true" />
</properties>
</breakpoint>
</default-breakpoints>
</breakpoint-manager>
</component>
</project>
+67
View File
@@ -0,0 +1,67 @@
# ==========================================
# Stage 1: Build dependencies
# ==========================================
FROM python:3.11-slim AS builder
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
# بهینه‌سازی ۱: استفاده از کش سیستم‌عامل برای apt
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libpq-dev
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
# بهینه‌سازی ۲: استفاده از مکانیزم کش BuildKit برای pip
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --default-timeout=1000 -i https://package-mirror.liara.ir/repository/pypi/simple -r requirements.txt
# ==========================================
# Stage 2: Final Production Image
# ==========================================
FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PATH="/opt/venv/bin:$PATH"
WORKDIR /app
# بهینه‌سازی ۳: ساخت کاربر سیستم غیر ریشه در بالاترین بخش برای حفظ کِش داکر
RUN useradd -m -u 1000 django
# بهینه‌سازی ۴: استفاده از کش apt و ساخت دایرکتوری‌ها به صورت یکپارچه
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
supervisor && \
mkdir -p /app/staticfiles /app/mediafiles /etc/supervisor /var/log/supervisor && \
chown -R django:django /app /etc/supervisor /var/log/supervisor
# کپی کردن و واگذاری مالکیت محیط مجازی به کاربر django
COPY --from=builder --chown=django:django /opt/venv /opt/venv
# کپی کردن تمام کدهای پروژه با مالکیت کاربر محدود شده
COPY --chown=django:django . .
# حل مشکل خطای Permission Denied اسکریپت ورود
RUN chmod +x /app/entrypoint.sh
# سوئیچ به کاربر غیر ریشه برای امنیت پروداکشن
USER django
# اکسپوز پورت وب‌سرور برای وب‌پروکسی Coolify
EXPOSE 8000
# نقطه ورود اصلی کانتینر
ENTRYPOINT ["/app/entrypoint.sh"]
+1 -1
View File
@@ -13,7 +13,7 @@ class CUserAdmin(admin.ModelAdmin):
class CUserRoleAdmin(admin.ModelAdmin):
pass
readonly_fields = ('created_at', 'updated_at')
class RoleTypeAdmin(admin.ModelAdmin):
+1 -1
View File
@@ -24,7 +24,7 @@ class CUserManager(BaseUserManager):
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)
name_family = models.CharField(max_length=100, null=True, blank=True)
is_active = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-04-16 06:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cuser', '0011_cuserrole_customer_panel'),
]
operations = [
migrations.AddField(
model_name='cuserrole',
name='is_admin',
field=models.BooleanField(blank=True, default=None, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-07-25 05:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cuser', '0012_cuserrole_is_admin'),
]
operations = [
migrations.AlterField(
model_name='cuser',
name='name_family',
field=models.CharField(blank=True, max_length=100, null=True),
),
]
+1
View File
@@ -26,6 +26,7 @@ class CuserRole(BaseModel):
mobile = models.PositiveBigIntegerField(null=False, blank=False)
mobile_verify = models.BooleanField(default=False)
customer_panel = models.BooleanField(default=None, blank=True, null=True)
is_admin = models.BooleanField(default=None, blank=True, null=True)
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)
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-07-25 05:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('customer', '0006_alter_customernote_options'),
]
operations = [
migrations.AlterField(
model_name='customer',
name='name_family',
field=models.CharField(max_length=100),
),
]
+1 -1
View File
@@ -49,7 +49,7 @@ class CustomerGroup(BaseModel):
class Customer(BaseModel):
name_family = models.CharField(max_length=25, null=False, blank=False)
name_family = models.CharField(max_length=100, 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)
+4 -5
View File
@@ -77,10 +77,10 @@ def list_customer_v2(request):
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)
# 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)
customer_objs = CustomerModel.objects.filter(sys_ws__uuid=user_workspace_uuid)
return customer_objs
except Exception as exp:
@@ -101,7 +101,6 @@ def customer_form_item(request):
'shahr': shahr,
'responsible_groups': responsible_groups
}
return data
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-01-27 09:24
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0074_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('7652353e-6437-4f8d-aa3d-b1ff9de99aac'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-01-27 11:50
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0075_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('f97fef73-113f-42f4-bd03-893cd0a6c4cd'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-01-27 12:04
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0076_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('21a181c7-140b-4044-b43a-310712b904ba'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-02-06 10:23
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0077_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('78001c0b-3608-492f-b7c6-0a834b160c85'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-02-06 10:25
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0078_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('66663299-ba4b-46f8-8e68-62c6be077d93'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-02-06 11:44
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0079_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('b639ab0a-7a39-41f2-a2cf-d65736eb71ef'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-02-06 12:30
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0080_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('8f0b7e63-3d78-4de2-9e59-931a8734fc02'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-02-10 09:59
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0081_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('6537ef3d-8ef9-486d-a1b6-7892e021dc19'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-02-10 11:06
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0082_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('745db968-c736-42cd-ad3d-4de41e8e5b98'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-02-18 09:44
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0083_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('96211808-5203-451b-a6ce-eee394e02afc'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-02-27 09:59
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0084_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('561d07a5-fff4-436f-bab3-4e3b16e05fba'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-04-16 06:28
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0085_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('b5346fad-8d28-482d-8ed9-2807850b6a4d'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-07-25 05:38
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0086_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('0fc1ec70-e817-495e-91c9-e98666cbc2c1'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-07-25 05:42
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0087_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('35e24d20-a3c0-4e33-9307-aff890cfd593'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-08-24 13:14
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0088_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('70141bbf-e757-4eae-a6a4-7d430084c8a6'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-08-24 13:21
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0089_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('48476cd6-6133-4355-a10b-319b7de479ee'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-08-27 06:14
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0090_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('2072fa9d-9601-4c50-b1ee-bd2b437bee2c'), unique=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-08-27 06:38
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('jwt_auth', '0091_alter_cuserroleloginopt_uuid'),
]
operations = [
migrations.AlterField(
model_name='cuserroleloginopt',
name='uuid',
field=models.UUIDField(default=uuid.UUID('52471ac1-6299-4ffa-b56f-8b1e8a343d18'), unique=True),
),
]
+24 -4
View File
@@ -5,14 +5,16 @@ 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()
import requests
from datetime import timedelta
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)
@@ -52,21 +54,35 @@ def login_by_mobile(mobile: int):
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)
expire_time = timezone.now() + timedelta(minutes=3)
opt_code = SmsOptModel(mobile=mobile, opt=random_number, expire=expire_time)
opt_code.save()
sms_text = f' کد ورود شما: {random_number}'
payload = {
'USERNAME': 'vsms7937',
'PASSWORD': '335577775533',
'FROM': '30005367',
'TO': int(mobile),
'TEXT': f'{sms_text}'
}
r = requests.get('http://www.0098sms.com/sendsmslink.aspx', params=payload)
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]
time_now = timezone.now()
opt_obj = SmsOptModel.objects.filter(mobile=mobile, opt=opt, expire__gte=time_now)[0]
if opt_obj:
cuser_roles = CuserRoleModel.objects.filter(cuser__mobile=mobile, is_active=True)
@@ -87,4 +103,8 @@ def verify_mobile_and_get_token(mobile: int, opt: int) -> CUserRoleLoginOpt:
SmsOptModel.objects.filter(mobile=mobile).delete()
return login_opts
except Exception as exp:
try:
SmsOptModel.objects.filter(mobile=mobile).delete()
except Exception as exp:
pass
raise exceptions.ValidationError({'error': "cannot verify this mobile and opt code:"+str(exp)})
+4
View File
@@ -33,4 +33,8 @@ class LeadReasonAdmin(admin.ModelAdmin):
pass
@admin.register(LeadEvent)
class LeadEventAdmin(admin.ModelAdmin):
pass
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-01-27 09:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0028_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-01-27 11:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0029_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-01-27 12:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0030_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-06 10:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0031_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-06 10:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0032_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-06 11:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0033_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-06 12:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0034_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-10 09:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0035_alter_lead_status'),
]
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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-10 11:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0036_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-18 09:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0037_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-27 09:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0038_alter_lead_status'),
]
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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-04-16 06:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0039_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-07-25 05:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0040_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),
),
]
@@ -0,0 +1,33 @@
# Generated by Django 4.2.1 on 2024-07-25 05:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0041_alter_lead_status'),
]
operations = [
migrations.AlterField(
model_name='lead',
name='desc',
field=models.TextField(),
),
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),
),
migrations.AlterField(
model_name='lead',
name='title',
field=models.CharField(max_length=300),
),
migrations.AlterField(
model_name='leadcomment',
name='desc',
field=models.TextField(),
),
]
@@ -0,0 +1,33 @@
# Generated by Django 4.2.1 on 2024-08-24 13:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0042_alter_lead_desc_alter_lead_status_alter_lead_title_and_more'),
]
operations = [
migrations.AddField(
model_name='lead',
name='mobile1',
field=models.PositiveBigIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='lead',
name='mobile2',
field=models.PositiveBigIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='lead',
name='phone',
field=models.PositiveBigIntegerField(blank=True, null=True),
),
migrations.AlterField(
model_name='lead',
name='status',
field=models.CharField(blank=True, choices=[('UN', 'unknown'), ('SU', 'OK'), ('FA', 'FAIL')], default='UN', max_length=2, null=True),
),
]
+28
View File
@@ -0,0 +1,28 @@
# Generated by Django 4.2.1 on 2024-08-24 13:21
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('workspace', '0016_remove_workspace_cuser_owner'),
('lead', '0043_lead_mobile1_lead_mobile2_lead_phone_and_more'),
]
operations = [
migrations.CreateModel(
name='LeadEvent',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=50)),
('sys_ws', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workspace.workspace')),
],
options={
'abstract': False,
},
),
]
@@ -0,0 +1,24 @@
# Generated by Django 4.2.1 on 2024-08-27 06:14
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('lead', '0044_leadevent'),
]
operations = [
migrations.AddField(
model_name='lead',
name='lead_event',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='lead.leadevent'),
),
migrations.AlterField(
model_name='lead',
name='status',
field=models.CharField(blank=True, choices=[('FA', 'FAIL'), ('UN', 'unknown'), ('SU', 'OK')], default='UN', max_length=2, null=True),
),
]
@@ -0,0 +1,23 @@
# Generated by Django 4.2.1 on 2024-08-27 06:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lead', '0045_lead_lead_event_alter_lead_status'),
]
operations = [
migrations.AddField(
model_name='leadevent',
name='message',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='leadevent',
name='send_sms',
field=models.BooleanField(blank=True, default=False, null=True),
),
]
+15 -3
View File
@@ -30,6 +30,14 @@ class LeadStep(BaseModel):
def __str__(self):
return self.name
class LeadEvent(BaseModel):
name = models.CharField(max_length=50)
message = models.TextField(null=True, blank=True)
send_sms = models.BooleanField(default=False, null=True, blank=True)
sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False)
def __str__(self):
return self.name
class LeadReason(BaseModel):
name = models.CharField(max_length=50)
@@ -48,9 +56,13 @@ class Lead(BaseModel):
("UN", "unknown"),
}
title = models.CharField(max_length=100)
desc = models.CharField(max_length=300)
title = models.CharField(max_length=300)
desc = models.TextField()
mobile1 = models.PositiveBigIntegerField(null=True, blank=True)
mobile2 = models.PositiveBigIntegerField(null=True, blank=True)
phone = models.PositiveBigIntegerField(null=True, blank=True)
customer = models.ForeignKey(CustomerModel, on_delete=models.CASCADE, null=False, blank=False)
lead_event = models.ForeignKey(LeadEvent, on_delete=models.SET_NULL, null=True, blank=True)
product_type = models.ForeignKey(ProductTypeModel, on_delete=models.SET_NULL, null=True, blank=True)
type = models.ForeignKey(LeadType, on_delete=models.SET_NULL, null=True, blank=True)
source = models.ForeignKey(LeadSource, on_delete=models.SET_NULL, null=True, blank=True)
@@ -68,7 +80,7 @@ class Lead(BaseModel):
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)
desc = models.TextField()
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)
+24 -4
View File
@@ -1,9 +1,9 @@
from apps.lead.models import Lead as LeadModel, LeadType as LeadTypeModel,\
LeadSource as LeadSourceModel, LeadStep as LeadStepModel
LeadSource as LeadSourceModel, LeadStep as LeadStepModel, LeadEvent as LeadEventModel
from apps.shop.models import ProductType as ProductTypeModel
from apps.workspace.models import City as CityModel
from utils.get_user_workspace import get_user_workspace_uuid
from utils.get_cuser_role import get_cuser_role_uuid
from utils.get_cuser_role import get_cuser_role_uuid, get_cuser_role
from rest_framework import exceptions
@@ -12,7 +12,16 @@ 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')
cuser_role = get_cuser_role(request)
base_filter = {
'sys_ws__uuid': workspace_uuid,
}
#if not cuser_role.is_admin:
# base_filter['cuser_role__uuid'] = cuser_role_uuid
leads = LeadModel.objects.filter(**base_filter).order_by('-created_at')
return leads
except Exception as exp:
@@ -25,7 +34,16 @@ 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)
cuser_role = get_cuser_role(request)
base_filter = {
'sys_ws__uuid': workspace_uuid,
}
#if not cuser_role.is_admin:
# base_filter['cuser_role__uuid'] = cuser_role_uuid
lead = LeadModel.objects.get(id=id, **base_filter)
return lead
except Exception as exp:
message = f'Error retrieve Leads: {exp}'
@@ -66,6 +84,7 @@ def fields_new_lead_form(request):
product_types = ProductTypeModel.objects.filter(sys_ws__uuid=workspace_uuid)
lead_source = LeadSourceModel.objects.filter(sys_ws__uuid=workspace_uuid)
lead_step = LeadStepModel.objects.filter(sys_ws__uuid=workspace_uuid)
lead_event = LeadEventModel.objects.filter(sys_ws__uuid=workspace_uuid)
shahr = CityModel.objects.filter()
data = {
@@ -73,6 +92,7 @@ def fields_new_lead_form(request):
'lead_types': lead_type,
'lead_source': lead_source,
'lead_steps': lead_step,
'lead_event': lead_event,
'shahr': shahr,
}
return data
+8 -1
View File
@@ -1,7 +1,8 @@
from rest_framework import serializers
from apps.lead.models import (
LeadType as LeadTypeModel, LeadSource as LeadSourceModel,
LeadStep as LeadStepModel, LeadReason as LeadReasonModel
LeadStep as LeadStepModel, LeadReason as LeadReasonModel,
LeadEvent as LeadEventModel
)
from apps.shop.models import ProductType as ProductTypeModel
from apps.workspace.models import City as CityModel
@@ -39,6 +40,12 @@ class LeadReasonSerializers(serializers.ModelSerializer):
exclude = ['sys_ws', 'created_at', 'updated_at']
class LeadEventSerializers(serializers.ModelSerializer):
class Meta:
model = LeadEventModel
exclude = ['sys_ws', 'created_at', 'updated_at']
class ShahrSerializers(serializers.ModelSerializer):
class Meta:
model = CityModel
+41 -11
View File
@@ -1,16 +1,17 @@
from apps.lead.models import Lead as LeadModel,\
LeadType as LeadTypeModel, LeadStep as LeadStepModel, LeadSource as LeadSourceModel, LeadComment as LeadCommentModel
LeadType as LeadTypeModel, LeadStep as LeadStepModel, LeadSource as LeadSourceModel, LeadComment as LeadCommentModel, LeadEvent as LeadEventModel
from apps.shop.models import ProductType as ProductTypeModel
from utils.get_user_workspace import get_user_workspace
from utils.get_cuser_role import get_cuser_role
from utils.validations import ObjectValidationById
from apps.customer.models import Customer as CustomerModel
from apps.workspace.models import City as CityModel
from utils.services.sms import DefaultSmsProvider
from rest_framework import exceptions
def create_new_lead(request, title: str, customer_id: str, desc: str, product_type_id: int,
lead_type_id: int, lead_source_id: int, city_id: int, lead_step_id: int) -> LeadModel:
def create_new_lead(request, title: str, customer_id: str, mobile1: int, desc: str, product_type_id: int,
lead_type_id: int, lead_source_id: int, city_id: int, lead_step_id: int, mobile2: int = None, phone: int = None, lead_event_id: int = None) -> LeadModel:
try:
sys_ws = get_user_workspace(request=request)
cuser_role = get_cuser_role(request=request)
@@ -22,9 +23,20 @@ def create_new_lead(request, title: str, customer_id: str, desc: str, product_ty
product_type = ObjectValidationById(object_model=ProductTypeModel, request=request).is_valid(product_type_id)
city = ObjectValidationById(object_model=CityModel, request=request).is_valid(city_id)
lead_event_obj = None
new_lead = LeadModel()
new_lead.title = title
new_lead.customer = customer
if mobile1:
new_lead.mobile1 = mobile1
if mobile2:
new_lead.mobile2 = mobile2
if phone:
new_lead.phone = phone
if lead_event_id and (lead_event_id != '0' or lead_event_id != 0):
lead_event_obj = LeadEventModel.objects.get(id=lead_event_id, sys_ws=sys_ws)
new_lead.lead_event = lead_event_obj
new_lead.desc = desc
new_lead.type = lead_type
new_lead.step = lead_step
@@ -36,6 +48,14 @@ def create_new_lead(request, title: str, customer_id: str, desc: str, product_ty
new_lead.save()
# send sms
if lead_event_obj and lead_event_obj.send_sms:
sms_provider = DefaultSmsProvider()
if mobile1:
sms_provider.send_sms(str(mobile1), lead_event_obj.message)
if mobile2:
sms_provider.send_sms(str(mobile2), lead_event_obj.message)
return new_lead
except Exception as exp:
@@ -45,10 +65,10 @@ def create_new_lead(request, title: str, customer_id: str, desc: str, product_ty
def update_lead(request, lead_id: int, title: str, customer_id: str, desc: str, product_type_id: int,
lead_type_id: int, lead_source_id: int, city_id: int, lead_step_id: int) -> LeadModel:
lead_type_id: int, lead_source_id: int, city_id: int, lead_step_id: int, mobile1: int = None, mobile2: int = None, phone: int = None) -> LeadModel:
try:
sys_ws = get_user_workspace(request=request)
cuser_role = get_cuser_role(request=request)
#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)
@@ -57,9 +77,16 @@ def update_lead(request, lead_id: int, title: str, customer_id: str, desc: str,
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 = LeadModel.objects.get(id=lead_id, sys_ws=sys_ws, cuser_role=cuser_role)
lead = LeadModel.objects.get(id=lead_id, sys_ws=sys_ws)
lead.title = title
lead.customer = customer
if mobile1:
lead.mobile1 = mobile1
if mobile2:
lead.mobile2 = mobile2
if phone:
lead.phone = phone
lead.desc = desc
lead.type = lead_type
lead.step = lead_step
@@ -96,9 +123,10 @@ def update_lead_status(request, lead_id: int, status: str) -> LeadModel:
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)
#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 = LeadCommentModel.objects.get(id=lead_comment_id, sys_ws=sys_ws, cuser_role=cuser_role)
lead_comment = LeadCommentModel.objects.get(id=lead_comment_id, sys_ws=sys_ws)
lead_comment.desc = desc
lead_comment.save()
@@ -116,7 +144,8 @@ def create_lead_comment(request, lead_id, desc: str) -> LeadCommentModel:
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.lead = LeadModel.objects.get(id=lead_id, sys_ws=sys_ws, cuser_role=cuser_role)
lead_comment.lead = LeadModel.objects.get(id=lead_id, sys_ws=sys_ws)
lead_comment.sys_ws = sys_ws
lead_comment.cuser_role = cuser_role
lead_comment.desc = desc
@@ -132,9 +161,10 @@ def create_lead_comment(request, lead_id, desc: str) -> LeadCommentModel:
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)
#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 = LeadCommentModel.objects.get(id=lead_comment_id, sys_ws=sys_ws, cuser_role=cuser_role)
lead_comment = LeadCommentModel.objects.get(id=lead_comment_id, sys_ws=sys_ws)
lead_comment.delete()
return lead_comment
+14 -4
View File
@@ -4,7 +4,7 @@ from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from rest_framework import serializers
from apps.lead.serializers import LeadTypeSerializers, ProductTypeSerializers,\
LeadSourceSerializers, LeadStepSerializers, ShahrSerializers
LeadSourceSerializers, LeadStepSerializers, ShahrSerializers, LeadEventSerializers
from apps.lead.selectors import fields_new_lead_form
from apps.lead.services import create_new_lead, update_lead, update_lead_status,\
update_lead_comment, delete_lead_comment, create_lead_comment
@@ -25,6 +25,7 @@ class LeadFormItemsApi(APIView):
lead_types = LeadTypeSerializers(many=True)
product_types = ProductTypeSerializers(many=True)
lead_source = LeadSourceSerializers(many=True)
lead_event = LeadEventSerializers(many=True)
lead_steps = LeadStepSerializers(many=True)
shahr = ShahrSerializers(many=True)
@@ -41,7 +42,11 @@ class LeadCreateApi(APIView):
class InputSerializer(serializers.Serializer):
title = serializers.CharField(max_length=100, required=True)
customer_id = serializers.IntegerField(required=True)
desc = serializers.CharField(max_length=200, required=True)
desc = serializers.CharField(required=True)
mobile1 = serializers.IntegerField(required=True)
mobile2 = serializers.IntegerField(required=False)
lead_event_id = serializers.IntegerField(required=False)
phone = serializers.IntegerField(required=False)
lead_type_id = serializers.IntegerField(required=True)
product_type_id = serializers.IntegerField(required=True)
lead_source_id = serializers.IntegerField(required=True)
@@ -68,7 +73,7 @@ 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)
desc = serializers.CharField(max_length=2000, required=True)
lead_type_id = serializers.IntegerField(required=True)
product_type_id = serializers.IntegerField(required=True)
lead_source_id = serializers.IntegerField(required=True)
@@ -139,6 +144,11 @@ class LeadListApi(ListAPIView):
'ostan': inline_serializer(many=False, fields={'name': serializers.CharField() })
})
cuser_role = inline_serializer(many=False, fields={
'id': serializers.IntegerField(),
'name_family': serializers.CharField()
})
class Meta:
model = LeadModel
fields = '__all__'
@@ -223,7 +233,7 @@ class LeadRetrieveApi(APIView):
class LeadCommentCreateApi(APIView):
class InputSerializer(serializers.Serializer):
desc = serializers.CharField(max_length=200, required=True)
desc = serializers.CharField(required=True)
class OutputSerializer(serializers.Serializer):
id = serializers.IntegerField(required=True)
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-01-27 09:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0024_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('LE', 'LEAD'), ('CU', 'CUSTOMER'), ('UN', 'UNKNOWN'), ('NO', 'NOTE')], default='UN', max_length=2),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-01-27 12:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0025_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('UN', 'UNKNOWN'), ('LE', 'LEAD'), ('CU', 'CUSTOMER'), ('NO', 'NOTE')], default='UN', max_length=2),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-06 10:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0026_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('LE', 'LEAD'), ('NO', 'NOTE'), ('UN', 'UNKNOWN'), ('CU', 'CUSTOMER')], default='UN', max_length=2),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-06 10:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0027_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('LE', 'LEAD'), ('UN', 'UNKNOWN'), ('NO', 'NOTE'), ('CU', 'CUSTOMER')], default='UN', max_length=2),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-06 12:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0028_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-10 09:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0029_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('LE', 'LEAD'), ('NO', 'NOTE'), ('UN', 'UNKNOWN'), ('CU', 'CUSTOMER')], default='UN', max_length=2),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-10 11:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0030_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-18 09:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0031_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('UN', 'UNKNOWN'), ('CU', 'CUSTOMER'), ('LE', 'LEAD'), ('NO', 'NOTE')], default='UN', max_length=2),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-27 09:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0032_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('LE', 'LEAD'), ('UN', 'UNKNOWN'), ('CU', 'CUSTOMER'), ('NO', 'NOTE')], default='UN', max_length=2),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-04-16 06:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0033_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-07-25 05:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0034_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('UN', 'UNKNOWN'), ('CU', 'CUSTOMER'), ('LE', 'LEAD'), ('NO', 'NOTE')], default='UN', max_length=2),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-07-25 05:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0035_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),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-08-24 13:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0036_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('LE', 'LEAD'), ('CU', 'CUSTOMER'), ('UN', 'UNKNOWN'), ('NO', 'NOTE')], default='UN', max_length=2),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-08-24 13:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0037_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('LE', 'LEAD'), ('UN', 'UNKNOWN'), ('CU', 'CUSTOMER'), ('NO', 'NOTE')], default='UN', max_length=2),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-08-27 06:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reminder', '0038_alter_reminder_type'),
]
operations = [
migrations.AlterField(
model_name='reminder',
name='type',
field=models.CharField(choices=[('CU', 'CUSTOMER'), ('UN', 'UNKNOWN'), ('LE', 'LEAD'), ('NO', 'NOTE')], default='UN', max_length=2),
),
]
View File
Whitespace-only changes.
+41
View File
@@ -0,0 +1,41 @@
from apps.lead.models import Lead as LeadModel
from apps.cuser.models import CuserRole as CuserRoleModel, RoleType as RoleTypeModel
from rest_framework import serializers
from django.db.models import Count
from django.db.models import Count
from rest_framework import exceptions
def list_leads_api() -> LeadModel:
try:
base_filter = {
'sys_ws__uuid': 'ca918836-ddf2-4d88-8d76-033d285062e4',
}
leads = LeadModel.objects.filter(**base_filter).order_by('-created_at')
return leads
except Exception as exp:
message = f'Error Fetching List Leads: {exp}'
raise exceptions.ValidationError({'error': message})
def user_lead_report_count_by_date(start_datetime, end_datetime, status: str | None = None,):
base_filer = {
'cuser_role__role_type__role_name_en': 'workspace_operator',
'created_at__range': [start_datetime, end_datetime],
}
if status:
base_filer['status'] = status
return LeadModel.objects.filter(**base_filer).values('cuser_role__name_family').annotate(
count=Count('cuser_role__name_family')).order_by('-count')
def operators_cuser_role():
return CuserRoleModel.objects.filter(sys_ws_id=2, role_type__role_name_en='workspace_operator')
+1
View File
@@ -0,0 +1 @@
+8
View File
@@ -0,0 +1,8 @@
from django.urls import path
from apps.report.views import ReportApiView, LeadListApi
urlpatterns = [
path('lead/', ReportApiView.as_view(), name='report-lead'),
path('lead/list/loolegostar-x89i20/', LeadListApi.as_view(), name='report-lead'),
]
+115
View File
@@ -0,0 +1,115 @@
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import serializers
from rest_framework.generics import ListAPIView
from rest_framework.views import APIView
from apps.lead.models import Lead as LeadModel
from rest_framework.response import Response
from rest_framework import status
from apps.report.selector import user_lead_report_count_by_date
from apps.cuser.models import CuserRole as CuserRoleModel
from datetime import datetime
from apps.report.selector import list_leads_api
from utils.pagination import CustomPagination
from utils.tools import inline_serializer
class ReportApiView(APIView):
class ResultSerializer(serializers.Serializer):
class OperatorSerializer(serializers.Serializer):
cuser_role__name_family = serializers.CharField()
count = serializers.IntegerField()
su = OperatorSerializer(many=True)
un = OperatorSerializer(many=True)
fa = OperatorSerializer(many=True)
all = OperatorSerializer(many=True)
def get(self, request):
date_format = "%Y-%m-%d"
start_datetime = request.query_params.get('start_datetime', None)
if start_datetime:
start_datetime = start_datetime.replace(" ", "")
end_datetime = request.query_params.get('end_datetime', None)
if end_datetime:
end_datetime = end_datetime.replace(" ", "")
#example start_datetime : 2024-03-20T14:31:16 03:30
if start_datetime and end_datetime:
start_dt = datetime.strptime(start_datetime, date_format)
end_dt = datetime.strptime(end_datetime, date_format)
else:
start_dt = datetime.now()
end_dt = datetime.now()
# status = request.query_params.get('status')
#همه
users_all_lead_count = user_lead_report_count_by_date(start_datetime=start_dt, end_datetime=end_dt)
#موفق
users_su_lead_count = user_lead_report_count_by_date(status='SU', start_datetime=start_dt, end_datetime=end_dt)
#شکست
users_fa_lead_count = user_lead_report_count_by_date(status='FA', start_datetime=start_dt, end_datetime=end_dt)
#نا معلوم
users_un_lead_count = user_lead_report_count_by_date(status='UN', start_datetime=start_dt, end_datetime=end_dt)
result = {
'su': users_su_lead_count,
'fa': users_fa_lead_count,
'un': users_un_lead_count,
'all': users_all_lead_count,
}
output_serializer = self.ResultSerializer(result)
return Response(output_serializer.data)
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() })
})
cuser_role = inline_serializer(many=False, fields={
'id': serializers.IntegerField(),
'name_family': 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_api()
+1 -1
View File
@@ -22,7 +22,7 @@ class ProductCategoryAdmin(admin.ModelAdmin):
@admin.register(ProductModel)
class ProductAdmin(admin.ModelAdmin):
pass
search_fields = ("name", "category__name", "acc_code")
@admin.register(OrderModel)
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-01-27 09:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0013_alter_productcategory_image'),
]
operations = [
migrations.AlterField(
model_name='order',
name='total_price',
field=models.PositiveIntegerField(),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-01-27 11:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0014_alter_order_total_price'),
]
operations = [
migrations.AddField(
model_name='order',
name='view_at',
field=models.DateTimeField(blank=True, default=None, null=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.1 on 2024-01-27 12:04
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('shop', '0015_order_view_at'),
]
operations = [
migrations.AlterField(
model_name='orderitem',
name='order',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='order_item', to='shop.order'),
),
]
@@ -0,0 +1,23 @@
# Generated by Django 4.2.1 on 2024-02-06 10:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0016_alter_orderitem_order'),
]
operations = [
migrations.AddField(
model_name='product',
name='order_prd',
field=models.PositiveIntegerField(blank=True, default=None, null=True),
),
migrations.AddField(
model_name='productcategory',
name='order_cat',
field=models.PositiveIntegerField(blank=True, default=None, null=True),
),
]
@@ -0,0 +1,23 @@
# Generated by Django 4.2.1 on 2024-02-06 11:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0017_product_order_prd_productcategory_order_cat'),
]
operations = [
migrations.AlterField(
model_name='product',
name='name',
field=models.CharField(max_length=200),
),
migrations.AlterField(
model_name='productcategory',
name='name',
field=models.CharField(max_length=60),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-06 12:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0018_alter_product_name_alter_productcategory_name'),
]
operations = [
migrations.AlterField(
model_name='product',
name='price',
field=models.PositiveBigIntegerField(),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-27 09:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0019_alter_product_price'),
]
operations = [
migrations.AddField(
model_name='product',
name='view_only',
field=models.BooleanField(blank=True, default=False, null=True),
),
]
+11 -5
View File
@@ -1,3 +1,5 @@
from turtle import mode
from django.db import models
from utils.base_model import BaseModel
from apps.workspace.models import Workspace as WorkspaceModel
@@ -15,9 +17,10 @@ class ProductType(BaseModel):
class ProductCategory(BaseModel):
name = models.CharField(max_length=30, null=False, blank=False)
name = models.CharField(max_length=60, 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/')
order_cat = models.PositiveIntegerField(null=True, blank=True, default=None)
sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False)
def __str__(self):
@@ -25,11 +28,13 @@ class ProductCategory(BaseModel):
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)
name = models.CharField(max_length=200, null=False, blank=False)
price = models.PositiveBigIntegerField(null=False, blank=False)
category = models.ForeignKey(ProductCategory, on_delete=models.CASCADE, null=False, blank=False)
acc_code = models.CharField(null=True, blank=True)
order_prd = models.PositiveIntegerField(null=True, blank=True, default=None)
attr = models.JSONField(null=True, blank=True)
view_only = models.BooleanField(null=True, blank=True, default=False)
sys_ws = models.ForeignKey(WorkspaceModel, on_delete=models.CASCADE, null=False, blank=False)
@@ -63,17 +68,18 @@ class Order(BaseModel):
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)
total_price = models.PositiveIntegerField(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)
view_at = models.DateTimeField(null=True, blank=True, default=None)
def __str__(self):
return f'{self.customer}'
class OrderItem(BaseModel):
order = models.ForeignKey(Order, on_delete=models.CASCADE, null=False, blank=False)
order = models.ForeignKey(Order, on_delete=models.CASCADE, null=False, blank=False, related_name='order_item')
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)
+1 -1
View File
@@ -2,7 +2,7 @@ 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)
return CartModel.objects.filter(sys_ws__uuid=workspace_uuid, cuser_role__uuid=cuser_role_uuid).order_by('-created_at')
def clean_cart(cuser_role_uuid: str, workspace_uuid: str) -> CartModel:
+109 -1
View File
@@ -1,9 +1,14 @@
from apps.shop.models import Order as OrderModel
from apps.shop.models import Order as OrderModel, OrderItem as OrderItemModel
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 utils.get_user_workspace import get_user_workspace_uuid, get_user_workspace
from utils.get_cuser_role import get_cuser_role, get_cuser_role_uuid
from apps.customer.models import Customer as CustomerModel
from django.http import HttpResponse
import pandas as pd
from django.utils import timezone
def retrieve_order(cuser_role_uuid: str, order_id: int, workspace_uuid: str) -> OrderModel:
@@ -44,3 +49,106 @@ def list_order(cuser_role_uuid: str, workspace_uuid: str) -> OrderModel:
except Exception as exp:
message = f'Error Fetching List Order: {exp}'
raise exceptions.ValidationError({'error': message})
def list_order_operators(request) -> OrderModel:
try:
workspace_uuid = get_user_workspace_uuid(request)
cuser_role_uuid = get_cuser_role_uuid(request)
cuser_role = get_cuser_role(request)
customer_responsibles = CustomerResponsibleModel.objects.filter(cuser_role__uuid=cuser_role_uuid, sys_ws__uuid=workspace_uuid)
responsible_group_ids = customer_responsibles.values_list('responsible_group__id', flat=True)
base_filter = {
'sys_ws__uuid': workspace_uuid,
}
#if not cuser_role.is_admin:
# base_filter['responsible_group_id__in'] = responsible_group_ids
orders = OrderModel.objects.filter(**base_filter).order_by('-created_at')[:50]
return orders
except Exception as exp:
message = f'Error Fetching List Order: {exp}'
raise exceptions.ValidationError({'error': message})
def retrieve_order_operators(request, order_id) -> OrderModel:
try:
workspace_uuid = get_user_workspace_uuid(request)
cuser_role_uuid = get_cuser_role_uuid(request)
cuser_role = get_cuser_role(request)
customer_responsibles = CustomerResponsibleModel.objects.filter(cuser_role__uuid=cuser_role_uuid, sys_ws__uuid=workspace_uuid)
responsible_group_ids = customer_responsibles.values_list('responsible_group__id', flat=True)
base_filter = {
'sys_ws__uuid': workspace_uuid,
'id': order_id,
}
#if not cuser_role.is_admin:
# base_filter['responsible_group_id__in'] = responsible_group_ids
order = OrderModel.objects.get(**base_filter)
if not cuser_role.is_admin:
order.view_at = timezone.now()
order.save()
return order
except Exception as exp:
message = f'Error Retrive Order: {exp}'
raise exceptions.ValidationError({'error': message})
def retrieve_order_item_excel(request, order_id):
try:
workspace_uuid = get_user_workspace_uuid(request)
cuser_role_uuid = get_cuser_role_uuid(request)
customer_responsibles = CustomerResponsibleModel.objects.filter(cuser_role__uuid=cuser_role_uuid,
sys_ws__uuid=workspace_uuid)
#responsible_group_ids = customer_responsibles.values_list('responsible_group__id', flat=True)
#order_items = OrderItemModel.objects.filter(order_id=order_id,
# order__responsible_group_id__in=responsible_group_ids)
order_items = OrderItemModel.objects.filter(order_id=order_id)
df = pd.DataFrame(list(order_items.values('product__acc_code', 'product__category__name', 'product_name', 'amount', 'product__attr__acc_vahed')))
#df['واحد سنجش'] = ''
df['عامل کنترل موجودی اول'] = ''
df['عامل کنترل موجودی دوم'] = ''
df['عامل کنترل موجودی سوم'] = ''
df['عامل کنترل موجودی سوم'] = ''
df['عامل کنترل موجودی چهارم'] = ''
df['عامل کنترل موجودی پنجم'] = ''
df['محصول'] = df['product__category__name'] + df['product_name']
df = df.drop(columns=['product__category__name', 'product_name'])
df.rename(columns={
'product__acc_code': ' کد محصول',
'amount': 'مقدار',
'product__attr__acc_vahed': 'واحد سنجش'
}, inplace=True)
df = df[['محصول',
' کد محصول',
'واحد سنجش',
'عامل کنترل موجودی اول',
'عامل کنترل موجودی دوم',
'عامل کنترل موجودی سوم',
'عامل کنترل موجودی چهارم',
'عامل کنترل موجودی پنجم',
'مقدار',
]]
return df
except Exception as exp:
message = f'Error Retrive Order Items for excel: {exp}'
raise exceptions.ValidationError({'error': message})
+12 -1
View File
@@ -22,13 +22,24 @@ def list_product(workspace_uuid: str) -> ProductModel:
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)
product_category_obj = ProductModel.objects.filter(sys_ws__uuid=workspace_uuid, category_id=category_id,
view_only=False)
return product_category_obj
except Exception as exp:
message = f'Error Fetching List Product by category: {exp}'
raise exceptions.ValidationError({'error': message})
def list_view_only_product_by_category(workspace_uuid: str, category_id: int) -> ProductModel:
try:
view_only_product_category_obj = ProductModel.objects.filter(sys_ws__uuid=workspace_uuid, category_id=category_id,
view_only=True)
return view_only_product_category_obj
except Exception as exp:
message = f'Error Fetching View Only 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()
+7 -1
View File
@@ -23,7 +23,9 @@ urlpatterns = [
path('order/update/<int:order_id>/', OrderUpdateApi.as_view(), name='update_order'),
path('order/delete/<int:order_id>/', OrderDeleteApi.as_view(), name='delete_order'),
path('order/list/', OrderListApi.as_view(), name='list_order'),
path('order/list-operator/', OrderListOperatorApi.as_view(), name='list_order_operator'),
path('order/<int:order_id>/operator/', OrderRetrieveOperatorApi.as_view(), name='order_retrieve_operator'),
path('order/<int:order_id>/operator/excel/', OrderRetrieveOperatorExcelApi.as_view(), name='order_retrieve_operator_excel'),
path('order/iteams/list/<int:order_id>/', OrderItemsListApi.as_view(), name='list_order_items'),
@@ -31,4 +33,8 @@ urlpatterns = [
path('cart/', CartListApi.as_view(), name='list_cart'),
path('cart/add/', CartCreateApi.as_view(), name='add_cart'),
path('cart/delete/<int:cart_id>/', CartDeleteApi.as_view(), name='delete_cart'),
path('import/', ImportExcelView.as_view(), name='import_excel'),
path('import/customer/', ImportExcelCustomersView.as_view(), name='import_excel_customer'),
#path('import/city/', ImportExcelCitysView.as_view(), name='import_excel_city'),
]
+3
View File
@@ -8,9 +8,12 @@ from .product import (
)
from .order import (
OrderListOperatorApi, OrderRetrieveOperatorApi, OrderRetrieveOperatorExcelApi,
OrderCreateApi, OrderUpdateApi, OrderDeleteApi, OrderRetrieveApi, OrderListApi, OrderAndItemCreateApi
)
from .cart import (CartListApi, CartCreateApi, CartDeleteApi)
from .order_item import (OrderItemsListApi, )
from .excel_import import ImportExcelView, ImportExcelCitysView, ImportExcelCustomersView
+114
View File
@@ -0,0 +1,114 @@
from rest_framework.views import APIView
from rest_framework.response import Response
import pandas as pd
from apps.shop.models import Product as ProductModel, ProductCategory as ProductCategoryModel
from apps.workspace.models import Workspace as WorkspaceModel, City as CityModel
from apps.customer.models import Customer as CustomerModel
from apps.customer.models import ResponsibleGroup as ResponsibleGroupModel
import os
class ImportExcelView(APIView):
def get(self, request):
df = pd.read_excel('pe.xlsx')
for index, row in df.iterrows():
cate_name = row['cate_name']
#order_cat = row['order_cat']
acc_code = row['acc_code']
prd_name = row['name']
order_prd = row['size']
vahed = row['vahed']
workspace = WorkspaceModel.objects.get(id=2)
try:
prd_ct = ProductCategoryModel.objects.get(name=cate_name, sys_ws=workspace)
except ProductCategoryModel.DoesNotExist as exp:
prd_ct = ProductCategoryModel.objects.create(name=cate_name, sys_ws=workspace)
if ProductModel.objects.filter(acc_code=acc_code).exists() == False:
new_product = ProductModel()
new_product.name = prd_name
new_product.price = 0
new_product.category = ProductCategoryModel.objects.get(id=prd_ct.id)
new_product.acc_code = acc_code
new_product.order_prd = order_prd
new_product.view_only = True
new_product.attr = {"acc_vahed": vahed}
new_product.sys_ws = workspace
new_product.save()
else:
up_product = ProductModel.objects.get(acc_code=acc_code)
up_product.name = prd_name
up_product.save()
return Response(data={"result": 'ok'})
class ImportExcelCitysView(APIView):
def get(self, request):
df = pd.read_excel('cities.xlsx')
for index, row in df.iterrows():
city_name = row['name']
pv_id = row['province_id']
try:
ostan = CityModel.objects.get(pv_id=pv_id, is_ostan=True)
except Exception as exp:
print(city_name)
print(pv_id)
city_obj = CityModel()
city_obj.name = city_name
city_obj.is_ostan = False
city_obj.ostan = ostan
city_obj.save()
return Response(data={"result": 'ok'})
class ImportExcelCustomersView(APIView):
def get(self, request):
df = pd.read_excel('customersData.xlsx')
for index, row in df.iterrows():
name = row['name']
mobile = row['mobile']
if name and mobile:
phone = row['phone']
address = row['address']
ostan = row['ostan']
shahr = row['shar']
mantaghe = row['id_mantaghe']
try:
ostan_obj = CityModel.objects.get(name=ostan, is_ostan=True)
shahr_obj = CityModel.objects.get(name=shahr, ostan=ostan_obj)
except Exception as exp:
shahr_obj = CityModel.objects.get(id=1229)
try:
cs = CustomerModel()
cs.name_family = name
cs.mobile = mobile
if phone:
cs.phone = phone
cs.city = shahr_obj
if address:
cs.address = address
cs.responsible_group = ResponsibleGroupModel.objects.get(id=mantaghe)
cs.sys_ws = WorkspaceModel.objects.get(id=2)
cs.save()
except Exception as exp:
print(name)
return Response(data={"result": 'ok'})
+79 -2
View File
@@ -11,6 +11,9 @@ from rest_framework import generics
from apps.customer.models import Customer as CustomerModel
from rest_framework.permissions import IsAuthenticated
from utils import custom_permissions
from utils.tools import inline_serializer
from io import BytesIO
class OrderCreateApi(APIView):
@@ -121,7 +124,7 @@ class OrderUpdateApi(APIView):
class OrderDeleteApi(APIView):
permission_classes = (IsAuthenticated,)
def delete(self, request, order_id: int):
user_workspace_uuid = get_user_workspace_uuid(request=request)
@@ -136,7 +139,7 @@ class OrderDeleteApi(APIView):
class OrderListApi(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
class OutputSerializer(serializers.ModelSerializer):
class Meta:
model = OrderModel
@@ -150,3 +153,77 @@ class OrderListApi(generics.ListAPIView):
return list_order(cuser_role_uuid=cuser_role_uuid, workspace_uuid=user_workspace_uuid)
class OrderListOperatorApi(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
class OutputSerializer(serializers.ModelSerializer):
customer = inline_serializer(fields={
'id': serializers.IntegerField(),
'name_family': serializers.CharField(),
})
class Meta:
model = OrderModel
exclude = ('sys_ws', )
serializer_class = OutputSerializer
def get_queryset(self):
return list_order_operators(self.request)
class OrderRetrieveOperatorApi(APIView):
permission_classes = (IsAuthenticated,)
class OutputSerializer(serializers.ModelSerializer):
customer = inline_serializer(fields={
'id': serializers.IntegerField(),
'name_family': serializers.CharField(),
})
order_item = inline_serializer(many=True, fields={
'id': serializers.IntegerField(),
'product': inline_serializer(fields={
'id': serializers.IntegerField(),
'name': serializers.CharField(),
'category': inline_serializer(fields={
'id': serializers.IntegerField(),
'name': serializers.CharField(),
})
}),
'product_name': serializers.CharField(),
'price': serializers.IntegerField(),
'amount': serializers.IntegerField(),
})
class Meta:
model = OrderModel
exclude = ('sys_ws', )
serializer_class = OutputSerializer
def get(self, request, order_id):
order = retrieve_order_operators(request, order_id=order_id)
output = self.OutputSerializer(order)
return Response(data=output.data, status=status.HTTP_200_OK)
class OrderRetrieveOperatorExcelApi(APIView):
permission_classes = (IsAuthenticated, )
def get(self, request, order_id):
order_df = retrieve_order_item_excel(request=request, order_id=order_id)
excel_file = BytesIO()
order_df.to_excel(excel_file, engine='xlsxwriter', index=False)
excel_file.seek(0)
response = HttpResponse(excel_file.read(), content_type='application/ms-excel')
response['Content-Disposition'] = 'attachment; filename="output.xlsx"'
return response
+10 -1
View File
@@ -134,6 +134,7 @@ class ProductListCategoryApi(generics.ListAPIView):
id = serializers.IntegerField()
name = serializers.CharField()
price = serializers.FloatField()
view_only = serializers.BooleanField()
class Meta:
exclude = ('sys_ws', )
@@ -144,6 +145,8 @@ class ProductListCategoryApi(generics.ListAPIView):
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)
view_only_products = list_view_only_product_by_category(category_id=category_id, workspace_uuid=workspace_uuid)
view_only_products_serializer = self.get_serializer(view_only_products, many=True)
if len(serializer.data) == 0:
return Response(
@@ -151,9 +154,15 @@ class ProductListCategoryApi(generics.ListAPIView):
)
data = {
"category_name": category.name,
"products": serializer.data
"category_img": category.image.url,
"products": serializer.data,
}
if len(view_only_products_serializer.data) >=1:
data['view_only_products'] = view_only_products_serializer.data
else:
data['view_only_products'] = None
return Response(data)
def get_queryset(self):
+1 -1
View File
@@ -156,7 +156,7 @@ class ProductsCategoryApi(APIView):
def get(self, request):
p_cate = ProductCategoryModel.objects.annotate(product_count=Count('product')).filter(product_count__gte=1)
p_cate = ProductCategoryModel.objects.annotate(product_count=Count('product')).filter(product_count__gte=1).order_by('order_cat')
output = self.OutputSerializer(p_cate, many=True)
return Response(data=output.data, status=status.HTTP_200_OK)
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-07-25 05:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ticket', '0008_alter_ticketcommentfile_ticket_comment'),
]
operations = [
migrations.AlterField(
model_name='ticketcomment',
name='desc',
field=models.TextField(),
),
]
+1 -1
View File
@@ -25,7 +25,7 @@ class Ticket(BaseModel):
class TicketComment(BaseModel):
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, null=False, blank=False)
desc = models.CharField(max_length=500)
desc = models.TextField()
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)
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2024-02-10 09:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('workspace', '0013_alter_workspace_cuser_owner'),
]
operations = [
migrations.AddField(
model_name='city',
name='pv_id',
field=models.PositiveSmallIntegerField(blank=True, null=True),
),
]
Loaded 100 of 115 files, more files were not shown because too many files have changed in this diff. Show more