diff --git a/core/apps/evaluation/admin/__init__.py b/core/apps/evaluation/admin/__init__.py index 890c520..c710df3 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 .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/admin/document.py b/core/apps/evaluation/admin/document.py new file mode 100644 index 0000000..cb21ddb --- /dev/null +++ b/core/apps/evaluation/admin/document.py @@ -0,0 +1,12 @@ +from django.contrib import admin +from unfold.admin import ModelAdmin + +from core.apps.evaluation.models import ValuationDocumentModel + + +@admin.register(ValuationDocumentModel) +class ValuationdocumentAdmin(ModelAdmin): + list_display = ( + "id", + "__str__", + ) diff --git a/core/apps/evaluation/choices/document.py b/core/apps/evaluation/choices/document.py new file mode 100644 index 0000000..3e2658e --- /dev/null +++ b/core/apps/evaluation/choices/document.py @@ -0,0 +1,13 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class DocumentType(models.TextChoices): + CERTIFICATE = "certificate", _("Certificate") + PASSPORT = "passport", _("Passport") + TECH_PASSPORT = "tech_passport", _("Tech Passport") + CADASTRAL = "cadastral", _("Cadastral Document") + PHOTO = "photo", _("Photo") + CONTRACT = "contract", _("Contract") + POWER_OF_ATTORNEY = "power_of_attorney", _("Power of Attorney") + OTHER = "other", _("Other") diff --git a/core/apps/evaluation/filters/__init__.py b/core/apps/evaluation/filters/__init__.py index 890c520..c710df3 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 .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/filters/document.py b/core/apps/evaluation/filters/document.py new file mode 100644 index 0000000..eaa333c --- /dev/null +++ b/core/apps/evaluation/filters/document.py @@ -0,0 +1,13 @@ +from django_filters import rest_framework as filters + +from core.apps.evaluation.models import ValuationDocumentModel + + +class ValuationdocumentFilter(filters.FilterSet): + # name = filters.CharFilter(field_name="name", lookup_expr="icontains") + + class Meta: + model = ValuationDocumentModel + fields = [ + "name", + ] diff --git a/core/apps/evaluation/forms/__init__.py b/core/apps/evaluation/forms/__init__.py index 890c520..c710df3 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 .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/forms/document.py b/core/apps/evaluation/forms/document.py new file mode 100644 index 0000000..e566577 --- /dev/null +++ b/core/apps/evaluation/forms/document.py @@ -0,0 +1,10 @@ +from django import forms + +from core.apps.evaluation.models import ValuationDocumentModel + + +class ValuationdocumentForm(forms.ModelForm): + + class Meta: + model = ValuationDocumentModel + fields = "__all__" diff --git a/core/apps/evaluation/migrations/0009_valuationdocumentmodel.py b/core/apps/evaluation/migrations/0009_valuationdocumentmodel.py new file mode 100644 index 0000000..4034137 --- /dev/null +++ b/core/apps/evaluation/migrations/0009_valuationdocumentmodel.py @@ -0,0 +1,36 @@ +# Generated by Django 5.2.7 on 2026-02-18 12:54 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('evaluation', '0008_evaluationreportmodel'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='ValuationDocumentModel', + 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)), + ('document_type', models.CharField(choices=[('certificate', 'Certificate'), ('passport', 'Passport'), ('tech_passport', 'Tech Passport'), ('cadastral', 'Cadastral Document'), ('photo', 'Photo'), ('contract', 'Contract'), ('power_of_attorney', 'Power of Attorney'), ('other', 'Other')], default='other', max_length=50, verbose_name='document type')), + ('title', models.CharField(blank=True, max_length=255, verbose_name='title')), + ('file', models.FileField(upload_to='evaluation/documents/%Y/%m/', verbose_name='file')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='uploaded_documents', to=settings.AUTH_USER_MODEL, verbose_name='uploaded by')), + ('valuation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='evaluation.valuationmodel', verbose_name='valuation')), + ], + options={ + 'verbose_name': 'Valuation Document', + 'verbose_name_plural': 'Valuation Documents', + 'db_table': 'ValuationDocument', + 'ordering': ['-created_at'], + }, + ), + ] diff --git a/core/apps/evaluation/models/__init__.py b/core/apps/evaluation/models/__init__.py index 890c520..c710df3 100644 --- a/core/apps/evaluation/models/__init__.py +++ b/core/apps/evaluation/models/__init__.py @@ -1,5 +1,6 @@ from .auto import * # noqa from .customer import * # noqa +from .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/models/document.py b/core/apps/evaluation/models/document.py new file mode 100644 index 0000000..b27bae6 --- /dev/null +++ b/core/apps/evaluation/models/document.py @@ -0,0 +1,56 @@ +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.document import DocumentType + + +class ValuationDocumentModel(AbstractBaseModel): + valuation = models.ForeignKey( + ValuationModel, + on_delete=models.CASCADE, + related_name="documents", + verbose_name=_("valuation"), + ) + document_type = models.CharField( + verbose_name=_("document type"), + max_length=50, + choices=DocumentType.choices, + default=DocumentType.OTHER, + ) + title = models.CharField( + verbose_name=_("title"), + max_length=255, + blank=True, + ) + file = models.FileField( + verbose_name=_("file"), + upload_to="evaluation/documents/%Y/%m/", + ) + uploaded_by = models.ForeignKey( + "accounts.User", + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="uploaded_documents", + verbose_name=_("uploaded by"), + ) + description = models.TextField( + verbose_name=_("description"), + blank=True, + ) + + def __str__(self): + return f"{self.get_document_type_display()} — {self.valuation}" + + @classmethod + def _baker(cls): + return baker.make(cls) + + class Meta: + db_table = "ValuationDocument" + verbose_name = _("Valuation Document") + verbose_name_plural = _("Valuation Documents") + ordering = ["-created_at"] diff --git a/core/apps/evaluation/permissions/__init__.py b/core/apps/evaluation/permissions/__init__.py index 890c520..c710df3 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 .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/permissions/document.py b/core/apps/evaluation/permissions/document.py new file mode 100644 index 0000000..8f71d7f --- /dev/null +++ b/core/apps/evaluation/permissions/document.py @@ -0,0 +1,12 @@ +from rest_framework import permissions + + +class ValuationdocumentPermission(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 890c520..c710df3 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 .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/serializers/document/ValuationDocument.py b/core/apps/evaluation/serializers/document/ValuationDocument.py new file mode 100644 index 0000000..3c36f8b --- /dev/null +++ b/core/apps/evaluation/serializers/document/ValuationDocument.py @@ -0,0 +1,28 @@ +from rest_framework import serializers + +from core.apps.evaluation.models import ValuationDocumentModel + + +class BaseValuationdocumentSerializer(serializers.ModelSerializer): + class Meta: + model = ValuationDocumentModel + fields = [ + "id", + "document_type", + ] + + +class ListValuationdocumentSerializer(BaseValuationdocumentSerializer): + class Meta(BaseValuationdocumentSerializer.Meta): ... + + +class RetrieveValuationdocumentSerializer(BaseValuationdocumentSerializer): + class Meta(BaseValuationdocumentSerializer.Meta): ... + + +class CreateValuationdocumentSerializer(BaseValuationdocumentSerializer): + class Meta(BaseValuationdocumentSerializer.Meta): + fields = [ + "id", + "document_type", + ] diff --git a/core/apps/evaluation/serializers/document/__init__.py b/core/apps/evaluation/serializers/document/__init__.py new file mode 100644 index 0000000..307d410 --- /dev/null +++ b/core/apps/evaluation/serializers/document/__init__.py @@ -0,0 +1 @@ +from .ValuationDocument import * # noqa diff --git a/core/apps/evaluation/signals/__init__.py b/core/apps/evaluation/signals/__init__.py index 890c520..c710df3 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 .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/signals/document.py b/core/apps/evaluation/signals/document.py new file mode 100644 index 0000000..16e2648 --- /dev/null +++ b/core/apps/evaluation/signals/document.py @@ -0,0 +1,8 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver + +from core.apps.evaluation.models import ValuationDocumentModel + + +@receiver(post_save, sender=ValuationDocumentModel) +def ValuationdocumentSignal(sender, instance, created, **kwargs): ... diff --git a/core/apps/evaluation/tests/__init__.py b/core/apps/evaluation/tests/__init__.py index 890c520..c710df3 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 .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/tests/document/__init__.py b/core/apps/evaluation/tests/document/__init__.py new file mode 100644 index 0000000..6d4c049 --- /dev/null +++ b/core/apps/evaluation/tests/document/__init__.py @@ -0,0 +1 @@ +from .test_ValuationDocument import * # noqa diff --git a/core/apps/evaluation/tests/document/test_ValuationDocument.py b/core/apps/evaluation/tests/document/test_ValuationDocument.py new file mode 100644 index 0000000..b7b3215 --- /dev/null +++ b/core/apps/evaluation/tests/document/test_ValuationDocument.py @@ -0,0 +1,101 @@ +import pytest +from django.urls import reverse +from rest_framework.test import APIClient + +from core.apps.evaluation.models import ValuationDocumentModel + + +@pytest.fixture +def instance(db): + return ValuationDocumentModel._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("valuation-document-list"), + "retrieve": reverse("valuation-document-detail", kwargs={"pk": instance.pk}), + "retrieve-not-found": reverse("valuation-document-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 890c520..c710df3 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 .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/translation/document.py b/core/apps/evaluation/translation/document.py new file mode 100644 index 0000000..5cbba53 --- /dev/null +++ b/core/apps/evaluation/translation/document.py @@ -0,0 +1,8 @@ +from modeltranslation.translator import TranslationOptions, register + +from core.apps.evaluation.models import ValuationDocumentModel + + +@register(ValuationDocumentModel) +class ValuationdocumentTranslation(TranslationOptions): + fields = [] diff --git a/core/apps/evaluation/urls.py b/core/apps/evaluation/urls.py index d1e5dbf..02cf61f 100644 --- a/core/apps/evaluation/urls.py +++ b/core/apps/evaluation/urls.py @@ -9,11 +9,13 @@ from .views import ( PropertyOwnerView, QuickEvaluationView, RealEstateEvaluationView, + ValuationDocumentView, ValuationView, VehicleView, ) router = DefaultRouter() +router.register("valuation-document", ValuationDocumentView, basename="valuation-document") 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") diff --git a/core/apps/evaluation/validators/__init__.py b/core/apps/evaluation/validators/__init__.py index 890c520..c710df3 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 .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/validators/document.py b/core/apps/evaluation/validators/document.py new file mode 100644 index 0000000..7bee7f6 --- /dev/null +++ b/core/apps/evaluation/validators/document.py @@ -0,0 +1,8 @@ +# from django.core.exceptions import ValidationError + + +class ValuationdocumentValidator: + 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 890c520..c710df3 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 .document import * # noqa from .movable import * # noqa from .quick import * # noqa from .real_estate import * # noqa diff --git a/core/apps/evaluation/views/document.py b/core/apps/evaluation/views/document.py new file mode 100644 index 0000000..d74407a --- /dev/null +++ b/core/apps/evaluation/views/document.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 ValuationDocumentModel +from core.apps.evaluation.serializers.document import ( + CreateValuationdocumentSerializer, + ListValuationdocumentSerializer, + RetrieveValuationdocumentSerializer, +) + + +@extend_schema(tags=["ValuationDocument"]) +class ValuationDocumentView(BaseViewSetMixin, ReadOnlyModelViewSet): + queryset = ValuationDocumentModel.objects.all() + serializer_class = ListValuationdocumentSerializer + permission_classes = [AllowAny] + + action_permission_classes = {} + action_serializer_class = { + "list": ListValuationdocumentSerializer, + "retrieve": RetrieveValuationdocumentSerializer, + "create": CreateValuationdocumentSerializer, + }