diff --git a/client/src/app/shared/models/users/user.ts b/client/src/app/shared/models/users/user.ts index 1ec91284b..00179e534 100644 --- a/client/src/app/shared/models/users/user.ts +++ b/client/src/app/shared/models/users/user.ts @@ -42,6 +42,6 @@ export class User extends BaseDecimalModel { } protected getDecimalFields(): string[] { - return ["vote_weight"]; + return ['vote_weight']; } } diff --git a/openslides/assignments/migrations/0012_assignmetn_vote_unique_together.py b/openslides/assignments/migrations/0012_assignment_vote_unique_together.py similarity index 100% rename from openslides/assignments/migrations/0012_assignmetn_vote_unique_together.py rename to openslides/assignments/migrations/0012_assignment_vote_unique_together.py diff --git a/openslides/assignments/views.py b/openslides/assignments/views.py index e130673a9..6aaba3356 100644 --- a/openslides/assignments/views.py +++ b/openslides/assignments/views.py @@ -2,7 +2,6 @@ from decimal import Decimal from django.contrib.auth import get_user_model from django.db import transaction -from django.db.utils import IntegrityError from openslides.poll.views import BaseOptionViewSet, BasePollViewSet, BaseVoteViewSet from openslides.utils.auth import has_perm @@ -526,17 +525,11 @@ class AssignmentPollViewSet(BasePollViewSet): poll.voted.add(check_user) + def add_user_to_voted_array(self, user, poll): + VotedModel = AssignmentPoll.voted.through + VotedModel.objects.create(assignmentpoll=poll, user=user) + def handle_named_vote(self, data, poll, user): - try: - with transaction.atomic(): - return self.try_handle_named_vote(data, poll, user) - except IntegrityError: - raise ValidationError({"detail": "You have already voted"}) - - def try_handle_named_vote(self, data, poll, user): - if user in poll.voted.all(): - raise ValidationError({"detail": "You have already voted"}) - if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES: self.create_votes_type_votes(data, poll, user) elif poll.pollmethod in ( @@ -546,16 +539,6 @@ class AssignmentPollViewSet(BasePollViewSet): self.create_votes_type_named_pseudoanonymous(data, poll, user, user) def handle_pseudoanonymous_vote(self, data, poll, user): - try: - with transaction.atomic(): - return self.try_handle_pseudoanonymous_vote(data, poll, user) - except IntegrityError: - raise ValidationError({"detail": "You have already voted"}) - - def try_handle_pseudoanonymous_vote(self, data, poll, user): - if user in poll.voted.all(): - raise ValidationError({"detail": "You have already voted"}) - if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES: self.create_votes_type_votes(data, poll, user) diff --git a/openslides/motions/views.py b/openslides/motions/views.py index 01450660d..857060b95 100644 --- a/openslides/motions/views.py +++ b/openslides/motions/views.py @@ -1176,6 +1176,10 @@ class MotionPollViewSet(BasePollViewSet): return result + def add_user_to_voted_array(self, user, poll): + VotedModel = MotionPoll.voted.through + VotedModel.objects.create(motionpoll=poll, user=user) + def handle_analog_vote(self, data, poll, user): option = poll.options.get() vote, _ = MotionVote.objects.get_or_create(option=option, value="Y") @@ -1223,21 +1227,11 @@ class MotionPollViewSet(BasePollViewSet): raise ValidationError("Data must be Y or N") def handle_named_vote(self, data, poll, user): - if user in poll.voted.all(): - raise ValidationError({"detail": "You have already voted"}) - poll.voted.add(user) - poll.save() - option = poll.options.get() vote = MotionVote.objects.create(user=user, option=option) self.handle_named_and_pseudoanonymous_vote(data, user, poll, option, vote) def handle_pseudoanonymous_vote(self, data, poll, user): - if user in poll.voted.all(): - raise ValidationError({"detail": "You have already voted"}) - poll.voted.add(user) - poll.save() - option = poll.options.get() vote = MotionVote.objects.create(user=None, option=option) self.handle_named_and_pseudoanonymous_vote(data, user, poll, option, vote) diff --git a/openslides/poll/views.py b/openslides/poll/views.py index 1debc08c0..0b27719ac 100644 --- a/openslides/poll/views.py +++ b/openslides/poll/views.py @@ -2,6 +2,7 @@ from textwrap import dedent from django.contrib.auth.models import AnonymousUser from django.db import transaction +from django.db.utils import IntegrityError from rest_framework import status from openslides.utils.auth import in_some_groups @@ -184,6 +185,7 @@ class BasePollViewSet(ModelViewSet): return Response() @detail_route(methods=["POST"]) + @transaction.atomic def vote(self, request, pk): """ For motion polls: Just "Y", "N" or "A" (if pollmethod is "YNA") @@ -219,10 +221,10 @@ class BasePollViewSet(ModelViewSet): def assert_can_vote(self, poll, request): """ - Raises a permission denied, if the user is not allowed to vote. + Raises a permission denied, if the user is not allowed to vote (or has already voted). + Adds the user to the voted array, so this needs to be reverted on error! Analog: has to have manage permissions Named & Pseudoanonymous: has to be in a poll group and present - Note: For pseudoanonymous it is *not* tested, if the user has already voted! """ if poll.type == BasePoll.TYPE_ANALOG: if not self.has_manage_permissions(): @@ -231,11 +233,17 @@ class BasePollViewSet(ModelViewSet): if poll.state != BasePoll.STATE_STARTED: raise ValidationError("You can only vote on a started poll.") if not request.user.is_present or not in_some_groups( - request.user.id, - list(poll.groups.values_list("pk", flat=True)), - exact=True, + request.user.id, + list(poll.groups.values_list("pk", flat=True)), + exact=True, ): - self.permission_denied(request) + self.permission_denied(request) + + try: + self.add_user_to_voted_array(request.user, poll) + inform_changed_data(poll) + except IntegrityError: + raise ValidationError({"detail": "You have already voted"}) def parse_vote_value(self, obj, key): """ Raises a ValidationError on incorrect values, including None """ @@ -257,6 +265,13 @@ class BasePollViewSet(ModelViewSet): """ pass + def add_user_to_voted_array(self, user, poll): + """ + To be implemented by subclass. Adds the given user to the voted array of the given poll. + Throws an IntegrityError if the user already exists in the array + """ + raise NotImplementedError() + def validate_vote_data(self, data, poll, user): """ To be implemented by subclass. Validates the data according to poll type and method and fields by validated versions.