feat: add ReferenceItem model and update QuickEvaluation FKs

- Create ReferenceitemModel with type, name, parent (self FK), order, is_active fields
- Add ReferenceType choices: brand, marka, color, fuel_type, body_type, car_position, state_car
- Implement ReferenceItem API (list, retrieve) with filter by type/parent/is_active, search, ordering
- Add ReferenceItem admin with list_filter, search, inline editing
- Change QuickEvaluation FK fields from shared.OptionsModel to evaluation.ReferenceitemModel
- Update serializers and views to use .name instead of .key
- Add ReferenceItem to unfold admin navigation
This commit is contained in:
Husanjonazamov
2026-03-09 16:04:15 +05:00
parent fd2ecd953a
commit 3798037240
36 changed files with 744 additions and 106 deletions

View File

@@ -107,4 +107,15 @@ PAGES = [
},
],
},
{
"title": _("Ma'lumotnomalari"),
"separator": True,
"items": [
{
"title": _("Ma'lumotnomalar"),
"icon": "category",
"link": reverse_lazy("admin:evaluation_referenceitemmodel_changelist"),
},
],
},
]

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -10,10 +10,9 @@ class QuickEvaluationAdmin(ModelAdmin):
"id",
"created_by",
"brand",
"model",
"license_plate",
"manufacture_year",
"condition",
"marka",
"car_number",
"car_manufactured_date",
"estimated_price",
"status",
"car_type",
@@ -21,20 +20,14 @@ class QuickEvaluationAdmin(ModelAdmin):
"created_at",
)
list_filter = (
"fuel_type",
"body_type",
"condition",
"status",
"car_type",
"state_car",
)
search_fields = (
"brand",
"model",
"license_plate",
"car_number",
"vin_number",
"engine_number",
"tech_passport_number",
"tex_passport_serie_num",
)
readonly_fields = ("created_at", "updated_at")
autocomplete_fields = ("created_by",)
@@ -42,20 +35,25 @@ class QuickEvaluationAdmin(ModelAdmin):
("Foydalanuvchi", {
"fields": ("created_by",),
}),
("Tex passport", {
"fields": (
"tex_passport_serie_num",
("tech_passport_issued_date", "tech_passport_issued_place"),
"tex_passport_file",
),
}),
("Transport ma'lumotlari", {
"fields": (
"tech_passport_number",
"license_plate",
("brand", "model"),
("manufacture_year", "color"),
"car_number",
("brand", "marka"),
("car_manufactured_date", "color"),
("vin_number", "engine_number"),
"mileage",
("distance_covered", "car_position"),
),
}),
("Texnik holat", {
"fields": (
("fuel_type", "body_type"),
"condition",
("car_type", "state_car"),
),
}),

View File

@@ -0,0 +1,18 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import ReferenceitemModel
@admin.register(ReferenceitemModel)
class ReferenceitemAdmin(ModelAdmin):
list_display = ("id", "type", "name", "parent", "order", "is_active")
list_filter = ("type", "is_active")
search_fields = ("name",)
list_editable = ("order", "is_active")
autocomplete_fields = ("parent",)
fieldsets = (
(None, {
"fields": ("type", "name", "parent", "order", "is_active"),
}),
)

View File

@@ -15,9 +15,3 @@ class CarType(models.TextChoices):
TRUCK = "truck", _("Truck")
BUS = "bus", _("Bus")
MOTO = "moto", _("Moto")
class CarState(models.TextChoices):
GOOD = "good", _("Good")
SATISFACTORY = "satisfactory", _("Satisfactory")
BAD = "bad", _("Bad")

View File

