Compare commits

...

10 Commits

Author SHA1 Message Date
behruz
8ae2b9cce1 fix 500 bug 2025-12-12 11:03:50 +05:00
behruz
437e9aeef9 add search for contract list 2025-12-12 10:58:09 +05:00
behruz-dev
1fa76dad96 change two api 2025-12-03 13:42:06 +05:00
behruz-dev
9890bbea8e contractni ochirish uchun delete api qoshildi 2025-12-01 17:01:32 +05:00
behruz-dev
2587a220a6 fiux 2025-11-28 15:52:06 +05:00
behruz-dev
31d52667c6 change redis port 2025-11-28 15:48:56 +05:00
behruz-dev
2a77a18197 remove and recreate migration 2025-11-28 15:37:30 +05:00
behruz-dev
c54886d586 fix 2025-11-11 19:43:46 +05:00
behruz-dev
5626269999 fox 2025-11-05 13:54:25 +05:00
behruz-dev
dd6c7ce9dc accounts: add user profile and user update api 2025-11-05 12:09:13 +05:00
28 changed files with 367 additions and 17 deletions

View File

@@ -27,6 +27,7 @@ urlpatterns = [
[
path('', include('core.apps.accounts.urls')),
path('contracts/', include('core.apps.contracts.urls')),
path('shared/', include('core.apps.shared.urls')),
]
)),
]

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.2 on 2025-07-22 15:26
# Generated by Django 5.2 on 2025-11-28 15:37
import core.apps.accounts.managers.user
import django.core.validators

View File

@@ -0,0 +1,28 @@
from rest_framework import serializers
from core.apps.accounts.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'id', 'phone', 'indentification_num', 'profile_image', 'first_name', 'last_name', 'email', 'role'
]
class UserUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'indentification_num', 'profile_image', 'first_name', 'last_name', 'email'
]
def update(self, instance, validated_data):
instance.indentification_num = validated_data.get('indentification_num', instance.indentification_num)
instance.profile_image = validated_data.get('profile_image', instance.profile_image)
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.email = validated_data.get('email', instance.email)
instance.save()
return instance

View File

@@ -2,6 +2,7 @@ from django.urls import path, include
from core.apps.accounts.views.auth import LoginApiView, RegisterApiView, ConfirUserApiView, ChoiceUserRoleApiView, SearchUserPhoneApiView
from core.apps.accounts.views.forgot_password import ConfirmCodeApiView, SendCodeApiView, ResetPasswordApiView
from core.apps.accounts.views.user import UserProfileApiView, UserProfileUpdateApiView
urlpatterns = [
path('auth/', include(
@@ -15,6 +16,8 @@ urlpatterns = [
path('user/', include(
[
path('<str:number>/search/', SearchUserPhoneApiView.as_view()),
path('profile/', UserProfileApiView.as_view()),
path('profile/update/', UserProfileUpdateApiView.as_view()),
]
)),
path('forgot_password/', include(

View File

@@ -0,0 +1,29 @@
from rest_framework import generics, views
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from core.apps.accounts.serializers import user as serializers
from core.apps.accounts.models import User
class UserProfileApiView(views.APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
user = request.user
serializer = serializers.UserSerializer(user)
return Response(serializer.data, status=200)
class UserProfileUpdateApiView(generics.GenericAPIView):
serializer_class = serializers.UserUpdateSerializer
permission_classes = [IsAuthenticated]
queryset = User.objects.all()
def patch(self, request):
user = request.user
serializer = self.serializer_class(data=request.data, instance=user)
if serializer.is_valid(raise_exception=True):
data = serializer.save()
return Response(serializers.UserSerializer(data).data, status=200)
return Response(serializer.errors, status=400)

View File

@@ -0,0 +1,2 @@
from .contract import *
from .folder import *

View File

@@ -0,0 +1,8 @@
from django.contrib import admin
from core.apps.contracts.models.folder import Folder
@admin.register(Folder)
class FolderAdmin(admin.ModelAdmin):
list_display = ['id', 'name']

View File

@@ -6,4 +6,4 @@ class ContractsConfig(AppConfig):
name = 'core.apps.contracts'
def ready(self):
import core.apps.contracts.admins.contract
import core.apps.contracts.admins

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.2 on 2025-07-17 14:48
# Generated by Django 5.2 on 2025-11-28 15:37
import django.db.models.deletion
import uuid
@@ -30,7 +30,7 @@ class Migration(migrations.Migration):
('attach_file', models.BooleanField(default=False)),
('add_folder', models.BooleanField(default=False)),
('add_notification', models.BooleanField(default=False)),
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='c', to=settings.AUTH_USER_MODEL)),
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contracts', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'contract',
@@ -94,4 +94,23 @@ class Migration(migrations.Migration):
'db_table': 'contract_signature_codes',
},
),
migrations.CreateModel(
name='Folder',
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)),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='folders', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Fayl',
'verbose_name_plural': 'Fayllar',
},
),
migrations.AddField(
model_name='contract',
name='folder',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contracts', to='contracts.folder'),
),
]

