From 65a78f5b0f005d5f5d401f211dd3c049eff562f5 Mon Sep 17 00:00:00 2001 From: Wes Appler Date: Fri, 6 Dec 2024 15:38:17 -0500 Subject: [PATCH 01/10] Seperate SOW & PAF, along with minor project aesthetic improvements --- hypha/apply/funds/views.py | 9 +- hypha/apply/projects/forms/project.py | 1 - ...ove_project_user_has_updated_pf_details.py | 16 ++ hypha/apply/projects/models/project.py | 19 +- .../includes/project_documents.html | 41 ++- .../project_approval_form.html | 29 +-- .../project_sow_detail.html | 4 +- .../project_sow_form.html | 76 ++++++ .../projects/templatetags/project_tags.py | 2 +- hypha/apply/projects/tests/test_forms.py | 4 +- hypha/apply/projects/urls.py | 7 +- hypha/apply/projects/views/project.py | 243 ++++++++++-------- hypha/apply/todo/options.py | 20 +- hypha/apply/todo/utils.py | 4 +- .../sass/components/_docs-block.scss | 2 +- .../static_src/sass/components/_sidebar.scss | 2 +- 16 files changed, 319 insertions(+), 160 deletions(-) create mode 100644 hypha/apply/projects/migrations/0094_remove_project_user_has_updated_pf_details.py create mode 100644 hypha/apply/projects/templates/application_projects/project_sow_form.html diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index 2976a0ffec..cdc6ac4cb2 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -60,7 +60,7 @@ from hypha.apply.projects.forms import ProjectCreateForm from hypha.apply.review.models import Review from hypha.apply.stream_forms.blocks import GroupToggleBlock -from hypha.apply.todo.options import PROJECT_WAITING_PAF +from hypha.apply.todo.options import PROJECT_WAITING_PAF, PROJECT_WAITING_SOW from hypha.apply.todo.views import add_task_to_user from hypha.apply.users.decorators import ( is_apply_staff, @@ -290,6 +290,13 @@ def post(self, *args, **kwargs): user=project.lead, related_obj=project, ) + if self.submission.page.specific.sow_forms.first(): + # Add SOW task if one exists on the parent + add_task_to_user( + code=PROJECT_WAITING_SOW, + user=project.lead, + related_obj=project, + ) return HttpResponseClientRedirect(project.get_absolute_url()) return render( self.request, diff --git a/hypha/apply/projects/forms/project.py b/hypha/apply/projects/forms/project.py index e351e80fa0..65219cd129 100644 --- a/hypha/apply/projects/forms/project.py +++ b/hypha/apply/projects/forms/project.py @@ -142,7 +142,6 @@ def save(self, *args, **kwargs): if field in self.cleaned_data } self.instance.process_file_data(self.cleaned_data) - self.instance.user_has_updated_details = True return super().save(*args, **kwargs) diff --git a/hypha/apply/projects/migrations/0094_remove_project_user_has_updated_pf_details.py b/hypha/apply/projects/migrations/0094_remove_project_user_has_updated_pf_details.py new file mode 100644 index 0000000000..9b0f3c36a5 --- /dev/null +++ b/hypha/apply/projects/migrations/0094_remove_project_user_has_updated_pf_details.py @@ -0,0 +1,16 @@ +# Generated by Django 4.2.16 on 2024-12-06 16:39 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("application_projects", "0093_remove_reportversion_form_fields"), + ] + + operations = [ + migrations.RemoveField( + model_name="project", + name="user_has_updated_details", + ), + ] diff --git a/hypha/apply/projects/models/project.py b/hypha/apply/projects/models/project.py index eea7caafdd..61134d075a 100644 --- a/hypha/apply/projects/models/project.py +++ b/hypha/apply/projects/models/project.py @@ -274,8 +274,6 @@ class Project(BaseStreamForm, AccessFormData, models.Model): # tracks read/write state of the Project is_locked = models.BooleanField(default=False) - # tracks updates to the Projects fields via the Project Application Form. - user_has_updated_details = models.BooleanField(default=False) submitted_contract_documents = models.BooleanField( "Submit Contracting Documents", default=False ) @@ -412,6 +410,21 @@ def editable_by(self, user): return True return False + @property + def user_has_updated_pf_details(self) -> bool: + """Determines if the user has updated the Project Form""" + return bool(self.form_fields) + + @property + def user_has_updated_sow_details(self) -> bool | None: + """Determines if the user has updated the SOW form + + If there is no configured SOW, None will be returned + """ + if self.submission.page.specific.sow_forms.first() and self.sow: + return bool(self.sow.form_data) + return None + @property def editable(self): if self.is_locked: @@ -456,7 +469,7 @@ def can_send_for_approval(self): being locked. """ correct_state = self.status == DRAFT and not self.is_locked - return correct_state and self.user_has_updated_details + return correct_state and self.user_has_updated_pf_details @property def is_in_progress(self): diff --git a/hypha/apply/projects/templates/application_projects/includes/project_documents.html b/hypha/apply/projects/templates/application_projects/includes/project_documents.html index 991dd4349a..c5af6ffb2e 100644 --- a/hypha/apply/projects/templates/application_projects/includes/project_documents.html +++ b/hypha/apply/projects/templates/application_projects/includes/project_documents.html @@ -77,18 +77,18 @@

  • - {% if object.user_has_updated_details %} + {% if object.user_has_updated_pf_details %} {% heroicon_outline "check-circle" class="stroke-light-blue me-1" aria_hidden=true %} {% else %} {% heroicon_outline "check-circle" class="stroke-gray-400 me-1" aria_hidden=true %} {% endif %} -

    {% trans "Project Form" %}

    +

    {% trans "Project form" %}

    {% has_project_sow_form object as project_sow %} - {% if project_sow and object.user_has_updated_details and not user.is_applicant %} -
      + {% if project_sow and not user.is_applicant %} + diff --git a/hypha/apply/projects/templates/application_projects/project_approval_form.html b/hypha/apply/projects/templates/application_projects/project_approval_form.html index d9880266b4..f8ed737a86 100644 --- a/hypha/apply/projects/templates/application_projects/project_approval_form.html +++ b/hypha/apply/projects/templates/application_projects/project_approval_form.html @@ -15,19 +15,12 @@ {% if approval_form_exists %} {% include "forms/includes/form_errors.html" with form=paf_form %} - {% if sow_form_exists %} - {% include "forms/includes/form_errors.html" with form=sow_form %} - {% endif %}
      {% csrf_token %} {{ paf_form.media }} - {% if sow_form_exists %} - {{ sow_form.media }} - {% endif %} - {% for field in paf_form %} {% if field.field %} {% if field.field.multi_input_field %} @@ -45,26 +38,6 @@ {{ hidden_field }} {% endfor %} - {% if sow_form_exists %} -
      - {% for field in sow_form %} - {% if field.field %} - {% if field.field.multi_input_field %} - {% include "forms/includes/multi_input_field.html" %} - {% else %} - {% include "forms/includes/field.html" %} - {% endif %} - {% else %} - {{ field.block }} - {% endif %} - {% endfor %} - - - {% for hidden_field in sow_form.hidden_fields %} - {{ hidden_field }} - {% endfor %} - {% endif %} - {% trans "Save draft" as save_draft %} {% for button_name, button_type, button_value in buttons %} @@ -72,12 +45,14 @@
      diff --git a/hypha/apply/projects/templates/application_projects/project_sow_detail.html b/hypha/apply/projects/templates/application_projects/project_sow_detail.html index 001ae7eeb6..121441b1f1 100644 --- a/hypha/apply/projects/templates/application_projects/project_sow_detail.html +++ b/hypha/apply/projects/templates/application_projects/project_sow_detail.html @@ -24,10 +24,10 @@
      -

      {% trans "Project scope of work (SOW)" %}

      +

      {% trans "Scope of Work" %}

      {% if object.sow.output_answers %} -
      +
      {{ object.sow.output_answers }}
      {% endif %} diff --git a/hypha/apply/projects/templates/application_projects/project_sow_form.html b/hypha/apply/projects/templates/application_projects/project_sow_form.html new file mode 100644 index 0000000000..72ad6b9f4e --- /dev/null +++ b/hypha/apply/projects/templates/application_projects/project_sow_form.html @@ -0,0 +1,76 @@ +{% extends "base-apply.html" %} +{% load i18n static %} +{% block title %}{% trans "Editing" %}: {{object.title }}{% endblock %} +{% block content %} + + {% adminbar %} + {% slot back_link %} + + {% trans "View project page" %} + + {% endslot %} + {% slot header %}{% trans "Editing" %}: {{ object.title }}{% endslot %} + {% endadminbar %} + + {% if sow_form_exists %} + {% include "forms/includes/form_errors.html" with form=sow_form %} +
      +
      +
      + {% csrf_token %} + {{ sow_form.media }} + {% for field in sow_form %} + {% if field.field %} + {% if field.field.multi_input_field %} + {% include "forms/includes/multi_input_field.html" %} + {% else %} + {% include "forms/includes/field.html" %} + {% endif %} + {% else %} + {{ field.block }} + {% endif %} + {% endfor %} + + + {% for hidden_field in sow_form.hidden_fields %} + {{ hidden_field }} + {% endfor %} + {% trans "Save draft" as save_draft %} + {% for button_name, button_type, button_value in buttons %} + + {% endfor %} +
      +
      + +
      + {% else %} +
      +
      +

      + {% trans "Scope of work form not configured. Please add it in the" %} + {% trans "fund settings" %}. +

      +
      +
      + {% endif %} + +{% endblock %} + +{% block extra_js %} + + + + + {% if not show_all_group_fields %} + + {% endif %} +{% endblock %} diff --git a/hypha/apply/projects/templatetags/project_tags.py b/hypha/apply/projects/templatetags/project_tags.py index 85032cbbf3..d0ce4b3639 100644 --- a/hypha/apply/projects/templatetags/project_tags.py +++ b/hypha/apply/projects/templatetags/project_tags.py @@ -38,7 +38,7 @@ def user_next_step_on_project(project, user, request=None): if project.status == DRAFT: if user.is_apply_staff: - if not project.user_has_updated_details: + if not project.user_has_updated_pf_details: return { "heading": _("To do"), "text": _("Fill in the Project Form"), diff --git a/hypha/apply/projects/tests/test_forms.py b/hypha/apply/projects/tests/test_forms.py index b16f9281d9..8c7ede2387 100644 --- a/hypha/apply/projects/tests/test_forms.py +++ b/hypha/apply/projects/tests/test_forms.py @@ -219,7 +219,7 @@ class TestProjectForm(TestCase): def test_updating_fields_sets_changed_flag(self): project = ProjectFactory() - self.assertFalse(project.user_has_updated_details) + self.assertFalse(project.user_has_updated_pf_details) # Use querydict for request.POST data = QueryDict("").copy() @@ -238,7 +238,7 @@ def test_updating_fields_sets_changed_flag(self): form.save() - self.assertTrue(project.user_has_updated_details) + self.assertTrue(project.user_has_updated_pf_details) class TestCreateInvoiceForm(TestCase): diff --git a/hypha/apply/projects/urls.py b/hypha/apply/projects/urls.py index 9b73c7105d..d4dcba57d9 100644 --- a/hypha/apply/projects/urls.py +++ b/hypha/apply/projects/urls.py @@ -1,6 +1,8 @@ from django.urls import include, path from django.views.generic import RedirectView +from hypha.apply.projects.views.project import ProjectSOWEditView + from .views import ( ApproveContractView, BatchUpdateInvoiceStatusView, @@ -82,7 +84,6 @@ ), path("partial/lead/", partial_project_lead, name="project_lead"), path("partial/title/", partial_project_title, name="project_title"), - path("edit/", ProjectFormEditView.as_view(), name="edit"), path("lead/update/", UpdateLeadView.as_view(), name="lead_update"), path( "status/update/", @@ -94,6 +95,10 @@ UpdateProjectTitleView.as_view(), name="project_title_update", ), + path( + "edit/project-form", ProjectFormEditView.as_view(), name="edit_pf" + ), + path("edit/project-sow", ProjectSOWEditView.as_view(), name="edit_sow"), path( "paf/skip/", SkipPAFApprovalProcessView.as_view(), name="paf_skip" ), diff --git a/hypha/apply/projects/views/project.py b/hypha/apply/projects/views/project.py index d3204ffcaa..ad2631fa12 100644 --- a/hypha/apply/projects/views/project.py +++ b/hypha/apply/projects/views/project.py @@ -44,12 +44,13 @@ PAF_REQUIRED_CHANGES, PAF_WAITING_APPROVAL, PAF_WAITING_ASSIGNEE, - PROJECT_SUBMIT_PAF, + PROJECT_SUBMIT_PF, PROJECT_WAITING_CONTRACT, PROJECT_WAITING_CONTRACT_DOCUMENT, PROJECT_WAITING_CONTRACT_REVIEW, PROJECT_WAITING_INVOICE, PROJECT_WAITING_PAF, + PROJECT_WAITING_SOW, ) from hypha.apply.todo.utils import get_project_lead_tasks from hypha.apply.todo.views import ( @@ -155,7 +156,7 @@ def post(self, *args, **kwargs): # remove PAF submission task for staff group remove_tasks_for_user( - code=PROJECT_SUBMIT_PAF, + code=PROJECT_SUBMIT_PF, user=self.object.lead, related_obj=self.object, ) @@ -824,7 +825,7 @@ def form_valid(self, form): # remove PAF submission task for staff group remove_tasks_for_user( - code=PROJECT_SUBMIT_PAF, + code=PROJECT_SUBMIT_PF, user=self.object.lead, related_obj=self.object, ) @@ -2027,13 +2028,8 @@ def get_supporting_documents(self, project): return documents_dict -@method_decorator(staff_or_finance_or_contracting_required, name="dispatch") -class ProjectFormEditView(BaseStreamForm, UpdateView): +class ProjectFormsEditView(BaseStreamForm, UpdateView): model = Project - template_name = "application_projects/project_approval_form.html" - # Remember to assign paf_form first and then sow_form, else get_defined_fields method may provide unexpected results - paf_form = None - sow_form = None def buttons(self): yield ("submit", "primary", _("Save")) @@ -2048,23 +2044,21 @@ def dispatch(self, request, *args, **kwargs): messages.info(self.request, msg) return super().dispatch(request, *args, **kwargs) - @cached_property - def approval_form(self): - # fetching from the fund directly instead of going through round - approval_form = ( - self.object.submission.page.specific.approval_forms.first() - ) # picking up the first one - - return approval_form - - @cached_property - def approval_sow_form(self): - # fetching from the fund directly instead of going through round - approval_sow_form = ( - self.object.submission.page.specific.sow_forms.first() - ) # picking up the first one + def get_context_data(self, **kwargs): + submission_attachments = [] + for _field, files in self.object.submission.extract_files().items(): + if isinstance(files, list): + submission_attachments.extend(files) + else: + submission_attachments.append(files) - return approval_sow_form + return { + "title": self.object.title, + "buttons": self.buttons(), + "object": self.object, + "submissions_attachments": submission_attachments, + **kwargs, + } def get_form_class(self, form_class, draft=False, form_data=None, user=None): return type( @@ -2073,20 +2067,46 @@ def get_form_class(self, form_class, draft=False, form_data=None, user=None): self.get_form_fields(draft, form_data, user), ) + +class ProjectFormEditView(ProjectFormsEditView): + paf_form = None + + template_name = "application_projects/project_approval_form.html" + + @cached_property + def approval_form(self): + """Fetch the project form from the fund directly instead of going through round""" + approval_form = self.object.submission.page.specific.approval_forms.first() + + return approval_form + def get_paf_form(self, form_class=None): if form_class is None: form_class = self.get_form_class(ProjectForm) + return form_class(**self.get_paf_form_kwargs()) - def get_sow_form(self, form_class=None): - if form_class is None: - form_class = self.get_form_class(ProjectSOWForm) - return form_class(**self.get_sow_form_kwargs()) + def get_paf_form_kwargs(self): + kwargs = super().get_form_kwargs() + + if self.approval_form: + fields = self.approval_form.form.get_form_fields() + else: + fields = {} + + kwargs["extra_fields"] = fields + initial = self.object.raw_data + for field_id in self.object.file_field_ids: + initial.pop(field_id + "-uploads", False) + initial[field_id] = get_placeholder_file(self.object.raw_data.get(field_id)) + kwargs["initial"].update(initial) + return kwargs + + def get_paf_form_fields(self): + return self.object.form_fields or self.approval_form.form.form_fields def get_context_data(self, **kwargs): self.paf_form = self.get_paf_form() - if self.approval_sow_form: - self.sow_form = self.get_sow_form() submission_attachments = [] for _field, files in self.object.submission.extract_files().items(): @@ -2095,51 +2115,96 @@ def get_context_data(self, **kwargs): else: submission_attachments.append(files) - return { - "title": self.object.title, - "buttons": self.buttons(), + ctx = { "approval_form_exists": True if self.approval_form else False, - "sow_form_exists": True if self.approval_sow_form else False, "paf_form": self.paf_form, - "sow_form": self.sow_form, - "object": self.object, - "submissions_attachments": submission_attachments, + **super().get_context_data(), **kwargs, } - def get_paf_form_fields(self): - return self.object.form_fields or self.approval_form.form.form_fields - - def get_sow_form_fields(self): - if hasattr(self.object, "sow"): - return ( - self.object.sow.form_fields or self.approval_sow_form.form.form_fields - ) - return self.approval_sow_form.form.form_fields + return ctx def get_defined_fields(self): approval_form = self.approval_form if approval_form and not self.paf_form: return self.get_paf_form_fields() - if self.approval_sow_form and self.paf_form and not self.sow_form: - return self.get_sow_form_fields() return self.object.get_defined_fields() - def get_paf_form_kwargs(self): - kwargs = super().get_form_kwargs() + def post(self, request, *args, **kwargs): + """ + Handle POST requests: instantiate a form instance with the passed + POST variables and then check if it's valid. + """ - if self.approval_form: - fields = self.approval_form.form.get_form_fields() + self.paf_form = self.get_paf_form() + if self.paf_form.is_valid(): + # paf can exist alone also, it needs to be valid + try: + paf_form_fields = self.get_paf_form_fields() + except AttributeError: + paf_form_fields = [] + self.paf_form.save(paf_form_fields=paf_form_fields) + self.paf_form.delete_temporary_files() + # remove PAF addition task for staff group + remove_tasks_for_user( + code=PROJECT_WAITING_PAF, + user=self.object.lead, + related_obj=self.object, + ) + # add project forms submission task for staff group if SOW has been updated + if self.object.user_has_updated_sow_details: + add_task_to_user( + code=PROJECT_SUBMIT_PF, + user=self.object.lead, + related_obj=self.object, + ) + return HttpResponseRedirect(self.get_success_url()) else: - fields = {} + return self.form_invalid(self.paf_form) - kwargs["extra_fields"] = fields - initial = self.object.raw_data - for field_id in self.object.file_field_ids: - initial.pop(field_id + "-uploads", False) - initial[field_id] = get_placeholder_file(self.object.raw_data.get(field_id)) - kwargs["initial"].update(initial) - return kwargs + +class ProjectSOWEditView(ProjectFormEditView): + sow_form = None + + template_name = "application_projects/project_sow_form.html" + + @cached_property + def approval_sow_form(self): + """Fetch the project form from the fund directly instead of going through round""" + approval_sow_form = self.object.submission.page.specific.sow_forms.first() + + return approval_sow_form + + def get_sow_form(self, form_class=None): + if form_class is None: + form_class = self.get_form_class(ProjectSOWForm) + + return form_class(**self.get_sow_form_kwargs()) + + def get_sow_form_fields(self): + if hasattr(self.object, "sow"): + return ( + self.object.sow.form_fields or self.approval_sow_form.form.form_fields + ) + return self.approval_sow_form.form.form_fields + + def get_context_data(self, **kwargs): + if self.approval_sow_form: + self.sow_form = self.get_sow_form() + + submission_attachments = [] + for _field, files in self.object.submission.extract_files().items(): + if isinstance(files, list): + submission_attachments.extend(files) + else: + submission_attachments.append(files) + + return { + "sow_form_exists": True if self.approval_sow_form else False, + "sow_form": self.sow_form, + **super().get_context_data(), + **kwargs, + } def get_sow_form_kwargs(self): kwargs = super().get_form_kwargs() @@ -2161,71 +2226,45 @@ def get_sow_form_kwargs(self): kwargs["initial"].update({"project": self.object}) return kwargs + def get_defined_fields(self): + if self.approval_sow_form and not self.sow_form: + return self.get_sow_form_fields() + return self.object.get_defined_fields() + def post(self, request, *args, **kwargs): """ Handle POST requests: instantiate a form instance with the passed POST variables and then check if it's valid. """ - self.paf_form = self.get_paf_form() if self.approval_sow_form: self.sow_form = self.get_sow_form() - if self.paf_form.is_valid() and self.sow_form.is_valid(): - # if both forms exists, both needs to be valid together - try: - paf_form_fields = self.get_paf_form_fields() - except AttributeError: - paf_form_fields = [] + if self.sow_form.is_valid(): try: sow_form_fields = self.get_sow_form_fields() except AttributeError: sow_form_fields = [] - self.paf_form.save(paf_form_fields=paf_form_fields) self.sow_form.save(sow_form_fields=sow_form_fields, project=self.object) - self.paf_form.delete_temporary_files() self.sow_form.delete_temporary_files() - # remove PAF addition task for staff group + # remove SOW addition task for staff group remove_tasks_for_user( - code=PROJECT_WAITING_PAF, - user=self.object.lead, - related_obj=self.object, - ) - # add PAF submission task for staff group - add_task_to_user( - code=PROJECT_SUBMIT_PAF, + code=PROJECT_WAITING_SOW, user=self.object.lead, related_obj=self.object, ) + # add project forms submission task for staff group if project form has been updated + if self.object.user_has_updated_pf_details: + add_task_to_user( + code=PROJECT_SUBMIT_PF, + user=self.object.lead, + related_obj=self.object, + ) return HttpResponseRedirect(self.get_success_url()) else: if not self.paf_form.is_valid(): return self.form_invalid(self.paf_form) return self.form_invalid(self.sow_form) - else: - if self.paf_form.is_valid(): - # paf can exist alone also, it needs to be valid - try: - paf_form_fields = self.get_paf_form_fields() - except AttributeError: - paf_form_fields = [] - self.paf_form.save(paf_form_fields=paf_form_fields) - self.paf_form.delete_temporary_files() - # remove PAF addition task for staff group - remove_tasks_for_user( - code=PROJECT_WAITING_PAF, - user=self.object.lead, - related_obj=self.object, - ) - # add PAF submission task for staff group - add_task_to_user( - code=PROJECT_SUBMIT_PAF, - user=self.object.lead, - related_obj=self.object, - ) - return HttpResponseRedirect(self.get_success_url()) - else: - return self.form_invalid(self.paf_form) @method_decorator(staff_or_finance_or_contracting_required, name="dispatch") diff --git a/hypha/apply/todo/options.py b/hypha/apply/todo/options.py index ea1a9f1d14..c1aa20f3b0 100644 --- a/hypha/apply/todo/options.py +++ b/hypha/apply/todo/options.py @@ -8,7 +8,9 @@ DETERMINATION_DRAFT = "determination_draft" REVIEW_DRAFT = "review_draft" PROJECT_WAITING_PAF = "project_waiting_paf" -PROJECT_SUBMIT_PAF = "project_submit_paf" +PROJECT_WAITING_SOW = "project_waiting_sow" +PROJECT_SUBMIT_PF = "project_submit_pfs" +PROJECT_SUBMIT_SOW = "project_submit_sow" PAF_REQUIRED_CHANGES = "paf_required_changes" PAF_WAITING_ASSIGNEE = "paf_waiting_assignee" PAF_WAITING_APPROVAL = "paf_waiting_approval" @@ -28,7 +30,9 @@ (DETERMINATION_DRAFT, "Determination draft"), (REVIEW_DRAFT, "Review Draft"), (PROJECT_WAITING_PAF, "Project waiting project form"), - (PROJECT_SUBMIT_PAF, "Project submit project form"), + (PROJECT_WAITING_SOW, "Project waiting scope of work"), + (PROJECT_SUBMIT_PF, "Project submit project form(s)"), + (PROJECT_SUBMIT_SOW, "Project submit scope of work"), (PAF_REQUIRED_CHANGES, "Project form required changes"), (PAF_WAITING_ASSIGNEE, "Project form waiting assignee"), (PAF_WAITING_APPROVAL, "Project form waiting approval"), @@ -87,9 +91,17 @@ "url": "{link}", "type": _("project"), }, - PROJECT_SUBMIT_PAF: { + PROJECT_WAITING_SOW: { "text": _( - 'Project [{related.title}]({link} "{related.title}") is waiting for project form submission' + 'Project [{related.title}]({link} "{related.title}") is waiting for scope of work' + ), + "icon": "dashboard-paf", + "url": "{link}", + "type": _("project"), + }, + PROJECT_SUBMIT_PF: { + "text": _( + 'Project [{related.title}]({link} "{related.title}") is waiting for project(s) form submission' ), "icon": "dashboard-paf", "url": "{link}", diff --git a/hypha/apply/todo/utils.py b/hypha/apply/todo/utils.py index 77ee94d7aa..cbe762cb82 100644 --- a/hypha/apply/todo/utils.py +++ b/hypha/apply/todo/utils.py @@ -5,7 +5,7 @@ INVOICE_REQUIRED_CHANGES, INVOICE_WAITING_APPROVAL, PAF_REQUIRED_CHANGES, - PROJECT_SUBMIT_PAF, + PROJECT_SUBMIT_PF, PROJECT_WAITING_CONTRACT, PROJECT_WAITING_CONTRACT_REVIEW, PROJECT_WAITING_PAF, @@ -13,7 +13,7 @@ project_lead_task_codes = [ PROJECT_WAITING_PAF, - PROJECT_SUBMIT_PAF, + PROJECT_SUBMIT_PF, PAF_REQUIRED_CHANGES, PROJECT_WAITING_CONTRACT, PROJECT_WAITING_CONTRACT_REVIEW, diff --git a/hypha/static_src/sass/components/_docs-block.scss b/hypha/static_src/sass/components/_docs-block.scss index 11921de999..b3a22899e0 100644 --- a/hypha/static_src/sass/components/_docs-block.scss +++ b/hypha/static_src/sass/components/_docs-block.scss @@ -143,7 +143,6 @@ &__document { display: flex; align-items: center; - justify-content: space-between; padding-block-end: 1rem; flex-wrap: wrap; @@ -168,6 +167,7 @@ &__actions { display: flex; align-items: center; + margin-left: auto; } } diff --git a/hypha/static_src/sass/components/_sidebar.scss b/hypha/static_src/sass/components/_sidebar.scss index 3a37db288a..e8b3e9df26 100644 --- a/hypha/static_src/sass/components/_sidebar.scss +++ b/hypha/static_src/sass/components/_sidebar.scss @@ -30,7 +30,7 @@ position: -webkit-sticky; /* for Safari */ position: sticky; align-self: flex-start; - inset-block-start: 0; + inset-block-start: 2rem; } &__paf-approvals { From 2c3b7caf083416e97edac177383d35dc0f2ff42f Mon Sep 17 00:00:00 2001 From: Wes Appler Date: Fri, 6 Dec 2024 15:51:19 -0500 Subject: [PATCH 02/10] More PAF -> PF renaming --- hypha/apply/funds/views.py | 4 ++-- hypha/apply/projects/views/project.py | 4 ++-- hypha/apply/todo/options.py | 6 +++--- hypha/apply/todo/utils.py | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index cdc6ac4cb2..5045e190ca 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -60,7 +60,7 @@ from hypha.apply.projects.forms import ProjectCreateForm from hypha.apply.review.models import Review from hypha.apply.stream_forms.blocks import GroupToggleBlock -from hypha.apply.todo.options import PROJECT_WAITING_PAF, PROJECT_WAITING_SOW +from hypha.apply.todo.options import PROJECT_WAITING_PF, PROJECT_WAITING_SOW from hypha.apply.todo.views import add_task_to_user from hypha.apply.users.decorators import ( is_apply_staff, @@ -286,7 +286,7 @@ def post(self, *args, **kwargs): ) # add task for staff to add PAF to the project add_task_to_user( - code=PROJECT_WAITING_PAF, + code=PROJECT_WAITING_PF, user=project.lead, related_obj=project, ) diff --git a/hypha/apply/projects/views/project.py b/hypha/apply/projects/views/project.py index ad2631fa12..db418813ce 100644 --- a/hypha/apply/projects/views/project.py +++ b/hypha/apply/projects/views/project.py @@ -49,7 +49,7 @@ PROJECT_WAITING_CONTRACT_DOCUMENT, PROJECT_WAITING_CONTRACT_REVIEW, PROJECT_WAITING_INVOICE, - PROJECT_WAITING_PAF, + PROJECT_WAITING_PF, PROJECT_WAITING_SOW, ) from hypha.apply.todo.utils import get_project_lead_tasks @@ -2147,7 +2147,7 @@ def post(self, request, *args, **kwargs): self.paf_form.delete_temporary_files() # remove PAF addition task for staff group remove_tasks_for_user( - code=PROJECT_WAITING_PAF, + code=PROJECT_WAITING_PF, user=self.object.lead, related_obj=self.object, ) diff --git a/hypha/apply/todo/options.py b/hypha/apply/todo/options.py index c1aa20f3b0..1b5a61073e 100644 --- a/hypha/apply/todo/options.py +++ b/hypha/apply/todo/options.py @@ -7,7 +7,7 @@ SUBMISSION_DRAFT = "submission_draft" DETERMINATION_DRAFT = "determination_draft" REVIEW_DRAFT = "review_draft" -PROJECT_WAITING_PAF = "project_waiting_paf" +PROJECT_WAITING_PF = "project_waiting_paf" PROJECT_WAITING_SOW = "project_waiting_sow" PROJECT_SUBMIT_PF = "project_submit_pfs" PROJECT_SUBMIT_SOW = "project_submit_sow" @@ -29,7 +29,7 @@ (SUBMISSION_DRAFT, "Submission Draft"), (DETERMINATION_DRAFT, "Determination draft"), (REVIEW_DRAFT, "Review Draft"), - (PROJECT_WAITING_PAF, "Project waiting project form"), + (PROJECT_WAITING_PF, "Project waiting project form"), (PROJECT_WAITING_SOW, "Project waiting scope of work"), (PROJECT_SUBMIT_PF, "Project submit project form(s)"), (PROJECT_SUBMIT_SOW, "Project submit scope of work"), @@ -83,7 +83,7 @@ }, # PROJECT actions # draft state (staff action) - PROJECT_WAITING_PAF: { + PROJECT_WAITING_PF: { "text": _( 'Project [{related.title}]({link} "{related.title}") is waiting for project form' ), diff --git a/hypha/apply/todo/utils.py b/hypha/apply/todo/utils.py index cbe762cb82..6784b2166d 100644 --- a/hypha/apply/todo/utils.py +++ b/hypha/apply/todo/utils.py @@ -8,11 +8,11 @@ PROJECT_SUBMIT_PF, PROJECT_WAITING_CONTRACT, PROJECT_WAITING_CONTRACT_REVIEW, - PROJECT_WAITING_PAF, + PROJECT_WAITING_PF, ) project_lead_task_codes = [ - PROJECT_WAITING_PAF, + PROJECT_WAITING_PF, PROJECT_SUBMIT_PF, PAF_REQUIRED_CHANGES, PROJECT_WAITING_CONTRACT, From a0bab769f449cbf41978e506ebfe61aeb651d1ea Mon Sep 17 00:00:00 2001 From: Wes Appler Date: Fri, 6 Dec 2024 16:12:56 -0500 Subject: [PATCH 03/10] Added migration for SOW task --- .../todo/migrations/0007_alter_task_code.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 hypha/apply/todo/migrations/0007_alter_task_code.py diff --git a/hypha/apply/todo/migrations/0007_alter_task_code.py b/hypha/apply/todo/migrations/0007_alter_task_code.py new file mode 100644 index 0000000000..85a94778e9 --- /dev/null +++ b/hypha/apply/todo/migrations/0007_alter_task_code.py @@ -0,0 +1,46 @@ +# Generated by Django 4.2.17 on 2024-12-06 21:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("todo", "0006_alter_task_code"), + ] + + operations = [ + migrations.AlterField( + model_name="task", + name="code", + field=models.CharField( + choices=[ + ("comment_task", "Comment Task"), + ("submission_draft", "Submission Draft"), + ("determination_draft", "Determination draft"), + ("review_draft", "Review Draft"), + ("project_waiting_paf", "Project waiting project form"), + ("project_waiting_sow", "Project waiting scope of work"), + ("project_submit_pfs", "Project submit project form(s)"), + ("project_submit_sow", "Project submit scope of work"), + ("paf_required_changes", "Project form required changes"), + ("paf_waiting_assignee", "Project form waiting assignee"), + ("paf_waiting_approval", "Project form waiting approval"), + ("project_waiting_contract", "Project waiting contract"), + ( + "project_waiting_contract_document", + "Project waiting contract document", + ), + ( + "project_waiting_contract_review", + "Project waiting contract review", + ), + ("project_waiting_invoice", "Project waiting invoice"), + ("invoice_required_changes", "Invoice required changes"), + ("invoice_waiting_approval", "Invoice waiting approval"), + ("invoice_waiting_paid", "Invoice waiting paid"), + ("report_due", "Report due"), + ], + max_length=50, + ), + ), + ] From 166dfd9904f242d465e767cd08b7f3bbe04d1387 Mon Sep 17 00:00:00 2001 From: Wes Appler Date: Fri, 6 Dec 2024 16:27:18 -0500 Subject: [PATCH 04/10] More PAF renaming & test fixes --- hypha/apply/projects/tests/test_forms.py | 7 +++-- hypha/apply/projects/views/project.py | 33 +++++++++++------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/hypha/apply/projects/tests/test_forms.py b/hypha/apply/projects/tests/test_forms.py index 8c7ede2387..9e3ddf9005 100644 --- a/hypha/apply/projects/tests/test_forms.py +++ b/hypha/apply/projects/tests/test_forms.py @@ -215,10 +215,13 @@ def test_comment_is_not_required(self): self.assertEqual(form.errors, {}) -class TestProjectForm(TestCase): - def test_updating_fields_sets_changed_flag(self): +class TestProjectForms(TestCase): + def test_updating_pf_fields_sets_changed_flag(self): project = ProjectFactory() + project.form_fields = {} + project.save() + self.assertFalse(project.user_has_updated_pf_details) # Use querydict for request.POST diff --git a/hypha/apply/projects/views/project.py b/hypha/apply/projects/views/project.py index db418813ce..0a8ce213ce 100644 --- a/hypha/apply/projects/views/project.py +++ b/hypha/apply/projects/views/project.py @@ -2080,13 +2080,13 @@ def approval_form(self): return approval_form - def get_paf_form(self, form_class=None): + def get_pf_form(self, form_class=None): if form_class is None: form_class = self.get_form_class(ProjectForm) - return form_class(**self.get_paf_form_kwargs()) + return form_class(**self.get_pf_form_kwargs()) - def get_paf_form_kwargs(self): + def get_pf_form_kwargs(self): kwargs = super().get_form_kwargs() if self.approval_form: @@ -2102,11 +2102,11 @@ def get_paf_form_kwargs(self): kwargs["initial"].update(initial) return kwargs - def get_paf_form_fields(self): + def get_pf_form_fields(self): return self.object.form_fields or self.approval_form.form.form_fields def get_context_data(self, **kwargs): - self.paf_form = self.get_paf_form() + self.pf_form = self.get_pf_form() submission_attachments = [] for _field, files in self.object.submission.extract_files().items(): @@ -2117,7 +2117,7 @@ def get_context_data(self, **kwargs): ctx = { "approval_form_exists": True if self.approval_form else False, - "paf_form": self.paf_form, + "pf_form": self.pf_form, **super().get_context_data(), **kwargs, } @@ -2126,8 +2126,8 @@ def get_context_data(self, **kwargs): def get_defined_fields(self): approval_form = self.approval_form - if approval_form and not self.paf_form: - return self.get_paf_form_fields() + if approval_form and not self.pf_form: + return self.get_pf_form_fields() return self.object.get_defined_fields() def post(self, request, *args, **kwargs): @@ -2136,15 +2136,14 @@ def post(self, request, *args, **kwargs): POST variables and then check if it's valid. """ - self.paf_form = self.get_paf_form() - if self.paf_form.is_valid(): - # paf can exist alone also, it needs to be valid + self.pf_form = self.get_pf_form() + if self.pf_form.is_valid(): try: - paf_form_fields = self.get_paf_form_fields() + pf_form_fields = self.get_pf_form_fields() except AttributeError: - paf_form_fields = [] - self.paf_form.save(paf_form_fields=paf_form_fields) - self.paf_form.delete_temporary_files() + pf_form_fields = [] + self.pf_form.save(pf_form_fields=pf_form_fields) + self.pf_form.delete_temporary_files() # remove PAF addition task for staff group remove_tasks_for_user( code=PROJECT_WAITING_PF, @@ -2160,7 +2159,7 @@ def post(self, request, *args, **kwargs): ) return HttpResponseRedirect(self.get_success_url()) else: - return self.form_invalid(self.paf_form) + return self.form_invalid(self.pf_form) class ProjectSOWEditView(ProjectFormEditView): @@ -2262,8 +2261,6 @@ def post(self, request, *args, **kwargs): ) return HttpResponseRedirect(self.get_success_url()) else: - if not self.paf_form.is_valid(): - return self.form_invalid(self.paf_form) return self.form_invalid(self.sow_form) From eccb4e910b2e3d5abd79c7ff18cbf342890f7c2b Mon Sep 17 00:00:00 2001 From: Wes Appler Date: Wed, 11 Dec 2024 11:15:30 -0500 Subject: [PATCH 05/10] Removed test & added final fixes --- hypha/apply/projects/forms/project.py | 2 +- hypha/apply/projects/models/project.py | 2 +- .../project_approval_form.html | 9 +++--- .../project_sow_detail.html | 2 +- hypha/apply/projects/tests/test_forms.py | 32 ------------------- hypha/apply/projects/views/project.py | 17 ++++++---- hypha/apply/todo/options.py | 8 ++--- hypha/apply/todo/utils.py | 4 +-- .../sass/components/_docs-block.scss | 2 +- 9 files changed, 24 insertions(+), 54 deletions(-) diff --git a/hypha/apply/projects/forms/project.py b/hypha/apply/projects/forms/project.py index 65219cd129..6bf42f0a46 100644 --- a/hypha/apply/projects/forms/project.py +++ b/hypha/apply/projects/forms/project.py @@ -135,7 +135,7 @@ def clean(self): return cleaned_data def save(self, *args, **kwargs): - self.instance.form_fields = kwargs.pop("paf_form_fields", {}) + self.instance.form_fields = kwargs.pop("pf_form_fields", {}) self.instance.form_data = { field: self.cleaned_data[field] for field in self.instance.question_field_ids diff --git a/hypha/apply/projects/models/project.py b/hypha/apply/projects/models/project.py index 61134d075a..2bb189bfd2 100644 --- a/hypha/apply/projects/models/project.py +++ b/hypha/apply/projects/models/project.py @@ -421,7 +421,7 @@ def user_has_updated_sow_details(self) -> bool | None: If there is no configured SOW, None will be returned """ - if self.submission.page.specific.sow_forms.first() and self.sow: + if self.submission.page.specific.sow_forms.first() and hasattr(self, "sow"): return bool(self.sow.form_data) return None diff --git a/hypha/apply/projects/templates/application_projects/project_approval_form.html b/hypha/apply/projects/templates/application_projects/project_approval_form.html index f8ed737a86..032b63823d 100644 --- a/hypha/apply/projects/templates/application_projects/project_approval_form.html +++ b/hypha/apply/projects/templates/application_projects/project_approval_form.html @@ -13,15 +13,14 @@ {% endadminbar %} {% if approval_form_exists %} - - {% include "forms/includes/form_errors.html" with form=paf_form %} + {% include "forms/includes/form_errors.html" with form=pf_form %}
      {% csrf_token %} - {{ paf_form.media }} - {% for field in paf_form %} + {{ pf_form.media }} + {% for field in pf_form %} {% if field.field %} {% if field.field.multi_input_field %} {% include "forms/includes/multi_input_field.html" %} @@ -34,7 +33,7 @@ {% endfor %} {# Hidden fields needed e.g. for django-file-form. See `StreamBaseForm.hidden_fields` #} - {% for hidden_field in paf_form.hidden_fields %} + {% for hidden_field in pf_form.hidden_fields %} {{ hidden_field }} {% endfor %} diff --git a/hypha/apply/projects/templates/application_projects/project_sow_detail.html b/hypha/apply/projects/templates/application_projects/project_sow_detail.html index 121441b1f1..1d76a00bd5 100644 --- a/hypha/apply/projects/templates/application_projects/project_sow_detail.html +++ b/hypha/apply/projects/templates/application_projects/project_sow_detail.html @@ -27,7 +27,7 @@

      {% trans "Scope of Work" %}

      {% if object.sow.output_answers %} -
      +
      {{ object.sow.output_answers }}
      {% endif %} diff --git a/hypha/apply/projects/tests/test_forms.py b/hypha/apply/projects/tests/test_forms.py index 9e3ddf9005..f2efe2647a 100644 --- a/hypha/apply/projects/tests/test_forms.py +++ b/hypha/apply/projects/tests/test_forms.py @@ -3,7 +3,6 @@ from io import BytesIO from django.core.files.uploadedfile import SimpleUploadedFile -from django.http import QueryDict from django.test import TestCase from hypha.apply.users.tests.factories import ( @@ -23,7 +22,6 @@ ) from ..forms.project import ( ChangePAFStatusForm, - ProjectForm, StaffUploadContractForm, UploadContractForm, ) @@ -44,7 +42,6 @@ PAFReviewerRoleFactory, ProjectFactory, SupportingDocumentFactory, - address_to_form_data, ) @@ -215,35 +212,6 @@ def test_comment_is_not_required(self): self.assertEqual(form.errors, {}) -class TestProjectForms(TestCase): - def test_updating_pf_fields_sets_changed_flag(self): - project = ProjectFactory() - - project.form_fields = {} - project.save() - - self.assertFalse(project.user_has_updated_pf_details) - - # Use querydict for request.POST - data = QueryDict("").copy() - - data.update( - { - "title": f"{project.title} test", - "value": project.value, - "proposed_start": project.proposed_start, - "proposed_end": project.proposed_end, - } - ) - data.update(address_to_form_data()) - form = ProjectForm(instance=project, data=data) - self.assertTrue(form.is_valid(), form.errors.as_text()) - - form.save() - - self.assertTrue(project.user_has_updated_pf_details) - - class TestCreateInvoiceForm(TestCase): def test_adding_invoice(self): data = { diff --git a/hypha/apply/projects/views/project.py b/hypha/apply/projects/views/project.py index 0a8ce213ce..f079d8958c 100644 --- a/hypha/apply/projects/views/project.py +++ b/hypha/apply/projects/views/project.py @@ -44,7 +44,7 @@ PAF_REQUIRED_CHANGES, PAF_WAITING_APPROVAL, PAF_WAITING_ASSIGNEE, - PROJECT_SUBMIT_PF, + PROJECT_SUBMIT_PAF, PROJECT_WAITING_CONTRACT, PROJECT_WAITING_CONTRACT_DOCUMENT, PROJECT_WAITING_CONTRACT_REVIEW, @@ -156,7 +156,7 @@ def post(self, *args, **kwargs): # remove PAF submission task for staff group remove_tasks_for_user( - code=PROJECT_SUBMIT_PF, + code=PROJECT_SUBMIT_PAF, user=self.object.lead, related_obj=self.object, ) @@ -825,7 +825,7 @@ def form_valid(self, form): # remove PAF submission task for staff group remove_tasks_for_user( - code=PROJECT_SUBMIT_PF, + code=PROJECT_SUBMIT_PAF, user=self.object.lead, related_obj=self.object, ) @@ -2069,7 +2069,7 @@ def get_form_class(self, form_class, draft=False, form_data=None, user=None): class ProjectFormEditView(ProjectFormsEditView): - paf_form = None + pf_form = None template_name = "application_projects/project_approval_form.html" @@ -2151,9 +2151,12 @@ def post(self, request, *args, **kwargs): related_obj=self.object, ) # add project forms submission task for staff group if SOW has been updated - if self.object.user_has_updated_sow_details: + # OR if the SOW doesn't exist (user_has_updated_sow_details == None) + if ( + updated_sow := self.object.user_has_updated_sow_details + ) or updated_sow is None: add_task_to_user( - code=PROJECT_SUBMIT_PF, + code=PROJECT_SUBMIT_PAF, user=self.object.lead, related_obj=self.object, ) @@ -2255,7 +2258,7 @@ def post(self, request, *args, **kwargs): # add project forms submission task for staff group if project form has been updated if self.object.user_has_updated_pf_details: add_task_to_user( - code=PROJECT_SUBMIT_PF, + code=PROJECT_SUBMIT_PAF, user=self.object.lead, related_obj=self.object, ) diff --git a/hypha/apply/todo/options.py b/hypha/apply/todo/options.py index 1b5a61073e..8aa92b727d 100644 --- a/hypha/apply/todo/options.py +++ b/hypha/apply/todo/options.py @@ -9,7 +9,7 @@ REVIEW_DRAFT = "review_draft" PROJECT_WAITING_PF = "project_waiting_paf" PROJECT_WAITING_SOW = "project_waiting_sow" -PROJECT_SUBMIT_PF = "project_submit_pfs" +PROJECT_SUBMIT_PAF = "project_submit_pfs" PROJECT_SUBMIT_SOW = "project_submit_sow" PAF_REQUIRED_CHANGES = "paf_required_changes" PAF_WAITING_ASSIGNEE = "paf_waiting_assignee" @@ -31,7 +31,7 @@ (REVIEW_DRAFT, "Review Draft"), (PROJECT_WAITING_PF, "Project waiting project form"), (PROJECT_WAITING_SOW, "Project waiting scope of work"), - (PROJECT_SUBMIT_PF, "Project submit project form(s)"), + (PROJECT_SUBMIT_PAF, "Project submit project form(s)"), (PROJECT_SUBMIT_SOW, "Project submit scope of work"), (PAF_REQUIRED_CHANGES, "Project form required changes"), (PAF_WAITING_ASSIGNEE, "Project form waiting assignee"), @@ -99,9 +99,9 @@ "url": "{link}", "type": _("project"), }, - PROJECT_SUBMIT_PF: { + PROJECT_SUBMIT_PAF: { "text": _( - 'Project [{related.title}]({link} "{related.title}") is waiting for project(s) form submission' + 'Project [{related.title}]({link} "{related.title}") is waiting for project form(s) submission' ), "icon": "dashboard-paf", "url": "{link}", diff --git a/hypha/apply/todo/utils.py b/hypha/apply/todo/utils.py index 6784b2166d..9eacef173f 100644 --- a/hypha/apply/todo/utils.py +++ b/hypha/apply/todo/utils.py @@ -5,7 +5,7 @@ INVOICE_REQUIRED_CHANGES, INVOICE_WAITING_APPROVAL, PAF_REQUIRED_CHANGES, - PROJECT_SUBMIT_PF, + PROJECT_SUBMIT_PAF, PROJECT_WAITING_CONTRACT, PROJECT_WAITING_CONTRACT_REVIEW, PROJECT_WAITING_PF, @@ -13,7 +13,7 @@ project_lead_task_codes = [ PROJECT_WAITING_PF, - PROJECT_SUBMIT_PF, + PROJECT_SUBMIT_PAF, PAF_REQUIRED_CHANGES, PROJECT_WAITING_CONTRACT, PROJECT_WAITING_CONTRACT_REVIEW, diff --git a/hypha/static_src/sass/components/_docs-block.scss b/hypha/static_src/sass/components/_docs-block.scss index b3a22899e0..20a14f226f 100644 --- a/hypha/static_src/sass/components/_docs-block.scss +++ b/hypha/static_src/sass/components/_docs-block.scss @@ -167,7 +167,7 @@ &__actions { display: flex; align-items: center; - margin-left: auto; + margin-inline-start: auto; } } From 93e6681f30c240014c4e18bfc01e6a255cf20c3d Mon Sep 17 00:00:00 2001 From: Wes Appler Date: Thu, 12 Dec 2024 10:26:04 -0500 Subject: [PATCH 06/10] Fixed formatting, added SOW edit button to SOW detail view --- .../application_projects/includes/project_documents.html | 6 +++--- .../application_projects/project_approval_detail.html | 6 +++--- .../application_projects/project_sow_detail.html | 8 ++++++-- hypha/apply/projects/templatetags/approval_tools.py | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/hypha/apply/projects/templates/application_projects/includes/project_documents.html b/hypha/apply/projects/templates/application_projects/includes/project_documents.html index c5af6ffb2e..2693c07cb1 100644 --- a/hypha/apply/projects/templates/application_projects/includes/project_documents.html +++ b/hypha/apply/projects/templates/application_projects/includes/project_documents.html @@ -85,8 +85,8 @@

      {% trans "Project form" %}

      - {% user_can_edit_paf object user as can_edit_paf %} - {% if can_edit_paf %} + {% user_can_edit_pfs object user as can_edit_pfs %} + {% if can_edit_pfs %} {% if object.user_has_updated_pf_details %} {% heroicon_micro "pencil-square" class="inline me-1 w-4 h-4" aria_hidden=true %} @@ -116,7 +116,7 @@

      {% trans "Scope of work" %}

      - {% if can_edit_paf %} + {% if can_edit_pfs %} {% if object.user_has_updated_sow_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 8ea763c2cb..08d6cc5ffd 100644 --- a/hypha/apply/projects/templates/application_projects/project_approval_detail.html +++ b/hypha/apply/projects/templates/application_projects/project_approval_detail.html @@ -98,9 +98,9 @@

      {% trans "Supporting Documents" %}