From 2cb094cbd36f5749d52a9c9c5eba95aca74180f4 Mon Sep 17 00:00:00 2001 From: Husanjonazamov Date: Fri, 13 Feb 2026 17:57:56 +0500 Subject: [PATCH] movable-model qoshildi bu (Ko'char mulk) modeli to'liq va optimallashgan holda tayyor! --- core/apps/evaluation/admin/__init__.py | 1 + core/apps/evaluation/admin/movable.py | 12 +++ core/apps/evaluation/choices/movable.py | 19 ++++ core/apps/evaluation/filters/__init__.py | 1 + core/apps/evaluation/filters/movable.py | 13 +++ core/apps/evaluation/forms/__init__.py | 1 + core/apps/evaluation/forms/movable.py | 10 ++ .../0006_movablepropertyevaluationmodel.py | 34 ++++++ core/apps/evaluation/models/__init__.py | 5 +- core/apps/evaluation/models/movable.py | 54 ++++++++++ core/apps/evaluation/permissions/__init__.py | 1 + core/apps/evaluation/permissions/movable.py | 12 +++ core/apps/evaluation/serializers/__init__.py | 1 + .../movable/MovablePropertyEvaluation.py | 28 +++++ .../serializers/movable/__init__.py | 1 + core/apps/evaluation/signals/__init__.py | 1 + core/apps/evaluation/signals/movable.py | 8 ++ core/apps/evaluation/tests/__init__.py | 1 + .../apps/evaluation/tests/movable/__init__.py | 1 + .../movable/test_MovablePropertyEvaluation.py | 101 ++++++++++++++++++ core/apps/evaluation/translation/__init__.py | 1 + core/apps/evaluation/translation/movable.py | 8 ++ core/apps/evaluation/urls.py | 2 + core/apps/evaluation/validators/__init__.py | 1 + core/apps/evaluation/validators/movable.py | 8 ++ core/apps/evaluation/views/__init__.py | 1 + core/apps/evaluation/views/movable.py | 25 +++++ 27 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 core/apps/evaluation/admin/movable.py create mode 100644 core/apps/evaluation/choices/movable.py create mode 100644 core/apps/evaluation/filters/movable.py create mode 100644 core/apps/evaluation/forms/movable.py create mode 100644 core/apps/evaluation/migrations/0006_movablepropertyevaluationmodel.py create mode 100644 core/apps/evaluation/models/movable.py create mode 100644 core/apps/evaluation/permissions/movable.py create mode 100644 core/apps/evaluation/serializers/movable/MovablePropertyEvaluation.py create mode 100644 core/apps/evaluation/serializers/movable/__init__.py create mode 100644 core/apps/evaluation/signals/movable.py create mode 100644 core/apps/evaluation/tests/movable/__init__.py create mode 100644 core/apps/evaluation/tests/movable/test_MovablePropertyEvaluation.py create mode 100644 core/apps/evaluation/translation/movable.py create mode 100644 core/apps/evaluation/validators/movable.py create mode 100644 core/apps/evaluation/views/movable.py diff --git a/core/apps/evaluation/admin/__init__.py b/core/apps/evaluation/admin/__init__.py index e83b3b8..ce44113 100644 --- a/core/apps/evaluation/admin/__init__.py +++ b/core/apps/evaluation/admin/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/admin/movable.py b/core/apps/evaluation/admin/movable.py new file mode 100644 index 0000000..81af37e --- /dev/null +++ b/core/apps/evaluation/admin/movable.py @@ -0,0 +1,12 @@ +from django.contrib import admin +from unfold.admin import ModelAdmin + +from core.apps.evaluation.models import MovablePropertyEvaluationModel + + +@admin.register(MovablePropertyEvaluationModel) +class MovablepropertyevaluationAdmin(ModelAdmin): + list_display = ( + "id", + "__str__", + ) diff --git a/core/apps/evaluation/choices/movable.py b/core/apps/evaluation/choices/movable.py new file mode 100644 index 0000000..e109b2b --- /dev/null +++ b/core/apps/evaluation/choices/movable.py @@ -0,0 +1,19 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class MovablePropertyCategory(models.TextChoices): + EQUIPMENT = "equipment", _("Equipment") + MACHINERY = "machinery", _("Machinery") + FURNITURE = "furniture", _("Furniture") + ELECTRONICS = "electronics", _("Electronics") + COMMODITY = "commodity", _("Commodity/Goods") + OTHER = "other", _("Other") + + +class MovablePropertyCondition(models.TextChoices): + NEW = "new", _("New") + GOOD = "good", _("Good") + AVERAGE = "average", _("Average") + POOR = "poor", _("Poor/Needs repair") + SCRAP = "scrap", _("Scrap") diff --git a/core/apps/evaluation/filters/__init__.py b/core/apps/evaluation/filters/__init__.py index e83b3b8..ce44113 100644 --- a/core/apps/evaluation/filters/__init__.py +++ b/core/apps/evaluation/filters/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/filters/movable.py b/core/apps/evaluation/filters/movable.py new file mode 100644 index 0000000..374bcd8 --- /dev/null +++ b/core/apps/evaluation/filters/movable.py @@ -0,0 +1,13 @@ +# from django_filters import rest_framework as filters + +# from core.apps.evaluation.models import MovablePropertyEvaluationModel + + +# class MovablepropertyevaluationFilter(filters.FilterSet): +# # name = filters.CharFilter(field_name="name", lookup_expr="icontains") + +# class Meta: +# model = MovablePropertyEvaluationModel +# fields = [ +# "name", +# ] diff --git a/core/apps/evaluation/forms/__init__.py b/core/apps/evaluation/forms/__init__.py index e83b3b8..ce44113 100644 --- a/core/apps/evaluation/forms/__init__.py +++ b/core/apps/evaluation/forms/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/forms/movable.py b/core/apps/evaluation/forms/movable.py new file mode 100644 index 0000000..3d40a0f --- /dev/null +++ b/core/apps/evaluation/forms/movable.py @@ -0,0 +1,10 @@ +from django import forms + +from core.apps.evaluation.models import MovablePropertyEvaluationModel + + +class MovablepropertyevaluationForm(forms.ModelForm): + + class Meta: + model = MovablePropertyEvaluationModel + fields = "__all__" diff --git a/core/apps/evaluation/migrations/0006_movablepropertyevaluationmodel.py b/core/apps/evaluation/migrations/0006_movablepropertyevaluationmodel.py new file mode 100644 index 0000000..6237ac2 --- /dev/null +++ b/core/apps/evaluation/migrations/0006_movablepropertyevaluationmodel.py @@ -0,0 +1,34 @@ +# Generated by Django 5.2.7 on 2026-02-13 12:51 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('evaluation', '0005_realestateevaluationmodel'), + ] + + operations = [ + migrations.CreateModel( + name='MovablePropertyEvaluationModel', + 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)), + ('property_name', models.CharField(max_length=255, verbose_name='property name')), + ('property_category', models.CharField(choices=[('equipment', 'Equipment'), ('machinery', 'Machinery'), ('furniture', 'Furniture'), ('electronics', 'Electronics'), ('commodity', 'Commodity/Goods'), ('other', 'Other')], default='other', max_length=50, verbose_name='property category')), + ('serial_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='serial number')), + ('manufacture_year', models.IntegerField(blank=True, null=True, verbose_name='manufacture year')), + ('condition', models.CharField(blank=True, choices=[('new', 'New'), ('good', 'Good'), ('average', 'Average'), ('poor', 'Poor/Needs repair'), ('scrap', 'Scrap')], max_length=50, null=True, verbose_name='condition')), + ('quantity', models.IntegerField(default=1, verbose_name='quantity')), + ('valuation', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='movable_property_detail', to='evaluation.valuationmodel', verbose_name='valuation')), + ], + options={ + 'verbose_name': 'Movable Property Evaluation', + 'verbose_name_plural': 'Movable Property Evaluations', + 'db_table': 'MovablePropertyEvaluation', + }, + ), + ] diff --git a/core/apps/evaluation/models/__init__.py b/core/apps/evaluation/models/__init__.py index e1a2425..0f9a0ef 100644 --- a/core/apps/evaluation/models/__init__.py +++ b/core/apps/evaluation/models/__init__.py @@ -1,7 +1,8 @@ from .auto import * # noqa -from .real_estate import * # noqa - from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa +from .movable import * # noqa + from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/models/movable.py b/core/apps/evaluation/models/movable.py new file mode 100644 index 0000000..c6075db --- /dev/null +++ b/core/apps/evaluation/models/movable.py @@ -0,0 +1,54 @@ +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 +from core.apps.evaluation.choices.movable import ( + MovablePropertyCategory, + MovablePropertyCondition, +) + + +class MovablePropertyEvaluationModel(AbstractBaseModel): + valuation = models.OneToOneField( + ValuationModel, + on_delete=models.CASCADE, + related_name="movable_property_detail", + verbose_name=_("valuation"), + ) + property_name = models.CharField(verbose_name=_("property name"), max_length=255) + property_category = models.CharField( + verbose_name=_("property category"), + max_length=50, + choices=MovablePropertyCategory.choices, + default=MovablePropertyCategory.OTHER, + ) + serial_number = models.CharField( + verbose_name=_("serial number"), max_length=100, blank=True, null=True + ) + manufacture_year = models.IntegerField( + verbose_name=_("manufacture year"), blank=True, null=True + ) + condition = models.CharField( + verbose_name=_("condition"), + max_length=50, + choices=MovablePropertyCondition.choices, + blank=True, + null=True, + ) + quantity = models.IntegerField(verbose_name=_("quantity"), default=1) + + def __str__(self): + return f"Movable Property Evaluation: {self.property_name} ({self.valuation})" + + @classmethod + def _baker(cls): + return baker.make(cls) + + class Meta: + db_table = "MovablePropertyEvaluation" + verbose_name = _("Movable Property Evaluation") + verbose_name_plural = _("Movable Property Evaluations") + diff --git a/core/apps/evaluation/permissions/__init__.py b/core/apps/evaluation/permissions/__init__.py index e83b3b8..ce44113 100644 --- a/core/apps/evaluation/permissions/__init__.py +++ b/core/apps/evaluation/permissions/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/permissions/movable.py b/core/apps/evaluation/permissions/movable.py new file mode 100644 index 0000000..86338b1 --- /dev/null +++ b/core/apps/evaluation/permissions/movable.py @@ -0,0 +1,12 @@ +from rest_framework import permissions + + +class MovablepropertyevaluationPermission(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 e83b3b8..ce44113 100644 --- a/core/apps/evaluation/serializers/__init__.py +++ b/core/apps/evaluation/serializers/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/serializers/movable/MovablePropertyEvaluation.py b/core/apps/evaluation/serializers/movable/MovablePropertyEvaluation.py new file mode 100644 index 0000000..34b49b1 --- /dev/null +++ b/core/apps/evaluation/serializers/movable/MovablePropertyEvaluation.py @@ -0,0 +1,28 @@ +from rest_framework import serializers + +from core.apps.evaluation.models import MovablePropertyEvaluationModel + + +class BaseMovablepropertyevaluationSerializer(serializers.ModelSerializer): + class Meta: + model = MovablePropertyEvaluationModel + fields = [ + "id", + # "name", + ] + + +class ListMovablepropertyevaluationSerializer(BaseMovablepropertyevaluationSerializer): + class Meta(BaseMovablepropertyevaluationSerializer.Meta): ... + + +class RetrieveMovablepropertyevaluationSerializer(BaseMovablepropertyevaluationSerializer): + class Meta(BaseMovablepropertyevaluationSerializer.Meta): ... + + +class CreateMovablepropertyevaluationSerializer(BaseMovablepropertyevaluationSerializer): + class Meta(BaseMovablepropertyevaluationSerializer.Meta): + fields = [ + "id", + # "name", + ] diff --git a/core/apps/evaluation/serializers/movable/__init__.py b/core/apps/evaluation/serializers/movable/__init__.py new file mode 100644 index 0000000..183051b --- /dev/null +++ b/core/apps/evaluation/serializers/movable/__init__.py @@ -0,0 +1 @@ +from .MovablePropertyEvaluation import * # noqa diff --git a/core/apps/evaluation/signals/__init__.py b/core/apps/evaluation/signals/__init__.py index e83b3b8..ce44113 100644 --- a/core/apps/evaluation/signals/__init__.py +++ b/core/apps/evaluation/signals/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/signals/movable.py b/core/apps/evaluation/signals/movable.py new file mode 100644 index 0000000..7f126aa --- /dev/null +++ b/core/apps/evaluation/signals/movable.py @@ -0,0 +1,8 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver + +from core.apps.evaluation.models import MovablePropertyEvaluationModel + + +@receiver(post_save, sender=MovablePropertyEvaluationModel) +def MovablepropertyevaluationSignal(sender, instance, created, **kwargs): ... diff --git a/core/apps/evaluation/tests/__init__.py b/core/apps/evaluation/tests/__init__.py index e83b3b8..ce44113 100644 --- a/core/apps/evaluation/tests/__init__.py +++ b/core/apps/evaluation/tests/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/tests/movable/__init__.py b/core/apps/evaluation/tests/movable/__init__.py new file mode 100644 index 0000000..f748384 --- /dev/null +++ b/core/apps/evaluation/tests/movable/__init__.py @@ -0,0 +1 @@ +from .test_MovablePropertyEvaluation import * # noqa diff --git a/core/apps/evaluation/tests/movable/test_MovablePropertyEvaluation.py b/core/apps/evaluation/tests/movable/test_MovablePropertyEvaluation.py new file mode 100644 index 0000000..4a7be6c --- /dev/null +++ b/core/apps/evaluation/tests/movable/test_MovablePropertyEvaluation.py @@ -0,0 +1,101 @@ +import pytest +from django.urls import reverse +from rest_framework.test import APIClient + +from core.apps.evaluation.models import MovablePropertyEvaluationModel + + +@pytest.fixture +def instance(db): + return MovablePropertyEvaluationModel._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("movable-property-evaluation-list"), + "retrieve": reverse("movable-property-evaluation-detail", kwargs={"pk": instance.pk}), + "retrieve-not-found": reverse("movable-property-evaluation-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 e83b3b8..ce44113 100644 --- a/core/apps/evaluation/translation/__init__.py +++ b/core/apps/evaluation/translation/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/translation/movable.py b/core/apps/evaluation/translation/movable.py new file mode 100644 index 0000000..f0ad029 --- /dev/null +++ b/core/apps/evaluation/translation/movable.py @@ -0,0 +1,8 @@ +from modeltranslation.translator import TranslationOptions, register + +from core.apps.evaluation.models import MovablePropertyEvaluationModel + + +@register(MovablePropertyEvaluationModel) +class MovablepropertyevaluationTranslation(TranslationOptions): + fields = [] diff --git a/core/apps/evaluation/urls.py b/core/apps/evaluation/urls.py index 623a1ee..0934568 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, + MovablePropertyEvaluationView, PropertyOwnerView, RealEstateEvaluationView, ValuationView, @@ -11,6 +12,7 @@ from .views import ( ) router = DefaultRouter() +router.register("movable-property-evaluation", MovablePropertyEvaluationView, basename="movable-property-evaluation") router.register("real-estate-evaluation", RealEstateEvaluationView, basename="real-estate-evaluation") router.register("auto-evaluation", AutoEvaluationView, basename="auto-evaluation") router.register("vehicle", VehicleView, basename="vehicle") diff --git a/core/apps/evaluation/validators/__init__.py b/core/apps/evaluation/validators/__init__.py index e83b3b8..ce44113 100644 --- a/core/apps/evaluation/validators/__init__.py +++ b/core/apps/evaluation/validators/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/validators/movable.py b/core/apps/evaluation/validators/movable.py new file mode 100644 index 0000000..cc88fc1 --- /dev/null +++ b/core/apps/evaluation/validators/movable.py @@ -0,0 +1,8 @@ +# from django.core.exceptions import ValidationError + + +class MovablepropertyevaluationValidator: + 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 e83b3b8..ce44113 100644 --- a/core/apps/evaluation/views/__init__.py +++ b/core/apps/evaluation/views/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .movable import * # noqa from .real_estate import * # noqa from .valuation import * # noqa from .vehicle import * # noqa diff --git a/core/apps/evaluation/views/movable.py b/core/apps/evaluation/views/movable.py new file mode 100644 index 0000000..0ee0dbf --- /dev/null +++ b/core/apps/evaluation/views/movable.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 MovablePropertyEvaluationModel +from core.apps.evaluation.serializers.movable import ( + CreateMovablepropertyevaluationSerializer, + ListMovablepropertyevaluationSerializer, + RetrieveMovablepropertyevaluationSerializer, +) + + +@extend_schema(tags=["MovablePropertyEvaluation"]) +class MovablePropertyEvaluationView(BaseViewSetMixin, ReadOnlyModelViewSet): + queryset = MovablePropertyEvaluationModel.objects.all() + serializer_class = ListMovablepropertyevaluationSerializer + permission_classes = [AllowAny] + + action_permission_classes = {} + action_serializer_class = { + "list": ListMovablepropertyevaluationSerializer, + "retrieve": RetrieveMovablepropertyevaluationSerializer, + "create": CreateMovablepropertyevaluationSerializer, + }