View File

@@ -1 +1,2 @@
from .contract import *
from .folder import *

View File

@@ -6,6 +6,7 @@ from django.contrib.auth import get_user_model
from django.utils import timezone
from core.apps.shared.models.base import BaseModel
from core.apps.contracts.models.folder import Folder
from core.apps.contracts.enums.contract import SIDES, STATUS
from core.apps.contracts.enums.contract_side import ROLE
from core.apps.contracts.enums.contract_signature import SIGNATURE_TYPE, SIGNATURE_STATUS
@@ -24,7 +25,8 @@ class Contract(BaseModel):
add_folder = models.BooleanField(default=False)
add_notification = models.BooleanField(default=False)
company = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='c')
company = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='contracts')
folder = models.ForeignKey(Folder, on_delete=models.SET_NULL, null=True, blank=True, related_name='contracts')
def __str__(self):
return f'{self.name}'

View File

@@ -0,0 +1,16 @@
from django.db import models
from core.apps.shared.models import BaseModel
from core.apps.accounts.models import User
class Folder(BaseModel):
name = models.CharField(max_length=200)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='folders', null=True )
def __str__(self):
return f"{self.name} folder {self.user.username}"
class Meta:
verbose_name = "Fayl"
verbose_name_plural = "Fayllar"

View File

@@ -2,7 +2,7 @@ from django.db import transaction
from rest_framework import serializers
from core.apps.contracts.models.contract import Contract
from core.apps.contracts.models.contract import Contract, Folder
from core.apps.contracts.serializers.contract_side import ContractSideCreateSerializer, ContractSideListSerializer
@@ -15,6 +15,15 @@ class ContractCreateSerializer(serializers.Serializer):
attach_file = serializers.BooleanField()
add_folder = serializers.BooleanField()
add_notification = serializers.BooleanField()
folder_id = serializers.UUIDField(required=False)
def validate(self, data):
if data.get('folder_id'):
folder = Folder.objects.filter(id=data.get('folder_id')).first()
if not folder:
raise serializers.ValidationError("Folder not found")
data['folder'] = folder
return data
def create(self, validated_data):
with transaction.atomic():
@@ -28,8 +37,10 @@ class ContractCreateSerializer(serializers.Serializer):
attach_file=validated_data.pop('attach_file'),
add_folder=validated_data.pop('add_folder'),
add_notification=validated_data.pop('add_notification'),
company=user
company=user,
folder=validated_data.get('folder'),
)
return contract.id
@@ -49,3 +60,14 @@ class ContractDetailSerializer(serializers.ModelSerializer):
fields = [
'id', 'name', 'file', 'status', 'contract_number', 'contract_sides',
]
class ContractUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Contract
fields = ['folder']
def update(self, instance, validated_data):
instance.folder = validated_data.get('folder', instance.folder)
instance.save()
return instance

View File

@@ -40,5 +40,7 @@ class ContractSideListSerializer(serializers.ModelSerializer):
]
def get_contract_signature(self, obj):
contract_signature = obj.contract_signatures
return ContractSignatureListSerializer(contract_signature).data if contract_signature else None
signature = getattr(obj, "contract_signatures", None)
if signature is None:
return None
return ContractSignatureListSerializer(signature).data if signature else None

View File

