Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SC-83 Invite evaluators to start evaluation tasks #2017

Merged
merged 2 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions app/controllers/support/cases/email_evaluators_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
module Support
class Cases::EmailEvaluatorsController < Cases::ApplicationController
helper_method :back_to_url_b64

content_security_policy do |policy|
policy.style_src_attr :unsafe_inline
end

before_action :set_current_case
before_action :set_email_addresses
before_action :set_documents
before_action :set_template
before_action { @back_url = support_case_path(@current_case, anchor: "tasklist") }

def edit
@draft = Email::Draft.new(
default_content: default_template,
default_subject:,
template_id: @template_id,
ticket: current_case.to_model,
to_recipients: @to_recipients,
).save_draft!

@support_email_id = @draft.id
@email_evaluators = Email::Draft.find(@support_email_id)
parse_template
end

def update
@email_evaluators = Email::Draft.find(params[:id])
@email_evaluators.attributes = form_params
parse_template
if @email_evaluators.valid?(:new_message)
@email_evaluators.save_draft!
@email_evaluators.deliver_as_new_message

@current_case.update!(sent_email_to_evaluators: true)

redirect_to @back_url
else
render :edit
end
end

def back_to_url_b64
return Base64.encode64(edit_support_case_message_thread_path(case_id: current_case.id, id: params[:id])) if action_name == "submit"

current_url_b64
end

private

def set_current_case
@current_case = Support::Case.find(params[:case_id])
end

def set_email_addresses
@evaluators = @current_case.evaluators.all
@email_addresses = @evaluators.map(&:email)
@to_recipients = @email_addresses.to_json
end

def set_documents
@documents = @current_case.upload_documents
end

def form_params
params.require(:email_evaluators).permit(:html_content)
end

def draft_email_params
params.require(:email_evaluators).permit(:id)
end

def default_subject = "Case #{current_case.ref} - invitation to complete procurement evaluation"

def default_template = render_to_string(partial: "support/cases/email_evaluators/form_template")

def set_template
template = Support::EmailTemplate.find_by(title: "Invitation to complete procurement evaluation")
@template_id = template.id if template
@unique_link = evaluation_verify_evaluators_unique_link_path(@current_case, host: request.host)
end

def parse_template
@email_evaluators.html_content = Support::EmailEvaluatorsVariableParser.new(@current_case, @email_evaluators, @unique_link).parse_template
end
end
end
4 changes: 4 additions & 0 deletions app/models/email/template_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ class Email::TemplateParser
def initialize(variables: {})
@variables = {
"caseworker_full_name" => Current.actor.try(:full_name),
"organisation_name" => "{{organisation_name}}",
"sub_category" => "{{sub_category}}",
"unique_case_specific_link" => "{{unique_case_specific_link}}",
"evaluation_due_date" => "{{evaluation_due_date}}",
threedaymonk marked this conversation as resolved.
Show resolved Hide resolved
}.merge(variables)
end

Expand Down
4 changes: 4 additions & 0 deletions app/models/support/case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,5 +208,9 @@ def assign_to_agent(agent, assigned_by: Current.agent)
update!(agent:)
agent.notify_assigned_to_case(support_case: self, assigned_by:)
end

def sub_category_with_indefinite_article
sub_category.starts_with?("A", "E", "I", "O", "U") ? "an #{sub_category}" : "a #{sub_category}"
end
end
end
24 changes: 24 additions & 0 deletions app/services/support/email_evaluators_variable_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Support
class EmailEvaluatorsVariableParser
include ActionView::Helpers::UrlHelper

def initialize(current_case, email_evaluators, unique_link)
@current_case = current_case
@email_evaluators = email_evaluators
@unique_link = unique_link
end

def variables
{
"organisation_name" => @current_case.organisation_name || @current_case.email,
"sub_category" => @current_case.sub_category_with_indefinite_article || "[sub_category]",
"unique_case_specific_link" => link_to("unique case-specific link", @unique_link, target: "_blank", rel: "noopener noreferrer"),
"evaluation_due_date" => @current_case.evaluation_due_date.strftime("%d %B %Y"),
}
end

