diff --git a/core/apps/evaluation/admin/auto.py b/core/apps/evaluation/admin/auto.py index e6aa6f1..df61c71 100644 --- a/core/apps/evaluation/admin/auto.py +++ b/core/apps/evaluation/admin/auto.py @@ -55,7 +55,7 @@ class AutoEvaluationAdmin(ModelAdmin): "fields": ( "tex_passport_serie_num", ("tex_passport_gived_date", "tex_passport_gived_location"), - ("car_type", "car_wheel"), + ("car_wheel",), ("car_brand", "car_model"), ("car_number", "manufacture_year"), ("car_dvigatel_number", "car_color"), diff --git a/core/apps/evaluation/choices/auto.py b/core/apps/evaluation/choices/auto.py index b9af220..0e2574f 100644 --- a/core/apps/evaluation/choices/auto.py +++ b/core/apps/evaluation/choices/auto.py @@ -6,6 +6,8 @@ class AutoObjectType(models.TextChoices): LIGHTWEIGHT_AUTO = "lightweight_auto", _("Yengil automobil") TRUCK_CAR = "truck_car", _("Yuk automobil") SPECIAL_TECH = "special_tech", _("Maxsus texnika") + BUS = "bus", _("Avtobus") + MOTO = "moto", _("Mototsikl") class AutoEvaluationStatus(models.TextChoices): diff --git a/core/apps/evaluation/migrations/0044_remove_autoevaluationmodel_car_type_and_more.py b/core/apps/evaluation/migrations/0044_remove_autoevaluationmodel_car_type_and_more.py new file mode 100644 index 0000000..4e957d0 --- /dev/null +++ b/core/apps/evaluation/migrations/0044_remove_autoevaluationmodel_car_type_and_more.py @@ -0,0 +1,167 @@ +# Generated by Django 5.2.7 on 2026-05-05 12:06 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("evaluation", "0043_mechanicautoevaluationmodel_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="autoevaluationmodel", + name="car_type", + ), + migrations.RemoveField( + model_name="autoevaluationmodel", + name="tex_passport_file", + ), + migrations.AddField( + model_name="autoevaluationmodel", + name="assessment_task_type", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="evaluation_auto_assessment_task_type", + to="evaluation.referenceitemmodel", + verbose_name="assessment task type", + ), + ), + migrations.AddField( + model_name="autoevaluationmodel", + name="body_type", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="evaluation_auto_body_type", + to="evaluation.referenceitemmodel", + verbose_name="body type", + ), + ), + migrations.AddField( + model_name="autoevaluationmodel", + name="car_position", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="evaluation_auto_car_position", + to="evaluation.referenceitemmodel", + verbose_name="car position", + ), + ), + migrations.AddField( + model_name="autoevaluationmodel", + name="distance_covered", + field=models.PositiveIntegerField(blank=True, null=True, verbose_name="distance covered"), + ), + migrations.AddField( + model_name="autoevaluationmodel", + name="fuel_type", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="evaluation_auto_fuel_type", + to="evaluation.referenceitemmodel", + verbose_name="fuel type", + ), + ), + migrations.AddField( + model_name="autoevaluationmodel", + name="object_owner_residence", + field=models.CharField(blank=True, max_length=255, null=True, verbose_name="object owner residence"), + ), + migrations.AddField( + model_name="autoevaluationmodel", + name="state_car", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="evaluation_auto_state_car", + to="evaluation.referenceitemmodel", + verbose_name="state car", + ), + ), + migrations.AlterField( + model_name="autoevaluationmodel", + name="object_type", + field=models.CharField( + blank=True, + choices=[ + ("lightweight_auto", "Yengil automobil"), + ("truck_car", "Yuk automobil"), + ("special_tech", "Maxsus texnika"), + ("bus", "Avtobus"), + ("moto", "Mototsikl"), + ], + max_length=50, + null=True, + verbose_name="object type", + ), + ), + migrations.AlterField( + model_name="bonuscategory", + name="category", + field=models.CharField( + choices=[ + ("lightweight_auto", "Yengil automobil"), + ("truck_car", "Yuk automobil"), + ("special_tech", "Maxsus texnika"), + ("bus", "Avtobus"), + ("moto", "Mototsikl"), + ], + max_length=50, + ), + ), + migrations.AlterField( + model_name="mechanicautoevaluationmodel", + name="object_type", + field=models.CharField( + blank=True, + choices=[ + ("lightweight_auto", "Yengil automobil"), + ("truck_car", "Yuk automobil"), + ("special_tech", "Maxsus texnika"), + ("bus", "Avtobus"), + ("moto", "Mototsikl"), + ], + max_length=50, + null=True, + verbose_name="object type", + ), + ), + migrations.CreateModel( + name="AutoEvaluationTexPassportFile", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "file", + models.FileField( + upload_to="auto_evaluation/tech_passports/%Y/%m/", verbose_name="tech passport file" + ), + ), + ( + "auto_evaluation", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="tex_passport_files", + to="evaluation.autoevaluationmodel", + ), + ), + ], + options={ + "verbose_name": "Auto Evaluation Tex Passport File", + "verbose_name_plural": "Auto Evaluation Tex Passport Files", + "db_table": "AutoEvaluationTexPassportFile", + }, + ), + ] diff --git a/core/apps/evaluation/models/auto.py b/core/apps/evaluation/models/auto.py index 9a406f0..b52a6a9 100644 --- a/core/apps/evaluation/models/auto.py +++ b/core/apps/evaluation/models/auto.py @@ -4,7 +4,6 @@ from django_core.models import AbstractBaseModel from model_bakery import baker from core.apps.evaluation.choices.auto import ( - AutoCarType, AutoCarWheel, AutoEvaluationStatus, AutoObjectType, @@ -54,12 +53,57 @@ class AutoEvaluationModel(AbstractBaseModel): null=True, ) - tex_passport_file = models.FileField( - verbose_name=_("tech passport file"), - upload_to="quick_evaluation/tech_passports/%Y/%m/", + distance_covered = models.PositiveIntegerField( + verbose_name=_("distance covered"), blank=True, null=True, ) + object_owner_residence = models.CharField( + verbose_name=_("object owner residence"), + max_length=255, + blank=True, + null=True, + ) + car_position = models.ForeignKey( + 'evaluation.ReferenceitemModel', + verbose_name=_("car position"), + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name='evaluation_auto_car_position', + ) + body_type = models.ForeignKey( + 'evaluation.ReferenceitemModel', + verbose_name=_("body type"), + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name='evaluation_auto_body_type', + ) + fuel_type = models.ForeignKey( + 'evaluation.ReferenceitemModel', + verbose_name=_("fuel type"), + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name='evaluation_auto_fuel_type', + ) + state_car = models.ForeignKey( + 'evaluation.ReferenceitemModel', + verbose_name=_("state car"), + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name='evaluation_auto_state_car', + ) + assessment_task_type = models.ForeignKey( + 'evaluation.ReferenceitemModel', + verbose_name=_("assessment task type"), + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name='evaluation_auto_assessment_task_type', + ) # ── Step 1 — Umumiy ma'lumotlar ────────────────────────────────── registration_number = models.CharField( @@ -174,12 +218,6 @@ class AutoEvaluationModel(AbstractBaseModel): blank=True, null=True, ) - car_type = models.IntegerField( - verbose_name=_("car type"), - choices=AutoCarType.choices, - blank=True, - null=True, - ) car_wheel = models.IntegerField( verbose_name=_("car wheel"), choices=AutoCarWheel.choices, @@ -252,3 +290,27 @@ class AutoEvaluationModel(AbstractBaseModel): db_table = "AutoEvaluation" verbose_name = _("Auto Evaluation") verbose_name_plural = _("Auto Evaluations") + + +class AutoEvaluationTexPassportFile(AbstractBaseModel): + auto_evaluation = models.ForeignKey( + AutoEvaluationModel, + on_delete=models.CASCADE, + related_name="tex_passport_files", + ) + file = models.FileField( + verbose_name=_("tech passport file"), + upload_to="auto_evaluation/tech_passports/%Y/%m/", + ) + + def __str__(self): + return f"Tex passport file for AutoEvaluation #{self.auto_evaluation_id}" + + @classmethod + def _baker(cls): + return baker.make(cls) + + class Meta: + db_table = "AutoEvaluationTexPassportFile" + verbose_name = _("Auto Evaluation Tex Passport File") + verbose_name_plural = _("Auto Evaluation Tex Passport Files") diff --git a/core/apps/evaluation/serializers/auto/AutoEvaluation.py b/core/apps/evaluation/serializers/auto/AutoEvaluation.py index 16e91cf..190810a 100644 --- a/core/apps/evaluation/serializers/auto/AutoEvaluation.py +++ b/core/apps/evaluation/serializers/auto/AutoEvaluation.py @@ -3,13 +3,36 @@ import re from django.contrib.auth import get_user_model from rest_framework import serializers +from django.db import transaction + from core.apps.evaluation.choices.request import RequestStatus -from core.apps.evaluation.models import AutoEvaluationModel, ReferenceitemModel, EvaluationrequestModel +from core.apps.evaluation.models import ( + AutoEvaluationModel, + AutoEvaluationTexPassportFile, + ReferenceitemModel, + EvaluationrequestModel, +) from core.apps.evaluation.serializers.reference import ListReferenceitemSerializer User = get_user_model() +class AutoEvaluationTexPassportFileSerializer(serializers.ModelSerializer): + file = serializers.SerializerMethodField() + + class Meta: + model = AutoEvaluationTexPassportFile + fields = ["id", "file"] + + def get_file(self, obj): + request = self.context.get("request") + if not obj.file: + return None + if request: + return request.build_absolute_uri(obj.file.url) + return obj.file.url + + class BaseAutoevaluationSerializer(serializers.ModelSerializer): status_display = serializers.CharField(source="get_status_display", read_only=True) object_type_display = serializers.CharField(source="get_object_type_display", read_only=True, default=None) @@ -17,6 +40,12 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer): default=None) rate_type = ListReferenceitemSerializer(read_only=True) value_determined = ListReferenceitemSerializer(read_only=True) + car_position = ListReferenceitemSerializer(read_only=True) + body_type = ListReferenceitemSerializer(read_only=True) + fuel_type = ListReferenceitemSerializer(read_only=True) + state_car = ListReferenceitemSerializer(read_only=True) + assessment_task_type = ListReferenceitemSerializer(read_only=True) + tex_passport_files = AutoEvaluationTexPassportFileSerializer(many=True, read_only=True) user = serializers.SerializerMethodField(method_name="get_user", read_only=True) class Meta: @@ -32,7 +61,9 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer): "object_owner_individual_person_p_name", "object_owner_legal_entity", "object_owner_legal_inn", + "object_owner_residence", "tex_passport_serie_num", + "tex_passport_files", "rating_goal", "registration_number", "object_type", @@ -42,6 +73,12 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer): "car_number", "manufacture_year", "car_color", + "distance_covered", + "car_position", + "body_type", + "fuel_type", + "state_car", + "assessment_task_type", "status", "status_display", "created_at", @@ -68,7 +105,6 @@ class ListAutoevaluationSerializer(BaseAutoevaluationSerializer): class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer): - car_type_display = serializers.CharField(source="get_car_type_display", read_only=True, default=None) car_wheel_display = serializers.CharField(source="get_car_wheel_display", read_only=True, default=None) class Meta(BaseAutoevaluationSerializer.Meta): @@ -89,11 +125,8 @@ class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer): "object_owner_legal_inn", # Step 4 "tex_passport_serie_num", - "tex_passport_file", "tex_passport_gived_date", "tex_passport_gived_location", - "car_type", - "car_type_display", "car_wheel", "car_wheel_display", "car_dvigatel_number", @@ -106,11 +139,6 @@ class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer): class UpdateAutoevaluationSerializer(serializers.ModelSerializer): - value_determined = serializers.PrimaryKeyRelatedField( - queryset=ReferenceitemModel.objects.all(), - required=False, - allow_null=True, - ) value_determined = serializers.PrimaryKeyRelatedField( queryset=ReferenceitemModel.objects.all(), required=False, @@ -121,6 +149,36 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer): required=False, allow_null=True, ) + car_position = serializers.PrimaryKeyRelatedField( + queryset=ReferenceitemModel.objects.all(), + required=False, + allow_null=True, + ) + body_type = serializers.PrimaryKeyRelatedField( + queryset=ReferenceitemModel.objects.all(), + required=False, + allow_null=True, + ) + fuel_type = serializers.PrimaryKeyRelatedField( + queryset=ReferenceitemModel.objects.all(), + required=False, + allow_null=True, + ) + state_car = serializers.PrimaryKeyRelatedField( + queryset=ReferenceitemModel.objects.all(), + required=False, + allow_null=True, + ) + assessment_task_type = serializers.PrimaryKeyRelatedField( + queryset=ReferenceitemModel.objects.all(), + required=False, + allow_null=True, + ) + tex_passport_files = serializers.ListField( + child=serializers.FileField(), + required=False, + write_only=True, + ) class Meta: model = AutoEvaluationModel @@ -140,14 +198,15 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer): "object_owner_individual_person_passport_num", "object_owner_legal_entity", "object_owner_legal_inn", + "object_owner_residence", "value_determined", "rate_type", + "assessment_task_type", # Step 4 - "tex_passport_file", + "tex_passport_files", "tex_passport_serie_num", "tex_passport_gived_date", "tex_passport_gived_location", - "car_type", "car_wheel", "car_brand", "car_model", @@ -155,6 +214,11 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer): "manufacture_year", "car_dvigatel_number", "car_color", + "distance_covered", + "car_position", + "body_type", + "fuel_type", + "state_car", ] def validate_tex_passport_serie_num(self, value): @@ -199,13 +263,23 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer): return attrs + def update(self, instance, validated_data): + files = validated_data.pop("tex_passport_files", None) + with transaction.atomic(): + for attr, value in validated_data.items(): + setattr(instance, attr, value) + instance.save() + if files is not None: + AutoEvaluationTexPassportFile.objects.bulk_create([ + AutoEvaluationTexPassportFile(auto_evaluation=instance, file=f) for f in files + ]) + return instance + + def to_representation(self, instance): + return RetrieveAutoevaluationSerializer(instance, context=self.context).data + class CreateAutoevaluationSerializer(serializers.ModelSerializer): - value_determined = serializers.PrimaryKeyRelatedField( - queryset=ReferenceitemModel.objects.all(), - required=False, - allow_null=True, - ) value_determined = serializers.PrimaryKeyRelatedField( queryset=ReferenceitemModel.objects.all(), required=False, @@ -216,11 +290,41 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer): required=False, allow_null=True, ) + car_position = serializers.PrimaryKeyRelatedField( + queryset=ReferenceitemModel.objects.all(), + required=False, + allow_null=True, + ) + body_type = serializers.PrimaryKeyRelatedField( + queryset=ReferenceitemModel.objects.all(), + required=False, + allow_null=True, + ) + fuel_type = serializers.PrimaryKeyRelatedField( + queryset=ReferenceitemModel.objects.all(), + required=False, + allow_null=True, + ) + state_car = serializers.PrimaryKeyRelatedField( + queryset=ReferenceitemModel.objects.all(), + required=False, + allow_null=True, + ) + assessment_task_type = serializers.PrimaryKeyRelatedField( + queryset=ReferenceitemModel.objects.all(), + required=False, + allow_null=True, + ) evaluation_request = serializers.PrimaryKeyRelatedField( queryset=EvaluationrequestModel.objects.all(), required=False, allow_null=True, ) + tex_passport_files = serializers.ListField( + child=serializers.FileField(), + required=False, + write_only=True, + ) class Meta: model = AutoEvaluationModel @@ -241,14 +345,15 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer): "object_owner_individual_person_passport_num", "object_owner_legal_entity", "object_owner_legal_inn", + "object_owner_residence", "value_determined", "rate_type", + "assessment_task_type", # Step 4 "tex_passport_serie_num", - "tex_passport_file", + "tex_passport_files", "tex_passport_gived_date", "tex_passport_gived_location", - "car_type", "car_wheel", "car_brand", "car_model", @@ -256,6 +361,11 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer): "manufacture_year", "car_dvigatel_number", "car_color", + "distance_covered", + "car_position", + "body_type", + "fuel_type", + "state_car", ] def validate_tex_passport_serie_num(self, value): @@ -300,13 +410,23 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer): return attrs def create(self, validated_data): + files = validated_data.pop("tex_passport_files", []) user = self.context.get('request').user validated_data['user'] = user evaluation_req = validated_data.get("evaluation_request") if evaluation_req: evaluation_req.status = RequestStatus.IN_PROGRESS evaluation_req.save() - return super().create(validated_data) + with transaction.atomic(): + instance = super().create(validated_data) + if files: + AutoEvaluationTexPassportFile.objects.bulk_create([ + AutoEvaluationTexPassportFile(auto_evaluation=instance, file=f) for f in files + ]) + return instance + + def to_representation(self, instance): + return RetrieveAutoevaluationSerializer(instance, context=self.context).data class AutoEvaluationAppraisersSerializer(serializers.Serializer): @@ -343,8 +463,7 @@ class AutoEvaluationModelSerializer(serializers.ModelSerializer): class Meta: model = AutoEvaluationModel - fields = ("tex_passport_file", - + fields = ( "registration_number", "contract_date", "object_inspection_date", @@ -359,13 +478,14 @@ class AutoEvaluationModelSerializer(serializers.ModelSerializer): "object_owner_individual_person_passport_num", "object_owner_legal_entity", "object_owner_legal_inn", + "object_owner_residence", "value_determined", "rate_type", + "assessment_task_type", "tex_passport_serie_num", "tex_passport_gived_date", "tex_passport_gived_location", - "car_type", "car_wheel", "car_brand", "car_model", @@ -373,6 +493,11 @@ class AutoEvaluationModelSerializer(serializers.ModelSerializer): "manufacture_year", "car_dvigatel_number", "car_color", + "distance_covered", + "car_position", + "body_type", + "fuel_type", + "state_car", "rating_goal", "status", diff --git a/task.txt b/task.txt index 350f4b8..f9c768b 100644 --- a/task.txt +++ b/task.txt @@ -1,11 +1,13 @@ -Mexanik avto baholash yaratish'ga "user" field qo'shish kerak +/v1/auto-evaluation/ field qo'shish va o'zgartirish kerak -API: /api/v1/mechanic-auto-evaluation/ -Quyidagilar bo’lsin: - -1. List da ushbu field kelishi kerak. id emas object kelsin - -2. details bo’yicha get qilinganida ham kelsin. id emas object kelsin - -3. edit qilishda ham ushbu field ni yanlash imkoni bo’lsin +object_type => Bu hozirda select. O’shanga value qo’shish kerak: bus, moto +car_position => Qo’shish kerak. Bu hozirda select. /api/v1/reference-item/ api’dan value yuboraman +distance_covered => Qo’shish kerak. Number. Bosib o’tilgan masofasi +body_type => Qo’shish kerak. Bu hozirda select. /api/v1/reference-item/ api’dan value yuboraman +fuel_type => Qo’shish kerak. Bu hozirda select. /api/v1/reference-item/ api’dan value yuboraman +state_car => Qo’shish kerak. Bu hozirda select. /api/v1/reference-item/ api’dan value yuboraman +car_type => manashu field ni olib tashlash kerak +tex_passport_file => multiple qilish kerak +assessment_task_type => Baholash vazifasi. Qo’shish kerak. Select bo’ladi. /api/v1/reference-item/ api’dan value yuboraman +object_owner_residence => Obyekt egasi yashash joyi. Qo’shish kerak. string bo’ladi. \ No newline at end of file