Implementing AHVN13 Validation: Leveraging FHIRPath Expressions

Using FHIRPath Expressions for the Validation of the AHVN13 Identifier for the Use in Implementation Guides

Goal

This blog post will cover the requirements that must be met for an AHVN13 number to be considered valid, using FHIRPath for validation. Additionally, we will explore the AHVN13 number and its potential applications as an identifier, such as in an Implementation Guide.

Context and History

The AHV has used an insured person number since 1948. In 2008, a 13-digit AHVN was introduced, which can be systematically used under specific conditions. However, using the AHVN outside of the AHV is only permitted under certain circumstances. The AHVN does not provide any personal information about the holder, making it impossible to draw any conclusions about their personal characteristics. The unique patient identification number for the electronic patient dossier (EPD) is a register-specific number managed by the ZAS, which is linked to the AHVN.

Structure

Example: 756.2295.8830.70

Validation of AHVN13 with FHIRPath Expression

Provided that AHVN13 is represented as a dotless string, the FHIRPath expressions below can be utilized to perform the corresponding validations:

Does the AHVN13 Number Start With 756?

The function startsWith(prefix : String) : Boolean can be used:

value.startsWith('756')

Does the AHVN13 Number Consist of Thirteen Digits Composed of the Numbers 0-9?

The function matches(regex : String) : Boolean can be used in combination with a simple regex:

value.matches('^[0-9]{13}$')

Is the AHVN13 Check Digit Correct?

The FHIRPath expression used in these examples computes the check digit based on ean13 and verifies it against the given check digit.

(((10-(28+(value.substring(3,1).toInteger()\*3)+(value.substring(4,1).toInteger()\*1)+(value.substring(5,1).toInteger()\*3)+(value.substring(6,1).toInteger()\*1)+(value.substring(7,1).toInteger()\*3)+(value.substring(8,1).toInteger()\*1)+(value.substring(9,1).toInteger()\*3)+(value.substring(10,1).toInteger()\*1)+(value.substring(11,1).toInteger()\*3))mod(10))mod(10))=value.substring(12,1).toInteger())

In this blog post, there are two examples provided regarding check digits - one with a valid ahvn13 and the other with an invalid one.

AHVN13 Identifier in Implementation Guides

The CHCorePatient profile is an example of a resource that makes use of an identifier, i.e., amongst others, the AHVN13Identifier. This identifier profile has multiple constraints attached to it. In FHIR terms these are called invariants. Namely, they are ahvn13-length, ahvn14-startswith756 and ahvn13-digit-check.

Profile: AHVN13Identifier
Parent: Identifier
Id: ch-core-ahvn13-identifier
Title: "AHVN13 / NAVS13 Identifier"
Description: "Identifier holding a 13 digit social security number. The number shall have exactly 13 digits and shall not contain point characters for separation."

* system 1..
* system = "urn:oid:2.16.756.5.32" (exactly)
* value 1..
* value obeys ahvn13-length and ahvn13-startswith756 and ahvn13-digit-check

For deduplication purposes the invariants were not defined inline, but rather in seperate .fsh files in a separate folder.

Definition of the Invariants

ahvn13

Invariant: ahvn13-digit-check
Description: "AHVN13 / NAVS13 must pass digit check - https://www.gs1.org/services/how-calculate-check-digit-manually"
Severity: #error
Expression: "(((10-(28+(value.substring(3,1).toInteger()*3)+(value.substring(4,1).toInteger()*1)+(value.substring(5,1).toInteger()*3)+(value.substring(6,1).toInteger()*1)+(value.substring(7,1).toInteger()*3)+(value.substring(8,1).toInteger()*1)+(value.substring(9,1).toInteger()*3)+(value.substring(10,1).toInteger()*1)+(value.substring(11,1).toInteger()*3))mod(10))mod(10))=value.substring(12,1).toInteger())"

ahvn13-length

Invariant: ahvn13-length
Description: "AHVN13 / NAVS13 must be exactly 13 characters long"
Severity: #error
Expression: "value.matches('^[0-9]{13}$')"

ahvn13-startswith756

Invariant: ahvn13-startswith756
Description: "AHVN13 / NAVS13 must start with 756"
Severity: #error
Expression: "value.startsWith('756')"

Formation of the Check Digit According to EAN13

The purpose of the check digit is to detect input errors and confirm the correctness of the social security number. How is the check digit created? To demonstrate, let’s examine the AHVN13 (756.2295.8830.70) and confirm that the correct check digit is indeed 0.

Format D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13
GTIN-13 7 5 6 2 2 9 5 8 8 3 0 7 0

