From 9819afe829c45aeaa91fde5de10d6b229f8fe3b1 Mon Sep 17 00:00:00 2001 From: Kate Bobyn Date: Tue, 28 Apr 2026 09:27:18 +0100 Subject: [PATCH 1/8] NRL-2199 Add D2A type belonging to new non-SNOMED system --- layer/nrlf/core/constants.py | 11 ++++- .../fhir/NRLF-RecordType-CodeSystem.json | 34 +++++++++++++ resources/fhir/NRLF-RecordType-ValueSet.json | 13 ++++- .../createDocumentReference-success.feature | 49 +++++++++++++++++++ 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 resources/fhir/NRLF-RecordType-CodeSystem.json diff --git a/layer/nrlf/core/constants.py b/layer/nrlf/core/constants.py index a54d962c5..6e3a7b93f 100644 --- a/layer/nrlf/core/constants.py +++ b/layer/nrlf/core/constants.py @@ -92,6 +92,9 @@ class PointerTypes(Enum): APPOINTMENT = "http://snomed.info/sct|749001000000101" SHARED_CARE_RECORD = "http://snomed.info/sct|887181000000106" ABOUT_ME = "http://snomed.info/sct|1515851000000101" # NOSONAR (S5332) This is a type code, not an actual URL + DISCHARGE_PLAN = ( + "https://fhir.nhs.uk/England/CodeSystem/England-NRLFormatCode|HDTAP" + ) @staticmethod def list(): @@ -194,6 +197,7 @@ def coding_value(self): }, PointerTypes.SHARED_CARE_RECORD.value: {"display": "Clinical summary"}, PointerTypes.ABOUT_ME.value: {"display": "About me"}, + PointerTypes.DISCHARGE_PLAN.value: {"display": "Hospital Discharge to Assess Plan"}, } TYPE_CATEGORIES = { @@ -228,6 +232,7 @@ def coding_value(self): # # Clinical documents PointerTypes.ABOUT_ME.value: Categories.CLINICAL_DOCUMENT.value, + PointerTypes.DISCHARGE_PLAN.value: Categories.CLINICAL_DOCUMENT.value, } # @@ -711,7 +716,11 @@ def coding_value(self): } -SYSTEM_SHORT_IDS = {"http://snomed.info/sct": "SCT", "https://nicip.nhs.uk": "NICIP"} +SYSTEM_SHORT_IDS = { + "http://snomed.info/sct": "SCT", + "https://nicip.nhs.uk": "NICIP", + "https://fhir.nhs.uk/England/CodeSystem/England-NRLTypeCode": "NRL", +} CONTENT_STABILITY_EXTENSION_URL = ( "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-ContentStability" ) diff --git a/resources/fhir/NRLF-RecordType-CodeSystem.json b/resources/fhir/NRLF-RecordType-CodeSystem.json new file mode 100644 index 000000000..4e4e4eae2 --- /dev/null +++ b/resources/fhir/NRLF-RecordType-CodeSystem.json @@ -0,0 +1,34 @@ +{ + "resourceType": "CodeSystem", + "id": "England-NRLRetrievalMechanism", + "url": "https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType", + "version": "1.1.0", + "name": "EnglandNRLRecordType", + "title": "England NRL Record Typê", + "status": "draft", + "experimental": false, + "date": "2026-04-28", + "publisher": "NHS England", + "contact": [ + { + "name": "NRL Team at NHS Digital", + "telecom": [ + { + "system": "email", + "value": "nrls@nhs.net", + "use": "work" + } + ] + } + ], + "description": "A CodeSystem to define types of DocumentReference that can be published and retrieved via NHS England's National Record Locator v3 APIs.", + "copyright": "Copyright © 2026+ NHS England Licensed under the Apache License, Version 2.0 (the \\\"License\\\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \\\"AS IS\\\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. HL7® FHIR® standard Copyright © 2011+ HL7 The HL7® FHIR® standard is used under the FHIR license. You may obtain a copy of the FHIR license at https://www.hl7.org/fhir/license.html.", + "content": "complete", + "concept": [ + { + "code": "HDTAP", + "display": "Hospital Discharge to Assess Plan", + "definition": "A \\\"Discharge to Assess\\\" (D2A) plan. D2A is a model whereby people with new or additional health and/or social care needs on discharge receive post-discharge recovery support, and where assessments of longer-term or ongoing needs (if required) are fully completed only once a person has reached a point of recovery and stability." + } + ] +} diff --git a/resources/fhir/NRLF-RecordType-ValueSet.json b/resources/fhir/NRLF-RecordType-ValueSet.json index 08ab5bc31..19aaa3aa3 100644 --- a/resources/fhir/NRLF-RecordType-ValueSet.json +++ b/resources/fhir/NRLF-RecordType-ValueSet.json @@ -2,10 +2,10 @@ "resourceType": "ValueSet", "id": "NRLF-RecordType", "url": "https://fhir.nhs.uk/England/ValueSet/England-NRLRecordType", - "version": "1.1.4", + "version": "1.1.6", "name": "NRLF Record Type", "status": "draft", - "date": "2025-08-14T00:00:00+00:00", + "date": "2026-04-27T00:00:00+00:00", "publisher": "NHS Digital", "contact": { "name": "NRL Team at NHS Digital", @@ -84,6 +84,15 @@ } ] }, + { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType", + "concept": [ + { + "code": "HDTAP", + "display": "Hospital Discharge to Assess Plan" + } + ] + }, { "system": "https://nicip.nhs.uk", "concept": [ diff --git a/tests/features/producer/createDocumentReference-success.feature b/tests/features/producer/createDocumentReference-success.feature index bf1ce5c35..fa4d917bc 100644 --- a/tests/features/producer/createDocumentReference-success.feature +++ b/tests/features/producer/createDocumentReference-success.feature @@ -382,6 +382,55 @@ Feature: Producer - createDocumentReference - Success Scenarios | formatCode | urn:nhs-ic:structured | | formatDisplay | Structured Document | + Scenario: Successfully create a Document Pointer (non-SNOMED type) + Given the application 'DataShare' (ID 'z00z-y11y-x22x') is registered to access the API + And the organisation 'ANGY1' is authorised to access pointer types: + | system | value | + | https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType | HDTAP | + When producer 'ANGY1' creates a DocumentReference with values: + | property | value | + | subject | 9278693472 | + | status | current | + | type | HDTAP | + | type_system | https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType | + | category | 721981007 | + | custodian | ANGY1 | + | author | HAR1 | + | url | https://example.org/my-doc.pdf | + | practiceSetting | 224891009 | + Then the response status code is 201 + And the response is an OperationOutcome with 1 issue + And the OperationOutcome contains the issue: + """ + { + "severity": "information", + "code": "informational", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/NRL-ResponseCode", + "code": "RESOURCE_CREATED", + "display": "Resource created" + } + ] + }, + "diagnostics": "The document has been created" + } + """ + And the response has a Location header + And the Location header starts with '/DocumentReference/ANGY1-' + And the resource in the Location header exists with values: + | property | value | + | subject | 9278693472 | + | status | current | + | type | HDTAP | + | type_system | https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType | + | category | 721981007 | + | custodian | ANGY1 | + | author | HAR1 | + | url | https://example.org/my-doc.pdf | + | practiceSetting | 224891009 | + Scenario: Successfully create a DocumentReference with two contents and different retrieval mechanisms Given the application 'DataShare' (ID 'z00z-y11y-x22x') is registered to access the API And the organisation 'TSTCUS' is authorised to access pointer types: From 436bbc5ee43588986207ba983e0b44a52b935d44 Mon Sep 17 00:00:00 2001 From: Kate Bobyn Date: Wed, 29 Apr 2026 08:39:04 +0100 Subject: [PATCH 2/8] NRL-2199 fix codesystem constant --- layer/nrlf/core/constants.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/layer/nrlf/core/constants.py b/layer/nrlf/core/constants.py index 6e3a7b93f..2af9a5850 100644 --- a/layer/nrlf/core/constants.py +++ b/layer/nrlf/core/constants.py @@ -92,9 +92,7 @@ class PointerTypes(Enum): APPOINTMENT = "http://snomed.info/sct|749001000000101" SHARED_CARE_RECORD = "http://snomed.info/sct|887181000000106" ABOUT_ME = "http://snomed.info/sct|1515851000000101" # NOSONAR (S5332) This is a type code, not an actual URL - DISCHARGE_PLAN = ( - "https://fhir.nhs.uk/England/CodeSystem/England-NRLFormatCode|HDTAP" - ) + DISCHARGE_PLAN = "https://fhir.nhs.uk/England/CodeSystem/England-NRLTypeCode|HDTAP" @staticmethod def list(): From d9c7ced583429d53753b2f849d420d25ad0bdd59 Mon Sep 17 00:00:00 2001 From: Kate Bobyn Date: Wed, 29 Apr 2026 08:54:38 +0100 Subject: [PATCH 3/8] NRL-2199 fix validation messages to account for new record type system --- layer/nrlf/core/constants.py | 4 +++- layer/nrlf/core/tests/test_validators.py | 2 +- layer/nrlf/core/validators.py | 8 ++++++-- resources/fhir/NRLF-RecordType-CodeSystem.json | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/layer/nrlf/core/constants.py b/layer/nrlf/core/constants.py index 2af9a5850..5c07053ea 100644 --- a/layer/nrlf/core/constants.py +++ b/layer/nrlf/core/constants.py @@ -92,7 +92,9 @@ class PointerTypes(Enum): APPOINTMENT = "http://snomed.info/sct|749001000000101" SHARED_CARE_RECORD = "http://snomed.info/sct|887181000000106" ABOUT_ME = "http://snomed.info/sct|1515851000000101" # NOSONAR (S5332) This is a type code, not an actual URL - DISCHARGE_PLAN = "https://fhir.nhs.uk/England/CodeSystem/England-NRLTypeCode|HDTAP" + DISCHARGE_PLAN = ( + "https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType|HDTAP" + ) @staticmethod def list(): diff --git a/layer/nrlf/core/tests/test_validators.py b/layer/nrlf/core/tests/test_validators.py index 39939aa56..8737b062a 100644 --- a/layer/nrlf/core/tests/test_validators.py +++ b/layer/nrlf/core/tests/test_validators.py @@ -580,7 +580,7 @@ def test_validate_type_coding_invalid_system(): } ] }, - "diagnostics": "Invalid type system: http://snoooooomed/sctfffffg Type system must be either 'http://snomed.info/sct' or 'https://nicip.nhs.uk'", + "diagnostics": "Invalid type system: http://snoooooomed/sctfffffg Type system must be either 'http://snomed.info/sct', 'https://nicip.nhs.uk' or ''https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType'", "expression": ["type.coding[0].system"], } diff --git a/layer/nrlf/core/validators.py b/layer/nrlf/core/validators.py index e213c5bfe..447a7e253 100644 --- a/layer/nrlf/core/validators.py +++ b/layer/nrlf/core/validators.py @@ -351,11 +351,15 @@ def _validate_type(self, model: DocumentReference): return coding = model.type.coding[0] - if coding.system not in ["http://snomed.info/sct", "https://nicip.nhs.uk"]: + if coding.system not in [ + "http://snomed.info/sct", + "https://nicip.nhs.uk", + "https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType", + ]: self.result.add_error( issue_code="business-rule", error_code="UNPROCESSABLE_ENTITY", - diagnostics=f"Invalid type system: {coding.system} Type system must be either 'http://snomed.info/sct' or 'https://nicip.nhs.uk'", + diagnostics=f"Invalid type system: {coding.system} Type system must be either 'http://snomed.info/sct', 'https://nicip.nhs.uk' or 'https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType'", field="type.coding[0].system", ) return diff --git a/resources/fhir/NRLF-RecordType-CodeSystem.json b/resources/fhir/NRLF-RecordType-CodeSystem.json index 4e4e4eae2..7e909bedc 100644 --- a/resources/fhir/NRLF-RecordType-CodeSystem.json +++ b/resources/fhir/NRLF-RecordType-CodeSystem.json @@ -1,6 +1,6 @@ { "resourceType": "CodeSystem", - "id": "England-NRLRetrievalMechanism", + "id": "England-NRLRecordType", "url": "https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType", "version": "1.1.0", "name": "EnglandNRLRecordType", From 2da99e897d35235e31326b85374aa8f4fbb74b46 Mon Sep 17 00:00:00 2001 From: Kate Bobyn Date: Wed, 29 Apr 2026 08:55:54 +0100 Subject: [PATCH 4/8] NRL-2199 fix validation messages to account for new record type system --- layer/nrlf/core/tests/test_validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layer/nrlf/core/tests/test_validators.py b/layer/nrlf/core/tests/test_validators.py index 8737b062a..400780f1e 100644 --- a/layer/nrlf/core/tests/test_validators.py +++ b/layer/nrlf/core/tests/test_validators.py @@ -580,7 +580,7 @@ def test_validate_type_coding_invalid_system(): } ] }, - "diagnostics": "Invalid type system: http://snoooooomed/sctfffffg Type system must be either 'http://snomed.info/sct', 'https://nicip.nhs.uk' or ''https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType'", + "diagnostics": "Invalid type system: http://snoooooomed/sctfffffg Type system must be either 'http://snomed.info/sct', 'https://nicip.nhs.uk' or 'https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType'", "expression": ["type.coding[0].system"], } From ff57532561eeedfa72a7f257ca5b864aedf05afa Mon Sep 17 00:00:00 2001 From: Kate Bobyn Date: Wed, 29 Apr 2026 09:03:20 +0100 Subject: [PATCH 5/8] NRL-2199 fix short id for new type system constant --- layer/nrlf/core/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layer/nrlf/core/constants.py b/layer/nrlf/core/constants.py index 5c07053ea..166f1c71b 100644 --- a/layer/nrlf/core/constants.py +++ b/layer/nrlf/core/constants.py @@ -719,7 +719,7 @@ def coding_value(self): SYSTEM_SHORT_IDS = { "http://snomed.info/sct": "SCT", "https://nicip.nhs.uk": "NICIP", - "https://fhir.nhs.uk/England/CodeSystem/England-NRLTypeCode": "NRL", + "https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType": "NRL", } CONTENT_STABILITY_EXTENSION_URL = ( "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-ContentStability" From 39ba39f319cf449b0390c885bb69033dbb9d5177 Mon Sep 17 00:00:00 2001 From: Kate Bobyn Date: Wed, 29 Apr 2026 09:21:57 +0100 Subject: [PATCH 6/8] NRL-2199 update error messages in integration tests to account for new type system --- tests/features/producer/createDocumentReference-failure.feature | 2 +- tests/features/producer/upsertDocumentReference-failure.feature | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/features/producer/createDocumentReference-failure.feature b/tests/features/producer/createDocumentReference-failure.feature index ff1d2bfda..50140e079 100644 --- a/tests/features/producer/createDocumentReference-failure.feature +++ b/tests/features/producer/createDocumentReference-failure.feature @@ -571,7 +571,7 @@ Feature: Producer - createDocumentReference - Failure Scenarios } ] }, - "diagnostics": "Invalid type system: http://invalidsystem.info/sct Type system must be either 'http://snomed.info/sct' or 'https://nicip.nhs.uk'", + "diagnostics": "Invalid type system: http://invalidsystem.info/sct Type system must be either 'http://snomed.info/sct', 'https://nicip.nhs.uk' or 'https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType'", "expression": ["type.coding[0].system"] } """ diff --git a/tests/features/producer/upsertDocumentReference-failure.feature b/tests/features/producer/upsertDocumentReference-failure.feature index 192b6439a..be0a62267 100644 --- a/tests/features/producer/upsertDocumentReference-failure.feature +++ b/tests/features/producer/upsertDocumentReference-failure.feature @@ -110,7 +110,7 @@ Feature: Producer - upsertDocumentReference - Failure Scenarios } ] }, - "diagnostics": "Invalid type system: http://invalidsystem.info/sct Type system must be either 'http://snomed.info/sct' or 'https://nicip.nhs.uk'", + "diagnostics": "Invalid type system: http://invalidsystem.info/sct Type system must be either 'http://snomed.info/sct', 'https://nicip.nhs.uk' or 'https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType'", "expression": ["type.coding[0].system"] } """ From 4f573d3070466a3fa3d7a81ad8fd230b6610b85f Mon Sep 17 00:00:00 2001 From: Kate Bobyn Date: Wed, 29 Apr 2026 09:48:48 +0100 Subject: [PATCH 7/8] NRL-2199 update category in integration test for D2A --- .../features/producer/createDocumentReference-success.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/features/producer/createDocumentReference-success.feature b/tests/features/producer/createDocumentReference-success.feature index fa4d917bc..06e8cf79a 100644 --- a/tests/features/producer/createDocumentReference-success.feature +++ b/tests/features/producer/createDocumentReference-success.feature @@ -393,7 +393,7 @@ Feature: Producer - createDocumentReference - Success Scenarios | status | current | | type | HDTAP | | type_system | https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType | - | category | 721981007 | + | category | 423876004 | | custodian | ANGY1 | | author | HAR1 | | url | https://example.org/my-doc.pdf | @@ -425,7 +425,7 @@ Feature: Producer - createDocumentReference - Success Scenarios | status | current | | type | HDTAP | | type_system | https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType | - | category | 721981007 | + | category | 423876004 | | custodian | ANGY1 | | author | HAR1 | | url | https://example.org/my-doc.pdf | From fbc58719e99f55fb73ba72f293d643e03bd79f4b Mon Sep 17 00:00:00 2001 From: Kate Bobyn Date: Wed, 29 Apr 2026 15:36:54 +0100 Subject: [PATCH 8/8] NRL-2199 fix regex to allow - character in type system URLs --- layer/nrlf/core/dynamodb/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layer/nrlf/core/dynamodb/model.py b/layer/nrlf/core/dynamodb/model.py index 6f3ed4202..b996e43fb 100644 --- a/layer/nrlf/core/dynamodb/model.py +++ b/layer/nrlf/core/dynamodb/model.py @@ -230,7 +230,7 @@ def validate_type(cls, type: str) -> str: Validate the type of the DocumentPointer The type should be in the format | """ - if not re.match(r"^[A-Za-z0-9:/\.]+\|[A-Za-z0-9]+$", type): + if not re.match(r"^[A-Za-z0-9:/\.\-]+\|[A-Za-z0-9]+$", type): raise ValueError("type must be in the format |") return type