From 9e4449d0d6a20e1a751ac74fbe29e0dbc21c2597 Mon Sep 17 00:00:00 2001 From: i701 Date: Fri, 25 Jul 2025 00:01:56 +0500 Subject: [PATCH] =?UTF-8?q?feat(agreement):=20add=20agreement=20field=20to?= =?UTF-8?q?=20user=20model=20and=20implement=20agreement=20upload=20functi?= =?UTF-8?q?onality=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + api/migrations/0018_user_agreement.py | 22 +++++++++++++ api/models.py | 7 +++++ api/serializers.py | 2 ++ api/urls.py | 6 ++++ api/views.py | 45 +++++++++++++++++++++++++++ 6 files changed, 83 insertions(+) create mode 100644 api/migrations/0018_user_agreement.py diff --git a/.gitignore b/.gitignore index 1a216ac..50ff047 100644 --- a/.gitignore +++ b/.gitignore @@ -163,3 +163,4 @@ cython_debug/ #staticfiles staticfiles/ postgres_data/ +media/ \ No newline at end of file diff --git a/api/migrations/0018_user_agreement.py b/api/migrations/0018_user_agreement.py new file mode 100644 index 0000000..5ef2fa3 --- /dev/null +++ b/api/migrations/0018_user_agreement.py @@ -0,0 +1,22 @@ +# Generated by Django 5.2 on 2025-07-24 18:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("api", "0017_alter_temporaryuser_t_id_card_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="agreement", + field=models.FileField( + blank=True, + help_text="Upload the agreement file signed by the user.", + null=True, + upload_to="agreements/", + ), + ), + ] diff --git a/api/models.py b/api/models.py index f92cb6e..0506147 100644 --- a/api/models.py +++ b/api/models.py @@ -34,6 +34,13 @@ class User(AbstractUser): island = models.ForeignKey( "Island", on_delete=models.SET_NULL, null=True, blank=True, related_name="users" ) + + agreement = models.FileField( + upload_to="agreements/", + blank=True, + null=True, + help_text="Upload the agreement file signed by the user.", + ) created_at = models.DateTimeField(default=timezone.now) updated_at = models.DateTimeField(auto_now=True) diff --git a/api/serializers.py b/api/serializers.py index 7643397..e3b0c35 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -45,6 +45,7 @@ class UserUpdateSerializer(serializers.ModelSerializer): "dob", "atoll", "island", + "agreement", ) @@ -109,6 +110,7 @@ class CustomReadOnlyUserSerializer(serializers.ModelSerializer): "address", "acc_no", "id_card", + "agreement", ) depth = 1 diff --git a/api/urls.py b/api/urls.py index 7517049..fa49f2c 100644 --- a/api/urls.py +++ b/api/urls.py @@ -23,6 +23,7 @@ from .views import ( UserVerifyAPIView, UserUpdateAPIView, UserRejectAPIView, + UpdateAgreementView, ) @@ -45,6 +46,11 @@ urlpatterns = [ path("users/temp/filter/", filter_temporary_user, name="filter-temporary-users"), # User verification flow path("users//verify/", UserVerifyAPIView.as_view(), name="user-verify"), + path( + "users//agreement/", + UpdateAgreementView.as_view(), + name="user-agreement", + ), path("users//reject/", UserRejectAPIView.as_view(), name="user-reject"), path("healthcheck/", healthcheck, name="healthcheck"), path("test/", test_email, name="testemail"), diff --git a/api/views.py b/api/views.py index 2e1fbd0..1a4eadd 100644 --- a/api/views.py +++ b/api/views.py @@ -358,6 +358,51 @@ class UserUpdateAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView): return super().update(request, *args, **kwargs) +class UpdateAgreementView(StaffEditorPermissionMixin, generics.UpdateAPIView): + serializer_class = UserUpdateSerializer + queryset = User.objects.all() + lookup_field = "pk" + + def update(self, request, *args, **kwargs): + user_id = kwargs.get("pk") + user = get_object_or_404(User, pk=user_id) + agreement_file = request.data.get("agreement_file") + if not agreement_file: + return Response( + {"message": "Agreement file is required."}, + status=status.HTTP_400_BAD_REQUEST, + ) + if agreement_file.size > 10 * 1024 * 1024: # 5 MB limit + return Response( + {"message": "File size exceeds 10 MB limit."}, + status=status.HTTP_400_BAD_REQUEST, + ) + if agreement_file.content_type not in [ + "application/pdf", + ]: + return Response( + {"message": "Invalid file type. Only PDF files are allowed."}, + status=status.HTTP_400_BAD_REQUEST, + ) + if request.user != user and ( + not request.user.is_authenticated + or not getattr(request.user, "is_admin", False) + ): + return Response( + {"message": "You are not authorized to update this user."}, + status=status.HTTP_403_FORBIDDEN, + ) + serializer = self.get_serializer( + user, + data=request.data, + partial=True, + ) + serializer.is_valid(raise_exception=True) + user.agreement = agreement_file + user.save() + return super().update(request, *args, **kwargs) + + class KnoxTokenListApiView( StaffEditorPermissionMixin, generics.ListAPIView,