Skip to main content

Unique ID assignment

Unique IDs are unique identifier values assigned to a resource (e.g. Patient) and are associated with a single entity.

Unique ID assignment configs determine how pre-generated unique IDs are retrieved from a Group FHIR resource and subsequently populated in a Questionnaire field.

Here is a sample configuration for the unique identifier assignment:

{
"uniqueIdAssignment": {
"linkId": "phn",
"idFhirPathExpression": "Group.characteristic.where(exclude=false and code.text='phn').first().value.text",
"readOnly": false,
"resource": "Group",
"sortConfigs": [
{
"paramName": "_lastUpdated",
"dataType": "DATE",
"order": "DESCENDING"
}
],
"resourceFilterExpression": {
"conditionalFhirPathExpressions": [
"Group.active = true and Group.type = 'device' and Group.name = 'Unique IDs'"
],
"matchAll": true
}
}
}

The configuration contains the following properties:

linkId - The linkId for the targeted Questionnaire item idFhirPathExpression - The FHIR path expression used to extract ID from a resource readOnly - Enable or disable editing of the field. Defaults to true resource - FHIR resource used to store generated unique IDs sortConfigs - For ordering resources. It is important to ensure the resources are ordered by last updated resourceFilterExpression - Extra configurations to apply filter via code on the declared Resource

NOTE: If the readOnly configuration is set to false, the ID field in the Questionnaire becomes editable. If the prepopulated ID is modified and a different ID is submitted with the Questionnaire, the prepopulated ID will not be marked as used. This means that it will still be prepopulated the next time the Questionnaire is launched.

Characteristic-based Group resource for unique IDs

IDs are stored as text in a valueCodeableConcept in the characteristic field. The batch of IDs is assigned to a Practitioner using the managingEntity.

When an ID is used, the characteristic entry with that ID is updated to be excluded by setting "exclude": true. Once all IDs in the Group are used, the group is set to inactive.

Sample Group resource with unique IDs

{
"resourceType": "Group",
"id": "37312ad4-538e-4535-82d2-ea14f40deeb9",
"meta": {
"versionId": "9",
"lastUpdated": "2023-12-22T06:43:35.986+00:00",
"source": "#04a1c85fb6adf0cc",
"tag": [
{
"system": "https://smartregister.org/care-team-tag-id",
"code": "3e005baf-854b-40a7-bdd5-9b73f63aa9a3",
"display": "Practitioner CareTeam"
},
{
"system": "https://smartregister.org/organisation-tag-id",
"code": "41eae946-bdc4-4179-b404-6503ff12f59c",
"display": "Practitioner Organization"
},
{
"system": "https://smartregister.org/location-tag-id",
"code": "3816",
"display": "Practitioner Location"
},
{
"system": "https://smartregister.org/practitioner-tag-id",
"code": "49b72a3d-44cd-4a74-9459-4dc9f6b543fa",
"display": "Practitioner"
},
{
"system": "https://smartregister.org/app-version",
"code": "Not defined",
"display": "Application Version"
}
]
},
"identifier": [
{
"system": "http://smartregister.org",
"value": "37312ad4-538e-4535-82d2-ea14f40deeb9"
}
],
"active": true,
"type": "device",
"actual": true,
"name": "Unique IDs",
"managingEntity": {
"reference": "Practitioner/49b72a3d-44cd-4a74-9459-4dc9f6b543fa"
},
"characteristic": [
{
"code": {
"text": "phn"
},
"valueCodeableConcept": {
"text": "1000010001"
},
"exclude": false
},
{
"code": {
"text": "phn"
},
"valueCodeableConcept": {
"text": "1000020002"
},
"exclude": false
},
{
"code": {
"text": "phn"
},
"valueCodeableConcept": {
"text": "1000030003"
},
"exclude": false
},
{
"code": {
"text": "phn"
},
"valueCodeableConcept": {
"text": "1000040004"
},
"exclude": false
},
{
"code": {
"text": "phn"
},
"valueCodeableConcept": {
"text": "1000050005"
},
"exclude": false
}
]
}

Verhoeff algorithm for ID generation

