diff --git a/apps/pos_device/middlewares.py b/apps/pos_device/middlewares.py index acc2e46..72c7c25 100644 --- a/apps/pos_device/middlewares.py +++ b/apps/pos_device/middlewares.py @@ -48,7 +48,7 @@ class PosDeviceValidationMiddleware: raise def validate_request(self, request): - headers = request.headers.kiani + headers = request.headers data = {key: headers.get(key) for key in self.REQUIRED_HEADERS} missing = [key for key, value in data.items() if not value] diff --git a/apps/pos_device/migrations/0059_device_pre_registered_alter_device_acceptor_and_more.py b/apps/pos_device/migrations/0059_device_pre_registered_alter_device_acceptor_and_more.py new file mode 100644 index 0000000..20b5347 --- /dev/null +++ b/apps/pos_device/migrations/0059_device_pre_registered_alter_device_acceptor_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.0 on 2025-08-17 11:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pos_device', '0058_rename_assigned_device_assigned_state'), + ] + + operations = [ + migrations.AddField( + model_name='device', + name='pre_registered', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='device', + name='acceptor', + field=models.CharField(max_length=50, null=True), + ), + migrations.AlterField( + model_name='device', + name='terminal', + field=models.CharField(max_length=50, null=True), + ), + ] diff --git a/apps/pos_device/models.py b/apps/pos_device/models.py index f0f8284..a37402f 100644 --- a/apps/pos_device/models.py +++ b/apps/pos_device/models.py @@ -32,8 +32,8 @@ class ProviderCompany(BaseModel): class Device(BaseModel): device_identity = models.CharField(max_length=25, null=True) - acceptor = models.CharField(max_length=50) - terminal = models.CharField(max_length=50) + acceptor = models.CharField(max_length=50, null=True) + terminal = models.CharField(max_length=50, null=True) serial = models.TextField(null=True) password = models.CharField(max_length=25, null=True) multi_device = models.BooleanField(default=False) @@ -41,7 +41,7 @@ class Device(BaseModel): latitude = models.FloatField(default=0) longitude = models.FloatField(default=0) is_activated = models.BooleanField(default=False) - # pre_registered = models.BooleanField(default=False) + pre_registered = models.BooleanField(default=False) organization = models.ForeignKey( Organization, on_delete=models.CASCADE, @@ -57,8 +57,8 @@ class Device(BaseModel): """ generate identity for every device """ prefix = "POS" while True: - number_part = ''.join(random.choices(string.digits, k=6)) - code = f"{prefix}{number_part}" + number_part = ''.join(random.choices(string.digits, k=9)) + code = f"{number_part}" if not Device.objects.filter(device_identity=code).exists(): return code diff --git a/apps/pos_device/pos/api/v1/serializers.py b/apps/pos_device/pos/api/v1/serializers.py deleted file mode 100644 index e69de29..0000000 diff --git a/apps/pos_device/pos/api/v1/serializers/device.py b/apps/pos_device/pos/api/v1/serializers/device.py index e69de29..13ea4c9 100644 --- a/apps/pos_device/pos/api/v1/serializers/device.py +++ b/apps/pos_device/pos/api/v1/serializers/device.py @@ -0,0 +1,8 @@ +from apps.pos_device import models as pos_models +from rest_framework import serializers + + +class DeviceSerializer(serializers.ModelSerializer): + class Meta: + model = pos_models.Device + fields = '__all__' diff --git a/apps/pos_device/pos/api/v1/urls.py b/apps/pos_device/pos/api/v1/urls.py index c28983a..de64bf6 100644 --- a/apps/pos_device/pos/api/v1/urls.py +++ b/apps/pos_device/pos/api/v1/urls.py @@ -1,9 +1,7 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter -from .viewsets.device import TestViewSet router = DefaultRouter() -router.register('test', TestViewSet, basename='test') urlpatterns = [ path('v1/', include(router.urls)) diff --git a/apps/pos_device/pos/api/v1/viewsets/device.py b/apps/pos_device/pos/api/v1/viewsets/device.py index 54f6d5b..093e5ce 100644 --- a/apps/pos_device/pos/api/v1/viewsets/device.py +++ b/apps/pos_device/pos/api/v1/viewsets/device.py @@ -1,8 +1,108 @@ -from rest_framework import viewsets +from apps.pos_device.pos.api.v1.serializers.device import DeviceSerializer +from apps.pos_device import models as pos_models +from rest_framework.permissions import AllowAny +from rest_framework.decorators import action from rest_framework.response import Response +from common.generics import get_client_ip +from rest_framework import viewsets +from django.db import transaction +from rest_framework import status -class TestViewSet(viewsets.ModelViewSet): +class POSDeviceViewSet(viewsets.ModelViewSet): + device_queryset = pos_models.Device.objects.all() + session_queryset = pos_models.Sessions.objects.all() + serializer_class = DeviceSerializer + HEADERS = [ + 'device-mac', 'device-serial', 'device-name', + 'device-sdk', 'device-version', + 'device-lng', 'device-lot', 'device-provider', # noqa + 'device-vname', # noqa + ] + permission_classes = [AllowAny] - def list(self, request, *args, **kwargs): - return Response("Hello from the outsiiiiiiiiide") # noqa + @action( + methods=['post'], + detail=False, + url_path='login', + url_name='login', + name='login' + ) + @transaction.atomic + def login(self, request): + """ login of pos device """ + + # convert headers to dictionary + headers_data = {key: request.headers.get(key) for key in self.HEADERS} + + serial = headers_data['device-serial'] + sdk = headers_data['device-sdk'] + psp_name = headers_data['device-provider'] + organization = pos_models.Organization.objects.get(en_name=psp_name) + + # check if device exists + if 'device_identity' in request.data.keys(): + device = self.device_queryset.filter(device_identity=request.data['device_identity']) + else: + device = self.device_queryset.filter(serial=serial).first() + + # activate device + if device: + if not device.is_activated: + device.is_activated = True + device.save() + + session = pos_models.Sessions.objects.create( + device=device, + password=device.password, + version=headers_data['device-version'], + mac=headers_data['device-mac'], + ip=get_client_ip(request), + sdk=headers_data['device-sdk'], + serial=headers_data['device-serial'], + latitude=headers_data['device-lot'], + longitude=headers_data['device-lng'], + ) + return Response({ + "message": "login success - session activated", + "device_identity": device.device_identity, + "serial": device.serial + }, status=status.HTTP_200_OK) + + pre_device = pos_models.Device.objects.create( + serial=serial, + sdk=sdk, + organization=organization, + pre_regitered=True, + is_activated=False + ) + + return Response({ + "message": "device pre-registered", + "device_identity": pre_device.device_identity + }, status=status.HTTP_200_OK) + + @action( + methods=['post'], + detail=False, + url_path='merge_devices', + url_name='merge_devices', + name='merge_devices' + ) + @transaction.atomic + def merge_devices(self, request): + """ merge pre register device & device has registered by psp user """ + + pre_device = self.device_queryset.get(device_identity=request.data['pre_device']) + real_device = self.device_queryset.get(device_identity=request.data['real_device']) + + real_device.device_identity = pre_device.device_identity + real_device.save() + + pre_device.delete() + + return Response({ + "message": "device merged successfully", + "device_identity": real_device.device_identity, + "serial": real_device.serial + }) diff --git a/apps/pos_device/services.py b/apps/pos_device/services.py deleted file mode 100644 index 93888af..0000000 --- a/apps/pos_device/services.py +++ /dev/null @@ -1 +0,0 @@ -# Your services go here diff --git a/apps/pos_device/services/helper_service.py b/apps/pos_device/services/helper_service.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/pos_device/services/helper_service.py @@ -0,0 +1 @@ + diff --git a/apps/pos_device/urls.py b/apps/pos_device/urls.py index 937ec75..3fe481d 100644 --- a/apps/pos_device/urls.py +++ b/apps/pos_device/urls.py @@ -1,6 +1,12 @@ from django.urls import path, include +from .pos.api.v1.viewsets import device +from rest_framework.routers import DefaultRouter + +router = DefaultRouter() +router.register(r'', device.POSDeviceViewSet, basename='auth') urlpatterns = [ path('web/', include('apps.pos_device.web.api.v1.urls')), - path('pos/', include('apps.pos_device.pos.api.v1.urls')) + path('pos/', include('apps.pos_device.pos.api.v1.urls')), + path('auth/', include(router.urls)), ] diff --git a/common/generics.py b/common/generics.py index 15b0d85..18e8807 100644 --- a/common/generics.py +++ b/common/generics.py @@ -9,3 +9,8 @@ def base64_to_image_file(base64_string, filename="image.jpg"): img_format, img_str = base64_string.split(';base64,') # split before & after of ';base64,' ext = img_format.split('/')[-1] # split format of file return ContentFile(base64.b64decode(img_str), name=f"{filename}.{ext}"), ext + + +def get_client_ip(request): # noqa + forwarded = request.META.get('HTTP_X_FORWARDED_FOR') + return forwarded.split(',')[0] if forwarded else request.META.get('REMOTE_ADDR')