add project location

This commit is contained in:
behruz-dev
2025-08-07 12:40:16 +05:00
parent 2162fc2cdc
commit ec69b6f80b
17 changed files with 357 additions and 20 deletions

View File

@@ -150,7 +150,7 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = 'accounts.User' AUTH_USER_MODEL = 'accounts.User'
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', env.str("SWAGGER_PROTOCOL", 'https'))
from config.conf.rest_framework import * from config.conf.rest_framework import *
from config.conf.rest_framework_simplejwt import * from config.conf.rest_framework_simplejwt import *

View File

@@ -1,6 +1,6 @@
from django.contrib import admin from django.contrib import admin
from core.apps.projects.models.project import Project, ProjectFolder from core.apps.projects.models.project import Project, ProjectFolder, ProjectLocation
@@ -15,3 +15,8 @@ class ProjectAdmin(admin.ModelAdmin):
class ProjectFolderAdmin(admin.ModelAdmin): class ProjectFolderAdmin(admin.ModelAdmin):
list_display = ['id', 'name'] list_display = ['id', 'name']
search_fields = ['name'] search_fields = ['name']
@admin.register(ProjectLocation)
class ProjectLocation(admin.ModelAdmin):
list_display = ['address', 'latitude', 'longitude']

View File

@@ -0,0 +1,33 @@
# Generated by Django 5.2.4 on 2025-08-07 12:03
import django.db.models.deletion
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('projects', '0009_project_status'),
('shared', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='ProjectLocation',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('address', models.CharField(max_length=200)),
('latitude', models.FloatField()),
('longitude', models.FloatField()),
('district', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_locations', to='shared.district')),
('region', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_locations', to='shared.region')),
],
options={
'verbose_name': 'Loyiha lokatsiyasi',
'verbose_name_plural': 'Loyiha lokatsiyalari',
},
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.4 on 2025-08-07 12:04
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('projects', '0010_projectlocation'),
]
operations = [
migrations.AlterField(
model_name='project',
name='location',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projects', to='projects.projectlocation'),
),
]

View File

@@ -1,7 +1,7 @@
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from core.apps.shared.models import BaseModel from core.apps.shared.models import BaseModel, Region, District
from core.apps.projects.models.builder import Builder from core.apps.projects.models.builder import Builder
from core.apps.accounts.models.user import User from core.apps.accounts.models.user import User
from core.apps.wherehouse.models.wherehouse import WhereHouse from core.apps.wherehouse.models.wherehouse import WhereHouse
@@ -27,6 +27,23 @@ class ProjectFolder(BaseModel):
verbose_name_plural = _('Loyiha papkalari') verbose_name_plural = _('Loyiha papkalari')
class ProjectLocation(BaseModel):
address = models.CharField(max_length=200)
region = models.ForeignKey(Region, on_delete=models.CASCADE, related_name='project_locations')
district = models.ForeignKey(
District, on_delete=models.CASCADE, related_name='project_locations'
)
latitude = models.FloatField()
longitude = models.FloatField()
def __str__(self):
return self.address
class Meta:
verbose_name = _("Loyiha lokatsiyasi")
verbose_name_plural = _("Loyiha lokatsiyalari")
class Project(BaseModel): class Project(BaseModel):
STATUS = ( STATUS = (
('PLANNED', 'planned'), ('PLANNED', 'planned'),
@@ -36,7 +53,9 @@ class Project(BaseModel):
) )
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
location = models.CharField(max_length=200) location = models.ForeignKey(
ProjectLocation, on_delete=models.SET_NULL, null=True, related_name='projects'
)
start_date = models.DateField() start_date = models.DateField()
end_date = models.DateField() end_date = models.DateField()
folder = models.ForeignKey( folder = models.ForeignKey(

View File

@@ -2,10 +2,13 @@ from django.db import transaction
from rest_framework import serializers from rest_framework import serializers
from core.apps.projects.models.project import Project, ProjectFolder from core.apps.projects.models.project import Project, ProjectFolder, ProjectLocation
from core.apps.projects.serializers.project_location import ProjectLocationSerializer, ProjectLocationListSerializer
class ProjectListSerializer(serializers.ModelSerializer): class ProjectListSerializer(serializers.ModelSerializer):
location = ProjectLocationListSerializer()
class Meta: class Meta:
model = Project model = Project
fields = [ fields = [
@@ -14,6 +17,8 @@ class ProjectListSerializer(serializers.ModelSerializer):
class ProjectDetailSerialzier(serializers.ModelSerializer): class ProjectDetailSerialzier(serializers.ModelSerializer):
location = ProjectLocationListSerializer()
class Meta: class Meta:
model = Project model = Project
fields = [ fields = [
@@ -21,8 +26,33 @@ class ProjectDetailSerialzier(serializers.ModelSerializer):
] ]
class ProjectUpdateSerialzier(serializers.ModelSerializer):
location = ProjectLocationSerializer()
class Meta:
model = Project
fields = [
'id', 'name', 'location', 'start_date', 'end_date', 'status', 'benifit_plan'
]
def update(self, instance, validated_data):
location = validated_data.get('location')
instance.name = validated_data.get('name', instance.name)
instance.start_date = validated_data.get('start_date', instance.start_date)
instance.end_date = validated_data.get('end_date', instance.end_date)
instance.status = validated_data.get('name', instance.status)
instance.location.region = location.get('region', instance.location.region)
instance.location.district = location.get('district', instance.location.district)
instance.location.longitude = location.get('longitude', instance.location.longitude)
instance.location.latitude = location.get('latitude', instance.location.latitude)
instance.location.address = location.get('address', instance.location.address)
instance.location.save()
instance.save()
return instance
class ProjectCreateSerializer(serializers.Serializer): class ProjectCreateSerializer(serializers.Serializer):
location = serializers.CharField() location = ProjectLocationSerializer()
start_date = serializers.DateField() start_date = serializers.DateField()
end_date = serializers.DateField() end_date = serializers.DateField()
name = serializers.CharField() name = serializers.CharField()
@@ -48,11 +78,20 @@ class ProjectCreateSerializer(serializers.Serializer):
builder_id = validated_data.pop('builder_id') builder_id = validated_data.pop('builder_id')
with transaction.atomic(): with transaction.atomic():
location_data = validated_data.get('location')
location = ProjectLocation.objects.create(
address=location_data.get('address'),
region=location_data.get('region'),
district=location_data.get('district'),
latitude=location_data.get('latitude'),
longitude=location_data.get('longitude'),
)
project = Project.objects.create( project = Project.objects.create(
name=validated_data.get('name'), name=validated_data.get('name'),
start_date=validated_data.get('start_date'), start_date=validated_data.get('start_date'),
end_date=validated_data.get('end_date'), end_date=validated_data.get('end_date'),
location=validated_data.get('location'), location=location,
area=validated_data.get('area'), area=validated_data.get('area'),
currency=validated_data.get('currency'), currency=validated_data.get('currency'),
benifit_plan=validated_data.get('benifit_plan'), benifit_plan=validated_data.get('benifit_plan'),
@@ -89,10 +128,22 @@ class ProjectFolderListSerializer(serializers.ModelSerializer):
class ProjectFolderProjectCreateSerializer(serializers.Serializer): class ProjectFolderProjectCreateSerializer(serializers.Serializer):
folder_id = serializers.UUIDField() folder_id = serializers.UUIDField()
name = serializers.CharField() location = ProjectLocationSerializer()
location = serializers.CharField()
start_date = serializers.DateField() start_date = serializers.DateField()
end_date = serializers.DateField() end_date = serializers.DateField()
name = serializers.CharField()
builder_id = serializers.UUIDField()
area = serializers.IntegerField()
boss = serializers.ListSerializer(child=serializers.UUIDField())
foreman = serializers.ListSerializer(child=serializers.UUIDField())
other_members = serializers.ListSerializer(child=serializers.UUIDField())
wherehouse = serializers.ListSerializer(child=serializers.UUIDField())
cash_transaction = serializers.ListSerializer(child=serializers.UUIDField())
currency = serializers.ChoiceField(choices=[('uzs', 'uzs'), ('usd', 'usd')])
benifit_plan = serializers.IntegerField()
def validate(self, data): def validate(self, data):
folder = ProjectFolder.objects.filter(id=data['folder_id']).first() folder = ProjectFolder.objects.filter(id=data['folder_id']).first()
@@ -102,15 +153,43 @@ class ProjectFolderProjectCreateSerializer(serializers.Serializer):
return data return data
def create(self, validated_data): def create(self, validated_data):
boss_ids = validated_data.pop('boss')
foreman_ids = validated_data.pop('foreman')
other_member_ids = validated_data.pop('other_members')
warehouse_ids = validated_data.pop('wherehouse')
cash_transaction_ids = validated_data.pop('cash_transaction')
builder_id = validated_data.pop('builder_id')
with transaction.atomic(): with transaction.atomic():
return Project.objects.create( location_data = validated_data.get('location')
name=validated_data.get('name'), location = ProjectLocation.objects.create(
folder=validated_data.get('folder'), address=location_data.get('address'),
location=validated_data.get('location'), region=location_data.get('region'),
start_date=validated_data.get('start_date'), district=location_data.get('district'),
end_date=validated_data.get('end_date') latitude=location_data.get('latitude'),
longitude=location_data.get('longitude'),
) )
project = Project.objects.create(
name=validated_data.get('name'),
start_date=validated_data.get('start_date'),
end_date=validated_data.get('end_date'),
location=location,
area=validated_data.get('area'),
currency=validated_data.get('currency'),
benifit_plan=validated_data.get('benifit_plan'),
builder_id=builder_id,
folder=validated_data.get('folder')
)
project.boss.set(boss_ids)
project.foreman.set(foreman_ids)
project.other_members.set(other_member_ids)
project.wherehouse.set(warehouse_ids)
project.cash_transaction.set(cash_transaction_ids)
return project
class ProjectFolderUpdateSerializer(serializers.ModelSerializer): class ProjectFolderUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:

View File

@@ -0,0 +1,49 @@
from rest_framework import serializers
from core.apps.shared.models import Region, District
from core.apps.projects.models import ProjectLocation
class ProjectLocationSerializer(serializers.Serializer):
address = serializers.CharField()
region_id = serializers.UUIDField()
district_id = serializers.UUIDField()
longitude = serializers.FloatField()
latitude = serializers.FloatField()
def validate(self, data):
if data.get('region_id'):
region = Region.objects.filter(id=data.get('region_id')).first()
if not region:
raise serializers.ValidationError("Region not found")
data['region'] = region
if data.get('district_id'):
district = District.objects.filter(id=data['district_id']).first()
if not district:
raise serializers.ValidationError("District not found")
data['district'] = district
return data
class ProjectLocationListSerializer(serializers.ModelSerializer):
region = serializers.SerializerMethodField(method_name='get_region')
district = serializers.SerializerMethodField(method_name='get_district')
class Meta:
model = ProjectLocation
fields = [
'id', 'address', 'latitude', 'longitude', 'region', 'district'
]
def get_region(self, obj):
return {
'id': obj.region.id,
'name': obj.region.name
}
def get_district(self, obj):
return {
'id': obj.district.id,
'name': obj.district.name
}

View File

@@ -11,7 +11,7 @@ from core.apps.shared.paginations.custom import CustomPageNumberPagination
class ProjectListApiView(generics.ListAPIView): class ProjectListApiView(generics.ListAPIView):
serializer_class = serializers.ProjectListSerializer serializer_class = serializers.ProjectListSerializer
queryset = Project.objects.all() queryset = Project.objects.select_related('location')
permission_classes = [HasRolePermission] permission_classes = [HasRolePermission]
required_permissions = ['project'] required_permissions = ['project']
pagination_class = CustomPageNumberPagination pagination_class = CustomPageNumberPagination
@@ -22,7 +22,7 @@ class ProjectListApiView(generics.ListAPIView):
class ProjectDetailApiView(generics.RetrieveAPIView): class ProjectDetailApiView(generics.RetrieveAPIView):
serializer_class = serializers.ProjectDetailSerialzier serializer_class = serializers.ProjectDetailSerialzier
queryset = Project.objects.all() queryset = Project.objects.select_related('location')
permission_classes = [HasRolePermission] permission_classes = [HasRolePermission]
required_permissions = ['project'] required_permissions = ['project']
lookup_field = 'id' lookup_field = 'id'
@@ -36,7 +36,7 @@ class ProjectCreateApiView(generics.CreateAPIView):
class ProjectUpdateApiView(generics.UpdateAPIView): class ProjectUpdateApiView(generics.UpdateAPIView):
serializer_class = serializers.ProjectDetailSerialzier serializer_class = serializers.ProjectUpdateSerialzier
queryset = Project.objects.all() queryset = Project.objects.all()
permission_classes = [HasRolePermission] permission_classes = [HasRolePermission]
required_permissions = ['project'] required_permissions = ['project']

View File

@@ -0,0 +1 @@
from .region import *

View File

@@ -0,0 +1,23 @@
from django.contrib import admin
from core.apps.shared.models import Region, District
class DistrictInline(admin.TabularInline):
model = District
extra = 0
show_change_link = True
show_full_result_count = True
@admin.register(Region)
class ReginAdmin(admin.ModelAdmin):
list_display = ['name']
search_fields = ['name']
inlines = [DistrictInline]
@admin.register(District)
class DistrictAdmin(admin.ModelAdmin):
list_display = ['name']
search_fields = ['name']

View File

@@ -4,3 +4,7 @@ from django.apps import AppConfig
class SharedConfig(AppConfig): class SharedConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'core.apps.shared' name = 'core.apps.shared'
def ready(self):
from . import admin

View File

@@ -0,0 +1,43 @@
# Generated by Django 5.2.4 on 2025-08-07 11:39
import django.db.models.deletion
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Region',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=200)),
],
options={
'verbose_name': 'Mintaqa',
'verbose_name_plural': 'Mintaqalar',
},
),
migrations.CreateModel(
name='District',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=200)),
('region', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='districts', to='shared.region')),
],
options={
'verbose_name': 'Tuman',
'verbose_name_plural': 'Tumanlar',
},
),
]

View File

@@ -1 +1,2 @@
from .base import BaseModel from .base import BaseModel
from .region import *

View File

@@ -0,0 +1,27 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from core.apps.shared.models import BaseModel
class Region(BaseModel):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class Meta:
verbose_name = _("Mintaqa")
verbose_name_plural = _("Mintaqalar")
class District(BaseModel):
name = models.CharField(max_length=200)
region = models.ForeignKey(Region, on_delete=models.CASCADE, related_name='districts')
def __str__(self):
return self.name
class Meta:
verbose_name = _("Tuman")
verbose_name_plural = _("Tumanlar")

View File

@@ -0,0 +1,19 @@
from rest_framework import serializers
from core.apps.shared.models import Region, District
class DistrictListSerializer(serializers.ModelSerializer):
class Meta:
model = District
fields = ['id', 'name']
class RegionListSerializer(serializers.ModelSerializer):
districts = DistrictListSerializer(many=True)
class Meta:
model = Region
fields = [
'id', 'name', 'districts'
]

View File

@@ -1,5 +1,7 @@
from django.urls import path, include from django.urls import path, include
from core.apps.shared.views import region as region_views
urlpatterns = [ urlpatterns = [
path('region/list/', region_views.RegionListApiView.as_view()),
] ]

View File

@@ -0,0 +1,13 @@
from rest_framework import generics
from core.apps.shared.serializers.region import RegionListSerializer
from core.apps.shared.models import Region
from core.apps.accounts.permissions.permissions import HasRolePermission
class RegionListApiView(generics.ListAPIView):
permission_classes = [HasRolePermission]
queryset = Region.objects.prefetch_related('districts')
serializer_class = RegionListSerializer
required_permissions = ['project', 'project_folder']