diff --git a/core/apps/orders/admin/party.py b/core/apps/orders/admin/party.py index cd71f0e..34c70ec 100644 --- a/core/apps/orders/admin/party.py +++ b/core/apps/orders/admin/party.py @@ -1,9 +1,23 @@ from django.contrib import admin -from core.apps.orders.models import Party +from core.apps.orders.models import Party, PartyAmount + + +class PartyAmountInline(admin.StackedInline): + model = PartyAmount + extra = 1 + show_change_link = True @admin.register(Party) class PartyAdmin(admin.ModelAdmin): list_display = ['mediator', 'delivery_date', 'payment_date'] - \ No newline at end of file + inlines = [PartyAmountInline] + + +@admin.register(PartyAmount) +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 diff --git a/core/apps/orders/migrations/0010_party_coled_date_party_confirmation_party_number_and_more.py b/core/apps/orders/migrations/0010_party_coled_date_party_confirmation_party_number_and_more.py new file mode 100644 index 0000000..3ee77c4 --- /dev/null +++ b/core/apps/orders/migrations/0010_party_coled_date_party_confirmation_party_number_and_more.py @@ -0,0 +1,70 @@ +# Generated by Django 5.2.4 on 2025-08-21 10:55 + +import django.db.models.deletion +import django.utils.timezone +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0009_order_counterparty_order_currency_order_total_price_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='party', + name='coled_date', + field=models.DateField(blank=True, null=True), + ), + migrations.AddField( + model_name='party', + name='confirmation', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='party', + name='number', + field=models.PositiveIntegerField(default=1), + ), + migrations.AddField( + model_name='party', + name='order_date', + field=models.DateField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='party', + name='payment_status', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='party', + name='process', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='party', + name='status', + field=models.CharField(blank=True, choices=[('ORDERED', 'yetkazildi'), ('PROCESS', 'jarayonda')], max_length=20, null=True), + ), + migrations.CreateModel( + name='PartyAmount', + 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)), + ('total_price', models.PositiveBigIntegerField()), + ('cost_amount', models.PositiveBigIntegerField(default=0)), + ('calculated_amount', models.PositiveBigIntegerField(default=0)), + ('paid_amount', models.PositiveBigIntegerField(default=0)), + ('payment_amount', models.BigIntegerField(default=0)), + ('party', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='party_amount', to='orders.party')), + ], + options={ + 'verbose_name': 'Partiya Summasi', + 'verbose_name_plural': 'Partiya summalari', + }, + ), + ] diff --git a/core/apps/orders/models/party.py b/core/apps/orders/models/party.py index 5c63367..b81f4c5 100644 --- a/core/apps/orders/models/party.py +++ b/core/apps/orders/models/party.py @@ -6,10 +6,23 @@ from core.apps.accounts.models import User class Party(BaseModel): + number = models.PositiveIntegerField(default=1) orders = models.ManyToManyField(Order, related_name='parties', null=True, blank=True) mediator = models.ForeignKey(User, on_delete=models.CASCADE, related_name='parties') + # dates delivery_date = models.DateField() + coled_date = models.DateField(null=True, blank=True) + order_date = models.DateField(auto_now_add=True) payment_date = models.DateField() + + status = models.CharField( + max_length=20, choices=[('ORDERED', 'yetkazildi'), ('PROCESS', 'jarayonda')], + null=True, blank=True + ) + payment_status = models.FloatField(null=True, blank=True) + process = models.FloatField(null=True, blank=True) + confirmation = models.BooleanField(default=False) + comment = models.TextField(null=True, blank=True) audit = models.CharField( max_length=20, choices=[('CHECKED', 'tekshirildi'),('PROCESS', 'jarayonda')], @@ -18,8 +31,34 @@ class Party(BaseModel): audit_comment = models.TextField(null=True, blank=True) def __str__(self): - return f'{self.mediator.full_name} {self.delivery_date}' + return str(self.number) + def save(self, *args, **kwargs): + if not self.pk: + last_party = Party.objects.order_by('number').last() + if last_party: + self.number = last_party.number + 1 + else: + self.number = 1 + return super().save(*args, **kwargs) + class Meta: verbose_name = 'Partiya' verbose_name_plural = 'Partiyalar' + + +class PartyAmount(BaseModel): + party = models.OneToOneField(Party, on_delete=models.CASCADE, related_name='party_amount') + total_price = models.PositiveBigIntegerField() + cost_amount = models.PositiveBigIntegerField(default=0) + calculated_amount = models.PositiveBigIntegerField(default=0) + paid_amount = models.PositiveBigIntegerField(default=0) + payment_amount = models.BigIntegerField(default=0) + + def __str__(self): + return f'{self.party} amount' + + class Meta: + verbose_name = 'Partiya Summasi' + verbose_name_plural = 'Partiya summalari' + \ No newline at end of file diff --git a/core/apps/orders/serializers/offer.py b/core/apps/orders/serializers/offer.py index 3b02442..594c7ea 100644 --- a/core/apps/orders/serializers/offer.py +++ b/core/apps/orders/serializers/offer.py @@ -94,4 +94,30 @@ class OffersSerializer(serializers.ModelSerializer): model = Offer fields = [ 'id', 'number', 'price', 'price_type', 'phone', 'comment', 'qqs', - ] \ No newline at end of file + ] + + + +class OrderListForOfferSerializer(serializers.ModelSerializer): + offers = OffersSerializer(many=True) + product = serializers.SerializerMethodField(method_name='get_product') + unity = serializers.SerializerMethodField(method_name='get_unity') + + class Meta: + model = Order + fields = [ + 'id', 'product', 'unity', 'quantity', 'date', 'offers' + ] + + def get_product(self, obj): + return { + 'id': obj.product.id, + 'name': obj.product.name, + 'type': obj.product.type, + } + + def get_unity(self, obj): + return { + 'id': obj.unity.id, + 'value': obj.unity.value + } \ No newline at end of file diff --git a/core/apps/orders/serializers/order.py b/core/apps/orders/serializers/order.py index 79dba64..f49f740 100644 --- a/core/apps/orders/serializers/order.py +++ b/core/apps/orders/serializers/order.py @@ -13,7 +13,8 @@ from core.apps.wherehouse.serializers.wherehouse import WhereHouseListSerializer # projects from core.apps.projects.models import Project, ProjectFolder from core.apps.projects.serializers.project import ProjectListSerializer, ProjectFolderListSerializer - +# counterparty +from core.apps.counterparty.models import Counterparty class OrderCreateSerializer(serializers.Serializer): @@ -64,7 +65,6 @@ class MultipleOrderCreateSerializer(serializers.Serializer): def create(self, validated_data): with transaction.atomic(): - print(self.context['user']) resources = validated_data.pop('resources') common_date = validated_data.get('date') orders = [] @@ -113,4 +113,47 @@ class OrderUpdateSerializer(serializers.ModelSerializer): model = Order fields = [ 'product', 'unity', 'quantity', 'project', 'project_folder', 'wherehouse', 'date', - ] \ No newline at end of file + ] + + +class MultipleOrderAddSerializer(serializers.Serializer): + product_id = serializers.UUIDField() + unity_id = serializers.UUIDField() + project_folder_id = serializers.UUIDField(required=False) + project_id = serializers.UUIDField(required=False) + counterparty_id = serializers.UUIDField() + wherehouse_id = serializers.UUIDField() + + quantity = serializers.IntegerField() + unit_amount = serializers.IntegerField() + currency = serializers.ChoiceField(choices=[('uzs', 'uzs'), ('usd', 'usd')]) + amount = serializers.UUIDField() + + def validate(self, data): + product = Product.objects.filter(id=data['product_id']).first() + if not product: + raise serializers.ValidationError('product not found') + unity = Unity.objects.filter(id=data['unity_id']).first() + if not unity: + raise serializers.ValidationError("Unity not found") + wherehouse = WhereHouse.objects.filter(id=data['wherehouse_id']) + if not wherehouse: + raise serializers.ValidationError("WhereHouse not found") + counterparty = Counterparty.objects.filter(id=data['counterparty_id']).first() + if not counterparty: + raise serializers.ValidationError("Counterparty not found") + if data.get('project_id'): + project = Project.objects.filter(id=data.get('project_id')).first() + if not project: + raise serializers.ValidationError("Project not found") + data['project'] = project + if data.get('project_folder_id'): + project_folder = ProjectFolder.objects.filter(id=data.get('project_folder_id')).first() + if not project_folder: + raise serializers.ValidationError("Project Folder not found") + data['project_folder'] = project_folder + data['product'] = product + data['unity'] = unity + data['wherehouse'] = wherehouse + data['counterparty'] = counterparty + return data \ No newline at end of file diff --git a/core/apps/orders/serializers/party.py b/core/apps/orders/serializers/party.py new file mode 100644 index 0000000..5b911a9 --- /dev/null +++ b/core/apps/orders/serializers/party.py @@ -0,0 +1,42 @@ +from django.db import transaction + +from rest_framework import serializers + +from core.apps.orders.models import Party, PartyAmount, Order +from core.apps.orders.serializers.order import MultipleOrderAddSerializer +from core.apps.accounts.models import User + + +class PartyCreateSerializer(serializers.Serializer): + resources = MultipleOrderAddSerializer(many=True) + mediator_id = serializers.UUIDField() + delivery_date = serializers.DateField() + payment_date = serializers.DateField() + comment = serializers.CharField(required=False) + + audit = serializers.ChoiceField( + choices=[('CHECKED', 'tekshirildi'),('PROCESS', 'jarayonda')], required=False + ) + audit_comment = serializers.CharField(required=False) + + def validate(self, data): + user = User.objects.filter(id=data['mediator_id']).first() + if not user: + raise serializers.ValidationError("User not found") + data['user'] = user + return data + + def create(self, validated_data): + with transaction.atomic(): + resources = validated_data.pop('resources') + orders = [] + for resource in resources: + orders.append(Order( + product=resource['product'], + unity=resource['unity'], + project_folder=resource.get('project_folder'), + project=resource.get('project'), + counterparty=resource.get('counterparty'), + wherehouse=resource.get('wherehouse'), + + )) \ No newline at end of file diff --git a/core/apps/orders/views/offer.py b/core/apps/orders/views/offer.py index 6a21575..61298dc 100644 --- a/core/apps/orders/views/offer.py +++ b/core/apps/orders/views/offer.py @@ -8,8 +8,8 @@ from django_filters.rest_framework.backends import DjangoFilterBackend from core.apps.shared.paginations.custom import PageNumberPagination from core.apps.accounts.permissions.permissions import HasRolePermission from core.apps.orders.serializers import offer as serializers -from core.apps.orders.models import Offer -from core.apps.orders.filters.offer import OfferFilter +from core.apps.orders.models import Offer, Order +from core.apps.orders.filters.order import OrderFilter class OffersCreateApiView(generics.GenericAPIView): @@ -33,15 +33,15 @@ class OffersCreateApiView(generics.GenericAPIView): class OfferListApiView(generics.GenericAPIView): permission_classes = [HasRolePermission] - queryset = Offer.objects.select_related('order') + queryset = Order.objects.select_related('product', 'unity').prefetch_related('offers') required_permissions = ['offer'] - serializer_class = serializers.OfferListSerializer + serializer_class = serializers.OrderListForOfferSerializer filter_backends = [DjangoFilterBackend] - filterset_class = OfferFilter + filterset_class = OrderFilter def get(self, request): - offers = self.filter_queryset(self.get_queryset()) - page = self.paginate_queryset(offers) + orders = self.filter_queryset(self.get_queryset()) + page = self.paginate_queryset(orders) if page is not None: serializer = self.serializer_class(page, many=True) return self.get_paginated_response(serializer.data)