9 Commits

Author SHA1 Message Date
github-actions[bot]
28fdf82c08 🔄 Update image to 49 [CI SKIP] 2026-03-17 14:38:21 +00:00
a06cf173b6 Merge pull request 'Baholash maqsadi uchun api chiqarildi' (#30) from feat/valuation-purpose-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m59s
Reviewed-on: #30
2026-03-17 14:36:47 +00:00
Husanjonazamov
9593b02c2d Baholash maqsadi uchun api chiqarildi 2026-03-17 19:36:21 +05:00
386e9ead89 Merge pull request 'user request yuborishda api fix qilindi' (#29) from fix/user-request into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m34s
Reviewed-on: #29
2026-03-17 14:07:08 +00:00
Husanjonazamov
9a13a2b4f0 user request yuborishda api fix qilindi 2026-03-17 19:06:38 +05:00
github-actions[bot]
9b57611b62 🔄 Update image to 47 [CI SKIP] 2026-03-17 13:27:52 +00:00
4479a0c1a5 Merge pull request 'use modeliga avatar fieldni qoshildi' (#28) from feat/user into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m34s
Reviewed-on: #28
2026-03-17 13:25:41 +00:00
Husanjonazamov
0d574a92a9 use modeliga avatar fieldni qoshildi 2026-03-17 18:25:20 +05:00
github-actions[bot]
397f239b1d 🔄 Update image to 46 [CI SKIP] 2026-03-10 09:06:20 +00:00
15 changed files with 139 additions and 17 deletions

View File

@@ -13,7 +13,7 @@ from config.env import env
def home(request): def home(request):
return HttpResponse("OK: #5bb3dcd432ba0c045e6f2ef91c23bd4a16a1d256") return HttpResponse("OK: #a06cf173b69ce36a54b5e1cacd87c2f1b8edf6e9")
urlpatterns = [ urlpatterns = [

View File

@@ -1,5 +1,6 @@
from django.contrib.auth import admin from django.contrib.auth import admin
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils.safestring import mark_safe
from unfold.admin import ModelAdmin from unfold.admin import ModelAdmin
from unfold.forms import AdminPasswordChangeForm # UserCreationForm, from unfold.forms import AdminPasswordChangeForm # UserCreationForm,
from unfold.forms import UserChangeForm from unfold.forms import UserChangeForm
@@ -10,6 +11,7 @@ class CustomUserAdmin(admin.UserAdmin, ModelAdmin):
# add_form = UserCreationForm # add_form = UserCreationForm
form = UserChangeForm form = UserChangeForm
list_display = ( list_display = (
"display_avatar",
"first_name", "first_name",
"last_name", "last_name",
"phone", "phone",
@@ -17,7 +19,7 @@ class CustomUserAdmin(admin.UserAdmin, ModelAdmin):
) )
search_fields = ("phone", "first_name", "last_name", "username") search_fields = ("phone", "first_name", "last_name", "username")
autocomplete_fields = ["groups", "user_permissions"] autocomplete_fields = ["groups", "user_permissions"]
fieldsets = ((None, {"fields": ("phone",)}),) + ( fieldsets = ((None, {"fields": ("phone", "avatar",)}),) + (
(None, {"fields": ("username", "password")}), (None, {"fields": ("username", "password")}),
(_("Personal info"), {"fields": ("first_name", "last_name", "email")}), (_("Personal info"), {"fields": ("first_name", "last_name", "email")}),
( (
@@ -36,6 +38,15 @@ class CustomUserAdmin(admin.UserAdmin, ModelAdmin):
(_("Important dates"), {"fields": ("last_login", "date_joined")}), (_("Important dates"), {"fields": ("last_login", "date_joined")}),
) )
def display_avatar(self, obj):
if obj.avatar:
return mark_safe(
f'<img src="{obj.avatar.url}" width="35" height="35" style="border-radius: 50%; object-fit: cover;" />'
)
return _("No Image")
display_avatar.short_description = _("Avatar")
class PermissionAdmin(ModelAdmin): class PermissionAdmin(ModelAdmin):
list_display = ("name",) list_display = ("name",)

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2026-03-17 12:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0002_alter_user_role'),
]
operations = [
migrations.AddField(
model_name='user',
name='avatar',
field=models.ImageField(blank=True, null=True, upload_to='avatars/'),
),
]

View File

@@ -16,6 +16,7 @@ class User(auth_models.AbstractUser):
choices=RoleChoice, choices=RoleChoice,
default=RoleChoice.USER, default=RoleChoice.USER,
) )
avatar = models.ImageField(upload_to="avatars/", null=True, blank=True)
USERNAME_FIELD = "phone" USERNAME_FIELD = "phone"
objects = UserManager() objects = UserManager()

View File

@@ -19,5 +19,6 @@ class UserUpdateSerializer(serializers.ModelSerializer):
model = get_user_model() model = get_user_model()
fields = [ fields = [
"first_name", "first_name",
"last_name" "last_name",
"avatar"
] ]

View File

@@ -6,7 +6,7 @@ from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_core import exceptions from django_core import exceptions
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
from rest_framework import status, throttling, request from rest_framework import status, throttling, request, parsers
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from rest_framework.viewsets import GenericViewSet from rest_framework.viewsets import GenericViewSet
@@ -160,6 +160,11 @@ class ResetPasswordView(BaseViewSetMixin, GenericViewSet, UserService):
@extend_schema(tags=["me"]) @extend_schema(tags=["me"])
class MeView(BaseViewSetMixin, GenericViewSet, UserService): class MeView(BaseViewSetMixin, GenericViewSet, UserService):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
parser_classes = (
parsers.MultiPartParser,
parsers.FormParser,
parsers.JSONParser,
)
def get_serializer_class(self): def get_serializer_class(self):
match self.action: match self.action:

View File

@@ -10,3 +10,4 @@ class ReferenceType(models.TextChoices):
BODY_TYPE = "body_type", _("Body type") BODY_TYPE = "body_type", _("Body type")
CAR_POSITION = "car_position", _("Car position") CAR_POSITION = "car_position", _("Car position")
STATE_CAR = "state_car", _("Car state") STATE_CAR = "state_car", _("Car state")
EVALUATION_PURPOSE = "evaluation_purpose", _("Evaluation purpose")

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.2.7 on 2026-03-17 13:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0018_evaluationrequestmodel'),
]
operations = [
migrations.AddField(
model_name='evaluationrequestmodel',
name='location_name',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='location name'),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='chassi',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='chassi'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2026-03-17 14:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0019_evaluationrequestmodel_location_name_and_more'),
]
operations = [
migrations.AlterField(
model_name='referenceitemmodel',
name='type',
field=models.CharField(choices=[('brand', 'Brand'), ('marka', 'Marka'), ('color', 'Color'), ('fuel_type', 'Fuel type'), ('body_type', 'Body type'), ('car_position', 'Car position'), ('state_car', 'Car state'), ('evaluation_purpose', 'Evaluation purpose')], max_length=50, verbose_name='type'),
),
]

View File

@@ -65,8 +65,9 @@ class EvaluationrequestModel(AbstractBaseModel):
blank=True, blank=True,
null=True, null=True,
) )
chassi = models.IntegerField( chassi = models.CharField(
verbose_name=_("chassi"), verbose_name=_("chassi"),
max_length=100,
blank=True, blank=True,
null=True, null=True,
) )
@@ -74,6 +75,12 @@ class EvaluationrequestModel(AbstractBaseModel):
verbose_name=_("need delivering"), verbose_name=_("need delivering"),
default=True, default=True,
) )
location_name = models.CharField(
verbose_name=_("location name"),
max_length=255,
blank=True,
null=True,
)
location_lat = models.DecimalField( location_lat = models.DecimalField(
verbose_name=_("location latitude"), verbose_name=_("location latitude"),
max_digits=9, max_digits=9,

View File

@@ -46,3 +46,12 @@ class CreateReferenceitemSerializer(BaseReferenceitemSerializer):
"order", "order",
"is_active", "is_active",
] ]
class EvaluationPurposeSerializer(serializers.ModelSerializer):
label = serializers.CharField(source="name")
value = serializers.IntegerField(source="id")
class Meta:
model = ReferenceitemModel
fields = ["label", "value"]

