- <% if @page.header.present? %>
-
- <% if !@page.hide_subsection_label %>
- <%= @subsection.label %>
- <% end %>
- <%= @page.header %>
-
- <% end %>
-
- <% if @page.description.present? %>
-
<%= @page.description.html_safe %>
- <% end %>
-
- <%= form_with model: @case_log, url: form_case_log_path(@case_log), method: "post" do |f| %>
+ <%= form_with model: @case_log, url: form_case_log_path(@case_log), method: "post" do |f| %>
+
+
<% remove_other_page_errors(@case_log, @page) %>
<%= f.govuk_error_summary %>
+
+ <% if @page.header.present? %>
+
+ <% if !@page.hide_subsection_label %>
+ <%= @subsection.label %>
+ <% end %>
+ <%= @page.header %>
+
+ <% end %>
+
+ <% if @page.description.present? %>
+
<%= @page.description.html_safe %>
+ <% end %>
+
<% @page.non_conditional_questions.map do |question| %>
<%= display_question_key_div(@page, question) %> >
<% if question.read_only? %>
@@ -46,8 +47,12 @@
<% end %>
<%= f.hidden_field :page, value: @page.id %>
- <%= f.govuk_submit "Save and continue" %>
- <% end %>
+ <% if @case_log.form.is_last_question?(@page, @subsection, @case_log) %>
+ <%= f.govuk_submit "Submit lettings log" %>
+ <%else %>
+ <%= f.govuk_submit "Save and continue" %>
+ <%end %>
+
-
+ <% end %>
<% end %>
diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json
index 7ebdfdfef..33fdaa6e0 100644
--- a/config/forms/2021_2022.json
+++ b/config/forms/2021_2022.json
@@ -9,30 +9,6 @@
"setup": {
"label": "Set up your lettings log",
"pages": {
- "gdpr_acceptance": {
- "header": "",
- "description": "",
- "questions": {
- "gdpr_acceptance": {
- "check_answer_label": "Privacy notice seen",
- "header": "Has the tenant or buyer seen the Department for Levelling Up, Housing and Communities (DLUHC) privacy notice?",
- "hint_text": "You must
show the privacy notice to the tenant or buyer before you can use this service.",
- "type": "radio",
- "answer_options": {
- "0": "Yes",
- "1": "No"
- }
- }
- }
- },
- "gdpr_declined": {
- "hide_subsection_label": true,
- "header": "You cannot use this service",
- "hint_text": "",
- "description": "We cannot accept data about a tenant or buyer unless they’ve seen the
DLUHC privacy notice.
Go to your logs",
- "questions": {},
- "depends_on": [{ "gdpr_acceptance": "No" }]
- },
"organisation_details": {
"header": "Organisation details",
"description": "",
@@ -57,8 +33,7 @@
"1": "B"
}
}
- },
- "depends_on": [{ "gdpr_acceptance": "Yes" }]
+ }
},
"renewal": {
"header": "",
@@ -74,10 +49,7 @@
"0": "No"
}
}
- },
- "depends_on": [{
- "gdpr_acceptance": "Yes"
- }]
+ }
},
"startdate": {
"header": "",
@@ -89,10 +61,7 @@
"hint_text": "For example, 27 3 2021.",
"type": "date"
}
- },
- "depends_on": [{
- "gdpr_acceptance": "Yes"
- }]
+ }
},
"about_this_letting": {
"header": "Tell us about this letting",
@@ -132,10 +101,7 @@
"0": "Supported housing"
}
}
- },
- "depends_on": [{
- "gdpr_acceptance": "Yes"
- }]
+ }
},
"tenant_code": {
"header": "",
@@ -148,10 +114,7 @@
"type": "text",
"width": 10
}
- },
- "depends_on": [{
- "gdpr_acceptance": "Yes"
- }]
+ }
},
"property_reference": {
"header": "",
@@ -164,8 +127,7 @@
"type": "text",
"width": 10
}
- },
- "depends_on": [{ "gdpr_acceptance": "Yes" }]
+ }
}
}
}
@@ -2330,95 +2292,56 @@
"depends_on": [{ "setup": "completed" }],
"pages": {
"net_income_known": {
- "header": "Household’s combined income",
+ "header": "",
"description": "",
"questions": {
"net_income_known": {
- "check_answer_label": "How often household receives income",
- "header": "How often does the household receive income?",
- "guidance_partial": "what_counts_as_income",
+ "check_answer_label": "Do you know the household’s combined income?",
+ "header": "Do you know the household’s combined income?",
"hint_text": "",
"type": "radio",
"answer_options": {
- "0": "Weekly",
- "1": "Monthly",
- "2": "Annually",
+ "0": "Yes",
+ "1": "No",
"divider_a": true,
- "3": "Don’t know",
- "4": "Tenant prefers not to say"
+ "2": "Don’t know",
+ "3": "Tenant prefers not to say"
}
}
}
},
- "weekly_net_income": {
- "depends_on": [{ "net_income_known": "Weekly" }],
- "header": "",
+ "net_income": {
+ "depends_on": [{ "net_income_known": "Yes" }],
+ "header": "Household’s combined income after tax",
"description": "",
"questions": {
"earnings": {
"check_answer_label": "Total household income",
- "header": "How much income does the household have in total every week?",
+ "header": "How much income does the household have in total?",
+ "guidance_partial": "what_counts_as_income",
"hint_text": "",
"type": "numeric",
"min": 0,
"step": "1",
"width": 5,
"prefix": "£",
- "suffix": " every week"
- }
- },
- "soft_validations": {
- "override_net_income_validation": {
- "check_answer_label": "Net income confirmed?",
- "type": "validation_override",
- "answer_options": {
- "override_net_income_validation": "Yes"
- }
- }
- }
- },
- "monthly_net_income": {
- "depends_on": [{ "net_income_known": "Monthly" }],
- "header": "",
- "description": "",
- "questions": {
- "earnings": {
- "check_answer_label": "Total household income",
- "header": "How much income does the household have in total every month?",
+ "suffix": [
+ { "label": "every week", "depends_on" : { "incfreq": "Weekly" } },
+ { "label": "every month", "depends_on" : { "incfreq": "Monthly" } },
+ { "label": "every month", "depends_on" : { "incfreq": "Yearly" } }
+ ]
+ },
+ "incfreq": {
+ "check_answer_label": "How often does the household receive this amount?",
+ "header": "How often does the household receive this amount?",
"hint_text": "",
- "type": "numeric",
- "min": 0,
- "step": "1",
- "width": 5,
- "prefix": "£",
- "suffix": " every month"
- }
- },
- "soft_validations": {
- "override_net_income_validation": {
- "check_answer_label": "Net income confirmed?",
- "type": "validation_override",
+ "type": "radio",
"answer_options": {
- "override_net_income_validation": "Yes"
- }
- }
- }
- },
- "yearly_net_income": {
- "depends_on": [{ "net_income_known": "Annually" }],
- "header": "",
- "description": "",
- "questions": {
- "earnings": {
- "check_answer_label": "Total household income",
- "header": "How much income does the household have in total every year?",
- "hint_text": "",
- "type": "numeric",
- "min": 0,
- "step": "1",
- "width": 5,
- "prefix": "£",
- "suffix": " every year"
+ "0": "Weekly",
+ "1": "Monthly",
+ "2": "Yearly"
+ },
+ "hidden_in_check_answers": true
}
},
"soft_validations": {
@@ -3226,9 +3149,12 @@
"questions": {
"declaration": {
"check_answer_label": "",
- "header": "What is the tenant code?",
+ "header": "Submit your lettings log ",
"hint_text": "",
- "type": "text"
+ "type": "checkbox",
+ "answer_options": {
+ "declaration": "The tenant has seen the Department for Levelling Up, Housing & Communities (DLUHC) privacy notice"
+ }
}
}
}
diff --git a/config/initializers/active_admin.rb b/config/initializers/active_admin.rb
index 47561059e..e8014c7e2 100644
--- a/config/initializers/active_admin.rb
+++ b/config/initializers/active_admin.rb
@@ -340,3 +340,7 @@ end
Rails.application.config.after_initialize do
ActiveAdmin.application.stylesheets.delete("active_admin/print.css")
end
+
+Rails.application.config.after_initialize do
+ ActiveAdmin::BaseController.include Admin::PaperTrail
+end
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index de41a7b75..d377aacd0 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -189,7 +189,7 @@ Devise.setup do |config|
# ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this
# time the user will be asked for credentials again. Default is 30 minutes.
- # config.timeout_in = 30.minutes
+ config.timeout_in = 1.day
# ==> Configuration for :lockable
# Defines which strategy will be used to lock an account.
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 17e5ce3aa..f4d8cda63 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -68,6 +68,8 @@ en:
earnings:
under_hard_max: "Net income cannot be greater than %{hard_max} given the tenant’s working situation"
over_hard_min: "Net income cannot be less than %{hard_min} given the tenant’s working situation"
+ freq_missing: "Select how often the household receives income"
+ earnings_missing: "Enter how much income the household has in total"
household:
reasonpref:
@@ -115,6 +117,9 @@ en:
referral:
rsnvac_non_temp: "Answer cannot be this source of referral as you already told us this is a re-let to tenant who occupied the same property as temporary accommodation"
+ declaration:
+ missing: "You must show the DLUHC privacy notice to the tenant before you can submit this log."
+
soft_validations:
net_income:
hint_text: "This is based on the tenant’s work situation: %{ecstat1}"
diff --git a/db/migrate/20220207091117_add_declaration.rb b/db/migrate/20220207091117_add_declaration.rb
new file mode 100644
index 000000000..f9b6be7e3
--- /dev/null
+++ b/db/migrate/20220207091117_add_declaration.rb
@@ -0,0 +1,7 @@
+class AddDeclaration < ActiveRecord::Migration[7.0]
+ def change
+ change_table :case_logs, bulk: true do |t|
+ t.column :declaration, :integer
+ end
+ end
+end
diff --git a/db/migrate/20220207151239_create_versions.rb b/db/migrate/20220207151239_create_versions.rb
new file mode 100644
index 000000000..2d69235bb
--- /dev/null
+++ b/db/migrate/20220207151239_create_versions.rb
@@ -0,0 +1,22 @@
+# This migration creates the `versions` table, the only schema PT requires.
+# All other migrations PT provides are optional.
+class CreateVersions < ActiveRecord::Migration[7.0]
+ # The largest text column available in all supported RDBMS is
+ # 1024^3 - 1 bytes, roughly one gibibyte. We specify a size
+ # so that MySQL will use `longtext` instead of `text`. Otherwise,
+ # when serializing very large objects, `text` might not be big enough.
+ TEXT_BYTES = 1_073_741_823
+
+ def change
+ create_table :versions do |t|
+ t.string :item_type, null: false, limit: 191
+ t.bigint :item_id, null: false
+ t.string :event, null: false
+ t.string :whodunnit
+ t.text :object, limit: TEXT_BYTES
+
+ t.datetime :created_at
+ end
+ add_index :versions, %i[item_type item_id]
+ end
+end
diff --git a/db/migrate/20220207151312_remove_discarded_at_from_case_logs.rb b/db/migrate/20220207151312_remove_discarded_at_from_case_logs.rb
new file mode 100644
index 000000000..00886f520
--- /dev/null
+++ b/db/migrate/20220207151312_remove_discarded_at_from_case_logs.rb
@@ -0,0 +1,11 @@
+class RemoveDiscardedAtFromCaseLogs < ActiveRecord::Migration[7.0]
+ def up
+ remove_index :case_logs, :discarded_at
+ remove_column :case_logs, :discarded_at
+ end
+
+ def down
+ add_column :case_logs, :discarded_at, :datetime
+ add_index :case_logs, :discarded_at
+ end
+end
diff --git a/db/migrate/20220208101235_remove_gdpr_fields.rb b/db/migrate/20220208101235_remove_gdpr_fields.rb
new file mode 100644
index 000000000..7272c483d
--- /dev/null
+++ b/db/migrate/20220208101235_remove_gdpr_fields.rb
@@ -0,0 +1,15 @@
+class RemoveGdprFields < ActiveRecord::Migration[7.0]
+ def up
+ change_table :case_logs, bulk: true do |t|
+ t.remove :gdpr_declined
+ t.remove :gdpr_acceptance
+ end
+ end
+
+ def down
+ change_table :case_logs, bulk: true do |t|
+ t.column :gdpr_declined, :string
+ t.column :gdpr_acceptance, :string
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 29e45f85d..891c11299 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,8 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2022_02_07_1123100) do
+ActiveRecord::Schema.define(version: 202202071123100) do
+
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -92,6 +93,7 @@ ActiveRecord::Schema.define(version: 2022_02_07_1123100) do
t.integer "beds"
t.integer "offered"
t.integer "wchair"
+ t.integer "earnings"
t.integer "incfreq"
t.integer "benefits"
t.integer "period"
@@ -126,11 +128,8 @@ ActiveRecord::Schema.define(version: 2022_02_07_1123100) do
t.integer "rp_medwel"
t.integer "rp_hardship"
t.integer "rp_dontknow"
- t.datetime "discarded_at"
t.string "tenancyother"
t.integer "override_net_income_validation"
- t.string "gdpr_acceptance"
- t.string "gdpr_declined"
t.string "property_owner_organisation"
t.string "property_manager_organisation"
t.string "sale_or_letting"
@@ -183,7 +182,6 @@ ActiveRecord::Schema.define(version: 2022_02_07_1123100) do
t.integer "is_carehome"
t.integer "letting_in_sheltered_accomodation"
t.integer "household_charge"
- t.integer "earnings"
t.integer "referral"
t.decimal "brent", precision: 10, scale: 2
t.decimal "scharge", precision: 10, scale: 2
@@ -192,7 +190,7 @@ ActiveRecord::Schema.define(version: 2022_02_07_1123100) do
t.decimal "tcharge", precision: 10, scale: 2
t.decimal "tshortfall", precision: 10, scale: 2
t.decimal "chcharge", precision: 10, scale: 2
- t.index ["discarded_at"], name: "index_case_logs_on_discarded_at"
+ t.integer "declaration"
t.index ["managing_organisation_id"], name: "index_case_logs_on_managing_organisation_id"
t.index ["owning_organisation_id"], name: "index_case_logs_on_owning_organisation_id"
end
@@ -252,4 +250,14 @@ ActiveRecord::Schema.define(version: 2022_02_07_1123100) do
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
+ create_table "versions", force: :cascade do |t|
+ t.string "item_type", limit: 191, null: false
+ t.bigint "item_id", null: false
+ t.string "event", null: false
+ t.string "whodunnit"
+ t.text "object"
+ t.datetime "created_at", precision: 6
+ t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id"
+ end
+
end
diff --git a/infrastructure_setup.md b/infrastructure_setup.md
index cd6b7f64c..1f7d7b422 100644
--- a/infrastructure_setup.md
+++ b/infrastructure_setup.md
@@ -1,10 +1,10 @@
# Staging
1. Login:\
-`cf login -a api.london.cloud.service.gov.uk -u
`
+ `cf login -a api.london.cloud.service.gov.uk -u `
2. Set your deployment target (staging):\
-`cf target -o dluhc-core -s staging`
+ `cf target -o dluhc-core -s staging`
3. Create required Postgres and S3 bucket backing services (this will take ~15 mins to finish creating):\
`cf create-service postgres tiny-unencrypted-13 dluhc-core-staging-postgres`
@@ -13,9 +13,34 @@
`cf create-service aws-s3-bucket default dluhc-core-staging-export-bucket`
+4. Deploy manifest:\
+ `cf push dluhc-core-staging --strategy rolling`
+
+5. Bind S3 services to app:\
`cf bind-service dluhc-core-staging dluhc-core-staging-import-bucket -c '{"permissions": "read-only"}'`
`cf bind-service dluhc-core-staging dluhc-core-staging-export-bucket -c '{"permissions": "read-write"}'`
+
+# Production
+
+1. Login:\
+ `cf login -a api.london.cloud.service.gov.uk -u `
+
+2. Set your deployment target (production):\
+ `cf target -o dluhc-core -s production`
+
+3. Create required Postgres and S3 bucket backing services (this will take ~15 mins to finish creating):\
+ `cf create-service postgres small-ha-13 dluhc-core-production-postgres`
+
+ `cf create-service aws-s3-bucket default dluhc-core-production-import-bucket`
+
+ `cf create-service aws-s3-bucket default dluhc-core-production-export-bucket`
+
4. Deploy manifest:\
-`cf push dluhc-core-staging --strategy rolling`
+ `cf push dluhc-core-production --strategy rolling`
+
+5. Bind S3 services to app:\
+ `cf bind-service dluhc-core-production dluhc-core-production-import-bucket -c '{"permissions": "read-only"}'`
+
+ `cf bind-service dluhc-core-production dluhc-core-production-export-bucket -c '{"permissions": "read-write"}'`
diff --git a/manifest.yml b/manifest.yml
index 8938a313c..a80004d41 100644
--- a/manifest.yml
+++ b/manifest.yml
@@ -20,6 +20,11 @@ applications:
- name: dluhc-core-production
<<: *defaults
+ processes:
+ - type: web
+ command: bundle exec rake cf:on_first_instance db:migrate && bin/rails server
+ instances: 4
+ memory: 1G
env:
RAILS_ENV: production
host: submit-social-housing-lettings-sales-data
diff --git a/spec/controllers/admin/admin_users_controller_spec.rb b/spec/controllers/admin/admin_users_controller_spec.rb
index e5977d830..871a198c3 100644
--- a/spec/controllers/admin/admin_users_controller_spec.rb
+++ b/spec/controllers/admin/admin_users_controller_spec.rb
@@ -6,8 +6,11 @@ describe Admin::AdminUsersController, type: :controller do
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:resource_title) { "Admin Users" }
let(:valid_session) { {} }
+ let(:signed_in_admin_user) { FactoryBot.create(:admin_user) }
- login_admin_user
+ before do
+ sign_in signed_in_admin_user
+ end
describe "Get admin users" do
before do
@@ -27,22 +30,30 @@ describe Admin::AdminUsersController, type: :controller do
it "creates a new admin user" do
expect { post :create, session: valid_session, params: params }.to change(AdminUser, :count).by(1)
end
+
+ it "tracks who created the record" do
+ post :create, session: valid_session, params: params
+ created_id = response.location.match(/[0-9]+/)[0]
+ whodunnit_actor = AdminUser.find_by(id: created_id).versions.last.actor
+ expect(whodunnit_actor).to be_a(AdminUser)
+ expect(whodunnit_actor.id).to eq(signed_in_admin_user.id)
+ end
end
describe "Update admin users" do
- context "when editing the form" do
+ context "when viewing the form" do
before do
get :edit, session: valid_session, params: { id: AdminUser.first.id }
end
- it "shows an edit form" do
+ it "shows the correct fields" do
expect(page).to have_field("admin_user_email")
expect(page).to have_field("admin_user_password")
expect(page).to have_field("admin_user_password_confirmation")
end
end
- context "when updating the form" do
+ context "when updating an admin user" do
let(:admin_user) { FactoryBot.create(:admin_user) }
let(:email) { "new_email@example.com" }
let(:params) { { id: admin_user.id, admin_user: { email: email } } }
@@ -55,6 +66,13 @@ describe Admin::AdminUsersController, type: :controller do
admin_user.reload
expect(admin_user.email).to eq(email)
end
+
+ it "tracks who updated the record" do
+ admin_user.reload
+ whodunnit_actor = admin_user.versions.last.actor
+ expect(whodunnit_actor).to be_a(AdminUser)
+ expect(whodunnit_actor.id).to eq(signed_in_admin_user.id)
+ end
end
end
end
diff --git a/spec/controllers/admin/case_logs_controller_spec.rb b/spec/controllers/admin/case_logs_controller_spec.rb
index 6aa25b1d6..31b79a5bd 100644
--- a/spec/controllers/admin/case_logs_controller_spec.rb
+++ b/spec/controllers/admin/case_logs_controller_spec.rb
@@ -6,8 +6,7 @@ describe Admin::CaseLogsController, type: :controller do
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:resource_title) { "Logs" }
let(:valid_session) { {} }
-
- login_admin_user
+ let(:admin_user) { FactoryBot.create(:admin_user) }
describe "Get case logs" do
let!(:case_log) { FactoryBot.create(:case_log, :in_progress) }
@@ -39,5 +38,49 @@ describe Admin::CaseLogsController, type: :controller do
it "creates a new case log" do
expect { post :create, session: valid_session, params: params }.to change(CaseLog, :count).by(1)
end
+
+ it "tracks who created the record" do
+ post :create, session: valid_session, params: params
+ created_id = response.location.match(/[0-9]+/)[0]
+ whodunnit_actor = CaseLog.find_by(id: created_id).versions.last.actor
+ expect(whodunnit_actor).to be_a(AdminUser)
+ expect(whodunnit_actor.id).to eq(admin_user.id)
+ end
+ end
+
+ describe "Update case log" do
+ let!(:case_log) { FactoryBot.create(:case_log, :in_progress) }
+
+ context "when viewing the edit form" do
+ before do
+ get :edit, session: valid_session, params: { id: case_log.id }
+ end
+
+ it "has the correct fields" do
+ expect(page).to have_field("case_log_age1")
+ expect(page).to have_field("case_log_tenant_code")
+ end
+ end
+
+ context "when updating the case_log" do
+ let(:tenant_code) { "New tenant code by Admin" }
+ let(:params) { { id: case_log.id, case_log: { tenant_code: tenant_code } } }
+
+ before do
+ patch :update, session: valid_session, params: params
+ end
+
+ it "updates the case log" do
+ case_log.reload
+ expect(case_log.tenant_code).to eq(tenant_code)
+ end
+
+ it "tracks who updated the record" do
+ case_log.reload
+ whodunnit_actor = case_log.versions.last.actor
+ expect(whodunnit_actor).to be_a(AdminUser)
+ expect(whodunnit_actor.id).to eq(admin_user.id)
+ end
+ end
end
end
diff --git a/spec/controllers/admin/dashboard_controller_spec.rb b/spec/controllers/admin/dashboard_controller_spec.rb
index 776a04eea..d6fa9fe0c 100644
--- a/spec/controllers/admin/dashboard_controller_spec.rb
+++ b/spec/controllers/admin/dashboard_controller_spec.rb
@@ -6,8 +6,7 @@ describe Admin::DashboardController, type: :controller do
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:resource_title) { "Dashboard" }
let(:valid_session) { {} }
-
- login_admin_user
+ let(:admin_user) { FactoryBot.create(:admin_user) }
describe "Get case logs" do
before do
diff --git a/spec/controllers/admin/organisations_controller_spec.rb b/spec/controllers/admin/organisations_controller_spec.rb
index 16dedcb69..014d13e8c 100644
--- a/spec/controllers/admin/organisations_controller_spec.rb
+++ b/spec/controllers/admin/organisations_controller_spec.rb
@@ -7,8 +7,11 @@ describe Admin::OrganisationsController, type: :controller do
let(:resource_title) { "Organisations" }
let(:valid_session) { {} }
let!(:organisation) { FactoryBot.create(:organisation) }
+ let!(:admin_user) { FactoryBot.create(:admin_user) }
- login_admin_user
+ before do
+ sign_in admin_user
+ end
describe "Organisations" do
before do
@@ -22,23 +25,54 @@ describe Admin::OrganisationsController, type: :controller do
end
end
- describe "Create admin users" do
+ describe "Create organisation" do
let(:params) { { organisation: { name: "DLUHC" } } }
it "creates a organisation" do
expect { post :create, session: valid_session, params: params }.to change(Organisation, :count).by(1)
end
+
+ it "tracks who created the record" do
+ post :create, session: valid_session, params: params
+ created_id = response.location.match(/[0-9]+/)[0]
+ whodunnit_actor = Organisation.find_by(id: created_id).versions.last.actor
+ expect(whodunnit_actor).to be_a(AdminUser)
+ expect(whodunnit_actor.id).to eq(admin_user.id)
+ end
end
describe "Update organisation" do
- before do
- get :edit, session: valid_session, params: { id: organisation.id }
+ context "when viewing the edit form" do
+ before do
+ get :edit, session: valid_session, params: { id: organisation.id }
+ end
+
+ it "has the correct fields" do
+ expect(page).to have_field("organisation_name")
+ expect(page).to have_field("organisation_provider_type")
+ expect(page).to have_field("organisation_phone")
+ end
end
- it "creates a new admin users" do
- expect(page).to have_field("organisation_name")
- expect(page).to have_field("organisation_provider_type")
- expect(page).to have_field("organisation_phone")
+ context "when updating the organisation" do
+ let(:name) { "New Org Name by Admin" }
+ let(:params) { { id: organisation.id, organisation: { name: name } } }
+
+ before do
+ patch :update, session: valid_session, params: params
+ end
+
+ it "updates the organisation" do
+ organisation.reload
+ expect(organisation.name).to eq(name)
+ end
+
+ it "tracks who updated the record" do
+ organisation.reload
+ whodunnit_actor = organisation.versions.last.actor
+ expect(whodunnit_actor).to be_a(AdminUser)
+ expect(whodunnit_actor.id).to eq(admin_user.id)
+ end
end
end
end
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 78d97405b..9a7f7e264 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -8,8 +8,11 @@ describe Admin::UsersController, type: :controller do
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:resource_title) { "Users" }
let(:valid_session) { {} }
+ let!(:admin_user) { FactoryBot.create(:admin_user) }
- login_admin_user
+ before do
+ sign_in admin_user
+ end
describe "Get users" do
before do
@@ -39,15 +42,23 @@ describe Admin::UsersController, type: :controller do
it "creates a new user" do
expect { post :create, session: valid_session, params: params }.to change(User, :count).by(1)
end
+
+ it "tracks who created the record" do
+ post :create, session: valid_session, params: params
+ created_id = response.location.match(/[0-9]+/)[0]
+ whodunnit_actor = User.find_by(id: created_id).versions.last.actor
+ expect(whodunnit_actor).to be_a(AdminUser)
+ expect(whodunnit_actor.id).to eq(admin_user.id)
+ end
end
describe "Update users" do
- context "when updating the form" do
+ context "when viewing the edit form" do
before do
get :edit, session: valid_session, params: { id: user.id }
end
- it "shows an edit form" do
+ it "has the correct fields" do
expect(page).to have_field("user_email")
expect(page).to have_field("user_name")
expect(page).to have_field("user_organisation_id")
@@ -69,6 +80,13 @@ describe Admin::UsersController, type: :controller do
user.reload
expect(user.name).to eq(name)
end
+
+ it "tracks who updated the record" do
+ user.reload
+ whodunnit_actor = user.versions.last.actor
+ expect(whodunnit_actor).to be_a(AdminUser)
+ expect(whodunnit_actor.id).to eq(admin_user.id)
+ end
end
end
end
diff --git a/spec/factories/case_log.rb b/spec/factories/case_log.rb
index 0a73e7750..4e59c1656 100644
--- a/spec/factories/case_log.rb
+++ b/spec/factories/case_log.rb
@@ -3,7 +3,6 @@ FactoryBot.define do
owning_organisation { FactoryBot.create(:organisation) }
managing_organisation { FactoryBot.create(:organisation) }
trait :about_completed do
- gdpr_acceptance { "Yes" }
renewal { "No" }
needstype { 1 }
rent_type { 1 }
@@ -70,6 +69,7 @@ FactoryBot.define do
offered { 2 }
wchair { "Yes" }
earnings { 68 }
+ incfreq { "Weekly" }
benefits { "Some" }
period { "Every 2 weeks" }
brent { 200 }
@@ -108,12 +108,9 @@ FactoryBot.define do
rp_medwel { "No" }
rp_hardship { "No" }
rp_dontknow { "No" }
- discarded_at { nil }
tenancyother { nil }
override_net_income_validation { nil }
- net_income_known { "Weekly" }
- gdpr_acceptance { "Yes" }
- gdpr_declined { "No" }
+ net_income_known { "Yes" }
property_owner_organisation { "Test" }
property_manager_organisation { "Test" }
renewal { 1 }
@@ -152,6 +149,7 @@ FactoryBot.define do
chcharge { 7 }
letting_in_sheltered_accomodation { "No" }
la_known { "Yes" }
+ declaration { "Yes" }
end
created_at { Time.zone.now }
updated_at { Time.zone.now }
diff --git a/spec/features/form/check_answers_page_spec.rb b/spec/features/form/check_answers_page_spec.rb
index dc1a5f442..e50bc0e23 100644
--- a/spec/features/form/check_answers_page_spec.rb
+++ b/spec/features/form/check_answers_page_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe "Form Check Answers Page" do
end
context "when the user needs to check their answers for a subsection" do
- let(:last_question_for_subsection) { "household-number-of-other-members" }
+ let(:last_question_for_subsection) { "propcode" }
it "can be visited by URL" do
visit("/logs/#{id}/#{subsection}/check-answers")
@@ -44,7 +44,7 @@ RSpec.describe "Form Check Answers Page" do
end
it "redirects to the check answers page when answering the last question and clicking save and continue" do
- fill_in_number_question(id, "other_hhmemb", 0, last_question_for_subsection)
+ fill_in_number_question(id, "propcode", 0, last_question_for_subsection)
expect(page).to have_current_path("/logs/#{id}/#{subsection}/check-answers")
end
@@ -207,7 +207,7 @@ RSpec.describe "Form Check Answers Page" do
end
it "they can click a button to cycle around to the next incomplete section" do
- visit("/logs/#{cycle_sections_case_log.id}/setup/check-answers")
+ visit("/logs/#{cycle_sections_case_log.id}/declaration/check-answers")
click_link("Save and go to next incomplete section")
expect(page).to have_current_path("/logs/#{cycle_sections_case_log.id}/tenant-code")
end
diff --git a/spec/features/form/checkboxes_spec.rb b/spec/features/form/checkboxes_spec.rb
new file mode 100644
index 000000000..60ce22f14
--- /dev/null
+++ b/spec/features/form/checkboxes_spec.rb
@@ -0,0 +1,41 @@
+require "rails_helper"
+require_relative "helpers"
+require_relative "../../request_helper"
+
+RSpec.describe "Checkboxes" do
+ include Helpers
+ let(:user) { FactoryBot.create(:user) }
+ let(:case_log) do
+ FactoryBot.create(
+ :case_log,
+ :in_progress,
+ owning_organisation: user.organisation,
+ managing_organisation: user.organisation,
+ )
+ end
+ let(:id) { case_log.id }
+
+ before do
+ RequestHelper.stub_http_requests
+ sign_in user
+ end
+
+ context "when exclusive checkbox is selected", js: true do
+ it "deselects all other checkboxes" do
+ visit("/logs/#{id}/accessibility-requirements")
+ page.check("case-log-accessibility-requirements-housingneeds-a-field", allow_label_click: true)
+ click_button("Save and continue")
+
+ case_log.reload
+ expect(case_log["housingneeds_a"]).to eq("Yes")
+
+ visit("/logs/#{id}/accessibility-requirements")
+ page.check("case-log-accessibility-requirements-housingneeds-h-field", allow_label_click: true)
+ click_button("Save and continue")
+
+ case_log.reload
+ expect(case_log["housingneeds_a"]).to eq("No")
+ expect(case_log["housingneeds_h"]).to eq("Yes")
+ end
+ end
+end
diff --git a/spec/features/form/tasklist_page_spec.rb b/spec/features/form/tasklist_page_spec.rb
index 12a6afd6f..ba731e05e 100644
--- a/spec/features/form/tasklist_page_spec.rb
+++ b/spec/features/form/tasklist_page_spec.rb
@@ -33,12 +33,12 @@ RSpec.describe "Task List" do
it "shows the number of completed sections if no sections are completed" do
visit("/logs/#{empty_case_log.id}")
- expect(page).to have_content("You have completed 0 of 10 sections.")
+ expect(page).to have_content("You have completed 0 of 9 sections.")
end
it "shows the number of completed sections if one section is completed" do
answer_all_questions_in_income_subsection(empty_case_log)
visit("/logs/#{empty_case_log.id}")
- expect(page).to have_content("You have completed 1 of 10 sections.")
+ expect(page).to have_content("You have completed 1 of 9 sections.")
end
end
diff --git a/spec/features/form/validations_spec.rb b/spec/features/form/validations_spec.rb
index e740bd9b4..19eb63394 100644
--- a/spec/features/form/validations_spec.rb
+++ b/spec/features/form/validations_spec.rb
@@ -23,6 +23,16 @@ RSpec.describe "validations" do
managing_organisation: user.organisation,
)
end
+ let(:completed_without_declaration) do
+ FactoryBot.create(
+ :case_log,
+ :completed,
+ owning_organisation: user.organisation,
+ managing_organisation: user.organisation,
+ status: 1,
+ declaration: nil,
+ )
+ end
let(:id) { case_log.id }
describe "Question validation" do
@@ -160,4 +170,25 @@ RSpec.describe "validations" do
end
end
end
+
+ describe "Submission validation" do
+ context "when tenant has not seen the privacy notice" do
+ it "shows a warning" do
+ visit("/logs/#{completed_without_declaration.id}/declaration")
+ expect(page).to have_current_path("/logs/#{completed_without_declaration.id}/declaration")
+ click_button("Submit lettings log")
+ expect(page).to have_content("You must show the DLUHC privacy notice to the tenant")
+ end
+ end
+
+ context "when tenant has seen the privacy notice" do
+ it "lets submit the log" do
+ completed_without_declaration.update!({ declaration: "Yes" })
+ visit("/logs/#{completed_without_declaration.id}/declaration")
+ expect(page).to have_current_path("/logs/#{completed_without_declaration.id}/declaration")
+ click_button("Submit lettings log")
+ expect(page).to have_current_path("/logs")
+ end
+ end
+ end
end
diff --git a/spec/fixtures/complete_case_log.json b/spec/fixtures/complete_case_log.json
index 577523dc9..a172080c3 100644
--- a/spec/fixtures/complete_case_log.json
+++ b/spec/fixtures/complete_case_log.json
@@ -40,7 +40,7 @@
"age8": 2,
"sex8": "Prefer not to say",
"ecstat8": "Child under 16",
- "homeless": "Yes - other homelessness",
+ "homeless": "No",
"reason": 1,
"underoccupation_benefitcap": "No",
"leftreg": "No - they left up to 5 years ago",
@@ -74,8 +74,9 @@
"mrcyear": 2020,
"offered": 2,
"wchair": "Yes",
- "net_income_known": "Weekly",
+ "net_income_known": "Yes",
"earnings": 150,
+ "incfreq": "Weekly",
"benefits": "Some",
"hb": "Universal Credit with housing element (excluding housing benefit)",
"period": "Every 2 weeks",
@@ -89,8 +90,7 @@
"lawaitlist": "Less than 1 year",
"prevloc": "Ashford",
"previous_postcode": "SE2 6RT",
- "reasonpref": "Yes",
- "reasonable_preference_reason": "dummy",
+ "reasonpref": "No",
"cbl": "Yes",
"chr": "Yes",
"cap": "No",
@@ -115,15 +115,13 @@
"illness_type_9": "No",
"illness_type_10": "No",
"condition_effects_prefer_not_to_say": "Yes",
- "rp_homeless": "Yes",
+ "rp_homeless": "No",
"rp_insan_unsat": "No",
"rp_medwel": "No",
"rp_hardship": "No",
"rp_dontknow": "No",
"discarded_at": "05/05/2020",
"override_net_income_validation": "",
- "gdpr_acceptance": "",
- "gdpr_declined": "",
"property_owner_organisation": "",
"property_manager_organisation": "",
"rent_type": "Social Rent",
@@ -147,6 +145,7 @@
"household_charge": "Yes",
"is_carehome": "Yes",
"chcharge": "6",
- "letting_in_sheltered_accomodation": "No"
+ "letting_in_sheltered_accomodation": "No",
+ "declaration": "Yes"
}
}
diff --git a/spec/fixtures/forms/2021_2022.json b/spec/fixtures/forms/2021_2022.json
index 3a322369e..1498880fc 100644
--- a/spec/fixtures/forms/2021_2022.json
+++ b/spec/fixtures/forms/2021_2022.json
@@ -104,6 +104,15 @@
}
}
}
+ },
+ "propcode": {
+ "questions": {
+ "propcode": {
+ "check_answer_label": "",
+ "header": "property reference?",
+ "type": "text"
+ }
+ }
}
}
},
@@ -358,7 +367,11 @@
"step": 1,
"width": 5,
"prefix": "£",
- "suffix": "incfreq"
+ "suffix": [
+ { "label": "every week", "depends_on" : { "incfreq": "Weekly" } },
+ { "label": "every month", "depends_on" : { "incfreq": "Monthly" } },
+ { "label": "every month", "depends_on" : { "incfreq": "Yearly" } }
+ ]
},
"incfreq": {
"check_answer_label": "Income Frequency",
@@ -626,32 +639,6 @@
}
}
},
- "setup": {
- "label": "Before you start",
- "subsections": {
- "setup": {
- "label": "Set up your lettings log",
- "pages": {
- "gdpr_acceptance": {
- "header": "",
- "description": "",
- "questions": {
- "gdpr_acceptance": {
- "check_answer_label": "Privacy notice seen",
- "header": "Has the tenant or buyer seen the Department for Levelling Up, Housing and Communities (DLUHC) privacy notice?",
- "hint_text": "You must show the privacy notice to the tenant or buyer before you can use this service.",
- "type": "radio",
- "answer_options": {
- "0": "Yes",
- "1": "No"
- }
- }
- }
- }
- }
- }
- }
- },
"submission": {
"label": "Submission",
"subsections": {
@@ -661,19 +648,18 @@
"household_characteristics": "completed",
"household_needs": "completed",
"tenancy_information": "completed",
- "property_information": "completed",
- "income_and_benefits": "completed",
- "rent_and_charges": "completed",
- "local_authority": "completed"
+ "property_information": "completed"
}],
"pages": {
"declaration": {
"questions": {
"declaration": {
"check_answer_label": "",
- "header": "What is the tenant code?",
- "type": "text",
- "width": 10
+ "header": "Submit your lettings log ",
+ "type": "checkbox",
+ "answer_options": {
+ "declaration": "The tenant has seen the Department for Levelling Up, Housing & Communities (DLUHC) privacy notice"
+ }
}
}
}
diff --git a/spec/fixtures/forms/2022_2023.json b/spec/fixtures/forms/2022_2023.json
index 68d1c1247..b599e3067 100644
--- a/spec/fixtures/forms/2022_2023.json
+++ b/spec/fixtures/forms/2022_2023.json
@@ -7,30 +7,6 @@
"setup": {
"label": "Set up your lettings log",
"pages": {
- "gdpr_acceptance": {
- "header": "",
- "description": "",
- "questions": {
- "gdpr_acceptance": {
- "check_answer_label": "Privacy notice seen",
- "header": "Has the tenant or buyer seen the Department for Levelling Up, Housing and Communities (DLUHC) privacy notice?",
- "hint_text": "You must show the privacy notice to the tenant or buyer before you can use this service.",
- "type": "radio",
- "answer_options": {
- "0": "Yes",
- "1": "No"
- }
- }
- }
- },
- "gdpr_declined": {
- "header": "You cannot use this service",
- "hint_text": "",
- "description": "We cannot accept data about a tenant or buyer unless they’ve seen the DLUHC privacy notice.",
- "questions": {
- },
- "depends_on": [{ "gdpr_acceptance": "No" }]
- },
"renewal": {
"header": "",
"description": "",
@@ -45,10 +21,7 @@
"0": "No"
}
}
- },
- "depends_on": [{
- "gdpr_acceptance": "Yes"
- }]
+ }
},
"startdate": {
"header": "",
@@ -60,10 +33,7 @@
"hint_text": "For example, 27 3 2007",
"type": "date"
}
- },
- "depends_on": [{
- "gdpr_acceptance": "Yes"
- }]
+ }
},
"about_this_letting": {
"header": "Tell us about this letting",
@@ -101,10 +71,7 @@
"1": "General needs"
}
}
- },
- "depends_on": [{
- "gdpr_acceptance": "Yes"
- }]
+ }
}
}
}
diff --git a/spec/helpers/check_answers_helper_spec.rb b/spec/helpers/check_answers_helper_spec.rb
index 7778ed0d9..0d1a94d6d 100644
--- a/spec/helpers/check_answers_helper_spec.rb
+++ b/spec/helpers/check_answers_helper_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe CheckAnswersHelper do
context "when a section hasn't been completed yet" do
it "returns that you have unanswered questions" do
expect(display_answered_questions_summary(subsection, case_log))
- .to match(/You have answered 2 of 4 questions./)
+ .to match(/You have answered 2 of 5 questions./)
end
end
@@ -17,6 +17,7 @@ RSpec.describe CheckAnswersHelper do
it "returns that you have answered all the questions" do
case_log.sex1 = "F"
case_log.other_hhmemb = 0
+ case_log.propcode = "123"
expect(display_answered_questions_summary(subsection, case_log))
.to match(/You answered all the questions./)
expect(display_answered_questions_summary(subsection, case_log))
diff --git a/spec/helpers/tasklist_helper_spec.rb b/spec/helpers/tasklist_helper_spec.rb
index 5b99cdf69..a9834a165 100644
--- a/spec/helpers/tasklist_helper_spec.rb
+++ b/spec/helpers/tasklist_helper_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe TasklistHelper do
describe "get sections count" do
it "returns the total of sections if no status is given" do
- expect(get_subsections_count(empty_case_log)).to eq(10)
+ expect(get_subsections_count(empty_case_log)).to eq(9)
end
it "returns 0 sections for completed sections if no sections are completed" do
@@ -25,7 +25,7 @@ RSpec.describe TasklistHelper do
end
it "returns the number of not started sections" do
- expect(get_subsections_count(empty_case_log, :not_started)).to eq(9)
+ expect(get_subsections_count(empty_case_log, :not_started)).to eq(8)
end
it "returns the number of sections in progress" do
diff --git a/spec/models/admin_user_spec.rb b/spec/models/admin_user_spec.rb
index a112bfb5b..ca25d39b8 100644
--- a/spec/models/admin_user_spec.rb
+++ b/spec/models/admin_user_spec.rb
@@ -20,33 +20,46 @@ RSpec.describe AdminUser, type: :model do
)
}.to raise_error(ActiveRecord::RecordInvalid)
end
- end
- it "requires an email" do
- expect {
- described_class.create!(
- password: "password123",
- phone: "075752137",
- )
- }.to raise_error(ActiveRecord::RecordInvalid)
- end
+ it "requires an email" do
+ expect {
+ described_class.create!(
+ password: "password123",
+ phone: "075752137",
+ )
+ }.to raise_error(ActiveRecord::RecordInvalid)
+ end
- it "requires a password" do
- expect {
- described_class.create!(
- email: "admin_test@example.com",
- phone: "075752137",
- )
- }.to raise_error(ActiveRecord::RecordInvalid)
+ it "requires a password" do
+ expect {
+ described_class.create!(
+ email: "admin_test@example.com",
+ phone: "075752137",
+ )
+ }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+
+ it "can be created" do
+ expect {
+ described_class.create!(
+ email: "admin_test@example.com",
+ password: "password123",
+ phone: "075752137",
+ )
+ }.to change(described_class, :count).by(1)
+ end
end
- it "can be created" do
- expect {
- described_class.create!(
- email: "admin_test@example.com",
- password: "password123",
- phone: "075752137",
- )
- }.to change(described_class, :count).by(1)
+ describe "paper trail" do
+ let(:admin_user) { FactoryBot.create(:admin_user) }
+
+ it "creates a record of changes to a log" do
+ expect { admin_user.update!(phone: "09673867853") }.to change(admin_user.versions, :count).by(1)
+ end
+
+ it "allows case logs to be restored to a previous version" do
+ admin_user.update!(phone: "09673867853")
+ expect(admin_user.paper_trail.previous_version.phone).to eq("07563867654")
+ end
end
end
diff --git a/spec/models/case_log_spec.rb b/spec/models/case_log_spec.rb
index 12afa50a7..e3f4d784b 100644
--- a/spec/models/case_log_spec.rb
+++ b/spec/models/case_log_spec.rb
@@ -1087,17 +1087,6 @@ RSpec.describe CaseLog do
end
end
- context "when saving net_income" do
- it "infers the income frequency" do
- case_log.update!(net_income_known: "Weekly")
- expect(case_log.reload.incfreq).to eq("Weekly")
- case_log.update!(net_income_known: "Monthly")
- expect(case_log.reload.incfreq).to eq("Monthly")
- case_log.update!(net_income_known: "Annually")
- expect(case_log.reload.incfreq).to eq("Yearly")
- end
- end
-
context "when saving rent and charges" do
let!(:case_log) do
described_class.create({
@@ -1159,6 +1148,35 @@ RSpec.describe CaseLog do
record_from_db = ActiveRecord::Base.connection.execute("select has_benefits from case_logs where id=#{case_log.id}").to_a[0]
expect(record_from_db["has_benefits"]).to eq("Yes")
end
+
+ context "when it is a renewal" do
+ let!(:case_log) do
+ described_class.create({
+ managing_organisation: organisation,
+ owning_organisation: organisation,
+ renewal: "Yes",
+ year: 2021,
+ })
+ end
+
+ it "correctly derives and saves layear" do
+ record_from_db = ActiveRecord::Base.connection.execute("select layear from case_logs where id=#{case_log.id}").to_a[0]
+ expect(record_from_db["layear"]).to eq(2)
+ expect(case_log["layear"]).to eq("Less than 1 year")
+ end
+
+ it "correctly derives and saves underoccupation_benefitcap if year is 2021" do
+ record_from_db = ActiveRecord::Base.connection.execute("select underoccupation_benefitcap from case_logs where id=#{case_log.id}").to_a[0]
+ expect(record_from_db["underoccupation_benefitcap"]).to eq(2)
+ expect(case_log["underoccupation_benefitcap"]).to eq("No")
+ end
+
+ it "correctly derives and saves homeless" do
+ record_from_db = ActiveRecord::Base.connection.execute("select homeless from case_logs where id=#{case_log.id}").to_a[0]
+ expect(record_from_db["homeless"]).to eq(1)
+ expect(case_log["homeless"]).to eq("No")
+ end
+ end
end
describe "resetting invalidated fields" do
@@ -1178,4 +1196,17 @@ RSpec.describe CaseLog do
end
end
end
+
+ describe "paper trail" do
+ let(:case_log) { FactoryBot.create(:case_log, :in_progress) }
+
+ it "creates a record of changes to a log" do
+ expect { case_log.update!(age1: 64) }.to change(case_log.versions, :count).by(1)
+ end
+
+ it "allows case logs to be restored to a previous version" do
+ case_log.update!(age1: 63)
+ expect(case_log.paper_trail.previous_version.age1).to eq(17)
+ end
+ end
end
diff --git a/spec/models/form/question_spec.rb b/spec/models/form/question_spec.rb
index 2d166b73b..7fe6cff28 100644
--- a/spec/models/form/question_spec.rb
+++ b/spec/models/form/question_spec.rb
@@ -99,16 +99,17 @@ RSpec.describe Form::Question, type: :model do
context "with a case log" do
let(:case_log) { FactoryBot.build(:case_log, :in_progress) }
+ let(:question_id) { "incfreq" }
it "has an answer label" do
- case_log.earnings = 100
- expect(question.answer_label(case_log)).to eq("100")
+ case_log.incfreq = "Weekly"
+ expect(question.answer_label(case_log)).to eq("Weekly")
end
it "has an update answer link text helper" do
- expect(question.update_answer_link_name(case_log)).to eq("Answer income")
- case_log[question_id] = 5
- expect(question.update_answer_link_name(case_log)).to eq("Change income")
+ expect(question.update_answer_link_name(case_log)).to match(/Answer/)
+ case_log["incfreq"] = "Weekly"
+ expect(question.update_answer_link_name(case_log)).to match(/Change/)
end
context "when type is date" do
@@ -155,6 +156,23 @@ RSpec.describe Form::Question, type: :model do
expect(question.enabled?(case_log)).to be true
end
end
+
+ context "when answers have a suffix dependent on another answer" do
+ let(:section_id) { "rent_and_charges" }
+ let(:subsection_id) { "income_and_benefits" }
+ let(:page_id) { "net_income" }
+ let(:question_id) { "earnings" }
+
+ it "displays the correct label for given suffix and answer the suffix depends on" do
+ case_log.incfreq = "Weekly"
+ case_log.earnings = 500
+ expect(question.answer_label(case_log)).to eq("£500.00 every week")
+ case_log.incfreq = "Monthly"
+ expect(question.answer_label(case_log)).to eq("£500.00 every month")
+ case_log.incfreq = "Yearly"
+ expect(question.answer_label(case_log)).to eq("£500.00 every year")
+ end
+ end
end
describe ".completed?" do
@@ -169,17 +187,5 @@ RSpec.describe Form::Question, type: :model do
expect(question.completed?(case_log)).to be(true)
end
end
-
- context "when the gdpr acceptance is No" do
- let(:section_id) { "setup" }
- let(:subsection_id) { "setup" }
- let(:page_id) { "gdpr_acceptance" }
- let(:question_id) { "gdpr_acceptance" }
-
- it "returns false" do
- case_log["gdpr_acceptance"] = "No"
- expect(question.completed?(case_log)).to be(false)
- end
- end
end
end
diff --git a/spec/models/form/subsection_spec.rb b/spec/models/form/subsection_spec.rb
index f48c3ef4d..d086356f4 100644
--- a/spec/models/form/subsection_spec.rb
+++ b/spec/models/form/subsection_spec.rb
@@ -1,4 +1,5 @@
require "rails_helper"
+require_relative "../../request_helper"
RSpec.describe Form::Subsection, type: :model do
subject(:sub_section) { described_class.new(subsection_id, subsection_definition, section) }
@@ -11,6 +12,10 @@ RSpec.describe Form::Subsection, type: :model do
let(:subsection_id) { "household_characteristics" }
let(:subsection_definition) { section_definition["subsections"][subsection_id] }
+ before do
+ RequestHelper.stub_http_requests
+ end
+
it "has an id" do
expect(sub_section.id).to eq(subsection_id)
end
@@ -20,12 +25,12 @@ RSpec.describe Form::Subsection, type: :model do
end
it "has pages" do
- expected_pages = %w[tenant_code person_1_age person_1_gender household_number_of_other_members]
+ expected_pages = %w[tenant_code person_1_age person_1_gender household_number_of_other_members propcode]
expect(sub_section.pages.map(&:id)).to eq(expected_pages)
end
it "has questions" do
- expected_questions = %w[tenant_code age1 sex1 other_hhmemb relat2 age2 sex2 ecstat2]
+ expected_questions = %w[tenant_code age1 sex1 other_hhmemb relat2 age2 sex2 ecstat2 propcode]
expect(sub_section.questions.map(&:id)).to eq(expected_questions)
end
@@ -53,9 +58,9 @@ RSpec.describe Form::Subsection, type: :model do
end
it "has question helpers for the number of applicable questions" do
- expected_questions = %w[tenant_code age1 sex1 other_hhmemb]
+ expected_questions = %w[tenant_code age1 sex1 other_hhmemb propcode]
expect(sub_section.applicable_questions(case_log).map(&:id)).to eq(expected_questions)
- expect(sub_section.applicable_questions_count(case_log)).to eq(4)
+ expect(sub_section.applicable_questions_count(case_log)).to eq(5)
end
it "has question helpers for the number of answered questions" do
@@ -72,28 +77,25 @@ RSpec.describe Form::Subsection, type: :model do
end
it "has a question helpers for the unanswered questions" do
- expected_questions = %w[sex1 other_hhmemb]
+ expected_questions = %w[sex1 other_hhmemb propcode]
expect(sub_section.unanswered_questions(case_log).map(&:id)).to eq(expected_questions)
end
end
- context "when the privacy notice has not been shown" do
- let(:section_id) { "setup" }
- let(:subsection_id) { "setup" }
- let(:case_log) { FactoryBot.build(:case_log, :about_completed, gdpr_acceptance: "No") }
-
- it "does not mark the section as completed" do
- expect(sub_section.status(case_log)).to eq(:in_progress)
- end
- end
-
context "with a completed case log" do
let(:case_log) { FactoryBot.build(:case_log, :completed) }
+ let(:case_log_too) { FactoryBot.build(:case_log, :in_progress) }
it "has a status" do
expect(sub_section.status(case_log)).to eq(:completed)
end
+ it "has a status when optional fields are not filled" do
+ case_log.update!({ propcode: nil })
+ case_log.reload
+ expect(sub_section.status(case_log)).to eq(:completed)
+ end
+
it "has status helpers" do
expect(sub_section.is_incomplete?(case_log)).to be(false)
expect(sub_section.is_started?(case_log)).to be(true)
diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb
index 4ab1b6ada..93bca44c3 100644
--- a/spec/models/form_spec.rb
+++ b/spec/models/form_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Form, type: :model do
describe "next_incomplete_section_redirect_path" do
let(:case_log) { FactoryBot.build(:case_log, :in_progress) }
let(:subsection) { form.get_subsection("household_characteristics") }
- let(:later_subsection) { form.get_subsection("setup") }
+ let(:later_subsection) { form.get_subsection("declaration") }
context "when a user is on the check answers page for a subsection" do
def answer_household_needs(case_log)
@@ -85,10 +85,6 @@ RSpec.describe Form, type: :model do
case_log.mrcdate = Time.zone.parse("03/11/2019")
end
- def answer_local_gdpr_acceptance(case_log)
- case_log.gdpr_acceptance = "Yes"
- end
-
before do
case_log.tenant_code = "123"
case_log.age1 = 35
@@ -111,15 +107,14 @@ RSpec.describe Form, type: :model do
expect(form.next_incomplete_section_redirect_path(subsection, case_log)).to eq("tenancy-code")
end
- it "returns the next incomplete section by cycling back around if next subsections are completed" do
- answer_local_gdpr_acceptance(case_log)
- expect(form.next_incomplete_section_redirect_path(later_subsection, case_log)).to eq("armed-forces")
- end
-
it "returns the declaration section for a completed case log" do
expect(form.next_incomplete_section_redirect_path(subsection, completed_case_log)).to eq("declaration")
end
+ it "returns the next incomplete section by cycling back around if next subsections are completed" do
+ expect(form.next_incomplete_section_redirect_path(later_subsection, case_log)).to eq("armed-forces")
+ end
+
it "returns the declaration section if all sections are complete but the case log is in progress" do
answer_household_needs(case_log)
answer_tenancy_information(case_log)
@@ -128,7 +123,6 @@ RSpec.describe Form, type: :model do
answer_income_and_benefits(case_log)
answer_rent_and_charges(case_log)
answer_local_authority(case_log)
- answer_local_gdpr_acceptance(case_log)
expect(form.next_incomplete_section_redirect_path(subsection, case_log)).to eq("declaration")
end
diff --git a/spec/models/organisation_spec.rb b/spec/models/organisation_spec.rb
index c8fc8b64a..9aa6c75cd 100644
--- a/spec/models/organisation_spec.rb
+++ b/spec/models/organisation_spec.rb
@@ -49,4 +49,17 @@ RSpec.describe Organisation, type: :model do
end
end
end
+
+ describe "paper trail" do
+ let(:organisation) { FactoryBot.create(:organisation) }
+
+ it "creates a record of changes to a log" do
+ expect { organisation.update!(name: "new test name") }.to change(organisation.versions, :count).by(1)
+ end
+
+ it "allows case logs to be restored to a previous version" do
+ organisation.update!(name: "new test name")
+ expect(organisation.paper_trail.previous_version.name).to eq("DLUHC")
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 3195b73a0..934ab57a1 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -47,4 +47,17 @@ RSpec.describe User, type: :model do
expect(user.data_coordinator?).to be false
end
end
+
+ describe "paper trail" do
+ let(:user) { FactoryBot.create(:user) }
+
+ it "creates a record of changes to a log" do
+ expect { user.update!(name: "new test name") }.to change(user.versions, :count).by(1)
+ end
+
+ it "allows case logs to be restored to a previous version" do
+ user.update!(name: "new test name")
+ expect(user.paper_trail.previous_version.name).to eq("Danny Rojas")
+ end
+ end
end
diff --git a/spec/models/validations/financial_validations_spec.rb b/spec/models/validations/financial_validations_spec.rb
new file mode 100644
index 000000000..1cbd1cde1
--- /dev/null
+++ b/spec/models/validations/financial_validations_spec.rb
@@ -0,0 +1,24 @@
+require "rails_helper"
+
+RSpec.describe Validations::FinancialValidations do
+ subject(:financial_validator) { validator_class.new }
+
+ let(:validator_class) { Class.new { include Validations::FinancialValidations } }
+ let(:record) { FactoryBot.create(:case_log) }
+
+ describe "earnings and income frequency" do
+ it "when earnings are provided it validates that income frequency must be provided" do
+ record.earnings = 500
+ record.incfreq = nil
+ financial_validator.validate_net_income(record)
+ expect(record.errors["incfreq"]).to include(match I18n.t("validations.financial.earnings.freq_missing"))
+ end
+
+ it "when income frequency is provided it validates that earnings must be provided" do
+ record.earnings = nil
+ record.incfreq = "Weekly"
+ financial_validator.validate_net_income(record)
+ expect(record.errors["earnings"]).to include(match I18n.t("validations.financial.earnings.earnings_missing"))
+ end
+ end
+end
diff --git a/spec/requests/case_logs_controller_spec.rb b/spec/requests/case_logs_controller_spec.rb
index 612d1b74d..b9ae0105b 100644
--- a/spec/requests/case_logs_controller_spec.rb
+++ b/spec/requests/case_logs_controller_spec.rb
@@ -32,84 +32,104 @@ RSpec.describe CaseLogsController, type: :request do
let(:in_progress) { "in_progress" }
let(:completed) { "completed" }
- let(:params) do
- {
- "owning_organisation_id": owning_organisation.id,
- "managing_organisation_id": managing_organisation.id,
- "tenant_code": tenant_code,
- "age1": age1,
- "property_postcode": property_postcode,
- "offered": offered,
- }
- end
-
- before do
- post "/logs", headers: headers, params: params.to_json
- end
-
- it "returns http success" do
- expect(response).to have_http_status(:success)
- end
-
- it "returns a serialized Case Log" do
- json_response = JSON.parse(response.body)
- expect(json_response.keys).to match_array(CaseLog.new.attributes.keys)
- end
+ context "when API" do
+ let(:params) do
+ {
+ "owning_organisation_id": owning_organisation.id,
+ "managing_organisation_id": managing_organisation.id,
+ "tenant_code": tenant_code,
+ "age1": age1,
+ "property_postcode": property_postcode,
+ "offered": offered,
+ }
+ end
- it "creates a case log with the values passed" do
- json_response = JSON.parse(response.body)
- expect(json_response["tenant_code"]).to eq(tenant_code)
- expect(json_response["age1"]).to eq(age1)
- expect(json_response["property_postcode"]).to eq(property_postcode)
- end
+ before do
+ post "/logs", headers: headers, params: params.to_json
+ end
- context "with invalid json parameters" do
- let(:age1) { 2000 }
- let(:offered) { 21 }
+ it "returns http success" do
+ expect(response).to have_http_status(:success)
+ end
- it "validates case log parameters" do
+ it "returns a serialized Case Log" do
json_response = JSON.parse(response.body)
- expect(response).to have_http_status(:unprocessable_entity)
- expect(json_response["errors"]).to match_array([["offered", [I18n.t("validations.property.offered.relet_number")]], ["age1", [I18n.t("validations.household.age.must_be_valid", lower_bound: 16)]]])
+ expect(json_response.keys).to match_array(CaseLog.new.attributes.keys)
end
- end
- context "with a partial case log submission" do
- it "marks the record as in_progress" do
+ it "creates a case log with the values passed" do
json_response = JSON.parse(response.body)
- expect(json_response["status"]).to eq(in_progress)
+ expect(json_response["tenant_code"]).to eq(tenant_code)
+ expect(json_response["age1"]).to eq(age1)
+ expect(json_response["property_postcode"]).to eq(property_postcode)
end
- end
- context "with a complete case log submission" do
- let(:org_params) do
- {
- "case_log" => {
- "owning_organisation_id" => owning_organisation.id,
- "managing_organisation_id" => managing_organisation.id,
- },
- }
+ context "with invalid json parameters" do
+ let(:age1) { 2000 }
+ let(:offered) { 21 }
+
+ it "validates case log parameters" do
+ json_response = JSON.parse(response.body)
+ expect(response).to have_http_status(:unprocessable_entity)
+ expect(json_response["errors"]).to match_array([["offered", [I18n.t("validations.property.offered.relet_number")]], ["age1", [I18n.t("validations.household.age.must_be_valid", lower_bound: 16)]]])
+ end
end
- let(:case_log_params) { JSON.parse(File.open("spec/fixtures/complete_case_log.json").read) }
- let(:params) do
- case_log_params.merge(org_params) { |_k, a_val, b_val| a_val.merge(b_val) }
+
+ context "with a partial case log submission" do
+ it "marks the record as in_progress" do
+ json_response = JSON.parse(response.body)
+ expect(json_response["status"]).to eq(in_progress)
+ end
end
- it "marks the record as completed" do
- json_response = JSON.parse(response.body)
+ context "with a complete case log submission" do
+ let(:org_params) do
+ {
+ "case_log" => {
+ "owning_organisation_id" => owning_organisation.id,
+ "managing_organisation_id" => managing_organisation.id,
+ },
+ }
+ end
+ let(:case_log_params) { JSON.parse(File.open("spec/fixtures/complete_case_log.json").read) }
+ let(:params) do
+ case_log_params.merge(org_params) { |_k, a_val, b_val| a_val.merge(b_val) }
+ end
- expect(json_response).not_to have_key("errors")
- expect(json_response["status"]).to eq(completed)
+ it "marks the record as completed" do
+ json_response = JSON.parse(response.body)
+
+ expect(json_response).not_to have_key("errors")
+ expect(json_response["status"]).to eq(completed)
+ end
+ end
+
+ context "with a request containing invalid credentials" do
+ let(:basic_credentials) do
+ ActionController::HttpAuthentication::Basic.encode_credentials(api_username, "Oops")
+ end
+
+ it "returns 401" do
+ expect(response).to have_http_status(:unauthorized)
+ end
end
end
- context "with a request containing invalid credentials" do
- let(:basic_credentials) do
- ActionController::HttpAuthentication::Basic.encode_credentials(api_username, "Oops")
+ context "when UI" do
+ let(:user) { FactoryBot.create(:user) }
+ let(:headers) { { "Accept" => "text/html" } }
+
+ before do
+ RequestHelper.stub_http_requests
+ sign_in user
+ post "/logs", headers: headers
end
- it "returns 401" do
- expect(response).to have_http_status(:unauthorized)
+ it "tracks who created the record" do
+ created_id = response.location.match(/[0-9]+/)[0]
+ whodunnit_actor = CaseLog.find_by(id: created_id).versions.last.actor
+ expect(whodunnit_actor).to be_a(User)
+ expect(whodunnit_actor.id).to eq(user.id)
end
end
end
@@ -197,7 +217,7 @@ RSpec.describe CaseLogsController, type: :request do
end
it "displays a section status for a case log" do
- assert_select ".govuk-tag", text: /Not started/, count: 9
+ assert_select ".govuk-tag", text: /Not started/, count: 8
assert_select ".govuk-tag", text: /Completed/, count: 0
assert_select ".govuk-tag", text: /Cannot start yet/, count: 1
end
@@ -219,7 +239,7 @@ RSpec.describe CaseLogsController, type: :request do
end
it "displays a section status for a case log" do
- assert_select ".govuk-tag", text: /Not started/, count: 8
+ assert_select ".govuk-tag", text: /Not started/, count: 7
assert_select ".govuk-tag", text: /Completed/, count: 1
assert_select ".govuk-tag", text: /Cannot start yet/, count: 1
end
@@ -398,9 +418,8 @@ RSpec.describe CaseLogsController, type: :request do
expect(response).to have_http_status(:success)
end
- it "soft deletes the case log" do
+ it "deletes the case log" do
expect { CaseLog.find(id) }.to raise_error(ActiveRecord::RecordNotFound)
- expect(CaseLog.with_discarded.find(id)).to be_a(CaseLog)
end
context "with an invalid case log id" do
@@ -425,7 +444,7 @@ RSpec.describe CaseLogsController, type: :request do
context "when a case log deletion fails" do
before do
allow(CaseLog).to receive(:find_by).and_return(case_log)
- allow(case_log).to receive(:discard).and_return(false)
+ allow(case_log).to receive(:delete).and_return(false)
delete "/logs/#{id}", headers: headers
end
diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb
index 3d77492b7..f4b49e129 100644
--- a/spec/requests/form_controller_spec.rb
+++ b/spec/requests/form_controller_spec.rb
@@ -155,6 +155,13 @@ RSpec.describe FormController, type: :request do
expect(case_log.age1).to eq(answer)
expect(case_log.age2).to be nil
end
+
+ it "tracks who updated the record" do
+ case_log.reload
+ whodunnit_actor = case_log.versions.last.actor
+ expect(whodunnit_actor).to be_a(User)
+ expect(whodunnit_actor.id).to eq(user.id)
+ end
end
end
diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb
index 24ca82101..b92c8da53 100644
--- a/spec/requests/organisations_controller_spec.rb
+++ b/spec/requests/organisations_controller_spec.rb
@@ -185,6 +185,13 @@ RSpec.describe OrganisationsController, type: :request do
follow_redirect!
expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
end
+
+ it "tracks who updated the record" do
+ organisation.reload
+ whodunnit_actor = organisation.versions.last.actor
+ expect(whodunnit_actor).to be_a(User)
+ expect(whodunnit_actor.id).to eq(user.id)
+ end
end
context "with an organisation that the user does not belong to" do
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index 59997f3ce..66cbeb5c2 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -196,6 +196,13 @@ RSpec.describe UsersController, type: :request do
user.reload
expect(user.name).to eq(new_value)
end
+
+ it "tracks who updated the record" do
+ user.reload
+ whodunnit_actor = user.versions.last.actor
+ expect(whodunnit_actor).to be_a(User)
+ expect(whodunnit_actor.id).to eq(user.id)
+ end
end
context "when the update fails to persist" do
diff --git a/spec/support/controller_macros.rb b/spec/support/controller_macros.rb
index 2e21831dd..680663367 100644
--- a/spec/support/controller_macros.rb
+++ b/spec/support/controller_macros.rb
@@ -6,12 +6,4 @@ module ControllerMacros
sign_in user
end
end
-
- def login_admin_user
- before do
- @request.env["devise.mapping"] = Devise.mappings[:admin_user]
- admin_user = FactoryBot.create(:admin_user)
- sign_in admin_user
- end
- end
end
diff --git a/spec/views/form/page_view_spec.rb b/spec/views/form/page_view_spec.rb
index 5cdeb12e5..5f653dfa6 100644
--- a/spec/views/form/page_view_spec.rb
+++ b/spec/views/form/page_view_spec.rb
@@ -68,6 +68,27 @@ RSpec.describe "form/page" do
expect(rendered).to match(/govuk-input__suffix/)
expect(rendered).to match("every week")
end
+
+ context "when the suffix is conditional and not a string" do
+ let(:question_attributes) do
+ {
+ type: "numeric",
+ prefix: "£",
+ suffix: [
+ { "label": "every week", "depends_on": { "incfreq": "Weekly" } },
+ { "label": "every month", "depends_on": { "incfreq": "Monthly" } },
+ { "label": "every month", "depends_on": { "incfreq": "Yearly" } },
+ ],
+ }
+ end
+
+ it "does not render the suffix" do
+ expect(rendered).not_to match(/govuk-input__suffix/)
+ expect(rendered).not_to match("every week")
+ expect(rendered).not_to match("every month")
+ expect(rendered).not_to match("every year")
+ end
+ end
end
context "with a question containing extra guidance" do