diff --git a/core/apps/evaluation/admin/__init__.py b/core/apps/evaluation/admin/__init__.py index f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/admin/__init__.py +++ b/core/apps/evaluation/admin/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/admin/valuation.py b/core/apps/evaluation/admin/valuation.py new file mode 100644 index 0000000..b219ad8 --- /dev/null +++ b/core/apps/evaluation/admin/valuation.py @@ -0,0 +1,12 @@ +from django.contrib import admin +from unfold.admin import ModelAdmin + +from core.apps.evaluation.models import ValuationModel + + +@admin.register(ValuationModel) +class ValuationAdmin(ModelAdmin): + list_display = ( + "id", + "__str__", + ) diff --git a/core/apps/evaluation/admin/vehicle.py b/core/apps/evaluation/admin/vehicle.py new file mode 100644 index 0000000..507b45f --- /dev/null +++ b/core/apps/evaluation/admin/vehicle.py @@ -0,0 +1,12 @@ +from django.contrib import admin +from unfold.admin import ModelAdmin + +from core.apps.evaluation.models import VehicleModel + + +@admin.register(VehicleModel) +class VehicleAdmin(ModelAdmin): + list_display = ( + "id", + "__str__", + ) diff --git a/core/apps/evaluation/choices/valuation.py b/core/apps/evaluation/choices/valuation.py new file mode 100644 index 0000000..fb7ae9d --- /dev/null +++ b/core/apps/evaluation/choices/valuation.py @@ -0,0 +1,33 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class EvaluationPurpose(models.TextChoices): + SALE = "sale", _("Sale") + BANK = "bank", _("Bank/Loan") + INSURANCE = "insurance", _("Insurance") + INHERITANCE = "inheritance", _("Inheritance") + COURT = "court", _("Court/Judicial") + OTHER = "other", _("Other") + + +class EvaluationType(models.TextChoices): + AUTO = "auto", _("Auto") + REAL_ESTATE = "real_estate", _("Real Estate") + MOVABLE_PROPERTY = "movable_property", _("Movable Property") + + +class ValuationStatus(models.TextChoices): + DRAFT = "draft", _("Draft") + PENDING = "pending", _("Pending") + IN_REVIEW = "in_review", _("In Review") + APPROVED = "approved", _("Approved") + REJECTED = "rejected", _("Rejected") + PAID = "paid", _("Paid") + COMPLETED = "completed", _("Completed") + + +class PaymentStatus(models.TextChoices): + UNPAID = "unpaid", _("Unpaid") + PENDING = "pending", _("Pending") + PAID = "paid", _("Paid") diff --git a/core/apps/evaluation/choices/vehicle.py b/core/apps/evaluation/choices/vehicle.py new file mode 100644 index 0000000..f8e6f4a --- /dev/null +++ b/core/apps/evaluation/choices/vehicle.py @@ -0,0 +1,28 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class FuelType(models.TextChoices): + PETROL = "petrol", _("Petrol") + DIESEL = "diesel", _("Diesel") + GAS = "gas", _("Gas") + ELECTRIC = "electric", _("Electric") + HYBRID = "hybrid", _("Hybrid") + + +class BodyType(models.TextChoices): + HATCHBACK = "hatchback", _("Hatchback") + SEDAN = "sedan", _("Sedan") + UNIVERSAL = "universal", _("Universal") + COUPE = "coupe", _("Coupe") + CABRIOLET = "cabriolet", _("Cabriolet") + LIFTBACK = "liftback", _("Liftback") + MINIVAN = "minivan", _("Minivan") + CROSSOVER = "crossover", _("Crossover") + + +class VehicleCondition(models.TextChoices): + EXCELLENT = "excellent", _("Excellent") + GOOD = "good", _("Good") + AVERAGE = "average", _("Average") + NEEDS_REPAIR = "needs_repair", _("Needs repair") diff --git a/core/apps/evaluation/filters/__init__.py b/core/apps/evaluation/filters/__init__.py index f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/filters/__init__.py +++ b/core/apps/evaluation/filters/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/filters/valuation.py b/core/apps/evaluation/filters/valuation.py new file mode 100644 index 0000000..09fe822 --- /dev/null +++ b/core/apps/evaluation/filters/valuation.py @@ -0,0 +1,13 @@ +from django_filters import rest_framework as filters + +from core.apps.evaluation.models import ValuationModel + + +class ValuationFilter(filters.FilterSet): + # name = filters.CharFilter(field_name="name", lookup_expr="icontains") + + class Meta: + model = ValuationModel + fields = [ + "name", + ] diff --git a/core/apps/evaluation/filters/vehicle.py b/core/apps/evaluation/filters/vehicle.py new file mode 100644 index 0000000..a250cde --- /dev/null +++ b/core/apps/evaluation/filters/vehicle.py @@ -0,0 +1,13 @@ +from django_filters import rest_framework as filters + +from core.apps.evaluation.models import VehicleModel + + +class VehicleFilter(filters.FilterSet): + # name = filters.CharFilter(field_name="name", lookup_expr="icontains") + + class Meta: + model = VehicleModel + fields = [ + "name", + ] diff --git a/core/apps/evaluation/forms/__init__.py b/core/apps/evaluation/forms/__init__.py index f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/forms/__init__.py +++ b/core/apps/evaluation/forms/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/forms/valuation.py b/core/apps/evaluation/forms/valuation.py new file mode 100644 index 0000000..bd2545e --- /dev/null +++ b/core/apps/evaluation/forms/valuation.py @@ -0,0 +1,10 @@ +from django import forms + +from core.apps.evaluation.models import ValuationModel + + +class ValuationForm(forms.ModelForm): + + class Meta: + model = ValuationModel + fields = "__all__" diff --git a/core/apps/evaluation/forms/vehicle.py b/core/apps/evaluation/forms/vehicle.py new file mode 100644 index 0000000..c173e1e --- /dev/null +++ b/core/apps/evaluation/forms/vehicle.py @@ -0,0 +1,10 @@ +from django import forms + +from core.apps.evaluation.models import VehicleModel + + +class VehicleForm(forms.ModelForm): + + class Meta: + model = VehicleModel + fields = "__all__" diff --git a/core/apps/evaluation/migrations/0003_vehiclemodel_valuationmodel.py b/core/apps/evaluation/migrations/0003_vehiclemodel_valuationmodel.py new file mode 100644 index 0000000..2083aec --- /dev/null +++ b/core/apps/evaluation/migrations/0003_vehiclemodel_valuationmodel.py @@ -0,0 +1,73 @@ +# Generated by Django 5.2.7 on 2026-02-13 10:20 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('evaluation', '0002_alter_customermodel_options_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='VehicleModel', + 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)), + ('tech_passport_series', models.CharField(blank=True, max_length=10, null=True, verbose_name='tech passport series')), + ('tech_passport_number', models.CharField(blank=True, max_length=20, null=True, verbose_name='tech passport number')), + ('tech_passport_issued_date', models.DateField(blank=True, null=True, verbose_name='tech passport issued date')), + ('tech_passport_issued_by', models.CharField(blank=True, max_length=255, null=True, verbose_name='tech passport issued by')), + ('license_plate', models.CharField(blank=True, max_length=20, null=True, verbose_name='license plate')), + ('brand', models.CharField(blank=True, max_length=100, null=True, verbose_name='brand')), + ('model', models.CharField(blank=True, max_length=100, null=True, verbose_name='model')), + ('manufacture_year', models.IntegerField(blank=True, null=True, verbose_name='manufacture year')), + ('vin_number', models.CharField(blank=True, max_length=25, null=True, verbose_name='VIN number')), + ('engine_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='engine number')), + ('color', models.CharField(blank=True, max_length=50, null=True, verbose_name='color')), + ('mileage', models.IntegerField(blank=True, help_text='Distance in km', null=True, verbose_name='mileage')), + ('fuel_type', models.CharField(blank=True, choices=[('petrol', 'Petrol'), ('diesel', 'Diesel'), ('gas', 'Gas'), ('electric', 'Electric'), ('hybrid', 'Hybrid')], max_length=20, null=True, verbose_name='fuel type')), + ('body_type', models.CharField(blank=True, choices=[('hatchback', 'Hatchback'), ('sedan', 'Sedan'), ('universal', 'Universal'), ('coupe', 'Coupe'), ('cabriolet', 'Cabriolet'), ('liftback', 'Liftback'), ('minivan', 'Minivan'), ('crossover', 'Crossover')], max_length=20, null=True, verbose_name='body type')), + ('condition', models.CharField(blank=True, choices=[('excellent', 'Excellent'), ('good', 'Good'), ('average', 'Average'), ('needs_repair', 'Needs repair')], max_length=20, null=True, verbose_name='condition')), + ('position', models.CharField(blank=True, max_length=50, null=True, verbose_name='position')), + ], + options={ + 'verbose_name': 'Vehicle', + 'verbose_name_plural': 'Vehicles', + 'db_table': 'Vehicle', + }, + ), + migrations.CreateModel( + name='ValuationModel', + 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)), + ('conclusion_number', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='conclusion number')), + ('evaluation_purpose', models.CharField(choices=[('sale', 'Sale'), ('bank', 'Bank/Loan'), ('insurance', 'Insurance'), ('inheritance', 'Inheritance'), ('court', 'Court/Judicial'), ('other', 'Other')], default='sale', max_length=50, verbose_name='evaluation purpose')), + ('evaluation_type', models.CharField(choices=[('auto', 'Auto'), ('real_estate', 'Real Estate'), ('movable_property', 'Movable Property')], max_length=50, verbose_name='evaluation type')), + ('evaluation_subtype', models.CharField(blank=True, max_length=100, null=True, verbose_name='evaluation subtype')), + ('status', models.CharField(choices=[('draft', 'Draft'), ('pending', 'Pending'), ('in_review', 'In Review'), ('approved', 'Approved'), ('rejected', 'Rejected'), ('paid', 'Paid'), ('completed', 'Completed')], default='draft', max_length=20, verbose_name='status')), + ('estimated_price', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True, verbose_name='estimated price')), + ('final_price', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True, verbose_name='final price')), + ('payment_status', models.CharField(choices=[('unpaid', 'Unpaid'), ('pending', 'Pending'), ('paid', 'Paid')], default='unpaid', max_length=20, verbose_name='payment status')), + ('is_courier_delivery', models.BooleanField(default=False, verbose_name='courier delivery')), + ('courier_extra_amount', models.DecimalField(decimal_places=2, default=0, max_digits=12, verbose_name='courier extra amount')), + ('notes', models.TextField(blank=True, null=True, verbose_name='notes')), + ('assigned_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_valuations', to=settings.AUTH_USER_MODEL, verbose_name='assigned to')), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='created_valuations', to=settings.AUTH_USER_MODEL, verbose_name='created by')), + ('customer', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='valuations', to='evaluation.customermodel', verbose_name='customer')), + ('property_owner', models.ForeignKey(blank=True, help_text='Keep empty if customer is the owner', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='valuations', to='evaluation.propertyownermodel', verbose_name='property owner')), + ], + options={ + 'verbose_name': 'Valuation', + 'verbose_name_plural': 'Valuations', + 'db_table': 'Valuation', + }, + ), + ] diff --git a/core/apps/evaluation/models/__init__.py b/core/apps/evaluation/models/__init__.py index f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/models/__init__.py +++ b/core/apps/evaluation/models/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/models/valuation.py b/core/apps/evaluation/models/valuation.py new file mode 100644 index 0000000..c8f5b61 --- /dev/null +++ b/core/apps/evaluation/models/valuation.py @@ -0,0 +1,127 @@ +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.valuation import ( + EvaluationPurpose, + EvaluationType, + ValuationStatus, + PaymentStatus, +) +from .customer import CustomerModel, PropertyOwnerModel + + +class ValuationModel(AbstractBaseModel): + # 📋 Asosiy ma'lumotlar + conclusion_number = models.CharField( + verbose_name=_("conclusion number"), + max_length=50, + unique=True, + blank=True, + null=True, + ) + evaluation_purpose = models.CharField( + verbose_name=_("evaluation purpose"), + max_length=50, + choices=EvaluationPurpose.choices, + default=EvaluationPurpose.SALE, + ) + evaluation_type = models.CharField( + verbose_name=_("evaluation type"), + max_length=50, + choices=EvaluationType.choices, + ) + evaluation_subtype = models.CharField( + verbose_name=_("evaluation subtype"), + max_length=100, + blank=True, + null=True, + ) + status = models.CharField( + verbose_name=_("status"), + max_length=20, + choices=ValuationStatus.choices, + default=ValuationStatus.DRAFT, + ) + + # 👥 Bog'lanishlar + customer = models.ForeignKey( + CustomerModel, + on_delete=models.PROTECT, + related_name="valuations", + verbose_name=_("customer"), + ) + property_owner = models.ForeignKey( + PropertyOwnerModel, + on_delete=models.PROTECT, + related_name="valuations", + verbose_name=_("property owner"), + null=True, + blank=True, + help_text=_("Keep empty if customer is the owner"), + ) + created_by = models.ForeignKey( + "accounts.User", + on_delete=models.PROTECT, + related_name="created_valuations", + verbose_name=_("created by"), + ) + assigned_to = models.ForeignKey( + "accounts.User", + on_delete=models.SET_NULL, + related_name="assigned_valuations", + verbose_name=_("assigned to"), + null=True, + blank=True, + ) + + # 💰 Narx va To'lov + estimated_price = models.DecimalField( + verbose_name=_("estimated price"), + max_digits=15, + decimal_places=2, + null=True, + blank=True, + ) + final_price = models.DecimalField( + verbose_name=_("final price"), + max_digits=15, + decimal_places=2, + null=True, + blank=True, + ) + payment_status = models.CharField( + verbose_name=_("payment status"), + max_length=20, + choices=PaymentStatus.choices, + default=PaymentStatus.UNPAID, + ) + + # 🚚 Yetkazib berish + is_courier_delivery = models.BooleanField( + verbose_name=_("courier delivery"), default=False + ) + courier_extra_amount = models.DecimalField( + verbose_name=_("courier extra amount"), + max_digits=12, + decimal_places=2, + default=0, + ) + + # 📝 Izohlar + notes = models.TextField(verbose_name=_("notes"), blank=True, null=True) + + def __str__(self): + return f"Valuation {self.conclusion_number} - {self.customer}" + + @classmethod + def _baker(cls): + return baker.make(cls) + + class Meta: + db_table = "Valuation" + verbose_name = _("Valuation") + verbose_name_plural = _("Valuations") + + diff --git a/core/apps/evaluation/models/vehicle.py b/core/apps/evaluation/models/vehicle.py new file mode 100644 index 0000000..c3a65ba --- /dev/null +++ b/core/apps/evaluation/models/vehicle.py @@ -0,0 +1,87 @@ +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.vehicle import FuelType, BodyType, VehicleCondition + + +class VehicleModel(AbstractBaseModel): + # 🌐 Texnik passport ma'lumotlari (API orqali olinadi) + tech_passport_series = models.CharField( + verbose_name=_("tech passport series"), max_length=10, blank=True, null=True + ) + tech_passport_number = models.CharField( + verbose_name=_("tech passport number"), max_length=20, blank=True, null=True + ) + tech_passport_issued_date = models.DateField( + verbose_name=_("tech passport issued date"), blank=True, null=True + ) + tech_passport_issued_by = models.CharField( + verbose_name=_("tech passport issued by"), max_length=255, blank=True, null=True + ) + + license_plate = models.CharField( + verbose_name=_("license plate"), max_length=20, blank=True, null=True + ) + brand = models.CharField( + verbose_name=_("brand"), max_length=100, blank=True, null=True + ) + model = models.CharField( + verbose_name=_("model"), max_length=100, blank=True, null=True + ) + manufacture_year = models.IntegerField( + verbose_name=_("manufacture year"), blank=True, null=True + ) + vin_number = models.CharField( + verbose_name=_("VIN number"), max_length=25, 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 + ) + + # 🛠 Texnik holati + mileage = models.IntegerField( + verbose_name=_("mileage"), blank=True, null=True, help_text=_("Distance in km") + ) + fuel_type = models.CharField( + verbose_name=_("fuel type"), + max_length=20, + choices=FuelType.choices, + blank=True, + null=True, + ) + body_type = models.CharField( + verbose_name=_("body type"), + max_length=20, + choices=BodyType.choices, + blank=True, + null=True, + ) + condition = models.CharField( + verbose_name=_("condition"), + max_length=20, + choices=VehicleCondition.choices, + blank=True, + null=True, + ) + position = models.CharField( + verbose_name=_("position"), max_length=50, blank=True, null=True + ) + + def __str__(self): + return f"{self.brand} {self.model} ({self.license_plate})" + + @classmethod + def _baker(cls): + return baker.make(cls) + + class Meta: + db_table = "Vehicle" + verbose_name = _("Vehicle") + verbose_name_plural = _("Vehicles") + diff --git a/core/apps/evaluation/permissions/__init__.py b/core/apps/evaluation/permissions/__init__.py index f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/permissions/__init__.py +++ b/core/apps/evaluation/permissions/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/permissions/valuation.py b/core/apps/evaluation/permissions/valuation.py new file mode 100644 index 0000000..f38020b --- /dev/null +++ b/core/apps/evaluation/permissions/valuation.py @@ -0,0 +1,12 @@ +from rest_framework import permissions + + +class ValuationPermission(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/permissions/vehicle.py b/core/apps/evaluation/permissions/vehicle.py new file mode 100644 index 0000000..f1509aa --- /dev/null +++ b/core/apps/evaluation/permissions/vehicle.py @@ -0,0 +1,12 @@ +from rest_framework import permissions + + +class VehiclePermission(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 f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/serializers/__init__.py +++ b/core/apps/evaluation/serializers/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/serializers/valuation/Valuation.py b/core/apps/evaluation/serializers/valuation/Valuation.py new file mode 100644 index 0000000..ecbec83 --- /dev/null +++ b/core/apps/evaluation/serializers/valuation/Valuation.py @@ -0,0 +1,49 @@ +from rest_framework import serializers + +from core.apps.evaluation.models import ValuationModel + + +class BaseValuationSerializer(serializers.ModelSerializer): + class Meta: + model = ValuationModel + fields = [ + "id", + "conclusion_number", + "evaluation_purpose", + "evaluation_type", + "status", + "created_at", + ] + + +class ListValuationSerializer(BaseValuationSerializer): + class Meta(BaseValuationSerializer.Meta): + pass + + +class RetrieveValuationSerializer(BaseValuationSerializer): + class Meta(BaseValuationSerializer.Meta): + fields = BaseValuationSerializer.Meta.fields + [ + "customer", + "property_owner", + "created_by", + "assigned_to", + "estimated_price", + "final_price", + "payment_status", + "notes", + ] + + +class CreateValuationSerializer(BaseValuationSerializer): + class Meta(BaseValuationSerializer.Meta): + fields = [ + "customer", + "property_owner", + "evaluation_purpose", + "evaluation_type", + "evaluation_subtype", + "is_courier_delivery", + "notes", + ] + diff --git a/core/apps/evaluation/serializers/valuation/__init__.py b/core/apps/evaluation/serializers/valuation/__init__.py new file mode 100644 index 0000000..0496a47 --- /dev/null +++ b/core/apps/evaluation/serializers/valuation/__init__.py @@ -0,0 +1 @@ +from .Valuation import * # noqa diff --git a/core/apps/evaluation/serializers/vehicle/Vehicle.py b/core/apps/evaluation/serializers/vehicle/Vehicle.py new file mode 100644 index 0000000..51e0763 --- /dev/null +++ b/core/apps/evaluation/serializers/vehicle/Vehicle.py @@ -0,0 +1,39 @@ +from rest_framework import serializers + +from core.apps.evaluation.models import VehicleModel + + +class BaseVehicleSerializer(serializers.ModelSerializer): + class Meta: + model = VehicleModel + fields = [ + "id", + "brand", + "model", + "license_plate", + "manufacture_year", + ] + + +class ListVehicleSerializer(BaseVehicleSerializer): + class Meta(BaseVehicleSerializer.Meta): + pass + + +class RetrieveVehicleSerializer(BaseVehicleSerializer): + class Meta(BaseVehicleSerializer.Meta): + fields = "__all__" + + +class CreateVehicleSerializer(BaseVehicleSerializer): + class Meta(BaseVehicleSerializer.Meta): + fields = [ + "tech_passport_series", + "tech_passport_number", + "license_plate", + "mileage", + "fuel_type", + "body_type", + "condition", + ] + diff --git a/core/apps/evaluation/serializers/vehicle/__init__.py b/core/apps/evaluation/serializers/vehicle/__init__.py new file mode 100644 index 0000000..bc2dee4 --- /dev/null +++ b/core/apps/evaluation/serializers/vehicle/__init__.py @@ -0,0 +1 @@ +from .Vehicle import * # noqa diff --git a/core/apps/evaluation/signals/__init__.py b/core/apps/evaluation/signals/__init__.py index f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/signals/__init__.py +++ b/core/apps/evaluation/signals/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/signals/valuation.py b/core/apps/evaluation/signals/valuation.py new file mode 100644 index 0000000..04213a2 --- /dev/null +++ b/core/apps/evaluation/signals/valuation.py @@ -0,0 +1,8 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver + +from core.apps.evaluation.models import ValuationModel + + +@receiver(post_save, sender=ValuationModel) +def ValuationSignal(sender, instance, created, **kwargs): ... diff --git a/core/apps/evaluation/signals/vehicle.py b/core/apps/evaluation/signals/vehicle.py new file mode 100644 index 0000000..677cb88 --- /dev/null +++ b/core/apps/evaluation/signals/vehicle.py @@ -0,0 +1,8 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver + +from core.apps.evaluation.models import VehicleModel + + +@receiver(post_save, sender=VehicleModel) +def VehicleSignal(sender, instance, created, **kwargs): ... diff --git a/core/apps/evaluation/tests/__init__.py b/core/apps/evaluation/tests/__init__.py index f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/tests/__init__.py +++ b/core/apps/evaluation/tests/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/tests/valuation/__init__.py b/core/apps/evaluation/tests/valuation/__init__.py new file mode 100644 index 0000000..4358338 --- /dev/null +++ b/core/apps/evaluation/tests/valuation/__init__.py @@ -0,0 +1 @@ +from .test_Valuation import * # noqa diff --git a/core/apps/evaluation/tests/valuation/test_Valuation.py b/core/apps/evaluation/tests/valuation/test_Valuation.py new file mode 100644 index 0000000..9929a2b --- /dev/null +++ b/core/apps/evaluation/tests/valuation/test_Valuation.py @@ -0,0 +1,59 @@ +import pytest +from django.urls import reverse +from rest_framework.test import APIClient + +from core.apps.evaluation.models import ValuationModel + + +@pytest.fixture +def instance(db): + return ValuationModel._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-list"), + "retrieve": reverse("Valuation-detail", kwargs={"pk": instance.pk}), + "retrieve-not-found": reverse("Valuation-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 + diff --git a/core/apps/evaluation/tests/vehicle/__init__.py b/core/apps/evaluation/tests/vehicle/__init__.py new file mode 100644 index 0000000..e24eace --- /dev/null +++ b/core/apps/evaluation/tests/vehicle/__init__.py @@ -0,0 +1 @@ +from .test_Vehicle import * # noqa diff --git a/core/apps/evaluation/tests/vehicle/test_Vehicle.py b/core/apps/evaluation/tests/vehicle/test_Vehicle.py new file mode 100644 index 0000000..5a0fc62 --- /dev/null +++ b/core/apps/evaluation/tests/vehicle/test_Vehicle.py @@ -0,0 +1,59 @@ +import pytest +from django.urls import reverse +from rest_framework.test import APIClient + +from core.apps.evaluation.models import VehicleModel + + +@pytest.fixture +def instance(db): + return VehicleModel._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("Vehicle-list"), + "retrieve": reverse("Vehicle-detail", kwargs={"pk": instance.pk}), + "retrieve-not-found": reverse("Vehicle-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 + diff --git a/core/apps/evaluation/translation/__init__.py b/core/apps/evaluation/translation/__init__.py index f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/translation/__init__.py +++ b/core/apps/evaluation/translation/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/translation/valuation.py b/core/apps/evaluation/translation/valuation.py new file mode 100644 index 0000000..990b168 --- /dev/null +++ b/core/apps/evaluation/translation/valuation.py @@ -0,0 +1,8 @@ +from modeltranslation.translator import TranslationOptions, register + +from core.apps.evaluation.models import ValuationModel + + +@register(ValuationModel) +class ValuationTranslation(TranslationOptions): + fields = [] diff --git a/core/apps/evaluation/translation/vehicle.py b/core/apps/evaluation/translation/vehicle.py new file mode 100644 index 0000000..acf1e8b --- /dev/null +++ b/core/apps/evaluation/translation/vehicle.py @@ -0,0 +1,8 @@ +from modeltranslation.translator import TranslationOptions, register + +from core.apps.evaluation.models import VehicleModel + + +@register(VehicleModel) +class VehicleTranslation(TranslationOptions): + fields = [] diff --git a/core/apps/evaluation/urls.py b/core/apps/evaluation/urls.py index 6842719..1b74be8 100644 --- a/core/apps/evaluation/urls.py +++ b/core/apps/evaluation/urls.py @@ -1,9 +1,11 @@ from django.urls import include, path from rest_framework.routers import DefaultRouter -from .views import CustomerView, PropertyOwnerView +from .views import CustomerView, PropertyOwnerView, ValuationView, VehicleView router = DefaultRouter() +router.register("Vehicle", VehicleView, basename="Vehicle") +router.register("Valuation", ValuationView, basename="Valuation") router.register("property-owner", PropertyOwnerView, basename="property-owner") router.register("customer", CustomerView, basename="customer") urlpatterns = [path("", include(router.urls))] diff --git a/core/apps/evaluation/validators/__init__.py b/core/apps/evaluation/validators/__init__.py index f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/validators/__init__.py +++ b/core/apps/evaluation/validators/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/validators/valuation.py b/core/apps/evaluation/validators/valuation.py new file mode 100644 index 0000000..7142f9f --- /dev/null +++ b/core/apps/evaluation/validators/valuation.py @@ -0,0 +1,8 @@ +# from django.core.exceptions import ValidationError + + +class ValuationValidator: + def __init__(self): ... + + def __call__(self): + return True diff --git a/core/apps/evaluation/validators/vehicle.py b/core/apps/evaluation/validators/vehicle.py new file mode 100644 index 0000000..92f0c4b --- /dev/null +++ b/core/apps/evaluation/validators/vehicle.py @@ -0,0 +1,8 @@ +# from django.core.exceptions import ValidationError + + +class VehicleValidator: + 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 f22e1ba..9683ac0 100644 --- a/core/apps/evaluation/views/__init__.py +++ b/core/apps/evaluation/views/__init__.py @@ -1 +1,3 @@ from .customer import * # noqa +from .valuation import * # noqa +from .vehicle import * # noqa diff --git a/core/apps/evaluation/views/valuation.py b/core/apps/evaluation/views/valuation.py new file mode 100644 index 0000000..a439393 --- /dev/null +++ b/core/apps/evaluation/views/valuation.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 ValuationModel +from core.apps.evaluation.serializers.valuation import ( + CreateValuationSerializer, + ListValuationSerializer, + RetrieveValuationSerializer, +) + + +@extend_schema(tags=["Valuation"]) +class ValuationView(BaseViewSetMixin, ReadOnlyModelViewSet): + queryset = ValuationModel.objects.all() + serializer_class = ListValuationSerializer + permission_classes = [AllowAny] + + action_permission_classes = {} + action_serializer_class = { + "list": ListValuationSerializer, + "retrieve": RetrieveValuationSerializer, + "create": CreateValuationSerializer, + } diff --git a/core/apps/evaluation/views/vehicle.py b/core/apps/evaluation/views/vehicle.py new file mode 100644 index 0000000..d997480 --- /dev/null +++ b/core/apps/evaluation/views/vehicle.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 VehicleModel +from core.apps.evaluation.serializers.vehicle import ( + CreateVehicleSerializer, + ListVehicleSerializer, + RetrieveVehicleSerializer, +) + + +@extend_schema(tags=["Vehicle"]) +class VehicleView(BaseViewSetMixin, ReadOnlyModelViewSet): + queryset = VehicleModel.objects.all() + serializer_class = ListVehicleSerializer + permission_classes = [AllowAny] + + action_permission_classes = {} + action_serializer_class = { + "list": ListVehicleSerializer, + "retrieve": RetrieveVehicleSerializer, + "create": CreateVehicleSerializer, + }