@@ -0,0 +1,42 @@
from rest_framework import serializers
from core.apps.contracts.models import Folder
from core.apps.contracts.serializers.contract import ContractListSerializer
class FolderListSerializer(serializers.ModelSerializer):
class Meta:
model = Folder
fields = [
'id', 'name'
]
class FolderSerializer(serializers.ModelSerializer):
class Meta:
model = Folder
fields = [
'name'
]
def create(self, validated_data):
folder = Folder.objects.create(
name=validated_data.get('name'),
user=self.context.get('user'),
)
return folder
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.save()
return instance
class FolderDetailSerializer(serializers.ModelSerializer):
contracts = ContractListSerializer(many=True)
class Meta:
model = Folder
fields = [
'id', 'name', 'contracts'
]

View File

@@ -3,6 +3,7 @@ from django.urls import path, include
from core.apps.contracts.views import contract as contract_views
from core.apps.contracts.views import contract_side as contract_side_views
from core.apps.contracts.views import contract_signature as contract_signature_views
from core.apps.contracts.views import folder as folder_views
urlpatterns = [
@@ -11,6 +12,8 @@ urlpatterns = [
path('create/', contract_views.ContractCreateApiView.as_view(), name='create-contract'),
path('list/', contract_views.ContractListApiView.as_view(), name='list-contract'),
path('<uuid:id>/', contract_views.ContractDetailApiView.as_view(), name='detail-contract'),
path('<uuid:id>/update/', contract_views.ContractUpdateApiView.as_view()),
path('<uuid:id>/delete/', contract_views.ContractDeleteApiView.as_view()),
]
)),
path('contract_side/', include([
@@ -22,5 +25,14 @@ urlpatterns = [
path('send_signature_code/<uuid:signature_id>/', contract_signature_views.SendContractSignatureCodeApiView.as_view(), name='send-signature-code'),
path('sign_contract/', contract_signature_views.SigningContractApiView.as_view(), name='sign-contract'),
]
))
)),
path('folder/', include(
[
path('list/', folder_views.FolderListApiView.as_view()),
path('create/', folder_views.FolderCreateApiView.as_view()),
path('<uuid:folder_id>/update/', folder_views.FolderUpdateApiView.as_view()),
path('<uuid:id>/contracts/', folder_views.ContractListApiView.as_view()),
path('<uuid:id>/delete/', folder_views.ContractFolderDeleteApiView.as_view()),
]
)),
]

View File

@@ -1,5 +1,8 @@
from django.shortcuts import get_object_or_404
from rest_framework import generics, views, status, permissions, parsers
from rest_framework.response import Response
from rest_framework.filters import SearchFilter
from core.apps.contracts.serializers import contract as contract_serializer
from core.apps.contracts.models.contract import Contract
@@ -26,6 +29,8 @@ class ContractCreateApiView(generics.CreateAPIView):
class ContractListApiView(generics.ListAPIView):
serializer_class = contract_serializer.ContractListSerializer
queryset = Contract.objects.all()
filter_backends = [SearchFilter]
search_fields = ['name']
def get_queryset(self):
return Contract.objects.filter(contract_sides__user=self.request.user)
@@ -38,3 +43,26 @@ class ContractDetailApiView(views.APIView):
return error_message("Contract not found", 404)
serializer = contract_serializer.ContractDetailSerializer(contract)
return Response(serializer.data, status=200)
class ContractUpdateApiView(generics.GenericAPIView):
serializer_class = contract_serializer.ContractUpdateSerializer
queryset = Contract.objects.all()
permission_classes = [permissions.IsAuthenticated]
def patch(self, request, id):
contract = get_object_or_404(Contract, id=id)
serializer = self.serializer_class(data=request.data, instance=contract)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response({"success": True, 'message': 'updated'}, status=200)
return Response({'success': False, 'message': serializer.errors}, status=400)
class ContractDeleteApiView(views.APIView):
permission_classes = [permissions.IsAuthenticated]
def delete(self, request, id):
contract = get_object_or_404(Contract, id=id)
contract.delete()
return Response({'success': True, 'message': "deleted"}, status=204)

View File

