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)