diff --git a/hypha/apply/dashboard/tests/test_views.py b/hypha/apply/dashboard/tests/test_views.py index 4f1fc1ae03f6c94f4652f3afbe3dc4e08062c7cc..a4e160acbce7aca7e0e68b5cb1b231cf9b4db15d 100644 --- a/hypha/apply/dashboard/tests/test_views.py +++ b/hypha/apply/dashboard/tests/test_views.py @@ -10,7 +10,7 @@ from hypha.apply.projects.models.payment import ( RESUBMITTED, SUBMITTED, ) -from hypha.apply.projects.models.project import COMMITTED +from hypha.apply.projects.models.project import WAITING_FOR_APPROVAL from hypha.apply.projects.tests.factories import InvoiceFactory, ProjectFactory from hypha.apply.review.tests.factories import ReviewFactory, ReviewOpinionFactory from hypha.apply.users.groups import APPROVER_GROUP_NAME @@ -135,13 +135,13 @@ class TestStaffDashboard(BaseViewTestCase): self.assertNotContains(response, "Active Invoices") def test_non_project_approver_cannot_see_projects_awaiting_review_stats_or_table(self): - ProjectFactory(is_locked=True, status=COMMITTED) + ProjectFactory(is_locked=False, status=WAITING_FOR_APPROVAL) response = self.get_page() self.assertNotContains(response, "Projects awaiting approval") def test_project_approver_can_see_projects_awaiting_review_stats_or_table(self): - ProjectFactory(is_locked=True, status=COMMITTED) + ProjectFactory(is_locked=False, status=WAITING_FOR_APPROVAL) user = StaffFactory() user.groups.add(GroupFactory(name=APPROVER_GROUP_NAME)) diff --git a/hypha/apply/dashboard/views.py b/hypha/apply/dashboard/views.py index bfc9d8150b0f0cc6c24ed2196fa47b7b02de1f58..9ac43c32d82e9ec7b365309490083d2fc595bf93 100644 --- a/hypha/apply/dashboard/views.py +++ b/hypha/apply/dashboard/views.py @@ -122,7 +122,7 @@ class AdminDashboardView(MyFlaggedMixin, TemplateView): 'table': None, } - to_approve = Project.objects.in_approval().for_table() + to_approve = Project.objects.waiting_for_approval().for_table() return { 'count': to_approve.count(), diff --git a/hypha/apply/projects/forms/project.py b/hypha/apply/projects/forms/project.py index 7d20ee35aabc6ad4b66793758d8e22e75da71288..b967c98022e550d38d3ee01b945b50791cbe7207 100644 --- a/hypha/apply/projects/forms/project.py +++ b/hypha/apply/projects/forms/project.py @@ -151,15 +151,8 @@ class SetPendingForm(forms.ModelForm): if self.instance.status != COMMITTED: raise forms.ValidationError(_('A Project can only be sent for Approval when Committed.')) - if self.instance.is_locked: - raise forms.ValidationError(_('A Project can only be sent for Approval once')) - super().clean() - def save(self, *args, **kwargs): - self.instance.is_locked = True - return super().save(*args, **kwargs) - class UploadContractForm(forms.ModelForm): class Meta: diff --git a/hypha/apply/projects/models/project.py b/hypha/apply/projects/models/project.py index 907b5502179536c0f59d66b21c0229701f0f32c1..594392f62f8c9adf868b8cc8ee52ceb7eb1b4e92 100644 --- a/hypha/apply/projects/models/project.py +++ b/hypha/apply/projects/models/project.py @@ -80,13 +80,6 @@ class ProjectQuerySet(models.QuerySet): def complete(self): return self.filter(status=COMPLETE) - def in_approval(self): - return self.filter( - is_locked=True, - status=COMMITTED, - approvals__isnull=True, - ) - def waiting_for_approval(self): return self.filter( status=WAITING_FOR_APPROVAL, @@ -259,10 +252,12 @@ class Project(BaseStreamForm, AccessFormData, models.Model): def end_date(self): # Aiming for the proposed end date as the last day of the project # If still ongoing assume today is the end - return max( - self.proposed_end.date(), - timezone.now().date(), - ) + if self.proposed_end: + return max( + self.proposed_end.date(), + timezone.now().date(), + ) + return timezone.now().date() def paid_value(self): return self.invoices.paid_value() @@ -295,24 +290,27 @@ class Project(BaseStreamForm, AccessFormData, models.Model): def editable_by(self, user): if self.editable: - return True + # Approver can edit it when they are approving + if self.can_make_approval: + if user.is_finance or user.is_approver or user.is_contracting: + return True + + # Lead can make changes to the project + if user == self.lead: + return True - # Approver can edit it when they are approving - if self.can_make_approval: - if user.is_finance or user.is_approver or user.is_contracting: + # Staff can edit project + if user.is_apply_staff: return True + return False @property def editable(self): - if self.status not in (CONTRACTING, WAITING_FOR_APPROVAL, COMMITTED): - return True - - # Someone has approved the project - consider it locked while with contracting - if self.approvals.exists(): + if self.is_locked: return False - - # Someone must lead the project to make changes - return self.lead and not self.is_locked + elif self.status in (COMMITTED, WAITING_FOR_APPROVAL): # locked condition is enough,it is just for double check + return True + return False def get_absolute_url(self): if settings.PROJECTS_ENABLED: @@ -321,7 +319,7 @@ class Project(BaseStreamForm, AccessFormData, models.Model): @property def can_make_approval(self): - return self.is_locked and self.status == WAITING_FOR_APPROVAL + return self.status == WAITING_FOR_APPROVAL @property def can_make_final_approval(self): diff --git a/hypha/apply/projects/templates/application_projects/project_admin_detail.html b/hypha/apply/projects/templates/application_projects/project_admin_detail.html index 120b6cdeb96f840c04e5b0ae75fd50dc610611f8..19d215cc15c2b88cc8a95b2182b7d8fd991169c5 100644 --- a/hypha/apply/projects/templates/application_projects/project_admin_detail.html +++ b/hypha/apply/projects/templates/application_projects/project_admin_detail.html @@ -57,7 +57,7 @@ {% trans "A lead must be assigned" %} {% elif not object.user_has_updated_details %} {% trans "Project approval form must be completed" %} - {% elif object.is_locked %} + {% elif object.can_make_approval or object.can_make_final_approval %} {% trans "Currently awaiting approval" %} {% endif %}" {% endif %} diff --git a/hypha/apply/projects/tests/test_views.py b/hypha/apply/projects/tests/test_views.py index 6d49b8bb0881901d000acf96d0bd1b625daa77e6..163fd6d4a7a891451289d6bce985c2b497ea197d 100644 --- a/hypha/apply/projects/tests/test_views.py +++ b/hypha/apply/projects/tests/test_views.py @@ -110,7 +110,7 @@ class TestSendForApprovalView(BaseViewTestCase): project.refresh_from_db() - self.assertTrue(project.is_locked) + self.assertFalse(project.is_locked) self.assertEqual(project.status, WAITING_FOR_APPROVAL) @@ -241,7 +241,7 @@ class TestFinalApprovalView(BaseViewTestCase): self.assertEqual(response.status_code, 200) project.refresh_from_db() - self.assertFalse(project.is_locked) + self.assertTrue(project.is_locked) self.assertEqual(project.status, CONTRACTING) def test_final_rejection(self): diff --git a/hypha/apply/projects/views/project.py b/hypha/apply/projects/views/project.py index 34d8bb4cf3a9daa52ab57a28c52fd350932ebb75..0f4f800fe1d5887772d6cc59a7946ee6acc3440a 100644 --- a/hypha/apply/projects/views/project.py +++ b/hypha/apply/projects/views/project.py @@ -176,7 +176,7 @@ class FinalApprovalView(DelegatedViewMixin, UpdateView): source=project, ) - project.is_locked = False + project.is_locked = True project.status = CONTRACTING project.save(update_fields=['is_locked', 'status']) @@ -488,8 +488,7 @@ class ChangePAFStatusView(DelegatedViewMixin, UpdateView): if paf_status == REQUEST_CHANGE: self.object.status = COMMITTED - self.object.is_locked = False - self.object.save(update_fields=['status', 'is_locked']) + self.object.save(update_fields=['status']) messenger( MESSAGES.REQUEST_PROJECT_CHANGE,