Compare commits
88 Commits
0de50ec328
...
behruz
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25e92623fd | ||
|
|
cb795fe3b8 | ||
|
|
9a730fa0a3 | ||
|
|
e665939b1b | ||
|
|
928561be51 | ||
|
|
9dcce296a6 | ||
|
|
4ac21100a3 | ||
|
|
b0d5a2f334 | ||
| ba3d6c4e47 | |||
| f3834fad70 | |||
|
|
269817f25e | ||
|
|
8158d7146e | ||
|
|
617bed99ae | ||
|
|
9b782fe7bd | ||
| 135f580db2 | |||
|
|
0c622759cc | ||
|
|
1d750b1c1c | ||
| 2b26c52a5c | |||
|
|
51b3535a80 | ||
|
|
9028e2f102 | ||
|
|
0c9c726756 | ||
|
|
c88ea1aa77 | ||
|
|
581021cbb7 | ||
| 62f65385e1 | |||
|
|
76d2fe5090 | ||
| 92d23901a1 | |||
|
|
42987e4154 | ||
| 84b14da3f4 | |||
|
|
1ff23af8bf | ||
|
|
feecb580c1 | ||
|
|
cb53924f9b | ||
|
|
f53125cfdc | ||
| 65ab51e652 | |||
| 2997810fae | |||
|
|
d014f5a2fb | ||
|
|
7d49929772 | ||
|
|
c29546a04b | ||
|
|
b39c080de3 | ||
|
|
7ad385af94 | ||
| 3781ce29e5 | |||
|
|
db7e34c1c2 | ||
|
|
1cb9551e81 | ||
|
|
51b30c2cc4 | ||
|
|
dc4c98bfc9 | ||
|
|
abed9e59b4 | ||
| f238c92a09 | |||
|
|
113f2da120 | ||
|
|
99b265f68f | ||
| c5d60e799c | |||
|
|
7829c9c625 | ||
|
|
7f462674a8 | ||
| f7be3be5d2 | |||
|
|
557f9f821d | ||
|
|
5f70d69896 | ||
|
|
4ea7070a8f | ||
| 8b02f3a3a3 | |||
|
|
f0d93b10ac | ||
|
|
172ddf4da4 | ||
| 435dd56334 | |||
|
|
779c9db924 | ||
| eaaba123b0 | |||
|
|
63c4ad81eb | ||
|
|
d065891ad5 | ||
| 94c4d03925 | |||
|
|
4a958f064b | ||
|
|
d1f0a5a9ae | ||
| 0084d11c62 | |||
|
|
d1340cdd52 | ||
|
|
d7ea1acba6 | ||
| 560cbe8000 | |||
|
|
37d6a93529 | ||
|
|
e1b445d515 | ||
| ef87112c79 | |||
|
|
8c01c1dc2d | ||
|
|
921b54ab7c | ||
|
|
a74c348187 | ||
| 52fab30588 | |||
|
|
e346546d24 | ||
| e97c6c7ab2 | |||
|
|
f7706e77ee | ||
| affd3e1221 | |||
|
|
3ac6263035 | ||
| 2c6d7dd2f7 | |||
|
|
b64073e1ad | ||
| e3ffdddc46 | |||
| faea9bdb89 | |||
|
|
1dd1a132e4 | ||
| 04e193bae6 |
@@ -23,7 +23,7 @@ DB_ENGINE=django.db.backends.postgresql_psycopg2
|
||||
DB_NAME=django
|
||||
DB_USER=postgres
|
||||
DB_PASSWORD=2309
|
||||
DB_HOST=db
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
|
||||
# Cache
|
||||
|
||||
5
.github/workflows/deploy.yaml
vendored
5
.github/workflows/deploy.yaml
vendored
@@ -151,6 +151,11 @@ jobs:
|
||||
git fetch origin main
|
||||
git reset --hard origin/main
|
||||
|
||||
if [ ! -f .env ]; then
|
||||
cp .env.example .env
|
||||
echo ".env yaratildi, production qiymatlarini kiriting!"
|
||||
fi
|
||||
|
||||
export PORT=8085
|
||||
docker pull ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.run_number }}
|
||||
docker stack deploy -c stack.yaml ${{ env.PROJECT_NAME }} --with-registry-auth
|
||||
|
||||
@@ -14,6 +14,8 @@ APPS = [
|
||||
"rest_framework_simplejwt",
|
||||
"django_core",
|
||||
"core.apps.accounts.apps.AccountsConfig",
|
||||
'core.apps.tasks.apps.TasksConfig',
|
||||
'core.apps.documents.apps.DocumentsConfig',
|
||||
]
|
||||
|
||||
if env.bool("SILK_ENABLED", False):
|
||||
|
||||
@@ -186,5 +186,57 @@ PAGES = [
|
||||
"link": reverse_lazy("admin:shared_villagemodel_changelist"),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": _("Ruxsatlar"),
|
||||
"separator": True,
|
||||
"items": [
|
||||
{
|
||||
"title": _("Ruxsatlar"),
|
||||
"icon": "attach_file",
|
||||
"link": reverse_lazy("admin:accounts_permission_changelist"),
|
||||
},
|
||||
{
|
||||
"title": _("Sahifa uchun ruxsatlar"),
|
||||
"icon": "attach_file",
|
||||
"link": reverse_lazy("admin:accounts_permissiontotab_changelist"),
|
||||
},
|
||||
{
|
||||
"title": _("Actionlar uchun ruxsatlar"),
|
||||
"icon": "attach_file",
|
||||
"link": reverse_lazy("admin:accounts_permissiontoaction_changelist"),
|
||||
},
|
||||
{
|
||||
"title": _("Role"),
|
||||
"icon": "attach_file",
|
||||
"link": reverse_lazy("admin:accounts_role_changelist"),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": _("Task Management"),
|
||||
"separator": True,
|
||||
"items": [
|
||||
{
|
||||
"title": _("Task"),
|
||||
"icon": "task",
|
||||
"link": reverse_lazy("admin:tasks_task_changelist"),
|
||||
},
|
||||
{
|
||||
"title": _("Column"),
|
||||
"icon": "tag",
|
||||
"link": reverse_lazy("admin:tasks_column_changelist"),
|
||||
},
|
||||
{
|
||||
"title": _("Comment"),
|
||||
"icon": "message",
|
||||
"link": reverse_lazy("admin:tasks_comment_changelist"),
|
||||
},
|
||||
{
|
||||
"title": _("Label"),
|
||||
"icon": "tag",
|
||||
"link": reverse_lazy("admin:tasks_label_changelist"),
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -13,7 +13,7 @@ from config.env import env
|
||||
|
||||
|
||||
def home(request):
|
||||
return HttpResponse("OK: #3ede209e52cd3cbd23fc608c3aa250f637882258")
|
||||
return HttpResponse("OK: #ba3d6c4e475ba59a6c3dea6f204e435f08e6ab3c")
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
@@ -23,6 +23,8 @@ urlpatterns = [
|
||||
path("api/v1/", include("core.apps.evaluation.urls")),
|
||||
path("api/v1/", include("core.apps.payment.urls")),
|
||||
path("api/v1/", include("core.apps.chat.urls")),
|
||||
path("api/v1/tasks/", include("core.apps.tasks.urls")),
|
||||
path("api/v1/documents/", include("core.apps.documents.urls")),
|
||||
]
|
||||
urlpatterns += [
|
||||
path("admin/", admin.site.urls),
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
from .core import * # noqa
|
||||
from .user import * # noqa
|
||||
from .permission import *
|
||||
|
||||
77
core/apps/accounts/admin/permission.py
Normal file
77
core/apps/accounts/admin/permission.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from django.contrib import admin
|
||||
from core.apps.accounts.models.permission import (
|
||||
PermissionToAction,
|
||||
PermissionToTab,
|
||||
Permission,
|
||||
Role,
|
||||
)
|
||||
|
||||
@admin.register(PermissionToAction)
|
||||
class PermissionToActionAdmin(admin.ModelAdmin):
|
||||
list_display = ("id", "name", "code", "created_at")
|
||||
search_fields = ("name", "code")
|
||||
|
||||
fieldsets = (
|
||||
("Asosiy", {
|
||||
"fields": ("name", "code"),
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(PermissionToTab)
|
||||
class PermissionToTabAdmin(admin.ModelAdmin):
|
||||
list_display = ("id", "name", "code", "created_at")
|
||||
search_fields = ("name", "code")
|
||||
filter_horizontal = ("permission_to_actions",)
|
||||
|
||||
fieldsets = (
|
||||
("Asosiy", {
|
||||
"fields": ("name", "code"),
|
||||
}),
|
||||
("Harakatlar", {
|
||||
"fields": ("permission_to_actions",),
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Permission)
|
||||
class PermissionAdmin(admin.ModelAdmin):
|
||||
list_display = ("id", "name", "code", "created_at")
|
||||
search_fields = ("name", "code")
|
||||
filter_horizontal = ("permission_tabs",)
|
||||
|
||||
fieldsets = (
|
||||
("Asosiy", {
|
||||
"fields": ("name", "code"),
|
||||
}),
|
||||
("Bog‘lanishlar", {
|
||||
"fields": ("permission_tabs",),
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Role)
|
||||
class RoleAdmin(admin.ModelAdmin):
|
||||
list_display = ("id", "name")
|
||||
search_fields = ("name",)
|
||||
|
||||
filter_horizontal = (
|
||||
"permissions",
|
||||
"permission_to_tabs",
|
||||
"permission_to_actions",
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
("Asosiy ma'lumotlar", {
|
||||
"fields": ("name", "comment"),
|
||||
}),
|
||||
("Sahifa ruxsatlari", {
|
||||
"fields": ("permissions",),
|
||||
}),
|
||||
("Bo‘lim ruxsatlari", {
|
||||
"fields": ("permission_to_tabs",),
|
||||
}),
|
||||
("Harakat ruxsatlari", {
|
||||
"fields": ("permission_to_actions",),
|
||||
}),
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.7 on 2026-04-28 11:02
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0004_permissiontoaction_permissiontotab_permission_role_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='permission',
|
||||
old_name='permission_tab',
|
||||
new_name='permission_tabs',
|
||||
),
|
||||
]
|
||||
@@ -33,7 +33,7 @@ class PermissionToTab(AbstractBaseModel):
|
||||
class Permission(AbstractBaseModel):
|
||||
name = models.CharField(max_length=200)
|
||||
code = models.CharField(max_length=100, unique=True)
|
||||
permission_tab = models.ManyToManyField(PermissionToTab, related_name='permissions')
|
||||
permission_tabs = models.ManyToManyField(PermissionToTab, related_name='permissions')
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.name} - {self.code}'
|
||||
|
||||
15
core/apps/accounts/permissions.py
Normal file
15
core/apps/accounts/permissions.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.permissions import BasePermission
|
||||
|
||||
from core.apps.accounts.choices import RoleChoice
|
||||
|
||||
|
||||
class IsAdminRole(BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
if not request.user.is_authenticated:
|
||||
return False
|
||||
|
||||
if request.user.role != RoleChoice.ADMIN:
|
||||
raise PermissionDenied("Only admin can access this")
|
||||
|
||||
return True
|
||||
@@ -6,21 +6,50 @@ from core.apps.accounts.models.permission import PermissionToAction, PermissionT
|
||||
class PermissionToActionSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = PermissionToAction
|
||||
fields = "__all__"
|
||||
fields = ['id', 'name']
|
||||
|
||||
|
||||
class PermissionToTabSerializer(serializers.ModelSerializer):
|
||||
permission_to_actions = PermissionToActionSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PermissionToTab
|
||||
fields = '__all__'
|
||||
fields = ['id', 'name', 'permission_to_actions']
|
||||
|
||||
|
||||
class PermissionSerializer(serializers.ModelSerializer):
|
||||
permission_tabs = PermissionToTabSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Permission
|
||||
fields = '__all__'
|
||||
fields = ['id', 'name', 'permission_tabs']
|
||||
|
||||
|
||||
class RoleSerializer(serializers.ModelSerializer):
|
||||
class PermissionToActionListSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = PermissionToAction
|
||||
fields = ['id', 'name']
|
||||
|
||||
|
||||
class PermissionToTabListSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = PermissionToTab
|
||||
fields = ['id', 'name']
|
||||
|
||||
|
||||
class PermissionListSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Permission
|
||||
fields = ['id', 'name']
|
||||
|
||||
|
||||
class RoleListSerializer(serializers.ModelSerializer):
|
||||
permissions = PermissionListSerializer(many=True)
|
||||
permission_to_tabs = PermissionToTabListSerializer(many=True)
|
||||
permission_to_actions = PermissionToActionListSerializer(many=True)
|
||||
|
||||
class Meta:
|
||||
model = Role
|
||||
fields = '__all__'
|
||||
fields = [
|
||||
'id', 'name', 'comment', 'permissions', 'permission_to_tabs', 'permission_to_actions',
|
||||
]
|
||||
|
||||
@@ -54,4 +54,22 @@ class UserCreateSerializer(serializers.ModelSerializer):
|
||||
"first_name",
|
||||
"last_name",
|
||||
"password",
|
||||
"role"]
|
||||
"role"
|
||||
]
|
||||
|
||||
|
||||
class ShortUserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = get_user_model()
|
||||
fields = [
|
||||
'id',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'avatar',
|
||||
]
|
||||
|
||||
def get_avatar(self, obj):
|
||||
request = self.context.get('request')
|
||||
if obj.avatar:
|
||||
return request.build_absolute_uri(obj.avatar.url)
|
||||
return None
|
||||
@@ -9,6 +9,7 @@ from .views import RegisterView, ResetPasswordView, MeView, ChangePasswordView,
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from .views.permission import PermissionToActionViewSet, PermissionToTabViewSet, PermissionViewSet, RoleViewSet
|
||||
from core.apps.accounts.views.user import DeleteAdminUserApiView, UserDetailAPIView
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register("auth", RegisterView, basename="auth")
|
||||
@@ -26,9 +27,11 @@ urlpatterns = [
|
||||
path("", include(router.urls)),
|
||||
path("auth/token/", jwt_views.TokenObtainPairView.as_view(), name="token_obtain_pair"),
|
||||
path("auth/token/verify/", jwt_views.TokenVerifyView.as_view(), name="token_verify"),
|
||||
path("auth/token/refresh/",jwt_views.TokenRefreshView.as_view()),
|
||||
path("auth/token/refresh/", jwt_views.TokenRefreshView.as_view()),
|
||||
path("user/list/", UserListApiView.as_view(), name="user-list"),
|
||||
path("admin-user/list/", AdminUserListApiView.as_view(), name="admin-user-list"),
|
||||
path("admin/create/", AdminCreateAPIView.as_view(), name="user-create"),
|
||||
path("admin/update/", AdminUpdateAPIView.as_view(), name="user-update"),
|
||||
path("admin/update/<int:pk>/", AdminUpdateAPIView.as_view(), name="user-update"),
|
||||
path('user/admin/<int:pk>/delete/', DeleteAdminUserApiView.as_view(), name='user-delete'),
|
||||
path('user/<int:pk>/', UserDetailAPIView.as_view(), name='user-detail'),
|
||||
]
|
||||
|
||||
@@ -5,7 +5,7 @@ from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from core.apps.accounts.models.permission import PermissionToAction, PermissionToTab, Permission, Role
|
||||
from core.apps.accounts.serializers.permission import PermissionToActionSerializer, PermissionToTabSerializer, \
|
||||
PermissionSerializer, RoleSerializer
|
||||
PermissionSerializer, RoleListSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["permission"])
|
||||
@@ -30,7 +30,6 @@ class PermissionToTabViewSet(BaseViewSetMixin, ModelViewSet):
|
||||
serializer_class = PermissionToTabSerializer
|
||||
|
||||
|
||||
|
||||
@extend_schema(tags=["permission"])
|
||||
class PermissionViewSet(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = Permission.objects.all()
|
||||
@@ -39,4 +38,4 @@ class PermissionViewSet(BaseViewSetMixin, ModelViewSet):
|
||||
|
||||
class RoleViewSet(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = Role.objects.all()
|
||||
serializer_class = RoleSerializer
|
||||
serializer_class = RoleListSerializer
|
||||
|
||||
@@ -9,6 +9,8 @@ from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from core.apps.accounts.choices.user import RoleChoice
|
||||
from core.apps.accounts.models import Role
|
||||
from core.apps.accounts.serializers.permission import RoleListSerializer
|
||||
from core.apps.accounts.serializers.user import UserSerializer, AdminUserSerializer, UserCreateSerializer
|
||||
|
||||
User = get_user_model()
|
||||
@@ -64,11 +66,10 @@ class AdminCreateAPIView(APIView):
|
||||
return Response(serializer.data, status=201)
|
||||
|
||||
|
||||
@extend_schema(tags=['User'],
|
||||
responses={200: UserSerializer},
|
||||
request=UserCreateSerializer)
|
||||
class AdminUpdateAPIView(APIView):
|
||||
@extend_schema(tags=['User'], )
|
||||
class AdminUpdateAPIView(generics.GenericAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = UserCreateSerializer
|
||||
|
||||
def put(self, request, pk):
|
||||
if request.user.role not in (RoleChoice.SUPERUSER, RoleChoice.ADMIN):
|
||||
@@ -80,3 +81,32 @@ class AdminUpdateAPIView(APIView):
|
||||
serializer.save()
|
||||
|
||||
return Response(serializer.data, status=200)
|
||||
|
||||
|
||||
class DeleteAdminUserApiView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def delete(self, request, pk):
|
||||
user = get_object_or_404(User, pk=pk)
|
||||
user.delete()
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
class UserDetailAPIView(generics.RetrieveAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = UserSerializer
|
||||
lookup_field = 'id'
|
||||
|
||||
|
||||
class AdminPermissionsAPIView(generics.GenericAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
queryset = User.objects.all()
|
||||
|
||||
def get(self, request):
|
||||
if request.user.role.name != RoleChoice.ADMIN:
|
||||
return Response({'detail': 'Forbidden'}, status=403)
|
||||
|
||||
admin_role = Role.objects.get(name=RoleChoice.ADMIN)
|
||||
|
||||
serializer = RoleListSerializer(admin_role)
|
||||
return Response(serializer.data)
|
||||
0
core/apps/documents/__init__.py
Normal file
0
core/apps/documents/__init__.py
Normal file
5
core/apps/documents/apps.py
Normal file
5
core/apps/documents/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DocumentsConfig(AppConfig):
|
||||
name = "core.apps.documents"
|
||||
0
core/apps/documents/migrations/__init__.py
Normal file
0
core/apps/documents/migrations/__init__.py
Normal file
7
core/apps/documents/urls.py
Normal file
7
core/apps/documents/urls.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.urls import path
|
||||
|
||||
from core.apps.documents.views.contract import ValuationReportPDFView
|
||||
|
||||
urlpatterns = [
|
||||
path('generate-contract-pdf/<int:pk>/', ValuationReportPDFView.as_view(), name='generate_contract_pdf'),
|
||||
]
|
||||
387
core/apps/documents/views/contract.py
Normal file
387
core/apps/documents/views/contract.py
Normal file
@@ -0,0 +1,387 @@
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template.loader import render_to_string
|
||||
from django.http import HttpResponse
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from weasyprint import HTML
|
||||
|
||||
from core.apps.evaluation.models import AutoEvaluationModel
|
||||
|
||||
|
||||
UZ_MONTHS = {
|
||||
1: "yanvar", 2: "fevral", 3: "mart", 4: "aprel",
|
||||
5: "may", 6: "iyun", 7: "iyul", 8: "avgust",
|
||||
9: "sentabr", 10: "oktabr", 11: "noyabr", 12: "dekabr",
|
||||
}
|
||||
|
||||
UZ_ONES = [
|
||||
"", "bir", "ikki", "uch", "to'rt", "besh",
|
||||
"olti", "yetti", "sakkiz", "to'qqiz",
|
||||
]
|
||||
UZ_TENS = [
|
||||
"", "o'n", "yigirma", "o'ttiz", "qirq", "ellik",
|
||||
"oltmish", "yetmish", "sakson", "to'qson",
|
||||
]
|
||||
|
||||
|
||||
def _format_currency(value):
|
||||
if value is None:
|
||||
return "0"
|
||||
try:
|
||||
int_val = int(Decimal(value))
|
||||
except (ValueError, TypeError):
|
||||
return "0"
|
||||
return f"{int_val:,}".replace(",", " ")
|
||||
|
||||
|
||||
def _format_date(value):
|
||||
if not value:
|
||||
return ""
|
||||
return value.strftime("%d.%m.%Y")
|
||||
|
||||
|
||||
def _three_digit_words(num):
|
||||
if num == 0:
|
||||
return ""
|
||||
words = []
|
||||
hundreds = num // 100
|
||||
rest = num % 100
|
||||
if hundreds:
|
||||
if hundreds == 1:
|
||||
words.append("bir yuz")
|
||||
else:
|
||||
words.append(f"{UZ_ONES[hundreds]} yuz")
|
||||
tens = rest // 10
|
||||
ones = rest % 10
|
||||
if tens:
|
||||
words.append(UZ_TENS[tens])
|
||||
if ones:
|
||||
words.append(UZ_ONES[ones])
|
||||
return " ".join(words)
|
||||
|
||||
|
||||
def _number_to_uzbek_words(value):
|
||||
if value is None:
|
||||
return ""
|
||||
try:
|
||||
num = int(Decimal(value))
|
||||
except (ValueError, TypeError):
|
||||
return ""
|
||||
if num == 0:
|
||||
return "nol"
|
||||
|
||||
parts = []
|
||||
billions = num // 1_000_000_000
|
||||
millions = (num % 1_000_000_000) // 1_000_000
|
||||
thousands = (num % 1_000_000) // 1_000
|
||||
rest = num % 1_000
|
||||
|
||||
if billions:
|
||||
parts.append(f"{_three_digit_words(billions)} milliard")
|
||||
if millions:
|
||||
parts.append(f"{_three_digit_words(millions)} million")
|
||||
if thousands:
|
||||
parts.append(f"{_three_digit_words(thousands)} ming")
|
||||
if rest:
|
||||
parts.append(_three_digit_words(rest))
|
||||
|
||||
text = " ".join(parts).strip()
|
||||
return text[0].upper() + text[1:] if text else ""
|
||||
|
||||
|
||||
class ValuationReportPDFView(APIView):
|
||||
"""
|
||||
Baholash hisobotini PDF formatida yuklab olish uchun API.
|
||||
|
||||
GET /api/documents/generate-contract-pdf/<pk>/
|
||||
pk — AutoEvaluationModel id si.
|
||||
"""
|
||||
|
||||
def get(self, request, pk, *args, **kwargs):
|
||||
return self._generate_pdf(request, pk)
|
||||
|
||||
def post(self, request, pk, *args, **kwargs):
|
||||
return self._generate_pdf(request, pk)
|
||||
|
||||
def _generate_pdf(self, request, pk):
|
||||
auto_evaluation = get_object_or_404(
|
||||
AutoEvaluationModel.objects.select_related(
|
||||
"vehicle",
|
||||
"vehicle__brand",
|
||||
"vehicle__model",
|
||||
"vehicle__color",
|
||||
"vehicle__fuel_type",
|
||||
"vehicle__body_type",
|
||||
"valuation",
|
||||
"valuation__customer",
|
||||
"valuation__property_owner",
|
||||
),
|
||||
pk=pk,
|
||||
)
|
||||
|
||||
context = self._build_context(auto_evaluation)
|
||||
|
||||
html_string = render_to_string("documents/contract.html", context)
|
||||
base_url = request.build_absolute_uri("/")
|
||||
|
||||
try:
|
||||
pdf_file = HTML(string=html_string, base_url=base_url).write_pdf()
|
||||
except Exception as e:
|
||||
return Response(
|
||||
{"error": f"PDF yaratishda xatolik: {str(e)}"},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
|
||||
report_number = context["report"]["number"]
|
||||
filename = f"baholash_hisoboti_{report_number}.pdf"
|
||||
|
||||
response = HttpResponse(pdf_file, content_type="application/pdf")
|
||||
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||
response["Content-Length"] = len(pdf_file)
|
||||
return response
|
||||
|
||||
def _build_context(self, auto):
|
||||
vehicle = auto.vehicle
|
||||
valuation = auto.valuation
|
||||
customer = valuation.customer if valuation else None
|
||||
owner = valuation.property_owner if valuation and valuation.property_owner else customer
|
||||
report = getattr(valuation, "report", None) if valuation else None
|
||||
|
||||
report_date = (
|
||||
auto.rate_report_date
|
||||
or auto.contract_date
|
||||
or (report.created_at.date() if report else None)
|
||||
or date.today()
|
||||
)
|
||||
valuation_date = auto.rate_date or report_date
|
||||
inspection_date = auto.object_inspection_date or report_date
|
||||
|
||||
report_number = (
|
||||
(report.report_number if report else None)
|
||||
or auto.registration_number
|
||||
or (valuation.conclusion_number if valuation else None)
|
||||
or f"{auto.pk}/{report_date.year}"
|
||||
)
|
||||
|
||||
final_value = None
|
||||
if report and report.final_value is not None:
|
||||
final_value = report.final_value
|
||||
elif valuation and valuation.final_price is not None:
|
||||
final_value = valuation.final_price
|
||||
elif valuation and valuation.estimated_price is not None:
|
||||
final_value = valuation.estimated_price
|
||||
|
||||
market_value_formatted = (
|
||||
f"{_format_currency(final_value)} so'm" if final_value is not None else "0 so'm"
|
||||
)
|
||||
market_value_words = (
|
||||
f"{_number_to_uzbek_words(final_value)} so'm"
|
||||
if final_value is not None
|
||||
else ""
|
||||
)
|
||||
|
||||
cost_final = final_value
|
||||
comparative_final = final_value
|
||||
|
||||
brand_name = ""
|
||||
model_name = ""
|
||||
if vehicle:
|
||||
brand_name = vehicle.brand.name if vehicle.brand else ""
|
||||
model_name = vehicle.model.name if vehicle.model else ""
|
||||
if not brand_name:
|
||||
brand_name = auto.car_brand or ""
|
||||
if not model_name:
|
||||
model_name = auto.car_model or ""
|
||||
full_brand = f"{brand_name} {model_name}".strip()
|
||||
|
||||
plate_number = (vehicle.license_plate if vehicle else None) or auto.car_number or ""
|
||||
manufacture_year = ""
|
||||
if vehicle and vehicle.manufacture_year:
|
||||
manufacture_year = str(vehicle.manufacture_year)
|
||||
elif auto.manufacture_year:
|
||||
manufacture_year = str(auto.manufacture_year)
|
||||
|
||||
production_date = f"{manufacture_year}-yil" if manufacture_year else ""
|
||||
engine_number = (vehicle.engine_number if vehicle else None) or auto.car_dvigatel_number or ""
|
||||
body_number = vehicle.vin_number if vehicle and vehicle.vin_number else ""
|
||||
color_value = ""
|
||||
if vehicle and vehicle.color:
|
||||
color_value = vehicle.color.name
|
||||
elif auto.car_color:
|
||||
color_value = auto.car_color
|
||||
fuel_type_value = ""
|
||||
if vehicle and vehicle.fuel_type:
|
||||
fuel_type_value = vehicle.fuel_type.name
|
||||
|
||||
tech_passport_value = ""
|
||||
if vehicle and (vehicle.tech_passport_series or vehicle.tech_passport_number):
|
||||
tech_passport_value = (
|
||||
f"{vehicle.tech_passport_series or ''} № {vehicle.tech_passport_number or ''}"
|
||||
).strip()
|
||||
elif auto.tex_passport_serie_num:
|
||||
tech_passport_value = auto.tex_passport_serie_num
|
||||
|
||||
customer_ctx = self._customer_context(customer)
|
||||
owner_ctx = self._owner_context(owner)
|
||||
if not owner_ctx["name"]:
|
||||
owner_ctx = customer_ctx
|
||||
|
||||
contract_ctx = self._contract_context(auto, report_date)
|
||||
|
||||
director_name = customer.director_name if customer and customer.director_name else "—"
|
||||
|
||||
ctx = {
|
||||
"logo_url": "",
|
||||
"report": {
|
||||
"number": report_number,
|
||||
"date": _format_date(report_date),
|
||||
"valuation_date": _format_date(valuation_date),
|
||||
"inspection_date": _format_date(inspection_date),
|
||||
"year": str(report_date.year),
|
||||
"market_value_formatted": market_value_formatted,
|
||||
"market_value_words": market_value_words,
|
||||
},
|
||||
"vehicle": {
|
||||
"brand": full_brand,
|
||||
"plate_number": plate_number,
|
||||
"production_date": production_date,
|
||||
"production_year": manufacture_year,
|
||||
"type": auto.get_object_type_display() if auto.object_type else "",
|
||||
"engine_number": engine_number,
|
||||
"body_number": body_number,
|
||||
"chassis_number": body_number,
|
||||
"color": color_value,
|
||||
"tech_passport": tech_passport_value,
|
||||
"fuel_type": fuel_type_value,
|
||||
"engine_power": "",
|
||||
"full_weight": "",
|
||||
"empty_weight": "",
|
||||
},
|
||||
"customer": customer_ctx,
|
||||
"owner": owner_ctx,
|
||||
"contract": contract_ctx,
|
||||
"company": {
|
||||
"director": director_name,
|
||||
},
|
||||
"rates": {
|
||||
"rur": "",
|
||||
"usd": "",
|
||||
"eur": "",
|
||||
},
|
||||
"inspection": {
|
||||
"tires": "",
|
||||
"engine": "",
|
||||
"chassis": "",
|
||||
"transmission": "",
|
||||
"body": "",
|
||||
},
|
||||
"cost": {
|
||||
"engine_volume": "",
|
||||
"factory_price": _format_currency(cost_final),
|
||||
"replacement_value": _format_currency(cost_final),
|
||||
"wear_percent": "",
|
||||
"final_value": _format_currency(cost_final),
|
||||
"final_value_words": _number_to_uzbek_words(cost_final) + (" so'm" if cost_final else ""),
|
||||
},
|
||||
"comparative": {
|
||||
"final_value": _format_currency(comparative_final),
|
||||
"final_value_usd": "",
|
||||
"final_value_words": _number_to_uzbek_words(comparative_final) + (" so'm" if comparative_final else ""),
|
||||
},
|
||||
"approach": {
|
||||
"cost": {
|
||||
"value": _format_currency(cost_final),
|
||||
"weight": "30%",
|
||||
"weighted": "",
|
||||
},
|
||||
"comparative": {
|
||||
"value": _format_currency(comparative_final),
|
||||
"weight": "70%",
|
||||
"weighted": "",
|
||||
},
|
||||
"weighted_total": _format_currency(final_value),
|
||||
},
|
||||
"analog_1": self._empty_analog(),
|
||||
"analog_2": self._empty_analog(),
|
||||
"analog_3": self._empty_analog(),
|
||||
}
|
||||
return ctx
|
||||
|
||||
def _customer_context(self, customer):
|
||||
empty = {
|
||||
"name": "",
|
||||
"address": "",
|
||||
"phone": "",
|
||||
"tin": "",
|
||||
"account": "",
|
||||
"bank": "",
|
||||
"mfo": "",
|
||||
}
|
||||
if not customer:
|
||||
return empty
|
||||
if customer.customer_type == "legal":
|
||||
return {
|
||||
"name": customer.org_name or "",
|
||||
"address": customer.org_address or "",
|
||||
"phone": "",
|
||||
"tin": customer.inn or "",
|
||||
"account": customer.bank_account or "",
|
||||
"bank": "",
|
||||
"mfo": customer.mfo or "",
|
||||
}
|
||||
full_name = " ".join(
|
||||
filter(None, [customer.last_name, customer.first_name, customer.middle_name])
|
||||
)
|
||||
return {
|
||||
"name": full_name,
|
||||
"address": customer.address or "",
|
||||
"phone": "",
|
||||
"tin": customer.jshshir or "",
|
||||
"account": "",
|
||||
"bank": "",
|
||||
"mfo": "",
|
||||
}
|
||||
|
||||
def _owner_context(self, owner):
|
||||
empty = {"name": "", "address": ""}
|
||||
if not owner:
|
||||
return empty
|
||||
type_field = getattr(owner, "owner_type", None) or getattr(owner, "customer_type", None)
|
||||
if type_field == "legal":
|
||||
return {
|
||||
"name": owner.org_name or "",
|
||||
"address": owner.org_address or "",
|
||||
}
|
||||
full_name = " ".join(
|
||||
filter(None, [owner.last_name, owner.first_name, owner.middle_name])
|
||||
)
|
||||
return {
|
||||
"name": full_name,
|
||||
"address": owner.address or "",
|
||||
}
|
||||
|
||||
def _contract_context(self, auto, fallback_date):
|
||||
contract_date = auto.contract_date or fallback_date
|
||||
return {
|
||||
"number": auto.registration_number or str(auto.pk),
|
||||
"day": f"{contract_date.day:02d}",
|
||||
"month": UZ_MONTHS.get(contract_date.month, ""),
|
||||
"year": str(contract_date.year),
|
||||
}
|
||||
|
||||
def _empty_analog(self):
|
||||
return {
|
||||
"source": "",
|
||||
"phone": "",
|
||||
"description": "",
|
||||
"year": "",
|
||||
"mileage": "",
|
||||
"price": "",
|
||||
"adjusted_price_1": "",
|
||||
"final_price": "",
|
||||
"weight": "",
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class AutoEvaluationAdmin(ModelAdmin):
|
||||
"fields": (
|
||||
"tex_passport_serie_num",
|
||||
("tex_passport_gived_date", "tex_passport_gived_location"),
|
||||
("car_type", "car_wheel"),
|
||||
("car_wheel",),
|
||||
("car_brand", "car_model"),
|
||||
("car_number", "manufacture_year"),
|
||||
("car_dvigatel_number", "car_color"),
|
||||
|
||||
@@ -6,6 +6,8 @@ class AutoObjectType(models.TextChoices):
|
||||
LIGHTWEIGHT_AUTO = "lightweight_auto", _("Yengil automobil")
|
||||
TRUCK_CAR = "truck_car", _("Yuk automobil")
|
||||
SPECIAL_TECH = "special_tech", _("Maxsus texnika")
|
||||
BUS = "bus", _("Avtobus")
|
||||
MOTO = "moto", _("Mototsikl")
|
||||
|
||||
|
||||
class AutoEvaluationStatus(models.TextChoices):
|
||||
|
||||
@@ -14,3 +14,4 @@ class ReferenceType(models.TextChoices):
|
||||
DETERMINED_VALUE = "determined_value", _("Determined value type")
|
||||
PROPERTY_RIGHTS = "property_rights", _("Property rights")
|
||||
OWNERSHIP_FORM = "ownership_form", _("Ownership form")
|
||||
ASSESSMENT_TASK_TYPE = "assessment_task_type", _("Assessment task type")
|
||||
|
||||
@@ -19,3 +19,8 @@ class RequestStatus(models.TextChoices):
|
||||
IN_PROGRESS = "in_progress", _("Jarayonda")
|
||||
COMPLETED = "completed", _("Bajarildi")
|
||||
REJECTED = "rejected", _("Rad etildi")
|
||||
|
||||
|
||||
class RequestPersonType(models.TextChoices):
|
||||
INDIVIDUAL_PERSON = "individual_person", "Jismoniy shaxs"
|
||||
LEGAL_PERSON = "legal_person", 'Yuridik shaxs',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from .auto import * # noqa
|
||||
from .mechanic_auto import * # noqa
|
||||
from .customer import * # noqa
|
||||
from .document import * # noqa
|
||||
from .documentcategory import * # noqa
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
from django_filters import rest_framework as filters
|
||||
|
||||
from core.apps.evaluation.choices.history import EvaluationEventType
|
||||
from core.apps.evaluation.models import AutoevaluationhistoryModel, QuickevaluationhistoryModel
|
||||
from core.apps.evaluation.models import (
|
||||
AutoevaluationhistoryModel,
|
||||
MechanicAutoevaluationhistoryModel,
|
||||
QuickevaluationhistoryModel,
|
||||
)
|
||||
|
||||
|
||||
class AutoevaluationhistoryFilter(filters.FilterSet):
|
||||
@@ -29,6 +33,31 @@ class AutoevaluationhistoryFilter(filters.FilterSet):
|
||||
]
|
||||
|
||||
|
||||
class MechanicAutoevaluationhistoryFilter(filters.FilterSet):
|
||||
mechanic_auto_evaluation = filters.NumberFilter(
|
||||
field_name="mechanic_auto_evaluation", lookup_expr="exact"
|
||||
)
|
||||
event_type = filters.ChoiceFilter(
|
||||
field_name="event_type",
|
||||
choices=EvaluationEventType.choices,
|
||||
)
|
||||
created_from = filters.DateTimeFilter(
|
||||
field_name="created_at", lookup_expr="gte"
|
||||
)
|
||||
created_to = filters.DateTimeFilter(
|
||||
field_name="created_at", lookup_expr="lte"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MechanicAutoevaluationhistoryModel
|
||||
fields = [
|
||||
"mechanic_auto_evaluation",
|
||||
"event_type",
|
||||
"created_from",
|
||||
"created_to",
|
||||
]
|
||||
|
||||
|
||||
class QuickevaluationhistoryFilter(filters.FilterSet):
|
||||
quick_evaluation = filters.NumberFilter(
|
||||
field_name="quick_evaluation", lookup_expr="exact"
|
||||
|
||||
37
core/apps/evaluation/filters/mechanic_auto.py
Normal file
37
core/apps/evaluation/filters/mechanic_auto.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from django_filters import rest_framework as filters
|
||||
|
||||
from core.apps.evaluation.models import MechanicAutoEvaluationModel
|
||||
|
||||
|
||||
class MechanicAutoevaluationFilter(filters.FilterSet):
|
||||
status = filters.CharFilter(method="filter_status")
|
||||
object_type = filters.CharFilter(field_name="object_type", lookup_expr="exact")
|
||||
object_owner_type = filters.NumberFilter(field_name="object_owner_type", lookup_expr="exact")
|
||||
rate_type = filters.NumberFilter(field_name="rate_type", lookup_expr="exact")
|
||||
value_determined = filters.NumberFilter(field_name="value_determined", lookup_expr="exact")
|
||||
client = filters.NumberFilter(field_name="valuation__customer", lookup_expr="exact")
|
||||
created_from = filters.DateFilter(field_name="created_at", lookup_expr="gte")
|
||||
created_to = filters.DateFilter(field_name="created_at", lookup_expr="lte")
|
||||
rate_date_from = filters.DateFilter(field_name="rate_date", lookup_expr="gte")
|
||||
rate_date_to = filters.DateFilter(field_name="rate_date", lookup_expr="lte")
|
||||
|
||||
def filter_status(self, queryset, name, value):
|
||||
if value:
|
||||
statuses = [s.strip() for s in value.split(",") if s.strip()]
|
||||
return queryset.filter(status__in=statuses)
|
||||
return queryset
|
||||
|
||||
class Meta:
|
||||
model = MechanicAutoEvaluationModel
|
||||
fields = [
|
||||
"status",
|
||||
"object_type",
|
||||
"object_owner_type",
|
||||
"rate_type",
|
||||
"value_determined",
|
||||
"client",
|
||||
"created_from",
|
||||
"created_to",
|
||||
"rate_date_from",
|
||||
"rate_date_to",
|
||||
]
|
||||
@@ -0,0 +1,53 @@
|
||||
# Generated by Django 5.2.7 on 2026-04-28 11:07
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evaluation', '0035_autoevaluationmodel_is_archived'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='autoevaluationmodel',
|
||||
name='form_ownership',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='autoevaluationmodel',
|
||||
name='property_rights',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='autoevaluationmodel',
|
||||
name='rate_object_name',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='autoevaluationmodel',
|
||||
name='appraisers',
|
||||
field=models.ManyToManyField(blank=True, null=True, to=settings.AUTH_USER_MODEL, verbose_name='appraisers'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='autoevaluationmodel',
|
||||
name='evaluation_request',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='auto_evaluations_request', to='evaluation.evaluationrequestmodel'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='autoevaluationmodel',
|
||||
name='user',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='auto_evaluations_user', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='autoevaluationmodel',
|
||||
name='valuation',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='auto_detail', to='evaluation.valuationmodel'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='autoevaluationmodel',
|
||||
name='vehicle',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='evaluation', to='evaluation.vehiclemodel'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,49 @@
|
||||
# Generated by Django 5.2.7 on 2026-04-28 11:41
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evaluation', '0036_remove_autoevaluationmodel_form_ownership_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='customer_and_owner_same',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EvaluationRequestCustomerModel',
|
||||
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=[('individual_person', 'Jismoniy shaxs'), ('legal_person', 'Yuridik shaxs')], max_length=100)),
|
||||
('jshshir', models.CharField(max_length=100)),
|
||||
('evaluation_request', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='customer', to='evaluation.evaluationrequestmodel')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Evaluation Request Customer',
|
||||
'verbose_name_plural': 'Evaluation Request Customers',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EvaluationRequestOwnerModel',
|
||||
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=[('individual_person', 'Jismoniy shaxs'), ('legal_person', 'Yuridik shaxs')], max_length=100)),
|
||||
('jshshir', models.CharField(max_length=100)),
|
||||
('evaluation_request', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='owner', to='evaluation.evaluationrequestmodel')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Evaluation Request Owner',
|
||||
'verbose_name_plural': 'Evaluation Request Owners',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,88 @@
|
||||
# Generated by Django 5.2.7 on 2026-04-28 11:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evaluation', '0037_evaluationrequestmodel_customer_and_owner_same_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='distance_covered',
|
||||
field=models.FloatField(blank=True, default=0.0, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='gov_number',
|
||||
field=models.CharField(blank=True, max_length=100, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='chassi',
|
||||
field=models.CharField(blank=True, max_length=100, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='customer_inn_number',
|
||||
field=models.CharField(max_length=20),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='is_archive',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='location_lat',
|
||||
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='location_lng',
|
||||
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='location_name',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='need_delivering',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='object_type',
|
||||
field=models.CharField(blank=True, choices=[('lightweight_auto', 'Yengil automobil'), ('truck_car', 'Yuk automobil'), ('special_tech', 'Maxsus texnika')], max_length=50, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='owner_inn_number',
|
||||
field=models.CharField(max_length=20),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='rate_type',
|
||||
field=models.CharField(choices=[('auto', 'Automobil'), ('real_estate', "Ko'chmas mulk"), ('equipment', 'Uskuna')], max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('pending', 'Kutilmoqda'), ('in_progress', 'Jarayonda'), ('completed', 'Bajarildi'), ('rejected', 'Rad etildi')], default='pending', max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='tex_passport',
|
||||
field=models.CharField(blank=True, max_length=20, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evaluationrequestmodel',
|
||||
name='worked_hours',
|
||||
field=models.IntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
31
core/apps/evaluation/migrations/0039_bonus.py
Normal file
31
core/apps/evaluation/migrations/0039_bonus.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# Generated by Django 5.2.7 on 2026-05-01 06:45
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evaluation', '0038_evaluationrequestmodel_distance_covered_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Bonus',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('bonus_type', models.CharField(choices=[('lightweight_auto', 'Yengil automobil'), ('truck_car', 'Yuk automobil'), ('special_tech', 'Maxsus texnika')], max_length=50)),
|
||||
('percentage', models.FloatField()),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('price', models.FloatField()),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bonuses', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,59 @@
|
||||
# Generated by Django 5.2.7 on 2026-05-01 11:43
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evaluation', '0039_bonus'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BaseValueBonus',
|
||||
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)),
|
||||
('base_price', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='BonusType',
|
||||
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=255)),
|
||||
('category', models.CharField(choices=[('auto_transport', 'Avtotransport'), ('real estate', "ko'chmas mulk"), ('equipment', 'uskuna va jihozlar')], max_length=50)),
|
||||
('percentage', models.PositiveIntegerField()),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EmployeeBonus',
|
||||
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)),
|
||||
('percentage', models.PositiveIntegerField()),
|
||||
('bonus_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='evaluation.bonustype')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bonuses', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('user', 'bonus_type')},
|
||||
},
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Bonus',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 5.2.7 on 2026-05-01 12:06
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evaluation', '0040_basevaluebonus_bonustype_employeebonus_delete_bonus'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='BonusType',
|
||||
new_name='BonusCategory',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.7 on 2026-05-04 12:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evaluation', '0041_rename_bonustype_bonuscategory'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='bonuscategory',
|
||||
name='category',
|
||||
field=models.CharField(choices=[('lightweight_auto', 'Yengil automobil'), ('truck_car', 'Yuk automobil'), ('special_tech', 'Maxsus texnika')], max_length=50),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,268 @@
|
||||
# Generated by Django 5.2.7 on 2026-05-05 11:52
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("evaluation", "0042_alter_bonuscategory_category"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="MechanicAutoEvaluationModel",
|
||||
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)),
|
||||
(
|
||||
"tex_passport_file",
|
||||
models.FileField(
|
||||
blank=True,
|
||||
null=True,
|
||||
upload_to="mechanic_evaluation/tech_passports/%Y/%m/",
|
||||
verbose_name="tech passport file",
|
||||
),
|
||||
),
|
||||
(
|
||||
"registration_number",
|
||||
models.CharField(blank=True, max_length=50, null=True, verbose_name="registration number"),
|
||||
),
|
||||
("contract_date", models.DateField(blank=True, null=True, verbose_name="contract date")),
|
||||
(
|
||||
"object_inspection_date",
|
||||
models.DateField(blank=True, null=True, verbose_name="object inspection date"),
|
||||
),
|
||||
("rate_date", models.DateField(blank=True, null=True, verbose_name="rate date")),
|
||||
("rate_report_date", models.DateField(blank=True, null=True, verbose_name="rate report date")),
|
||||
(
|
||||
"object_type",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("lightweight_auto", "Yengil automobil"),
|
||||
("truck_car", "Yuk automobil"),
|
||||
("special_tech", "Maxsus texnika"),
|
||||
],
|
||||
max_length=50,
|
||||
null=True,
|
||||
verbose_name="object type",
|
||||
),
|
||||
),
|
||||
(
|
||||
"object_owner_type",
|
||||
models.IntegerField(
|
||||
blank=True,
|
||||
choices=[(1, "Jismoniy shaxs"), (2, "Yuridik shaxs")],
|
||||
null=True,
|
||||
verbose_name="object owner type",
|
||||
),
|
||||
),
|
||||
(
|
||||
"object_owner_individual_person_f_name",
|
||||
models.CharField(blank=True, max_length=100, null=True, verbose_name="owner first name"),
|
||||
),
|
||||
(
|
||||
"object_owner_individual_person_l_name",
|
||||
models.CharField(blank=True, max_length=100, null=True, verbose_name="owner last name"),
|
||||
),
|
||||
(
|
||||
"object_owner_individual_person_p_name",
|
||||
models.CharField(blank=True, max_length=100, null=True, verbose_name="owner patronymic"),
|
||||
),
|
||||
(
|
||||
"object_owner_individual_person_passport_num",
|
||||
models.CharField(blank=True, max_length=20, null=True, verbose_name="owner passport number"),
|
||||
),
|
||||
(
|
||||
"object_owner_legal_entity",
|
||||
models.CharField(blank=True, max_length=255, null=True, verbose_name="legal entity name"),
|
||||
),
|
||||
(
|
||||
"object_owner_legal_inn",
|
||||
models.CharField(blank=True, max_length=20, null=True, verbose_name="legal entity INN"),
|
||||
),
|
||||
(
|
||||
"tex_passport_serie_num",
|
||||
models.CharField(
|
||||
blank=True, max_length=20, null=True, verbose_name="tech passport series and number"
|
||||
),
|
||||
),
|
||||
(
|
||||
"tex_passport_gived_date",
|
||||
models.DateField(blank=True, null=True, verbose_name="tech passport given date"),
|
||||
),
|
||||
(
|
||||
"tex_passport_gived_location",
|
||||
models.CharField(
|
||||
blank=True, max_length=255, null=True, verbose_name="tech passport given location"
|
||||
),
|
||||
),
|
||||
(
|
||||
"car_type",
|
||||
models.IntegerField(
|
||||
blank=True, choices=[(1, "Xetchbek"), (2, "Universal")], null=True, verbose_name="car type"
|
||||
),
|
||||
),
|
||||
(
|
||||
"car_wheel",
|
||||
models.IntegerField(blank=True, choices=[(1, "4x4")], null=True, verbose_name="car wheel"),
|
||||
),
|
||||
("car_brand", models.CharField(blank=True, max_length=100, null=True, verbose_name="car brand")),
|
||||
("car_model", models.CharField(blank=True, max_length=100, null=True, verbose_name="car model")),
|
||||
("car_number", models.CharField(blank=True, max_length=20, null=True, verbose_name="car number")),
|
||||
(
|
||||
"manufacture_year",
|
||||
models.CharField(blank=True, max_length=10, null=True, verbose_name="manufacture year"),
|
||||
),
|
||||
(
|
||||
"car_dvigatel_number",
|
||||
models.CharField(blank=True, max_length=50, null=True, verbose_name="engine number"),
|
||||
),
|
||||
("car_color", models.CharField(blank=True, max_length=50, null=True, verbose_name="car color")),
|
||||
("rating_goal", models.CharField(blank=True, max_length=50, null=True, verbose_name="rating goal")),
|
||||
(
|
||||
"status",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("yaratildi", "Yaratildi"),
|
||||
("baxolovchi_biriktirildi", "Baholovchi biriktirildi"),
|
||||
("baxolandi", "Baholandi"),
|
||||
("rad_etildi", "Rad etildi"),
|
||||
("tasdiqlandi", "Tasdiqlandi"),
|
||||
],
|
||||
default="yaratildi",
|
||||
max_length=50,
|
||||
verbose_name="status",
|
||||
),
|
||||
),
|
||||
("is_archived", models.BooleanField(default=False, verbose_name="is archived")),
|
||||
(
|
||||
"appraisers",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="mechanic_auto_evaluation_appraisers",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="appraisers",
|
||||
),
|
||||
),
|
||||
(
|
||||
"evaluation_request",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="mechanic_auto_evaluations_request",
|
||||
to="evaluation.evaluationrequestmodel",
|
||||
),
|
||||
),
|
||||
(
|
||||
"rate_type",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_mechanic_auto_rate_type",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="rate type",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="mechanic_auto_evaluations_user",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"valuation",
|
||||
models.OneToOneField(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="mechanic_auto_detail",
|
||||
to="evaluation.valuationmodel",
|
||||
),
|
||||
),
|
||||
(
|
||||
"value_determined",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_mechanic_auto_value_determined",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="value determined",
|
||||
),
|
||||
),
|
||||
(
|
||||
"vehicle",
|
||||
models.OneToOneField(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="mechanic_evaluation",
|
||||
to="evaluation.vehiclemodel",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Mechanic Auto Evaluation",
|
||||
"verbose_name_plural": "Mechanic Auto Evaluations",
|
||||
"db_table": "MechanicAutoEvaluation",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="MechanicAutoevaluationhistoryModel",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
(
|
||||
"event_type",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("order_created", "Buyurtma yaratildi"),
|
||||
("status_changed", "Status o'zgartirildi"),
|
||||
("evaluator_assigned", "Baholovchi biriktirildi"),
|
||||
("document_uploaded", "Hujjat yuklandi"),
|
||||
("payment_made", "To'lov qilindi"),
|
||||
],
|
||||
max_length=50,
|
||||
verbose_name="event type",
|
||||
),
|
||||
),
|
||||
("actor_id", models.BigIntegerField(blank=True, null=True, verbose_name="actor id")),
|
||||
("actor_full_name", models.CharField(default="Tizim", max_length=255, verbose_name="actor full name")),
|
||||
("actor_role", models.CharField(default="system", max_length=50, verbose_name="actor role")),
|
||||
("meta", models.JSONField(blank=True, default=dict, verbose_name="meta")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True, verbose_name="created at")),
|
||||
(
|
||||
"mechanic_auto_evaluation",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="history",
|
||||
to="evaluation.mechanicautoevaluationmodel",
|
||||
verbose_name="mechanic auto evaluation",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Mechanic Auto Evaluation History",
|
||||
"verbose_name_plural": "Mechanic Auto Evaluation Histories",
|
||||
"db_table": "MechanicAutoEvaluationHistory",
|
||||
"ordering": ["created_at"],
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["mechanic_auto_evaluation_id", "created_at"], name="mech_auto_hist_eval_date_idx"
|
||||
),
|
||||
models.Index(fields=["event_type"], name="mech_auto_hist_event_type_idx"),
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,167 @@
|
||||
# Generated by Django 5.2.7 on 2026-05-05 12:06
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("evaluation", "0043_mechanicautoevaluationmodel_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="autoevaluationmodel",
|
||||
name="car_type",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="autoevaluationmodel",
|
||||
name="tex_passport_file",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="autoevaluationmodel",
|
||||
name="assessment_task_type",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_auto_assessment_task_type",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="assessment task type",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="autoevaluationmodel",
|
||||
name="body_type",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_auto_body_type",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="body type",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="autoevaluationmodel",
|
||||
name="car_position",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_auto_car_position",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="car position",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="autoevaluationmodel",
|
||||
name="distance_covered",
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name="distance covered"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="autoevaluationmodel",
|
||||
name="fuel_type",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_auto_fuel_type",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="fuel type",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="autoevaluationmodel",
|
||||
name="object_owner_residence",
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name="object owner residence"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="autoevaluationmodel",
|
||||
name="state_car",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_auto_state_car",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="state car",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="autoevaluationmodel",
|
||||
name="object_type",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("lightweight_auto", "Yengil automobil"),
|
||||
("truck_car", "Yuk automobil"),
|
||||
("special_tech", "Maxsus texnika"),
|
||||
("bus", "Avtobus"),
|
||||
("moto", "Mototsikl"),
|
||||
],
|
||||
max_length=50,
|
||||
null=True,
|
||||
verbose_name="object type",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="bonuscategory",
|
||||
name="category",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("lightweight_auto", "Yengil automobil"),
|
||||
("truck_car", "Yuk automobil"),
|
||||
("special_tech", "Maxsus texnika"),
|
||||
("bus", "Avtobus"),
|
||||
("moto", "Mototsikl"),
|
||||
],
|
||||
max_length=50,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="mechanicautoevaluationmodel",
|
||||
name="object_type",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("lightweight_auto", "Yengil automobil"),
|
||||
("truck_car", "Yuk automobil"),
|
||||
("special_tech", "Maxsus texnika"),
|
||||
("bus", "Avtobus"),
|
||||
("moto", "Mototsikl"),
|
||||
],
|
||||
max_length=50,
|
||||
null=True,
|
||||
verbose_name="object type",
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="AutoEvaluationTexPassportFile",
|
||||
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(
|
||||
upload_to="auto_evaluation/tech_passports/%Y/%m/", verbose_name="tech passport file"
|
||||
),
|
||||
),
|
||||
(
|
||||
"auto_evaluation",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="tex_passport_files",
|
||||
to="evaluation.autoevaluationmodel",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Auto Evaluation Tex Passport File",
|
||||
"verbose_name_plural": "Auto Evaluation Tex Passport Files",
|
||||
"db_table": "AutoEvaluationTexPassportFile",
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evaluation', '0044_remove_autoevaluationmodel_car_type_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='referenceitemmodel',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('brand', 'Brand'), ('marka', 'Marka'), ('color', 'Color'), ('fuel_type', 'Fuel type'), ('body_type', 'Body type'), ('car_position', 'Car position'), ('state_car', 'Car state'), ('evaluation_purpose', 'Evaluation purpose'), ('determined_value', 'Determined value type'), ('property_rights', 'Property rights'), ('ownership_form', 'Ownership form'), ('assessment_task_type', 'Assessment task type')], max_length=50, verbose_name='type'),
|
||||
),
|
||||
]
|
||||
@@ -1,4 +1,5 @@
|
||||
from .auto import * # noqa
|
||||
from .mechanic_auto import * # noqa
|
||||
from .customer import * # noqa
|
||||
from .document import * # noqa
|
||||
from .documentcategory import * # noqa
|
||||
|
||||
@@ -4,19 +4,15 @@ from django_core.models import AbstractBaseModel
|
||||
from model_bakery import baker
|
||||
|
||||
from core.apps.evaluation.choices.auto import (
|
||||
AutoCarType,
|
||||
AutoCarWheel,
|
||||
AutoEvaluationStatus,
|
||||
AutoObjectType,
|
||||
# FormOwnership,
|
||||
LocationConvenience,
|
||||
LocationHighways,
|
||||
ObjectOwnerType,
|
||||
# PropertyRights,
|
||||
# RateType,
|
||||
# ValueDetermined,
|
||||
)
|
||||
|
||||
from .valuation import ValuationModel
|
||||
from .vehicle import VehicleModel
|
||||
|
||||
@@ -26,7 +22,6 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
"accounts.User",
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="auto_evaluations_user",
|
||||
verbose_name=_("user"),
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
@@ -34,7 +29,6 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
"evaluation.EvaluationRequestModel",
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="auto_evaluations_request",
|
||||
verbose_name=_("evaluation request"),
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
@@ -42,7 +36,6 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
ValuationModel,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="auto_detail",
|
||||
verbose_name=_("valuation"),
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
@@ -50,24 +43,67 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
VehicleModel,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="evaluation",
|
||||
verbose_name=_("vehicle"),
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
appraisers = models.ManyToManyField(
|
||||
"accounts.User",
|
||||
verbose_name=_("appraisers"),
|
||||
related_name="auto_evaluations",
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
tex_passport_file = models.FileField(
|
||||
verbose_name=_("tech passport file"),
|
||||
upload_to="quick_evaluation/tech_passports/%Y/%m/",
|
||||
distance_covered = models.PositiveIntegerField(
|
||||
verbose_name=_("distance covered"),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
object_owner_residence = models.CharField(
|
||||
verbose_name=_("object owner residence"),
|
||||
max_length=255,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
car_position = models.ForeignKey(
|
||||
'evaluation.ReferenceitemModel',
|
||||
verbose_name=_("car position"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='evaluation_auto_car_position',
|
||||
)
|
||||
body_type = models.ForeignKey(
|
||||
'evaluation.ReferenceitemModel',
|
||||
verbose_name=_("body type"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='evaluation_auto_body_type',
|
||||
)
|
||||
fuel_type = models.ForeignKey(
|
||||
'evaluation.ReferenceitemModel',
|
||||
verbose_name=_("fuel type"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='evaluation_auto_fuel_type',
|
||||
)
|
||||
state_car = models.ForeignKey(
|
||||
'evaluation.ReferenceitemModel',
|
||||
verbose_name=_("state car"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='evaluation_auto_state_car',
|
||||
)
|
||||
assessment_task_type = models.ForeignKey(
|
||||
'evaluation.ReferenceitemModel',
|
||||
verbose_name=_("assessment task type"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='evaluation_auto_assessment_task_type',
|
||||
)
|
||||
|
||||
# ── Step 1 — Umumiy ma'lumotlar ──────────────────────────────────
|
||||
registration_number = models.CharField(
|
||||
@@ -96,12 +132,6 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
rate_object_name = models.CharField(
|
||||
verbose_name=_("rate object name"),
|
||||
max_length=255,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
object_type = models.CharField(
|
||||
verbose_name=_("object type"),
|
||||
max_length=50,
|
||||
@@ -153,23 +183,6 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
property_rights = models.ForeignKey(
|
||||
'evaluation.ReferenceitemModel',
|
||||
verbose_name=_("property rights"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='evaluation_auto_property_rights'
|
||||
)
|
||||
form_ownership = models.ForeignKey(
|
||||
'evaluation.ReferenceitemModel',
|
||||
verbose_name=_("form of ownership"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='evaluation_auto_form_ownership'
|
||||
)
|
||||
value_determined = models.ForeignKey(
|
||||
'evaluation.ReferenceitemModel',
|
||||
verbose_name=_("value determined"),
|
||||
@@ -205,12 +218,6 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
car_type = models.IntegerField(
|
||||
verbose_name=_("car type"),
|
||||
choices=AutoCarType.choices,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
car_wheel = models.IntegerField(
|
||||
verbose_name=_("car wheel"),
|
||||
choices=AutoCarWheel.choices,
|
||||
@@ -272,8 +279,6 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
default=False,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"Auto Evaluation {self.registration_number or self.pk}"
|
||||
|
||||
@@ -285,3 +290,27 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
db_table = "AutoEvaluation"
|
||||
verbose_name = _("Auto Evaluation")
|
||||
verbose_name_plural = _("Auto Evaluations")
|
||||
|
||||
|
||||
class AutoEvaluationTexPassportFile(AbstractBaseModel):
|
||||
auto_evaluation = models.ForeignKey(
|
||||
AutoEvaluationModel,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="tex_passport_files",
|
||||
)
|
||||
file = models.FileField(
|
||||
verbose_name=_("tech passport file"),
|
||||
upload_to="auto_evaluation/tech_passports/%Y/%m/",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"Tex passport file for AutoEvaluation #{self.auto_evaluation_id}"
|
||||
|
||||
@classmethod
|
||||
def _baker(cls):
|
||||
return baker.make(cls)
|
||||
|
||||
class Meta:
|
||||
db_table = "AutoEvaluationTexPassportFile"
|
||||
verbose_name = _("Auto Evaluation Tex Passport File")
|
||||
verbose_name_plural = _("Auto Evaluation Tex Passport Files")
|
||||
|
||||
33
core/apps/evaluation/models/bonus.py
Normal file
33
core/apps/evaluation/models/bonus.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from django.db import models
|
||||
from django.db.models.fields import PositiveIntegerField
|
||||
from django_core.models import AbstractBaseModel
|
||||
|
||||
from core.apps.evaluation.choices.auto import AutoObjectType
|
||||
|
||||
|
||||
class BaseValueBonus(AbstractBaseModel):
|
||||
base_price = models.DecimalField(max_digits=12, decimal_places=2)
|
||||
|
||||
def __str__(self):
|
||||
return f"Base: {self.base_price}"
|
||||
|
||||
|
||||
class BonusCategory(AbstractBaseModel):
|
||||
name = models.CharField(max_length=255)
|
||||
category = models.CharField(
|
||||
max_length=50,
|
||||
choices=AutoObjectType.choices
|
||||
)
|
||||
percentage = PositiveIntegerField()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class EmployeeBonus(AbstractBaseModel):
|
||||
user = models.ForeignKey("accounts.User", on_delete=models.CASCADE, related_name="bonuses", )
|
||||
bonus_type = models.ForeignKey(BonusCategory, on_delete=models.CASCADE)
|
||||
percentage = models.PositiveIntegerField()
|
||||
|
||||
class Meta:
|
||||
unique_together = ("user", "bonus_type")
|
||||
@@ -69,6 +69,66 @@ class AutoevaluationhistoryModel(models.Model):
|
||||
]
|
||||
|
||||
|
||||
class MechanicAutoevaluationhistoryModel(models.Model):
|
||||
"""MechanicAutoEvaluation bo'yicha barcha harakatlar logi — faqat o'qiladi, signallar tomonidan yoziladi."""
|
||||
|
||||
mechanic_auto_evaluation = models.ForeignKey(
|
||||
"evaluation.MechanicAutoEvaluationModel",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="history",
|
||||
verbose_name=_("mechanic auto evaluation"),
|
||||
)
|
||||
event_type = models.CharField(
|
||||
verbose_name=_("event type"),
|
||||
max_length=50,
|
||||
choices=EvaluationEventType.choices,
|
||||
)
|
||||
|
||||
actor_id = models.BigIntegerField(
|
||||
verbose_name=_("actor id"),
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
actor_full_name = models.CharField(
|
||||
verbose_name=_("actor full name"),
|
||||
max_length=255,
|
||||
default="Tizim",
|
||||
)
|
||||
actor_role = models.CharField(
|
||||
verbose_name=_("actor role"),
|
||||
max_length=50,
|
||||
default="system",
|
||||
)
|
||||
|
||||
meta = models.JSONField(
|
||||
verbose_name=_("meta"),
|
||||
default=dict,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(
|
||||
verbose_name=_("created at"),
|
||||
auto_now_add=True,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.get_event_type_display()} — MechanicAutoEval #{self.mechanic_auto_evaluation_id}"
|
||||
|
||||
@classmethod
|
||||
def _baker(cls):
|
||||
return baker.make(cls)
|
||||
|
||||
class Meta:
|
||||
db_table = "MechanicAutoEvaluationHistory"
|
||||
verbose_name = _("Mechanic Auto Evaluation History")
|
||||
verbose_name_plural = _("Mechanic Auto Evaluation Histories")
|
||||
ordering = ["created_at"]
|
||||
indexes = [
|
||||
models.Index(fields=["mechanic_auto_evaluation_id", "created_at"], name="mech_auto_hist_eval_date_idx"),
|
||||
models.Index(fields=["event_type"], name="mech_auto_hist_event_type_idx"),
|
||||
]
|
||||
|
||||
|
||||
class QuickevaluationhistoryModel(models.Model):
|
||||
"""QuickEvaluation bo'yicha barcha harakatlar logi — faqat o'qiladi, signallar tomonidan yoziladi."""
|
||||
|
||||
|
||||
250
core/apps/evaluation/models/mechanic_auto.py
Normal file
250
core/apps/evaluation/models/mechanic_auto.py
Normal file
@@ -0,0 +1,250 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_core.models import AbstractBaseModel
|
||||
from model_bakery import baker
|
||||
|
||||
from core.apps.evaluation.choices.auto import (
|
||||
AutoCarType,
|
||||
AutoCarWheel,
|
||||
AutoEvaluationStatus,
|
||||
AutoObjectType,
|
||||
ObjectOwnerType,
|
||||
)
|
||||
from .valuation import ValuationModel
|
||||
from .vehicle import VehicleModel
|
||||
|
||||
|
||||
class MechanicAutoEvaluationModel(AbstractBaseModel):
|
||||
user = models.ForeignKey(
|
||||
"accounts.User",
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="mechanic_auto_evaluations_user",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
evaluation_request = models.ForeignKey(
|
||||
"evaluation.EvaluationRequestModel",
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="mechanic_auto_evaluations_request",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
valuation = models.OneToOneField(
|
||||
ValuationModel,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="mechanic_auto_detail",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
vehicle = models.OneToOneField(
|
||||
VehicleModel,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="mechanic_evaluation",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
appraisers = models.ManyToManyField(
|
||||
"accounts.User",
|
||||
verbose_name=_("appraisers"),
|
||||
related_name="mechanic_auto_evaluation_appraisers",
|
||||
blank=True,
|
||||
)
|
||||
|
||||
tex_passport_file = models.FileField(
|
||||
verbose_name=_("tech passport file"),
|
||||
upload_to="mechanic_evaluation/tech_passports/%Y/%m/",
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
# ── Step 1 — Umumiy ma'lumotlar ──────────────────────────────────
|
||||
registration_number = models.CharField(
|
||||
verbose_name=_("registration number"),
|
||||
max_length=50,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
contract_date = models.DateField(
|
||||
verbose_name=_("contract date"),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
object_inspection_date = models.DateField(
|
||||
verbose_name=_("object inspection date"),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
rate_date = models.DateField(
|
||||
verbose_name=_("rate date"),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
rate_report_date = models.DateField(
|
||||
verbose_name=_("rate report date"),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
object_type = models.CharField(
|
||||
verbose_name=_("object type"),
|
||||
max_length=50,
|
||||
choices=AutoObjectType.choices,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
# ── Step 2 — Shaxs ma'lumotlari ─────────────────────────────────
|
||||
object_owner_type = models.IntegerField(
|
||||
verbose_name=_("object owner type"),
|
||||
choices=ObjectOwnerType.choices,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
object_owner_individual_person_f_name = models.CharField(
|
||||
verbose_name=_("owner first name"),
|
||||
max_length=100,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
object_owner_individual_person_l_name = models.CharField(
|
||||
verbose_name=_("owner last name"),
|
||||
max_length=100,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
object_owner_individual_person_p_name = models.CharField(
|
||||
verbose_name=_("owner patronymic"),
|
||||
max_length=100,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
object_owner_individual_person_passport_num = models.CharField(
|
||||
verbose_name=_("owner passport number"),
|
||||
max_length=20,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
object_owner_legal_entity = models.CharField(
|
||||
verbose_name=_("legal entity name"),
|
||||
max_length=255,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
object_owner_legal_inn = models.CharField(
|
||||
verbose_name=_("legal entity INN"),
|
||||
max_length=20,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
value_determined = models.ForeignKey(
|
||||
'evaluation.ReferenceitemModel',
|
||||
verbose_name=_("value determined"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='evaluation_mechanic_auto_value_determined'
|
||||
)
|
||||
rate_type = models.ForeignKey(
|
||||
'evaluation.ReferenceitemModel',
|
||||
verbose_name=_("rate type"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='evaluation_mechanic_auto_rate_type'
|
||||
)
|
||||
|
||||
# ── Step 4 — Avtomobil ma'lumotlari ─────────────────────────────
|
||||
tex_passport_serie_num = models.CharField(
|
||||
verbose_name=_("tech passport series and number"),
|
||||
max_length=20,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
tex_passport_gived_date = models.DateField(
|
||||
verbose_name=_("tech passport given date"),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
tex_passport_gived_location = models.CharField(
|
||||
verbose_name=_("tech passport given location"),
|
||||
max_length=255,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
car_type = models.IntegerField(
|
||||
verbose_name=_("car type"),
|
||||
choices=AutoCarType.choices,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
car_wheel = models.IntegerField(
|
||||
verbose_name=_("car wheel"),
|
||||
choices=AutoCarWheel.choices,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
car_brand = models.CharField(
|
||||
verbose_name=_("car brand"),
|
||||
max_length=100,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
car_model = models.CharField(
|
||||
verbose_name=_("car model"),
|
||||
max_length=100,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
car_number = models.CharField(
|
||||
verbose_name=_("car number"),
|
||||
max_length=20,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
manufacture_year = models.CharField(
|
||||
verbose_name=_("manufacture year"),
|
||||
max_length=10,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
car_dvigatel_number = models.CharField(
|
||||
verbose_name=_("engine number"),
|
||||
max_length=50,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
car_color = models.CharField(
|
||||
verbose_name=_("car color"),
|
||||
max_length=50,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
# ── Natija ───────────────────────────────────────────────────────
|
||||
rating_goal = models.CharField(
|
||||
verbose_name=_("rating goal"),
|
||||
max_length=50,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
status = models.CharField(
|
||||
verbose_name=_("status"),
|
||||
max_length=50,
|
||||
choices=AutoEvaluationStatus.choices,
|
||||
default=AutoEvaluationStatus.CREATED,
|
||||
)
|
||||
is_archived = models.BooleanField(
|
||||
verbose_name=_("is archived"),
|
||||
default=False,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"Mechanic Auto Evaluation {self.registration_number or self.pk}"
|
||||
|
||||
@classmethod
|
||||
def _baker(cls):
|
||||
return baker.make(cls)
|
||||
|
||||
class Meta:
|
||||
db_table = "MechanicAutoEvaluation"
|
||||
verbose_name = _("Mechanic Auto Evaluation")
|
||||
verbose_name_plural = _("Mechanic Auto Evaluations")
|
||||
@@ -3,12 +3,11 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django_core.models import AbstractBaseModel
|
||||
from model_bakery import baker
|
||||
|
||||
|
||||
from .valuation import ValuationModel
|
||||
from core.apps.evaluation.choices.movable import (
|
||||
MovablePropertyCategory,
|
||||
MovablePropertyCondition,
|
||||
)
|
||||
from .valuation import ValuationModel
|
||||
|
||||
|
||||
class MovablePropertyEvaluationModel(AbstractBaseModel):
|
||||
@@ -51,4 +50,3 @@ class MovablePropertyEvaluationModel(AbstractBaseModel):
|
||||
db_table = "MovablePropertyEvaluation"
|
||||
verbose_name = _("Movable Property Evaluation")
|
||||
verbose_name_plural = _("Movable Property Evaluations")
|
||||
|
||||
|
||||
@@ -8,64 +8,41 @@ from core.apps.evaluation.choices.request import (
|
||||
EvaluationRateType,
|
||||
RequestObjectType,
|
||||
RequestStatus,
|
||||
RequestPersonType,
|
||||
)
|
||||
from core.apps.evaluation.models import ReferenceitemModel
|
||||
|
||||
|
||||
class EvaluationrequestModel(AbstractBaseModel):
|
||||
# request sender
|
||||
rate_type = models.CharField(max_length=50,choices=EvaluationRateType.choices)
|
||||
object_type = models.CharField(max_length=50,choices=RequestObjectType.choices,blank=True,null=True)
|
||||
status = models.CharField(max_length=50, choices=RequestStatus.choices, default=RequestStatus.PENDING)
|
||||
|
||||
distance_covered = models.FloatField(default=0.0, null=True, blank=True)
|
||||
worked_hours = models.IntegerField(blank=True,null=True)
|
||||
customer_inn_number = models.CharField(max_length=20)
|
||||
owner_inn_number = models.CharField(max_length=20)
|
||||
tex_passport = models.CharField(max_length=20,blank=True,null=True)
|
||||
chassi = models.CharField(max_length=100,blank=True,null=True)
|
||||
gov_number = models.CharField(max_length=100, null=True, blank=True)
|
||||
|
||||
location_name = models.CharField(max_length=255,blank=True,null=True)
|
||||
location_lat = models.DecimalField(max_digits=9,decimal_places=6,blank=True, null=True)
|
||||
location_lng = models.DecimalField(max_digits=9,decimal_places=6,blank=True,null=True)
|
||||
|
||||
need_delivering = models.BooleanField(default=True)
|
||||
is_archive = models.BooleanField(default=False)
|
||||
customer_and_owner_same = models.BooleanField(default=False)
|
||||
|
||||
###################
|
||||
# Foreign Keys
|
||||
###################
|
||||
user = models.ForeignKey(
|
||||
"accounts.User",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="evaluation_requests",
|
||||
verbose_name=_("user"),
|
||||
)
|
||||
|
||||
# request type -> "Automobil", "Ko'chmas mulk", "Uskuna"
|
||||
rate_type = models.CharField(
|
||||
verbose_name=_("rate type"),
|
||||
max_length=50,
|
||||
choices=EvaluationRateType.choices,
|
||||
)
|
||||
###################
|
||||
# Automobil fields
|
||||
###################
|
||||
object_type = models.CharField(
|
||||
verbose_name=_("object type"),
|
||||
max_length=50,
|
||||
choices=RequestObjectType.choices,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
customer_inn_number = models.CharField(
|
||||
verbose_name=_("customer INN number"),
|
||||
max_length=20,
|
||||
)
|
||||
owner_inn_number = models.CharField(
|
||||
verbose_name=_("owner INN number"),
|
||||
max_length=20,
|
||||
)
|
||||
tex_passport = models.CharField(
|
||||
verbose_name=_("tex passport"),
|
||||
max_length=20,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
worked_hours = models.IntegerField(
|
||||
verbose_name=_("worked hours"),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
chassi = models.CharField(
|
||||
verbose_name=_("chassi"),
|
||||
max_length=100,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
###################
|
||||
# Value fields
|
||||
###################
|
||||
value_determined = models.ForeignKey(
|
||||
"evaluation.ReferenceitemModel",
|
||||
verbose_name=_("value determined"),
|
||||
@@ -99,47 +76,6 @@ class EvaluationrequestModel(AbstractBaseModel):
|
||||
null=True,
|
||||
)
|
||||
|
||||
###################
|
||||
# Location fields
|
||||
###################
|
||||
location_name = models.CharField(
|
||||
verbose_name=_("location name"),
|
||||
max_length=255,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
location_lat = models.DecimalField(
|
||||
verbose_name=_("location latitude"),
|
||||
max_digits=9,
|
||||
decimal_places=6,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
location_lng = models.DecimalField(
|
||||
verbose_name=_("location longitude"),
|
||||
max_digits=9,
|
||||
decimal_places=6,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
###################
|
||||
# Other fields
|
||||
###################
|
||||
need_delivering = models.BooleanField(
|
||||
verbose_name=_("need delivering"),
|
||||
default=True,
|
||||
)
|
||||
status = models.CharField(
|
||||
verbose_name=_("status"),
|
||||
max_length=50,
|
||||
choices=RequestStatus.choices,
|
||||
default=RequestStatus.PENDING,
|
||||
)
|
||||
is_archive = models.BooleanField(
|
||||
verbose_name=_("is archive"),
|
||||
default=False,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"Requests #{self.pk} — {self.get_rate_type_display()}"
|
||||
@@ -166,3 +102,29 @@ class EvaluationrequestModel(AbstractBaseModel):
|
||||
db_table = "EvaluationRequest"
|
||||
verbose_name = _("Evaluation Request")
|
||||
verbose_name_plural = _("Evaluation Requests")
|
||||
|
||||
|
||||
class EvaluationRequestOwnerModel(AbstractBaseModel):
|
||||
evaluation_request = models.OneToOneField(EvaluationrequestModel, on_delete=models.CASCADE, related_name='owner')
|
||||
type = models.CharField(max_length=100, choices=RequestPersonType.choices)
|
||||
jshshir = models.CharField(max_length=100)
|
||||
|
||||
def __str__(self):
|
||||
return f"Owner #{self.pk} — {self.type}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Evaluation Request Owner")
|
||||
verbose_name_plural = _("Evaluation Request Owners")
|
||||
|
||||
|
||||
class EvaluationRequestCustomerModel(AbstractBaseModel):
|
||||
evaluation_request = models.OneToOneField(EvaluationrequestModel, on_delete=models.CASCADE, related_name='customer')
|
||||
type = models.CharField(max_length=100, choices=RequestPersonType.choices)
|
||||
jshshir = models.CharField(max_length=100)
|
||||
|
||||
def __str__(self):
|
||||
return f"Customer #{self.pk} — {self.type}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Evaluation Request Customer")
|
||||
verbose_name_plural = _("Evaluation Request Customers")
|
||||
|
||||
@@ -3,13 +3,36 @@ import re
|
||||
from django.contrib.auth import get_user_model
|
||||
from rest_framework import serializers
|
||||
|
||||
from django.db import transaction
|
||||
|
||||
from core.apps.evaluation.choices.request import RequestStatus
|
||||
from core.apps.evaluation.models import AutoEvaluationModel, ReferenceitemModel, EvaluationrequestModel
|
||||
from core.apps.evaluation.models import (
|
||||
AutoEvaluationModel,
|
||||
AutoEvaluationTexPassportFile,
|
||||
ReferenceitemModel,
|
||||
EvaluationrequestModel,
|
||||
)
|
||||
from core.apps.evaluation.serializers.reference import ListReferenceitemSerializer
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class AutoEvaluationTexPassportFileSerializer(serializers.ModelSerializer):
|
||||
file = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = AutoEvaluationTexPassportFile
|
||||
fields = ["id", "file"]
|
||||
|
||||
def get_file(self, obj):
|
||||
request = self.context.get("request")
|
||||
if not obj.file:
|
||||
return None
|
||||
if request:
|
||||
return request.build_absolute_uri(obj.file.url)
|
||||
return obj.file.url
|
||||
|
||||
|
||||
class BaseAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
status_display = serializers.CharField(source="get_status_display", read_only=True)
|
||||
object_type_display = serializers.CharField(source="get_object_type_display", read_only=True, default=None)
|
||||
@@ -17,8 +40,12 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
default=None)
|
||||
rate_type = ListReferenceitemSerializer(read_only=True)
|
||||
value_determined = ListReferenceitemSerializer(read_only=True)
|
||||
property_rights = ListReferenceitemSerializer(read_only=True)
|
||||
form_ownership = ListReferenceitemSerializer(read_only=True)
|
||||
car_position = ListReferenceitemSerializer(read_only=True)
|
||||
body_type = ListReferenceitemSerializer(read_only=True)
|
||||
fuel_type = ListReferenceitemSerializer(read_only=True)
|
||||
state_car = ListReferenceitemSerializer(read_only=True)
|
||||
assessment_task_type = ListReferenceitemSerializer(read_only=True)
|
||||
tex_passport_files = AutoEvaluationTexPassportFileSerializer(many=True, read_only=True)
|
||||
user = serializers.SerializerMethodField(method_name="get_user", read_only=True)
|
||||
|
||||
class Meta:
|
||||
@@ -34,7 +61,9 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"object_owner_individual_person_p_name",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"object_owner_residence",
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_files",
|
||||
"rating_goal",
|
||||
"registration_number",
|
||||
"object_type",
|
||||
@@ -44,13 +73,17 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"car_number",
|
||||
"manufacture_year",
|
||||
"car_color",
|
||||
"distance_covered",
|
||||
"car_position",
|
||||
"body_type",
|
||||
"fuel_type",
|
||||
"state_car",
|
||||
"assessment_task_type",
|
||||
"status",
|
||||
"status_display",
|
||||
"created_at",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"property_rights",
|
||||
"form_ownership",
|
||||
"user",
|
||||
"evaluation_request",
|
||||
]
|
||||
@@ -72,16 +105,8 @@ class ListAutoevaluationSerializer(BaseAutoevaluationSerializer):
|
||||
|
||||
|
||||
class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer):
|
||||
car_type_display = serializers.CharField(source="get_car_type_display", read_only=True, default=None)
|
||||
car_wheel_display = serializers.CharField(source="get_car_wheel_display", read_only=True, default=None)
|
||||
|
||||
# object_location_highways_display = serializers.CharField(
|
||||
# source="get_object_location_highways_display", read_only=True, default=None
|
||||
# )
|
||||
# object_location_covenience_display = serializers.CharField(
|
||||
# source="get_object_location_covenience_display", read_only=True, default=None
|
||||
# )
|
||||
|
||||
class Meta(BaseAutoevaluationSerializer.Meta):
|
||||
fields = BaseAutoevaluationSerializer.Meta.fields + [
|
||||
# Step 1
|
||||
@@ -89,7 +114,6 @@ class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer):
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"rate_object_name",
|
||||
# Step 2
|
||||
"object_owner_type",
|
||||
"object_owner_type_display",
|
||||
@@ -101,11 +125,8 @@ class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer):
|
||||
"object_owner_legal_inn",
|
||||
# Step 4
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_file",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_type",
|
||||
"car_type_display",
|
||||
"car_wheel",
|
||||
"car_wheel_display",
|
||||
"car_dvigatel_number",
|
||||
@@ -118,21 +139,6 @@ class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer):
|
||||
|
||||
|
||||
class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
property_rights = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
value_determined = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
form_ownership = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
value_determined = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
@@ -143,6 +149,36 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
car_position = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
body_type = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
fuel_type = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
state_car = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
assessment_task_type = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
tex_passport_files = serializers.ListField(
|
||||
child=serializers.FileField(),
|
||||
required=False,
|
||||
write_only=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = AutoEvaluationModel
|
||||
@@ -153,7 +189,6 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"rate_object_name",
|
||||
"object_type",
|
||||
# Step 2
|
||||
"object_owner_type",
|
||||
@@ -163,16 +198,15 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"property_rights",
|
||||
"form_ownership",
|
||||
"object_owner_residence",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"assessment_task_type",
|
||||
# Step 4
|
||||
"tex_passport_file",
|
||||
"tex_passport_files",
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_type",
|
||||
"car_wheel",
|
||||
"car_brand",
|
||||
"car_model",
|
||||
@@ -180,6 +214,11 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"manufacture_year",
|
||||
"car_dvigatel_number",
|
||||
"car_color",
|
||||
"distance_covered",
|
||||
"car_position",
|
||||
"body_type",
|
||||
"fuel_type",
|
||||
"state_car",
|
||||
]
|
||||
|
||||
def validate_tex_passport_serie_num(self, value):
|
||||
@@ -224,23 +263,23 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
|
||||
return attrs
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
files = validated_data.pop("tex_passport_files", None)
|
||||
with transaction.atomic():
|
||||
for attr, value in validated_data.items():
|
||||
setattr(instance, attr, value)
|
||||
instance.save()
|
||||
if files is not None:
|
||||
AutoEvaluationTexPassportFile.objects.bulk_create([
|
||||
AutoEvaluationTexPassportFile(auto_evaluation=instance, file=f) for f in files
|
||||
])
|
||||
return instance
|
||||
|
||||
def to_representation(self, instance):
|
||||
return RetrieveAutoevaluationSerializer(instance, context=self.context).data
|
||||
|
||||
|
||||
class CreateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
property_rights = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
value_determined = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
form_ownership = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
value_determined = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
@@ -251,11 +290,41 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
car_position = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
body_type = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
fuel_type = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
state_car = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
assessment_task_type = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
evaluation_request = serializers.PrimaryKeyRelatedField(
|
||||
queryset=EvaluationrequestModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
tex_passport_files = serializers.ListField(
|
||||
child=serializers.FileField(),
|
||||
required=False,
|
||||
write_only=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = AutoEvaluationModel
|
||||
@@ -267,7 +336,6 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"rate_object_name",
|
||||
"object_type",
|
||||
# Step 2
|
||||
"object_owner_type",
|
||||
@@ -277,16 +345,15 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"property_rights",
|
||||
"form_ownership",
|
||||
"object_owner_residence",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"assessment_task_type",
|
||||
# Step 4
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_file",
|
||||
"tex_passport_files",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_type",
|
||||
"car_wheel",
|
||||
"car_brand",
|
||||
"car_model",
|
||||
@@ -294,6 +361,11 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"manufacture_year",
|
||||
"car_dvigatel_number",
|
||||
"car_color",
|
||||
"distance_covered",
|
||||
"car_position",
|
||||
"body_type",
|
||||
"fuel_type",
|
||||
"state_car",
|
||||
]
|
||||
|
||||
def validate_tex_passport_serie_num(self, value):
|
||||
@@ -338,13 +410,23 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
return attrs
|
||||
|
||||
def create(self, validated_data):
|
||||
files = validated_data.pop("tex_passport_files", [])
|
||||
user = self.context.get('request').user
|
||||
validated_data['user'] = user
|
||||
evaluation_req = validated_data.get("evaluation_request")
|
||||
if evaluation_req:
|
||||
evaluation_req.status = RequestStatus.IN_PROGRESS
|
||||
evaluation_req.save()
|
||||
return super().create(validated_data)
|
||||
with transaction.atomic():
|
||||
instance = super().create(validated_data)
|
||||
if files:
|
||||
AutoEvaluationTexPassportFile.objects.bulk_create([
|
||||
AutoEvaluationTexPassportFile(auto_evaluation=instance, file=f) for f in files
|
||||
])
|
||||
return instance
|
||||
|
||||
def to_representation(self, instance):
|
||||
return RetrieveAutoevaluationSerializer(instance, context=self.context).data
|
||||
|
||||
|
||||
class AutoEvaluationAppraisersSerializer(serializers.Serializer):
|
||||
@@ -359,6 +441,7 @@ class AutoEvaluationAppraisersSerializer(serializers.Serializer):
|
||||
data['users'] = users
|
||||
return data
|
||||
|
||||
|
||||
class AutoEvaluationSerializer(serializers.Serializer):
|
||||
brand = serializers.CharField()
|
||||
brand_model = serializers.CharField()
|
||||
@@ -368,3 +451,64 @@ class AutoEvaluationSerializer(serializers.Serializer):
|
||||
condition = serializers.CharField()
|
||||
fuel_type = serializers.CharField()
|
||||
mileage = serializers.CharField()
|
||||
|
||||
|
||||
class AutoEvaluationModelSerializer(serializers.ModelSerializer):
|
||||
user = serializers.StringRelatedField(read_only=True)
|
||||
appraisers = serializers.PrimaryKeyRelatedField(
|
||||
many=True,
|
||||
queryset=User.objects.all(),
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = AutoEvaluationModel
|
||||
fields = (
|
||||
"registration_number",
|
||||
"contract_date",
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"object_type",
|
||||
|
||||
"object_owner_type",
|
||||
"object_owner_individual_person_f_name",
|
||||
"object_owner_individual_person_l_name",
|
||||
"object_owner_individual_person_p_name",
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"object_owner_residence",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"assessment_task_type",
|
||||
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_wheel",
|
||||
"car_brand",
|
||||
"car_model",
|
||||
"car_number",
|
||||
"manufacture_year",
|
||||
"car_dvigatel_number",
|
||||
"car_color",
|
||||
"distance_covered",
|
||||
"car_position",
|
||||
"body_type",
|
||||
"fuel_type",
|
||||
"state_car",
|
||||
|
||||
"rating_goal",
|
||||
"status",
|
||||
"is_archived",
|
||||
|
||||
"created_at",
|
||||
"updated_at",
|
||||
)
|
||||
|
||||
read_only_fields = (
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
)
|
||||
|
||||
370
core/apps/evaluation/serializers/auto/MechanicAutoEvaluation.py
Normal file
370
core/apps/evaluation/serializers/auto/MechanicAutoEvaluation.py
Normal file
@@ -0,0 +1,370 @@
|
||||
import re
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.evaluation.choices.request import RequestStatus
|
||||
from core.apps.evaluation.models import (
|
||||
MechanicAutoEvaluationModel,
|
||||
ReferenceitemModel,
|
||||
EvaluationrequestModel,
|
||||
)
|
||||
from core.apps.evaluation.serializers.reference import ListReferenceitemSerializer
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class BaseMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
status_display = serializers.CharField(source="get_status_display", read_only=True)
|
||||
object_type_display = serializers.CharField(source="get_object_type_display", read_only=True, default=None)
|
||||
object_owner_type_display = serializers.CharField(source="get_object_owner_type_display", read_only=True,
|
||||
default=None)
|
||||
rate_type = ListReferenceitemSerializer(read_only=True)
|
||||
value_determined = ListReferenceitemSerializer(read_only=True)
|
||||
user = serializers.SerializerMethodField(method_name="get_user", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = MechanicAutoEvaluationModel
|
||||
fields = [
|
||||
"id",
|
||||
"contract_date",
|
||||
"object_inspection_date",
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_type",
|
||||
"object_owner_individual_person_f_name",
|
||||
"object_owner_individual_person_l_name",
|
||||
"object_owner_individual_person_p_name",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"tex_passport_serie_num",
|
||||
"rating_goal",
|
||||
"registration_number",
|
||||
"object_type",
|
||||
"object_type_display",
|
||||
"car_brand",
|
||||
"car_model",
|
||||
"car_number",
|
||||
"manufacture_year",
|
||||
"car_color",
|
||||
"status",
|
||||
"status_display",
|
||||
"created_at",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"user",
|
||||
"evaluation_request",
|
||||
]
|
||||
|
||||
def get_user(self, obj):
|
||||
request = self.context.get('request')
|
||||
return {
|
||||
"id": obj.user.id,
|
||||
"phone": obj.user.phone,
|
||||
"first_name": obj.user.first_name,
|
||||
"last_name": obj.user.last_name,
|
||||
"avatar": request.build_absolute_uri(obj.user.avatar.url) if obj.user.avatar else None
|
||||
} if obj.user else None
|
||||
|
||||
|
||||
class ListMechanicAutoevaluationSerializer(BaseMechanicAutoevaluationSerializer):
|
||||
class Meta(BaseMechanicAutoevaluationSerializer.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class RetrieveMechanicAutoevaluationSerializer(BaseMechanicAutoevaluationSerializer):
|
||||
car_type_display = serializers.CharField(source="get_car_type_display", read_only=True, default=None)
|
||||
car_wheel_display = serializers.CharField(source="get_car_wheel_display", read_only=True, default=None)
|
||||
|
||||
class Meta(BaseMechanicAutoevaluationSerializer.Meta):
|
||||
fields = BaseMechanicAutoevaluationSerializer.Meta.fields + [
|
||||
"contract_date",
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"object_owner_type",
|
||||
"object_owner_type_display",
|
||||
"object_owner_individual_person_f_name",
|
||||
"object_owner_individual_person_l_name",
|
||||
"object_owner_individual_person_p_name",
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_file",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_type",
|
||||
"car_type_display",
|
||||
"car_wheel",
|
||||
"car_wheel_display",
|
||||
"car_dvigatel_number",
|
||||
"valuation",
|
||||
"vehicle",
|
||||
"rating_goal",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
|
||||
class UpdateMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
value_determined = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
rate_type = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
user = serializers.PrimaryKeyRelatedField(
|
||||
queryset=User.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MechanicAutoEvaluationModel
|
||||
fields = [
|
||||
"user",
|
||||
"registration_number",
|
||||
"contract_date",
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"object_type",
|
||||
"object_owner_type",
|
||||
"object_owner_individual_person_f_name",
|
||||
"object_owner_individual_person_l_name",
|
||||
"object_owner_individual_person_p_name",
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"tex_passport_file",
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_type",
|
||||
"car_wheel",
|
||||
"car_brand",
|
||||
"car_model",
|
||||
"car_number",
|
||||
"manufacture_year",
|
||||
"car_dvigatel_number",
|
||||
"car_color",
|
||||
]
|
||||
|
||||
def validate_tex_passport_serie_num(self, value):
|
||||
if value and not re.match(r"^[A-Z]{3}\s?\d{7}$", value):
|
||||
raise serializers.ValidationError(
|
||||
"Format: AAA 1234567 (3 harf + 7 raqam)"
|
||||
)
|
||||
return value
|
||||
|
||||
def validate_object_owner_individual_person_passport_num(self, value):
|
||||
if value and not re.match(r"^[A-Z]{2}\s?\d{7}$", value):
|
||||
raise serializers.ValidationError(
|
||||
"Format: AA 1234567 (2 harf + 7 raqam)"
|
||||
)
|
||||
return value
|
||||
|
||||
def validate(self, attrs):
|
||||
owner_type = attrs.get("object_owner_type")
|
||||
|
||||
if owner_type == 1:
|
||||
required_fields = {
|
||||
"object_owner_individual_person_f_name": "Ismi",
|
||||
"object_owner_individual_person_l_name": "Familiyasi",
|
||||
"object_owner_individual_person_p_name": "Sharifi",
|
||||
"object_owner_individual_person_passport_num": "Passport raqami",
|
||||
}
|
||||
for field, label in required_fields.items():
|
||||
if not attrs.get(field):
|
||||
raise serializers.ValidationError(
|
||||
{field: f"Jismoniy shaxs uchun {label} majburiy."}
|
||||
)
|
||||
|
||||
elif owner_type == 2:
|
||||
if not attrs.get("object_owner_legal_entity"):
|
||||
raise serializers.ValidationError(
|
||||
{"object_owner_legal_entity": "Yuridik shaxs nomi majburiy."}
|
||||
)
|
||||
if not attrs.get("object_owner_legal_inn"):
|
||||
raise serializers.ValidationError(
|
||||
{"object_owner_legal_inn": "INN raqami majburiy."}
|
||||
)
|
||||
|
||||
return attrs
|
||||
|
||||
|
||||
class CreateMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
value_determined = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
rate_type = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ReferenceitemModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
evaluation_request = serializers.PrimaryKeyRelatedField(
|
||||
queryset=EvaluationrequestModel.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
user = serializers.PrimaryKeyRelatedField(
|
||||
queryset=User.objects.all(),
|
||||
required=True,
|
||||
allow_null=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MechanicAutoEvaluationModel
|
||||
fields = [
|
||||
"user",
|
||||
"registration_number",
|
||||
"evaluation_request",
|
||||
"contract_date",
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"object_type",
|
||||
"object_owner_type",
|
||||
"object_owner_individual_person_f_name",
|
||||
"object_owner_individual_person_l_name",
|
||||
"object_owner_individual_person_p_name",
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_file",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_type",
|
||||
"car_wheel",
|
||||
"car_brand",
|
||||
"car_model",
|
||||
"car_number",
|
||||
"manufacture_year",
|
||||
"car_dvigatel_number",
|
||||
"car_color",
|
||||
]
|
||||
|
||||
def validate_tex_passport_serie_num(self, value):
|
||||
if value and not re.match(r"^[A-Z]{3}\s?\d{7}$", value):
|
||||
raise serializers.ValidationError(
|
||||
"Format: AAA 1234567 (3 harf + 7 raqam)"
|
||||
)
|
||||
return value
|
||||
|
||||
def validate_object_owner_individual_person_passport_num(self, value):
|
||||
if value and not re.match(r"^[A-Z]{2}\s?\d{7}$", value):
|
||||
raise serializers.ValidationError(
|
||||
"Format: AA 1234567 (2 harf + 7 raqam)"
|
||||
)
|
||||
return value
|
||||
|
||||
def validate(self, attrs):
|
||||
owner_type = attrs.get("object_owner_type")
|
||||
if owner_type == 1:
|
||||
required_fields = {
|
||||
"object_owner_individual_person_f_name": "Ismi",
|
||||
"object_owner_individual_person_l_name": "Familiyasi",
|
||||
"object_owner_individual_person_p_name": "Sharifi",
|
||||
"object_owner_individual_person_passport_num": "Passport raqami",
|
||||
}
|
||||
for field, label in required_fields.items():
|
||||
if not attrs.get(field):
|
||||
raise serializers.ValidationError(
|
||||
{field: f"Jismoniy shaxs uchun {label} majburiy."}
|
||||
)
|
||||
|
||||
elif owner_type == 2:
|
||||
if not attrs.get("object_owner_legal_entity"):
|
||||
raise serializers.ValidationError(
|
||||
{"object_owner_legal_entity": "Yuridik shaxs nomi majburiy."}
|
||||
)
|
||||
if not attrs.get("object_owner_legal_inn"):
|
||||
raise serializers.ValidationError(
|
||||
{"object_owner_legal_inn": "INN raqami majburiy."}
|
||||
)
|
||||
|
||||
return attrs
|
||||
|
||||
def create(self, validated_data):
|
||||
evaluation_req = validated_data.get("evaluation_request")
|
||||
if evaluation_req:
|
||||
evaluation_req.status = RequestStatus.IN_PROGRESS
|
||||
evaluation_req.save()
|
||||
return super().create(validated_data)
|
||||
|
||||
|
||||
class MechanicAutoEvaluationAppraisersSerializer(serializers.Serializer):
|
||||
ids = serializers.ListField(child=serializers.IntegerField())
|
||||
|
||||
def validate(self, data):
|
||||
if not data.get("ids"):
|
||||
raise serializers.ValidationError("Appraisers IDs are required.")
|
||||
users = User.objects.filter(id__in=data["ids"])
|
||||
if not users:
|
||||
raise serializers.ValidationError("Invalid appraisers IDs.")
|
||||
data['users'] = users
|
||||
return data
|
||||
|
||||
|
||||
class MechanicAutoEvaluationModelSerializer(serializers.ModelSerializer):
|
||||
user = serializers.StringRelatedField(read_only=True)
|
||||
appraisers = serializers.PrimaryKeyRelatedField(
|
||||
many=True,
|
||||
queryset=User.objects.all(),
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MechanicAutoEvaluationModel
|
||||
fields = (
|
||||
"tex_passport_file",
|
||||
"registration_number",
|
||||
"contract_date",
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"object_type",
|
||||
"object_owner_type",
|
||||
"object_owner_individual_person_f_name",
|
||||
"object_owner_individual_person_l_name",
|
||||
"object_owner_individual_person_p_name",
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_type",
|
||||
"car_wheel",
|
||||
"car_brand",
|
||||
"car_model",
|
||||
"car_number",
|
||||
"manufacture_year",
|
||||
"car_dvigatel_number",
|
||||
"car_color",
|
||||
"rating_goal",
|
||||
"status",
|
||||
"is_archived",
|
||||
"user",
|
||||
"appraisers",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
)
|
||||
|
||||
read_only_fields = (
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
)
|
||||
@@ -1 +1,2 @@
|
||||
from .AutoEvaluation import * # noqa
|
||||
from .MechanicAutoEvaluation import * # noqa
|
||||
|
||||
56
core/apps/evaluation/serializers/bonus/Bonus.py
Normal file
56
core/apps/evaluation/serializers/bonus/Bonus.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.evaluation.models.bonus import BonusCategory, EmployeeBonus, BaseValueBonus
|
||||
|
||||
|
||||
class BaseBonusSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = BaseValueBonus
|
||||
fields = ['id', 'base_price']
|
||||
|
||||
def create(self, validated_data):
|
||||
if BaseValueBonus.objects.exists():
|
||||
raise serializers.ValidationError("Base bonus already exists")
|
||||
|
||||
return super().create(validated_data)
|
||||
|
||||
|
||||
class BonusCategorySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = BonusCategory
|
||||
fields = ['name', 'category', 'percentage']
|
||||
|
||||
|
||||
class BonusCategoryListSerializer(serializers.ModelSerializer):
|
||||
price = serializers.DecimalField(max_digits=12, decimal_places=2)
|
||||
|
||||
class Meta:
|
||||
model = BonusCategory
|
||||
fields = ['id', 'name', 'category', 'percentage' , 'price']
|
||||
|
||||
def get_price(self, obj):
|
||||
base_obj = BaseValueBonus.objects.first()
|
||||
if not base_obj:
|
||||
return 0
|
||||
|
||||
return (base_obj.base_price * obj.percentage) / 100
|
||||
|
||||
|
||||
class BonusEmployeeBonusSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = EmployeeBonus
|
||||
fields = ['user', 'bonus_type', 'percentage']
|
||||
|
||||
|
||||
class EmployeeBonusListSerializer(serializers.ModelSerializer):
|
||||
price = serializers.DecimalField(max_digits=12, decimal_places=2)
|
||||
|
||||
class Meta:
|
||||
model = EmployeeBonus
|
||||
fields = ['id', 'user', 'bonus_type', 'percentage' , 'price']
|
||||
|
||||
def get_price(self, obj):
|
||||
base_obj = BaseValueBonus.objects.first()
|
||||
if not base_obj:
|
||||
return 0
|
||||
return (base_obj.base_price * obj.percentage) / 100
|
||||
0
core/apps/evaluation/serializers/bonus/__init__.py
Normal file
0
core/apps/evaluation/serializers/bonus/__init__.py
Normal file
@@ -3,7 +3,7 @@ from core.apps.evaluation.models import CertificateModel
|
||||
|
||||
|
||||
class BaseCertificateSerializer(serializers.ModelSerializer):
|
||||
file = serializers.SerializerMethodField()
|
||||
file = serializers.SerializerMethodField(method_name='get_file', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = CertificateModel
|
||||
@@ -16,7 +16,15 @@ class BaseCertificateSerializer(serializers.ModelSerializer):
|
||||
def get_file(self, obj):
|
||||
if obj.file:
|
||||
request = self.context.get('request')
|
||||
if request:
|
||||
return request.build_absolute_uri(obj.file.url)
|
||||
return obj.file.url
|
||||
return None
|
||||
|
||||
|
||||
class CreateCertificateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = CertificateModel
|
||||
fields = [
|
||||
"id",
|
||||
"title",
|
||||
"file",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.evaluation.models import MechanicAutoevaluationhistoryModel
|
||||
|
||||
|
||||
class BaseMechanicAutoevaluationhistorySerializer(serializers.ModelSerializer):
|
||||
event_type_display = serializers.CharField(source="get_event_type_display", read_only=True)
|
||||
actor = serializers.SerializerMethodField()
|
||||
|
||||
def get_actor(self, obj):
|
||||
return {
|
||||
"id": obj.actor_id,
|
||||
"full_name": obj.actor_full_name,
|
||||
"role": obj.actor_role,
|
||||
}
|
||||
|
||||
class Meta:
|
||||
model = MechanicAutoevaluationhistoryModel
|
||||
fields = [
|
||||
"id",
|
||||
"event_type",
|
||||
"event_type_display",
|
||||
"actor",
|
||||
"meta",
|
||||
"created_at",
|
||||
]
|
||||
|
||||
|
||||
class ListMechanicAutoevaluationhistorySerializer(BaseMechanicAutoevaluationhistorySerializer):
|
||||
class Meta(BaseMechanicAutoevaluationhistorySerializer.Meta): ...
|
||||
|
||||
|
||||
class RetrieveMechanicAutoevaluationhistorySerializer(BaseMechanicAutoevaluationhistorySerializer):
|
||||
class Meta(BaseMechanicAutoevaluationhistorySerializer.Meta):
|
||||
fields = BaseMechanicAutoevaluationhistorySerializer.Meta.fields + ["mechanic_auto_evaluation"]
|
||||
|
||||
|
||||
class CreateMechanicAutoevaluationhistorySerializer(BaseMechanicAutoevaluationhistorySerializer):
|
||||
class Meta(BaseMechanicAutoevaluationhistorySerializer.Meta): ...
|
||||
@@ -1,2 +1,3 @@
|
||||
from .AutoEvaluationHistory import * # noqa
|
||||
from .MechanicAutoEvaluationHistory import * # noqa
|
||||
from .QuickEvaluationHistory import * # noqa
|
||||
|
||||
@@ -128,3 +128,42 @@ class CreateQuickevaluationSerializer(serializers.ModelSerializer):
|
||||
return super().create(validated_data)
|
||||
|
||||
|
||||
class QuickEvaluationModelSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = QuickEvaluationModel
|
||||
fields = (
|
||||
"id",
|
||||
|
||||
"created_by",
|
||||
"brand",
|
||||
"marka",
|
||||
"car_position",
|
||||
"body_type",
|
||||
"color",
|
||||
"fuel_type",
|
||||
"state_car",
|
||||
|
||||
"tex_passport_serie_num",
|
||||
"tech_passport_issued_date",
|
||||
"tech_passport_issued_place",
|
||||
|
||||
"car_type",
|
||||
"distance_covered",
|
||||
"vin_number",
|
||||
"car_number",
|
||||
"car_manufactured_date",
|
||||
"engine_number",
|
||||
|
||||
"estimated_price",
|
||||
"status",
|
||||
"is_archive",
|
||||
|
||||
"created_at",
|
||||
"updated_at",
|
||||
)
|
||||
|
||||
read_only_fields = (
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
)
|
||||
@@ -4,8 +4,11 @@ from django.contrib.auth import get_user_model
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.evaluation.models import EvaluationrequestModel, ReferenceitemModel
|
||||
from core.apps.evaluation.models import EvaluationrequestModel, ReferenceitemModel, EvaluationRequestOwnerModel, EvaluationRequestCustomerModel
|
||||
from core.apps.evaluation.serializers.reference import ListReferenceitemSerializer
|
||||
from core.apps.evaluation.serializers.request.owner import EvaluationRequestOwnerSerializer
|
||||
from core.apps.evaluation.serializers.request.req_customer import EvaluationRequestCustomerSerializer
|
||||
|
||||
|
||||
|
||||
User = get_user_model()
|
||||
@@ -29,6 +32,8 @@ class BaseEvaluationrequestSerializer(serializers.ModelSerializer):
|
||||
property_rights = ListReferenceitemSerializer(read_only=True)
|
||||
form_ownership = ListReferenceitemSerializer(read_only=True)
|
||||
user = serializers.SerializerMethodField(method_name="get_user")
|
||||
customer = EvaluationRequestCustomerSerializer(read_only=True)
|
||||
owner = EvaluationRequestOwnerSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = EvaluationrequestModel
|
||||
@@ -56,6 +61,8 @@ class BaseEvaluationrequestSerializer(serializers.ModelSerializer):
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"is_archive",
|
||||
"customer",
|
||||
"owner",
|
||||
]
|
||||
|
||||
def get_location(self, obj):
|
||||
@@ -113,6 +120,8 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
|
||||
rate_goal = serializers.PrimaryKeyRelatedField(required=False, queryset=ReferenceitemModel.objects.all())
|
||||
property_rights = serializers.PrimaryKeyRelatedField(required=False, queryset=ReferenceitemModel.objects.all())
|
||||
form_ownership = serializers.PrimaryKeyRelatedField(required=False, queryset=ReferenceitemModel.objects.all())
|
||||
customer = EvaluationRequestCustomerSerializer()
|
||||
owner = EvaluationRequestOwnerSerializer()
|
||||
|
||||
class Meta:
|
||||
model = EvaluationrequestModel
|
||||
@@ -131,6 +140,11 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
|
||||
"need_delivering",
|
||||
"location",
|
||||
"locationName",
|
||||
"customer",
|
||||
"owner",
|
||||
"customer_and_owner_same",
|
||||
"distance_covered",
|
||||
"gov_number"
|
||||
]
|
||||
|
||||
def validate_tex_passport(self, value):
|
||||
@@ -179,7 +193,31 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
|
||||
if location_name:
|
||||
validated_data["location_name"] = str(location_name)
|
||||
validated_data["user"] = self.context["request"].user
|
||||
return super().create(validated_data)
|
||||
|
||||
instance = super().create(validated_data)
|
||||
|
||||
customer = validated_data.pop("customer", None)
|
||||
owner = validated_data.pop("owner", None)
|
||||
EvaluationRequestCustomerModel.objects.create(
|
||||
evaluation_request=instance,
|
||||
type=customer.get("type"),
|
||||
jshshir=customer.get("jshshir")
|
||||
)
|
||||
if not instance.customer_and_owner_same:
|
||||
EvaluationRequestOwnerModel.objects.create(
|
||||
evaluation_request=instance,
|
||||
type=owner.get("type"),
|
||||
jshshir=owner.get("jshshir")
|
||||
)
|
||||
else:
|
||||
EvaluationRequestOwnerModel.objects.create(
|
||||
evaluation_request=instance,
|
||||
type=customer.get("type"),
|
||||
jshshir=customer.get("jshshir")
|
||||
)
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
class ArchiveEvaluationrequestSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField(required=True)
|
||||
|
||||
9
core/apps/evaluation/serializers/request/owner.py
Normal file
9
core/apps/evaluation/serializers/request/owner.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.evaluation.models.request import EvaluationRequestOwnerModel
|
||||
|
||||
|
||||
class EvaluationRequestOwnerSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = EvaluationRequestOwnerModel
|
||||
fields = ["id", "evaluation_request", "type", "jshshir"]
|
||||
9
core/apps/evaluation/serializers/request/req_customer.py
Normal file
9
core/apps/evaluation/serializers/request/req_customer.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.evaluation.models.request import EvaluationRequestCustomerModel
|
||||
|
||||
|
||||
class EvaluationRequestCustomerSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = EvaluationRequestCustomerModel
|
||||
fields = ["id", "evaluation_request", "type", "jshshir"]
|
||||
@@ -87,3 +87,29 @@ class CreateVehicleSerializer(serializers.ModelSerializer):
|
||||
"condition",
|
||||
"position",
|
||||
]
|
||||
|
||||
|
||||
class VehicleApplicationSerializer(serializers.Serializer):
|
||||
person_name = serializers.CharField()
|
||||
property_owner = serializers.CharField(max_length=100)
|
||||
address = serializers.CharField(max_length=255)
|
||||
marka = serializers.CharField(max_length=100)
|
||||
model = serializers.CharField(max_length=100)
|
||||
configuration = serializers.CharField(max_length=100)
|
||||
auto_number = serializers.CharField(max_length=100)
|
||||
date_created = serializers.DateTimeField()
|
||||
mileage = serializers.IntegerField()
|
||||
vehicle_identification = serializers.CharField(max_length=100)
|
||||
engine_number = serializers.CharField(max_length=100)
|
||||
colour = serializers.CharField(max_length=100)
|
||||
registration_certificate_series = serializers.CharField(max_length=100)
|
||||
tec_passport_number = serializers.CharField(max_length=100)
|
||||
tec_passport_date = serializers.DateTimeField()
|
||||
tec_passport_place = serializers.CharField(max_length=255)
|
||||
body_type = serializers.CharField(max_length=100)
|
||||
chassis = serializers.CharField(max_length=100)
|
||||
plate = serializers.CharField(max_length=100)
|
||||
value_type = serializers.CharField(max_length=100)
|
||||
evaluation_purpose = serializers.CharField(max_length=100)
|
||||
personal_id_number = serializers.IntegerField()
|
||||
id_number = serializers.CharField(max_length=9)
|
||||
|
||||
@@ -8,6 +8,7 @@ router = DefaultRouter()
|
||||
router.register("document-category", views.DocumentCategoryView, basename="DocumentCategory")
|
||||
router.register("document", views.DocumentView, basename="Document")
|
||||
router.register("auto-evaluation-history", views.AutoEvaluationHistoryView, basename="auto-evaluation-history")
|
||||
router.register("mechanic-auto-evaluation-history", views.MechanicAutoEvaluationHistoryView, basename="mechanic-auto-evaluation-history")
|
||||
router.register("quick-evaluation-history", views.QuickEvaluationHistoryView, basename="quick-evaluation-history")
|
||||
router.register("determined-value", views.DeterminedValueView, basename="determined-value")
|
||||
router.register("evaluation-purpose", views.EvaluationPurposeView, basename="evaluation-purpose")
|
||||
@@ -22,11 +23,15 @@ router.register("quick-evaluation", views.QuickEvaluationView, basename="quick-e
|
||||
router.register("movable-property-evaluation", views.MovablePropertyEvaluationView, basename="movable-property-evaluation")
|
||||
router.register("real-estate-evaluation", views.RealEstateEvaluationView, basename="real-estate-evaluation")
|
||||
router.register("auto-evaluation", views.AutoEvaluationView, basename="auto-evaluation")
|
||||
router.register("mechanic-auto-evaluation", views.MechanicAutoEvaluationView, basename="mechanic-auto-evaluation")
|
||||
router.register("vehicle", views.VehicleView, basename="vehicle")
|
||||
router.register("valuation", views.ValuationView, basename="valuation")
|
||||
router.register("property-owner", views.PropertyOwnerView, basename="property-owner")
|
||||
router.register("customer", views.CustomerView, basename="customer")
|
||||
router.register("certificate", views.CertificateView, basename="certificate")
|
||||
router.register("bonus-type", views.BonusTypeView, basename="bonus-type")
|
||||
router.register("bonus-employee", views.BonusEmployeeViewSet, basename="bonus-employee")
|
||||
router.register("bonus-base", views.BaseBonusViewSet, basename="bonus-base")
|
||||
|
||||
urlpatterns = [
|
||||
path("", include(router.urls)),
|
||||
@@ -37,6 +42,7 @@ urlpatterns = [
|
||||
# Quick Evaluation
|
||||
path('quick-evaluation/', include(
|
||||
[
|
||||
path("admin/", views.AdminQuickEvalAPIView.as_view(), name="quick-evaluation"),
|
||||
path(
|
||||
'archive/', include(
|
||||
[
|
||||
@@ -51,6 +57,7 @@ urlpatterns = [
|
||||
# Auto Evaluation
|
||||
path("auto-evaluation/", include(
|
||||
[
|
||||
path("admin/", views.AdminEvaluationsAPIView.as_view(), name="admin-evaluations"),
|
||||
path('archive/', include(
|
||||
[
|
||||
path('<int:pk>/', views.AutoEvaluationArchiveAPIView.as_view()),
|
||||
@@ -67,6 +74,26 @@ urlpatterns = [
|
||||
]
|
||||
)),
|
||||
|
||||
# Mechanic Auto Evaluation
|
||||
path("mechanic-auto-evaluation/", include(
|
||||
[
|
||||
path("admin/", views.AdminMechanicEvaluationsAPIView.as_view(), name="admin-mechanic-evaluations"),
|
||||
path('archive/', include(
|
||||
[
|
||||
path('<int:pk>/', views.MechanicAutoEvaluationArchiveAPIView.as_view()),
|
||||
path('list/', views.MechanicAutoEvaluationArchivedListAPIView.as_view())
|
||||
]
|
||||
)),
|
||||
path('appraisers/', include(
|
||||
[
|
||||
path("<int:id>/list/", views.MechanicAutoEvaluationListAppraisersView.as_view()),
|
||||
path("<int:id>/set/", views.MechanicAutoEvaluationSetAppraisersView.as_view()),
|
||||
path("<int:id>/remove/", views.MechanicAutoEvaluationRemoveAppraisersView.as_view()),
|
||||
]
|
||||
))
|
||||
]
|
||||
)),
|
||||
|
||||
# Evaluation Request
|
||||
path("evaluation-request/", include(
|
||||
[
|
||||
@@ -83,4 +110,5 @@ urlpatterns = [
|
||||
)),
|
||||
|
||||
path("calculate_avg_cost/", views.AvgCostAPIView.as_view()),
|
||||
path("vehicle_document/", views.GeneratePDFView.as_view()),
|
||||
]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from .auto import * # noqa
|
||||
from .mechanic_auto import * # noqa
|
||||
from .customer import * # noqa
|
||||
from .document import * # noqa
|
||||
from .documentcategory import * # noqa
|
||||
@@ -15,3 +16,4 @@ from .didox import * # noqa
|
||||
from .tech_passport import * # noqa
|
||||
from .certificate import * # noqa
|
||||
from .avg_cost import *
|
||||
from .bonus import *
|
||||
@@ -1,10 +1,9 @@
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from drf_spectacular.utils import extend_schema, OpenApiParameter
|
||||
|
||||
from rest_framework import generics
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.generics import GenericAPIView, ListAPIView
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
@@ -12,10 +11,12 @@ from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from core.apps.accounts.permissions import IsAdminRole
|
||||
from core.apps.accounts.serializers.user import UserSerializer
|
||||
from core.apps.evaluation.filters.auto import AutoevaluationFilter
|
||||
from core.apps.evaluation.models import AutoEvaluationModel
|
||||
from core.apps.evaluation.serializers import auto as serializers
|
||||
from core.apps.evaluation.serializers import auto as serializers, AutoEvaluationModelSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["AutoEvaluation"])
|
||||
class AutoEvaluationView(BaseViewSetMixin, ModelViewSet):
|
||||
@@ -62,8 +63,6 @@ class AutoEvaluationView(BaseViewSetMixin, ModelViewSet):
|
||||
"created_at",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"property_rights",
|
||||
"form_ownership",
|
||||
]
|
||||
ordering = ["-created_at"]
|
||||
|
||||
@@ -177,3 +176,17 @@ class AutoEvaluationArchiveAPIView(APIView):
|
||||
},
|
||||
status=200
|
||||
)
|
||||
|
||||
|
||||
@extend_schema(tags=["AutoEvaluation"])
|
||||
class AdminEvaluationsAPIView(generics.GenericAPIView):
|
||||
permission_classes = [IsAuthenticated, IsAdminRole]
|
||||
queryset = AutoEvaluationModel.objects.all()
|
||||
serializer_class = AutoEvaluationModel
|
||||
|
||||
def get(self, request):
|
||||
auto_eval = AutoEvaluationModel.objects.filter(
|
||||
created_by=self.request.user
|
||||
).select_related('appraisers').distinct()
|
||||
serializer = AutoEvaluationModelSerializer(auto_eval, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
60
core/apps/evaluation/views/bonus.py
Normal file
60
core/apps/evaluation/views/bonus.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
# core
|
||||
from core.apps.evaluation.models.bonus import BonusCategory, EmployeeBonus, BaseValueBonus
|
||||
from core.apps.evaluation.serializers.bonus.Bonus import BonusCategorySerializer, \
|
||||
BonusCategoryListSerializer, EmployeeBonusListSerializer, BonusEmployeeBonusSerializer, BaseBonusSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["BaseBonus"])
|
||||
class BaseBonusViewSet(BaseViewSetMixin, viewsets.ModelViewSet):
|
||||
queryset = BaseValueBonus.objects.all()
|
||||
serializer_class = BaseBonusSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["Bonus-Category"])
|
||||
class BonusTypeView(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = BonusCategory.objects.all()
|
||||
|
||||
serializer_class = BonusCategorySerializer
|
||||
|
||||
action_serializer_class = {
|
||||
'create': BonusCategorySerializer,
|
||||
'update': BonusCategorySerializer,
|
||||
'partial_update': BonusCategorySerializer,
|
||||
'list': BonusCategoryListSerializer,
|
||||
'retrieve': BonusCategoryListSerializer,
|
||||
}
|
||||
|
||||
action_permission_classes = {
|
||||
'create': [IsAdminUser],
|
||||
'update': [IsAdminUser],
|
||||
'partial_update': [IsAdminUser],
|
||||
'destroy': [IsAdminUser],
|
||||
'list': [IsAdminUser],
|
||||
}
|
||||
|
||||
|
||||
class BonusEmployeeViewSet(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = EmployeeBonus.objects.all()
|
||||
serializer_class = BonusEmployeeBonusSerializer
|
||||
|
||||
action_serializer_class = {
|
||||
'create': BonusEmployeeBonusSerializer,
|
||||
'update': BonusEmployeeBonusSerializer,
|
||||
'partial_update': BonusEmployeeBonusSerializer,
|
||||
'list': EmployeeBonusListSerializer,
|
||||
'retrieve': EmployeeBonusListSerializer,
|
||||
}
|
||||
|
||||
action_permission_classes = {
|
||||
'create': [IsAdminUser],
|
||||
'update': [IsAdminUser],
|
||||
'partial_update': [IsAdminUser],
|
||||
'destroy': [IsAdminUser],
|
||||
'list': [IsAdminUser],
|
||||
}
|
||||
@@ -12,7 +12,7 @@ from rest_framework.parsers import MultiPartParser, FormParser
|
||||
|
||||
# local apps
|
||||
from core.apps.evaluation.models import CertificateModel
|
||||
from core.apps.evaluation.serializers.certificate import BaseCertificateSerializer
|
||||
from core.apps.evaluation.serializers.certificate import BaseCertificateSerializer, CreateCertificateSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["Certificate"],request=BaseCertificateSerializer)
|
||||
@@ -25,3 +25,6 @@ class CertificateView(BaseViewSetMixin, ModelViewSet):
|
||||
search_fields = ["title"]
|
||||
pagination_class = None
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"create": CreateCertificateSerializer
|
||||
}
|
||||
|
||||
@@ -16,9 +16,14 @@ from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
# core apps
|
||||
from core.apps.evaluation.filters.history import (
|
||||
AutoevaluationhistoryFilter,
|
||||
MechanicAutoevaluationhistoryFilter,
|
||||
QuickevaluationhistoryFilter,
|
||||
)
|
||||
from core.apps.evaluation.models import AutoevaluationhistoryModel, QuickevaluationhistoryModel
|
||||
from core.apps.evaluation.models import (
|
||||
AutoevaluationhistoryModel,
|
||||
MechanicAutoevaluationhistoryModel,
|
||||
QuickevaluationhistoryModel,
|
||||
)
|
||||
from core.apps.evaluation.serializers import history as serializers
|
||||
|
||||
|
||||
@@ -72,6 +77,56 @@ class AutoEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
})
|
||||
|
||||
|
||||
@extend_schema(
|
||||
tags=["MechanicAutoEvaluationHistory"],
|
||||
parameters=[
|
||||
OpenApiParameter("mechanic_auto_evaluation", int, description="MechanicAutoEvaluation ID bo'yicha filter"),
|
||||
OpenApiParameter("event_type", str, description="Event turi bo'yicha filter"),
|
||||
OpenApiParameter("created_from", str, description="Boshlanish sanasi (ISO 8601)"),
|
||||
OpenApiParameter("created_to", str, description="Tugash sanasi (ISO 8601)"),
|
||||
],
|
||||
)
|
||||
class MechanicAutoEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = MechanicAutoevaluationhistoryModel.objects.only(
|
||||
"id", "mechanic_auto_evaluation_id", "event_type",
|
||||
"actor_id", "actor_full_name", "actor_role",
|
||||
"meta", "created_at",
|
||||
)
|
||||
serializer_class = serializers.ListMechanicAutoevaluationhistorySerializer
|
||||
permission_classes = [AllowAny]
|
||||
pagination_class = None
|
||||
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter]
|
||||
filterset_class = MechanicAutoevaluationhistoryFilter
|
||||
ordering_fields = [
|
||||
"id",
|
||||
"event_type",
|
||||
"event_type_display",
|
||||
"actor",
|
||||
"meta",
|
||||
"created_at",
|
||||
]
|
||||
ordering = ["created_at"]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": serializers.ListMechanicAutoevaluationhistorySerializer,
|
||||
"retrieve": serializers.RetrieveMechanicAutoevaluationhistorySerializer,
|
||||
"create": serializers.CreateMechanicAutoevaluationhistorySerializer,
|
||||
}
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
results = list(queryset)
|
||||
serializer = self.get_serializer(results, many=True)
|
||||
return Response({
|
||||
"count": len(results),
|
||||
"next": None,
|
||||
"previous": None,
|
||||
"results": serializer.data,
|
||||
})
|
||||
|
||||
|
||||
@extend_schema(
|
||||
tags=["QuickEvaluationHistory"],
|
||||
parameters=[
|
||||
|
||||
198
core/apps/evaluation/views/mechanic_auto.py
Normal file
198
core/apps/evaluation/views/mechanic_auto.py
Normal file
@@ -0,0 +1,198 @@
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from drf_spectacular.utils import extend_schema, OpenApiParameter
|
||||
from rest_framework import generics
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.generics import GenericAPIView, ListAPIView
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from core.apps.accounts.permissions import IsAdminRole
|
||||
from core.apps.accounts.serializers.user import UserSerializer
|
||||
from core.apps.evaluation.filters.mechanic_auto import MechanicAutoevaluationFilter
|
||||
from core.apps.evaluation.models import MechanicAutoEvaluationModel
|
||||
from core.apps.evaluation.serializers.auto.MechanicAutoEvaluation import (
|
||||
ListMechanicAutoevaluationSerializer,
|
||||
RetrieveMechanicAutoevaluationSerializer,
|
||||
CreateMechanicAutoevaluationSerializer,
|
||||
UpdateMechanicAutoevaluationSerializer,
|
||||
MechanicAutoEvaluationAppraisersSerializer,
|
||||
MechanicAutoEvaluationModelSerializer,
|
||||
)
|
||||
|
||||
|
||||
@extend_schema(tags=["MechanicAutoEvaluation"])
|
||||
class MechanicAutoEvaluationView(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = MechanicAutoEvaluationModel.objects.select_related(
|
||||
"valuation",
|
||||
"valuation__customer",
|
||||
"vehicle",
|
||||
).filter(is_archived=False)
|
||||
serializer_class = ListMechanicAutoevaluationSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
filterset_class = MechanicAutoevaluationFilter
|
||||
search_fields = [
|
||||
"registration_number",
|
||||
"car_model",
|
||||
"car_brand",
|
||||
"car_number",
|
||||
]
|
||||
ordering_fields = [
|
||||
"id",
|
||||
"contract_date",
|
||||
"object_inspection_date",
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_type",
|
||||
"object_owner_individual_person_f_name",
|
||||
"object_owner_individual_person_l_name",
|
||||
"object_owner_individual_person_p_name",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"tex_passport_serie_num",
|
||||
"rating_goal",
|
||||
"registration_number",
|
||||
"object_type",
|
||||
"object_type_display",
|
||||
"car_brand",
|
||||
"car_model",
|
||||
"car_number",
|
||||
"manufacture_year",
|
||||
"car_color",
|
||||
"status",
|
||||
"status_display",
|
||||
"created_at",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
]
|
||||
ordering = ["-created_at"]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListMechanicAutoevaluationSerializer,
|
||||
"retrieve": RetrieveMechanicAutoevaluationSerializer,
|
||||
"create": CreateMechanicAutoevaluationSerializer,
|
||||
"update": UpdateMechanicAutoevaluationSerializer,
|
||||
"partial_update": UpdateMechanicAutoevaluationSerializer,
|
||||
}
|
||||
|
||||
def serializer_context(self):
|
||||
return self.serializer_class(context={'request': self.request})
|
||||
|
||||
|
||||
@extend_schema(tags=["MechanicAutoEvaluation"])
|
||||
class MechanicAutoEvaluationSetAppraisersView(GenericAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
queryset = MechanicAutoEvaluationModel.objects.all()
|
||||
serializer_class = MechanicAutoEvaluationAppraisersSerializer
|
||||
|
||||
def post(self, request, id):
|
||||
try:
|
||||
evaluation = get_object_or_404(MechanicAutoEvaluationModel, id=id)
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
users = serializer.validated_data.get("users")
|
||||
evaluation.appraisers.set(users)
|
||||
evaluation.save()
|
||||
return Response({"success": True, "data": "Foydalanuvchilar muvaffaqiyatli qo'shildi"})
|
||||
except Exception as e:
|
||||
return Response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
@extend_schema(tags=["MechanicAutoEvaluation"])
|
||||
class MechanicAutoEvaluationRemoveAppraisersView(GenericAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
queryset = MechanicAutoEvaluationModel.objects.all()
|
||||
serializer_class = MechanicAutoEvaluationAppraisersSerializer
|
||||
|
||||
def post(self, request, id):
|
||||
try:
|
||||
evaluation = get_object_or_404(MechanicAutoEvaluationModel, id=id)
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
users = serializer.validated_data.get("users")
|
||||
evaluation.appraisers.remove(*users)
|
||||
evaluation.save()
|
||||
return Response({"success": True, "data": "Foydalanuvchilar muvaffaqiyatli o'chirildi"})
|
||||
except Exception as e:
|
||||
return Response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
@extend_schema(tags=["MechanicAutoEvaluation"])
|
||||
class MechanicAutoEvaluationListAppraisersView(GenericAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
queryset = MechanicAutoEvaluationModel.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
|
||||
@extend_schema(
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
name="search",
|
||||
type=str,
|
||||
description="Search query",
|
||||
required=False,
|
||||
)
|
||||
]
|
||||
)
|
||||
def get(self, request, id):
|
||||
try:
|
||||
search_query = request.query_params.get("search", "")
|
||||
evaluation = get_object_or_404(MechanicAutoEvaluationModel, id=id)
|
||||
query = evaluation.appraisers.all()
|
||||
if search_query:
|
||||
query = query.filter(
|
||||
Q(phone__icontains=search_query) |
|
||||
Q(first_name__icontains=search_query) |
|
||||
Q(last_name__icontains=search_query)
|
||||
)
|
||||
page = self.paginate_queryset(query)
|
||||
serializer = self.serializer_class(page, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
except Exception as e:
|
||||
return Response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
@extend_schema(tags=["MechanicAutoEvaluation"])
|
||||
class MechanicAutoEvaluationArchivedListAPIView(ListAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = ListMechanicAutoevaluationSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return MechanicAutoEvaluationModel.objects.filter(is_archived=True)
|
||||
|
||||
|
||||
@extend_schema(tags=["MechanicAutoEvaluation"])
|
||||
class MechanicAutoEvaluationArchiveAPIView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request, pk):
|
||||
evaluation = get_object_or_404(MechanicAutoEvaluationModel, pk=pk)
|
||||
evaluation.is_archived = request.data["is_archived"]
|
||||
evaluation.save()
|
||||
return Response(
|
||||
{
|
||||
"success": True,
|
||||
"status": evaluation.status,
|
||||
"id": evaluation.pk
|
||||
},
|
||||
status=200
|
||||
)
|
||||
|
||||
|
||||
@extend_schema(tags=["MechanicAutoEvaluation"])
|
||||
class AdminMechanicEvaluationsAPIView(generics.GenericAPIView):
|
||||
permission_classes = [IsAuthenticated, IsAdminRole]
|
||||
queryset = MechanicAutoEvaluationModel.objects.all()
|
||||
serializer_class = MechanicAutoEvaluationModelSerializer
|
||||
|
||||
def get(self, request):
|
||||
evaluations = MechanicAutoEvaluationModel.objects.filter(
|
||||
created_by=self.request.user
|
||||
).distinct()
|
||||
serializer = MechanicAutoEvaluationModelSerializer(evaluations, many=True)
|
||||
return Response(serializer.data)
|
||||
@@ -1,17 +1,13 @@
|
||||
# django
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
# django core
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
|
||||
# django filters
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
|
||||
# swagger
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
# rest framework
|
||||
from rest_framework import status
|
||||
from rest_framework import status, generics
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.generics import ListAPIView
|
||||
from rest_framework.parsers import FormParser, MultiPartParser
|
||||
@@ -20,10 +16,11 @@ from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from core.apps.accounts.permissions import IsAdminRole
|
||||
# core apps
|
||||
from core.apps.evaluation.filters.quick import QuickevaluationFilter
|
||||
from core.apps.evaluation.models import QuickEvaluationModel
|
||||
from core.apps.evaluation.serializers import quick as serializers
|
||||
from core.apps.evaluation.serializers import quick as serializers, QuickEvaluationModelSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["QuickEvaluation"])
|
||||
@@ -87,3 +84,18 @@ class QuickEvaluationArchivedListAPIView(ListAPIView):
|
||||
|
||||
def get_queryset(self):
|
||||
return QuickEvaluationModel.objects.filter(is_archive=True)
|
||||
|
||||
|
||||
@extend_schema(tags=["QuickEvaluation"])
|
||||
class AdminQuickEvalAPIView(generics.GenericAPIView):
|
||||
permission_classes = [IsAuthenticated, IsAdminRole]
|
||||
queryset = QuickEvaluationModel.objects.all()
|
||||
serializer_class = QuickEvaluationModelSerializer
|
||||
|
||||
def get(self, request):
|
||||
quick_eval = QuickEvaluationModel.objects.filter(
|
||||
created_by=self.request.user
|
||||
).select_related('created_by').distinct()
|
||||
serializer = QuickEvaluationModelSerializer(quick_eval, many=True)
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# rest framework
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.permissions import BasePermission
|
||||
from rest_framework.generics import GenericAPIView
|
||||
|
||||
# swagger
|
||||
@@ -10,11 +10,18 @@ from drf_spectacular.utils import extend_schema
|
||||
# core apps
|
||||
from core.services.tech_passport import TechPassportService
|
||||
from core.apps.evaluation.serializers import TechPassportSerializer
|
||||
from core.apps.accounts.choices import RoleChoice
|
||||
|
||||
|
||||
class IsNotUserRole(BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
if not request.user or not request.user.is_authenticated:
|
||||
return False
|
||||
return request.user.role != RoleChoice.USER
|
||||
|
||||
|
||||
class TechPassportAPIView(GenericAPIView):
|
||||
authentication_classes = []
|
||||
permission_classes = [IsAuthenticated]
|
||||
permission_classes = [IsNotUserRole]
|
||||
|
||||
@extend_schema(
|
||||
tags=["Tech Passport"],
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
# django core
|
||||
from django.http import HttpResponse
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
|
||||
# swagger
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
# rest framework
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
# core apps
|
||||
from core.apps.evaluation.models import VehicleModel
|
||||
from core.apps.evaluation.serializers import vehicle as serialziers
|
||||
from core.apps.evaluation.serializers import vehicle as serialziers, VehicleApplicationSerializer
|
||||
from core.utils.generation_pdf import PDFService
|
||||
|
||||
|
||||
@extend_schema(tags=["Vehicle"])
|
||||
@@ -27,3 +30,19 @@ class VehicleView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
"retrieve": serialziers.RetrieveVehicleSerializer,
|
||||
"create": serialziers.CreateVehicleSerializer,
|
||||
}
|
||||
|
||||
|
||||
@extend_schema(tags=['GenerationDocument'], request=VehicleApplicationSerializer)
|
||||
class GeneratePDFView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
serializer = VehicleApplicationSerializer(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=400)
|
||||
|
||||
pdf_buffer = PDFService.generate_vehicle_pdf(serializer.validated_data)
|
||||
|
||||
response = HttpResponse(pdf_buffer, content_type='application/pdf')
|
||||
response['Content-Disposition'] = 'attachment; filename="ariza.pdf"'
|
||||
return response
|
||||
|
||||
0
core/apps/tasks/__init__.py
Normal file
0
core/apps/tasks/__init__.py
Normal file
4
core/apps/tasks/admin/__init__.py
Normal file
4
core/apps/tasks/admin/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .column import *
|
||||
from .comment import *
|
||||
from .task import *
|
||||
from .label import *
|
||||
7
core/apps/tasks/admin/column.py
Normal file
7
core/apps/tasks/admin/column.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from core.apps.tasks.models import Column
|
||||
|
||||
@admin.register(Column)
|
||||
class ColumnAdmin(admin.ModelAdmin):
|
||||
list_display = ('name',)
|
||||
7
core/apps/tasks/admin/comment.py
Normal file
7
core/apps/tasks/admin/comment.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from core.apps.tasks.models import Comment
|
||||
|
||||
@admin.register(Comment)
|
||||
class CommentAdmin(admin.ModelAdmin):
|
||||
list_display = ('created_by', 'type')
|
||||
7
core/apps/tasks/admin/label.py
Normal file
7
core/apps/tasks/admin/label.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from core.apps.tasks.models import Label
|
||||
|
||||
@admin.register(Label)
|
||||
class LabelAdmin(admin.ModelAdmin):
|
||||
list_display = ('name',)
|
||||
7
core/apps/tasks/admin/task.py
Normal file
7
core/apps/tasks/admin/task.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from core.apps.tasks.models import Task
|
||||
|
||||
@admin.register(Task)
|
||||
class TaskAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'created_by', 'priority')
|
||||
8
core/apps/tasks/apps.py
Normal file
8
core/apps/tasks/apps.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class TasksConfig(AppConfig):
|
||||
name = "core.apps.tasks"
|
||||
|
||||
def ready(self):
|
||||
from core.apps.tasks import admin
|
||||
6
core/apps/tasks/choices/comment.py
Normal file
6
core/apps/tasks/choices/comment.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class MessageChoice(models.TextChoices):
|
||||
FILE = "file", "File"
|
||||
TEXT = "text", "Text"
|
||||
7
core/apps/tasks/choices/task.py
Normal file
7
core/apps/tasks/choices/task.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class PriorityChoice(models.TextChoices):
|
||||
LOW = "low", "Low"
|
||||
MEDIUM = "medium", "Medium"
|
||||
HIGH = "high", "High"
|
||||
77
core/apps/tasks/migrations/0001_initial.py
Normal file
77
core/apps/tasks/migrations/0001_initial.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# Generated by Django 5.2.7 on 2026-04-29 13:18
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Column',
|
||||
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=255)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Label',
|
||||
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=255)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Task',
|
||||
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=255)),
|
||||
('description', models.TextField(blank=True)),
|
||||
('priority', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], max_length=255)),
|
||||
('from_date', models.DateField(blank=True, null=True)),
|
||||
('to_date', models.DateField(blank=True, null=True)),
|
||||
('assignees', models.ManyToManyField(related_name='assigned_tasks', to=settings.AUTH_USER_MODEL)),
|
||||
('column', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to='tasks.column')),
|
||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_tasks', to=settings.AUTH_USER_MODEL)),
|
||||
('labels', models.ManyToManyField(related_name='tasks', to='tasks.label')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Comment',
|
||||
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)),
|
||||
('message', models.TextField()),
|
||||
('file', models.FileField(blank=True, null=True, upload_to='comments/')),
|
||||
('type', models.CharField(choices=[('file', 'File'), ('text', 'Text')], max_length=255)),
|
||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_comments', to=settings.AUTH_USER_MODEL)),
|
||||
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='tasks.task')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
21
core/apps/tasks/migrations/0002_alter_comment_created_by.py
Normal file
21
core/apps/tasks/migrations/0002_alter_comment_created_by.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Generated by Django 5.2.7 on 2026-04-29 13:20
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tasks', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='comment',
|
||||
name='created_by',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,29 @@
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tasks', '0002_alter_comment_created_by'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='comment',
|
||||
name='file',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CommentFile',
|
||||
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(upload_to='comments/')),
|
||||
('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='tasks.comment')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
0
core/apps/tasks/migrations/__init__.py
Normal file
0
core/apps/tasks/migrations/__init__.py
Normal file
4
core/apps/tasks/models/__init__.py
Normal file
4
core/apps/tasks/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .column import *
|
||||
from .comment import *
|
||||
from .task import *
|
||||
from .label import *
|
||||
10
core/apps/tasks/models/column.py
Normal file
10
core/apps/tasks/models/column.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.db import models
|
||||
|
||||
from django_core.models import AbstractBaseModel
|
||||
|
||||
|
||||
class Column(AbstractBaseModel):
|
||||
name = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
25
core/apps/tasks/models/comment.py
Normal file
25
core/apps/tasks/models/comment.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from django.db import models
|
||||
|
||||
from django_core.models import AbstractBaseModel
|
||||
|
||||
from core.apps.tasks.choices.comment import MessageChoice
|
||||
|
||||
|
||||
class Comment(AbstractBaseModel):
|
||||
task = models.ForeignKey('tasks.Task', on_delete=models.CASCADE, related_name='comments')
|
||||
message = models.TextField()
|
||||
type = models.CharField(max_length=255, choices=MessageChoice.choices)
|
||||
created_by = models.ForeignKey('accounts.User', on_delete=models.CASCADE, related_name='comments')
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.message} created by {self.created_by}"
|
||||
|
||||
|
||||
class CommentFile(AbstractBaseModel):
|
||||
comment = models.ForeignKey(
|
||||
'tasks.Comment', on_delete=models.CASCADE, related_name='files'
|
||||
)
|
||||
file = models.FileField(upload_to='comments/')
|
||||
|
||||
def __str__(self):
|
||||
return f"File for comment {self.comment_id}"
|
||||
10
core/apps/tasks/models/label.py
Normal file
10
core/apps/tasks/models/label.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.db import models
|
||||
|
||||
from django_core.models import AbstractBaseModel
|
||||
|
||||
|
||||
class Label(AbstractBaseModel):
|
||||
name = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
20
core/apps/tasks/models/task.py
Normal file
20
core/apps/tasks/models/task.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from django.db import models
|
||||
|
||||
from django_core.models import AbstractBaseModel
|
||||
|
||||
from core.apps.tasks.choices.task import PriorityChoice
|
||||
|
||||
|
||||
class Task(AbstractBaseModel):
|
||||
column = models.ForeignKey('tasks.Column', on_delete=models.CASCADE, related_name='tasks')
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField(blank=True)
|
||||
priority = models.CharField(max_length=255, choices=PriorityChoice.choices)
|
||||
from_date = models.DateField(null=True, blank=True)
|
||||
to_date = models.DateField(null=True, blank=True)
|
||||
labels = models.ManyToManyField('tasks.Label', related_name='tasks')
|
||||
assignees = models.ManyToManyField('accounts.User', related_name='assigned_tasks')
|
||||
created_by = models.ForeignKey('accounts.User', on_delete=models.CASCADE, related_name='created_tasks')
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} created by {self.created_by}"
|
||||
22
core/apps/tasks/serializers/board.py
Normal file
22
core/apps/tasks/serializers/board.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.tasks.serializers.comment import CommentSerializer
|
||||
from core.apps.tasks.serializers.task import TaskSerializer
|
||||
from core.apps.tasks.models import Column
|
||||
|
||||
|
||||
class BoardTaskSerializer(TaskSerializer):
|
||||
comments = CommentSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta(TaskSerializer.Meta):
|
||||
TaskSerializer.Meta.fields += ['comments']
|
||||
|
||||
|
||||
class BoardSerializer(serializers.ModelSerializer):
|
||||
tasks = BoardTaskSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Column
|
||||
fields = [
|
||||
'id', 'name', 'tasks',
|
||||
]
|
||||
11
core/apps/tasks/serializers/column.py
Normal file
11
core/apps/tasks/serializers/column.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.tasks.models.column import Column
|
||||
|
||||
|
||||
class ColumnSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Column
|
||||
fields = [
|
||||
'id', 'name'
|
||||
]
|
||||
89
core/apps/tasks/serializers/comment.py
Normal file
89
core/apps/tasks/serializers/comment.py
Normal file
@@ -0,0 +1,89 @@
|
||||
from django.db import transaction
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.tasks.models.comment import Comment, CommentFile
|
||||
|
||||
|
||||
class CommentCreatedBySerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField()
|
||||
first_name = serializers.CharField()
|
||||
last_name = serializers.CharField()
|
||||
avatar = serializers.SerializerMethodField()
|
||||
|
||||
def get_avatar(self, obj):
|
||||
request = self.context.get('request')
|
||||
if obj.avatar and request:
|
||||
return request.build_absolute_uri(obj.avatar.url)
|
||||
if obj.avatar:
|
||||
return obj.avatar.url
|
||||
return None
|
||||
|
||||
|
||||
class CommentFileSerializer(serializers.ModelSerializer):
|
||||
file = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = CommentFile
|
||||
fields = ['id', 'file']
|
||||
|
||||
def get_file(self, obj):
|
||||
request = self.context.get('request')
|
||||
if not obj.file:
|
||||
return None
|
||||
if request:
|
||||
return request.build_absolute_uri(obj.file.url)
|
||||
return obj.file.url
|
||||
|
||||
|
||||
class CommentSerializer(serializers.ModelSerializer):
|
||||
created_by = CommentCreatedBySerializer(read_only=True)
|
||||
files = CommentFileSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = [
|
||||
'id', 'message', 'type', 'created_by', 'created_at', 'files'
|
||||
]
|
||||
|
||||
|
||||
class CommentCreateSerializer(serializers.ModelSerializer):
|
||||
files = serializers.ListField(
|
||||
child=serializers.FileField(),
|
||||
required=False,
|
||||
write_only=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = [
|
||||
'id', 'message', 'type', 'task', 'files', 'created_at'
|
||||
]
|
||||
read_only_fields = ['id', 'created_at']
|
||||
|
||||
def create(self, validated_data):
|
||||
files = validated_data.pop('files', [])
|
||||
with transaction.atomic():
|
||||
comment = Comment.objects.create(
|
||||
created_by=self.context['request'].user,
|
||||
**validated_data
|
||||
)
|
||||
CommentFile.objects.bulk_create([
|
||||
CommentFile(comment=comment, file=f) for f in files
|
||||
])
|
||||
return comment
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
files = validated_data.pop('files', None)
|
||||
with transaction.atomic():
|
||||
for attr, value in validated_data.items():
|
||||
setattr(instance, attr, value)
|
||||
instance.save()
|
||||
if files is not None:
|
||||
CommentFile.objects.bulk_create([
|
||||
CommentFile(comment=instance, file=f) for f in files
|
||||
])
|
||||
return instance
|
||||
|
||||
def to_representation(self, instance):
|
||||
return CommentSerializer(instance, context=self.context).data
|
||||
11
core/apps/tasks/serializers/label.py
Normal file
11
core/apps/tasks/serializers/label.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.tasks.models.label import Label
|
||||
|
||||
|
||||
class LabelSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Label
|
||||
fields = [
|
||||
'id', 'name'
|
||||
]
|
||||
52
core/apps/tasks/serializers/task.py
Normal file
52
core/apps/tasks/serializers/task.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.tasks.models.task import Task
|
||||
from core.apps.accounts.serializers.user import ShortUserSerializer
|
||||
from core.apps.tasks.serializers.label import LabelSerializer
|
||||
|
||||
|
||||
class TaskSerializer(serializers.ModelSerializer):
|
||||
labels = LabelSerializer(many=True)
|
||||
assignees = serializers.SerializerMethodField(method_name='get_assignees')
|
||||
created_by = serializers.SerializerMethodField(method_name='get_created_by')
|
||||
|
||||
class Meta:
|
||||
model = Task
|
||||
fields = [
|
||||
'id',
|
||||
'column',
|
||||
'name',
|
||||
'description',
|
||||
'priority',
|
||||
'from_date',
|
||||
'to_date',
|
||||
'labels',
|
||||
'assignees',
|
||||
'created_by'
|
||||
]
|
||||
|
||||
def get_assignees(self, obj):
|
||||
return ShortUserSerializer(obj.assignees.all(), many=True, context={"request": self.context['request']}).data
|
||||
|
||||
def get_created_by(self, obj):
|
||||
return ShortUserSerializer(obj.created_by, context={"request": self.context['request']}).data
|
||||
|
||||
|
||||
class TaskCreateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Task
|
||||
fields = [
|
||||
'id',
|
||||
'column',
|
||||
'name',
|
||||
'description',
|
||||
'priority',
|
||||
'from_date',
|
||||
'to_date',
|
||||
'labels',
|
||||
'assignees',
|
||||
]
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data['created_by'] = self.context['request'].user
|
||||
return super().create(validated_data)
|
||||
34
core/apps/tasks/urls.py
Normal file
34
core/apps/tasks/urls.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from django.urls import path, include
|
||||
|
||||
from core.apps.tasks.views import task, column, comment, label, board
|
||||
|
||||
urlpatterns = [
|
||||
path('column/', include(
|
||||
[
|
||||
path('list/', column.ColumnListApiView.as_view()),
|
||||
path('create/', column.ColumnCreateApiView.as_view()),
|
||||
path('<int:id>/update/', column.ColumnUpdateApiView.as_view()),
|
||||
path('<int:id>/delete/', column.ColumnDeleteApiView.as_view())
|
||||
]
|
||||
)),
|
||||
path('label/', include(
|
||||
[
|
||||
path('', label.LabelListCreateApiView.as_view()),
|
||||
path('<int:id>/', label.LabelRetrieveUpdateDestroyApiView.as_view()),
|
||||
]
|
||||
)),
|
||||
path('task/', include(
|
||||
[
|
||||
path('list/', task.TaskListView.as_view()),
|
||||
path('<int:pk>/', task.TaskDetailView.as_view()),
|
||||
path('create/', task.TaskCreateView.as_view()),
|
||||
]
|
||||
)),
|
||||
path('comment/', include(
|
||||
[
|
||||
path('', comment.CommentListCreateAPIView.as_view()),
|
||||
path('<int:pk>/', comment.CommentDetailAPIView.as_view()),
|
||||
]
|
||||
)),
|
||||
path('board/', board.BoardListView.as_view()),
|
||||
]
|
||||
15
core/apps/tasks/views/board.py
Normal file
15
core/apps/tasks/views/board.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework.response import Response
|
||||
|
||||
from core.apps.tasks.serializers.board import BoardSerializer
|
||||
from core.apps.tasks.models import Column
|
||||
|
||||
|
||||
#test commit
|
||||
|
||||
class BoardListView(generics.ListAPIView):
|
||||
queryset = Column.objects.order_by('id')
|
||||
serializer_class = BoardSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
|
||||
47
core/apps/tasks/views/column.py
Normal file
47
core/apps/tasks/views/column.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from django.db import transaction
|
||||
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework.response import Response
|
||||
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from core.apps.tasks.serializers.column import ColumnSerializer
|
||||
from core.apps.tasks.models.column import Column
|
||||
|
||||
|
||||
@extend_schema(tags=['Tasks'])
|
||||
class ColumnCreateApiView(generics.GenericAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
serializer_class = ColumnSerializer
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
column = serializer.save()
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
@extend_schema(tags=['Tasks'])
|
||||
class ColumnListApiView(generics.ListAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
serializer_class = ColumnSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return Column.objects.order_by('id')
|
||||
|
||||
|
||||
@extend_schema(tags=['Tasks'])
|
||||
class ColumnUpdateApiView(generics.UpdateAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
serializer_class = ColumnSerializer
|
||||
lookup_field = 'id'
|
||||
queryset = Column.objects.all()
|
||||
|
||||
|
||||
@extend_schema(tags=['Tasks'])
|
||||
class ColumnDeleteApiView(generics.DestroyAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
serializer_class = ColumnSerializer
|
||||
lookup_field = 'id'
|
||||
queryset = Column.objects.all()
|
||||
51
core/apps/tasks/views/comment.py
Normal file
51
core/apps/tasks/views/comment.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
from core.apps.tasks.models.comment import Comment
|
||||
from core.apps.tasks.serializers.comment import CommentSerializer, CommentCreateSerializer
|
||||
|
||||
|
||||
class CommentListCreateAPIView(generics.ListCreateAPIView):
|
||||
queryset = Comment.objects.all()
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method == 'POST':
|
||||
return CommentCreateSerializer
|
||||
return CommentSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
task_id = self.request.query_params.get('task_id')
|
||||
queryset = self.queryset
|
||||
|
||||
if task_id:
|
||||
queryset = queryset.filter(task_id=task_id)
|
||||
|
||||
return queryset.order_by('-id')
|
||||
|
||||
def get_serializer_context(self):
|
||||
return {"request": self.request}
|
||||
|
||||
|
||||
class CommentDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
|
||||
queryset = Comment.objects.all()
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method in ['PUT', 'PATCH']:
|
||||
return CommentCreateSerializer
|
||||
return CommentSerializer
|
||||
|
||||
def get_serializer_context(self):
|
||||
return {"request": self.request}
|
||||
|
||||
def perform_update(self, serializer):
|
||||
comment = self.get_object()
|
||||
if comment.created_by != self.request.user:
|
||||
raise PermissionDenied("You cannot edit this comment")
|
||||
serializer.save()
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
if instance.created_by != self.request.user:
|
||||
raise PermissionDenied("You cannot delete this comment")
|
||||
instance.delete()
|
||||
22
core/apps/tasks/views/label.py
Normal file
22
core/apps/tasks/views/label.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework.response import Response
|
||||
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from core.apps.tasks.serializers.label import LabelSerializer
|
||||
from core.apps.tasks.models.label import Label
|
||||
|
||||
|
||||
@extend_schema(tags=['Tasks'])
|
||||
class LabelListCreateApiView(generics.ListCreateAPIView):
|
||||
queryset = Label.objects.order_by('id')
|
||||
serializer_class = LabelSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
|
||||
@extend_schema(tags=['Tasks'])
|
||||
class LabelRetrieveUpdateDestroyApiView(generics.RetrieveUpdateDestroyAPIView):
|
||||
queryset = Label.objects.order_by('id')
|
||||
serializer_class = LabelSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
lookup_field = 'id'
|
||||
39
core/apps/tasks/views/task.py
Normal file
39
core/apps/tasks/views/task.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django.db import transaction
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework import permissions, generics, status
|
||||
from rest_framework.response import Response
|
||||
|
||||
from core.apps.tasks.models.task import Task
|
||||
from core.apps.tasks.serializers.task import TaskSerializer, TaskCreateSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=['Tasks'])
|
||||
class TaskCreateView(generics.GenericAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
serializer_class = TaskCreateSerializer
|
||||
queryset = Task.objects.order_by('id')
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
if not serializer.is_valid(raise_exception=True):
|
||||
return Response(serializer.validated_data, status=status.HTTP_400_BAD_REQUEST)
|
||||
serializer.save()
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
@extend_schema(tags=['Tasks'])
|
||||
class TaskListView(generics.ListAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
serializer_class = TaskSerializer
|
||||
queryset = Task.objects.order_by('id')
|
||||
|
||||
def serializer_context(self):
|
||||
return self.serializer_class(context={"request": self.request})
|
||||
|
||||
|
||||
@extend_schema(tags=['Tasks'])
|
||||
class TaskDetailView(generics.RetrieveUpdateDestroyAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
serializer_class = TaskSerializer
|
||||
queryset = Task.objects.order_by('id')
|
||||
138
core/utils/generation_pdf.py
Normal file
138
core/utils/generation_pdf.py
Normal file
@@ -0,0 +1,138 @@
|
||||
# services.py
|
||||
from io import BytesIO
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.enums import TA_RIGHT, TA_CENTER
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
||||
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
|
||||
|
||||
|
||||
class PDFService:
|
||||
@staticmethod
|
||||
def generate_vehicle_pdf(data):
|
||||
buffer = BytesIO()
|
||||
doc = SimpleDocTemplate(
|
||||
buffer, pagesize=A4,
|
||||
rightMargin=40, leftMargin=40,
|
||||
topMargin=40, bottomMargin=40
|
||||
)
|
||||
styles = getSampleStyleSheet()
|
||||
|
||||
cell_style = ParagraphStyle(
|
||||
'CellStyle', parent=styles['Normal'],
|
||||
fontSize=9, leading=11
|
||||
)
|
||||
cell_style_bold = ParagraphStyle(
|
||||
'CellStyleBold', parent=cell_style,
|
||||
fontName='Helvetica-Bold',
|
||||
textColor=colors.red
|
||||
)
|
||||
header_style = ParagraphStyle(
|
||||
'HeaderStyle', parent=styles['Normal'],
|
||||
alignment=TA_RIGHT, fontSize=10, leading=12
|
||||
)
|
||||
title_style = ParagraphStyle(
|
||||
'TitleStyle', parent=styles['Normal'],
|
||||
alignment=TA_CENTER, fontSize=14, leading=16
|
||||
)
|
||||
|
||||
elements = []
|
||||
|
||||
header_text = (
|
||||
f'<b>"Sifat baholash" MChJ direktori<br/>'
|
||||
f"T.R.To'rayevga</b><br/><br/>"
|
||||
f"{data.get('address', '')}<br/>"
|
||||
f"ro'yxatda turuvchi fuqaro<br/>"
|
||||
f"<u>{data.get('person_name', '')}</u> tomonidan<br/>"
|
||||
f"<u>Avtotransport vositasini baholash uchun</u>"
|
||||
)
|
||||
elements.append(Paragraph(header_text, header_style))
|
||||
elements.append(Spacer(1, 25))
|
||||
|
||||
elements.append(Paragraph("<b>A R I Z A</b>", title_style))
|
||||
elements.append(Spacer(1, 10))
|
||||
elements.append(Paragraph(
|
||||
"Ushbu orqali quyidagi avtotransport vositasini baholab berishingizni so'rayman:",
|
||||
cell_style
|
||||
))
|
||||
elements.append(Spacer(1, 10))
|
||||
|
||||
date_created = data.get('date_created')
|
||||
year_str = str(date_created.year) + " yil" if date_created else ""
|
||||
|
||||
tec_date = data.get('tec_passport_date')
|
||||
tec_date_str = tec_date.strftime('%d.%m.%Y yil') if tec_date else ""
|
||||
|
||||
raw_data = [
|
||||
["Mulk egasi", data.get('property_owner', '')],
|
||||
["Manzil", data.get('address', '')],
|
||||
["Marka", data.get('marka', '')],
|
||||
["Model", data.get('model', '')],
|
||||
["Komplektatsiya", data.get('configuration', '')],
|
||||
["Davlat raqami", data.get('auto_number', '')],
|
||||
["Ishlab chiqarilgan yili", year_str],
|
||||
["Bosib o'tgan masofasi", f"{data.get('mileage', 0):,}".replace(',', ' ')],
|
||||
["№ kuzov (VIN)", data.get('vehicle_identification', '')],
|
||||
["№ dvigatel", data.get('engine_number', '')],
|
||||
["Rang", data.get('colour', '')],
|
||||
["Texnik passport seriyasi", data.get('registration_certificate_series', '')],
|
||||
["Texnik passport raqami", data.get('tec_passport_number', '')],
|
||||
["Texnik passport berilgan sanasi", tec_date_str],
|
||||
["Texnik passport berilgan joyi", data.get('tec_passport_place', '')],
|
||||
["Kuzov turi", data.get('body_type', '')],
|
||||
["Shassi", data.get('chassis', '')],
|
||||
["Davlat belgisi (plate)", data.get('plate', '')], # ← was missing
|
||||
]
|
||||
|
||||
table_data = [
|
||||
[Paragraph(row[0], cell_style), Paragraph(str(row[1]), cell_style_bold)]
|
||||
for row in raw_data
|
||||
]
|
||||
table = Table(table_data, colWidths=[170, 345])
|
||||
table.setStyle(TableStyle([
|
||||
('GRID', (0, 0), (-1, -1), 0.5, colors.black),
|
||||
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 6),
|
||||
('TOPPADDING', (0, 0), (-1, -1), 4),
|
||||
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
|
||||
]))
|
||||
elements.append(table)
|
||||
elements.append(Spacer(1, 15))
|
||||
|
||||
elements.append(Paragraph("<b>Baholash maqsadi:</b>", cell_style))
|
||||
purpose_raw = [
|
||||
["Aniqlanayotgan qiymat turi", data.get('value_type', '')],
|
||||
["Baholash maqsadi", data.get('evaluation_purpose', '')],
|
||||
]
|
||||
purpose_data = [
|
||||
[Paragraph(r[0], cell_style), Paragraph(str(r[1]), cell_style_bold)]
|
||||
for r in purpose_raw
|
||||
]
|
||||
pt = Table(purpose_data, colWidths=[170, 345])
|
||||
pt.setStyle(TableStyle([
|
||||
('GRID', (0, 0), (-1, -1), 0.5, colors.black),
|
||||
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||
]))
|
||||
elements.append(pt)
|
||||
elements.append(Spacer(1, 15))
|
||||
|
||||
elements.append(Paragraph("<b>Buyurtmachi rekvizitlari:</b>", cell_style))
|
||||
footer_raw = [
|
||||
["Shaxsiy raqam (PINFL)", str(data.get('personal_id_number', ''))],
|
||||
["ID karta raqami", data.get('id_number', '')],
|
||||
]
|
||||
footer_data = [
|
||||
[Paragraph(f[0], cell_style), Paragraph(str(f[1]), cell_style_bold)]
|
||||
for f in footer_raw
|
||||
]
|
||||
ft = Table(footer_data, colWidths=[170, 345])
|
||||
ft.setStyle(TableStyle([
|
||||
('GRID', (0, 0), (-1, -1), 0.5, colors.black),
|
||||
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||
]))
|
||||
elements.append(ft)
|
||||
|
||||
doc.build(elements)
|
||||
buffer.seek(0)
|
||||
return buffer
|
||||
@@ -5,6 +5,17 @@ ENV SCRIPT=$SCRIPT
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
RUN apk add --no-cache \
|
||||
glib \
|
||||
gdk-pixbuf \
|
||||
pango \
|
||||
cairo \
|
||||
libffi \
|
||||
shared-mime-info \
|
||||
fontconfig \
|
||||
ttf-dejavu \
|
||||
ttf-liberation
|
||||
|
||||
COPY requirements.txt /code/requirements.txt
|
||||
|
||||
RUN uv pip install -r requirements.txt
|
||||
@@ -16,5 +27,3 @@ COPY ./resources/scripts/$SCRIPT /code/$SCRIPT
|
||||
RUN chmod +x /code/resources/scripts/$SCRIPT
|
||||
|
||||
CMD sh /code/resources/scripts/$SCRIPT
|
||||
|
||||
|
||||
|
||||
184
requirements.txt
184
requirements.txt
@@ -1,46 +1,146 @@
|
||||
backports.tarfile==1.2.0
|
||||
celery==5.4.0
|
||||
django-cors-headers==4.6.0
|
||||
django-environ==0.11.2
|
||||
django-extensions==3.2.3
|
||||
django-filter==24.3
|
||||
django-redis==5.4.0
|
||||
django-unfold==0.65.0
|
||||
djangorestframework-simplejwt==5.3.1
|
||||
drf-spectacular==0.28.0
|
||||
importlib-metadata==8.5.0
|
||||
importlib-resources==6.4.5
|
||||
inflect==7.3.1
|
||||
jaraco.collections==5.1.0
|
||||
packaging==24.2
|
||||
pip-chill==1.0.3
|
||||
platformdirs==4.3.6
|
||||
psycopg2-binary==2.9.10
|
||||
tomli==2.2.1
|
||||
uvicorn==0.32.1
|
||||
jst-django-core~=1.2.2
|
||||
rich
|
||||
pydantic
|
||||
aiohappyeyeballs
|
||||
aiohttp
|
||||
aiosignal
|
||||
amqp
|
||||
annotated-doc
|
||||
annotated-types
|
||||
arrow
|
||||
asgiref
|
||||
astor
|
||||
attrs
|
||||
backports.tarfile
|
||||
bcrypt
|
||||
pytest-django
|
||||
requests
|
||||
model_bakery
|
||||
|
||||
|
||||
|
||||
django-modeltranslation~=0.19.11
|
||||
django-ckeditor-5==0.2.15
|
||||
|
||||
django-rosetta==0.10.1
|
||||
django-cacheops~=7.1
|
||||
|
||||
|
||||
# !NOTE: on-server
|
||||
# gunicorn
|
||||
|
||||
|
||||
django-storages
|
||||
billiard
|
||||
binaryornot
|
||||
black
|
||||
boto3
|
||||
botocore
|
||||
brotli
|
||||
celery
|
||||
certifi
|
||||
cffi
|
||||
chardet
|
||||
charset-normalizer
|
||||
click
|
||||
click-didyoumean
|
||||
click-plugins
|
||||
click-repl
|
||||
colorlog
|
||||
cookiecutter
|
||||
cssselect2
|
||||
Django
|
||||
django-cacheops
|
||||
django-ckeditor-5
|
||||
django-cors-headers
|
||||
django-environ
|
||||
django-extensions
|
||||
django-filter
|
||||
django-modeltranslation
|
||||
django-redis
|
||||
django-rosetta
|
||||
django-storages
|
||||
django-unfold
|
||||
djangorestframework
|
||||
djangorestframework-simplejwt
|
||||
drf-spectacular
|
||||
flake8
|
||||
fonttools
|
||||
frozenlist
|
||||
funcy
|
||||
g4f
|
||||
grpcio
|
||||
grpcio-tools
|
||||
h11
|
||||
idna
|
||||
importlib_metadata
|
||||
importlib_resources
|
||||
inflect
|
||||
inflection
|
||||
iniconfig
|
||||
isort
|
||||
jaraco.collections
|
||||
jaraco.context
|
||||
jaraco.functools
|
||||
jaraco.text
|
||||
Jinja2
|
||||
jmespath
|
||||
jsonschema
|
||||
jsonschema-specifications
|
||||
jst-aicommit
|
||||
jst-django
|
||||
jst-django-core
|
||||
kombu
|
||||
markdown-it-py
|
||||
MarkupSafe
|
||||
mccabe
|
||||
mdurl
|
||||
model-bakery
|
||||
more-itertools
|
||||
multidict
|
||||
mypy-extensions
|
||||
nest-asyncio
|
||||
packaging
|
||||
pathspec
|
||||
pillow
|
||||
pip-chill
|
||||
platformdirs
|
||||
pluggy
|
||||
polib
|
||||
prompt-toolkit
|
||||
propcache
|
||||
protobuf
|
||||
psycopg2-binary
|
||||
pycodestyle
|
||||
pycparser
|
||||
pycryptodome
|
||||
pydantic
|
||||
pydantic_core
|
||||
pydyf
|
||||
pyflakes
|
||||
Pygments
|
||||
PyJWT
|
||||
pyphen
|
||||
pytest
|
||||
pytest-django
|
||||
python-dateutil
|
||||
python-slugify
|
||||
PyYAML
|
||||
questionary
|
||||
redis
|
||||
referencing
|
||||
reportlab
|
||||
requests
|
||||
rich
|
||||
rpds-py
|
||||
s3transfer
|
||||
setuptools
|
||||
shellingham
|
||||
six
|
||||
sqlparse
|
||||
tenacity
|
||||
text-unidecode
|
||||
tinycss2
|
||||
tinyhtml5
|
||||
tomli
|
||||
tqdm
|
||||
typeguard
|
||||
typer
|
||||
typer-slim
|
||||
types-python-dateutil
|
||||
typing-inspection
|
||||
typing_extensions
|
||||
tzdata
|
||||
uritemplate
|
||||
urllib3
|
||||
uvicorn
|
||||
vine
|
||||
wcwidth
|
||||
weasyprint
|
||||
webencodings
|
||||
yarl
|
||||
zipfile36
|
||||
zipp
|
||||
zopfli
|
||||
|
||||
|
||||
# !NOTE: on-websocket
|
||||
@@ -50,3 +150,5 @@ boto3
|
||||
grpcio>=1.62.0
|
||||
grpcio-tools>=1.62.0
|
||||
protobuf>=4.25.0
|
||||
|
||||
reportlab
|
||||
|
||||
2417
resources/templates/documents/contract.html
Normal file
2417
resources/templates/documents/contract.html
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user