def parse_template
Liquid::Template.parse(@email_evaluators.body, error_mode: :strict).render(variables)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="govuk-form-group govuk-!-static-margin-bottom-8">
<h2 class="govuk-heading-m"><%= I18n.t("support.cases.email_evaluators.documents") %></h2>

<dl class="govuk-summary-list">
<% @documents.each do |document| %>
<div class="govuk-summary-list__row">
<dd class="govuk-summary-list__value"><%= link_to document.file_name, support_document_download_path(document, type: document.class), class: "govuk-link", target: "_blank" %></dd>
</div>
<% end %>
</dl>

</div>
12 changes: 12 additions & 0 deletions app/views/support/cases/email_evaluators/_email_list.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="govuk-form-group govuk-!-static-margin-bottom-8">
<h2 class="govuk-heading-m"><%= I18n.t("support.cases.email_evaluators.sharing_with") %></h2>

<dl class="govuk-summary-list">
<% @evaluators.each do |item| %>
<div class="govuk-summary-list__row">
<dd class="govuk-summary-list__value"><%= item.email %></dd>
</div>
<% end %>
</dl>

</div>
45 changes: 45 additions & 0 deletions app/views/support/cases/email_evaluators/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<% unique_id = "A#{SecureRandom.uuid}" %>
<% turbo_frame = "messages-frame" %>
<%= form_with model: @email_evaluators, scope: :email_evaluators, url: support_case_email_evaluators_path(@current_case, id: @support_email_id), method: :patch,
html: {
"data-controller" => "draft-email",
"data-draft-email-list-attachments-endpoint-value" => attachments_messages_path(message_id: @support_email_id),
"data-draft-email-upload-attachments-endpoint-value" => attachments_messages_path(message_id: @support_email_id),
"data-draft-email-remove-attachments-endpoint-value" => attachments_remove_messages_path(message_id: @support_email_id),
"data-draft-email-update-endpoint-value" => messages_path(message_id: @support_email_id, unique_id: unique_id),
"data-draft-email-target" => "form"
} do |form| %>

<%= render "email_list" %>
<%= render "document_list"%>

<h2 class="govuk-heading-m"><%= I18n.t("support.cases.email_evaluators.preview_template") %></h2>
<%= form.govuk_text_area :html_content, label: { text: I18n.t("support.case.label.messages.reply.body"), class: "govuk-!-display-none" }, rows: 5, class: "message-reply-box",
"data-component" => "tinymce",
"data-tinymce-profile" => "basic",
"data-tinymce-selector" => ".message-reply-box",
value: form.object.body
%>

<%= form.hidden_field :to_recipients, value: @to_recipients %>
<%= form.hidden_field :id, value: @support_email_id %>

<div class="govuk-button-group flex-align-center">
<%= form.submit I18n.t("support.cases.email_evaluators.submit"), class: "govuk-button" , "data-prevent-double-click" => true, "data-draft-email-target" => "btnSubmit" %>
<%= link_to I18n.t("generic.button.cancel"), @back_url, class: "govuk-link govuk-link--no-visited-state" %>
</div>

<h2 class="govuk-heading-s govuk-!-margin-bottom-2 govuk-!-display-none"><%= I18n.t("support.emails.attachments.attach_files") %></h2>
<ul class="govuk-list govuk-!-display-none">
<li><%= govuk_link_to I18n.t("support.emails.attachments.email_attachments"), attach_email_attachments_path(message_id: @support_email_id, back_to: back_to_url_b64, turbo_frame: turbo_frame), no_visited_state: true, "data-draft-email-target": "emailAttachmentsLink" %></li>
<li><%= govuk_link_to I18n.t("support.emails.attachments.case_files"), attach_case_files_path(message_id: @support_email_id, back_to: back_to_url_b64, turbo_frame: turbo_frame), no_visited_state: true, "data-draft-email-target": "caseFilesLink" %></li>
<li>
<span class="fake-link" role="link" data-draft-email-target="btnDisplayFileDialog">
<%= I18n.t("support.emails.attachments.files_from_your_computer") %>
</span>
</li>
</ul>