View File

@@ -17,6 +17,8 @@ class BaseEvaluationrequestSerializer(serializers.ModelSerializer):
) )
location = serializers.SerializerMethodField() location = serializers.SerializerMethodField()
location_name = serializers.CharField(required=False)
class Meta: class Meta:
model = EvaluationrequestModel model = EvaluationrequestModel
fields = [ fields = [
@@ -36,6 +38,7 @@ class BaseEvaluationrequestSerializer(serializers.ModelSerializer):
"chassi", "chassi",
"need_delivering", "need_delivering",
"location", "location",
"location_name",
"status", "status",
"status_display", "status_display",
"created_at", "created_at",
@@ -44,7 +47,11 @@ class BaseEvaluationrequestSerializer(serializers.ModelSerializer):
def get_location(self, obj): def get_location(self, obj):
if obj.location_lat is not None and obj.location_lng is not None: if obj.location_lat is not None and obj.location_lng is not None:
return {"lat": float(obj.location_lat), "lng": float(obj.location_lng)} return {
"lat": float(obj.location_lat),
"lng": float(obj.location_lng),
"name": obj.location_name
}
return None return None
@@ -59,9 +66,9 @@ class RetrieveEvaluationrequestSerializer(BaseEvaluationrequestSerializer):
class CreateEvaluationrequestSerializer(serializers.ModelSerializer): class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
location = serializers.DictField( location = serializers.DictField(required=False)
child=serializers.FloatField(), required=False # Frontend may send locationName
) locationName = serializers.CharField(write_only=True, required=False)
class Meta: class Meta:
model = EvaluationrequestModel model = EvaluationrequestModel
@@ -79,6 +86,7 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
"chassi", "chassi",
"need_delivering", "need_delivering",
"location", "location",
"locationName",
] ]
def validate_tex_passport(self, value): def validate_tex_passport(self, value):
@@ -104,7 +112,7 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
{"tex_passport": "rate_type 'auto' bo'lganda tex_passport majburiy."} {"tex_passport": "rate_type 'auto' bo'lganda tex_passport majburiy."}
) )
# worked_hours va chassi majburiy agar object_type=truck_car # worked_hours va chassi faqat yuk automobil uchun majburiy (truck_car)
if object_type == "truck_car": if object_type == "truck_car":
if attrs.get("worked_hours") is None: if attrs.get("worked_hours") is None:
raise serializers.ValidationError( raise serializers.ValidationError(
@@ -115,18 +123,20 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
{"chassi": "Yuk automobil uchun shassi majburiy."} {"chassi": "Yuk automobil uchun shassi majburiy."}
) )
# location majburiy agar need_delivering=true
if attrs.get("need_delivering", True) and not attrs.get("location"):
raise serializers.ValidationError(
{"location": "Yetkazish kerak bo'lganda manzil majburiy."}
)
return attrs return attrs
def create(self, validated_data): def create(self, validated_data):
location = validated_data.pop("location", None) location = validated_data.pop("location", None)
location_name = validated_data.pop("locationName", None)
if location: if location:
validated_data["location_lat"] = location.get("lat") validated_data["location_lat"] = location.get("lat")
validated_data["location_lng"] = location.get("lng") validated_data["location_lng"] = location.get("lng")
if not location_name:
location_name = location.get("name") or location.get("locationName")
if location_name:
validated_data["location_name"] = location_name
validated_data["user"] = self.context["request"].user validated_data["user"] = self.context["request"].user
return super().create(validated_data) return super().create(validated_data)

