From 4ddd4f138f1afd81bc2c80787c9a49291c1aa328 Mon Sep 17 00:00:00 2001 From: Husanjonazamov Date: Wed, 18 Feb 2026 17:44:15 +0500 Subject: [PATCH] feat(evaluation): baholash modellari yaratildi (auto, ko'chmas mulk, ko'char mulk, tezkor, hisobot) --- core/apps/evaluation/admin/__init__.py | 1 + core/apps/evaluation/admin/report.py | 12 +++ core/apps/evaluation/filters/__init__.py | 1 + core/apps/evaluation/filters/report.py | 13 +++ core/apps/evaluation/forms/__init__.py | 1 + core/apps/evaluation/forms/report.py | 10 ++ .../migrations/0008_evaluationreportmodel.py | 36 +++++++ core/apps/evaluation/models/__init__.py | 1 + core/apps/evaluation/models/report.py | 48 +++++++++ core/apps/evaluation/permissions/__init__.py | 1 + core/apps/evaluation/permissions/report.py | 12 +++ core/apps/evaluation/serializers/__init__.py | 1 + .../serializers/report/EvaluationReport.py | 39 +++++++ .../evaluation/serializers/report/__init__.py | 1 + core/apps/evaluation/signals/__init__.py | 1 + core/apps/evaluation/signals/report.py | 8 ++ core/apps/evaluation/tests/__init__.py | 1 + core/apps/evaluation/tests/report/__init__.py | 1 + .../tests/report/test_EvaluationReport.py | 101 ++++++++++++++++++ core/apps/evaluation/translation/__init__.py | 1 + core/apps/evaluation/translation/report.py | 8 ++ core/apps/evaluation/urls.py | 2 + core/apps/evaluation/validators/__init__.py | 1 + core/apps/evaluation/validators/report.py | 8 ++ core/apps/evaluation/views/__init__.py | 1 + core/apps/evaluation/views/report.py | 25 +++++ 26 files changed, 335 insertions(+) create mode 100644 core/apps/evaluation/admin/report.py create mode 100644 core/apps/evaluation/filters/report.py create mode 100644 core/apps/evaluation/forms/report.py create mode 100644 core/apps/evaluation/migrations/0008_evaluationreportmodel.py create mode 100644 core/apps/evaluation/models/report.py create mode 100644 core/apps/evaluation/permissions/report.py create mode 100644 core/apps/evaluation/serializers/report/EvaluationReport.py create mode 100644 core/apps/evaluation/serializers/report/__init__.py create mode 100644 core/apps/evaluation/signals/report.py create mode 100644 core/apps/evaluation/tests/report/__init__.py create mode 100644 core/apps/evaluation/tests/report/test_EvaluationReport.py create mode 100644 core/apps/evaluation/translation/report.py create mode 100644 core/apps/evaluation/validators/report.py create mode 100644 core/apps/evaluation/views/report.py diff --git a/core/apps/evaluation/admin/__init__.py b/core/apps/evaluation/admin/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/admin/__init__.py +++ b/core/apps/evaluation/admin/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/admin/report.py b/core/apps/evaluation/admin/report.py new file mode 100644 index 0000000..ab5cd5e --- /dev/null +++ b/core/apps/evaluation/admin/report.py @@ -0,0 +1,12 @@ +from django.contrib import admin +from unfold.admin import ModelAdmin + +from core.apps.evaluation.models import EvaluationReportModel + + +@admin.register(EvaluationReportModel) +class EvaluationreportAdmin(ModelAdmin): + list_display = ( + "id", + "__str__", + ) diff --git a/core/apps/evaluation/filters/__init__.py b/core/apps/evaluation/filters/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/filters/__init__.py +++ b/core/apps/evaluation/filters/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/filters/report.py b/core/apps/evaluation/filters/report.py new file mode 100644 index 0000000..a950d76 --- /dev/null +++ b/core/apps/evaluation/filters/report.py @@ -0,0 +1,13 @@ +from django_filters import rest_framework as filters + +from core.apps.evaluation.models import EvaluationReportModel + + +class EvaluationreportFilter(filters.FilterSet): + # name = filters.CharFilter(field_name="name", lookup_expr="icontains") + + class Meta: + model = EvaluationReportModel + fields = [ + "name", + ] diff --git a/core/apps/evaluation/forms/__init__.py b/core/apps/evaluation/forms/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/forms/__init__.py +++ b/core/apps/evaluation/forms/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/forms/report.py b/core/apps/evaluation/forms/report.py new file mode 100644 index 0000000..7df2ec7 --- /dev/null +++ b/core/apps/evaluation/forms/report.py @@ -0,0 +1,10 @@ +from django import forms + +from core.apps.evaluation.models import EvaluationReportModel + + +class EvaluationreportForm(forms.ModelForm): + + class Meta: + model = EvaluationReportModel + fields = "__all__" diff --git a/core/apps/evaluation/migrations/0008_evaluationreportmodel.py b/core/apps/evaluation/migrations/0008_evaluationreportmodel.py new file mode 100644 index 0000000..a0c031d --- /dev/null +++ b/core/apps/evaluation/migrations/0008_evaluationreportmodel.py @@ -0,0 +1,36 @@ +# Generated by Django 5.2.7 on 2026-02-18 12:34 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('evaluation', '0007_quickevaluationmodel'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='EvaluationReportModel', + 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)), + ('report_number', models.CharField(max_length=100, unique=True, verbose_name='report number')), + ('final_value', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='final value')), + ('report_file', models.FileField(blank=True, null=True, upload_to='evaluation/reports/', verbose_name='report file')), + ('conclusion_text', models.TextField(blank=True, verbose_name='conclusion text')), + ('approved_at', models.DateTimeField(blank=True, null=True, verbose_name='approved at')), + ('evaluator', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='reports', to=settings.AUTH_USER_MODEL, verbose_name='evaluator')), + ('valuation', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='report', to='evaluation.valuationmodel', verbose_name='valuation')), + ], + options={ + 'verbose_name': 'Evaluation Report', + 'verbose_name_plural': 'Evaluation Reports', + 'db_table': 'EvaluationReport', + }, + ), + ] diff --git a/core/apps/evaluation/models/__init__.py b/core/apps/evaluation/models/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/models/__init__.py +++ b/core/apps/evaluation/models/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/models/report.py b/core/apps/evaluation/models/report.py new file mode 100644 index 0000000..857b772 --- /dev/null +++ b/core/apps/evaluation/models/report.py @@ -0,0 +1,48 @@ +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 .valuation import ValuationModel + + +class EvaluationReportModel(AbstractBaseModel): + valuation = models.OneToOneField( + ValuationModel, + on_delete=models.CASCADE, + related_name="report", + verbose_name=_("valuation"), + ) + evaluator = models.ForeignKey( + "accounts.User", + on_delete=models.PROTECT, + related_name="reports", + verbose_name=_("evaluator"), + ) + report_number = models.CharField( + verbose_name=_("report number"), max_length=100, unique=True + ) + final_value = models.DecimalField( + verbose_name=_("final value"), max_digits=15, decimal_places=2 + ) + report_file = models.FileField( + verbose_name=_("report file"), upload_to="evaluation/reports/", blank=True, null=True + ) + conclusion_text = models.TextField(verbose_name=_("conclusion text"), blank=True) + approved_at = models.DateTimeField( + verbose_name=_("approved at"), blank=True, null=True + ) + + def __str__(self): + return f"Report #{self.report_number} for {self.valuation}" + + @classmethod + def _baker(cls): + return baker.make(cls) + + class Meta: + db_table = "EvaluationReport" + verbose_name = _("Evaluation Report") + verbose_name_plural = _("Evaluation Reports") + diff --git a/core/apps/evaluation/permissions/__init__.py b/core/apps/evaluation/permissions/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/permissions/__init__.py +++ b/core/apps/evaluation/permissions/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/permissions/report.py b/core/apps/evaluation/permissions/report.py new file mode 100644 index 0000000..7348fa1 --- /dev/null +++ b/core/apps/evaluation/permissions/report.py @@ -0,0 +1,12 @@ +from rest_framework import permissions + + +class EvaluationreportPermission(permissions.BasePermission): + + def __init__(self) -> None: ... + + def __call__(self, *args, **kwargs): + return self + + def has_permission(self, request, view): + return True diff --git a/core/apps/evaluation/serializers/__init__.py b/core/apps/evaluation/serializers/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/serializers/__init__.py +++ b/core/apps/evaluation/serializers/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/serializers/report/EvaluationReport.py b/core/apps/evaluation/serializers/report/EvaluationReport.py new file mode 100644 index 0000000..23840ba --- /dev/null +++ b/core/apps/evaluation/serializers/report/EvaluationReport.py @@ -0,0 +1,39 @@ +from rest_framework import serializers + +from core.apps.evaluation.models import EvaluationReportModel + + +class BaseEvaluationreportSerializer(serializers.ModelSerializer): + class Meta: + model = EvaluationReportModel + fields = [ + "id", + "valuation", + "evaluator", + "report_number", + "final_value", + "report_file", + "conclusion_text", + "approved_at", + ] + + +class ListEvaluationreportSerializer(BaseEvaluationreportSerializer): + class Meta(BaseEvaluationreportSerializer.Meta): ... + + +class RetrieveEvaluationreportSerializer(BaseEvaluationreportSerializer): + class Meta(BaseEvaluationreportSerializer.Meta): ... + + +class CreateEvaluationreportSerializer(BaseEvaluationreportSerializer): + class Meta(BaseEvaluationreportSerializer.Meta): + fields = [ + "id", + "valuation", + "evaluator", + "report_number", + "final_value", + "report_file", + "conclusion_text", + ] diff --git a/core/apps/evaluation/serializers/report/__init__.py b/core/apps/evaluation/serializers/report/__init__.py new file mode 100644 index 0000000..2e25347 --- /dev/null +++ b/core/apps/evaluation/serializers/report/__init__.py @@ -0,0 +1 @@ +from .EvaluationReport import * # noqa diff --git a/core/apps/evaluation/signals/__init__.py b/core/apps/evaluation/signals/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/signals/__init__.py +++ b/core/apps/evaluation/signals/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/signals/report.py b/core/apps/evaluation/signals/report.py new file mode 100644 index 0000000..a12a7ff --- /dev/null +++ b/core/apps/evaluation/signals/report.py @@ -0,0 +1,8 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver + +from core.apps.evaluation.models import EvaluationReportModel + + +@receiver(post_save, sender=EvaluationReportModel) +def EvaluationreportSignal(sender, instance, created, **kwargs): ... diff --git a/core/apps/evaluation/tests/__init__.py b/core/apps/evaluation/tests/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/tests/__init__.py +++ b/core/apps/evaluation/tests/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/tests/report/__init__.py b/core/apps/evaluation/tests/report/__init__.py new file mode 100644 index 0000000..5e9cb77 --- /dev/null +++ b/core/apps/evaluation/tests/report/__init__.py @@ -0,0 +1 @@ +from .test_EvaluationReport import * # noqa diff --git a/core/apps/evaluation/tests/report/test_EvaluationReport.py b/core/apps/evaluation/tests/report/test_EvaluationReport.py new file mode 100644 index 0000000..0bff44b --- /dev/null +++ b/core/apps/evaluation/tests/report/test_EvaluationReport.py @@ -0,0 +1,101 @@ +import pytest +from django.urls import reverse +from rest_framework.test import APIClient + +from core.apps.evaluation.models import EvaluationReportModel + + +@pytest.fixture +def instance(db): + return EvaluationReportModel._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("evaluation-report-list"), + "retrieve": reverse("evaluation-report-detail", kwargs={"pk": instance.pk}), + "retrieve-not-found": reverse("evaluation-report-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 diff --git a/core/apps/evaluation/translation/__init__.py b/core/apps/evaluation/translation/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/translation/__init__.py +++ b/core/apps/evaluation/translation/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/translation/report.py b/core/apps/evaluation/translation/report.py new file mode 100644 index 0000000..96a88a0 --- /dev/null +++ b/core/apps/evaluation/translation/report.py @@ -0,0 +1,8 @@ +from modeltranslation.translator import TranslationOptions, register + +from core.apps.evaluation.models import EvaluationReportModel + + +@register(EvaluationReportModel) +class EvaluationreportTranslation(TranslationOptions): + fields = [] diff --git a/core/apps/evaluation/urls.py b/core/apps/evaluation/urls.py index 35b4b57..d1e5dbf 100644 --- a/core/apps/evaluation/urls.py +++ b/core/apps/evaluation/urls.py @@ -4,6 +4,7 @@ from rest_framework.routers import DefaultRouter from .views import ( AutoEvaluationView, CustomerView, + EvaluationReportView, MovablePropertyEvaluationView, PropertyOwnerView, QuickEvaluationView, @@ -13,6 +14,7 @@ from .views import ( ) router = DefaultRouter() +router.register("evaluation-report", EvaluationReportView, basename="evaluation-report") router.register("quick-evaluation", QuickEvaluationView, basename="quick-evaluation") router.register("movable-property-evaluation", MovablePropertyEvaluationView, basename="movable-property-evaluation") router.register("real-estate-evaluation", RealEstateEvaluationView, basename="real-estate-evaluation") diff --git a/core/apps/evaluation/validators/__init__.py b/core/apps/evaluation/validators/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/validators/__init__.py +++ b/core/apps/evaluation/validators/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/validators/report.py b/core/apps/evaluation/validators/report.py new file mode 100644 index 0000000..fc3a107 --- /dev/null +++ b/core/apps/evaluation/validators/report.py @@ -0,0 +1,8 @@ +# from django.core.exceptions import ValidationError + + +class EvaluationreportValidator: + def __init__(self): ... + + def __call__(self): + return True diff --git a/core/apps/evaluation/views/__init__.py b/core/apps/evaluation/views/__init__.py index ef86837..890c520 100644 --- a/core/apps/evaluation/views/__init__.py +++ b/core/apps/evaluation/views/__init__.py @@ -3,5 +3,6 @@ from .customer import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa +from .report import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/views/report.py b/core/apps/evaluation/views/report.py new file mode 100644 index 0000000..20314e7 --- /dev/null +++ b/core/apps/evaluation/views/report.py @@ -0,0 +1,25 @@ +from django_core.mixins import BaseViewSetMixin +from drf_spectacular.utils import extend_schema +from rest_framework.permissions import AllowAny +from rest_framework.viewsets import ReadOnlyModelViewSet + +from core.apps.evaluation.models import EvaluationReportModel +from core.apps.evaluation.serializers.report import ( + CreateEvaluationreportSerializer, + ListEvaluationreportSerializer, + RetrieveEvaluationreportSerializer, +) + + +@extend_schema(tags=["EvaluationReport"]) +class EvaluationReportView(BaseViewSetMixin, ReadOnlyModelViewSet): + queryset = EvaluationReportModel.objects.all() + serializer_class = ListEvaluationreportSerializer + permission_classes = [AllowAny] + + action_permission_classes = {} + action_serializer_class = { + "list": ListEvaluationreportSerializer, + "retrieve": RetrieveEvaluationreportSerializer, + "create": CreateEvaluationreportSerializer, + }