From 0acc0a920a4c7db8bbea8df4571cc19a970aad33 Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Wed, 24 Sep 2025 15:34:42 +0500 Subject: [PATCH] change: change offer model, and add new api --- core/apps/orders/admin/offer.py | 2 +- core/apps/orders/filters/order.py | 2 +- .../orders/migrations/0024_offer_status.py | 18 +++++++++++++ .../migrations/0025_alter_offer_status.py | 18 +++++++++++++ core/apps/orders/models/order_offer.py | 10 +++++++- core/apps/orders/serializers/offer.py | 15 ++++++++--- core/apps/orders/urls.py | 1 + core/apps/orders/views/offer.py | 25 +++++++++++++++++-- 8 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 core/apps/orders/migrations/0024_offer_status.py create mode 100644 core/apps/orders/migrations/0025_alter_offer_status.py diff --git a/core/apps/orders/admin/offer.py b/core/apps/orders/admin/offer.py index 677a7d1..5ba8acc 100644 --- a/core/apps/orders/admin/offer.py +++ b/core/apps/orders/admin/offer.py @@ -5,6 +5,6 @@ from core.apps.orders.models import Offer @admin.register(Offer) class OfferAdmin(admin.ModelAdmin): - list_display = ['id', 'number', 'order'] + list_display = ['id', 'number', 'order', 'status'] search_fields = ['phone', 'number', 'price'] diff --git a/core/apps/orders/filters/order.py b/core/apps/orders/filters/order.py index 332a398..b06287e 100644 --- a/core/apps/orders/filters/order.py +++ b/core/apps/orders/filters/order.py @@ -7,6 +7,6 @@ class OrderFilter(django_filters.FilterSet): class Meta: model = Order fields = [ - 'wherehouse', 'project', 'project_folder', 'date' + 'wherehouse', 'project', 'project_folder', 'date', ] \ No newline at end of file diff --git a/core/apps/orders/migrations/0024_offer_status.py b/core/apps/orders/migrations/0024_offer_status.py new file mode 100644 index 0000000..c1612ee --- /dev/null +++ b/core/apps/orders/migrations/0024_offer_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-09-24 14:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0023_alter_party_orders'), + ] + + operations = [ + migrations.AddField( + model_name='offer', + name='status', + field=models.CharField(choices=[('CANCELLED', 'rad etildi'), ('PENDING', 'kutilmoqda'), ('CONFIRMED', 'tasdiqlandi')], default='PENDING', max_length=20), + ), + ] diff --git a/core/apps/orders/migrations/0025_alter_offer_status.py b/core/apps/orders/migrations/0025_alter_offer_status.py new file mode 100644 index 0000000..b309fcb --- /dev/null +++ b/core/apps/orders/migrations/0025_alter_offer_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-09-24 14:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0024_offer_status'), + ] + + operations = [ + migrations.AlterField( + model_name='offer', + name='status', + field=models.CharField(blank=True, choices=[('CANCELLED', 'rad etildi'), ('PENDING', 'kutilmoqda'), ('CONFIRMED', 'tasdiqlandi')], default='PENDING', max_length=20, null=True), + ), + ] diff --git a/core/apps/orders/models/order_offer.py b/core/apps/orders/models/order_offer.py index fe23c47..4555a3a 100644 --- a/core/apps/orders/models/order_offer.py +++ b/core/apps/orders/models/order_offer.py @@ -10,15 +10,23 @@ class Offer(BaseModel): ('UZS', 'uzs'), ('USD', 'usd') ) + STATUS = ( + ('CANCELLED', 'rad etildi'), + ('PENDING', 'kutilmoqda'), + ('CONFIRMED', 'tasdiqlandi'), + ) order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='offers') - counterparty = models.ForeignKey(Counterparty, on_delete=models.CASCADE, related_name='offers', null=True) + counterparty = models.ForeignKey( + Counterparty, on_delete=models.CASCADE, related_name='offers', null=True + ) price = models.PositiveBigIntegerField() price_type = models.CharField(choices=PRICE_TYPE, default='uzs') phone = models.CharField(max_length=15, null=True, blank=True) comment = models.TextField(null=True, blank=True) qqs = models.BooleanField(default=False, null=True, blank=True) number = models.PositiveIntegerField(default=1) + status = models.CharField(max_length=20, choices=STATUS, default='PENDING', null=True, blank=True) def __str__(self): return str(self.number) diff --git a/core/apps/orders/serializers/offer.py b/core/apps/orders/serializers/offer.py index f77da68..63b8dbc 100644 --- a/core/apps/orders/serializers/offer.py +++ b/core/apps/orders/serializers/offer.py @@ -95,7 +95,8 @@ class OffersSerializer(serializers.ModelSerializer): class Meta: model = Offer fields = [ - 'id', 'number', 'price', 'price_type', 'phone', 'comment', 'qqs', 'counterparty', 'created_at' + 'id', 'number', 'price', 'price_type', 'phone', 'comment', 'qqs', 'counterparty', + 'status', 'created_at' ] def get_counterparty(self, obj): @@ -106,7 +107,7 @@ class OffersSerializer(serializers.ModelSerializer): class OrderListForOfferSerializer(serializers.ModelSerializer): - offers = OffersSerializer(many=True) + offers = serializers.SerializerMethodField(method_name='get_offers') product = serializers.SerializerMethodField(method_name='get_product') unity = serializers.SerializerMethodField(method_name='get_unity') @@ -127,4 +128,12 @@ class OrderListForOfferSerializer(serializers.ModelSerializer): return { 'id': obj.unity.id, 'value': obj.unity.value - } \ No newline at end of file + } + + def get_offers(self, obj): + status = self.context.get('status') + if status: + offers = obj.offers.filter(status=status) + return OffersSerializer(offers, many=True).data + else: + return OffersSerializer(obj.offers, many=True).data \ No newline at end of file diff --git a/core/apps/orders/urls.py b/core/apps/orders/urls.py index 0189683..6c576e8 100644 --- a/core/apps/orders/urls.py +++ b/core/apps/orders/urls.py @@ -25,6 +25,7 @@ urlpatterns = [ path('/delete/', offer_views.OfferDeleteApiView.as_view()), path('/update/', offer_views.OfferUpdateApiView.as_view()), path('bulk-delete/', offer_views.DeleteMultipleOfferApiView.as_view()), + path('/change-status/', offer_views.ChangeOfferStatusApiView.as_view()), ] )), path('party/', include( diff --git a/core/apps/orders/views/offer.py b/core/apps/orders/views/offer.py index daaa5a8..3c3ef5b 100644 --- a/core/apps/orders/views/offer.py +++ b/core/apps/orders/views/offer.py @@ -40,10 +40,14 @@ class OfferListApiView(generics.GenericAPIView): filterset_class = OrderFilter def get(self, request): + offer_status = request.query_params.get('status', None) orders = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(orders) if page is not None: - serializer = self.serializer_class(page, many=True) + if offer_status: + serializer = self.serializer_class(page, many=True, context={'status': offer_status}) + else: + serializer = self.serializer_class(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.serializer_class(offers, many=True) return Response(serializer.data, status=200) @@ -100,4 +104,21 @@ class DeleteMultipleOfferApiView(views.APIView): return Response({"detail": "offer_ids kerak"}, status=400) deleted_count, _ = Offer.objects.filter(id__in=ids).delete() - return Response({"deleted": deleted_count}, status=200) \ No newline at end of file + return Response({"deleted": deleted_count}, status=200) + + +class ChangeOfferStatusApiView(views.APIView): + permission_classes = [HasRolePermission] + + def post(self, request, id): + offer = get_object_or_404(Offer, id=id) + status = request.data.get('status') + if not status: + return Response({'success': False, 'message': 'status field is required'}, status=400) + offer.status = status + offer.save() + return Response( + {'success': True, 'message': 'status updated'}, + status=200 + ) + \ No newline at end of file