From e6656d62f6db57426bb15f298127c106ea4f6397 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Wed, 20 Nov 2024 11:43:46 +0530 Subject: [PATCH] Fix auto transition on submit logic --- hypha/apply/funds/models/submissions.py | 5 ++- hypha/apply/funds/views.py | 51 +++++++++++++++---------- hypha/apply/funds/workflow.py | 35 +++++++++++++++-- 3 files changed, 66 insertions(+), 25 deletions(-) diff --git a/hypha/apply/funds/models/submissions.py b/hypha/apply/funds/models/submissions.py index a1279c4ca3..6bf73d5909 100644 --- a/hypha/apply/funds/models/submissions.py +++ b/hypha/apply/funds/models/submissions.py @@ -286,7 +286,7 @@ def transition_id(target, phase): class AddTransitions(models.base.ModelBase): def __new__(cls, name, bases, attrs, **kwargs): for workflow in WORKFLOWS.values(): - for phase, data in workflow.items(): + for phase_name, data in workflow.items(): for transition_name, action in data.transitions.items(): method_name = transition_id(transition_name, data) permission_name = method_name + "_permission" @@ -305,10 +305,11 @@ def __new__(cls, name, bases, attrs, **kwargs): # Wrap with transition decorator transition_func = transition( attrs["status"], - source=phase, + source=phase_name, target=transition_name, permission=permission_func, conditions=conditions, + custom=action.get("custom", {}), )(transition_state) # Attach to new class diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index aebea8cefe..66d9f1ad15 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -1260,13 +1260,6 @@ class BaseSubmissionEditView(UpdateView): model = ApplicationSubmission - @property - def transitions(self): - transitions = self.object.get_available_user_status_transitions( - self.request.user - ) - return {transition.name: transition for transition in transitions} - def render_preview(self, request: HttpRequest, form: BaseModelForm) -> HttpResponse: """Gets a rendered preview of a form @@ -1327,6 +1320,25 @@ def get_object_fund_current_round(self): return assigned_fund.open_round return False + def get_on_submit_transition(self, user): + """Gets the transition that should be triggered when a form is submitted. + + Checks all available status transitions for the current user and returns the first + one that has trigger_on_submit=True in its custom settings. + + Returns: + dict: The transition configuration dictionary with trigger_on_submit=True, + or None if no matching transition is found. + """ + return next( + ( + t + for t in self.object.get_available_user_status_transitions(user) + if t.custom.get("trigger_on_submit", False) + ), + None, + ) + def form_valid(self, form: BaseModelForm) -> HttpResponse: """Handle the form returned from a `SubmissionEditView`. @@ -1379,19 +1391,18 @@ def form_valid(self, form: BaseModelForm) -> HttpResponse: related=revision, ) - action = set(self.request.POST.keys()) & set(self.transitions.keys()) - try: - transition = self.transitions[action.pop()] - except KeyError: - pass - else: - self.object.perform_transition( - transition.target, - self.request.user, - request=self.request, - notify=not (revision or submitting_proposal) - or self.object.status == DRAFT_STATE, # Use the other notification - ) + if "submit" in self.request.POST: + if transition := self.get_on_submit_transition(self.request.user): + notify = ( + not (revision or submitting_proposal) + or self.object.status == DRAFT_STATE, + ) + self.object.perform_transition( + transition.target, + self.request.user, + request=self.request, + notify=notify, # Use the other notification + ) # Required for django-file-form: delete temporary files for the new files # uploaded while edit. diff --git a/hypha/apply/funds/workflow.py b/hypha/apply/funds/workflow.py index 26be6c890a..74884df78f 100644 --- a/hypha/apply/funds/workflow.py +++ b/hypha/apply/funds/workflow.py @@ -140,6 +140,9 @@ def __init__( transition["permissions"] = action.get( "permissions", default_permissions ) + if "custom" in action: + transition["custom"] = action["custom"] + self.transitions[transition_target] = transition def __str__(self): @@ -249,6 +252,7 @@ def make_permissions(edit=None, review=None, view=None): "display": _("Submit"), "permissions": {UserPermissions.APPLICANT}, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("Draft"), @@ -282,6 +286,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, "determination": _("Ready For Determination"), "almost": _("Accept but additional info required"), @@ -332,6 +337,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, "determination": _("Ready For Determination"), "almost": _("Accept but additional info required"), @@ -388,6 +394,7 @@ def make_permissions(edit=None, review=None, view=None): "display": _("Submit"), "permissions": {UserPermissions.APPLICANT}, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("Draft"), @@ -419,6 +426,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("More information required"), @@ -464,6 +472,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("More information required"), @@ -507,6 +516,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("More information required"), @@ -564,6 +574,7 @@ def make_permissions(edit=None, review=None, view=None): "display": _("Submit"), "permissions": {UserPermissions.APPLICANT}, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("Draft"), @@ -597,6 +608,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("More information required"), @@ -666,6 +678,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("More information required"), @@ -709,6 +722,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("More information required"), @@ -766,6 +780,7 @@ def make_permissions(edit=None, review=None, view=None): "display": _("Submit"), "permissions": {UserPermissions.APPLICANT}, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("Draft"), @@ -798,6 +813,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, "concept_rejected": _("Dismiss"), "invited_to_proposal": _("Invite to Proposal"), @@ -847,6 +863,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, "invited_to_proposal": _("Invite to Proposal"), }, @@ -899,6 +916,7 @@ def make_permissions(edit=None, review=None, view=None): "display": _("Submit"), "permissions": {UserPermissions.APPLICANT}, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, "external_review": _("Open External Review"), "proposal_determination": _("Ready For Final Determination"), @@ -933,6 +951,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, "external_review": _("Open External Review"), "proposal_determination": _("Ready For Final Determination"), @@ -981,6 +1000,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, "external_review": _("Open External Review"), }, @@ -1025,6 +1045,7 @@ def make_permissions(edit=None, review=None, view=None): UserPermissions.ADMIN, }, "method": "create_revision", + "custom": {"trigger_on_submit": True}, }, }, "display": _("More information required"), @@ -1071,12 +1092,20 @@ def make_permissions(edit=None, review=None, view=None): def unpack_phases(phases): - for step, step_data in enumerate(phases): - for name, phase_data in step_data.items(): - yield step, name, phase_data + """Unpack a list of phases into a generator of step, name, phase_data.""" + return ( + (step, name, phase_data) + for step, step_data in enumerate(phases) + for name, phase_data in step_data.items() + ) def phase_data(phases): + """Convert a list of phases into a dictionary of Phase objects. + + Adds the step number to the phase data. The step number is the index of the + phase in the list of phases. + """ return { phase_name: Phase(phase_name, step=step, **phase_data) for step, phase_name, phase_data in unpack_phases(phases)