diff --git a/client/src/app/core/ui-services/voting-banner.service.ts b/client/src/app/core/ui-services/voting-banner.service.ts index 88cadfcf9..5a4aa46d9 100644 --- a/client/src/app/core/ui-services/voting-banner.service.ts +++ b/client/src/app/core/ui-services/voting-banner.service.ts @@ -7,8 +7,10 @@ import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment- import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll'; import { ViewBasePoll } from 'app/site/polls/models/view-base-poll'; import { PollListObservableService } from 'app/site/polls/services/poll-list-observable.service'; +import { ViewUser } from 'app/site/users/models/view-user'; import { BannerDefinition, BannerService } from './banner.service'; import { OpenSlidesStatusService } from '../core-services/openslides-status.service'; +import { OperatorService } from '../core-services/operator.service'; import { VotingService } from './voting.service'; @Injectable({ @@ -19,23 +21,46 @@ export class VotingBannerService { private subText = _('Click here to vote!'); + private polls: ViewBasePoll[]; + + private delegations: ViewUser[]; + public constructor( pollListObservableService: PollListObservableService, private banner: BannerService, private translate: TranslateService, private OSStatus: OpenSlidesStatusService, - private votingService: VotingService + private votingService: VotingService, + private operator: OperatorService ) { - pollListObservableService.getViewModelListObservable().subscribe(polls => this.checkForVotablePolls(polls)); + pollListObservableService.getViewModelListObservable().subscribe(polls => { + this.polls = polls; + this.checkForVotablePolls(); + }); + operator.getViewUserObservable().subscribe(user => { + if (user) { + this.delegations = user.voteDelegationsFrom; + this.checkForVotablePolls(); + } + }); } /** * checks all polls for votable ones and displays a banner for them * @param polls the updated poll list */ - private checkForVotablePolls(polls: ViewBasePoll[]): void { + private checkForVotablePolls(): void { // display no banner if in history mode or there are no polls to vote - const pollsToVote = polls.filter(poll => this.votingService.canVote(poll) && !poll.user_has_voted); + const pollsToVote = this.polls.filter(poll => { + if (this.votingService.canVote(poll) && !poll.user_has_voted) { + return true; + } + for (const delegation of this.delegations) { + if (this.votingService.canVote(poll, delegation) && !poll.hasVotedId(delegation.id)) { + return true; + } + } + }); if ((this.OSStatus.isInHistoryMode && this.currentBanner) || !pollsToVote.length) { this.sliceBanner(); return; diff --git a/server/openslides/poll/models.py b/server/openslides/poll/models.py index 0bb3d3819..7a6169446 100644 --- a/server/openslides/poll/models.py +++ b/server/openslides/poll/models.py @@ -278,7 +278,10 @@ class BasePoll(models.Model): entitled_users_ids = set() for group in self.groups.all(): for user in group.user_set.all(): - if user.is_present and user.id not in entitled_users_ids: + if ( + user.is_present + or (user.vote_delegated_to and user.vote_delegated_to.is_present) + ) and user.id not in entitled_users_ids: entitled_users_ids.add(user.id) entitled_users.append( { diff --git a/server/tests/integration/motions/test_polls.py b/server/tests/integration/motions/test_polls.py index 60063b628..6bf633490 100644 --- a/server/tests/integration/motions/test_polls.py +++ b/server/tests/integration/motions/test_polls.py @@ -1193,6 +1193,9 @@ class StopMotionPoll(TestCase): def test_stop_poll_with_entitled_users_and_vote_delegation(self): self.setup_entitled_users() user, _ = self.create_user() + user.is_present = True + user.save() + self.admin.is_present = False self.admin.vote_delegated_to = user self.admin.save() response = self.client.post(reverse("motionpoll-stop", args=[self.poll.pk]))