5 Commits

Author SHA1 Message Date
Shaxobff
1ff23af8bf update 2026-05-01 17:15:01 +05:00
Shaxobff
feecb580c1 update 2026-05-01 16:54:38 +05:00
github-actions[bot]
f53125cfdc 🔄 Update image to 147 [CI SKIP] 2026-04-30 11:05:42 +00:00
65ab51e652 Merge pull request 'update' (#128) from shaxob into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m11s
Reviewed-on: #128
2026-04-30 11:03:51 +00:00
Shaxobff
c29546a04b update 2026-04-30 11:11:12 +05:00
21 changed files with 387 additions and 22 deletions

View File

@@ -13,7 +13,7 @@ from config.env import env
def home(request):
return HttpResponse("OK: #3781ce29e5447f1473964c4c47fbdef2a38c6751")
return HttpResponse("OK: #65ab51e65224a92a4b6d488d3e8f9b21d3256876")
urlpatterns = [

View 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

View File

@@ -27,7 +27,7 @@ urlpatterns = [
path("", include(router.urls)),
path("auth/token/", jwt_views.TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("auth/token/verify/", jwt_views.TokenVerifyView.as_view(), name="token_verify"),
path("auth/token/refresh/",jwt_views.TokenRefreshView.as_view()),
path("auth/token/refresh/", jwt_views.TokenRefreshView.as_view()),
path("user/list/", UserListApiView.as_view(), name="user-list"),
path("admin-user/list/", AdminUserListApiView.as_view(), name="admin-user-list"),
path("admin/create/", AdminCreateAPIView.as_view(), name="user-create"),

View File

@@ -106,6 +106,7 @@ class UserDetailAPIView(generics.RetrieveAPIView):
class AdminPermissionsAPIView(generics.GenericAPIView):
permission_classes = [IsAuthenticated]
queryset = User.objects.all()
def get(self, request):
if request.user.role.name != RoleChoice.ADMIN:

View File

@@ -0,0 +1,8 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class EvaluationCategory(models.TextChoices):
AUTO = "auto_transport", _("Avtotransport")
REAL_ESTATE = "real estate", _("ko'chmas mulk")
EQUIPMENT = "equipment", _("uskuna va jihozlar")

View 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,
},
),
]

View File

@@ -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',
),
]

View File

@@ -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',
),
]

View File

@@ -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
@@ -244,8 +241,6 @@ class AutoEvaluationModel(AbstractBaseModel):
default=False,
)
def __str__(self):
return f"Auto Evaluation {self.registration_number or self.pk}"

View 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.bonus import EvaluationCategory
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=EvaluationCategory.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")

View File

@@ -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")

View File

@@ -321,6 +321,7 @@ class AutoEvaluationAppraisersSerializer(serializers.Serializer):
data['users'] = users
return data
class AutoEvaluationSerializer(serializers.Serializer):
brand = serializers.CharField()
brand_model = serializers.CharField()
@@ -331,7 +332,58 @@ class AutoEvaluationSerializer(serializers.Serializer):
fuel_type = serializers.CharField()
mileage = serializers.CharField()
class AutoEvaluationModelSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True)
appraisers = serializers.PrimaryKeyRelatedField(
many=True,
queryset=User.objects.all(),
required=False
)
class Meta:
model = AutoEvaluationModel
fields = "__all__"
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",
)

View 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

View File

@@ -131,4 +131,39 @@ class CreateQuickevaluationSerializer(serializers.ModelSerializer):
class QuickEvaluationModelSerializer(serializers.ModelSerializer):
class Meta:
model = QuickEvaluationModel
fields = '__all__'
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",
)

View File

@@ -27,6 +27,9 @@ 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)),

View File

@@ -15,3 +15,4 @@ from .didox import * # noqa
from .tech_passport import * # noqa
from .certificate import * # noqa
from .avg_cost import *
from .bonus import *

View File

@@ -11,7 +11,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from core.apps.accounts.choices import RoleChoice
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
@@ -177,13 +177,14 @@ class AutoEvaluationArchiveAPIView(APIView):
status=200
)
@extend_schema(tags=["AutoEvaluation"])
class AdminEvaluationsAPIView(generics.GenericAPIView):
permission_classes = [IsAuthenticated]
permission_classes = [IsAuthenticated, IsAdminRole]
queryset = AutoEvaluationModel.objects.all()
serializer_class = AutoEvaluationModel
def get(self, request):
if request.user.role != RoleChoice.ADMIN:
return Response({'detail': 'Forbidden'}, status=403)
auto_eval = AutoEvaluationModel.objects.filter(
created_by=self.request.user
).select_related('appraisers').distinct()

View 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],
}

View File

@@ -16,7 +16,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from core.apps.accounts.choices import RoleChoice
from core.apps.accounts.permissions import IsAdminRole
# core apps
from core.apps.evaluation.filters.quick import QuickevaluationFilter
from core.apps.evaluation.models import QuickEvaluationModel
@@ -88,11 +88,11 @@ class QuickEvaluationArchivedListAPIView(ListAPIView):
@extend_schema(tags=["QuickEvaluation"])
class AdminQuickEvalAPIView(generics.GenericAPIView):
permission_classes = [IsAuthenticated]
permission_classes = [IsAuthenticated, IsAdminRole]
queryset = QuickEvaluationModel.objects.all()
serializer_class = QuickEvaluationModelSerializer
def get(self, request):
if request.user.role != RoleChoice.ADMIN:
return Response({'detail': 'Forbidden'}, status=403)
quick_eval = QuickEvaluationModel.objects.filter(
created_by=self.request.user
).select_related('created_by').distinct()

View File

@@ -84,7 +84,7 @@ services:
max-file: "5"
web:
image: husanjon/sifatbaho:145
image: husanjon/sifatbaho:147
env_file:
- .env
environment:
@@ -129,7 +129,7 @@ services:
max-file: "5"
celery:
image: husanjon/sifatbaho:145
image: husanjon/sifatbaho:147
env_file:
- .env
environment: