diff --git a/hypha/apply/funds/models/mixins.py b/hypha/apply/funds/models/mixins.py index 75766bd0b28c7c993103768739f8f05a1ec5ed73..5ef80ebfa294e4b837075b04a1c3922e9b6893d5 100644 --- a/hypha/apply/funds/models/mixins.py +++ b/hypha/apply/funds/models/mixins.py @@ -267,7 +267,7 @@ class AccessFormData: data = self.data(field_id) # Some migrated content have empty address. if not data: - return '-' + return field.render(context={'data': '', 'include_question': include_question}) return field.render(context={'data': data, 'include_question': include_question}) def render_answers(self): diff --git a/hypha/apply/funds/templates/funds/includes/delegated_form_base.html b/hypha/apply/funds/templates/funds/includes/delegated_form_base.html index 7575378d7293019cd60fb8405f29c66228959a9d..5026428c4cd39549a49c3075fefb10bd40cf11ce 100644 --- a/hypha/apply/funds/templates/funds/includes/delegated_form_base.html +++ b/hypha/apply/funds/templates/funds/includes/delegated_form_base.html @@ -2,7 +2,7 @@ <form class="form {{ extra_classes }}" method="post" - id="{{ form.name }}" + id="{% if form_id %}{{ form_id }}{% else %}{{ form.name }}{% endif %}" enctype="multipart/form-data" {% if action %}action="{{ action }}"{% endif %} > @@ -36,7 +36,7 @@ id="{{ form.name }}-submit" name="{{ form_prefix }}{{ form.name }}" type="submit" - form="{{ form.name }}"> + form="{% if form_id %}{{ form_id }}{% else %}{{ form.name }}{% endif %}"> {{ value }} </button> diff --git a/hypha/apply/projects/models/payment.py b/hypha/apply/projects/models/payment.py index a77b4923b6576a275ea1fb86fba0ad501d986b43..bcafd0f4a961db2661eb40b6573de7acd24ac19c 100644 --- a/hypha/apply/projects/models/payment.py +++ b/hypha/apply/projects/models/payment.py @@ -184,7 +184,7 @@ class Invoice(models.Model): return prefix + '-'.join(wrap(f"{self.id:06}", 3)) def can_user_delete(self, user): - if user.is_applicant or user.is_apply_staff or user.is_finance_level_1 or user.is_finance_level_2 or user.is_contracting: + if user.is_applicant or user.is_apply_staff: if self.status in (SUBMITTED): return True diff --git a/hypha/apply/projects/permissions.py b/hypha/apply/projects/permissions.py index ad5e6973b269456fd494cbee6c061b482c1c5cf6..18b4c69b54cb0f6fd35572bcf1b36f0f6859d461 100644 --- a/hypha/apply/projects/permissions.py +++ b/hypha/apply/projects/permissions.py @@ -2,6 +2,7 @@ from django.core.exceptions import PermissionDenied from .models.project import ( CLOSING, + COMMITTED, COMPLETE, CONTRACTING, IN_PROGRESS, @@ -169,6 +170,10 @@ def can_access_project(user, project): if user.is_applicant and user == project.user: return True, 'Applicant(project user) can view project in all statuses' + if project.status in [COMMITTED, WAITING_FOR_APPROVAL] and project.paf_approvals.exists() and \ + user.id in project.paf_approvals.all().values_list('user', flat=True): + return True, 'PAF Approvers can access the project in Committed and Approval state' + return False, 'Forbidden Error' diff --git a/hypha/apply/projects/templates/application_projects/includes/contracting_documents.html b/hypha/apply/projects/templates/application_projects/includes/contracting_documents.html index 66330783ac11d22bcae4e5c24a917de40d86f95c..2f686c0b58210a404edd35716435b992db3ce436 100644 --- a/hypha/apply/projects/templates/application_projects/includes/contracting_documents.html +++ b/hypha/apply/projects/templates/application_projects/includes/contracting_documents.html @@ -1,9 +1,14 @@ {% load i18n contract_tools project_tags %} {% allow_collapsible_header object header_type='contracting_documents' as collapsible_header %} -<div class="docs-block wrapper--outer-space-large" {% if collapsible_header %} x-data="{ collapsed: false }" {% endif %}> - <div class="docs-block__header" {% if collapsible_header %} x-on:click="collapsed = ! collapsed" {% endif %}> - <div class="docs-block__heading" >{% trans "Contracting documents" %}</div> +<div class="docs-block wrapper--outer-space-large" {% if collapsible_header %} x-data="{ collapsed: true }" {% endif %}> + <div class="docs-block__header" id="contract-documents-section" {% if collapsible_header %} x-on:click="collapsed = ! collapsed" role="button" aria-label="Toggle Contract documents visibility" aria-controls="contract-documents-elements" {% endif %}> + <div class="docs-block__heading" >{% trans "Contracting documents" %} + {% if collapsible_header %} + <svg class="icon icon--caret-up" x-show="!collapsed" aria-hidden=true><use xlink:href="#caret-up"></use></svg> + <svg class="icon icon--caret-down" x-show="collapsed" aria-hidden=true><use xlink:href="#caret-down"></use></svg> + {% endif %} + </div> {% user_can_upload_contract object request.user as can_upload_contract %} {% user_can_submit_contract object user contract as can_submit_contract %} {% if can_submit_contract %} @@ -33,7 +38,8 @@ {% endif %} </div> - <ul class="docs-block__inner" {% if collapsible_header %} x-show="collapsed" {% endif %}> + <ul class="docs-block__inner" id="contract-documents-elements" {% if collapsible_header %} x-show="!collapsed" role="region" + aria-labelledby="contract-documents-section" {% endif %}> {% is_project_contract_approved object as is_contract_approved %} {% contract_uploaded_by_contracting object as contract_uploaded %} @@ -50,7 +56,7 @@ </a> </div> {% if contract.updated_at %} - <ul class="docs-block__document-list" style="margin-top:0;"> + <ul class="mt-0 w-full pl-9"> <li class="docs-block__document"> <div class="docs-block__row-inner"> </div> @@ -61,7 +67,7 @@ </ul> {% endif %} {% if not is_contract_approved %} - <ul class="docs-block__document-list" style="margin-top:0;"> + <ul class="mt-0 w-full pl-9"> <li class="docs-block__document"> <div class="docs-block__row-inner"> </div> @@ -88,7 +94,7 @@ </div> {% if can_upload_contract and user.is_contracting %} <div class="docs-block__row-inner docs-block__row-inner__contract-upload-row"> - <a data-fancybox class="docs-block__icon-link button--project-action" style="padding:5px" href="#" data-src="#upload-contract"> + <a data-fancybox class="font-bold flex items-center w-auto text-left bg-light-blue text-white mr-0 p-2.5 border-none" href="#" data-src="#upload-contract"> <svg class="icon icon--arrow-up-short-bar icon--arrow-up-short-bar--contract-upload"><use xlink:href="#arrow-up-short-bar"></use></svg> {% if not contract %} {% trans "Upload" %} @@ -108,7 +114,7 @@ </div> {% if can_upload_contract and user.is_applicant %} <div class="docs-block__row-inner docs-block__row-inner__contract-upload-row"> - <a data-fancybox class="docs-block__icon-link button--project-action" style="padding:5px" href="#" data-src="#upload-contract"> + <a data-fancybox class="font-bold flex items-center w-auto text-left bg-light-blue text-white mr-0 p-2.5 border-none" href="#" data-src="#upload-contract"> <svg class="icon icon--arrow-up-short-bar icon--arrow-up-short-bar--contract-upload"><use xlink:href="#arrow-up-short-bar"></use></svg> {% if not contract.signed_by_applicant %} {% trans "Upload" %} @@ -134,7 +140,7 @@ {% can_update_contracting_documents object user as can_update_documents %} {% if can_update_documents %} <div class="docs-block__row-inner"> - <a data-fancybox data-src="#upload-contracting-doc" class="docs-block__icon-link" style="margin-right:0;" href="#"> + <a data-fancybox data-src="#upload-contracting-doc" class="font-bold flex items-center mr-0" href="#"> <svg class="icon icon--arrow-up-short-bar"><use xlink:href="#arrow-up-short-bar"></use></svg> {% trans "Upload new" %} </a> @@ -147,7 +153,7 @@ </div> {% endif %} {% if remaining_contract_document_categories %} - <ul class="docs-block__document-list" style="padding-left:2.25rem;"> + <ul class="mt-4 w-full pl-9"> <li class="docs-block__document"> <div class="docs-block__document-inner"> <p> @@ -164,7 +170,7 @@ {% endif %} {% if object.contract_packet_files.exists %} - <ul class="docs-block__document-list" style="padding-left:2.25rem;"> + <ul class="mt-4 w-full pl-9"> {% for document in object.contract_packet_files.all %} <li class="docs-block__document"> <div class="docs-block__document-inner"> @@ -178,7 +184,7 @@ </a> {% if can_update_documents %} <form method="POST" id="{{ remove_contract_document_form.name }}" class="docs-block__icon-link"> - <svg class="icon icon--delete" style="margin-left:0; margin-right:2px"><use xlink:href="#delete"></use></svg> + <svg class="w-4 h-4 inline-block fill-tomato mr-0.5 ml-0"><use xlink:href="#delete"></use></svg> {% csrf_token %} {{ document.get_remove_form }} <input @@ -207,12 +213,13 @@ <div class="modal" id="upload-contract"> {% if user.is_applicant %} <h4 class="modal__project-header-bar">{% trans "Upload Countersigned Contract" %}</h4> + {% trans "Upload" as upload %} {% else %} <h4 class="modal__project-header-bar">{% trans "Upload Signed Contract" %}</h4> + <p><i><b>{% trans "The signed contract will be sent to Applicant once you submit." %}</b></i></p> + <br> + {% trans "Submit" as upload %} {% endif %} - <p><i><b>{% trans "The signed contract will be uploaded once you submit." %}</b></i></p> - <br> - {% trans "Submit" as upload %} {% include 'funds/includes/delegated_form_base.html' with form=contract_form value=upload %} </div> {% endif %} diff --git a/hypha/apply/projects/templates/application_projects/includes/invoices.html b/hypha/apply/projects/templates/application_projects/includes/invoices.html index f3afdfcf265dd1e618544d1d6278d8002bc54cce..e0747d8f57c8c301d028273416defe3fa9c91f83 100644 --- a/hypha/apply/projects/templates/application_projects/includes/invoices.html +++ b/hypha/apply/projects/templates/application_projects/includes/invoices.html @@ -5,7 +5,7 @@ <p class="data-block__title">{% trans "Invoices" %}</p> {% user_can_add_invoices object user as can_add_invoice %} {% if can_add_invoice %} - <a class="button button--project-action" + <a class="button button--project-action button--project-action--white" target="_blank" href="{% url "apply:projects:invoice" pk=object.pk %}"> {% trans "Add Invoice" %} </a> @@ -15,38 +15,58 @@ <table class="data-block__table"> <thead> <tr> - <th class="data-block__table-amount">{% trans "Submitted" %}</th> - <th class="data-block__table-status">{% trans "Invoice Number" %}</th> - <th class="data-block__table-date">{% trans "Status" %}</th> + <th class="data-block__table-date">{% trans "Submitted" %}</th> + <th class="data-block__table-amount">{% trans "Invoice Number" %}</th> + <th class="data-block__table-status">{% trans "Status" %}</th> <th class="data-block__table-update"></th> </tr> </thead> <tbody> {% for invoice in object.invoices.not_rejected %} <tr> - <td><span class="data-block__mobile-label">{% trans "Submitted" %}: </span>{{ invoice.requested_at.date }}</td> - <td><span class="data-block__mobile-label">{% trans "Invoice Number" %}: </span>{{ invoice.vendor_document_number }}</td> - <td><span class="data-block__mobile-label">{% trans "Status" %}: </span>{{ invoice.get_status_display }}</td> - <td style="display:flex; flex-wrap:wrap;"> - <a class="data-block__action-icon-link" href="{{ invoice.get_absolute_url }}"> + <td class="py-4 px-2.5"><span class="data-block__mobile-label">{% trans "Submitted" %}: </span>{{ invoice.requested_at.date }}</td> + <td class="py-4 px-2.5"><span class="data-block__mobile-label">{% trans "Invoice Number" %}: </span>{{ invoice.vendor_document_number }}</td> + <td class="py-4 px-2.5"><span class="data-block__mobile-label">{% trans "Status" %}: </span>{{ invoice.get_status_display }}</td> + <td class="flex flex-wrap justify-end py-4 px-0"> + <a class="data-block__action-icon-link" href="{{ invoice.get_absolute_url }}" target="_blank"> <svg class="icon icon--project-eye"><use xlink:href="#eye"></use></svg> {% trans "View" %} </a> {% can_edit invoice user as user_can_edit_request %} {% if user_can_edit_request %} - <a class="data-block__action-icon-link" href="{% url "apply:projects:invoice-edit" pk=invoice.project.pk invoice_pk=invoice.pk %}"> + <a class="data-block__action-icon-link" target="_blank" href="{% url "apply:projects:invoice-edit" pk=invoice.project.pk invoice_pk=invoice.pk %}"> <svg class="icon icon--project-pen"><use xlink:href="#pen"></use></svg> {% trans "Edit" %} </a> {% endif %} {% can_delete invoice user as user_can_delete_request %} - {% if user_can_delete_request %} - <a class="data-block__action-icon-link data-block__action-icon-link--remove" href="{% url 'apply:projects:invoice-delete' pk=invoice.project.pk invoice_pk=invoice.pk %}"> + {% if user.is_applicant and user_can_delete_request %} + <a class="data-block__action-icon-link data-block__action-icon-link--remove" target="_blank" href="{% url 'apply:projects:invoice-delete' pk=invoice.project.pk invoice_pk=invoice.pk %}"> <svg class="icon icon--delete" style="margin-left:0; margin-right:2px"><use xlink:href="#delete"></use></svg> {% trans "Delete" %} </a> {% endif %} + {% can_change_status invoice user as can_change_invoice_status %} + {% if can_change_invoice_status %} + <a + data-fancybox + data-src="#change-invoice-status-{{ invoice.id }}" + id="update_invoice_status-{{ invoice.id }}" + class="data-block__button button button--primary" + href="#" + > + {% trans "Update Status" %} + </a> + <div class="modal" id="change-invoice-status-{{ invoice.id }}"> + {% get_invoice_form invoice user as invoice_form %} + {% get_invoice_form_id invoice_form invoice as invoice_form_id %} + <h4 class="modal__project-header-bar">{% trans "Update Invoice status" %}</h4> + <p>{% trans "Current status" %}: {{ invoice.get_status_display }}</p> + {% trans "Update Status" as update %} + {% include 'funds/includes/delegated_form_base.html' with form=invoice_form value=update action=invoice.get_absolute_url form_id=invoice_form_id %} + </div> + {% endif %} </td> </tr> {% empty %} @@ -65,19 +85,24 @@ <table class="data-block__table is-hidden js-payment-block-rejected-table"> <thead> <tr> - <th class="data-block__table-amount">{% trans "Submitted" %}</th> + <th class="data-block__table-date">{% trans "Submitted" %}</th> <th class="data-block__table-amount">{% trans "Invoice Number" %}</th> <th class="data-block__table-status">{% trans "Status" %}</th> - <th class="data-block__table-view"></th> + <th class="data-block__table-update"></th> </tr> </thead> <tbody> {% for invoice in object.invoices.rejected %} <tr> - <td><span class="data-block__mobile-label">{% trans "Submitted" %}: </span>{{ invoice.requested_at.date }}</td> - <td><span class="data-block__mobile-label">{% trans "Invoice Number" %}: </span>{{ invoice.vendor_document_number }}</td> - <td><span class="data-block__mobile-label">{% trans "Status" %}: </span>{{ invoice.get_status_display }}</td> - <td><a href="{{ invoice.get_absolute_url }}">{% trans "View" %}</a></td> + <td class="py-4 px-2.5"><span class="data-block__mobile-label">{% trans "Submitted" %}: </span>{{ invoice.requested_at.date }}</td> + <td class="py-4 px-2.5"><span class="data-block__mobile-label">{% trans "Invoice Number" %}: </span>{{ invoice.vendor_document_number }}</td> + <td class="py-4 px-2.5"><span class="data-block__mobile-label">{% trans "Status" %}: </span>{{ invoice.get_status_display }}</td> + <td class="flex justify-end py-4 px-0"> + <a class="data-block__action-icon-link" href="{{ invoice.get_absolute_url }}" target="_blank"> + <svg class="icon icon--project-eye"><use xlink:href="#eye"></use></svg> + {% trans "View" %} + </a> + </td> </tr> {% endfor %} </tbody> diff --git a/hypha/apply/projects/templates/application_projects/includes/reports.html b/hypha/apply/projects/templates/application_projects/includes/reports.html index db46072a26073b97b5a83e545dd769fd9596691a..a7dce495e900a05ea780f4790f32059473aa8ba1 100644 --- a/hypha/apply/projects/templates/application_projects/includes/reports.html +++ b/hypha/apply/projects/templates/application_projects/includes/reports.html @@ -51,10 +51,10 @@ <tbody> {% for report in object.reports.done %} <tr {% if forloop.counter > 8 %}class="is-hidden"{% endif %}> - <td> + <td class="py-4 px-2.5"> <span class="data-block__mobile-label">{% trans "Period End" %}: </span>{{ report.end_date }} </td> - <td> + <td class="py-4 px-2.5"> <span class="data-block__mobile-label">{% trans "Submitted" %}: </span>{{ report.submitted_date|default:"Skipped" }} </td> <td class="data-block__links"> diff --git a/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html b/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html index e756f15b9a5b4e0970ef0d34b7ae7042c45c1f84..e696b7bd9628f13d0c123b3e708eedc09dc65f6c 100644 --- a/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html +++ b/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html @@ -2,9 +2,14 @@ {% user_can_edit_project object request.user as editable %} {% allow_collapsible_header object header_type='project_documents' as collapsible_header %} -<div class="docs-block wrapper--outer-space-large" {% if collapsible_header %} x-data="{ collapsed: false }" {% endif %}> - <div class="docs-block__header" {% if collapsible_header %} x-on:click="collapsed = ! collapsed" {% endif %}> - <div class="docs-block__heading">{% trans "Project documents" %}</div> +<div class="docs-block wrapper--outer-space-large" {% if collapsible_header %} x-data="{ collapsed: true }" {% endif %}> + <div class="docs-block__header" id="project-documents-section" {% if collapsible_header %} x-on:click="collapsed = ! collapsed" role="button" aria-label="Toggle Project documents visibility" aria-controls="project-documents-elements" {% endif %}> + <div class="docs-block__heading">{% trans "Project documents" %} + {% if collapsible_header %} + <svg class="icon icon--caret-up" x-show="!collapsed" aria-hidden=true><use xlink:href="#caret-up"></use></svg> + <svg class="icon icon--caret-down" x-show="collapsed" aria-hidden=true><use xlink:href="#caret-down"></use></svg> + {% endif %} + </div> {% user_can_send_for_approval object user as can_send_to_approve %} {% if can_send_to_approve %} <a data-fancybox @@ -37,7 +42,8 @@ </a> {% endif %} </div> - <ul class="docs-block__inner" {% if collapsible_header %} x-show="collapsed" {% endif %}> + <ul class="docs-block__inner" id="project-documents-elements" {% if collapsible_header %} x-show="!collapsed" role="region" + aria-labelledby="project-documents-section" {% endif %}> <li class="docs-block__row"> <div class="docs-block__row-inner"> @@ -112,7 +118,7 @@ </div> {% has_project_sow_form object as project_sow %} {% if project_sow and object.user_has_updated_details and not user.is_applicant %} - <ul class="docs-block__document-list" style="padding-left:2.25rem;"> + <ul class="mt-4 w-full pl-9"> <li class="docs-block__document"> <div class="docs-block__document-inner"> <p class="docs-block__document-info">{% trans "Scope Of Work (SOW)" %}</p> @@ -138,14 +144,14 @@ </div> {% if user.is_apply_staff %} <div class="docs-block__row-inner"> - <a data-fancybox data-src="#upload-supporting-doc" class="docs-block__icon-link" style="margin-right:0;" href="#"> + <a data-fancybox data-src="#upload-supporting-doc" class="font-bold flex items-center mr-0" href="#"> <svg class="icon icon--arrow-up-short-bar"><use xlink:href="#arrow-up-short-bar"></use></svg> {% trans "Upload new" %} </a> </div> {% endif %} {% if remaining_document_categories %} - <ul class="docs-block__document-list" style="padding-left:2.25rem;"> + <ul class="w-full mt-4 pl-9"> <li class="docs-block__document"> <div class="docs-block__document-inner"> <p> @@ -162,7 +168,7 @@ {% endif %} {% if object.packet_files.exists %} - <ul class="docs-block__document-list" style="padding-left:2.25rem;"> + <ul class="mt-4 w-full pl-9"> {% for document in object.packet_files.all %} <li class="docs-block__document"> <div class="docs-block__document-inner"> @@ -177,7 +183,7 @@ {% user_can_remove_supporting_documents object user as can_remove_supporting_doc %} {% if can_remove_supporting_doc %} <form method="POST" id="{{ remove_document_form.name }}" class="docs-block__icon-link"> - <svg class="icon icon--delete" style="margin-left:0; margin-right:2px"><use xlink:href="#delete"></use></svg> + <svg class="w-4 h-4 inline-block fill-tomato mr-0.5 ml-0"><use xlink:href="#delete"></use></svg> {% csrf_token %} {{ document.get_remove_form }} <input diff --git a/hypha/apply/projects/templates/application_projects/invoice_confirm_delete.html b/hypha/apply/projects/templates/application_projects/invoice_confirm_delete.html index 2ad489d5eef377017ddbc0280f85a65fbd54c77e..1e6722843740a10a723b380b36b894ecce01c5da 100644 --- a/hypha/apply/projects/templates/application_projects/invoice_confirm_delete.html +++ b/hypha/apply/projects/templates/application_projects/invoice_confirm_delete.html @@ -18,7 +18,10 @@ <div class="card card--solid"> <p class="card__text"><b>{% trans "Status" %}:</b> {{ object.get_status_display }}</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 "Invoice Number" %}:</b> {{ object.pk }}</p> </div> diff --git a/hypha/apply/projects/templates/application_projects/invoice_form.html b/hypha/apply/projects/templates/application_projects/invoice_form.html index 1dac38d065300699852f11772759ffcccb3012c5..c5205b0a9f9212ebd1893d2b07c99300a1818120 100644 --- a/hypha/apply/projects/templates/application_projects/invoice_form.html +++ b/hypha/apply/projects/templates/application_projects/invoice_form.html @@ -5,6 +5,9 @@ {% block content %} <div class="admin-bar"> <div class="admin-bar__inner"> + <a class="simplified__projects-link" href="{{ object.project.get_absolute_url }}"> + {% trans "Back to project" %} + </a> <h2 class="heading heading--no-margin">{% if object %}{% trans "Editing" %}{% else %}{% trans "Create" %}{% endif %} {% trans "Invoice" %}</h2> <h5 class="heading heading--no-margin">{% if object %}{{ object.project.title }}{% else %}{% trans "For" %}: {{ project.title }}{% endif %}</h5> </div> @@ -25,7 +28,9 @@ {{ field }} {% endif %} {% endfor %} - <button class="button button--submit button--top-space button--primary" type="submit" name="save">{% trans "Save" %}</button> + {% for button_name, button_type, button_value in buttons %} + <button class="button button--submit button--top-space button--{{ button_type }}" type="submit" name="{{ button_name }} {% if button_value == delete %}formnovalidate{% endif %}">{{ button_value }}</button> + {% endfor %} </form> </div> </div> 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 9e03f0a26b27535fe6cec8bdcb124922709d6d9a..62e1686702eb0fc1d6105b28ea4d48fcc0e86e73 100644 --- a/hypha/apply/projects/templates/application_projects/project_approval_detail.html +++ b/hypha/apply/projects/templates/application_projects/project_approval_detail.html @@ -1,5 +1,5 @@ {% extends "base-apply.html" %} -{% load i18n static approval_tools %} +{% load i18n static approval_tools project_tags %} {% block title %}{{ object.title }}{% endblock %} @@ -142,8 +142,9 @@ {% endfor %} </div> </article> - {% user_can_update_paf_status object user request=request as user_can_take_actions %} - {% if user_can_take_actions %} + {% user_can_update_paf_status object user request=request as can_update_paf_status %} + {% user_can_take_actions object user as can_take_actions %} + {% if can_take_actions %} <aside class="sidebar sidebar__project"> {% if mobile %} <a class="js-actions-toggle button button--white button--full-width button--actions">{% trans "Actions to take" %}</a> @@ -156,7 +157,7 @@ <a class="button button--bottom-space button--primary button--full-width {% if user_can_approve %} is-disabled {% endif %}" href="{% url 'apply:projects:edit' pk=object.pk %}">{% trans "Edit PAF" %}</a> {% endif %} <div class="dropdown"> - <button class="button button--white dropbtn button--bottom-space button--full-width" type="button" data-dropdown-target="#dropdown-content-download"> + <button class="button button--project-action--paf-download dropbtn button--bottom-space button--full-width" type="button" data-dropdown-target="#dropdown-content-download"> {% trans 'Download Approval Form' %} </button> <div id="dropdown-content-download" class="dropdown-content"> @@ -166,6 +167,7 @@ {% trans 'Download as DOCX' %}</a> </div> </div> + {% if can_update_paf_status %} <a data-fancybox data-src="#change-status" class="button button--primary button--full-width {% if user_can_approve %} is-disabled {% endif %}" href="#">{% trans "Update Status" %}</a> <div class="modal" id="change-status"> <h4 class="modal__project-header-bar">{% trans "Update status" %}</h4> @@ -173,6 +175,7 @@ {% trans "Update Status" as update %} {% include 'funds/includes/delegated_form_base.html' with form=change_paf_status value=update %} </div> + {% endif %} </div> </aside> diff --git a/hypha/apply/projects/templates/application_projects/project_detail.html b/hypha/apply/projects/templates/application_projects/project_detail.html index 486890be9843ff3f0204e2f342c591f53acd5832..cf1134f9bad54e82639d1475d0398c83404ac704 100644 --- a/hypha/apply/projects/templates/application_projects/project_detail.html +++ b/hypha/apply/projects/templates/application_projects/project_detail.html @@ -185,9 +185,9 @@ <h5>{% trans "PAF Approvals" %}</h5> {% for paf_approval in object.paf_approvals.all %} {% if paf_approval.approved %} - <p class="sidebar__paf-approvals--approved">{% trans "Approved by " %}{{ paf_approval.user }} {% if paf_approval.approved_at %}(<i>{{ paf_approval.approved_at|date }}</i>){% endif %}</p> + <p class="sidebar__paf-approvals--approved m-0">{% trans "Approved by " %}{{ paf_approval.user }} {% if paf_approval.approved_at %}(<i>{{ paf_approval.approved_at|date }}</i>){% endif %}</p> {% else %} - <p class="sidebar__paf-approvals--pending">{% trans "Pending approval from " %}{{ paf_approval.user }}</p> + <p class="sidebar__paf-approvals--pending m-0">{% trans "Pending approval from " %}{{ paf_approval.user }}</p> {% endif %} <br> {% endfor %} diff --git a/hypha/apply/projects/templatetags/invoice_tools.py b/hypha/apply/projects/templatetags/invoice_tools.py index 11552a1b8bc22dc8705138b1d1094adbfc2e9fe9..f860dca13b8f9aa7d0d796c3f55a293f09f99e33 100644 --- a/hypha/apply/projects/templatetags/invoice_tools.py +++ b/hypha/apply/projects/templatetags/invoice_tools.py @@ -66,3 +66,18 @@ def user_can_add_invoices(project, user): def is_vendor_setup(request): project_settings = ProjectSettings.for_request(request) return project_settings.vendor_setup_required + + +@register.simple_tag +def get_invoice_form(invoice, user): + from hypha.apply.projects.views.payment import ChangeInvoiceStatusForm + form = ChangeInvoiceStatusForm(instance=invoice, user=user) + if form: + form.name = "change_invoice_status" + return form + return None + + +@register.simple_tag +def get_invoice_form_id(form, invoice): + return f'{form.name}-{invoice.id}' diff --git a/hypha/apply/projects/templatetags/project_tags.py b/hypha/apply/projects/templatetags/project_tags.py index 40666c296bcdef6513b7994907888748b104fd0a..341dbd8f93ebb3675d44f876731da8098cf45fca 100644 --- a/hypha/apply/projects/templatetags/project_tags.py +++ b/hypha/apply/projects/templatetags/project_tags.py @@ -27,7 +27,11 @@ def user_next_step_on_project(project, user): if user.is_apply_staff: if not project.user_has_updated_details: return "Fill in the Approval Form(PAF)" + if project.paf_approvals.exists(): + return "Resubmit project documents for approval" return "Submit project documents for approval" + if project.paf_approvals.exists(): + return "Changes requested. Awaiting documents to be resubmitted." return "Awaiting approval form to be created." elif project.status == WAITING_FOR_APPROVAL: if user.id in project.paf_approvals.values_list('user', flat=True): @@ -53,18 +57,21 @@ def user_next_step_on_project(project, user): elif project.status == IN_PROGRESS: if user.is_applicant: return "Add invoices" - elif user.is_apply_staff: + elif user.is_apply_staff or user.is_finance: return "Review invoice and take action" return False @register.simple_tag def user_next_step_instructions(project, user): + """ + To provide instructions incase next step is not enough like 'contracting documents submitted by an applicant' + """ if project.status == CONTRACTING and user == project.user and project.contracts.exists(): contract = project.contracts.order_by('-created_at').first() if contract and not contract.signed_by_applicant: return ['Please download the signed contract uploaded by contracting team', - 'Counter Sign', + 'Countersign', 'Upload it back', 'Please also make sure to upload other required contracting documents'] return False @@ -147,3 +154,15 @@ def user_can_remove_supporting_documents(project, user): if user.is_apply_staff and project.status == COMMITTED: return True return False + + +@register.simple_tag +def user_can_take_actions(project, user): + """ + Checking permissions for 'Action to take' section on paf approval details page. + """ + if user.is_apply_staff or user.is_contracting: + return True + if user.id in project.paf_approvals.values_list('user', flat=True): + return True + return False diff --git a/hypha/apply/projects/views/payment.py b/hypha/apply/projects/views/payment.py index d613b0313e6e5a68b1a9a0722a08a72cd50713b4..ac1c48bcb43574f5e0d9730e11ac8f07d7d1bd8c 100644 --- a/hypha/apply/projects/views/payment.py +++ b/hypha/apply/projects/views/payment.py @@ -55,6 +55,7 @@ class InvoiceAccessMixin(UserPassesTestMixin): class ChangeInvoiceStatusView(DelegatedViewMixin, InvoiceAccessMixin, UpdateView): form_class = ChangeInvoiceStatusForm context_name = 'change_invoice_status' + model = Invoice def form_valid(self, form): response = super().form_valid(form) @@ -99,6 +100,10 @@ class ChangeInvoiceStatusView(DelegatedViewMixin, InvoiceAccessMixin, UpdateView class DeleteInvoiceView(DeleteView): model = Invoice + def get_object(self): + project = get_object_or_404(Project, pk=self.kwargs['pk']) + return get_object_or_404(project.invoices.all(), pk=self.kwargs['invoice_pk']) + def dispatch(self, request, *args, **kwargs): self.object = self.get_object() if not self.object.can_user_delete(request.user): @@ -160,8 +165,11 @@ class CreateInvoiceView(CreateView): return redirect(self.project) return super().dispatch(request, *args, **kwargs) + def buttons(self): + yield ('submit', 'primary', _('Save')) + def get_context_data(self, **kwargs): - return super().get_context_data(project=self.project, **kwargs) + return super().get_context_data(project=self.project, buttons=self.buttons(), **kwargs) def form_valid(self, form): form.instance.project = self.project @@ -210,6 +218,11 @@ class EditInvoiceView(InvoiceAccessMixin, UpdateView): return redirect(invoice) return super().dispatch(request, *args, **kwargs) + def buttons(self): + yield ('submit', 'primary', _('Save')) + if self.object.can_user_delete(self.request.user): + yield ('delete', 'warning', _('Delete')) + def get_initial(self): initial = super().get_initial() initial["supporting_documents"] = [ @@ -217,6 +230,19 @@ class EditInvoiceView(InvoiceAccessMixin, UpdateView): ] return initial + def get_context_data(self, **kwargs): + return super().get_context_data(buttons=self.buttons(), **kwargs) + + def post(self, request, *args, **kwargs): + self.object = self.get_object() + form = self.get_form() + if 'delete' in form.data: + return redirect('apply:projects:invoice-delete', pk=self.object.project.id, invoice_pk=self.object.id) + if form.is_valid(): + return self.form_valid(form) + else: + return self.form_invalid(form) + def form_valid(self, form): response = super().form_valid(form) if form.cleaned_data: diff --git a/hypha/apply/projects/views/project.py b/hypha/apply/projects/views/project.py index 9cdace4268729abc8b9ab35904a34ec09156df8f..b7a46e1fea218036590e1ac562c5d3246acc1358 100644 --- a/hypha/apply/projects/views/project.py +++ b/hypha/apply/projects/views/project.py @@ -88,6 +88,7 @@ from ..models.project import ( from ..models.report import Report from ..permissions import has_permission from ..tables import InvoiceListTable, ProjectsListTable, ReportListTable +from ..views.payment import ChangeInvoiceStatusView from .report import ReportFrequencyUpdate, ReportingMixin @@ -344,7 +345,7 @@ class ApproveContractView(DelegatedViewMixin, UpdateView): ) messages.success(self.request, _("Contractor documents have been approved." - " You can start receive invoices from applicant now."), + " You can receive invoices from applicant now."), extra_tags=PROJECT_ACTION_MESSAGE_TAG) return response @@ -666,6 +667,7 @@ class AdminProjectDetailView( UploadDocumentView, ChangePAFStatusView, ChangeProjectstatusView, + ChangeInvoiceStatusView, ] model = Project template_name_suffix = '_admin_detail' @@ -759,6 +761,9 @@ class ProjectPrivateMediaView(UserPassesTestMixin, PrivateMediaView): if self.request.user == self.project.user: return True + if self.request.user.id in self.project.paf_approvals.filter(approved=False).values_list('user__id', flat=True): + return True + return False diff --git a/hypha/static_src/src/sass/apply/components/_button.scss b/hypha/static_src/src/sass/apply/components/_button.scss index 391a3b55983207a4fc41fca2b73c819f8e0ac3a1..87c7dc99084a5c17944d41b45d145286b95939b1 100644 --- a/hypha/static_src/src/sass/apply/components/_button.scss +++ b/hypha/static_src/src/sass/apply/components/_button.scss @@ -49,6 +49,22 @@ color: $color--white; } } + + &--paf-download { + @include button($color--white, $color--light-blue); + display: inline-block; + color: $color--light-blue; + border: 1px solid $color--mid-grey; + padding: .5rem 3.3rem; + + &:focus { + color: $color--light-blue; + } + + &:hover { + color: $color--white; + } + } } &--left-space { diff --git a/hypha/static_src/src/sass/apply/components/_data-block.scss b/hypha/static_src/src/sass/apply/components/_data-block.scss index c24c2a761e36ad8561cd585cac1c281b9147619c..5d5e4efff9c1adb1c477d9ae71cc38913e15e1d0 100644 --- a/hypha/static_src/src/sass/apply/components/_data-block.scss +++ b/hypha/static_src/src/sass/apply/components/_data-block.scss @@ -119,42 +119,29 @@ } td { - padding: 0 0 .5rem; word-break: break-word; - - @include media-query(tablet-landscape) { - padding: 1rem; - } - - &:first-child { - padding: 1rem 0 .5rem; - - @include media-query(tablet-landscape) { - padding: 1rem; - } - } } } } &__table-amount { - width: 25%; + width: 12%; min-width: 90px; } &__table-status { min-width: 160px; - width: 25%; + width: 15%; } &__table-date { min-width: 180px; - width: 25%; + width: 15%; } &__table-update { min-width: 160px; - width: 20%; + width: 25%; @include media-query(desktop) { width: 30%; diff --git a/hypha/static_src/src/sass/apply/components/_file-form.scss b/hypha/static_src/src/sass/apply/components/_file-form.scss new file mode 100644 index 0000000000000000000000000000000000000000..acce1c118be87927ec75f44f231806e9e7605690 --- /dev/null +++ b/hypha/static_src/src/sass/apply/components/_file-form.scss @@ -0,0 +1,4 @@ +.dff-uploader .dff-files .dff-delete { + margin: 8px; + color: $color--tomato; +} diff --git a/hypha/static_src/src/sass/apply/components/_icon.scss b/hypha/static_src/src/sass/apply/components/_icon.scss index 512e051c2d1cadf0dd21e9d0369a60c57ddf9c4f..60ec25b5d9bcd6c201bc88a9446ee226502de366 100644 --- a/hypha/static_src/src/sass/apply/components/_icon.scss +++ b/hypha/static_src/src/sass/apply/components/_icon.scss @@ -113,6 +113,7 @@ fill: transparent; width: 16px; height: 16px; + cursor: pointer; } &--project-eye { @@ -125,11 +126,20 @@ &--caret-down { position: relative; - top: .1em; + top: .4em; align-self: center; width: 1em; height: 1em; - fill: $color--light-blue; + margin-left: 10px; + } + + &--caret-up { + position: relative; + top: .4em; + align-self: center; + width: 1em; + height: 1em; + margin-left: 10px; } &--arrow-up { @@ -145,7 +155,7 @@ position: relative; align-self: center; height: 1.4em; - margin-right: 2px; + margin-right: 3px; fill: $color--light-blue; &--contract-upload { diff --git a/hypha/static_src/src/sass/apply/main.scss b/hypha/static_src/src/sass/apply/main.scss index 116bc23be46857bdddd7941f28982e9c5aa363b1..44f37e26a0ce00f7e1b943d612454d1f5c7d400b 100644 --- a/hypha/static_src/src/sass/apply/main.scss +++ b/hypha/static_src/src/sass/apply/main.scss @@ -72,6 +72,7 @@ @import 'components/activity-notifications'; @import 'components/dropdown'; @import 'components/banner'; +@import 'components/file-form'; // Layout @import 'layout/header'; diff --git a/hypha/templates/includes/sprites.html b/hypha/templates/includes/sprites.html index dca72aaabd29b7556af6818393856a17ecd1b5fa..4c6274b956376562f38283f545a3b3d2109fca2d 100644 --- a/hypha/templates/includes/sprites.html +++ b/hypha/templates/includes/sprites.html @@ -228,8 +228,12 @@ </g> </symbol> - <symbol id="caret-down" viewBox="0 0 16 16"> - <path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/> + <symbol id="caret-down" viewBox="0 0 16 10"> + <path d="M1 1.5L8 8.5L15 1.5" stroke-width="2"/> + </symbol> + + <symbol id="caret-up" viewBox="0 0 16 10"> + <path d="M15 8.5L8 1.5L1 8.5" stroke-width="2"/> </symbol> <symbol id="arrow-up" viewBox="0 0 15 15"> diff --git a/tailwind.config.js b/tailwind.config.js index e87828eaa1eb68a1907f6dab0a22414d5ba3dd75..c022d8117c3af0b1f1b0a2020bb09059c4a5d8ce 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -2,7 +2,12 @@ module.exports = { content: ["./hypha/templates/**/*.html", "./hypha/**/templates/**/*.html"], theme: { - extend: {}, + extend: { + colors: { + 'light-blue' : '#0d7db0', + 'tomato': '#f05e54', + }, + }, }, plugins: [], };