Merge pull request #3019 from alphagov/sign-online

Allow online acceptance of the contract/memorandum of understanding
This commit is contained in:
Chris Hill-Scott
2019-06-27 15:26:41 +01:00
committed by GitHub
13 changed files with 553 additions and 49 deletions

View File

@@ -110,3 +110,9 @@
}
}
.conditional-radio-panel {
border-left: 4px solid $border-colour;
margin: -20px 0 0 17px;
padding: 10px 0 0 28px;
}

View File

@@ -1462,3 +1462,68 @@ class GoLiveNotesForm(StripWhitespaceForm):
'Go live notes',
filters=[lambda x: x or None],
)
class AcceptAgreementForm(StripWhitespaceForm):
@classmethod
def from_organisation(cls, org):
if org.agreement_signed_on_behalf_of_name and org.agreement_signed_on_behalf_of_email_address:
who = 'someone-else'
elif org.agreement_signed_version: # only set if user has submitted form previously
who = 'me'
else:
who = None
return cls(
version=org.agreement_signed_version,
who=who,
on_behalf_of_name=org.agreement_signed_on_behalf_of_name,
on_behalf_of_email=org.agreement_signed_on_behalf_of_email_address,
)
version = StringField(
'Which version of the agreement are you accepting?'
)
who = RadioField(
'Who is accepting the agreement?',
choices=(
(
'me',
'Im accepting the agreement',
),
(
'someone-else',
'Im accepting the agreement on behalf of someone else',
),
),
validators=[DataRequired()],
)
on_behalf_of_name = StringField(
'Who are you accepting the agreement on behalf of?'
)
on_behalf_of_email = email_address(
'Whats their email address?',
required=False,
gov_user=False,
)
def __validate_if_nominating(self, field):
if self.who.data == 'someone-else':
if not field.data:
raise ValidationError('Cant be empty')
else:
field.data = ''
validate_on_behalf_of_name = __validate_if_nominating
validate_on_behalf_of_email = __validate_if_nominating
def validate_version(self, field):
try:
float(field.data)
except (TypeError, ValueError):
raise ValidationError("Must be a number")

View File

@@ -1,8 +1,11 @@
from flask import abort, render_template, request, send_file, url_for
from datetime import datetime
from flask import abort, redirect, render_template, request, send_file, url_for
from flask_login import current_user, login_required
from app import current_service
from app.main import main
from app.main.forms import AcceptAgreementForm
from app.main.views.sub_navigation_dictionaries import features_nav
from app.s3_client.s3_mou_client import get_mou
@@ -26,6 +29,50 @@ def service_agreement(service_id):
)
@main.route('/services/<uuid:service_id>/agreement/accept', methods=['GET', 'POST'])
@login_required
def service_accept_agreement(service_id):
if not current_service.organisation:
abort(404)
form = AcceptAgreementForm.from_organisation(current_service.organisation)
if form.validate_on_submit():
current_service.organisation.update(
agreement_signed_version=float(form.version.data),
agreement_signed_on_behalf_of_name=form.on_behalf_of_name.data,
agreement_signed_on_behalf_of_email_address=form.on_behalf_of_email.data,
)
return redirect(url_for('main.service_confirm_agreement', service_id=current_service.id))
return render_template(
'views/agreement/agreement-accept.html',
form=form,
)
@main.route('/services/<uuid:service_id>/agreement/confirm', methods=['GET', 'POST'])
@login_required
def service_confirm_agreement(service_id):
if (
not current_service.organisation
or current_service.organisation.agreement_signed_version is None
):
abort(403)
if request.method == 'POST':
current_service.organisation.update(
agreement_signed=True,
agreement_signed_at=str(datetime.utcnow()),
agreement_signed_by_id=current_user.id,
)
return redirect(url_for('main.request_to_go_live', service_id=current_service.id))
return render_template('views/agreement/agreement-confirm.html')
@main.route('/agreement.pdf')
@login_required
def download_agreement():

View File

@@ -19,6 +19,8 @@ class Organisation(JSONModel):
'agreement_signed_at',
'agreement_signed_by_id',
'agreement_signed_version',
'agreement_signed_on_behalf_of_name',
'agreement_signed_on_behalf_of_email_address',
'domains',
'request_to_go_live_notes',
'count_of_live_services',
@@ -156,6 +158,10 @@ class Organisation(JSONModel):
key=lambda user: user.email_address.lower(),
)
def update(self, **kwargs):
response = organisations_client.update_organisation(self.id, **kwargs)
self.__init__(response)
class Organisations(ModelList):
client = organisations_client.get_organisations

View File

@@ -244,6 +244,8 @@ class HeaderNavigation(Navigation):
'service_add_letter_contact',
'service_add_sms_sender',
'service_agreement',
'service_accept_agreement',
'service_confirm_agreement',
'service_confirm_delete_email_reply_to',
'service_confirm_delete_sms_sender',
'service_dashboard',
@@ -377,6 +379,8 @@ class MainNavigation(Navigation):
'service_add_letter_contact',
'service_add_sms_sender',
'service_agreement',
'service_accept_agreement',
'service_confirm_agreement',
'service_confirm_delete_email_reply_to',
'service_confirm_delete_sms_sender',
'service_edit_email_reply_to',
@@ -765,6 +769,8 @@ class CaseworkNavigation(Navigation):
'service_add_letter_contact',
'service_add_sms_sender',
'service_agreement',
'service_accept_agreement',
'service_confirm_agreement',
'service_confirm_delete_email_reply_to',
'service_confirm_delete_sms_sender',
'service_dashboard',
@@ -1034,6 +1040,8 @@ class OrgNavigation(Navigation):
'service_add_letter_contact',
'service_add_sms_sender',
'service_agreement',
'service_accept_agreement',
'service_confirm_agreement',
'service_confirm_delete_email_reply_to',
'service_confirm_delete_sms_sender',
'service_dashboard',

View File

@@ -1,35 +1,12 @@
<p>
Before you can go live on GOV.UK Notify, your organisation needs to agree to our data sharing and financial agreement.
Before you can go live on GOV.UK Notify, your organisation needs to
accept to our data sharing and financial agreement.
</p>
<h2 class="heading-small">
Crown bodies
</h2>
<p>
Download the <a href="{{ url_for('main.public_download_agreement', variant='crown') }}">crown agreement</a>.
There are different agreements for crown and non-crown organisations.
</p>
<h2 class="heading-small">
Non-crown bodies
</h2>
<p>
Download the <a href="{{ url_for('main.public_download_agreement', variant='non-crown') }}">non-crown agreement</a>.
</p>
<div class="panel panel-border-wide">
<p>
<a href="{{ url_for('main.support') }}">Contact us</a> if youre
not sure whether your organisation is a crown or non-crown body.
</p>
</div>
<h2 class="heading-small">
Next steps
</h2>
<ol class="list list-number">
<li>
Get the agreement signed by someone who has the authority to do so on behalf of {{ owner or 'your organisation' }}.
</li>
<li>
Return the signed copy to <a href="mailto:notify-support@digital.cabinet-office.gov.uk">notify-support@digital.cabinet-office.gov.uk</a>.
</li>
</ol>
<p>
The agreement contains commercially sensitive information, so dont share it more widely than you need to.
<a href="{{ url_for('main.support') }}">Get in touch</a> to tell us
whether or not you work for a crown organisation. If youre not sure
well help you work it out.
</p>

View File

@@ -1,17 +1,17 @@
<p>
Before you can go live on GOV.UK Notify, your organisation needs to agree to our data sharing and financial agreement.
To use GOV.UK Notify your organisation (Cabinet Office) must accept the GOV.UK Notify data sharing and financial agreement.
</p>
<p>
This agreement only needs to be accepted once and will then cover all Notify services from
{{ current_service.organisation.name }}.
</p>
<p>
It needs to be accepted by, or on behalf of someone who can sign contracts for your organisation.
</p>
<p>
<a href="{{ url_for('main.download_agreement') }}">Download a copy of the agreement</a>.
</p>
<ol class="list list-number">
<li>
<a href="{{ url_for('main.download_agreement') }}">Download the agreement</a>.
</li>
<li>
Get it signed by someone who has the authority to do so on behalf of {{ owner }}.
</li>
<li>
Return the signed copy to <a href="mailto:notify-support@digital.cabinet-office.gov.uk">notify-support@digital.cabinet-office.gov.uk</a>.
</li>
</ol>
<p>
The agreement contains commercially sensitive information, so dont share it more widely than you need to.
</p>
<a href="{{ url_for('main.service_accept_agreement', service_id=current_service.id) }}" class="button">Continue</a>

View File

@@ -0,0 +1,42 @@
{% extends "withnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/radios.html" import radio %}
{% from "components/select-input.html" import select_wrapper %}
{% from "components/form.html" import form_wrapper %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/page-header.html" import page_header %}
{% block per_page_title %}
Accept data sharing and financial agreement
{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-five-sixths">
{{ page_header(
'Accept data sharing and financial agreement',
back_link=url_for('main.service_agreement', service_id=current_service.id)
) }}
{% call form_wrapper(class='top-gutter') %}
{{ textbox(form.version, width='1-3', hint='The version number is on the front page, for example 3.6') }}
{% call select_wrapper(form.who) %}
{% for option in form.who %}
{{ radio(option, data_target='on-behalf-of' if option.data == 'someone-else' else None) }}
{% endfor %}
{% endcall %}
<div class="conditional-radio-panel js-hidden" id="on-behalf-of">
{{ textbox(form.on_behalf_of_name, width='1-1') }}
{{ textbox(form.on_behalf_of_email, width='1-1') }}
</div>
{{ page_footer('Continue') }}
{% endcall %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,39 @@
{% extends "withnav_template.html" %}
{% from "components/form.html" import form_wrapper %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/page-header.html" import page_header %}
{% block per_page_title %}
GOV.UK Notify data sharing and financial agreement
{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-five-sixths">
{{ page_header(
'GOV.UK Notify data sharing and financial agreement',
back_link=url_for('main.service_accept_agreement', service_id=current_service.id)
) }}
{% call form_wrapper(class='top-gutter') %}
{% if current_service.organisation.agreement_signed_on_behalf_of_name and current_service.organisation.agreement_signed_on_behalf_of_email_address %}
<p>
I confirm that I have the legal authority to accept the GOV.UK Notify data sharing and financial agreement (version {{ current_service.organisation.agreement_signed_version }}) on behalf of {{ current_service.organisation.agreement_signed_on_behalf_of_name }} ({{ current_service.organisation.agreement_signed_on_behalf_of_email_address }}) and that {{ current_service.organisation.name }} will be bound by it.
</p>
{% else %}
<p>
I confirm that I have the legal authority to accept the GOV.UK Notify data sharing and financial agreement (version {{ current_service.organisation.agreement_signed_version }}) and that {{ current_service.organisation.name }} will be bound by it.
</p>
{% endif %}
{{ page_footer('Accept this agreement') }}
{% endcall %}
</div>
</div>
{% endblock %}

View File

@@ -51,7 +51,7 @@
{% if show_agreement %}
{{ task_list_item(
agreement_signed,
'Sign our data sharing and financial agreement',
'Accept our data sharing and financial agreement',
url_for('main.service_agreement', service_id=current_service.id),
) }}
{% endif %}