Skip to content

Commit 7bb2ae1

Browse files
committed
Add "nhs_number_first_added_at" field to Patient
* Will be used by Automated Careplus reports Jira-Issue: MAV-7091
1 parent da6262d commit 7bb2ae1

9 files changed

Lines changed: 131 additions & 5 deletions

app/models/patient.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
# invalidated_at :datetime
2323
# local_authority_mhclg_code :string
2424
# nhs_number :string
25+
# nhs_number_first_added_at :datetime
2526
# pending_changes :jsonb not null
2627
# preferred_family_name :string
2728
# preferred_given_name :string
@@ -47,6 +48,7 @@
4748
# index_patients_on_names_family_first (family_name,given_name)
4849
# index_patients_on_names_given_first (given_name,family_name)
4950
# index_patients_on_nhs_number (nhs_number) UNIQUE
51+
# index_patients_on_nhs_number_first_added_at (nhs_number_first_added_at)
5052
# index_patients_on_pending_changes_not_empty (id) WHERE (pending_changes <> '{}'::jsonb)
5153
# index_patients_on_school_id (school_id)
5254
#
@@ -495,6 +497,8 @@ class Patient < ApplicationRecord
495497
it.blank? ? nil : it.normalise_whitespace.gsub(/\s/, "")
496498
end
497499

500+
before_validation :set_nhs_number_first_added_at,
501+
if: :will_save_change_to_nhs_number?
498502
after_update :sync_vaccinations_to_nhs_immunisations_api
499503
after_commit :generate_important_notice_if_needed, on: :update
500504
after_commit :search_vaccinations_from_nhs_immunisations_api, on: :update
@@ -822,6 +826,25 @@ def locations_are_correct_type
822826
end
823827
end
824828

829+
def destroy_childless_parents
830+
parents_to_check = parents.to_a # Store parents before destroying relationships
831+
832+
# Manually destroy the parent_relationships associated with this Child
833+
parent_relationships.each(&:destroy)
834+
835+
parents_to_check.each do |parent|
836+
parent.destroy! if parent.parent_relationships.count.zero?
837+
end
838+
end
839+
840+
def set_nhs_number_first_added_at
841+
old_nhs_number, new_nhs_number = nhs_number_change_to_be_saved
842+
843+
return unless old_nhs_number.blank? && new_nhs_number.present?
844+
845+
self.nhs_number_first_added_at ||= Time.current
846+
end
847+
825848
def archive_due_to_deceased!
826849
archive_reasons =
827850
teams.map do |team|
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# frozen_string_literal: true
2+
3+
class BackfillNHSNumberFirstAddedAtForPatients < ActiveRecord::Migration[8.1]
4+
disable_ddl_transaction!
5+
6+
BATCH_SIZE = 1000
7+
8+
def up
9+
migration = self.class.name
10+
started_at = Time.zone.now
11+
scope = Patient.where(nhs_number_first_added_at: nil).where.not(nhs_number: nil)
12+
total_records = scope.count
13+
total_batches = (total_records.to_f / BATCH_SIZE).ceil
14+
records_updated = 0
15+
16+
Rails.logger.info(
17+
event: "data_migration_start",
18+
migration:,
19+
total_records:,
20+
batch_size: BATCH_SIZE,
21+
total_batches:
22+
)
23+
24+
scope.in_batches(of: BATCH_SIZE).each_with_index do |batch, index|
25+
updated_count = batch.update_all("nhs_number_first_added_at = created_at")
26+
records_updated += updated_count
27+
28+
Rails.logger.info(
29+
event: "data_migration_batch",
30+
migration:,
31+
batch_number: index + 1,
32+
total_batches:,
33+
updated_count:,
34+
records_updated:
35+
)
36+
end
37+
38+
duration_minutes = ((Time.zone.now - started_at) / 60.0).round
39+
40+
Rails.logger.info(
41+
event: "data_migration_finish",
42+
migration:,
43+
duration_minutes:,
44+
records_updated:
45+
)
46+
end
47+
48+
def down
49+
raise ActiveRecord::IrreversibleMigration
50+
end
51+
end

db/data_schema.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
DataMigrate::Data.define(version: 2026_04_22_155320)
1+
DataMigrate::Data.define(version: 2026_04_22_120100)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# frozen_string_literal: true
2+
3+
class AddNHSNumberFirstAddedAtForPatient < ActiveRecord::Migration[8.1]
4+
def change
5+
add_column :patients, :nhs_number_first_added_at, :datetime
6+
end
7+
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
class AddIndexOnPatientsNHSNumberFirstAddedAt < ActiveRecord::Migration[8.1]
4+
disable_ddl_transaction!
5+
6+
def change
7+
add_index :patients, :nhs_number_first_added_at, algorithm: :concurrently
8+
end
9+
end

db/schema.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@
162162
t.bigint "class_import_id", null: false
163163
t.bigint "parent_relationship_id", null: false
164164
t.index ["class_import_id", "parent_relationship_id"], name: "idx_on_class_import_id_parent_relationship_id_8225058195", unique: true
165-
t.index ["parent_relationship_id", "class_import_id"], name: "idx_on_parent_relationship_id_class_import_id_d7c05d6c2c", unique: true
166165
end
167166

168167
create_table "class_imports_parents", id: false, force: :cascade do |t|
@@ -221,7 +220,6 @@
221220
t.bigint "cohort_import_id", null: false
222221
t.bigint "parent_relationship_id", null: false
223222
t.index ["cohort_import_id", "parent_relationship_id"], name: "idx_on_cohort_import_id_parent_relationship_id_c65e20d1f8", unique: true
224-
t.index ["parent_relationship_id", "cohort_import_id"], name: "idx_on_parent_relationship_id_cohort_import_id_40fb9846d6", unique: true
225223
end
226224

