From 1b90b51574f95b3f520a53a75e6f1d74f3ed1072 Mon Sep 17 00:00:00 2001 From: Chris Zubak-Skees Date: Tue, 22 Feb 2022 03:30:33 -0500 Subject: [PATCH 01/15] feat: add submission withdrawal Issue #3296 --- hypha/apply/funds/models/submissions.py | 3 +- ...pplicationsubmission_confirm_withdraw.html | 23 +++++++++++ .../funds/applicationsubmission_detail.html | 9 ++++ hypha/apply/funds/urls.py | 2 + hypha/apply/funds/views.py | 33 ++++++++++++++- hypha/apply/funds/workflow.py | 41 +++++++++++++++++++ hypha/static_src/sass/custom/_custom.scss | 7 +++- 7 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 hypha/apply/funds/templates/funds/applicationsubmission_confirm_withdraw.html diff --git a/hypha/apply/funds/models/submissions.py b/hypha/apply/funds/models/submissions.py index 6bf73d5909..b9bb1727be 100644 --- a/hypha/apply/funds/models/submissions.py +++ b/hypha/apply/funds/models/submissions.py @@ -944,7 +944,8 @@ def in_external_review_phase(self): def is_finished(self): accepted = self.status in PHASES_MAPPING["accepted"]["statuses"] dismissed = self.status in PHASES_MAPPING["dismissed"]["statuses"] - return accepted or dismissed + withdrawn = self.status in PHASES_MAPPING["withdrawn"]["statuses"] + return accepted or dismissed or withdrawn # Methods for accessing data on the submission diff --git a/hypha/apply/funds/templates/funds/applicationsubmission_confirm_withdraw.html b/hypha/apply/funds/templates/funds/applicationsubmission_confirm_withdraw.html new file mode 100644 index 0000000000..d0830b01b6 --- /dev/null +++ b/hypha/apply/funds/templates/funds/applicationsubmission_confirm_withdraw.html @@ -0,0 +1,23 @@ +{% extends "base-apply.html" %} +{% load i18n static %} + +{% block title %}{% trans "Withdrawing" %}: {{object.title }}{% endblock %} + +{% block content %} +
+
+

{% trans "Withdrawing" %}: {{ object.title }}

+
+
+ +
+
+
+ {% csrf_token %} +

{% blocktrans %}Are you sure you want to withdraw "{{ object }}" from consideration?{% endblocktrans %}