View File

@@ -14,9 +14,11 @@ from .views import (
ValuationDocumentView, ValuationDocumentView,
ValuationView, ValuationView,
VehicleView, VehicleView,
EvaluationPurposeView,
) )
router = DefaultRouter() router = DefaultRouter()
router.register("evaluation-purpose", EvaluationPurposeView, basename="evaluation-purpose")
router.register("evaluation-request", EvaluationrequestView, basename="evaluation-request") router.register("evaluation-request", EvaluationrequestView, basename="evaluation-request")
router.register("reference-item", ReferenceitemView, basename="reference-item") router.register("reference-item", ReferenceitemView, basename="reference-item")
router.register("valuation-document", ValuationDocumentView, basename="valuation-document") router.register("valuation-document", ValuationDocumentView, basename="valuation-document")

View File

@@ -11,9 +11,25 @@ from core.apps.evaluation.serializers.reference import (
CreateReferenceitemSerializer, CreateReferenceitemSerializer,
ListReferenceitemSerializer, ListReferenceitemSerializer,
RetrieveReferenceitemSerializer, RetrieveReferenceitemSerializer,
EvaluationPurposeSerializer,
) )
@extend_schema(tags=["EvaluationPurpose"])
class EvaluationPurposeView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ReferenceitemModel.objects.filter(
type="evaluation_purpose", is_active=True
).order_by("order", "name")
serializer_class = EvaluationPurposeSerializer
permission_classes = [AllowAny]
pagination_class = None
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
search_fields = ["name"]
ordering_fields = ["name", "order"]
ordering = ["order", "name"]
@extend_schema(tags=["ReferenceItem"]) @extend_schema(tags=["ReferenceItem"])
class ReferenceitemView(BaseViewSetMixin, ReadOnlyModelViewSet): class ReferenceitemView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ReferenceitemModel.objects.select_related("parent").filter(is_active=True) queryset = ReferenceitemModel.objects.select_related("parent").filter(is_active=True)

View File

@@ -84,7 +84,7 @@ services:
max-file: "5" max-file: "5"
web: web:
image: husanjon/sifatbaho:45 image: husanjon/sifatbaho:49
env_file: env_file:
- .env - .env
environment: environment: