diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt index f149795610..2efc2ab683 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/ResourceExtension.kt @@ -188,6 +188,7 @@ fun List.generateMissingItems( } } } + /** * Set all questions that are not of type [Questionnaire.QuestionnaireItemType.GROUP] to readOnly if * [readOnly] is true. This also generates the correct FHIRPath population expression for each @@ -195,7 +196,7 @@ fun List.generateMissingItems( */ fun List.prepareQuestionsForReadingOrEditing( path: String, - readOnly: Boolean, + readOnly: Boolean = false, readOnlyLinkIds: List? = emptyList() ) { forEach { item -> @@ -214,6 +215,31 @@ fun List.prepareQuestionsForReadingOrE } } +/** + * Set all questions that are not of type [Questionnaire.QuestionnaireItemType.GROUP] to readOnly if + * [readOnlyLinkIds] item are there while editing the form. This also generates the correct FHIRPath + * population expression for each question when mapped to the corresponding [QuestionnaireResponse] + */ +fun List.prepareQuestionsForEditing( + path: String, + readOnlyLinkIds: List? = emptyList() +) { + forEach { item -> + if (item.type != Questionnaire.QuestionnaireItemType.GROUP) { + item.readOnly = readOnlyLinkIds?.contains(item.linkId) == true + item.item.prepareQuestionsForEditing( + "$path.where(linkId = '${item.linkId}').answer.item", + readOnlyLinkIds + ) + } else { + item.item.prepareQuestionsForEditing( + "$path.where(linkId = '${item.linkId}').item", + readOnlyLinkIds + ) + } + } +} + /** Delete resources in [QuestionnaireResponse.contained] from the database */ suspend fun QuestionnaireResponse.deleteRelatedResources(defaultRepository: DefaultRepository) { contained.forEach { defaultRepository.delete(it) } diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/configuration/QuestionnaireConfigTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/configuration/QuestionnaireConfigTest.kt index b0cecce70a..924c697c5d 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/configuration/QuestionnaireConfigTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/configuration/QuestionnaireConfigTest.kt @@ -17,6 +17,7 @@ package org.smartregister.fhircore.engine.configuration import dagger.hilt.android.testing.HiltAndroidTest +import org.hl7.fhir.r4.model.ResourceType import org.junit.Assert import org.junit.Test import org.smartregister.fhircore.engine.configuration.event.EventWorkflow @@ -62,7 +63,7 @@ class QuestionnaireConfigTest : RobolectricTest() { groupResource = GroupResourceConfig( groupIdentifier = "@{groupIdentifier}", - memberResourceType = "Condition", + memberResourceType = ResourceType.Condition, removeMember = true, removeGroup = true, deactivateMembers = false @@ -131,7 +132,7 @@ class QuestionnaireConfigTest : RobolectricTest() { @Test fun testDefaultGroupResourceConfig() { val groupIdentifier = "groupIdentifier" - val memberResourceType = "Condition" + val memberResourceType = ResourceType.Condition val groupResourceConfig = GroupResourceConfig(groupIdentifier, memberResourceType) groupResourceConfig.apply { Assert.assertEquals(groupIdentifier, this.groupIdentifier) diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt index 1553873c62..a27f4158b1 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/data/local/DefaultRepositoryTest.kt @@ -604,7 +604,7 @@ class DefaultRepositoryTest : RobolectricTest() { defaultRepositorySpy.removeGroupMember( memberId, null, - patientMemberRep.resourceType.name, + patientMemberRep.resourceType, emptyMap() ) Assert.assertFalse(patientMemberRep.active) @@ -627,7 +627,7 @@ class DefaultRepositoryTest : RobolectricTest() { defaultRepositorySpy.removeGroupMember( memberId, null, - patientMemberRep.resourceType.name, + patientMemberRep.resourceType, emptyMap() ) Assert.assertTrue(patientMemberRep.active) @@ -718,7 +718,7 @@ class DefaultRepositoryTest : RobolectricTest() { defaultRepository.removeGroupMember( memberId = memberId, groupId = groupId, - groupMemberResourceType = groupMemberResourceType, + memberResourceType = ResourceType.Patient, emptyMap() ) } diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/robolectric/RobolectricTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/robolectric/RobolectricTest.kt index 9501f03b56..4e0cea7d53 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/robolectric/RobolectricTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/robolectric/RobolectricTest.kt @@ -21,15 +21,15 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import dagger.hilt.android.testing.HiltTestApplication import io.mockk.clearAllMocks -import java.io.File -import java.io.FileReader -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit import org.junit.AfterClass import org.junit.BeforeClass import org.junit.runner.RunWith import org.robolectric.annotation.Config import org.smartregister.fhircore.engine.app.fakes.FakeKeyStore +import java.io.File +import java.io.FileReader +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit @RunWith(FhircoreTestRunner::class) @Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class) @@ -41,7 +41,7 @@ abstract class RobolectricTest { val latch = CountDownLatch(1) val observer: Observer = object : Observer { - override fun onChanged(o: T?) { + override fun onChanged(o: T) { data[0] = o latch.countDown() liveData.removeObserver(this) diff --git a/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt b/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt index 9939eb0a84..ca4aec493a 100644 --- a/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt +++ b/android/engine/src/test/java/org/smartregister/fhircore/engine/util/extension/ResourceExtensionTest.kt @@ -25,7 +25,6 @@ import io.mockk.coVerify import io.mockk.mockk import java.math.BigDecimal import java.util.Date -import junit.framework.Assert.assertEquals import kotlinx.coroutines.runBlocking import org.hl7.fhir.r4.model.BooleanType import org.hl7.fhir.r4.model.CodeableConcept @@ -746,4 +745,17 @@ class ResourceExtensionTest : RobolectricTest() { Assert.assertTrue(questionnaire.item[1].readOnly) Assert.assertTrue(questionnaire.item[2].readOnly) } + + @Test + fun `prepareQuestionsForEditing should set readOnly correctly when readOnlyLinkIds passed`() { + val questionnaire = Questionnaire() + questionnaire.item.add(Questionnaire.QuestionnaireItemComponent().apply { linkId = "1" }) + questionnaire.item.add(Questionnaire.QuestionnaireItemComponent().apply { linkId = "2" }) + questionnaire.item.add(Questionnaire.QuestionnaireItemComponent().apply { linkId = "3" }) + questionnaire.item.prepareQuestionsForEditing("", readOnlyLinkIds = listOf("1", "3")) + + Assert.assertTrue(questionnaire.item[0].readOnly) + Assert.assertFalse(questionnaire.item[1].readOnly) + Assert.assertTrue(questionnaire.item[2].readOnly) + } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivity.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivity.kt index 494b73a45e..79252a3809 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivity.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivity.kt @@ -171,7 +171,8 @@ open class QuestionnaireActivity : BaseMultiLanguageActivity(), View.OnClickList val questionnaire = questionnaireViewModel.loadQuestionnaire( questionnaireConfig = questionnaireConfig, - prePopulationParams = prePopulationParams + prePopulationParams = prePopulationParams, + readOnlyLinkIds = questionnaireConfig.readOnlyLinkIds ) if (questionnaire == null) { showToast(getString(R.string.questionnaire_not_found)) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index bb3f717c50..b63a08a388 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -76,6 +76,7 @@ import org.smartregister.fhircore.engine.util.extension.findSubject import org.smartregister.fhircore.engine.util.extension.isExtractionCandidate import org.smartregister.fhircore.engine.util.extension.isIn import org.smartregister.fhircore.engine.util.extension.prePopulateInitialValues +import org.smartregister.fhircore.engine.util.extension.prepareQuestionsForEditing import org.smartregister.fhircore.engine.util.extension.prepareQuestionsForReadingOrEditing import org.smartregister.fhircore.engine.util.extension.referenceValue import org.smartregister.fhircore.engine.util.extension.resourceClassType @@ -130,13 +131,16 @@ constructor( readOnlyLinkIds: List? = emptyList() ): Questionnaire? = defaultRepository.loadResource(questionnaireConfig.id)?.apply { - if (questionnaireConfig.type.isReadOnly() || questionnaireConfig.type.isEditMode()) { + if (questionnaireConfig.type.isReadOnly()) { item.prepareQuestionsForReadingOrEditing( QUESTIONNAIRE_RESPONSE_ITEM, - questionnaireConfig.type.isReadOnly() || questionnaireConfig.type.isEditMode(), + questionnaireConfig.type.isReadOnly(), readOnlyLinkIds ) } + if (questionnaireConfig.type.isEditMode()) { + item.prepareQuestionsForEditing(QUESTIONNAIRE_RESPONSE_ITEM, readOnlyLinkIds) + } // prepopulate questionnaireItems with initial values prePopulationParams?.takeIf { it.isNotEmpty() }?.let { nonEmptyParams -> editQuestionnaireResourceParams = @@ -273,12 +277,14 @@ constructor( else if (bundleEntry.resource.resourceType.isIn(ResourceType.List)) { // No need to update List resource subject to the questionnaire subject // only need to change location reference as in entry.item-reference - val lastLocationRes = - bundle.entry - .first { item -> item.resource.resourceType.isIn(ResourceType.Group) } - .resource - (bundleEntry.resource as ListResource).entry[0].item.reference = - (lastLocationRes as Group).referenceValue() + if (!bundle.entry.first().hasResource()) { + val lastLocationRes = + bundle.entry + .first { item -> item.resource.resourceType.isIn(ResourceType.Group) } + .resource + (bundleEntry.resource as ListResource).entry[0].item.reference = + (lastLocationRes as Group).referenceValue() + } } else { bundleEntry.resource.setPropertySafely("subject", questionnaireResponse.subject) bundleEntry.resource.setPropertySafely("patient", questionnaireResponse.subject)