diff --git a/layer/nrlf/core/constants.py b/layer/nrlf/core/constants.py index a54d962c5..166f1c71b 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-NRLRecordType|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-NRLRecordType": "NRL", +} CONTENT_STABILITY_EXTENSION_URL = ( "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-ContentStability" ) 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 diff --git a/layer/nrlf/core/tests/test_validators.py b/layer/nrlf/core/tests/test_validators.py index 39939aa56..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' 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 new file mode 100644 index 000000000..7e909bedc --- /dev/null +++ b/resources/fhir/NRLF-RecordType-CodeSystem.json @@ -0,0 +1,34 @@ +{ + "resourceType": "CodeSystem", + "id": "England-NRLRecordType", + "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-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/createDocumentReference-success.feature b/tests/features/producer/createDocumentReference-success.feature index bf1ce5c35..06e8cf79a 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 | 423876004 | + | 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 | 423876004 | + | 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: 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"] } """