@@ -0,0 +1,12 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class ReferenceType(models.TextChoices):
BRAND = "brand", _("Brand")
MARKA = "marka", _("Marka")
COLOR = "color", _("Color")
FUEL_TYPE = "fuel_type", _("Fuel type")
BODY_TYPE = "body_type", _("Body type")
CAR_POSITION = "car_position", _("Car position")
STATE_CAR = "state_car", _("Car state")

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -6,11 +6,11 @@ from core.apps.evaluation.models import QuickEvaluationModel
class QuickevaluationFilter(filters.FilterSet):
status = filters.CharFilter(method="filter_status")
car_type = filters.CharFilter(field_name="car_type", lookup_expr="exact")
state_car = filters.CharFilter(field_name="state_car", lookup_expr="exact")
state_car = filters.NumberFilter(field_name="state_car", lookup_expr="exact")
created_from = filters.DateFilter(field_name="created_at", lookup_expr="gte")
created_to = filters.DateFilter(field_name="created_at", lookup_expr="lte")
year_from = filters.NumberFilter(field_name="manufacture_year", lookup_expr="gte")
year_to = filters.NumberFilter(field_name="manufacture_year", lookup_expr="lte")
year_from = filters.NumberFilter(field_name="car_manufactured_date", lookup_expr="gte")
year_to = filters.NumberFilter(field_name="car_manufactured_date", lookup_expr="lte")
def filter_status(self, queryset, name, value):
if value:

View File

@@ -0,0 +1,17 @@
from django_filters import rest_framework as filters
from core.apps.evaluation.models import ReferenceitemModel
class ReferenceitemFilter(filters.FilterSet):
type = filters.CharFilter(field_name="type", lookup_expr="exact")
parent = filters.NumberFilter(field_name="parent", lookup_expr="exact")
is_active = filters.BooleanFilter(field_name="is_active")
class Meta:
model = ReferenceitemModel
fields = [
"type",
"parent",
"is_active",
]

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -0,0 +1,10 @@
from django import forms
from core.apps.evaluation.models import ReferenceitemModel
class ReferenceitemForm(forms.ModelForm):
class Meta:
model = ReferenceitemModel
fields = "__all__"

View File

@@ -0,0 +1,109 @@
# Generated by Django 5.2.7 on 2026-03-09 09:57
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0011_update_choices_to_english'),
('shared', '0002_settingsmodel_created_at_settingsmodel_description_and_more'),
]
operations = [
migrations.RemoveField(
model_name='quickevaluationmodel',
name='condition',
),
migrations.RemoveField(
model_name='quickevaluationmodel',
name='license_plate',
),
migrations.RemoveField(
model_name='quickevaluationmodel',
name='manufacture_year',
),
migrations.RemoveField(
model_name='quickevaluationmodel',
name='mileage',
),
migrations.RemoveField(
model_name='quickevaluationmodel',
name='model',
),
migrations.RemoveField(
model_name='quickevaluationmodel',
name='tech_passport_number',
),
migrations.AddField(
model_name='quickevaluationmodel',
name='car_manufactured_date',
field=models.IntegerField(blank=True, null=True, verbose_name='manufacture year'),
),
migrations.AddField(
model_name='quickevaluationmodel',
name='car_number',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='car number'),
),
migrations.AddField(
model_name='quickevaluationmodel',
name='car_position',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_positions', to='shared.optionsmodel', verbose_name='car position'),
),
migrations.AddField(
model_name='quickevaluationmodel',
name='distance_covered',
field=models.IntegerField(blank=True, null=True, verbose_name='distance covered (km)'),
),
migrations.AddField(
model_name='quickevaluationmodel',
name='marka',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_markas', to='shared.optionsmodel', verbose_name='marka'),
),
migrations.AddField(
model_name='quickevaluationmodel',
name='tech_passport_issued_date',
field=models.DateField(blank=True, null=True, verbose_name='tech passport issued date'),
),
migrations.AddField(
model_name='quickevaluationmodel',
name='tech_passport_issued_place',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='tech passport issued place'),
),
migrations.AddField(
model_name='quickevaluationmodel',
name='tex_passport_file',
field=models.FileField(blank=True, null=True, upload_to='quick_evaluation/tech_passports/%Y/%m/', verbose_name='tech passport file'),
),
migrations.AddField(
model_name='quickevaluationmodel',
name='tex_passport_serie_num',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='tech passport series and number'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='body_type',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_body_types', to='shared.optionsmodel', verbose_name='body type'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='brand',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_brands', to='shared.optionsmodel', verbose_name='brand'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='color',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_colors', to='shared.optionsmodel', verbose_name='color'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='fuel_type',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_fuel_types', to='shared.optionsmodel', verbose_name='fuel type'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='state_car',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_states', to='shared.optionsmodel', verbose_name='car state'),
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 5.2.7 on 2026-03-09 10:49
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0012_remove_quickevaluationmodel_condition_and_more'),
]
operations = [
migrations.CreateModel(
name='ReferenceitemModel',
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=[('brand', 'Brand'), ('marka', 'Marka'), ('color', 'Color'), ('fuel_type', 'Fuel type'), ('body_type', 'Body type'), ('car_position', 'Car position'), ('state_car', 'Car state')], max_length=50, verbose_name='type')),
('name', models.CharField(max_length=255, verbose_name='name')),
('order', models.IntegerField(default=0, verbose_name='order')),
('is_active', models.BooleanField(default=True, verbose_name='is active')),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='evaluation.referenceitemmodel', verbose_name='parent')),
],
options={
'verbose_name': 'Reference Item',
'verbose_name_plural': 'Reference Items',
'db_table': 'ReferenceItem',
'ordering': ['type', 'order', 'name'],
},
),
]

