diff --git a/opentech/apply/funds/tests/factories/models.py b/opentech/apply/funds/tests/factories/models.py index 591854e893a1154f4ea875c92297eef859825c3f..c90f97426cdc0a8523c58fa11d480234451e3d93 100644 --- a/opentech/apply/funds/tests/factories/models.py +++ b/opentech/apply/funds/tests/factories/models.py @@ -202,6 +202,9 @@ class ApplicationSubmissionFactory(factory.DjangoModelFactory): status='draft_proposal', workflow_name='double', ) + rejected = factory.Trait( + status='rejected' + ) form_fields = blocks.CustomFormFieldsFactory form_data = factory.SubFactory( diff --git a/opentech/apply/funds/workflow.py b/opentech/apply/funds/workflow.py index f46140ebb47abb717ab5d8358dc76815cae8e514..59291fdd156d891b95978928a870023f76645915 100644 --- a/opentech/apply/funds/workflow.py +++ b/opentech/apply/funds/workflow.py @@ -79,32 +79,52 @@ class Stage: return self.name -class Permission: +class BasePermissions: def can_edit(self, user: 'User') -> bool: + if user.is_apply_staff: + return self.can_staff_edit(user) + + if user.is_applicant: + return self.can_applicant_edit(user) + + def can_staff_edit(self, user: 'User') -> bool: + return False + + def can_applicant_edit(self, user: 'User') -> bool: return False + def can_review(self, user: 'User') -> bool: + if user.is_apply_staff: + return self.can_staff_review(user) + + if user.is_reviewer: + return self.can_reviewer_review(user) + def can_staff_review(self, user: 'User') -> bool: return False def can_reviewer_review(self, user: 'User') -> bool: return False - def can_review(self, user: 'User') -> bool: - return self.can_staff_review(user) or self.can_reviewer_review(user) + +class NoPermissions(BasePermissions): + pass -class StaffReviewPermission(Permission): +class DefaultPermissions(BasePermissions): + # Other Permissions should inherit from this class + # Staff can review at any time def can_staff_review(self, user: 'User') -> bool: - return user.is_apply_staff + return True -class ReviewerReviewPermission(Permission): +class ReviewerReviewPermissions(DefaultPermissions): def can_reviewer_review(self, user: 'User') -> bool: - return user.is_reviewer + return True -class CanEditPermission(Permission): - def can_edit(self, user: 'User') -> bool: +class CanEditPermissions(DefaultPermissions): + def can_applicant_edit(self, user: 'User') -> bool: return True @@ -126,7 +146,7 @@ SingleStageDefinition = { }, 'display': 'Under Discussion', 'stage': Request, - 'permissions': Permission(), + 'permissions': DefaultPermissions(), 'step': 0, }, 'more_info': { @@ -135,7 +155,7 @@ SingleStageDefinition = { }, 'display': 'More information required', 'stage': Request, - 'permissions': CanEditPermission(), + 'permissions': CanEditPermissions(), 'step': 0, }, 'internal_review': { @@ -144,7 +164,7 @@ SingleStageDefinition = { }, 'display': 'Internal Review', 'stage': Request, - 'permissions': StaffReviewPermission(), + 'permissions': DefaultPermissions(), 'step': 1, }, 'post_review_discussion': { @@ -155,7 +175,7 @@ SingleStageDefinition = { }, 'display': 'Under Discussion', 'stage': Request, - 'permissions': Permission(), + 'permissions': DefaultPermissions(), 'step': 2, }, 'post_review_more_info': { @@ -164,20 +184,20 @@ SingleStageDefinition = { }, 'display': 'More information required', 'stage': Request, - 'permissions': CanEditPermission(), + 'permissions': CanEditPermissions(), 'step': 2, }, 'accepted': { 'display': 'Accepted', 'stage': Request, - 'permissions': Permission(), + 'permissions': NoPermissions(), 'step': 3, }, 'rejected': { 'display': 'Rejected', 'stage': Request, - 'permissions': Permission(), + 'permissions': NoPermissions(), 'step': 3, }, } @@ -192,7 +212,7 @@ DoubleStageDefinition = { }, 'display': 'Under Discussion', 'stage': Concept, - 'permissions': Permission(), + 'permissions': DefaultPermissions(), 'step': 0, }, 'concept_more_info': { @@ -201,7 +221,7 @@ DoubleStageDefinition = { }, 'display': 'More information required', 'stage': Concept, - 'permissions': CanEditPermission(), + 'permissions': CanEditPermissions(), 'step': 0, }, 'concept_internal_review': { @@ -210,7 +230,7 @@ DoubleStageDefinition = { }, 'display': 'Internal Review', 'stage': Concept, - 'permissions': StaffReviewPermission(), + 'permissions': DefaultPermissions(), 'step': 1, }, 'concept_review_discussion': { @@ -221,7 +241,7 @@ DoubleStageDefinition = { }, 'display': 'Under Discussion', 'stage': Concept, - 'permissions': Permission(), + 'permissions': DefaultPermissions(), 'step': 2, }, 'concept_review_more_info': { @@ -230,7 +250,7 @@ DoubleStageDefinition = { }, 'display': 'More information required', 'stage': Concept, - 'permissions': CanEditPermission(), + 'permissions': CanEditPermissions(), 'step': 2, }, 'invited_to_proposal': { @@ -244,13 +264,13 @@ DoubleStageDefinition = { }, }, 'stage': Concept, - 'permissions': Permission(), + 'permissions': NoPermissions(), 'step': 3, }, 'concept_rejected': { 'display': 'Rejected', 'stage': Concept, - 'permissions': Permission(), + 'permissions': NoPermissions(), 'step': 3, }, 'draft_proposal': { @@ -259,7 +279,7 @@ DoubleStageDefinition = { }, 'display': 'Invited for Proposal', 'stage': Proposal, - 'permissions': CanEditPermission(), + 'permissions': CanEditPermissions(), 'step': 4, }, 'proposal_discussion': { @@ -270,7 +290,7 @@ DoubleStageDefinition = { }, 'display': 'Under Discussion', 'stage': Proposal, - 'permissions': Permission(), + 'permissions': DefaultPermissions(), 'step': 5, }, 'proposal_more_info': { @@ -279,7 +299,7 @@ DoubleStageDefinition = { }, 'display': 'More information required', 'stage': Proposal, - 'permissions': CanEditPermission(), + 'permissions': CanEditPermissions(), 'step': 5, }, 'proposal_internal_review': { @@ -288,7 +308,7 @@ DoubleStageDefinition = { }, 'display': 'Internal Review', 'stage': Proposal, - 'permissions': StaffReviewPermission(), + 'permissions': DefaultPermissions(), 'step': 6, }, 'post_proposal_review_discussion': { @@ -299,7 +319,7 @@ DoubleStageDefinition = { }, 'display': 'Under Discussion', 'stage': Proposal, - 'permissions': Permission(), + 'permissions': DefaultPermissions(), 'step': 7, }, 'post_proposal_review_more_info': { @@ -308,7 +328,7 @@ DoubleStageDefinition = { }, 'display': 'More information required', 'stage': Proposal, - 'permissions': CanEditPermission(), + 'permissions': CanEditPermissions(), 'step': 7, }, 'external_review': { @@ -317,7 +337,7 @@ DoubleStageDefinition = { }, 'display': 'Advisory Council Review', 'stage': Proposal, - 'permissions': ReviewerReviewPermission(), + 'permissions': ReviewerReviewPermissions(), 'step': 8, }, 'post_external_review_discussion': { @@ -328,7 +348,7 @@ DoubleStageDefinition = { }, 'display': 'Under Discussion', 'stage': Proposal, - 'permissions': Permission(), + 'permissions': DefaultPermissions(), 'step': 9, }, 'post_external_review_more_info': { @@ -337,19 +357,19 @@ DoubleStageDefinition = { }, 'display': 'More information required', 'stage': Proposal, - 'permissions': CanEditPermission(), + 'permissions': CanEditPermissions(), 'step': 9, }, 'proposal_accepted': { 'display': 'Accepted', 'stage': Proposal, - 'permissions': Permission(), + 'permissions': NoPermissions(), 'step': 10, }, 'proposal_rejected': { 'display': 'Rejected', 'stage': Proposal, - 'permissions': Permission(), + 'permissions': NoPermissions(), 'step': 10, }, diff --git a/opentech/apply/review/tests/test_views.py b/opentech/apply/review/tests/test_views.py index 3ac1e6ab244cbc935459a6afccb74eff8b6f13f8..09b4fefaa5195a26a001c85b9ffa3bbd6b25fc0f 100644 --- a/opentech/apply/review/tests/test_views.py +++ b/opentech/apply/review/tests/test_views.py @@ -61,7 +61,7 @@ class StaffReviewFormTestCase(BaseViewTestCase): self.assertContains(response, reverse('funds:submissions:detail', kwargs={'pk': submission.id})) def test_cant_access_wrong_status(self): - submission = ApplicationSubmissionFactory() + submission = ApplicationSubmissionFactory(rejected=True) response = self.get_page(submission, 'form') self.assertEqual(response.status_code, 403) @@ -73,8 +73,6 @@ class StaffReviewFormTestCase(BaseViewTestCase): self.assertEqual(response.context['title'], 'Update Review draft') def test_can_edit_draft_review(self): - # FIXME fix form generation issue in ReviewFundTypeFactory review_forms() - return submission = ApplicationSubmissionFactory(status='internal_review') ReviewFactory(submission=submission, author=self.user, is_draft=True) response = self.post_page(submission, {'data': 'value'}, 'form') diff --git a/opentech/apply/users/models.py b/opentech/apply/users/models.py index 62e753ef3dc3fbf270a0873add6ebc4364477ded..63b06aca30a659d95376598cc8f54c59aa440b04 100644 --- a/opentech/apply/users/models.py +++ b/opentech/apply/users/models.py @@ -93,6 +93,10 @@ class User(AbstractUser): def is_reviewer(self): return self.groups.filter(name=REVIEWER_GROUP_NAME).exists() + @property + def is_applicant(self): + return not self.groups.exists() + class Meta: ordering = ('full_name', 'email')