feat: wire contract PDF context and align MechanicAuto with AutoEvaluation
- contract PDF: map report/customer/owner/contract from AutoEvaluationModel fields, accept inspection via POST serializer, fetch CBU.uz currency rates - MechanicAutoEvaluation: add distance_covered, object_owner_residence and car_position/body_type/fuel_type/state_car/assessment_task_type FKs; drop car_type and single tex_passport_file in favour of multi-file FK model Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("evaluation", "0045_alter_referenceitemmodel_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="mechanicautoevaluationmodel",
|
||||
name="car_type",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="mechanicautoevaluationmodel",
|
||||
name="tex_passport_file",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="mechanicautoevaluationmodel",
|
||||
name="distance_covered",
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name="distance covered"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="mechanicautoevaluationmodel",
|
||||
name="object_owner_residence",
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name="object owner residence"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="mechanicautoevaluationmodel",
|
||||
name="car_position",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_mechanic_auto_car_position",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="car position",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="mechanicautoevaluationmodel",
|
||||
name="body_type",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_mechanic_auto_body_type",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="body type",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="mechanicautoevaluationmodel",
|
||||
name="fuel_type",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_mechanic_auto_fuel_type",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="fuel type",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="mechanicautoevaluationmodel",
|
||||
name="state_car",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_mechanic_auto_state_car",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="state car",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="mechanicautoevaluationmodel",
|
||||
name="assessment_task_type",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="evaluation_mechanic_auto_assessment_task_type",
|
||||
to="evaluation.referenceitemmodel",
|
||||
verbose_name="assessment task type",
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="MechanicAutoEvaluationTexPassportFile",
|
||||
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="mechanic_evaluation/tech_passports/%Y/%m/",
|
||||
verbose_name="tech passport file",
|
||||
),
|
||||
),
|
||||
(
|
||||
"mechanic_auto_evaluation",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="tex_passport_files",
|
||||
to="evaluation.mechanicautoevaluationmodel",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Mechanic Auto Evaluation Tex Passport File",
|
||||
"verbose_name_plural": "Mechanic Auto Evaluation Tex Passport Files",
|
||||
"db_table": "MechanicAutoEvaluationTexPassportFile",
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -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,
|
||||
@@ -50,12 +49,57 @@ class MechanicAutoEvaluationModel(AbstractBaseModel):
|
||||
blank=True,
|
||||
)
|
||||
|
||||
tex_passport_file = models.FileField(
|
||||
verbose_name=_("tech passport file"),
|
||||
upload_to="mechanic_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_mechanic_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_mechanic_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_mechanic_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_mechanic_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_mechanic_auto_assessment_task_type',
|
||||
)
|
||||
|
||||
# ── Step 1 — Umumiy ma'lumotlar ──────────────────────────────────
|
||||
registration_number = models.CharField(
|
||||
@@ -170,12 +214,6 @@ class MechanicAutoEvaluationModel(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,
|
||||
@@ -248,3 +286,27 @@ class MechanicAutoEvaluationModel(AbstractBaseModel):
|
||||
db_table = "MechanicAutoEvaluation"
|
||||
verbose_name = _("Mechanic Auto Evaluation")
|
||||
verbose_name_plural = _("Mechanic Auto Evaluations")
|
||||
|
||||
|
||||
class MechanicAutoEvaluationTexPassportFile(AbstractBaseModel):
|
||||
mechanic_auto_evaluation = models.ForeignKey(
|
||||
MechanicAutoEvaluationModel,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="tex_passport_files",
|
||||
)
|
||||
file = models.FileField(
|
||||
verbose_name=_("tech passport file"),
|
||||
upload_to="mechanic_evaluation/tech_passports/%Y/%m/",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"Tex passport file for MechanicAutoEvaluation #{self.mechanic_auto_evaluation_id}"
|
||||
|
||||
@classmethod
|
||||
def _baker(cls):
|
||||
return baker.make(cls)
|
||||
|
||||
class Meta:
|
||||
db_table = "MechanicAutoEvaluationTexPassportFile"
|
||||
verbose_name = _("Mechanic Auto Evaluation Tex Passport File")
|
||||
verbose_name_plural = _("Mechanic Auto Evaluation Tex Passport Files")
|
||||
|
||||
@@ -3,9 +3,12 @@ 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 (
|
||||
MechanicAutoEvaluationModel,
|
||||
MechanicAutoEvaluationTexPassportFile,
|
||||
ReferenceitemModel,
|
||||
EvaluationrequestModel,
|
||||
)
|
||||
@@ -14,6 +17,22 @@ from core.apps.evaluation.serializers.reference import ListReferenceitemSerializ
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class MechanicAutoEvaluationTexPassportFileSerializer(serializers.ModelSerializer):
|
||||
file = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = MechanicAutoEvaluationTexPassportFile
|
||||
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 BaseMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
status_display = serializers.CharField(source="get_status_display", read_only=True)
|
||||
object_type_display = serializers.CharField(source="get_object_type_display", read_only=True, default=None)
|
||||
@@ -21,6 +40,12 @@ class BaseMechanicAutoevaluationSerializer(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 = MechanicAutoEvaluationTexPassportFileSerializer(many=True, read_only=True)
|
||||
user = serializers.SerializerMethodField(method_name="get_user", read_only=True)
|
||||
|
||||
class Meta:
|
||||
@@ -36,7 +61,9 @@ class BaseMechanicAutoevaluationSerializer(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",
|
||||
@@ -46,6 +73,12 @@ class BaseMechanicAutoevaluationSerializer(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",
|
||||
@@ -72,7 +105,6 @@ class ListMechanicAutoevaluationSerializer(BaseMechanicAutoevaluationSerializer)
|
||||
|
||||
|
||||
class RetrieveMechanicAutoevaluationSerializer(BaseMechanicAutoevaluationSerializer):
|
||||
car_type_display = serializers.CharField(source="get_car_type_display", read_only=True, default=None)
|
||||
car_wheel_display = serializers.CharField(source="get_car_wheel_display", read_only=True, default=None)
|
||||
|
||||
class Meta(BaseMechanicAutoevaluationSerializer.Meta):
|
||||
@@ -90,11 +122,8 @@ class RetrieveMechanicAutoevaluationSerializer(BaseMechanicAutoevaluationSeriali
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_file",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_type",
|
||||
"car_type_display",
|
||||
"car_wheel",
|
||||
"car_wheel_display",
|
||||
"car_dvigatel_number",
|
||||
@@ -116,11 +145,41 @@ class UpdateMechanicAutoevaluationSerializer(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,
|
||||
)
|
||||
user = serializers.PrimaryKeyRelatedField(
|
||||
queryset=User.objects.all(),
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
tex_passport_files = serializers.ListField(
|
||||
child=serializers.FileField(),
|
||||
required=False,
|
||||
write_only=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MechanicAutoEvaluationModel
|
||||
@@ -139,13 +198,14 @@ class UpdateMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
"object_owner_individual_person_passport_num",
|
||||
"object_owner_legal_entity",
|
||||
"object_owner_legal_inn",
|
||||
"object_owner_residence",
|
||||
"value_determined",
|
||||
"rate_type",
|
||||
"tex_passport_file",
|
||||
"assessment_task_type",
|
||||
"tex_passport_files",
|
||||
"tex_passport_serie_num",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_type",
|
||||
"car_wheel",
|
||||
"car_brand",
|
||||
"car_model",
|
||||
@@ -153,6 +213,11 @@ class UpdateMechanicAutoevaluationSerializer(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):
|
||||
@@ -197,6 +262,21 @@ class UpdateMechanicAutoevaluationSerializer(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:
|
||||
MechanicAutoEvaluationTexPassportFile.objects.bulk_create([
|
||||
MechanicAutoEvaluationTexPassportFile(mechanic_auto_evaluation=instance, file=f) for f in files
|
||||
])
|
||||
return instance
|
||||
|
||||
def to_representation(self, instance):
|
||||
return RetrieveMechanicAutoevaluationSerializer(instance, context=self.context).data
|
||||
|
||||
|
||||
class CreateMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
value_determined = serializers.PrimaryKeyRelatedField(
|
||||
@@ -209,6 +289,31 @@ class CreateMechanicAutoevaluationSerializer(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,
|
||||
@@ -219,6 +324,11 @@ class CreateMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
required=True,
|
||||
allow_null=False,
|
||||
)
|
||||
tex_passport_files = serializers.ListField(
|
||||
child=serializers.FileField(),
|
||||
required=False,
|
||||
write_only=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MechanicAutoEvaluationModel
|
||||
@@ -238,13 +348,14 @@ class CreateMechanicAutoevaluationSerializer(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_file",
|
||||
"tex_passport_files",
|
||||
"tex_passport_gived_date",
|
||||
"tex_passport_gived_location",
|
||||
"car_type",
|
||||
"car_wheel",
|
||||
"car_brand",
|
||||
"car_model",
|
||||
@@ -252,6 +363,11 @@ class CreateMechanicAutoevaluationSerializer(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):
|
||||
@@ -296,11 +412,21 @@ class CreateMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
||||
return attrs
|
||||
|
||||
def create(self, validated_data):
|
||||
files = validated_data.pop("tex_passport_files", [])
|
||||
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:
|
||||
MechanicAutoEvaluationTexPassportFile.objects.bulk_create([
|
||||
MechanicAutoEvaluationTexPassportFile(mechanic_auto_evaluation=instance, file=f) for f in files
|
||||
])
|
||||
return instance
|
||||
|
||||
def to_representation(self, instance):
|
||||
return RetrieveMechanicAutoevaluationSerializer(instance, context=self.context).data
|
||||
|
||||
|
||||
class MechanicAutoEvaluationAppraisersSerializer(serializers.Serializer):
|
||||
@@ -327,7 +453,6 @@ class MechanicAutoEvaluationModelSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = MechanicAutoEvaluationModel
|
||||
fields = (
|
||||
"tex_passport_file",
|
||||
"registration_number",
|
||||
"contract_date",
|
||||
"object_inspection_date",
|
||||
@@ -341,12 +466,13 @@ class MechanicAutoEvaluationModelSerializer(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",
|
||||
@@ -354,6 +480,11 @@ class MechanicAutoEvaluationModelSerializer(serializers.ModelSerializer):
|
||||
"manufacture_year",
|
||||
"car_dvigatel_number",
|
||||
"car_color",
|
||||
"distance_covered",
|
||||
"car_position",
|
||||
"body_type",
|
||||
"fuel_type",
|
||||
"state_car",
|
||||
"rating_goal",
|
||||
"status",
|
||||
"is_archived",
|
||||
|
||||
Reference in New Issue
Block a user