To use the Verhoeff algorithm for ID generation on Android, you will need to implement the algorithm for checksum validation. The Verhoeff algorithm ensures that the IDs generated are error-resistant, specifically detecting common errors like transpositions or single-digit mistakes. Here is how you can implement it in Android.

Step-by-Step Guide:

Verhoeff algorithm overview

The Verhoeff algorithm uses:

  • A multiplication table (d table) to determine how numbers are multiplied.
  • A permutation table (p table) to rearrange digits before adding them.
  • An inverse table (inv table) to compute the check digit.

Verhoeff algorithm tables

Here are the tables used in the algorithm:

object Verhoeff {
// D table for multiplication
private val d = arrayOf(
intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
intArrayOf(1, 2, 3, 4, 0, 6, 7, 8, 9, 5),
intArrayOf(2, 3, 4, 0, 1, 7, 8, 9, 5, 6),
intArrayOf(3, 4, 0, 1, 2, 8, 9, 5, 6, 7),
intArrayOf(4, 0, 1, 2, 3, 9, 5, 6, 7, 8),
intArrayOf(5, 9, 8, 7, 6, 0, 4, 3, 2, 1),
intArrayOf(6, 5, 9, 8, 7, 1, 0, 4, 3, 2),
intArrayOf(7, 6, 5, 9, 8, 2, 1, 0, 4, 3),
intArrayOf(8, 7, 6, 5, 9, 3, 2, 1, 0, 4),
intArrayOf(9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
)

// P table for permutation
private val p = arrayOf(
intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
intArrayOf(1, 5, 7, 6, 2, 8, 3, 0, 9, 4),
intArrayOf(5, 8, 0, 3, 7, 9, 6, 1, 4, 2),
intArrayOf(8, 9, 1, 6, 0, 4, 3, 5, 2, 7),
intArrayOf(9, 4, 5, 3, 1, 2, 6, 8, 7, 0),
intArrayOf(4, 2, 8, 6, 5, 7, 3, 9, 0, 1),
intArrayOf(2, 7, 9, 3, 8, 0, 6, 4, 1, 5),
intArrayOf(7, 0, 4, 6, 9, 1, 3, 2, 5, 8)
)

// Inv table for inverse
private val inv = intArrayOf(0, 4, 3, 2, 1, 5, 6, 7, 8, 9)
}

Generate checksum

Here is the code to compute the Verhoeff check digit for a given number:

// Function to generate Verhoeff Checksum
fun generateVerhoeff(number: String): String {
var c = 0 // Checksum
val myArray = stringToReversedIntArray(number)

for (i in myArray.indices) {
c = Verhoeff.d[c][Verhoeff.p[i % 8][myArray[i]]]
}

return Verhoeff.inv[c].toString() // Return Verhoeff check digit
}

// Convert string to reversed int array
private fun stringToReversedIntArray(num: String): IntArray {
val myArray = num.map { it.toString().toInt() }.toIntArray()
return myArray.reversedArray()
}

Validate an ID

To validate an ID with its check digit, you need to verify that the check digit is correct. Here’s the code for validation:

// Function to validate Verhoeff check digit
fun validateVerhoeff(number: String): Boolean {
var c = 0
val myArray = stringToReversedIntArray(number)

for (i in myArray.indices) {
c = Verhoeff.d[c][Verhoeff.p[i % 8][myArray[i]]]
}

return c == 0 // If the checksum is 0, the number is valid
}

Usage in an Android App

You can integrate the above code into an Android project. Here’s how to use it for generating a patient ID and checking its validity:

To generate a patient ID with a Verhoeff check digit:

val baseID = "123456"  // Example patient ID (without checksum)
val checkDigit = generateVerhoeff(baseID)
val fullID = baseID + checkDigit // Full ID with Verhoeff check digit
println("Generated Patient ID: $fullID")


• To validate a patient ID:
val isValid = validateVerhoeff(fullID)
if (isValid) {
println("The ID is valid.")
} else {
println("The ID is invalid.")
}

Syncing or storing IDs

Once you have generated the ID, you can store it in a local database (e.g., Room or SQLite) or sync it with a server. You can implement these features based on your app’s design, whether you are using server communication (REST API) or local storage.