From 392fc13f8e5939da570dd8275e2fab95aea073cc Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Sat, 30 Aug 2025 15:41:29 +0500 Subject: [PATCH] change: change counterparty model and add create api --- core/apps/counterparty/admin/counterparty.py | 10 +- .../0002_counterpartyfolder_and_more.py | 91 +++++++++++++++++++ core/apps/counterparty/models/conterparty.py | 45 +++++++-- .../counterparty/serializers/counterparty.py | 68 +++++++++++--- core/apps/counterparty/urls.py | 1 + core/apps/counterparty/views/counterparty.py | 22 ++++- core/apps/orders/serializers/party.py | 4 +- 7 files changed, 216 insertions(+), 25 deletions(-) create mode 100644 core/apps/counterparty/migrations/0002_counterpartyfolder_and_more.py diff --git a/core/apps/counterparty/admin/counterparty.py b/core/apps/counterparty/admin/counterparty.py index 10431d5..fb72859 100644 --- a/core/apps/counterparty/admin/counterparty.py +++ b/core/apps/counterparty/admin/counterparty.py @@ -1,9 +1,15 @@ from django.contrib import admin -from core.apps.counterparty.models import Counterparty +from core.apps.counterparty.models import Counterparty, CounterpartyFolder @admin.register(Counterparty) class CounterpartyAdmin(admin.ModelAdmin): - list_display = ['id', 'name', 'person'] + list_display = ['id', 'name', 'phone', 'type', 'inn'] + + +@admin.register(CounterpartyFolder) +class CounterpartyFolderAdmin(admin.ModelAdmin): + list_display = ['id', 'name'] + list_filter = ['name'] \ No newline at end of file diff --git a/core/apps/counterparty/migrations/0002_counterpartyfolder_and_more.py b/core/apps/counterparty/migrations/0002_counterpartyfolder_and_more.py new file mode 100644 index 0000000..5d913b7 --- /dev/null +++ b/core/apps/counterparty/migrations/0002_counterpartyfolder_and_more.py @@ -0,0 +1,91 @@ +# Generated by Django 5.2.4 on 2025-08-30 15:11 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('counterparty', '0001_initial'), + ('shared', '0002_usdcourse'), + ] + + operations = [ + migrations.CreateModel( + name='CounterpartyFolder', + 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': 'Kontragent papkasi', + 'verbose_name_plural': 'Kontragent papkalari', + }, + ), + migrations.RenameField( + model_name='counterparty', + old_name='description', + new_name='comment', + ), + migrations.RemoveField( + model_name='counterparty', + name='person', + ), + migrations.RemoveField( + model_name='counterparty', + name='start_date', + ), + migrations.RemoveField( + model_name='counterparty', + name='status', + ), + migrations.AddField( + model_name='counterparty', + name='balance', + field=models.PositiveBigIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='counterparty', + name='balance_currency', + field=models.CharField(blank=True, choices=[('usd', 'usd'), ('uzs', 'uzs')], max_length=3, null=True), + ), + migrations.AddField( + model_name='counterparty', + name='balance_date', + field=models.DateField(blank=True, null=True), + ), + migrations.AddField( + model_name='counterparty', + name='district', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='counterparties', to='shared.district'), + ), + migrations.AddField( + model_name='counterparty', + name='inn', + field=models.CharField(max_length=20, null=True), + ), + migrations.AddField( + model_name='counterparty', + name='phone', + field=models.CharField(max_length=15, null=True), + ), + migrations.AddField( + model_name='counterparty', + name='region', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='counterparties', to='shared.region'), + ), + migrations.AlterField( + model_name='counterparty', + name='type', + field=models.CharField(choices=[('SUPPLIER', "ta'minotchi"), ('WORKER', 'ishchi')], default='SUPPLIER', max_length=20), + ), + migrations.AddField( + model_name='counterparty', + name='folder', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='counterparties', to='counterparty.counterpartyfolder'), + ), + ] diff --git a/core/apps/counterparty/models/conterparty.py b/core/apps/counterparty/models/conterparty.py index b499ffb..14cc583 100644 --- a/core/apps/counterparty/models/conterparty.py +++ b/core/apps/counterparty/models/conterparty.py @@ -1,18 +1,47 @@ from django.db import models -from core.apps.shared.models import BaseModel +from core.apps.shared.models import BaseModel, Region, District from core.apps.accounts.models import User -class Counterparty(BaseModel): +class CounterpartyFolder(BaseModel): name = models.CharField(max_length=200) - description = models.TextField(null=True, blank=True) - start_date = models.DateField() - status = models.CharField( - max_length=20, choices=[('active', 'active'), ('inactive', 'inactive')], default='active' + + def __str__(self): + return self.name + + class Meta: + verbose_name = 'Kontragent papkasi' + verbose_name_plural = 'Kontragent papkalari' + + +class Counterparty(BaseModel): + TYPE = ( + ('SUPPLIER', "ta'minotchi"), + ('WORKER', 'ishchi') ) - type = models.CharField(max_length=20, choices=[('supplier', 'supplier')]) - person = models.ForeignKey(User, on_delete=models.CASCADE, related_name='counterparties') + + inn = models.CharField(max_length=20, null=True) + name = models.CharField(max_length=200) + phone = models.CharField(max_length=15, null=True) + + type = models.CharField(max_length=20, choices=TYPE, default='SUPPLIER') + folder = models.ForeignKey( + CounterpartyFolder, on_delete=models.SET_NULL, null=True, blank=True, + related_name='counterparties', + ) + region = models.ForeignKey( + Region, on_delete=models.SET_NULL, null=True, blank=True, related_name='counterparties' + ) + district = models.ForeignKey( + District, on_delete=models.SET_NULL, null=True, blank=True, related_name='counterparties' + ) + balance = models.PositiveBigIntegerField(null=True, blank=True) + balance_currency = models.CharField( + max_length=3, choices=[('usd', 'usd'), ('uzs', 'uzs')], null=True, blank=True + ) + balance_date = models.DateField(null=True, blank=True) + comment = models.TextField(null=True, blank=True) def __str__(self): return self.name diff --git a/core/apps/counterparty/serializers/counterparty.py b/core/apps/counterparty/serializers/counterparty.py index 351ec43..7ff25b5 100644 --- a/core/apps/counterparty/serializers/counterparty.py +++ b/core/apps/counterparty/serializers/counterparty.py @@ -1,22 +1,68 @@ +from django.db import transaction + from rest_framework import serializers -from core.apps.accounts.serializers.user import UserListSerializer -from core.apps.counterparty.models import Counterparty +from core.apps.counterparty.models import Counterparty, CounterpartyFolder +from core.apps.shared.models import Region, District +class CounterpartyListSerializer(serializers.ModelSerializer): + class Meta: + model = Counterparty + fields = [ + 'id', 'inn', 'name', 'phone', 'type', 'folder', 'type', 'region', 'district', + 'balance', 'balance_currency', 'balance_date', 'comment', + ] + + class CounterpartySerializer(serializers.ModelSerializer): - person = UserListSerializer() - class Meta: model = Counterparty fields = [ - 'id', 'name', 'type', 'status', 'description', 'start_date', 'person' + 'id', 'name' ] + +class CounterpartyCreateSerializer(serializers.Serializer): + inn = serializers.CharField() + name = serializers.CharField() + phone = serializers.CharField() + type = serializers.ChoiceField(choices=Counterparty.TYPE, required=False) + folder_id = serializers.UUIDField(required=False) + region_id = serializers.UUIDField(required=False) + district_id = serializers.UUIDField(required=False) + balance = serializers.IntegerField(required=False) + balance_date = serializers.DateField(required=False) + comment = serializers.CharField(required=False) -class CounterpartyListPartySerializer(serializers.ModelSerializer): - class Meta: - model = Counterparty - fields = [ - 'id', 'name', - ] \ No newline at end of file + def validate(self, data): + if data.get('folder_id'): + folder = CounterpartyFolder.objects.filter(id=data.get('folder_id')).first() + if not folder: + raise serializers.ValidationError("Counterparty Folder not found") + data['folder'] = folder + 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.get('district_id')).first() + if not district: + raise serializers.ValidationError("District not found") + return data + + def create(self, validated_data): + with transaction.atomic(): + return Counterparty.objects.create( + inn=validated_data.get('inn'), + name=validated_data.get('name'), + phone=validated_data.get('phone'), + type=validated_data.get('type'), + folder=validated_data.get('folder'), + region=validated_data.get('region'), + district=validated_data.get('district'), + balance=validated_data.get('balance'), + balance_date=validated_data.get('balance_date'), + comment=validated_data.get('comment'), + ) diff --git a/core/apps/counterparty/urls.py b/core/apps/counterparty/urls.py index 9b0ee21..db30888 100644 --- a/core/apps/counterparty/urls.py +++ b/core/apps/counterparty/urls.py @@ -7,6 +7,7 @@ urlpatterns = [ path('counterparty/', include( [ path('list/', cp_views.CounterpartyListApiView.as_view()), + path('create/', cp_views.CounterpartyCreateApiView.as_view()), ] )) ] \ No newline at end of file diff --git a/core/apps/counterparty/views/counterparty.py b/core/apps/counterparty/views/counterparty.py index 95213dc..0c3b6d4 100644 --- a/core/apps/counterparty/views/counterparty.py +++ b/core/apps/counterparty/views/counterparty.py @@ -8,10 +8,28 @@ from core.apps.counterparty.serializers import counterparty as serializers class CounterpartyListApiView(generics.ListAPIView): - serializer_class = serializers.CounterpartySerializer - queryset = Counterparty.objects.select_related('person') + serializer_class = serializers.CounterpartyListSerializer + queryset = Counterparty.objects.all() pagination_class = [HasRolePermission] required_permissions = [] pagination_class = CustomPageNumberPagination +class CounterpartyCreateApiView(generics.GenericAPIView): + serializer_class = serializers.CounterpartyCreateSerializer + queryset = Counterparty.objects.all() + permission_classes = [HasRolePermission] + required_permissions = [] + + def post(self, request): + serializer = self.serializer_class(data=request.data) + if serializer.is_valid(raise_exception=True): + serializer.save() + return Response( + {'success': True, 'message': 'Conterparty Created'}, + status=201 + ) + return Response( + {'success': False, 'message': serializer.errors}, + status=400 + ) \ No newline at end of file diff --git a/core/apps/orders/serializers/party.py b/core/apps/orders/serializers/party.py index 4eea61f..9a697a1 100644 --- a/core/apps/orders/serializers/party.py +++ b/core/apps/orders/serializers/party.py @@ -5,7 +5,7 @@ from rest_framework import serializers 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 -from core.apps.counterparty.serializers.counterparty import CounterpartyListPartySerializer +from core.apps.counterparty.serializers.counterparty import CounterpartySerializer from core.apps.orders.tasks.order import create_inventory from core.apps.shared.models import UsdCourse from core.apps.products.models import Product, Unity @@ -137,7 +137,7 @@ class PartyListSerializer(serializers.ModelSerializer): {"id": c["counterparty__id"], "name": c["counterparty__name"]} for c in counterparties ] - return CounterpartyListPartySerializer(counterparties, many=True).data + return CounterpartySerializer(counterparties, many=True).data class DeletedPartyCreateSerializer(serializers.Serializer):