From c3d5ef48f8b8633567d885d0d12a24673add1c3d Mon Sep 17 00:00:00 2001
From: Sandeep Chauhan <sandeepsajan0@gmail.com>
Date: Wed, 3 Jul 2024 11:35:19 +0530
Subject: [PATCH] Make paf and vendors details editable for active projects
 (#3924)

Fixes #3883  and #3908

Right now, PAF and Contracting information is only editable in Draft
state. But with these changes, PAF and Contracting details are editable
for active projects(*reviewer roles condition applied).
---
 hypha/apply/projects/permissions.py            | 18 ++++++++++++++++++
 .../includes/supporting_documents.html         |  7 ++++---
 .../project_approval_detail.html               |  4 ++--
 .../application_projects/vendor_detail.html    |  4 ++--
 .../projects/templatetags/approval_tools.py    | 11 +++++++++--
 hypha/apply/projects/views/project.py          | 11 ++++++-----
 hypha/apply/projects/views/vendor.py           | 11 +++++------
 7 files changed, 46 insertions(+), 20 deletions(-)

diff --git a/hypha/apply/projects/permissions.py b/hypha/apply/projects/permissions.py
index 7dd2a9e8d..b0ee9a4d8 100644
--- a/hypha/apply/projects/permissions.py
+++ b/hypha/apply/projects/permissions.py
@@ -364,6 +364,22 @@ def can_access_project(user, project):
     return False, "Forbidden Error"
 
 
+def can_edit_paf(user, project):
+    if no_pafreviewer_role() and project.status != COMPLETE:
+        return True, "Paf is editable for active projects if no reviewer roles"
+    if project.editable_by(user):
+        return True, "PAF is editable in Draft by this user"
+    return False, "You are not allowed to edit the project at this time"
+
+
+def can_edit_vendor_details(user, project):
+    if project.status == COMPLETE:
+        return False, "Only active project's details can be edited"
+    if user.is_apply_staff or user == project.lead:
+        return True, "Lead and staff can edit vendor details for any active project"
+    return False, "Forbidden Error"
+
+
 permissions_map = {
     "contract_approve": can_approve_contract,
     "contract_upload": can_upload_contract,
@@ -378,4 +394,6 @@ permissions_map = {
     "report_view": can_view_report,
     "submit_contract_documents": can_submit_contract_documents,
     "project_access": can_access_project,
+    "paf_edit": can_edit_paf,
+    "vendor_edit": can_edit_vendor_details,
 }
diff --git a/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html b/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html
index 57ec81692..6a789f4b4 100644
--- a/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html
+++ b/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html
@@ -1,5 +1,5 @@
 {% load i18n approval_tools project_tags heroicons %}
-{% user_can_edit_project object request.user as editable %}
+{% user_can_edit_vendor_details object request.user as can_edit_vendor_details %}
 {% allow_collapsible_header object header_type='project_documents' as collapsible_header %}
 
 <div class="docs-block wrapper--outer-space-large" {% if collapsible_header %} x-data="{ collapsed: true }" {% endif %}>
@@ -96,7 +96,7 @@
                         <p class="docs-block__title">{% trans "Contracting Information" %}</p>
                     </div>
                     <div class="docs-block__row-inner">
-                        {% if editable %}
+                        {% if can_edit_vendor_details %}
                             <a class="{% if not project.vendor.user_has_updated_details %}button button--project-action{% else %}docs-block__icon-link{% endif %}" href="{% url 'apply:projects:vendor' pk=project.pk %}">
                                 {% if project.vendor.user_has_updated_details %}
                                     {% heroicon_micro "pencil-square" class="inline me-1 w-4 h-4" aria_hidden=true %}
@@ -126,7 +126,8 @@
                     <p class="docs-block__title">{% trans "Project Form" %}</p>
                 </div>
                 <div class="docs-block__row-inner">
-                    {% if editable and not user.is_applicant %}
+                    {% user_can_edit_paf object user as can_edit_paf %}
+                    {% if can_edit_paf %}
                         <a class="{% if not object.user_has_updated_details %}button button--project-action{% else %}docs-block__icon-link{% endif %}" href="{% url 'apply:projects:edit' pk=object.pk %}">
                             {% if object.user_has_updated_details %}
                                 {% heroicon_micro "pencil-square" class="inline me-1 w-4 h-4" aria_hidden=true %}
diff --git a/hypha/apply/projects/templates/application_projects/project_approval_detail.html b/hypha/apply/projects/templates/application_projects/project_approval_detail.html
index a0bff7233..42d4abbe8 100644
--- a/hypha/apply/projects/templates/application_projects/project_approval_detail.html
+++ b/hypha/apply/projects/templates/application_projects/project_approval_detail.html
@@ -101,8 +101,8 @@
                     <aside class="sidebar sidebar__project">
                         <div class="js-actions-sidebar sidebar__inner sidebar__inner--light-blue sidebar__inner--actions {% if mobile %}sidebar__inner--mobile{% endif %}">
                             <h5>{% trans "Actions to take" %}</h5>
-                            {% user_can_edit_project object user as can_edit_project %}
-                            {% if can_edit_project %}
+                            {% user_can_edit_paf object user as can_edit_paf %}
+                            {% if can_edit_paf %}
                                 <a class="button button--bottom-space button--primary button--full-width {% if user_can_approve %} is-disabled {% endif %}" href="{% url 'apply:projects:edit' pk=object.pk %}">{% trans "Edit PAF" %}</a>
                             {% endif %}
                             <div x-data="{ show: false }" class="dropdown">
diff --git a/hypha/apply/projects/templates/application_projects/vendor_detail.html b/hypha/apply/projects/templates/application_projects/vendor_detail.html
index a7aebf254..e531e868c 100644
--- a/hypha/apply/projects/templates/application_projects/vendor_detail.html
+++ b/hypha/apply/projects/templates/application_projects/vendor_detail.html
@@ -1,6 +1,6 @@
 {% extends "base-apply.html" %}
 {% load nh3_tags i18n approval_tools heroicons %}
-{% user_can_edit_project object request.user as editable %}
+{% user_can_edit_vendor_details object request.user as can_edit_vendor_details %}
 {% block title %}{% trans "Contracting Information for" %} {{ project.title }} {% endblock %}
 
 {% block content %}
@@ -18,7 +18,7 @@
         <div>
             <h5 class="vendor-info">{% trans "Last Updated" %}: {{ vendor.updated_at|date:'DATE_FORMAT' }}</h5>
         </div>
-        {% if editable %}
+        {% if can_edit_vendor_details %}
             <div>
                 <a class="link link--edit-vendor is-active" href="{% url 'apply:projects:vendor' pk=project.pk %}">
                     {% heroicon_micro "pencil-square" class="inline me-1" aria_hidden=true %}
diff --git a/hypha/apply/projects/templatetags/approval_tools.py b/hypha/apply/projects/templatetags/approval_tools.py
index 00db39432..09313f730 100644
--- a/hypha/apply/projects/templatetags/approval_tools.py
+++ b/hypha/apply/projects/templatetags/approval_tools.py
@@ -67,8 +67,15 @@ def user_can_update_paf_status(project, user, **kwargs):
 
 
 @register.simple_tag
-def user_can_edit_project(project, user):
-    return project.editable_by(user)
+def user_can_edit_vendor_details(project, user):
+    permission, _ = has_permission("vendor_edit", user, project, raise_exception=False)
+    return permission
+
+
+@register.simple_tag
+def user_can_edit_paf(project, user):
+    permission, _ = has_permission("paf_edit", user, project, raise_exception=False)
+    return permission
 
 
 @register.simple_tag
diff --git a/hypha/apply/projects/views/project.py b/hypha/apply/projects/views/project.py
index 50efccccc..e3535bb13 100644
--- a/hypha/apply/projects/views/project.py
+++ b/hypha/apply/projects/views/project.py
@@ -1794,11 +1794,12 @@ class ProjectFormEditView(BaseStreamForm, UpdateView):
 
     def dispatch(self, request, *args, **kwargs):
         self.object = self.get_object()
-        if not self.object.editable_by(request.user):
-            messages.info(
-                self.request, _("You are not allowed to edit the project at this time")
-            )
-            return redirect(self.object)
+
+        permission, msg = has_permission(
+            "paf_edit", self.request.user, self.object, raise_exception=True
+        )
+        if not permission:
+            messages.info(self.request, msg)
         return super().dispatch(request, *args, **kwargs)
 
     @cached_property
diff --git a/hypha/apply/projects/views/vendor.py b/hypha/apply/projects/views/vendor.py
index 133627465..ce6c4e40a 100644
--- a/hypha/apply/projects/views/vendor.py
+++ b/hypha/apply/projects/views/vendor.py
@@ -33,6 +33,7 @@ from ..models import (
     ProjectSettings,
     Vendor,
 )
+from ..permissions import has_permission
 
 
 def show_extra_info_form(wizard):
@@ -47,15 +48,13 @@ class CreateVendorAccessMixin:
         project_settings = ProjectSettings.for_request(request)
         if not project_settings.vendor_setup_required:
             raise PermissionDenied
-        is_admin = request.user.is_apply_staff
         project = self.get_project()
-        is_owner = request.user == project.user
-        if not (is_owner or is_admin):
-            raise PermissionDenied
-        if not project.editable_by(request.user):
-            raise PermissionDenied
+        # is_owner = request.user == project.user   :todo: confirm it
         if not project.vendor:
             raise Http404
+        permission, _ = has_permission(
+            "vendor_edit", request.user, project, raise_exception=True
+        )
         return super().dispatch(request, *args, **kwargs)
 
 
-- 
GitLab