View File

@@ -0,0 +1,49 @@
# Generated by Django 5.2.7 on 2026-03-09 10:51
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0013_referenceitemmodel'),
]
operations = [
migrations.AlterField(
model_name='quickevaluationmodel',
name='body_type',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_body_types', to='evaluation.referenceitemmodel', verbose_name='body type'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='brand',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_brands', to='evaluation.referenceitemmodel', verbose_name='brand'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='car_position',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_positions', to='evaluation.referenceitemmodel', verbose_name='car position'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='color',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_colors', to='evaluation.referenceitemmodel', verbose_name='color'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='fuel_type',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_fuel_types', to='evaluation.referenceitemmodel', verbose_name='fuel type'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='marka',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_markas', to='evaluation.referenceitemmodel', verbose_name='marka'),
),
migrations.AlterField(
model_name='quickevaluationmodel',
name='state_car',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quick_eval_states', to='evaluation.referenceitemmodel', verbose_name='car state'),
),
]

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -3,8 +3,7 @@ from django.utils.translation import gettext_lazy as _
from django_core.models import AbstractBaseModel
from model_bakery import baker
from core.apps.evaluation.choices.quick import CarState, CarType, QuickEvaluationStatus
from core.apps.evaluation.choices.vehicle import BodyType, FuelType, VehicleCondition
from core.apps.evaluation.choices.quick import CarType, QuickEvaluationStatus
class QuickEvaluationModel(AbstractBaseModel):
@@ -16,46 +15,115 @@ class QuickEvaluationModel(AbstractBaseModel):
related_name="quick_evaluations",
verbose_name=_("created by"),
)
tech_passport_number = models.CharField(
verbose_name=_("tech passport number"), max_length=50, blank=True, null=True
# Tex passport
tex_passport_serie_num = models.CharField(
verbose_name=_("tech passport series and number"),
max_length=20,
blank=True,
null=True,
)
license_plate = models.CharField(
verbose_name=_("license plate"), max_length=20, blank=True, null=True
tech_passport_issued_date = models.DateField(
verbose_name=_("tech passport issued date"),
blank=True,
null=True,
)
model = models.CharField(verbose_name=_("model"), max_length=255, blank=True, null=True)
brand = models.CharField(verbose_name=_("brand"), max_length=255, blank=True, null=True)
manufacture_year = models.IntegerField(
verbose_name=_("manufacture year"), blank=True, null=True
tech_passport_issued_place = models.CharField(
verbose_name=_("tech passport issued place"),
max_length=255,
blank=True,
null=True,
)
tex_passport_file = models.FileField(
verbose_name=_("tech passport file"),
upload_to="quick_evaluation/tech_passports/%Y/%m/",
blank=True,
null=True,
)
# Car info
car_type = models.CharField(
verbose_name=_("car type"),
max_length=50,
choices=CarType.choices,
blank=True,
null=True,
)
brand = models.ForeignKey(
"evaluation.ReferenceitemModel",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="quick_eval_brands",
verbose_name=_("brand"),
)
marka = models.ForeignKey(
"evaluation.ReferenceitemModel",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="quick_eval_markas",
verbose_name=_("marka"),
)
car_position = models.ForeignKey(
"evaluation.ReferenceitemModel",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="quick_eval_positions",
verbose_name=_("car position"),
)
distance_covered = models.IntegerField(
verbose_name=_("distance covered (km)"),
blank=True,
null=True,
)
body_type = models.ForeignKey(
"evaluation.ReferenceitemModel",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="quick_eval_body_types",
verbose_name=_("body type"),
)
mileage = models.IntegerField(verbose_name=_("mileage"), blank=True, null=True)
vin_number = models.CharField(
verbose_name=_("VIN number"), max_length=50, blank=True, null=True
)
car_number = models.CharField(
verbose_name=_("car number"), max_length=20, blank=True, null=True
)
car_manufactured_date = models.IntegerField(
verbose_name=_("manufacture year"), blank=True, null=True
)
engine_number = models.CharField(
verbose_name=_("engine number"), max_length=50, blank=True, null=True
)
color = models.CharField(verbose_name=_("color"), max_length=50, blank=True, null=True)
fuel_type = models.CharField(
color = models.ForeignKey(
"evaluation.ReferenceitemModel",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="quick_eval_colors",
verbose_name=_("color"),
)
fuel_type = models.ForeignKey(
"evaluation.ReferenceitemModel",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="quick_eval_fuel_types",
verbose_name=_("fuel type"),
max_length=50,
choices=FuelType.choices,
blank=True,
null=True,
)
body_type = models.CharField(
verbose_name=_("body type"),
max_length=50,
choices=BodyType.choices,
blank=True,
state_car = models.ForeignKey(
"evaluation.ReferenceitemModel",
on_delete=models.SET_NULL,
null=True,
)
condition = models.CharField(
verbose_name=_("condition"),
max_length=50,
choices=VehicleCondition.choices,
blank=True,
null=True,
related_name="quick_eval_states",
verbose_name=_("car state"),
)
# Result
estimated_price = models.DecimalField(
verbose_name=_("estimated price"),
max_digits=15,
@@ -69,20 +137,6 @@ class QuickEvaluationModel(AbstractBaseModel):
choices=QuickEvaluationStatus.choices,
default=QuickEvaluationStatus.CREATED,
)
car_type = models.CharField(
verbose_name=_("car type"),
max_length=50,
choices=CarType.choices,
blank=True,
null=True,
)
state_car = models.CharField(
verbose_name=_("car state"),
max_length=50,
choices=CarState.choices,
blank=True,
null=True,
)
def __str__(self):
return f"Quick Evaluation {self.pk} by {self.created_by}"

View File

@@ -0,0 +1,38 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_core.models import AbstractBaseModel
from model_bakery import baker
from core.apps.evaluation.choices.reference import ReferenceType
class ReferenceitemModel(AbstractBaseModel):
type = models.CharField(
verbose_name=_("type"),
max_length=50,
choices=ReferenceType.choices,
)
name = models.CharField(verbose_name=_("name"), max_length=255)
parent = models.ForeignKey(
"self",
on_delete=models.CASCADE,
null=True,
blank=True,
related_name="children",
verbose_name=_("parent"),
)
order = models.IntegerField(verbose_name=_("order"), default=0)
is_active = models.BooleanField(verbose_name=_("is active"), default=True)
def __str__(self):
return f"{self.get_type_display()}: {self.name}"
@classmethod
def _baker(cls):
return baker.make(cls)
class Meta:
db_table = "ReferenceItem"
verbose_name = _("Reference Item")
verbose_name_plural = _("Reference Items")
ordering = ["type", "order", "name"]

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -0,0 +1,12 @@
from rest_framework import permissions
class ReferenceitemPermission(permissions.BasePermission):
def __init__(self) -> None: ...
def __call__(self, *args, **kwargs):
return self
def has_permission(self, request, view):
return True

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -1,14 +1,21 @@
import re
from rest_framework import serializers
from core.apps.evaluation.models import QuickEvaluationModel
class BaseQuickevaluationSerializer(serializers.ModelSerializer):
fuel_type_display = serializers.CharField(source="get_fuel_type_display", read_only=True)
body_type_display = serializers.CharField(source="get_body_type_display", read_only=True)
condition_display = serializers.CharField(source="get_condition_display", read_only=True)
created_by_name = serializers.CharField(source="created_by.get_full_name", read_only=True)
status_display = serializers.CharField(source="get_status_display", read_only=True)
car_type_display = serializers.CharField(source="get_car_type_display", read_only=True)
state_car_display = serializers.CharField(source="get_state_car_display", read_only=True)
brand_name = serializers.CharField(source="brand.name", read_only=True, default=None)
marka_name = serializers.CharField(source="marka.name", read_only=True, default=None)
color_name = serializers.CharField(source="color.name", read_only=True, default=None)
fuel_type_name = serializers.CharField(source="fuel_type.name", read_only=True, default=None)
body_type_name = serializers.CharField(source="body_type.name", read_only=True, default=None)
state_car_name = serializers.CharField(source="state_car.name", read_only=True, default=None)
car_position_name = serializers.CharField(source="car_position.name", read_only=True, default=None)
class Meta:
model = QuickEvaluationModel
@@ -17,55 +24,104 @@ class BaseQuickevaluationSerializer(serializers.ModelSerializer):
"created_by",
"created_by_name",
"brand",
"model",
"license_plate",
"manufacture_year",
"brand_name",
"marka",
"marka_name",
"car_number",
"car_manufactured_date",
"estimated_price",
"status",
"status_display",
"car_type",
"car_type_display",
"state_car",
"state_car_display",
"state_car_name",
"created_at",
]
class ListQuickevaluationSerializer(BaseQuickevaluationSerializer):
class Meta(BaseQuickevaluationSerializer.Meta):
pass
class RetrieveQuickevaluationSerializer(BaseQuickevaluationSerializer):
class Meta(BaseQuickevaluationSerializer.Meta):
fields = BaseQuickevaluationSerializer.Meta.fields + [
"tech_passport_number",
"mileage",
"tex_passport_serie_num",
"tech_passport_issued_date",
"tech_passport_issued_place",
"tex_passport_file",
"car_position",
"car_position_name",
"distance_covered",
"body_type",
"body_type_name",
"vin_number",
"engine_number",
"color",
"color_name",
"fuel_type",
"fuel_type_display",
"body_type",
"body_type_display",
"condition",
"condition_display",
"fuel_type_name",
"updated_at",
]
class CreateQuickevaluationSerializer(BaseQuickevaluationSerializer):
class Meta(BaseQuickevaluationSerializer.Meta):
class CreateQuickevaluationSerializer(serializers.ModelSerializer):
class Meta:
model = QuickEvaluationModel
fields = [
"tech_passport_number",
"license_plate",
"model",
"tex_passport_serie_num",
"tech_passport_issued_date",
"tech_passport_issued_place",
"tex_passport_file",
"car_type",
"brand",
"manufacture_year",
"mileage",
"marka",
"car_position",
"distance_covered",
"body_type",
"vin_number",
"car_number",
"car_manufactured_date",
"engine_number",
"color",
"fuel_type",
"body_type",
"condition",
"car_type",
"state_car",
]
def validate_tex_passport_serie_num(self, value):
if value and not re.match(r"^[A-Z]{3}\s?\d{7}$", value):
raise serializers.ValidationError(
"Format: AAA 1234567 (3 harf + 7 raqam)"
)
return value
def validate_vin_number(self, value):
if value and len(value) != 17:
raise serializers.ValidationError("VIN raqami 17 belgidan iborat bo'lishi kerak.")
return value
def validate(self, attrs):
car_type = attrs.get("car_type")
if car_type == "lightweight":
if not attrs.get("distance_covered"):
raise serializers.ValidationError(
{"distance_covered": "car_type 'lightweight' uchun majburiy."}
)
if not attrs.get("body_type"):
raise serializers.ValidationError(
{"body_type": "car_type 'lightweight' uchun majburiy."}
)
if car_type == "truck":
if not attrs.get("vin_number"):
raise serializers.ValidationError(
{"vin_number": "car_type 'truck' uchun majburiy."}
)
return attrs
def create(self, validated_data):
request = self.context.get("request")
if request and request.user and request.user.is_authenticated:
validated_data["created_by"] = request.user
return super().create(validated_data)

View File

@@ -0,0 +1,48 @@
from rest_framework import serializers
from core.apps.evaluation.models import ReferenceitemModel
class BaseReferenceitemSerializer(serializers.ModelSerializer):
type_display = serializers.CharField(source="get_type_display", read_only=True)
parent_name = serializers.CharField(source="parent.name", read_only=True, default=None)
class Meta:
model = ReferenceitemModel
fields = [
"id",
"type",
"type_display",
"name",
"parent",
"parent_name",
"order",
"is_active",
]
class ListReferenceitemSerializer(BaseReferenceitemSerializer):
class Meta(BaseReferenceitemSerializer.Meta):
pass
class RetrieveReferenceitemSerializer(BaseReferenceitemSerializer):
children = serializers.SerializerMethodField()
class Meta(BaseReferenceitemSerializer.Meta):
fields = BaseReferenceitemSerializer.Meta.fields + ["children"]
def get_children(self, obj):
children = obj.children.filter(is_active=True).order_by("order", "name")
return ListReferenceitemSerializer(children, many=True).data
class CreateReferenceitemSerializer(BaseReferenceitemSerializer):
class Meta(BaseReferenceitemSerializer.Meta):
fields = [
"type",
"name",
"parent",
"order",
"is_active",
]

View File

@@ -0,0 +1 @@
from .ReferenceItem import * # noqa

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -0,0 +1,8 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from core.apps.evaluation.models import ReferenceitemModel
@receiver(post_save, sender=ReferenceitemModel)
def ReferenceitemSignal(sender, instance, created, **kwargs): ...

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -0,0 +1 @@
from .test_ReferenceItem import * # noqa

View File

@@ -0,0 +1,101 @@
import pytest
from django.urls import reverse
from rest_framework.test import APIClient
from core.apps.evaluation.models import ReferenceitemModel
@pytest.fixture
def instance(db):
return ReferenceitemModel._baker()
@pytest.fixture
def api_client(instance):
client = APIClient()
##client.force_authenticate(user=instance.user)
return client, instance
@pytest.fixture
def data(api_client):
client, instance = api_client
return (
{
"list": reverse("ReferenceItem-list"),
"retrieve": reverse("ReferenceItem-detail", kwargs={"pk": instance.pk}),
"retrieve-not-found": reverse("ReferenceItem-detail", kwargs={"pk": 1000}),
},
client,
instance,
)
@pytest.mark.django_db
def test_list(data):
urls, client, _ = data
response = client.get(urls["list"])
data_resp = response.json()
assert response.status_code == 200
assert data_resp["status"] is True
@pytest.mark.django_db
def test_retrieve(data):
urls, client, _ = data
response = client.get(urls["retrieve"])
data_resp = response.json()
assert response.status_code == 200
assert data_resp["status"] is True
@pytest.mark.django_db
def test_retrieve_not_found(data):
urls, client, _ = data
response = client.get(urls["retrieve-not-found"])
data_resp = response.json()
assert response.status_code == 404
assert data_resp["status"] is False
# @pytest.mark.django_db
# def test_create(data):
# urls, client, _ = data
# response = client.post(urls["list"], data={"name": "test"})
# assert response.json()["status"] is True
# assert response.status_code == 201
# @pytest.mark.django_db
# def test_update(data):
# urls, client, _ = data
# response = client.patch(urls["retrieve"], data={"name": "updated"})
# assert response.json()["status"] is True
# assert response.status_code == 200
#
# # verify updated value
# response = client.get(urls["retrieve"])
# assert response.json()["status"] is True
# assert response.status_code == 200
# assert response.json()["data"]["name"] == "updated"
# @pytest.mark.django_db
# def test_partial_update():
# urls, client, _ = data
# response = client.patch(urls["retrieve"], data={"name": "updated"})
# assert response.json()["status"] is True
# assert response.status_code == 200
#
# # verify updated value
# response = client.get(urls["retrieve"])
# assert response.json()["status"] is True
# assert response.status_code == 200
# assert response.json()["data"]["name"] == "updated"
# @pytest.mark.django_db
# def test_destroy(data):
# urls, client, _ = data
# response = client.delete(urls["retrieve"])
# assert response.status_code == 204

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -0,0 +1,8 @@
from modeltranslation.translator import TranslationOptions, register
from core.apps.evaluation.models import ReferenceitemModel
@register(ReferenceitemModel)
class ReferenceitemTranslation(TranslationOptions):
fields = []

View File

@@ -9,12 +9,14 @@ from .views import (
PropertyOwnerView,
QuickEvaluationView,
RealEstateEvaluationView,
ReferenceitemView,
ValuationDocumentView,
ValuationView,
VehicleView,
)
router = DefaultRouter()
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")

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -0,0 +1,8 @@
# from django.core.exceptions import ValidationError
class ReferenceitemValidator:
def __init__(self): ...
def __call__(self):
return True

View File

@@ -4,6 +4,7 @@ from .document import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -2,8 +2,9 @@ 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.parsers import FormParser, MultiPartParser
from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework.viewsets import ModelViewSet
from core.apps.evaluation.filters.quick import QuickevaluationFilter
from core.apps.evaluation.models import QuickEvaluationModel
@@ -15,27 +16,31 @@ from core.apps.evaluation.serializers.quick import (
@extend_schema(tags=["QuickEvaluation"])
class QuickEvaluationView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = QuickEvaluationModel.objects.select_related("created_by").all()
class QuickEvaluationView(BaseViewSetMixin, ModelViewSet):
queryset = QuickEvaluationModel.objects.select_related(
"created_by", "brand", "marka", "color", "fuel_type",
"body_type", "state_car", "car_position",
).all()
serializer_class = ListQuickevaluationSerializer
permission_classes = [AllowAny]
parser_classes = [MultiPartParser, FormParser]
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_class = QuickevaluationFilter
search_fields = ["license_plate", "model", "brand"]
search_fields = ["car_number", "marka__name", "brand__name"]
ordering_fields = [
"created_at",
"updated_at",
"license_plate",
"brand",
"model",
"car_number",
"brand__name",
"marka__name",
"car_type",
"manufacture_year",
"color",
"fuel_type",
"state_car",
"car_manufactured_date",
"color__name",
"fuel_type__name",
"state_car__name",
"status",
"mileage",
"distance_covered",
]
ordering = ["-created_at"]

View File

@@ -0,0 +1,34 @@
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.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet
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,
)
@extend_schema(tags=["ReferenceItem"])
class ReferenceitemView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ReferenceitemModel.objects.select_related("parent").filter(is_active=True)
serializer_class = 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,
}