diff --git a/app/controllers/publish/courses/ratifying_provider_controller.rb b/app/controllers/publish/courses/ratifying_provider_controller.rb
new file mode 100644
index 0000000000..8bb974586b
--- /dev/null
+++ b/app/controllers/publish/courses/ratifying_provider_controller.rb
@@ -0,0 +1,172 @@
+# frozen_string_literal: true
+
+module Publish
+ module Courses
+ class RatifyingProviderController < PublishController
+ include CourseBasicDetailConcern
+ before_action :build_course, only: %i[edit update]
+ before_action :build_course_params, only: :continue
+ helper_method :accredited_partners
+
+ def accredited_partners
+ if Settings.features.provider_partnerships
+ @provider.accredited_partners
+ else
+ @provider.accredited_bodies
+ end
+ end
+
+ def show
+ @course = build_course&.decorate
+ render_not_found if @course.accrediting_provider.blank?
+ end
+
+ def edit
+ build_provider
+ end
+
+ def continue
+ authorize(@provider, :can_create_course?)
+
+ code = course_params[:accredited_provider_code]
+ query = @accredited_provider
+
+ @errors = errors_for_search_query(code, query)
+
+ if @errors.present?
+ render :new
+ elsif other_selected_with_no_autocompleted_code?(code)
+ redirect_to(
+ search_new_publish_provider_recruitment_cycle_courses_accredited_provider_path(
+ query: @accredited_provider,
+ course: course_params
+ )
+ )
+ else
+ params[:course][:accredited_provider_code] = @autocompleted_provider_code if @autocompleted_provider_code.present?
+ super
+ end
+ end
+
+ def search_new
+ authorize(provider, :can_create_course?)
+
+ # These are not before_action hooks as they conflict with hooks
+ # defined within the CourseBasicDetailConcern and cannot be overridden
+ # without causing failures in other routes in this controller
+ build_new_course
+ build_provider
+ build_previous_course_creation_params
+ @query = params[:query]
+ @provider_suggestions = recruitment_cycle.providers.with_findable_courses.provider_search(@query).limit(10)
+ end
+
+ def update
+ build_provider
+ begin
+ code = update_course_params[:accredited_provider_code]
+ query = update_course_params[:accredited_provider]
+ rescue ActionController::ParameterMissing
+ @errors = errors_for_search_query(code, query)
+ return render :edit if @errors.present?
+ end
+
+ if update_params[:accredited_provider_code] == 'other'
+ redirect_to_provider_search
+ elsif @course.update(update_params)
+ course_updated_message('Accredited provider')
+ redirect_to_update_successful
+ else
+ @errors = @course.errors.messages
+ render :edit
+ end
+ end
+
+ def search
+ build_course
+ @query = params[:query]
+ @provider_suggestions = recruitment_cycle.providers.with_findable_courses.provider_search(@query).limit(10)
+ end
+
+ private
+
+ def build_provider
+ @provider = RecruitmentCycle.find_by(year: params[:recruitment_cycle_year])
+ .providers
+ .find_by(provider_code: params[:provider_code])
+ end
+
+ def error_keys
+ [:accredited_provider_code]
+ end
+
+ def redirect_to_provider_search
+ redirect_to(
+ accredited_provider_search_provider_recruitment_cycle_course_path(
+ @course.provider_code,
+ @course.recruitment_cycle_year,
+ @course.course_code,
+ query: update_course_params[:accredited_provider]
+ )
+ )
+ end
+
+ def redirect_to_update_successful
+ redirect_to(
+ details_publish_provider_recruitment_cycle_course_path(
+ @course.provider_code,
+ @course.recruitment_cycle_year,
+ @course.course_code
+ )
+ )
+ end
+
+ def current_step
+ :accredited_provider
+ end
+
+ def build_course_params
+ @accredited_provider = params[:course].delete(:accredited_provider)
+ @autocompleted_provider_code = params[:course].delete(:autocompleted_provider_code)
+ end
+
+ def errors_for_search_query(code, query)
+ errors = {}
+
+ if other_selected_with_no_autocompleted_code?(code) && query.length < 2
+ errors = { accredited_provider: ['Accredited provider search too short, enter 2 or more characters'] }
+ elsif code.blank?
+ errors = { accredited_provider_code: ['Select an accredited provider'] }
+ end
+
+ errors
+ end
+
+ def update_course_params
+ params.require(:course).permit(
+ :autocompleted_provider_code,
+ :accredited_provider_code,
+ :accredited_provider
+ )
+ end
+
+ def update_params
+ autocompleted_code = update_course_params[:autocompleted_provider_code]
+ code = update_course_params[:accredited_provider_code]
+
+ {
+ accredited_provider_code: autocompleted_code.presence || code
+ }
+ end
+
+ def other_selected_with_no_autocompleted_code?(code)
+ code == 'other' && @autocompleted_provider_code.blank?
+ end
+
+ def build_course
+ super
+ authorize @course
+ end
+ end
+ end
+end
diff --git a/app/views/publish/courses/ratifying_provider/_provider_search_field.html.erb b/app/views/publish/courses/ratifying_provider/_provider_search_field.html.erb
new file mode 100644
index 0000000000..16d4443104
--- /dev/null
+++ b/app/views/publish/courses/ratifying_provider/_provider_search_field.html.erb
@@ -0,0 +1,11 @@
+<%= render "publish/shared/error_wrapper", error_keys: [:accredited_provider], data_qa: "course__provider_search" do %>
+ <%= form.label :accredited_provider,
+ for: "course_accredited_provider",
+ class: "govuk-label" do %>
+ Enter the provider name or code <%= render "publish/shared/error_messages", error_keys: [:accredited_provider] %>
+ <% end %>
+ <%= form.text_field :accredited_provider,
+ autocomplete: "off",
+ class: "govuk-input govuk-!-width-two-thirds" %>
+
+<% end %>
diff --git a/app/views/publish/courses/ratifying_provider/_provider_suggestion.html.erb b/app/views/publish/courses/ratifying_provider/_provider_suggestion.html.erb
new file mode 100644
index 0000000000..c8d347af03
--- /dev/null
+++ b/app/views/publish/courses/ratifying_provider/_provider_suggestion.html.erb
@@ -0,0 +1,10 @@
+
+ <%= form.radio_button :accredited_provider_code,
+ provider_suggestion[:provider_code],
+ checked: provider_suggestion[:provider_code] == @course.accrediting_provider&.provider_code,
+ class: "govuk-radios__input" %>
+ <%= form.label :accredited_provider_code,
+ provider_suggestion[:provider_name],
+ value: provider_suggestion[:provider_code],
+ class: "govuk-label govuk-radios__label" %>
+
diff --git a/app/views/publish/courses/ratifying_provider/edit.html.erb b/app/views/publish/courses/ratifying_provider/edit.html.erb
new file mode 100644
index 0000000000..f7a7c807a0
--- /dev/null
+++ b/app/views/publish/courses/ratifying_provider/edit.html.erb
@@ -0,0 +1,43 @@
+<% content_for :page_title, title_with_error_prefix("Ratifying provider – #{course.name_and_code}", course.errors.any?) %>
+
+<% content_for :before_content do %>
+ <%= govuk_back_link_to(details_publish_provider_recruitment_cycle_course_path(course.provider_code, course.recruitment_cycle_year, course.course_code)) %>
+<% end %>
+
+<%= render "publish/shared/errors" %>
+
+
diff --git a/app/views/publish/courses/ratifying_provider/new.html.erb b/app/views/publish/courses/ratifying_provider/new.html.erb
new file mode 100644
index 0000000000..163b73fca3
--- /dev/null
+++ b/app/views/publish/courses/ratifying_provider/new.html.erb
@@ -0,0 +1,39 @@
+<% content_for :page_title, title_with_error_prefix("Accredited provider – #{course.name_and_code}", @errors && @errors.any?) %>
+
+<% content_for :before_content do %>
+ <%= govuk_back_link_to(@back_link_path) %>
+<% end %>
+
+<%= render "publish/shared/errors" %>
+
+
+ <%= form_with url: continue_publish_provider_recruitment_cycle_courses_accredited_provider_path(@provider.provider_code, @provider.recruitment_cycle_year), method: :get do |form| %>
+ <%= render "publish/courses/new_fields_holder", form:, except_keys: [:accredited_provider_code] do |fields| %>
+ <%= render "publish/shared/error_wrapper", error_keys: [:accredited_provider_code], data_qa: "course__accredited_provider" do %>
+
+
+ <% end %>
+
+
diff --git a/app/views/publish/courses/ratifying_provider/search.html.erb b/app/views/publish/courses/ratifying_provider/search.html.erb
new file mode 100644
index 0000000000..4e3c6e27b9
--- /dev/null
+++ b/app/views/publish/courses/ratifying_provider/search.html.erb
@@ -0,0 +1,57 @@
+<% content_for :page_title, title_with_error_prefix("Organisation search", course.errors.any?) %>
+
+<% content_for :before_content do %>
+ <%= govuk_back_link_to(details_provider_recruitment_cycle_course_path(course.provider_code, course.recruitment_cycle_year, course.course_code)) %>
+<% end %>
+
+<%= render "publish/shared/errors" %>
+
+
+
+
+ Pick an accredited provider
+
+
+
You searched for ‘<%= @query %>’.
+
+ <%= form_with model: course,
+ url: accredited_provider_provider_recruitment_cycle_course_path(params[:provider_code], params[:recruitment_cycle_year], params[:code]),
+ method: :put do |form| %>
+
+ <% if @provider_suggestions.any? %>
+
We found these providers which matched your search:
+
+
+ <%= render partial: "provider_suggestion", collection: @provider_suggestions, locals: { form: } %>
+
or
+
+
+ <%= form.radio_button :accredited_provider_code,
+ "other",
+ class: "govuk-radios__input",
+ data: { "aria-controls" => "other-container" } %>
+ <%= form.label :accredited_provider_code,
+ "Try another accrediting provider",
+ for: "course_accredited_provider_code_other",
+ value: false,
+ class: "govuk-label govuk-radios__label" %>
+
+
+
+ <%= render "provider_search_field", form: %>
+
+
+ <% else %>
+
We did not find any accredited bodies which matched your search.
+
+
Try another search:
+
+ <%= form.hidden_field :accredited_provider_code, value: :other %>
+ <%= render "provider_search_field", form: %>
+ <% end %>
+
+ <%= form.submit course.is_running? ? "Save and publish changes" : "Save",
+ class: "govuk-button govuk-!-margin-top-3", data: { qa: "course__save" } %>
+ <% end %>
+
+
diff --git a/app/views/publish/courses/ratifying_provider/search_new.html.erb b/app/views/publish/courses/ratifying_provider/search_new.html.erb
new file mode 100644
index 0000000000..1d11bc2dc2
--- /dev/null
+++ b/app/views/publish/courses/ratifying_provider/search_new.html.erb
@@ -0,0 +1,61 @@
+<% content_for :page_title, title_with_error_prefix("Organisation search", course.errors.any?) %>
+
+<%= render "publish/shared/errors" %>
+
+
+
+
+ Pick an accredited provider
+
+
+
You searched for ‘<%= @query %>’.
+
+ <%= form_with model: course,
+ url: continue_publish_provider_recruitment_cycle_courses_accredited_provider_path(
+ @provider.provider_code,
+ @provider.recruitment_cycle_year
+ ),
+ method: :get do |form| %>
+
+ <%= render "publish/shared/course_creation_hidden_fields",
+ form:,
+ course_creation_params: @course_creation_params,
+ except_keys: [:accredited_provider_code] %>
+
+ <% if @provider_suggestions.any? %>
+
We found these providers which matched your search:
+
+
+ <%= render partial: "provider_suggestion", collection: @provider_suggestions, locals: { form: } %>
+
or
+
+
+ <%= form.radio_button :accredited_provider_code,
+ "other",
+ class: "govuk-radios__input",
+ data: { "aria-controls" => "other-container" } %>
+ <%= form.label :accredited_provider_code,
+ "Try another accrediting provider",
+ for: "course_accredited_provider_code_other",
+ value: false,
+ class: "govuk-label govuk-radios__label" %>
+
+
+
+ <%= render "provider_search_field", form: %>
+
+
+ <% else %>
+
We did not find any accredited bodies which matched your search.
+
+
Try another search:
+
+ <%= form.hidden_field :accredited_provider_code, value: :other %>
+ <%= render "provider_search_field", form: %>
+ <% end %>
+
+ <%= form.submit course.is_running? ? "Save and publish changes" : "Save",
+ class: "govuk-button govuk-!-margin-top-3", data: { qa: "course__save" } %>
+ <% end %>
+
+
diff --git a/app/views/publish/courses/ratifying_provider/show.html.erb b/app/views/publish/courses/ratifying_provider/show.html.erb
new file mode 100644
index 0000000000..e10e5ae934
--- /dev/null
+++ b/app/views/publish/courses/ratifying_provider/show.html.erb
@@ -0,0 +1,26 @@
+<%= content_for :page_title do %>
+ <%= t(
+ "publish.providers.courses.accredited_provider.show.heading",
+ provider_name: @course.accrediting_provider
+ ) %>
+<% end %>
+<% content_for :before_content do %>
+ <%= govuk_back_link(
+ href: preview_publish_provider_recruitment_cycle_course_path(
+ @course.provider_code,
+ @course.recruitment_cycle_year,
+ @course.course_code
+ ),
+ text: t(
+ "publish.providers.courses.accredited_provider.show.back",
+ course_name: @course.name,
+ course_code: @course.course_code
+ )
+ ) %>
+<% end %>
+
+
+
+ <%= render partial: "find/courses/about_accrediting_provider", locals: { course: @course } %>
+
+
diff --git a/spec/features/publish/courses/adding_a_ratifying_provider_to_an_unpublished_course_spec.rb b/spec/features/publish/courses/adding_a_ratifying_provider_to_an_unpublished_course_spec.rb
new file mode 100644
index 0000000000..283e2a9968
--- /dev/null
+++ b/spec/features/publish/courses/adding_a_ratifying_provider_to_an_unpublished_course_spec.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+feature 'unpublished course without accredited provider', { can_edit_current_and_next_cycles: false } do
+ before do
+ allow(Settings.features).to receive(:provider_partnerships).and_return(true)
+ end
+
+ scenario 'adding and changing an accredited provider' do
+ given_i_am_authenticated_as_a_provider_user
+ and_i_visit_the_course_details_page_of_a_course_without_an_accredited_provider
+ and_i_click_the_add_accredited_provider_link
+ and_i_create_a_new_accredited_provider
+ and_i_revisit_the_course_details_page
+ when_i_click_select_an_accredited_provider
+ and_i_choose_the_new_accredited_provider
+ then_i_should_see_the_success_message
+
+ given_i_click_change_accredited_provider
+ and_i_click_update_accredited_provider
+ then_i_should_see_the_success_message
+ end
+
+ def given_i_click_change_accredited_provider
+ click_link_or_button 'Change accredited provider'
+ end
+
+ def then_i_should_see_the_success_message
+ expect(page).to have_text('Accredited provider updated')
+ end
+
+ def and_i_click_update_accredited_provider
+ click_link_or_button 'Update ratifying partner'
+ end
+
+ def and_i_choose_the_new_accredited_provider
+ choose @accredited_provider.provider_name
+ and_i_click_update_accredited_provider
+ end
+
+ def when_i_click_select_an_accredited_provider
+ click_link_or_button 'Select an accredited partner'
+ end
+
+ def and_i_create_a_new_accredited_provider
+ and_there_is_an_accredited_provider_in_the_database
+ and_i_click_add_accredited_provider_link
+ and_i_search_for_an_accredited_provider_with_a_valid_query
+ and_i_select_the_provider
+ and_i_input_some_information
+ and_i_click_add_accredited_provider_button
+ end
+
+ def and_i_select_the_provider
+ choose @accredited_provider.provider_name
+ click_continue
+ end
+
+ def and_i_input_some_information
+ fill_in 'About the accredited partner', with: 'This is a description'
+ click_continue
+ end
+
+ def and_there_is_an_accredited_provider_in_the_database
+ @accredited_provider = create(:provider, :accredited_provider, provider_name: 'UCL')
+ end
+
+ def and_i_search_for_an_accredited_provider_with_a_valid_query
+ fill_in form_title, with: @accredited_provider.provider_name
+ click_continue
+ end
+
+ def click_continue
+ click_link_or_button 'Continue'
+ end
+
+ def form_title
+ 'Enter a provider name, UKPRN or postcode'
+ end
+
+ def and_i_click_the_add_accredited_provider_link
+ click_link_or_button 'Add at least one accredited partner'
+ end
+
+ def and_i_click_add_accredited_provider_link
+ click_link_or_button 'Add accredited partner'
+ end
+
+ def and_i_click_add_accredited_provider_button
+ click_link_or_button 'Add accredited partner'
+ end
+
+ def given_i_am_authenticated_as_a_provider_user
+ given_i_am_authenticated(
+ user: create(
+ :user,
+ providers: [
+ create(:provider, sites: [build(:site)], courses: [build(:course)])
+ ]
+ )
+ )
+ end
+
+ def and_i_visit_the_course_details_page_of_a_course_without_an_accredited_provider
+ publish_provider_courses_details_page.load(
+ provider_code: provider.provider_code, recruitment_cycle_year: provider.recruitment_cycle_year, course_code: course.course_code
+ )
+ end
+
+ alias_method :and_i_revisit_the_course_details_page, :and_i_visit_the_course_details_page_of_a_course_without_an_accredited_provider
+
+ def provider
+ @current_user.providers.first
+ end
+
+ def course
+ provider.courses.first
+ end
+end