Step 1: The individual digits D1 - D12 are multiplied by 1 and by 3 alternately from left to right and the sum is calculated.

\[\displaylines{ (D1 \cdot 1) + (D2 \cdot 3) + (D3 \cdot 1) + \\ (D4 \cdot 3) + (D5 \cdot 1) + (D6 \cdot 3)+ \\ (D7 \cdot 1) + (D8 \cdot 3) + (D9 \cdot 1) + \\ (D10 \cdot 3) + (D11 \cdot 1) + (D12 \cdot 3) \\ = sum }\]

or with the numbers inserted:

\[\displaylines{ (7 \cdot 1) + (5 \cdot 3) + (6 \cdot 1) + \\ (2 \cdot 3) + (2 \cdot 1) + (5 \cdot 3) + \\ (9 \cdot 1) + (8 \cdot 3) + (8 \cdot 1) + \\ (3 \cdot 3) + (0 \cdot 1) + (7 \cdot 3) \\ = 130 }\]

Since the country code (756) is always the same, we can shorten the expression as follows:

\[\displaylines{ (7 \cdot 1) + (5 \cdot 3) + (6 \cdot 1) \\ = 28 }\]

Which leaves us with:

\[\displaylines{ 28 + \\ (2 \cdot 3) + (2 \cdot 1) + (5 \cdot 3)+ \\ (9 \cdot 1) + (8 \cdot 3) + (8 \cdot 1) + \\ (3 \cdot 3) + (0 \cdot 1) + (7 \cdot 3) \\ = 130 }\]

Step 2: Subtract the sum from nearest equal or higher multiple of ten

\[130 - 130 = 0\]

Result: In our case given the sum of 130, the number itself is the nearest equal or higher multiple of ten. By subtracting 130 from it, we therefore get the checksum 0.

Examples

Here are two examples that demonstrate how the FHIRPath expression validates the ahvn13 with the check digit. The first example shows a valid ahvn13, while the second example shows an invalid one.

Valid AHVN13 Number

AHVN13 is correct: 756.1234.5678.97

(((10-(28+(value.substring(3,1).toInteger()\*3)+(value.substring(4,1).toInteger()\*1)+(value.substring(5,1).toInteger()\*3)+(value.substring(6,1).toInteger()\*1)+(value.substring(7,1).toInteger()\*3)+(value.substring(8,1).toInteger()\*1)+(value.substring(9,1).toInteger()\*3)+(value.substring(10,1).toInteger()\*1)+(value.substring(11,1).toInteger()\*3))mod(10))mod(10))=value.substring(12,1).toInteger())

The above FHIRPath expression is equivalent to:

\[\displaylines{ (((10-(28+(1\cdot3))+(2\cdot1)+(3\cdot3)+(4\cdot1)+(5\cdot3) + \\ (6\cdot1)+(7\cdot3)+(8\cdot1))+(9\cdot3))mod(10))mod(10))=7) }\]
\[\displaylines{ (((10-((31)+(2)+(9)+(4)+(15) + \\ (6)+(21)+(8)+(27))mod(10))mod(10))=7) }\]
\[(((10-123)mod(10))mod(10))=7)\]
\[(((113)mod(10))mod(10))=7)\]
\[((3)mod(10))=7)\]
\[7=7\]

Invalid AHVN13 Number

AHVN13 is incorrect: 756.2435.3002.21

(((10-(28+(value.substring(3,1).toInteger()\*3)+(value.substring(4,1).toInteger()\*1)+(value.substring(5,1).toInteger()\*3)+(value.substring(6,1).toInteger()\*1)+(value.substring(7,1).toInteger()\*3)+(value.substring(8,1).toInteger()\*1)+(value.substring(9,1).toInteger()\*3)+(value.substring(10,1).toInteger()\*1)+(value.substring(11,1).toInteger()\*3))mod(10))mod(10))=value.substring(12,1).toInteger())

The above FHIRPath expression is equivalent to:

\[\displaylines{ (((10-(28+(2\cdot3))+(4\cdot1)+(3\cdot3)+(5\cdot1)+(3\cdot3)+ \\ (0\cdot1)+(0\cdot3)+(2\cdot1))+(1\cdot2))mod(10))mod(10))=1) }\]
\[\displaylines{ (((10-((34)+(4)+(9)+(5)+(9)+ \\ (0)+(0)+(2)+(2))mod(10))mod(10))=1) }\]
\[(((10-65)mod(10))mod(10))=1)\]
\[(((55)mod(10))mod(10))=1)\]
\[((5)mod(10))=1)\]
\[5\neq7\]