From da971b2da7d2590c63803ce02110af64f4c9b86b Mon Sep 17 00:00:00 2001 From: Katie Smith Date: Mon, 13 May 2019 14:50:40 +0100 Subject: [PATCH] Allow custom notes when service goes live This adds an option on the organisation settings page to add 'request_to_go_live_notes'. When a service belonging to this organisation requests to go live, any go live notes for the organisation will be added to the Zendesk ticket in the 'Agreement signed' section. --- app/main/forms.py | 7 +++ app/main/views/organisations.py | 25 +++++++++ app/models/organisation.py | 15 ++++-- app/navigation.py | 4 ++ .../settings/edit-go-live-notes.html | 25 +++++++++ .../organisation/settings/index.html | 9 ++++ tests/__init__.py | 2 + .../test_organisation_invites.py | 51 ++++++++++++++++++ tests/app/main/views/test_service_settings.py | 54 ++++++++++++++++++- 9 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 app/templates/views/organisations/organisation/settings/edit-go-live-notes.html diff --git a/app/main/forms.py b/app/main/forms.py index 76f3c03ea..c99eeef22 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -1453,3 +1453,10 @@ class ClearCacheForm(StripWhitespaceForm): 'What do you want to clear today', validators=[DataRequired()] ) + + +class GoLiveNotesForm(StripWhitespaceForm): + request_to_go_live_notes = TextAreaField( + 'Go live notes', + filters=[lambda x: x or None], + ) diff --git a/app/main/views/organisations.py b/app/main/views/organisations.py index c59e103ac..e5de746f6 100644 --- a/app/main/views/organisations.py +++ b/app/main/views/organisations.py @@ -17,6 +17,7 @@ from app.main import main from app.main.forms import ( ConfirmPasswordForm, CreateOrUpdateOrganisation, + GoLiveNotesForm, InviteOrgUserForm, OrganisationAgreementSignedForm, OrganisationCrownStatusForm, @@ -464,3 +465,27 @@ def confirm_edit_organisation_name(org_id): 'views/organisations/organisation/settings/edit-name/confirm.html', new_name=session['organisation_name_change'], form=form) + + +@main.route("/organisations//settings/edit-go-live-notes", methods=['GET', 'POST']) +@login_required +@user_has_permissions() +@user_is_platform_admin +def edit_organisation_go_live_notes(org_id): + + form = GoLiveNotesForm() + + if form.validate_on_submit(): + organisations_client.update_organisation( + org_id, + request_to_go_live_notes=form.request_to_go_live_notes.data + ) + return redirect(url_for('.organisation_settings', org_id=org_id)) + + org = organisations_client.get_organisation(org_id) + form.request_to_go_live_notes.data = org['request_to_go_live_notes'] + + return render_template( + 'views/organisations/organisation/settings/edit-go-live-notes.html', + form=form, + ) diff --git a/app/models/organisation.py b/app/models/organisation.py index b3e01eb20..1335a08e1 100644 --- a/app/models/organisation.py +++ b/app/models/organisation.py @@ -18,6 +18,7 @@ class Organisation(JSONModel): 'agreement_signed_by_id', 'agreement_signed_version', 'domains', + 'request_to_go_live_notes', } def __init__(self, _dict): @@ -30,14 +31,13 @@ class Organisation(JSONModel): self.agreement_signed = None self.domains = [] self.organisation_type = None + self.request_to_go_live_notes = None def as_human_readable(self, fallback_domain): - if 'dwp.' in ''.join(self.domains): - return 'DWP - Requires OED approval' if self.agreement_signed: - return 'Yes, on behalf of {}'.format(self.name) + agreement_statement = 'Yes, on behalf of {}.'.format(self.name) elif self.name: - return '{} (organisation is {}, {})'.format( + agreement_statement = '{} (organisation is {}, {}).'.format( { False: 'No', None: 'Can’t tell', @@ -50,7 +50,12 @@ class Organisation(JSONModel): }.get(self.crown), ) else: - return 'Can’t tell (domain is {})'.format(fallback_domain) + agreement_statement = 'Can’t tell (domain is {}).'.format(fallback_domain) + + if self.request_to_go_live_notes: + agreement_statement = agreement_statement + ' ' + self.request_to_go_live_notes + + return agreement_statement def as_info_for_branding_request(self, fallback_domain): return self.name or 'Can’t tell (domain is {})'.format(fallback_domain) diff --git a/app/navigation.py b/app/navigation.py index 972323802..b5f2d82fd 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -166,6 +166,7 @@ class HeaderNavigation(Navigation): 'edit_organisation_domains', 'edit_organisation_email_branding', 'edit_organisation_letter_branding', + 'edit_organisation_go_live_notes', 'edit_organisation_name', 'edit_organisation_type', 'edit_provider', @@ -451,6 +452,7 @@ class MainNavigation(Navigation): 'edit_organisation_crown_status', 'edit_organisation_email_branding', 'edit_organisation_domains', + 'edit_organisation_go_live_notes', 'edit_organisation_letter_branding', 'edit_organisation_name', 'edit_organisation_type', @@ -630,6 +632,7 @@ class CaseworkNavigation(Navigation): 'edit_organisation_crown_status', 'edit_organisation_domains', 'edit_organisation_email_branding', + 'edit_organisation_go_live_notes', 'edit_organisation_letter_branding', 'confirm_edit_organisation_name', 'confirm_edit_user_email', @@ -854,6 +857,7 @@ class OrgNavigation(Navigation): 'edit_organisation_email_branding', 'edit_organisation_letter_branding', 'edit_organisation_domains', + 'edit_organisation_go_live_notes', 'edit_organisation_name', 'edit_organisation_type', 'organisation_preview_email_branding', diff --git a/app/templates/views/organisations/organisation/settings/edit-go-live-notes.html b/app/templates/views/organisations/organisation/settings/edit-go-live-notes.html new file mode 100644 index 000000000..67f4143f8 --- /dev/null +++ b/app/templates/views/organisations/organisation/settings/edit-go-live-notes.html @@ -0,0 +1,25 @@ +{% from "components/form.html" import form_wrapper %} +{% from "components/page-footer.html" import page_footer %} +{% from "components/textbox.html" import textbox %} + +{% extends "org_template.html" %} + +{% block org_page_title %} + Edit request to go live notes +{% endblock %} + +{% block maincolumn_content %} +

Edit request to go live notes

+
+
+

+ Text entered here will be displayed in the Zendesk ticket when a service + belonging to this organisation requests to go live. +

+ {% call form_wrapper() %} + {{ textbox(form.request_to_go_live_notes, width='1-1', rows=3) }} + {{ page_footer('Save') }} + {% endcall %} +
+
+{% endblock %} diff --git a/app/templates/views/organisations/organisation/settings/index.html b/app/templates/views/organisations/organisation/settings/index.html index af2e26786..aa440f700 100644 --- a/app/templates/views/organisations/organisation/settings/index.html +++ b/app/templates/views/organisations/organisation/settings/index.html @@ -79,6 +79,15 @@ ) }} {% endcall %} + {% call row() %} + {{ text_field('Request to go live notes') }} + {{ optional_text_field(current_org.request_to_go_live_notes, default='None') }} + {{ edit_field( + 'Change', + url_for('.edit_organisation_go_live_notes', org_id=current_org.id) + ) + }} + {% endcall %} {% call row() %} {{ text_field('Default email branding') }} {{ text_field(email_branding) }} diff --git a/tests/__init__.py b/tests/__init__.py index c54c5db6d..f76941948 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -195,6 +195,7 @@ def organisation_json( crown=True, agreement_signed=False, organisation_type='', + request_to_go_live_notes=None, ): if users is None: users = [] @@ -215,6 +216,7 @@ def organisation_json( 'agreement_signed_at': None, 'agreement_signed_by': None, 'domains': domains or [], + 'request_to_go_live_notes': request_to_go_live_notes, } diff --git a/tests/app/main/views/organisations/test_organisation_invites.py b/tests/app/main/views/organisations/test_organisation_invites.py index 7f6d80c05..fc113ab48 100644 --- a/tests/app/main/views/organisations/test_organisation_invites.py +++ b/tests/app/main/views/organisations/test_organisation_invites.py @@ -503,6 +503,7 @@ def test_organisation_settings_for_platform_admin( 'Organisation type Not set Change', 'Crown organisation Yes Change', 'Data sharing and financial agreement Not signed Change', + 'Request to go live notes None Change', 'Default email branding GOV.UK Change', 'Default letter branding No branding Change', 'Known email domains None Change', @@ -945,3 +946,53 @@ def test_confirm_update_organisation_with_name_already_in_use( assert response.status_code == 302 assert response.location == url_for('main.edit_organisation_name', org_id=organisation_one['id'], _external=True) + + +def test_get_edit_organisation_go_live_notes_page( + logged_in_platform_admin_client, + mock_get_organisation, + organisation_one, +): + response = logged_in_platform_admin_client.get( + url_for( + '.edit_organisation_go_live_notes', + org_id=organisation_one['id'] + ) + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert page.find('textarea', id='request_to_go_live_notes') + + +@pytest.mark.parametrize('input_note,saved_note', [ + ('Needs permission', 'Needs permission'), + (' ', None) +]) +def test_post_edit_organisation_go_live_notes_updates_go_live_notes( + logged_in_platform_admin_client, + mock_get_organisation, + mock_update_organisation, + organisation_one, + input_note, + saved_note, +): + response = logged_in_platform_admin_client.post( + url_for( + '.edit_organisation_go_live_notes', + org_id=organisation_one['id'], + ), + data={'request_to_go_live_notes': input_note} + ) + + mock_update_organisation.assert_called_once_with( + organisation_one['id'], + request_to_go_live_notes=saved_note + ) + assert response.status_code == 302 + assert response.location == url_for( + '.organisation_settings', + org_id=organisation_one['id'], + _external=True + ) diff --git a/tests/app/main/views/test_service_settings.py b/tests/app/main/views/test_service_settings.py index c9723d509..fc6d33c72 100644 --- a/tests/app/main/views/test_service_settings.py +++ b/tests/app/main/views/test_service_settings.py @@ -1380,7 +1380,7 @@ def test_should_redirect_after_request_to_go_live( '\n' '---\n' 'Organisation type: Central\n' - 'Agreement signed: Can’t tell (domain is user.gov.uk)\n' + 'Agreement signed: Can’t tell (domain is user.gov.uk).\n' '{formatted_displayed_volumes}' 'Consent to research: Yes\n' 'Other live services: No\n' @@ -1405,6 +1405,58 @@ def test_should_redirect_after_request_to_go_live( ) +def test_request_to_go_live_displays_go_live_notes_in_zendesk_ticket( + client_request, + mocker, + active_user_with_permissions, + single_reply_to_email_address, + single_letter_contact_block, + mock_get_organisations_and_services_for_user, + single_sms_sender, + mock_get_service_settings_page_common, + mock_get_service_templates, + mock_get_users_by_service, + mock_update_service, + mock_get_invites_without_manage_permission, +): + go_live_note = 'This service is not allowed to go live' + + mocker.patch( + 'app.organisations_client.get_service_organisation', + side_effect=lambda org_id: organisation_json( + ORGANISATION_ID, + 'Org 1', + request_to_go_live_notes=go_live_note, + ) + ) + mock_post = mocker.patch('app.main.views.service_settings.zendesk_client.create_ticket', autospec=True) + client_request.post( + 'main.request_to_go_live', + service_id=SERVICE_ONE_ID, + _follow_redirects=True + ) + + assert mock_post.call_args[1]['message'] == ( + 'Service: service one\n' + 'http://localhost/services/{service_id}\n' + '\n' + '---\n' + 'Organisation type: Central\n' + 'Agreement signed: No (organisation is Org 1, a crown body). {go_live_note}\n' + 'Emails in next year: 111,111\n' + 'Text messages in next year: 222,222\n' + 'Letters in next year: 333,333\n' + 'Consent to research: Yes\n' + 'Other live services: No\n' + '\n' + '---\n' + 'Request sent by test@user.gov.uk\n' + ).format( + service_id=SERVICE_ONE_ID, + go_live_note=go_live_note + ) + + def test_should_be_able_to_request_to_go_live_with_no_organisation( client_request, mocker,