From b52ae9bb69a37fa865a64884243d649e37a53e35 Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 15 Jul 2026 14:06:27 +0300 Subject: [PATCH 1/5] [MS-1336] Fayda card support added --- .../externalcredential/ext/ResourceExt.kt | 2 ++ .../ExternalCredentialScanOcrViewModel.kt | 1 + .../screens/scanocr/model/OcrDocumentType.kt | 3 +++ .../screens/scanocr/model/OcrScanResult.kt | 6 +++++ .../scanocr/model/ScannedMfidDocument.kt | 1 + .../screens/scanocr/reader/OcrQuery.kt | 4 +++ .../usecase/BuildMfidDocumentUseCase.kt | 12 +++++++++ .../usecase/FaydaCardOcrReaderUseCase.kt | 23 ++++++++++++++++++ .../usecase/ScanMfidDocumentUseCase.kt | 3 +++ .../ExternalCredentialSearchViewModel.kt | 8 +++++- .../screens/search/model/MfidDocument.kt | 6 +++++ .../search/model/ScannedCredentialResult.kt | 1 + .../search/model/SearchCredentialState.kt | 2 +- .../ExternalCredentialSelectFragment.kt | 1 + .../view/ExternalCredentialTypeAdapter.kt | 2 ++ .../ExternalCredentialEventTrackerUseCase.kt | 2 ++ .../src/main/res/drawable/fayda_card.webp | Bin 0 -> 48624 bytes .../response/AppExternalCredentialMapper.kt | 1 + .../local/models/ExternalCredentialType.kt | 2 ++ .../models/MutliFactorIdConfiguration.kt | 12 +++++++++ .../models/MultiFactorIdConfiguration.kt | 5 ++++ .../models/ApiMultiFactorIdConfiguration.kt | 15 ++++++++++++ .../src/main/proto/project_config.proto | 6 +++++ .../ExternalCredentialType.kt | 1 + .../cosync/v1/CoSyncExternalCredentialType.kt | 3 +++ .../resources/src/main/res/values/strings.xml | 2 ++ .../InsertEnrollmentRecordsUseCase.kt | 1 + 27 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/FaydaCardOcrReaderUseCase.kt create mode 100644 feature/external-credential/src/main/res/drawable/fayda_card.webp diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ext/ResourceExt.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ext/ResourceExt.kt index ea7dc69c47..22af423ee0 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ext/ResourceExt.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ext/ResourceExt.kt @@ -24,6 +24,7 @@ fun Resources.getCredentialFieldTitle(type: ExternalCredentialType): String = wh ExternalCredentialType.NHISCard -> IDR.string.mfid_nhis_card_credential_field ExternalCredentialType.GhanaIdCard -> IDR.string.mfid_ghana_id_credential_field ExternalCredentialType.QRCode -> IDR.string.mfid_qr_credential_field + ExternalCredentialType.FaydaCard -> IDR.string.mfid_fayda_card_credential_field }.run(::getString) fun Resources.getCredentialTypeString(type: ExternalCredentialType?): String = getCredentialTypeRes(type).run(::getString) @@ -32,5 +33,6 @@ fun Resources.getCredentialTypeRes(type: ExternalCredentialType?): Int = when (t ExternalCredentialType.NHISCard -> IDR.string.mfid_type_nhis_card ExternalCredentialType.GhanaIdCard -> IDR.string.mfid_type_ghana_id_card ExternalCredentialType.QRCode -> IDR.string.mfid_type_qr_code + ExternalCredentialType.FaydaCard -> IDR.string.mfid_type_fayda_card null -> IDR.string.mfid_type_any_document } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/ExternalCredentialScanOcrViewModel.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/ExternalCredentialScanOcrViewModel.kt index 980acdb274..19a087abea 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/ExternalCredentialScanOcrViewModel.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/ExternalCredentialScanOcrViewModel.kt @@ -102,6 +102,7 @@ internal class ExternalCredentialScanOcrViewModel @AssistedInject constructor( fun getDocumentTypeRes(): Int = when (ocrDocumentType) { OcrDocumentType.NhisCard -> R.string.mfid_type_nhis_card OcrDocumentType.GhanaIdCard -> R.string.mfid_type_ghana_id_card + OcrDocumentType.FaydaCard -> R.string.mfid_type_fayda_card } fun startScanning() { diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/OcrDocumentType.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/OcrDocumentType.kt index 46c6e8cca5..39ad085301 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/OcrDocumentType.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/OcrDocumentType.kt @@ -5,15 +5,18 @@ import com.simprints.core.domain.externalcredential.ExternalCredentialType enum class OcrDocumentType { NhisCard, GhanaIdCard, + FaydaCard, } fun ExternalCredentialType.asOcrDocumentType() = when (this) { ExternalCredentialType.NHISCard -> OcrDocumentType.NhisCard ExternalCredentialType.GhanaIdCard -> OcrDocumentType.GhanaIdCard + ExternalCredentialType.FaydaCard -> OcrDocumentType.FaydaCard ExternalCredentialType.QRCode -> throw IllegalArgumentException("Cannot create Ocr Document Type from $this") } fun OcrDocumentType.asExternalCredentialType() = when (this) { OcrDocumentType.NhisCard -> ExternalCredentialType.NHISCard OcrDocumentType.GhanaIdCard -> ExternalCredentialType.GhanaIdCard + OcrDocumentType.FaydaCard -> ExternalCredentialType.FaydaCard } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/OcrScanResult.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/OcrScanResult.kt index b19f986a2c..99fc3d7841 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/OcrScanResult.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/OcrScanResult.kt @@ -36,4 +36,10 @@ internal sealed class OcrScanResult { val dateOfIssue: OcrLine? = null, val dateOfExpiry: OcrLine? = null, ) : OcrScanResult() + + @Serializable + @SerialName("OcrScanResult.FaydaCard") + data class FaydaCard( + override val credential: OcrLine, + ) : OcrScanResult() } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/ScannedMfidDocument.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/ScannedMfidDocument.kt index cb37f85a2b..112a121fa3 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/ScannedMfidDocument.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/model/ScannedMfidDocument.kt @@ -25,5 +25,6 @@ internal data class ScannedMfidDocument( get() = when (ocrScanResult) { is OcrScanResult.GhanaIdCard -> OcrDocumentType.GhanaIdCard is OcrScanResult.GhanaNhisCard -> OcrDocumentType.NhisCard + is OcrScanResult.FaydaCard -> OcrDocumentType.FaydaCard } } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/reader/OcrQuery.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/reader/OcrQuery.kt index 3616adf803..8bce54972a 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/reader/OcrQuery.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/reader/OcrQuery.kt @@ -13,6 +13,10 @@ internal class OcrQuery { filters += { line -> regex.matches(line.text) } } + fun matchesCondition(condition: (line: String) -> Boolean) { + filters += { line -> condition(line.text) } + } + fun containsPattern(regex: Regex) { filters += { line -> regex.containsMatchIn(line.text) } } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/BuildMfidDocumentUseCase.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/BuildMfidDocumentUseCase.kt index b9e3db160c..38e00f4bfa 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/BuildMfidDocumentUseCase.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/BuildMfidDocumentUseCase.kt @@ -17,6 +17,7 @@ internal class BuildMfidDocumentUseCase @Inject constructor( ): MfidDocument = when (documentType) { OcrDocumentType.NhisCard -> buildNhisCard(scannedDocuments) OcrDocumentType.GhanaIdCard -> buildGhanaIdCard(scannedDocuments) + OcrDocumentType.FaydaCard -> buildFaydaCard(scannedDocuments) } /** @@ -68,6 +69,14 @@ internal class BuildMfidDocumentUseCase @Inject constructor( ) } + private fun buildFaydaCard(scannedDocuments: List): MfidDocument.FaydaCard { + val credential = scannedDocuments + .map { it.ocrScanResult.credential.text } + .let { credentials -> getBestReadoutBasedOnConfidenceUseCase(credentials, targetLength = CREDENTIAL_LENGTH_FAYDA_CARD) } + .asTokenizableRaw() + return MfidDocument.FaydaCard(credential = credential) + } + private fun List.bestReadoutOrNull(ocrLine: (T) -> OcrLine?) = mapNotNull(ocrLine) .takeUnless(List::isEmpty) ?.let { getBestReadoutBasedOnConfidenceUseCase(it.map(OcrLine::text)) } @@ -79,5 +88,8 @@ internal class BuildMfidDocumentUseCase @Inject constructor( // Ghana ID field contains 15 chars: GHA-123456789-0 private const val CREDENTIAL_LENGTH_GHANA_ID = 15 + + // Fayda Alias Number (FAN) contains 16 digits + private const val CREDENTIAL_LENGTH_FAYDA_CARD = 16 } } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/FaydaCardOcrReaderUseCase.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/FaydaCardOcrReaderUseCase.kt new file mode 100644 index 0000000000..2bcbed4165 --- /dev/null +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/FaydaCardOcrReaderUseCase.kt @@ -0,0 +1,23 @@ +package com.simprints.feature.externalcredential.screens.scanocr.usecase + +import com.simprints.feature.externalcredential.screens.scanocr.model.OcrScanResult +import com.simprints.feature.externalcredential.screens.scanocr.reader.OcrReader +import javax.inject.Inject + +internal class FaydaCardOcrReaderUseCase @Inject constructor() { + operator fun invoke(ocrReader: OcrReader): OcrScanResult.FaydaCard? { + val credential = ocrReader.find { + matchesCondition { line -> + // Removing non-digits from the line readout. Checking if remaining digits match FAN_PATTERN + line.filter(Char::isDigit).matches(FAN_PATTERN) + } + } ?: return null + val credentialText = credential.text.filter(Char::isDigit) + return OcrScanResult.FaydaCard(credential.copy(text = credentialText)) + } + + companion object { + // Fayda Alias Number (FAN): exactly 16 digits after stripping all non-digit characters + val FAN_PATTERN = Regex("^\\d{16}$") + } +} diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/ScanMfidDocumentUseCase.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/ScanMfidDocumentUseCase.kt index 2757c6fbde..cd874bac94 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/ScanMfidDocumentUseCase.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/ScanMfidDocumentUseCase.kt @@ -17,6 +17,7 @@ internal class ScanMfidDocumentUseCase @Inject constructor( private val readTextFromImage: ReadTextFromImageUseCase, private val ghanaNhisCardOcrReaderUseCase: GhanaNhisCardOcrReaderUseCase, private val ghanaIdCardOcrReaderUseCase: GhanaIdCardOcrReaderUseCase, + private val faydaCardOcrReaderUseCase: FaydaCardOcrReaderUseCase, private val credentialImageRepository: CredentialImageRepository, ) { suspend operator fun invoke( @@ -27,6 +28,7 @@ internal class ScanMfidDocumentUseCase @Inject constructor( val isCapturingAllFields = when (documentType) { OcrDocumentType.NhisCard -> config.nhisCardConfig?.isCapturingAllFields OcrDocumentType.GhanaIdCard -> config.ghanaIdCardConfig?.isCapturingAllFields + OcrDocumentType.FaydaCard -> config.faydaCardConfig?.isCapturingAllFields } ?: false return try { val ocrText = readTextFromImage(bitmap) ?: return null @@ -34,6 +36,7 @@ internal class ScanMfidDocumentUseCase @Inject constructor( val scanResult = when (documentType) { OcrDocumentType.NhisCard -> ghanaNhisCardOcrReaderUseCase(ocrReader, isCapturingAllFields) OcrDocumentType.GhanaIdCard -> ghanaIdCardOcrReaderUseCase(ocrReader, isCapturingAllFields) + OcrDocumentType.FaydaCard -> faydaCardOcrReaderUseCase(ocrReader) } if (scanResult != null) { val savedImagePath = credentialImageRepository.saveCredentialScan(bitmap, imageType = FullDocument) diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt index 57b16f3c17..93a042f416 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt @@ -13,6 +13,7 @@ import com.simprints.core.livedata.send import com.simprints.core.tools.time.TimeHelper import com.simprints.feature.externalcredential.ExternalCredentialSearchResult import com.simprints.feature.externalcredential.model.ExternalCredentialParams +import com.simprints.feature.externalcredential.screens.scanocr.usecase.FaydaCardOcrReaderUseCase import com.simprints.feature.externalcredential.screens.scanocr.usecase.GhanaIdCardOcrReaderUseCase import com.simprints.feature.externalcredential.screens.scanocr.usecase.GhanaNhisCardOcrReaderUseCase import com.simprints.feature.externalcredential.screens.search.model.ScannedCredentialResult @@ -161,8 +162,9 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( * alpha-numeric, while the NHIS card memberships contain only digits. */ fun getKeyBoardInputType() = when (scannedCredentialResult.credentialType) { - // NHIS card membership contains only numbers + // NHIS card membership and Fayda FAN contain only numbers ExternalCredentialType.NHISCard -> InputType.TYPE_CLASS_NUMBER + ExternalCredentialType.FaydaCard -> InputType.TYPE_CLASS_NUMBER ExternalCredentialType.GhanaIdCard -> InputType.TYPE_CLASS_TEXT ExternalCredentialType.QRCode -> InputType.TYPE_CLASS_TEXT } @@ -211,6 +213,10 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( // Ghana ID card number pattern is "GHA-123456789-0" GhanaIdCardOcrReaderUseCase.GHANA_ID_PATTERN.matches(credential) } + ExternalCredentialType.FaydaCard -> { + // Fayda Alias Number (FAN): exactly 16 digits after stripping non-digit chars + FaydaCardOcrReaderUseCase.FAN_PATTERN.matches(credential.filter(Char::isDigit)) + } ExternalCredentialType.QRCode -> { // No QR code validation as of 2025.4.1 true diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/MfidDocument.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/MfidDocument.kt index 0ec43fbe79..c95f2c65da 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/MfidDocument.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/MfidDocument.kt @@ -43,4 +43,10 @@ sealed class MfidDocument : StepResult { data class GhanaQrCode( override val credential: TokenizableString.Raw, ) : MfidDocument() + + @Serializable + @SerialName("MfidDocument.FaydaCard") + data class FaydaCard( + override val credential: TokenizableString.Raw, + ) : MfidDocument() } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/ScannedCredentialResult.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/ScannedCredentialResult.kt index 5edf70dcc6..ce7f95ae3f 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/ScannedCredentialResult.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/ScannedCredentialResult.kt @@ -30,5 +30,6 @@ data class ScannedCredentialResult( is MfidDocument.GhanaIdCard -> ExternalCredentialType.GhanaIdCard is MfidDocument.GhanaNhisCard -> ExternalCredentialType.NHISCard is MfidDocument.GhanaQrCode -> ExternalCredentialType.QRCode + is MfidDocument.FaydaCard -> ExternalCredentialType.FaydaCard } } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/SearchCredentialState.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/SearchCredentialState.kt index a446b3815d..63a52b5299 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/SearchCredentialState.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/SearchCredentialState.kt @@ -28,7 +28,7 @@ internal data class SearchCredentialState( // [MS-1484] Editing and confirming the readout value should only be available for the OCR readouts (i.e.: Ghana NHIS card) // QR codes are not readable by humans, hence there is no point of asking the user to confirm the readout, nor to edit the readout val isOcrReadout = when (scannedCredentialResult.credentialType) { - ExternalCredentialType.NHISCard, ExternalCredentialType.GhanaIdCard -> true + ExternalCredentialType.NHISCard, ExternalCredentialType.GhanaIdCard, ExternalCredentialType.FaydaCard -> true ExternalCredentialType.QRCode -> false } return SearchCredentialState( diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectFragment.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectFragment.kt index 31f0040a85..ee3022dd56 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectFragment.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectFragment.kt @@ -116,6 +116,7 @@ internal class ExternalCredentialSelectFragment : Fragment(R.layout.fragment_ext when (type) { ExternalCredentialType.NHISCard, ExternalCredentialType.GhanaIdCard, + ExternalCredentialType.FaydaCard, -> startOcr(type.asOcrDocumentType()) ExternalCredentialType.QRCode -> startQrScan() diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/view/ExternalCredentialTypeAdapter.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/view/ExternalCredentialTypeAdapter.kt index 6898a68470..3a60dba2f5 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/view/ExternalCredentialTypeAdapter.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/view/ExternalCredentialTypeAdapter.kt @@ -35,12 +35,14 @@ internal class ExternalCredentialTypeAdapter( ExternalCredentialType.NHISCard -> IDR.string.mfid_type_nhis_card ExternalCredentialType.GhanaIdCard -> IDR.string.mfid_type_ghana_id_card ExternalCredentialType.QRCode -> IDR.string.mfid_type_qr_code + ExternalCredentialType.FaydaCard -> IDR.string.mfid_type_fayda_card }.run(c::getString) val text = c.resources.getString(IDR.string.mfid_scan_action, credentialTypeText) val image = when (credentialType) { ExternalCredentialType.NHISCard -> R.drawable.ghana_nhis_card ExternalCredentialType.GhanaIdCard -> R.drawable.ghana_id_card ExternalCredentialType.QRCode -> R.drawable.qr_code + ExternalCredentialType.FaydaCard -> R.drawable.fayda_card } binding.documentText.text = text binding.documentImage.setImageResource(image) diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCase.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCase.kt index 7749f950d3..430337be60 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCase.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCase.kt @@ -102,6 +102,7 @@ internal class ExternalCredentialEventTrackerUseCase @Inject constructor( ExternalCredentialType.NHISCard -> NHIS_CARD_ID_LENGTH ExternalCredentialType.GhanaIdCard -> GHANA_ID_CARD_ID_LENGTH ExternalCredentialType.QRCode -> QR_CODE_LENGTH + ExternalCredentialType.FaydaCard -> FAYDA_CARD_FAN_LENGTH } suspend fun saveSelectionEvent( @@ -154,5 +155,6 @@ internal class ExternalCredentialEventTrackerUseCase @Inject constructor( private const val NHIS_CARD_ID_LENGTH = 8 private const val GHANA_ID_CARD_ID_LENGTH = 15 private const val QR_CODE_LENGTH = 6 + private const val FAYDA_CARD_FAN_LENGTH = 16 } } diff --git a/feature/external-credential/src/main/res/drawable/fayda_card.webp b/feature/external-credential/src/main/res/drawable/fayda_card.webp new file mode 100644 index 0000000000000000000000000000000000000000..853ba53e361340fcf6ce4c329c7ba964c00bea61 GIT binary patch literal 48624 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

Z>xWzLF&13zhYl-rIX?WJxA; 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

XH& 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ϙ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 document.toNonCredentialFields() is MfidDocument.GhanaNhisCard -> document.toNonCredentialFields() is MfidDocument.GhanaQrCode -> emptyMap() + is MfidDocument.FaydaCard -> emptyMap() } return AppExternalCredential( id = scannedCredentialResult.credentialScanId, diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/ExternalCredentialType.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/ExternalCredentialType.kt index c2f20a6014..5f567e8e10 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/ExternalCredentialType.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/ExternalCredentialType.kt @@ -7,6 +7,7 @@ internal fun ExternalCredentialType.toProto(): ProtoExternalCredentialType = whe ExternalCredentialType.NHISCard -> ProtoExternalCredentialType.NHIS_CARD ExternalCredentialType.GhanaIdCard -> ProtoExternalCredentialType.GHANA_ID_CARD ExternalCredentialType.QRCode -> ProtoExternalCredentialType.QR_CODE + ExternalCredentialType.FaydaCard -> ProtoExternalCredentialType.FAYDA_CARD } internal fun ProtoExternalCredentialType.toDomain(): ExternalCredentialType = when(this){ @@ -15,4 +16,5 @@ internal fun ProtoExternalCredentialType.toDomain(): ExternalCredentialType = wh ProtoExternalCredentialType.NHIS_CARD -> ExternalCredentialType.NHISCard ProtoExternalCredentialType.GHANA_ID_CARD -> ExternalCredentialType.GhanaIdCard ProtoExternalCredentialType.QR_CODE -> ExternalCredentialType.QRCode + ProtoExternalCredentialType.FAYDA_CARD -> ExternalCredentialType.FaydaCard } diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/MutliFactorIdConfiguration.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/MutliFactorIdConfiguration.kt index 4a1443e2d6..1d8124419f 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/MutliFactorIdConfiguration.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/MutliFactorIdConfiguration.kt @@ -1,5 +1,6 @@ package com.simprints.infra.config.store.local.models +import com.simprints.infra.config.store.models.FaydaCardConfig import com.simprints.infra.config.store.models.GhanaIdCardConfig import com.simprints.infra.config.store.models.MultiFactorIdConfiguration import com.simprints.infra.config.store.models.NhisCardConfig @@ -11,6 +12,7 @@ internal fun MultiFactorIdConfiguration.toProto(): ProtoMultiFactorIdConfigurati .also { if (ghanaIdCardConfig != null) it.setGhanaIdCardConfig(ghanaIdCardConfig.toProto()) } .also { if (nhisCardConfig != null) it.setNhisCardConfig(nhisCardConfig.toProto()) } .also { if (qrCodeConfig != null) it.setQrCodeConfig(qrCodeConfig.toProto()) } + .also { if (faydaCardConfig != null) it.setFaydaCardConfig(faydaCardConfig.toProto()) } .build() internal fun GhanaIdCardConfig.toProto(): ProtoGhanaIdCardConfig = ProtoGhanaIdCardConfig @@ -27,11 +29,17 @@ internal fun QrCodeConfig.toProto(): ProtoQrCodeConfig = ProtoQrCodeConfig .newBuilder() .build() +internal fun FaydaCardConfig.toProto(): ProtoFaydaCardConfig = ProtoFaydaCardConfig + .newBuilder() + .setIsCapturingAllFields(isCapturingAllFields) + .build() + internal fun ProtoMultiFactorIdConfiguration.toDomain(): MultiFactorIdConfiguration = MultiFactorIdConfiguration( allowedExternalCredentials = allowedExternalCredentialsList.map { it.toDomain() }, ghanaIdCardConfig = if (hasGhanaIdCardConfig()) ghanaIdCardConfig.toDomain() else null, nhisCardConfig = if (hasNhisCardConfig()) nhisCardConfig.toDomain() else null, qrCodeConfig = if (hasQrCodeConfig()) QrCodeConfig else null, + faydaCardConfig = if (hasFaydaCardConfig()) faydaCardConfig.toDomain() else null, ) internal fun ProtoGhanaIdCardConfig.toDomain() = GhanaIdCardConfig( @@ -41,3 +49,7 @@ internal fun ProtoGhanaIdCardConfig.toDomain() = GhanaIdCardConfig( internal fun ProtoNhisCardConfig.toDomain() = NhisCardConfig( isCapturingAllFields = isCapturingAllFields, ) + +internal fun ProtoFaydaCardConfig.toDomain() = FaydaCardConfig( + isCapturingAllFields = isCapturingAllFields, +) diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/models/MultiFactorIdConfiguration.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/models/MultiFactorIdConfiguration.kt index eb1fa31c65..02cc3d1ab7 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/models/MultiFactorIdConfiguration.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/models/MultiFactorIdConfiguration.kt @@ -7,6 +7,7 @@ data class MultiFactorIdConfiguration( val ghanaIdCardConfig: GhanaIdCardConfig?, val nhisCardConfig: NhisCardConfig?, val qrCodeConfig: QrCodeConfig?, + val faydaCardConfig: FaydaCardConfig?, ) data class GhanaIdCardConfig( @@ -18,3 +19,7 @@ data class NhisCardConfig( ) object QrCodeConfig + +data class FaydaCardConfig( + val isCapturingAllFields: Boolean, +) diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiMultiFactorIdConfiguration.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiMultiFactorIdConfiguration.kt index bd1f2bdc5e..03c25e12ce 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiMultiFactorIdConfiguration.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiMultiFactorIdConfiguration.kt @@ -2,10 +2,12 @@ package com.simprints.infra.config.store.remote.models import androidx.annotation.Keep import com.simprints.core.domain.externalcredential.ExternalCredentialType +import com.simprints.infra.config.store.models.FaydaCardConfig import com.simprints.infra.config.store.models.GhanaIdCardConfig import com.simprints.infra.config.store.models.MultiFactorIdConfiguration import com.simprints.infra.config.store.models.NhisCardConfig import com.simprints.infra.config.store.models.QrCodeConfig +import com.simprints.infra.config.store.remote.models.ApiExternalCredentialType.FAYDA_CARD import com.simprints.infra.config.store.remote.models.ApiExternalCredentialType.GHANA_CARD import com.simprints.infra.config.store.remote.models.ApiExternalCredentialType.NHIS_CARD import com.simprints.infra.config.store.remote.models.ApiExternalCredentialType.QR_CODE @@ -18,12 +20,14 @@ internal data class ApiMultiFactorIdConfiguration( val ghanaCard: ApiGhanaIdCardConfig? = null, val nhisCard: ApiNhisCardConfig? = null, val qrCode: ApiQrCodeConfig? = null, + val faydaCard: ApiFaydaCardConfig? = null, ) { fun toDomain(): MultiFactorIdConfiguration = MultiFactorIdConfiguration( allowedExternalCredentials = allowedExternalCredentials.map { it.toDomain() }, ghanaIdCardConfig = ghanaCard?.toDomain(), nhisCardConfig = nhisCard?.toDomain(), qrCodeConfig = qrCode?.toDomain(), + faydaCardConfig = faydaCard?.toDomain(), ) } @@ -49,17 +53,27 @@ object ApiQrCodeConfig { fun toDomain() = QrCodeConfig } +@Keep +@Serializable +data class ApiFaydaCardConfig( + val isCapturingAllFields: Boolean, +) { + fun toDomain() = FaydaCardConfig(isCapturingAllFields = isCapturingAllFields) +} + @Keep enum class ApiExternalCredentialType { NHIS_CARD, GHANA_CARD, QR_CODE, + FAYDA_CARD, ; fun toDomain(): ExternalCredentialType = when (this) { NHIS_CARD -> ExternalCredentialType.NHISCard GHANA_CARD -> ExternalCredentialType.GhanaIdCard QR_CODE -> ExternalCredentialType.QRCode + FAYDA_CARD -> ExternalCredentialType.FaydaCard } } @@ -67,4 +81,5 @@ fun ExternalCredentialType.fromDomainToApi(): ApiExternalCredentialType = when ( ExternalCredentialType.NHISCard -> NHIS_CARD ExternalCredentialType.GhanaIdCard -> GHANA_CARD ExternalCredentialType.QRCode -> QR_CODE + ExternalCredentialType.FaydaCard -> FAYDA_CARD } diff --git a/infra/config-store/src/main/proto/project_config.proto b/infra/config-store/src/main/proto/project_config.proto index cf018c7198..dd19b3d1fa 100644 --- a/infra/config-store/src/main/proto/project_config.proto +++ b/infra/config-store/src/main/proto/project_config.proto @@ -22,6 +22,7 @@ enum ProtoExternalCredentialType { NHIS_CARD = 1; GHANA_ID_CARD = 2; QR_CODE = 3; + FAYDA_CARD = 4; } message ProtoMultiFactorIdConfiguration { @@ -29,6 +30,7 @@ message ProtoMultiFactorIdConfiguration { optional ProtoGhanaIdCardConfig ghana_id_card_config = 2; optional ProtoNhisCardConfig nhis_card_config = 3; optional ProtoQrCodeConfig qr_code_config = 4; + optional ProtoFaydaCardConfig fayda_card_config = 5; } message ProtoGhanaIdCardConfig { @@ -43,6 +45,10 @@ message ProtoQrCodeConfig { // Empty, reserved for future configuration } +message ProtoFaydaCardConfig { + bool is_capturing_all_fields = 1; +} + message ProtoGeneralConfiguration { repeated Modality modalities = 1; repeated string language_options = 2; diff --git a/infra/core/src/main/java/com/simprints/core/domain/externalcredential/ExternalCredentialType.kt b/infra/core/src/main/java/com/simprints/core/domain/externalcredential/ExternalCredentialType.kt index 01e93df4d2..afd6be00f6 100644 --- a/infra/core/src/main/java/com/simprints/core/domain/externalcredential/ExternalCredentialType.kt +++ b/infra/core/src/main/java/com/simprints/core/domain/externalcredential/ExternalCredentialType.kt @@ -9,4 +9,5 @@ enum class ExternalCredentialType { NHISCard, GhanaIdCard, QRCode, + FaydaCard, } diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/cosync/v1/CoSyncExternalCredentialType.kt b/infra/events/src/main/java/com/simprints/infra/events/event/cosync/v1/CoSyncExternalCredentialType.kt index 1bfa503f14..afc722790d 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/cosync/v1/CoSyncExternalCredentialType.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/cosync/v1/CoSyncExternalCredentialType.kt @@ -16,16 +16,19 @@ enum class CoSyncExternalCredentialType { NHISCard, GhanaIdCard, QRCode, + FaydaCard, } fun ExternalCredentialType.toCoSync(): CoSyncExternalCredentialType = when (this) { ExternalCredentialType.NHISCard -> CoSyncExternalCredentialType.NHISCard ExternalCredentialType.GhanaIdCard -> CoSyncExternalCredentialType.GhanaIdCard ExternalCredentialType.QRCode -> CoSyncExternalCredentialType.QRCode + ExternalCredentialType.FaydaCard -> CoSyncExternalCredentialType.FaydaCard } fun CoSyncExternalCredentialType.toDomain(): ExternalCredentialType = when (this) { CoSyncExternalCredentialType.NHISCard -> ExternalCredentialType.NHISCard CoSyncExternalCredentialType.GhanaIdCard -> ExternalCredentialType.GhanaIdCard CoSyncExternalCredentialType.QRCode -> ExternalCredentialType.QRCode + CoSyncExternalCredentialType.FaydaCard -> ExternalCredentialType.FaydaCard } diff --git a/infra/resources/src/main/res/values/strings.xml b/infra/resources/src/main/res/values/strings.xml index ed545595d7..edf3a54659 100644 --- a/infra/resources/src/main/res/values/strings.xml +++ b/infra/resources/src/main/res/values/strings.xml @@ -498,9 +498,11 @@ NHIS Card Ghana ID Card QR Code + Fayda Card Membership number Personal ID QR Code value + Fayda Alias Number (FAN) a document Skip scanning? diff --git a/testing/data-generator/src/main/java/com/simprints/feature/datagenerator/enrollmentrecords/InsertEnrollmentRecordsUseCase.kt b/testing/data-generator/src/main/java/com/simprints/feature/datagenerator/enrollmentrecords/InsertEnrollmentRecordsUseCase.kt index c11150211b..095718229b 100644 --- a/testing/data-generator/src/main/java/com/simprints/feature/datagenerator/enrollmentrecords/InsertEnrollmentRecordsUseCase.kt +++ b/testing/data-generator/src/main/java/com/simprints/feature/datagenerator/enrollmentrecords/InsertEnrollmentRecordsUseCase.kt @@ -182,6 +182,7 @@ internal class InsertEnrollmentRecordsUseCase @Inject constructor( ExternalCredentialType.GhanaIdCard -> "GHA-12345789-0" ExternalCredentialType.NHISCard -> "12345678" ExternalCredentialType.QRCode -> "123456" + ExternalCredentialType.FaydaCard -> "1234567812345678" }.asTokenizableRaw() return tokenizationProcessor.encrypt( From f17ddf8bac0c18aa2b04b6d6c12dc5f0b199ce6d Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 15 Jul 2026 15:06:40 +0300 Subject: [PATCH 2/5] [MS-1336] Fayda card support tests fixed --- .../scanocr/usecase/ScanMfidDocumentUseCaseTest.kt | 9 +++++++++ .../usecase/ExternalCredentialEventTrackerUseCaseTest.kt | 1 + 2 files changed, 10 insertions(+) diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/ScanMfidDocumentUseCaseTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/ScanMfidDocumentUseCaseTest.kt index d0b5df3c8a..c8ef039226 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/ScanMfidDocumentUseCaseTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/ScanMfidDocumentUseCaseTest.kt @@ -6,6 +6,7 @@ import com.simprints.feature.externalcredential.model.BoundingBox import com.simprints.feature.externalcredential.screens.scanocr.model.OcrDocumentType import com.simprints.feature.externalcredential.screens.scanocr.model.OcrScanResult import com.simprints.feature.externalcredential.screens.scanocr.reader.OcrLine +import com.simprints.infra.config.store.models.FaydaCardConfig import com.simprints.infra.config.store.models.GhanaIdCardConfig import com.simprints.infra.config.store.models.MultiFactorIdConfiguration import com.simprints.infra.config.store.models.NhisCardConfig @@ -31,6 +32,9 @@ internal class ScanMfidDocumentUseCaseTest { @MockK private lateinit var ghanaIdCardOcrReaderUseCase: GhanaIdCardOcrReaderUseCase + @MockK + private lateinit var faydaCardOcrReaderUseCase: FaydaCardOcrReaderUseCase + @MockK private lateinit var credentialImageRepository: CredentialImageRepository @@ -43,18 +47,21 @@ internal class ScanMfidDocumentUseCaseTest { ghanaIdCardConfig = null, nhisCardConfig = NhisCardConfig(isCapturingAllFields = false), qrCodeConfig = null, + faydaCardConfig = null, ) private val ghanaIdConfig = MultiFactorIdConfiguration( allowedExternalCredentials = emptyList(), ghanaIdCardConfig = GhanaIdCardConfig(isCapturingAllFields = false), nhisCardConfig = null, qrCodeConfig = null, + faydaCardConfig = null, ) private val allFieldsConfig = MultiFactorIdConfiguration( allowedExternalCredentials = emptyList(), ghanaIdCardConfig = GhanaIdCardConfig(isCapturingAllFields = true), nhisCardConfig = NhisCardConfig(isCapturingAllFields = true), qrCodeConfig = null, + faydaCardConfig = FaydaCardConfig(isCapturingAllFields = true), ) @Before @@ -64,6 +71,7 @@ internal class ScanMfidDocumentUseCaseTest { readTextFromImage = readTextFromImage, ghanaNhisCardOcrReaderUseCase = ghanaNhisCardOcrReaderUseCase, ghanaIdCardOcrReaderUseCase = ghanaIdCardOcrReaderUseCase, + faydaCardOcrReaderUseCase = faydaCardOcrReaderUseCase, credentialImageRepository = credentialImageRepository, ) coEvery { credentialImageRepository.saveCredentialScan(any(), any()) } returns savedImagePath @@ -199,6 +207,7 @@ internal class ScanMfidDocumentUseCaseTest { ghanaIdCardConfig = null, nhisCardConfig = null, qrCodeConfig = null, + faydaCardConfig = null, ) every { readTextFromImage(bitmap) } returns mockk(relaxed = true) diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCaseTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCaseTest.kt index 15e764fd1f..1806dfdb7d 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCaseTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCaseTest.kt @@ -224,6 +224,7 @@ class ExternalCredentialEventTrackerUseCaseTest { ExternalCredentialType.NHISCard -> MfidDocument.GhanaNhisCard(credential = RAW_SCANNED_VALUE.asTokenizableRaw()) ExternalCredentialType.GhanaIdCard -> MfidDocument.GhanaIdCard(credential = RAW_SCANNED_VALUE.asTokenizableRaw()) ExternalCredentialType.QRCode -> MfidDocument.GhanaQrCode(credential = RAW_SCANNED_VALUE.asTokenizableRaw()) + ExternalCredentialType.FaydaCard -> MfidDocument.FaydaCard(credential = RAW_SCANNED_VALUE.asTokenizableRaw()) } val scannedResult = ScannedCredentialResult( credentialScanId = SCAN_ID, From f536897d01963b75052e3276fe29419c6b7d1588 Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 15 Jul 2026 15:15:03 +0300 Subject: [PATCH 3/5] [MS-1336] Fayda card credential check fixed --- .../search/ExternalCredentialSearchViewModel.kt | 4 ++-- .../ExternalCredentialSearchViewModelTest.kt | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt index 93a042f416..1d18882c6d 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt @@ -214,8 +214,8 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( GhanaIdCardOcrReaderUseCase.GHANA_ID_PATTERN.matches(credential) } ExternalCredentialType.FaydaCard -> { - // Fayda Alias Number (FAN): exactly 16 digits after stripping non-digit chars - FaydaCardOcrReaderUseCase.FAN_PATTERN.matches(credential.filter(Char::isDigit)) + // Fayda Alias Number (FAN): exactly 16 digits + FaydaCardOcrReaderUseCase.FAN_PATTERN.matches(credential) } ExternalCredentialType.QRCode -> { // No QR code validation as of 2025.4.1 diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt index 95e5051491..808f7e45da 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt @@ -342,4 +342,18 @@ internal class ExternalCredentialSearchViewModelTest { assertThat(viewModel.isCredentialFormatValid("")).isTrue() assertThat(viewModel.isCredentialFormatValid(null)).isFalse() } + + @Test + fun `isCredentialFormatValid validates Fayda card format`() = runTest { + every { mockScannedCredentialResult.credentialType } returns ExternalCredentialType.FaydaCard + + viewModel = createViewModel() + + assertThat(viewModel.isCredentialFormatValid("1234567812345678")).isTrue() + assertThat(viewModel.isCredentialFormatValid("1234 5678 1234 5678")).isFalse() + assertThat(viewModel.isCredentialFormatValid("1234-5678-1234-5678")).isFalse() + assertThat(viewModel.isCredentialFormatValid("123456781234567")).isFalse() + assertThat(viewModel.isCredentialFormatValid("invalid")).isFalse() + assertThat(viewModel.isCredentialFormatValid(null)).isFalse() + } } From 3ca2718c82ba63bd9f3c2fcf94ee32639cdbb76c Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 15 Jul 2026 15:23:16 +0300 Subject: [PATCH 4/5] [MS-1336] Fayda card support tests fixed --- .../java/com/simprints/infra/config/store/testtools/Models.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt b/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt index 8b3b3b9feb..f4af2b4687 100644 --- a/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt +++ b/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt @@ -427,6 +427,7 @@ internal val multiFactorIdConfiguration = MultiFactorIdConfiguration( allowedExternalCredentials = listOf(allowedExternalCredential), ghanaIdCardConfig = null, nhisCardConfig = null, + faydaCardConfig = null, qrCodeConfig = null, ) From 8d8f72b994bdbf39995279c4a0945329820bba69 Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 15 Jul 2026 15:33:59 +0300 Subject: [PATCH 5/5] [MS-1336] Fayda card support tests fixed --- .../java/com/simprints/infra/sync/config/testtools/Models.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt b/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt index d517b76ce0..aab4dbd69b 100644 --- a/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt +++ b/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt @@ -124,6 +124,7 @@ internal val multiFactorIdConfiguration = MultiFactorIdConfiguration( allowedExternalCredentials = listOf(allowedExternalCredential), ghanaIdCardConfig = null, nhisCardConfig = null, + faydaCardConfig = null, qrCodeConfig = null, )