Skip to content

Commit b69b151

Browse files
authored
Merge pull request #5742 from nhsuk/sync-national-reporting-vaccinations-mav-2733
Sync national reporting vaccinations to NHS Immunisations API
2 parents bc9ed13 + 6064b58 commit b69b151

9 files changed

Lines changed: 178 additions & 39 deletions

app/lib/fhir_mapper/vaccination_record.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ class VaccinationRecord
66

77
MAVIS_SYSTEM_NAME =
88
"http://manage-vaccinations-in-schools.nhs.uk/vaccination_records"
9+
MAVIS_NATIONAL_REPORTING_SYSTEM_NAME =
10+
"http://manage-vaccinations-in-schools.nhs.uk/national-reporting/vaccination-records"
911

1012
MILLILITER_SUB_STRINGS = %w[ml millilitre milliliter].freeze
1113

@@ -123,7 +125,15 @@ def self.from_fhir_record(fhir_record, patient:)
123125
private
124126

125127
def fhir_identifier
126-
FHIR::Identifier.new(system: MAVIS_SYSTEM_NAME, value: uuid)
128+
case source
129+
when "bulk_upload"
130+
FHIR::Identifier.new(
131+
system: MAVIS_NATIONAL_REPORTING_SYSTEM_NAME,
132+
value: uuid
133+
)
134+
else
135+
FHIR::Identifier.new(system: MAVIS_SYSTEM_NAME, value: uuid)
136+
end
127137
end
128138

129139
def fhir_vaccination_procedure_extension

app/lib/nhs/immunisations_api.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,12 @@ def should_be_in_immunisations_api?(
262262
vaccination_record,
263263
ignore_nhs_number: false
264264
)
265-
vaccination_record.kept? && vaccination_record.sourced_from_service? &&
265+
vaccination_record.kept? &&
266+
vaccination_record.correct_source_for_nhs_immunisations_api? &&
266267
vaccination_record.administered? &&
267268
Flipper.enabled?(:imms_api_sync_job, vaccination_record.programme) &&
268269
(ignore_nhs_number || vaccination_record.patient.nhs_number.present?) &&
269-
vaccination_record.notify_parents &&
270+
vaccination_record.notify_parents != false &&
270271
vaccination_record.patient.not_invalidated?
271272
end
272273

app/models/concerns/vaccination_record_sync_to_nhs_immunisations_api_concern.rb

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,24 @@ module VaccinationRecordSyncToNHSImmunisationsAPIConcern
44
extend ActiveSupport::Concern
55

66
included do
7-
scope :syncable_to_nhs_immunisations_api,
8-
-> { includes(:patient).sourced_from_service }
7+
scope :with_correct_source_for_nhs_immunisations_api,
8+
-> do
9+
includes(:patient).then do
10+
if Flipper.enabled?(:sync_national_reporting_to_imms_api)
11+
it.sourced_from_service.or(it.sourced_from_bulk_upload)
12+
else
13+
it.sourced_from_service
14+
end
15+
end
16+
end
917

1018
scope :sync_all_to_nhs_immunisations_api,
1119
-> do
1220
programmes =
1321
Programme.all.select { Flipper.enabled?(:imms_api_sync_job, it) }
1422