+ +
+
+
+ +{% endblock %} diff --git a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html index 06945383a2..4dedcdf5ab 100644 --- a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html +++ b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html @@ -137,6 +137,15 @@
{% blocktrans with stage=object.previous.stage %}Your {{ stage }} applicatio {% trans "Delete" %} {% endif %} + {% if request.user|has_edit_perm:object %} + {% if request.user.is_applicant %} + + {% trans "Withdraw" %} + + {% endif %} + {% endif %} {% if request.user|has_edit_perm:object %} /", SubmissionPrivateMediaView.as_view(), diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index 67d19600c9..94c8f1c276 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -39,7 +39,11 @@ UpdateView, ) from django.views.generic.base import TemplateView -from django.views.generic.detail import SingleObjectMixin +from django.views.generic.detail import ( + BaseDetailView, + SingleObjectMixin, + SingleObjectTemplateResponseMixin, +) from django_file_form.models import PlaceholderUploadedFile from django_filters.views import FilterView from django_htmx.http import ( @@ -1784,6 +1788,33 @@ def form_valid(self, form): # delete submission and redirect to success url return super().form_valid(form) +class SubmissionWithdrawView(SingleObjectTemplateResponseMixin, BaseDetailView): + model = ApplicationSubmission + success_url = reverse_lazy('funds:submissions:list') + template_name_suffix = '_confirm_withdraw' + + def post(self, request, *args, **kwargs): + return self.withdraw(request, *args, **kwargs) + + def withdraw(self, request, *args, **kwargs): + obj = self.get_object() + + if not obj.phase.permissions.can_edit(request.user): + raise PermissionDenied + + withdraw_actions = [action for action in obj.workflow[obj.status].transitions.keys() if 'withdraw' in action] + + if len(withdraw_actions) > 0: + action = withdraw_actions[0] + obj.perform_transition( + action, + self.request.user, + request=self.request, + notify=False + ) + + success_url = obj.get_absolute_url() + return HttpResponseRedirect(success_url) @method_decorator(login_required, name="dispatch") class SubmissionPrivateMediaView(UserPassesTestMixin, PrivateMediaView): diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 89c9b5b931..0e410e7795 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -542,6 +542,7 @@ def make_permissions(edit=None, review=None, view=None): "ext_internal_review": _("Open Review"), "ext_determination": _("Ready For Determination"), "ext_rejected": _("Dismiss"), + "ext_screening_withdrawn": _("Withdraw"), }, "display": _("Need screening"), "public": _("Application Received"), @@ -561,16 +562,23 @@ def make_permissions(edit=None, review=None, view=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, + "ext_screening_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": RequestExt, "permissions": applicant_edit_permissions, }, + "ext_screening_withdrawn": { + "display": _("Withdrawn"), + "stage": RequestExt, + "permissions": staff_edit_permissions, + }, }, { "ext_internal_review": { "transitions": { "ext_post_review_discussion": _("Close Review"), + "ext_review_withdrawn": _("Withdraw"), INITIAL_STATE: _("Need screening (revert)"), }, "display": _("Internal Review"), @@ -607,6 +615,7 @@ def make_permissions(edit=None, review=None, view=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, + "ext_review_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": RequestExt, @@ -623,6 +632,11 @@ def make_permissions(edit=None, review=None, view=None): "stage": RequestExt, "permissions": reviewer_review_permissions, }, + "ext_review_withdrawn": { + "display": _("Withdrawn"), + "stage": RequestExt, + "permissions": staff_edit_permissions, + }, }, { "ext_post_external_review_discussion": { @@ -633,6 +647,7 @@ def make_permissions(edit=None, review=None, view=None): "ext_almost": _("Accept but additional info required"), "ext_accepted": _("Accept"), "ext_rejected": _("Dismiss"), + "ext_external_review_withdrawn": _("Withdraw"), }, "display": _("Ready For Discussion"), "stage": RequestExt, @@ -651,11 +666,17 @@ def make_permissions(edit=None, review=None, view=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, + "ext_external_review_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": RequestExt, "permissions": applicant_edit_permissions, }, + "ext_external_review_withdrawn": { + "display": _("Withdrawn"), + "stage": RequestExt, + "permissions": staff_edit_permissions, + }, }, { "ext_determination": { @@ -666,6 +687,7 @@ def make_permissions(edit=None, review=None, view=None): "ext_almost": _("Accept but additional info required"), "ext_accepted": _("Accept"), "ext_rejected": _("Dismiss"), + "ext_withdrawn": _("Withdraw"), }, "display": _("Ready for Determination"), "permissions": hidden_from_applicant_permissions, @@ -695,6 +717,11 @@ def make_permissions(edit=None, review=None, view=None): "stage": RequestExt, "permissions": no_permissions, }, + "ext_withdrawn": { + "display": _("Withdrawn"), + "stage": RequestExt, + "permissions": staff_edit_permissions, + }, }, ] @@ -1383,11 +1410,21 @@ def get_dismissed_statuses(): return dismissed_statuses +def get_withdrawn_statuses(): + withdrawn_statuses = set() + for phase_name, phase in PHASES: + if phase.display_name == "Dismissed": + withdrawn_statuses.add(phase_name) + return withdrawn_statuses + + +ext_or_higher_statuses = get_ext_or_higher_statuses() review_statuses = get_review_statuses() ext_review_statuses = get_ext_review_statuses() ext_or_higher_statuses = get_ext_or_higher_statuses() accepted_statuses = get_accepted_statuses() dismissed_statuses = get_dismissed_statuses() +withdrawn_statuses = get_withdrawn_statuses() DETERMINATION_PHASES = [ phase_name for phase_name, _ in PHASES if "_discussion" in phase_name @@ -1487,6 +1524,10 @@ def phases_matching(phrase, exclude=None): "name": _("Dismissed"), "statuses": phases_matching("rejected"), }, + "withdrawn": { + "name": _("Withdrawn"), + "statuses": phases_matching("withdrawn"), + }, } OPEN_CALL_PHASES = [ diff --git a/hypha/static_src/sass/custom/_custom.scss b/hypha/static_src/sass/custom/_custom.scss index 061b3d43d2..cc17f79c15 100644 --- a/hypha/static_src/sass/custom/_custom.scss +++ b/hypha/static_src/sass/custom/_custom.scss @@ -1 +1,6 @@ -// stylelint-disable no-empty-source +.link--withdraw-submission { + margin-right: 1rem; + padding-right: 1rem; + border-right: 2px solid #cfcfcf; + font-weight: 700; +} From 3c8117067aba302d149b8992077a6ef03abde5d0 Mon Sep 17 00:00:00 2001 From: Chris Zubak-Skees Date: Tue, 22 Feb 2022 21:30:30 +0000 Subject: [PATCH 02/15] fix: remove refs to dismiss while adding withdrawn Issue #3296 --- hypha/apply/funds/workflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 0e410e7795..8be2f11fa1 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -1413,7 +1413,7 @@ def get_dismissed_statuses(): def get_withdrawn_statuses(): withdrawn_statuses = set() for phase_name, phase in PHASES: - if phase.display_name == "Dismissed": + if phase.display_name == "Withdrawn": withdrawn_statuses.add(phase_name) return withdrawn_statuses From e4cad102af0c5a4f1529f6d4840a9e09e91083cf Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Thu, 23 Mar 2023 05:20:20 -0500 Subject: [PATCH 03/15] Add ENABLE_SUBMISSION_WITHDRAWAL to allow enabling withdrawals This only affects about enabling them, not whether they are in the system. That means that if the configuration is changed over the lifetime of a system, things that were withdrawn when it was enabled retain that status. Issue #3296 --- docs/setup/administrators/configuration.md | 6 +++++- hypha/apply/funds/views.py | 3 +++ hypha/apply/funds/workflow.py | 2 +- hypha/core/context_processors.py | 1 + hypha/settings/base.py | 3 +++ 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/setup/administrators/configuration.md b/docs/setup/administrators/configuration.md index 59aa689c68..7a3e7cf978 100644 --- a/docs/setup/administrators/configuration.md +++ b/docs/setup/administrators/configuration.md @@ -112,7 +112,11 @@ If applicants should be forced to preview their application before submitting ---- -Set the allowed file extension for all uploads fields. +### Allow Withdrawing of Submissions + + ENABLE_SUBMISSION_WITHDRAWAL = env.bool('ENABLE_SUBMISSION_WITHDRAWAL', False) + +### Set the allowed file extension for all uploads fields. FILE_ALLOWED_EXTENSIONS = ['doc', 'docx', 'odp', 'ods', 'odt', 'pdf', 'ppt', 'pptx', 'rtf', 'txt', 'xls', 'xlsx'] FILE_ACCEPT_ATTR_VALUE = ', '.join(['.' + ext for ext in FILE_ALLOWED_EXTENSIONS]) diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index 94c8f1c276..3cee68a71a 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -1797,6 +1797,9 @@ def post(self, request, *args, **kwargs): return self.withdraw(request, *args, **kwargs) def withdraw(self, request, *args, **kwargs): + if not settings.ENABLE_SUBMISSION_WITHDRAWAL: + raise PermissionDenied + obj = self.get_object() if not obj.phase.permissions.can_edit(request.user): diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 8be2f11fa1..6794642ebb 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -547,7 +547,7 @@ def make_permissions(edit=None, review=None, view=None): "display": _("Need screening"), "public": _("Application Received"), "stage": RequestExt, - "permissions": default_permissions, + "permissions": applicant_edit_permissions, }, "ext_more_info": { "transitions": { diff --git a/hypha/core/context_processors.py b/hypha/core/context_processors.py index d1efdf6b6b..e79c2b6718 100644 --- a/hypha/core/context_processors.py +++ b/hypha/core/context_processors.py @@ -22,4 +22,5 @@ def global_vars(request): "SENTRY_DEBUG": settings.SENTRY_DEBUG, "SENTRY_PUBLIC_KEY": settings.SENTRY_PUBLIC_KEY, "SUBMISSIONS_TABLE_EXCLUDED_FIELDS": settings.SUBMISSIONS_TABLE_EXCLUDED_FIELDS, + "ENABLE_SUBMISSION_WITHDRAWAL": settings.ENABLE_SUBMISSION_WITHDRAWAL, } diff --git a/hypha/settings/base.py b/hypha/settings/base.py index 37efac3224..709a505370 100644 --- a/hypha/settings/base.py +++ b/hypha/settings/base.py @@ -162,6 +162,9 @@ # Require an applicant to view their rendered application before submitting SUBMISSION_PREVIEW_REQUIRED = env.bool("SUBMISSION_PREVIEW_REQUIRED", True) +# Allow Withdrawing of Submissions +ENABLE_SUBMISSION_WITHDRAWAL = env.bool('ENABLE_SUBMISSION_WITHDRAWAL', False) + # Project settings. # SECRET_KEY is required From c05bde335acd6acfca2651c60e413b47d569b519 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Sat, 15 Apr 2023 09:02:09 -0500 Subject: [PATCH 04/15] Change permissions to withdraw to just being an applicant Based on Review in #3298, the decision was made that an applicant can withdraw at any time, without edit permissions, and edit permissions should not be to applicants for their own submissions. Issue #3296 --- .../funds/applicationsubmission_detail.html | 14 ++++++-------- hypha/apply/funds/views.py | 2 +- hypha/apply/funds/workflow.py | 2 +- hypha/static_src/sass/custom/_custom.scss | 1 - 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html index 4dedcdf5ab..d3bf35fc95 100644 --- a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html +++ b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html @@ -137,14 +137,12 @@
{% blocktrans with stage=object.previous.stage %}Your {{ stage }} applicatio {% trans "Delete" %} {% endif %} - {% if request.user|has_edit_perm:object %} - {% if request.user.is_applicant %} - - {% trans "Withdraw" %} - - {% endif %} + {% if ENABLE_SUBMISSION_WITHDRAWAL and request.user.is_applicant %} + + {% trans "Withdraw" %} + {% endif %} {% if request.user|has_edit_perm:object %} Date: Sat, 15 Apr 2023 09:04:03 -0500 Subject: [PATCH 05/15] Add withdraw to all workflows, move to end of workflow This creates a single withdrawn status for each workflow, living at the end (next to accepted/rejected), and adds transistions for all the different workflow types. Issue #3296 --- hypha/apply/funds/workflow.py | 79 +++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 17 deletions(-) diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 8be2f11fa1..8b61cca38b 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -271,6 +271,7 @@ def make_permissions(edit=None, review=None, view=None): "almost": _("Accept but additional info required"), "accepted": _("Accept"), "rejected": _("Dismiss"), + "withdrawn": _("Withdraw"), }, "display": _("Need screening"), "public": _("Application Received"), @@ -294,6 +295,7 @@ def make_permissions(edit=None, review=None, view=None): "almost": _("Accept but additional info required"), "accepted": _("Accept"), "rejected": _("Dismiss"), + "withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": Request, @@ -305,6 +307,7 @@ def make_permissions(edit=None, review=None, view=None): "transitions": { "post_review_discussion": _("Close Review"), INITIAL_STATE: _("Need screening (revert)"), + "withdrawn": _("Withdraw"), }, "display": _("Internal Review"), "public": _("{org_short_name} Review").format( @@ -323,6 +326,7 @@ def make_permissions(edit=None, review=None, view=None): "almost": _("Accept but additional info required"), "accepted": _("Accept"), "rejected": _("Dismiss"), + "withdrawn": _("Withdraw"), }, "display": _("Ready For Discussion"), "stage": Request, @@ -345,6 +349,7 @@ def make_permissions(edit=None, review=None, view=None): "almost": _("Accept but additional info required"), "accepted": _("Accept"), "rejected": _("Dismiss"), + "withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": Request, @@ -358,6 +363,7 @@ def make_permissions(edit=None, review=None, view=None): "almost": _("Accept but additional info required"), "accepted": _("Accept"), "rejected": _("Dismiss"), + "withdrawn": _("Withdraw"), }, "display": _("Ready for Determination"), "permissions": hidden_from_applicant_permissions, @@ -375,6 +381,7 @@ def make_permissions(edit=None, review=None, view=None): "transitions": { "accepted": _("Accept"), "post_review_discussion": _("Ready For Discussion (revert)"), + "withdrawn": _("Withdraw"), }, "display": _("Accepted but additional info required"), "stage": Request, @@ -385,6 +392,11 @@ def make_permissions(edit=None, review=None, view=None): "stage": Request, "permissions": no_permissions, }, + "withdrawn": { + "display": _("Withdrawn"), + "stage": Request, + "permissions": staff_edit_permissions, + }, }, ] @@ -542,7 +554,7 @@ def make_permissions(edit=None, review=None, view=None): "ext_internal_review": _("Open Review"), "ext_determination": _("Ready For Determination"), "ext_rejected": _("Dismiss"), - "ext_screening_withdrawn": _("Withdraw"), + "ext_withdrawn": _("Withdraw"), }, "display": _("Need screening"), "public": _("Application Received"), @@ -562,13 +574,13 @@ def make_permissions(edit=None, review=None, view=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, - "ext_screening_withdrawn": _("Withdraw"), + "ext_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": RequestExt, "permissions": applicant_edit_permissions, }, - "ext_screening_withdrawn": { + "ext_withdrawn": { "display": _("Withdrawn"), "stage": RequestExt, "permissions": staff_edit_permissions, @@ -578,7 +590,7 @@ def make_permissions(edit=None, review=None, view=None): "ext_internal_review": { "transitions": { "ext_post_review_discussion": _("Close Review"), - "ext_review_withdrawn": _("Withdraw"), + "ext_withdrawn": _("Withdraw"), INITIAL_STATE: _("Need screening (revert)"), }, "display": _("Internal Review"), @@ -597,6 +609,7 @@ def make_permissions(edit=None, review=None, view=None): "ext_determination": _("Ready For Determination"), "ext_internal_review": _("Open Internal Review (revert)"), "ext_rejected": _("Dismiss"), + "ext_withdrawn": _("Withdraw"), }, "display": _("Ready For Discussion"), "stage": RequestExt, @@ -615,7 +628,7 @@ def make_permissions(edit=None, review=None, view=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, - "ext_review_withdrawn": _("Withdraw"), + "ext_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": RequestExt, @@ -632,11 +645,6 @@ def make_permissions(edit=None, review=None, view=None): "stage": RequestExt, "permissions": reviewer_review_permissions, }, - "ext_review_withdrawn": { - "display": _("Withdrawn"), - "stage": RequestExt, - "permissions": staff_edit_permissions, - }, }, { "ext_post_external_review_discussion": { @@ -647,7 +655,7 @@ def make_permissions(edit=None, review=None, view=None): "ext_almost": _("Accept but additional info required"), "ext_accepted": _("Accept"), "ext_rejected": _("Dismiss"), - "ext_external_review_withdrawn": _("Withdraw"), + "ext_withdrawn": _("Withdraw"), }, "display": _("Ready For Discussion"), "stage": RequestExt, @@ -666,17 +674,12 @@ def make_permissions(edit=None, review=None, view=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, - "ext_external_review_withdrawn": _("Withdraw"), + "ext_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": RequestExt, "permissions": applicant_edit_permissions, }, - "ext_external_review_withdrawn": { - "display": _("Withdrawn"), - "stage": RequestExt, - "permissions": staff_edit_permissions, - }, }, { "ext_determination": { @@ -707,6 +710,7 @@ def make_permissions(edit=None, review=None, view=None): "ext_post_external_review_discussion": _( "Ready For Discussion (revert)" ), + "ext_withdrawn": _("Withdraw"), }, "display": _("Accepted but additional info required"), "stage": RequestExt, @@ -751,6 +755,7 @@ def make_permissions(edit=None, review=None, view=None): "com_community_review": _("Open Community Review"), "com_determination": _("Ready For Determination"), "com_rejected": _("Dismiss"), + "com_withdrawn": _("Withdraw"), }, "display": _("Need screening"), "public": _("Application Received"), @@ -779,6 +784,7 @@ def make_permissions(edit=None, review=None, view=None): "transitions": { INITIAL_STATE: _("Need screening (revert)"), "com_rejected": _("Dismiss"), + "com_withdrawn": _("Withdraw"), }, "display": "Open Call (public)", "stage": RequestCom, @@ -792,6 +798,7 @@ def make_permissions(edit=None, review=None, view=None): "com_post_review_discussion": _("Close Review"), INITIAL_STATE: _("Need screening (revert)"), "com_rejected": _("Dismiss"), + "com_withdrawn": _("Withdraw"), }, "display": _("Internal Review"), "public": _("{org_short_name} Review").format( @@ -805,6 +812,7 @@ def make_permissions(edit=None, review=None, view=None): "com_post_review_discussion": _("Close Review"), "com_internal_review": _("Open Internal Review (revert)"), "com_rejected": _("Dismiss"), + "com_withdrawn": _("Withdraw"), }, "display": _("Community Review"), "public": _("{org_short_name} Review").format( @@ -822,6 +830,7 @@ def make_permissions(edit=None, review=None, view=None): "com_determination": _("Ready For Determination"), "com_internal_review": _("Open Internal Review (revert)"), "com_rejected": _("Dismiss"), + "com_withdrawn": _("Withdraw"), }, "display": _("Ready For Discussion"), "stage": RequestCom, @@ -840,6 +849,7 @@ def make_permissions(edit=None, review=None, view=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, + "com_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": RequestCom, @@ -851,6 +861,7 @@ def make_permissions(edit=None, review=None, view=None): "transitions": { "com_post_external_review_discussion": _("Close Review"), "com_post_review_discussion": _("Ready For Discussion (revert)"), + "com_withdrawn": _("Withdraw"), }, "display": _("External Review"), "stage": RequestCom, @@ -866,6 +877,7 @@ def make_permissions(edit=None, review=None, view=None): "com_almost": _("Accept but additional info required"), "com_accepted": _("Accept"), "com_rejected": _("Dismiss"), + "com_withdrawn": _("Withdraw"), }, "display": _("Ready For Discussion"), "stage": RequestCom, @@ -884,6 +896,7 @@ def make_permissions(edit=None, review=None, view=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, + "com_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": RequestCom, @@ -899,6 +912,7 @@ def make_permissions(edit=None, review=None, view=None): "com_almost": _("Accept but additional info required"), "com_accepted": _("Accept"), "com_rejected": _("Dismiss"), + "com_withdrawn": _("Withdraw"), }, "display": _("Ready for Determination"), "permissions": hidden_from_applicant_permissions, @@ -918,6 +932,7 @@ def make_permissions(edit=None, review=None, view=None): "com_post_external_review_discussion": _( "Ready For Discussion (revert)" ), + "com_withdrawn": _("Withdraw"), }, "display": _("Accepted but additional info required"), "stage": RequestCom, @@ -928,6 +943,11 @@ def make_permissions(edit=None, review=None, view=None): "stage": RequestCom, "permissions": no_permissions, }, + "com_withdrawn": { + "display": _("Withdrawn"), + "stage": RequestCom, + "permissions": staff_edit_permissions, + }, }, ] @@ -956,6 +976,7 @@ def make_permissions(edit=None, review=None, view=None): "concept_determination": _("Ready For Preliminary Determination"), "invited_to_proposal": _("Invite to Proposal"), "concept_rejected": _("Dismiss"), + "concept_withdrawn": _("Withdraw"), }, "display": _("Need screening"), "public": _("Concept Note Received"), @@ -978,6 +999,7 @@ def make_permissions(edit=None, review=None, view=None): "concept_rejected": _("Dismiss"), "invited_to_proposal": _("Invite to Proposal"), "concept_determination": _("Ready For Preliminary Determination"), + "concept_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": Concept, @@ -990,6 +1012,7 @@ def make_permissions(edit=None, review=None, view=None): "concept_review_discussion": _("Close Review"), INITIAL_STATE: _("Need screening (revert)"), "invited_to_proposal": _("Invite to Proposal"), + "concept_withdrawn": _("Withdraw"), }, "display": _("Internal Review"), "public": _("{org_short_name} Review").format( @@ -1007,6 +1030,7 @@ def make_permissions(edit=None, review=None, view=None): "concept_internal_review": _("Open Review (revert)"), "invited_to_proposal": _("Invite to Proposal"), "concept_rejected": _("Dismiss"), + "concept_withdrawn": _("Withdraw"), }, "display": _("Ready For Discussion"), "stage": Concept, @@ -1026,6 +1050,7 @@ def make_permissions(edit=None, review=None, view=None): "custom": {"trigger_on_submit": True}, }, "invited_to_proposal": _("Invite to Proposal"), + "concept_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": Concept, @@ -1038,6 +1063,7 @@ def make_permissions(edit=None, review=None, view=None): "concept_review_discussion": _("Ready For Discussion (revert)"), "invited_to_proposal": _("Invite to Proposal"), "concept_rejected": _("Dismiss"), + "concept_withdrawn": _("Withdraw"), }, "display": _("Ready for Preliminary Determination"), "permissions": hidden_from_applicant_permissions, @@ -1068,6 +1094,11 @@ def make_permissions(edit=None, review=None, view=None): "stage": Concept, "permissions": no_permissions, }, + "concept_withdrawn": { + "display": _("Withdrawn"), + "stage": Concept, + "permissions": staff_edit_permissions, + }, }, { "draft_proposal": { @@ -1095,6 +1126,7 @@ def make_permissions(edit=None, review=None, view=None): "external_review": _("Open External Review"), "proposal_determination": _("Ready For Final Determination"), "proposal_rejected": _("Dismiss"), + "proposal_withdrawn": _("Withdraw"), }, "display": _("Proposal Received"), "stage": Proposal, @@ -1116,6 +1148,7 @@ def make_permissions(edit=None, review=None, view=None): "external_review": _("Open External Review"), "proposal_determination": _("Ready For Final Determination"), "proposal_rejected": _("Dismiss"), + "proposal_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": Proposal, @@ -1127,6 +1160,7 @@ def make_permissions(edit=None, review=None, view=None): "transitions": { "post_proposal_review_discussion": _("Close Review"), "proposal_discussion": _("Proposal Received (revert)"), + "proposal_withdrawn": _("Withdraw"), }, "display": _("Internal Review"), "public": _("{org_short_name} Review").format( @@ -1144,6 +1178,7 @@ def make_permissions(edit=None, review=None, view=None): "proposal_determination": _("Ready For Final Determination"), "proposal_internal_review": _("Open Internal Review (revert)"), "proposal_rejected": _("Dismiss"), + "proposal_withdrawn": _("Withdraw"), }, "display": _("Ready For Discussion"), "stage": Proposal, @@ -1163,6 +1198,7 @@ def make_permissions(edit=None, review=None, view=None): "custom": {"trigger_on_submit": True}, }, "external_review": _("Open External Review"), + "proposal_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": Proposal, @@ -1174,6 +1210,7 @@ def make_permissions(edit=None, review=None, view=None): "transitions": { "post_external_review_discussion": _("Close Review"), "post_proposal_review_discussion": _("Ready For Discussion (revert)"), + "proposal_withdrawn": _("Withdraw"), }, "display": _("External Review"), "stage": Proposal, @@ -1189,6 +1226,7 @@ def make_permissions(edit=None, review=None, view=None): "proposal_almost": _("Accept but additional info required"), "proposal_accepted": _("Accept"), "proposal_rejected": _("Dismiss"), + "proposal_withdrawn": _("Withdraw"), }, "display": _("Ready For Discussion"), "stage": Proposal, @@ -1207,6 +1245,7 @@ def make_permissions(edit=None, review=None, view=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, + "proposal_withdrawn": _("Withdraw"), }, "display": _("More information required"), "stage": Proposal, @@ -1220,6 +1259,7 @@ def make_permissions(edit=None, review=None, view=None): "proposal_almost": _("Accept but additional info required"), "proposal_accepted": _("Accept"), "proposal_rejected": _("Dismiss"), + "proposal_withdrawn": _("Withdraw"), }, "display": _("Ready for Final Determination"), "permissions": hidden_from_applicant_permissions, @@ -1247,6 +1287,11 @@ def make_permissions(edit=None, review=None, view=None): "stage": Proposal, "permissions": no_permissions, }, + "proposal_withdrawn": { + "display": _("Withdrawn"), + "stage": Proposal, + "permissions": staff_edit_permissions, + }, }, ] From dc5dc212942a3845887ee57b0340f1f9a17d1be2 Mon Sep 17 00:00:00 2001 From: Fredrik Jonsson Date: Wed, 26 Jul 2023 10:16:57 +0200 Subject: [PATCH 06/15] Move css from custom file. Issue #3296 --- hypha/apply/funds/views.py | 4 ++-- hypha/static_src/sass/custom/_custom.scss | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index ef33642444..020461e806 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -1800,11 +1800,11 @@ def withdraw(self, request, *args, **kwargs): if not settings.ENABLE_SUBMISSION_WITHDRAWAL: raise PermissionDenied - obj = self.get_object() - if not request.user.is_applicant: raise PermissionDenied + obj = self.get_object() + withdraw_actions = [action for action in obj.workflow[obj.status].transitions.keys() if 'withdraw' in action] if len(withdraw_actions) > 0: diff --git a/hypha/static_src/sass/custom/_custom.scss b/hypha/static_src/sass/custom/_custom.scss index 55e678b65a..061b3d43d2 100644 --- a/hypha/static_src/sass/custom/_custom.scss +++ b/hypha/static_src/sass/custom/_custom.scss @@ -1,5 +1 @@ -.link--withdraw-submission { - margin-right: 1rem; - padding-right: 1rem; - font-weight: 700; -} +// stylelint-disable no-empty-source From 04939e86bb8aeb99088fd0b693aa4e6b817e46af Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Mon, 6 Nov 2023 12:14:52 -0600 Subject: [PATCH 07/15] Add "withdrawn" to list of inactive statuses This affected different calculating pages, like the open round percentage completions. Issue #3296 --- hypha/apply/funds/workflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 8b61cca38b..1afff5a07d 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -1381,7 +1381,7 @@ def get_stage_change_actions(): active_statuses = [ status for status, _ in PHASES - if "accepted" not in status and "rejected" not in status and "invited" not in status + if "accepted" not in status and "rejected" not in status and "invited" not in status and "withdrawn" not in status ] From 3f8fcbcfc1200b842741a099a574b5b03a12b9b4 Mon Sep 17 00:00:00 2001 From: Frank Duncan Date: Wed, 24 Apr 2024 06:16:44 -0500 Subject: [PATCH 08/15] Fix error in all submissions table Withdrawn status needs to be added to a mapping, apparently :) Issue #3296 --- hypha/apply/funds/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hypha/apply/funds/utils.py b/hypha/apply/funds/utils.py index 089e0cbe50..2fff9eebd0 100644 --- a/hypha/apply/funds/utils.py +++ b/hypha/apply/funds/utils.py @@ -94,6 +94,7 @@ def model_form_initial(instance, fields=None, exclude=None): ], "accepted": ["accepted"], "dismissed": ["dismissed"], + "withdrawn": ["withdrawn"], } From cccc5e2d6da606d11eb5bec91546b0704719980c Mon Sep 17 00:00:00 2001 From: Jesse Bickel Date: Wed, 24 Jul 2024 10:13:26 -0500 Subject: [PATCH 09/15] Clean up from cherry-pick/rebase Removed a redeclared variable and reformatted. Issue #3296 --- ...pplicationsubmission_confirm_withdraw.html | 24 +++++++++---------- hypha/apply/funds/urls.py | 4 +++- hypha/apply/funds/views.py | 17 +++++++------ hypha/apply/funds/workflow.py | 6 +++-- hypha/settings/base.py | 2 +- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/hypha/apply/funds/templates/funds/applicationsubmission_confirm_withdraw.html b/hypha/apply/funds/templates/funds/applicationsubmission_confirm_withdraw.html index d0830b01b6..396081fdbe 100644 --- a/hypha/apply/funds/templates/funds/applicationsubmission_confirm_withdraw.html +++ b/hypha/apply/funds/templates/funds/applicationsubmission_confirm_withdraw.html @@ -4,20 +4,20 @@ {% block title %}{% trans "Withdrawing" %}: {{object.title }}{% endblock %} {% block content %} -
-
-