227225
create_table "cohort_imports_parents", id: false, force: :cascade do |t|
@@ -460,7 +458,6 @@
460458
t.bigint "immunisation_import_id", null: false
461459
t.bigint "vaccination_record_id", null: false
462460
t.index ["immunisation_import_id", "vaccination_record_id"], name: "idx_on_immunisation_import_id_vaccination_record_id_588e859772", unique: true
463-
t.index ["vaccination_record_id", "immunisation_import_id"], name: "idx_on_vaccination_record_id_immunisation_import_id_813c516ad7", unique: true
464461
end
465462

466463
create_table "important_notices", force: :cascade do |t|
@@ -763,6 +760,7 @@
763760
t.datetime "invalidated_at"
764761
t.string "local_authority_mhclg_code"
765762
t.string "nhs_number"
763+
t.datetime "nhs_number_first_added_at"
766764
t.jsonb "pending_changes", default: {}, null: false
767765
t.string "preferred_family_name"
768766
t.string "preferred_given_name"
@@ -784,6 +782,7 @@
784782
t.index ["id"], name: "index_patients_on_pending_changes_not_empty", where: "(pending_changes <> '{}'::jsonb)"
785783
t.index ["local_authority_mhclg_code"], name: "index_patients_on_local_authority_mhclg_code"
786784
t.index ["nhs_number"], name: "index_patients_on_nhs_number", unique: true
785+
t.index ["nhs_number_first_added_at"], name: "index_patients_on_nhs_number_first_added_at"
787786
t.index ["school_id"], name: "index_patients_on_school_id"
788787
end
789788

spec/factories/patients.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
# invalidated_at :datetime
2323
# local_authority_mhclg_code :string
2424
# nhs_number :string
25+
# nhs_number_first_added_at :datetime
2526
# pending_changes :jsonb not null
2627
# preferred_family_name :string
2728
# preferred_given_name :string
@@ -47,6 +48,7 @@
4748
# index_patients_on_names_family_first (family_name,given_name)
4849
# index_patients_on_names_given_first (given_name,family_name)
4950
# index_patients_on_nhs_number (nhs_number) UNIQUE
51+
# index_patients_on_nhs_number_first_added_at (nhs_number_first_added_at)
5052
# index_patients_on_pending_changes_not_empty (id) WHERE (pending_changes <> '{}'::jsonb)
5153
# index_patients_on_school_id (school_id)
5254
#
@@ -91,6 +93,7 @@
9193
"#{base}#{check_digit}"
9294
end
9395
end
96+
nhs_number_first_added_at { nhs_number.present? ? Time.current : nil }
9497

9598
given_name { Faker::Name.first_name }
9699
family_name { Faker::Name.last_name }

spec/features/cli_reports_send_to_careplus_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
)
116116

117117
then_the_error_output_includes(
118-
"does not have CarePlus credentials configured"
118+
"needs the CarePlus username, password, and namespace configured to send reports."
119119
)
120120
and_no_request_was_made
121121
end

spec/models/patient_spec.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
# invalidated_at :datetime
2323
# local_authority_mhclg_code :string
2424
# nhs_number :string
25+
# nhs_number_first_added_at :datetime
2526
# pending_changes :jsonb not null
2627
# preferred_family_name :string
2728
# preferred_given_name :string
@@ -47,6 +48,7 @@
4748
# index_patients_on_names_family_first (family_name,given_name)
4849
# index_patients_on_names_given_first (given_name,family_name)
4950
# index_patients_on_nhs_number (nhs_number) UNIQUE
51+
# index_patients_on_nhs_number_first_added_at (nhs_number_first_added_at)
5052
# index_patients_on_pending_changes_not_empty (id) WHERE (pending_changes <> '{}'::jsonb)
5153
# index_patients_on_school_id (school_id)
5254
#
@@ -1307,6 +1309,38 @@
13071309
end
13081310
end
13091311

1312+
describe "NHS number first added timestamp" do
1313+
it "sets nhs_number_first_added_at when an NHS number is first added" do
1314+
patient =
1315+
create(:patient, nhs_number: nil, nhs_number_first_added_at: nil)
1316+
1317+
freeze_time do
1318+
expect { patient.update!(nhs_number: "9449310475") }.to change {
1319+
patient.reload.nhs_number_first_added_at
1320+
}.from(nil).to(Time.current)
1321+
end
1322+
end
1323+
1324+
it "does not clear nhs_number_first_added_at when an NHS number is removed" do
1325+
patient = create(:patient)
1326+
1327+
expect { patient.update!(nhs_number: nil) }.not_to(
1328+
change { patient.reload.nhs_number_first_added_at }
1329+
)
1330+
end
1331+
1332+
it "does not overwrite nhs_number_first_added_at when an NHS number is re-added" do
1333+
patient = create(:patient)
1334+
first_added_at = patient.nhs_number_first_added_at
1335+
1336+
patient.update!(nhs_number: nil)
1337+
1338+
expect { patient.update!(nhs_number: "9449310475") }.not_to change {
1339+
patient.reload.nhs_number_first_added_at
1340+
}.from(first_added_at)
1341+
end
1342+
end
1343+
13101344
describe "#should_search_vaccinations_from_nhs_immunisations_api?" do
13111345
subject(:should_search_vaccinations_from_nhs_immunisations_api?) do
13121346
patient.send(:should_search_vaccinations_from_nhs_immunisations_api?)

0 commit comments

Comments
 (0)