Compare commits
121 Commits
bcb453d52a
...
behruz
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | |||
|
|
0de50ec328 | ||
|
|
e346546d24 | ||
| e97c6c7ab2 | |||
|
|
f7706e77ee | ||
|
|
e351ed5303 | ||
| affd3e1221 | |||
|
|
59ed3d23ac | ||
|
|
3ac6263035 | ||
| 2c6d7dd2f7 | |||
|
|
a6e0fca165 | ||
|
|
b64073e1ad | ||
| e3ffdddc46 | |||
|
|
6f48632e2d | ||
| faea9bdb89 | |||
|
|
e3e7f18d7f | ||
|
|
1dd1a132e4 | ||
| 04e193bae6 | |||
|
|
7134b2c185 | ||
|
|
306aecc956 | ||
| 3ede209e52 | |||
|
|
05857a227a | ||
| 10b25b5228 | |||
|
|
fcbfa94dd4 | ||
| 7e778d3a3e | |||
|
|
81a4287db1 | ||
|
|
e560fdaf2d | ||
|
|
0d96167a7b | ||
|
|
ae65d9d793 | ||
|
|
5249f7e6f7 | ||
|
|
e1b771e166 | ||
|
|
eded642bd7 | ||
| f830235813 | |||
|
|
a62cf3a1ee | ||
|
|
2f471173c3 | ||
| 3838fbaa47 | |||
|
|
b02078e618 | ||
|
|
1f59347d87 | ||
|
|
fe40057d95 | ||
| 550da049b9 | |||
|
|
bdd5aa9ce2 | ||
|
|
bd27205252 | ||
| 34aba90ebd | |||
|
|
260bc9101e | ||
|
|
f46dac515a | ||
|
|
c6fc150162 | ||
| 7272ef3fce | |||
|
|
1db936126d | ||
| 5e1b02064e | |||
|
|
4e242a4358 |
@@ -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
|
||||
@@ -75,7 +75,3 @@ STORAGE_PROTOCOL=http:
|
||||
|
||||
# Didox configs
|
||||
DIDOX_PARTNER_TOKEN=...
|
||||
|
||||
|
||||
|
||||
# Celery configs
|
||||
38
.github/workflows/deploy.yaml
vendored
38
.github/workflows/deploy.yaml
vendored
@@ -47,6 +47,24 @@ jobs:
|
||||
- name: Copy env
|
||||
run: |
|
||||
cp .env.example .env
|
||||
update_env() {
|
||||
local env_file=".env"
|
||||
for kv in "$@"; do
|
||||
local key="${kv%%=*}"
|
||||
local value="${kv#*=}"
|
||||
if grep -q "^$key=" "$env_file"; then
|
||||
sed -i "s|^$key=.*|$key=$value|" "$env_file"
|
||||
else
|
||||
echo "$key=$value" >> "$env_file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
update_env \
|
||||
"DB_HOST=postgres" \
|
||||
"DB_NAME=sifatbahodb" \
|
||||
"DB_PORT=5432" \
|
||||
"DIDOX_PARTNER_TOKEN=${{ secrets.DIDOX_TOKEN }}"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
@@ -125,6 +143,7 @@ jobs:
|
||||
rm -rf "$DIR"
|
||||
cd "$PROJECTS"
|
||||
git clone "$REPO_URL" "${{ env.PROJECT_NAME }}"
|
||||
echo "Clone qilindi"
|
||||
fi
|
||||
|
||||
cd "$DIR"
|
||||
@@ -132,18 +151,11 @@ jobs:
|
||||
git fetch origin main
|
||||
git reset --hard origin/main
|
||||
|
||||
cat > .env << 'ENVEOF'
|
||||
DB_HOST=postgres
|
||||
DB_NAME=sifatbahodb
|
||||
DB_PORT=5432
|
||||
DB_USER=${{ secrets.DB_USER }}
|
||||
DB_PASSWORD=${{ secrets.DB_PASSWORD }}
|
||||
REDIS_URL=redis://redis:6379
|
||||
SECRET_KEY=${{ secrets.SECRET_KEY }}
|
||||
DIDOX_TOKEN=${{ secrets.DIDOX_TOKEN }}
|
||||
DEBUG=False
|
||||
PORT=8085
|
||||
|
||||
ENVEOF
|
||||
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
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from config.env import env
|
||||
|
||||
APPS = [
|
||||
|
||||
|
||||
"cacheops",
|
||||
"rosetta",
|
||||
"django_ckeditor_5",
|
||||
|
||||
|
||||
"drf_spectacular",
|
||||
"rest_framework",
|
||||
"corsheaders",
|
||||
@@ -14,9 +14,11 @@ 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):
|
||||
APPS += [
|
||||
|
||||
|
||||
]
|
||||
|
||||
@@ -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: #88dedd85c79ccf732b2adac03616bd14e67a1579")
|
||||
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",),
|
||||
}),
|
||||
)
|
||||
@@ -1,4 +1,5 @@
|
||||
from django.contrib.auth import admin
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.safestring import mark_safe
|
||||
from unfold.admin import ModelAdmin
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# Generated by Django 5.2.7 on 2026-04-27 09:33
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0003_user_avatar'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PermissionToAction',
|
||||
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=200)),
|
||||
('code', models.CharField(max_length=100, unique=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Harakatlar uchun ruxsatnoma',
|
||||
'verbose_name_plural': 'Harakatlar uchun ruxsatnomalar',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PermissionToTab',
|
||||
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=200)),
|
||||
('code', models.CharField(max_length=100, unique=True)),
|
||||
('permission_to_actions', models.ManyToManyField(related_name='permission_to_tabs', to='accounts.permissiontoaction')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': "Bo'lim uchun ruxsatnoma",
|
||||
'verbose_name_plural': "Bo'lim uchun ruxsatnomalar",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Permission',
|
||||
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=200)),
|
||||
('code', models.CharField(max_length=100, unique=True)),
|
||||
('permission_tab', models.ManyToManyField(related_name='permissions', to='accounts.permissiontotab')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Sahifa uchun ruxsatnoma',
|
||||
'verbose_name_plural': 'Sahifa uchun ruxsatnomalar',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Role',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, unique=True)),
|
||||
('comment', models.CharField(blank=True, max_length=200, null=True)),
|
||||
('permission_to_actions', models.ManyToManyField(blank=True, related_name='roles', to='accounts.permissiontoaction')),
|
||||
('permission_to_tabs', models.ManyToManyField(blank=True, related_name='roles', to='accounts.permissiontotab')),
|
||||
('permissions', models.ManyToManyField(blank=True, related_name='roles', to='accounts.permission')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Rol',
|
||||
'verbose_name_plural': 'Rollar',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='permission',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='users', to='accounts.role'),
|
||||
),
|
||||
]
|
||||
@@ -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',
|
||||
),
|
||||
]
|
||||
@@ -1,7 +1,6 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_core.models import AbstractBaseModel
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class PermissionToAction(AbstractBaseModel):
|
||||
@@ -34,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}'
|
||||
|
||||
@@ -18,7 +18,7 @@ class User(auth_models.AbstractUser):
|
||||
default=RoleChoice.USER,
|
||||
)
|
||||
avatar = models.ImageField(upload_to="avatars/", null=True, blank=True)
|
||||
permission = models.ForeignKey(Role, on_delete=models.SET_NULL, null=True)
|
||||
permission = models.ForeignKey(Role, on_delete=models.SET_NULL, null=True, blank=True, related_name='users')
|
||||
|
||||
USERNAME_FIELD = "phone"
|
||||
objects = UserManager()
|
||||
|
||||
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
|
||||
55
core/apps/accounts/serializers/permission.py
Normal file
55
core/apps/accounts/serializers/permission.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.accounts.models.permission import PermissionToAction, PermissionToTab, Permission, Role
|
||||
|
||||
|
||||
class PermissionToActionSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = PermissionToAction
|
||||
fields = ['id', 'name']
|
||||
|
||||
|
||||
class PermissionToTabSerializer(serializers.ModelSerializer):
|
||||
permission_to_actions = PermissionToActionSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PermissionToTab
|
||||
fields = ['id', 'name', 'permission_to_actions']
|
||||
|
||||
|
||||
class PermissionSerializer(serializers.ModelSerializer):
|
||||
permission_tabs = PermissionToTabSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Permission
|
||||
fields = ['id', 'name', 'permission_tabs']
|
||||
|
||||
|
||||
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 = [
|
||||
'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
|
||||
@@ -8,25 +8,30 @@ from .views import RegisterView, ResetPasswordView, MeView, ChangePasswordView,
|
||||
AdminUserView, AdminCreateAPIView, AdminUpdateAPIView
|
||||
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")
|
||||
router.register("auth", ResetPasswordView, basename="reset-password")
|
||||
router.register("auth", MeView, basename="me")
|
||||
router.register("auth", ChangePasswordView, basename="change-password")
|
||||
router.register("user", AdminUserView, basename="user-crud")
|
||||
router.register("action", PermissionToActionViewSet, basename="action")
|
||||
router.register("permission-to-tab", PermissionToTabViewSet, basename="permission-to-tab")
|
||||
router.register("permission", PermissionViewSet, basename="permission")
|
||||
router.register("permission-role", RoleViewSet, basename="permission-role")
|
||||
|
||||
|
||||
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(),
|
||||
name="token_refresh",
|
||||
),
|
||||
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'),
|
||||
]
|
||||
|
||||
41
core/apps/accounts/views/permission.py
Normal file
41
core/apps/accounts/views/permission.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.permissions import AllowAny, IsAdminUser
|
||||
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, RoleListSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["permission"])
|
||||
class PermissionToActionViewSet(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = PermissionToAction.objects.all()
|
||||
serializer_class = PermissionToActionSerializer
|
||||
|
||||
action_serializer_class = {
|
||||
'create': PermissionToActionSerializer,
|
||||
'update': PermissionToActionSerializer,
|
||||
}
|
||||
|
||||
action_permission_classes = {
|
||||
'create': [AllowAny],
|
||||
'destroy': [IsAdminUser],
|
||||
}
|
||||
|
||||
|
||||
@extend_schema(tags=["permission"])
|
||||
class PermissionToTabViewSet(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = PermissionToTab.objects.all()
|
||||
serializer_class = PermissionToTabSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["permission"])
|
||||
class PermissionViewSet(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = Permission.objects.all()
|
||||
serializer_class = PermissionSerializer
|
||||
|
||||
|
||||
class RoleViewSet(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = Role.objects.all()
|
||||
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)
|
||||
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": "",
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.7 on 2026-04-27 09:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evaluation', '0034_remove_certificatemodel_file_url_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='autoevaluationmodel',
|
||||
name='is_archived',
|
||||
field=models.BooleanField(default=False, verbose_name='is archived'),
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -9,14 +9,11 @@ from core.apps.evaluation.choices.auto import (
|
||||
AutoEvaluationStatus,
|
||||
AutoObjectType,
|
||||
# FormOwnership,
|
||||
LocationConvenience,
|
||||
LocationHighways,
|
||||
ObjectOwnerType,
|
||||
# PropertyRights,
|
||||
# RateType,
|
||||
# ValueDetermined,
|
||||
)
|
||||
|
||||
from .valuation import ValuationModel
|
||||
from .vehicle import VehicleModel
|
||||
|
||||
@@ -26,7 +23,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 +30,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 +37,6 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
ValuationModel,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="auto_detail",
|
||||
verbose_name=_("valuation"),
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
@@ -50,14 +44,12 @@ 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,
|
||||
)
|
||||
@@ -96,12 +88,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 +139,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"),
|
||||
@@ -272,8 +241,6 @@ class AutoEvaluationModel(AbstractBaseModel):
|
||||
default=False,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"Auto Evaluation {self.registration_number or self.pk}"
|
||||
|
||||
|
||||
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")
|
||||
@@ -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,43 +8,41 @@ from core.apps.evaluation.choices.request import (
|
||||
EvaluationRateType,
|
||||
RequestObjectType,
|
||||
RequestStatus,
|
||||
RequestPersonType,
|
||||
)
|
||||
from core.apps.evaluation.models import ReferenceitemModel
|
||||
|
||||
|
||||
class EvaluationrequestModel(AbstractBaseModel):
|
||||
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"),
|
||||
)
|
||||
rate_type = models.CharField(
|
||||
verbose_name=_("rate type"),
|
||||
max_length=50,
|
||||
choices=EvaluationRateType.choices,
|
||||
)
|
||||
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,
|
||||
)
|
||||
value_determined = models.ForeignKey(
|
||||
"evaluation.ReferenceitemModel",
|
||||
verbose_name=_("value determined"),
|
||||
@@ -77,51 +75,8 @@ class EvaluationrequestModel(AbstractBaseModel):
|
||||
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,
|
||||
)
|
||||
need_delivering = models.BooleanField(
|
||||
verbose_name=_("need delivering"),
|
||||
default=True,
|
||||
)
|
||||
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,
|
||||
)
|
||||
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()}"
|
||||
|
||||
@@ -147,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")
|
||||
|
||||
@@ -17,8 +17,6 @@ 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)
|
||||
user = serializers.SerializerMethodField(method_name="get_user", read_only=True)
|
||||
|
||||
class Meta:
|
||||
@@ -49,8 +47,6 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"created_at",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"property_rights",
|
||||
"form_ownership",
|
||||
"user",
|
||||
"evaluation_request",
|
||||
]
|
||||
@@ -75,13 +71,6 @@ 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 +78,6 @@ class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer):
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"rate_object_name",
|
||||
# Step 2
|
||||
"object_owner_type",
|
||||
"object_owner_type_display",
|
||||
@@ -118,21 +106,11 @@ 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,
|
||||
@@ -153,7 +131,6 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"rate_object_name",
|
||||
"object_type",
|
||||
# Step 2
|
||||
"object_owner_type",
|
||||
@@ -163,8 +140,6 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"property_rights",
|
||||
"form_ownership",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
# Step 4
|
||||
@@ -226,21 +201,11 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
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,
|
||||
@@ -267,7 +232,6 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"object_inspection_date",
|
||||
"rate_date",
|
||||
"rate_report_date",
|
||||
"rate_object_name",
|
||||
"object_type",
|
||||
# Step 2
|
||||
"object_owner_type",
|
||||
@@ -277,8 +241,6 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"property_rights",
|
||||
"form_ownership",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
# Step 4
|
||||
@@ -359,6 +321,7 @@ class AutoEvaluationAppraisersSerializer(serializers.Serializer):
|
||||
data['users'] = users
|
||||
return data
|
||||
|
||||
|
||||
class AutoEvaluationSerializer(serializers.Serializer):
|
||||
brand = serializers.CharField()
|
||||
brand_model = serializers.CharField()
|
||||
@@ -367,4 +330,60 @@ class AutoEvaluationSerializer(serializers.Serializer):
|
||||
transmission = serializers.CharField()
|
||||
condition = serializers.CharField()
|
||||
fuel_type = serializers.CharField()
|
||||
mileage = 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 = ("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",
|
||||
|
||||
"created_at",
|
||||
"updated_at",
|
||||
)
|
||||
|
||||
read_only_fields = (
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
)
|
||||
|
||||
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,6 +3,7 @@ from core.apps.evaluation.models import CertificateModel
|
||||
|
||||
|
||||
class BaseCertificateSerializer(serializers.ModelSerializer):
|
||||
file = serializers.SerializerMethodField(method_name='get_file', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = CertificateModel
|
||||
@@ -10,4 +11,20 @@ class BaseCertificateSerializer(serializers.ModelSerializer):
|
||||
"id",
|
||||
"title",
|
||||
"file",
|
||||
]
|
||||
]
|
||||
|
||||
def get_file(self, obj):
|
||||
if obj.file:
|
||||
request = self.context.get('request')
|
||||
return request.build_absolute_uri(obj.file.url)
|
||||
return None
|
||||
|
||||
|
||||
class CreateCertificateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = CertificateModel
|
||||
fields = [
|
||||
"id",
|
||||
"title",
|
||||
"file",
|
||||
]
|
||||
|
||||
@@ -128,6 +128,42 @@ class CreateQuickevaluationSerializer(serializers.ModelSerializer):
|
||||
return super().create(validated_data)
|
||||
|
||||
|
||||
class ArchiveQuickevaluationSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField(required=True)
|
||||
is_archive = serializers.BooleanField(required=True)
|
||||
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):
|
||||
@@ -92,7 +99,6 @@ class RetrieveEvaluationrequestSerializer(BaseEvaluationrequestSerializer):
|
||||
class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
|
||||
location = serializers.DictField(required=False, allow_empty=True)
|
||||
|
||||
# locationName — string qabul qiladi, location ichida yoki tashqarida yuborsa bo'ladi
|
||||
locationName = serializers.CharField(
|
||||
write_only=True,
|
||||
required=False,
|
||||
@@ -100,8 +106,6 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
|
||||
allow_blank=True,
|
||||
)
|
||||
|
||||
# Faqat truck_car uchun majburiy — field darajasida optional qilib belgilaymiz,
|
||||
# validate() da object_type ga qarab tekshiramiz
|
||||
worked_hours = serializers.IntegerField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
@@ -116,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
|
||||
@@ -134,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):
|
||||
@@ -157,7 +168,6 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
|
||||
{"tex_passport": "rate_type 'auto' bo'lganda tex_passport majburiy."}
|
||||
)
|
||||
|
||||
# worked_hours va chassi FAQAT yuk avtomobil (truck_car) uchun majburiy
|
||||
if object_type == "truck_car":
|
||||
if attrs.get("worked_hours") is None:
|
||||
raise serializers.ValidationError(
|
||||
@@ -183,8 +193,32 @@ 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)
|
||||
is_archive = serializers.BooleanField(required=True)
|
||||
is_archive = serializers.BooleanField(required=True)
|
||||
|
||||
@@ -1 +1 @@
|
||||
from .EvaluationRequest import * # noqa
|
||||
from .EvaluationRequest import * # noqa
|
||||
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)
|
||||
|
||||
@@ -1,90 +1,92 @@
|
||||
from django.urls import include, path
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from .views import (
|
||||
AdminEvaluationrequestView,
|
||||
AutoEvaluationHistoryView,
|
||||
AutoEvaluationView,
|
||||
CustomerView,
|
||||
DeterminedValueView,
|
||||
DocumentCategoryView,
|
||||
DocumentView,
|
||||
EvaluationPurposeView,
|
||||
EvaluationReportView,
|
||||
EvaluationrequestView,
|
||||
MovablePropertyEvaluationView,
|
||||
OwnershipFormView,
|
||||
PropertyOwnerView,
|
||||
PropertyRightsView,
|
||||
QuickEvaluationHistoryView,
|
||||
QuickEvaluationView,
|
||||
RealEstateEvaluationView,
|
||||
ReferenceitemView,
|
||||
ValuationDocumentView,
|
||||
ValuationView,
|
||||
VehicleView,
|
||||
AutoEvaluationListAppraisersView,
|
||||
AutoEvaluationSetAppraisersView,
|
||||
AutoEvaluationRemoveAppraisersView,
|
||||
DidoxCompanyInfoAPIView,
|
||||
TechPassportAPIView,
|
||||
EvaluationStatusChange,
|
||||
CertificateView,
|
||||
ArchiveQuickEvaluationView,
|
||||
ArchiveEvaluationrequestView, GetArchivedEvaluationListAPIView, ArchivedEvaluation,
|
||||
)
|
||||
from core.apps.evaluation import views
|
||||
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register("document-category", DocumentCategoryView, basename="DocumentCategory")
|
||||
router.register("document", DocumentView, basename="Document")
|
||||
router.register("auto-evaluation-history", AutoEvaluationHistoryView, basename="auto-evaluation-history")
|
||||
router.register("quick-evaluation-history", QuickEvaluationHistoryView, basename="quick-evaluation-history")
|
||||
router.register("determined-value", DeterminedValueView, basename="determined-value")
|
||||
router.register("evaluation-purpose", EvaluationPurposeView, basename="evaluation-purpose")
|
||||
router.register("property-rights", PropertyRightsView, basename="property-rights")
|
||||
router.register("ownership-form", OwnershipFormView, basename="ownership-form")
|
||||
router.register("evaluation-request", EvaluationrequestView, basename="evaluation-request")
|
||||
router.register("admin-evaluation-request", AdminEvaluationrequestView, basename="admin-evaluation-request")
|
||||
router.register("reference-item", ReferenceitemView, basename="reference-item")
|
||||
router.register("valuation-document", ValuationDocumentView, basename="valuation-document")
|
||||
router.register("evaluation-report", EvaluationReportView, basename="evaluation-report")
|
||||
router.register("quick-evaluation", QuickEvaluationView, basename="quick-evaluation")
|
||||
router.register("movable-property-evaluation", MovablePropertyEvaluationView, basename="movable-property-evaluation")
|
||||
router.register("real-estate-evaluation", RealEstateEvaluationView, basename="real-estate-evaluation")
|
||||
router.register("auto-evaluation", AutoEvaluationView, basename="auto-evaluation")
|
||||
router.register("vehicle", VehicleView, basename="vehicle")
|
||||
router.register("valuation", ValuationView, basename="valuation")
|
||||
router.register("property-owner", PropertyOwnerView, basename="property-owner")
|
||||
router.register("customer", CustomerView, basename="customer")
|
||||
router.register("certificate", CertificateView, basename="certificate")
|
||||
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("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")
|
||||
router.register("property-rights", views.PropertyRightsView, basename="property-rights")
|
||||
router.register("ownership-form", views.OwnershipFormView, basename="ownership-form")
|
||||
router.register("evaluation-request", views.EvaluationrequestView, basename="evaluation-request")
|
||||
router.register("admin-evaluation-request", views.AdminEvaluationrequestView, basename="admin-evaluation-request")
|
||||
router.register("reference-item", views.ReferenceitemView, basename="reference-item")
|
||||
router.register("valuation-document", views.ValuationDocumentView, basename="valuation-document")
|
||||
router.register("evaluation-report", views.EvaluationReportView, basename="evaluation-report")
|
||||
router.register("quick-evaluation", views.QuickEvaluationView, basename="quick-evaluation")
|
||||
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("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)),
|
||||
path("auto-evaluation/appraisers/", include(
|
||||
|
||||
path("didox/info/<int:tin>/", views.DidoxCompanyInfoAPIView.as_view()),
|
||||
path("tech-passport/", views.TechPassportAPIView.as_view()),
|
||||
|
||||
# Quick Evaluation
|
||||
path('quick-evaluation/', include(
|
||||
[
|
||||
path("<int:id>/list/", AutoEvaluationListAppraisersView.as_view(), name="auto-evaluation-list-appraisers"),
|
||||
path("<int:id>/set/", AutoEvaluationSetAppraisersView.as_view(), name="auto-evaluation-set-appraisers"),
|
||||
path("<int:id>/remove/", AutoEvaluationRemoveAppraisersView.as_view(), name="auto-evaluation-remove-appraisers"),
|
||||
path("admin/", views.AdminQuickEvalAPIView.as_view(), name="quick-evaluation"),
|
||||
path(
|
||||
'archive/', include(
|
||||
[
|
||||
path("list/", views.QuickEvaluationArchivedListAPIView.as_view()),
|
||||
path("<int:pk>/", views.QuickEvaluationArchiveAPIView.as_view()),
|
||||
]
|
||||
)
|
||||
),
|
||||
]
|
||||
)),
|
||||
path(
|
||||
"didox/info/<int:tin>/",
|
||||
DidoxCompanyInfoAPIView.as_view(),
|
||||
name="didox-info"
|
||||
),
|
||||
path(
|
||||
"tech-passport/",
|
||||
TechPassportAPIView.as_view(),
|
||||
name="tech-passport"
|
||||
),
|
||||
path("evaluation-request/<int:pk>/change-status/", EvaluationStatusChange.as_view(),
|
||||
name="evaluation-change-status"),
|
||||
path("archive/quick-evaluation/", ArchiveQuickEvaluationView.as_view(), name="quick-evaluation-archive"),
|
||||
path("archive/evaluation-request/", ArchiveEvaluationrequestView.as_view(), name="evaluation-request-archive"),
|
||||
|
||||
path("archived-evaluvation/", GetArchivedEvaluationListAPIView.as_view(),
|
||||
name="archived-evaluation"),
|
||||
# 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()),
|
||||
path('list/', views.AutoEvaluationArchivedListAPIView.as_view())
|
||||
]
|
||||
)),
|
||||
path('appraisers/', include(
|
||||
[
|
||||
path("<int:id>/list/", views.AutoEvaluationListAppraisersView.as_view()),
|
||||
path("<int:id>/set/", views.AutoEvaluationSetAppraisersView.as_view()),
|
||||
path("<int:id>/remove/", views.AutoEvaluationRemoveAppraisersView.as_view()),
|
||||
]
|
||||
))
|
||||
]
|
||||
)),
|
||||
|
||||
path("auto-evaluvation-change-status/<int:pk>", ArchivedEvaluation.as_view(),
|
||||
name="archived-evaluation"),
|
||||
# Evaluation Request
|
||||
path("evaluation-request/", include(
|
||||
[
|
||||
path("<int:pk>/change-status/", views.EvaluationStatusChange.as_view()),
|
||||
path(
|
||||
'archive/', include(
|
||||
[
|
||||
path('list/', views.RequestEvaluationArchivedListAPIView.as_view()),
|
||||
path('<int:pk>/', views.RequestEvaluationArchiveAPIView.as_view()),
|
||||
]
|
||||
)
|
||||
)
|
||||
]
|
||||
)),
|
||||
|
||||
path("calculate_avg_cost/", views.AvgCostAPIView.as_view()),
|
||||
path("vehicle_document/", views.GeneratePDFView.as_view()),
|
||||
]
|
||||
|
||||
@@ -14,3 +14,5 @@ from .vehicle import * # noqa
|
||||
from .didox import * # noqa
|
||||
from .tech_passport import * # noqa
|
||||
from .certificate import * # noqa
|
||||
from .avg_cost import *
|
||||
from .bonus import *
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -10,16 +11,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
|
||||
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.auto import (
|
||||
CreateAutoevaluationSerializer,
|
||||
ListAutoevaluationSerializer,
|
||||
RetrieveAutoevaluationSerializer,
|
||||
AutoEvaluationAppraisersSerializer,
|
||||
UpdateAutoevaluationSerializer
|
||||
)
|
||||
from core.apps.evaluation.serializers import auto as serializers, AutoEvaluationModelSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["AutoEvaluation"])
|
||||
@@ -28,8 +24,8 @@ class AutoEvaluationView(BaseViewSetMixin, ModelViewSet):
|
||||
"valuation",
|
||||
"valuation__customer",
|
||||
"vehicle",
|
||||
).all()
|
||||
serializer_class = ListAutoevaluationSerializer
|
||||
).filter(is_archived=False)
|
||||
serializer_class = serializers.ListAutoevaluationSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
@@ -67,18 +63,16 @@ class AutoEvaluationView(BaseViewSetMixin, ModelViewSet):
|
||||
"created_at",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"property_rights",
|
||||
"form_ownership",
|
||||
]
|
||||
ordering = ["-created_at"]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListAutoevaluationSerializer,
|
||||
"retrieve": RetrieveAutoevaluationSerializer,
|
||||
"create": CreateAutoevaluationSerializer,
|
||||
"update": UpdateAutoevaluationSerializer,
|
||||
"partial_update": UpdateAutoevaluationSerializer,
|
||||
"list": serializers.ListAutoevaluationSerializer,
|
||||
"retrieve": serializers.RetrieveAutoevaluationSerializer,
|
||||
"create": serializers.CreateAutoevaluationSerializer,
|
||||
"update": serializers.UpdateAutoevaluationSerializer,
|
||||
"partial_update": serializers.UpdateAutoevaluationSerializer,
|
||||
}
|
||||
|
||||
def serializer_context(self):
|
||||
@@ -89,7 +83,7 @@ class AutoEvaluationView(BaseViewSetMixin, ModelViewSet):
|
||||
class AutoEvaluationSetAppraisersView(GenericAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
queryset = AutoEvaluationModel.objects.all()
|
||||
serializer_class = AutoEvaluationAppraisersSerializer
|
||||
serializer_class = serializers.AutoEvaluationAppraisersSerializer
|
||||
|
||||
def post(self, request, id):
|
||||
try:
|
||||
@@ -108,7 +102,7 @@ class AutoEvaluationSetAppraisersView(GenericAPIView):
|
||||
class AutoEvaluationRemoveAppraisersView(GenericAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
queryset = AutoEvaluationModel.objects.all()
|
||||
serializer_class = AutoEvaluationAppraisersSerializer
|
||||
serializer_class = serializers.AutoEvaluationAppraisersSerializer
|
||||
|
||||
def post(self, request, id):
|
||||
try:
|
||||
@@ -158,18 +152,41 @@ class AutoEvaluationListAppraisersView(GenericAPIView):
|
||||
|
||||
|
||||
@extend_schema(tags=["AutoEvaluation"])
|
||||
class GetArchivedEvaluationListAPIView(ListAPIView):
|
||||
class AutoEvaluationArchivedListAPIView(ListAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = serializers.ListAutoevaluationSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return AutoEvaluationModel.objects.filter(is_archived=True)
|
||||
|
||||
|
||||
@extend_schema(tags=["AutoEvaluation"])
|
||||
class ArchivedEvaluation(APIView):
|
||||
class AutoEvaluationArchiveAPIView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request, pk):
|
||||
auto_evaluation = get_object_or_404(AutoEvaluationModel, pk=pk)
|
||||
auto_evaluation.is_archived = request.data["is_archived"]
|
||||
auto_evaluation.save()
|
||||
return Response({"success": True}, status=200)
|
||||
return Response(
|
||||
{
|
||||
"success": True,
|
||||
"status": auto_evaluation.status,
|
||||
"id": auto_evaluation.pk
|
||||
},
|
||||
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)
|
||||
|
||||
@@ -1,18 +1,37 @@
|
||||
# rest framework
|
||||
from rest_framework import generics
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import permissions
|
||||
|
||||
# core apps
|
||||
from core.apps.evaluation.serializers.auto.AvgCost import AvgCostSerializer
|
||||
from core.services.grpc.auto import get_auto_avg_cost
|
||||
|
||||
|
||||
class AvgCostView(generics.GenericAPIView):
|
||||
class AvgCostAPIView(generics.GenericAPIView):
|
||||
serializer_class = AvgCostSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def post(self):
|
||||
serializer = self.get_serializer(data=self.request.data)
|
||||
def post(self, request):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
avg_cost = get_auto_avg_cost(serializer.validated_data)
|
||||
print(serializer.validated_data)
|
||||
brand = serializer.validated_data['brand']
|
||||
condition = serializer.validated_data['condition']
|
||||
model = serializer.validated_data['model']
|
||||
complication = serializer.validated_data['complication']
|
||||
manufacture_date = serializer.validated_data['manufacture_date']
|
||||
distance_covered = serializer.validated_data['distance_covered']
|
||||
color = serializer.validated_data['color']
|
||||
|
||||
avg_cost = get_auto_avg_cost(
|
||||
brand=brand,
|
||||
condition=condition,
|
||||
model=model,
|
||||
complication=complication,
|
||||
manufacture_date=manufacture_date,
|
||||
distance_covered=str(distance_covered),
|
||||
color=color
|
||||
)
|
||||
return Response(avg_cost, status=200)
|
||||
return Response(serializer.errors, status=400)
|
||||
|
||||
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],
|
||||
}
|
||||
@@ -1,23 +1,30 @@
|
||||
# django core
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
|
||||
# swagger
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
# rest framework
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from core.apps.evaluation.models import CertificateModel
|
||||
from core.apps.evaluation.serializers.certificate import BaseCertificateSerializer
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.filters import SearchFilter
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
|
||||
# local apps
|
||||
from core.apps.evaluation.models import CertificateModel
|
||||
from core.apps.evaluation.serializers.certificate import BaseCertificateSerializer, CreateCertificateSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["Certificate"],request=BaseCertificateSerializer)
|
||||
class CertificateView(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = CertificateModel.objects.all()
|
||||
serializer_class = BaseCertificateSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
filter_backends = [SearchFilter]
|
||||
search_fields = ["title"]
|
||||
|
||||
pagination_class = None
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"create": CreateCertificateSerializer
|
||||
}
|
||||
|
||||
@@ -3,44 +3,33 @@ from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
# local apps
|
||||
from core.apps.evaluation.models import CustomerModel, PropertyOwnerModel
|
||||
from core.apps.evaluation.serializers.customer import (
|
||||
CreateCustomerSerializer,
|
||||
CreatePropertyOwnerSerializer,
|
||||
ListCustomerSerializer,
|
||||
ListPropertyOwnerSerializer,
|
||||
RetrieveCustomerSerializer,
|
||||
RetrievePropertyOwnerSerializer,
|
||||
)
|
||||
from core.apps.evaluation.serializers import customer as serializers
|
||||
|
||||
|
||||
@extend_schema(tags=["Customer"])
|
||||
class CustomerView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = CustomerModel.objects.all()
|
||||
serializer_class = ListCustomerSerializer
|
||||
serializer_class = serializers.ListCustomerSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListCustomerSerializer,
|
||||
"retrieve": RetrieveCustomerSerializer,
|
||||
"create": CreateCustomerSerializer,
|
||||
"list": serializers.ListCustomerSerializer,
|
||||
"retrieve": serializers.RetrieveCustomerSerializer,
|
||||
"create": serializers.CreateCustomerSerializer,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@extend_schema(tags=["PropertyOwner"])
|
||||
class PropertyOwnerView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = PropertyOwnerModel.objects.all()
|
||||
serializer_class = ListPropertyOwnerSerializer
|
||||
serializer_class = serializers.ListPropertyOwnerSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListPropertyOwnerSerializer,
|
||||
"retrieve": RetrievePropertyOwnerSerializer,
|
||||
"create": CreatePropertyOwnerSerializer,
|
||||
}
|
||||
|
||||
#test commit
|
||||
"list": serializers.ListPropertyOwnerSerializer,
|
||||
"retrieve": serializers.RetrievePropertyOwnerSerializer,
|
||||
"create": serializers.CreatePropertyOwnerSerializer,
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
# rest framework
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.generics import GenericAPIView
|
||||
|
||||
|
||||
# swagger
|
||||
from drf_spectacular.utils import extend_schema, OpenApiParameter
|
||||
|
||||
# local apps
|
||||
from core.services.didox import DidoxService
|
||||
|
||||
|
||||
@@ -38,7 +40,6 @@ class DidoxCompanyInfoAPIView(GenericAPIView):
|
||||
{"detail": "TIN must be a valid integer"},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
data = DidoxService.get_company_info(tin)
|
||||
|
||||
if not data:
|
||||
@@ -47,14 +48,11 @@ class DidoxCompanyInfoAPIView(GenericAPIView):
|
||||
status=status.HTTP_502_BAD_GATEWAY
|
||||
)
|
||||
|
||||
# if both name and personalNum are null/empty -> 404
|
||||
name = data.get("name")
|
||||
personal_num = data.get("personalNum")
|
||||
|
||||
if not name and not personal_num:
|
||||
return Response(
|
||||
{"detail": "Company or person not found"},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
# django
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
|
||||
# swagger
|
||||
from drf_spectacular.utils import extend_schema, OpenApiParameter
|
||||
|
||||
# rest framework
|
||||
from rest_framework.exceptions import NotFound, PermissionDenied
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
|
||||
from rest_framework.parsers import FormParser, MultiPartParser
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
# filters
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
|
||||
from core.apps.evaluation.models import DocumentModel, ValuationDocumentModel, AutoEvaluationModel
|
||||
from core.apps.evaluation.serializers.document import (
|
||||
CreateDocumentSerializer,
|
||||
CreateValuationdocumentSerializer,
|
||||
ListDocumentSerializer,
|
||||
ListValuationdocumentSerializer,
|
||||
RetrieveDocumentSerializer,
|
||||
RetrieveValuationdocumentSerializer,
|
||||
)
|
||||
from core.apps.evaluation.serializers import document as serializers
|
||||
|
||||
|
||||
@extend_schema(tags=["ValuationDocument"])
|
||||
class ValuationDocumentView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = ValuationDocumentModel.objects.all()
|
||||
serializer_class = ListValuationdocumentSerializer
|
||||
serializer_class = serializers.ListValuationdocumentSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListValuationdocumentSerializer,
|
||||
"retrieve": RetrieveValuationdocumentSerializer,
|
||||
"create": CreateValuationdocumentSerializer,
|
||||
"list": serializers.ListValuationdocumentSerializer,
|
||||
"retrieve": serializers.RetrieveValuationdocumentSerializer,
|
||||
"create": serializers.CreateValuationdocumentSerializer,
|
||||
}
|
||||
|
||||
|
||||
@extend_schema(tags=["Document"])
|
||||
class DocumentView(BaseViewSetMixin, ModelViewSet):
|
||||
queryset = DocumentModel.objects.all()
|
||||
serializer_class = ListDocumentSerializer
|
||||
serializer_class = serializers.ListDocumentSerializer
|
||||
permission_classes = [AllowAny]
|
||||
parser_classes = [FormParser, MultiPartParser]
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListDocumentSerializer,
|
||||
"retrieve": RetrieveDocumentSerializer,
|
||||
"create": CreateDocumentSerializer,
|
||||
"list": serializers.ListDocumentSerializer,
|
||||
"retrieve": serializers.RetrieveDocumentSerializer,
|
||||
"create": serializers.CreateDocumentSerializer,
|
||||
}
|
||||
|
||||
@extend_schema(
|
||||
@@ -83,9 +83,9 @@ class DocumentView(BaseViewSetMixin, ModelViewSet):
|
||||
documents = documents.filter(category_id=category_id)
|
||||
page = self.paginate_queryset(documents)
|
||||
if page is not None:
|
||||
serializer = ListDocumentSerializer(page, many=True, context={"request": request})
|
||||
serializer = serializers.ListDocumentSerializer(page, many=True, context={"request": request})
|
||||
return self.get_paginated_response(serializer.data)
|
||||
serializer = ListDocumentSerializer(documents, many=True, context={"request": request})
|
||||
serializer = serializers.ListDocumentSerializer(documents, many=True, context={"request": request})
|
||||
return Response(serializer.data)
|
||||
except AutoEvaluationModel.DoesNotExist:
|
||||
raise NotFound("Auto evaluation not found")
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
# django core
|
||||
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.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
# core apps
|
||||
from core.apps.evaluation.models import DocumentcategoryModel
|
||||
from core.apps.evaluation.serializers.documentcategory import (
|
||||
ListDocumentcategorySerializer,
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
# django
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
|
||||
# django filters
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
|
||||
# swagger
|
||||
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
||||
|
||||
# rest framework
|
||||
from rest_framework.filters import OrderingFilter
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
# core apps
|
||||
from core.apps.evaluation.filters.history import (
|
||||
AutoevaluationhistoryFilter,
|
||||
QuickevaluationhistoryFilter,
|
||||
)
|
||||
from core.apps.evaluation.models import AutoevaluationhistoryModel, QuickevaluationhistoryModel
|
||||
from core.apps.evaluation.serializers.history import (
|
||||
CreateAutoevaluationhistorySerializer,
|
||||
CreateQuickevaluationhistorySerializer,
|
||||
ListAutoevaluationhistorySerializer,
|
||||
ListQuickevaluationhistorySerializer,
|
||||
RetrieveAutoevaluationhistorySerializer,
|
||||
RetrieveQuickevaluationhistorySerializer,
|
||||
)
|
||||
from core.apps.evaluation.serializers import history as serializers
|
||||
|
||||
|
||||
@extend_schema(
|
||||
@@ -31,13 +32,12 @@ from core.apps.evaluation.serializers.history import (
|
||||
],
|
||||
)
|
||||
class AutoEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
# select_related("auto_evaluation") faqat retrieve uchun — list uchun kerak emas
|
||||
queryset = AutoevaluationhistoryModel.objects.only(
|
||||
"id", "auto_evaluation_id", "event_type",
|
||||
"actor_id", "actor_full_name", "actor_role",
|
||||
"meta", "created_at",
|
||||
)
|
||||
serializer_class = ListAutoevaluationhistorySerializer
|
||||
serializer_class = serializers.ListAutoevaluationhistorySerializer
|
||||
permission_classes = [AllowAny]
|
||||
pagination_class = None
|
||||
|
||||
@@ -55,14 +55,13 @@ class AutoEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListAutoevaluationhistorySerializer,
|
||||
"retrieve": RetrieveAutoevaluationhistorySerializer,
|
||||
"create": CreateAutoevaluationhistorySerializer,
|
||||
"list": serializers.ListAutoevaluationhistorySerializer,
|
||||
"retrieve": serializers.RetrieveAutoevaluationhistorySerializer,
|
||||
"create": serializers.CreateAutoevaluationhistorySerializer,
|
||||
}
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
# Queryset bir marta evaluate qilinadi — COUNT uchun alohida query yo'q
|
||||
results = list(queryset)
|
||||
serializer = self.get_serializer(results, many=True)
|
||||
return Response({
|
||||
@@ -88,7 +87,7 @@ class QuickEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
"actor_id", "actor_full_name", "actor_role",
|
||||
"meta", "created_at",
|
||||
)
|
||||
serializer_class = ListQuickevaluationhistorySerializer
|
||||
serializer_class = serializers.ListQuickevaluationhistorySerializer
|
||||
permission_classes = [AllowAny]
|
||||
pagination_class = None
|
||||
|
||||
@@ -99,9 +98,9 @@ class QuickEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListQuickevaluationhistorySerializer,
|
||||
"retrieve": RetrieveQuickevaluationhistorySerializer,
|
||||
"create": CreateQuickevaluationhistorySerializer,
|
||||
"list": serializers.ListQuickevaluationhistorySerializer,
|
||||
"retrieve": serializers.RetrieveQuickevaluationhistorySerializer,
|
||||
"create": serializers.CreateQuickevaluationhistorySerializer,
|
||||
}
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
# django core
|
||||
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.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
# core apps
|
||||
from core.apps.evaluation.models import MovablePropertyEvaluationModel
|
||||
from core.apps.evaluation.serializers.movable import (
|
||||
CreateMovablepropertyevaluationSerializer,
|
||||
ListMovablepropertyevaluationSerializer,
|
||||
RetrieveMovablepropertyevaluationSerializer,
|
||||
)
|
||||
from core.apps.evaluation.serializers import movable as serializers
|
||||
|
||||
|
||||
@extend_schema(tags=["MovablePropertyEvaluation"])
|
||||
class MovablePropertyEvaluationView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = MovablePropertyEvaluationModel.objects.all()
|
||||
serializer_class = ListMovablepropertyevaluationSerializer
|
||||
serializer_class = serializers.ListMovablepropertyevaluationSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListMovablepropertyevaluationSerializer,
|
||||
"retrieve": RetrieveMovablepropertyevaluationSerializer,
|
||||
"create": CreateMovablepropertyevaluationSerializer,
|
||||
"list": serializers.ListMovablepropertyevaluationSerializer,
|
||||
"retrieve": serializers.RetrieveMovablepropertyevaluationSerializer,
|
||||
"create": serializers.CreateMovablepropertyevaluationSerializer,
|
||||
}
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
# 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
|
||||
from drf_spectacular.utils import extend_schema, OpenApiResponse
|
||||
# swagger
|
||||
from drf_spectacular.utils import extend_schema
|
||||
# rest framework
|
||||
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
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from rest_framework.generics import GenericAPIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from django.shortcuts import get_object_or_404
|
||||
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.quick import (
|
||||
CreateQuickevaluationSerializer,
|
||||
ListQuickevaluationSerializer,
|
||||
RetrieveQuickevaluationSerializer,
|
||||
ArchiveQuickevaluationSerializer,
|
||||
)
|
||||
from core.apps.evaluation.serializers import quick as serializers, QuickEvaluationModelSerializer
|
||||
|
||||
|
||||
@extend_schema(tags=["QuickEvaluation"])
|
||||
@@ -26,7 +29,7 @@ class QuickEvaluationView(BaseViewSetMixin, ModelViewSet):
|
||||
"created_by", "brand", "marka", "color", "fuel_type",
|
||||
"body_type", "state_car", "car_position",
|
||||
).filter(is_archive=False)
|
||||
serializer_class = ListQuickevaluationSerializer
|
||||
serializer_class = serializers.ListQuickevaluationSerializer
|
||||
permission_classes = [AllowAny]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
@@ -51,80 +54,48 @@ class QuickEvaluationView(BaseViewSetMixin, ModelViewSet):
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListQuickevaluationSerializer,
|
||||
"retrieve": RetrieveQuickevaluationSerializer,
|
||||
"create": CreateQuickevaluationSerializer,
|
||||
"list": serializers.ListQuickevaluationSerializer,
|
||||
"retrieve": serializers.RetrieveQuickevaluationSerializer,
|
||||
"create": serializers.CreateQuickevaluationSerializer,
|
||||
}
|
||||
|
||||
|
||||
@extend_schema(tags=["QuickEvaluation"])
|
||||
class ArchiveQuickEvaluationView(GenericAPIView):
|
||||
class QuickEvaluationArchiveAPIView(APIView):
|
||||
|
||||
def post(self, request, pk):
|
||||
instance = get_object_or_404(QuickEvaluationModel, pk=pk)
|
||||
|
||||
is_archived = request.data.get("is_archived")
|
||||
if is_archived is None:
|
||||
return Response(
|
||||
{"error": "Поле 'is_archived' обязательно"},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
instance.is_archive = is_archived
|
||||
instance.save()
|
||||
return Response({"success": True}, status=200)
|
||||
|
||||
|
||||
@extend_schema(tags=["QuickEvaluation"])
|
||||
class QuickEvaluationArchivedListAPIView(ListAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = serializers.ListQuickevaluationSerializer
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method == "GET":
|
||||
return ListQuickevaluationSerializer
|
||||
return ArchiveQuickevaluationSerializer
|
||||
def get_queryset(self):
|
||||
return QuickEvaluationModel.objects.filter(is_archive=True)
|
||||
|
||||
@extend_schema(
|
||||
tags=["QuickEvaluation"],
|
||||
summary="Get archived quick evaluations list",
|
||||
description="""
|
||||
Returns only archived quick evaluations.
|
||||
|
||||
This endpoint works like quick-evaluation/,
|
||||
but only records with is_archive=True are returned.
|
||||
""",
|
||||
responses={200: ListQuickevaluationSerializer(many=True)},
|
||||
)
|
||||
def get(self, request, *args, **kwargs):
|
||||
queryset = QuickEvaluationModel.objects.filter(
|
||||
is_archive=True
|
||||
).order_by("-created_at")
|
||||
@extend_schema(tags=["QuickEvaluation"])
|
||||
class AdminQuickEvalAPIView(generics.GenericAPIView):
|
||||
permission_classes = [IsAuthenticated, IsAdminRole]
|
||||
queryset = QuickEvaluationModel.objects.all()
|
||||
serializer_class = QuickEvaluationModelSerializer
|
||||
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
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, status=status.HTTP_200_OK)
|
||||
|
||||
@extend_schema(
|
||||
tags=["QuickEvaluation"],
|
||||
summary="Archive or unarchive quick evaluation",
|
||||
description="""
|
||||
Update archive status for quick evaluation.
|
||||
|
||||
- is_archive=true → archive
|
||||
- is_archive=false → remove from archive
|
||||
""",
|
||||
request=ArchiveQuickevaluationSerializer,
|
||||
responses={
|
||||
200: OpenApiResponse(
|
||||
description="Archive status updated successfully"
|
||||
),
|
||||
400: OpenApiResponse(
|
||||
description="Validation error"
|
||||
),
|
||||
404: OpenApiResponse(
|
||||
description="Quick evaluation not found"
|
||||
),
|
||||
},
|
||||
)
|
||||
def post(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
validated_data = serializer.validated_data
|
||||
|
||||
obj = get_object_or_404(
|
||||
QuickEvaluationModel,
|
||||
id=validated_data["id"]
|
||||
)
|
||||
|
||||
obj.is_archive = validated_data["is_archive"]
|
||||
obj.save(update_fields=["is_archive"])
|
||||
|
||||
return Response(
|
||||
{
|
||||
"success": True,
|
||||
"message": "Archive status updated successfully"
|
||||
},
|
||||
status=status.HTTP_200_OK
|
||||
)
|
||||
return Response(serializer.data)
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
# django core
|
||||
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.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
# core
|
||||
from core.apps.evaluation.models import RealEstateEvaluationModel
|
||||
from core.apps.evaluation.serializers.real_estate import (
|
||||
CreateRealestateevaluationSerializer,
|
||||
ListRealestateevaluationSerializer,
|
||||
RetrieveRealestateevaluationSerializer,
|
||||
)
|
||||
from core.apps.evaluation.serializers import real_estate as serializers
|
||||
|
||||
|
||||
@extend_schema(tags=["RealEstateEvaluation"])
|
||||
class RealEstateEvaluationView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = RealEstateEvaluationModel.objects.all()
|
||||
serializer_class = ListRealestateevaluationSerializer
|
||||
serializer_class = serializers.ListRealestateevaluationSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListRealestateevaluationSerializer,
|
||||
"retrieve": RetrieveRealestateevaluationSerializer,
|
||||
"create": CreateRealestateevaluationSerializer,
|
||||
"list": serializers.ListRealestateevaluationSerializer,
|
||||
"retrieve": serializers.RetrieveRealestateevaluationSerializer,
|
||||
"create": serializers.CreateRealestateevaluationSerializer,
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
# 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.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
# core apps
|
||||
from core.apps.evaluation.filters.reference import ReferenceitemFilter
|
||||
from core.apps.evaluation.models import ReferenceitemModel
|
||||
from core.apps.evaluation.serializers.reference import (
|
||||
CreateReferenceitemSerializer,
|
||||
ListReferenceitemSerializer,
|
||||
RetrieveReferenceitemSerializer,
|
||||
EvaluationPurposeSerializer,
|
||||
DeterminedValueSerializer,
|
||||
LabelValueSerializer,
|
||||
)
|
||||
from core.apps.evaluation.serializers import reference as serializers
|
||||
|
||||
|
||||
@extend_schema(tags=["EvaluationPurpose"])
|
||||
@@ -22,10 +23,9 @@ class EvaluationPurposeView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = ReferenceitemModel.objects.filter(
|
||||
type="evaluation_purpose", is_active=True
|
||||
).order_by("order", "name")
|
||||
serializer_class = EvaluationPurposeSerializer
|
||||
serializer_class = serializers.EvaluationPurposeSerializer
|
||||
permission_classes = [AllowAny]
|
||||
pagination_class = None
|
||||
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
search_fields = ["name"]
|
||||
ordering_fields = ["name", "order"]
|
||||
@@ -37,10 +37,9 @@ class DeterminedValueView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = ReferenceitemModel.objects.filter(
|
||||
type="determined_value", is_active=True
|
||||
).order_by("order", "name")
|
||||
serializer_class = DeterminedValueSerializer
|
||||
serializer_class = serializers.DeterminedValueSerializer
|
||||
permission_classes = [AllowAny]
|
||||
pagination_class = None
|
||||
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
search_fields = ["name"]
|
||||
ordering_fields = ["name", "order"]
|
||||
@@ -52,10 +51,9 @@ class PropertyRightsView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = ReferenceitemModel.objects.filter(
|
||||
type="property_rights", is_active=True
|
||||
).order_by("order", "name")
|
||||
serializer_class = LabelValueSerializer
|
||||
serializer_class = serializers.LabelValueSerializer
|
||||
permission_classes = [AllowAny]
|
||||
pagination_class = None
|
||||
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
search_fields = ["name"]
|
||||
ordering_fields = ["name", "order"]
|
||||
@@ -67,10 +65,9 @@ class OwnershipFormView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = ReferenceitemModel.objects.filter(
|
||||
type="ownership_form", is_active=True
|
||||
).order_by("order", "name")
|
||||
serializer_class = LabelValueSerializer
|
||||
serializer_class = serializers.LabelValueSerializer
|
||||
permission_classes = [AllowAny]
|
||||
pagination_class = None
|
||||
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
search_fields = ["name"]
|
||||
ordering_fields = ["name", "order"]
|
||||
@@ -80,18 +77,16 @@ class OwnershipFormView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
@extend_schema(tags=["ReferenceItem"])
|
||||
class ReferenceitemView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = ReferenceitemModel.objects.select_related("parent").filter(is_active=True)
|
||||
serializer_class = ListReferenceitemSerializer
|
||||
serializer_class = serializers.ListReferenceitemSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
filterset_class = ReferenceitemFilter
|
||||
search_fields = ["name"]
|
||||
ordering_fields = ["name", "order", "type"]
|
||||
ordering = ["order", "name"]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListReferenceitemSerializer,
|
||||
"retrieve": RetrieveReferenceitemSerializer,
|
||||
"create": CreateReferenceitemSerializer,
|
||||
"list": serializers.ListReferenceitemSerializer,
|
||||
"retrieve": serializers.RetrieveReferenceitemSerializer,
|
||||
"create": serializers.CreateReferenceitemSerializer,
|
||||
}
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
# django core
|
||||
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.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
# core apps
|
||||
from core.apps.evaluation.models import EvaluationReportModel
|
||||
from core.apps.evaluation.serializers.report import (
|
||||
CreateEvaluationreportSerializer,
|
||||
ListEvaluationreportSerializer,
|
||||
RetrieveEvaluationreportSerializer,
|
||||
)
|
||||
from core.apps.evaluation.serializers import report as serializers
|
||||
|
||||
|
||||
@extend_schema(tags=["EvaluationReport"])
|
||||
class EvaluationReportView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = EvaluationReportModel.objects.all()
|
||||
serializer_class = ListEvaluationreportSerializer
|
||||
serializer_class = serializers.ListEvaluationreportSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListEvaluationreportSerializer,
|
||||
"retrieve": RetrieveEvaluationreportSerializer,
|
||||
"create": CreateEvaluationreportSerializer,
|
||||
"list": serializers.ListEvaluationreportSerializer,
|
||||
"retrieve": serializers.RetrieveEvaluationreportSerializer,
|
||||
"create": serializers.CreateEvaluationreportSerializer,
|
||||
}
|
||||
|
||||
@@ -1,42 +1,33 @@
|
||||
# django
|
||||
from django.db.models.base import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
# django core
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework import status
|
||||
|
||||
# django filters
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
|
||||
# swagger
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
# rest framework
|
||||
from rest_framework import status, filters, generics, permissions, views, viewsets
|
||||
from rest_framework.response import Response
|
||||
|
||||
# core apps
|
||||
from core.apps.accounts.choices import RoleChoice
|
||||
from core.apps.evaluation.choices.request import RequestStatus
|
||||
from core.apps.evaluation.filters.request import EvaluationrequestFilter
|
||||
from core.apps.evaluation.models import EvaluationrequestModel
|
||||
from core.apps.evaluation.serializers.request import (
|
||||
CreateEvaluationrequestSerializer,
|
||||
ListEvaluationrequestSerializer,
|
||||
RetrieveEvaluationrequestSerializer,
|
||||
ArchiveEvaluationrequestSerializer,
|
||||
)
|
||||
from core.apps.evaluation.choices.request import RequestStatus
|
||||
from rest_framework.generics import GenericAPIView
|
||||
from drf_spectacular.utils import OpenApiResponse
|
||||
|
||||
|
||||
# class RequestPagination(PageNumberPagination):
|
||||
# page_size = 20
|
||||
# page_size_query_param = "limit"
|
||||
# max_page_size = 100
|
||||
from core.apps.evaluation.serializers import request as serializers
|
||||
|
||||
|
||||
@extend_schema(tags=["EvaluationRequest"])
|
||||
class EvaluationrequestView(BaseViewSetMixin, ModelViewSet):
|
||||
serializer_class = ListEvaluationrequestSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
# pagination_class = RequestPagination
|
||||
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
class EvaluationrequestView(BaseViewSetMixin, viewsets.ModelViewSet):
|
||||
serializer_class = serializers.ListEvaluationrequestSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
||||
filterset_class = EvaluationrequestFilter
|
||||
search_fields = [
|
||||
"customer_inn_number",
|
||||
@@ -71,9 +62,9 @@ class EvaluationrequestView(BaseViewSetMixin, ModelViewSet):
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListEvaluationrequestSerializer,
|
||||
"retrieve": RetrieveEvaluationrequestSerializer,
|
||||
"create": CreateEvaluationrequestSerializer,
|
||||
"list": serializers.ListEvaluationrequestSerializer,
|
||||
"retrieve": serializers.RetrieveEvaluationrequestSerializer,
|
||||
"create": serializers.CreateEvaluationrequestSerializer,
|
||||
}
|
||||
|
||||
def serializer_context(self):
|
||||
@@ -81,18 +72,15 @@ class EvaluationrequestView(BaseViewSetMixin, ModelViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
return EvaluationrequestModel.objects.filter(
|
||||
user=self.request.user
|
||||
user=self.request.user, is_archive=False
|
||||
).order_by("-created_at")
|
||||
|
||||
|
||||
|
||||
@extend_schema(tags=["EvaluationRequest"])
|
||||
class AdminEvaluationrequestView(BaseViewSetMixin, ModelViewSet):
|
||||
serializer_class = ListEvaluationrequestSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
# pagination_class = RequestPagination
|
||||
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
class AdminEvaluationrequestView(BaseViewSetMixin, viewsets.ModelViewSet):
|
||||
serializer_class = serializers.ListEvaluationrequestSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
||||
filterset_class = EvaluationrequestFilter
|
||||
search_fields = [
|
||||
"customer_inn_number",
|
||||
@@ -127,29 +115,29 @@ class AdminEvaluationrequestView(BaseViewSetMixin, ModelViewSet):
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListEvaluationrequestSerializer,
|
||||
"retrieve": RetrieveEvaluationrequestSerializer,
|
||||
"create": CreateEvaluationrequestSerializer,
|
||||
"list": serializers.ListEvaluationrequestSerializer,
|
||||
"retrieve": serializers.RetrieveEvaluationrequestSerializer,
|
||||
"create": serializers.CreateEvaluationrequestSerializer,
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return EvaluationrequestModel.objects.select_related("value_determined", "rate_goal", "property_rights", "form_ownership", "user").order_by("-created_at")
|
||||
return EvaluationrequestModel.objects.\
|
||||
select_related("value_determined","rate_goal","property_rights","form_ownership","user")\
|
||||
.order_by("-created_at")
|
||||
|
||||
def serializer_context(self):
|
||||
return self.serializer_class(context={"request": self.request})
|
||||
|
||||
|
||||
@extend_schema(tags=["EvaluationRequest"])
|
||||
class EvaluationStatusChange(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
class EvaluationStatusChange(views.APIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def post(self, request, pk):
|
||||
if request.user.role not in [RoleChoice.ADMIN, RoleChoice.SUPERUSER]:
|
||||
return Response({'detail': 'Forbidden'}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
evaluation = get_object_or_404(EvaluationrequestModel, pk=pk)
|
||||
|
||||
|
||||
status_value = request.data.get('status')
|
||||
if not status_value:
|
||||
return Response({'detail': 'Status is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
@@ -176,75 +164,30 @@ class EvaluationStatusChange(APIView):
|
||||
'id': evaluation.pk
|
||||
})
|
||||
|
||||
|
||||
@extend_schema(tags=["EvaluationRequest"])
|
||||
class ArchiveEvaluationrequestView(GenericAPIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
class RequestEvaluationArchivedListAPIView(generics.ListAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
serializer_class = serializers.ListEvaluationrequestSerializer
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method == "GET":
|
||||
return ListEvaluationrequestSerializer
|
||||
return ArchiveEvaluationrequestSerializer
|
||||
def get_queryset(self):
|
||||
return EvaluationrequestModel.objects.filter(is_archive=True)
|
||||
|
||||
@extend_schema(
|
||||
tags=["EvaluationRequest"],
|
||||
summary="Get archived evaluation requests list",
|
||||
description="""
|
||||
Returns only archived evaluation requests.
|
||||
|
||||
This endpoint works like evaluation-request/,
|
||||
but only records with is_archive=True are returned.
|
||||
""",
|
||||
responses={200: ListEvaluationrequestSerializer(many=True)},
|
||||
)
|
||||
def get(self, request, *args, **kwargs):
|
||||
queryset = EvaluationrequestModel.objects.filter(
|
||||
is_archive=True
|
||||
).order_by("-created_at")
|
||||
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@extend_schema(
|
||||
tags=["EvaluationRequest"],
|
||||
summary="Archive or unarchive evaluation request",
|
||||
description="""
|
||||
Update archive status for evaluation request.
|
||||
|
||||
- is_archive=true → archive
|
||||
- is_archive=false → remove from archive
|
||||
""",
|
||||
request=ArchiveEvaluationrequestSerializer,
|
||||
responses={
|
||||
200: OpenApiResponse(
|
||||
description="Archive status updated successfully"
|
||||
),
|
||||
400: OpenApiResponse(
|
||||
description="Validation error"
|
||||
),
|
||||
404: OpenApiResponse(
|
||||
description="Evaluation request not found"
|
||||
),
|
||||
},
|
||||
)
|
||||
def post(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
validated_data = serializer.validated_data
|
||||
|
||||
obj = get_object_or_404(
|
||||
EvaluationrequestModel,
|
||||
id=validated_data["id"]
|
||||
)
|
||||
|
||||
obj.is_archive = validated_data["is_archive"]
|
||||
obj.save(update_fields=["is_archive"])
|
||||
@extend_schema(tags=["EvaluationRequest"])
|
||||
class RequestEvaluationArchiveAPIView(views.APIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request, pk):
|
||||
req_evaluation = get_object_or_404(EvaluationrequestModel, pk=pk)
|
||||
req_evaluation.is_archive = request.data["is_archived"]
|
||||
req_evaluation.save()
|
||||
return Response(
|
||||
{
|
||||
"success": True,
|
||||
"message": "Archive status updated successfully"
|
||||
"status": req_evaluation.status,
|
||||
"id": req_evaluation.pk
|
||||
},
|
||||
status=status.HTTP_200_OK
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# rest framework
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.generics import GenericAPIView
|
||||
|
||||
from drf_spectacular.utils import (
|
||||
extend_schema,
|
||||
OpenApiExample,
|
||||
)
|
||||
# swagger
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
# core apps
|
||||
from core.services.tech_passport import TechPassportService
|
||||
from ..serializers import TechPassportSerializer
|
||||
from core.apps.evaluation.serializers import TechPassportSerializer
|
||||
|
||||
|
||||
class TechPassportAPIView(GenericAPIView):
|
||||
@@ -18,12 +18,6 @@ class TechPassportAPIView(GenericAPIView):
|
||||
|
||||
@extend_schema(
|
||||
tags=["Tech Passport"],
|
||||
summary="Get vehicle information by technical passport",
|
||||
description=(
|
||||
"This endpoint retrieves vehicle information using "
|
||||
"autonumber, technical passport number, and technical passport series "
|
||||
"via Gross Insurance API integration."
|
||||
),
|
||||
request=TechPassportSerializer,
|
||||
responses={
|
||||
200: dict,
|
||||
@@ -45,15 +39,13 @@ class TechPassportAPIView(GenericAPIView):
|
||||
response_data = result["data"]
|
||||
status_code = result["status_code"]
|
||||
|
||||
# success bo‘lsa faqat data ichidagi data qaytariladi
|
||||
if status_code == 200:
|
||||
return Response(
|
||||
response_data.get("data", {}),
|
||||
status=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
# error bo‘lsa original response qaytariladi
|
||||
return Response(
|
||||
response_data,
|
||||
status=status_code
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
# django core
|
||||
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.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
# core apps
|
||||
from core.apps.evaluation.models import ValuationModel
|
||||
from core.apps.evaluation.serializers.valuation import (
|
||||
CreateValuationSerializer,
|
||||
ListValuationSerializer,
|
||||
RetrieveValuationSerializer,
|
||||
)
|
||||
from core.apps.evaluation.serializers import valuation as serializers
|
||||
|
||||
|
||||
@extend_schema(tags=["Valuation"])
|
||||
class ValuationView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = ValuationModel.objects.all()
|
||||
serializer_class = ListValuationSerializer
|
||||
serializer_class = serializers.ListValuationSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListValuationSerializer,
|
||||
"retrieve": RetrieveValuationSerializer,
|
||||
"create": CreateValuationSerializer,
|
||||
"list": serializers.ListValuationSerializer,
|
||||
"retrieve": serializers.RetrieveValuationSerializer,
|
||||
"create": serializers.CreateValuationSerializer,
|
||||
}
|
||||
|
||||
@@ -1,14 +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.vehicle import (
|
||||
CreateVehicleSerializer,
|
||||
ListVehicleSerializer,
|
||||
RetrieveVehicleSerializer,
|
||||
)
|
||||
from core.apps.evaluation.serializers import vehicle as serialziers, VehicleApplicationSerializer
|
||||
from core.utils.generation_pdf import PDFService
|
||||
|
||||
|
||||
@extend_schema(tags=["Vehicle"])
|
||||
@@ -16,12 +21,28 @@ class VehicleView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = VehicleModel.objects.select_related(
|
||||
"brand", "model", "color", "fuel_type", "body_type", "position",
|
||||
).all()
|
||||
serializer_class = ListVehicleSerializer
|
||||
serializer_class = serialziers.ListVehicleSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListVehicleSerializer,
|
||||
"retrieve": RetrieveVehicleSerializer,
|
||||
"create": CreateVehicleSerializer,
|
||||
"list": serialziers.ListVehicleSerializer,
|
||||
"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')
|
||||
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime
|
||||
import grpc
|
||||
from google.protobuf.timestamp_pb2 import Timestamp
|
||||
from datetime import datetime
|
||||
@@ -15,13 +16,17 @@ def get_auto_avg_cost(
|
||||
distance_covered,
|
||||
color
|
||||
):
|
||||
# url = f"{env.str('RPC_IP')}:{env.str('RPC_PORT')}"
|
||||
# channel = grpc.insecure_channel(url)
|
||||
channel = grpc.insecure_channel("192.168.1.120:50051")
|
||||
channel = grpc.insecure_channel("94.230.232.47:50051")
|
||||
stub = auto_pb2_grpc.AutoAvgCostServiceStub(channel)
|
||||
|
||||
|
||||
ts = Timestamp()
|
||||
ts.FromDatetime(manufacture_date)
|
||||
if isinstance(manufacture_date, datetime):
|
||||
dt = manufacture_date
|
||||
else:
|
||||
dt = datetime.combine(manufacture_date, datetime.min.time())
|
||||
|
||||
ts.FromDatetime(dt)
|
||||
|
||||
response = stub.AutoAvgCost(auto_pb2.AutoAvgCostRequest(
|
||||
brand=brand,
|
||||
@@ -32,6 +37,7 @@ def get_auto_avg_cost(
|
||||
distance_covered=distance_covered,
|
||||
color=color,
|
||||
))
|
||||
print("manufacture_date:", manufacture_date, type(manufacture_date))
|
||||
|
||||
return {
|
||||
"avg_cost": response.avg_cost,
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
186
requirements.txt
186
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
|
||||
@@ -49,4 +149,6 @@ boto3
|
||||
|
||||
grpcio>=1.62.0
|
||||
grpcio-tools>=1.62.0
|
||||
protobuf>=4.25.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