{% trans "Withdrawing" %}: {{ object.title }}

+
+
+

{% trans "Withdrawing" %}: {{ object.title }}

+
-
-
-
-
- {% csrf_token %} -

{% blocktrans %}Are you sure you want to withdraw "{{ object }}" from consideration?{% endblocktrans %}

- -
+
+
+
+ {% csrf_token %} +

{% blocktrans %}Are you sure you want to withdraw "{{ object }}" from consideration?{% endblocktrans %}

+ +
+
-
{% endblock %} diff --git a/hypha/apply/funds/urls.py b/hypha/apply/funds/urls.py index 7dd2b5ec5f..5e16bdcf59 100644 --- a/hypha/apply/funds/urls.py +++ b/hypha/apply/funds/urls.py @@ -259,7 +259,9 @@ "download/", SubmissionDetailPDFView.as_view(), name="download" ), path("delete/", SubmissionDeleteView.as_view(), name="delete"), - path('withdraw/', SubmissionWithdrawView.as_view(), name="withdraw"), + path( + "withdraw/", SubmissionWithdrawView.as_view(), name="withdraw" + ), path( "documents//", SubmissionPrivateMediaView.as_view(), diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index 020461e806..c4e6f52e3e 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -1788,10 +1788,11 @@ def form_valid(self, form): # delete submission and redirect to success url return super().form_valid(form) + class SubmissionWithdrawView(SingleObjectTemplateResponseMixin, BaseDetailView): model = ApplicationSubmission - success_url = reverse_lazy('funds:submissions:list') - template_name_suffix = '_confirm_withdraw' + success_url = reverse_lazy("funds:submissions:list") + template_name_suffix = "_confirm_withdraw" def post(self, request, *args, **kwargs): return self.withdraw(request, *args, **kwargs) @@ -1805,20 +1806,22 @@ def withdraw(self, request, *args, **kwargs): obj = self.get_object() - withdraw_actions = [action for action in obj.workflow[obj.status].transitions.keys() if 'withdraw' in action] + withdraw_actions = [ + action + for action in obj.workflow[obj.status].transitions.keys() + if "withdraw" in action + ] if len(withdraw_actions) > 0: action = withdraw_actions[0] obj.perform_transition( - action, - self.request.user, - request=self.request, - notify=False + action, self.request.user, request=self.request, notify=False ) success_url = obj.get_absolute_url() return HttpResponseRedirect(success_url) + @method_decorator(login_required, name="dispatch") class SubmissionPrivateMediaView(UserPassesTestMixin, PrivateMediaView): raise_exception = True diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 1afff5a07d..dc8390e2d5 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -1381,7 +1381,10 @@ def get_stage_change_actions(): active_statuses = [ status for status, _ in PHASES - if "accepted" not in status and "rejected" not in status and "invited" not in status and "withdrawn" not in status + if "accepted" not in status + and "rejected" not in status + and "invited" not in status + and "withdrawn" not in status ] @@ -1463,7 +1466,6 @@ def get_withdrawn_statuses(): return withdrawn_statuses -ext_or_higher_statuses = get_ext_or_higher_statuses() review_statuses = get_review_statuses() ext_review_statuses = get_ext_review_statuses() ext_or_higher_statuses = get_ext_or_higher_statuses() diff --git a/hypha/settings/base.py b/hypha/settings/base.py index 709a505370..b2a2419b82 100644 --- a/hypha/settings/base.py +++ b/hypha/settings/base.py @@ -163,7 +163,7 @@ SUBMISSION_PREVIEW_REQUIRED = env.bool("SUBMISSION_PREVIEW_REQUIRED", True) # Allow Withdrawing of Submissions -ENABLE_SUBMISSION_WITHDRAWAL = env.bool('ENABLE_SUBMISSION_WITHDRAWAL', False) +ENABLE_SUBMISSION_WITHDRAWAL = env.bool("ENABLE_SUBMISSION_WITHDRAWAL", False) # Project settings. From 90849fbb79af5f9bd2ece9990bf3b20b653b20d0 Mon Sep 17 00:00:00 2001 From: Jesse Bickel Date: Thu, 25 Jul 2024 12:20:36 -0500 Subject: [PATCH 10/15] Distinguish withdrawal permissions Add distinct withdrawal permissions within stages of workflows. By default, let the applicant withdraw at any stage. On the submission page, display the Withdraw button assuming the setting `ENABLE_SUBMISSION_WITHDRAWAL` is true and the withdrawal permissions are met. Issue #3296 --- .../templates/funds/applicationsubmission_detail.html | 2 +- hypha/apply/funds/templatetags/workflow_tags.py | 7 +++++++ hypha/apply/funds/workflow.py | 6 +++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html index d3bf35fc95..167b268ea7 100644 --- a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html +++ b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html @@ -137,7 +137,7 @@
{% blocktrans with stage=object.previous.stage %}Your {{ stage }} applicatio {% trans "Delete" %} {% endif %} - {% if ENABLE_SUBMISSION_WITHDRAWAL and request.user.is_applicant %} + {% if request.user|has_withdraw_perm:object %} diff --git a/hypha/apply/funds/templatetags/workflow_tags.py b/hypha/apply/funds/templatetags/workflow_tags.py index 274a1e88b6..91951a22b5 100644 --- a/hypha/apply/funds/templatetags/workflow_tags.py +++ b/hypha/apply/funds/templatetags/workflow_tags.py @@ -74,3 +74,10 @@ def display_submission_author(context: dict, revision_author: bool = False) -> s return settings.ORG_LONG_NAME # Likely an edge case but covering bases return str(author) + + +@register.filter +def has_withdraw_perm(user, submission): + if settings.ENABLE_SUBMISSION_WITHDRAWAL: + return check_permission(user, "withdraw", submission) + return False diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index dc8390e2d5..f238fda96b 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -181,6 +181,9 @@ def can_review(self, user): def can_view(self, user): return self.can_do(user, "view") + def can_withdraw(self, user): + return self.can_do(user, "withdraw") + staff_can = lambda user: user.is_apply_staff # NOQA @@ -193,7 +196,7 @@ def can_view(self, user): community_can = lambda user: user.is_community_reviewer # NOQA -def make_permissions(edit=None, review=None, view=None): +def make_permissions(edit=None, review=None, view=None, withdraw=None): return { "edit": edit or [], "review": review or [], @@ -204,6 +207,7 @@ def make_permissions(edit=None, review=None, view=None): reviewer_can, partner_can, ], + "withdraw": withdraw or [applicant_can], } From d5797e1b2c329ae13e92334da972c54f46cd4dfd Mon Sep 17 00:00:00 2001 From: Jesse Bickel Date: Thu, 25 Jul 2024 12:50:20 -0500 Subject: [PATCH 11/15] Make "Withdraw" button more consistent with others Add a heroicon to "Withdraw" button and style it similar to "Delete". Issue #3296 --- .../funds/templates/funds/applicationsubmission_detail.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html index 167b268ea7..f3d26c4fa5 100644 --- a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html +++ b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html @@ -139,8 +139,9 @@
{% blocktrans with stage=object.previous.stage %}Your {{ stage }} applicatio {% endif %} {% if request.user|has_withdraw_perm:object %} + {% heroicon_micro "arrow-uturn-down" class="inline me-1 fill-red-600" aria_hidden=true %} {% trans "Withdraw" %} {% endif %} From 2beb7db2f38345fd7a7b0899d8e5c178ee7ac602 Mon Sep 17 00:00:00 2001 From: Jesse Bickel Date: Tue, 30 Jul 2024 11:14:11 -0500 Subject: [PATCH 12/15] Improve withdrawal permissions and error handling The permissions on the `*withdraw` items should be `no_permissions`. After a submission is withdrawn, nothing further should happen to it. When `perform_transition` is called in the View, that function will already check for valid transitions and raise an exception if needed. So do not bother looking into the transitions, instead look directly for the withdraw action. And expect exactly one of those, otherwise raise an exception with details of expectations. Issue #3296 --- hypha/apply/funds/views.py | 16 +++++++++++----- hypha/apply/funds/workflow.py | 13 ++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index c4e6f52e3e..82c621b6e9 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -12,7 +12,7 @@ user_passes_test, ) from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin -from django.core.exceptions import PermissionDenied +from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.db.models import Count, Q from django.forms import BaseModelForm from django.http import ( @@ -1807,16 +1807,22 @@ def withdraw(self, request, *args, **kwargs): obj = self.get_object() withdraw_actions = [ - action - for action in obj.workflow[obj.status].transitions.keys() - if "withdraw" in action + action for action in obj.workflow.keys() if "withdraw" in action ] - if len(withdraw_actions) > 0: + if len(withdraw_actions) == 1: action = withdraw_actions[0] obj.perform_transition( action, self.request.user, request=self.request, notify=False ) + elif len(withdraw_actions) > 1: + raise ImproperlyConfigured( + f'In workflow "{obj.workflow}" too many withdraw actions: "{withdraw_actions}"' + ) + elif len(withdraw_actions) < 1: + raise ImproperlyConfigured( + f'No withdraw actions found in workflow "{obj.workflow}"' + ) success_url = obj.get_absolute_url() return HttpResponseRedirect(success_url) diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index f238fda96b..3d409773a2 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -399,7 +399,7 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "withdrawn": { "display": _("Withdrawn"), "stage": Request, - "permissions": staff_edit_permissions, + "permissions": no_permissions, }, }, ] @@ -584,11 +584,6 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "stage": RequestExt, "permissions": applicant_edit_permissions, }, - "ext_withdrawn": { - "display": _("Withdrawn"), - "stage": RequestExt, - "permissions": staff_edit_permissions, - }, }, { "ext_internal_review": { @@ -728,7 +723,7 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "ext_withdrawn": { "display": _("Withdrawn"), "stage": RequestExt, - "permissions": staff_edit_permissions, + "permissions": no_permissions, }, }, ] @@ -950,7 +945,7 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "com_withdrawn": { "display": _("Withdrawn"), "stage": RequestCom, - "permissions": staff_edit_permissions, + "permissions": no_permissions, }, }, ] @@ -1294,7 +1289,7 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "proposal_withdrawn": { "display": _("Withdrawn"), "stage": Proposal, - "permissions": staff_edit_permissions, + "permissions": no_permissions, }, }, ] From ed98ac4543d659e6ffabfd93c34511d83cd5c409 Mon Sep 17 00:00:00 2001 From: Fredrik Jonsson Date: Wed, 7 Aug 2024 21:10:01 +0200 Subject: [PATCH 13/15] Permission fixes. --- hypha/apply/funds/views.py | 45 +++--- hypha/apply/funds/workflow.py | 268 ++++++++++++++++++++++++++++------ 2 files changed, 245 insertions(+), 68 deletions(-) diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index 82c621b6e9..d253cbab28 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -140,6 +140,7 @@ PHASES_MAPPING, STAGE_CHANGE_ACTIONS, active_statuses, + get_withdraw_action_for_stage, review_statuses, ) @@ -1789,43 +1790,39 @@ def form_valid(self, form): return super().form_valid(form) -class SubmissionWithdrawView(SingleObjectTemplateResponseMixin, BaseDetailView): +@method_decorator(login_required, name="dispatch") +class SubmissionWithdrawView( + SingleObjectTemplateResponseMixin, UserPassesTestMixin, BaseDetailView +): model = ApplicationSubmission success_url = reverse_lazy("funds:submissions:list") template_name_suffix = "_confirm_withdraw" + def dispatch(self, *args, **kwargs): + self.submission = self.get_object() + return super().dispatch(*args, **kwargs) + def post(self, request, *args, **kwargs): return self.withdraw(request, *args, **kwargs) def withdraw(self, request, *args, **kwargs): - if not settings.ENABLE_SUBMISSION_WITHDRAWAL: - raise PermissionDenied + withdraw_action = get_withdraw_action_for_stage(self.submission.stage) - if not request.user.is_applicant: - raise PermissionDenied - - obj = self.get_object() - - withdraw_actions = [ - action for action in obj.workflow.keys() if "withdraw" in action - ] - - if len(withdraw_actions) == 1: - action = withdraw_actions[0] - obj.perform_transition( - action, self.request.user, request=self.request, notify=False - ) - elif len(withdraw_actions) > 1: - raise ImproperlyConfigured( - f'In workflow "{obj.workflow}" too many withdraw actions: "{withdraw_actions}"' + if withdraw_action: + self.submission.perform_transition( + withdraw_action, self.request.user, request=self.request, notify=False ) - elif len(withdraw_actions) < 1: + else: raise ImproperlyConfigured( - f'No withdraw actions found in workflow "{obj.workflow}"' + f'No withdraw actions found in workflow "{self.submission.workflow}"' ) - success_url = obj.get_absolute_url() - return HttpResponseRedirect(success_url) + return HttpResponseRedirect(self.submission.get_absolute_url()) + + def test_func(self): + can_withdraw = self.submission.phase.permissions.can_withdraw(self.request.user) + + return settings.ENABLE_SUBMISSION_WITHDRAWAL and can_withdraw @method_decorator(login_required, name="dispatch") diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 3d409773a2..538d6bca7d 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -207,7 +207,7 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): reviewer_can, partner_can, ], - "withdraw": withdraw or [applicant_can], + "withdraw": withdraw or [], } @@ -228,7 +228,7 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): ) applicant_edit_permissions = make_permissions( - edit=[applicant_can, partner_can], review=[staff_can] + edit=[applicant_can, partner_can], review=[staff_can], withdraw=[applicant_can] ) staff_edit_permissions = make_permissions(edit=[staff_can]) @@ -275,7 +275,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "almost": _("Accept but additional info required"), "accepted": _("Accept"), "rejected": _("Dismiss"), - "withdrawn": _("Withdraw"), + "withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Need screening"), "public": _("Application Received"), @@ -299,7 +303,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "almost": _("Accept but additional info required"), "accepted": _("Accept"), "rejected": _("Dismiss"), - "withdrawn": _("Withdraw"), + "withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": Request, @@ -311,7 +319,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "transitions": { "post_review_discussion": _("Close Review"), INITIAL_STATE: _("Need screening (revert)"), - "withdrawn": _("Withdraw"), + "withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Internal Review"), "public": _("{org_short_name} Review").format( @@ -330,7 +342,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "almost": _("Accept but additional info required"), "accepted": _("Accept"), "rejected": _("Dismiss"), - "withdrawn": _("Withdraw"), + "withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready For Discussion"), "stage": Request, @@ -353,7 +369,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "almost": _("Accept but additional info required"), "accepted": _("Accept"), "rejected": _("Dismiss"), - "withdrawn": _("Withdraw"), + "withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": Request, @@ -367,7 +387,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "almost": _("Accept but additional info required"), "accepted": _("Accept"), "rejected": _("Dismiss"), - "withdrawn": _("Withdraw"), + "withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready for Determination"), "permissions": hidden_from_applicant_permissions, @@ -385,7 +409,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "transitions": { "accepted": _("Accept"), "post_review_discussion": _("Ready For Discussion (revert)"), - "withdrawn": _("Withdraw"), + "withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Accepted but additional info required"), "stage": Request, @@ -558,7 +586,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "ext_internal_review": _("Open Review"), "ext_determination": _("Ready For Determination"), "ext_rejected": _("Dismiss"), - "ext_withdrawn": _("Withdraw"), + "ext_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Need screening"), "public": _("Application Received"), @@ -578,7 +610,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, - "ext_withdrawn": _("Withdraw"), + "ext_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": RequestExt, @@ -589,7 +625,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "ext_internal_review": { "transitions": { "ext_post_review_discussion": _("Close Review"), - "ext_withdrawn": _("Withdraw"), + "ext_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, INITIAL_STATE: _("Need screening (revert)"), }, "display": _("Internal Review"), @@ -608,7 +648,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "ext_determination": _("Ready For Determination"), "ext_internal_review": _("Open Internal Review (revert)"), "ext_rejected": _("Dismiss"), - "ext_withdrawn": _("Withdraw"), + "ext_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready For Discussion"), "stage": RequestExt, @@ -627,7 +671,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, - "ext_withdrawn": _("Withdraw"), + "ext_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": RequestExt, @@ -654,7 +702,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "ext_almost": _("Accept but additional info required"), "ext_accepted": _("Accept"), "ext_rejected": _("Dismiss"), - "ext_withdrawn": _("Withdraw"), + "ext_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready For Discussion"), "stage": RequestExt, @@ -673,7 +725,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, - "ext_withdrawn": _("Withdraw"), + "ext_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": RequestExt, @@ -689,7 +745,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "ext_almost": _("Accept but additional info required"), "ext_accepted": _("Accept"), "ext_rejected": _("Dismiss"), - "ext_withdrawn": _("Withdraw"), + "ext_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready for Determination"), "permissions": hidden_from_applicant_permissions, @@ -709,7 +769,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "ext_post_external_review_discussion": _( "Ready For Discussion (revert)" ), - "ext_withdrawn": _("Withdraw"), + "ext_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Accepted but additional info required"), "stage": RequestExt, @@ -754,7 +818,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "com_community_review": _("Open Community Review"), "com_determination": _("Ready For Determination"), "com_rejected": _("Dismiss"), - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Need screening"), "public": _("Application Received"), @@ -783,7 +851,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "transitions": { INITIAL_STATE: _("Need screening (revert)"), "com_rejected": _("Dismiss"), - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": "Open Call (public)", "stage": RequestCom, @@ -797,7 +869,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "com_post_review_discussion": _("Close Review"), INITIAL_STATE: _("Need screening (revert)"), "com_rejected": _("Dismiss"), - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Internal Review"), "public": _("{org_short_name} Review").format( @@ -811,7 +887,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "com_post_review_discussion": _("Close Review"), "com_internal_review": _("Open Internal Review (revert)"), "com_rejected": _("Dismiss"), - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Community Review"), "public": _("{org_short_name} Review").format( @@ -829,7 +909,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "com_determination": _("Ready For Determination"), "com_internal_review": _("Open Internal Review (revert)"), "com_rejected": _("Dismiss"), - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready For Discussion"), "stage": RequestCom, @@ -848,7 +932,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": RequestCom, @@ -860,7 +948,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "transitions": { "com_post_external_review_discussion": _("Close Review"), "com_post_review_discussion": _("Ready For Discussion (revert)"), - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("External Review"), "stage": RequestCom, @@ -876,7 +968,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "com_almost": _("Accept but additional info required"), "com_accepted": _("Accept"), "com_rejected": _("Dismiss"), - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready For Discussion"), "stage": RequestCom, @@ -895,7 +991,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": RequestCom, @@ -911,7 +1011,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "com_almost": _("Accept but additional info required"), "com_accepted": _("Accept"), "com_rejected": _("Dismiss"), - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready for Determination"), "permissions": hidden_from_applicant_permissions, @@ -931,7 +1035,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "com_post_external_review_discussion": _( "Ready For Discussion (revert)" ), - "com_withdrawn": _("Withdraw"), + "com_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Accepted but additional info required"), "stage": RequestCom, @@ -975,7 +1083,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "concept_determination": _("Ready For Preliminary Determination"), "invited_to_proposal": _("Invite to Proposal"), "concept_rejected": _("Dismiss"), - "concept_withdrawn": _("Withdraw"), + "concept_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Need screening"), "public": _("Concept Note Received"), @@ -998,7 +1110,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "concept_rejected": _("Dismiss"), "invited_to_proposal": _("Invite to Proposal"), "concept_determination": _("Ready For Preliminary Determination"), - "concept_withdrawn": _("Withdraw"), + "concept_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": Concept, @@ -1011,7 +1127,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "concept_review_discussion": _("Close Review"), INITIAL_STATE: _("Need screening (revert)"), "invited_to_proposal": _("Invite to Proposal"), - "concept_withdrawn": _("Withdraw"), + "concept_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Internal Review"), "public": _("{org_short_name} Review").format( @@ -1029,7 +1149,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "concept_internal_review": _("Open Review (revert)"), "invited_to_proposal": _("Invite to Proposal"), "concept_rejected": _("Dismiss"), - "concept_withdrawn": _("Withdraw"), + "concept_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready For Discussion"), "stage": Concept, @@ -1049,7 +1173,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "custom": {"trigger_on_submit": True}, }, "invited_to_proposal": _("Invite to Proposal"), - "concept_withdrawn": _("Withdraw"), + "concept_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": Concept, @@ -1062,7 +1190,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "concept_review_discussion": _("Ready For Discussion (revert)"), "invited_to_proposal": _("Invite to Proposal"), "concept_rejected": _("Dismiss"), - "concept_withdrawn": _("Withdraw"), + "concept_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready for Preliminary Determination"), "permissions": hidden_from_applicant_permissions, @@ -1125,7 +1257,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "external_review": _("Open External Review"), "proposal_determination": _("Ready For Final Determination"), "proposal_rejected": _("Dismiss"), - "proposal_withdrawn": _("Withdraw"), + "proposal_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Proposal Received"), "stage": Proposal, @@ -1147,7 +1283,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "external_review": _("Open External Review"), "proposal_determination": _("Ready For Final Determination"), "proposal_rejected": _("Dismiss"), - "proposal_withdrawn": _("Withdraw"), + "proposal_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": Proposal, @@ -1159,7 +1299,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "transitions": { "post_proposal_review_discussion": _("Close Review"), "proposal_discussion": _("Proposal Received (revert)"), - "proposal_withdrawn": _("Withdraw"), + "proposal_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Internal Review"), "public": _("{org_short_name} Review").format( @@ -1177,7 +1321,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "proposal_determination": _("Ready For Final Determination"), "proposal_internal_review": _("Open Internal Review (revert)"), "proposal_rejected": _("Dismiss"), - "proposal_withdrawn": _("Withdraw"), + "proposal_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready For Discussion"), "stage": Proposal, @@ -1197,7 +1345,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "custom": {"trigger_on_submit": True}, }, "external_review": _("Open External Review"), - "proposal_withdrawn": _("Withdraw"), + "proposal_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": Proposal, @@ -1209,7 +1361,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "transitions": { "post_external_review_discussion": _("Close Review"), "post_proposal_review_discussion": _("Ready For Discussion (revert)"), - "proposal_withdrawn": _("Withdraw"), + "proposal_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("External Review"), "stage": Proposal, @@ -1225,7 +1381,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "proposal_almost": _("Accept but additional info required"), "proposal_accepted": _("Accept"), "proposal_rejected": _("Dismiss"), - "proposal_withdrawn": _("Withdraw"), + "proposal_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready For Discussion"), "stage": Proposal, @@ -1244,7 +1404,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, - "proposal_withdrawn": _("Withdraw"), + "proposal_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": Proposal, @@ -1258,7 +1422,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "proposal_almost": _("Accept but additional info required"), "proposal_accepted": _("Accept"), "proposal_rejected": _("Dismiss"), - "proposal_withdrawn": _("Withdraw"), + "proposal_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready for Final Determination"), "permissions": hidden_from_applicant_permissions, @@ -1441,6 +1609,18 @@ def get_ext_or_higher_statuses(): return reviews +def get_withdraw_action_for_stage(stage=None): + """ + Returns the withdraw action for stage. + """ + for workflow in WORKFLOWS.values(): + for phase in workflow.values(): + if phase.stage == stage and phase.name in withdrawn_statuses: + return phase.name + + return False + + def get_accepted_statuses(): accepted_statuses = set() for phase_name, phase in PHASES: From e937ab8349b68d50df10ca7dd7aca77fe613fb7b Mon Sep 17 00:00:00 2001 From: Fredrik Jonsson Date: Thu, 8 Aug 2024 17:24:18 +0200 Subject: [PATCH 14/15] Applicant can withdraw in default_permissions. --- hypha/apply/funds/workflow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 538d6bca7d..1098416045 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -213,7 +213,9 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): no_permissions = make_permissions() -default_permissions = make_permissions(edit=[staff_can], review=[staff_can]) +default_permissions = make_permissions( + edit=[staff_can], review=[staff_can], withdraw=[applicant_can] +) hidden_from_applicant_permissions = make_permissions( edit=[staff_can], review=[staff_can], view=[staff_can, reviewer_can] From 4d15ac99ee626c73cec52f5f9bc5030d6fe48ac4 Mon Sep 17 00:00:00 2001 From: Fredrik Jonsson Date: Wed, 27 Nov 2024 08:10:39 +0100 Subject: [PATCH 15/15] Add withdraw to new workflow. --- hypha/apply/funds/workflow.py | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 1098416045..6bb87e1095 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -457,6 +457,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "same_internal_review": _("Open Review"), "same_determination": _("Ready For Determination"), "same_rejected": _("Dismiss"), + "same_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Need screening"), "public": _("Application Received"), @@ -476,6 +481,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, + "same_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": RequestSame, @@ -486,6 +496,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "same_internal_review": { "transitions": { "same_post_review_discussion": _("Close Review"), + "same_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, INITIAL_STATE: _("Need screening (revert)"), }, "display": _("Review"), @@ -503,6 +518,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "same_determination": _("Ready For Determination"), "same_internal_review": _("Open Review (revert)"), "same_rejected": _("Dismiss"), + "same_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready For Discussion"), "stage": RequestSame, @@ -521,6 +541,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "method": "create_revision", "custom": {"trigger_on_submit": True}, }, + "same_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("More information required"), "stage": RequestSame, @@ -534,6 +559,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "same_almost": _("Accept but additional info required"), "same_accepted": _("Accept"), "same_rejected": _("Dismiss"), + "same_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Ready for Determination"), "permissions": hidden_from_applicant_permissions, @@ -551,6 +581,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "transitions": { "same_accepted": _("Accept"), "same_post_review_discussion": _("Ready For Discussion (revert)"), + "same_withdrawn": { + "display": _("Withdraw"), + "permissions": {UserPermissions.APPLICANT}, + "method": "withdraw", + }, }, "display": _("Accepted but additional info required"), "stage": RequestSame, @@ -561,6 +596,11 @@ def make_permissions(edit=None, review=None, view=None, withdraw=None): "stage": RequestSame, "permissions": no_permissions, }, + "same_withdrawn": { + "display": _("Withdraw"), + "stage": RequestSame, + "permissions": no_permissions, + }, }, ]