<%= render "components/draft_email_attachments" %>

<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<p>{{organisation_name}} has asked you to participate in the evaluation process for {{sub_category}} procurement. </p>

<p>Follow the steps below to complete this evaluation:</p>

<p><b>1. Complete the declaration of interest form</b></p>
<p>If you do not have a conflict of interest with any of the following suppliers, then sign the attached declaration and reply to this email with the completed declaration as an attachment.</p>

<ul>
<li>[SUPPLIER 1]</li>

<li>[SUPPLIER 2]</li>

<li>[SUPPLIER 3]</li>
</ul>


<p><b>What happens if I identify a conflict of interest?</b></p>

<p>In some situations, it is possible to mitigate a conflict of interest. When this is not possible a new evaluator can be selected. </p>

<p>Reply to this email if you have a conflict of interest so the procurement is not delayed.</p>

<p><b>2. Watch the evaluation training video (10 minutes)</b></p>
The video provides necessary knowledge for scoring and justifying your assessments. Evaluators are required to be appropriately trained to evaluate tenders in a fair, transparent, and compliant manner.  
<br />
<p>You must watch the evaluation training video before starting the evaluation process. The video link is: <a href="https://youtu.be/cbL65QlOas4?si=3fA-vKVPRGn9TEJ_">https://youtu.be/cbL65QlOas4?si=3fA-vKVPRGn9TEJ_</a> </p>
<br />

<p><b>3. Sign in to complete the evaluation</b></p>
<p>Click the {{unique_case_specific_link}} to complete your evaluation with your DfE Sign-in account. If you do not have a DfE Sign-in account, you will need to create one. </p>
<br />

<p><b>4. Complete the evaluation</b></p>
<p>Complete the tasks required by {{evaluation_due_date}} or the procurement could be delayed.</p>

<br />

<p>Help completing the evaluation</p>

<p>Bids must be evaluated using the pre-populated evaluation sheet with scores allocated using the scoring methodology and reasoning shared. Bids must be evaluated independently of each other and other evaluators. Insufficient justification for scores will result in evaluations being returned, which could delay the procurement.</p>

<p>Reply to this email if you have any questions or need help.</p>

<br />

<p><b> 5. Attend the moderation meeting</b></p>

<p>The moderation meeting is scheduled for [ENTER DAY, DATE AND TIME]</p>

<br>

<p> {{caseworker_full_name}} <br>
Procurement Specialist <br>
Get help buying for schools
</p>

<p>Reply to this email within 15 working days or by the date agreed. Late replies can delay the procurement process.</p>

<p>We'll close the case if we don't hear back from you. You can reopen the case at any time by replying to this email.</p>

<br>

<p><b><i>GET HELP BUYING FOR SCHOOLS SERVICE DISCLAIMER</i></b></p>

<p><b><i>Please note that the expertise and advice being provided by the Service shall not in any way affect your obligations to comply with all relevant procurement law and the Department's guidance, including but not limited to the Public Contracts Regulations, Academy Trust Handbook, ESFA Funding Agreement and the Schemes for Financing Local Authority Maintained schools.</i></b></p>

<p><b><i>For the avoidance of doubt, the Department shall bear no liability as a result of using our Service.</i></b></p>
14 changes: 14 additions & 0 deletions app/views/support/cases/email_evaluators/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<%= render partial: "support/cases/components/case_header", locals: { current_case: @current_case } %>
<h1 class="govuk-heading-l"><%= I18n.t("support.cases.email_evaluators.header") %></h1>

<p class="govuk-body-m govuk-!-static-margin-bottom-8"><%= I18n.t("support.cases.email_evaluators.hint") %></p>

<p class="govuk-body-m"><%= I18n.t("support.cases.email_evaluators.check_list.header") %></p>

<ul class="govuk-list govuk-list--bullet govuk-!-static-margin-bottom-8">
<li><%= I18n.t("support.cases.email_evaluators.check_list.item.email_addresses") %></li>
<li><%= I18n.t("support.cases.email_evaluators.check_list.item.documents_available") %></li>
<li><%= I18n.t("support.cases.email_evaluators.check_list.item.email_template") %></li>
</ul>

