diff --git a/.idea/Rasaddam_Backend.iml b/.idea/Rasaddam_Backend.iml index c5d6090..168bde0 100644 --- a/.idea/Rasaddam_Backend.iml +++ b/.idea/Rasaddam_Backend.iml @@ -14,7 +14,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index 29f5506..296aa57 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/apps/authentication/api/v1/api.py b/apps/authentication/api/v1/api.py index 7192d70..f0726c2 100644 --- a/apps/authentication/api/v1/api.py +++ b/apps/authentication/api/v1/api.py @@ -300,12 +300,6 @@ class OrganizationViewSet(ModelViewSet, DynamicSearchMixin): else: queryset = self.queryset.filter(province=request.user.province) - # if param self was in request, remove my organization from queryset - if 'self' in request.GET.keys(): - queryset = self.queryset.filter( - province=int(request.GET['province']) - ).exclude(user_organization__user=request.user) - filtered_query = self.filter_query(queryset) page = self.paginate_queryset(filtered_query.order_by('-create_date')) # paginate queryset diff --git a/apps/pos_device/exceptions.py b/apps/pos_device/exceptions.py new file mode 100644 index 0000000..a2e926c --- /dev/null +++ b/apps/pos_device/exceptions.py @@ -0,0 +1,7 @@ +from rest_framework.exceptions import APIException +from rest_framework import status + + +class DeviceAlreadyAssigned(APIException): + status_code = status.HTTP_403_FORBIDDEN + default_detail = "این دستگاه قبلا به این کلاینت تخصیص داده شده است" # noqa diff --git a/apps/pos_device/migrations/0055_alter_posclient_organization_and_more.py b/apps/pos_device/migrations/0055_alter_posclient_organization_and_more.py new file mode 100644 index 0000000..a4d73ff --- /dev/null +++ b/apps/pos_device/migrations/0055_alter_posclient_organization_and_more.py @@ -0,0 +1,26 @@ +# Generated by Django 5.0 on 2025-08-16 10:08 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0033_organization_en_name_alter_organization_name'), + ('pos_device', '0054_alter_deviceactivationcode_expires_at'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='posclient', + name='organization', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='client', to='authentication.organization'), + ), + migrations.AddConstraint( + model_name='deviceassignment', + constraint=models.UniqueConstraint(fields=('client', 'device'), name='unique_assign_client_device', violation_error_code=403, violation_error_message='این کلاینت با همین دستگاه قبلا تخصیص داده شده است'), + ), + ] diff --git a/apps/pos_device/migrations/0056_remove_deviceassignment_unique_assign_client_device.py b/apps/pos_device/migrations/0056_remove_deviceassignment_unique_assign_client_device.py new file mode 100644 index 0000000..5104aef --- /dev/null +++ b/apps/pos_device/migrations/0056_remove_deviceassignment_unique_assign_client_device.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0 on 2025-08-16 10:50 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('pos_device', '0055_alter_posclient_organization_and_more'), + ] + + operations = [ + migrations.RemoveConstraint( + model_name='deviceassignment', + name='unique_assign_client_device', + ), + ] diff --git a/apps/pos_device/models.py b/apps/pos_device/models.py index c6d8ca9..b6cdb68 100644 --- a/apps/pos_device/models.py +++ b/apps/pos_device/models.py @@ -148,7 +148,7 @@ class POSClient(BaseModel): ('organization', 'سازمان') # noqa ]) is_organization = models.BooleanField(default=False) - organization = models.OneToOneField( + organization = models.ForeignKey( Organization, on_delete=models.CASCADE, related_name='client', diff --git a/apps/pos_device/web/api/v1/serilaizers/client.py b/apps/pos_device/web/api/v1/serilaizers/client.py index 83e625d..cce4aa6 100644 --- a/apps/pos_device/web/api/v1/serilaizers/client.py +++ b/apps/pos_device/web/api/v1/serilaizers/client.py @@ -1,5 +1,7 @@ from rest_framework.serializers import ModelSerializer from apps.pos_device import models as pos_models +from rest_framework.exceptions import APIException +from rest_framework import status class POSClientSerializer(ModelSerializer): @@ -7,6 +9,26 @@ class POSClientSerializer(ModelSerializer): model = pos_models.POSClient fields = '__all__' + def validate(self, attrs): + org = attrs['organization'] + + # check for duplicate organization client + if org: + if self.Meta.model.objects.filter(organization=org).exists(): + raise APIException("قبلا کلاینت با این سازمان ثبت شده است", code=status.HTTP_403_FORBIDDEN) # noqa + + return attrs + + def to_representation(self, instance): + representation = super().to_representation(instance) + + representation['organization'] = { + 'name': instance.organization.name, + 'id': instance.organization.id + } + + return representation + class POSClientAttributeSerializer(ModelSerializer): class Meta: diff --git a/apps/pos_device/web/api/v1/serilaizers/device.py b/apps/pos_device/web/api/v1/serilaizers/device.py index f067e66..826c312 100644 --- a/apps/pos_device/web/api/v1/serilaizers/device.py +++ b/apps/pos_device/web/api/v1/serilaizers/device.py @@ -1,5 +1,9 @@ +from apps.pos_device.web.api.v1.serilaizers import client as client_serializer from rest_framework.serializers import ModelSerializer from apps.pos_device import models as pos_models +from rest_framework.exceptions import APIException +from apps.pos_device import exceptions as pos_exceptions +from rest_framework import status class ProviderCompanySerializer(ModelSerializer): @@ -23,6 +27,10 @@ class DeviceSerializer(ModelSerializer): 'province': instance.organization.province.id } + representation['assignment'] = DeviceAssignmentSerializer( + instance.assignment.all().first() + ).data + return representation @@ -43,6 +51,23 @@ class DeviceAssignmentSerializer(ModelSerializer): model = pos_models.DeviceAssignment fields = '__all__' + def validate(self, attrs): + + device = attrs['device'] + client = attrs['client'] + + if self.Meta.model.objects.filter(device=device, client=client).exists(): + raise pos_exceptions.DeviceAlreadyAssigned() + + def to_representation(self, instance): + representation = super().to_representation(instance) + + representation['client'] = client_serializer.POSClientSerializer( + instance.client + ).data + + return representation + class StakeHoldersSerializer(ModelSerializer): class Meta: diff --git a/apps/pos_device/web/api/v1/viewsets/device.py b/apps/pos_device/web/api/v1/viewsets/device.py index 77045ea..17c2dec 100644 --- a/apps/pos_device/web/api/v1/viewsets/device.py +++ b/apps/pos_device/web/api/v1/viewsets/device.py @@ -164,15 +164,8 @@ class DeviceAssignmentViewSet(viewsets.ModelViewSet): queryset = pos_models.DeviceAssignment.objects.all() serializer_class = device_serializer.DeviceAssignmentSerializer - @action( - methods=['post'], - detail=False, - url_path='assignment', - url_name='assignment', - name='assignment', - ) @transaction.atomic - def device_assignment(self, request): + def create(self, request, *args, **kwargs): """ assign pos device to client by company """ if 'organization' not in request.data.keys(): @@ -180,12 +173,22 @@ class DeviceAssignmentViewSet(viewsets.ModelViewSet): request.data.update({'organization': organization.id}) if 'client_data' in request.data.keys(): - client = CustomOperations().custom_create( - request=request, - view=POSClientViewSet(), - data=request.data['client_data'] - ) - request.data.update({'client': client['id']}) + + # if client will be an organization + if request.data['client_data']['is_organization']: + client = pos_models.POSClient.objects.filter( + organization_id=request.data['client_data']['organization'] + ) + + if client.exists(): + request.data.update({'client': client.first().id}) + else: + client = CustomOperations().custom_create( + request=request, + view=POSClientViewSet(), + data=request.data['client_data'] + ) + request.data.update({'client': client['id']}) # create assignment serializer = self.serializer_class(data=request.data) @@ -193,8 +196,9 @@ class DeviceAssignmentViewSet(viewsets.ModelViewSet): assignment = serializer.save() # set organization having pos status - assignment.organization.has_pos = True - assignment.organization.save() + if assignment.client.organization: + assignment.client.organization.has_pos = True + assignment.client.organization.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_403_FORBIDDEN)