From 1f09a4bff5730c623cd0ff3b1bef27842a8c6f28 Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Fri, 22 Aug 2025 16:34:02 +0500 Subject: [PATCH] add new field to party mode --- core/apps/orders/admin/party.py | 10 +++-- .../0018_party_is_deleted_deletedparty.py | 34 ++++++++++++++ .../migrations/0019_deletedparty_comment.py | 18 ++++++++ .../0020_party_discount_currency.py | 18 ++++++++ core/apps/orders/models/party.py | 26 ++++++++++- core/apps/orders/serializers/party.py | 37 ++++++++++++++- core/apps/orders/urls.py | 2 + core/apps/orders/views/party.py | 45 ++++++++++++++++--- 8 files changed, 178 insertions(+), 12 deletions(-) create mode 100644 core/apps/orders/migrations/0018_party_is_deleted_deletedparty.py create mode 100644 core/apps/orders/migrations/0019_deletedparty_comment.py create mode 100644 core/apps/orders/migrations/0020_party_discount_currency.py diff --git a/core/apps/orders/admin/party.py b/core/apps/orders/admin/party.py index 34c70ec..427f2c3 100644 --- a/core/apps/orders/admin/party.py +++ b/core/apps/orders/admin/party.py @@ -1,6 +1,6 @@ from django.contrib import admin -from core.apps.orders.models import Party, PartyAmount +from core.apps.orders.models import Party, PartyAmount, DeletedParty class PartyAmountInline(admin.StackedInline): @@ -11,7 +11,7 @@ class PartyAmountInline(admin.StackedInline): @admin.register(Party) class PartyAdmin(admin.ModelAdmin): - list_display = ['mediator', 'delivery_date', 'payment_date'] + list_display = ['mediator', 'delivery_date', 'payment_date', 'is_deleted'] inlines = [PartyAmountInline] @@ -20,4 +20,8 @@ class PartyAmountAdmin(admin.ModelAdmin): list_display = ['id', 'total_price', 'cost_amount'] def has_module_permission(self, request): - return False \ No newline at end of file + return False + +@admin.register(DeletedParty) +class DeletedPartyAdmin(admin.ModelAdmin): + list_display = ['id', 'deleted_date', 'party'] \ No newline at end of file diff --git a/core/apps/orders/migrations/0018_party_is_deleted_deletedparty.py b/core/apps/orders/migrations/0018_party_is_deleted_deletedparty.py new file mode 100644 index 0000000..713803c --- /dev/null +++ b/core/apps/orders/migrations/0018_party_is_deleted_deletedparty.py @@ -0,0 +1,34 @@ +# Generated by Django 5.2.4 on 2025-08-22 14:47 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0017_alter_party_confirmation'), + ] + + operations = [ + migrations.AddField( + model_name='party', + name='is_deleted', + field=models.BooleanField(default=False), + ), + migrations.CreateModel( + name='DeletedParty', + 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)), + ('deleted_date', models.DateField(auto_now_add=True)), + ('party', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='deleted_parties', to='orders.party')), + ], + options={ + 'verbose_name': "O'chirilgan partiya", + 'verbose_name_plural': "O'chirilgan partiyalar", + }, + ), + ] diff --git a/core/apps/orders/migrations/0019_deletedparty_comment.py b/core/apps/orders/migrations/0019_deletedparty_comment.py new file mode 100644 index 0000000..57dde5f --- /dev/null +++ b/core/apps/orders/migrations/0019_deletedparty_comment.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-08-22 14:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0018_party_is_deleted_deletedparty'), + ] + + operations = [ + migrations.AddField( + model_name='deletedparty', + name='comment', + field=models.CharField(blank=True, max_length=200, null=True), + ), + ] diff --git a/core/apps/orders/migrations/0020_party_discount_currency.py b/core/apps/orders/migrations/0020_party_discount_currency.py new file mode 100644 index 0000000..9c511ad --- /dev/null +++ b/core/apps/orders/migrations/0020_party_discount_currency.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-08-22 16:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0019_deletedparty_comment'), + ] + + operations = [ + migrations.AddField( + model_name='party', + name='discount_currency', + field=models.CharField(blank=True, choices=[('uzs', 'uzs'), ('usd', 'usd')], default='uzs', max_length=3, null=True), + ), + ] diff --git a/core/apps/orders/models/party.py b/core/apps/orders/models/party.py index 7663be5..ef1d451 100644 --- a/core/apps/orders/models/party.py +++ b/core/apps/orders/models/party.py @@ -50,9 +50,13 @@ class Party(BaseModel): ) audit_comment = models.TextField(null=True, blank=True) discount = models.PositiveBigIntegerField(default=0, null=True, blank=True) + discount_currency = models.CharField( + max_length=3, choices=[('uzs', 'uzs'), ('usd', 'usd')], default='uzs', null=True, blank=True + ) + is_deleted = models.BooleanField(default=False) def __str__(self): - return str(self.number) + return f'P - {self.number}' def save(self, *args, **kwargs): if not self.pk: @@ -82,4 +86,22 @@ class PartyAmount(BaseModel): class Meta: verbose_name = 'Partiya Summasi' verbose_name_plural = 'Partiya summalari' - \ No newline at end of file + + + +class DeletedParty(BaseModel): + party = models.ForeignKey(Party, on_delete=models.CASCADE, related_name='deleted_parties') + deleted_date = models.DateField(auto_now_add=True) + comment = models.CharField(max_length=200, null=True, blank=True) + + def __str__(self): + return f'{self.party} deleted at {self.deleted_date}' + + def save(self, *args, **kwargs): + self.party.is_deleted = True + self.party.save() + return super().save(*args, **kwargs) + + class Meta: + verbose_name = "O'chirilgan partiya" + verbose_name_plural = "O'chirilgan partiyalar" \ No newline at end of file diff --git a/core/apps/orders/serializers/party.py b/core/apps/orders/serializers/party.py index 90be94b..5515d65 100644 --- a/core/apps/orders/serializers/party.py +++ b/core/apps/orders/serializers/party.py @@ -2,7 +2,7 @@ from django.db import transaction from rest_framework import serializers -from core.apps.orders.models import Party, PartyAmount, Order +from core.apps.orders.models import Party, PartyAmount, Order, DeletedParty from core.apps.orders.serializers.order import MultipleOrderAddSerializer, OrderListSerializer from core.apps.accounts.models import User @@ -14,6 +14,7 @@ class PartyCreateSerializer(serializers.Serializer): payment_date = serializers.DateField() comment = serializers.CharField(required=False) discount = serializers.IntegerField(required=False) + discount_currency = serializers.ChoiceField(choices=[('uzs', 'uzs'), ('usd', 'usd')]) audit = serializers.ChoiceField( choices=[('CHECKED', 'tekshirildi'),('PROCESS', 'jarayonda')], required=False ) @@ -58,6 +59,7 @@ class PartyCreateSerializer(serializers.Serializer): audit=validated_data.get('audit'), audit_comment=validated_data.get('audit_comment'), discount=validated_data.get('discount'), + discount_currency=validated_data.get('discount_currency'), ) party.orders.add(*created_orders) party.save() @@ -98,4 +100,35 @@ class PartyListSerializer(serializers.ModelSerializer): 'id','number', 'delivery_date', 'closed_date', 'order_date', 'payment_date', 'status', 'payment_status', 'process', 'confirmation', 'comment', 'audit', 'audit_comment', 'party_amount' - ] \ No newline at end of file + ] + + +class DeletedPartyCreateSerializer(serializers.Serializer): + comment = serializers.CharField(required=False) + + def validate(self, data): + party = Party.objects.filter(id=self.context.get('party_id')).first() + if not party: + raise serializers.ValidationError("Party not found") + data['party'] = party + return data + + def create(self, validated_data): + with transaction.atomic(): + return DeletedParty.objects.create( + comment=validated_data.get('comment'), + party=validated_data.get('party') + ) + + +class DeletedPartyListSerializer(serializers.ModelSerializer): + party_number = serializers.IntegerField(source='party.number') + party_total_price = serializers.IntegerField(source='party.party_amount.total_price') + # mediator = serializers.SerializerMethodField(method_name='get_mediator') + + class Meta: + model = DeletedParty + fields = [ + 'id', 'deleted_date', 'party_number', 'party_total_price', + ] + \ No newline at end of file diff --git a/core/apps/orders/urls.py b/core/apps/orders/urls.py index a3f812d..271fa65 100644 --- a/core/apps/orders/urls.py +++ b/core/apps/orders/urls.py @@ -30,6 +30,8 @@ urlpatterns = [ path('create/', party_views.PartyCreateApiView.as_view()), path('list/', party_views.PartyListApiView.as_view()), path('/', party_views.PartyDetailApiView.as_view()), + path('/delete/', party_views.PartyDeleteApiView.as_view()), + path('deleted_pary/list/', party_views.DeletedPartyListApiView.as_view()), ] )), ] \ No newline at end of file diff --git a/core/apps/orders/views/party.py b/core/apps/orders/views/party.py index e612fcc..94280a9 100644 --- a/core/apps/orders/views/party.py +++ b/core/apps/orders/views/party.py @@ -5,7 +5,7 @@ from django_filters.rest_framework.backends import DjangoFilterBackend from core.apps.accounts.permissions.permissions import HasRolePermission from core.apps.orders.serializers import party as serializers -from core.apps.orders.models import Order, Party, PartyAmount +from core.apps.orders.models import Party, PartyAmount, DeletedParty from core.apps.orders.filters.party import PartyFilter @@ -13,7 +13,7 @@ class PartyCreateApiView(generics.GenericAPIView): serializer_class = serializers.PartyCreateSerializer queryset = Party.objects.all() permission_classes = [HasRolePermission] - required_permissions = [] + required_permissions = ['party'] def post(self, request): serializer = self.serializer_class(data=request.data, context={'user': request.user}) @@ -31,9 +31,9 @@ class PartyCreateApiView(generics.GenericAPIView): class PartyListApiView(generics.GenericAPIView): serializer_class = serializers.PartyListSerializer - queryset = Party.objects.select_related('party_amount') + queryset = Party.objects.select_related('party_amount').exclude(is_deleted=True) permission_classes = [HasRolePermission] - required_permissions = [] + required_permissions = ['party'] filter_backends = [DjangoFilterBackend] filterset_class = PartyFilter @@ -49,7 +49,7 @@ class PartyListApiView(generics.GenericAPIView): class PartyDetailApiView(generics.GenericAPIView): permission_classes = [HasRolePermission] - required_permissions = [] + required_permissions = ['party'] serializer_class = serializers.PartyDetailSerializer queryset = Party.objects.select_related('party_amount').prefetch_related('orders') @@ -58,4 +58,39 @@ class PartyDetailApiView(generics.GenericAPIView): if not party: return Response({'success': False, 'message': 'party not found'}, status=404) serializer = self.serializer_class(party) + return Response(serializer.data, status=200) + + +class PartyDeleteApiView(generics.GenericAPIView): + serializer_class = serializers.DeletedPartyCreateSerializer + queryset = Party.objects.all() + permission_classes = [HasRolePermission] + required_permissions = [] + + def post(self, request, party_id): + serializer = self.serializer_class(data=request.data, context={'party_id': party_id}) + if serializer.is_valid(raise_exception=True): + serializer.save() + return Response( + {'success': True, 'message': 'Party deleted'}, + status=200 + ) + return Response( + {'success': False, 'message': 'error while deletign party', 'error': serializer.errors}, + status=400 + ) + + +class DeletedPartyListApiView(generics.GenericAPIView): + serializer_class = serializers.DeletedPartyListSerializer + queryset = DeletedParty.objects.select_related('party') + permission_classes = [HasRolePermission] + required_permissions = [] + + def get(self, request): + deleted_parties = DeletedParty.objects.select_related('party') + page = self.paginate_queryset(deleted_parties) + if page is not None: + serializer = self.serializer_class(page, many=True) + return self.get_paginated_response(serializer.data) return Response(serializer.data, status=200) \ No newline at end of file