diff --git a/app/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form.rb b/app/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form.rb
new file mode 100644
index 0000000000..27f12d2f4d
--- /dev/null
+++ b/app/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form.rb
@@ -0,0 +1,27 @@
+module Journeys
+ module EarlyYearsPayment
+ module Provider
+ module Authenticated
+ class ClaimantNameForm < Form
+ attribute :first_name
+ attribute :surname
+
+ validates :first_name, presence: {message: i18n_error_message("first_name.presence")}
+ validates :first_name, length: {maximum: 100, message: i18n_error_message("first_name.length")}, if: -> { first_name.present? }
+ validates :first_name, name_format: {message: i18n_error_message("first_name.format")}
+
+ validates :surname, presence: {message: i18n_error_message("last_name.presence")}
+ validates :surname, length: {maximum: 100, message: i18n_error_message("last_name.length")}, if: -> { surname.present? }
+ validates :surname, name_format: {message: i18n_error_message("last_name.format")}
+
+ def save
+ return false if invalid?
+
+ journey_session.answers.assign_attributes(first_name:, surname:)
+ journey_session.save!
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/forms/personal_details_form.rb b/app/forms/personal_details_form.rb
index cd255e293d..2999451e78 100644
--- a/app/forms/personal_details_form.rb
+++ b/app/forms/personal_details_form.rb
@@ -5,7 +5,6 @@ def future?
end
end
- NAME_REGEX_FILTER = /\A[^"=$%#&*+\/\\()@?!<>0-9]*\z/
NINO_REGEX_FILTER = /\A[A-Z]{2}[0-9]{6}[A-D]{1}\Z/
DOB_PARAM_CONVERSION = {
"date_of_birth(3i)" => "day",
@@ -23,19 +22,16 @@ def future?
attribute :national_insurance_number
validates :first_name, presence: {message: "Enter your first name"}
- validates :first_name,
- length: {maximum: 100, message: "First name must be less than 100 characters"},
- format: {with: NAME_REGEX_FILTER, message: "First name cannot contain special characters"},
- if: -> { first_name.present? }
- validates :middle_name,
- length: {maximum: 61, message: "Middle names must be less than 61 characters"},
- format: {with: NAME_REGEX_FILTER, message: "Middle names cannot contain special characters"},
- if: -> { middle_name.present? }
+ validates :first_name, length: {maximum: 100, message: "First name must be less than 100 characters"}, if: -> { first_name.present? }
+ validates :first_name, name_format: {message: "First name cannot contain special characters"}
+
+ validates :middle_name, length: {maximum: 61, message: "Middle names must be less than 61 characters"}, if: -> { middle_name.present? }
+ validates :middle_name, name_format: {message: "Middle names cannot contain special characters"}
+
validates :surname, presence: {message: "Enter your last name"}
- validates :surname,
- length: {maximum: 100, message: "Last name must be less than 100 characters"},
- format: {with: NAME_REGEX_FILTER, message: "Last name cannot contain special characters"},
- if: -> { surname.present? }
+ validates :surname, length: {maximum: 100, message: "Last name must be less than 100 characters"}, if: -> { surname.present? }
+ validates :surname, name_format: {message: "Last name cannot contain special characters"}
+
validate :date_of_birth_criteria
validates :national_insurance_number, presence: {message: "Enter a National Insurance number in the correct format"}
validate :ni_number_is_correct_format
diff --git a/app/models/journeys/early_years_payment/provider/authenticated.rb b/app/models/journeys/early_years_payment/provider/authenticated.rb
index 3d4aca0ac2..9bac7f3cbc 100644
--- a/app/models/journeys/early_years_payment/provider/authenticated.rb
+++ b/app/models/journeys/early_years_payment/provider/authenticated.rb
@@ -12,7 +12,8 @@ module Authenticated
FORMS = {
"claims" => {
"consent" => ConsentForm,
- "current-nursery" => CurrentNurseryForm
+ "current-nursery" => CurrentNurseryForm,
+ "claimant-name" => ClaimantNameForm
}
}
START_WITH_MAGIC_LINK = true
diff --git a/app/models/journeys/early_years_payment/provider/authenticated/slug_sequence.rb b/app/models/journeys/early_years_payment/provider/authenticated/slug_sequence.rb
index 2a28e3cc9b..69b725ac84 100644
--- a/app/models/journeys/early_years_payment/provider/authenticated/slug_sequence.rb
+++ b/app/models/journeys/early_years_payment/provider/authenticated/slug_sequence.rb
@@ -7,6 +7,7 @@ class SlugSequence
consent
current-nursery
claimant-name
+ start-date
ineligible
].freeze
diff --git a/app/models/name_format_validator.rb b/app/models/name_format_validator.rb
new file mode 100644
index 0000000000..1fb21104e0
--- /dev/null
+++ b/app/models/name_format_validator.rb
@@ -0,0 +1,11 @@
+class NameFormatValidator < ActiveModel::EachValidator
+ NAME_REGEX_FILTER = /\A[^\[\]\^"=$%#&*+\/\\()@?!<>_`|{}~0-9]*\z/
+
+ def validate_each(record, attribute, value)
+ return unless value.present?
+
+ unless NAME_REGEX_FILTER.match?(value)
+ record.errors.add(attribute, options[:message])
+ end
+ end
+end
diff --git a/app/views/early_years_payment/provider/authenticated/claims/claimant_name.html.erb b/app/views/early_years_payment/provider/authenticated/claims/claimant_name.html.erb
index e69de29bb2..6520031c1a 100644
--- a/app/views/early_years_payment/provider/authenticated/claims/claimant_name.html.erb
+++ b/app/views/early_years_payment/provider/authenticated/claims/claimant_name.html.erb
@@ -0,0 +1,23 @@
+<% content_for(:page_title, page_title(@form.t(:question), journey: current_journey_routing_name, show_error: @form.errors.any?)) %>
+
+
+
+ <%= form_with model: @form, url: claim_path(current_journey_routing_name), method: :patch, builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %>
+ <%= f.govuk_error_summary %>
+
+
+ <%= @form.t(:question) %>
+
+
+
+ <%= @form.t(:hint) %>
+
+
+ <%= f.govuk_text_field :first_name, label: { text: "First name" }, spellcheck: "false", autocomplete: "name" %>
+
+ <%= f.govuk_text_field :surname, label: { text: "Last name" }, spellcheck: "false", autocomplete: "name" %>
+
+ <%= f.govuk_submit "Continue" %>
+ <% end %>
+
+
diff --git a/app/views/early_years_payment/provider/authenticated/claims/start_date.html.erb b/app/views/early_years_payment/provider/authenticated/claims/start_date.html.erb
new file mode 100644
index 0000000000..136d06384a
--- /dev/null
+++ b/app/views/early_years_payment/provider/authenticated/claims/start_date.html.erb
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/config/locales/en.yml b/config/locales/en.yml
index fb713aaf31..ffb5604294 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -865,7 +865,9 @@ en:
min_length: Enter a college name or postcode that is at least 3 characters long
select_provision:
heading: Select where you are employed
- hint: Select the further education provider you are employed by. If you work for a college or sixth-form group, you should select the group name.
+ hint:
+ Select the further education provider you are employed by. If you work for a college or sixth-form group, you
+ should select the group name.
errors:
blank: Select where you are employed
contract_type:
@@ -940,7 +942,8 @@ en:
question: Which building and construction courses do you teach?
question_check_your_answers: Building and construction courses
options:
- level3_buildingconstruction_approved: Qualifications approved for funding at level 3 and below in the %{link} sector subject area
+ level3_buildingconstruction_approved:
+ Qualifications approved for funding at level 3 and below in the %{link} sector subject area
tlevel_building: T Level in building services engineering for construction
tlevel_onsiteconstruction: T Level in onsite construction
tlevel_design_surveying: T Level in design, surveying and planning for construction
@@ -953,7 +956,9 @@ en:
options:
alevel_chemistry: A or AS level chemistry
gcse_chemistry: GCSE chemistry
- ibo_level_3_chemistry: IBO level 3 SL and HL chemistry, taught as part of a diploma or career related programme or as a standalone certificate
+ ibo_level_3_chemistry:
+ IBO level 3 SL and HL chemistry, taught as part of a diploma or career related programme or as a standalone
+ certificate
ibo_level_1_2_myp_chemistry: IBO level 1 / level 2 MYP chemistry
none: I do not teach any of these courses
computing_courses:
@@ -986,9 +991,12 @@ en:
question: Which engineering and manufacturing courses do you teach?
question_check_your_answers: Engineering and manufacturing courses
options:
- approved_level_321_engineering: Qualifications approved for funding at level 3 and below in the %{link} sector subject area
- approved_level_321_manufacturing: Qualifications approved for funding at level 3 and below in the %{link} sector subject area
- approved_level_321_transportation: Qualifications approved for funding at level 3 and below in the %{link} sector subject area
+ approved_level_321_engineering:
+ Qualifications approved for funding at level 3 and below in the %{link} sector subject area
+ approved_level_321_manufacturing:
+ Qualifications approved for funding at level 3 and below in the %{link} sector subject area
+ approved_level_321_transportation:
+ Qualifications approved for funding at level 3 and below in the %{link} sector subject area
tlevel_design: T Level in design and development for engineering and manufacturing
tlevel_maintenance: T Level in maintenance, installation and repair for engineering and manufacturing
tlevel_engineering: T Level in engineering, manufacturing, processing and control
@@ -999,9 +1007,11 @@ en:
question: Which maths courses do you teach?
question_check_your_answers: Maths courses
options:
- approved_level_321_maths: Qualifications approved for funding at level 3 and below in the %{link} sector subject area
+ approved_level_321_maths:
+ Qualifications approved for funding at level 3 and below in the %{link} sector subject area
gcse_maths:
- GCSE in maths, functional skills qualifications and %{link} approved for teaching to 16 to 19-year-olds who meet the condition of funding
+ GCSE in maths, functional skills qualifications and %{link} approved for teaching to 16 to 19-year-olds who
+ meet the condition of funding
none: I do not teach any of these courses
physics_courses:
<<: *courses
@@ -1011,7 +1021,9 @@ en:
alevel_physics: A or AS level physics
gcse_physics: GCSE physics
ibo_level_1_2_myp_physics: IBO level 1 / level 2 MYP physics
- ibo_level_3_physics: IBO level 3 in SL and HL physics, taught as part of a diploma or career related programme or as a standalone certificate
+ ibo_level_3_physics:
+ IBO level 3 in SL and HL physics, taught as part of a diploma or career related programme or as a standalone
+ certificate
none: I do not teach any of these courses
teaching_qualification:
question: Do you have a teaching qualification?
@@ -1105,6 +1117,20 @@ en:
question: Confirm that your employee works at the location below
errors:
presence: Select the nursery where your employee works
+ claimant_name:
+ question: Who are you making this claim for?
+ hint:
+ Enter the employee’s full legal name as it appears on official identity documents such as passports or driving
+ licenses.
+ errors:
+ first_name:
+ presence: Enter employee’s first name
+ length: Employee’s first name must be less than 100 characters
+ format: Employee’s first name cannot contain special characters
+ last_name:
+ presence: Enter employee’s last name
+ length: Employee’s last name must be less than 100 characters
+ format: Employee’s last name cannot contain special characters
early_years_payments:
<<: *early_years_payment_provider_authenticated
claim_subject: "Early Years Payment"
diff --git a/spec/features/early_years_payment/provider/authenticated/happy_path_spec.rb b/spec/features/early_years_payment/provider/authenticated/happy_path_spec.rb
index d8870aea02..711b0f476b 100644
--- a/spec/features/early_years_payment/provider/authenticated/happy_path_spec.rb
+++ b/spec/features/early_years_payment/provider/authenticated/happy_path_spec.rb
@@ -15,6 +15,7 @@
expect(journey_session.reload.answers.email_address).to eq email_address
expect(journey_session.reload.answers.email_verified).to be true
expect(page).to have_content("Declaration of Employee Consent")
+
check "I confirm that I have obtained consent from my employee and have provided them with the relevant privacy notice."
click_button "Continue"
expect(page.current_path).to eq "/early-years-payment-provider/current-nursery"
@@ -22,6 +23,11 @@
choose nursery.nursery_name
click_button "Continue"
expect(page.current_path).to eq "/early-years-payment-provider/claimant-name"
+
+ fill_in "First name", with: "Bobby"
+ fill_in "Last name", with: "Bobberson"
+ click_button "Continue"
+ expect(page.current_path).to eq "/early-years-payment-provider/start-date"
end
scenario "using magic link after having completed some of the journey" do
diff --git a/spec/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form_spec.rb b/spec/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form_spec.rb
new file mode 100644
index 0000000000..5e3823430e
--- /dev/null
+++ b/spec/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form_spec.rb
@@ -0,0 +1,50 @@
+require "rails_helper"
+
+RSpec.describe Journeys::EarlyYearsPayment::Provider::Authenticated::ClaimantNameForm, type: :model do
+ let(:journey) { Journeys::EarlyYearsPayment::Provider::Authenticated }
+ let(:journey_session) { create(:early_years_payment_provider_authenticated_session) }
+ let(:first_name) { nil }
+ let(:surname) { nil }
+
+ let(:params) do
+ ActionController::Parameters.new(
+ claim: {
+ first_name:,
+ surname:
+ }
+ )
+ end
+
+ subject do
+ described_class.new(journey_session:, journey:, params:)
+ end
+
+ describe "validations" do
+ # First name validations
+ it { should validate_presence_of(:first_name).with_message("Enter employee’s first name") }
+ it { should validate_length_of(:first_name).is_at_most(100).with_message("Employee’s first name must be less than 100 characters") }
+ it { should allow_value("O'Brian").for(:first_name) }
+ %w[* | { ^ /].each do |char|
+ it { should_not allow_value(char).for(:first_name).with_message("Employee’s first name cannot contain special characters") }
+ end
+
+ # Surname validations
+ it { should validate_presence_of(:surname).with_message("Enter employee’s last name") }
+ it { should validate_length_of(:surname).is_at_most(100).with_message("Employee’s last name must be less than 100 characters") }
+ it { should_not allow_value("5").for(:surname).with_message("Employee’s last name cannot contain special characters") }
+ it { should allow_value("O'Brian").for(:surname) }
+ end
+
+ describe "#save" do
+ let(:first_name) { "Bobby" }
+ let(:surname) { "Bobberson" }
+
+ it "updates the journey session" do
+ expect { subject.save }.to(
+ change { journey_session.answers.first_name }.to(first_name).and(
+ change { journey_session.answers.surname }.to(surname)
+ )
+ )
+ end
+ end
+end