1523
ids =
16-
syncable_to_nhs_immunisations_api.for_programmes(
24+
with_correct_source_for_nhs_immunisations_api.for_programmes(
1725
programmes
1826
).pluck(:id)
1927

@@ -29,7 +37,13 @@ module VaccinationRecordSyncToNHSImmunisationsAPIConcern
2937
after_commit :queue_sync_to_nhs_immunisations_api
3038
end
3139

32-
def syncable_to_nhs_immunisations_api? = sourced_from_service?
40+
def correct_source_for_nhs_immunisations_api?
41+
sourced_from_service? ||
42+
(
43+
Flipper.enabled?(:sync_national_reporting_to_imms_api) &&
44+
sourced_from_bulk_upload?
45+
)
46+
end
3347

3448
def sync_status
3549
should_be_synced =
@@ -61,14 +75,14 @@ def changes_need_to_be_synced_to_nhs_immunisations_api?
6175

6276
def touch_nhs_immunisations_api_sync_pending_at
6377
return unless Flipper.enabled?(:imms_api_sync_job, programme)
64-
return unless syncable_to_nhs_immunisations_api?
78+
return unless correct_source_for_nhs_immunisations_api?
6579

6680
self.nhs_immunisations_api_sync_pending_at = Time.current
6781
end
6882

6983
def queue_sync_to_nhs_immunisations_api
7084
return unless Flipper.enabled?(:imms_api_sync_job, programme)
71-
return unless syncable_to_nhs_immunisations_api?
85+
return unless correct_source_for_nhs_immunisations_api?
7286
return if nhs_immunisations_api_sync_pending_at.nil?
7387

7488
if nhs_immunisations_api_synced_at &&

config/feature_flags.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ reporting_api: Enables the Commissioner reporting component to authenticate to M
4343
Authorization Code Flow (https://datatracker.ietf.org/doc/html/rfc6749#section-4.1), and retrieve
4444
statistics from /api/reporting/
4545

46+
sync_national_reporting_to_imms_api: |
47+
Sync immunisations records uploaded as part of national reporting to the NHS
48+
Immunisations API.
49+
4650
testing_api: Basic API useful for automated testing.
4751

4852
mmrv: Adds support for MMRV vaccinations

spec/features/import_vaccination_records_bulk_spec.rb

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
given_mavis_logins_are_configured
88
given_i_am_signed_in_as_a_bulk_upload_user
99
given_a_patient_already_exists
10+
and_sending_to_nhs_immunisations_api_is_enabled
1011

1112
when_i_go_to_the_import_page
1213
then_i_should_see_the_upload_link
@@ -29,6 +30,7 @@
2930
and_the_patients_should_now_be_associated_with_the_team
3031
and_the_newly_created_patients_should_be_archived
3132
and_the_existing_patients_should_not_be_archived
33+
and_the_vaccination_records_are_sent_to_the_imms_api
3234

3335
when_i_click_on_a_vaccination_record
3436
then_i_should_see_the_vaccination_record
@@ -80,10 +82,14 @@ def given_a_patient_already_exists
8082
create(:vaccination_record, patient: @existing_patient, team: @team)
8183
end
8284

83-
def and_school_locations_exist
84-
create(:school, urn: "110158")
85-
create(:school, urn: "120026")
86-
create(:school, urn: "144012")
85+
def and_sending_to_nhs_immunisations_api_is_enabled
86+
Flipper.enable(:imms_api_integration)
87+
Flipper.enable(:imms_api_sync_job, Programme.flu)
88+
Flipper.enable(:imms_api_sync_job, Programme.hpv)
89+
Flipper.enable(:sync_national_reporting_to_imms_api)
90+
91+
@stubbed_post_request =
92+
stub_immunisations_api_post(Random.uuid, Random.uuid)
8793
end
8894

8995
def when_i_go_to_the_import_page
@@ -100,16 +106,6 @@ def when_i_click_on_the_upload_link
100106
click_on "Upload records"
101107
end
102108

103-
def when_i_click_on_the_imports_tab
104-
click_on "Imports"
105-
end
106-
107-
def and_i_choose_to_import_child_records
108-
click_on "Upload records"
109-
choose "Vaccination records"
110-
click_on "Continue"
111-
end
112-
113109
def then_i_should_see_the_upload_page
114110
expect(page).to have_content("Upload vaccination records")
115111
end
@@ -189,6 +185,12 @@ def and_the_existing_patients_should_not_be_archived
189185
expect(@existing_patient.archived?(team: @team)).to be false
190186
end
191187

188+
def and_the_vaccination_records_are_sent_to_the_imms_api
189+
SyncVaccinationRecordToNHSJob.drain
190+
191+
expect(@stubbed_post_request).to have_been_requested.times(2)
192+
end
193+
192194
def when_i_click_on_a_vaccination_record
193195
find(".nhsuk-details__summary", text: "2 imported records").click
194196
click_on "WEASLEY, Ron"

spec/lib/fhir_mapper/vaccination_record_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,28 @@
8888

8989
its(:value) { should eq vaccination_record.uuid }
9090
its(:system) { should eq mavis_system }
91+
92+
context "when the vaccination record is from national reporting" do
93+
let(:national_reporting_test_ods_code) { "NR121" }
94+
let(:vaccination_record) do
95+
create(
96+
:vaccination_record,
97+
:sourced_from_bulk_upload,
98+
uploaded_by: User.first,
99+
performed_ods_code: :national_reporting_test_ods_code,
100+
patient:,
101+
programme:,
102+
vaccine: programme.vaccines.first,
103+
outcome: "administered"
104+
)
105+
end
106+
107+
let(:national_reporting_system) do
108+
"http://manage-vaccinations-in-schools.nhs.uk/national-reporting/vaccination-records"
109+
end
110+
111+
its(:system) { should eq national_reporting_system }
112+
end
91113
end
92114

93115
describe "vaccine code" do

spec/lib/nhs/immunisations_api_spec.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,8 +680,12 @@
680680
it { should be false }
681681
end
682682

683-
context "when the vaccination record is not recorded in service" do
684-
let(:session) { nil }
683+
context "when the vaccination record doesn't have the correct source" do
684+
before do
685+
allow(vaccination_record).to receive(
686+
:correct_source_for_nhs_immunisations_api?
687+
).and_return(false)
688+
end
685689

686690
it { should be false }
687691
end
@@ -740,6 +744,12 @@
740744
it { should be false }
741745
end
742746

747+
context "when notify_parents is not set" do
748+
let(:notify_parents) { nil }
749+
750+
it { should be true }
751+
end
752+
743753
context "when the patient is invalidated" do
744754
before { patient.update(invalidated_at: Time.current) }
745755

spec/models/concerns/vaccination_record_sync_to_nhs_immunisations_api_concern_spec.rb

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
context "when the vaccination record isn't syncable" do
3030
before do
3131
allow(vaccination_record).to receive(
32-
:syncable_to_nhs_immunisations_api?
32+
:correct_source_for_nhs_immunisations_api?
3333
).and_return(false)
3434
end
3535

@@ -71,8 +71,10 @@
7171
end
7272
end
7373

74-
describe "syncable_to_nhs_immunisations_api scope" do
75-
subject { VaccinationRecord.syncable_to_nhs_immunisations_api }
74+
describe "with_correct_source_for_nhs_immunisations_api scope" do
75+
subject { VaccinationRecord.with_correct_source_for_nhs_immunisations_api }
76+
77+
before { Flipper.enable(:sync_national_reporting_to_imms_api) }
7678

7779
let!(:vaccination_record) do
7880
create(:vaccination_record, programme:, session:)
@@ -83,10 +85,46 @@
8385

8486
it { should include(vaccination_record) }
8587
it { should_not include(vaccination_record_outside_of_session) }
88+
89+
context "when vaccination record was uploaded through national reporting portal" do
90+
let!(:vaccination_record) do
91+
create(:vaccination_record, :sourced_from_bulk_upload, programme:)
92+
end
93+
94+
it { should include(vaccination_record) }
95+
96+
context "with the sync_national_reporting_to_imms_api feature flag disabled" do
97+
before { Flipper.disable(:sync_national_reporting_to_imms_api) }
98+
99+
let!(:vaccination_record) do
100+
create(:vaccination_record, :sourced_from_bulk_upload, programme:)
101+
end
102+
103+
it { should_not include(vaccination_record) }
104+
end
105+
end
106+
107+
context "when vaccination record was part of a historical upload" do
108+
let!(:vaccination_record) do
109+
create(:vaccination_record, source: :historical_upload, programme:)
110+
end
111+
112+
it { should_not include(vaccination_record) }
113+
end
114+
115+
context "a vaccination record created because patient is already vaccinated" do
116+
let!(:vaccination_record) do
117+
create(:vaccination_record, source: :consent_refusal, programme:)
118+
end
119+
120+
it { should_not include(vaccination_record) }
121+
end
86122
end
87123

88-
describe "#syncable_to_nhs_immunisations_api?" do
89-
subject { vaccination_record.syncable_to_nhs_immunisations_api? }
124+
describe "#correct_source_to_nhs_immunisations_api?" do
125+
subject { vaccination_record.correct_source_for_nhs_immunisations_api? }
126+
127+
before { Flipper.enable(:sync_national_reporting_to_imms_api) }
90128

91129
context "when the vaccination record is eligible to sync" do
92130
it { should be true }
@@ -104,6 +142,38 @@
104142
it { should be false }
105143
end
106144

145+
context "a vaccination record uploaded through national reporting portal" do
146+
let(:vaccination_record) do
147+
build(
148+
:vaccination_record,
149+
:sourced_from_bulk_upload,
150+
outcome:,
151+
programme:
152+
)
153+
end
154+
155+
it { should be true }
156+
157+
context "with the sync_national_reporting_to_imms_api feature flag disabled" do
158+
before { Flipper.disable(:sync_national_reporting_to_imms_api) }
159+
160+
it { should be false }
161+
end
162+
end
163+
164+
context "a vaccination record created because patient is already vaccinated" do
165+
let(:vaccination_record) do
166+
build(
167+
:vaccination_record,
168+
source: :consent_refusal,
169+
outcome:,
170+
programme:
171+
)
172+
end
173+
174+
it { should be false }
175+
end
176+
107177
context "a patient without an nhs number" do
108178
let(:patient) do
109179
create(:patient, nhs_number: nil, school: session.location)

spec/support/immunisations_api_helper.rb

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
# frozen_string_literal: true
22

33
module ImmunisationsAPIHelper
4-
def stub_immunisations_api_post(uuid: Random.uuid)
4+
def stub_immunisations_api_post(*uuids, uuid: Random.uuid)
5+
uuids << uuid
6+
responses =
7+
uuids.map do |id|
8+
{
9+
status: 201,
10+
body: nil,
11+
headers: {
12+
location:
13+
"https://localhost:4000/immunisation-fhir-api/Immunization/#{id}"
14+
}
15+
}
16+
end
17+
518
stub_request(
619
:post,
720
"https://sandbox.api.service.nhs.uk/immunisation-fhir-api/FHIR/R4/Immunization"
8-
).to_return(
9-
status: 201,
10-
body: nil,
11-
headers: {
12-
location:
13-
"https://localhost:4000/immunisation-fhir-api/Immunization/#{uuid}"
14-
}
15-
)
21+
).to_return(responses)
1622
end
1723

1824
def stub_immunisations_api_put(uuid: Random.uuid)

0 commit comments

Comments
 (0)