@@ -0,0 +1,66 @@
from django.shortcuts import get_object_or_404
from rest_framework import generics, views
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from core.apps.contracts.models.folder import Folder
from core.apps.contracts.serializers import folder as serializers
class FolderListApiView(views.APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
user = request.user
folders = Folder.objects.filter(user=user)
serializer = serializers.FolderListSerializer(folders, many=True)
return Response(serializer.data, status=200)
class FolderCreateApiView(generics.GenericAPIView):
serializer_class = serializers.FolderSerializer
queryset = Folder.objects.all()
permission_classes = [IsAuthenticated]
def post(self, request):
user = request.user
serializer = self.serializer_class(data=request.data, context={"user": user})
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response({"message":"Folder qoshild"}, status=200)
return Response(serializer.errors, status=400)
class FolderUpdateApiView(generics.GenericAPIView):
serializer_class = serializers.FolderSerializer
queryset = Folder.objects.all()
permission_classes = [IsAuthenticated]
def patch(self, request, folder_id):
user = request.user
folder = get_object_or_404(Folder, id=folder_id, user=user)
serializer = self.serializer_class(data=request.data, instance=folder)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response({"message":"Folder tahrirlandi"}, status=200)
return Response(serializer.errors, status=400)
class ContractListApiView(generics.GenericAPIView):
serializer_class = serializers.FolderDetailSerializer
queryset = Folder.objects.all()
permission_classes = [IsAuthenticated]
def get(self, request, id):
folder = get_object_or_404(Folder, id=id, user=request.user)
serializer = self.serializer_class(folder)
return Response(serializer.data)
class ContractFolderDeleteApiView(views.APIView):
permission_classes = [IsAuthenticated]
def delete(self, request, id):
folder = get_object_or_404(Folder, id=id)
folder.delete()
return Response(status=204)

View File

@@ -0,0 +1,2 @@
from .contact_us import *
from .folder import *

View File

@@ -0,0 +1,6 @@
from django.contrib import admin
from core.apps.shared.models import ContactUs
admin.site.register(ContactUs)

View File

@@ -6,4 +6,4 @@ class SharedConfig(AppConfig):
name = 'core.apps.shared'
def ready(self):
import core.apps.shared.admins.folder
import core.apps.shared.admins

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.2 on 2025-07-15 15:36
# Generated by Django 5.2 on 2025-11-28 15:37
import uuid
from django.db import migrations, models
@@ -12,6 +12,19 @@ class Migration(migrations.Migration):
]
operations = [
migrations.CreateModel(
name='ContactUs',
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)),
('phone_number', models.CharField(max_length=20)),
('telegram_url', models.URLField()),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Folder',
fields=[

View File

@@ -1,2 +1,3 @@
from .base import *
from .folder import *
from .contact_us import *

View File

@@ -0,0 +1,12 @@
from django.db import models
from core.apps.shared.models.base import BaseModel
class ContactUs(BaseModel):
phone_number = models.CharField(max_length=20)
telegram_url = models.URLField()
def __str__(self):
return self.phone_number

View File

@@ -0,0 +1,9 @@
from rest_framework import serializers
from core.apps.shared.models.contact_us import ContactUs
class ContactUsSerializer(serializers.ModelSerializer):
class Meta:
model = ContactUs
fields = '__all__'

8
core/apps/shared/urls.py Normal file
View File

@@ -0,0 +1,8 @@
from django.urls import path
from core.apps.shared.views.contact_us import ContactUsApiView
urlpatterns = [
path('contact_us/', ContactUsApiView.as_view()),
]

View File

@@ -0,0 +1,18 @@
from rest_framework import generics
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from core.apps.shared.utils.response import success_message, error_message
from core.apps.shared.serializers.contact_us import ContactUsSerializer
from core.apps.shared.models import ContactUs
class ContactUsApiView(generics.GenericAPIView):
permission_classes = [IsAuthenticated]
queryset = ContactUs.objects.all()
serializer_class = ContactUsSerializer
def get(self, request):
obj = ContactUs.objects.first()
serializer = self.serializer_class(obj)
return Response(serializer.data, status=200)

View File

@@ -79,7 +79,7 @@ services:
restart: always
image: redis:latest
ports:
- 6380:6379
- 6387:6379
# bot:
# build: