diff --git a/docs/references/notifications.md b/docs/references/notifications.md index e421b2986ca510a9094444a9b70827757cc37801..78f0762a3ffe313ae1b133074903e44f78dfea96 100644 --- a/docs/references/notifications.md +++ b/docs/references/notifications.md @@ -12,10 +12,10 @@ The types of messages are as follows: The options for statuses for messages are as follows: -- `APPROVED_BY_FINANCE_1` +- `APPROVED_BY_FINANCE` - `APPROVED_BY_FINANCE_2` - `APPROVED_BY_STAFF` -- `CHANGES_REQUESTED_BY_FINANCE_1` +- `CHANGES_REQUESTED_BY_FINANCE` - `CHANGES_REQUESTED_BY_FINANCE_2` - `CONVERTED` - `PAID` diff --git a/hypha/apply/activity/adapters/activity_feed.py b/hypha/apply/activity/adapters/activity_feed.py index 9a968aeea99f239da5376139b0812a0a2cd8e681..bd3e6ca898778a9c2e5d672e12033884a43672df 100644 --- a/hypha/apply/activity/adapters/activity_feed.py +++ b/hypha/apply/activity/adapters/activity_feed.py @@ -50,6 +50,7 @@ class ActivityAdapter(AdapterBase): "Lead changed from {old_lead} to {source.lead}" ), MESSAGES.SEND_FOR_APPROVAL: _("Requested approval"), + MESSAGES.APPROVE_PAF: _("PAF assigned to {user}"), MESSAGES.APPROVE_PROJECT: _("Approved"), MESSAGES.REQUEST_PROJECT_CHANGE: _( 'Requested changes for acceptance: "{comment}"' @@ -89,6 +90,7 @@ class ActivityAdapter(AdapterBase): MESSAGES.APPROVE_PROJECT, MESSAGES.REQUEST_PROJECT_CHANGE, MESSAGES.SEND_FOR_APPROVAL, + MESSAGES.APPROVE_PAF, MESSAGES.NEW_REVIEW, MESSAGES.UPDATE_PROJECT_LEAD, ]: diff --git a/hypha/apply/activity/adapters/slack.py b/hypha/apply/activity/adapters/slack.py index 3c2a1dc69978e48209ccf3ff69286756b212c91b..74d0daaf2a2309b8f002c58cd8d47cd4a49137d0 100644 --- a/hypha/apply/activity/adapters/slack.py +++ b/hypha/apply/activity/adapters/slack.py @@ -9,12 +9,13 @@ from hypha.apply.activity.adapters.base import AdapterBase from hypha.apply.activity.adapters.utils import link_to, reviewers_message from hypha.apply.activity.options import MESSAGES from hypha.apply.projects.models.payment import ( - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, APPROVED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2, PAID, + PAYMENT_FAILED, RESUBMITTED, SUBMITTED, ) @@ -189,9 +190,10 @@ class SlackAdapter(AdapterBase): if related.status in [ SUBMITTED, RESUBMITTED, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, APPROVED_BY_FINANCE_2, PAID, + PAYMENT_FAILED, ]: # Notify project lead/staff return recipients @@ -202,7 +204,7 @@ class SlackAdapter(AdapterBase): for user in User.objects.finances_level_1() if self.slack_id(user) ] - if related.status in [APPROVED_BY_FINANCE_1]: + if related.status in [APPROVED_BY_FINANCE]: # Notify finance 2 return [ self.slack_id(user) diff --git a/hypha/apply/activity/adapters/utils.py b/hypha/apply/activity/adapters/utils.py index ecaffde8a6712ab52d081a7b70f851bba64ba120..98f95af826163c5b59edc738bd0eb1396d8eb45b 100644 --- a/hypha/apply/activity/adapters/utils.py +++ b/hypha/apply/activity/adapters/utils.py @@ -7,7 +7,7 @@ from django.utils.translation import gettext as _ from hypha.apply.activity.options import MESSAGES from hypha.apply.projects.models import ProjectSettings from hypha.apply.projects.models.payment import ( - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, CHANGES_REQUESTED_BY_STAFF, DECLINED, @@ -64,10 +64,7 @@ def is_invoice_public_transition(invoice): PAID, ]: return True - if ( - not settings.INVOICE_EXTENDED_WORKFLOW - and invoice.status == APPROVED_BY_FINANCE_1 - ): + if not settings.INVOICE_EXTENDED_WORKFLOW and invoice.status == APPROVED_BY_FINANCE: return True return False diff --git a/hypha/apply/api/v1/tests/test_views.py b/hypha/apply/api/v1/tests/test_views.py index f8fe382d71b67ed0473d0d27b75c6c7e7c26d25b..da222eab59234ae459298b5f9e43e2b385c15735 100644 --- a/hypha/apply/api/v1/tests/test_views.py +++ b/hypha/apply/api/v1/tests/test_views.py @@ -5,7 +5,7 @@ from rest_framework.exceptions import ErrorDetail from hypha.apply.activity.models import ALL, APPLICANT, Activity from hypha.apply.activity.tests.factories import CommentFactory from hypha.apply.projects.models.payment import ( - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, APPROVED_BY_STAFF, SUBMITTED, @@ -183,7 +183,7 @@ class TestInvoiceDeliverableViewset(TestCase): def test_finance2_can_add_deliverables(self): user = Finance2Factory() project = ProjectFactory() - invoice = InvoiceFactory(project=project, status=APPROVED_BY_FINANCE_1) + invoice = InvoiceFactory(project=project, status=APPROVED_BY_FINANCE) deliverable = DeliverableFactory(project=project) self.client.force_login(user) @@ -241,7 +241,7 @@ class TestInvoiceDeliverableViewset(TestCase): def test_finance1_cant_remove_deliverables_after_finance1_approval(self): user = FinanceFactory() project = ProjectFactory() - invoice = InvoiceFactory(project=project, status=APPROVED_BY_FINANCE_1) + invoice = InvoiceFactory(project=project, status=APPROVED_BY_FINANCE) deliverable = DeliverableFactory(project=project) invoice_deliverable = InvoiceDeliverableFactory(deliverable=deliverable) invoice.deliverables.add(invoice_deliverable) @@ -254,7 +254,7 @@ class TestInvoiceDeliverableViewset(TestCase): def test_finance2_can_remove_deliverables(self): user = Finance2Factory() project = ProjectFactory() - invoice = InvoiceFactory(project=project, status=APPROVED_BY_FINANCE_1) + invoice = InvoiceFactory(project=project, status=APPROVED_BY_FINANCE) deliverable = DeliverableFactory(project=project) invoice_deliverable = InvoiceDeliverableFactory(deliverable=deliverable) invoice.deliverables.add(invoice_deliverable) diff --git a/hypha/apply/projects/forms/payment.py b/hypha/apply/projects/forms/payment.py index ceb6fe68fa4473bf39847b35d7c7d9d143197397..8379e92de535512b7243fef996db491327a77b48 100644 --- a/hypha/apply/projects/forms/payment.py +++ b/hypha/apply/projects/forms/payment.py @@ -11,15 +11,16 @@ from django_file_form.forms import FileFormMixin from hypha.apply.stream_forms.fields import MultiFileField, SingleFileField from ..models.payment import ( - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, APPROVED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2, CHANGES_REQUESTED_BY_STAFF, DECLINED, INVOICE_STATUS_CHOICES, PAID, + PAYMENT_FAILED, RESUBMITTED, SUBMITTED, Invoice, @@ -59,27 +60,29 @@ class ChangeInvoiceStatusForm(forms.ModelForm): ), APPROVED_BY_STAFF: filter_request_choices( [ - CHANGES_REQUESTED_BY_FINANCE_1, - APPROVED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, + APPROVED_BY_FINANCE, ], user_choices, ), - CHANGES_REQUESTED_BY_FINANCE_1: filter_request_choices( + CHANGES_REQUESTED_BY_FINANCE: filter_request_choices( [CHANGES_REQUESTED_BY_STAFF, DECLINED], user_choices ), - APPROVED_BY_FINANCE_1: filter_request_choices([PAID], user_choices), + APPROVED_BY_FINANCE: filter_request_choices([PAID], user_choices), + PAID: filter_request_choices([PAYMENT_FAILED], user_choices), + PAYMENT_FAILED: filter_request_choices([PAID], user_choices), } if settings.INVOICE_EXTENDED_WORKFLOW: possible_status_transitions_lut.update( { CHANGES_REQUESTED_BY_FINANCE_2: filter_request_choices( [ - CHANGES_REQUESTED_BY_FINANCE_1, - APPROVED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, + APPROVED_BY_FINANCE, ], user_choices, ), - APPROVED_BY_FINANCE_1: filter_request_choices( + APPROVED_BY_FINANCE: filter_request_choices( [CHANGES_REQUESTED_BY_FINANCE_2, APPROVED_BY_FINANCE_2], user_choices, ), diff --git a/hypha/apply/projects/migrations/0080_alter_invoice_status.py b/hypha/apply/projects/migrations/0080_alter_invoice_status.py new file mode 100644 index 0000000000000000000000000000000000000000..1378fa06a539f5df4c4fc04afd20e2c0025f90f9 --- /dev/null +++ b/hypha/apply/projects/migrations/0080_alter_invoice_status.py @@ -0,0 +1,34 @@ +# Generated by Django 3.2.20 on 2023-09-19 06:23 + +from django.db import migrations +import django_fsm + + +class Migration(migrations.Migration): + dependencies = [ + ("application_projects", "0079_alter_invoice_add_fields"), + ] + + operations = [ + migrations.AlterField( + model_name="invoice", + name="status", + field=django_fsm.FSMField( + choices=[ + ("submitted", "Submitted"), + ("resubmitted", "Resubmitted"), + ("changes_requested_staff", "Changes requested by staff"), + ("changes_requested_finance_1", "Changes requested by finance"), + ("changes_requested_finance_2", "Changes requested by finance 2"), + ("approved_by_staff", "Approved by staff"), + ("approved_by_finance_1", "Approved by finance"), + ("approved_by_finance_2", "Approved by finance 2"), + ("paid", "Paid"), + ("payment_failed", "Payment failed"), + ("declined", "Declined"), + ], + default="submitted", + max_length=50, + ), + ), + ] diff --git a/hypha/apply/projects/models/payment.py b/hypha/apply/projects/models/payment.py index 952ec3264c4ce33b345111c042efca24fa28dea8..c96cccf88764c3f1812e6a9f9bfce8a227ff4dd6 100644 --- a/hypha/apply/projects/models/payment.py +++ b/hypha/apply/projects/models/payment.py @@ -18,24 +18,26 @@ from hypha.apply.utils.storage import PrivateStorage SUBMITTED = "submitted" RESUBMITTED = "resubmitted" CHANGES_REQUESTED_BY_STAFF = "changes_requested_staff" -CHANGES_REQUESTED_BY_FINANCE_1 = "changes_requested_finance_1" +CHANGES_REQUESTED_BY_FINANCE = "changes_requested_finance_1" CHANGES_REQUESTED_BY_FINANCE_2 = "changes_requested_finance_2" APPROVED_BY_STAFF = "approved_by_staff" -APPROVED_BY_FINANCE_1 = "approved_by_finance_1" +APPROVED_BY_FINANCE = "approved_by_finance_1" APPROVED_BY_FINANCE_2 = "approved_by_finance_2" PAID = "paid" +PAYMENT_FAILED = "payment_failed" DECLINED = "declined" INVOICE_STATUS_CHOICES = [ (SUBMITTED, _("Submitted")), (RESUBMITTED, _("Resubmitted")), - (CHANGES_REQUESTED_BY_STAFF, _("Changes Requested by Staff")), - (CHANGES_REQUESTED_BY_FINANCE_1, _("Changes Requested by Finance 1")), - (CHANGES_REQUESTED_BY_FINANCE_2, _("Changes Requested by Finance 2")), - (APPROVED_BY_STAFF, _("Approved by Staff")), - (APPROVED_BY_FINANCE_1, _("Approved by Finance 1")), - (APPROVED_BY_FINANCE_2, _("Approved by Finance 2")), + (CHANGES_REQUESTED_BY_STAFF, _("Changes requested by staff")), + (CHANGES_REQUESTED_BY_FINANCE, _("Changes requested by finance")), + (CHANGES_REQUESTED_BY_FINANCE_2, _("Changes requested by finance 2")), + (APPROVED_BY_STAFF, _("Approved by staff")), + (APPROVED_BY_FINANCE, _("Approved by finance")), + (APPROVED_BY_FINANCE_2, _("Approved by finance 2")), (PAID, _("Paid")), + (PAYMENT_FAILED, _("Payment failed")), (DECLINED, _("Declined")), ] @@ -44,26 +46,31 @@ INVOICE_TRANISTION_TO_RESUBMITTED = [ SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2, ] INVOICE_STATUS_PM_CHOICES = [CHANGES_REQUESTED_BY_STAFF, APPROVED_BY_STAFF, DECLINED] INVOICE_STATUS_FINANCE_1_CHOICES = [ - CHANGES_REQUESTED_BY_FINANCE_1, - APPROVED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, + APPROVED_BY_FINANCE, + DECLINED, PAID, + PAYMENT_FAILED, ] INVOICE_STATUS_FINANCE_2_CHOICES = [] if settings.INVOICE_EXTENDED_WORKFLOW: INVOICE_STATUS_FINANCE_1_CHOICES = [ - CHANGES_REQUESTED_BY_FINANCE_1, - APPROVED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, + APPROVED_BY_FINANCE, + DECLINED, ] INVOICE_STATUS_FINANCE_2_CHOICES = [ CHANGES_REQUESTED_BY_FINANCE_2, APPROVED_BY_FINANCE_2, + DECLINED, PAID, + PAYMENT_FAILED, ] @@ -89,7 +96,7 @@ class InvoiceQueryset(models.QuerySet): return self.filter(status=APPROVED_BY_STAFF) def approved_by_finance_1(self): - return self.filter(status=APPROVED_BY_FINANCE_1) + return self.filter(status=APPROVED_BY_FINANCE) def approved_by_finance_2(self): return self.filter(status=APPROVED_BY_FINANCE_2) @@ -97,20 +104,18 @@ class InvoiceQueryset(models.QuerySet): def waiting_to_convert(self): if settings.INVOICE_EXTENDED_WORKFLOW: return self.filter(status=APPROVED_BY_FINANCE_2) - return self.filter(status=APPROVED_BY_FINANCE_1) + return self.filter(status=APPROVED_BY_FINANCE) def for_finance_1(self): if settings.INVOICE_EXTENDED_WORKFLOW: return self.filter( status__in=[APPROVED_BY_STAFF, CHANGES_REQUESTED_BY_FINANCE_2] ) - return self.filter(status__in=[APPROVED_BY_STAFF, APPROVED_BY_FINANCE_1]) + return self.filter(status__in=[APPROVED_BY_STAFF, APPROVED_BY_FINANCE]) def for_finance_2(self): if settings.INVOICE_EXTENDED_WORKFLOW: - return self.filter( - status__in=[APPROVED_BY_FINANCE_1, APPROVED_BY_FINANCE_2] - ) + return self.filter(status__in=[APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2]) return [] def rejected(self): @@ -231,7 +236,7 @@ class Invoice(models.Model): return True if user.is_apply_staff: - if self.status in {SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_FINANCE_1}: + if self.status in {SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_FINANCE}: return True return False @@ -248,7 +253,7 @@ class Invoice(models.Model): ): return False # Users can't change status - if self.status in {PAID, DECLINED}: + if self.status in {DECLINED}: return False if user.is_contracting: @@ -260,7 +265,7 @@ class Invoice(models.Model): SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, }: return True @@ -269,11 +274,21 @@ class Invoice(models.Model): if self.status in {APPROVED_BY_STAFF, CHANGES_REQUESTED_BY_FINANCE_2}: return True else: - if self.status in {APPROVED_BY_STAFF, APPROVED_BY_FINANCE_1}: + if self.status in { + APPROVED_BY_STAFF, + APPROVED_BY_FINANCE, + PAID, + PAYMENT_FAILED, + }: return True if user.is_finance_level_2: - if self.status in {APPROVED_BY_FINANCE_1, APPROVED_BY_FINANCE_2}: + if self.status in { + APPROVED_BY_FINANCE, + APPROVED_BY_FINANCE_2, + PAID, + PAYMENT_FAILED, + }: return True return False @@ -284,7 +299,7 @@ class Invoice(models.Model): ): return False if user.is_apply_staff: - if self.status in {SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_FINANCE_1}: + if self.status in {SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_FINANCE}: return True if user.is_finance_level_1: if self.status in {APPROVED_BY_STAFF}: @@ -294,7 +309,7 @@ class Invoice(models.Model): ]: return True if user.is_finance_level_2: - if self.status in {APPROVED_BY_FINANCE_1}: + if self.status in {APPROVED_BY_FINANCE}: return True return False diff --git a/hypha/apply/projects/tables.py b/hypha/apply/projects/tables.py index 97e177d3f0f485b8e1e8ba03e67b9eadc376fb05..117d6db83bd59105cf473888bb3029e413ea1be9 100644 --- a/hypha/apply/projects/tables.py +++ b/hypha/apply/projects/tables.py @@ -8,7 +8,7 @@ from .models import Invoice, Project, Report class BaseInvoiceTable(tables.Table): - vendor_document_number = tables.LinkColumn( + invoice_number = tables.LinkColumn( "funds:projects:invoice-detail", verbose_name=_("Invoice Number"), args=[tables.utils.A("project__pk"), tables.utils.A("pk")], diff --git a/hypha/apply/projects/templates/application_projects/invoice_detail.html b/hypha/apply/projects/templates/application_projects/invoice_detail.html index 01ad8ead450eb4d8a513b43a88cfcdd131283149..edf5fa4d9a052b5374f2bf72b3be8b3ddf454b25 100644 --- a/hypha/apply/projects/templates/application_projects/invoice_detail.html +++ b/hypha/apply/projects/templates/application_projects/invoice_detail.html @@ -19,9 +19,8 @@ <div class="card card--solid"> <p class="card__text"><b>{% trans "Invoice number" %}:</b> {{ object.invoice_number }}</p> {% is_vendor_setup request as show_vendor_information %} - {% if show_vendor_information %} - <p class="card__text"><b>{% trans "Vendor" %}:</b> {{ object.project.vendor.name }}</p> - {% endif %} + <p class="card__text"><b>{% trans "Vendor" %}:</b> + {% if show_vendor_information %}{{ object.project.vendor.name }}{% else %}{{ object.project.user }}{% endif %}</p> <p class="card__text"><b>{% trans "Lead" %}:</b> {{ object.project.lead }}</p> <p class="card__text"><b>{% trans "Fund" %}:</b> {{ object.project.submission.page }}</p> </div> @@ -29,18 +28,32 @@ <div class="flex-none"> <p><b>{% trans "Status" %}: </b></p> </div> - <div class="flex-1 pl-2"> + <div class="pl-2"> {% extract_status latest_activity user as latest_activity_status %} - <p>{{ latest_activity_status }} ({{ latest_activity.user }})</p> + {% get_comment_for_invoice_action object latest_activity as latest_activity_comment %} + <p>{{ latest_activity_status }} {% if user.is_applicant and latest_activity.user != user %} ({{ ORG_SHORT_NAME }}){% else %}({{ latest_activity.user }}){% endif %} + <span class="text-gray-400">{{ latest_activity.timestamp }}</span> + {% if latest_activity_comment %} + <svg class="icon icon--request-changes"><use xlink:href="#request-changes"></use></svg> + <a href="{% url 'apply:projects:detail' pk=object.project.id %}#communications#{{ latest_activity_comment.id }}" class="font-bold" target="_blank">View comment</a> + {% endif %} + </p> {% for activity in activities %} {% extract_status activity user as activity_status %} - <p x-show="!collapsed">{{ activity_status }} ({{ activity.user }})</p> + {% get_comment_for_invoice_action object activity as activity_comment %} + <p x-show="!collapsed">{{ activity_status }} {% if user.is_applicant and activity.user != user %} ({{ ORG_SHORT_NAME }}){% else %}({{ activity.user }}){% endif %} + <span class="text-gray-400">{{ activity.timestamp }}</span> + {% if activity_comment %} + <svg class="icon icon--request-changes"><use xlink:href="#request-changes"></use></svg> + <a href="{% url 'apply:projects:detail' pk=object.project.id %}#communications#{{ activity_comment.id }}" class="font-bold" target="_blank">View comment</a> + {% endif %} + </p> {% endfor %} </div> <div class="flex-1 text-right" x-on:click="collapsed = ! collapsed" role="button"> - <p class="font-bold text-light-blue" x-show="collapsed">{% trans "View status history" %} + <p class="font-bold text-light-blue" x-show="collapsed">{% trans "View" %} <svg class="icon icon--arrow-down top-1" aria-hidden=true><use xlink:href="#arrow-down"></use></svg></p> - <p class="font-bold text-light-blue" x-show="!collapsed">{% trans "Hide status history" %} + <p class="font-bold text-light-blue" x-show="!collapsed">{% trans "Hide" %} <svg class="icon icon--arrow-up top-1" aria-hidden=true><use xlink:href="#arrow-up"></use></svg></p> </div> diff --git a/hypha/apply/projects/templates/application_projects/paf_export.html b/hypha/apply/projects/templates/application_projects/paf_export.html index 56a23d796bc9f22f7b993f3ea4c9f153841613b9..c5b3c55c1f0c2749929c88be345b75a76b77f4a9 100644 --- a/hypha/apply/projects/templates/application_projects/paf_export.html +++ b/hypha/apply/projects/templates/application_projects/paf_export.html @@ -39,7 +39,6 @@ .title { text-align: center; font-size: 22px; - font-weight: bold; } p { font-size: 16px; @@ -62,37 +61,35 @@ </head> <body> <div> - {%block page_header%} - <h1 class="title"> {{ org_name }}(Project Approval Form | <a href="{{ project_link }}">{{ title }}</a>)</h1> - {%endblock%} {%block content%} <table id="page-width"> <!-- Project details in table format --> <tr> - <td class="align-left"> - <b>Project Title</b> + <td rowspan="4" align="left" class="align-left"> + <p class="title"> <b>{{ org_name|upper }}</b> PROJECT APPROVAL FORM</p> </td> <td align="right" class="align-right"> - <b>Project ID</b> + <b>Project Title</b> </td> </tr> <tr> - <td> - {{ title }} + <td align="right" class="align-right"> + <a href="{{ project_link }}">{{ title }}</a> </td> + </tr> + <tr> <td align="right" class="align-right"> - {{ id }} + <b>Project ID</b> </td> </tr> - <tr></tr> - <tr> - <td class="align-left"> - <b>Contractor: </b> {{ contractor_name|default:"None" }} + <td align="right" class="align-right"> + {{ id }} </td> </tr> </table> + <br> <!-- PAF fields data in paragraph format--> {% for field_name, field_value in paf_data.items %} <p><b>{{ field_name }}</b></p> 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 3521fa187ab4d0c24aa6bfce82479e435dd02e4a..f2f5eb89c4eabcd7d88b02b16ecc2e7f801231b7 100644 --- a/hypha/apply/projects/templates/application_projects/project_approval_detail.html +++ b/hypha/apply/projects/templates/application_projects/project_approval_detail.html @@ -32,7 +32,7 @@ <div class="wrapper wrapper--large wrapper--tabs"> <div class="wrapper wrapper--sidebar"> <article class="wrapper--sidebar--inner"> - <h4>{% trans "Project Information" %}</h4> + <h4 class="mb-2">{% trans "Project Information" %}</h4> <div class="card card--solid"> {% if object.output_answers %} @@ -49,7 +49,7 @@ {% endif %} </div> - <h4>{% trans "Approvals" %}</h4> + <h4 class="mb-2">{% trans "Approvals" %}</h4> <div class="card card--solid"> {% for approval in project.paf_approvals.all %} {% if approval.approved %} @@ -60,7 +60,7 @@ {% endfor %} </div> - <h4>{% trans "Review" %}</h4> + <h4 class="mb-2">{% trans "Review" %}</h4> <div class="card card--solid"> <p class="font-bold mb-0 mt-0">{% trans "Submission lead" %}</p> <p class="mt-2 mb-0">{{ project.submission.lead }}</p> @@ -87,7 +87,7 @@ {% endfor %} </div> - <h4>{% trans "Supporting Documents" %}</h4> + <h4 class="mb-2">{% trans "Supporting Documents" %}</h4> <div class="card card--solid"> <p><a href="{% url 'apply:submissions:simplified' pk=object.submission_id %}">{% trans "Submission" %}</a></p> {% for packet_file in object.packet_files.all %} @@ -125,7 +125,7 @@ {% if user != project.lead %} <a data-fancybox data-src="#change-assigned-paf-approvers" - class="button button--bottom-space button--primary button--full-width" + class="button button--bottom-space button--white button--full-width" href="#"> {% trans "Change approver" %} </a> diff --git a/hypha/apply/projects/templates/application_projects/project_detail.html b/hypha/apply/projects/templates/application_projects/project_detail.html index e566323670da04ed72d58656db0b457e3ff30ad3..73183c690c009a148a1f0ca314119617d8c659ab 100644 --- a/hypha/apply/projects/templates/application_projects/project_detail.html +++ b/hypha/apply/projects/templates/application_projects/project_detail.html @@ -149,13 +149,15 @@ {% user_next_step_on_project object user request=request as next_step %} {% if next_step %} {% if mobile %} - <a class="js-actions-toggle button button--white button--full-width button--actions">{% trans "Next Step" %}</a> + <a class="js-actions-toggle button button--white button--full-width button--actions">{{ next_step.heading }}</a> {% endif %} <div class="js-actions-sidebar sidebar__inner sidebar__inner--actions {% if mobile %}sidebar__inner--mobile{% endif %}"> - <h5>{% trans "Next Step" %}</h5> - <p>{{ next_step }}</p> + <h5><svg class="icon icon--side-arrow"><use xlink:href="#side-arrow"></use></svg> + {{ next_step.heading }} + </h5> + <p>{{ next_step.text }}</p> {% user_next_step_instructions object user as instructions %} {% if instructions %} <div class="sidebar__inner--actions--instructions"> diff --git a/hypha/apply/projects/templatetags/invoice_tools.py b/hypha/apply/projects/templatetags/invoice_tools.py index 49bdcbb4321379d8aa081046f18dd7c8c17dea49..a808740e74b350fe690ce82fc139e7aeda5c08e1 100644 --- a/hypha/apply/projects/templatetags/invoice_tools.py +++ b/hypha/apply/projects/templatetags/invoice_tools.py @@ -1,7 +1,11 @@ import decimal +from datetime import timedelta from django import template +from django.conf import settings +from django.utils.translation import gettext_lazy as _ +from hypha.apply.activity.models import Activity from hypha.apply.activity.templatetags.activity_tags import display_for from hypha.apply.projects.models.project import ( CLOSING, @@ -92,9 +96,24 @@ def get_invoice_form_id(form, invoice): def extract_status(activity, user): if activity and user: invoice_activity_message = display_for(activity, user) - return invoice_activity_message.replace( + invoice_status = invoice_activity_message.replace( "Updated Invoice status to: ", "" ).replace(".", "") + if " by " not in str(invoice_status) and not user.is_applicant: + if activity.user.is_apply_staff: + user_role = "staff" + elif ( + activity.user.is_finance_level_2 and settings.INVOICE_EXTENDED_WORKFLOW + ): + user_role = "finance2" + elif activity.user.is_finance: + user_role = "finance" + else: + user_role = "vendor" + return _("{status} by {user_role}").format( + status=invoice_status, user_role=user_role + ) + return invoice_status return "" @@ -103,3 +122,16 @@ def display_invoice_status_for_user(user, invoice): if user.is_apply_staff or user.is_contracting or user.is_finance: return invoice.status_display return get_invoice_public_status(invoice_status=invoice.status) + + +@register.simple_tag +def get_comment_for_invoice_action(invoice, action): + if action and invoice: + return Activity.comments.filter( + timestamp__range=( + action.timestamp - timedelta(minutes=1), + action.timestamp + timedelta(minutes=1), + ), + related_content_type__model="invoice", + related_object_id=invoice.id, + ).first() diff --git a/hypha/apply/projects/templatetags/project_tags.py b/hypha/apply/projects/templatetags/project_tags.py index e68e10b293e4bfc529fc799e308782cb20e4f170..5432966ef40093f975ff4610e302678fb5261e42 100644 --- a/hypha/apply/projects/templatetags/project_tags.py +++ b/hypha/apply/projects/templatetags/project_tags.py @@ -32,24 +32,45 @@ 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: - return _("Fill in the Approval Form(PAF)") + return { + "heading": _("To do"), + "text": _("Fill in the Approval Form(PAF)"), + } if project.paf_approvals.exists(): - return _("Resubmit project documents for approval") - return _("Submit project documents for approval") + return { + "heading": _("To do"), + "text": _("Resubmit project documents for approval"), + } + return { + "heading": _("To do"), + "text": _("Submit project documents for approval"), + } elif user.is_applicant: - return _( - "Awaiting project documents to be created and approved by {org_short_name} internally. " - "Please check back when the project has moved to contracting stage." - ).format(org_short_name=settings.ORG_SHORT_NAME) + return { + "heading": _("Waiting for"), + "text": _( + "Awaiting project documents to be created and approved by {org_short_name} internally. " + "Please check back when the project has moved to contracting stage." + ).format(org_short_name=settings.ORG_SHORT_NAME), + } if project.paf_approvals.exists(): - return _("Changes requested. Awaiting documents to be resubmitted.") - return _("Awaiting approval form to be created.") + return { + "heading": _("Waiting for"), + "text": _("Changes requested. Awaiting documents to be resubmitted."), + } + return { + "heading": _("Waiting for"), + "text": _("Awaiting approval form to be created."), + } elif project.status == INTERNAL_APPROVAL: if user.is_applicant: - return _( - "Awaiting project documents to be created and approved by {org_short_name} internally. " - "Please check back when the project has moved to contracting stage." - ).format(org_short_name=settings.ORG_SHORT_NAME) + return { + "heading": _("Waiting for"), + "text": _( + "Awaiting project documents to be created and approved by {org_short_name} internally. " + "Please check back when the project has moved to contracting stage." + ).format(org_short_name=settings.ORG_SHORT_NAME), + } if request: project_settings = ProjectSettings.for_request(request=request) @@ -59,12 +80,20 @@ def user_next_step_on_project(project, user, request=None): ).first() if latest_unapproved_approval: if latest_unapproved_approval.user: - return _("Awaiting approval. Assigned to {approver}").format( - approver=latest_unapproved_approval.user - ) - return _("Awaiting {reviewer_role} to assign an approver").format( - reviewer_role=latest_unapproved_approval.paf_reviewer_role.label - ) + return { + "heading": _("Waiting for"), + "text": _( + "Awaiting approval. Assigned to {approver}" + ).format(approver=latest_unapproved_approval.user), + } + return { + "heading": _("Waiting for"), + "text": _( + "Awaiting {reviewer_role} to assign an approver" + ).format( + reviewer_role=latest_unapproved_approval.paf_reviewer_role.label + ), + } else: matched_roles = PAFReviewersRole.objects.annotate( roles_count=Count("user_roles") @@ -72,57 +101,109 @@ def user_next_step_on_project(project, user, request=None): for group in user.groups.all(): matched_roles = matched_roles.filter(user_roles__id=group.id) if not matched_roles: - return _("Awaiting PAF approval form to be approved") + return { + "heading": _("Waiting for"), + "text": _("Awaiting PAF approval form to be approved"), + } else: matched_unapproved_approval = project.paf_approvals.filter( approved=False, paf_reviewer_role__in=matched_roles ) if not matched_unapproved_approval.exists(): - return _("Awaiting approval from other approvers teams") + return { + "heading": _("Waiting for"), + "text": _("Awaiting approval from other approvers teams"), + } else: if matched_unapproved_approval.first().user: - return _( - "Awaiting approval. Assigned to {approver}" - ).format(approver=matched_unapproved_approval.first().user) - return _( - "Awaiting {reviewer_role} to assign an approver" - ).format( - reviewer_role=matched_unapproved_approval.first().paf_reviewer_role.label - ) - - return _("Awaiting project approval from assigned approvers") + return { + "heading": _("Waiting for"), + "text": _( + "Awaiting approval. Assigned to {approver}" + ).format( + approver=matched_unapproved_approval.first().user + ), + } + return { + "heading": _("Waiting for"), + "text": _( + "Awaiting {reviewer_role} to assign an approver" + ).format( + reviewer_role=matched_unapproved_approval.first().paf_reviewer_role.label + ), + } + + return { + "heading": _("Waiting for"), + "text": _("Awaiting project approval from assigned approvers"), + } elif project.status == CONTRACTING: if not project.contracts.exists(): if user.is_applicant: - return _("Awaiting signed contract from {org_short_name}").format( - org_short_name=settings.ORG_SHORT_NAME - ) - return _("Awaiting signed contract from Contracting team") + return { + "heading": _("Waiting for"), + "text": _("Awaiting signed contract from {org_short_name}").format( + org_short_name=settings.ORG_SHORT_NAME + ), + } + return { + "heading": _("Waiting for"), + "text": _("Awaiting signed contract from Contracting team"), + } else: contract = project.contracts.order_by("-created_at").first() if not contract.signed_by_applicant: if user.is_applicant: - return _("Awaiting contract documents to be submitted by you.") - return _("Awaiting countersigned contract from Vendor") + return { + "heading": _("To do"), + "text": _( + "Awaiting contract documents to be submitted by you." + ), + } + return { + "heading": _("Waiting for"), + "text": _("Awaiting countersigned contract from Vendor"), + } elif not project.submitted_contract_documents: if user.is_applicant: - return _("Awaiting contract documents submission by you") - return _("Awaiting contract documents submission from Vendor") + return { + "heading": _("To do"), + "text": _("Awaiting contract documents submission by you"), + } + return { + "heading": _("Waiting for"), + "text": _("Awaiting contract documents submission from Vendor"), + } else: if user.is_apply_staff: - return _( - "Review the contract for all relevant details and approve." - ) + return { + "heading": _("To do"), + "text": _( + "Review the contract for all relevant details and approve." + ), + } if user.is_applicant: - return _("Awaiting contract approval from {org_short_name}").format( - org_short_name=settings.ORG_SHORT_NAME - ) - return _("Awaiting contract approval from Staff") + return { + "heading": _("Waiting for"), + "text": _( + "Awaiting contract approval from {org_short_name}" + ).format(org_short_name=settings.ORG_SHORT_NAME), + } + return { + "heading": _("Waiting for"), + "text": _("Awaiting contract approval from Staff"), + } elif project.status == INVOICING_AND_REPORTING: if user.is_applicant and not project.invoices.exists(): - return _("Add invoices") + return { + "heading": _("To do"), + "text": _("Add invoices"), + } elif user.is_apply_staff or user.is_finance: - return _("Review invoice and take action") + return { + "heading": _("To do"), + "text": _("Review invoice and take action"), + } return False diff --git a/hypha/apply/projects/tests/test_forms.py b/hypha/apply/projects/tests/test_forms.py index a1d85c869bfa5d29f3ae686f9b63a4d1344977ca..0c9ff6274fb4b2eca005b0d32f9a94ebd5c8f579 100644 --- a/hypha/apply/projects/tests/test_forms.py +++ b/hypha/apply/projects/tests/test_forms.py @@ -28,10 +28,10 @@ from ..forms.project import ( UploadContractForm, ) from ..models.payment import ( - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, APPROVED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2, CHANGES_REQUESTED_BY_STAFF, DECLINED, @@ -176,7 +176,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): expected = set( filter_request_choices( - [CHANGES_REQUESTED_BY_FINANCE_1, APPROVED_BY_FINANCE_1], + [CHANGES_REQUESTED_BY_FINANCE, APPROVED_BY_FINANCE], invoice_status_user_choices(user), ) ) @@ -190,7 +190,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): expected = set( filter_request_choices( - [CHANGES_REQUESTED_BY_FINANCE_1, APPROVED_BY_FINANCE_1], + [CHANGES_REQUESTED_BY_FINANCE, APPROVED_BY_FINANCE], invoice_status_user_choices(user), ) ) @@ -204,7 +204,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): expected = set( filter_request_choices( - [CHANGES_REQUESTED_BY_FINANCE_1, APPROVED_BY_FINANCE_1], + [CHANGES_REQUESTED_BY_FINANCE, APPROVED_BY_FINANCE], invoice_status_user_choices(user), ) ) @@ -212,7 +212,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): self.assertEqual(expected, actual) def test_staff_choices_with_changes_requested_by_finance1_status(self): - invoice = InvoiceFactory(status=CHANGES_REQUESTED_BY_FINANCE_1) + invoice = InvoiceFactory(status=CHANGES_REQUESTED_BY_FINANCE) user = StaffFactory() form = ChangeInvoiceStatusForm(instance=invoice, user=user) @@ -226,7 +226,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): self.assertEqual(expected, actual) def test_finance1_choices_with_changes_requested_by_finance1_status(self): - invoice = InvoiceFactory(status=CHANGES_REQUESTED_BY_FINANCE_1) + invoice = InvoiceFactory(status=CHANGES_REQUESTED_BY_FINANCE) user = FinanceFactory() form = ChangeInvoiceStatusForm(instance=invoice, user=user) @@ -240,7 +240,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): self.assertEqual(expected, actual) def test_finance2_choices_with_changes_requested_by_finance1_status(self): - invoice = InvoiceFactory(status=CHANGES_REQUESTED_BY_FINANCE_1) + invoice = InvoiceFactory(status=CHANGES_REQUESTED_BY_FINANCE) user = Finance2Factory() form = ChangeInvoiceStatusForm(instance=invoice, user=user) @@ -254,7 +254,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): self.assertEqual(expected, actual) def test_staff_choices_with_approved_by_finance1_status(self): - invoice = InvoiceFactory(status=APPROVED_BY_FINANCE_1) + invoice = InvoiceFactory(status=APPROVED_BY_FINANCE) user = StaffFactory() form = ChangeInvoiceStatusForm(instance=invoice, user=user) @@ -269,7 +269,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): @override_settings(INVOICE_EXTENDED_WORKFLOW=True) def test_finance1_choices_with_approved_by_finance1_status_with_extended_flow(self): - invoice = InvoiceFactory(status=APPROVED_BY_FINANCE_1) + invoice = InvoiceFactory(status=APPROVED_BY_FINANCE) user = FinanceFactory() form = ChangeInvoiceStatusForm(instance=invoice, user=user) @@ -284,7 +284,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): @override_settings(INVOICE_EXTENDED_WORKFLOW=False) def test_finance1_choices_with_approved_by_finance1_status(self): - invoice = InvoiceFactory(status=APPROVED_BY_FINANCE_1) + invoice = InvoiceFactory(status=APPROVED_BY_FINANCE) user = FinanceFactory() form = ChangeInvoiceStatusForm(instance=invoice, user=user) @@ -296,7 +296,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): @override_settings(INVOICE_EXTENDED_WORKFLOW=True) def test_finance2_choices_with_approved_by_finance1_status(self): - invoice = InvoiceFactory(status=APPROVED_BY_FINANCE_1) + invoice = InvoiceFactory(status=APPROVED_BY_FINANCE) user = Finance2Factory() form = ChangeInvoiceStatusForm(instance=invoice, user=user) @@ -317,7 +317,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): expected = set( filter_request_choices( - [CHANGES_REQUESTED_BY_FINANCE_1, APPROVED_BY_FINANCE_1], + [CHANGES_REQUESTED_BY_FINANCE, APPROVED_BY_FINANCE], invoice_status_user_choices(user), ) ) @@ -332,7 +332,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): expected = set( filter_request_choices( - [CHANGES_REQUESTED_BY_FINANCE_1, APPROVED_BY_FINANCE_1], + [CHANGES_REQUESTED_BY_FINANCE, APPROVED_BY_FINANCE], invoice_status_user_choices(user), ) ) @@ -347,7 +347,7 @@ class TestChangeInvoiceStatusFormForm(TestCase): expected = set( filter_request_choices( - [CHANGES_REQUESTED_BY_FINANCE_1, APPROVED_BY_FINANCE_1], + [CHANGES_REQUESTED_BY_FINANCE, APPROVED_BY_FINANCE], invoice_status_user_choices(user), ) ) diff --git a/hypha/apply/projects/tests/test_models.py b/hypha/apply/projects/tests/test_models.py index 4a2199decceb0ee01e53d8eeb77f784f486efc31..df84018b99bf8cb73365b5eef5c8d0bad609aca6 100644 --- a/hypha/apply/projects/tests/test_models.py +++ b/hypha/apply/projects/tests/test_models.py @@ -13,10 +13,10 @@ from hypha.apply.users.tests.factories import ( ) from ..models.payment import ( - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, APPROVED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2, CHANGES_REQUESTED_BY_STAFF, DECLINED, @@ -24,6 +24,7 @@ from ..models.payment import ( INVOICE_STATUS_FINANCE_2_CHOICES, INVOICE_STATUS_PM_CHOICES, PAID, + PAYMENT_FAILED, RESUBMITTED, SUBMITTED, Invoice, @@ -162,7 +163,7 @@ class TestInvoiceModel(TestCase): SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, ] user = StaffFactory() for status in statuses: @@ -172,7 +173,7 @@ class TestInvoiceModel(TestCase): def test_staff_cant_change_status(self): statuses = [ APPROVED_BY_STAFF, - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, CHANGES_REQUESTED_BY_FINANCE_2, DECLINED, @@ -193,7 +194,7 @@ class TestInvoiceModel(TestCase): @override_settings(INVOICE_EXTENDED_WORKFLOW=False) def test_finance1_can_change_status(self): - statuses = [APPROVED_BY_STAFF, APPROVED_BY_FINANCE_1] + statuses = [APPROVED_BY_STAFF, APPROVED_BY_FINANCE, PAID, PAYMENT_FAILED] user = FinanceFactory() for status in statuses: invoice = InvoiceFactory(status=status) @@ -202,10 +203,10 @@ class TestInvoiceModel(TestCase): @override_settings(INVOICE_EXTENDED_WORKFLOW=True) def test_finance1_cant_change_status_with_extended_flow(self): statuses = [ - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, CHANGES_REQUESTED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, DECLINED, PAID, RESUBMITTED, @@ -220,9 +221,8 @@ class TestInvoiceModel(TestCase): def test_finance1_cant_change_status(self): statuses = [ CHANGES_REQUESTED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, DECLINED, - PAID, RESUBMITTED, SUBMITTED, ] @@ -233,7 +233,7 @@ class TestInvoiceModel(TestCase): @override_settings(INVOICE_EXTENDED_WORKFLOW=True) def test_finance2_can_change_status_with_extended_flow(self): - statuses = [APPROVED_BY_FINANCE_1, APPROVED_BY_FINANCE_2] + statuses = [APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, PAID, PAYMENT_FAILED] user = Finance2Factory() for status in statuses: invoice = InvoiceFactory(status=status) @@ -243,11 +243,10 @@ class TestInvoiceModel(TestCase): def test_finance2_cant_change_status(self): statuses = [ APPROVED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2, CHANGES_REQUESTED_BY_STAFF, DECLINED, - PAID, RESUBMITTED, SUBMITTED, ] @@ -265,10 +264,10 @@ class TestInvoiceModel(TestCase): def test_applicant_cant_edit_invoice(self): statuses = [ - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, APPROVED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2, DECLINED, PAID, @@ -279,7 +278,7 @@ class TestInvoiceModel(TestCase): self.assertFalse(invoice.can_user_edit(user)) def test_staff_can_edit_invoice(self): - statuses = [SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_FINANCE_1] + statuses = [SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_FINANCE] user = StaffFactory() for status in statuses: invoice = InvoiceFactory(status=status) @@ -287,7 +286,7 @@ class TestInvoiceModel(TestCase): def test_staff_cant_edit_invoice(self): statuses = [ - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, APPROVED_BY_STAFF, CHANGES_REQUESTED_BY_FINANCE_2, @@ -306,8 +305,8 @@ class TestInvoiceModel(TestCase): RESUBMITTED, CHANGES_REQUESTED_BY_STAFF, APPROVED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, - APPROVED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, + APPROVED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2, APPROVED_BY_FINANCE_2, DECLINED, @@ -319,7 +318,7 @@ class TestInvoiceModel(TestCase): self.assertFalse(invoice.can_user_edit_deliverables(user)) def test_staff_can_edit_deliverables(self): - statuses = [SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_FINANCE_1] + statuses = [SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_FINANCE] user = StaffFactory() for status in statuses: invoice = InvoiceFactory(status=status) @@ -327,7 +326,7 @@ class TestInvoiceModel(TestCase): def test_staff_cant_edit_deliverables(self): statuses = [ - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, APPROVED_BY_STAFF, CHANGES_REQUESTED_BY_FINANCE_2, @@ -358,9 +357,9 @@ class TestInvoiceModel(TestCase): def test_finance1_cant_edit_deliverables(self): statuses = [ - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, CHANGES_REQUESTED_BY_STAFF, DECLINED, PAID, @@ -374,7 +373,7 @@ class TestInvoiceModel(TestCase): @override_settings(INVOICE_EXTENDED_WORKFLOW=True) def test_finance2_can_edit_deliverables(self): - statuses = [APPROVED_BY_FINANCE_1] + statuses = [APPROVED_BY_FINANCE] user = Finance2Factory() for status in statuses: invoice = InvoiceFactory(status=status) @@ -385,7 +384,7 @@ class TestInvoiceModel(TestCase): statuses = [ APPROVED_BY_FINANCE_2, APPROVED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2, CHANGES_REQUESTED_BY_STAFF, DECLINED, @@ -412,7 +411,7 @@ class TestInvoiceQueryset(TestCase): def test_in_progress(self): InvoiceFactory(status=SUBMITTED) InvoiceFactory(status=APPROVED_BY_STAFF) - InvoiceFactory(status=CHANGES_REQUESTED_BY_FINANCE_1) + InvoiceFactory(status=CHANGES_REQUESTED_BY_FINANCE) InvoiceFactory(status=DECLINED) self.assertEqual(Invoice.objects.in_progress().count(), 3) @@ -421,7 +420,7 @@ class TestInvoiceQueryset(TestCase): self.assertEqual(Invoice.objects.approved_by_staff().count(), 1) def test_approved_by_finance_1(self): - InvoiceFactory(status=APPROVED_BY_FINANCE_1) + InvoiceFactory(status=APPROVED_BY_FINANCE) self.assertEqual(Invoice.objects.approved_by_finance_1().count(), 1) @override_settings(INVOICE_EXTENDED_WORKFLOW=True) @@ -434,7 +433,7 @@ class TestInvoiceQueryset(TestCase): @override_settings(INVOICE_EXTENDED_WORKFLOW=False) def test_for_finance_1(self): InvoiceFactory(status=APPROVED_BY_STAFF) - InvoiceFactory(status=APPROVED_BY_FINANCE_1) + InvoiceFactory(status=APPROVED_BY_FINANCE) InvoiceFactory(status=SUBMITTED) self.assertEqual(Invoice.objects.for_finance_1().count(), 2) diff --git a/hypha/apply/projects/utils.py b/hypha/apply/projects/utils.py index aab11ecce982351b8a8d95d19e02f7c820fcd069..b367f0ea5a08a754c0c7ac86bab50a1872d216f1 100644 --- a/hypha/apply/projects/utils.py +++ b/hypha/apply/projects/utils.py @@ -3,10 +3,10 @@ from django.utils.translation import gettext_lazy as _ from .models import Deliverable, Project from .models.payment import ( - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2, APPROVED_BY_STAFF, - CHANGES_REQUESTED_BY_FINANCE_1, + CHANGES_REQUESTED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2, CHANGES_REQUESTED_BY_STAFF, DECLINED, @@ -120,13 +120,13 @@ def get_paf_status_display(paf_status): def get_invoice_public_status(invoice_status): if ( invoice_status - in [SUBMITTED, RESUBMITTED, APPROVED_BY_STAFF, CHANGES_REQUESTED_BY_FINANCE_1] + in [SUBMITTED, RESUBMITTED, APPROVED_BY_STAFF, CHANGES_REQUESTED_BY_FINANCE] ) or ( - invoice_status in [APPROVED_BY_FINANCE_1, CHANGES_REQUESTED_BY_FINANCE_2] + invoice_status in [APPROVED_BY_FINANCE, CHANGES_REQUESTED_BY_FINANCE_2] and settings.INVOICE_EXTENDED_WORKFLOW ): - return _("Pending Approval") - if (invoice_status == APPROVED_BY_FINANCE_1) or ( + return _("Pending approval") + if (invoice_status == APPROVED_BY_FINANCE) or ( invoice_status == APPROVED_BY_FINANCE_2 and settings.INVOICE_EXTENDED_WORKFLOW ): return _("Approved") diff --git a/hypha/apply/projects/views/payment.py b/hypha/apply/projects/views/payment.py index a29fda24881ad69ff8d4e81031366335d8fa783b..7a3f934ad16a3f79ee6bc94715454794eb987654 100644 --- a/hypha/apply/projects/views/payment.py +++ b/hypha/apply/projects/views/payment.py @@ -21,7 +21,7 @@ from hypha.apply.utils.views import DelegateableView, DelegatedViewMixin, ViewDi from ..filters import InvoiceListFilter from ..forms import ChangeInvoiceStatusForm, CreateInvoiceForm, EditInvoiceForm from ..models.payment import ( - APPROVED_BY_FINANCE_1, + APPROVED_BY_FINANCE, APPROVED_BY_STAFF, INVOICE_TRANISTION_TO_RESUBMITTED, Invoice, @@ -82,7 +82,7 @@ class ChangeInvoiceStatusView(DelegatedViewMixin, InvoiceAccessMixin, UpdateView ) or ( settings.INVOICE_EXTENDED_WORKFLOW and self.request.user.is_finance_level_1 - and self.object.status == APPROVED_BY_FINANCE_1 + and self.object.status == APPROVED_BY_FINANCE ): messenger( MESSAGES.APPROVE_INVOICE, diff --git a/hypha/apply/templates/forms/includes/field.html b/hypha/apply/templates/forms/includes/field.html index acd6033620c2782680561c0ef95036bd0eed095f..9a943ba70076455d9c4ca29c809c67f43a918ae2 100644 --- a/hypha/apply/templates/forms/includes/field.html +++ b/hypha/apply/templates/forms/includes/field.html @@ -4,7 +4,7 @@ <div class="form__group {{ field.id_for_label }} form__group--{{ widget_type }} {% if widget_type == 'checkbox_input' %} form__group--checkbox{% endif %}{% if widget_type == 'clearable_file_input' or widget_type == 'multi_file_input' or widget_type == 'single_file_field_widget' or widget_type == 'multi_file_field_widget' %} form__group--file{% endif %}{% if field.help_text %} form__group--wrap{% endif %}{% if field.errors %} form__error{% endif %}{% if is_application and field.field.group_number > 1 %} field-group field-group-{{ field.field.group_number }}{% endif %}{% if is_application and field.field.grouper_for %} form-fields-grouper{% endif %}"{% if is_application and field.field.grouper_for %}data-grouper-for="{{ field.field.grouper_for }}" data-toggle-on="{{ field.field.choices.0.0 }}" data-toggle-off="{{ field.field.choices.1.0 }}"{% endif %}{% if is_application and field.field.group_number > 1 %} data-hidden="{% if not show_all_group_fields and not field.field.visible %}true{% else %}false{% endif %}" data-required="{{ field.field.required_when_visible }}"{% endif %}{% if field.field.word_limit %} data-word-limit="{{ field.field.word_limit }}"{% endif %}> {% if widget_type == 'clearable_file_input' or widget_type == 'multi_file_input' or widget_type == 'single_file_field_widget' or widget_type == 'multi_file_field_widget'%} - <span class="form__question">{{ field.label }}</span> + <span class="form__question form__file-label">{{ field.label }}</span> <label for="{{ field.id_for_label }}" class="form__question form__question--{{ field_type }} {{ widget_type }}" {% if field.field.required %}required{% endif %}> <span>{% trans "Upload" %}</span> {% if field.field.required %} diff --git a/hypha/static_src/src/sass/apply/components/_form.scss b/hypha/static_src/src/sass/apply/components/_form.scss index e38d4ee12877447926100b3dc33b317cb85234cf..c29c801be63775f026482e497133a909fb114c2e 100644 --- a/hypha/static_src/src/sass/apply/components/_form.scss +++ b/hypha/static_src/src/sass/apply/components/_form.scss @@ -134,6 +134,10 @@ // stylelint-enable selector-class-pattern } + &__file-label { + padding-bottom: 0.5rem; + } + &__file-list { ul { margin: 0.5rem 0; diff --git a/hypha/static_src/src/sass/apply/components/_icon.scss b/hypha/static_src/src/sass/apply/components/_icon.scss index 5cee7bc02e3f8ba51ba64df2f751e62ae065d656..dcc3730deec2e9d416edeabce3a0da74557c6359 100644 --- a/hypha/static_src/src/sass/apply/components/_icon.scss +++ b/hypha/static_src/src/sass/apply/components/_icon.scss @@ -164,6 +164,13 @@ stroke: $color--light-blue; } + &--side-arrow { + position: relative; + align-self: center; + width: 8px; + height: 30px; + } + &--arrow-up-short-bar { position: relative; align-self: center; diff --git a/hypha/static_src/src/sass/apply/components/_simplified.scss b/hypha/static_src/src/sass/apply/components/_simplified.scss index d68bc7418688638780fad285969b22d7545c0ed4..76161b522d410a8f015abd0c16635fb86ca82260 100644 --- a/hypha/static_src/src/sass/apply/components/_simplified.scss +++ b/hypha/static_src/src/sass/apply/components/_simplified.scss @@ -154,7 +154,12 @@ } &__paf_answers { + section { + margin: 0 0 1.5rem; + } + h4 { + margin: 0; font-size: 16px; } } diff --git a/hypha/templates/includes/sprites.html b/hypha/templates/includes/sprites.html index 159e4efee2464776ff580cd9ec63c7ed11aea06f..1087051313a55f766558e55cf6fc0566bf4f8555 100644 --- a/hypha/templates/includes/sprites.html +++ b/hypha/templates/includes/sprites.html @@ -254,6 +254,11 @@ <path d="M1 8L8 2L15 8" stroke-width="2"/> </svg> + <symbol id="side-arrow" viewBox="0 0 8 12" fill="none" > + <path fill-rule="evenodd" clip-rule="evenodd" d="M8 6L0 0L0 12L8 6Z" fill="#0D7DB0"/> + </symbol> + + <symbol id="wifi" viewBox="0 0 69 42">