<%= render "form" %>
12 changes: 10 additions & 2 deletions app/views/support/cases/show/_tasklist.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,17 @@
end

task_list.with_item(title: I18n.t("support.case.label.tasklist.item.upload_documents"), href: edit_support_case_document_uploads_path(@current_case), status: document_upload_Status)
task_list.with_item(title: I18n.t("support.case.label.tasklist.item.email_evaluators")) do | item |
item.with_status(text: I18n.t("support.case.label.tasklist.status.cannot_start"), cannot_start_yet: true)

if (@current_case.evaluators.any? && @current_case.evaluation_due_date && @current_case.has_uploaded_documents && @current_case.sent_email_to_evaluators)
task_list.with_item(title: I18n.t("support.case.label.tasklist.item.email_evaluators"), href: edit_support_case_email_evaluators_path(@current_case), status: govuk_tag(text: I18n.t("support.case.label.tasklist.status.complete"), colour: "green"))
elsif (@current_case.evaluators.any? && @current_case.evaluation_due_date && @current_case.has_uploaded_documents && @current_case.sent_email_to_evaluators == false)
task_list.with_item(title: I18n.t("support.case.label.tasklist.item.email_evaluators"), href: edit_support_case_email_evaluators_path(@current_case), status: govuk_tag(text: I18n.t("support.case.label.tasklist.status.to_do")))
else
task_list.with_item(title: I18n.t("support.case.label.tasklist.item.email_evaluators")) do | item |
item.with_status(text: I18n.t("support.case.label.tasklist.status.cannot_start"), cannot_start_yet: true)
end
end

task_list.with_item(title: I18n.t("support.case.label.tasklist.item.review_evaluations")) do | item |
item.with_status(text: I18n.t("support.case.label.tasklist.status.cannot_start"), cannot_start_yet: true)
end
Expand Down
13 changes: 13 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1795,6 +1795,19 @@ en:
destroyed: "%{name} successfully removed"
delete_confirmation: Are you sure you want to delete %{name}?
is_document_uploaded: Please select uploaded option
email_evaluators:
header: Email evaluators
hint: Check and then send the emails to notify people that the files have been shared. Files will only be accessible to email addresses added.
check_list:
header: "You should check the:"
item:
email_addresses: email addresses
documents_available: documents available to download
email_template: email template
sharing_with: Sharing with
documents: Documents
preview_template: Preview template
submit: Send email and continue
emails:
attachments:
attach_files: Attach files
Expand Down
6 changes: 6 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@
resources :evaluators, except: %i[show]
resource :evaluation_due_dates, only: %i[edit update]
resource :document_uploads, except: %i[show]
resource :email_evaluators, except: %i[show]
resource :email, only: %i[create] do
scope module: :emails do
resources :content, only: %i[show], param: :template
Expand Down Expand Up @@ -336,6 +337,11 @@
end
end

namespace :evaluation do
resources :tasks, only: %i[show edit]
get "verify/evaluator/link/:id", to: "tasks#edit", as: :verify_evaluators_unique_link
edwin-jebaraj marked this conversation as resolved.
Show resolved Hide resolved
end

# E&O Portal
namespace :engagement do
root to: "cases#index"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddSentEmailToEvaluatorsToCase < ActiveRecord::Migration[7.2]
def change
add_column :support_cases, :sent_email_to_evaluators, :boolean, default: false
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.2].define(version: 2024_12_09_104836) do
ActiveRecord::Schema[7.2].define(version: 2024_12_11_171744) do
create_sequence "evaluation_refs"
create_sequence "framework_refs"

Expand Down Expand Up @@ -637,6 +637,7 @@
t.boolean "is_evaluator", default: false
t.date "evaluation_due_date"
t.boolean "has_uploaded_documents"
t.boolean "sent_email_to_evaluators", default: false
t.index ["category_id"], name: "index_support_cases_on_category_id"
t.index ["existing_contract_id"], name: "index_support_cases_on_existing_contract_id"
t.index ["new_contract_id"], name: "index_support_cases_on_new_contract_id"
Expand Down
Loading
Loading